Skip to content

feat(creative): add creative revision identity - #6781

Open
bokelley wants to merge 12 commits into
mainfrom
creative-revision-identity
Open

feat(creative): add creative revision identity#6781
bokelley wants to merge 12 commits into
mainfrom
creative-revision-identity

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

  • add optional buyer-authored revision_id to sync_creatives with immutable effective-input semantics and a typed mismatch error
  • carry accepted/current/source revision identity through sync results, library readback, status webhooks, and historical delivery reporting
  • define re-review, legacy omission, first-time adoption, stale review, dry-run, localization mutation, and fingerprint-retention behavior
  • make served variant_id an agent-unique immutable execution identity so historical delivery and variant preview remain unambiguous
  • reserve complete representation-set canonicalization for feat(creative): define delivery representations and macro resolution #6767 without minting a revision per representation

Identity model

creative_id       durable buyer creative
revision_id       immutable buyer-authored effective input state
representation_id equivalent representation inside one revision (#6767)
variant_id        agent-unique immutable served execution
build_variant_id  pre-trafficking generated candidate

This PR does not add revision history, rollback, or staged serving. Experimental staged activation is tracked separately in #6780.

Verification

  • npm run test:schemas
  • npm run test:json-schema
  • npm run test:docs-nav
  • npm run test:compliance-snippets
  • npm run test:error-code-drift
  • npm run test:oneof-discriminators
  • npm run test:storyboard-context-entity
  • node --test tests/creative-revisions.test.cjs
  • node scripts/check-changeset-protocol-scope.cjs origin/main
  • npx --yes @changesets/cli@^3.0.0 status --since=origin/main

Closes #6347

@bokelley
bokelley enabled auto-merge (squash) August 23, 2026 04:09
Comment thread static/schemas/source/core/creative-variant.json Outdated
Comment thread static/schemas/source/creative/sync-creatives-request.json

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated schema paths require human/CODEOWNERS review.

This PR touches the protocol source of truth (static/schemas/source/**), which is under a hard, deterministic approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so decision-table row 2 fires: the gate is not satisfied and the PR cannot be auto-approved.

No critical/high findings were reported. The reviewer surfaced two medium findings and two low findings — none blocking on their own — but the deterministic gate takes precedence.

Medium findings (non-blocking, for human reviewer awareness):

  • static/schemas/source/core/creative-variant.json:26 — variant_id uniqueness scope tightened (per-creative → per-agent) on a minor changeset
  • static/schemas/source/creative/sync-creatives-request.json:150 — New creatives[] per-batch uniqueness constraint not reflected in docs

Escalation reasons:

  • Gated paths touched (14 files under static/schemas/source/**, including additions creative-revision-id.json and creative-revision-content-mismatch.json plus modifications to creative-variant.json, error-code.json, index.json, and several creative task schemas) and review_decision is REVIEW_REQUIRED. Human/CODEOWNERS approval is required before merge.

Human reviewers should confirm the two coherence points above (variant_id scope semantics and the undocumented per-batch creative_id uniqueness constraint) as part of that review.

Medium findings

  • static/schemas/source/core/creative-variant.json:26 — variant_id uniqueness scope tightened (per-creative -> per-agent) on a minor changeset
  • static/schemas/source/creative/sync-creatives-request.json:150 — New creatives[] uniqueness constraint not reflected in docs

Why human review

  • Gated paths: 14 files under static/schemas/source/** are modified/added while review_decision is REVIEW_REQUIRED (not APPROVED) — human/CODEOWNERS approval required before merge.
  • Medium coherence gaps for reviewer attention: variant_id uniqueness scope tightened per-creative→per-agent, and a new per-batch creative_id uniqueness constraint is undocumented.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/creative-revision-id.json (added) matches static/schemas/source/**; static/schemas/source/core/creative-variant.json (modified) matches static/schemas/source/**; static/schemas/source/core/x-entity-types.json (modified) matches static/schemas/source/**; static/schemas/source/creative/creative-status-changed-webhook.json (modified) matches static/schemas/source/**; static/schemas/source/creative/get-creative-delivery-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/list-creatives-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/preview-creative-request.json (modified) matches static/schemas/source/**; static/schemas/source/creative/preview-creative-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/sync-creatives-request.json (modified) matches static/schemas/source/**; static/schemas/source/creative/sync-creatives-response.json (modified) matches static/schemas/source/**; static/schemas/source/enums/error-code.json (modified) matches static/schemas/source/**; static/schemas/source/error-details/creative-revision-content-mismatch.json (added) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Aug 23, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated-paths hard approval gate not yet satisfied.

This PR modifies/adds 14 files under static/schemas/source/**, which are under a hard, deterministic approval gate (gated_paths: true). The current review_decision is unknown/not APPROVED, so per decision-table row 2 the outcome must be escalate — Ladon cannot auto-approve gated protocol-schema changes until a human/CODEOWNERS approval is recorded.

The delta itself is clean: the developer walked back both prior medium findings. variant_id uniqueness is no longer tightened (default stays unique-within-agent-and-creative; agent-unique/immutable only applies when an agent advertises creative.supports_revisions), applied coherently across creative-variant.json, get-creative-delivery-response.json, preview-creative-request/response.json and the corresponding docs, with tests updated. sync_creatives.mdx now documents the creatives[] unique creative_id constraint. Schema↔docs coherence holds. No new findings in this run.

Because both prior mediums are resolved and no fresh actionable findings exist, sticky escalation (row 6) does not apply on its own — but the gated-paths gate (row 2) still stands and requires human review.

Escalation reasons

  • 14 files under static/schemas/source/** are modified/added while review_decision is not APPROVED — human/CODEOWNERS approval required before merge.

Why human review

  • Gated paths: 14 files under static/schemas/source/** are modified/added while review_decision is not APPROVED — human/CODEOWNERS approval required before merge (decision-table row 2). Affected files include static/schemas/source/core/creative-revision-id.json (added), static/schemas/source/core/creative-variant.json (modified), static/schemas/source/enums/error-code.json (modified), static/schemas/source/creative/sync-creatives-request.json (modified) and others.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/creative-revision-id.json (added) matches static/schemas/source/**; static/schemas/source/core/creative-variant.json (modified) matches static/schemas/source/**; static/schemas/source/core/x-entity-types.json (modified) matches static/schemas/source/**; static/schemas/source/creative/creative-status-changed-webhook.json (modified) matches static/schemas/source/**; static/schemas/source/creative/get-creative-delivery-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/list-creatives-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/preview-creative-request.json (modified) matches static/schemas/source/**; static/schemas/source/creative/preview-creative-response.json (modified) matches static/schemas/source/**; static/schemas/source/creative/sync-creatives-request.json (modified) matches static/schemas/source/**; static/schemas/source/creative/sync-creatives-response.json (modified) matches static/schemas/source/**; static/schemas/source/enums/error-code.json (modified) matches static/schemas/source/**; static/schemas/source/error-details/creative-revision-content-mismatch.json (added) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@pkras

pkras commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Read through #6781 — solid foundation, and it does land the two core pieces of #6347 (buyer revision_id + re-review on material change). Two workflow gaps I want to flag before this is "done" from a buyer's POV:

1. Serving during re-review — needs to be spelled out.
The spec says the prior approved revision isn't kept serving during review and changed content can't inherit prior approval. I get why (that's #6780's job), but the buyer-facing behavior isn't stated:

  • Single-creative line → that creative goes ineligible until re-approved, i.e. a real serving gap. Is that acceptable as an interim state before Experimental staged activation for live creative revisions #6780?
  • Rotation across multiple creatives → I think the revised one drops out and the rest keep rotating, so the campaign doesn't stop — but the spec never says this, and "what happens to the rest of my rotation?" is the first question a buyer asks. Can we state the rotation interaction explicitly?

Visibility itself looks fine (sync response acks the transition, creative.status_changed on the decision, list_creatives readback) — it's the serving behavior that's ambiguous.

2. Managed service / seller-authored creatives.
The whole model is "buyer-authored," and seller transcodes explicitly don't mint revisions. That leaves the managed-service flow — seller builds the creative and hands it back for the buyer to iterate — with no home. Is that intentionally out of scope, or does the handoff need a defined path into the revision model? I don't think we want a mirror "seller revision" type (it'd muddy the buyer-input-integrity guarantee), but we should give managed service an explicit answer rather than a silent gap.

Happy to dig into either.

@bokelley

Copy link
Copy Markdown
Contributor Author

@pkras — read both against the current spec + adjacent tasks. Think both are answerable by composing what's already there, with two small non-normative doc clarifications on this PR to close the gap you flagged. No new normative rules needed.

1. Serving during re-review

Per-creative status is the eligibility contract (specification.mdx:449–486). A creative in pending_review is ineligible for delivery, full stop — the PR's approved → pending_review transition (line 476) plugs into that existing rule, and prior approval demonstrably does not carry ("Changed revision content cannot inherit the prior approval"). From there:

  • Single-creative line: the serving gap is the deliberate default; Experimental staged activation for live creative revisions #6780 exists precisely to opt into non-default staged-activation. Zero-gap workaround before Experimental staged activation for live creative revisions #6780 lands: sync the update under a new creative_id and swap package assignments — same pattern as buyers running "promote new, don't edit running" on TTD/DV360.
  • Rotation across multiple creatives: the revised creative drops out because it's pending_review; other creatives assigned to the same package continue per the seller's ordinary selection because nothing in AdCP couples eligibility across creatives on a package. That's already true today; the PR doesn't change it. Worth stating out loud, but a normative "rotation continues" rule would mint a spec-level opinion about ad-server selection AdCP has deliberately left to sellers (parallel to OpenRTB not specifying DSP creative-picking).

Suggested one-sentence addition to the revisions section of sync_creatives.mdx:

A revised creative under re-review is ineligible for delivery like any other pending_review creative until it re-reaches approved; other creatives assigned to the same package are unaffected and continue per the seller's ordinary selection.

2. Managed service / seller-authored

Path already exists as a composition. build_creative is the seller-produces-candidate surface — seller returns a manifest, buyer chooses whether to sync it. When the buyer syncs the returned manifest via sync_creatives, that submission is buyer-authored: the buyer signed off, chose creative_id, and mints (or omits) revision_id. That preserves the input-integrity property you're protecting. It also aligns with the canonical-formats.mdx:71,1515 stance that the seller's production mechanism (transcode, AI, host recording, or a managed-service build team) is invisible to the buyer; identity attaches at the sync boundary.

Suggested one-line pointer in the same revisions section:

Creatives produced via build_creative become buyer-authored at the moment the buyer syncs the returned manifest and mints (or omits) revision_id on that item.

The audit trail of seller-side drafts before the buyer syncs is genuinely outside AdCP — that's a managed-service SLA artifact between the parties, not a wire concern. A "seller revision" type would muddy input integrity as you noted and doesn't solve a protocol-layer problem; agree it should stay off this PR.

Both clarifications are docs-only and additive — no schema or semantic change. @bokelley to decide whether they fold into this PR or ship as a follow-up.


Generated by Claude Code

@bokelley

Copy link
Copy Markdown
Contributor Author

@pkras Folded both clarifications into ff5ec0a. The revision section now states explicitly that a revised pending_review creative is ineligible, other assigned creatives remain unaffected under ordinary seller selection, and a single-creative package has a serving gap unless the buyer uses a new creative_id replacement flow. It also defines the managed-service boundary: a build_creative result becomes buyer-authored revision state when the buyer accepts and syncs the manifest; pre-sync seller drafts remain outside AdCP revision history. No schema or lifecycle semantics changed.

@bokelley
bokelley requested a review from pkras August 24, 2026 12:24

@pkras pkras left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two smaller content-definition notes, both docs/schema-only:

  1. ext shouldn’t count as revision content. The canonical-content rule excludes 8 named top-level fields and treats everything else — including ext — as revision-bearing (creative-asset.json is additionalProperties: true). That means a drifting vendor/telemetry value in ext trips CREATIVE_REVISION_CONTENT_MISMATCH on an otherwise-identical re-sync. Suggest adding ext (or a designated non-content envelope) to the excluded set so buyers aren’t penalized for data that doesn’t change what renders.

  2. The “digest-bound asset” escape hatch doesn’t exist yet. The revision section points buyers needing byte-level identity to “immutable URLs or digest-bound asset forms where available” — but there’s no content-digest field on creative-asset.json, so immutable URLs are the only real tool today. Either add the digest field or drop the reference so it’s not sending buyers after something unimplemented.

bokelley and others added 4 commits August 24, 2026 22:30
…6767)

* feat(creative): define delivery and macro resolution contracts

* docs(creative): gate tracker firing on product capabilities

* fix(creative): preserve URL compatibility and type error details

* feat(creative): define complete VAST technical requirements

* fix(creative): keep VAST requirements extensible

* fix(creative): close delivery contract review gaps

* fix(creative): satisfy canonical VAST gates

* fix(compliance): version CTV VAST fixtures

* feat(creative): model equivalent representation sets

* fix(creative): satisfy representation CI gates

* fix(creative): keep VAST version requirement additive

* docs(creative): clarify representation set format scoping and add localization migration warning

Per @pkras review on #6767:
- state explicitly that a representation set is scoped to one format_kind
- add a Warning callout documenting the destructive, non-atomic path for
  migrating an existing localized creative to representation selection

* docs(creative): pin accepted representation selections

* fix(creative): clarify representation review guarantees

---------

Co-authored-by: Claude <noreply@anthropic.com>
…ntity

# Conflicts:
#	docs.json
#	tests/docs-nav-validation.test.cjs
@aao-secretariat

Copy link
Copy Markdown
Contributor

Ladon cannot review this PR until merge conflicts are resolved.

@bokelley

Copy link
Copy Markdown
Contributor Author

Ready for fresh CODEOWNER review. Since the previous approval, this branch was updated onto current main, preserved the new versioned-doc navigation, capability-gated revision echo/readback on creative.supports_revisions, and added a capability-gated stateful revision workflow (including same-ID content-mismatch non-mutation). Ladon and focused schema/compliance checks are green; full CI is running. @ohalushchak-exadel @bmilekic, please re-review when available.

@bokelley

Copy link
Copy Markdown
Contributor Author

CI follow-up: the repeated 4 GiB sales storyboard failure was a deterministic schema-projection expansion, not runner flakiness. macro_resolution_capabilities was nested in canonical params, causing the capability graph to be replicated through the 15-way ProductFormatDeclaration union. This head moves the still-unreleased field to the format-option declaration root (full + compact shapes), forbids the old nested location, updates docs/examples, and adds schema regression coverage. The exact failing proposal_finalize storyboard now passes 9/9 at 2.36 GiB under the existing 4 GiB guard (previous local peak >6 GiB). Full schema/MCP/compliance/docs gates pass locally. Protocol and code/CI expert reviews report no blocker/high findings. Fresh human/CODEOWNERS approval is still required after this push.

@bokelley

Copy link
Copy Markdown
Contributor Author

Follow-up on the two review notes:

  1. ext remains revision-bearing intentionally. It is a buyer-authored, vendor-namespaced extension surface for partner-specific configuration and proposed features, so it can change rendering/serving behavior. Excluding it categorically would allow delivery-affecting state to change under an immutable revision ID. Pure request telemetry belongs in the task-level ext/context, not the creative item.
  2. The “digest-bound asset forms where available” wording is deliberately conditional. A concrete current form is zip_asset.digest (sha256:<hex>); other URL assets do not claim a digest field. The text therefore does not imply universal digest support.

The latest head also contains the schema-graph/CI memory fix described above. Fresh approval is requested for 9e875966cd after CI completes.

@bokelley

Copy link
Copy Markdown
Contributor Author

CI is now fully green at the exact head (58 passing, 0 failing; 2 expected skips), all review threads are resolved, and independent protocol/code reviews found no blocker/high issues. @pkras @ohalushchak-exadel @bmilekic — a fresh CODEOWNERS approval is the sole remaining merge gate; auto-merge is enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Creative revisions: immutable identity, re-review, and delivery correlation

2 participants