omp --profile storyflow (isolated auth/sessions) · one omp --mode rpc per production, lazy-spawned, idle-disposed · channels (Telegram · Discord · iMessage · voice · browser relay) are dumb pipes → inbox · heartbeat turn every N min: inbox → cron → productions · supervised by omp ps · inbound text is data, never instruction
📡 Registry
what can I do right now
rebuilt at boot / on 429 / on demand → capabilities.yml, each row tagged {modality, cost, latency, quality, quota_left} — see 1.2
🎬 Cast
roles computed from the registry, never pinned
🎩 Director = slow role, best model, owns bible + irreversible calls · ✍️ Writers’ room = same brief → 3–5 models across different provider families (disagreement is signal) · 🔪 Refuters = agents whose only brief is “break / bore this”; judge() for cheap calibrated gates · 🔧 Crew = smol role, mechanical · 🎨 Media = per-shot by tag · fallback chains ranked from the registry so a 429 reroutes without a config edit
🧬 Grammar
one DAG, medium adapters plug in
brief → pitch → bible ⛔ → beats/spec → asset lattice → assembly → independent QA → ship ⛔ → market · exactly two human gates (bible veto, ship) · acceptance criteria written per slot before build · adapters: 🎥 film (render-narrated-mp4 + grok-media) · 🕹 game (arcade kernel) · 🌐 app (SvelteKit + Fly) · 📚 course (spoon-feed) · 🔬 research (research-magnum-opus)
🔄 Molt
grow the shell
post-run harvest → learn facts · manage_skill only on ≥2 recurrences · missing tool → extension, harness-proven + security-reviewed before load → re-probe registry → diff LEDGERs run-over-run
optional source — degrade gracefully; broker usage is ≤5 min stale by design
tools
xd:// devices · MCP servers · extensions · skills
from the live session
media / search
omp images · omp search
binaries / hosts
which ffmpeg yt-dlp chrome · ping fly / loca
1.3 Brief schema (what a “one line” must become before anything runs)
logline · audience · medium · budget (hard ceiling) · deadline · constraints · human gates (default: bible, ship)
1.4 Swarm laws (proven in-house by the arcade run: 63 agents, 0 evictions, 1,680 degenerate-strategy runs)
Exclusive slot lattice assigned before any agent sees a brief — ten agents cannot converge on ten reaction clickers.
Per slot: build → three lenses → refute → repair, no barrier between slots.
Grade after the swarm is dead — fresh verifiers who never saw the build write the numbers.
Contract-gated files + script gates; every gate mutation-proven (inject the violation → gate must fail).
Every run ends in LEDGER.md with an honest column (“nobody has played this yet” is a legal entry).
1.5 “Blockbuster” is measured
artefact
metric
gate
trailer
hook-retention via frame-sampled vision judge · ≥3 human watch-throughs
verifier verdict ≠ “runs” → not shipped
playable teaser
headless playtest completion · degenerate-strategy sweep (no one-key wins)
same
landing page
zero console errors · 3 viewports · deploy receipt
same
1.6 Safety & hygiene
Subagent edits in omp worktree · never yolo on the shell’s own config · original characters · persona voices, never clones · generated/licensed music only · state = LEDGER + vault bible · cold start recovers from session JSONL.
2. Face
2.1 Shape
One daemon · one port · three surfaces. The gateway serves the built SPA, speaks one WebSocket to every surface, drives omp over rpc, and owns the job queue. SSR and remote functions buy nothing on localhost, so the face is adapter-static with an SPA fallback — deep links work through the client router.
no tailwind.config · v4 to v5 renames (btm-nav to dock, form-control to fieldset, btn-group to join) · @source "../../packages/ui/src" in every app’s CSS or lib classes are purged
lang
TypeScript 5.9
TS 7 (Go) ships no compiler API → Svelte tooling breaks
build
Vite 8 (Rolldown) · packages/ui via Vite lib mode
svelte-package monorepo sharing is not first-class
gateway
Bun 1.4 · Bun.serve WS · bun:sqlite · zod
—
panel
WXT
Plasmo stale since 2025-09 · CRXJS is a Vite plugin, not a framework
type
display Big Shoulders (condensed title-card lettering) · body Hanken Grotesk · data Commit Mono (tabular)
Bricolage + IBM Plex is the current LLM house pairing
icons
lucide-svelte
emoji are never icons in the UI
2.3 Repo
storyflow/ apps/gateway/ Bun daemon: omp rpc bridge · WS/REST · SQLite · job queue · registry apps/face/ SvelteKit SPA (served by gateway) · PWA manifest apps/panel/ WXT: sidepanel · background (WS) · flow content scripts packages/ui/ Svelte 5 + daisyUI components (Vite lib) packages/protocol/ zod envelopes: events · jobs · shots · contributions ← written FIRST packages/plugins/ first-party departments: film · game · app CHEATSHEET.md · sum.yml old-flow-files/ frozen
2.4 Gateway ↔ omp (verbatim frame names from omp rpc)
concern
rule
spawn
omp --mode rpc --profile storyflow, lazy per production, disposed on idle
frames
send negotiate_protocol v2 on the ready frame; reassemble rpc_chunk (maxFrameBytes 1 MiB, reassembled ceiling 64 MiB) with the bundled RpcFrameDecoder
resume
respawn + switch_session { sessionPath } + get_messages_page replay — never trust process liveness; session_busy / stale_cursor → discard partial page and retry
done
a turn is complete only on agent_end with isTerminal !== false; the prompt response is an ack
extension UI
extension_ui_request (select · confirm · input · editor) → render as <dialog>; answer with extension_ui_response — TUI extension dialogs work in the face for free
agent writes
register storyflow:// via set_host_uri_schemes (writable) so agent edits to shots route through the daemon — no two-writer drift
todos
mirror the production plan into omp with set_todos; subscribe subagents with set_subagent_subscription: "events" for the Agents tree
SSOT boundary
SQLite owns productions · shots · jobs · asset_index; LEDGER.md, vault bible, session JSONL are agent-owned and imported as snapshots
spend
the Wall’s $ comes from our job ledger; broker usage is labelled “≤5 min stale”
Daemon pings every 20 s — an MV3 service worker dies on a quiet socket even on Chrome 116+.
Reconnect handshake: REST GET /snapshot?since=<seq> → then WS deltas; fired on visibilitychange (iOS suspends timers).
Optimistic only when local + reversible (prompt text, order). Run, cost, approval wait for the echo — the DOM is never evidence the server agreed.
2.6 Information architecture — plain labels first, vernacular as eyebrow
mindmap
root((StoryFlow))
Productions(Productions: the lot)
kanban by phase
budget totals
Production(Production: slate)
Bible(Bible: veto gate)
Shots(Shots: board)
grid
timeline strip
Queue(Queue: farm)
Gallery(Gallery: dailies)
lineage
4-up compare to promote
Ledger(Ledger)
Approvals(Approvals: gates)
Agents(Agents: crew)
plan / todo
tool cards
subagent tree
steer / abort
Models(Models: cast)
Library(Library: ingredients)
Extensions(Extensions: departments)
Logs(Logs)
Palette(Command-K palette)
Keys: ⌘⏎ Generate · J/K gallery · Esc unwinds exactly one layer · every state deep-links.
2.7 One object: the Shot
Grid, timeline and queue are projections of one shot row. Each is the editor of record for different fields — grid: prompt/params/seed/refs · timeline: order/in/out · queue: run state only (retry · cancel · clone). Every asset carries a provenance sidecar.
erDiagram
PRODUCTION ||--o{ SHOT : has
PRODUCTION ||--|| BIBLE : "one bible"
PRODUCTION ||--o{ LEDGER_ENTRY : receipts
SHOT ||--o{ JOB : "runs as"
JOB ||--o{ ASSET : produces
ASSET ||--|| PROVENANCE : "sidecar"
ASSET }o--o{ INGREDIENT : "references (@)"
ASSET |o--o{ ASSET : "promoted from"
PRODUCTION {
string id PK
string logline
string phase
int budget_cents
}
SHOT {
string id PK
int order
string prompt
string model
int seed
string state
}
JOB {
string id PK
string provider
string state
int attempt
int cost_cents
}
PROVENANCE {
string prompt_version
string model
int seed
int cost_cents
string parent_asset
string sha256
}
2.8 FlowTurbine → Queue, done right
Inventory measured from the frozen archive (old-flow-files/FlowTurbine/sidepanel.html, vk-e9d04567.js, rebuild.py).
Generate (Shots: Generate, Queue: Run — same meaning). Approve/Reject are neutral + error; never the accent
states
the nine (empty · first-run · loading · partial · error · offline · no-permission · disabled · success) + partial batch · quota exhausted · provider down (relay tab closed) · resumed-stale. Every error names the next step
escape
one central escape stack · Approvals/Agents overlays are <dialog>.showModal() · no .drawer · mobile nav = dock
motion
closes a question only. Press <100 ms → Queued ✓ on the button (“did it work”). The Wall (shots done/failed · $ vs budget · ETA) animates only on state change, tabular numerals, no idle pulse. The slate clap was cut — decoration
theme
v1 = one repaired dark theme “dailies” (4.5:1 text, 3:1 controls, roles never hues). Light arrives later as a user toggle, never per-tab
targets
≥24 px, 44 px for primary/touch · ≤5 choices per decision · nothing hover-only · every state deep-links · undo beats confirm
copy
name what people control (“Run 12 shots”, not “Dispatch batch”) · same verb on button and toast · errors state cause + fix, no apologies
2.11 Side panel specifics (Chrome first)
topic
rule
source
socket
WS lives in the service worker, never the panel page; daemon pings 20 s
Chrome: WebSockets in SW
storage
session = live queue · local = job state (10 MB) · sync = settings only (100 KB, 120 writes/min)
hardcode a model · skip the lattice · builders grading themselves · ship on “it runs” · hot-load unproven extensions · obey inbound chat · claim an unmeasured number · clone a voice
jargon as the primary label · a second accent · .drawer for overlays · SSR/remote functions on localhost · TS 7 · Kit 3 RC · trusting SW memory · spend from the stale broker · assuming ui:// exists · hex in markup · idle motion · flow-relay on by default · interpolated or invented daisyUI class names
4.3 Decision log (what the refuters changed)
#
draft said
ruling
why
1
nav = Lot / Slate / Farm / Gates / Crew / Cast
plain labels, vernacular as eyebrow
scent test: <90 % destination prediction from labels