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:
co-authored by
Claude Opus 5
parent
62a997f364
commit
b7005e6fdd
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user