spec: sync_creatives status, idempotency.supported, NOT_FOUND codes#2434
Merged
spec: sync_creatives status, idempotency.supported, NOT_FOUND codes#2434
Conversation
… codes Three pre-4.0 DX fixes surfaced during Python SDK v4.0.0-rc validation. - sync_creatives response (#2428): add optional per-item `status: CreativeStatus` so buyers learn approval/review state without a follow-up list_creatives; add third `SyncCreativesSubmitted` top-level shape mirroring the create_media_buy three-shape pattern for when the whole sync is queued asynchronously. - get_adcp_capabilities (#2429): add `adcp.idempotency.supported: boolean` mirroring the `request_signing.supported` pattern; `replay_ttl_seconds` is conditionally required only when `supported: true`, letting sellers without replay dedup declare it explicitly. - error-code enum (#2430): add `CREATIVE_NOT_FOUND` and `SIGNAL_NOT_FOUND` to match the existing PRODUCT_NOT_FOUND / MEDIA_BUY_NOT_FOUND / PACKAGE_NOT_FOUND pattern. Docs updated across capabilities, sync_creatives, error-handling, and all examples referencing the capabilities idempotency block. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Schema Link Check ResultsCommit:
|
…rors Addresses findings from ad-tech-protocol-expert, dx-expert, adtech-product-expert, code-reviewer, and security-reviewer on PR #2434: Schema tightening: - sync_creatives: forbid `status` on items when `action` is `failed` or `deleted` via `allOf/if/then` conditional (protocol-expert #1, code-reviewer #2). - sync_creatives: success and error branches now also exclude `task_id` in their `not.anyOf` clauses for defense-in-depth discrimination (code-reviewer #3). - sync_creatives + create_media_buy: `message` on submitted envelope gets `maxLength: 2000` and explicit "untrusted seller input — escape for UI, sanitize for LLM" guidance to prevent XSS and prompt-injection via the async task channel (security #3). Schema wording (security): - sync_creatives item `status` reframed as advisory UI/polling hint, NOT a spend-authorization gate. Buyers MUST NOT gate spend on `approved` from sync response — reconcile via list_creatives or signed webhook before committing (security #2). - error-code: `CREATIVE_NOT_FOUND` and `SIGNAL_NOT_FOUND` now require uniform return on any unknown-or-unauthorized ID to prevent cross-tenant enumeration (security note #4). Doc strengthening: - `idempotency.supported: false` documented as trap: sending idempotency_key is a no-op, naive retry will double-process (dx-expert #3). - New "Verifying the declaration" paragraph: sellers declaring `supported: true` MUST pass a payload-mutation replay probe returning IDEMPOTENCY_CONFLICT, as a baseline compliance storyboard assertion (security #1). - sync_creatives quick-start examples (JS + Python) now show three-branch discrimination and per-item pending_review handling (dx-expert #2). - Python examples use `getattr` instead of misleading `hasattr` on Pydantic models (code-reviewer #1). - Response section explicitly documents the new "status omitted when action=failed/deleted" conditional (code-reviewer #2). Changeset: - Calls out RC-breaking nature of adcp.idempotency.supported requirement. - Generator note covers Zod via zod-to-json-schema alongside openapi-typescript, datamodel-code-generator, quicktype (code-reviewer #4). - Migration note: strict validation is now the safe default for sync_creatives responses (security note #5). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 20, 2026
…rue shape Training-agent's get_adcp_capabilities handler was emitting the old pre-rc.4 idempotency shape (just replay_ttl_seconds). Schema now requires `supported` at the top of the block; CI caught this via collection-lists storyboard and training-agent capability tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
…loses #2436) Swap the draft-07 if/then conditional for a two-branch discriminated union on `supported`: `IdempotencySupported` carries `replay_ttl_seconds` (required); `IdempotencyUnsupported` forbids it via `not: {required: [replay_ttl_seconds]}`. Wire format unchanged. Code generators that silently drop `if/then` (openapi-typescript, zod-to-json-schema, pre-0.25 datamodel-code-generator, quicktype) now emit two named types with the invariant at the type level. Safe to fold into #2434 because `supported` is brand-new in that PR — no SDKs have generated against the interim `if/then` shape yet. Deferring to 4.0 per the original #2436 proposal would have meant shipping a known-bad shape for one whole release cycle. Compliance storyboard narrative and validations updated to reference the new shape; `supported: false` sellers skip the replay storyboard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
…loses #2436) Swap the draft-07 if/then conditional for a two-branch discriminated union on `supported`: `IdempotencySupported` carries `replay_ttl_seconds` (required); `IdempotencyUnsupported` forbids it via `not: {required: [replay_ttl_seconds]}`. Wire format unchanged. Code generators that silently drop `if/then` (openapi-typescript, zod-to-json-schema, pre-0.25 datamodel-code-generator, quicktype) now emit two named types with the invariant at the type level. Safe to fold into #2434 because `supported` is brand-new in that PR — no SDKs have generated against the interim `if/then` shape yet. Deferring to 4.0 per the original #2436 proposal would have meant shipping a known-bad shape for one whole release cycle. Compliance storyboard narrative and validations updated to reference the new shape; `supported: false` sellers skip the replay storyboard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
* spec(idempotency): refactor adcp.idempotency to discriminated oneOf (closes #2436) Swap the draft-07 if/then conditional for a two-branch discriminated union on `supported`: `IdempotencySupported` carries `replay_ttl_seconds` (required); `IdempotencyUnsupported` forbids it via `not: {required: [replay_ttl_seconds]}`. Wire format unchanged. Code generators that silently drop `if/then` (openapi-typescript, zod-to-json-schema, pre-0.25 datamodel-code-generator, quicktype) now emit two named types with the invariant at the type level. Safe to fold into #2434 because `supported` is brand-new in that PR — no SDKs have generated against the interim `if/then` shape yet. Deferring to 4.0 per the original #2436 proposal would have meant shipping a known-bad shape for one whole release cycle. Compliance storyboard narrative and validations updated to reference the new shape; `supported: false` sellers skip the replay storyboard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * spec(idempotency): negative-path fixtures + tone fix on const:true description Address review feedback on PR #2447: - Replace meta-phrasing on IdempotencySupported.supported with wire-behavior description, matching the tone of IdempotencyUnsupported.supported. - Add five schema fixtures exercising the oneOf discriminator: two positive (supported:true with TTL, supported:false alone) and three negative paths (TTL on unsupported, missing TTL on supported, empty block). The negative cases lock the `not: {required}` invariant that Ajv's default error text obscures. Codegen spot-check verified externally: openapi-typescript 7.13 emits `{supported: true; replay_ttl_seconds: number} | {supported: false}` and datamodel-code-generator 0.54 emits two Pydantic classes with `Literal[True]/Literal[False]` and required `replay_ttl_seconds` on the supported branch — exactly the type-level invariant the refactor promises. 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.
Three pre-4.0 DX fixes surfaced during Python SDK v4.0.0-rc validation (adcp-client-python#205). Bundled as one minor spec bump.
Summary
sync_creativesresponse now mirrors thecreate_media_buythree-shape pattern. Per-item: optionalstatus: CreativeStatus(so buyers learn approval/review state without a follow-uplist_creatives). Top-level: newSyncCreativesSubmittedshape (status: "submitted"+task_id) for when the whole sync is queued async (batch ingestion, governance-gated). Per-item async review (one creative inpending_reviewwhile the rest resolves) stays on the synchronous success branch via per-itemstatus— no fabricated sentinels. Resolves the doc/schema mismatch wheresync_creatives.mdxwas already teaching a submitted/completed async workflow that didn't exist in the schema.adcp.idempotency.supported: boolean(required), mirroring therequest_signing.supportedpattern.replay_ttl_secondsis conditionally required only whensupported: true, so sellers without replay dedup can declare it positively ({ "supported": false }) instead of emitting an ambiguous empty block.CREATIVE_NOT_FOUNDandSIGNAL_NOT_FOUNDto theerror-codeenum to match the existingPRODUCT_NOT_FOUND/MEDIA_BUY_NOT_FOUND/PACKAGE_NOT_FOUND/ACCOUNT_NOT_FOUND/SESSION_NOT_FOUNDpattern. Both classified ascorrectablerecovery.Docs updated
docs/protocol/get_adcp_capabilities.mdx— newidempotencysubsection with thesupported: boolcontract, plus updated every in-doc capabilities example across the repo to use the new shape (signals, media-buy, creative, migration, etc.)docs/creative/task-reference/sync_creatives.mdx— three-shape response documented explicitly; per-itemstatusfield table entry; async-approval workflow split into per-creative review vs operation-level asyncdocs/building/implementation/error-handling.mdx— addedCREATIVE_NOT_FOUNDandSIGNAL_NOT_FOUNDto the reference tablesTest plan
npm run test:schemas— 483 schemas, structural + registry +$refvalidationnpm run test:json-schema— 249 in-doc examples with$schemadirectives validate after idempotency-shape updatesnpm run test:composed— discriminated union shapes (including the new three-branch oneOf on sync_creatives)npm run test:error-handling— reference/SDK/doc consistency on error codesnpm run test:examples+test:extensions+test:extension-schemastest:unit(587 tests) +typecheck🤖 Generated with Claude Code