{
  "type": "object",
  "properties": {
    "shots": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "scene_index": {
            "type": "integer"
          },
          "shot_index": {
            "type": "integer"
          },
          "framing_scale": {
            "type": "string",
            "enum": [
              "close",
              "medium",
              "wide",
              "insert"
            ],
            "description": "See the system prompt section defining `framing_scale`."
          },
          "perspective": {
            "type": "string",
            "description": "Camera POV: subjective_pov, over_shoulder, observer, omniscient, object_pov, voyeur"
          },
          "pov_character": {
            "type": "string",
            "description": "For subjective POV: the character whose eyes we see through (must NOT appear in image). Empty string if not subjective."
          },
          "perception_mode": {
            "type": "string",
            "enum": [
              "direct",
              "hallucination",
              "dream",
              "memory",
              "reflection",
              "mirror",
              "through_device",
              "projection"
            ],
            "description": "8-token enum. See the system prompt section defining `perception_mode`."
          },
          "camera_direction": {
            "type": "string",
            "description": "See the system prompt sections defining `camera_direction`."
          },
          "lighting_mood": {
            "type": "string",
            "description": "Lighting technique and color mood (1 sentence, English)"
          },
          "character_angles": {
            "type": "array",
            "description": "See the system prompt section defining `character_angles`.",
            "items": {
              "type": "object",
              "properties": {
                "character": {
                  "type": "string",
                  "description": "Character name (as registered)"
                },
                "angle": {
                  "type": "string",
                  "description": "See the system prompt section defining `character_angles`."
                },
                "body_pose": {
                  "type": "string",
                  "description": "See the system prompt section defining `body_pose`."
                },
                "gaze_direction_kind": {
                  "type": "string",
                  "enum": [
                    "camera",
                    "down",
                    "up",
                    "distant",
                    "closed_eyes",
                    "off_screen",
                    "looks_at_character",
                    "looks_at_object"
                  ],
                  "description": "Eye direction kind. Closed-world enum — LLM judges intent class only. closed_eyes = eye-closed only (not state). off_screen = eyes outside camera frame, no registered target resolved (NOT visibility decision)."
                },
                "gaze_target_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Always emit. Registered short_id when gaze_direction_kind ∈ {looks_at_character, looks_at_object}; null otherwise. Shape (Q7 closed): registered short_id only — ^C\\d{2,3}$ for looks_at_character; ^P\\d{2,3}$ or ^B\\d{2,3}$ for looks_at_object. outlook O## / canonical name / Korean noun / ASCII noun fallback 모두 금지. Non-registered target case = looks_at_* 사용 금지, off_screen/distant 등 사용. kind↔target pairing은 shot_staging 코드측 validate_pairing이 강제(OpenAI strict 호환을 위해 schema combinator 미사용)."
                },
                "subject_state": {
                  "type": "string",
                  "enum": [
                    "alive",
                    "unconscious",
                    "dead",
                    "severely_injured"
                  ],
                  "description": "Character state — separate axis from gaze direction. 'alive' explicit; missing = schema violation. immobility (의식 있지만 못 움직임) = 별도 차원 (character_state element)."
                }
              },
              "required": [
                "character",
                "angle",
                "body_pose",
                "gaze_direction_kind",
                "gaze_target_id",
                "subject_state"
              ],
              "additionalProperties": false
            }
          },
          "key_bg_elements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "element": {
                  "type": "string",
                  "description": "Background element name"
                },
                "state": {
                  "type": "string",
                  "description": "State in this shot (open/closed, clean/dirty, on/off, etc.)"
                },
                "orientation": {
                  "type": "string",
                  "description": "Natural-language description tied to directionality_class. For content_surface: which face/side is visible to camera and what is on it. For reflective_surface: what is being reflected and how. For transparent_surface: surface state and what is seen through it. For directional_3d: which side faces camera. For non_directional: empty allowed. content_surface and reflective_surface MUST have a non-empty orientation."
                },
                "camera_use": {
                  "type": "string",
                  "description": "How this element is used in the shot (foreground frame, focal point, light source, etc.)"
                },
                "directionality_class": {
                  "type": "string",
                  "enum": [
                    "content_surface",
                    "reflective_surface",
                    "transparent_surface",
                    "directional_3d",
                    "non_directional"
                  ],
                  "description": "Semantic classification of this element. content_surface = thin object that carries content on one face; which face is visible changes the meaning. reflective_surface = object whose surface reflects content; what is reflected determines meaning. transparent_surface = object the camera sees through; surface state and what lies beyond determine meaning. directional_3d = 3D object with multiple faces where the camera-facing side matters. non_directional = texture/surface/ambient element with no directional meaning. Judge by meaning, not by surface vocabulary or specific examples (non-exhaustive examples, do not classify by this list)."
                }
              },
              "required": [
                "element",
                "state",
                "orientation",
                "camera_use",
                "directionality_class"
              ],
              "additionalProperties": false
            }
          },
          "frame_spatial_contract": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "movement_direction",
                  "points_to_anchor",
                  "looks_to_anchor",
                  "shared_space_relation",
                  "required_background_position",
                  "primary_subject_isolation"
                ],
                "description": "Single primary trigger — see system prompt 'Frame Spatial Contract' section."
              },
              "constraints": {
                "type": "array",
                "minItems": 1,
                "maxItems": 3,
                "items": {
                  "type": "object",
                  "properties": {
                    "target_kind": {
                      "type": "string",
                      "enum": [
                        "character",
                        "prop",
                        "background"
                      ]
                    },
                    "target_id": {
                      "type": "string",
                      "description": "character → C##, prop → P##, background → empty string"
                    },
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "description": "Human-readable label"
                    },
                    "screen_zone": {
                      "type": "string",
                      "enum": [
                        "upper_left",
                        "upper_center",
                        "upper_right",
                        "middle_left",
                        "middle_center",
                        "middle_right",
                        "lower_left",
                        "lower_center",
                        "lower_right"
                      ]
                    },
                    "depth_plane": {
                      "type": "string",
                      "enum": [
                        "foreground",
                        "midground",
                        "background"
                      ]
                    },
                    "gesture_action": {
                      "type": "string",
                      "enum": [
                        "none",
                        "points_to",
                        "reaches_for",
                        "looks_toward",
                        "moves_toward"
                      ]
                    },
                    "gesture_target_label": {
                      "type": "string",
                      "description": "non-empty when gesture_action != 'none', empty string when gesture_action == 'none'"
                    }
                  },
                  "required": [
                    "target_kind",
                    "target_id",
                    "label",
                    "screen_zone",
                    "depth_plane",
                    "gesture_action",
                    "gesture_target_label"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "reason",
              "constraints"
            ],
            "additionalProperties": false,
            "description": "Opt-in frame-space contract. null when not needed; object form is type-array nullable (OpenAI strict 호환 — combinator 미사용). See system prompt 'Frame Spatial Contract' section for emit gating."
          },
          "subject_reference_policy": {
            "type": "array",
            "description": "See the system prompt section defining `subject_reference_policy[]`.",
            "items": {
              "type": "object",
              "properties": {
                "subject_id": {
                  "type": "string",
                  "pattern": "^C\\d{2,3}(?:O\\d{2,3})?$",
                  "description": "Base C## recommended. C##O## form is accepted and normalized to base (outlook context not available in shot_staging input)."
                },
                "policy_type": {
                  "type": "string",
                  "enum": [
                    "identity_reference"
                  ],
                  "description": "v1 single value. Other values forbidden (envelope future hook only)."
                },
                "policy": {
                  "type": "string",
                  "enum": [
                    "id_and_outlook_required",
                    "base_id_required",
                    "generic_descriptor_allowed"
                  ]
                },
                "reason": {
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": [
                "subject_id",
                "policy_type",
                "policy",
                "reason"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "scene_index",
          "shot_index",
          "framing_scale",
          "perspective",
          "pov_character",
          "perception_mode",
          "camera_direction",
          "lighting_mood",
          "character_angles",
          "key_bg_elements",
          "frame_spatial_contract",
          "subject_reference_policy"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "shots"
  ],
  "additionalProperties": false
}
