01 · The spine
The Canon Ledger
One structured source of truth for the world, the characters, the established facts, and the state of every path through the story. Not a wiki you maintain out of guilt. A database the rest of the software reads.
A story IDE for long fiction
twriter is a desktop writing environment where your manuscript and your canon live in one place, and a deterministic linter catches the moment chapter 15 contradicts chapter 3. Not a model guessing. A checker that gives the same answer every time.
Free. macOS and Windows.
Reference to 'found_diray' but it is not declared in the Canon Ledger (declarations.json).
Involved canon: flag:found_diray
PLACEHOLDER. HTML recreation of the Paper theme from apps/nightshift/src/renderer/theme.css. Swap for a real capture before launch. Strings are real: fixtures/synthetic-story/story.ink + checks/h007.ts.
A character's eyes change colour in chapter 12. A door you locked in chapter 3 is open in chapter 9. A name drifts by one letter and stays drifted for two hundred pages. None of it is hard to fix. It's hard to find, and it compounds with every chapter you add.
Branch the story and it stops compounding and starts multiplying. Forty endings is forty timelines that each have to stay true.
Three parts, one of them the point
01 · The spine
One structured source of truth for the world, the characters, the established facts, and the state of every path through the story. Not a wiki you maintain out of guilt. A database the rest of the software reads.
02 · The product
Deterministic checks over your story files and your canon. Same input, same answer, every run: no context window, no sampling, no “the model didn't notice this time.” It runs as you type and it gates every AI-drafted change before it can reach your files.
03 · Optional, off by default
A writing agent that reads your canon before it writes a line. Off by default, your own API key, and it can never touch your files without you accepting the change first.
Works the same for ordinary linear manuscripts.
twriter is built on Ink, the narrative scripting language behind 80 Days, Heaven's Vault, and Sable. Branching fiction is the hardest case for continuity, which is exactly why it's the one the linter was built against. If it holds forty endings together, it holds a trilogy together.
What the linter catches
Three real diagnostics, from the project's own test fixture. The checker isn't pattern-matching your prose. It is reading a declaration you wrote and holding you to it.
story.ink:24
Reference to 'found_diray' but it is not declared in the Canon Ledger (declarations.json).
Tab to fix · Change 'found_diray' to declared 'found_diary'
canon/declarations.json
"flags": [
{ "id": "found_diary",
"description": "Reader found
Sarah's diary in the attic.",
"default": false }
] Nearest declared name is found_diary. One letter of drift, two hundred pages of consequence.
story.ink:29
Variable 'trust' = 99 is out of declared range [0, 10].
Tab to fix · Clamp to declared range: 99 → 10
canon/declarations.json
"variables": [
{ "id": "trust", "type": "int",
"range": [0, 10],
"description": "Reader's trust
with Marcus." }
] You declared the bounds yourself. The checker is only holding you to them.
story.ink:27
Sarah is marked dead on this path but has dialogue here.
No deterministic repair · yours to resolve
canon/characters/sarah.json
"status": {
"alive_per_path": {
"found_diary=1|": false,
"found_diary=0|": true
}
} This path sets found_diary = true, so Sarah is dead here, and only here. The other branch is fine.
“Tab to fix” is literal. Where a check has one safe repair, twriter offers it inline and writes it only when you press Tab. Those repairs are computed from your canon, so they need no model and no API key.
Diagnostic strings from packages/continuity-linter/src/checks/. Fixture: packages/mutation-harness/fixtures/synthetic-story/.
Your voice, measured
The measuring needs no key and no network.
twriter reads the pages you have written and measures how they actually move: how long your sentences run, how much that length varies, how often a semicolon or an adverb or a line of dialogue turns up, how wide the vocabulary is across a page. The numbers land in a file inside your project. They are a description of what you already do, not a target, and not a house style out of somebody else's book.
It refuses to guess. Under two thousand words there is not enough prose to describe anybody, so it stops and writes nothing at all, not even the numbers. A confident description of your voice that happens to be wrong is worse than no description: it is the one thing on the page you have no reason to doubt.
There is a written half as well, a short card describing the voice in words, and that half needs your own API key. It arrives as a draft you accept, edit, or throw away, and nothing a model wrote about your prose is saved until you accept it. A card that quotes a line you never wrote is rejected before it reaches you, and it is not allowed to describe you as writing like any named author.
With that key in place, a review pass measures a new passage against your own baseline and says where it moved away from it, citing the file your numbers live in. It is arithmetic about your pages rather than an opinion about them, and your baseline is a description of what you usually do rather than a rule you broke.
When you do turn a model on, that description goes in front of every surface that drafts, so it is writing toward your prose rather than toward a house style.
Every AI-drafted change waits for you: a diff to accept or reject, or a suggestion that only lands when you press Tab. There is no auto-apply: not as a setting, not as a power-user mode.
Your manuscript is yours; it goes to a model only when you ask it to, under your own API key.
The free tier has no AI in it at all. The linter is the product.
Plain text on your disk, in a folder you chose. Ink and Markdown. Delete twriter tomorrow and your story is still there.
When it compiles your prose into a playable story, it counts your paragraphs out the other side. If one came back altered or missing, the chapter is refused rather than saved, and it tells you which one went. That check is arithmetic, not judgement.
Free. macOS and Windows. Your files stay on your disk.