RFC #3305 implementation preview — v2 creative formats (full spec, not for merge)#3307
Open
bokelley wants to merge 54 commits into
Open
RFC #3305 implementation preview — v2 creative formats (full spec, not for merge)#3307bokelley wants to merge 54 commits into
bokelley wants to merge 54 commits into
Conversation
…_type, video.mdx fix First PR implementing the v2 creative formats RFC (#3305). Backwards-compatible additions only. - Add static/schemas/source/core/asset-group-vocabulary.json (canonical asset_group_id registry — 7 existing catalog vocab entries + 12 audit-driven additions, with landing_page_url canonicalizing 6 v1 alias names) - Add static/schemas/source/creative/scenes.json (typed scene-by-scene structure for build_creative input; renamed from "storyboard" to avoid collision with the testing-harness storyboard concept) - Add optional delivery_type discriminator to html-asset.json and javascript-asset.json via oneOf (inline branch matches v1 producers without delivery_type; url branch lets zip URLs and 3P tag URLs round-trip cleanly) - Fix docs/creative/channels/video.mdx VAST/VPAID format examples (asset_type "url"+asset_role "vast_url" → asset_type "vast"; VPAID uses asset_type "vast" with vpaid_enabled: true) Tracks #3305 (v2 RFC). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…register vocabulary in source index Two follow-ups from independent expert review of #3307: - audio.mdx:200 had the same bug pattern as video.mdx (caught by code-reviewer): the audio_30s_vast manifest example used asset_type "url" + url_type "tracker" for what should be a VAST audio tag. Corrected to asset_type "vast" with delivery_type "url"; renamed slot key from "vast_url" to "vast_tag" for clarity. - Register asset-group-vocabulary.json under core schemas and scenes.json under creative.build_inputs in static/schemas/source/index.json so the new schemas are discoverable via the public registry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI caught: when I changed asset_type from "url" to "vast" in video.mdx:410, the vast-asset-requirements.json schema started applying — it requires vast_version as a single string from the enum, not an array. Original doc had vast_version: ["3.0", "4.0", "4.1", "4.2"] which the looser "url" asset_type tolerated. Fix: vast_version: "4.2" (matches the manifest example at line 511). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New schemas (asset-group-vocabulary.json, scenes.json) and a new optional field (delivery_type on html/javascript) are additive features. Patch is reserved for bug fixes only. Aligns with the RFC's "3.1 preview track" framing — Phase 1 is the first PR toward 3.1.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 design scrub. The delivery_type oneOf addition on html/javascript schemas wasn't earning its keep — URL-delivered HTML/JS already routes through url-asset.json with appropriate url_type. The real gap was a zip asset type for HTML5 banner bundles, which is genuinely missing from the registry today. - Revert delivery_type from html-asset.json and javascript-asset.json (back to inline-only, matching v1 behavior) - Add static/schemas/source/core/assets/zip-asset.json — new asset type for bundled HTML5 banners (url + max_file_size_kb + entry_point + allowed_inner_extensions + backup_image_url + sha256 digest) - Register zip in creative/asset-types/index.json - Add IndividualZipAsset / GroupZipAsset branches to format.json - Add zip-asset.json $ref to creative-manifest.json, creative-asset.json, creative/list-creatives-response.json, offering-asset-group.json - Clarify scenes.json description re: reference-asset.json purpose: "storyboard" (related but different concept — structured plan vs visual reference asset) - Rename changeset accordingly Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eclaration, validate_input, build capabilities Foundation for the v2 RFC architecture (#3305). Backwards-compatible additions only — v1 named formats and v1 producers continue to work unchanged. **11 canonical format definitions** at static/schemas/source/formats/canonical/: - image (static), html5 (interactive bundle), display_tag (3P-served) - image_carousel (multi-card, polymorphic items) - video_hosted (direct file, OM-SDK + external trackers) - video_vast (VAST tag, inherent VAST event tracking) - audio_hosted (direct file) - audio_daast (DAAST tag) - sponsored_placement (retail-media catalog-driven, deterministic composition) - asset_pool_composed (Google PMax family, algorithmic composition) - brand_mention (text/audio AI-surface composition) - _base.json with shared fields (composition_model, provenance_required, platform_extensions, tracking_extensions) Each canonical bakes in its tracking model and references the asset_group_id vocabulary shipped in Phase 1. Format-keyed-by-name structure (no canonical discriminator field). **ProductFormatDeclaration** (static/schemas/source/core/product-format-declaration.json): keyed by canonical format name with minProperties: 1, maxProperties: 1, no additionalProperties — exactly one canonical key per declaration. Includes worked examples for Meta Reels, IAB MREC, podcast host-read. **Product/manifest additive fields:** - product.json: optional `format` field (v2 inline declaration), optional `build_capability_ref` (for products requiring agent-produced creative). v1 format_ids path remains supported. - creative-manifest.json: optional `brand` field (resolves brand.json) and `brand_kit_override` (explicit override for missing/stale brand.json). **Build capabilities:** - build-capability.json: schema for creative agents declaring what canonical formats they can build, with parameter narrowing and typed inputs. - build-capability-ref.json: reference type used on products. - get_adcp_capabilities response: added `creative.creative_build_capabilities` array. Replaces v1 list_creative_formats discovery surface for creative agents. **Platform extension references:** - platform-extension-ref.json: URI + content-digest reference to platform extension definitions. Bundled in get_products responses to avoid extra fetches; SDK caches by URI@digest. **validate_input tool:** - validate-input-request.json, validate-input-response.json, validate-input-result.json: cheap dry-run primitive for validating a manifest against canonical formats and/or specific products. predicted field carries pre-flight estimates; no protocol state for orphaned out-of-spec artifacts (nondeterministic platforms run their own QA loop). Tracks #3305 (v2 RFC). Phase 1 (#3307) primitives unblock this Phase 2 surface; together they let buyers and adopters point at one preview branch and build end-to-end against the v2 spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… MREC, podcast host-read) Adopter-facing documentation for the v2 RFC architecture (#3305) shipping in the #3307 preview branch. Walks through the canonical-formats-narrowed- by-products model with three concrete worked examples that illustrate the full surface: - Meta Reels narrowing video_hosted with platform extensions - IAB Medium Rectangle (300x250) narrowing image, plus a sibling NYTimes HTML5 banner narrowing html5 (different canonical for different tracking model) - The Daily 30s host-read narrowing audio_hosted with build_capability_ref pointing at the publisher's creative agent Includes worked validate_input flow, brand_kit_override usage, platform extension distribution explanation (URI+digest bundled in get_products), and explicit notes on what's NOT in v2 (brand safety frameworks, universal macros schema, destination_kinds schema, cta_vocabulary, list_build_ capabilities tool — all dropped per design scrub). Examples are marked test=false because they intentionally show only the v2-specific surface, not the full Product schema (which would clutter the illustrations with unrelated required fields like reporting_capabilities, delivery_type, etc.). Adopters can refer to actual reference fixtures for fully-valid product examples. Tracks #3305 (v2 RFC). Doc lives at /docs/creative/v2-overview alongside existing creative docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… asset_group_id to format slots; expand vocabulary aliases Simplifications from the working-example walkthrough: 1. Drop build_capability and build_capability_ref schemas + product field. The input contract folds into the format declaration as a parameter. External creative agents are invisible to the buyer in the typical case — buyer-seller boundary is the only relationship the protocol models. 2. Add `inputs` field to canonical format _base.json. Tells the buyer what the format requires (script for host-read, creative_brief for generative, voice_id for TTS variation). When absent, format accepts only buyer-uploaded creative. 3. Rename creative.creative_build_capabilities → creative.supported_formats on get_adcp_capabilities response. Drops "build_" framing on discovery surface naming. Each entry uses ProductFormatDeclaration shape — one primitive, two homes (sales-side inline on products, creative-agent- side as supported_formats list). 4. Add `asset_group_id` field to format.json baseIndividualAsset and baseGroupAsset. Lets v1 reference creatives declare their canonical equivalents inline (e.g., slot click_url → asset_group_id landing_page_url). Strengthens v1↔v2 migration bridge. 5. Expand `aliases` arrays in asset-group-vocabulary.json with audit- grounded sets for headlines, descriptions, images_landscape, images_vertical, images_square, logo, video, audio. 6. Update v2-overview.mdx — host-read example uses inline inputs, two- flow explanation (buyer pre-produces via build_creative on a creative agent → sync_creatives, OR sync_creatives directly with inputs → seller produces internally). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… preview-as-verification section Diff comment review surfaced that the comparison table's "Generative formats" row was reasoning about a category that doesn't exist at the protocol level. Sellers take inputs (script, brief, voice_id) OR assets (image, video, audio uploads) per the format declaration. Whether the seller's internal production is generative AI, host recording, transcoding, or pixel-perfect asset rendering is invisible to the buyer. There is no "generative" axis to model. - Reframe line 20 as "Format input contract" — captures the v2 collapse without leaning on a generative special case - Add "Preview as the universal what-does-this-produce surface" section after validate_input — makes explicit that preview_creative shows output regardless of submission shape (asset-driven OR input-driven). Different sellers may produce differently internally; preview surface is uniform. - Tighten "nondeterministic generative platforms" wording to "nondeterministic synthesis" — same point, less reliance on the dropped category. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…responsive_creative; brand_mention → agent_placement; brand uses BrandRef
Diff comments surfaced two naming concerns:
1. asset_pool_composed → responsive_creative
"Responsive" is the industry-recognized term Google uses (RDA, RSA, PMax,
Demand Gen). Meta uses "Advantage+ creative" / older "Dynamic Creative"
for the same concept. The new name aligns with how ad-tech adopters
already think about this category. Files renamed:
formats/canonical/asset_pool_composed.json → responsive_creative.json
ProductFormatDeclaration key renamed; cross-references in _base.json and
sponsored_placement.json description updated.
2. brand_mention → agent_placement
"brand_mention" overloaded with affiliate / sponsored-podcast vocabulary
that predates AI surfaces by decades. Architectural property is "AI
surface composes a sponsored placement in its response" — distinct from
si_chat (user converses with brand-owned agent; existing SI track) and
from sponsored_placement (retail-media catalog-driven). agent_placement
parallels sponsored_placement structurally; both are surface-composed
placements differing by surface type.
formats/canonical/brand_mention.json → agent_placement.json
Description updated to make si_chat distinction explicit.
3. creative-manifest.json brand → BrandRef
Diff comment flagged that brand: { domain } should reference brand-ref.json
(the canonical BrandRef schema). Updated to use $ref instead of inlining.
BrandRef carries domain plus optional brand_id for house-of-brands plus
optional industries / data_subject_contestation overrides.
4. v2-overview.mdx
- Discovery row consolidated: list_creative_formats deprecated by
creative.supported_formats on get_adcp_capabilities (uniform
replacement regardless of agent role); sales agents additionally
expose get_products
- Brand identity row references BrandRef explicitly with house-of-brands
note
- Canonical format table updated with new names, industry-term
attributions, and si_chat distinction on agent_placement
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…son v1/v2 oneOf + fixture validation test Phase 3 deliverables for upstream implementor review: **Migration guide** at docs/creative/v2-migration.mdx (~1500 words): - Side-by-side v1 named format → v2 product format declaration - Slot name mapping table (v1 author-invented → canonical asset_group_id) - Generative format dissolution (~30 *_generated_* files collapse into format inputs) - Brand identity slots → BrandRef + brand_kit_override - Discovery surface migration (list_creative_formats → get_products + creative.supported_formats with server-side flatten wrapper guidance) - Adopter migration paths per role (sales agent / creative agent / buyer / publisher direct) **5 reference fixtures** at static/examples/products/v2/, fully-valid Product objects that pass strict schema validation: - meta_reels_us.json (video_hosted, vertical orientation, Meta-specific extensions) - nytimes_homepage_mrec.json (image, IAB MREC 300x250) - nytimes_homepage_html5.json (html5, sibling product on same placement — different canonical because different tracking model) - the_daily_30s_host_read.json (audio_hosted with inline inputs: script + brand + offering_ref; production_window_business_days: 7) - amazon_sponsored_products.json (sponsored_placement, catalog-driven, ASIN-keyed) **Schema fix on product.json**: format_ids was unconditionally required at the schema level, blocking v2 products. Restructured to oneOf: - v1 branch: requires format_ids (named-format reference path) - v2 branch: requires format (inline ProductFormatDeclaration path) A product is one or the other, not both. **New test:v2-fixtures script** at tests/v2-fixture-validation.test.cjs. Validates all fixtures in static/examples/products/v2/ against /schemas/core/product.json. Wired into package.json scripts. **Schema validation test update**: replaced static format_ids assertion on product.json with explicit oneOf(format_ids, format) check that matches the new shape. **Doc cross-link**: v2-overview.mdx Related section links to v2-migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ng fixtures, vocabulary gaps, slot declarations Implementor review of #3307 surfaced concrete issues. All addressed: **Doc/schema mismatches (must-fix before adopters touch this):** - v2-overview.mdx: 'web' → 'display' on channels (web is not in the channels enum) - v2-overview.mdx: publisher_property_selector shape — replaced { type: "publisher", publisher_domain } with the schema-correct { publisher_domain, selection_type: "all" } across all four worked examples - audio_hosted.json description: removed stale build_capability_ref reference; reframed around inline inputs convention - product-format-declaration.json: host-read example description and data updated to use inline inputs (no build_capability_ref) **Missing fixtures (5/11 → 7/11 canonical coverage):** - google_performance_max.json — responsive_creative narrowing for Google PMax with full asset-pool min/max declarations and CPA pricing - chatgpt_brand_mention.json — agent_placement with text output, brand + offering_ref inputs, disclosure_required, advisory tone constraints **Vocabulary additions (filling gaps reviewer flagged):** - cta — call-to-action button label slot - price — product price slot for retail / shopping creative - disclaimer — legal disclaimer / fine print slot (regulated verticals) - phone_number — click-to-call slot - promo_code — coupon / offer code slot - subtitle_file — caption file URL slot - source_catalog — catalog reference slot for sponsored_placement - hero_asset — buyer-supplied hero/banner alongside catalog - Aliases added: cards (carousel_cards, slides, etc.), youtube_video_id (existing_yt_video_id, etc.) - Vocabulary header now explicitly defers input names — inputs use separate convention not yet canonicalized at spec level **Programmatic slot declarations:** - _base.json: new optional `slots` field — array of { asset_group_id, required, min, max } entries letting SDK codegen and validators enumerate format slots without parsing prose descriptions - responsive_creative: default slots array enumerates 9 canonical slots - agent_placement: default slots array (just landing_page_url since the format is composed-by-surface) **Other clarifications:** - _base.json synthesis_nondeterministic boolean — distinct axis from composition_model; covers Veo/Sora/Runway-class where predictive validate_input is impossible and the platform's own QA loop applies - agent_placement.tone_constraints — explicitly marked advisory (LLM/agentic surfaces have no protocol mechanism to enforce) - _base.json tracking_extensions description — clarified relationship to platform_extensions (subset for buyer convenience, not enforcement) Vocabulary version bumped 1.0.0 → 1.1.0; lastUpdated 2026-04-26 → 2026-04-28. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xture; agent_placement landing_page_url moves to inputs; product_card carve-out documented
Continuing review-feedback work:
**Manifest `inputs` field:**
- creative-manifest.json gains optional `inputs` field for input-driven
submissions (script, creative_brief, voice_id, offering_ref,
landing_page_url, etc.). Buyers populate it for products whose format
declares an inputs contract; v1 consumers ignore it. Some formats
accept both — buyer can mix uploaded assets with inputs that drive
seller-side composition.
**agent_placement landing_page_url moves from slot to input:**
- agent_placement has no buyer-fillable creative slots by definition;
the format is composed-by-surface. Putting landing_page_url in the
manifest's assets map (slot semantic) muddled "rendered creative"
with "metadata the agent uses." It's now declared as an input on the
format, populated via the manifest's `inputs` field.
- agent_placement.json slots default is now empty; description added
explaining the inputs-only nature
- chatgpt_brand_mention.json fixture updated to declare landing_page_url
in inputs
**Bundled extensions schema + fixture:**
- get-products-response.json gains optional `extensions` field — keyed
by `<extension_uri>@<digest>` patternProperties matching SHA-256
digests; each value is { extends, fields, version, description }
- New fixture at static/examples/get_products_responses/v2/
meta_with_bundled_extensions.json — two Meta products (Reels + Feed
Image) sharing meta_pixel extension plus separate placements_reels /
placements_feed extensions, all bundled in the response under
uri@digest keys
- v2-fixture-validation test extended to cover get_products response
fixtures alongside Product fixtures
**product_card carve-out documented:**
- v2-migration.mdx adds a section explaining why product_card and
product_card_detailed stay on v1 format_id even on v2 products:
they're the UI rendering of the product itself, not the ad creative
the product accepts. Different purpose, different schema lifecycle.
v2-only clients can ignore product_card if they don't render product
UIs.
Validation: 8 fixtures pass (7 products + 1 get_products response with
bundled extensions). All schema/json-schema/v2-fixtures tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r, type product_card inline
Three architectural simplifications from review feedback:
**1. Drop `inputs` as a separate concept.** Post build_capability collapse,
the inputs/assets distinction had lost its anchor — both maps were
"things the buyer ships." Adopters had to guess: is `script` an asset or
an input? Is `landing_page_url` rendered or contextual? The answer
varied by format and was invisible without reading prose.
The new model: format declares `slots`; manifest has one `assets` map.
Some assets are rendered verbatim (image, video); some are consumed
for production (script, creative_brief, scenes); the seller dispatches
per the format's slot declaration. Buyer mental model is uniform —
"here's what I'm shipping."
Concretely:
- _base.json: removed `inputs` field; kept production_window_business_days
- creative-manifest.json: removed `inputs` field
- audio_hosted.json description: rewritten around slot-based
submission ("buyer ships a script text asset to the script slot")
- agent_placement.json slots default: now declares
offering_ref + landing_page_url as text/url assets (no inputs map)
- asset-group-vocabulary.json: added canonical entries for `script`,
`creative_brief`, `scenes`, `voice_id`, `offering_ref`,
`style_reference`, `starter_assets`. Vocabulary header rewritten to
cover everything the buyer ships (rendered + consumed-for-production).
**2. format_kind discriminator on ProductFormatDeclaration.**
Was: keyed-union shape `{ format: { video_hosted: { ...params } } }`
generates awkward TS/Pydantic codegen (11 separate "is this image / html5
/ ... " probes per access).
Now: `{ format: { format_kind: "video_hosted", params: { ... } } }`
generates clean tagged unions. JSON Schema `discriminator` keyword
with oneOf branches. Each branch is `format_kind: const "<canonical>"`
+ `params: $ref to that canonical's schema`.
All 7 product fixtures + 1 get_products response fixture restructured
to the new shape. v2-overview.mdx + v2-migration.mdx examples
restructured.
**3. Typed product_card and product_card_detailed.**
Was: `format_id` + `manifest` indirection (referenced v1
product_card_standard format file).
Now: inline typed structure on Product:
- product_card: image + title + description + price_label + cta_label
- product_card_detailed: hero_image + carousel_images + title +
description + specifications array + price_label + cta_label
Drops the v1 format_id punt the reviewer flagged. product_card serves a
different purpose (UI rendering of the product itself, not the ad
creative the product accepts) — typing it inline avoids conflating
ad-creative formats with UI-display metadata.
Validation: 8 fixtures pass; all schema tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…elationship
Reviewer asked whether style_reference is redundant with brand.json.
Not redundant — different scope:
- brand.json: declarative brand-level style (hex colors, voice
description, logo, tagline) stable across campaigns
- style_reference: per-creative reference image ("make it look like
THIS") that ships in the manifest for image-to-image variation,
style transfer, hero-shot lighting reuse
For pure brand-style consistency, BrandRef → brand.json is sufficient
and style_reference isn't needed. style_reference is for the cases
where the buyer wants to convey style by example rather than by
declarative attribute.
Industry parallels: MidJourney --sref, Adobe Firefly structure/style
reference, Runway/Pika style image inputs. Without canonicalization
each platform invents its own slot name (reference_image, style_image,
inspiration_image, structure_reference).
Tightened the canonical entry's description to make the brand.json
relationship explicit; added aliases for the common platform-invented
slot names.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ondeterministic + provenance examples Address review feedback on the inputs→slots collapse: **Doc prose mismatches** (adopters reading docs got the wrong model): - v2-overview.mdx line 20 architectural-shift table: "inputs concept" → "slots array enumerating everything the buyer ships" - v2-overview.mdx host-read prose / Flow 1 / Flow 2 / preview section: rewritten around the slot/asset model. No more "input-driven" framing. - v2-overview.mdx "What's NOT in v2" build_capability bullet: collapsed into the canonical slot/asset model, not into a non-existent inputs map. - v2-migration.mdx *_generated_* collapse bullet, sales agent bullet, creative agent migration step 1, buyer migration step 4: all reframed around slots. - v2-migration.mdx product_card section: rewritten — product_card is typed inline now, not a v1 punt. Old framing was inconsistent with the typed-inline change in commit 53f8dbe. **Schema description mismatches** (codegen tools read these): - product.json `format` field description: removed "keyed by canonical format name" and "optional inputs"; describes the format_kind + params discriminator shape and the slots-on-format model. - creative-manifest.json `assets` description: covers both v1 (asset_id-keyed) and v2 (asset_group_id-keyed) paths with the current canonical example slot names; added zip to the asset_type list. **Slot schema typing** — _base.json's slots schema now defines asset_type (enum of 16 asset types), max_chars, max_size_kb, and description as first-class fields instead of slipping through additionalProperties: true. Codegen now sees asset_type info per slot. Existing fixtures + canonical defaults updated to include asset_type on every slot entry. **Vocabulary gap**: added long_headlines canonical entry. responsive_ creative.json's default slots referenced it but it wasn't in the registry — soft-warning case the registry was meant to catch caught the canonical itself. Now consistent. **sponsored_placement default slots** — added (was the only canonical without a default; reviewer flagged the asymmetry). Default slots: source_catalog (catalog asset, required), hero_asset (image, optional), landing_page_url (url, optional). **Canonical-policy doc** — added x-canonical-policy-required-params-not- enforced annotation on _base.json explaining the intentional choice: canonicals are loose contracts; products narrow them; required-param enforcement happens at the product level, not the canonical level. **4 missing canonical fixtures added** (5/11 → 11/11 canonical coverage): - gam_3p_display_tag.json (display_tag canonical) - meta_carousel.json (image_carousel canonical with polymorphic items) - youtube_vast_preroll.json (video_vast canonical with VPAID-disabled skippable pre-roll) - triton_daast_audio_30s.json (audio_daast canonical) **Veo fixture** (veo_generative_video_15s.json) exercises both synthesis_nondeterministic: true and provenance_required: true with a representative scenes-driven generative video shape. Closes the "no fixture demonstrates these flags" gap. **validate_input example slot key** — changed video_main → video to match v2 canonical vocabulary. **Phase status table** — Phase 3 now shows what actually shipped (the migration guide, fixtures, fixture-validation test); Phase 4 is the SDK codegen / flatten wrapper work. Validation: 13 fixtures pass (12 products + 1 get_products response with bundled extensions). All schema/json-schema tests green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s agents Reviewer flagged three implementation-side hooks v2 introduces that existing server implementations (e.g., the salesagent reference impl) don't have today. Added a "Server-side implementation considerations" subsection to the v2-migration.mdx sales-agent migration path: - sync_creatives provenance verification when format.params.provenance_ required: true. Natural extension of existing AI-provenance tracking (EU AI Act Article 50); the new piece is a validation hook that gates submission of unsigned synthesized assets. - get_products response gathers extension definitions when products carry platform_extensions. Trivial when no v2 declarations; only kicks in for opt-in tenants. - production_window_business_days on host-read / agent-produced products. Most server impls don't model production turnaround today; v2 makes it declarable. Pure docs change; no schema impact. 13 fixtures still pass strict validation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cab-scenes-delivery # Conflicts: # static/schemas/source/creative/list-creatives-response.json
…nical status, hosting paragraph, third-party creative-agent worked example Schema: - Rename product.format → product.format_options (array of ProductFormatDeclaration). Restores v1 format_ids cardinality on the v2 path. The 90% case is single-element; multi-element declares "accepts any of" (e.g., Flashtalking-served html5 OR internal display_tag; hosted video OR VAST tag). Mutually exclusive with format_ids. - Add status: stable | preview | deprecated to canonical _base.json. Default stable. Mark agent_placement and responsive_creative as preview in 3.1, with a note that schemas may break in 3.2 once 2-3 adopters land. Other 9 canonicals stay stable (anchored in IAB / platform standards). Docs: - New worked example in v2-overview.mdx: third-party creative agent path (Flashtalking + NYTimes display). Multi-actor walkthrough alongside the existing single-actor host-read. Documents that the seller validates against the canonical, not against the creative agent's narrowing — that's the creative agent's contract with the buyer. - Platform extension hosting paragraph added to v2-overview.mdx: publisher subdomain hosts canonical artifact; immutable caching enabled by digest pinning; ≥99.9% / 30-day availability target; 404 degrades gracefully (extension unavailable, don't fail the buy); AAO mirror is best-effort fallback. - Adoption-driven format_ids removal trigger documented in v2-migration.mdx: AAO computes format_options adoption ratio from cached get_products responses; 5.0 cut sequence opens when the ratio crosses 80% for 30 consecutive days. Replaces calendar trigger. Schema housekeeping: - validate-input-response.json description documents the intent behind the 3-schema split (Result reused by planned async-validation surfaces — build_creative async paths, sync_creatives async validation). - 12 v2 product fixtures + 1 get_products response fixture migrated to format_options array. All 13 still validate via npm run test:v2-fixtures. - tests/schema-validation.test.cjs core-required-fields rule updated to assert format_options on the v2 oneOf branch. Tracks #3305 (RFC) and #3307 (preview branch). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y-creative-agent worked example The Flow 1 worked example I added in 19e6a30 fabricated a `creative_agents` field on the sales agent's `get_adcp_capabilities.creative` block. That field does not exist in v2: - `creative_agents[]` is a v1 field on `list_creative_formats` (recursive- discovery hint, not a list of "approved creative agents"). It's part of the deprecated v1 surface. - `creative.supported_formats` lives on the *creative agent's* capabilities response, declaring what that agent can produce. It's not a sales-agent- side list. - The v2 sales agent's authoritative declaration of accepted formats is the product catalog (`format_options` on each product). - Buyers choose creative agents independently — through brand-side relationships, AAO registry, or direct knowledge. Rewrites the worked example accordingly: buyer reads NYTimes products, picks Flashtalking out-of-band, calls Flashtalking's build_creative, ships the manifest to NYTimes. NYTimes validates against the canonical its product narrows; it knows nothing about Flashtalking and maintains no list of approved creative agents. Drops the bogus auto-projection prose ("SDK derives supported_formats by fetching each creative_agents[].agent_url and unioning") — that projection has no basis in the v2 schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ce / item_production_model) for generative-DSP and multi-output patterns Closes the asymmetry where audio_hosted handled "who renders" via audio_source but image and video_hosted had no analogous parameter. That left generative-DSP-shaped adopters (universalads, Pencil, AdCreative.ai-shaped tools, GenStudio-shaped tools) without a clean expression — they had to fudge composition_model or invent platform extensions for what's actually a common pattern. Added: - image_source on image canonical: buyer_uploaded | seller_pre_rendered_from_brief | seller_human_designed | agent_synthesized (default buyer_uploaded). Plus buyer_image_acceptance: accepted | rejected. - video_source on video_hosted canonical: same enum and pattern mirroring image_source. Plus buyer_video_acceptance. - item_production_model on sponsored_placement: same enum applied per catalog item. Captures the multi-output generative pattern (1 brief × N catalog items → N rendered creatives — universalads_generated_offerings shape) under sponsored_placement without requiring a 12th canonical. These are informational, not the binding contract. The format's slots declaration is what binds; *_source describes how the product produces the rendered creative so buyers can pick products whose production model fits their workflow. v2-overview.mdx now explicitly differentiates the two orthogonal axes: - composition_model — how the surface composes per-impression (deterministic vs algorithmic per-impression). - production source — who renders, and when (per-canonical *_source parameters). Conflating them was the gap. A generative DSP that produces ONE rendered image from a brief is composition_model:deterministic + image_source:seller_pre_rendered_from_brief — not a new composition pattern, just a different production source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… cards, audio_source widening, oneOf tightening, slots inline, status pathway, digest collision
Closes red-team Must-Fix items from the protocol-expert pass:
- M1: Manifest v2 path. creative-manifest.json and creative-asset.json now carry
oneOf(format_id v1 path | format_kind v2 path) with explicit not on each
branch. Adds /schemas/core/canonical-format-kind.json enum to back the v2 path.
Adds optional capability_id field to disambiguate when a product's
format_options carries multiple declarations sharing the same format_kind.
Without this, v2 products had no v2 manifest counterpart — every SDK author
would invent a different bridge.
- M2: format_options routing. ProductFormatDeclaration grows capability_id
(stable identifier for routing) and applies_to_channels (subset of the
product's channels this declaration applies to — covers S15 too). Lets a
multi-channel product carry channel-specific format_options.
- M3: Veo fixture used audio_source / buyer_audio_acceptance on a video_hosted
format. Renamed to video_source / buyer_video_acceptance.
- M4: audio_source enum was narrower than image_source / video_source.
Widened to match (added seller_pre_rendered_from_brief and
seller_human_designed). TTS-from-brief and studio-produced audio now
expressible.
- M5: product.json oneOf branches got explicit not: required: [other] so a
payload carrying both format_ids AND format_options fails closed under any
validator.
- M6: get-adcp-capabilities-response.json supported_formats descriptions
referenced the dropped 'inputs' concept (collapsed into slots in r4).
Replaced with format_kind + params + slots framing.
- M7: image_carousel slot model. Added a default slots declaration with cards
slot (asset_type: object, min/max bounds), plus a normative card_shape
parameter documenting the per-card object structure (media + headline +
landing_page_url). assets.cards is now the unambiguous array-under-one-key
contract; per-card key conventions (card_0_headline, cards.0.headline) are
forbidden.
- N: Slots inline default added to all 11 canonicals (previously only on 3).
SDK codegen now produces typed slot lists for every canonical.
- N: Synthesis_nondeterministic compatibility table added to _base.json
description. seller_pre_rendered_from_brief / seller_human_designed /
agent_synthesized may pair with synthesis_nondeterministic: true.
buyer_uploaded and publisher_host_recorded MUST NOT.
- N: platform-extension-ref digest collision behavior documented.
Within a single response, divergent digests for the same uri MUST fail
closed. Across responses, divergence is normal (extension version updates).
- S16: status:preview deprecation pathway. _base.json status field gets
since_version + migration_target_version siblings, plus a stabilization
rubric ("preview → stable when 2 adopters ship + 90 days no breaking
change"). Adopters get a schema-level signal of where each canonical is
in the lifecycle.
Test rule updated for the new oneOf shape on creative-asset.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hosting reframe, worked examples, OpenRTB differential, decision rules, cross-doc banners Closes red-team Should-Fix and Nit items from the docs-expert and adtech-product-expert passes: - v2-overview.mdx: 25-term glossary at the top of the doc; asset_group_id vocabulary table (was only in JSON); refined "Two axes" section to show the unified 5-value production-source enum; tracker assembly under seller-rendered sources documented (macro-substituted vs sync-creatives tracker block); "Channels not yet canonicalized" section (native, linear TV, OOH, DAI, in-game, live). - v2-overview.mdx worked examples: generative DSP (universalads-class, image_source: seller_pre_rendered_from_brief), multi-format product (Flashtalking html5 OR internal display_tag), sponsored_placement with item_production_model (1 brief × N items → N creatives). Closes the gap where the new schema fields had no concrete worked-example coverage. - v2-overview.mdx hosting reframe: two normative paths. Open-ecosystem (publisher hosts the canonical artifact, immutable digest-pinned caching) vs closed-platform (AAO mirror translates walled-garden format docs into AdCP extension artifacts and hosts them under mirror.adcontextprotocol.org). Walled gardens were previously framed as a parenthetical fallback; reality is they ARE the AAO-mirror primary path. - v2-overview.mdx validate_input: "when to use" decision rule (pre-flight, multi-target dry-run, debug rejection) plus comparison table with build_creative and sync_creatives. Closes the gap where the doc showed an example without explaining when the primitive fits. Cross-link to /docs/creative/task-reference/build_creative. - v2-overview.mdx scaling: client-side filtering + multi-target validate_input as the high-product-count operational pattern. Addresses the per-product validate_input scaling concern. - v2-overview.mdx narrative tuning: generative-DSP fields (synthesis_nondeterministic, item_production_model: agent_synthesized) demoted to a forward-looking subsection. Universalads/Pencil are real adopters but small share of 2026 spend; the schema breadth must not read as AI-first. - v2-overview.mdx creative-agent business model: clarifies v2 disaggregation is conceptual — creative agents continue to host produced asset bytes and instrument tracking via platform extensions. - v2-overview.mdx preview canonicals stabilization rubric and Phase 4 SDK codegen blocker callout in the status banner. - v2-migration.mdx: v1 deprecation calendar floor (2027-Q4) and ceiling (2029-Q1) bounding the 80%/30-day adoption trigger; adoption-trigger metric defined with denominator + numerator + AAO publishing surface; creative_id stability invariant across v1 ↔ v2; "What v2 gives you that OpenRTB doesn't" subsection (canonical-as-contract decoupling, runtime discovery, declared production source, canonical tracking model). - v2-migration.mdx fixture count reconciled (12 product fixtures + 1 response fixture, all 11 canonicals covered). - Cross-doc v2 preview banners on formats.mdx, key-concepts.mdx, generative-creative.mdx, specification.mdx, implementing-creative-agents.mdx, asset-types.mdx so readers landing from search have a signpost back to v2. - asset-types.mdx updated for v2 with asset_group_id framing, full v2 asset_type table including brief, catalog, zip, markdown, webhook, object types. Validation: schema tests, example tests, v2 fixture tests all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The migration doc was sales-agent-centric — creative agents (Flashtalking, AudioStack, Pencil, AdCreative.ai-shaped tools) got 3 short bullets. v2 reshapes their path enough to warrant a real walkthrough. Adds: - A what-changes table covering format catalog publishing, authoring, discovery, build_creative contract, production-source declaration, tracking integration, and hosting of produced bytes. - Concrete worked example for an ad-server-shaped creative agent (Flashtalking) showing how 30+ named formats collapse to a smaller supported_formats set keyed by canonical format_kind + capability_id + Flashtalking-specific platform_extensions for pixel IDs and viewability. - Concrete worked example for a transformation-shaped creative agent (AudioStack) showing two distinct capabilities (brief-to-audio vs script-to-audio) declared as separate supported_formats entries sharing format_kind: audio_hosted but with different audio_source values (seller_pre_rendered_from_brief vs agent_synthesized). capability_id disambiguates which capability the buyer is invoking on build_creative. - Server-side hooks specific to creative agents: supported_formats as public contract, synthesis_nondeterministic implying a QA-loop obligation, provenance_required requiring C2PA attestation. - Migration timing table: keep v1 list_creative_formats through 4.x; add supported_formats anytime in 3.1+ (additive); stop publishing new v1 named formats when 80% of your buyers read supported_formats; drop list_creative_formats coordinated with the v1 deprecation calendar (2027-Q4 / 2029-Q1). Closes the gap surfaced by review of the third-party-creative-agent worked example in v2-overview — that example showed the BUYER flow but didn't tell creative agents what THEY do to migrate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cab-scenes-delivery # Conflicts: # static/schemas/source/core/creative-asset.json # static/schemas/source/core/creative-manifest.json # static/schemas/source/creative/list-creatives-response.json
This was referenced Apr 30, 2026
… inline normative fixes Six inline fixes from the SDK-team critical review (out of nine raised; #4 and #5 filed as follow-ups): - IR1 v2→v1 projection: add `v2_only` boolean on ProductFormatDeclaration (required `true` when `format_kind: "custom"`). Producer + consumer divergence rules normative in canonical-formats.mdx. No synthetic v1 format_id namespace — explicit v2_only is the marker. - IR2 non-projectable v1: SDKs MUST emit structured warning on resolution failure (carrying format_id, product_id, failure reason). Prevents silent v2-only inventory shrinkage. - IR3 format_schema fetch contract: https-only, hard-fail on digest mismatch, ≤5s timeout, $ref sandbox (same-origin / AAO mirror / intra-doc; no file://; depth ≤8), graceful 404 degradation, invalid-schema hard-fail. In both schema description and canonical-formats.mdx. - IR6 codegen vs runtime: doc callout that generated TS/Pydantic types lose if/then narrowing on format_kind:custom + result_kind; Ajv runtime validator is the gate. - IR8 agent_placement: explicit 3.2-track stamp — tracking macro/postback/dedup intentionally underspecified for 3.1; adopters SHOULD ship as runtime_status: preview or declared_only. - IR9 migration math: realistic-coverage paragraph (15 registry entries, ~76% projectable with seller/registry action, 71+ v1-only out of gate, dual-read realistic through 3.3). Negative fixtures expanded: format_kind:custom now rejects without v2_only:true (added 2 new fixtures, total 13 passing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 16, 2026
…on ProductFormatDeclaration Field name should follow the canonical-formats naming convention established by the v2 → canonical-formats rename. v2_only perpetuated the v2 terminology that we agreed to retire from machine-readable identifiers (path: file paths, field names, registry identifiers use canonical-formats; v1↔v2 contrast remains only as narrative shorthand inside schema descriptions). Updates field name + schema description + if/then required-list + example in product-format-declaration.json; mirrors the rename in canonical-formats.mdx, negative-fixture test, and changeset. All 13 positive + 13 negative fixtures still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-level, security, narrowing, auto-promote-stable Triaged from 5 expert reviews + a second SDK-implementor review. 4 blocks of inline fixes; 3 follow-ups filed (#4591 scope extended, #4592, #4599). Block A — security hardening on format_schema fetch: - SSRF deny-list (RFC 1918, loopback, link-local, CGNAT, RFC 6761 names; cloud metadata IPs); DNS rebinding defense via IP pinning - No HTTP redirects; 1 MiB response cap; schema-compile DoS controls (keyword count, $ref count, regex timeout, validation budget) - Digest format pinned (sha256: + 64 lowercase hex); $ref sandbox normalized per RFC 3986 - Transport rules apply to BOTH format_schema (load-bearing) AND platform_extensions (informational); platform-extension-ref.json uri pattern: ^https:// Block B — wire-level load-bearing concerns: - B1: per-slot consumed_for_production boolean on _base.json#slots — dispatch hint for build_creative and v1↔v2 translators - B2: SHOULD-warn surfaces via errors[] envelope (not logger-only); 2 new error codes (FORMAT_PROJECTION_FAILED, FORMAT_DECLARATION_DIVERGENT) - B3: open-enum guidance on canonical-format-kind.json — consumers MUST treat as open; unknown values retained + treated as runtime_status: declared_only - B4: validate_input request renamed to discriminated targets[] mirroring response shape; eliminates wire-shape collision with Product.format_ids Block C — auto-promote-stable + correctness + narrowing semantics: - C1: 6 canonicals to status: stable at GA (image, display_tag, video_hosted, video_vast, audio_hosted, audio_daast — Track-A v1-translatable); 5 stay preview (html5, image_carousel, sponsored_placement, responsive_creative, agent_placement) - C2: negative-test AJV now uses discriminator: true matching positive suite - C3: new format_kind: custom fixture (nytimes_homepage_takeover_custom.json) for the riskiest oneOf branch - C5: positive controls for canonical_formats_only on non-custom branches - C6 (N1): formal "narrows" definition — parameter-by-parameter subsumption rules for dual-emission divergence detection - C7 (N3): canonical_parameters drift contract — SDKs SHOULD lint-time check, producers SHOULD prefer derived over authored - C8 (N7): asset_group_id alias collision precedence — declaration order authoritative, collisions surfaced via FORMAT_PROJECTION_FAILED - C9 (N8): declared_only SHOULD-filter by default on buyer SDKs (opt-in to surface) Block D — docs structural pass (selective): - TL;DR block at top of canonical-formats.mdx — 6/11 stable at GA, 71+ v1-only out of gate, Phase 4 codegen is gating dep - Codegen-vs-runtime promoted from H3-buried to its own H2 - "canonical formats" used as primary body term; v1↔v2 reserved for schema-description shorthand Tests: 14 positive + 15 negative fixtures all green under discriminator: true strict mode. Follow-ups: #4591 (storyboard matrix scope extended), #4592 (sponsored_placement adapter docs), #4599 (synthetic v1 format_id docs) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…4599) Plugs a coherence hole on PR #3307: the spec rejected an aao-synth/* synthetic namespace but didn't say what a v2-native seller actually does when they need to emit format_ids for v1-only buyers. Adopters would hit this on day 1. Two acceptable answers, both documented inline: 1. Default — canonical_formats_only: true, omit from format_ids. Product is functionally invisible to v1-only buyers but the v1 surface stays clean. 2. Synthesize seller-scoped IDs like acme.adcp/canonical_image_300x250 when the seller wants v1 reach. Constraints normative: - MUST be seller-scoped (never aao-synth/* or cross-seller namespace) - MUST be declared in the seller's published format catalog - Buyers MUST NOT pattern-match on the convention (catalog is authoritative) - Synthetic id + format_options entry must satisfy dual-emission narrowing Closes #4599 (filed earlier this session as a follow-up; small enough to roll in rather than carry as a separate PR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…itions Two CI failures on PR #3307: 1. Changeset CLI rejected both canonical-formats changesets because they declared package `@adcontextprotocol/adcp` which is not the workspace name. The actual package.json `name` is `adcontextprotocol`. Both changesets fixed. 2. error-code-drift lint rejected FORMAT_PROJECTION_FAILED and FORMAT_DECLARATION_DIVERGENT (added on this PR) because they're present in source but absent from origin/3.0.x and had no dispositions entry. Added both with `held-for-next-minor` + target_version=3.1 — this is wire change territory for the canonical-formats v1↔v2 surface; 3.0.x stays clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…o BrandRef Addresses nas review on PR #3307: brand_kit_override on creative-manifest.json duplicated a pattern BrandRef already establishes for inline overrides (industries, data_subject_contestation). Move the field to BrandRef so all per-call brand context lives in one place, eliminates the parallel vocabulary on CreativeManifest, and adopters don't have to learn two override patterns. Shape changes: - brand-ref.json: add brand_kit_override property (logo, colors, voice, tagline) next to industries and data_subject_contestation. Same precedence rules: brand.json is canonical, inline override takes precedence for fields present. - creative-manifest.json: drop brand_kit_override; the brand description points at BrandRef's inline override. - docs (canonical-formats.mdx + canonical-formats-migration.mdx): updated the worked example to show brand_kit_override nested inside brand: { domain, brand_kit_override }. Out-of-subset fields (voice_attributes, prohibited_terms, etc.) still require publishing a different brand.json — the inline override is intentionally narrow to a small high-traffic subset. No fixture changes needed (no existing fixture used the old shape). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tor review Addresses gaps surfaced by a third SDK-implementor review (Python/Pydantic angle). Four small normative additions; three reviewer items filed separately as follow-ups. 1. Platform-extension collision precedence (_base.json#platform_extensions). When two platform_extensions[] entries extend the same target with overlapping field names, array order is authoritative — later entries override earlier ones per-field. SDKs surface via errors[] with structured code. Same flavor as the asset_group_id alias collision rule we just landed. 2. brand_kit_override field-level merge (brand-ref.json#brand_kit_override). Now explicit: merge is field-level, not whole-object replacement. Composite fields (colors.primary, colors.secondary, colors.accent) merge one level deeper. SDKs MUST NOT treat a present override.colors as wiping brand.json's colors block entirely; only per-slot fields present in the override take precedence. 3. validate_input scope clarification (validate-input-result.json). validate_input validates manifest STRUCTURE, not output rendering. For composition_model: algorithmic formats (responsive_creative, agent_placement), the asset pool is still structurally validatable (counts/sizes/lengths) → validated_pass / validated_fail. unvalidatable_nondeterministic is reserved for synthesis_nondeterministic: true cases where the production pipeline itself can't be evaluated upfront. Buyers calling validate_input on an algorithmic-composition target SHOULD expect a structural verdict, not a rendering preview. 4. SDK canonical-catalog version negotiation (get-adcp-capabilities-response). Optional canonical_catalog_version (semver-shaped) on capabilities.creative. Lets codegenned SDKs detect canonical-catalog skew between their generated types and the seller's actual support. Pairs with the open-enum semantics on canonical-format-kind.json (B3) — skew is soft-warn, not hard error. All tests green: 7 schema + 14 positive fixtures + 15 negative fixtures + error-code drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eprecate canonical_parameters Flips the v1↔v2 link direction per Brian's design pass: v2 declaration is the source of truth for shape; the v1 named format is just the legacy identifier the v2 declaration links back to via v1_format_ref. Eliminates the canonical_parameters drift surface (v1 file mirroring the v2 shape) by removing the parallel declaration entirely. Schema changes: - ProductFormatDeclaration: new optional v1_format_ref ($ref FormatId). - Mutual exclusion with canonical_formats_only:true enforced via allOf/not. - format_kind:"custom" relaxed: requires format_shape + format_schema AND EITHER canonical_formats_only:true OR v1_format_ref. One or the other. - format.json#canonical_parameters: marked deprecated:true. Retained for 3.1 backward-compat (SDKs MUST honor when present); removed at 4.0. New code SHOULD migrate to v1_format_ref on the v2 side. Resolution order updated (v1-canonical-mapping.json): 1. v2 declaration with v1_format_ref pointing at this v1 format (authoritative) 2. v1 file's explicit canonical field (seller-asserted) 3. format_id_glob registry match 4. structural registry match 5. fail-closed with FORMAT_PROJECTION_FAILED Negative fixtures expanded: 4 new cases (v1_format_ref on Track-A canonical accepted; v1_format_ref on custom accepted; canonical_formats_only:true AND v1_format_ref rejected; custom with neither rejected). 19 negative + 14 positive fixtures all passing. Docs: new "v2 → v1 linking via v1_format_ref" subsection in canonical-formats.mdx. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mental; drop tracking_extensions; rename *_source to asset_source; pin version patterns Big architectural cleanup per Brian's design pass on the punch list (items B/C/D/E/I). D — collapse two stability axes to one experimental flag: - Drop _base.json#status enum (was: stable | preview | deprecated) - Drop ProductFormatDeclaration#runtime_status enum (was: stable | preview | declared_only) - Add _base.json#experimental boolean (default false) - Add _base.json#deprecated boolean (kept separate — different concept) - Add ProductFormatDeclaration#experimental boolean (independent of canonical-level) - Drop the Track A/B GA promotion rubric prose entirely - Same semantic as 'experimental' on protocols: 'may not work, have a fallback' E — mark four canonicals experimental: true: - sponsored_placement (4 adapter contracts) - responsive_creative (algorithmic, no v1 equivalent) - agent_placement (3.2-track, tracking model underspecified) - custom (handled via per-declaration experimental flag) - The 6 IAB/VAST/DAAST re-encodings plus html5 and image_carousel ship non-experimental B — drop tracking_extensions field: - _base.json#tracking_extensions removed - Buyers filter platform_extensions via extensions[uri].extends === 'tracking' - Single source of truth; no parallel array C — rename *_source fields to single asset_source: - audio_source / image_source / video_source → asset_source (shared 5-value enum) - buyer_audio_acceptance / buyer_image_acceptance / buyer_video_acceptance → buyer_asset_acceptance - item_production_model on sponsored_placement keeps its name (semantically different) - but now described as 'sharing the same enum' as asset_source - Cross-canonical leak fixed (audio_source on image canonical no longer accepted in practice) - Fixtures migrated (the_daily_30s_host_read.json, veo_generative_video_15s.json) I — pin semver patterns: - _base.json#since_version: ^\d+\.\d+$ - _base.json#migration_target_version: ^\d+\.\d+$ - Reject patch precision and placeholders like 'unknown' (omit field instead) Docs: glossary entries updated; replaced 'Status / runtime_status' sections with single 'experimental' section; tracking_extensions references replaced with platform_extensions filter pattern. Tests: 14 positive fixtures + 19 negative fixtures all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cription/cta
F (4c) — slot max_chars/max_size_kb mutex enforced via if/then on _base.json:
- text/markdown/brief asset_types: max_chars allowed, max_size_kb rejected
- image/video/audio/zip asset_types: max_size_kb allowed, max_chars rejected
- url/catalog/html/css/javascript/webhook/daast/vast/card/object: both rejected
- Field descriptions clarified to call out the mutex
- Producers can no longer ship {asset_type: text, max_chars: 100, max_size_kb: 50} — downstream picks-arbitrarily bug eliminated
H — card-asset.json expanded to cover Pinterest/Meta/TikTok/AI-surface multi-card patterns:
- Added `description: string` (longer body copy, 100-500 chars typical) — Pinterest pin description, Meta carousel description, AI-result body
- Added `cta: string` (per-card call-to-action label, MUST be in parent format's cta_values) — Meta and TikTok per-card CTA support
- image_carousel.json: added `card_description_max_chars` parameter (parallel to existing `card_headline_max_chars`)
- Description prose updated to call out Meta/Pinterest/Snap/TikTok/AI-surface coverage
- platform_extensions remains the home for genuinely platform-specific fields (Pinterest rich-pin, Snap price tags, source attribution)
Tests: 14 positive + 19 negative all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er tighten, narrowing-check normative
Triaged from three expert reviews (code-reviewer, ad-tech-protocol, adtech-product):
1. Rename stragglers — audio_source/image_source/video_source/buyer_*_acceptance still in:
- product-format-declaration.json (description prose + audio_hosted example)
- audio_hosted.json (description + slots default description)
- the_daily_30s_host_read fixture (description prose)
- canonical-formats.mdx (production-source bullet list, generative DSP example)
- canonical-formats-migration.mdx (multiple rows + prose)
Schema accepted via additionalProperties:true but adopters reading the spec saw old names — worst kind of drift.
2. sponsored_placement.item_production_model now correctly says "4-value subset of asset_source's 5-value enum" instead of "shares the same enum" — the previous claim was structurally wrong (drops publisher_host_recorded). Glossary entry updated accordingly.
3. Glossary table had a sed-mishap "asset_source / asset_source" duplicate header — cleaned up.
4. tracking_extensions removed from product-format-declaration.json description prose (had been dropped from the schema but a reference survived in the field description).
5. since_version / migration_target_version pattern tightened from `^\d+\.\d+$` to `^[1-9]\d*\.(0|[1-9]\d*)$` — rejects `03.1`, `3.01`, leading-zero variants. MAJOR can't be 0 since AdCP is 3.x.
6. v1-canonical-mapping.json resolution-order step 1 now normatively requires SDKs SHOULD run the narrows check between the v2 declaration and the referenced v1 format's requirements; surfaces FORMAT_DECLARATION_DIVERGENT on conflict. Was prose-only ('Narrows — formal definition' section), now it's the registry step. Closes the gap that v1_format_ref was a hint, not a contract.
7. scripts/oneof-discriminators.baseline.json updated to accept 6 new undiscriminated oneOfs (all structurally distinguishable per code-reviewer: 4 single-vs-array assets patterns, 2 format_id-vs-format_kind mutexes, 1 v1_pattern variant).
All 14 positive + 19 negative fixtures green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lint surface + Phase 4 commitment Two changes from the 4th SDK-implementor review's actionable items: 1. **Soft-warn surface clarified** — FORMAT_PROJECTION_FAILED and FORMAT_DECLARATION_DIVERGENT are now described as 'either side MAY emit; SDKs detecting on consumption SHOULD surface via lint-output channel OR errors[] augmentation, SDK's choice.' Previous wording 'MUST surface via errors[]' closed the door on the cleaner SDK-lint-output pattern. Both surfaces remain non-logger-only — the operator-actionable requirement is preserved. error-code.json + v1-canonical-mapping.json resolution-order step 5 + canonical-formats.mdx all updated consistently. 2. **Phase 4 commitment tightened** — was 'blocking adoption' (vague), now 'MUST ship alongside 3.1.0 beta, not after.' Expanded scope: SDK codegen + flatten wrapper + platform_extensions URI+digest fetch+cache helper + typed accessors for slot-level scheduling hints. Closes the risk that walled gardens never move because list_creative_formats reality skews while Phase 4 trails. Filed as follow-up (not inline): - platform_extensions fetch+cache reference helper as part of Phase 4 work (rolled into the Phase 4 row) - typed accessor for production_window_business_days etc. (rolled into Phase 4 row) Stale-draft items (key-as-discriminator vs string-discriminator, format_ids XOR oneOf) not addressed — reviewer was on a pre-#3765 draft. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 17, 2026
…RMAT_PROJECTION_FAILED + FORMAT_DECLARATION_DIVERGENT Per 4th implementor review's nit on #6: "both surfaces" phrasing was ambiguous about whose lint channel a downstream consumer can subscribe to (it's whichever SDK processed the response — lint output isn't a wire artifact and doesn't propagate to subsequent consumers). Explicit normative paragraph added to both error codes' enumDescriptions: - Wire errors[] reflects only what the producer self-detected - Consumer-SDK lint output is not visible to downstream consumers of the original wire response - Downstream consumers needing full visibility MUST subscribe to the lint channel of the SDK that processed the response - Absence in errors[] does NOT imply absence of the underlying condition Closes the wire-boundary ambiguity that the previous "either side MAY emit; SDK MAY use lint" prose left open. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erify Product 'neither' rejected From 5th SDK-implementor review. #1 — format_shape promotion migration story (real concern): When a format_shape (e.g., multi_placement_takeover) graduates from custom to a first-class canonical, any client code branching on `format_kind == "custom"` silently stops matching that publisher's products. Spec now carries a normative migration contract on format-shape-vocabulary.json and canonical-formats.mdx: 1. Transition window ≥90 days where sellers MAY emit BOTH shapes (old custom + new canonical) on the same product's format_options[] 2. Consumer SDKs SHOULD emit a structured deprecation warning via lint channel when they see format_kind:"custom" with a promoted format_shape; payload: { format_shape, promoted_to, promotion_release, transition_end } 3. Registry's `promotion_status` field lifecycle: 'tracking — see adcp#3666' → 'promoted to <format_kind> in <version>; transition ends <date>' 4. Post-transition: sellers SHOULD drop the legacy custom declaration; buyers MAY then assume custom == long-tail/non-promoted Without this contract every promotion event silently breaks adopter code. With it, the deprecation warning is the early signal during transition. #2 — Product "neither format_ids nor format_options" case: verified the schema's anyOf already rejects this case (validate fails with 'must have required property format_ids' / 'must match a schema in anyOf'). No fix needed; existing prose on product.json's format_ids field already says 'MUST carry format_ids, format_options, or BOTH; at least one is required.' Stale-draft items in the review (key-as-discriminator, runtime_status:declared_only) not addressed — reviewer was on pre-#3765 + pre-D-block drafts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tch format-id wire pattern Dragon report bug #1: format-id.json id pattern is ^[a-zA-Z0-9_-]+$ (no slashes), but v1-canonical-mapping.json shipped 8 named-glob entries with 'iab/X' style prefixes. None could ever match a wire-valid v1 format_id — 8 of 15 registry entries were dead code. Only the 7 structural entries actually triggered. Fix: rename all 'iab/X' globs to 'iab_X' (underscore namespace). Updated 8 entries: mrec_300x250, leaderboard_728x90, wide_skyscraper_160x600, billboard_970x250, half_page_300x600, mobile_banner_320x50, mobile_interstitial_320x480, large_rectangle_336x280. Registry description now spells out the namespace convention: prefix with source organization separated by underscore (e.g., iab_mrec_300x250, meta_reels, daast_audio_30s); the format_id wire pattern doesn't permit forward slashes. Bugs #2 (SDK adcp_version emit) and #3 (PR body says oneOf / 'format') are not spec-scope — #2 is an SDK fix in adcp-client-python, #3 is a GitHub PR description edit. Both noted; this commit covers only the spec bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 17, 2026
…2260) (#4628) * docs(creative): creatives outlast campaigns + signal state changes (#2260) Replace the 3.0 "retention seller-defined" hedge with a normative rule: library creatives MUST persist independently of the buys that reference them, regardless of submission path (sync_creatives, inline, or platform-native). Sellers MAY archive unassigned creatives for inactivity, post-flight expiry, or storage policy, but MUST signal any state change on a buyer-synced creative — via impairment on the buy when live assignments exist, via a creative state-change notification otherwise (#2261 owns transport). Widen the creative-status archived enumDescription to acknowledge seller-initiated archive and require the state-change signal. Additive description-only change; no new enum values, no new fields. Frame a library creative as the bundle of buyer-supplied inputs (assets, brief, brand+catalog pointers, or any combination), sidestepping the generative/non-generative fuzziness. Variant addressability is a format-level concern handled by #3305/#3307. Closes #2260. Refs #2261, #3305, #3307. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(creative): address expert review on retention contract (#2260) Two blockers from protocol review: 1. Replace "MUST emit via buyer's registered notification channel" with the surface that exists today — list_creatives must reflect the new status on the next read. Aligns with the snapshot-and-log contract, which already names list_creatives as the conformant signal for resource state changes outside an active buy. #2261 owns the future push channel; promoted to SHOULD-additionally once it ships. 2. Add approved → archived (seller-initiated) edge to the creative state machine, scoped to creatives without active package assignments. Sellers MUST NOT seller-archive a creative with active assignments — the approved → rejected revocation path with an impairment is the only conformant route when active serving is involved. Friction note from product review: add explicit "library can be a thin view over per-buy storage" line for CTV/podcast vendors whose ad server has no library object distinct from per-buy attachment. Docs nits: tighten archived enumDescription wording; constrain seller-archive to no-active-assignments; update changeset to reflect the state-machine edit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
…ace for SDK-detected advisories Per implementor review's option 3 (decide so everyone does it the same way): collapse the two-surface choice (lint-channel OR errors[] augmentation) down to a single mandate. errors[] augmentation wins because the multi-hop agent network needs warnings to propagate across SDK boundaries via the wire response. Lint-output channels are local to whoever processed and die at the SDK boundary — wrong abstraction for a federated network. Schema additions (core/error.json): new 'source' field (enum: producer | sdk) on each error entry distinguishes seller-emitted from SDK-augmented; new 'sdk_id' field (format: <package>@<version>) identifies which intermediate processor inserted an entry. SDK-augmented entries MUST carry both fields. Multi-hop deduplication rule: each hop SHOULD dedup by (code, field) rather than re-emit. Updated FORMAT_PROJECTION_FAILED and FORMAT_DECLARATION_DIVERGENT prose in error-code.json + the v1-canonical-mapping resolution-order step 5 + canonical-formats.mdx Dual-emission section. All four now consistently mandate errors[] augmentation; lint-output explicitly NOT acceptable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…type Five spec-level fixes from the v2 SDK prototype work: 1. v1_translatable boolean on _base.json (default true). Override false on the 4 inherently-v2 canonicals: image_carousel, sponsored_placement, responsive_creative, agent_placement. Lets SDKs distinguish 'no v1 path possible' (structural) from 'registry not covered yet' (correctable). SDKs MUST NOT emit FORMAT_PROJECTION_FAILED on v1_translatable:false canonicals. 2. New error code FORMAT_DECLARATION_V1_AMBIGUOUS for the family-only registry-match case (e.g., video_vast has a structural entry but no invertible literal). 7 of 13 fixtures fall in this bucket. SDKs MUST surface this rather than synthesize an arbitrary v1 format_id. 3. Direction-of-truth: v1-canonical-mapping.json description now explicit that the registry is authoritative for v1 → v2 only. v2 → v1 projection MUST rely on v1_format_ref; SDKs MUST NOT synthesize v1 format_ids by inverting structural matches. Inter-SDK divergence on synthetic agent_url choices was the bug. 4. Resolution order gains a 5th step (ambiguous family) before fail-closed, surfacing FORMAT_DECLARATION_V1_AMBIGUOUS. 5. Reference fixtures now demonstrate v1_format_ref: nytimes_homepage_mrec → iab_mrec_300x250, triton_daast_audio_30s → daast_audio_30s_v1_1. Spec exemplars now model the dual-emission pattern the spec body recommends. Refs: adcp-client #1815 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_format_ref convention Brian's three-point follow-up to the 'why don't more project cleanly' observation: 1. Annotated 4 more fixtures with v1_format_ref (was 2/13, now 6/13 with explicit v1↔v2 pairing): gam_3p_display_tag → iab_3p_tag_300x250, nytimes_homepage_html5 → iab_html5_300x250, youtube_vast_preroll → iab_vast_preroll_4x, meta_reels_us → meta_reels. Plus migrated the existing 2 (nytimes_homepage_mrec, triton_daast_audio_30s) from publisher-scoped agent_url to AAO-hosted. 2. Added 5 new literal registry entries (iab_3p_tag_300x250, iab_html5_300x250, iab_vast_preroll_4x, iab_daast_audio_30s, meta_reels) so SDKs have invertible lookups for these canonicals beyond just the IAB image sizes. SDK best-effort v2→v1 inversion now works for display_tag, html5, video_vast, audio_daast, and Meta-platform video_hosted in addition to image. 3. Normative AAO-hosted convention on v1_format_ref: IAB-standard formats SHOULD point at https://creative.adcontextprotocol.org with the registry-published id. Platform-specific formats point at the platform's agent_url. Seller-bespoke formats point at the seller. Converges the v1-wire namespace for IAB shapes — every seller's 300x250 MREC points at the same {agent_url, id} pair, so v1-only buyer allowlists work uniformly across publishers. Without this, per-publisher namespace sprawl reproduces exactly the fragmentation canonical-formats was designed to eliminate. Schema validation: 14 positive + 19 negative fixtures still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…aming The 13 literal registry entries I'd authored as 'iab_*' don't match what creative.adcontextprotocol.org actually serves. The catalog (server/src/creative-agent/reference-formats.json) uses display_<dim>_<delivery> naming (display_300x250_image, display_300x250_html, video_vast_30s, audio_standard_30s, etc.). My registry IDs were orphans that no wire traffic would ever match. Part A — registry renames (v1-canonical-mapping.json): - 8 iab_*_image → display_<dim>_image (mrec, leaderboard, wide_skyscraper, billboard, half_page, mobile_banner, large_rectangle) - iab_html5_300x250 → display_300x250_html, added display_728x90_html - iab_3p_tag_300x250 dropped (no standalone catalog entry); display_js generic added for the third-party-tag pattern - iab_vast_preroll_4x dropped; video_vast + video_vast_30s added (matching catalog) - iab_daast_audio_30s dropped (no DAAST entry in catalog yet) - iab_mobile_interstitial_320x480 dropped (no catalog entry) - audio_standard_15s/30s/60s + video_standard_15s/30s added (matching catalog) - meta_reels kept (intentional non-AAO platform format on meta.adcp) Part B — fixture v1_format_ref updates: - nytimes_homepage_mrec → display_300x250_image - nytimes_homepage_html5 → display_300x250_html - gam_3p_display_tag → display_js - youtube_vast_preroll → video_vast_30s - triton_daast_audio_30s: dropped v1_format_ref, set canonical_formats_only:true (no DAAST entry in catalog) - meta_reels_us kept as-is (platform-specific) - agent_url corrected to https://creative.adcontextprotocol.org/ (trailing slash, matches catalog publish format) Part C — AAO catalog (reference-formats.json): - Added canonical field to 32 catalog entries (image/html5/display_tag/video_hosted/video_vast/audio_hosted/sponsored_placement) - Catalog now fires resolution-order step 2 (seller-asserted canonical) directly; v2-aware SDKs read v1↔v2 projection inline without registry round-trip - Native, DOOH, broadcast, and card-scaffolding entries unannotated — no clean canonical mapping yet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ith RFC 2606 .example Brian caught it: meta.adcp / nytimes.adcp / flashtalking.adcp / tiktok.adcp / triton.adcp / youtube.adcp / google.adcp / openai.adcp / audiostack.adcp / yourpub.adcp don't exist. These were placeholders dressed up as real URIs in examples and schema descriptions. Replaced all 17 PR-touched files with RFC 2606 example domains (.example). Files updated: 7 fixture files, 5 schema files, 2 mdx docs, 2 protocol files, 1 docs/building file. All .adcp references converted to .example via sed. No semantic change — the URIs were illustrative placeholders in either case; the .example convention is the honest one. Closed-platform AAO-mirror pattern (https://mirror.adcontextprotocol.org/translated/<platform>/) remains documented in canonical-formats.mdx Platform Extensions section as the normative production handling for walled gardens — examples just use .example placeholders rather than implying we run a real translation service today. Out of scope (pre-existing on main): static/test-vectors/transport-error-mapping.json, static/compliance/source/universal/error-compliance.yaml, docs/protocol/get_adcp_capabilities.mdx, static/schemas/source/{media-buy,creative}/list-creative-formats-response.json — these have .adcp references that predate this PR. Tracking separately. Tests: 14 positive + 19 negative fixtures all still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
to adcontextprotocol/adcp-client
that referenced
this pull request
May 17, 2026
CI doesn't sync the 3.1.0-beta.0 cache — it's gitignored and only
exists in workspaces that have manually built the unreleased spec PR.
The projection layer's registry + canonical-properties loaders depend
on schemas/cache/3.1.0-beta.0/, so attempting to project without it
throws and the tests fail.
Skip cleanly with a clear reason via `{ skip: SKIP_REASON }` on every
describe. Locally (cache present) all 25 tests still pass; in CI all
5 suites report `# SKIP requires schemas/cache/3.1.0-beta.0/`.
Same pattern as `adcp-version-release-precision.test.js`'s wire-
prerelease guard. Will start running in CI when upstream
adcontextprotocol/adcp#3307 merges and `npm run sync-schemas` can
pull a real 3.1.0 tarball.
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.
Status: Preview branch implementing RFC #3305 end-to-end. Not for merge until the 3.1.0 beta cycle opens — merging earlier would force a 3.1.0 release before the rest of v2 work is ready. Adopters and upstream implementors can check this branch out and build against the v2 spec.
Scope: Phase 1 (foundational primitives) + Phase 2 (canonical format catalog, product format declarations, validate_input tool, supporting schemas) + Phase 3 (migration guide, reference fixtures, fixture-validation test). Backwards-compatible additions only — no v1 producers or consumers are affected.
Read this first
docs/creative/v2-overview.mdx— adopter-facing walkthrough with Meta Reels, IAB MREC + sibling HTML5, podcast host-read, and Amazon SP worked examplesdocs/creative/v2-migration.mdx— concrete v1 → v2 migration paths per adopter typeValidate against the spec yourself
Five fully-valid reference Product fixtures at
static/examples/products/v2/pass strict schema validation:Fixtures:
meta_reels_us.json—video_hosted(vertical), Meta-specific extensionsnytimes_homepage_mrec.json—image(IAB MREC 300×250)nytimes_homepage_html5.json—html5(sibling product, different canonical because different tracking model)the_daily_30s_host_read.json—audio_hostedwith inlineinputs(script + brand)amazon_sponsored_products.json—sponsored_placement(catalog-driven, ASIN-keyed)What landed in this PR
Foundations (Phase 1)
asset-group-vocabulary.json— canonicalasset_group_idregistry (7 existing catalog entries + 12 audit-driven additions). Aliases expanded for headlines, descriptions, images_*, logo, video, audio. Canonicalizeslanding_page_urlover 6 v1 alias names.scenes.json— typed scene-by-scene structure for generative video inputs.zip-asset.json— first-class asset type for HTML5 banner bundles.docs/creative/channels/{video,audio}.mdxcorrecting VAST asset_type.Canonical format catalog (Phase 2)
11 canonical format definitions at
static/schemas/source/formats/canonical/:imagehtml5display_tagimage_carouselvideo_hostedvideo_vastaudio_hostedaudio_daastsponsored_placementresponsive_creativeagent_placementsi_chat.Each canonical bakes in its tracking model. Format-keyed-by-name structure (no
canonicaldiscriminator field)._base.jsondefines shared parameters:composition_model,provenance_required,platform_extensions,tracking_extensions,inputs,production_window_business_days.Product/manifest declarations (Phase 2)
product-format-declaration.json— keyed by canonical format name, exactly one key per declarationproduct.json— adds optionalformat_optionsarray (v2 inline declarations);anyOfpermits dual emission — products MAY carryformat_ids,format_options, or BOTH (at least one required per RFC #3305 amendment: allow bothformat_idsandformaton products; decouple wire shape from AdCP-Version #3765 fold-in)creative-manifest.json— adds optionalbrand(using canonicalBrandRef) andbrand_kit_overrideformat.jsonslot declarations — gain optionalasset_group_idfield (v1↔v2 migration bridge)Tools (Phase 2)
validate-input-{request,response,result}.json— buyer dry-run primitivecreative.supported_formatsfield added toget_adcp_capabilitiesresponse — uniform replacement forlist_creative_formatsregardless of agent roleplatform-extension-ref.json— URI + content-digest reference for platform extensionsAdopter docs (Phase 2 + Phase 3)
docs/creative/v2-overview.mdx— full architecture walkthrough with three-layer model, 11 canonicals, three worked examples (Meta Reels / NYTimes IAB MREC + sibling HTML5 / podcast host-read), two flows for inputs-driven creative, preview as universal verification surface, brand identity via BrandRef.docs/creative/v2-migration.mdx(new) — concrete v1 → v2 migration paths: side-by-side translations, slot mapping table, generative format dissolution, discovery surface migration, adopter paths per role, realistic timelines.Fixtures + fixture-validation test (Phase 3)
static/examples/products/v2/*.json— 5 fully-valid Product reference fixturestests/v2-fixture-validation.test.cjs— validates all fixtures against/schemas/core/product.json; runs vianpm run test:v2-fixturesformat_idsassertion on product.json with the new anyOf(format_ids, format_options) checkWhat's NOT in this PR (deferred or out of scope)
list_creative_formatsshape from v2 product declarations; Phase 4v1 backwards compatibility
format_idas{ agent_url, id }) continue to work through 3.x and 4.xlist_creative_formatsdeprecated but functional; sellers SHOULD provide server-side flatten wrappers through 4.0; removed at 5.0Hold reason
Changeset is
minor(this lands as 3.1.0). Merging now would cut a 3.1.0 release before the rest of v2 work is ready. Branch sits open as a preview adopters can build against; merge timing aligns with the 3.1.0 beta cycle opening.Test plan
npm run build:schemas— clean; 510+ schemas indist/schemas/latest/npm run test:schemas— 7/7npm run test:json-schema— 255/255 MDX JSON blocks validnpm run test:v2-fixtures— 5/5 reference fixtures pass strict Product validationtest:unit,test:test-dynamic-imports,typecheck)🤖 Generated with Claude Code