{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "AKARI Video analysis.json v0",
  "description": "素材 1 本の分析結果を表す中間契約。",
  "$comment": "意味制約として、start/end を持つ全区間では end > start とする。Draft 2020-12 標準では兄弟プロパティ間を比較できないため、この条件は生成・検証手順で確認する。",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "version",
    "source",
    "transcript",
    "keyframes",
    "events",
    "tracks"
  ],
  "properties": {
    "version": {
      "const": 0
    },
    "source": {
      "type": "string",
      "minLength": 1,
      "description": "分析対象素材へのパス。相対パスは analysis.json の所在ディレクトリを基準に解決する。"
    },
    "transcript": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/transcriptSegment"
      }
    },
    "transcript_language": {
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
      "description": "文字起こしが記録した言語（BCP 47）。transcript 配列の兄弟として置く。transcript は配列のまま。"
    },
    "keyframes": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/keyframe"
      }
    },
    "events": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/event"
      }
    },
    "tracks": {
      "$ref": "#/$defs/tracks"
    },
    "probe": {
      "$ref": "#/$defs/mediaProbe"
    },
    "observations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/observation"
      }
    }
  },
  "$defs": {
    "seconds": {
      "type": "number",
      "minimum": 0
    },
    "mediaProbe": {
      "type": "object",
      "additionalProperties": false,
      "required": ["sha256", "size_bytes", "container", "duration_s", "video", "audio", "tool"],
      "properties": {
        "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
        "size_bytes": { "type": "integer", "minimum": 0 },
        "container": { "type": "string", "minLength": 1 },
        "duration_s": { "$ref": "#/$defs/seconds" },
        "video": {
          "oneOf": [
            { "type": "null" },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["width", "height", "fps", "codec", "rotation"],
              "properties": {
                "width": { "type": "integer", "minimum": 1 },
                "height": { "type": "integer", "minimum": 1 },
                "fps": { "type": "number", "exclusiveMinimum": 0 },
                "codec": { "type": "string", "minLength": 1 },
                "rotation": { "type": "number" }
              }
            }
          ]
        },
        "audio": {
          "oneOf": [
            { "type": "null" },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["codec", "channels", "sample_rate"],
              "properties": {
                "codec": { "type": "string", "minLength": 1 },
                "channels": { "type": "integer", "minimum": 1 },
                "sample_rate": { "type": "integer", "minimum": 1 }
              }
            }
          ]
        },
        "tool": {
          "type": "object",
          "additionalProperties": false,
          "required": ["ffprobe"],
          "properties": { "ffprobe": { "type": "string", "minLength": 1 } }
        }
      }
    },
    "observation": {
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "at", "args", "outputs", "tool"],
      "properties": {
        "kind": { "enum": ["probe", "grab", "filmstrip", "waveform", "transcribe"] },
        "at": { "type": "string", "minLength": 1 },
        "range": {
          "type": "object",
          "additionalProperties": false,
          "required": ["in", "out"],
          "properties": { "in": { "$ref": "#/$defs/seconds" }, "out": { "$ref": "#/$defs/seconds" } }
        },
        "args": { "type": "object", "additionalProperties": true },
        "outputs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
        "tool": { "type": "string", "minLength": 1 }
      }
    },
    "waveformTrackPointer": {
      "type": "object",
      "additionalProperties": false,
      "required": ["path", "tool", "generated_at"],
      "properties": {
        "path": { "type": "string", "minLength": 1 },
        "tool": { "type": "string", "minLength": 1 },
        "generated_at": { "type": "string", "minLength": 1 }
      }
    },
    "word": {
      "$comment": "M5 契約例で省略されている words の要素を、区間・本文を持つ最小形として定義する。",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "start",
        "end",
        "text"
      ],
      "properties": {
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        },
        "text": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "unrecognizedSpan": {
      "$comment": "音はあるが文字にできなかった区間。無音ではなく、transcriptSegment.words とは重ならない。",
      "type": "object",
      "additionalProperties": false,
      "required": ["start", "end"],
      "properties": {
        "start": { "$ref": "#/$defs/seconds" },
        "end": { "$ref": "#/$defs/seconds" }
      }
    },
    "transcriptSegment": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "start",
        "end",
        "text"
      ],
      "properties": {
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        },
        "text": {
          "type": "string",
          "minLength": 1
        },
        "speaker": {
          "type": "string",
          "minLength": 1
        },
        "words": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/word"
          }
        },
        "unrecognized": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/unrecognizedSpan"
          }
        }
      }
    },
    "keyframe": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "t",
        "path",
        "note"
      ],
      "properties": {
        "t": {
          "$ref": "#/$defs/seconds"
        },
        "path": {
          "type": "string",
          "minLength": 1,
          "description": "キーフレーム画像へのパス。相対パスは analysis.json の所在ディレクトリを基準に解決する。"
        },
        "note": {
          "type": "string",
          "minLength": 1
        },
        "origin": {
          "enum": [
            "scene",
            "interval",
            "transcript"
          ],
          "description": "採用元の抽出系統（2026-07-14 追加・任意）。scene = 映像変化検出、interval = 一定間隔、transcript = 発話由来（highlight 等の時刻から抽出）。候補と note の対応トレーサビリティにも使う。"
        }
      }
    },
    "fillerEvent": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "start",
        "end"
      ],
      "properties": {
        "type": {
          "const": "filler"
        },
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        }
      }
    },
    "troubleEvent": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "start",
        "end",
        "note"
      ],
      "properties": {
        "type": {
          "const": "trouble"
        },
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        },
        "note": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "chapterEvent": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "t",
        "title"
      ],
      "properties": {
        "type": {
          "const": "chapter"
        },
        "t": {
          "$ref": "#/$defs/seconds"
        },
        "title": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "hookScore": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "hook",
        "self_contained",
        "emotion",
        "density",
        "punch"
      ],
      "properties": {
        "hook": {
          "$ref": "#/$defs/scoreValue"
        },
        "self_contained": {
          "$ref": "#/$defs/scoreValue"
        },
        "emotion": {
          "$ref": "#/$defs/scoreValue"
        },
        "density": {
          "$ref": "#/$defs/scoreValue"
        },
        "punch": {
          "$ref": "#/$defs/scoreValue"
        }
      }
    },
    "scoreValue": {
      "$comment": "1〜5 の整数尺度は analysis.json v0 の運用仮定。採用閾値は M5 契約どおり運用で調整する。",
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "hookEvent": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "start",
        "end",
        "score"
      ],
      "properties": {
        "type": {
          "const": "hook"
        },
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        },
        "score": {
          "$ref": "#/$defs/hookScore"
        }
      }
    },
    "highlightEvent": {
      "$comment": "2026-07-14 追加（後方互換の追加語彙）。ショート向けの hook と異なり、決定事項・結論・数字・強い主張など編集判断全般の根拠になる重要発言を記録する。",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "start",
        "end",
        "quote",
        "reason"
      ],
      "properties": {
        "type": {
          "const": "highlight"
        },
        "start": {
          "$ref": "#/$defs/seconds"
        },
        "end": {
          "$ref": "#/$defs/seconds"
        },
        "quote": {
          "type": "string",
          "minLength": 1,
          "description": "transcript の実発言に忠実な引用。要約・創作をしない。"
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "description": "なぜ重要か（決定事項・結論・数値/実績・強い主張などの根拠カテゴリと内容）。"
        },
        "importance": {
          "$ref": "#/$defs/scoreValue"
        }
      }
    },
    "event": {
      "oneOf": [
        {
          "$ref": "#/$defs/fillerEvent"
        },
        {
          "$ref": "#/$defs/troubleEvent"
        },
        {
          "$ref": "#/$defs/chapterEvent"
        },
        {
          "$ref": "#/$defs/highlightEvent"
        },
        {
          "$ref": "#/$defs/hookEvent"
        }
      ]
    },
    "speakerSpan": {
      "type": "array",
      "prefixItems": [
        {
          "$ref": "#/$defs/seconds"
        },
        {
          "$ref": "#/$defs/seconds"
        }
      ],
      "items": false,
      "minItems": 2,
      "maxItems": 2
    },
    "speakerTrack": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "spans"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "spans": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/speakerSpan"
          }
        }
      }
    },
    "faceBox": {
      "description": "素材フレームに対する [x, y, width, height] 順の正規化座標。各要素は 0〜1。",
      "$comment": "意味制約として x + width <= 1 かつ y + height <= 1 とする。Draft 2020-12 標準では配列要素間を加算・比較できないため、この条件は生成・検証手順で確認する。",
      "type": "array",
      "prefixItems": [
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        }
      ],
      "items": false,
      "minItems": 4,
      "maxItems": 4
    },
    "faceTrackPoint": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "speaker",
        "t",
        "box"
      ],
      "properties": {
        "speaker": {
          "type": "string",
          "minLength": 1
        },
        "t": {
          "$ref": "#/$defs/seconds"
        },
        "box": {
          "$ref": "#/$defs/faceBox"
        }
      }
    },
    "personMatteTrack": {
      "$comment": "2026-07-23 追加、2026-08-27 mask_path / mask_format を additive 追加（docs/contract-2026-07-23-analysis-person-matte.md）。path の実体は VP9 alpha WebM（コンテナに alpha_mode=1）、mask_path は追加の full-range H.264 グレースケールマスク。意味制約として、両動画の時刻 0 は素材の時刻 0 と一致し、fps は動画の実 fps と一致する。Draft 2020-12 標準では外部ファイルを参照できないため、この条件は生成・検証手順で確認する。",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "path",
        "fps"
      ],
      "properties": {
        "path": {
          "type": "string",
          "minLength": 1,
          "description": "マット動画（VP9 alpha WebM）へのパス。相対パスは analysis.json の所在ディレクトリを基準に解決し、区切りは / を使う。"
        },
        "mask_path": {
          "type": "string",
          "minLength": 1,
          "description": "追加のグレースケール H.264 マスクへのパス。相対パス規則は path と同じ。"
        },
        "mask_format": {
          "type": "string",
          "minLength": 1,
          "description": "マスク形式。現行の生成器は gray-h264-fullrange を書く。"
        },
        "fps": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "マット動画の fps。元素材の fps と一致しなくてよい。"
        },
        "quality": {
          "type": "string",
          "minLength": 1,
          "description": "生成品質。fast / balanced / accurate を例示するが、enum 強制はしない（beats[].kind と同じ流儀）。"
        },
        "generated_at": {
          "type": "string",
          "minLength": 1,
          "description": "生成時刻（ISO8601）。"
        },
        "tool": {
          "type": "string",
          "minLength": 1,
          "description": "生成手段の記録。vision-person-segmentation を例示する。"
        }
      }
    },
    "visionTrackPointer": {
      "$comment": "2026-08-11 追加、2026-08-13 body_pose_3d、2026-08-14 face_expression を additive 追加（docs/contract-2026-08-11-analysis-vision-tracks-v0.md）。tracks.face_landmarks / tracks.hand_pose / tracks.body_pose_3d / tracks.face_expression の共通形。トラック実体は本スキマではなく skills/analyze-footage/references/vision-tracks.schema.json（契約 §2）が定義する外部ファイル。optional キーであり tracks.required には入れない（真に任意）。",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "path",
        "sample_fps"
      ],
      "properties": {
        "path": {
          "type": "string",
          "minLength": 1,
          "description": "トラックファイル（vision-tracks v0 形式）へのパス。相対パスは analysis.json の所在ディレクトリを基準に解決し、区切りは / を使う。"
        },
        "sample_fps": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "トラックのサンプリング fps。元素材の fps と一致しなくてよい。"
        },
        "provider": {
          "type": "string",
          "minLength": 1,
          "description": "検出プロバイダの自由文字列。apple-vision を例示するが enum 強制はしない（quality / tool と同じ流儀）。"
        },
        "tool": {
          "type": "string",
          "minLength": 1,
          "description": "生成手段の記録。vision-tracks.mjs v0 を例示する。"
        },
        "generated_at": {
          "type": "string",
          "minLength": 1,
          "description": "生成時刻（ISO8601）。"
        },
        "features": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "uniqueItems": true,
          "description": "トラック実体が持つ additive な任意特徴。face_landmarks の face_contour などを列挙する。省略された既存ポインタも valid。"
        }
      }
    },
    "tracks": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "speakers",
        "faces",
        "person_matte"
      ],
      "properties": {
        "speakers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/speakerTrack"
          }
        },
        "faces": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/faceTrackPoint"
          }
        },
        "person_matte": {
          "description": "生成済み人物マット。未生成なら null。object 形が正（2026-07-23 追加）。string 形は { path } の糖衣として後方互換で受ける（非推奨）。相対パスは analysis.json の所在ディレクトリを基準に解決する。",
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "string",
              "minLength": 1
            },
            {
              "$ref": "#/$defs/personMatteTrack"
            }
          ]
        },
        "face_landmarks": {
          "$ref": "#/$defs/visionTrackPointer"
        },
        "hand_pose": {
          "$ref": "#/$defs/visionTrackPointer"
        },
        "body_pose_3d": {
          "$ref": "#/$defs/visionTrackPointer"
        },
        "face_expression": {
          "$ref": "#/$defs/visionTrackPointer"
        },
        "waveform": {
          "$ref": "#/$defs/waveformTrackPointer"
        }
      }
    }
  }
}
