Docs/Skills and code/skill video-script-production
.claude/skills/video-script-production/references/runbook.md
Runbook: from a topic to a script at the v6 bar: This is the procedure that produced Moser Baer v6, written so that it can be run again on another topic without the author in the room.
Mentioned on: page 04, page 12, page in
Runbook: from a topic to a script at the v6 bar
This is the procedure that produced Moser Baer v6, written so that it can be run again on another topic without the author in the room. It was reconstructed from the session that built it (history.md), not invented afterwards. Read quality-bar.md first: it says what "finished" means. Everything here serves that bar.
The one command that tells you where you are:
node .claude/skills/video-script-production/scripts/check.js --dir research/<slug>/script --version vN [--prev vM] [--fix] [--no-selftest]
It runs every automatable gate, reads the manual gates from HANDOFF-vN.md, infers which stage the files show you have reached, writes STATUS-vN.md, and prints what to do next. Run it at the start of a session (to resume) and at the end of every stage. Its verdicts are HARD GATES FAILING, HARD GATES PASS, BAR NOT YET MET and BAR MET. Stop only on BAR MET, or when the user tells you to stop (then say which stage you reached).
0. Ground rules (these came from the user, in their words or by their corrections)
- Do the research before the prose. The user's repeated instruction: "only after deep research and consideration". The thesis changed once (v4) and the story changed once (v5) because of a source nobody had opened.
- Say plainly what is unproven. Every hand-off states it. The lessons are design constraints, not findings; a linter score of 100 only means the declared structure matches the text.
- Do not spawn subagents, commit, push or publish unless the user asks. The reader pass (Stage 10) needs subagents; if the user has not asked for them, leave it as the first item under "Decisions for you".
- Keep the user's direction. When they say "keep it subtle, I like the current direction", change as little of the pipeline as the lesson needs.
- Never edit
rules.jsonto make a script pass. Fix the script. Change a threshold only when data contradicts it twice, and record why inlearnings.md(seelesson-protocol.md). - Ask only for decisions that are the user's: the title (when the evidence is weaker than the packaging), whether to name a private person, whether to run the readers, and commit/push. Everything else has a default below.
- Shell hygiene. Heredocs and
sedswallow backslashes (a\bbecame a backspace character twice). Write code and regexes with the Write or Edit tool.check.jsscans for stray control characters.
1. Defaults, so nothing stalls (override only if the user says otherwise)
| Decision | Default | Why |
|---|---|---|
| Language | English | Hinglish later; the chain, pulls, figures and cast carry over, parables get rewritten in local terms |
| "First 60 mins" | the first 60 seconds | The user wrote 60 mins; it can only mean seconds. Say so in the hand-off |
| Length | whatever the story needs, 12 to 20 minutes; say it plainly and name the cut candidates | v4 was 12.9 min, v6 19.6 |
| Beats | 12 to 15 beats of 40 to 105 s, hook last to write | beat_words in rules.json (90 to 260 words) |
| Hook | 60 to 110 s; over 75 s it must carry the B8 elements | The user found a 73 s hook "thin" |
| Voice | earnest about facts and people, aloof about verdicts | texture.md |
| Title | give the working title and 2 to 3 alternates; flag any word that outruns the evidence ("killed", "scam"); never silently pick | The user has not chosen for Moser Baer |
| Private people | name only someone who is on the record in a national outlet; flag it in the hand-off | Sharma |
| Allegations | state exactly what is alleged and what a court decided; never "villain", "scam", "fraud" without a conviction | v6 beat 12 |
| Commit / push | do not | The user has not asked since 156897f |
2. The stages
| # | Stage | You produce | Tool | Leave the stage when |
|---|---|---|---|---|
| S0 | Set up | the folder, the lessons gate read | scaffold.js | learnings.md and rules.json read; slug chosen |
| S1 | Rival-video research brief | research/<slug>/report.md, synthesis.json | src/topic-research/cli.js, references/s1-rival-brief.md | brief exists; contested claims and verification queries listed |
| S1b | Harvest existing articles and documents | sources/ (saved pages, INDEX.md), sources/ai-mode-notes.md, a dated TIMELINE-AND-TRIVIA.md | scripts/fetch-articles.mjs, WebSearch, skill google-ai-mode, references/research-protocol.md section 7 | the primary documents (tribunal, court, filing) are saved, 15+ articles are saved or marked blocked, and the timeline lists the figures that disagree |
| S2 | Teardown of rival videos | teardown/SYNTHESIS.md | skill winning-video-teardown, references/s2-teardown.md | table stakes and differentiators known (skip if the series teardown already covers the packaging) |
| S3 | Fact base | FACTS.md | web search and fetch, references/research-protocol.md | 5+ sourced rows per beat-worth of story; each causal link sourced; each magnitude cross-checked; people researched; do-not-use table started |
| S4 | Thesis and packaging | chain.cdl, script sections 1 and 2 | references/opening-protocol.md | four scored CDL candidates, one chosen; title tension stated; withheld list written |
| S5 | Chain and texture plan | chain-vN.json complete | scaffold.js, references/chain-schema.md, texture.md | every section of the chain filled (beats with arcs, junctions, hero numbers with landings, 6+ pulls, cast, viewer stakes, persona, figures, hook elements, loops) |
| S6 | Draft the voice-over | SCRIPT-vN.md section 3 | extract-vo.js to review | every beat has VO; hook written last |
| S7 | Texture pass, then voice pass | the same file | SKILL.md Steps 4a and 4b | the pass questions all answered |
| S8 | Gate loop | lint/, STATUS-vN.md | check.js, references/lint-fixes.md | 0 FAIL, 0 WARN, damage test passes, ledger passes, timecodes current, bar numbers met |
| S9 | Read-aloud pass | edits, Read-aloud pass: done in the hand-off | extract-vo.js, read-aloud-checklist.md | the checklist is answered; S8 is green again |
| S10 | Reader pass (user must authorise) | reader reports, fixes backed by quotes | extract-vo.js --mode blind, reader-prompts.md | fixes applied and S8 green; or Reader pass: skipped: <reason> |
| S11 | Hand-off | HANDOFF-vN.md, the final message, the research record, learnings | handoff-template.md, session-log.js | check.js says BAR MET |
S0. Set up
- Read
learnings.md(apply its [method] and [measure] bullets; [content] bullets are hypotheses) andrules.json. - Choose the slug (
slugifyof the topic, assrc/topic-researchdoes) and createresearch/<slug>/script/. node scripts/scaffold.js --dir research/<slug>/script --version v1 --beats 13 --title "<working title>"writes the script template (all sectionsledger-check.jsexpects), the chain template (every section the linter reads),FACTS.mdandHANDOFF-v1.md.- Run
check.js. It will fail; that is the starting point and proves the harness runs.
S1. Rival-video research brief (full procedure: references/s1-rival-brief.md)
node src/topic-research/cli.js "the fall of <topic>" --entity="<Entity>" --max=8 [--also="<query>"] searches YouTube with eight fixed queries, fetches the captions of the top videos, extracts events, figures, causes and surprising claims with quotes verified in code, merges them across videos and writes research/<slug>/report.md and synthesis.json. It is cached and resumable; a full run should take roughly 20 to 25 minutes (a 3-video test took 8 min 21 s), so start it in the background. Read the selected video list before you trust it: the ranking does not read the angle (a test run selected two "rebirth" videos for a "fall" topic). Then mine the report into FACTS.md as [CHECK] rows: the timeline, the figures to cross-check, the causes, the people, the contested claims, the verification queries. Everything in it is a lead; captions garble names and numbers, and "several sources" can be one original claim.
S1b. Harvest what already exists (full procedure: references/research-protocol.md section 7)
S1 reads what YouTube says. S1b collects what was written before any video: news articles, long-form magazine pieces, tribunal and court orders, company filings, annual reports. Run it right after S1 (it can run while S1 extracts). In short: (1) search the web with the query templates, widely, and keep a sources/urls-roundN.txt (one URL per line, a note after a space); (2) save the pages with node scripts/fetch-articles.mjs --dir research/<slug>/sources --urls research/<slug>/sources/urls-round1.txt (it writes one text file per page and an INDEX.md that lists what was blocked); (3) ask Google AI Mode for a dated timeline with links, collect the links, add them to the next round (skill google-ai-mode; it is a finder of links, not a source); (4) read the primary document first (a tribunal or court order has the dates, the numbers and the parties' own words); (5) write TIMELINE-AND-TRIVIA.md: a dated timeline with a status on every line, trivia that a source supports, the figures that disagree, what the rival videos get wrong and omit (measure it by searching their transcripts), and the open items. Then mine it into FACTS.md at S3. The Videocon run (research/the-fall-of-videocon/) is the worked example.
S2. Teardown of rival videos (full procedure: references/s2-teardown.md)
Export the chosen videos (export-csv.js, export-cohort.js), prepare the cases (prepare.js --perf-unknown), have the analysts write quote-verified JSON (option A: Sonnet subagents, only if the user has authorised subagents; option B: you do the analyst task yourself on the top 2 or 3 videos with the same validator; option C: a lite read of 2 or 3 transcripts), validate (85% of quotes must occur in the transcript), aggregate, report, and write teardown/SYNTHESIS.md: table stakes, differentiators, moves worth copying, weak points, claims not to repeat. For the "why X died" series the 20-winner teardown already gives the genre baseline (section 7 of the S2 file).
S3. The fact base (the stage that decides the thesis)
Follow research-protocol.md. In short: the person before the company; primary or near-primary sources; a status on every row; a source for every causal link the script will lean on; the magnitude of every key figure checked in a second source; rival-video claims that cannot be verified go in the do-not-use table; single-source anecdotes about private individuals stay out unless they are on the record in a national outlet. Output FACTS.md; the script's ledger is derived from it. If a causal claim has no source, change the thesis, not the evidence (v4: "the bond caused it" became "the same borrowed bet twice").
S4. Thesis and packaging
opening-protocol.md steps 1 to 5: the packaging question, the viewer's priors, 3 to 5 scored candidate loops (one chosen; the rest recorded with why not), the destination hint (under 20 words, no route terms), the withheld list. Write the one-line thesis, the named idea (the governing metaphor if it has one), the promise ladder (three rungs and one unannounced bonus) and the title options. State any title/evidence tension to the user.
S5. The chain and the texture plan
Fill chain-vN.json completely before writing prose (chain-schema.md). In this order: (1) the beats as one-line statements with arc tags (origin, rise, turn, response, fall, people, blame, lesson), typed junctions with link_why; (2) the Understand/Care/Change/Compel of each beat; (3) hero numbers (7 at most), each with a stake line and a landing; (4) pulls: at least 4 of 3 or more kinds (the v6 chain has 8 of 7 kinds), one a person at risk, one a motif that returns at the close; (5) the cast: protagonist with a want, counterpart, witness who pays, viewer stakes; (6) the persona lines and the two-sided calls; (7) the figures: one governing metaphor chosen by the four tests in texture.md, a gloss by analogy for each hard term, one or two parables; (8) the hook elements (including the turn, the tease and the takeaway); (9) the clarity plan: tags for every person, the witness's cost line, the audience's known terms, the spoken counts, the named ideas, the names that will appear once and why, and which beats are dense and need a headline. The quotes can be placeholders until S6; they are verified in S8.
S6. Draft the voice-over
Beat by beat in the contract order, hook last. Open each beat on a person or a picture, then the claim, then the number, then a landing. Name a person before any "he" or "she". The care line comes before the beat's first figure. Say the hinge, not a signpost. Gloss every term at first use, by analogy. Write it to be said: contractions, one-breath sentences, no staged announcements, no "not X, it's Y". Hedge what you cannot show, aloud. Use examples/moser-baer-v6/ (with its README) as the model for how the chain's fields appear in the text.
S7. The two passes
4a, the texture pass (adds): read once per question in texture.md (who is in every beat and what do they want; where is "you"; what lands after each number; any run of more than two number-heavy 30-second stretches; does the governing metaphor return and state its limit; an earnest and an aloof line in each third; is every "I" true; does any "he" or "she" at a hinge have its name in the same or previous sentence). 4b, the voice pass (subtracts): voice.md. 4c, the clarity pass: read each beat for the K, L, M questions (checklist items 1 to 3 and 20 to 26): tag every person where they arrive and return, cut or tie any name that appears once, back every count, define each named idea once, state the prize and speak the contrast, say the cost where someone pays, give each dense beat its headline. Order matters: add, then subtract, then clarify, then lint.
S8. The gate loop
Run check.js. Fix every FAIL in the script, never in rules.json; read every WARN and decide (some are false alarms: reword, do not raise the budget). references/lint-fixes.md lists what each rule usually means and the fix that worked. Expect 3 to 5 loops with the cheat sheet (v6 took about ten without it). Use --no-selftest while drafting and run it before you stop; use --fix to refresh the timecodes. When you change a rule or lint.js, the damage test is the safety net: it must still say every mutation is caught.
S9. The read-aloud pass
node scripts/extract-vo.js --script SCRIPT-vN.md --mode timed and read the whole thing as spoken text, once, then go through read-aloud-checklist.md. The linter cannot see a vague referent, an unhedged claim about someone's mind, or a doubled phrase; v6's pass caught all three. Fix, re-run S8, and write Read-aloud pass: done (<date>; what was found) in the hand-off file.
S10. The reader pass (only if the user has asked for subagents)
reader-prompts.md has the two prompts used on v4 (a first-time viewer and a skeptical editor), plus the texture questions added for v6. Give each reader only the blind text (extract-vo.js --mode blind). Fix only what a reader backs with a quote; mark any change that touches a fact; each disagreement with the declared chain is a defect in the script, not the reader. Log what they found in learnings.md. If the user has not authorised readers, write Reader pass: skipped: not authorised and put it first under "Decisions for you".
S11. Hand-off and close
- Complete
HANDOFF-vN.md(the three linescheck.jsreads, the decisions, the open items, what changed). - Write the final message with
handoff-template.md: what was built, what changed and why, corrections to earlier versions, what is unproven, the open items, the decisions for the user. node scripts/session-log.js --since <start of the run> --out research/<slug>/script/RESEARCH-LOG-vN.mdrecords every search and fetch, so the research behind this version is in the repo and not only in the chat.- Append what this run taught to
learnings.mdas [method], [measure] or [content] bullets, and add a row totools.mdfor any tool you made. - Run
check.jslast. Do not commit or push unless asked.
3. If you must stop early: the maturity ladder
Each rung is a legitimate intermediate result. Say which rung you reached.
| Rung | What exists | Looks like |
|---|---|---|
| L0 | Rival brief and teardown | what rivals say, and what they get wrong |
| L1 | Fact base and thesis (S3, S4) | a verified story and a chosen question; no prose |
| L2 | Chain and texture plan (S5) | the whole script declared; the lint mostly warns "not found in the text" |
| L3 | Draft with 0 FAIL (S6 to S8) | Moser Baer v4/v5: structure sound, voice partly machine-like, story or texture incomplete |
| L4 | The v6 bar: BAR MET except the reader pass | Moser Baer v6 |
| L5 | L4 plus a reader pass and fixes | what v4 had after its two readers; the target for the next script |
4. What this does not guarantee
That a second topic reaches the bar in one go has not been demonstrated: v6 was reached across five rounds of the user's feedback. This runbook, the gate runner and the cheat sheets are built so that the later rounds' lessons are in from the start. The first run on a new topic is the test; record the deltas in learnings.md and adjust this file.
5. Recording a beat with Gemini TTS (lead-in, record, transcribe, align, trim)
Use when the user asks for the audio of a beat. The recording always starts with a lead-in sentence ("This is B2 of The Fall of Videocon. Starting the script now."), because the first sentence or two of a Gemini take come out slow. The lead-in is trimmed afterwards, never heard by the listener.
- Put
lead_inin the spec ({beat}is the beat id), then runtts-gemini.js. It writesTTS-<name>-gemini-3.1.mdandalign-<name>-<beat>.txt. - Record:
node scripts/tts-gemini-record.js --prompt <TTS-...-gemini-3.1.md> --beat B2 --out audio/B2-raw.wav --voice Kore. The key is read fromGEMINI_API_KEYor the project.env. - Transcribe with word times (
groq-transcribe,--lang en, no prompt). - Align:
script-audio-alignwithalign-<name>-<beat>.txtas the script and the transcript JSON. Paragraph 1 is the lead-in, paragraph 2 the first script paragraph. - Trim:
node scripts/tts-trim-lead-in.js --wav audio/B2-raw.wav --alignment <outDir>/alignment.json --out audio/B2.wav. It refuses if either boundary word is interpolated. - Check: transcribe the trimmed file once more and confirm it starts on the first script word and has the script's word count less the lead-in's.
If the Gemini API quota is used up (the recorder prints HTTP 429 with a retry delay of hours), do not wait. Record the same prompt in AI Studio through Claude in Chrome, then run steps 3 to 6 on the download. The full procedure, with the gotchas (random dropouts, documented tags only, the Algenib voice must be picked explicitly, copy the download from the Downloads folder), is in the global memory note reference_gemini-tts-quota-fallback.md. It worked for Videocon Hinglish B10 on 2026-11-02.
Settings used so far: model gemini-3.1-flash-tts-preview, voice Kore. The voice the project used before was not recorded; match it in AI Studio before comparing takes.
Show the raw text
# Runbook: from a topic to a script at the v6 bar
This is the procedure that produced Moser Baer v6, written so that it can be run again on another topic without the author in the room. It was reconstructed from the session that built it (`history.md`), not invented afterwards. Read `quality-bar.md` first: it says what "finished" means. Everything here serves that bar.
**The one command that tells you where you are:**
```
node .claude/skills/video-script-production/scripts/check.js --dir research/<slug>/script --version vN [--prev vM] [--fix] [--no-selftest]
```
It runs every automatable gate, reads the manual gates from `HANDOFF-vN.md`, infers which stage the files show you have reached, writes `STATUS-vN.md`, and prints what to do next. Run it at the start of a session (to resume) and at the end of every stage. Its verdicts are `HARD GATES FAILING`, `HARD GATES PASS, BAR NOT YET MET` and `BAR MET`. Stop only on `BAR MET`, or when the user tells you to stop (then say which stage you reached).
## 0. Ground rules (these came from the user, in their words or by their corrections)
1. **Do the research before the prose.** The user's repeated instruction: "only after deep research and consideration". The thesis changed once (v4) and the story changed once (v5) because of a source nobody had opened.
2. **Say plainly what is unproven.** Every hand-off states it. The lessons are design constraints, not findings; a linter score of 100 only means the declared structure matches the text.
3. **Do not spawn subagents, commit, push or publish unless the user asks.** The reader pass (Stage 10) needs subagents; if the user has not asked for them, leave it as the first item under "Decisions for you".
4. **Keep the user's direction.** When they say "keep it subtle, I like the current direction", change as little of the pipeline as the lesson needs.
5. **Never edit `rules.json` to make a script pass.** Fix the script. Change a threshold only when data contradicts it twice, and record why in `learnings.md` (see `lesson-protocol.md`).
6. **Ask only for decisions that are the user's:** the title (when the evidence is weaker than the packaging), whether to name a private person, whether to run the readers, and commit/push. Everything else has a default below.
7. **Shell hygiene.** Heredocs and `sed` swallow backslashes (a `\b` became a backspace character twice). Write code and regexes with the Write or Edit tool. `check.js` scans for stray control characters.
## 1. Defaults, so nothing stalls (override only if the user says otherwise)
| Decision | Default | Why |
|---|---|---|
| Language | English | Hinglish later; the chain, pulls, figures and cast carry over, parables get rewritten in local terms |
| "First 60 mins" | the first 60 seconds | The user wrote 60 mins; it can only mean seconds. Say so in the hand-off |
| Length | whatever the story needs, 12 to 20 minutes; say it plainly and name the cut candidates | v4 was 12.9 min, v6 19.6 |
| Beats | 12 to 15 beats of 40 to 105 s, hook last to write | `beat_words` in `rules.json` (90 to 260 words) |
| Hook | 60 to 110 s; over 75 s it must carry the B8 elements | The user found a 73 s hook "thin" |
| Voice | earnest about facts and people, aloof about verdicts | `texture.md` |
| Title | give the working title and 2 to 3 alternates; flag any word that outruns the evidence ("killed", "scam"); never silently pick | The user has not chosen for Moser Baer |
| Private people | name only someone who is on the record in a national outlet; flag it in the hand-off | Sharma |
| Allegations | state exactly what is alleged and what a court decided; never "villain", "scam", "fraud" without a conviction | v6 beat 12 |
| Commit / push | do not | The user has not asked since `156897f` |
## 2. The stages
| # | Stage | You produce | Tool | Leave the stage when |
|---|---|---|---|---|
| S0 | Set up | the folder, the lessons gate read | `scaffold.js` | `learnings.md` and `rules.json` read; slug chosen |
| S1 | Rival-video research brief | `research/<slug>/report.md`, `synthesis.json` | `src/topic-research/cli.js`, `references/s1-rival-brief.md` | brief exists; contested claims and verification queries listed |
| S1b | Harvest existing articles and documents | `sources/` (saved pages, `INDEX.md`), `sources/ai-mode-notes.md`, a dated `TIMELINE-AND-TRIVIA.md` | `scripts/fetch-articles.mjs`, WebSearch, skill `google-ai-mode`, `references/research-protocol.md` section 7 | the primary documents (tribunal, court, filing) are saved, 15+ articles are saved or marked blocked, and the timeline lists the figures that disagree |
| S2 | Teardown of rival videos | `teardown/SYNTHESIS.md` | skill `winning-video-teardown`, `references/s2-teardown.md` | table stakes and differentiators known (skip if the series teardown already covers the packaging) |
| S3 | Fact base | `FACTS.md` | web search and fetch, `references/research-protocol.md` | 5+ sourced rows per beat-worth of story; each causal link sourced; each magnitude cross-checked; people researched; do-not-use table started |
| S4 | Thesis and packaging | `chain.cdl`, script sections 1 and 2 | `references/opening-protocol.md` | four scored CDL candidates, one chosen; title tension stated; withheld list written |
| S5 | Chain and texture plan | `chain-vN.json` complete | `scaffold.js`, `references/chain-schema.md`, `texture.md` | every section of the chain filled (beats with arcs, junctions, hero numbers with landings, 6+ pulls, cast, viewer stakes, persona, figures, hook elements, loops) |
| S6 | Draft the voice-over | `SCRIPT-vN.md` section 3 | `extract-vo.js` to review | every beat has VO; hook written last |
| S7 | Texture pass, then voice pass | the same file | `SKILL.md` Steps 4a and 4b | the pass questions all answered |
| S8 | Gate loop | `lint/`, `STATUS-vN.md` | `check.js`, `references/lint-fixes.md` | 0 FAIL, 0 WARN, damage test passes, ledger passes, timecodes current, bar numbers met |
| S9 | Read-aloud pass | edits, `Read-aloud pass: done` in the hand-off | `extract-vo.js`, `read-aloud-checklist.md` | the checklist is answered; S8 is green again |
| S10 | Reader pass (user must authorise) | reader reports, fixes backed by quotes | `extract-vo.js --mode blind`, `reader-prompts.md` | fixes applied and S8 green; or `Reader pass: skipped: <reason>` |
| S11 | Hand-off | `HANDOFF-vN.md`, the final message, the research record, learnings | `handoff-template.md`, `session-log.js` | `check.js` says `BAR MET` |
### S0. Set up
1. Read `learnings.md` (apply its [method] and [measure] bullets; [content] bullets are hypotheses) and `rules.json`.
2. Choose the slug (`slugify` of the topic, as `src/topic-research` does) and create `research/<slug>/script/`.
3. `node scripts/scaffold.js --dir research/<slug>/script --version v1 --beats 13 --title "<working title>"` writes the script template (all sections `ledger-check.js` expects), the chain template (every section the linter reads), `FACTS.md` and `HANDOFF-v1.md`.
4. Run `check.js`. It will fail; that is the starting point and proves the harness runs.
### S1. Rival-video research brief (full procedure: `references/s1-rival-brief.md`)
`node src/topic-research/cli.js "the fall of <topic>" --entity="<Entity>" --max=8 [--also="<query>"]` searches YouTube with eight fixed queries, fetches the captions of the top videos, extracts events, figures, causes and surprising claims with quotes verified in code, merges them across videos and writes `research/<slug>/report.md` and `synthesis.json`. It is cached and resumable; a full run should take roughly 20 to 25 minutes (a 3-video test took 8 min 21 s), so start it in the background. **Read the selected video list before you trust it**: the ranking does not read the angle (a test run selected two "rebirth" videos for a "fall" topic). Then mine the report into `FACTS.md` as `[CHECK]` rows: the timeline, the figures to cross-check, the causes, the people, the contested claims, the verification queries. Everything in it is a lead; captions garble names and numbers, and "several sources" can be one original claim.
### S1b. Harvest what already exists (full procedure: `references/research-protocol.md` section 7)
S1 reads what YouTube says. S1b collects what was written before any video: news articles, long-form magazine pieces, tribunal and court orders, company filings, annual reports. Run it right after S1 (it can run while S1 extracts). In short: (1) search the web with the query templates, widely, and keep a `sources/urls-roundN.txt` (one URL per line, a note after a space); (2) save the pages with `node scripts/fetch-articles.mjs --dir research/<slug>/sources --urls research/<slug>/sources/urls-round1.txt` (it writes one text file per page and an `INDEX.md` that lists what was blocked); (3) ask Google AI Mode for a dated timeline **with links**, collect the links, add them to the next round (skill `google-ai-mode`; it is a finder of links, not a source); (4) read the primary document first (a tribunal or court order has the dates, the numbers and the parties' own words); (5) write `TIMELINE-AND-TRIVIA.md`: a dated timeline with a status on every line, trivia that a source supports, the figures that disagree, what the rival videos get wrong and omit (measure it by searching their transcripts), and the open items. Then mine it into `FACTS.md` at S3. The Videocon run (`research/the-fall-of-videocon/`) is the worked example.
### S2. Teardown of rival videos (full procedure: `references/s2-teardown.md`)
Export the chosen videos (`export-csv.js`, `export-cohort.js`), prepare the cases (`prepare.js --perf-unknown`), have the analysts write quote-verified JSON (option A: Sonnet subagents, **only if the user has authorised subagents**; option B: you do the analyst task yourself on the top 2 or 3 videos with the same validator; option C: a lite read of 2 or 3 transcripts), validate (85% of quotes must occur in the transcript), aggregate, report, and write `teardown/SYNTHESIS.md`: table stakes, differentiators, moves worth copying, weak points, claims not to repeat. For the "why X died" series the 20-winner teardown already gives the genre baseline (section 7 of the S2 file).
### S3. The fact base (the stage that decides the thesis)
Follow `research-protocol.md`. In short: the person before the company; primary or near-primary sources; a status on every row; a source for every *causal link* the script will lean on; the magnitude of every key figure checked in a second source; rival-video claims that cannot be verified go in the do-not-use table; single-source anecdotes about private individuals stay out unless they are on the record in a national outlet. Output `FACTS.md`; the script's ledger is derived from it. **If a causal claim has no source, change the thesis, not the evidence** (v4: "the bond caused it" became "the same borrowed bet twice").
### S4. Thesis and packaging
`opening-protocol.md` steps 1 to 5: the packaging question, the viewer's priors, 3 to 5 scored candidate loops (one chosen; the rest recorded with why not), the destination hint (under 20 words, no route terms), the withheld list. Write the one-line thesis, the named idea (the governing metaphor if it has one), the promise ladder (three rungs and one unannounced bonus) and the title options. State any title/evidence tension to the user.
### S5. The chain and the texture plan
Fill `chain-vN.json` completely before writing prose (`chain-schema.md`). In this order: (1) the beats as one-line statements with `arc` tags (origin, rise, turn, response, fall, people, blame, lesson), typed junctions with `link_why`; (2) the Understand/Care/Change/Compel of each beat; (3) hero numbers (7 at most), each with a stake line and a landing; (4) pulls: at least 4 of 3 or more kinds (the v6 chain has 8 of 7 kinds), one a person at risk, one a motif that returns at the close; (5) the cast: protagonist with a want, counterpart, witness who pays, viewer stakes; (6) the persona lines and the two-sided calls; (7) the figures: one governing metaphor chosen by the four tests in `texture.md`, a gloss by analogy for each hard term, one or two parables; (8) the hook elements (including the turn, the tease and the takeaway); (9) the clarity plan: tags for every person, the witness's cost line, the audience's known terms, the spoken counts, the named ideas, the names that will appear once and why, and which beats are dense and need a headline. The quotes can be placeholders until S6; they are verified in S8.
### S6. Draft the voice-over
Beat by beat in the contract order, hook last. Open each beat on a person or a picture, then the claim, then the number, then a landing. Name a person before any "he" or "she". The care line comes before the beat's first figure. Say the hinge, not a signpost. Gloss every term at first use, by analogy. Write it to be said: contractions, one-breath sentences, no staged announcements, no "not X, it's Y". Hedge what you cannot show, aloud. Use `examples/moser-baer-v6/` (with its README) as the model for how the chain's fields appear in the text.
### S7. The two passes
**4a, the texture pass (adds):** read once per question in `texture.md` (who is in every beat and what do they want; where is "you"; what lands after each number; any run of more than two number-heavy 30-second stretches; does the governing metaphor return and state its limit; an earnest and an aloof line in each third; is every "I" true; does any "he" or "she" at a hinge have its name in the same or previous sentence). **4b, the voice pass (subtracts):** `voice.md`. **4c, the clarity pass:** read each beat for the K, L, M questions (checklist items 1 to 3 and 20 to 26): tag every person where they arrive and return, cut or tie any name that appears once, back every count, define each named idea once, state the prize and speak the contrast, say the cost where someone pays, give each dense beat its headline. Order matters: add, then subtract, then clarify, then lint.
### S8. The gate loop
Run `check.js`. Fix every FAIL in the script, never in `rules.json`; read every WARN and decide (some are false alarms: reword, do not raise the budget). `references/lint-fixes.md` lists what each rule usually means and the fix that worked. Expect 3 to 5 loops with the cheat sheet (v6 took about ten without it). Use `--no-selftest` while drafting and run it before you stop; use `--fix` to refresh the timecodes. When you change a rule or `lint.js`, the damage test is the safety net: it must still say every mutation is caught.
### S9. The read-aloud pass
`node scripts/extract-vo.js --script SCRIPT-vN.md --mode timed` and read the whole thing as spoken text, once, then go through `read-aloud-checklist.md`. The linter cannot see a vague referent, an unhedged claim about someone's mind, or a doubled phrase; v6's pass caught all three. Fix, re-run S8, and write `Read-aloud pass: done (<date>; what was found)` in the hand-off file.
### S10. The reader pass (only if the user has asked for subagents)
`reader-prompts.md` has the two prompts used on v4 (a first-time viewer and a skeptical editor), plus the texture questions added for v6. Give each reader only the blind text (`extract-vo.js --mode blind`). Fix only what a reader backs with a quote; mark any change that touches a fact; each disagreement with the declared chain is a defect in the script, not the reader. Log what they found in `learnings.md`. If the user has not authorised readers, write `Reader pass: skipped: not authorised` and put it first under "Decisions for you".
### S11. Hand-off and close
1. Complete `HANDOFF-vN.md` (the three lines `check.js` reads, the decisions, the open items, what changed).
2. Write the final message with `handoff-template.md`: what was built, what changed and why, corrections to earlier versions, what is unproven, the open items, the decisions for the user.
3. `node scripts/session-log.js --since <start of the run> --out research/<slug>/script/RESEARCH-LOG-vN.md` records every search and fetch, so the research behind this version is in the repo and not only in the chat.
4. Append what this run taught to `learnings.md` as [method], [measure] or [content] bullets, and add a row to `tools.md` for any tool you made.
5. Run `check.js` last. Do not commit or push unless asked.
## 3. If you must stop early: the maturity ladder
Each rung is a legitimate intermediate result. Say which rung you reached.
| Rung | What exists | Looks like |
|---|---|---|
| L0 | Rival brief and teardown | what rivals say, and what they get wrong |
| L1 | Fact base and thesis (S3, S4) | a verified story and a chosen question; no prose |
| L2 | Chain and texture plan (S5) | the whole script declared; the lint mostly warns "not found in the text" |
| L3 | Draft with 0 FAIL (S6 to S8) | Moser Baer **v4/v5**: structure sound, voice partly machine-like, story or texture incomplete |
| L4 | **The v6 bar**: `BAR MET` except the reader pass | Moser Baer **v6** |
| L5 | L4 plus a reader pass and fixes | what v4 had after its two readers; the target for the next script |
## 4. What this does not guarantee
That a second topic reaches the bar in one go has not been demonstrated: v6 was reached across five rounds of the user's feedback. This runbook, the gate runner and the cheat sheets are built so that the later rounds' lessons are in from the start. The first run on a new topic is the test; record the deltas in `learnings.md` and adjust this file.
## 5. Recording a beat with Gemini TTS (lead-in, record, transcribe, align, trim)
Use when the user asks for the audio of a beat. The recording always starts with a lead-in sentence ("This is B2 of The Fall of Videocon. Starting the script now."), because the first sentence or two of a Gemini take come out slow. The lead-in is trimmed afterwards, never heard by the listener.
1. Put `lead_in` in the spec (`{beat}` is the beat id), then run `tts-gemini.js`. It writes `TTS-<name>-gemini-3.1.md` and `align-<name>-<beat>.txt`.
2. Record: `node scripts/tts-gemini-record.js --prompt <TTS-...-gemini-3.1.md> --beat B2 --out audio/B2-raw.wav --voice Kore`. The key is read from `GEMINI_API_KEY` or the project `.env`.
3. Transcribe with word times (`groq-transcribe`, `--lang en`, no prompt).
4. Align: `script-audio-align` with `align-<name>-<beat>.txt` as the script and the transcript JSON. Paragraph 1 is the lead-in, paragraph 2 the first script paragraph.
5. Trim: `node scripts/tts-trim-lead-in.js --wav audio/B2-raw.wav --alignment <outDir>/alignment.json --out audio/B2.wav`. It refuses if either boundary word is interpolated.
6. Check: transcribe the trimmed file once more and confirm it starts on the first script word and has the script's word count less the lead-in's.
**If the Gemini API quota is used up** (the recorder prints HTTP 429 with a retry delay of hours), do not wait. Record the same prompt in AI Studio through Claude in Chrome, then run steps 3 to 6 on the download. The full procedure, with the gotchas (random dropouts, documented tags only, the Algenib voice must be picked explicitly, copy the download from the Downloads folder), is in the global memory note `reference_gemini-tts-quota-fallback.md`. It worked for Videocon Hinglish B10 on 2026-11-02.
Settings used so far: model `gemini-3.1-flash-tts-preview`, voice `Kore`. The voice the project used before was not recorded; match it in AI Studio before comparing takes.
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.