Skip to content

Commit 1cc7e20

Browse files
bokelleyclaude
andcommitted
feat(creative): custom format_kind + format_shape registry + format_schema URI+digest
Reverses the wrong call from #3666 (which recommended ext-as-vehicle for novel shapes). ext-only puts interesting structure in a free-form bag with no schema, no required fields, no defined semantics — buyer agents can see the blob but can't interpret it reliably, regressing to human-in-the-loop. That breaks the load-bearing claim of v2: buyer agents reason structurally without per-seller integration code. Adds: - canonical-format-kind.json: 'custom' added to the enum (12 values). Description documents that custom requires format_shape + format_schema and points at the promotion queue (#3666). - New /schemas/core/format-shape-vocabulary.json registry. Same pattern as asset-group-vocabulary.json: governance-light entries, non-canonical values valid (soft-warn), promotion to canonical happens when 2+ adopters land + 90 days. Seeded with 9 entries: multi_placement_takeover, roadblock, branded_content, cross_screen_sponsorship, sponsorship_lockup, newsletter_sponsorship, ar_lens, playable, live_event_sponsorship. Each entry carries description, typical_use, tracking_model_hint, promotion_status. - product-format-declaration.json: format_shape (string, references registry) and format_schema (URI+digest, $ref to platform-extension-ref.json) fields. allOf / if/then enforces: when format_kind=custom, format_shape AND format_schema are required; when format_kind=anything-else, both MUST be absent. New 'Custom Format Declaration' branch in the discriminator oneOf. Worked example added (NYTimes Homepage Takeover narrowing multi_placement_takeover with format_schema URI+digest). Buyer agents fetch the schema by uri@digest (immutable per digest, aggressive caching, same mechanic as platform_extensions), validate params and slots against the fetched schema, reason about manifests structurally. No per-seller integration code. ext stays for genuinely experimental shapes that don't even fit a format_shape registry entry — but that's the rare case. The dominant path for novel shapes is custom + format_shape + format_schema. Doc additions: - v2-overview.mdx: 'Custom formats' section between canonicals and asset-group-vocabulary. Explains the mechanism, the three required pieces when format_kind=custom, why custom + format_schema beats ext for agentic-first protocols, the promotion path to canonical. Glossary updated with three new entries. - v2-migration.mdx: 'Shipping a custom format' subsection on sales-agent server-side considerations. Three steps: pick format_shape from registry (or PR a new entry), author a JSON Schema describing your params/slots, host at a stable URI with immutable caching. Open-ecosystem publishers host on their own subdomain; walled-garden sellers route through AAO mirror. Validation: all schema/example/v2-fixture tests green. The worked-example fixture in the schema validates against the schema itself (proves the discriminator + allOf if/then constraints work as intended). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b3c29bc commit 1cc7e20

5 files changed

Lines changed: 202 additions & 5 deletions

File tree

docs/creative/v2-migration.mdx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ Override fields take precedence over `brand.json` for that creative.
234234

235235
### Sales agents (DSPs, SSPs, retail media networks, walled gardens)
236236

237-
1. **Inventory**: enumerate your existing v1 named formats. Confirm each maps to one of the 11 v2 canonicals.
238-
2. **Translate**: for each named format, write a v2 `ProductFormatDeclaration` narrowing the canonical with your platform's parameters.
237+
1. **Inventory**: enumerate your existing v1 named formats. Confirm each maps to one of the 11 v2 canonicals OR to a custom shape (see "Shipping a custom format" below). Composed/coordinated/sponsorship shapes (multi-placement takeover, roadblock, branded content, cross-screen sponsorship, sponsorship lockup, newsletter, AR lens, playable, live event sponsorship) ship as `format_kind: "custom"` with a `format_shape` registry classifier and a `format_schema` URI+digest reference.
238+
2. **Translate**: for each named format, write a v2 `ProductFormatDeclaration` narrowing the canonical with your platform's parameters. For custom shapes, author a JSON Schema describing your format's `params` and `slots`, host it at a stable URI on your subdomain (or via the AAO mirror for walled-garden sellers), and reference it from `format_schema`.
239239
3. **Be honest about runtime readiness**: set `runtime_status` on each declaration. `stable` (default) means your runtime fully honors the declared format and production source. `preview` means the basic path works but advanced axes (per-item fan-out under `item_production_model`, brief-driven overrides, advanced `platform_extensions`) may be partial. `declared_only` means the catalog declaration is forward-looking and your runtime does NOT yet implement the path — common during migration when you port v1 catalog declarations forward but haven't wired the new production-source axis yet. Buyers can filter on this; compliance storyboards skip-gate `declared_only` entries gracefully. Upgrade the value as your runtime catches up.
240240
4. **Test**: validate translated declarations against `/schemas/core/product.json` (use the `npm run test:v2-fixtures` pattern).
241241
5. **Publish dual**: keep your v1 named formats and `list_creative_formats` working through 4.x. Add the v2 `format_options` field on products that have it.
@@ -250,6 +250,18 @@ Three concrete hooks v2 introduces that existing seller implementations don't ha
250250
- **`get_products` response gathers extension definitions.** When products carry v2 `format.params.platform_extensions` references, the response SHOULD include the referenced extension definitions in the `extensions` map keyed by `<uri>@sha256:<digest>`. Implementations gather extensions referenced by any product in the response, dedupe by digest, and emit. Buyers cache by URI@digest; subsequent responses MAY omit definitions the buyer already has cached. Trivial when no products use v2 declarations; only kicks in when tenants opt in.
251251
- **`production_window_business_days` on host-read / agent-produced products.** Today most server implementations don't model production turnaround on Products — the field is a v2 addition. Only matters once a tenant ships a v2 host-read or generative-video product (audio_hosted with `audio_source: 'publisher_host_recorded'`, or any product with `synthesis_nondeterministic: true`). Today many of these flows route through hand-trafficked sponsorships and don't surface turnaround over the protocol; v2 makes it declarable.
252252

253+
#### Shipping a custom format
254+
255+
Sellers with creative structures that don't fit the 11 canonicals (multi-placement takeover, roadblock, branded content, cross-screen sponsorship, sponsorship lockup, newsletter sponsorship, AR lens, playable, live event sponsorship) ship via `format_kind: "custom"`. Three pieces:
256+
257+
1. **Pick a `format_shape`** from the [vocabulary registry](https://adcontextprotocol.org/schemas/v3/core/format-shape-vocabulary.json). If your shape isn't there, file a vocabulary PR — adding entries is governance-light, doesn't require a major version bump, and helps the working group track adoption velocity per shape.
258+
2. **Author a JSON Schema** describing your format's `params` and `slots`. The schema's job is to give buyer agents enough structure to validate manifests and reason about what assets you accept, how you track, what the impression contract is. Treat it like authoring a v1 named format file — same level of rigor, just hosted at your URI rather than under AdCP's roof. Industry-shared schemas (e.g., a shared `multi_placement_takeover_v1` schema several publishers converge on) are encouraged and accelerate canonical promotion.
259+
3. **Host the schema at a stable URI** with `Cache-Control: public, max-age=31536000, immutable` and a digest. Open-ecosystem publishers host on their own subdomain (`https://yourpub.adcp/schemas/formats/your_shape_v1`); walled-garden sellers route through the AAO mirror at `https://mirror.adcontextprotocol.org/translated/<vendor>/<shape>` (AAO accepts translation submissions; same hosting / immutability contract). Reference the schema from `format_schema: { uri, digest }` on your `ProductFormatDeclaration`.
260+
261+
Buyer agents fetch the schema by `uri@digest`, cache it (immutable), and validate manifests structurally. **No human-in-the-loop is required for buyer agents to interpret your format** — that's the load-bearing claim and the reason custom + format_schema isn't `ext`. Ext remains for genuinely experimental shapes that don't even fit a `format_shape` entry yet, but that's the rare case.
262+
263+
When 2+ adopters ship the same `format_shape` with substantively similar `format_schema` content for 90+ days, the working group promotes the shape to a first-class canonical (creates `/schemas/formats/canonical/<name>.json`, adds the value to `canonical-format-kind.json`, retires the registry entry). Adopters migrate from `format_kind: "custom"` to `format_kind: "<canonical>"` at that point. The promotion queue is tracked at [adcp#3666](https://github.com/adcontextprotocol/adcp/issues/3666).
264+
253265
### Creative agents (Flashtalking, AudioStack, generative platforms, AI rendering services)
254266

255267
The spec has historically read sales-agent-first. v2 reshapes the creative-agent path enough to warrant its own walkthrough — both for ad-server-shaped creative agents (Flashtalking, Innovid, Sizmek-class) and transformation-shaped creative agents (AudioStack, Pencil, AdCreative.ai-class).

docs/creative/v2-overview.mdx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ v2 collapses today's separate format registry into product-bound declarations. A
3939
| **`brand_kit_override`** | Per-creative override for the case where `brand.json` is missing, stale, or inappropriate. |
4040
| **`fanout_mode`** | On `sponsored_placement`: how items map to delivery — `per_item`, `multi_item_in_creative`, `single_item`. |
4141
| **`item_production_model`** | On `sponsored_placement`: how each per-item creative is produced. Captures multi-output generative (1 brief × N items → N creatives). |
42+
| **`format_kind: "custom"`** | Adopter-defined shape that doesn't fit the 11 canonicals (multi-placement takeover, branded content, AR lens, etc.). Requires `format_shape` (registry classifier) and `format_schema` (URI+digest reference to a fetchable schema). |
43+
| **`format_shape`** | Recognized global pattern from the [format-shape vocabulary registry](https://adcontextprotocol.org/schemas/v3/core/format-shape-vocabulary.json). Required when `format_kind: "custom"`. |
44+
| **`format_schema`** | URI+digest reference to the fetchable schema describing a custom shape's `params` and `slots`. Required when `format_kind: "custom"`. Same hosting model as `platform_extensions`. |
4245

4346
## Architectural shift
4447

@@ -121,6 +124,63 @@ When `*_source` is `buyer_uploaded`, the buyer ships rendered assets and any tra
121124

122125
`vast_tracker` and `daast_tracker` decomposed tracker assets work for both `buyer_uploaded` and seller-rendered sources — when the seller renders, those tracker assets are inputs to the rendered tag, attached to the appropriate VAST/DAAST `<TrackingEvents>` block at production time. When the buyer ships a complete `vast` or `daast` tag, the trackers travel inside the tag.
123126

127+
## Custom formats — shapes the 11 canonicals don't cover
128+
129+
The 11 canonicals cover atomic creative shapes (one image, one video, one display tag, one carousel, one catalog placement, one AI-surface mention). They don't cover composed / coordinated / sponsorship shapes that high-end publishers and broadcast networks sell as headline products: multi-placement takeover, roadblock, branded content, cross-screen sponsorship, sponsorship lockup, newsletter sponsorship, AR lens, playable, live event sponsorship.
130+
131+
These shapes are real ad-industry product types — but they're either multi-canonical compositions (takeover = image + video + display_tag + lockup, sold as a unit) or genuinely novel structures (branded content's editorial-sponsorship production model isn't a composition of the 11). v2 handles them via a structured custom mechanism that buyer agents can reason about, NOT via free-form `ext`.
132+
133+
### The mechanism
134+
135+
```json test=false
136+
{
137+
"format_options": [
138+
{
139+
"format_kind": "custom",
140+
"format_shape": "multi_placement_takeover",
141+
"format_schema": {
142+
"uri": "https://nytimes.adcp/schemas/formats/homepage_takeover_v3",
143+
"digest": "sha256:e1d4f6a9c2b5e8d1f4a7c0b3e6d9f2a5c8b1e4d7f0a3c6b9e2d5f8a1c4b7e0a3"
144+
},
145+
"capability_id": "nytimes_homepage_takeover_premium",
146+
"applies_to_channels": ["display", "olv"],
147+
"params": {
148+
"components": [
149+
{ "placement_type": "homepage_skin", "required": true },
150+
{ "placement_type": "preroll_video", "required": true },
151+
{ "placement_type": "sponsorship_lockup", "required": true }
152+
],
153+
"exclusivity_window_hours": 24
154+
}
155+
}
156+
]
157+
}
158+
```
159+
160+
Three required pieces when `format_kind: "custom"`:
161+
162+
1. **`format_shape`** — recognized global pattern from the [format-shape vocabulary registry](https://adcontextprotocol.org/schemas/v3/core/format-shape-vocabulary.json). Tells buyer agents what kind of pattern they're looking at (`multi_placement_takeover`, `branded_content`, `ar_lens`, etc.). The registry currently lists 9 shapes; non-canonical values are valid (validators MAY soft-warn) so adopters CAN ship a shape that isn't yet in the registry — adding entries is a vocabulary PR, not a major-version bump.
163+
2. **`format_schema`** — URI+digest reference to a fetchable schema describing the shape's actual `params` and `slots`. **Same hosting model as `platform_extensions`**: open-ecosystem publishers host the artifact at the canonical URI on their subdomain; closed-platform / walled-garden shapes resolve through the AAO mirror at `https://mirror.adcontextprotocol.org/translated/...`. Buyer agents fetch by `uri@digest` (immutable per digest, aggressive caching), validate `params` and `slots` against the fetched schema, and reason about manifests structurally.
164+
3. **`params`** — the actual structure, governed by the schema fetched from `format_schema.uri`. AdCP doesn't bake the params shape; the seller's schema does.
165+
166+
### Why custom + format_schema instead of `ext`
167+
168+
A buyer agent calling `get_products` and seeing a format with interesting structure buried in `ext` has no spec-level definition to reason against. There's no schema, no required fields, no defined semantics — the agent can see the blob but can't interpret it reliably. A human has to step in to evaluate whether the format fits the campaign brief, what assets are needed, how it tracks, what the impression contract is, whether the price makes sense.
169+
170+
That breaks the load-bearing claim of v2: **buyer agents can reason structurally without per-seller integration code.** ext-only puts interesting structure in a free-form bag, regressing to human-in-the-loop. Custom + `format_shape` + `format_schema` keeps the agentic-first contract: the shape has a registered classifier, the structure has a fetchable schema, the buyer agent reasons over both. Same caching mechanics buyer agents already have for `platform_extensions`.
171+
172+
`ext` remains for genuinely experimental shapes that don't even fit a `format_shape` entry yet — but that's the rare case, not the default. The dominant path for novel shapes is custom + format_shape + format_schema.
173+
174+
### Promotion to canonical
175+
176+
A `format_shape` entry is promoted to a first-class `format_kind` when:
177+
1. At least 2 production adopters ship it via custom + format_schema
178+
2. 90 consecutive days without a breaking change to the shape adopters converged on
179+
3. The shape has a defined tracking model (which signals fire, which trackers attach, what the impression contract is)
180+
4. The working group opens a per-canonical promotion issue, drafts a canonical schema (`/schemas/formats/canonical/<name>.json`), lands a fixture, and ships in the next minor release
181+
182+
Same governance pattern that produced the 11 canonicals from the v1 audit. The promotion queue lives at [adcp#3666](https://github.com/adcontextprotocol/adcp/issues/3666); current candidates are the 9 entries in the format-shape registry.
183+
124184
## Asset group vocabulary
125185

126186
Format `slots` reference canonical `asset_group_id` values from the [vocabulary registry](https://adcontextprotocol.org/schemas/v3/core/asset-group-vocabulary.json). The current canonical entries:

static/schemas/source/core/canonical-format-kind.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "http://json-schema.org/draft-07/schema#",
33
"$id": "/schemas/core/canonical-format-kind.json",
44
"title": "Canonical Format Kind",
5-
"description": "Discriminator value naming one of the 11 canonical creative formats. Used by `product-format-declaration.json` (the product's inline format declaration), `creative-manifest.json` (the buyer's v2 manifest path), and any other surface that needs to identify which canonical a payload targets. The enum mirrors the `oneOf` branches in `product-format-declaration.json`; keep them in sync.",
5+
"description": "Discriminator value naming one of the 11 canonical creative formats — plus `custom` for adopter-defined shapes that don't fit the canonicals (multi-placement takeover, roadblock, branded content, cross-screen sponsorship, AR lens, etc.). Used by `product-format-declaration.json` (the product's inline format declaration), `creative-manifest.json` (the buyer's v2 manifest path), and any other surface that needs to identify which canonical a payload targets.\n\nWhen `format_kind: \"custom\"`, the declaration MUST also carry `format_shape` (referencing the [format-shape vocabulary registry](/schemas/core/format-shape-vocabulary.json) — recognized global pattern this custom shape is an instance of) and `format_schema` (URI+digest reference to a fetchable schema describing the shape's actual `params` and `slots`). Buyer agents fetch the schema, validate manifests structurally, and reason about manifests without per-seller integration code — same mechanic as `platform_extensions`. See [adcp#3666](https://github.com/adcontextprotocol/adcp/issues/3666) for the canonical promotion queue.\n\nThe canonical enum mirrors the `oneOf` branches in `product-format-declaration.json`; keep them in sync.",
66
"type": "string",
77
"enum": [
88
"image",
@@ -15,6 +15,7 @@
1515
"audio_daast",
1616
"sponsored_placement",
1717
"responsive_creative",
18-
"agent_placement"
18+
"agent_placement",
19+
"custom"
1920
]
2021
}

0 commit comments

Comments
 (0)