# v1.2.6 — Tools for the working novelist

1.2.6 is a big release. Where 1.2.5 was a single headline
(in-process Typst), this one moves on multiple fronts at
once. The unifying theme: turn inkhaven from a TUI for
writing into a TUI for **publishing** — every feature that
landed this cycle helps the author cross the gap between a
draft and a shippable manuscript.

Six theme groups carried the cycle:

1. **Tagging is a first-class metadata layer.**  Project-
   wide picker, rename, search, tag-filtered export, and
   tree-pane pips. Scrivener keywords come across as tags
   on import.
2. **Diagnostics get an editor surface.**  Red `●` gutter
   markers, an F8 list modal, Ctrl+V N / Shift+N
   navigation, and a Ctrl+F12 AI-explain chord that
   sends the diagnostic + ±5 context lines to the model.
3. **AI as a writing partner, not a tool.**  F12 critique
   (mode-aware), opt-in per-paragraph memory, and a
   side-by-side diff modal that gates every buffer-
   replacing apply.
4. **Snapshots learn to label themselves.**  F5 pops an
   annotation prompt; F6 surfaces it in the picker.
5. **The story view splits into two scopes.**  Ctrl+V w
   (lowercase) for the paragraph-focused mini view;
   Ctrl+V Shift+W for the full book.
6. **A story timeline.**  Calendar-aware events,
   chronological picker (Ctrl+V e), a swim-lane view
   (Ctrl+V t) with scope navigation, and an AI health
   critique. New CLI subcommands; new Bund stdlib +
   hooks. Phase 1–4 all in 1.2.6 — opt-in via
   `timeline.enabled: true`.

Around those: a Bund stdlib for tags + events + story
view + 1-based cursor positioning; new hooks
(`hook.on_diagnostic`, `hook.on_event_added`,
`hook.on_event_orphaned`); a render-preview live zoom;
seven new walkthrough tutorials (25–31); and a
companion book — the **Book of Inkhaven** — that
collects the whole user surface into one printed +
markdown artefact.

The branch carries 25 commits and ~6,000 lines of new
code. 249 tests + 5 ignored pass throughout.

## Headline changes

### Tags as a project-wide metadata layer

```
Ctrl+B ]   — tag picker on the open paragraph
g (tree)   — same picker, applies to multi-selection
Ctrl+B }   — project-wide tag search
```

Inside the picker:

```
Space   toggle the cursor tag
A       add a new tag (one-line prompt)
R       rename project-wide (merges if name exists)
D       delete project-wide (confirm + count)
T       commit marked tags onto the target
```

Tag pips render in the tree pane as compact `#tag` chips
after the progress dot. Up to two visible + a `+N` count.

CLI: `inkhaven export --tag <name>` filters the export to
paragraphs carrying that tag. AND-combines with `--status`.

Bund: five `ink.tag.*` words (list, list_for, search,
add, remove). Reads under `store_read`; writes under
`store_write`.

Scrivener: keyword import (modern Scrivener 3.x
`<KeywordRef>` + older inline `<Keywords>`) lands as
inkhaven tags automatically.

### Diagnostics get an editor surface

Gutter `●` (red, bold) appears in the line-number column
of every line that carries a typst diagnostic. Both parse
(always on) and semantic (requires `engine = "inprocess"`)
diagnostics surface here.

```
F8                  diagnostics list modal — Enter jumps cursor
Ctrl+V N / Shift+N  next / previous diagnostic in the open buffer
Ctrl+F12            AI explain the diagnostic at cursor
                    (was F11; macOS grabs F11 — see below)
```

`hook.on_diagnostic ( uuid count first-message -- )`
fires on state changes (clean → errored, count change,
top message change). Debounced — no spam on idle ticks.

`ink.editor.set_cursor ( row col -- )` is the 1-based
counterpart to the existing 0-based `ink.editor.goto`.
Matches typst's diagnostic line/col convention so a
hook can auto-jump to the first error without offset
arithmetic.

### AI critique + per-paragraph memory + diff modal

Three additions that compound:

```
F12                  AI critique (mode-aware)
                       plain edit  → critique-edit prompt
                       split-edit  → critique-changes prompt

ai: {
  per_paragraph_memory: true   # default false
  diff_review_on_apply: true   # default true
}
```

`per_paragraph_memory` records every Paragraph-scoped
turn onto the open paragraph's `Node.ai_memory` field;
subsequent Paragraph-scope prompts prepend it to the
chat history. Effect: a paragraph carries its own
conversation with the model. Come back days later, the
context restores.

`diff_review_on_apply` (default true) routes every
buffer-replacing apply (`r` and `g` in the AI pane)
through a side-by-side diff modal:

```
a / A / Enter   accept — apply and refocus the editor
r / R           reject — buffer unchanged
e / E           alias for `a`
↑ ↓ PgUp PgDn   scroll
Esc             same as reject
```

Smart extraction: the AI's reply is scanned for a
discrete corrected block before applying. Tries
`<<<CORRECTED>>>` markers, relaxed Unicode bracket pairs
(`«»`, `≪≫`), trailing code fence, and `Corrected:`
heading in order. If any matches, only the block lands —
not the commentary around it. Models that drift from
the canonical marker shape (deepseek, occasionally
gemini under load) get caught by the relaxed pass.

### Snapshot annotations

`F5` opens a one-line prompt before creating the
snapshot:

```
┌── Snapshot annotation — F5 ──────────────────────────────┐
│  Snapshot `The Storm` — annotation:                       │
│  › before the lighthouse rewrite│                         │
│   Enter commits (empty = no note) · Esc cancels           │
└──────────────────────────────────────────────────────────┘
```

The buffer to snapshot is captured at prompt-open time so
you can keep typing in the editor without affecting what
gets saved. Empty annotation commits without a note —
backward-compatible with the pre-1.2.6 path.

F6 picker renders annotations as italic-cyan indented
second lines under their rows:

```
2026-05-22 14:32   145w   The storm rolled…
       ✎ after the lighthouse rewrite
2026-05-22 14:18   141w   The storm rolled…
       ✎ first complete draft
```

### Story view — paragraph mini view

Lowercase `Ctrl+V w` (new) for the paragraph-focused
mini view: open paragraph at the centre, hop-1 wiki-link
neighbours on the inner ring (outgoing right, incoming
left), Places / Characters / Artefacts mentions on the
outer ring.

Uppercase `Ctrl+V Shift+W` is the existing book view
(unchanged behaviour; previously bound to `Ctrl+V w` —
case-sensitive remapping is the only behaviour change).

New Bund word `ink.story.render ( book-name path -- )`
writes the book view's PNG to disk without opening the
modal. Policy: `fs_write` (default-denied — opt in).
Useful as a `hook.on_save` side effect that keeps a
fresh graph available alongside the manuscript.

### Story timeline (opt-in)

The headliner of the cycle and the biggest single
feature. Off by default — enable via
`timeline.enabled: true`.

```hjson
timeline: {
  enabled: true
  default_track: "main"
  calendar: { preset: "gregorian" }    # or "sols" / "custom"
}
```

Three preset shapes. Custom calendars define their own
unit stack (e.g. 30-day months + 12-month years), named
months, seasons, epoch labels, and a format string. The
parser infers precision from the input shape — `1A` is
year-precision, `1A.3` is month, `1A.spring` is season
(90-day window), `1A.3.15` is day. Negative ticks for
prequels.

**CLI:**

```
inkhaven event add "Storm" --start "1A.2.3" --end "1A.2.5" \
                            --track main --book-name "Aerin Saga"
inkhaven event list --book-name "Aerin Saga" --track main
inkhaven event show aerin-saga/timeline/storm
```

A `Timeline` chapter materialises lazily inside the
target book on first `event add`. Tagged
`system_tag: book_timeline` so renames don't break it.

**TUI:**

```
Ctrl+V e         chronological event picker (track filter, t/T)
Ctrl+V Shift+E   new event from any pane — opens timeline and
                 immediately fires the title prompt; works on a
                 fresh project (no chicken-and-egg with the CLI)
Ctrl+V Shift+T   swim-lane view — scope-aware, auto-fits the
                 full event span on open
Ctrl+V Shift+I   edit the open event's start | end | track
                 (pipe-separated; precision re-derived from start)
```

Inside Ctrl+V Shift+T:

```
↑ / ↓     hop cursor to prev / next event (chronological)
← / →     scroll viewport (~10 cells)
PgUp/PgDn page-scroll (~60 cells)
+/-       zoom in / out          0       reset zoom
Home/End  jump first / last
Tab       cycle highlighted track
Enter     open event nearest cursor
n         new event at cursor tick

u / U     up-scope               b       jump to book scope
d / D     descent picker         p       toggle project overlay
y         critique current track-and-scope
Y         critique current scope (all tracks)
Ctrl+Y    book-wide critique (all tracks)
```

Scope: events at sub-book scope are filtered by their
`linked_paragraphs` — an event appears iff itself OR any
of its linked paragraphs sits in the current subtree.
Orphan events render with `◌`.

**AI critique** (y/Y/Ctrl+Y) builds a flat-text payload
from the visible events + their links + their characters
/ places. The prompt asks the model to surface
travel-time conflicts, paragraph-vs-event date
mismatches, fuzzy-precision overlaps, orphan signals,
and pacing outliers. Custom prompt: rename
`Prompts/timeline-health.example` to drop the suffix.

**Bund:** seven `ink.event.*` words —
`list / list_orphans / add / set_end / set_precision /
set_track / link_paragraph`. Two new hooks:
`hook.on_event_added ( uuid -- )` (fires on every add)
and `hook.on_event_orphaned ( uuid -- )` (fires on
linked → orphan transition).

### Render preview gets live zoom

Inside `Ctrl+V R` modal (introduced in 1.2.5):

```
+ / =   zoom in  (multiply ticks/cell by 0.66)
- / _   zoom out (multiply by 1.5)
0       reset to 1.00×
```

Range `[0.05, 6.00]×`. The cursor's screen column stays
anchored through zoom — drilling in feels like
inspecting, not jumping.

### F11 → Ctrl+F12

The AI explain-diagnostic chord moved from `F11` to
`Ctrl+F12`. macOS grabs F11 globally (Mission Control /
Show Desktop) and the chord never reached the TUI.

Linux + Windows users who prefer F11 can bind it back:

```hjson
keys: {
  bindings: [
    { chord: "F11", action: "editor.explain_diagnostic" }
  ]
}
```

The Ctrl+F12 binding stays — both fire the action.

## Smaller changes

- **Prompt seeding** — `inkhaven init` writes five
  `.example` seeds into the Prompts book
  (`grammar-check`, `explain-diagnostic`,
  `critique-edit`, `critique-changes`,
  `timeline-health`). Rename to drop `.example` to
  activate. Re-seed runs on every TUI open
  (idempotent — skips entries that already exist) so
  pre-1.2.6 projects get the same set on upgrade.
- **Delete-time link scrub** — deleting a paragraph
  walks every other paragraph + every event and prunes
  references to the deleted UUID. No
  `--prune-links` reindex needed.
- **Quickref auto-pull** — `Ctrl+B H` now grabs from
  all four chord layers (TopLevel + MetaSub + BundSub +
  ViewSub) so newly-added chords surface
  automatically.
- **Event picker (Ctrl+V e)** — the vertical
  counterpart to the swim-lane view. Filters by track.
- **Story-view edges** — wiki-links rendered as dashed
  edges; lexicon mentions as dotted; hierarchy as
  solid. Visible legend in the chapter.

## Documentation

Seven new tutorials (25 — 31): tag workflows, story
view, diagnostics, AI critique + memory, snapshot
annotations, render preview, story timeline.

`Documentation/CONFIGURATION.md` and
`Documentation/KEYBINDING.md` updated with every new
chord + HJSON knob.

`Documentation/Bund/BUND_TUTORIAL.md` extended with the
new stdlib words + hooks.

A new **Book of Inkhaven** ships at `Book/` — a bound
companion volume with 30 chapters + 3 appendices,
designed for print, with a markdown mirror suitable for
`inkhaven import-help` so F1 RAG covers the whole user
surface.

## Migration

No breaking changes. Every new behaviour is opt-in or
default-equivalent:

- `timeline.enabled: false` is the default — existing
  projects upgrade transparently.
- `ai.per_paragraph_memory: false` is the default —
  the existing project-wide chat history is unchanged.
- `ai.diff_review_on_apply: true` is the default — the
  modal can be disabled to restore pre-1.2.6 immediate
  apply.
- F11 still works on Linux + Windows if you bind it
  back; on macOS the binding silently never fires
  regardless. Ctrl+F12 is the supported chord.

## Statistics

- 25 commits since 1.2.5.
- 249 tests pass; 5 ignored. No regressions.
- ~6,000 LOC added (timeline alone is ~2,500 LOC).
- Zero new dependencies. Existing crates (`regex`,
  `chrono`, `uuid`, `quick-xml`, `genai`, ratatui
  family) carry every new feature.

## What's next

1.2.7 is already in flight. Planned themes:

- Cascade-impact AI when an event is moved.
- F11-on-paragraph "suggest event" variant.
- Visible book-slug prefix in the project-overlay
  timeline.
- Scrivener `<CustomMeta>` date-import bridge.
- `inkhaven export timeline` (SVG/PNG/Typst).
- Polish + bug fixes against real-project usage.

Then 1.3 — likely a focus on full-screen split-view
ergonomics and the F4 split-edit workflow.

— inkhaven 1.2.6, May 2026.
