CineSpine
The append-only event spine, 3-axis discrepancy engine
and multi-camera AI previz studio for film & TV production.
Agentic Cinema: The Blockbuster Hackathon · 2026 · ClickHouse track
Nothing crashes. Nobody is alerted.
The mistake waits for the conform.
What should happen
Screenplay scenes, call sheets, one-liners, actor schedules, shot plans.
What crew believe happened
Script supervisor logs, camera reports, sound reports, false-start notes.
What physically exists
Camera raw clips, offload checksums, poly-WAV tracks, editorial conforms.
- Script supervisor logs a take as False Start. Sound files it as Good.
- A roll typo,
A120vsA_0120, silently drops four audio channels. - Found weeks later in the post-production conform. The footage is gone.
“A document is a witness. Witnesses disagree, and the disagreement is the product.”
Three axes. One immutable ledger.
Every disagreement becomes a first-class record.
Documents are witnesses, not truth
Every upload is appended as an immutable, sequenced, typed event. Nothing is edited. Nothing is overwritten. Reconciliation runs across the witnesses and writes the disagreement down.
PAPERWORK_WITHOUT_MEDIA
Clip logged on set, missing from the offload report.
MEDIA_WITHOUT_PAPERWORK
File exists on the volume, referenced by no camera or sound log.
TIMECODE_DRIFT
Script supervisor 14:05:00:00 vs sound 14:05:00:12. Twelve frames.
- Also caught: shoot-date disagreement, roll-name collisions, circled take absent from the plan, wild tracks with no slate.
- Every finding carries its witness cards: who said what, on which axis, with the roll and circled flag.
One Cloud Run service. Three partner backbones.
Runtime: where a request goes, and who may touch what
Every department is an independent witness
// one immutable, sequenced envelope per action { "sequence_num": 1042, "production_id": "DEMO_PRODUCTION", "shoot_day": "Day 31", "user_role": "SCRIPT_SUPERVISOR", "event_type": "TAKE_LOGGED", "payload": { "slate": "101/1", "take_number": 1, "status": "GOOD", "circled": true } }
- Director / DoP ·
SCREENPLAY_PARSEDCAMERA_ANGLE_ADDEDDOP_OPTICS_CONFIGURED - Script supervisor ·
TAKE_LOGGEDFALSE_START_RECORDEDCIRCLED_TAKE_FLAGGED - Sound mixer ·
SOUND_ALE_INGESTEDPOLY_WAV_TRACKS_MAPPEDWILD_TRACK_LOGGED - DIT ·
CARD_OFFLOAD_VERIFIEDCHECKSUM_VALIDATEDSILVERSTACK_MANIFEST_INGESTED - Editorial ·
DISCREPANCY_INSPECTEDOVERRIDE_APPLIEDMASTER_CONFORM_LOCKED
Append in ClickHouse. Push over SSE. Zero polling.
POST /api/events/publish
Monotonic sequence_num. Projected into ClickHouse tables (events, takes, discrepancies, requirement_events, user_activity).
GET /api/events/subscribe
Server-Sent Events scoped by production, shoot day and role. @assistant_editor sees editorial; sound sees sound. Cloud Run timeout raised to 3600s so the stream lives.
Master Sheet, Discrepancy Matrix, Previz Studio update live
A DIT drops an offload manifest; the assistant editor's discrepancy card resolves on their screen without a refresh.
From paperwork to agents, five stages, no broker
The assistant editor's cockpit: one card per slate, every witness attached

- Composed Master Sheet (15) · slate cards fusing ZoeLog camera clips, Sound CSV poly-WAVs and the script supervisor's lined notes.
- Sequences Log Matrix (6) · scene × take grid; circled, wild-track and VFX filters.
- Card & Roll Map · A001, A120, B039, C005 … which card carried what, and where it was offloaded.
- Active Discrepancies (10) · the reconciliation output, one click from resolution.
- Requirements & Alerts · blockers assigned to
@dit,@sound_supervisor, with audit fields on resolve. - Drop Paperwork · PDF / CSV / ALE / XML ingestion; deterministic parsers, no LLM in the reconciliation path.
Ten open findings on Day 31. Each one names its witnesses.
Script Supervisor 14:05:00:00 vs Sound Department 14:05:00:12. Four witness cards: Script (Belief), Sound (Belief), Camera A (Belief), DIT / Silverstack (Existence).
A120_C002_260728, B039_C002_260728, C005_C002_260728 logged on set, missing from the offload report. Three cameras, one take, one root cause.
Exists on the volume. Referenced by no camera or sound log. The orphan nobody would find until the conform.
Resolve / Assign Card appends a DISCREPANCY_RESOLVED event with resolved_by and resolved_at. Diagnose opens the witness diff. The original finding is never deleted.

One call says how a production is doing. Eight tools let an agent act on it.

@dit on top, then the reconciler’s three findings. Same answer the WebMCP tool returns.
The page is the tool surface
The SPA registers tools on document.modelContext, so a browser agent acts on the page instead of scraping it. Tools resolve through a ref, so an agent always runs the latest React state.
navigate_appset_dop_settingsgenerate_dop_test_renderselect_charactertrigger_wrap_rescue_agentrun_full_demosummarize_production_statusranked status as JSON plus a plain-text summarywipe_all_production_datadestructiveHint, refuses withoutconfirm=true
No shipping browser exposes document.modelContext yet outside Chrome's early preview. Nothing changes for ordinary users.
From the writer's words to a multi-camera coverage plan

- Ingests
.fountain.fdx.pdf.md.txt. Transition slugs (CUT TO:,SMASH CUT TO:) become shot boundaries. - Every parse returns a
script_idderived from the text plusparse_warnings. A poor parse explains itself instead of presenting as success.

- Validated, not trusted. Descriptions are checked for filler, placeholders and at least one concrete noun; vague output gets one targeted retry, then is reported.
- Durable. Profiles live in SQLite keyed by
script_id; hand edits survive re-upload and restart. Large casts are inferred in concurrent batches; frames are prompted only with the characters in the scene.
Optics that are computed, not decorative
Twelve master DoP presets, a manual matrix, and a viewfinder that does the sensor maths for real.

2·atan(w / 2f). Not a lookup table.- Depth of field from the circle of confusion,
c = f² / (N · s), rendered as a livebackdrop-filterblur mask. T2.8 at 3.00 m: 2.67–3.42 m. - Presets carry Kelvin, key-to-fill ratio and film-stock LUT: Deakins 35mm T2.8 5600K Vision3 500T; Fincher 28mm T2.0 4300K bleach bypass.
- Per-camera overrides and saved custom presets. The compiled prompt on screen is exactly what is sent to Gemini image generation.
Gemini for judgement. ADK for agents. Cloud Run for everything.
One service, one origin
- FastAPI + React SPA in one image,
europe-west4,max-instances=1: one SQLite writer - Litestream ships the WAL to
gs://cinespine-spine-dbevery 1s, hourly snapshots; a revision restores before uvicorn starts CINESPINE_RESTORE_GENERATIONpins the restore for recovery; Secret Manager holds keys, read by thecinespine-runSA only- Second service: self-hosted
mcp-clickhouse, scale-to-zero
Semantic work, routed by cost
- Character inference, screenplay breakdown, DoP prompt compilation, Wrap Rescue memo
llm_router: 4-model fallback on 404 / 429 / 503, cache by prompt hash- Ingestion parsers call Document AI, Speech, Video Intelligence for scans, audio and footage
- Flash for trivial tasks, Pro only when context demands; Vertex AI via
GOOGLE_GENAI_USE_VERTEXAI
Two agents, both traced
- Wrap Rescue Agent: queries ClickHouse over MCP, ranks blockers, writes requirements + audit event, drafts handoff memo
- Assistant Editor Queue Agent: balances clean scenes across assistants for one day or all days
- Every
generate_contentandexecute_toolis an OTel GenAI span
Archival & durability
- Screenplay PDFs and media archived with
google-cloud-storage - Point-in-time restore of the spine:
litestream restore -timestamp … GET /api/productions/{id}/status: done, running, blocking, left, missing, in one call- Runs without any of it: every integration degrades gracefully and the UI says so

Every place a model is used, and where it is not
ClickHouse is the agent's operational memory
The Wrap Rescue Agent does not guess what is blocking wrap. It asks the database, through the official mcp-clickhouse server.
-- mcp-clickhouse · run_select_query SELECT requirement_id, priority, assigned_to, dateDiff('hour', created_at, now()) AS age_h, acknowledged_at IS NULL AS never_opened FROM cinespine.requirement_events FINAL WHERE production_id = 'DEMO_PRODUCTION' AND status = 'blocked' ORDER BY priority, age_h DESC
- Projections, not a second source of truth. Every mutation route mirrors into six tables:
production_events,takes_meta,audit_discrepancies,requirement_events,editorial_tag_events,user_activity.FINALfor current state. - Agent loop:
list_tables→run_select_query→ rank blockers by severity, age and missing acknowledgement → file a requirement per blocker, assigned to the owning department → audit event. The memo is delivered: a notification from@wrap_rescue_agentto whoever ran it plus the director, producers and post supervisors. - Same mirror, read three ways. Analytics panels, the crew workload ledger and the ranked status endpoint are all ClickHouse queries; the operational SQLite never answers a whole-production question.
- Honest failure. An MCP error is a 200 with an error body; the client checks
isErrorand shows the failed call. The memo walks the Gemini chain (live:gemini-2.5-flash404,gemini-3.5-flashdrafted) and says so when every model fails.

@dit and @sound_supervisor.


Production health, read straight from ClickHouse
A dashboard a line producer can read: what the audit found, who has filed, what was shot, what is blocked, who is looking.

grafana_ro user · variable per production.- Continuity: open CRITICAL / WARNING, findings by type
- Ingestion: documents per department, minutes since last filing
- Takes: per shoot day; post-production stage per scene
- Requirements: by status × priority; blocked, oldest first
- Engagement: actions/hour; median time-to-acknowledge
- Traces, logs, metrics over OTLP HTTP; logs stamped with
trace_id - Business metrics as OTel instruments (
cinespine_*): discrepancies by severity, events by axis, live SSE connections - Faro RUM from the SPA; Synthetic checks on
/api/healthdeep probe
One screen proves the agent did what it says
A Wrap Rescue run is a span tree: ADK → mcp-clickhouse → ClickHouse → Gemini. Runs, MCP tool calls, p95, tokens by model, active discrepancies, on one dashboard.




cinespine.ingest·cinespine.parse·cinespine.reconcile(each unresolved finding as a span event)cinespine.mirror.insert·cinespine.mcp.tool·cinespine.agent.*
str(enum), so Severity.WARNING matched nothing. Fixed 9 Sep; the test now feeds enums.traceparent; the sampler now keeps sampling under it, so curl, probes and agent triggers trace too.Built to be verified, not believed
Three minutes on the running service
- Factory Reset → Run Full Demo once, before recording, never during: it empties the whole spine
- Run Wrap Rescue once off-camera: MCP cold start ~45s, and every run files requirements about the previous run
- Open Day 31 in the app first; the Grafana gauge is recorded on open, not scraped, and should read 3. A 0 is a regression (fixed 9 Sep), not a cold cache
- Warm
/statusonce (first call reconciles every day); a test render must not answer “Previz Placeholder”
- 1
Hackathon Demo → Factory Reset → Run Full Demo
Seeds Day 31: script supervisor log, camera and sound reports, one Silverstack offload. Watch the SSE feed light up.
- 2
Set & Editorial Spine → Day 31 → Active Discrepancies
Timecode drift, paperwork without media, media without paperwork. Open Diagnose on 2/1 Take 2; resolve one and see the event appended.
- 3
Productions → Wrap Rescue Agent → Run
MCP status,
list_tables,run_select_query, ranked blockers, a requirement per blocker for@ditand@sound_supervisor. Click the bell: the memo arrived from@wrap_rescue_agent. - 4
Dashboard → Where do we stand · Grafana → Wrap Rescue Agent
The ranked status card, most urgent item as the headline. Then one Grafana screen: the run you just triggered as a span tree, ADK → mcp-clickhouse → ClickHouse → Gemini.
- 5
Screenplay & Previz Studio → Demo Production
6 scenes, 6 characters. Break down Scene 1 or 4: both carry a
CUT TO:, the path where Gemini writes the breakdown. Pick Deakins, switch 2.39:1 → 4:3.
The honest gaps, and what closes them
Agent generation ingest
Wire AGENTO11Y_* so Agent Observability gets conversation replay, per-agent cost and live evaluations, on top of the OTel spans it already reads.
Hosted MCP via OAuth
Move the agent from the self-hosted mcp-clickhouse service to ClickHouse Cloud's remote MCP once a headless OAuth flow is available.
Image generation on billing
Gemini image models sit behind a billed project; until then the studio serves a labelled placeholder and logs every model it tried.
More witnesses
Avid / Resolve bin exports and EDLs as the Editorial axis; ARRI and RED camera metadata straight from the card, not the report.
Browsers that expose modelContext
Eight page tools are registered today; only Chrome's early preview can see them. When the W3C draft ships, a browser agent drives CineSpine without scraping.
Multi-tenant productions
Per-production auth and crew invitations; the spine already keys everything by production_id, so isolation is a policy, not a rewrite.
“A document is a witness.
Witnesses disagree, and the disagreement is the product.”
CineSpine · Francisco Tena · Agentic Cinema: The Blockbuster Hackathon 2026 · ClickHouse track · MIT
The product calls Google Cloud AI only: Gemini via the GenAI SDK and Vertex AI, Google ADK, plus the official ClickHouse MCP server. An AI coding assistant (Anthropic’s Claude Code) was used as a developer tool during development.


