first-span-feedback
The one moment a run STOPS on its first delivered span, shows it to a reviewer, and lets that feedback steer the rest of the run — the only pipeline step that can change a skill's code or default so a defect caught on span 0 never recurs. grade-clip/director-pass/fix-cold-open only ever patch one clip's pixels in place; this loop makes the correction stick.
Usage
first-span-feedback.sh <work_id> <clip_stem> <slug> <saved_mp4> [--pane <tmux>]
Invoked by the entrypoint at the Phase 1 → fan-out seam, right after span 0 is saved and before the lanes claim spans 1…N.
How it works
- Record —
build_page.py fills page-template.html from clip_00.grade.json (grade/tier + retention signals, failing signal flagged) into work/<id>/_preview/span0-review.html; storyboard.py emits a shot-scraper video storyboard that serves the page over a throwaway python -m http.server (a real http://127.0.0.1 origin so <video> autoplays) and plays span 0 through once; uvx shot-scraper video records span0-demo.mp4.
- Review — the demo is sampled into ONE labelled contact sheet (reusing
director-pass/frames.py), build_prompt.py assembles the sheet + grade.json signals + the transcript, and ONE Claude vision call returns a verdict: {defect, defect_class, signal, where, rationale}. parse_reply.py extracts it with a deterministic {"defect": false} fail-safe.
- Fix + three-gate re-verify + commit (Phases 2-3 — not in this slice) — a flagged systemic defect drives an agentic fixer, a hard re-render → re-grade → re-review accept, and a fixer-path-scoped commit + push.
Phase 1 status: record → review → write span0-feedback.json + .fsfmeta, exit 0. A flagged defect is surfaced only (action: fix_pending); no fixer, no git, no commit yet.
Artifacts — work/<id>/_preview/
span0-review.html — the generated review page.
storyboard.yml — the shot-scraper storyboard (server + play scene).
span0.mp4 / span0-demo.webm / span0-demo.mp4 — the served clip copy + the recorded demo.
review/sheet.png + prompt.txt + reply.txt — the contact sheet, reviewer prompt, and raw reply.
span0-feedback.json — the persisted verdict {work_id, clip, demo, reviewed, verdict{…}, action, phase}.
Knobs
FIRST_SPAN_FEEDBACK (1) — 0 disables the gate entirely (no _preview/, no review).
FSF_MAX_ITERS (1) — fixer review→apply rounds (Phase 2).
FSF_FRAMES (12) — frames sampled into the contact sheet.
FSF_PORT (8917) — port for the throwaway http.server that serves the review page.
FSF_REPLY_FILE — test seam: a canned verdict file stands in for the live reviewer (mirrors DIRECTOR_REPLY_FILE).
FSF_RERENDER_CMD — entrypoint-injected span-0 re-render command; unset ⇒ surface-only, never a code change (Phase 2).
Guarantees
- NON-FATAL — a missing
shot-scraper, a record failure, a parse failure — all leave span 0 and the code untouched and exit 0, so fan-out proceeds on unchanged code.
- Idempotent —
.fsfmeta = mtime(span0.mp4)|mtime(grade.json)|FIRST_SPAN_FEEDBACK|v1; a resumed run skips a loop that already ran.
- Isolated dependency —
shot-scraper runs through uvx in an ephemeral env (playwright ≥ 1.61), never touching the vendored 1.60 Node Playwright in like-subscribe-overlay.
- Reuses, never edits —
director-pass/frames.py for the contact sheet, _lib/pane.sh for the reviewer dispatch, grade-clip's grade.json as the gate oracle.
1---2name: first-span-feedback3description: The pre-fanout LEARNING GATE. start.sh finishes SPAN 0 alone (through the normal edit/captions/completion chain) before any lane launches, then this skill records a shot-scraper `video` demo of a generated per-span REVIEW PAGE (the 9:16 player + the grade/tier + the retention signals with the failing one flagged), has a Claude reviewer WATCH it via a contact sheet (like director-pass), and — when the reviewer names a SYSTEMIC defect that clears a hard three-gate re-verify (re-render span 0, grade≥prev + flagged signal cleared, re-record + re-review defect-gone) — makes a PERMANENT codebase change and commits only the fixer's paths, so every later span in the run and every future run inherits the fix. NON-FATAL everywhere (missing shot-scraper / record fail / parse fail / rejected gate / push fail all leave span 0 and the code untouched, exit 0). Idempotent via .fsfmeta. Gated by FIRST_SPAN_FEEDBACK (default 1), bounded by FSF_MAX_ITERS (default 1). Wired into start.sh / shorts.sh; autopilot.sh inherits it.4---56# first-span-feedback78The one moment a run STOPS on its first delivered span, shows it to a reviewer, and lets that feedback steer the rest of the run — the only pipeline step that can change a skill's *code or default* so a defect caught on span 0 never recurs. `grade-clip`/`director-pass`/`fix-cold-open` only ever patch one clip's pixels in place; this loop makes the correction stick.910## Usage1112```bash13first-span-feedback.sh <work_id> <clip_stem> <slug> <saved_mp4> [--pane <tmux>]14```1516Invoked by the entrypoint at the Phase 1 → fan-out seam, right after span 0 is saved and before the lanes claim spans 1…N.1718## How it works19201. **Record** — `build_page.py` fills `page-template.html` from `clip_00.grade.json` (grade/tier + retention signals, failing signal flagged) into `work/<id>/_preview/span0-review.html`; `storyboard.py` emits a `shot-scraper video` storyboard that serves the page over a throwaway `python -m http.server` (a real `http://127.0.0.1` origin so `<video>` autoplays) and plays span 0 through once; `uvx shot-scraper video` records `span0-demo.mp4`.212. **Review** — the demo is sampled into ONE labelled contact sheet (reusing `director-pass/frames.py`), `build_prompt.py` assembles the sheet + `grade.json` signals + the transcript, and ONE Claude vision call returns a verdict: `{defect, defect_class, signal, where, rationale}`. `parse_reply.py` extracts it with a deterministic `{"defect": false}` fail-safe.223. **Fix + three-gate re-verify + commit** *(Phases 2-3 — not in this slice)* — a flagged systemic defect drives an agentic fixer, a hard re-render → re-grade → re-review accept, and a fixer-path-scoped commit + push.2324**Phase 1 status:** record → review → write `span0-feedback.json` + `.fsfmeta`, exit 0. A flagged defect is *surfaced only* (`action: fix_pending`); no fixer, no git, no commit yet.2526## Artifacts — `work/<id>/_preview/`2728- `span0-review.html` — the generated review page.29- `storyboard.yml` — the shot-scraper storyboard (server + play scene).30- `span0.mp4` / `span0-demo.webm` / `span0-demo.mp4` — the served clip copy + the recorded demo.31- `review/sheet.png` + `prompt.txt` + `reply.txt` — the contact sheet, reviewer prompt, and raw reply.32- `span0-feedback.json` — the persisted verdict `{work_id, clip, demo, reviewed, verdict{…}, action, phase}`.3334## Knobs3536- `FIRST_SPAN_FEEDBACK` (1) — `0` disables the gate entirely (no `_preview/`, no review).37- `FSF_MAX_ITERS` (1) — fixer review→apply rounds *(Phase 2)*.38- `FSF_FRAMES` (12) — frames sampled into the contact sheet.39- `FSF_PORT` (8917) — port for the throwaway http.server that serves the review page.40- `FSF_REPLY_FILE` — test seam: a canned verdict file stands in for the live reviewer (mirrors `DIRECTOR_REPLY_FILE`).41- `FSF_RERENDER_CMD` — entrypoint-injected span-0 re-render command; unset ⇒ surface-only, never a code change *(Phase 2)*.4243## Guarantees4445- **NON-FATAL** — a missing `shot-scraper`, a record failure, a parse failure — all leave span 0 and the code untouched and exit 0, so fan-out proceeds on unchanged code.46- **Idempotent** — `.fsfmeta` = `mtime(span0.mp4)|mtime(grade.json)|FIRST_SPAN_FEEDBACK|v1`; a resumed run skips a loop that already ran.47- **Isolated dependency** — `shot-scraper` runs through `uvx` in an ephemeral env (`playwright ≥ 1.61`), never touching the vendored 1.60 Node Playwright in `like-subscribe-overlay`.48- **Reuses, never edits** — `director-pass/frames.py` for the contact sheet, `_lib/pane.sh` for the reviewer dispatch, `grade-clip`'s `grade.json` as the gate oracle.