A story IDE for long fiction

The story bible that enforces itself.

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.

story
=== ch1_attic ===
# location: attic
A leather-bound diary lies open on a dusty trunk. Alice picks it up.
~ found_diray = true
# speaker: alice
This is it. Sarah wrote everything down. …
H007

Reference to 'found_diray' but it is not declared in the Canon Ledger (declarations.json).

Involved canon: flag:found_diray

Change 'found_diray' to declared 'found_diary' Declare 'found_diray' as a flag or variable in declarations.json

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.

Continuity is the tax on long fiction.

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

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.

02 · The product

The continuity linter

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.

no networkno context windowsame answer every run

03 · Optional, off by default

An optional writing partner

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.

Every branch, always canon.

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.

Built on Ink 80 DaysHeaven's VaultSable Your files stay Ink and Markdown, on your disk.

What the linter catches

The line, the check, and the canon it contradicts.

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

~ found_diray = true
H007 hard

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

~ trust = 99
H006 hard

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

# speaker: sarah
This is it. Sarah wrote everything down.
H001 hard

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

Your style guide is the prose you already wrote.

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.

sentence lengthparagraph lengthrhythm variationpunctuation habitsdialogue ratiovocabulary range

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.

What twriter will not do.

It will not write into your files.

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.

It will not train on your work.

Your manuscript is yours; it goes to a model only when you ask it to, under your own API key.

It will not put AI in your way.

The free tier has no AI in it at all. The linter is the product.

It will not hold your files hostage.

Plain text on your disk, in a folder you chose. Ink and Markdown. Delete twriter tomorrow and your story is still there.

It will not quietly reword your paragraphs.

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.

Put the story bible where it can argue back.

Free. macOS and Windows. Your files stay on your disk.