Skip to content

RFC #3305 amendment: v1↔v2 canonical format mapping registry + canonical field on v1 format declarations #3767

@bokelley

Description

@bokelley

Type: spec amendment to #3305
Target: 3.1 (lands with v2 creatives)
Companion to: #3765 (dual-emission of format_ids + format)

Problem

#3305 / #3307 establish 11 v2 canonical formats and add asset_group_id on v1 format slot declarations as a v1↔v2 bridge. That's the seed of a translation system, but it's incomplete:

  • No format-level mapping — the slot bridge tells you which v2 canonical slot a v1 slot corresponds to, but not which v2 canonical format the v1 format itself maps to.
  • No registry of well-known mappings — every SDK would have to invent its own table for IAB MREC → image, VAST 4.x → video_vast, DAAST → audio_daast, etc. SDKs will diverge.
  • No fallback story for custom seller formats that don't appear in any registry.

Without a single source of truth for mappings, the dual-emission pattern from #3765 (seller authors once, SDK emits both wire shapes) has nothing to project through, and every SDK reinvents the projection rules with subtle differences.

Proposed change

Three additions, all in the spec repo:

1. Canonical mapping registry (AAO-published)

A new content-digested registry at static/registries/v1-canonical-mapping.json, governed under the same vocabulary-governance rules as asset-group-vocabulary.json (PR + rationale + ≥1 reference adopter, AAO maintainer review, versioned + digested).

Shape:

{
  "version": "1.0.0",
  "digest": "sha256:...",
  "mappings": [
    {
      "v1_pattern": { "format_id_glob": "iab/mrec_300x250" },
      "v2": { "canonical": "image", "parameters": { "width": 300, "height": 250 } }
    },
    {
      "v1_pattern": { "structural": { "asset_types": ["vast"], "vast_versions": [">=4.0"] } },
      "v2": { "canonical": "video_vast", "parameters": { "vast_versions": [">=4.0"] } }
    },
    {
      "v1_pattern": { "format_id_glob": "iab/leaderboard_*" },
      "v2": { "canonical": "image" }
    }
    // ... covering all IAB display sizes, VAST/DAAST versions, common HTML5 banner shapes, audio, etc.
  ]
}

Two match modes:

  • format_id_glob — exact / glob match against format_id. For named-format conventions (IAB sizes, named platform formats, common publisher conventions).
  • structural — match against the format's slot shape and asset types. Catches custom v1 formats that are structurally a standard format under a different name (e.g., a seller's acme_homepage_300x250 is structurally an IAB MREC).

The registry covers the ~76% of v1 formats that fit a small canonical set per the audit in #3305. Initial scope: IAB display sizes, OpenRTB Native 1.2 patterns, VAST 2.x-4.x, DAAST 1.x, common HTML5 banner shapes, common audio shapes. Estimated 50-100 entries.

2. canonical field on v1 format declarations

For custom seller formats not in the registry, the seller declares the mapping inline at the format-declaration level:

{
  "format_id": "acme/sponsored_recipe_card",
  "name": "Sponsored Recipe Card",
  "canonical": "sponsored_placement",        // NEW — names the v2 canonical
  "canonical_parameters": {                   // NEW — optional, narrows the canonical
    "catalog_id_field": "recipe_id"
  },
  "slots": [
    { "name": "headline", "asset_group_id": "headline", ... },     // already in #3307
    { "name": "recipe_image", "asset_group_id": "main_image", ... }
  ]
}

Combined with the slot-level asset_group_id already shipped in #3307, any v1 format declaration with canonical + slot-level asset_group_ids is fully self-describing for v1↔v2 translation. The seller does this once per custom format, not per buyer or per product.

3. Normative SDK projection rules

A new section in the spec (suggest §11.5 in the v2 RFC) defining the mapping resolution order:

Resolving a v1 format to its v2 canonical:
  1. If the format declaration carries `canonical`, use it (seller-declared).
  2. Else, look up the format_id in v1-canonical-mapping.json's `format_id_glob` entries.
  3. Else, attempt structural match against `structural` entries in the registry.
  4. Else, fail closed: surface a validation error
     ("v1 format <id> has no v2 canonical mapping; add `canonical` field
       or file an entry against v1-canonical-mapping.json").

Reverse direction (v2 → v1 demotion for v1 buyers reading a v2 seller) is the inverse projection: same registry, same algorithm, run backward. SDKs ship a single mapping engine that handles both directions.

Why this matters

Without this registry:

With this registry:

  • Buyers can flip to v2 whenever they want — SDK auto-promotes any v1 wire shape to v2 in-memory using the shared registry.
  • Sellers can migrate when their org allows — SDK auto-demotes v2 inline declarations to v1 wire shape using the same registry, run backward.
  • Custom format gap is bounded and self-served — sellers with bespoke formats add a canonical field once. Per-format cost, not per-buyer or per-product.
  • Cross-SDK behavior is identical because the data is identical.

Governance

  • Registry entries follow the same governance rules as asset-group-vocabulary.json: PR + rationale + ≥1 reference adopter, AAO maintainer review, versioned files with content digests.
  • Soft-warn semantics: SDKs MAY emit lint warnings for v1 formats that resolve via structural match (vs explicit canonical declaration or format_id_glob match), suggesting the seller add an explicit canonical field for clarity.
  • Registry is additive; entries are not removed once published. New mappings get new versions.

Implementation note

Initial registry can be authored from the audit data already cited in #3305. The 12-platform / 86-format audit table is essentially the seed dataset. Bootstrapping cost is one PR with a curated table; ongoing cost is a few entries per quarter as new IAB / OpenRTB / VAST patterns emerge.

SDK-side mapping engine is small — projection rules are mechanical given the registry. Reference TypeScript implementation in @adcp/sdk; Python SDK consumes the same JSON registry on its next codegen pass.

Summary

#3765 makes "publish both wire shapes" legal. This issue makes "publish both wire shapes consistently" tractable by giving SDKs a shared projection rule. Together they unblock the gradual-migration timeline — buyers and sellers move independently, neither blocked by the other's pace.

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.creativerfcProtocol change — auto-adds to roadmap boardschemaJSON Schema source-of-truth: definitions, codegen artifacts, validation, hygiene

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions