M5: genre-neutral authoritative narrative state, with review corrections

Replaces AI-DnD's RPG relative-delta world state with the genre-neutral typed
narrative state of ADR 010: explicit, absolute, allowlisted events proposed by
the model, validated by the application, applied to one authoritative document,
and snapshotted per position so restore stays a row read.

This commit includes the corrective pass that followed the independent review
in planning/reports/M5-IMPLEMENTATION-REPORT.md. The invariant it exists to
hold is:

    visible active transcript position == stored head == authoritative state

Narrator editing (D10, STORY-BRANCH-SEMANTICS §§14-15)

  A narrator edit no longer rewrites a row. It returns to the state before the
  turn, takes the reader's exact text as the accepted narration, re-derives the
  state that text implies, and becomes a new active continuation — while the
  original narration keeps its words, its live flag and its whole future as
  retained history. At the tip the correction is another take; with story below
  it, it forks. No new history machinery: this is the existing fork/take/head
  path with the reader's text in place of a generated reply. The §14A refusal
  is therefore gone for narrator turns, and remains only for player input.

Pre-M5 positions

  Migration 88 backfills the empty narrative document onto every action written
  before M5, and a missing snapshot now restores the empty document instead of
  leaving the previous position's state standing. Restoring to an old Save
  Point no longer leaves a later position's entities and facts on screen.

Narrator context

  Replayed history carries prose only; the machine-readable block is no longer
  reconstructed into past turns, where it contradicted the authoritative state
  in the same prompt. A fact withdrawn by a manual correction is now named as
  no longer true, with the reader's reason, rather than silently dropped.

Also

  - state_changes joins the action-list bulk read, removing one query per row.
  - Extraction takes only the application's own protocol payload: an ordinary
    ```json or ```python block in a story survives, and a mangled proposal
    still does not reach the reader.

Planning: ADR 013 records the authoritative document shape; §§14-15/14A, D10,
C04 and BUILD-MILESTONES are updated to describe what exists. Debt is recorded
against M8 (scenario editor UX) and M9 (export of the audit trail).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWU4gTfLYY6Qq9U7aa9Qw2
This commit is contained in:
JesseMarkowitz
2026-09-05 07:01:50 -04:00
co-authored by Claude Opus 5
parent 62a997f364
commit b7005e6fdd
57 changed files with 7257 additions and 474 deletions
+57 -25
View File
@@ -377,37 +377,69 @@ Therefore the system must:
The system must not simply replace visible text while leaving stale state behind.
## 14A. Editing In Place, Before §14-15 Are Implemented
### How this is built (M5 corrective pass)
§14 and §15 describe the finished behavior: a narrator edit becomes
authoritative, the state it implies is re-evaluated, a new continuation is
created, and the original narration and its future are retained. That
requirement stands in full and is **not** weakened by this section.
All five conditions are implemented. A narrator edit is not a write to the row
being corrected — nothing on the line being left is written to at all. It is the
same operation as playing the turn again from here, with the reader's words in
place of a generated reply, and it reuses the fork, take, head and snapshot
machinery M3 and M4 already provide rather than introducing a second history
mechanism.
It is not yet built. Re-evaluating the state implied by prose a user typed
requires the authoritative narrative-state extraction that the genre-neutral
state milestone introduces, so the finished behavior is completed there. What
exists in the meantime is a plain correction: it changes the words of one turn
and re-evaluates nothing.
```text
before after
That correction is safe while everything descending from the turn is on screen,
because the user can see what their change has to stay consistent with. It is
not safe when a continuation descends from the turn and is **off screen** —
undone and not yet redone, or left behind by a divergence — because the edit
would then silently change the words that retained story was written from, and
nothing on screen would show it. Retained history is not permitted to be made to
disagree with itself in a way the user cannot see.
parent parent
└── original narrator ├── original narrator ──> old future [retained]
└── old future └── corrected narrator [active]
```
Until §14-15 are implemented, the system must therefore **refuse** an in-place
edit of a turn that has story descending from it which is not currently being
shown, and say why. The user resolves it the same two ways as §10:
Two shapes, chosen by whether anything was written after the turn:
- **Redo**, bringing the later story back into view; or
- **play the turn again from here**, which is the §13 shape — return to the
parent position, continue differently, and keep the old line as retained
history.
- **Nothing below it.** The attempts at that turn are still leaves, so the
correction joins them as another take and the original stays beside it in the
pager. No branch is created, and the head does not move.
- **A story below it**, on screen or not. That story was written as a
continuation of the words that are there now, so it keeps them: the correction
leaves the path just before the turn, and the departed line keeps its node,
its future and its live flag.
Refusing is the minimum that keeps the invariant. It is not the destination.
The state the correction implies is derived from the snapshot on the node
*before* the edited turn — one row read, not a replay — and put through the same
validation the model's own proposals face. The correction's node then carries
that document as its own outcome, so the campaign's live state and the state
stored at the head are the same document. That equality is the invariant:
```text
visible active transcript position == stored head == authoritative state
```
The M5 review found it broken by the interim implementation, which rewrote the
row in place and rewound the campaign's live state to that position while the
head stayed at the tip. The reader saw a full transcript over a state document
describing an earlier moment, and the snapshots below the edit still described
prose that no longer existed.
## 14A. Editing In Place — Superseded for Narrator Output
§§14-15 are implemented, and the refusal this section described no longer
applies to narrator turns. It is kept because it explains why the current shape
is the one it is.
The refusal existed because an in-place edit rewrote the words that a
descending, invisible story had been written from, and retained history is not
permitted to be made to disagree with itself where the user cannot see it. A
fork removes the premise: the off-screen future keeps the exact narration it
descends from, so there is nothing left to refuse. What was the unsafe case is
now an ordinary one.
Two in-place edits remain, and neither can produce that disagreement:
- **A player's own input** (§13) is still edited in place, and is still refused
while a story descends from it off screen. Bringing §13 onto the same footing
as §§14-15 is not yet done.
- **A take the story is not telling** has no continuation of its own — keeping
one is what forking is for — so correcting its words contradicts nothing.
## 16. Manual State / Canon Correction