# Inkhaven 1.2.19 — Revision & continuity (multilingual)

**Status:** active scope (locked 2026-06-03)
**Cycle theme:** the draft-3-to-final grind — catch the
continuity errors, echoes, and contradictions that breed
during revision, in every language inkhaven supports.

## Motivation

Inkhaven is already rich on first-draft craft: the
manuscript intelligence dashboard, narrative-audit doctor
scans (dropped-character, pacing-collapse, stalled-thread,
naming-inconsistency), the Threads book, inline comments,
style-warning overlays, AI critique.

What it doesn't yet help with is **revision** — where
serious authors spend most of their time.  By draft 3,
continuity errors breed: a character's eye colour drifts,
a timeline contradicts itself, a vivid metaphor gets
reused eight pages later, a tension is set up and never
paid off.  Inkhaven has the right bones for this (the
Characters book, the doctor-scan infrastructure, the AI
envelope, the multilingual NLP stack) but only scratches
the surface today.

1.2.19 builds the revision layer — and builds it
**multilingual from the start**, because inkhaven's users
write in English, Russian, French, German, Spanish, and
invented languages, and a continuity checker that only
works in English would be useless to half of them.

## Locked design decisions

Resolved through brainstorming + the multilingual design
pass on 2026-06-03.

1. **Theme is revision & continuity.**  Four detectors +
   one finishing-line export.  Absorbs the deferred A.5.b
   (numeric/spatial contradictions) and A.6.b
   (unresolved-tension) carryovers.
2. **Multilingual via the three-tier model** (below).
   Most multilingual support is *inherited* from the
   existing Snowball / stop-word / whatlang / embedding
   stack; only the Tier-3 quantity lexicons are genuinely
   new per-language work.
3. **First-class multilingual support ships for the five
   supported languages** (en / ru / fr / de / es).  Other
   languages get graceful degradation + a `bootstrap-*`
   CLI to seed the missing lexicons, never silent garbage.
4. **The new detectors are `doctor --scan` classes** —
   Info severity, no autofix, author-judgment findings —
   consistent with the 1.2.16 narrative-audit classes.
   Surfaced identically in the CLI + the `Ctrl+B Shift+0`
   panel.
5. **Standard manuscript-format export is included** as
   the tangible finishing-line deliverable, pairing with
   the 1.2.18 ePub + audiobook exports.

## The three-tier multilingual model

Each continuity feature touches language differently, so
each gets a different multilingual mechanism.  This is
what keeps the cost bounded — most of it is inherited.

| Tier | Mechanism | Multilingual cost | Features |
|------|-----------|-------------------|----------|
| **1 — AI-driven** | Per-language prompt fragments (the 1.2.11 resolver, `iso_from_long`) | Write per-language prompt seeds | Continuity bible (C.3), unresolved-tension (C.4) |
| **2 — Stemmer/stop-word** | Existing Snowball (`parse_stemmer_language`, 18 langs) + stop-words + UAX-#29, keyed on `language` | ~free (same plumbing as the concordance) | Echo detector (C.1) |
| **3 — Lexicon** | New per-language quantity lexicons (number-words, units, directions) | The real new work | Numeric/temporal/spatial contradictions (C.2) |

The **language source of truth** is already settled: the
project `language` field, or per-paragraph `whatlang` when
`editor.prompt_language_mode = "paragraph_detected"`.
Every continuity feature inherits that — no new
language-selection config surface.

### Tier 1 — AI features

Multilingual for free via the existing prompt resolver.
The AI works in whatever language its prompt + context
are in; the resolver already picks per-language fragments.

* The new prompts (fact-extraction, tension-classifier)
  are `prompts.hjson` entries with per-language fragments,
  seeded by extending `bootstrap-prompts`.
* **Critical constraint:** fact *comparison* (is "green
  eyes" in ch.3 contradicted by ch.17?) must use
  **multilingual embeddings or the AI**, never string
  matching — in morphologically rich languages an
  attribute surfaces in many forms (Russian: `зелёные
  глаза` / `зелёными глазами` / `зелёных глаз`).  The
  vector store already holds MultilingualE5 embeddings, so
  cross-inflection (and cross-language, for bilingual
  editions) matching comes from cosine similarity.

### Tier 2 — Stemmer/stop-word features

The echo detector is "concordance machinery + a sliding
window + a distinctiveness threshold," so it's
multilingual by reuse: tokenise (UAX-#29) → stem
(Snowball) → drop stop-words → flag distinctive stems
reused within N paragraphs.

* **Distinctiveness without a per-language frequency
  corpus:** a stem is "distinctive" relative to *this
  manuscript's own* concordance distribution (rare overall
  but clustered) — a manuscript-relative measure that's
  language-agnostic by construction.  No frequency tables
  to ship.
* **Degradation:** languages outside the 18 Snowball set
  (Japanese, Chinese) fall back to exact-surface-form
  matching, documented — the same way the lexicon overlay
  already degrades.

### Tier 3 — Lexicon features

Where multilingual genuinely costs something, because
extraction is lexicon-bound.

* Digits are language-agnostic (`3 days`); number-words
  are per-language (`three` / `три` / `trois` / `drei` /
  `tres`).  Units + directions likewise.
* **Solution — a "continuity lexicon" bundled like the
  stop-word/filter-word seeds**, with a new `inkhaven
  bootstrap-continuity <lang>` CLI (mirroring
  `bootstrap-sdt` / `bootstrap-prompts`) that LLM-seeds
  number-words + units + directions, editable in HJSON.
* **Once normalised to `(value, unit)`, the contradiction
  logic is language-agnostic:** `3 days later` then `after
  a week` → `3d` vs `7d` ordering conflict, regardless of
  source language.
* **Ship** lexicons for en/ru/fr/de/es; other languages
  get a clear skip message ("no continuity lexicon for
  `<lang>` — run `inkhaven bootstrap-continuity <lang>`").

### Cross-cutting

* **Mixed-language manuscripts** (the bilingual
  split-view workflow): under `paragraph_detected`, each
  paragraph's check uses whatlang to pick the right
  stemmer/lexicon — the same per-paragraph detection the
  prompts already use.
* **Invented languages** (the Language system book): no
  Snowball stemmer for Klingon.  Invented-language
  passages fall back to exact-form matching against the
  language's declared alphabet (the Language book's `Meta`
  chapter), and contradiction detection uses the invented
  language's own dictionary.  Documented as Language-book
  integration, not a gap.

## Phase plan

Six phases.  Front-loads the lowest-new-language-cost
detector (echo) to validate the doctor-scan + overlay
integration, then the lexicon-heavy numeric work, then the
AI features, then the export, then docs.  Each phase
ships a commit after green tests.

### C.1 — Echo / repetition-at-distance detector

**Tier 2.**  A new `doctor --scan` class
`echo-repetition` + an opt-in editor overlay.  A
distinctive stem (rare in the manuscript's own
distribution, non-stop-word) reused within an N-paragraph
window is flagged: "`suddenly` appears 3× in this chapter"
or "the metaphor `glass harbor` recurs 6 paragraphs
later."

* Reuses `parse_stemmer_language` + the concordance's
  tokeniser + per-language stop-word lists.
* `editor.echo_window` (default 5 paragraphs) +
  `editor.echo_min_distinctiveness` config knobs.
* Overlay toggle pairs with the existing `Ctrl+B Shift+F`
  style-warning infrastructure.
* Multilingual immediately across all 18 Snowball
  languages; exact-form fallback otherwise.

**Test budget:** 12–14.  Per-language fixtures (en/ru/fr)
asserting the right stemmer collapses inflected echoes;
distinctiveness threshold; window boundary; stop-word
exclusion; exact-form fallback for an unsupported
language.

### C.2 — Continuity lexicon + numeric/temporal/spatial contradictions

**Tier 3 (the real multilingual work).**  Closes the
deferred A.5.b.

* **Continuity-lexicon loader** — per-language
  number-words (1–100 + scales), temporal units
  (sec/min/hour/day/week/month/year), spatial units
  (league/mile/km/…), directions (N/S/E/W).  HJSON,
  bundled for en/ru/fr/de/es.
* **`inkhaven bootstrap-continuity <lang>`** — LLM-seeds
  the lexicon for a new language, mirroring
  `bootstrap-sdt`.
* **`doctor --scan` class `numeric-contradiction`** —
  extracts `(value, unit)` quantities per chapter,
  normalises, flags ordering/arithmetic conflicts
  ("3 days later" → "after a week"; "200 leagues north"
  → "200 leagues south"; ages that don't add up).
* Pure normalisation + comparison (language-agnostic once
  extracted) → heavily testable.

**Test budget:** 16–18.  Lexicon parse; per-language
number-word + unit extraction (en/ru/fr/de/es); the pure
contradiction classifier (temporal ordering, spatial
direction, arithmetic); the "no lexicon" skip path.

### C.3 — Continuity bible / character fact extraction (headline)

**Tier 1 (AI) + embeddings.**  The cycle headline.

* **Fact extraction** — a new AI envelope walks the
  manuscript and extracts per-character established facts
  (appearance, relationships, possessions, origin) into
  structured HJSON in the Characters book.  Per-language
  extraction prompt (seeded via `bootstrap-prompts`).
* **Contradiction flagging** — `doctor --scan` class
  `continuity-drift`: compares each character's facts
  across chapters via **embedding similarity** (not string
  match — handles inflection + cross-language), flags
  divergent attributes ("ch.3: green eyes; ch.17: brown").
* **TUI surface** — a continuity-bible view
  (`Ctrl+V Shift+B`? — verify free) showing each
  character's extracted facts + any flagged drift.

**Test budget:** 12–14.  Fact-schema round-trip;
embedding-similarity contradiction classifier (with
fixture vectors, no live model); per-language prompt
selection; the Characters-book write path.

### C.4 — Unresolved-tension detector

**Tier 1 (AI).**  Closes the deferred A.6.b.

* `doctor --scan` class `unresolved-tension`: a
  small LLM classifier tags whether a paragraph
  *introduces* or *resolves* a tension/question/goal;
  flags introduced tensions with no downstream
  resolution.
* Per-language classifier prompt (seeded).
* Needs real-manuscript calibration before the
  false-positive rate is predictable — ships behind a
  `doctor --scan --class unresolved-tension` opt-in
  rather than the default scan set, with the calibration
  caveat documented.

**Test budget:** 8–10.  Classifier-result aggregation;
introduce-without-resolve detection; per-language prompt
selection; the opt-in gating.

### X.1 — Standard manuscript-format export

The finishing-line deliverable.  `inkhaven manuscript`
(or `build --format manuscript`) emits a submission-ready
document in **Shunn standard manuscript format**:
double-spaced, 12pt monospace, title page with contact +
rounded word count, running header (`Surname / TITLE /
page`), scene breaks as `#`, chapters starting a third
down the page.

* Mostly language-neutral (it's formatting), but
  **word-count rounding** + **scene-break detection** are
  language-aware (reuse the existing scene-break detector
  + word counter).
* Output as typst (compiles to PDF via the existing
  pipeline) + a plain `.docx`-friendly variant if cheap.

**Test budget:** 8–10.  Title-page word-count rounding;
header composition; scene-break → `#` conversion; chapter
page-break placement.

### D.1 — Docs

* Tutorial: "Revision & continuity — the continuity
  bible, echo + contradiction detectors" (C.1–C.4).
* Tutorial: "Submitting your manuscript — standard format
  export" (X.1).
* `CONFIGURATION.md` — the new `editor.echo_*` +
  continuity-lexicon blocks.
* `KEYBINDING.md` + `KEYS_REASSIGNMENT.md` — the
  continuity-bible chord.
* A multilingual-coverage table: which detector works in
  which language, and the `bootstrap-continuity` path for
  the rest.
* `RELEASE_NOTES/1.2.19.md` implementation log.

## Test budget total

Roughly 70–85 new tests.  1.2.18 landed at 1053; 1.2.19
should land around 1120–1140.

## Risk register

* **AI false-positive rate** (C.3, C.4).  Continuity
  drift + unresolved tension are judgment calls; an
  over-eager flag is noise.  Mitigate: embedding-
  similarity *threshold* tuning for C.3; opt-in gating +
  documented calibration caveat for C.4.  Both are Info
  severity, no autofix — the author decides.
* **Lexicon coverage gaps** (C.2).  A missing number-word
  silently drops a quantity.  Mitigate: the bundled
  en/ru/fr/de/es lexicons are reviewed against a fixture
  corpus; unknown languages skip with a clear message
  rather than half-working.
* **Morphological inflection** (all tiers).  The whole
  reason fact comparison uses embeddings, not strings.
  Risk: embedding similarity conflates *similar* facts
  ("blue eyes" vs "blue-grey eyes") as contradictions or
  misses real ones.  Mitigate: threshold tuning + the
  finding shows both source quotes so the author judges.
* **Echo distinctiveness tuning** (C.1).  Too low → every
  common verb flagged; too high → real echoes missed.
  Mitigate: manuscript-relative measure + a tunable
  threshold + per-language fixture validation.
* **Invented-language coverage.**  No stemmer; exact-form
  fallback is coarse.  Documented as a known limitation +
  Language-book integration point, not a silent gap.

## Out of scope for 1.2.19

* **Anachronism overlay** (the other half of A.5.b) —
  needs period-vocabulary lexicons, a different lexicon
  beast; defer to 1.2.19.b or the genre-packs cycle.
* **Character voice consistency** (the dialogue-&-voice
  theme) — belongs to its own cycle.
* **Query/synopsis AI helpers + front/back matter** (the
  finishing theme) — X.1 covers the concrete export; the
  rest is a separate cycle.

## Closing

Revision is where the book actually gets made, and it's
where inkhaven has the most headroom.  1.2.19 builds the
revision layer — continuity bible, echo detector, numeric
contradictions, unresolved tension — plus the
submission-format export that gets the finished book out
the door.

Multilingual is designed in, not bolted on: three tiers,
most of it inherited from the Snowball / stop-word /
whatlang / embedding stack inkhaven already ships, with
the genuine per-language work (Tier-3 quantity lexicons)
following the established `bootstrap-*` seed pattern and
shipping first-class for en/ru/fr/de/es.  No feature is
English-only; every one either works across all 18
Snowball languages, rides the per-language prompt
resolver, or degrades gracefully with a clear path to
full coverage.
