← How I make an episode Worked example: EP_SPEEDMASTER →

Workflow Blueprint

The five-stage pipeline behind every episode that ships on the channels. Plus six hard-won rules, one per failure that cost real time or money, that the pipeline enforces automatically. The proof is in the QA gate's actual measurements, not in this write-up.

The five stages

1. Plan → 2. Select → 3. Build → 4. Gate → 5. Post

Every episode that ships on the channels runs through these five stages in order. Each stage has its own scripts, its own inputs, and its own failure modes. The rules at the bottom are the ones that were learned by getting a stage wrong, and they are enforced automatically, not by convention.

Stage 1, Plan

Inputs

Wikipedia article URL (the source of every claim), beats.json (the audio timing), subject pick from the channel's `subject_catalog.json` (measured lift ratio).

Script

plan_choreography.py

What it does

Reads the beats.json (audio timing from the narrator TTS), writes brief.json (which shots must cover which beats, with timestamps). The brief is the contract the rest of the pipeline executes against.

Failure mode

Hand-writing N beats when the TTS produces N±2. The builder then rescales every shot to fit, and the pictures drift ahead of the captions until they are seconds out of place.

Hard rule

Narrate first, then write the brief. The brief must be written AGAINST the audio, not before it. Estimate N beats, hand-write N t values, the TTS pauses somewhere you did not predict. The builder reads the audio, not the brief. (Tengen 2: brief 19, audio 21. Tengen 3: brief 19, audio 17, both broken until brief was rewritten from the actual audio.)

Inputs: niche_config.json, beats.json, subject_catalog.json   Output: brief.json

Stage 2, Select

Inputs

brief.json, Wikipedia article URL (the visual source).

Script

select_frames.py

What it does

Fetches source frames from the Wikipedia article for each beat, ranks them by vs_hero similarity (ResNet50 cosine), and writes shots.json, the actual frame inventory the build step draws from.

Failure mode

Generic queries that produce the same 50 jpgs for every need. The selector dedups globally, so needs 7-16 silently get nothing and the build cycles a few stills across all 16 shots.

Hard rule

Five queries per need, character name first, plus a per-need verb. Identical queries across needs put the same 50 jpgs in every folder. The selector dedups globally, so needs 7-16 silently get nothing and the build cycles a few stills across all 16 shots. (EP_GOJO rebuild, 2026-10-22, ~$1.50 wasted before this rule.)

Inputs: brief.json, Wikipedia article   Output: shots.json

Stage 3, Build

Inputs

brief.json, shots.json, audio (the narrator's WAV).

Script

build_<channel>.py (per-channel variant)

What it does

Calls kie.ai (Seedance for video, Flux-2 / GPT-Image-2 for stills), assembles the audio + the chosen stills into the final MP4 with hard cuts on beat boundaries. Outputs EP_<NAME>.mp4 + the QA gate report.

Failure mode

A reject rule that says "no modern product photography" lets a modern Speedmaster through anyway because the rule was not specific enough to bite. Blanket NEVER rules applied where the forbidden thing IS the subject.

Hard rule

Every NEVER names its beat. Blanket "no Kanao" rejected all 12 candidates for the need whose line is "Then he met Kanao." "no romance" rejected all 12 for the need whose line is "He fell in love with her." Both blanket rules applied where the forbidden thing WAS the subject. Every NEVER names its beat. Hard character constraints stay absolute. (EP_OBANAI rebuild, 2x re-SELECT.)

Inputs: brief.json, shots.json, audio file   Output: EP_<NAME>.mp4

Stage 4, Gate

Inputs

EP_<NAME>.mp4, hero reference frame, source frame pool from Wikipedia.

Scripts

qa_selector.py + apply_gates.py

What it does

Extracts frames at t=0%, t=50%, t=98% of each scene. Runs ResNet50 cosine similarity vs-seed and vs-hero at each position. Then calls ask.py for vision-based verification (modern/anachronism detection). Writes qa.json + GATE_NOTES.md.

Failure mode 1

Transport timeout misread as model failure. Raw response was 361 bytes: gateway diagnostics plus [image] cN: embedded lines, then nothing. urlopen died before the response. select_frames.py discards stderr, so a timeout reads as a model fault. A 12-image vision call measured 101s against a 120s ceiling.

Failure mode 2

Pixel review at thumbnail resolution. Six frames passed the selector at 640px and failed human review at full resolution: watermark plate, second character, English speech bubbles, two pre-injury combat poses, and one frame with Tengen not in it at all.

Hard rules

(a) UNPARSEABLE response means TRANSPORT, not model failure. ask.py:1356 now 420s with 3-attempt backoff; MAX_CANDS reduced from 12 to 8.

(b) Pixel review must be at full 1080x1920. Confirm any "wrong character" call by opening the source PNG. Record rejections in sourced/<EP>/overrides.json, never in verdicts.json.

Bonus rule from Speedmaster (luxury_archive): "This looks modern" is the least reliable finding the gate produces. Across three passes Gemini gave 11 findings and 8 were false, mostly period photographs and green CRT displays read as modern. Open the frame before acting on any of them.

Inputs: EP_<NAME>.mp4, hero frame, source pool   Output: qa.json, GATE_NOTES.md

Stage 5, Post

Inputs

EP_<NAME>.mp4, qa.json (the gate report), channel copy/paste files, channel MCP session.

Script

POSTING/post.py

What it does

Queues the video, copy, thumbnail, and pinned comment to YouTube + TikTok + Instagram + Facebook via the channel's social MCPs. Writes EP_<NAME>_POSTED.txt and updates the channel's niche_config.json with the per-episode QA + view-history line.

Failure mode

Caption claims that are unsourced. A caption that asserts "Musashi arrived late and that upset his opponent" passes review because it sounds plausible, but the source says "Musashi deliberately arrived late to unbalance Kojirō's composure", different mechanism, different cause, same conclusion. Every factual claim must match the source text.

Hard rule

Every claim matches the source verbatim. Two claims were cut from the Musashi caption for being unsourced, and one claim was added to the Speedmaster caption because it WAS sourced (three brands officially considered: Rolex, Longines-Wittnauer and Omega). The verification record is in the channel's POSTING_VERIFICATION.md.

Inputs: EP_<NAME>.mp4, qa.json, channel copy   Output: EP_<NAME>_POSTED.txt, niche_config.json updated

The QA gate that actually runs

The gate that runs on every frame in every batch, qa_selector.py (ResNet50 cosine similarity, threshold M16.8, 16.9 calibrated on a reference bank held out from the active training run). Calibrated against ep_speedmaster/qa.json from the reverse-engineered luxury episode:

Scenef00 (t=0%)f50 (t=50%)f98 (t=98%)
s1_establishvs-seed 0.325
vs-hero 0.317
0.324 / 0.3160.329 / 0.320
s2_deepen0.335 / 0.3270.353 / 0.3460.369 / 0.362
s3_exit0.231 / 0.2300.228 / 0.2260.211 / 0.213

The gate's pass/fail criterion: both vs-seed and vs-hero stay within the calibrated band for the scene type. Catastrophic divergence triggers an automatic regenerate. Stylistic drift within tolerance does not. The third scene has the lowest scores, that's correct: a character walking away SHOULD diverge from the reference. The gate is calibrated to flag catastrophic divergence, not stylistic movement.

The six hard-won rules, summarised

1
Narrate first, then write the brief. The brief is written AGAINST the audio (the beats.json), not before. Estimate N beats, hand-write N t values, the TTS pauses somewhere you did not predict. The builder reads the audio, not the brief. Tengen 2: brief 19, audio 21. Tengen 3: brief 19, audio 17.
2
Five queries per need, character name first, plus a per-need verb. Identical queries put the same 50 jpgs in every folder. The selector dedups globally, so needs 7-16 silently get nothing and the build cycles a few stills. EP_GOJO rebuild cost ~$1.50 before this rule.
3
Every NEVER names its beat. Blanket rules like "no Kanao" or "no romance" rejected the wrong needs (the need where Kanao IS the subject, the need where romance IS the subject). Hard character constraints stay absolute, but the constraint is named per-beat, not globally. EP_OBANAI rebuild cost 2x re-SELECTs.
4
UNPARSEABLE response means TRANSPORT, not model failure. 361 bytes of raw response = gateway diagnostics, no model output. select_frames.py discards stderr so a timeout reads as a model fault. Fix: ask.py timeout 120s → 420s with 3-attempt backoff; MAX_CANDS 12 → 8. Cost: 4 hours of debugging on 2026-10-22.
5
Pixel review must be at full 1080x1920. Six frames passed the selector at 640px and failed human review at full resolution: watermark plate, second character, English speech bubbles, two pre-injury combat poses, and one frame with Tengen missing entirely. Confirm any "wrong character" call by opening the source PNG. Record rejections in sourced/<EP>/overrides.json, never in vererdicts.json.
6
Every claim matches the source verbatim. Two claims were cut from the Musashi caption for being unsourced; one claim was added to the Speedmaster caption because it WAS sourced. The verification record is in POSTING_VERIFICATION.md.

What this blueprint proves to a brand

A brand evaluating this work signs off on "the character looks like the character", not on the mechanism. The blueprint is the proof that the work is auditable: every claim traces to a Wikipedia source, every frame is gated against a reference, every NEAR rule has a name and a beat it applies to, and every failure mode is documented with the cost it took to learn it.

The worked example shows the gate's actual measurements on a real shipped episode, the s2_deepen scene drifts 10% over its 7.6 seconds (intentional: the scene's job is to deepen, not stay static), the s3_exit scene drops to the lowest scores (also intentional: the character walks away from the reference), and the gate passes both because they are within tolerance for the scene type. That's what the calibration buys you.

See the worked example How I make an episode All episodes