spec(governance): rename body-level status on check_governance and report_plan_outcome (#4897)#4902
Merged
Conversation
…report_plan_outcome (#4897) Frees the top-level `status` key for envelope task-status (TaskStatus) under MCP flat-on-the-wire serialization. The body-level governance field and the envelope share a root key but carry different enums; whichever wins on the wire, the other is silently destroyed and no validator catches it. WG-recommended Option A per the issue triage (both ad-tech-protocol-expert and adtech-product-expert): - check-governance-response.json: status → verdict (enum unchanged: approved | denied | conditions). Updates the three if/then discriminator blocks, the required[], and prose threads on findings/conditions/expires_at. - report-plan-outcome-response.json: status → outcome_state (enum unchanged: accepted | findings). Updates required[] and the findings prose thread. - get-plan-audit-logs-response.json: entries[].status → entries[].verdict (cascade for vocabulary consistency with check-governance-response). Other status fields on the same response (plans[].status, governed_actions[].status) are lifecycle states, not verdicts — left unchanged. Docs swept (~25 example bodies + table descriptions + prose): - docs/governance/overview.mdx - docs/governance/campaign/tasks/{check_governance,report_plan_outcome,get_plan_audit_logs}.mdx - docs/governance/campaign/{audit-trail,specification}.mdx - docs/reference/whats-new-in-3-1.mdx (migration note in Final-spec clarifications batch) Storyboards swept — the triage scoped this as "no yaml renames needed" but during implementation found four storyboards with field_present / field_value assertions keyed on path: "status" against check-governance-response. Updated to path: "verdict": - specialisms/governance-spend-authority/{index,denied}.yaml - specialisms/governance-delivery-monitor/index.yaml - protocols/governance/index.yaml (plus a stale expected block referencing status: recorded — not in the enum — corrected to outcome_state: accepted) Test fixture: composed-schema-validation report_plan_outcome case updated to use outcome_state. Adopter impact: wire-shape change on three experimental governance schemas (x-status: experimental). Buyers and sellers rename one property name per emitter / consumer; enum values unchanged. SDK regen required. Related: - #4876 — envelope status REQUIRED (beta.2) - #4895 — companion media-buy collision (separate PR) - #4896 — per-task envelope fold; carve-outs for these two schemas can be removed once this lands Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
LGTM. Clean rename, comprehensive sweep, sound rationale. Frees status for the envelope task-status while preserving the lifecycle status fields on plans[] and governed_actions[] — that disambiguation is the load-bearing part.
Things I checked
check-governance-response.json:11-74— all threeif/thendiscriminator blocks rekeyed onverdict; everyif.properties.verdictpaired withverdictinif.required; originalconditions → require conditions+minItems:1,denied → require findings+minItems:1,approved|conditions → require expires_atsemantics reproduced exactly. No stalestatuskeys leftover.check-governance-response.json:216—required: [check_id, verdict, plan_id, explanation]correctly carries the renamed field.report-plan-outcome-response.json:18-25,89-92—outcome_state(right shape —accepted | findingsis a state, not a verdict; reusingverdictwould have been wrong), enum unchanged, required[] updated.get-plan-audit-logs-response.json:258—entries[].verdictcascade with$refto/schemas/enums/governance-decision.json.plans[].statusandgoverned_actions[].statuscorrectly preserved as lifecycle states.verdictchoice cross-checked against existing AdCP idiom —content-standards/calibrate-content-response.jsonandenums/binary-verdict.jsonuse the same vocabulary.- Changeset type
minoragainstx-status: experimentalon all three schemas — consistent with prior experimental-surface renames (#4196, #4796) and with the documented contract atdocs/reference/experimental-status.mdx. - Storyboard sweep —
governance-spend-authority/{index,denied}.yaml,governance-delivery-monitor/index.yaml,protocols/governance/index.yamlall consistent; the stalestatus: recordedblock inprotocols/governance/index.yaml:601(value never in the enum) correctly corrected tooutcome_state: accepted. docs/reference/whats-new-in-3-1.mdx:202covers the rename, calls out the experimental-surface justification, lists migration steps.- CI:
Storyboards (/governance)andServer integration testsboth green at current floors.
Follow-ups (non-blocking — file as issues)
- Cascade miss:
entries[].outcome_status.get-plan-audit-logs-response.json:346still defines a free-stringoutcome_statuson outcome entries — surfaces the same concept as the renamedoutcome_stateonreport_plan_outcome. The PR rationale explicitly invokes vocabulary consistency for theentries[].verdictcascade; the symmetric outcome-side cascade is missing. Either rename tooutcome_stateand constrain to the same enum, or add one sentence to the schema description naming the deliberate divergence. summary.statusesis now misleading.get-plan-audit-logs-response.json:91keeps the property namestatusesbut its keys areapproved | denied | conditions | human_reviewed— i.e. it counts verdicts. After this PR, the rest of the file calls those "verdicts."summary.verdicts(orverdict_counts) restores consistency; the doc table atget_plan_audit_logs.mdx:229inherits the same word-overload.- Training-agent server still emits the old field names.
server/src/training-agent/governance-handlers.ts:1312(handleReportPlanOutcome→status:),:1498(handleGetPlanAuditLogsentries →status:),:1641(buildCheckResponse→status:). CI is green because the runtime validates against SDK-bundled schemas, not in-repo source — but once@adcp/sdkregens off this PR, the server stops conforming. Sequence: rename in the SDK regen, then flip the three emission sites and the ~25 assertion sites inserver/tests/unit/training-agent.test.ts. Worth a tracking issue so it doesn't fall through the cracks between SDK and server. - 6-week notice anchor. Experimental-surface contract requires notice; the changeset should cite the date or the issue (#4897) as the clock-start. Reads as wire-shape change with no notice attestation today.
Minor nits (non-blocking)
Status valuesheading survived.docs/governance/campaign/tasks/check_governance.mdx:88still reads## Status valuesand the table column header at L90 is| Status |. The prose underneath (L95, L102) was correctly migrated to "verdict" — the heading and column header are the stragglers.- Triage scope. The issue triage initially scoped storyboards as "no yaml renames needed" — four storyboards needed renames. Worth noting for future spec-rename triages; the wire-shape change always cascades into
field_present/field_valueassertions, even when the storyboard runner's lint suite doesn't catch them upfront.
Approving. The cascade miss on outcome_status is the one I'd want resolved before 3.1 GA — file it as a follow-up so the experimental-surface contract isn't paying down a second rename later.
Merged
7 tasks
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…pdate success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…pdate success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…pdate success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…pdate success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…pdate success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 21, 2026
…ditive-deprecate, #4895) (#4904) * spec(media-buy): additive-deprecate body-level `status` on create / update success responses (#4895) Under MCP flat-on-the-wire serialization, the envelope task-status (`status`, TaskStatus) and the body-level media-buy lifecycle status (`status`, MediaBuyStatus) share the same root key on `CreateMediaBuySuccess` and `UpdateMediaBuySuccess`. Enums overlap on completed | canceled | rejected and diverge elsewhere — a MediaBuyStatus is silently destroyed when the envelope stamps a TaskStatus at the same path. No validator catches it. WG-recommended Option E (additive-deprecate, 3.1 minor → 4.0 removal). **Strictly additive — no schema is renamed and no `required[]` constraint changes.** - create-media-buy-response.json `CreateMediaBuySuccess`: adds `media_buy_status: $ref media-buy-status.json` alongside the existing `status`. The legacy `status` is marked `deprecated: true` and slated for removal in 4.0. Neither field is in `required[]` (both optional in 3.1). `CreateMediaBuySubmitted` branch unchanged — its `status: { const: "submitted" }` is the TaskStatus discriminator. - update-media-buy-response.json `UpdateMediaBuySuccess`: symmetric. Out of scope (deliberate): `get-media-buys-response.json` `media_buys[].status`, `get-media-buy-delivery-response.json` `media_buy_deliveries[].status`, and `core/media-buy.json` `status` are NOT renamed. These fields live nested at depth ≥ 1 inside arrays, so the envelope `status` at the response root does not collide with them on the wire. Renaming them would require either a breaking `required[]` swap or a double-field transition for no wire-collision payoff. The nested-vocabulary inconsistency in 3.1 (one buyer call returns `media_buy_status` at root, the next returns `status` inside an array) is the price of keeping this strictly additive. Resolve in 4.0 alongside the legacy-`status` removal, when a clean cascade rename is on the table. `cancel_media_buy` is performed via update_media_buy with cancel intent — there is no dedicated cancel tool. Inherits the rename from UpdateMediaBuySuccess; no separate handling. Storyboards swept: - protocols/media-buy/state-machine.yaml — three field_present path:"status" assertions on update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/pending_creatives_to_start.yaml — two field_value assertions checking MediaBuyStatus values on create-media-buy-response.json / update-media-buy-response.json → path:"media_buy_status". - protocols/media-buy/scenarios/create_media_buy_async.yaml — left as path:"status" (this checks the submitted-arm TaskStatus discriminator). Docs: - task-reference/update_media_buy.mdx — cancellation success example shows the canonical media_buy_status form. - reference/whats-new-in-3-1.mdx — migration note in Final-spec clarifications batch. Adopter impact: - Sellers (3.1+) SHOULD emit media_buy_status on create/update success; MAY continue emitting the deprecated legacy `status` during the window. - Buyers (3.1+) MUST prefer media_buy_status when present; MAY fall back to legacy `status` for compatibility. - 3.0 sellers and buyers continue to work unchanged. No required-field swap, no rename, no breakage. - 4.0: deprecated top-level `status` removed from create/update success branches. The nested `status` fields on get-media-buys-response items, get-media-buy-delivery-response items, and core/media-buy.json should be addressed in the same 4.0 release as a coherent cascade. - SDK regen required for @adcp/client, adcp-go, Python. Related: - #4876 — envelope status REQUIRED (beta.2) - #4897 — companion governance schema rename (separate PR #4902) - adcontextprotocol/adcp-client#1898 — SDK-side audit + transport precedence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * spec(media-buy): fold #4908 — MUST-equality clause + storyboard assertions for deprecation window During the 3.1 deprecation window both `status` (deprecated) and `media_buy_status` (canonical) coexist on CreateMediaBuySuccess / UpdateMediaBuySuccess. The prior schema was silent on whether sellers emitting both fields must carry the same value; JSON Schema validation passed even for divergent values (e.g., status: active, media_buy_status: paused). - Schema descriptions: add MUST-identical-values clause to `status` and `media_buy_status` on both create-media-buy-response.json and update-media-buy-response.json. - Storyboard (pending_creatives_to_start.yaml): add `field_value_or_absent` assertions on the deprecated `status` path at the two steps that already check `media_buy_status` — passes when status is absent (conformant 3.1 sellers), fails only when status is present with a diverging value. - Migration doc: document the equality requirement and explain why if/then JSON Schema constraint was deferred (short window, codegen compat uncertainty, storyboard is sufficient). Resolves #4908. Refs #4895. * spec(media-buy): align schema description with 3.2 removal timeline (#4904 follow-up) The schema descriptions on CreateMediaBuySuccess / UpdateMediaBuySuccess for the deprecated top-level `status` and the new `media_buy_status` both still said "removed in 4.0" while every other surface (migration page, changeset adopter section, whats-new bullet, task-reference tables) was updated to "removed in 3.2 (#4906)" when the cascade was split. aao-release-bot caught the drift. Three replacements per file ("removed in 4.0" → "removed in 3.2 (#4906)") on: - create-media-buy-response.json: media_buy_status description, deprecated status description - update-media-buy-response.json: same Schema↔doc parity restored. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4897.
Summary
Frees the top-level
statuskey for envelope task-status (TaskStatus) under MCP flat-on-the-wire serialization. The body-level governance field and the envelope share a root key but carry different enums; whichever wins on the wire, the other is silently destroyed and no validator catches it.WG-recommended Option A per the issue triage (both
ad-tech-protocol-expertandadtech-product-expertconverged):check-governance-response.json:status→verdict(enum unchanged:approved | denied | conditions)report-plan-outcome-response.json:status→outcome_state(enum unchanged:accepted | findings)get-plan-audit-logs-response.json:entries[].status→entries[].verdict(vocabulary cascade)Other
statusfields on the same response shapes (plans[].status,governed_actions[].status) are lifecycle states, not verdicts — left unchanged.Scope
static/schemas/source/governance/{check-governance,report-plan-outcome,get-plan-audit-logs}-response.jsondocs/governance/overview.mdx,docs/governance/campaign/{audit-trail,specification}.mdx,docs/governance/campaign/tasks/{check_governance,report_plan_outcome,get_plan_audit_logs}.mdxdocs/reference/whats-new-in-3-1.mdx(Final-spec clarifications batch)static/compliance/source/specialisms/governance-{spend-authority,delivery-monitor}/*.yaml,static/compliance/source/protocols/governance/index.yamltests/composed-schema-validation.test.cjsStoryboard sweep correction: the issue triage scoped storyboards as "no yaml renames needed." During implementation found four storyboards with
field_present/field_valueassertions keyed onpath: "status"againstcheck-governance-response.json— updated topath: "verdict". Also corrected one staleexpectedblock inprotocols/governance/index.yamlthat referencedstatus: recorded(not in the schema enum) → nowoutcome_state: accepted.Adopter impact
Wire-shape change on three experimental governance schemas (
x-status: experimental). Buyers and sellers rename one property name per emitter / consumer; enum values are unchanged. SDK regen required for@adcp/client,adcp-go, and the Python client. Per the experimental-surface contract, this is a sanctioned 3.1 pre-GA adjustment.Test plan
npm run build:schemas— cleannpm run build:compliance— clean (all storyboard lints that key on the verdict path now resolve)npm run test:schemas— 8/8npm run test:examples— 36/36npm run test:composed— 43/43 (one pre-existing failure onreport_plan_outcome accepts replayedfixed by updating the fixture to useoutcome_state)npm run test:json-schema— 270/270npm run test:rejection-arm-mutex— 4/4npm run test:docs-nav— 15/15npm run test:storyboard-doc-parity— 0 failnpm run test:storyboard-contradictions— 0 failnpm run test:storyboard-validations-paths— 1 pre-existing failure unrelated to this change (brand-rights/governance_denied.yaml; verified on origin/main)npm run test:storyboard-context-output-paths— 1 pre-existing failure onmain(sales-guaranteed)npm run test:storyboard-sample-request-schema— 1 pre-existing failure onmain(native_in_feed)Related
statuson every task response envelope (closes #4832) #4876 — envelopestatusREQUIRED (beta.2)status(TaskStatus) collides with payloadstatus(MediaBuyStatus) at the same top-level key on create/update/cancel_media_buy #4895 — companion media-buystatus/MediaBuyStatuscollision (separate PR)check-governance-response.jsonandreport-plan-outcome-response.jsoncan be dropped once this merges — both schemas pick up the standard envelope fold cleanly🤖 Generated with Claude Code