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.
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.
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).
plan_choreography.py
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.
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.
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.)
niche_config.json, beats.json, subject_catalog.json Output: brief.jsonbrief.json, Wikipedia article URL (the visual source).
select_frames.py
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.
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.
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.)
brief.json, Wikipedia article Output: shots.jsonbrief.json, shots.json, audio (the narrator's WAV).
build_<channel>.py (per-channel variant)
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.
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.
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.)
brief.json, shots.json, audio file Output: EP_<NAME>.mp4EP_<NAME>.mp4, hero reference frame, source frame pool from Wikipedia.
qa_selector.py + apply_gates.py
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.
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.
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.
(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.
EP_<NAME>.mp4, hero frame, source pool Output: qa.json, GATE_NOTES.mdEP_<NAME>.mp4, qa.json (the gate report), channel copy/paste files, channel MCP session.
POSTING/post.py
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.
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.
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.
EP_<NAME>.mp4, qa.json, channel copy Output: EP_<NAME>_POSTED.txt, niche_config.json updatedThe 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:
| Scene | f00 (t=0%) | f50 (t=50%) | f98 (t=98%) |
|---|---|---|---|
| s1_establish | vs-seed 0.325 vs-hero 0.317 | 0.324 / 0.316 | 0.329 / 0.320 |
| s2_deepen | 0.335 / 0.327 | 0.353 / 0.346 | 0.369 / 0.362 |
| s3_exit | 0.231 / 0.230 | 0.228 / 0.226 | 0.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.
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.
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.
sourced/<EP>/overrides.json, never in vererdicts.json.
POSTING_VERIFICATION.md.
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.