Docs/Skills and code/skill video-script-production
.claude/skills/video-script-production/references/tools.md
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
| 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)
- Cohort rubric v4 for the E to M checks, so
script-cohort-analysiscan test them blind when there are enough comparable scripts. - 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.
- 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). - 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).
- A packaging check (title and thumbnail) in the linter; today the title tension is a judgement written in the hand-off.
- 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.