Repository navigation
feat(protocol)!: adopt the signed AdCP 3.3.0-beta.3 bundle - #1754
Conversation
Bring the 3.3.0-beta.2 adoption, the adcp#8547 envelope codegen fix and the PyPI publish pin into the reporting-subscription stack before it adopts 3.3.0-beta.3. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pin src/adcp/ADCP_VERSION to 3.3.0-beta.3 from the audited, cosign-verified release: schemas/releases/3.3.0-beta.3.json, source commit d5f8de383df1 from the Fulcio certificate, bundle sha256 ba61919ca0c9. Resync the schema cache, packaged compliance inputs and request-signing vectors with --require-signed-release, then regenerate the types and the public API snapshot. The wheel ships the 3.3.0-beta.3 schemas and compliance tree; the beta.2 trees stay in the repository as history and leave distributions. Serve the reporting-subscription surface from generated types. The _reporting_subscription_preview module and the beta.1 and beta.2 wire projections are removed, and serves_subscriptions, from SUBSCRIPTIONS_MIN_ADCP_VERSION 3.3.0-beta.3, is the one predicate for capabilities, coverage including status inactive with deactivated_at, and the principal readback. The extensible reporting-dimension vocabulary is reopened on the five generated fields that carry it. Fix the spec repository's Generate Python SDK import failure on beta.3. The preview module raised AttributeError on the renamed PriorVersion, adcp.types.__getattr__ read that as a missing name, and the next access imported _forward_compat again, so the targeting_overlay patch ran twice and reported a misleading lost annotation. The eager import now surfaces the first failure as ImportError, and the targeting and bundled-clone patches are idempotent. Generate the adcp#8547 response layout faithfully. The response-arm emitter folds core/task-submitted.json and its siblings into each arm, so submitted arms keep status submitted and a required task_id. get_task_status takes the envelope's declarations for its required task_id and status instead of Any. References to core/task-id.json and core/task-message.json generate as constrained str, so callers keep passing plain strings. Add the comply_test_controller reporting_subscription_probe scenario as a TestControllerStore hook with parameter validation. BREAKING CHANGE: the SDK no longer advertises or accepts AdCP 3.3-beta.2. get_supported_adcp_versions is 3.0, 3.1, 3.2, 3.3-beta.3 and 3.2-rc.7; an adcp_version pin of 3.3-beta.2 or 3.3.0-beta.2 raises ConfigurationError, and a peer that advertises only 3.3-beta.2 for the 3.3 line negotiates down to 3.2. Re-pin to 3.3-beta.3 or omit the pin. adcp.reporting.subscriptions drops project_offering, project_reporting_delivery, project_subscription_state, project_subscription_coverage and serves_settled_subscription_fields, and ReportingSubscriptionEngine.subscription_coverage no longer takes adcp_version. The generated PriorVersion, AccountsWithException and AccountsUnauthorizedItem are now ReportingSubscriptionPriorVersion, ReportingSubscriptionAccountExceptions and ReportingSubscriptionUnauthorizedAccount. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…3.0-beta.3 AdCP 3.3.0-beta.3 composes core/protocol-envelope.json on every task response, so list_products and decline_proposals now require status. Through 3.2 their compact roots forbid it. uses_task_status_envelope takes the release the response is shaped for, and the server, the validators and the version converter pass it. A 3.3 response is stamped status completed, and the 3.3 to 3.2 conversion still drops it for 3.2 callers. This fixes the multi_platform_seller storyboard's list_products status failure. Re-pin the tests the composed envelope moved. The task-message lattice residues are empty, and the error-code count is 124 with CREATIVE_LIVE_UPDATE_UNSUPPORTED. replayed defaults to false and accepts an explicit false. The governance test handler names its decision in verdict and outcome_state. The property-list test validates the response as served to a 3.2.1 caller. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…orpus valid The comply_test_controller task_id narrows core/task-id.json through a one-arm allOf with maxLength 128. Inlining the scalar alias inside that allOf let the generator's merge drop minLength 1, so the model accepted an empty task_id the schema refuses. The alias's keywords now fold into the narrowing node, so the field is str with min_length 1 and max_length 128. The round-trip corpus's check_governance response carries status completed, which 3.3.0-beta.3 requires on every governance response. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every task response and async status payload carries its task status from 3.3.0-beta.3 (adcp#8547), and a webhook's result is that payload. A result without status takes the webhook's status. A model result's defaulted envelope status is replaced with it, so a failed task's result never reads completed. An explicit status is kept. The TypeScript SDK made the same change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…equires core/reporting-subscription-state.json requires prior_versions_count, accounts_with_exceptions_count and accounts_unauthorized_count whenever their lists are present, so a readback without them fails 3.3.0-beta.3 validation. The registry now lists at most the 16 newest prior versions inline, with the total. The readback lists at most 1,000 accounts inline per list, in account_id byte order, with the total. Paging the rest follows in the engine slice. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…results The 3.3.0-beta.3 webhook result carries its task status, as create_mcp_webhook_payload now sets. The Postgres delivery supervisor test, which skips without a database, asserted the bare result. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — PR #1754 adopts the signed AdCP 3.3.0-beta.3 bundle with a correctly-signaled breaking change.
The reviewer surfaced no critical, high, or medium findings. Walking the decision table top-down:
- Row 1 (critical/high): no such findings — not triggered.
- Row 2 (gated_paths):
gated_pathsis false — not triggered. - Row 3 (high-risk deletion):
high_riskis true but every high-risk reason is(added)— newschemas/cache/3.3.0-beta.3/**files (scaffolding of the new schema bundle), no(deleted)entries — not triggered. - Row 4 (medium + data-loss/schema/infra): no medium findings — not triggered.
- Row 5 (high-risk modified + medium): all high-risk matches are additions and there are no medium findings — not triggered.
- Row 6 (sticky escalate): no prior decision — not triggered.
- Row 7 (no-auto-approve team): no team match — not triggered.
- Row 8 (≥3 medium): zero medium findings — not triggered.
- Row 9: approve.
The high_risk flag fires only on newly-added schema cache files, which is normal bundle-scaffolding and not escalation-worthy per the change-kind rules. The reviewer verified the load-bearing hand-written changes: version-aware uses_task_status_envelope, idempotent _forward_compat.py patchers, the eager-import ImportError re-raise, the breaking serves_subscriptions collapse, webhook task-status stamping, and required *_count fields — all covered by tests. The breaking change carries feat(protocol)!: + a BREAKING CHANGE footer plus MIGRATION_v9_to_v10.md and a refreshed public_api_snapshot.json, satisfying the semver-signal mandate.
No blocking findings. Ready to merge.
…se's schema (#1761) #1754 stamps status on list_products and decline_proposals from 3.3.0-beta.3 by comparing version strings. That leaves three defects for a 3.2.x peer, and a fourth for the next 3.2.x release: - A typed ListProductsResponse dumps its defaulted `status: completed`. A server serving 3.2.x sent it to a closed root that forbids it, so strict response validation failed on a handler that returned the typed model. - Converting a synchronous decline_proposals response from 3.3 to 3.2 raised ADCPVersionConversionError: the 3.2 root permits `status` only as `submitted`, and the converter had no rule for the status that 3.3 requires. - Converting up from 3.2 to 3.3 did not supply the `status` 3.3 requires, so the converted list_products / decline_proposals payload failed 3.3 validation. - A 3.2.x release that composes the envelope (the backport) would still be treated as one that does not. uses_task_status_envelope(tool, version) now asks the served release's own response schema whether it composes core/protocol-envelope.json, as the TypeScript SDK does (adcontextprotocol/adcp-client#3493). The version comparison from #1754 stays as the answer for a release that ships no schema for the tool. The server drops only the typed default `completed` where the served release has none for the tool (a submitted arm keeps its status), and convert_payload supplies `completed` going up and drops it, recorded as a loss, going down. Nothing is stripped for any other tool or release. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Slice P1a of the reporting-subscriptions DX plan (
.context/dx-reporting-subscriptions.md§6.2, P1). It adopts and serves the signed AdCP 3.3.0-beta.3 bundle. Stacked on #1751. The engine's conformance to the beta.3 rules and the re-pinned coverage vectors are in the next PR, which is stacked on this one.Breaking change:
3.3-beta.2is droppedThe SDK no longer advertises or accepts AdCP
3.3-beta.2. This matches how3.3-beta.0,3.3-beta.1and the 3.2 release candidates were dropped once superseded.get_supported_adcp_versions()→('3.0', '3.1', '3.2', '3.3-beta.3', '3.2-rc.7').adcp_version="3.3-beta.2"or"3.3.0-beta.2"now raisesConfigurationError.3.3-beta.2for the 3.3 line negotiates down to3.2.3.3-beta.2(the10.0.0-beta.3default) should re-pin to3.3-beta.3or remove the pin.The commit is
feat(protocol)!:with aBREAKING CHANGE:footer. The same footer also lists the reporting-subscription removals below.Release pin
schemas/releases/3.3.0-beta.3.jsonrecords the four artifacts' sizes and sha256. They were cosign-verified (Verified OK) againstrelease.yml@refs/heads/main.ba61919ca0c9d67874604fbbca8945c133eaceed65da1ac962f78d94d7879f00.d5f8de383df13525e11721ee3d493ee76352210fcomes from the Fulcio certificate (OIDs1.3.6.1.4.1.57264.1.3and.13). Thev3.3.0-beta.3tag (a6106149) is its Version Packages child, the same layout as beta.2.scripts/sync_schemas.py --require-signed-releaseand ran the full regeneration chain:fix_schema_refs,bundle_schemas, the versioned stubs and bases,generate_types,consolidate_exportsandgenerate_ergonomic_coercion. I then refreshed the public API snapshot.make validate-generatedis clean.Generate Python SDK failure in the spec repo
With beta.3 schemas, the spec repo's Generate Python SDK check failed on import with
PackageRequest.targeting_overlay lost its TargetingOverlayInput | None annotation. That message came from a second failure; the first one was hidden._reporting_subscription_previewraisedAttributeError, because beta.3 renamedPriorVersion.adcp.types.__getattr__reported thatAttributeErroras a missing name._forward_compatagain, over models the first attempt had already patched.The fix has three parts:
adcp.types.__getattr__re-raises anAttributeErrorfrom the eager import asImportError, so the first failure surfaces.targeting_overlayand bundled-clone patches are idempotent.Two subprocess regression tests cover this.
Serving the reporting-subscription fields from generated types
adcp.types._reporting_subscription_previewis deleted. Every field it hand-modelled is generated.project_offering,project_reporting_delivery,project_subscription_state,project_subscription_coverage,serves_settled_subscription_fields, and the principal readback gate.adcp.reporting.subscriptions.serves_subscriptions(adcp_version)is true fromSUBSCRIPTIONS_MIN_ADCP_VERSION = "3.3.0-beta.3". It replacesserves_settled_subscription_fields(beta.2),serves_inactive_coverage(beta.3) and the oldservice_subscriptions.serves_subscriptions(beta.1). It gates the capability block,get_reporting_statuscoverage (includingstatus: "inactive"withdeactivated_at) and the principal surface.ReportingSubscriptionEngine.subscription_coverage()no longer takesadcp_version.reporting-dimensionvocabularyx-extensible, but the generator closes it into an enum._forward_compatreopens the five fields that carry it (the selector, the offering, the capability block and their bundled copies) to the vocabulary'sx-pattern. This is a codegen limit, not a hand-modelled field. Without it, an offering advertisingstandard_dimensions: ["device"]would fail to parse.Codegen for the adcp#8547 layout in beta.3
#1741 handled the composed envelope. Beta.3 also composes
core/task-submitted.json(andtask-working/task-input-required) into branches and centralizestask_idandmessage. Three regressions showed up only on the real tree:statusand the requiredtask_id.CreateMediaBuySubmittedResponse(status="working")validated. The response-arm emitter now folds the composed task-state schema into each arm.GetTaskStatusResponse.task_idand.statusbecameAny. Only the envelope declares them now. A pre-generation pass copies the envelope's declaration onto a root that requires it.task_idandmessagefields would have becomeTaskId/TaskMessagescalar classes. Callers would have had to wrap plain strings, and the adoptermypy --strictsuite failed. References to those two scalar aliases now generate asstrwith the same constraints (min_length=1,max_length=2000). The classes are still generated.The composed envelope on compact responses (CI follow-up)
The first CI run on this branch caught where #8547 changes behavior on the wire. These are fixed in follow-up commits:
list_productsanddecline_proposalsnow needstatus. Through 3.2 their compact roots forbid it, and from 3.3.0-beta.3 the composed envelope requires it.uses_task_status_envelope(tool, version)is now version-aware, and the server, the validators and the converter pass the release. A 3.3 response is stampedstatus: completed, and the 3.3 → 3.2 conversion still drops it for 3.2 callers. This was themulti_platform_sellerstoryboard failure.CREATIVE_LIVE_UPDATE_UNSUPPORTED).replayeddefaults tofalseand accepts an explicitfalse.verdict/outcome_state, sincestatusis the task status.check_governanceresponse carriesstatus.create_mcp_webhook_payloadgives aresultwithoutstatusthe webhook's status, and replaces a model's defaulted envelope status with it, so a failed task's result never readscompleted. The TypeScript SDK made the same change.core/reporting-subscription-state.jsonrequiresprior_versions_count,accounts_with_exceptions_countandaccounts_unauthorized_countwhenever their lists are present. The readback lists at most 16 prior versions and 1,000 accounts per list inline, each with its total. Paging the rest is in feat(reporting): apply the AdCP 3.3.0-beta.3 seller rules for subscriptions #1756.status: submitted/task_idon the proposal tools' submitted branches, and generateddestination_overridesitems as empty objects. Neither happens here: every submitted arm keepsLiteral['submitted']and a requiredtask_id, andDestinationOverridehas its four fields.task_id. It narrowscore/task-id.jsonthrough a one-armallOf(maxLength: 128). The generator's merge droppedminLength: 1, so the alias now folds into the narrowing node.Other beta.3 surface
comply_test_controlleraddsreporting_subscription_probe, with operationsrevoke_grant,regrant,reduce_grant,set_account_timezoneandadvance_time.TestControllerStoregains the hook (not implemented by default) and the dispatcher validates its parameters.SCENARIOSmatches the packaged schema again.flight_history,cancellation_effective_at,regranted_at,destination_overrides, the paged readback counts,reporting_subscription_page, offeringavailable_from/withdrawn_at, and obligationstub.PriorVersion→ReportingSubscriptionPriorVersion,AccountsWithException→ReportingSubscriptionAccountExceptions,AccountsUnauthorizedItem→ReportingSubscriptionUnauthorizedAccount. Numbered names also renumber.adcp.types._generated.AllowedTask(internal) now resolves to the controller'sallowed_tasksitem. Both are task-name tokens.adcp.types.domains.core.AllowedTaskis unchanged.Packaging
_schemas/3.3.0-beta.3and_compliance/3.3.0-beta.3.Tests and docs
test_adcp_3_3_0_beta_3_adoption.py. It checks the signed identity values: 4684 manifest files, 247 provenance files and 1768 schemas. It also checks that beta.0, beta.1 and beta.2 are rejected.MIGRATION_v9_to_v10.mdhas the pin, the breaking note and rows for the new surface.docs/adcp-3.3-sdk-inventory.mdhas a new §13. Pin references are updated inRELEASING.md,docs/cross-version-conversion.md,docs/agent-identity-3-3-migration.mdanddocs/reliable-reporting-service.md.Proof
Local verification
ruff check src/,mypy src/adcp/, themypy --strictadopter checks, the pyright adopter checks, the type-ignore contract, the versioned stub and base checks, andmake validate-generated.targeting_overlaycompatibility, every reporting-subscription and principal test, and lazy types. Codegen, version and historical tests add another 1,236.ci.yml).🤖 Generated with Claude Code