🦞🎬 StoryFlow Bible — Under the Hood + Face

Imported table-for-table from CHEATSHEET.md (§1–§4) and sum.yml. When conflict arises, sum.yml wins.

flowchart TD
  subgraph HOOD["Under the Hood"]
    Shell["Shell: always-on presence (omp ps)"]
    Registry["Registry: capabilities.yml"]
    Cast["Cast: Director, Writers, Refuters, Crew, Media"]
    Grammar["Grammar: Brief -> Pitch -> Bible[VETO] -> Shots -> Assembly -> QA -> Ship[VETO]"]
    Molt["Molt: Learn, Skills, Extensions"]
    Shell --> Registry --> Cast --> Grammar --> Molt --> Registry
  end

1. Under the hood

1.1 Layers

layerwhat it ishow (omp-native)
🐚 Shellalways-on presenceomp --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
📡 Registrywhat can I do right nowrebuilt at boot / on 429 / on demand → capabilities.yml, each row tagged {modality, cost, latency, quality, quota_left} — see 1.2
🎬 Castroles 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
🧬 Grammarone DAG, medium adapters plug inbrief → 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)
🔄 Moltgrow the shellpost-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

1.2 Registry probes

probecommand / sourcenotes
quota per accountomp usageaggregate; --json [INFERENCE]
modelsomp models --jsonverified flag (omp://models.md)
latency/qualityomp benchcached, not per boot
credentialsomp auth-broker · broker HTTP /v1/snapshot?wait= (ETag long-poll)optional source — degrade gracefully; broker usage is ≤5 min stale by design
toolsxd:// devices · MCP servers · extensions · skillsfrom the live session
media / searchomp images · omp search
binaries / hostswhich 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)

  1. Exclusive slot lattice assigned before any agent sees a brief — ten agents cannot converge on ten reaction clickers.
  2. Per slot: build → three lenses → refute → repair, no barrier between slots.
  3. Grade after the swarm is dead — fresh verifiers who never saw the build write the numbers.
  4. Contract-gated files + script gates; every gate mutation-proven (inject the violation → gate must fail).
  5. Every run ends in LEDGER.md with an honest column (“nobody has played this yet” is a legal entry).

1.5 “Blockbuster” is measured

artefactmetricgate
trailerhook-retention via frame-sampled vision judge · ≥3 human watch-throughsverifier verdict ≠ “runs” → not shipped
playable teaserheadless playtest completion · degenerate-strategy sweep (no one-key wins)same
landing pagezero console errors · 3 viewports · deploy receiptsame

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.

2.2 Stack (pinned 2026-09, traps included)

layerpicktrap avoided
UISvelte 5.57 runes · SvelteKit 2.x adapter-static + fallback: index.htmlKit 3 is still RC · svelte-adapter-bun is unmaintained
CSSTailwind 4.3 + daisyUI 5.7, CSS-first @plugin "daisyui"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
langTypeScript 5.9TS 7 (Go) ships no compiler API → Svelte tooling breaks
buildVite 8 (Rolldown) · packages/ui via Vite lib modesvelte-package monorepo sharing is not first-class
gatewayBun 1.4 · Bun.serve WS · bun:sqlite · zod
panelWXTPlasmo stale since 2025-09 · CRXJS is a Vite plugin, not a framework
typedisplay Big Shoulders (condensed title-card lettering) · body Hanken Grotesk · data Commit Mono (tabular)Bricolage + IBM Plex is the current LLM house pairing
iconslucide-svelteemoji 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)

concernrule
spawnomp --mode rpc --profile storyflow, lazy per production, disposed on idle
framessend negotiate_protocol v2 on the ready frame; reassemble rpc_chunk (maxFrameBytes 1 MiB, reassembled ceiling 64 MiB) with the bundled RpcFrameDecoder
resumerespawn + switch_session { sessionPath } + get_messages_page replay — never trust process liveness; session_busy / stale_cursor → discard partial page and retry
donea turn is complete only on agent_end with isTerminal !== false; the prompt response is an ack
extension UIextension_ui_request (select · confirm · input · editor) → render as <dialog>; answer with extension_ui_response — TUI extension dialogs work in the face for free
agent writesregister storyflow:// via set_host_uri_schemes (writable) so agent edits to shots route through the daemon — no two-writer drift
todosmirror the production plan into omp with set_todos; subscribe subagents with set_subagent_subscription: "events" for the Agents tree
SSOT boundarySQLite owns productions · shots · jobs · asset_index; LEDGER.md, vault bible, session JSONL are agent-owned and imported as snapshots
spendthe Wall’s $ comes from our job ledger; broker usage is labelled “≤5 min stale”

2.5 Protocol (one WebSocket, typed envelopes)

{ v, t, prod, seq, data } · kinds snapshot · shot.* · job.* · asset.* · agent.* · ui.request · ui.response · ping

  • 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).

verdictfeatures
keepimage/video modes · speed presets (plus a raw concurrency slider) · prompt list · start/end/reference frames · reference mapper (now drag + @ingredients) · queue stats · JSON export/import · undo-delete · gallery multi-select + quality presets · AnimatePromote to video · library → Library · logs · naming schemes → token templates {production}/{shot:03}-{model}-{seed}
🗑 droplicensing / trial / ban overlays · announcement bar · Flow-tab coupling (“you need an open Flow tab”) · web_accessible_resources: <all_urls>
addCSV/JSON import + {var} templating · per-item retry with backoff · seed lock · 4-up compare + promote · productions (batches belong to something) · pre-flight cost (“this batch ≈ $4.20 — run?”) · asset lineage · documented shortcuts · drag-and-drop everywhere · a11y + responsive · credentials manager (the Token-Updater, grown up)

2.9 Extensibility — declared, not defined

storyflow.plugin.json reserves six contribution points: panels · commands · providers · mediumAdapters · gates · ingredientKinds. (toolRenderers and ledgerWidgets were cut — the tiers below already cover them.)

tierformechanismlimits we accept
T0 first-partypanels, tool cards, adaptersSvelte components, lazy import(), native daisyUI themetrusted code only
T1 MCP Apps (ui://, ratified 2026-01-26)tool-attached UI from omp extensions / MCP serverssandboxed iframe, JSON-RPC over postMessage; we write the host bridge (no supported host exists)spec sends only light|dark → map our tokens via hostContext.styles.variables, accept off-palette third parties · no ui:// → structured JSON viewer fallback
T2 Web Componentsleaf widgets shared across surfacesSvelte custom-element compileprop-declared · self-styled · no slots · client-only
Module Federation / import mapsnever — no shared runtime host between SPA and extension

2.10 Design doctrine applied

Ladder: Platform → Safety/undo → A11y → Scent → Latency → Continuity → Density → Juice.

ruleStoryFlow application
one accentGenerate (Shots: Generate, Queue: Run — same meaning). Approve/Reject are neutral + error; never the accent
statesthe 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
escapeone central escape stack · Approvals/Agents overlays are <dialog>.showModal() · no .drawer · mobile nav = dock
motioncloses 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
themev1 = 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
copyname 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)

topicrulesource
socketWS lives in the service worker, never the panel page; daemon pings 20 sChrome: WebSockets in SW
storagesession = live queue · local = job state (10 MB) · sync = settings only (100 KB, 120 writes/min)storage API
opensetPanelBehavior({ openPanelOnActionClick: true })sidePanel API
CSPscript-src 'self'; connect-src ws://localhost:* http://localhost:* — never unsafe-inline, no CDN buildsMV3 default
content scriptsMAIN world via unlisted script + injectScript, ISOLATED bridgeWXT content scripts
permissionsoptional_host_permissions for labs.google, requested on first useChrome prompts on broad hosts
⚠ flow-relaydrives Google Flow through the user’s own session — ToS risk [INFERENCE]: per-production acknowledgment + rate cap, off by default
parityFirefox uses sidebarAction; Safari MV3 incomplete → stretch goalsMDN sidebarAction

3. One run, end to end

sequenceDiagram
  autonumber
  actor U as You
  participant F as Face
  participant G as Gateway
  participant O as omp rpc
  participant S as Swarm
  participant P as Providers
  U->>F: logline + budget
  F->>G: shot.create / production.start
  G->>O: spawn, negotiate_protocol v2, prompt(brief)
  O->>S: writers' room xN families -> judge -> Director writes bible
  O-->>G: extension_ui_request (confirm bible)
  G-->>F: ui.request
  F-->>U: Approvals: bible veto
  U->>F: approve
  F->>G: ui.response
  G->>O: extension_ui_response
  O->>S: slot lattice -> shots (write storyflow://shots/...)
  O->>G: host_uri_request (write)
  G-->>F: shot.* deltas (grid fills)
  U->>F: Generate (pre-flight cost shown)
  F->>G: job.run
  G->>P: per-provider concurrency, retry/backoff
  P-->>G: assets + provenance sidecars
  G-->>F: asset.* deltas (Gallery, the Wall)
  O->>S: independent QA after swarm end
  O-->>G: agent_end (isTerminal true) + LEDGER.md snapshot
  G-->>F: ui.request: ship
  U->>F: ship

3.1 Shot / job lifecycle

stateDiagram-v2
  [*] --> draft
  draft --> queued: Generate
  queued --> running: slot free
  running --> done: asset + sidecar
  running --> failed: provider error
  failed --> retrying: attempt < max
  retrying --> running
  failed --> blocked: quota exhausted / provider down
  blocked --> queued: registry says go
  running --> stale: daemon restart
  stale --> queued: resync
  done --> promoted: promote to video
  promoted --> queued
  done --> [*]

4. Gates & checklists

4.1 Ship gate (doctrine ≥85/100 · any Tier 1–2 failure is an automatic fail)

  • squint · grayscale · 100 ms feedback · one-accent · zero-border · density morph
  • Esc unwinds exactly one layer everywhere (palette → dialog → panel)
  • every state deep-links; Back never loses work; undo over confirm
  • touch parity · reduced-motion parity · 320 px / 400 % zoom
  • cold-3G: LCP < 2.5 s · INP < 200 ms · CLS < 0.1
  • rage test · re-entry test · per-theme contrast gate exits 0
  • headless browser proof of every panel state (incl. partial / quota / provider-down / resumed-stale)
  • side panel: a batch survives service-worker eviction
  • production: verifier verdict ≠ “runs” → not shipped; LEDGER honest column filled

4.2 Never (merged, both halves)

🐚 under the hood🎭 face
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 voicejargon 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 saidrulingwhy
1nav = Lot / Slate / Farm / Gates / Crew / Castplain labels, vernacular as eyebrowscent test: <90 % destination prediction from labels
2”slate clap” on Generatecut; 100 ms press feedback + Queued ✓motion must close a question
3the Wall = live numbersanimate on change only, tabular numsno motion at rest
4two themes, dark per tabone repaired theme; light later as user toggletheme sovereignty; 4.5:1 repair surface
5resume = job queue resume+ rpc respawn · switch_session · get_messages_pagestdin close disposes the session
6”one WS”+ 20 s daemon ping · REST snapshot on reconnectSW eviction; iOS suspend
7SQLite SSOTSQLite for products/shots/jobs; agent files imported; storyflow:// host URI for agent writestwo-writer drift
8Wall $ from brokerfrom own job ledgerbroker cache 5 min ± jitter
9MCP Apps = theme via postMessagehost bridge is ours; only light/dark in spec; JSON fallbackspec 2026-01-26
108 contribution points6 (cut toolRenderers, ledgerWidgets)YAGNI; tiers cover them
11Bricolage + PlexBig Shoulders + Hanken + Commit MonoLLM default cluster
12flow-relay as a normal providerack + rate cap, off by defaultToS risk