{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "status",
    "fp_id",
    "bg_id",
    "shot_id",
    "camera_pose_source",
    "visible_items",
    "scene_visible_description",
    "bg_plate_visible_description",
    "plate_description_excludes_transient",
    "not_visible_or_occluded_summary",
    "vlm_reported_state",
    "self_consistency",
    "confidence",
    "missing_inputs",
    "diagnostics"
  ],
  "properties": {
    "status": { "type": "string", "enum": ["ok"] },
    "fp_id": { "type": "string" },
    "bg_id": { "type": "string" },
    "shot_id": { "type": "string" },
    "camera_pose_source": {
      "type": "string",
      "description": "Identifier/label of the supplied camera recommendation the VLM selected and described. The VLM does NOT invent coordinates or a new pose — it only references one of the supplied candidates."
    },
    "visible_items": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "marker_number",
          "marker_layer",
          "expected_label",
          "visibility",
          "horizontal_band",
          "depth_band",
          "occlusion_note",
          "evidence",
          "source_ref",
          "confidence"
        ],
        "properties": {
          "marker_number": {
            "type": "integer",
            "description": "Internal only — echo verbatim from the supplied marker inventory; used for evidence join. Never surfaced to the prose fields or to any downstream prompt."
          },
          "marker_layer": {
            "type": "string",
            "enum": ["base", "transient", "ignored_state_overlay"],
            "description": "Internal only — echo verbatim from the supplied inventory entry for this marker. The inventory already assigns each marker its layer (base = persistent spatial anchor, transient = momentary event object, ignored_state_overlay = excluded). Do NOT re-derive or re-label. Never surfaced to prose or downstream prompts."
          },
          "expected_label": {
            "type": "string",
            "description": "Internal only — echo verbatim from the supplied inventory entry for this marker number. Never surfaced to prose."
          },
          "visibility": { "type": "string", "enum": ["visible", "partial", "occluded", "out_of_frame"] },
          "horizontal_band": { "type": "string", "enum": ["left", "center", "right"] },
          "depth_band": { "type": "string", "enum": ["foreground", "midground", "background"] },
          "occlusion_note": { "type": "string" },
          "evidence": { "type": "string" },
          "source_ref": { "type": "string" },
          "confidence": { "type": ["number", "null"] }
        }
      }
    },
    "scene_visible_description": {
      "type": "string",
      "description": "FULL Korean natural-language visible description for a text-to-image model, covering BOTH base structure AND any transient/event objects present for this shot. This field feeds the per-shot scene / I2I layer. No coordinates, no grid, no marker numbers, no english enum/layer literals, no card metadata token — use natural Korean spatial language (왼쪽/중앙/오른쪽/전경/배경)."
    },
    "bg_plate_visible_description": {
      "type": "string",
      "description": "BACKGROUND-PLATE-only Korean natural-language description for a text-to-image model. Describe ONLY base structure, persistent fixtures/furniture, stable materials, lighting, and camera framing. DELIBERATELY OMIT every transient/event-overlay object, subject action, and event residue — do not mention them at all. This is produced by the VLM knowing each marker's layer (it omits transient markers when writing this field); the code does NOT slice the full prose. Same token-leakage constraints as scene_visible_description."
    },
    "plate_description_excludes_transient": {
      "type": "string",
      "enum": ["yes", "uncertain", "no"],
      "description": "Honest self-report on whether bg_plate_visible_description fully excluded all transient objects: 'yes' if none leaked in, 'uncertain' if unsure, 'no' if a transient object is still described."
    },
    "not_visible_or_occluded_summary": {
      "type": "string",
      "description": "Korean summary of what is occluded or out of frame. Empty string allowed as sentinel. Same token-leakage constraints as the other prose fields."
    },
    "vlm_reported_state": {
      "type": "string",
      "enum": ["ok", "low_confidence", "insufficient_evidence"],
      "description": "VLM self-report only. The downstream validator re-derives the authoritative validator_state separately and does not blindly trust this."
    },
    "self_consistency": {
      "type": "object",
      "additionalProperties": false,
      "required": ["prose_matches_structured", "notes"],
      "properties": {
        "prose_matches_structured": { "type": "string", "enum": ["consistent", "uncertain", "contradictory"] },
        "notes": { "type": "string" }
      }
    },
    "confidence": { "type": ["number", "null"] },
    "missing_inputs": { "type": "array", "items": { "type": "string" } },
    "diagnostics": { "type": "array", "items": { "type": "string" } }
  }
}
