{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "/schemas/3.1.30/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.\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.",
  "type": "object",
  "required": ["kind"],
  "properties": {
    "kind": {
      "$ref": "/schemas/3.1.30/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_daast`, `sponsored_placement`, `responsive_creative`, `agent_placement`, 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": "/schemas/3.1.30/core/canonical-projection-slot-override.json"
      }
    }
  },
  "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 }
      ]
    }
  ]
}
