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 -16
View File
@@ -517,6 +517,31 @@ Mara never learned where the silver key was found.
- correction is auditable,
- old transcript is not silently rewritten unless explicitly edited.
### Result — PASS for the live campaign (M5 corrective pass, 2026-09-04)
The M5 review found the first condition failing: the state section dropped a
withdrawn fact and the replayed history handed it straight back as an accepted
event, in the protocol's own words, with nothing saying it had been corrected.
Two changes fixed it, and both are pinned by
`test_c04_a_withdrawn_fact_does_not_come_back_through_history`:
- replayed history is prose only — the machine-readable block is no longer
reconstructed into past turns, so a turn's record of what was true *then*
cannot contradict what is authoritative now;
- a withdrawn fact is named in the state section under "No longer true — do not
treat these as established", with the reader's reason, rather than silently
omitted. Omitting it left the narration that first asserted it as the only
account in the prompt.
The old transcript is not rewritten: an invalidated fact stays in the document
with its status, its reason and its provenance.
**Carried debt, deferred to M9 (export/recovery):** a campaign's `state_events`
and `state_proposals` are not carried in an export, so an imported copy keeps
the correction's *effect* — the fact is still marked `manual_correction` — but
reports zero audit events. The auditable condition therefore holds for a live
campaign and not across a round trip.
---
## C05 — Canon Beats Reference
@@ -721,25 +746,41 @@ Mara wears a green cloak.
- downstream state is re-evaluated,
- old version/future remains retained/disposable.
### Milestone ownership
### Result — PASS (M5 corrective pass, 2026-09-04)
All three pass conditions stand for v1. They are delivered across three
milestones, and this note records which is which rather than reducing the
requirement:
All three pass conditions are met, and each was demonstrated rather than
inferred.
- **Edit becomes authoritative on the active path.** The reader's text is stored
verbatim, with only the protocol block stripped, and no model is called
(`test_the_corrected_text_is_used_verbatim_not_regenerated`).
- **Downstream state is re-evaluated.** The state is derived from the snapshot on
the node before the corrected turn and put through the normal validation path,
and the campaign's live document then equals the document stored at the head
(`test_editing_a_narrator_turn_with_visible_descendants_forks`, and the
live-state/head-snapshot assertion carried by every test in that group).
- **Old version/future remains retained/disposable.** Nothing on the departed
line is written to: the original node keeps its words and its live flag, and
every row played after it still exists
(`test_the_old_narration_and_its_future_leave_the_active_transcript`,
`test_editing_a_narrator_turn_with_an_undone_future_keeps_it`).
Verified in a real browser on the case the M5 review reproduced as broken:
correcting the earliest narrator turn with four turns of story on screen, then
checking the transcript, the retained rows, the state panel, the live document
against the head snapshot, Undo/Redo, and the next turn's assembled prompt —
16 of 16 checks.
The delivery history, kept because it explains the shape:
- **M3 — safe history behavior.** Replaying a narrator turn with different text
forks, keeps the original take and its future, and starts the new continuation
from the correct earlier state. In-place editing of a turn is **refused** while
story descends from it off screen, so retained history cannot be made to
contradict itself unseen (`STORY-BRANCH-SEMANTICS.md` §14A). Delivered.
- **M5 — authoritative state re-evaluation.** The second pass condition. Making a
hand-typed narrator correction authoritative and re-evaluating the state it
implies requires the narrative-state extraction pass, so it is completed there,
and the §14A refusal is replaced by it. Outstanding.
- **Later browser UX work — the finished editing workflow.** How the user reaches
and confirms the operation. Outstanding.
D10 is therefore **not** satisfied at the end of M3, and is not scored as such.
forks and keeps the original take and its future. In-place editing was
*refused* while story descended from a turn off screen.
- **M5 — authoritative state re-evaluation**, and the refusal replaced. A
narrator edit now forks rather than rewriting a row, so the off-screen case it
refused is simply handled (`STORY-BRANCH-SEMANTICS.md` §§14-15).
- **Later browser UX work.** How the reader reaches and confirms the operation
is M8's; the operation itself is complete and reachable through the ✎ control.
---