Author's Guide
You write a story once, completely and precisely, as structured data — a Story Package. The compiler performs it into prose on every read. Your job is the score, not the performance: you fix the world, the events, and the order they are shown in; the engine only ever chooses the sentences.
The three things you own
A Story Package is three parts, and each has its own editor section on the /edit screen:
- The World Model seed — the characters, locations, and objects that exist, and the relationships between them, before scene one starts. This is the Fabula: what is true.
- Scene Cards — the ordered sequence of scenes: who is present, whose POV, what must happen, what the reader must learn, what must stay hidden, and how the world changes by the end of it. This is the Syuzhet: the arrangement.
- The Voice Card — the narrator, as data: person, tense, distance, register, sentence rhythm, imagery palette, dialogue density. It shapes every scene's Performance without being part of the plot.
The compiler only ever writes the fourth thing, Performance — the actual sentences — and only from what you specified. Anything a Scene Card leaves unsaid (exact phrasing, imagery, micro-beat order) is free to come out differently on every read; that freedom is the whole point, not a gap to close.
Starting a story
/stories/new gives you three ways in: start completely from scratch, duplicate any retained version of any existing story under a new id, or import a package as JSON. Duplicating one of the five bundled fixtures is the fastest way to see a filled-in package before writing your own — they each ship a complete plant chain and a working Voice Card.
The Manuscript
Every edit you make lands in the Manuscript — your story's one mutable working copy. It autosaves as you type, and nothing else in the system can see it: the Working Draft, every reader-facing screen, and every past edition keep reading the published package until you publish again. A story can have a Manuscript and no published package at all — that's a story being written from scratch, and it's treated as loosely as you'd expect: no scenes yet is fine, missing fields are fine, until you try to publish.
Filling in the World Model seed
Every reference you make elsewhere in the package — a scene's POV, a location, a relationship — is a picker over this seed, on purpose: an author picking from a list cannot produce a dangling reference the way hand-typed ids can. Each entity carries a fixed core (name, description, the columns your schema defines) plus an open bag of key/value attributes for anything you invent that has no column of its own. Bag values stay flat — text, number, true/false, a list — so there is never a nested structure to lose track of.
Writing Scene Cards
Each Scene Card is the unit you author, and everything in it is a promise the compiler keeps exactly:
- Required beats — the tuning dial. Few beats and the performance is free to improvise widely between reads; many, and it stays close to identical prose each time. Set the density per scene as
tight,normal, orloose. - reader_must_learn / must_stay_hidden — facts the scene must reveal, and facts it must not. Both are checked mechanically against what the compiler actually produced.
- entry_state → exit_state — what must be true of the world when the scene starts, and what must be true when it ends. Edited as rows — entity, column, value — with each column's write-authority tier (physical/epistemic/volitional) shown beside it, so asserting a character's
goalreads as the volitional claim it is. - pays_off — for every fact this scene resolves, which earlier scene planted it (or that it was grounded in the World Model seed from the start). The picker only offers scenes that actually declare the fact — an undeclared plant is a hard authoring error, never a guess the compiler papers over.
- Invariants — the beats, facts, and exit state together are what stays fixed across every telling. Everything else about the scene is variance.
Deleting a card that a later scene plants a payoff against tells you before the delete, not after.
The Voice Card
Five style presets — Fairy-Tale/Fable, Gothic/Brooding, Whimsical/Playful, Hardboiled/Terse, Lyrical/Literary — expand into a fully editable card rather than staying an opaque tag: pick one, then change any field, and your edit lives in your card, never as a diff against the shared preset. That matters because a preset can change later without silently changing a story you already told in it. A Scene Card's own tone field never edits the Voice Card — it's a separate, scene-scoped instruction layered alongside it.
Linting and publishing
The linter runs continuously beside the editor, and every problem it finds is a link to the exact field that owns it. It distinguishes two severities: an error is a defect that would otherwise fail at compile time, further from the field that caused it — publishing is blocked until it's fixed. A warning is a judgment call about craft you're allowed to disagree with, and it never blocks anything.
Publish is the one deliberate act that turns your Manuscript into the next retained package_version: strict-parse, then lint, then max(retained) + 1, then retain the new version and point the story at it. Publishing leaves the Manuscript in place — you keep editing from where you left off. Discard is a separate, explicit action that drops the Manuscript and returns the story to whatever is currently published.
The Working Draft and the scene compile view
/stories/{storyId} is where you compile a story a card at a time, at author-time, with you in the loop. Compiling a scene shows you its diagnostics — an error or warn named against the exact field it contradicts — and adds any volitional proposal (a goal, an allegiance, a feeling the writer wants to assert) to a queue that waits for you to accept or reject it; leaving one pending never blocks compiling the next card.
Editing an already-compiled scene and recompiling it flags every later scene in the draft stale — never auto-recompiled, never cascaded — with a popover carrying exactly what changed. Stale-but-standing is a legitimate state: nothing nags you, and a draft with stale scenes stays fully readable.
A compile call costs one of the day's writer requests (see the note on the Gemini key below), so the Working Draft carries a compose from the Scene Card toggle: a stand-in writer that exercises validation, the continuity pass, and staleness without spending one. The compile view always says which of the two wrote a given scene.
The World & Discourse inspector
/stories/{storyId}/inspector answers "state as of scene N" against a single scrubber, over two tabs that read the same position two different ways: the World Model tab answers what is true; the told-ledger tab answers what the reader has been told, and when. Neither is stored per scene — both are reconstructed by replaying the commit log up to that point.
The run report and promoting to Baked
/stories/{storyId}/runs aggregates every completed telling by Scene Card — "this card degraded on 4 of 20 reads" is the shape of what it tells you. A completed, non-degraded run can be promoted to Baked from here: the fixed, known-good edition a reader gets by default, so their first experience of your story is never a coin flip. Promotion is always manual — a degraded run can never be auto-promoted.
Comparing two tellings
/stories/{storyId}/diff puts two runs of the same package_version side by side: the Scene Digest fields that came out differently, the volitional proposals the two runs resolved differently, and both performances to read. It is deliberately never a line-level text diff of prose — two performances of one Scene Card can share almost no words and still be the same story told correctly twice. Comparing across two different versions is a different question — that's what staleness in the Working Draft is for.
Mind what a run costs
The project's Gemini key has billing attached, so a run is no longer rationed by a free tier's daily request cap — but a writer call still costs real money, and a rate or spend limit can still be hit under enough load. Prove the mechanism works with the stand-in writer or the much cheaper light model first, and save the real writer model for the run whose prose you actually intend to read. Every run report shows what it actually cost afterward, on /stories/{storyId}/runs; see AGENTS.md in the repository for the current tier's limits and how to pace requests against them.