inkhaven 3.0.0

Inkhaven — TUI literary work editor for Typst books
# Plot Threads (THREADS)

*(1.2.14+ — `inkhaven thread …`; see
[`PROPOSALS/1.2.14_PLAN.md`](PROPOSALS/1.2.14_PLAN.md) for the full design)*

A novel is a weave of arcs — the inheritance subplot, the redemption arc, the
secret-society reveal — and the failure modes are structural: a thread set up and
never paid off, a "payoff" that nothing in the prose actually fires, an arc that
goes quiet for two hundred pages.

> **THREADS gives each narrative arc a home: a named record under the `Threads`
> system book that captures its status, weight, arc shape, and links into your
> Characters / Places / artefacts, plus a `doctor` that reads the whole set against
> the manuscript's paragraph links and reports the structural blind spots.**

Each thread is an HJSON-fronted Paragraph under the `Threads` system book — the same
content-type pattern as Language dictionary entries. Manuscript paragraphs attach to
a thread through the **existing paragraph-link mechanism** (`Ctrl+V A` outgoing,
`Ctrl+V I` incoming) — no new linking primitive — and those links are exactly what
the doctor counts.

---

## A thread and its lifecycle

`inkhaven thread add` seeds a fully-commented HJSON template you open and fill in:
identity (`title`), arc shape (`opening` / `midpoint` / `payoff`), connections
(`characters` / `places` / `artefacts` / `related_threads`), and metadata
(`tension` 0–10, `register`, `notes`).

Two fields drive the doctor:

- **`status`** — the arc's stage: `setup``develop``payoff``resolved` (plus
  `abandoned`). `add` defaults it to `setup`.
- **`weight`**`major` | `subplot` | `runner` | `bridge`. `add` defaults it to
  `major`.

```
inkhaven thread add "inheritance subplot"                    # setup · major
inkhaven thread add "redemption" --status develop --weight subplot
inkhaven thread add "the seal" --title "The Broken Seal"     # display title ≠ slug
```

`add` rejects a duplicate title case-insensitively, writes the seed body to disk
first (so the editor's on-disk `.typ` matches), and tells you which fields to open
and fill.

```
inkhaven thread list [--status S] [--weight W]   # table: status/weight/tension + link counts
inkhaven thread export [--format json|csv|markdown] [--output FILE]
```

`list` prints a summary table (name, status, weight, tension, and character / place
/ artefact link counts), optionally filtered by status or weight.
`export` (`-f`/`--format`, `-o`/`--output`, default JSON to stdout) emits the full
record set as JSON, a flat CSV table, or a printable Markdown inventory.

---

## The doctor

```
inkhaven thread doctor [--json]
```

The doctor reads every thread's HJSON, tallies a **project-wide reverse-link count**
per thread (how many manuscript paragraphs link *to* it), and reports the status and
weight distributions, the average tension, and three **blind-spot** passes that
cross a thread's declared status against the evidence of the prose:

| Check | Fires when | Reads |
| ----- | ---------- | ----- |
| `zero_links` | a thread's status is **past `setup`** yet **no** paragraph links to it | the arc claims to be underway but the manuscript shows no trace |
| `payoff_unfired` | status is **`payoff`** yet **zero** paragraph links | the arc is marked as landing but nothing fires it |
| `dormant` | status is **`develop`** yet **0–1** links project-wide | the arc is supposedly developing but has all but gone quiet |

It always **exits 0** — it informs, it never gates. `--json` emits
`{ thread_count, status_distribution, weight_distribution, tension_avg, blind_spots:
{ zero_links, payoff_unfired, dormant } }` for CI or a dashboard.

---

## The Bund surface

One read-only word (classified `STORE_READ`) exposes the thread set to scripts and
hooks:

```
ink.thread.list  ( -- list )   every thread as a dict { id, title, slug, status, weight }
```

Each dict describes one thread paragraph under the `Threads` book — the same
subtree the `inkhaven thread list` table reads, so the two agree. `status` and
`weight` are parsed from the thread's HJSON body (empty strings if unset). When
the `Threads` system book is absent (it only auto-spawns on 1.2.14+ projects) the
word returns an empty list rather than erroring. Writing threads is not exposed to
Bund — scripts read the weave, they don't author it.

---

## What it is not

- Not a new linking primitive — threads reuse the paragraph-link mechanism, and the
  doctor's evidence *is* those links.
- Not an AI reader — `add` / `list` / `doctor` / `export` are deterministic over the
  `Threads` book's HJSON and the project's link graph (the LLM thread audit is a
  separate editor surface).
- Not a gate — the doctor always exits 0; it points at blind spots, it doesn't fail
  your build.