For the judges

How Newsdesk meets the criteria

Everything below is checkable — the live app, the worker, and the code are public, and the claims carry the commands that verify them. The framing that matters: the governance is the product; the video is the output.

Check it yourself
live app · newsdesk-rosy.vercel.app — no sign-in
cd api && uv run pytest tests/ -q # 488 tests, no network, $0
uv run python -m newsdesk ../stories/cs2.yaml --only gate # the gate, free, no keys
The shape of it

Architecture

JOURNALISTfacts · sources · approvalWEB APPNext.js · VercelRENDER WORKERPython · Fly · own ffmpegPOST /runsTHE THREE WALLS1 · no fact without a source2 · policy gate — no network,no credentials, $0 refusals3 · no publish without anamed approverGENBLAZEpipeline · manifest · verifyevery paid stepGMI CLOUDstills · video · textELEVENLABSnarration voiceLMNTfallback voiceDECISION LEDGERevery refusal, and whypass · reject · reviseBACKBLAZE B2 — FIVE BUCKETS, ONE JOB EACHassets (public) · brand-kit (public) · runs · manifests · audit (private, enforced in code)no other database exists — every page load reads B2,every stage completion writes B2PUBLISHED MP4manifest embedded in the file · ledger digest folded inedit one refusal afterwards → genblaze verify failsassembly, only after a named approval
The shape of the system. Red marks what makes it Newsdesk: refusals cost $0 and refusals are part of the tamper-evident record.

Real-world utility

Does the app solve a practical problem for a clear audience?

Built by a working public-radio newsroom in Milwaukee, for the reason newsrooms don't use generative video today: a newsroom cannot publish what it cannot account for. Newsdesk is built for the checking, not around it — three walls between a fact and a frame.

The best evidence is a catch, not a video. On the Milwaukee lead-pipes story, the system's first script contained two correctly-sourced numbers that read as a contradiction (annual vs cumulative replacements). The wall surfaced it, the editor rewrote the fact, and the original wording stays in the file — corrections stay in the record. On the same story it also did arithmetic nobody entered — 65,000 pipes at 5,000 a year misses the 2037 target — traced it to the two facts it came from, and captioned it "THE MATH DOESN'T CLOSE."

Thin spot, stated plainly: one newsroom has used it — ours.

Production readiness

Does it function reliably beyond a demo?

Deployed in two pieces — the app on Vercel, the renderer on Fly with its own ffmpeg and typeface, and a health check that reports what it can actually do, not just that it's alive. 488 tests in ~18 seconds, zero network, $0 — enforced, not promised: a structure test walks the safety gate's import graph and fails the build if anything network-capable appears. The pipeline is resumable stage by stage, so a narration failure never re-buys the pictures.

It has failed in production, correctly: during a real upstream outage the claim-checker couldn't run, so the gate refused every block — nine attempts, $0 spent. "I can't verify this" is treated as "I won't ship this."

B2 storage & data orchestration

Is Backblaze B2 used meaningfully?

B2 is the database, not the file cabinet. There is no Postgres, no SQLite — the live state of every story is a state.json in the runs bucket; the desk you see is rendered from B2 on every load. Five buckets, one job each: assets and brand-kit (public), runs, manifests, and audit (private) — and the public/private split is a frozen set in code, so a bucket can't drift public by someone forgetting.

The audit bucket is the unusual one: a durable record of what the system refused to do, kept separately, and cryptographically tied to the finished video.

Use of Genblaze

Is Genblaze used meaningfully across models, providers, steps?

Every generative step rides Genblaze: five providers across four modalities — stills, video, and text on GMI Cloud, voice on ElevenLabs with an LMNT fallback the app walks by itself — landing in B2 through ObjectStorageSink, with the manifest embedded in the MP4 and validated by genblaze verify.

The contribution we're proudest of: Genblaze's manifest records everything that runs through a pipeline — but plain chat() calls can't ride one, and in this product those calls are the governance. A provenance record that only lists successes is a brochure. So we built the missing half: every judgement — pass, reject, revise — lands in a decision ledger, and at assembly a digest of that ledger is folded into the manifest Genblaze embeds. Changing a single refusal afterwards breaks genblaze verify on the video. Not consuming the framework — closing a real hole in it, with its own verification.

If you look at four things
  1. Run the gate command above — the refusal machinery, free, no credentials.
  2. Read api/newsdesk/decisions.py — the refusal-ledger design, explained at the source.
  3. Open the Milwaukee lead-pipes video and read its receipt.
  4. Read the Day-6 section of docs/HANDOFF.md — the journalism error the wall caught, written up as it happened.
See the published runsThe front page