A reference architecture for evidence-bounded generative media. An autonomous, grounded, eval-gated pipeline that turns a topic into a fact-checked short-form video — where the generator writes from fetched data, not model memory.
Most "LLM makes a video" pipelines are one prompt and a render. Here, separate gates check the two things that actually decide a short-form video — is it correct and will anyone watch it — under one rule: no processing step may increase the epistemic strength of a claim without evidence.
See the core of it run — deterministic, no network, no live model, no Node:
make demo # drives a synthetic topic through the REAL gates → a golden manifest + an SVG snapshotAbove: three beats of one auto-generated breakdown, rendered from a typed spec + forced-aligned captions. All data is the synthetic Scrapfall Vanguard example — the repo ships no third-party content.
This is a curated extract: a full production video can't be reproduced without the excluded data, voice model, and Remotion assets — that's the honest boundary. But the pipeline's shape runs, deterministically, and is regression-tested in CI:
demo/— a runnable end-to-end vertical slice:make demo(orpython demo/run_slice.py) drives a synthetic topic through the real gates — claim-guard, fact-check, play-check, temper, editorial — with the only faked pieces being the LLM boundary and the data source (demo/adapters.py, pinned toevals/). Chart numbers come from realmathhammer, the publish/hold decision from the realstatusmodel, and the typed spec is rendered to an SVG snapshot. Output is checked against a golden manifest. You can watch the guard roll back an overstatement, the claim get tempered because evidence was only inferred, and a dead hook get rewritten — live.tests/— a green suite over the deterministic parts + the slice (mathhammer,lexicon, thestatusmodel, the demo golden). Runpython -m pytest.evals/— goldeninput → expectedfixtures for the three judgement gates. They pin the core property — no step increases a claim's strength without evidence — as reviewable data, not an assertion.- standalone modules —
mathhammer.pyandlexicon.pyimport nothing external and run on their own.
What you can't do from this extract is render a pixel-identical production video — so treat the pipeline modules as an annotated architecture, and the demo + tests + evals as the part that's independently checkable.
The name is the point: a system built so that nothing it publishes is hearsay. That's a design goal, scoped to published output — the guarantee isn't "the model never errs", it's that no step may harden a claim past its evidence, and a claim that fails a critical gate can't auto-publish.
Ingest → Select → Ground → Generate → Gate → Produce → Publish → Measure → (feeds Select)
Every topic carries a format, and the format decides three things: which facts are pulled, how the script is written, and which of three render engines builds it.
If you're skimming, read these four files — they carry the ideas:
| File | Why it's worth a look |
|---|---|
pipeline/pipeline.py |
The orchestration spine — the exact stage order (ground → guard → fact-check → play-check → temper → editorial → TTS → render). |
pipeline/detach_grounding.py |
How facts are shaped by the video's angle before generation, and joined with precomputed math. |
pipeline/mathhammer.py |
Deterministic damage/probability math so before-after numbers are computed, not guessed — an estimate under an explicitly supported rules subset (its caveats/UNSUPPORTED_INTERACTIONS say what it doesn't model). Runs standalone. |
beats/DetachmentBreakdownHud.tsx |
The tested, prop-driven render target — you fill a typed spec with data, you don't generate JSX per video. |
The two self-contained modules also run on their own, no data or keys:
python3 pipeline/mathhammer.py
# → deterministic damage & charge-probability math on a synthetic weapon/target
python3 -c "import sys; sys.path.insert(0,'pipeline'); import lexicon; print(lexicon.fold('reever'))"
# → reaver (folds a mis-heard domain name back to its canonical form for alignment)| Stage | Module | What it does |
|---|---|---|
| Generate | generate.py |
grounded script from format-specific facts + voice rules |
| Humanize | (external) | de-AI the prose |
| Claim-conformance guard | claims.py |
compares grounded draft vs humanized text; rolls back overstatements humanizing introduced (can→will, dropped conditions, changed numbers) |
| Fact-check | repair.py |
grounding-constrained rule lint / full gate |
| Structured play-check + temper | playcheck.py |
not pass/fail — returns competitive_relevance / usage_rate / evidence_basis (observed|inferred|validated) / recommended_claim_strength. If evidence only supports a qualified claim, it tempers the script — adds the condition that makes it true instead of killing the punch |
| Editorial QA | editorial.py |
scores the engagement question (hook specificity, payoff gap, dead-open, info density). Weak hook → one claim-safe rewrite before TTS |
| TTS + align | words.py |
forced alignment: canonical script text on Whisper timings, plus lexicon.py so mis-heard domain names still get true per-word timing |
| Render | videogen.py |
routes to one of three engines (below) |
Each gate is env-toggleable and fail-soft for artifact generation — a crashing gate never hard-stops the pipeline. That is deliberately not fail-open for publication: a critical gate that couldn't run marks the manifest REVIEW_REQUIRED / publishable = false (see pipeline/status.py), so a skipped check can never silently become an auto-post. Fail-open to render, fail-closed to publish.
spec_hud— prop-driven, no codegen. An LLM emits text + cues + chart data; a resolver maps cues → frames from the alignment; the render feeds a tested React/Remotion component (beats/DetachmentBreakdownHud.tsx). This is the target architecture: a beat library you fill with data, not JSX you generate per video.scene— data-driven map system (units/positions/ranges in inches).codegen— an LLM writes the component from a template (compile-retry loop). Being migrated onto thespec_hudpattern;render_retriesis logged so the worst-offending format is migrated first.
A strong topic isn't a strong video; the angle matters more. angles.py turns one topic into N scored angle candidates (controversial take / common mistake / rules interaction / before-after / …) and picks the strongest before generation.
review_ledger.py— every accept/reject is recorded with its lineage (format, renderer, hook score, evidence basis, claim strength, render retries).performance.py— pulls real view counts, matches each post back to its lineage by caption overlap, and joins outcome × properties → median views per format/renderer/angle/hook-band. An opt-in signal can nudge topic selection once the data is trusted.
The domain here is niche, but the architecture isn't. The transferable moves:
- Ground, don't recall. The generator writes from fetched data + explicit rules, never model memory — so generation is evidence-bounded by construction (constrained to an explicit evidence envelope). It can still misread or over-combine correct data — which is exactly why the guard and fact-check exist — but it can't invent a fact that was never in the envelope.
- Guard the humanizer. "De-AI the prose" is where overstatements sneak in. A claim-conformance gate diffs pre/post and rolls back
can→will, dropped conditions, and changed numbers. - Gate two axes, not one. Correct and engaging are different evals with different failure modes. Run both, separately.
- Structured relevance beats pass/fail. Instead of "publish? y/n", grade the evidence (
observed | inferred | validated) → a claim strength → temper the script to what's provable, keeping the punch. - Props over codegen. Emit a typed spec into a tested component library; don't generate a fresh component per item. Fewer compile loops, consistent design, real unit tests.
- Close the loop. Every accept/reject and every real view count feeds selection — the system learns which formats/angles actually land.
The game-specific parts are already abstracted. To point this at your domain you'd swap three things and keep the rest: the grounding source (gamedata reader), the domain lexicon (lexicon.py, for speech-alignment of your proper nouns), and the beat components (beats/). The gates, the angle picker, the temper stage, and the capture→learning loop are domain-agnostic.
pipeline/ the Python pipeline (orchestration, gates, grounding, learning)
beats/ the prop-driven Remotion component library (the spec_hud render target)
examples/ a synthetic DetachmentSpec (invented faction — no game IP) to illustrate the beat
demo/ a runnable end-to-end vertical slice (real gates + fake adapters) → golden manifest
evals/ golden input→expected fixtures for the judgement gates (the spec, in data)
tests/ a green suite over the deterministic parts + the demo (run: python -m pytest)
docs/ the storyboard preview above
This extract deliberately omits:
- All game data — datasheets, points, the official app database mirror. Proprietary IP; not redistributable. The grounding modules that read it are shown; the data is not.
- The voice model and all generated audio/video/content.
- All secrets — API keys live outside the repo and are read from
~/.config/*/.envat runtime. - The
scenemap system, the topic database, schedulers, and ~50 support/one-off modules — this is a curated slice, not the full system.
Because of that, the code here is for reading and reference, not git clone && run. mathhammer.py and lexicon.py are self-contained and do run standalone; most other modules reference excluded dependencies.
Code: see LICENSE (MIT). This is an unofficial, non-commercial project and ships no third-party game content or data — the game names, units, rules and points that the real system reads at runtime are not included here. All third-party trademarks referenced by the underlying game belong to their respective owners. See NOTICE.md.