Docs/Skills and code/skill video-script-production

.claude/skills/video-script-production/references/tools.md

Markdown127 lines25.8 KBSkill: video-script-production

What the file says about itself

Tools and skills registry: Rule: when you create or change a skill, a script or a template for this pipeline, add or update its row here in the same session, with what it does, its input and output, whether it is deterministic, and what it…

Mentioned on: page 02, page 06, page 07, page 10, page 12, page 13, page in

Tools and skills registry

Rule: when you create or change a skill, a script or a template for this pipeline, add or update its row here in the same session, with what it does, its input and output, whether it is deterministic, and what it was tested on. This file is the answer to "what do we already have?" and the guard against building the same scratch script twice. (The standing instruction is also in the repo's CLAUDE.md.)

Kinds: D deterministic (same input, same output, no model), L uses a model (results vary), H a procedure for the agent to follow. Paths are from the repo root.

A. Skills

SkillKindWhat it doesUsed atNotes
video-script-production (.claude/skills/video-script-production/)D + HWrites and checks a script to the v6 bar: lessons A to M, linter, damage test, gate runner, ledger check, scaffold, retime, calibration, session log. Entry point SKILL.md; procedure references/runbook.mdS0, S3 to S11Built 10-24, extended 10-25. Tested on Moser Baer v4, v5, v6 only
winning-video-teardown (prepare.js, validate.js, aggregate.js, report.js; prepare.js --perf-unknown added 2026-10-25)L (Sonnet analysts) + D (validate, aggregate, report)Takes winning videos, finds each mechanism and quotes the moment; counts which recur. Taxonomy of script, packaging and thumbnail mechanisms; junction types; first-payoff fieldsS2Taxonomy extended for lessons A to D (10 mechanisms) and E to I (S-IMPLICIT-PULL, S-PLAIN-VOICE, S-LANDING, S-PARABLE, S-TWO-SIDED)
script-cohort-analysisL (Haiku raters via script-rater) + DBlind, pre-registered statistical test of which script rules predict performance in a cohort of transcripts. Rubric v3: 45 checks, 11 dimensionsafter enough comparable scripts existGap: the rubric does not yet include the E to J checks (a rubric v4 is the next step); the ledger H-012 to H-017 is gitignored
within-channel-studyD + HWhat separates a channel's best, median and worst videoschannel selectionNot used in the script work
ecosystem-raterDRates niches on demand, room, evergreen-ness, upload pressureniche selectionNot used in the script work
video-visual-production (.claude/skills/video-visual-production/, built 2026-11-01)D + HTurns a voice-over, its aligned word times and a hand-written beat sheet into a rendered video beat: asset fetch and article capture, lint with a damage test, frame timeline, canvas renderer piped to ffmpeg, synthesised effects, ducked placeholder bed, loudness-normalised mix, and a measured check. Entry point SKILL.md; plan in research/video-production/Video stage, after the script and the voice-overFirst proof is the B1 smoke test (video-visual-production/scripts/smoke-b1.js); status in research/the-fall-of-moser-baer/video/STATUS-video-B1.md. Only B1 and B2 are aligned

B. Scripts in video-script-production/scripts/

ScriptKindInput -> outputWhenTested on / notes
check.jsD--dir --version [--prev] [--fix] [--no-selftest] [--lang hi --english <version>] (the last two for a Hinglish version: lint and damage test with lang-hi.json, the ledger from the English script, and parity as gate P1; added 2026-11-02) -> runs every gate, writes STATUS-vN.md and lint/, prints BAR MET or what to do next; a chain may carry bar_overrides: {hook_sec: [min, max], reason} (hook length only; gate T4 requires the reason)end of every stage; session startv6 (all gates pass except the reader pass); a blank scaffold; a copy of v6 as v7. Exit 1 on a hard gate
lint.jsD--script --chain [--label --out] [--lang hi] (--lang hi adds the Hinglish word lists and patterns from lang-hi.json to the English ones; 2026-11-02) -> lint-<label>.json/.md; exit 1 on FAILS870 rules (A1 to J2, B8, F1 to F5, G1 to G4, H1 to H5, I1 to I6, K1 to K8, L1 to L4, M1 to M3). K, L, M added 2026-10-25 (review round 7): an old chain that declares none of their fields gets WARNs, not FAILs, so older scripts still pass the hard gates (the Cafe Coffee Day v1 chain: 0 FAIL, 9 WARN). Text-only mode when no chain
selftest.jsD--script --chain [--lang hi] (the mutations are language-neutral where they can be; 2026-11-02) -> breaks one property at a time and checks the matching rule firesS8, and whenever a rule or lint.js changes56 mutations on v7 (42 on v6 before round 7; v6 now: 46 behave, 10 skipped as undeclared; mutations touch only the voice-over lines, because a quote can also occur in the notes sections); on the v5 chain 28 behave and 14 are skipped as undeclared; the same 42 pass on a copy of v6 with every person and the company renamed (names are not hard-coded)
ledger-check.jsD--script [--chain --facts --strict] [--ledger-from <English script>] (a translation takes its ledger and hand-off sections from the English script; 2026-11-02) -> every spoken digit figure is in the ledger; the four hand-off sections exist; [CHECK] rows reach the open itemsS8Digit figures only (spelled numbers are not read). Found 4 real gaps in v5 and v6 on first run
retime.jsD--script [--write --check] -> heading time ranges from word counts (2026-11-02: it rounded the seconds before splitting the minutes, so a heading could read 6:60; it now rounds the total first)S8v6 (5 stale headings rewritten, body unchanged)
extract-vo.jsD`--script [--mode timedblindplain --out]` -> the spoken text, numberedS9, S10blind is what readers get
calibrate.jsD--dir <winner transcripts> [--script ...] [--out] -> voice, rhythm, comparison, admission, "you", name measures with medianswhen thresholds are set or new winners arriveReproduced the numbers behind the rhythm thresholds (8 English winners). Corrected an arithmetic mistake in my own notes
fetch-articles.mjsD--dir <folder> (--urls file | --url U ...) [--pause ms] -> one NN-host.txt per page (URL, date, HTTP status, title, note, text), PDFs saved as .pdf, and an INDEX.md that marks blocked pages (403, short body, consent wall). Reads a urls-roundN.txt (URL, then a note)S1bBuilt 2026-11-01 for the Videocon run: 18 of 23 and 19 of 28 links saved; blocked: Quartz, Moneylife, Finology, The Wire, an ICMR case study, Business Standard amp pages. Saves text, does not judge it. A scanned PDF (CBI FIR) gives no text with pdftotext
session-log.jsD[--jsonl --since --until --prompts --user --out] -> every search, fetch and subagent of a Claude Code session as markdownS11Produced research-log.md
tts-gemini.jsD--script --spec --out --name -> TTS-<name>-gemini-3.1.md and -3.8.md: per-beat Gemini TTS prompts. Optional spec lead_in (e.g. "This is {beat} of ...") is prepended to every transcript and an align-<name>-<beat>.txt is written per beat for the trim step (2026-11-02). The spec says where tags and stress capitals go; the tool adds rule-based normalisations (rupees, dollars, spaced acronyms) and refuses to write unless stripping tags and capitals gives the script back word for word, digits survive, no tags touch and every tag is documentedS11 (audio)Built 2026-10-25 to replace a scratch generator that was lost. Tested: Moser Baer v7 Hinglish (14 beats, 61 tags, 3,555 words) and English (61 tags, 3,066 words). Sound is not tested; the A/B tests T1 to T7 are in TTS-NOTES-gemini.md
tts-gemini-record.jsD--prompt <TTS-...-gemini-3.1.md> --beat B2 --out <file.wav> [--model gemini-3.1-flash-tts-preview] [--voice Kore] -> one beat's audio from the Gemini API as WAV (24 kHz mono 16-bit). Key from GEMINI_API_KEY or the project .env, never printedS11 (audio)Added 2026-11-02. Tested once: Videocon v1-short B2 (53.4 s, 136 words), voice Kore (the project's AI Studio voice was not recorded; confirm it matches). Retries once on 429/503 (not exercised live)
build-source.js (in .claude/skills/deploy-docs/scripts/)D[--report] -> every skill, agent and code file in the repository becomes a page under docs/source/, plus docs/15-skills-and-code.html; adds page 15 to every side navigation; links file names in the docs pages to those pages; checks every link. Reads the user name and e-mail at run time and shows the home directory as <home>; never publishes research/, data/, lock files, .env, files over 400 KB or any file with a key-like stringRecord (docs)Added 2026-11-03. First build: 297 files, 284 links, 0 excluded. Run by deploy-docs.js before each deploy
deploy-docs.js (in .claude/skills/deploy-docs/scripts/)D[--dry] [--no-build] -> builds the source pages, stages only docs/*.html, docs/source/**/*.html and docs/assets/**, checks structure and every link, deploys with wrangler 3, verifies every page and a sample of source pages liveRecord (docs)Extended 2026-11-03 for the source pages; the live checks have not been run on them until the first deploy
tts-trim-lead-in.jsD--wav <raw.wav> --alignment <alignment.json> --out <final.wav> -> cuts the spoken lead-in off a recording at the midpoint of the silence between paragraph 1 (lead-in) and paragraph 2 (script). Refuses if either boundary word is interpolated or the gap is under 0.05 sS11 (audio)Added 2026-11-02. Self-tested on a synthetic WAV; live test on Videocon B2: cut at 5.340 s, lead-in 4.500 s, 124 of 136 words left, re-transcribed as "By 2008 ..." at 0.52 s
hinglish-parity.jsD--english --hinglish [--allow --max-ratio] -> same beats and titles, same paragraph count, same digits, question marks, direct quotes, capitalised names and graphics per paragraph; word-count ratio per beatafter writing a Hinglish (or other) versionMoser Baer v7: passes (3,029 to 3,519 words, 16% longer; hook 18% longer). Checks facts and structure only, not how it sounds
compare.jsD--a --b --sa --sb --out -> side-by-side text-level measures and beat openersS8v3/v4, v4/v5, v5/v6
scaffold.jsD--dir --version [--beats --title] [--from vP] -> blank script, chain, FACTS.md, HANDOFF-vN.md; or the next version copied from the lastS0Blank and --from tested
rules.jsondataevery threshold, with its reason; the bar blockread, never edited to passlessons gate
lang-hi.jsondatathe Hinglish overlay for lint.js --lang hi: word lists (number words, units, stop words, starters), the connectives and greetings, the human and comparator words, the hinge pronouns, and the voice patterns (viewer address, figure markers, contrast and cost words, hypothetical markers, lab voice), added to the English ones; no_contractions skips the English contraction floor. A Hinglish script uses its own chain (quotes translated), not this fileS8 (Hinglish)Added 2026-11-02 for Videocon v1-short Hinglish. Lint 100/100 with 0 WARN; damage test 55 of 55 (one skip: no hinge line uses a pronoun, so that rule has nothing to break)
voice-patterns.jsondataregexes for negation pivots, run-ups, stock phrases, comparison markers, stock figures, verdict words, viewer addressread by lint.js

C. Repo tools used upstream (not mine, documented for the runbook)

ToolKindWhat it doesNotes
src/topic-research/cli.js "<topic>" --entity= --max= --also= --refreshL (extraction and synthesis by an LLM)search -> transcripts -> extract -> synthesize -> research/<slug>/report.md and synthesis.json, cachedS1, full procedure in references/s1-rival-brief.md. Captions are noisy; treat as leads. 2026-10-25: --refresh fixed to delete only its own files (it used to remove the whole research/<slug>/ folder, including script/). Tested on a 3-video run (8 min 21 s)
src/topic-research/export-csv.js <dir>, export-cohort.js <dir> <outDir> <ids...>D (network)every video seen with channel subscriber counts; transcripts in the teardown layoutS2 (references/s2-teardown.md). Trim candidates first or export-csv visits ~100 channels. Tested on the scratch run
src/series-analysis/* (npm run series -- <plan|search|mine|enrich|judge|rank|transcripts|cohorts|report> --series=<id>)D + Lfind, enrich, rank and cohort videos for a series; README-SERIES-ANALYSIS.mdphase 0
src/series-analysis/topic-study.js --series=fall-of-xDwhich subjects (companies, people) draw views, not which single video wins (uncommitted at the time of writing; belongs to the topic-selection stage, before S0)
src/transcript-fetch.js, src/video-info-fetch.jsDcaption and metadata fetchused by the stages above
.claude/agents/script-rater.mdLblind rater for the cohort skill

C2. Video stage (research/the-fall-of-moser-baer/video/, built 2026-10-25)

Word timing for the Gemini TTS beats. Plan and reasoning: research/the-fall-of-moser-baer/video/PRODUCTION-PLAN.md. Python 3.8 venv at <home>/.venvs/whisper38 (torch 2.4.1, transformers 4.46.3); run with -u and PYTHONIOENCODING=utf-8 on Windows.

ToolKindInput -> outputNotes
video/tools/beat_text.pyDTTS-*-gemini-3.1.md -> video/timing/reference-words.json (words with tags, paragraph ends, stress, question and number flags)Moser Baer v7 Hinglish: 14 beats, 3,555 words, 61 tags, 12 stress words. The 3.8 file uses a different layout and was not parsed
video/tools/whisper_words.pyL (Whisper decodes; word times depend on the model)beat WAVs -> video/timing/raw/B#.whisper.jsonopenai/whisper-base from the local HF cache. --language en translates (rejected). A Roman prompt causes zero-length word times (rejected). Default hi with no prompt is used
video/tools/align_words.pyDreference words + Whisper words + WAVs + SCRIPT-v7.md -> B#.json, words.json (global timeline), align-report.mdConsonant-skeleton matching, LCS Dice >= 0.6, interpolation, end snap to energy minima, silence and pause report. Reads Urdu and Devanagari script. Tested on the v7 Hinglish beats only

C3. Video visual stage (.claude/skills/video-visual-production/, built 2026-11-01)

ToolKindWhat it doesTested on
video-visual-production/scripts/assets.jsD (network)Fetches a Commons file with a free licence (refuses any other) and captures article passages with headless Chrome over the DevTools protocol: crops to the lines of one passage, records phrase boxes, URL, retrieval date and text hash; writes assets.csv and credits-B1.txt. A failed capture is recorded and the scene falls back to a typeset cardB1: three crops from one Business Today page, one Commons photo
video-visual-production/scripts/beatsheet-lint.jsDHard gates H1 to H8, T1, A3, A4, STRUCT and warnings P, L, C2, SYNC on the beat sheet; --damage runs 15 mutations that must each be caughtB1 sheet: 0 hard, 15 of 15 caught
video-visual-production/scripts/timeline.jsDBeat sheet and word times to integer frames at 30 fps, cue seconds per scene, sound events at cue plus contact offset, run-time reportB1
render/render.js, scenes.js, draw.js, contacts.jsDTwelve scene types as pure canvas functions; frames piped as raw RGBA to ffmpeg; --audit lists every text box; --stills writes PNGsB1
video-visual-production/scripts/sfx.js, video-visual-production/scripts/mix.jsDSynthesised tick, thud, stamp, whoosh, swipe, paper, pivot, clink, clock; ducked placeholder drone; gain plus limiter to -14 LUFS and under -1 dBTP (loudnorm linear mode refuses this voice)B1
video-visual-production/scripts/captions.jsDSRT sidecar from word times (not burned in)B1
video-visual-production/scripts/check-video.jsDQ0 to Q4, A1 to A3, S2, T2 to T4, H3 and H9 on the rendered file; contact sheet; spectrogram; writes STATUS-video-B1.md. Never reports BAR METB1
video-visual-production/scripts/contact-sheet.jsDLabelled grid of framesB1
video-visual-production/scripts/build-beat.jsDRuns every stage for any beat sheet (preflight, assets, lint, timeline, cached render, sound, mux, captions, review sheet, check); only changed scenes re-renderB1: first build 2.5 min render, edits re-render only the touched scene
video-visual-production/scripts/edit.jsDCheap edits to a beat sheet: words, show, set, cue, level, sfx-remove, undo, history. Each change is snapshotted, linted, logged and reverted if it breaks a hard gateB1
video-visual-production/scripts/preview-scene.jsDOne scene as a PNG in about a second, no videoB1
video-visual-production/scripts/review.jsDWrites REVIEW-<beat>.md: scene ids, times, what the voice says, what is on screen, sounds, and which changes are cheapB1
video-visual-production/scripts/new-beat.jsDScaffolds a valid beat sheet (one plain typographic scene per sentence) for a new aligned beatB2 (scratch)render/scenes-b0.jsDNine more scene types for the hook: disc (turning or filling a grid), black (declared), plant (dark windows, gate, date, notice, escape arrow), workers (tally marks, pay slip), person, chips3, dots, loop, pivotnoB0
video-visual-production/scripts/assets.js kind quoteDA short fragment typeset on purpose when the source is a PDF; recorded with its URL and hash; the on-screen tag says "(typeset quote)"B0 CARE rating note
render/scenes-data.js, render/selftest-scenes.jsDTwelve generic data and diagram scenes (stat, bars, line, photo2, chips, race, stalls, treadmill, unitgrid, stamps, paths, rungs), each with a working example; the self-test draws every example and fails on an error or text off the canvasself-test: 11 draw (photo2 needs an image)
video-visual-production/scripts/align-beat.jsD (calls Groq)One command: Groq Whisper (groq-transcribe) plus script-audio-align plus conversion; writes words-<beat>.json with local times; refuses under 85% matchedB0 90.2%, B2 90.6%
video-visual-production/scripts/brief-beat.jsDWrites brief-<beat>.md: sentence table with word indexes and times, spoken numerals, graphic note, plan section, candidate ledger rowsB2
video-visual-production/scripts/words-from-alignment.jsDConverts the aligner output into the pipeline words fileB0, B2
build slots (withSlot in video-visual-production/scripts/lib/util.js)Dbuild-beat.js holds one of two lock folders under C:/video-build/.slots so parallel agents queue instead of exhausting the 6 GB of RAMuntested under load until the first parallel batch
`.claude/skills/video-beat-preparestoryboardbuildreview and video-beats-orchestrate`HThe parallel pipeline: Haiku for prepare and build, Sonnet for storyboard and review, enforced by the agent files in .claude/agents/video-beat-*.md and by passing model explicitlyfirst batch B2 to B5
render/scenes-more.jsDSeven more generic scenes: calendar, deadline (pie clock with a day count), layers, objects (items with price tags), cards, queue, stopbtn; examples in EXAMPLES, drawn by the self-test (18 types)self-test; B6 to B9
video-visual-production/scripts/compose-beat.jsDCompiles a compact plan (plan-<beat>.json: sentence numbers, "@word" cues, props, facts) into the full beat sheet and ledger: word spans, cue indexes, on-screen text lists, spoken-as mappings, default sounds thinned to the limits, tiling and coverage errors in plain words. Saves the storyboarder most of its tokensB6 to B9 (and a scratch B2 plan)
video-visual-production/references/plan-format.md, render/make-plan-format.jsDThe storyboarder's whole manual (plan format, sentence-to-scene table, one example per type), generated from the verified EXAMPLES so it cannot driftB6 to B9
video-visual-production/scripts/align-beat.js (updated)D (calls Groq)Three attempts: turbo whole beat, large-v3 whole beat, then the beat cut at silences into about 30 s chunks; keeps the best; under 85% matched it stops. B3 needed the chunked pass (63.5%, 77.1%, then 87.0%)B0 to B9
slot locks (withSlot updated)DA lock whose owner process has died is removed at once (a killed build had blocked the queue for 30 minutes); otherwise stale after 12 minutesB5 incident
.claude/agents/video-beat-*.md (updated)HAll four stages run on Haiku (the user's decision after the first batch cost too many tokens on Sonnet storyboarding); the orchestrator may escalate one failed stage once to SonnetB6 to B9
video-visual-production/scripts/smoke-b1.jsDRuns build-beat for B1 with the lint damage testB1
video-visual-production/scripts/stitch.jsDStitches the built beats into one video: per-pair joins (dissolve 12 frames with the outgoing fade trimmed, dip through black, or cut; set in score-spec.json), only the join pieces blended, every piece cut by frame number and encoded once, joined with -c copy; voice and effects stems placed on one timeline (voice evened toward the median by at most 4 dB), the score added, normalised to -14 LUFS under a true-peak ceiling and re-measured on the AAC file (it lowers the ceiling and remuxes if the AAC overshoots); writes final.mp4, final-mix.wav, final-score-only.wav, final.srt, chapters.txt, seams.png, stitch-plan.json and STITCH-REPORT.md; checks frame count, A/V drift, loudness, true peak and undeclared blackB2 to B4 (test), then all 14
video-visual-production/scripts/score.js, research/<topic>/video/score-spec.jsonDAmbient score by additive synthesis, one spec per beat (root note, chords, brightness, sub drone, faint air, soft pulse that can quicken, level arc, reverb); crossfaded, ducked 3 dB under speech and gated in hard-stop black scenes by stitch.js. A plain pad, not a composed score; nobody has listened to itaudition clip of B4 (loudness measured only)
video-visual-production/scripts/mix.js (updated)DVoice and effects stems are now 32-bit float (a 16-bit stem clipped the voice, whose peaks run to +4 dBFS before the final limiter); normalize and measure are exportedall 14 beats
video-visual-production/scripts/build-beat.js (updated)DAAC at 256 kbps: at 192 kbps B11's file measured +1.6 dBTP although the WAV was -3.3B11
meme-fetcher/SKILL.md, meme-fetcher/scripts/library.cjsDSkill: from a situation, search the meme library then the web (Google AI Mode, Know Your Meme, Imgflip), verify, and add new memes to research/meme-library/catalogue.json with use-when and don't-use-when. Tool: search, exists, add, list, validate. Refuses tier D and non-image downloads, 5 MB cap; dedupes on name or alias, source or image URL, and image bytes (exit 2). Stills only: clips are catalogue-only. Nothing is cleared; the tiers are provisional until the video rules are revisitedduplicate, invalid and tier D cases tested; full flow run on the Videocon short's four open situations (7 entries added, 14 in the library)
meme-fetcher/scripts/gallery.cjsDBuilds research/meme-library/index.html from the catalogue: a filterable gallery (search by situation, tier and file filters, a Videocon beat map that links to cards, use-when and don't-use-when side by side, notes and sources collapsed). Dark and light. add in library.cjs now takes a JSON arrayopened on localhost: 14 cards, images load, beat map and filters work; narrow-screen layout not looked at

D. Scratch tools that were promoted (so you do not rebuild them)

Scratch script (session scratchpad)What it didNow
calib.js, persona.js, winners-lint.jsmeasured voice, comparisons, admissions, rhythm windows on winnerscalibrate.js
research-log.js, users.js, hist.jspulled searches, fetches, prompts and user messages from the session transcriptsession-log.js
reader/ed.js, vo-only.txtmade the numbered blind file for the readersextract-vo.js --mode blind
the awk one-liner I used to read the spoken textextract-vo.js
mk-tail.jsassembled the ledger sections of v6 from v5'sone-off; the scaffold.js templates now hold the sections
manual timecode editsretime.js
the Gemini TTS prompt generator (a scratch script in an earlier session; lost, so the v6 files could not be regenerated)per-beat prompts with tags, stress capitals and normalisationstts-gemini.js
the throwaway Hinglish parity check (beats, 56 paragraphs, digits, names, question marks)checked the v6 Hinglish against the Englishhinglish-parity.js

E. Known gaps (candidates for the next tools)

  1. Cohort rubric v4 for the E to M checks, so script-cohort-analysis can test them blind when there are enough comparable scripts.
  2. A rule that counts named people and unfamiliar terms per 30 seconds (the v4 readers found dense paragraphs that figure counts missed). Partly done in round 7: K3 counts new people and organisations per beat, K4 asks a name that appears once for a reason, M1 asks a dense beat for a headline. Still open: terms per 30 seconds, and a calibration of the budgets on winners.
  3. A check that a script's [CHECK] rows are the same set as its open items (today only the presence of the words is checked).
  4. A speech-timing check with a real text-to-speech pass (word counts at 150 wpm are an estimate; v3 was labelled 12 minutes and ran 9.5).
  5. A packaging check (title and thumbnail) in the linter; today the title tension is a judgement written in the hand-off.
  6. A second-topic validation run of the whole runbook. Until it is done, "reaches v6 in one go" is a design intent, not a result.

F. Memory notes that carry this forward (~/.claude/projects/.../memory/)

feedback_script-lessons-stakes-loop-chain (the user's rules A to M and preferences), project_moser-baer-script (the state of the script), project_why-x-died-series-study (phase 0), reference_script-pipeline-docs (where all of this lives).

Show the raw text
# Tools and skills registry

**Rule: when you create or change a skill, a script or a template for this pipeline, add or update its row here in the same session**, with what it does, its input and output, whether it is deterministic, and what it was tested on. This file is the answer to "what do we already have?" and the guard against building the same scratch script twice. (The standing instruction is also in the repo's `CLAUDE.md`.)

Kinds: **D** deterministic (same input, same output, no model), **L** uses a model (results vary), **H** a procedure for the agent to follow. Paths are from the repo root.

## A. Skills

| Skill | Kind | What it does | Used at | Notes |
|---|---|---|---|---|
| `video-script-production` (`.claude/skills/video-script-production/`) | D + H | Writes and checks a script to the v6 bar: lessons A to M, linter, damage test, gate runner, ledger check, scaffold, retime, calibration, session log. Entry point `SKILL.md`; procedure `references/runbook.md` | S0, S3 to S11 | Built 10-24, extended 10-25. Tested on Moser Baer v4, v5, v6 only |
| `winning-video-teardown` (`prepare.js`, `validate.js`, `aggregate.js`, `report.js`; `prepare.js --perf-unknown` added 2026-10-25) | L (Sonnet analysts) + D (validate, aggregate, report) | Takes winning videos, finds each mechanism and quotes the moment; counts which recur. Taxonomy of script, packaging and thumbnail mechanisms; junction types; first-payoff fields | S2 | Taxonomy extended for lessons A to D (10 mechanisms) and E to I (S-IMPLICIT-PULL, S-PLAIN-VOICE, S-LANDING, S-PARABLE, S-TWO-SIDED) |
| `script-cohort-analysis` | L (Haiku raters via `script-rater`) + D | Blind, pre-registered statistical test of which script rules predict performance in a cohort of transcripts. Rubric v3: 45 checks, 11 dimensions | after enough comparable scripts exist | **Gap: the rubric does not yet include the E to J checks** (a rubric v4 is the next step); the ledger H-012 to H-017 is gitignored |
| `within-channel-study` | D + H | What separates a channel's best, median and worst videos | channel selection | Not used in the script work |
| `ecosystem-rater` | D | Rates niches on demand, room, evergreen-ness, upload pressure | niche selection | Not used in the script work |
| `video-visual-production` (`.claude/skills/video-visual-production/`, built 2026-11-01) | D + H | Turns a voice-over, its aligned word times and a hand-written beat sheet into a rendered video beat: asset fetch and article capture, lint with a damage test, frame timeline, canvas renderer piped to ffmpeg, synthesised effects, ducked placeholder bed, loudness-normalised mix, and a measured check. Entry point `SKILL.md`; plan in `research/video-production/` | Video stage, after the script and the voice-over | First proof is the B1 smoke test (`video-visual-production/scripts/smoke-b1.js`); status in `research/the-fall-of-moser-baer/video/STATUS-video-B1.md`. Only B1 and B2 are aligned |

## B. Scripts in `video-script-production/scripts/`

| Script | Kind | Input -> output | When | Tested on / notes |
|---|---|---|---|---|
| `check.js` | D | `--dir --version [--prev] [--fix] [--no-selftest] [--lang hi --english <version>]` (the last two for a Hinglish version: lint and damage test with `lang-hi.json`, the ledger from the English script, and parity as gate P1; added 2026-11-02) -> runs every gate, writes `STATUS-vN.md` and `lint/`, prints `BAR MET` or what to do next; a chain may carry `bar_overrides: {hook_sec: [min, max], reason}` (hook length only; gate T4 requires the reason) | end of every stage; session start | v6 (all gates pass except the reader pass); a blank scaffold; a copy of v6 as v7. Exit 1 on a hard gate |
| `lint.js` | D | `--script --chain [--label --out] [--lang hi]` (`--lang hi` adds the Hinglish word lists and patterns from `lang-hi.json` to the English ones; 2026-11-02) -> `lint-<label>.json/.md`; exit 1 on FAIL | S8 | 70 rules (A1 to J2, B8, F1 to F5, G1 to G4, H1 to H5, I1 to I6, K1 to K8, L1 to L4, M1 to M3). K, L, M added 2026-10-25 (review round 7): an old chain that declares none of their fields gets WARNs, not FAILs, so older scripts still pass the hard gates (the Cafe Coffee Day v1 chain: 0 FAIL, 9 WARN). Text-only mode when no chain |
| `selftest.js` | D | `--script --chain [--lang hi]` (the mutations are language-neutral where they can be; 2026-11-02) -> breaks one property at a time and checks the matching rule fires | S8, and whenever a rule or `lint.js` changes | 56 mutations on v7 (42 on v6 before round 7; v6 now: 46 behave, 10 skipped as undeclared; mutations touch only the voice-over lines, because a quote can also occur in the notes sections); on the v5 chain 28 behave and 14 are skipped as undeclared; the same 42 pass on a copy of v6 with every person and the company renamed (names are not hard-coded) |
| `ledger-check.js` | D | `--script [--chain --facts --strict] [--ledger-from <English script>]` (a translation takes its ledger and hand-off sections from the English script; 2026-11-02) -> every spoken digit figure is in the ledger; the four hand-off sections exist; [CHECK] rows reach the open items | S8 | Digit figures only (spelled numbers are not read). Found 4 real gaps in v5 and v6 on first run |
| `retime.js` | D | `--script [--write --check]` -> heading time ranges from word counts (2026-11-02: it rounded the seconds before splitting the minutes, so a heading could read 6:60; it now rounds the total first) | S8 | v6 (5 stale headings rewritten, body unchanged) |
| `extract-vo.js` | D | `--script [--mode timed|blind|plain --out]` -> the spoken text, numbered | S9, S10 | `blind` is what readers get |
| `calibrate.js` | D | `--dir <winner transcripts> [--script ...] [--out]` -> voice, rhythm, comparison, admission, "you", name measures with medians | when thresholds are set or new winners arrive | Reproduced the numbers behind the rhythm thresholds (8 English winners). Corrected an arithmetic mistake in my own notes |
| `fetch-articles.mjs` | D | `--dir <folder> (--urls file \| --url U ...) [--pause ms]` -> one `NN-host.txt` per page (URL, date, HTTP status, title, note, text), PDFs saved as `.pdf`, and an `INDEX.md` that marks blocked pages (403, short body, consent wall). Reads a `urls-roundN.txt` (URL, then a note) | S1b | Built 2026-11-01 for the Videocon run: 18 of 23 and 19 of 28 links saved; blocked: Quartz, Moneylife, Finology, The Wire, an ICMR case study, Business Standard amp pages. Saves text, does not judge it. A scanned PDF (CBI FIR) gives no text with `pdftotext` |
| `session-log.js` | D | `[--jsonl --since --until --prompts --user --out]` -> every search, fetch and subagent of a Claude Code session as markdown | S11 | Produced `research-log.md` |
| `tts-gemini.js` | D | `--script --spec --out --name` -> `TTS-<name>-gemini-3.1.md` and `-3.8.md`: per-beat Gemini TTS prompts. Optional spec `lead_in` (e.g. "This is {beat} of ...") is prepended to every transcript and an `align-<name>-<beat>.txt` is written per beat for the trim step (2026-11-02). The spec says where tags and stress capitals go; the tool adds rule-based normalisations (rupees, dollars, spaced acronyms) and refuses to write unless stripping tags and capitals gives the script back word for word, digits survive, no tags touch and every tag is documented | S11 (audio) | Built 2026-10-25 to replace a scratch generator that was lost. Tested: Moser Baer v7 Hinglish (14 beats, 61 tags, 3,555 words) and English (61 tags, 3,066 words). Sound is not tested; the A/B tests T1 to T7 are in `TTS-NOTES-gemini.md` |
| `tts-gemini-record.js` | D | `--prompt <TTS-...-gemini-3.1.md> --beat B2 --out <file.wav> [--model gemini-3.1-flash-tts-preview] [--voice Kore]` -> one beat's audio from the Gemini API as WAV (24 kHz mono 16-bit). Key from `GEMINI_API_KEY` or the project `.env`, never printed | S11 (audio) | Added 2026-11-02. Tested once: Videocon v1-short B2 (53.4 s, 136 words), voice Kore (the project's AI Studio voice was not recorded; confirm it matches). Retries once on 429/503 (not exercised live) |
| `build-source.js` (in `.claude/skills/deploy-docs/scripts/`) | D | `[--report]` -> every skill, agent and code file in the repository becomes a page under `docs/source/`, plus `docs/15-skills-and-code.html`; adds page 15 to every side navigation; links file names in the docs pages to those pages; checks every link. Reads the user name and e-mail at run time and shows the home directory as `<home>`; never publishes `research/`, `data/`, lock files, `.env`, files over 400 KB or any file with a key-like string | Record (docs) | Added 2026-11-03. First build: 297 files, 284 links, 0 excluded. Run by `deploy-docs.js` before each deploy |
| `deploy-docs.js` (in `.claude/skills/deploy-docs/scripts/`) | D | `[--dry] [--no-build]` -> builds the source pages, stages only `docs/*.html`, `docs/source/**/*.html` and `docs/assets/**`, checks structure and every link, deploys with wrangler 3, verifies every page and a sample of source pages live | Record (docs) | Extended 2026-11-03 for the source pages; the live checks have not been run on them until the first deploy |
| `tts-trim-lead-in.js` | D | `--wav <raw.wav> --alignment <alignment.json> --out <final.wav>` -> cuts the spoken lead-in off a recording at the midpoint of the silence between paragraph 1 (lead-in) and paragraph 2 (script). Refuses if either boundary word is interpolated or the gap is under 0.05 s | S11 (audio) | Added 2026-11-02. Self-tested on a synthetic WAV; live test on Videocon B2: cut at 5.340 s, lead-in 4.500 s, 124 of 136 words left, re-transcribed as "By 2008 ..." at 0.52 s |
| `hinglish-parity.js` | D | `--english --hinglish [--allow --max-ratio]` -> same beats and titles, same paragraph count, same digits, question marks, direct quotes, capitalised names and graphics per paragraph; word-count ratio per beat | after writing a Hinglish (or other) version | Moser Baer v7: passes (3,029 to 3,519 words, 16% longer; hook 18% longer). Checks facts and structure only, not how it sounds |
| `compare.js` | D | `--a --b --sa --sb --out` -> side-by-side text-level measures and beat openers | S8 | v3/v4, v4/v5, v5/v6 |
| `scaffold.js` | D | `--dir --version [--beats --title] [--from vP]` -> blank script, chain, `FACTS.md`, `HANDOFF-vN.md`; or the next version copied from the last | S0 | Blank and `--from` tested |
| `rules.json` | data | every threshold, with its reason; the `bar` block | read, never edited to pass | `lessons gate` |
| `lang-hi.json` | data | the Hinglish overlay for `lint.js --lang hi`: word lists (number words, units, stop words, starters), the connectives and greetings, the human and comparator words, the hinge pronouns, and the voice patterns (viewer address, figure markers, contrast and cost words, hypothetical markers, lab voice), added to the English ones; `no_contractions` skips the English contraction floor. A Hinglish script uses its own chain (quotes translated), not this file | S8 (Hinglish) | Added 2026-11-02 for Videocon v1-short Hinglish. Lint 100/100 with 0 WARN; damage test 55 of 55 (one skip: no hinge line uses a pronoun, so that rule has nothing to break) |
| `voice-patterns.json` | data | regexes for negation pivots, run-ups, stock phrases, comparison markers, stock figures, verdict words, viewer address | read by `lint.js` | |

## C. Repo tools used upstream (not mine, documented for the runbook)

| Tool | Kind | What it does | Notes |
|---|---|---|---|
| `src/topic-research/cli.js "<topic>" --entity= --max= --also= --refresh` | L (extraction and synthesis by an LLM) | search -> transcripts -> extract -> synthesize -> `research/<slug>/report.md` and `synthesis.json`, cached | S1, full procedure in `references/s1-rival-brief.md`. Captions are noisy; treat as leads. 2026-10-25: `--refresh` fixed to delete only its own files (it used to remove the whole `research/<slug>/` folder, including `script/`). Tested on a 3-video run (8 min 21 s) |
| `src/topic-research/export-csv.js <dir>`, `export-cohort.js <dir> <outDir> <ids...>` | D (network) | every video seen with channel subscriber counts; transcripts in the teardown layout | S2 (`references/s2-teardown.md`). Trim `candidates` first or `export-csv` visits ~100 channels. Tested on the scratch run |
| `src/series-analysis/*` (`npm run series -- <plan\|search\|mine\|enrich\|judge\|rank\|transcripts\|cohorts\|report> --series=<id>`) | D + L | find, enrich, rank and cohort videos for a series; `README-SERIES-ANALYSIS.md` | phase 0 |
| `src/series-analysis/topic-study.js --series=fall-of-x` | D | which subjects (companies, people) draw views, not which single video wins (uncommitted at the time of writing; belongs to the topic-selection stage, before S0) | |
| `src/transcript-fetch.js`, `src/video-info-fetch.js` | D | caption and metadata fetch | used by the stages above |
| `.claude/agents/script-rater.md` | L | blind rater for the cohort skill | |

## C2. Video stage (`research/the-fall-of-moser-baer/video/`, built 2026-10-25)

Word timing for the Gemini TTS beats. Plan and reasoning: `research/the-fall-of-moser-baer/video/PRODUCTION-PLAN.md`. Python 3.8 venv at `<home>/.venvs/whisper38` (torch 2.4.1, transformers 4.46.3); run with `-u` and `PYTHONIOENCODING=utf-8` on Windows.

| Tool | Kind | Input -> output | Notes |
|---|---|---|---|
| `video/tools/beat_text.py` | D | `TTS-*-gemini-3.1.md` -> `video/timing/reference-words.json` (words with tags, paragraph ends, stress, question and number flags) | Moser Baer v7 Hinglish: 14 beats, 3,555 words, 61 tags, 12 stress words. The 3.8 file uses a different layout and was not parsed |
| `video/tools/whisper_words.py` | L (Whisper decodes; word times depend on the model) | beat WAVs -> `video/timing/raw/B#.whisper.json` | `openai/whisper-base` from the local HF cache. `--language en` translates (rejected). A Roman prompt causes zero-length word times (rejected). Default `hi` with no prompt is used |
| `video/tools/align_words.py` | D | reference words + Whisper words + WAVs + `SCRIPT-v7.md` -> `B#.json`, `words.json` (global timeline), `align-report.md` | Consonant-skeleton matching, LCS Dice >= 0.6, interpolation, end snap to energy minima, silence and pause report. Reads Urdu and Devanagari script. Tested on the v7 Hinglish beats only |

## C3. Video visual stage (`.claude/skills/video-visual-production/`, built 2026-11-01)

| Tool | Kind | What it does | Tested on |
|---|---|---|---|
| `video-visual-production/scripts/assets.js` | D (network) | Fetches a Commons file with a free licence (refuses any other) and captures article passages with headless Chrome over the DevTools protocol: crops to the lines of one passage, records phrase boxes, URL, retrieval date and text hash; writes `assets.csv` and `credits-B1.txt`. A failed capture is recorded and the scene falls back to a typeset card | B1: three crops from one Business Today page, one Commons photo |
| `video-visual-production/scripts/beatsheet-lint.js` | D | Hard gates H1 to H8, T1, A3, A4, STRUCT and warnings P, L, C2, SYNC on the beat sheet; `--damage` runs 15 mutations that must each be caught | B1 sheet: 0 hard, 15 of 15 caught |
| `video-visual-production/scripts/timeline.js` | D | Beat sheet and word times to integer frames at 30 fps, cue seconds per scene, sound events at cue plus contact offset, run-time report | B1 |
| `render/render.js`, `scenes.js`, `draw.js`, `contacts.js` | D | Twelve scene types as pure canvas functions; frames piped as raw RGBA to ffmpeg; `--audit` lists every text box; `--stills` writes PNGs | B1 |
| `video-visual-production/scripts/sfx.js`, `video-visual-production/scripts/mix.js` | D | Synthesised tick, thud, stamp, whoosh, swipe, paper, pivot, clink, clock; ducked placeholder drone; gain plus limiter to -14 LUFS and under -1 dBTP (loudnorm linear mode refuses this voice) | B1 |
| `video-visual-production/scripts/captions.js` | D | SRT sidecar from word times (not burned in) | B1 |
| `video-visual-production/scripts/check-video.js` | D | Q0 to Q4, A1 to A3, S2, T2 to T4, H3 and H9 on the rendered file; contact sheet; spectrogram; writes `STATUS-video-B1.md`. Never reports BAR MET | B1 |
| `video-visual-production/scripts/contact-sheet.js` | D | Labelled grid of frames | B1 |
| `video-visual-production/scripts/build-beat.js` | D | Runs every stage for any beat sheet (preflight, assets, lint, timeline, cached render, sound, mux, captions, review sheet, check); only changed scenes re-render | B1: first build 2.5 min render, edits re-render only the touched scene |
| `video-visual-production/scripts/edit.js` | D | Cheap edits to a beat sheet: words, show, set, cue, level, sfx-remove, undo, history. Each change is snapshotted, linted, logged and reverted if it breaks a hard gate | B1 |
| `video-visual-production/scripts/preview-scene.js` | D | One scene as a PNG in about a second, no video | B1 |
| `video-visual-production/scripts/review.js` | D | Writes REVIEW-<beat>.md: scene ids, times, what the voice says, what is on screen, sounds, and which changes are cheap | B1 |
| `video-visual-production/scripts/new-beat.js` | D | Scaffolds a valid beat sheet (one plain typographic scene per sentence) for a new aligned beat | B2 (scratch) || `render/scenes-b0.js` | D | Nine more scene types for the hook: disc (turning or filling a grid), black (declared), plant (dark windows, gate, date, notice, escape arrow), workers (tally marks, pay slip), person, chips3, dots, loop, pivotno | B0 |
| `video-visual-production/scripts/assets.js` kind `quote` | D | A short fragment typeset on purpose when the source is a PDF; recorded with its URL and hash; the on-screen tag says "(typeset quote)" | B0 CARE rating note |
| `render/scenes-data.js`, `render/selftest-scenes.js` | D | Twelve generic data and diagram scenes (stat, bars, line, photo2, chips, race, stalls, treadmill, unitgrid, stamps, paths, rungs), each with a working example; the self-test draws every example and fails on an error or text off the canvas | self-test: 11 draw (photo2 needs an image) |
| `video-visual-production/scripts/align-beat.js` | D (calls Groq) | One command: Groq Whisper (groq-transcribe) plus script-audio-align plus conversion; writes words-<beat>.json with local times; refuses under 85% matched | B0 90.2%, B2 90.6% |
| `video-visual-production/scripts/brief-beat.js` | D | Writes brief-<beat>.md: sentence table with word indexes and times, spoken numerals, graphic note, plan section, candidate ledger rows | B2 |
| `video-visual-production/scripts/words-from-alignment.js` | D | Converts the aligner output into the pipeline words file | B0, B2 |
| build slots (`withSlot` in `video-visual-production/scripts/lib/util.js`) | D | build-beat.js holds one of two lock folders under C:/video-build/.slots so parallel agents queue instead of exhausting the 6 GB of RAM | untested under load until the first parallel batch |
| `.claude/skills/video-beat-prepare|storyboard|build|review` and `video-beats-orchestrate` | H | The parallel pipeline: Haiku for prepare and build, Sonnet for storyboard and review, enforced by the agent files in `.claude/agents/video-beat-*.md` and by passing model explicitly | first batch B2 to B5 |
| `render/scenes-more.js` | D | Seven more generic scenes: calendar, deadline (pie clock with a day count), layers, objects (items with price tags), cards, queue, stopbtn; examples in EXAMPLES, drawn by the self-test (18 types) | self-test; B6 to B9 |
| `video-visual-production/scripts/compose-beat.js` | D | Compiles a compact plan (`plan-<beat>.json`: sentence numbers, "@word" cues, props, facts) into the full beat sheet and ledger: word spans, cue indexes, on-screen text lists, spoken-as mappings, default sounds thinned to the limits, tiling and coverage errors in plain words. Saves the storyboarder most of its tokens | B6 to B9 (and a scratch B2 plan) |
| `video-visual-production/references/plan-format.md`, `render/make-plan-format.js` | D | The storyboarder's whole manual (plan format, sentence-to-scene table, one example per type), generated from the verified EXAMPLES so it cannot drift | B6 to B9 |
| `video-visual-production/scripts/align-beat.js` (updated) | D (calls Groq) | Three attempts: turbo whole beat, large-v3 whole beat, then the beat cut at silences into about 30 s chunks; keeps the best; under 85% matched it stops. B3 needed the chunked pass (63.5%, 77.1%, then 87.0%) | B0 to B9 |
| slot locks (`withSlot` updated) | D | A lock whose owner process has died is removed at once (a killed build had blocked the queue for 30 minutes); otherwise stale after 12 minutes | B5 incident |
| `.claude/agents/video-beat-*.md` (updated) | H | All four stages run on Haiku (the user's decision after the first batch cost too many tokens on Sonnet storyboarding); the orchestrator may escalate one failed stage once to Sonnet | B6 to B9 |
| `video-visual-production/scripts/smoke-b1.js` | D | Runs build-beat for B1 with the lint damage test | B1 |
| `video-visual-production/scripts/stitch.js` | D | Stitches the built beats into one video: per-pair joins (dissolve 12 frames with the outgoing fade trimmed, dip through black, or cut; set in `score-spec.json`), only the join pieces blended, every piece cut by frame number and encoded once, joined with -c copy; voice and effects stems placed on one timeline (voice evened toward the median by at most 4 dB), the score added, normalised to -14 LUFS under a true-peak ceiling and re-measured on the AAC file (it lowers the ceiling and remuxes if the AAC overshoots); writes final.mp4, final-mix.wav, final-score-only.wav, final.srt, chapters.txt, seams.png, stitch-plan.json and `STITCH-REPORT.md`; checks frame count, A/V drift, loudness, true peak and undeclared black | B2 to B4 (test), then all 14 |
| `video-visual-production/scripts/score.js`, `research/<topic>/video/score-spec.json` | D | Ambient score by additive synthesis, one spec per beat (root note, chords, brightness, sub drone, faint air, soft pulse that can quicken, level arc, reverb); crossfaded, ducked 3 dB under speech and gated in hard-stop black scenes by stitch.js. A plain pad, not a composed score; nobody has listened to it | audition clip of B4 (loudness measured only) |
| `video-visual-production/scripts/mix.js` (updated) | D | Voice and effects stems are now 32-bit float (a 16-bit stem clipped the voice, whose peaks run to +4 dBFS before the final limiter); `normalize` and `measure` are exported | all 14 beats |
| `video-visual-production/scripts/build-beat.js` (updated) | D | AAC at 256 kbps: at 192 kbps B11's file measured +1.6 dBTP although the WAV was -3.3 | B11 |
| `meme-fetcher/SKILL.md`, `meme-fetcher/scripts/library.cjs` | D | Skill: from a situation, search the meme library then the web (Google AI Mode, Know Your Meme, Imgflip), verify, and add new memes to `research/meme-library/catalogue.json` with use-when and don't-use-when. Tool: `search`, `exists`, `add`, `list`, `validate`. Refuses tier D and non-image downloads, 5 MB cap; dedupes on name or alias, source or image URL, and image bytes (exit 2). Stills only: clips are catalogue-only. Nothing is cleared; the tiers are provisional until the video rules are revisited | duplicate, invalid and tier D cases tested; full flow run on the Videocon short's four open situations (7 entries added, 14 in the library) |
| `meme-fetcher/scripts/gallery.cjs` | D | Builds `research/meme-library/index.html` from the catalogue: a filterable gallery (search by situation, tier and file filters, a Videocon beat map that links to cards, use-when and don't-use-when side by side, notes and sources collapsed). Dark and light. `add` in `library.cjs` now takes a JSON array | opened on localhost: 14 cards, images load, beat map and filters work; narrow-screen layout not looked at |

## D. Scratch tools that were promoted (so you do not rebuild them)

| Scratch script (session scratchpad) | What it did | Now |
|---|---|---|
| `calib.js`, `persona.js`, `winners-lint.js` | measured voice, comparisons, admissions, rhythm windows on winners | `calibrate.js` |
| `research-log.js`, `users.js`, `hist.js` | pulled searches, fetches, prompts and user messages from the session transcript | `session-log.js` |
| `reader/ed.js`, `vo-only.txt` | made the numbered blind file for the readers | `extract-vo.js --mode blind` |
| the `awk` one-liner I used to read the spoken text | | `extract-vo.js` |
| `mk-tail.js` | assembled the ledger sections of v6 from v5's | one-off; the `scaffold.js` templates now hold the sections |
| manual timecode edits | | `retime.js` |
| the Gemini TTS prompt generator (a scratch script in an earlier session; lost, so the v6 files could not be regenerated) | per-beat prompts with tags, stress capitals and normalisations | `tts-gemini.js` |
| the throwaway Hinglish parity check (beats, 56 paragraphs, digits, names, question marks) | checked the v6 Hinglish against the English | `hinglish-parity.js` |

## E. Known gaps (candidates for the next tools)

1. Cohort rubric v4 for the E to M checks, so `script-cohort-analysis` can test them blind when there are enough comparable scripts.
2. A rule that counts **named people and unfamiliar terms** per 30 seconds (the v4 readers found dense paragraphs that figure counts missed). **Partly done in round 7:** K3 counts new people and organisations per beat, K4 asks a name that appears once for a reason, M1 asks a dense beat for a headline. Still open: terms per 30 seconds, and a calibration of the budgets on winners.
3. A check that a script's `[CHECK]` rows are the same set as its open items (today only the presence of the words is checked).
4. A speech-timing check with a real text-to-speech pass (word counts at 150 wpm are an estimate; v3 was labelled 12 minutes and ran 9.5).
5. A packaging check (title and thumbnail) in the linter; today the title tension is a judgement written in the hand-off.
6. A **second-topic validation run** of the whole runbook. Until it is done, "reaches v6 in one go" is a design intent, not a result.

## F. Memory notes that carry this forward (`~/.claude/projects/.../memory/`)

`feedback_script-lessons-stakes-loop-chain` (the user's rules A to M and preferences), `project_moser-baer-script` (the state of the script), `project_why-x-died-series-study` (phase 0), `reference_script-pipeline-docs` (where all of this lives).

Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.