You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 523df2e
Browse filesBrowse the repository at this point in the historyBrowse files
feat(errors): AdCP 3.3 mutation_outcome and unsupported-targeting-dimension details (#1642)
AdcpError(mutation_outcome=...) projects error.mutation_outcome onto
adcp_error and errors[] on MCP and A2A, and translate_error's A2A data;
an ADCPTaskError projects its first entry's claim, and the decisioning
dispatcher keeps it when it re-raises a task error as AdcpError.
The framework sets not_applied only where it holds pre-commit evidence:
a structured rejection create_tool_caller raises before a mutating
handler runs (schema and model validation, version gates, pre-validation
hooks) and IDEMPOTENCY_CONFLICT, detected on lookup before anything is
written. A claim the raise already makes is kept; errors a handler
raises, and the INTERNAL_ERROR wrap of an unexpected exception, carry no
claim. Reads never carry it.
UnsupportedTargetingDimensionError (an UnsupportedFeatureError) emits
UNSUPPORTED_FEATURE with error-details/unsupported-targeting-dimension
validated against the generated model and error.field at the targeting
path; unsupported_targeting_dimension_details reads it back.
Buyers read TaskResult.mutation_outcome, ADCPTaskError.mutation_outcome
and AdcpErrorInfo.mutation_outcome, normalized by
normalize_mutation_outcome: absent is None, an unrecognized, empty or
non-string value is "unknown".
The 3.2 and 3.0 core/error.json schemas allow additional properties, so
the field needs no gating for older callers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: MIGRATION_v9_to_v10.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -35,6 +35,7 @@ the SDK-relevant inventory is [docs/adcp-3.3-sdk-inventory.md](docs/adcp-3.3-sdk
35
35
|`PrincipalService` accepts `3.3` async payloads | The default `accepted_async_adcp_versions` is `("3.2", "3.3")` (was `("3.2",)`), since the SDK emits 3.3-line payloads. A seller pinned to 3.2 passes `accepted_async_adcp_versions=("3.2",)`. |
36
36
| Schema defaults appear in dumps for new 3.3 fields |`GetProductsRequest.include_collections` (`false`) and `proposal_refinement.hold_extension` (`false`) carry schema defaults, so a dumped model declares them. Tests that compare whole dictionaries need the new keys; a 3.3 → 3.2 downgrade drops them with a recorded loss. |
37
37
| 3.3 → 3.2 downgrades apply semantic rules | A buyer below 3.3 of a seller answering in 3.3 no longer gets `VERSION_UNSUPPORTED` for these responses: `by_spot` rows without `aired_at` (`scheduled` / `preempted`) and delivery rows without `impressions` are omitted (losses `row_omitted_not_aired`, `row_omitted_impressions_not_measured`), never sent with `impressions: 0`; `media_buy.features.catalog_ingestion` and a `price_breakdown` carrying an `operator_fee` adjustment are dropped; `age_determination_bases` moves to `ext.adcp.age_determination_bases`, and a 3.2 response upgraded to 3.3 reads it back into an absent top-level field. Requests the 3.2 contract cannot express fail with `ADCPVersionConversionError` (`UNSUPPORTED_FEATURE` on the client) before sending: a `reporting_webhook` / `artifact_webhook` without `authentication` (keep sending it while you buy from 3.2 sellers; the SDK never invents one) and a daypart with `start_time` / `end_time` (use `start_hour` / `end_hour`; the SDK never rounds). The downgrade's final check now validates formats as the SDK validators do: earlier betas let such a request through invalid when it carried `start_time: "asap"`. See [docs/cross-version-conversion.md](docs/cross-version-conversion.md#33--32-semantic-rules). |
38
+
| Errors can state `mutation_outcome`| AdCP 3.3 `error.mutation_outcome` says whether a failed state-changing request changed seller state. A structured rejection the framework raises before a mutating handler runs (schema or model validation, version gates, pre-validation hooks) and `IDEMPOTENCY_CONFLICT` now carry `"mutation_outcome": "not_applied"` in `adcp_error` and `errors[]`; a test that compares whole error envelopes sees the new key. Set it on your own raises with `AdcpError(..., mutation_outcome=...)` only with the evidence the spec requires; leave it unset otherwise. Buyers read `TaskResult.mutation_outcome`, `ADCPTaskError.mutation_outcome` or `AdcpErrorInfo.mutation_outcome` (unrecognized values read as `"unknown"`). Reject an unsupported or removed targeting dimension with `UnsupportedTargetingDimensionError` (`UNSUPPORTED_FEATURE` with the `unsupported-targeting-dimension` details) instead of minting `UNSUPPORTED_TARGETING_DIMENSION` / `DEPRECATED_TARGETING_DIMENSION`. |
38
39
| Delivery rows may omit `impressions`| Every `by_*` delivery-metrics row makes `impressions` optional. Treat absence as "not measured", never as zero. |
39
40
| Pixel trackers for AdCP 3.3 playback events are dropped when downgraded to the v1 url-tracker shape |`downgrade_pixel_tracker` returns `PixelTrackerDowngrade(v1=None, ...)` with a `PIXEL_TRACKER_LOSSY_DOWNGRADE` advisory for `start`, `first_quartile`, `midpoint`, `third_quartile` and `complete`, and for their 3.2 interim form (`event: custom` with `custom_event_name` set to one of them). SDK 9 put the interim form in the `impression_tracker` slot, which counted every impression as a quartile view. Check `result.v1 is not None` before reading it. `downgrade_pixel_trackers` leaves dropped trackers out of `items`. See the AdCP [3.2 to 3.3 migration guide](https://docs.adcontextprotocol.org/docs/reference/migration/3-2-to-3-3#hosted-video-and-audio-playback-trackers). |
40
41
| Pixel trackers for AdCP 3.3 playback events reach 3.2 peers in the interim `custom` form | The 3.3 → 3.2 conversion (a buyer's request to a 3.2 seller, a seller's response to a 3.2 buyer) rewrites a `pixel_tracker` whose `event` is `start`, `first_quartile`, `midpoint`, `third_quartile` or `complete` to `event: "custom"` with `custom_event_name` set to that value, and records a `playback_event_as_custom` loss on the `event` pointer. Earlier SDK 10 betas refused it with `ADCPVersionConversionError` (`UNSUPPORTED_FEATURE` on the client, `VERSION_UNSUPPORTED` on the server). A 3.2 seller that does not recognize the custom name no-ops it, or rejects it (`tracker_contract_mismatch`) under a `complete: true` tracker execution contract that does not list it. Upgrades leave the `custom` form as sent: a tracker execution selector matches it, not the 3.3 event. Nothing to change unless you match on conversion loss reasons. |
Copy file name to clipboardExpand all lines: docs/adcp-3.3-sdk-inventory.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,7 +55,7 @@ it adopted the bundle, and what is left to the codegen/wire-fidelity (2) and
55
55
|`hold_policy`, `extend_hold`, `holds`, `hold_rank`, `hold_placed_at`, `proposal_initiator`|`core/hold-policy.json`; `Product`, `CanonicalProduct`, `Proposal` (`refine_proposals`), `ProposalRefinement.Action.extend_hold`; capabilities `proposal_refinement.hold_extension`, `hold_semantics`| Experimental holds. `hold_extension` has a schema default `false`, so the generated capability model emits it. | generated |
56
56
|`include_collections` / `collections[]`|`get_products`, `list_products`, `request_proposals`, `refine_proposals` requests and responses; `GetBrandIdentityResponse.collections`; `Collection` gains `images`, `sample_content`, `url`, `publisher_domain`; `CollectionDistribution.followers`| Experimental collection cards. `include_collections` defaults to `false` and is materialized on dump; the 3.3 → 3.2 downgrade drops it with a recorded loss. | generated |
|`mutation_outcome`|`core/error.json` (`not_applied`, `unknown`, `applied`) | Every error can state whether the mutation applied. | generated; populate from the dispatcher on mutation errors: (3), #1642|
58
+
|`mutation_outcome`|`core/error.json` (`not_applied`, `unknown`, `applied`) | Every error can state whether the mutation applied. | generated; `AdcpError(mutation_outcome=...)` projected on MCP, A2A and `errors[]`; the framework sets `not_applied`on pre-dispatch rejections of mutating tools and on `IDEMPOTENCY_CONFLICT`; buyer `TaskResult.mutation_outcome` / `AdcpErrorInfo.mutation_outcome`: done (3), #1642|
59
59
|`operator_fee`| enum `adjustment-kind`| New price adjustment kind; send only to 3.3 peers. | generated; the 3.2 downgrade omits a price breakdown carrying it: done (3), #1667|
@@ -116,7 +116,7 @@ it adopted the bundle, and what is left to the codegen/wire-fidelity (2) and
116
116
|`request_signing.profiles`, `operation_sources`; `webhook_signing.profiles`| capabilities `RequestSigning.profiles` (`adcp/request-signing/v1`, `web-bot-auth`), `operation_sources` (`mcp_tools_call`, `a2a_invocation_skill`) |`required_for` enforced over A2A with `operation_sources` as the advertisement. | A2A hardening enforcement: done (3), #1659 (33 vectors pass); advertising `operation_sources`: (3), #1637|
117
117
|`ext.adcp` reserved |`extensions/adcp.json``AdcpOwnedExtensions`| Reserved namespace for the working group. | generated |
118
118
|`media_buy.features.catalog_ingestion`|`core/catalog-ingestion-capability.json` (`IngestionMode`, `ItemStatusReporting`); requires `catalog_management: true`| An object: fails 3.2 capability schemas (booleans only); the 3.3 → 3.2 downgrade must coarsen or drop it. `media_buy.features` is no longer boolean-only. | generated; downgrade drops it (`field_unavailable`): done (3), #1667|
119
-
|`error-details/unsupported-targeting-dimension.json`| under `UNSUPPORTED_FEATURE`; adapters minting `UNSUPPORTED_TARGETING_DIMENSION` / `DEPRECATED_TARGETING_DIMENSION` should migrate || generated; decisioning error minting: (3), #1642|
119
+
|`error-details/unsupported-targeting-dimension.json`| under `UNSUPPORTED_FEATURE`; adapters minting `UNSUPPORTED_TARGETING_DIMENSION` / `DEPRECATED_TARGETING_DIMENSION` should migrate || generated; `UnsupportedTargetingDimensionError` and `unsupported_targeting_dimension_details`: done (3), #1642|
120
120
|`error-details/agent-permission-denied.json``Reason`| new nested enum || generated |
121
121
|`brand.json` fetch cap 2 MiB | all fetch paths; `MAX_JWKS_BYTES`, `MAX_CAPABILITIES_BYTES` unchanged | 3.2 verifiers may reject files between 256 KiB and 2 MiB. | done (3), #1654: 2 MiB, depth 32, 10 s total deadline, bounded concurrent fetches; follow-ups on #1635|
@@ -154,8 +154,8 @@ Every new declaration is optional; 3.2 behaviour stands when undeclared.
154
154
|~~`REFERENCE_DEFINITION_CHANGED`~~ (withdrawn in 3.3.0-beta.2, section 12) |`error-details/reference-definition-changed.json`|`accept_proposal` when a pinned definition drifted; send only to 3.3 peers | generated; until then the 3.2 downgrade keeps it, since 3.2 `error.code` is open (#1667); minting and 3.2 downgrade mapping: (3), #1641|
0 commit comments