feat(schemas): regen for AdCP 3.0 GA — custom pricing + experimental_features#210
Merged
feat(schemas): regen for AdCP 3.0 GA — custom pricing + experimental_features#210
Conversation
…features Regenerates from upstream protocol bundle (PR adcontextprotocol/adcp#2422): - New `custom` variant in VendorPricing/VendorPricingOption unions (model: "custom", required description + metadata, optional currency). Consumers dispatching on `.root.model` must add a "custom" branch; recommended default is operator review, not auto-select. - `per_unit` pricing variant picked up from an earlier 3.0 rc. - New optional `experimental_features: list[str] | None` on GetAdcpCapabilitiesResponse for dot-namespaced feature ids (brand.rights_lifecycle, governance.campaign, trusted_match.core). - x-status: experimental metadata on Brand Rights, Campaign Governance, and TMP schemas (type output unchanged — metadata only). - Idempotency now carries a required `supported: bool`. Test fixtures updated to pass supported=True. Closes #204 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…trict gate The AdCP 3.0 GA schema promoted `adcp.idempotency.supported` to a required bool with explicit semantics: `supported=false` means the seller does NOT dedupe and a naive retry WILL double-process. The strict-idempotency gate was checking only `replay_ttl_seconds`, so a seller declaring `supported=false, replay_ttl_seconds=86400` (contradictory but type-valid) passed the gate silently. _ensure_idempotency_capability now raises IdempotencyUnsupportedError when: - adcp.idempotency is missing - supported=false - supported=true but replay_ttl_seconds is None The exception message carries a reason string so the specific mode is visible in logs. Added three targeted tests covering each failure mode plus the happy path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
Expert review follow-upRan the PR past three expert reviewers. Summary of findings and actions: Addressed in 9ff08c9 (follow-up commit)[must-fix from code-reviewer]
Updated Not addressed (acknowledged, scoped out of this PR)
Confirmed correct / no action needed
|
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
…se, webhook re-exports Three P0 blockers surfaced by round-6 storyboard validation on the AdCP 3.0 GA rebase. 1. IdempotencyStore.capability() now emits the required supported field --------------------------------------------------------------------- Upstream PR #210 (adcp 3.0 GA regen) made ``adcp.idempotency.supported`` REQUIRED on the capabilities response. ``IdempotencyStore.capability()`` at src/adcp/server/idempotency/store.py:78 was still returning ``{"replay_ttl_seconds": N}`` only, so every agent using the documented ``capabilities_response(idempotency=store.capability())`` pattern silently emitted a schema-invalid capabilities block. Now returns ``{"supported": True, "replay_ttl_seconds": N}``. Four existing tests updated to match. 2. adcp-keygen --purpose for webhook-signing keys ------------------------------------------------- The webhook verifier enforces ``adcp_use == "webhook-signing"`` on the JWK, but ``adcp-keygen`` hardcoded ``adcp_use: "request-signing"`` with no override. A user following keygen → publish JWKS → emit webhook got ``webhook_signature_key_purpose_invalid`` on first delivery — the exact failure mode round-6 DX exploration flagged as a blocker for discoverability of the new 9421 path. Added ``--purpose {request-signing,webhook-signing}`` CLI flag (default request-signing for back-compat) and threaded the value through generate_ed25519 / generate_es256. 3. Top-level adcp re-exports the new 9421 webhook surface --------------------------------------------------------- ``adcp/__init__.py`` re-exported the deprecated legacy ``get_adcp_signed_headers_for_webhook`` but NOT the new 9421 entry points. A coding agent scanning ``dir(adcp)`` for webhook primitives saw only legacy. Added: ``sign_webhook``, ``WebhookReceiver``, ``WebhookReceiverConfig``, ``WebhookVerifyOptions``, ``WebhookDedupStore``, ``MemoryBackend``, ``LegacyHmacFallback``, ``generate_webhook_idempotency_key``. Also promoted the MemoryBackend / WebhookDedupStore imports in adcp.webhooks to explicit ``as`` re-exports so mypy treats them as public. BREAKING CHANGE: IdempotencyStore.capability() return shape changes from ``{"replay_ttl_seconds": N}`` to ``{"supported": True, "replay_ttl_seconds": N}``. Callers that byte-compared against the old shape will need to update their expected value. Required to emit schema-valid AdCP 3.0 capabilities responses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
…se, webhook re-exports Three P0 blockers surfaced by round-6 storyboard validation on the AdCP 3.0 GA rebase. 1. IdempotencyStore.capability() now emits the required supported field --------------------------------------------------------------------- Upstream PR #210 (adcp 3.0 GA regen) made ``adcp.idempotency.supported`` REQUIRED on the capabilities response. ``IdempotencyStore.capability()`` at src/adcp/server/idempotency/store.py:78 was still returning ``{"replay_ttl_seconds": N}`` only, so every agent using the documented ``capabilities_response(idempotency=store.capability())`` pattern silently emitted a schema-invalid capabilities block. Now returns ``{"supported": True, "replay_ttl_seconds": N}``. Four existing tests updated to match. 2. adcp-keygen --purpose for webhook-signing keys ------------------------------------------------- The webhook verifier enforces ``adcp_use == "webhook-signing"`` on the JWK, but ``adcp-keygen`` hardcoded ``adcp_use: "request-signing"`` with no override. A user following keygen → publish JWKS → emit webhook got ``webhook_signature_key_purpose_invalid`` on first delivery — the exact failure mode round-6 DX exploration flagged as a blocker for discoverability of the new 9421 path. Added ``--purpose {request-signing,webhook-signing}`` CLI flag (default request-signing for back-compat) and threaded the value through generate_ed25519 / generate_es256. 3. Top-level adcp re-exports the new 9421 webhook surface --------------------------------------------------------- ``adcp/__init__.py`` re-exported the deprecated legacy ``get_adcp_signed_headers_for_webhook`` but NOT the new 9421 entry points. A coding agent scanning ``dir(adcp)`` for webhook primitives saw only legacy. Added: ``sign_webhook``, ``WebhookReceiver``, ``WebhookReceiverConfig``, ``WebhookVerifyOptions``, ``WebhookDedupStore``, ``MemoryBackend``, ``LegacyHmacFallback``, ``generate_webhook_idempotency_key``. Also promoted the MemoryBackend / WebhookDedupStore imports in adcp.webhooks to explicit ``as`` re-exports so mypy treats them as public. BREAKING CHANGE: IdempotencyStore.capability() return shape changes from ``{"replay_ttl_seconds": N}`` to ``{"supported": True, "replay_ttl_seconds": N}``. Callers that byte-compared against the old shape will need to update their expected value. Required to emit schema-valid AdCP 3.0 capabilities responses. 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 #204. Regenerates Pydantic models from the upstream AdCP 3.0 GA protocol bundle (adcontextprotocol/adcp#2422).
Summary
customvariant in vendor pricing discriminated union (VendorPricing,VendorPricingOption). Requiresdescriptionandmetadata(non-empty), optionalcurrency. Consumers dispatching on.root.modelshould add a"custom"branch — recommended default is operator review, not auto-select.per_unitpricing variant (added in an earlier 3.0 rc) is now in the generated models alongsidecustom.experimental_features: list[str] | NoneonGetAdcpCapabilitiesResponse. Dot-namespaced ids (brand.rights_lifecycle,governance.campaign,trusted_match.core) declared by sellers that implement experimental surfaces.x-status: experimentalannotations on Brand Rights, Campaign Governance, and TMP — metadata only;datamodel-code-generatorignores unknownx-*extensions, so type output is unchanged.Idempotencynow carries a requiredsupported: bool(pulled in via the latest bundle). Four test fixtures updated to passsupported=True.Wire format
No breaks. All changes are additive.
Test plan
make regenerate-schemassucceedsruff check src/passesmypy src/adcp/passes (671 source files)pytest tests/passes (1696 passed, 14 skipped)VendorPricing5/VendorPricingOption11carrymodel: Literal["custom"]with requireddescription+metadataexperimental_featuresfield onGetAdcpCapabilitiesResponsein bothprotocol/andbundled/protocol/🤖 Generated with Claude Code