Mining and verifying sources
Sources are mined in three layers, and only the last one counts as evidence. S1 searches YouTube for the topic, pulls the captions of the top videos and has a language model extract dated events, figures and causes, with each quote checked in code. S2 reads the strongest rival videos closely to find what they all do, where they are weak and which of their claims not to repeat. S3 is independent research from outside YouTube (long-form magazines, rating-agency notes, tribunal and court records, wire reports) that builds FACTS.md, where every row has a status and every cause has a source for the link itself.
- Rule 1: everything from S1 and S2 is a lead. Auto-captions garble names and numbers, and "several sources" can be one claim repeated.
- Rule 2: research the person before the company; verify the causal link, not only the numbers; cross-check each key figure in a second source.
- Rule 3: if a causal claim has no source, change the thesis, not the evidence.
- Time and cost: S1 with 3 videos took 8 min 21 s (an 8-video run is estimated at 20 to 25 min). S2 option A needs subagents and therefore the user's say-so.
The three layers
Rival-video research brief
"What does YouTube already say about this topic?" Output: research/<slug>/report.md, synthesis.json, search.json, transcripts/, extractions/.
Evidence value: leads and rival claims. Nothing is marked [V] from here.
Teardown of rival videos
"What do they do, how, where are they weak?" Output: research/<slug>/teardown/SYNTHESIS.md (plus run/ files for options A and B).
Evidence value: craft analysis. It cannot show that a move caused reach.
Independent fact base
"What is actually true, and what does the record not show?" Output: FACTS.md with five tables; the script ledger is derived from it.
Evidence value: the only source of facts for the script.
S1: the rival-video brief
Tool: src/topic-research/cli.js. Cached and resumable. Full procedure: references/s1-rival-brief.md.
Before you start
- Node 22 or later;
npm cirun once. - A language-model backend:
GEMINI_API_KEYin.env(Gemma 4 on the Gemini API), or Ollama (OLLAMA_HOST,OLLAMA_MODEL,LLM_PROVIDER=ollama). - Network access for YouTube search and captions.
- The topic string and the entity, the name a title must contain. If you omit
--entityit is the topic minus framing words. Use the shortest unambiguous form (Cafe Coffee Day usedCoffee Daybecause some videos say CCD).
Run
node src/topic-research/cli.js "the fall of cafe coffee day" --entity="Coffee Day" --max=8 \
--also="cafe coffee day siddhartha" --also="cafe coffee day kaise doob gaya"
| Flag | Meaning |
|---|---|
--entity="..." | Name a video's title or channel must contain to count. |
--max=N | How many videos to take (default 8; 6 to 8 is the working range). |
--also="query" | Extra searches on top of the fixed eight, repeatable. Use for the local-language title, the founder's name, or an angle. |
--refresh | Redo this stage's own files only (search.json, transcripts/, extractions/, synthesis.json, report.md, .llm-cache/). It once deleted the whole topic folder including script/; that was fixed on 2026-10-25. Avoid it unless needed. |
The cache means a re-run with the same arguments costs nothing. A change to --also or --max takes effect only with --refresh, because the search is cached.
What it does
- Search. Eight fixed query templates (topic; entity; entity documentary; explained; what happened; case study; rise and fall; collapse scandal) plus any
--also, 20 results each. Every video seen is kept insearch.jsonundercandidates. - Filter. Dropped if the entity is in neither title nor channel name, if the title looks like noise (ads, trailers, songs, unboxing, prices), or if shorter than three minutes. The reason is stored.
- Rank.
score = log10(views + 10) + 0.5 x queries that found it (max 4) + 1 if 5 to 60 minutes + 1.5 if the title carries a story word + 1 if the entity is in the title. The top--maxare selected. The score does not read the angle: "The Rebirth of Nokia" was selected on a test run. - Transcripts. Picks the caption track in the video's original spoken language, not the first listed. A video with no captions is skipped.
- Extract. One model pass per about 6,000 words of each transcript at temperature 0.1: relevance, summary, events, figures, surprises, causes, entities, claims to verify. The prompt says use only the transcript, keep numbers and units as spoken, quote verbatim up to 25 words, mark each stated, hedged or attributed. In code, each quote is checked against the transcript;
verifiedis true only if it occurs, and that pins the timestamp. - Synthesise. Facts merged across videos into a timeline, a figures table, surprising points and causes. Each merged item must cite fact ids; items without a valid id are dropped; source counts and verified-quote counts are computed in code. A final pass writes the one-line story, entities, open questions, contested claims and 6 to 12 verification queries.
- Report.
report.md: story in brief, timeline, hard numbers, surprising points, why it happened, people, contested claims, what is open, the queued verification queries, a sources table and a method note.
Read the output (this is the work of S1)
- Were the right videos selected? In
search.jsoncheck eachselectedtitle is about the collapse. A rebirth, stock-tip or product video is the wrong source. To change the set, copy entries fromcandidatesintoselected, re-run without--refresh(new transcripts are fetched), and record the swap and why. The Cafe Coffee Day run swapped three: two "Malavika Hegde success story" videos and "The Inspiring Comeback Story of Cafe Coffee Day"; one added video said "CCD" in its title and had been rejected on the entity rule. - How many sources were usable? Fewer than four: widen with
--alsoand--max. - Mine the report into
FACTS.mdas[CHECK]rows:
| Report section | Goes into | How to treat it |
|---|---|---|
| Timeline | facts table, in order | The "support" column says how many rival videos say it. One source with an unverified quote is the weakest. |
| Hard numbers | magnitude cross-check table | Conflicts are the first things to verify. |
| Why it happened | causal-claims table | Every cause is a claim to find a source for. Causes every video repeats are table stakes. |
| Surprising points | candidate teases and landings | Verify first; rivals already lead with them. |
| Key people | people table | Start person research in S3 from these names. |
| Contested or shaky claims | do-not-use table (until verified) | |
| Still open | the list of what a viewer will ask | Material for the destination hint. |
| Verification queries | the S3 search list | Written for exactly this. |
Gate to leave S1
report.mdandsynthesis.jsonexist; 4 or more usable sources, or the shortfall is stated.- The selected videos are about the story you will tell; swaps are recorded.
FACTS.mdholds the timeline, the figures, the causes, the people, the contested claims and the verification queries as[CHECK]. Nothing from the brief is marked[V].
Failure modes
| Symptom | Fix |
|---|---|
GEMINI_API_KEY is not set | Put it in .env or run Ollama. |
| Rate or daily limit, then slow progress | Per-project quota, resets at midnight Pacific. Cached answers are reused on re-run. |
[transcript] <id> skipped | Captions disabled. Take the next candidate by editing selected. |
| Selected videos off-topic | The score is blind to angle. Edit selected or tighten --entity. |
| Names or numbers garbled | Auto-captions. Verify every name in S3. |
| Fewer transcripts on disk than cited | Happened on Cafe Coffee Day: 3 of 8 on disk; four cited timestamps could not be audited. Say so in the hand-off; do not build the story on them. |
| AI Mode source | Not wired in (sources/google-ai-mode.js throws). A Playwright proof of concept and a long design thread exist (data/ai-mode-poc/, docs/google_ai_search_automation_thread.md); verification queries are searched by hand in S3. |
Observed test (topic "the fall of nokia", --max=3): 8 queries, 116 videos seen, 99 passed the filter, 3 selected (two were "rebirth" videos). 94 facts, 83 with a verified quote (88%); 33 timeline items, 38 figures, 8 causes, 12 verification queries; 8 min 21 s.
Optional exports (needed for S2)
node src/topic-research/export-csv.js research/<slug> # all-videos.csv with channel subscriber counts (visits every channel; slow)
node src/topic-research/export-cohort.js research/<slug> research/<slug>/teardown/cohort-<slug> <videoId> ...
Shortcut that worked: copy search.json to search.json.full, trim candidates to the videos you care about, run the export, restore the file. Views per day use YouTube's rounded age text, so they are approximate.
S2: the teardown of rival videos
Skill: winning-video-teardown. Full procedure: references/s2-teardown.md. Goal: write down, with quotes, what the rival videos all do (table stakes), what the big ones do that the small ones do not, what their packaging promises, where they are weak, and which of their claims you must not repeat.
Do you need it?
| Situation | Do |
|---|---|
| The series already has a teardown of its winners (it does: 20 videos) and this topic's rivals add little | Read the 20-video aggregate and do a lite pass (option C) on the 2 or 3 biggest rival videos. |
| 3 or more rival videos with real reach (hundreds of thousands of views) | Full S2: option A if the user authorised subagents, else option B on the top 2 or 3. |
| Fewer than 3 usable rivals | Option C, and say so in the hand-off. |
Who may run the analysts
| Option | What | Cost |
|---|---|---|
| A | One Sonnet subagent per video, full JSON analysis, validator, aggregate, report, synthesis. Only if the user asked for subagents. | Most |
| B | You act as the analyst on the 2 or 3 biggest videos, one at a time, with the same task files, JSON and validator. | Medium |
| C | Lite: read the 2 or 3 biggest transcripts yourself and write SYNTHESIS.md; every claim carries a quote and a timestamp. | Low |
Steps (A and B)
node .claude/skills/winning-video-teardown/scripts/prepare.js --cohort research/<slug>/teardown/cohort-<slug> --out research/<slug>/teardown/run --perf-unknown
node .claude/skills/winning-video-teardown/scripts/validate.js --file run/analyses/V01.json --case run/cases/V01 --strict 1
node .claude/skills/winning-video-teardown/scripts/aggregate.js --run research/<slug>/teardown/run
node .claude/skills/winning-video-teardown/scripts/report.js --run research/<slug>/teardown/run
--perf-unknowntells the analyst the video was picked for its topic, not because it outperformed. Without it, the task files claim it "performed far above the norm", which is false for a topic set.- Choose 5 to 8 videos (3 for B or C): the most-viewed that are about the story; a mix of big and small channels; no more than 2 per channel; no rebirth, stock-tip or product videos. Hindi, English and Malayalam captions all worked.
- The analyst task: mechanism, not praise; every claim in
doing_wellandscript.mechanismshas a verbatim quote (4 to 25 words, original language, English gloss) and timestamp; find every open loop; type every junction (THEREFORE, BUT, AND_THEN, NEW_TOPIC); record how each hero figure was framed; name real weaknesses; do not speculate about the audience or the algorithm. - The validator checks structure, that every mechanism id is in
taxonomy.md, minimum counts (3 to 6 doing-well; at least 5 script mechanisms) and that at least 85% of the quotes literally occur in the transcript. A failing analysis is rewritten, not patched. - Read at least five analyst claims against the transcript yourself and drop or downgrade any the text does not support.
The synthesis (the only prose)
teardown/SYNTHESIS.md: read-this-first limits; table stakes (in N of N, so they do not explain why one video beat another); common but not universal; where big videos differ from small (a lead only); moves worth copying with quotes; where these videos are weak (the opening a new video can take); claims they make that you could not verify (this feeds the do-not-use table); new mechanisms found by one analyst; spot-checks; packaging notes. The Moser Baer synthesis is the model.
- Table stakes in 7 of 7: rise-and-fall arc; specific numbers; peak-versus-trough numbers; a disaster word and household brand in the title; the logo and strong colour in the thumbnail.
- The bigger videos open on a human moment (an employee returning to a locked gate; "do you remember this name?"); the smaller ones lean on a formula title and a tidy thesis. Three against four, so a lead.
- Weak points: promise delivered fully in 1 of 7; long flat middles; ads and subscribe asks between hook and story; numbers (354.51 crore, 787 crore, 8,000 crore) not reconciled across videos.
- 159 of 160 quotes verified literally.
Pitfalls
- Treating a topic set as winners. Use
--perf-unknownand say how many have reach. - Counting a table-stakes mechanism as a differentiator.
- Copying a rival's dramatised or unsourced claim into the script. They go in the do-not-use table.
- Hindi or Malayalam captions garble names (an analyst wrote "Rahul Puri" for Ratul Puri). Work from meaning and correct names in S3.
- Analysts flatter. The prompt requires real weak points; a teardown with none is re-run.
S3: independent research and the fact base
Procedure: references/research-protocol.md. This stage decides the thesis. It changed the Moser Baer thesis once (v4) and its whole shape once (v5).
The eight rules, each earned by a mistake
- The person before the company. The first Moser Baer fact base was built from rival videos and results; two Business Today profiles of the founder (2003 and 2011) were never opened. Opening them turned a story about a bond into a story about a man who always left what was running out.
- Verify the link, not just the numbers. v3 said the solar bond turned decline into debt; sources showed the debt was already there in 2005 and none says where the bond's money went.
- Cross-check the magnitude of every key figure in a second source. One article's "In 2004, 3,500 crore" was a garble: another magazine had total debt at 1,620 crore in 2005 and 3,500 crore in 2011.
- Prefer primary or near-primary sources: rating-agency notes, liquidator and tribunal documents, results as reported by a wire, press conferences as reported by the trade press, court reports, long-form business magazines. Wikipedia is a map to the cited page, not a source.
- Mark every row with a status (below).
- Rival-video claims you cannot verify go in the do-not-use table with the reason. Moser Baer: "solar unit sold for 72 crore", "787 crore", "8,000 crore", market shares of 17, 70 and 80 percent, workers burning effigies.
- Sources disagree; say so, and use the safer one. The gate closing was 3 November in one report and 4 November in another, so the script says "November 2017".
- A person named in the script needs a record: on-record quotes in a national outlet, not a rumour. A private person is the user's decision.
Status tags
- [V]
- Checked against a non-YouTube page you opened and read.
- [V-search]
- Seen only in a search-result summary; the page could not be opened. Confirm before filming.
- [CHECK]
- Unverified.
- derived
- Computed; the arithmetic is shown.
- analysis
- Your reasoning; the voice-over must flag it as such.
Order of work
- Read the S1 brief: timeline with support counts, contested claims, verification queries. They are the first search list.
- The person and the early company: founder profile, family, first jobs, first company, why they left, partners, the first big bet, who doubted it.
- The rise: who financed it, the cost advantage, capacity, the peak numbers, the rival that was ahead, the company's own explanation then.
- The turn: the first bad result, what the company blamed, what changed in the market. Write the prediction your thesis makes and look for the figure that tests it.
- The responses: each diversification or rescue with dates and amounts, and what the company said in public at the time.
- The debt and the date: rating notes, bond terms, due dates, restructuring agreements, missed payments.
- The end: the insolvency petition, buyers who walked away, the liquidation order, the claims and the liquidation value.
- The people: workers' accounts (named, on record), when the signs began, what happened afterwards.
- The blame question: cases, agencies, amounts, court decisions, appeals; compute the scale of the allegations against the claims.
- The rival claims you will refute: search for each number a rival used and you did not; write "not found in any court record or news report" only after you looked.
Asking the fetch tool so it does not paraphrase
Fetch tools summarise. Ask: "Give the exact sentences, quoted verbatim, about X"; "What is the publication date or issue date of this article, and the year?"; "Return the article text verbatim as far as available; say if it is paywalled or truncated". If a page cannot be opened, search for the title and key names, accept the summary and mark the row [V-search].
Query templates (replace the entity)
<founder> founder profile early life how he started <first product> <city>
<company> history founded <year> <partner> <first plant>
<company> <year> results net loss revenue <business> Business Standard OR Storage Newsletter OR pv-magazine
<company> bond OR FCCB OR debenture <amount> due <year> recast OR restructuring
<company> CARE OR ICRA OR CRISIL rating <year> continuing losses negative net worth
<company> workers plant closed <year> salary unpaid <city> union president
<company> insolvency NCLT petition <lender> liquidation order claims <year>
<company> CBI OR ED case <bank> crore discharged OR chargesheet
<industry> price fall <years> <industry body>
"<company>" <exact figure a rival video used>
For each fact, note the stake
Whose loss it is, which belief of the viewer's it breaks, or which contrast it makes. A figure with no stake goes in the ledger and not in the voice-over (Lesson A). The same note becomes the stake line and the landing in S5.
FACTS.md: five tables
| Table | Holds |
|---|---|
| Facts | Columns: claim, status, source (URL and date), stake (whose loss, which belief, which contrast). |
| Causal claims | Every "X caused Y" the script leans on, with a source for the link. |
| Magnitude cross-check | Each key figure with at least two independent sources, and the basis and date of each. |
| People | Who, role, what is on record, whether naming them is safe. |
| Do-not-use | Rival claims you could not verify, with the reason. |
When to stop
Stop when each planned beat has at least one sourced fact for its claim, every causal link has a source or has been softened, and every key figure appears in two places. Do not stop at the first plausible account. Do not keep researching past the point where the open items are things only a primary document can answer (the bond's use of proceeds); list them and say aloud in the script that you could not find them.
Resolving conflicting figures (Cafe Coffee Day)
Resolve each conflicting figure by its date and basis before choosing. Three of the S1 brief's conflicts were different dates or bases, not errors: Rs 7,200 crore was the debt at the start of 2019, Rs 3,200 crore the debt in July 2020; Rs 6,550 crore (March 2019) and Rs 4,970 crore (group, August 2019) are different entities or dates. A real error (the 2015 IPO at Rs 50 crore; it raised about Rs 1,150 crore) was visible only against a second source.
The research record
Every search and fetch of a session is written to the repository, so the research behind a version is not only in the chat.
node .claude/skills/video-script-production/scripts/session-log.js --since <start of the run> --out research/<slug>/script/RESEARCH-LOG-vN.md
Existing records: references/research-log.md (every search and fetch behind Moser Baer v4 to v6, by phase), research-log-round7.md (v7), and research/the-fall-of-cafe-coffee-day/script/RESEARCH-LOG-v1.md (20 searches, 15 fetches, 0 subagents). For Cafe Coffee Day a PROGRESS.md logged each stage, its output, the gate result and the next action, so that a session that lost context could resume.
Special care: people, deaths and allegations
- Name a living person only if they are on the record in a national outlet. Single-source anecdotes about private individuals stay out. Dramatised claims (a death, self-immolation) stay out on purpose.
- State exactly what is alleged and what a court decided. Never call anyone a villain, crook, scam or fraud without a conviction or final finding. Moser Baer beat 12 states the CBI figures as allegations, the Ratul Puri discharges in 2025 and the CBI's challenge.
- A death is public record but also a family's grief. State only what national outlets and official findings state, in one or two plain sentences, with no detail of how, no speculation about why, and put the decision under "Decisions for the user".
- Claim sizes are not stolen money: comparing alleged fraud with creditor claims shows scale, not innocence.
Written 2026-10-27 from the repository files and the project memory notes. If a page and a file disagree, the file is right: fix the page.