Skip to content

feat(protocol)!: adopt the signed AdCP 3.3.0-beta.3 bundle - #1760

Closed
bokelley wants to merge 1 commit into
nextfrom
claude/issue-1560-adcp-3-3-0-beta-3
Closed

bokelley wants to merge 1 commit into
nextfrom
claude/issue-1560-adcp-3-3-0-beta-3

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Adopts the signed AdCP 3.3.0-beta.3 bundle on next (the pre-release line; RELEASING.md routes draft-spec work there, and the protocol repo's "Generate Python SDK" job already checks out next).

  • Bundle: schemas/releases/3.3.0-beta.3.json pins sha256 ba61919ca0c9d67874604fbbca8945c133eaceed65da1ac962f78d94d7879f00. I verified the .sha256 sidecar and ran cosign verify-blob against identity release.yml@refs/heads/main. Source commit d5f8de38 is read from the Fulcio certificate. The tag v3.3.0-beta.3 points at the Version Packages commit a6106149, and the signed workflow ran on the docs commit after it; the same split as noted in the test comment.
  • Package the AdCP 3.3 schema bundle (3.3.0-beta.0) #1560: a prerelease keeps its full-version bundle key. pyproject.toml package data and MANIFEST.in now name 3.3.0-beta.3 (_schemas, _compliance, schemas/cache) and prune beta.2 from distributions.
  • Generated response types can't represent the protocol envelope #1678 / post_generate_fixes.py matches exact base-class lists and breaks when envelope composition changes #1680: beta.3 is the first bundle with the envelope composed on every response (adcp#8547). Generated responses derive from ProtocolEnvelope (status, task_id, timestamp, adcp_version, ...). Two real generator problems surfaced against the real layout, both fixed generically, not per-schema (details below). The task-message lattice residue pins are now empty.
  • Version gating (matches adcp-client#3493): uses_task_status_envelope(tool, version) reads the served release's own response schema for whether it composes protocol-envelope. 3.3 requires and emits status on list_products / decline_proposals; 3.2.x neither requires nor emits it (the typed model's completed default is not sent); convert_payload supplies it going up and drops it, recorded as a loss, going down. No other tool or release is touched.

Generator fixes

  1. restore_response_variant_aliases re-emits eight response modules from raw schema JSON and recognised a submitted arm only by a local status: const submitted. Under #8547 that const and the required task_id live in core/task-submitted.json, so CreateMediaBuyResponse3 and seven siblings silently lost TaskSubmitted, the const and the required id. The emitter now recognises an arm that composes task-submitted. (Raw datamodel-code-generator output was already correct, so this one is in the post-generation pass.)
  2. A scalar that tightens a shared definition (comply_test_controller task_id, maxLength: 128 beside core/task-id.json) lost its bound. A small prep pass merges a scalar's own bound into the plain scalar its lone allOf $ref names; two schemas are affected today.

Behavior notes for review

  • BREAKING (pre-release): beta.2 is no longer an accepted pin; see the commit's BREAKING CHANGE and MIGRATION_v9_to_v10.md.
  • TaskId / TaskMessage scalars on responses: adopters construct TaskId("task_1") (domain path adcp.types.domains.core.task_id), same convention as PropertyTag.
  • Governance fixtures had passed status="approved" / "accepted" and only validated because the model allowed extras; status is now the typed task status, so those tests use verdict / outcome_state.
  • New reporting_subscription_probe controller scenario (store method + param validation).
  • _reporting_subscription_preview.py now only widens the extensible reporting-dimension vocabulary; every field it added is generated.
  • Hook note: the commit skipped only the local black pre-commit hook (SKIP=black). It pins 24.10 and reformats unrelated lines that the repo's committed style (newer black) disagrees with; CI runs ruff, not black. ruff, mypy, bandit and the other hooks ran.

Test plan

  • pytest tests : 20,800 passed, 0 failed, 2,829 skipped, 1 xfailed
  • make typecheck-all (mypy src, mypy --strict adopter fixtures, pyright, type-ignore contract)
  • make lint, make validate-generated (regeneration equality)
  • New tests/test_response_status_gating.py (26 tests), generator-safety and submitted-arm contract tests
  • Reproduced the protocol repo's "Generate Python SDK" job locally against the published bundle (result in the PR comments)

Fixes #1560. Fixes #1678. Fixes #1680.

🤖 Generated with Claude Code

Pin src/adcp/ADCP_VERSION to 3.3.0-beta.3 from the cosign-verified release
(schemas/releases/3.3.0-beta.3.json; bundle sha256 ba61919c..., source commit
d5f8de38 from the Fulcio certificate under release.yml@refs/heads/main).
Resync the schema cache, compliance inputs and request-signing vectors,
regenerate the types, and package the full-version bundle key
(`_schemas/3.3.0-beta.3`, `_compliance/3.3.0-beta.3`, `schemas/cache/3.3.0-beta.3`
in pyproject.toml and MANIFEST.in). Fixes #1560.

Beta.3 is the first bundle that composes core/protocol-envelope.json into every
task response (adcontextprotocol/adcp#8547). Generated responses derive from
ProtocolEnvelope and carry status, task_id, timestamp, adcp_version and the other
envelope fields. Fixes #1678 and #1680:

- The restore_response_variant_aliases emitter identified a submitted arm by a
  local `status: const submitted`. #8547 moves that const and the required task_id
  into core/task-submitted.json, so eight response modules (create/update_media_buy,
  build_creative, sync_creatives, sync_audiences, sync_catalogs, preview_creative,
  get_creative_features) lost TaskSubmitted, the const and the required id. The
  emitter now recognises an arm that composes task-submitted and declares both.
- A scalar that tightens a shared definition (the controller's task_id maxLength
  128 beside core/task-id.json) was losing its bound in the generator. A prep pass
  merges the bound into the scalar before generation.
- The task-message lattice pins for the residue of responses without an envelope
  are now empty, and stay as assertions.

Version gating (matches adcontextprotocol/adcp-client#3493): `status` is required
on 3.3 responses, including list_products and decline_proposals, which have none
under 3.2.x. uses_task_status_envelope(tool, version) reads the served release's
own response schema for whether it composes the envelope, so the answer follows the
release, not a version string or a hard-coded tool list. A 3.3 server fills
`status: completed` and validates with it; a 3.2.x server neither requires nor emits
it (the typed model's default is not sent); convert_payload supplies it going up
and drops it, recorded as a loss, going down. Nothing is stripped for any other
tool or release.

Other adoption work: the reporting-subscription preview patch module now only
widens the extensible reporting-dimension vocabulary, since every field it added is
generated; the comply_test_controller `reporting_subscription_probe` scenario is
wired (store method and parameter validation); the error-code count, the replayed
default, the governance response fixtures and the adoption tests follow the bundle.

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', '3.2-rc.7'); an
adcp_version pin of "3.3-beta.2" / "3.3.0-beta.2" raises ConfigurationError, and a
peer that advertises only 3.3-beta.2 among the 3.3 line is negotiated like any other
unserved prerelease (down to 3.2). Re-pin to 3.3-beta.3 or omit the pin. A response's
`task_id` is the TaskId scalar and `message` is TaskMessage; `replayed` may be false
and defaults to False; governance responses no longer accept `status` as a decision
(use `verdict` / `outcome_state`).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@aao-secretariat

Copy link
Copy Markdown

Ladon cannot review this PR until merge conflicts are resolved.

Copy link
Copy Markdown
Contributor Author

Reproduced the protocol repo's Generate Python SDK job (validate-schema-bundle.yml) locally against a fresh clone of this branch (80c5ed17):

  • the published 3.3.0-beta.3.tgz served from a local HTTP host with its .sha256 (checksum verified);
  • the job's bundle-key step: resolve_bundle_key is 3.3.0-beta.3 on both sides, so no pyproject.toml / MANIFEST.in edit is needed (this was the #1560 failure);
  • the candidate's release pin removed and PREVIEW_VERSIONS = (), as the job does;
  • make regenerate-schemas (ADCP_SKIP_SIGNATURE=1), then make validate-generated (generated types up to date, so regeneration is deterministic from the bundle), then pytest tests/test_code_generation.py tests/test_schemas_version_pin.py (133 passed).

One difference from the real job: it uses the PR bundle built from a protocol PR head, not the published tarball, and checks out next at that time. The published bundle is the same tree the next Version Packages PR will carry.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor Author

Closing as superseded. While this branch was open, #1754 / #1755 / #1756 landed the same adoption on next (the signed 3.3.0-beta.3 bundle, the controller task_id bound, the compact-response status, the error-code and replayed re-pins), and this PR then conflicted with it. After diffing the two I found what this PR still added and moved it to #1761:

  • the served release's own schema deciding status on list_products / decline_proposals (not a version comparison);
  • the typed status: completed default no longer reaching a closed 3.2.x root;
  • status handled in both directions of the 3.2 ⇄ 3.3 conversion (3.3 → 3.2 decline_proposals raises today).

Not carried over, on purpose: TaskSubmitted ancestry and TaskId / TaskMessage typed fields on the submitted arms. next keeps those fields typed str with the schema constraints (the status const and required task_id still hold), so adopters never wrap a value, and I don't want to diverge from that.

The local reproduction of the protocol repo's "Generate Python SDK" job in the comment above was run on this branch; I will repeat it against next once #1761 merges.

🤖 Generated with Claude Code

@bokelley bokelley closed this Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant