{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/core/canonical-projection-ref.json",
  "title": "Canonical Projection Reference",
  "description": "Authoritative v1 → v2 projection annotation on a v1 named format. Tells SDKs which canonical-formats v2 declaration to produce when projecting this v1 format to v2, including any non-default `asset_source` and slot-override hints needed when the v1 format's asset shape doesn't follow the canonical's defaults.\n\n**Why an object, not just a string.** A bare-string annotation like `canonical: \"image\"` carries the canonical's default slot shape (image_main: image, required) and default asset_source (buyer_uploaded). For v1 formats that genuinely follow those defaults (e.g., a 300×250 image upload), that's correct. For v1 formats whose asset shape differs (e.g., a generative entry whose input is `generation_prompt: text`, not an image upload), a bare-string annotation is lossy — the projected v2 declaration would claim buyer-uploaded image bytes when the underlying inventory wants a text prompt. The object form lets the catalog carry the asset_source + slot override so projection is lossless.\n\n**SDK projection rules (normative).**\n1. Read `kind` — this becomes `format_kind` on the projected v2 ProductFormatDeclaration.\n2. If `asset_source` is set, copy to the projected declaration's `params.asset_source`. If absent, projection uses the canonical's default asset_source (typically `buyer_uploaded`).\n3. If `slots_override` is set, copy to the projected declaration's `params.slots[]`, REPLACING the canonical's default slot set. If absent, projection inherits the canonical's defaults.\n4. v1 format's `requirements` (dimensions, durations, codecs, etc.) become `params` fields on the projected declaration per the canonical's parameter schema.\n5. v1 format's `assets[*]` shape MUST be consistent with the resulting `slots[]` — the v1 assets are the projected slots, just identified by `asset_id` (v1) ↔ `asset_group_id` (v2). The asset-group-vocabulary registry resolves aliases.\n6. If `supported_delivery_types` is set (only valid with `kind: \"display_tag\"`), copy it to the projected declaration's `params.supported_delivery_types`. If absent, the projected declaration omits it and inherits the canonical's backward-compatible `tag_url` default slot contract.\n\n**Inline markup from a legacy `html` asset.** When a v1 format's markup asset (`asset_type: \"html\"`) projects to a `display_tag` slot (`asset_type: \"display_tag\"`) under `supported_delivery_types: [\"inline_markup\"]`, the two are consistent under rule 5. The v1 HTML `content` and the display-tag asset's `markup` carry the same bytes, and the slot keeps the v1 `asset_id` as its `asset_group_id`, so the mapping is lossless in both directions. When writing to the legacy wire, the SDK emits the markup as the v1 `html` asset's `content` under that `asset_id` and drops `delivery_type` and `markup_type`, which have no legacy field. When reading a legacy `html` asset back into a canonical manifest, an SDK that needs `markup_type` SHOULD use `standard`.\n\n**Examples.**\n\nDefault-slot case (a 300×250 image upload format):\n```json\n\"canonical\": { \"kind\": \"image\" }\n```\nGenerative case (a 300×250 entry whose input is a text prompt):\n```json\n\"canonical\": {\n  \"kind\": \"image\",\n  \"asset_source\": \"agent_synthesized\",\n  \"slots_override\": [\n    { \"asset_group_id\": \"generation_prompt\", \"asset_type\": \"text\", \"required\": true }\n  ]\n}\n```\nBoth project to `format_kind: \"image\"` — the difference is the per-product `asset_source` + `slots[]` shape. v2-aware buyers see the projection and know whether to ship image bytes or a text prompt.\n\nInline-markup case (a legacy 300×250 HTML-markup format whose seller renders the buyer's markup verbatim):\n```json\n\"canonical\": {\n  \"kind\": \"display_tag\",\n  \"asset_source\": \"buyer_uploaded\",\n  \"slots_override\": [\n    { \"asset_group_id\": \"html_creative\", \"asset_type\": \"display_tag\", \"required\": true }\n  ],\n  \"supported_delivery_types\": [\"inline_markup\"]\n}\n```\nThis projects to `format_kind: \"display_tag\"` with `params.supported_delivery_types: [\"inline_markup\"]`, not to `html5`. `html5` is reserved for seller-hosted zip bundles.",
  "type": "object",
  "required": [
    "kind"
  ],
  "properties": {
    "kind": {
      "$ref": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/core/canonical-format-kind.json",
      "description": "The v2 canonical-format-kind this v1 format projects to (`image`, `html5`, `display_tag`, `image_carousel`, `video_hosted`, `video_vast`, `audio_hosted`, `audio_vast`, `audio_daast`, `sponsored_placement`, `native_in_feed`, `responsive_creative`, `agent_placement`, `seller_rendered_stateful_display`, `coordinated_placements`, or `custom`)."
    },
    "asset_source": {
      "type": "string",
      "enum": [
        "buyer_uploaded",
        "publisher_host_recorded",
        "seller_pre_rendered_from_brief",
        "seller_human_designed",
        "agent_synthesized",
        "publisher_owned_reference"
      ],
      "description": "Where the rendered asset bytes come from on the projected v2 declaration. Default (when omitted) is `buyer_uploaded` — the canonical's default. Set explicitly when the v1 named format doesn't follow that default. Required for generative entries (`agent_synthesized` or `seller_pre_rendered_from_brief`) because their asset shape doesn't carry image/video/audio bytes, and for published-post reference entries (`publisher_owned_reference`) because their asset shape carries a post reference rather than uploaded bytes. Projection without this hint produces a lossy v2 declaration that claims buyer-uploaded bytes."
    },
    "slots_override": {
      "type": "array",
      "minItems": 1,
      "description": "When the v1 named format's slot shape differs from the canonical's default slots, this carries the override that the projected v2 declaration's `params.slots[]` should use. REPLACES (does not merge with) the canonical's default slots — projection-time semantics. The slot vocabulary follows `asset-group-vocabulary.json`. Asset IDs in the v1 format's `assets[*]` MUST resolve (directly or via the vocabulary's aliases) to the `asset_group_id` values declared here.",
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/core/canonical-projection-slot-override.json"
      }
    },
    "supported_delivery_types": {
      "type": "array",
      "minItems": 1,
      "uniqueItems": true,
      "items": {
        "type": "string",
        "enum": [
          "tag_url",
          "inline_markup",
          "paired_redirect"
        ]
      },
      "description": "Delivery types the projected `display_tag` declaration accepts. Copied to the projected declaration's `params.supported_delivery_types` (rule 6). Only valid with `kind: \"display_tag\"`; uses the same closed set as the `display_tag` canonical parameter. Lets a catalog entry state that its legacy format takes byte-preserved inline markup (`inline_markup`) rather than the default single ad-request URL.",
      "x-added-in": "3.3.0"
    }
  },
  "if": {
    "required": [
      "supported_delivery_types"
    ]
  },
  "then": {
    "properties": {
      "kind": {
        "const": "display_tag"
      }
    }
  },
  "additionalProperties": true,
  "examples": [
    {
      "kind": "image"
    },
    {
      "kind": "image",
      "asset_source": "agent_synthesized",
      "slots_override": [
        {
          "asset_group_id": "generation_prompt",
          "asset_type": "text",
          "required": true
        }
      ]
    },
    {
      "kind": "video_hosted",
      "asset_source": "seller_pre_rendered_from_brief",
      "slots_override": [
        {
          "asset_group_id": "creative_brief",
          "asset_type": "brief",
          "required": true
        }
      ]
    },
    {
      "kind": "display_tag",
      "asset_source": "buyer_uploaded",
      "slots_override": [
        {
          "asset_group_id": "html_creative",
          "asset_type": "display_tag",
          "required": true
        }
      ],
      "supported_delivery_types": [
        "inline_markup"
      ]
    }
  ]
}
