Editorial Event Validation Workbench
A daily quiz game needs thousands of verified historical events. An LLM drafts, an editor decides, and a state machine makes sure nothing reaches players before a human said yes.
- Role
- Backend, frontend, and the LLM pipeline
- Context
- RCS MediaGroup
- Period
- 2026
- Stack
-
- Python
- FastAPI
- React
- Vite
- Tailwind
- Gemini
- Vertex AI
- Object storage
LLM — drafts
- Translate & phrase
- Classify section
- Add trivia
- Judge quality
Editor — decides
- Approve, edit or discard
- staged → promoted the only way into the playable pool
Objective
A daily quiz built on historical events needs a large validated pool: dates that are actually right, phrasing that reads like the newsroom wrote it, and no repeats. The raw material — tens of thousands of events from an open knowledge base — is nowhere near publishable, and asking editors to fix it by hand does not scale.
Approach
The LLM drafts and a human decides. Enrichment (translation, section, a piece of trivia, a quality judgement) runs asynchronously with checkpoints, then editors work in a review table where they approve, edit or discard.
The part I would defend in any design review is the state machine. Saving from the review
wizard does not make an event playable: it becomes staged, and only an explicit
promotion moves it to the pool the game generator can draw from. That is not
bureaucracy — an external step happens between the two, and without the distinction,
half-finished events would appear in front of players.
Two more constraints fell out of real use. Identity is a stable key, so the fields that compose it are not editable and the API rejects attempts — otherwise “editing” an event silently creates a second one. And re-extraction excludes both states, so an event waiting in the middle is never offered to another editor as fresh work.
Multiple editors share one dataset, so extraction reserves a range per user and writes use optimistic concurrency with retry. Two people working the same morning was the normal case, not the edge case.
Architecture
State lives in flat files in object storage with generation-matched writes, which gives concurrency control without running a database for a handful of editors. The client keeps a local draft and the server keeps a session manifest, so an interrupted session resumes even from a different machine. The match generator reads only promoted rows and applies category, year, cooldown and overlap constraints, and reconciliation against the official calendar is a client-side diff that never deletes.
What I owned
Everything: the extraction, the LLM enrichment with its caching and cost accounting, the state model, the API, the React app, and the generator.
Outcome
A pipeline the newsroom operates without engineering involvement, and a pool that grows without repeats. The state machine has paid for itself several times over — every “why is this event live?” question has an answer that is a field, not a guess.