Planning: close M3 and record active-head architecture
M3's review recommended planning changes and, following the M2 pattern, reported rather than applied them. This applies them, and adds the ADR the review asked for. ADR 012 records the architecture rather than the requirement. ADR 005 already says that going backward must preserve abandoned history and that the user sees Undo/Redo/Retry rather than branch management; it names a movable active head as the direction and stops. What M3 settled is the shape: the head is stored rather than derived, every read of the story is capped at it in one place, one mechanism moves it, the state of a position comes off the node rather than from a replay, the first write below a moved-back head is the divergence, and whether Redo exists is decided by the lineage rather than by a flag that could be stale. The last of those is the property worth keeping — a flag can be wrong and make the story wrong; a lineage cannot. Two semantics are ratified in STORY-BRANCH-SEMANTICS.md, both of them reversals or narrowings that a reader would otherwise take for bugs. Undo now crosses fork points and continues to the campaign opening, because refusing at the fork was a consequence of deleting rows the parent line was also reading, and nothing is deleted any more. And the system refuses to switch which take is live while a later story is off screen, because doing it quietly would leave retained history continuing from words the story no longer says. A new §14A covers editing in place. §14-15 describe the finished behaviour — the edit becomes authoritative, the state it implies is re-evaluated, a new continuation is created, the original is retained — and that requirement is intact and explicitly not weakened here. It is also not built, because re-evaluating state from prose a user typed needs M5's extraction pass. §14A says what exists in the meantime and why refusing is the minimum that holds the invariant rather than the destination. TECHNICAL-DESIGN.md gains §8.7 and §9.1, recording the implemented model and the bundle behaviour as fact in the way §5.2 records M1 and M2. §10.4 gains a constraint that is easy to lose: the snapshot half of the hybrid state model is a requirement, not an optimization. Head movement is a row lookup plus a restore, which is why Undo, Redo and Save Point restore cost the same at any distance into a campaign; a state model recoverable only by replaying from the opening would make all three proportional to campaign length, on exactly the long campaigns this product is for. DATA-MODEL.md records the head as stored on the campaign rather than derived from its newest turn — two campaigns holding identical turns can be read at different places, and nothing about the turns can tell them apart — and the branch disposition as implemented: the depth a divergent write left the branch at, deliberately advisory, and carried through export because every row of an abandoned line is exported either way. BUILD-MILESTONES.md marks M3 complete and states the one condition still open. M4 is told a Save Point is a durable pointer and that restoring one is head movement with a bounds check, not a restore system: a second mover is the specific failure to avoid, because the two paths would silently disagree about what restore means. M5 gets three constraints — keep state efficiently recoverable, move the test instrumentation rather than the assertions when the world-state protocol goes, and finish the narrator edit §14A defers. V1-ACCEPTANCE-TESTS.md clarifies ownership without lowering a bar. D10 keeps all three pass conditions and is explicitly recorded as *not* satisfied at the end of M3; what changed is that the document now says which milestone delivers which condition. D03's result is recorded as a full pass rather than the partial the text allowed for, I07 gains the pre-M3 bundle clause, and L01 gains the note that resolves its apparent conflict with A05 — a failed turn does advance the head by one, onto the player's retained input, and that is A05 working rather than L01 failing. README.md described a different application: a hosted demo, guest accounts, cloud providers, Postgres, a Render blueprint, an analytics dashboard, a QuickJS scripting engine, and 549 tests. M2 removed all of that and the README was never updated — a gap M2's own debt table missed. It now describes what this fork is, including the endpoint policy and the TLS behaviour, and the numbers in it are the current ones. M3's report is included here as its own evidence record: no separate baseline report was produced, so it carries the raw counts and runtime observations as well as the review, and §W records this closeout. SPECIFICATION.md and SECURITY-THREAT-MODEL.md are unchanged. M3 altered no product requirement and touched no path in the threat model. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QF5TcoB86QADgjHz1GZe8u
This commit is contained in:
co-authored by
Claude Opus 5
parent
7f082b61d8
commit
c8755c21c2
@@ -247,6 +247,47 @@ Must cover:
|
||||
|
||||
History operations are non-destructive, Redo works, divergence preserves old futures, and export/import reopens at the exact active head.
|
||||
|
||||
## Status: COMPLETE
|
||||
|
||||
Accepted 2026-09-03. Evidence: `planning/reports/M3-IMPLEMENTATION-REPORT.md`,
|
||||
which is M3's primary evidence record — no separate baseline report was produced,
|
||||
so that document carries the raw counts and runtime observations as well as the
|
||||
review. The architecture is recorded in **ADR 012**.
|
||||
|
||||
**Capabilities M3 delivered, which later milestones inherit rather than build:**
|
||||
|
||||
- **Undo that deletes zero accepted turns** — measured directly on row identity:
|
||||
15 rows before five Undos, 15 after,
|
||||
- **Redo**, round-tripping exactly (head 14 → 4 → 14) in transcript and in state,
|
||||
- a **single head-movement mechanism** every position change goes through, and a
|
||||
**single capped lineage** that bounds every read of the story,
|
||||
- **divergence decided by the lineage** rather than by a flag: the first write
|
||||
below a moved-back head forks, the displaced future keeps its rows, and
|
||||
ordinary Redo stops offering it with nothing to invalidate,
|
||||
- **branch-scoped memory isolation preserved for free** — a memory past the head
|
||||
is unretrievable and becomes eligible again on Redo, with no pruning and no
|
||||
re-embedding,
|
||||
- **an export that carries the reader's position**, so a campaign exported after
|
||||
two Undos imports still undone, with pre-M3 bundles opening at their tip,
|
||||
- **Undo across fork points** to the campaign opening, and refusal to change what
|
||||
a turn says while story descends from it off screen — both ratified in
|
||||
`STORY-BRANCH-SEMANTICS.md` (§5, §10, §14A).
|
||||
|
||||
**Outstanding closeout condition:** the required **browser smoke test has not
|
||||
been performed** — no session in which M3 was implemented or reviewed had a
|
||||
browser available. The equivalent sequence was driven end-to-end against the
|
||||
running application with real inference and a process restart, and every
|
||||
server-side behaviour it covers passes; the DOM-level behaviour of the Redo
|
||||
button, its disabled states and its keyboard shortcut remain unverified by
|
||||
observation. This does not block M4, which touches none of that wiring, but it
|
||||
remains an open M3 item until a human runs it.
|
||||
|
||||
**Debt carried forward, none of it blocking M4:** full narrator-edit state
|
||||
re-evaluation is deferred to M5 (`STORY-BRANCH-SEMANTICS.md` §14A records the
|
||||
interim refusal); `POST /adventures/import` returns every branch's rows rather
|
||||
than a head-capped window (inherited, harmless in the UI); `ActionPage` is
|
||||
constructed in two places. Full table in the implementation report §S.
|
||||
|
||||
---
|
||||
|
||||
# M4 — Named Save Points / Checkpoints
|
||||
@@ -283,6 +324,32 @@ Add durable user-facing Save Points on top of the active-head model.
|
||||
|
||||
The user can create a named Save Point, continue, restart, restore it, and continue differently without losing later history.
|
||||
|
||||
## Note from M3 — reuse the head machinery, do not build a second one
|
||||
|
||||
A Save Point is **a durable named pointer to a recoverable story position**, and
|
||||
nothing more. M3 made that position a stored coordinate and made moving to one a
|
||||
row lookup plus a state restore, so restoring a Save Point is head movement with
|
||||
a bounds check — not a restore system of its own.
|
||||
|
||||
Concretely, M4 should:
|
||||
|
||||
- store the coordinate, the name, and the metadata around them, and no copy of
|
||||
any story;
|
||||
- restore by calling M3's head-movement mechanism, so that state, transcript,
|
||||
context and memory eligibility all move together exactly as they do for Undo
|
||||
and Redo, and so that later history is retained rather than deleted (D13 is
|
||||
already satisfied by the mechanism);
|
||||
- let the existing fork-on-first-write-below-the-head rule handle divergence
|
||||
after a restore, rather than forking at restore time;
|
||||
- validate that a Save Point's coordinate is still on the lineage being read
|
||||
before moving to it.
|
||||
|
||||
A second restore path is the specific failure to avoid. The Phase 0B spike put
|
||||
the fork check in the write path and left Retry and add-take on the old one, and
|
||||
M3's cost was reconciling them; a parallel checkpoint mover would recreate that
|
||||
divergence in a place where the two paths would silently disagree about what
|
||||
"restore" means. See ADR 012.
|
||||
|
||||
---
|
||||
|
||||
# M5 — Genre-Neutral Authoritative Narrative State
|
||||
@@ -369,6 +436,41 @@ dropped.
|
||||
|
||||
Evidence: `planning/reports/M2-IMPLEMENTATION-REPORT.md` §K.2, §Q.
|
||||
|
||||
## Note from M3 — three constraints this milestone must satisfy
|
||||
|
||||
**1. Keep state efficiently recoverable at a retained position.**
|
||||
M3's head movement is a row lookup plus a state restore, which is why Undo, Redo
|
||||
and — from M4 — Save Point restore all cost the same regardless of how far into a
|
||||
campaign the position is. A state model recoverable only by replaying events from
|
||||
the campaign opening would make every one of those operations proportional to
|
||||
campaign length, on exactly the long campaigns this product exists for.
|
||||
|
||||
`TECHNICAL-DESIGN.md` §10.4 already selects a hybrid of validated events plus
|
||||
snapshots/cache. M3 turns the snapshot half from a preference into a requirement:
|
||||
keep per-node snapshots, or an equivalent cache with the same property, while
|
||||
adding the typed event model. See ADR 012.
|
||||
|
||||
**2. Move the instrumentation, keep the assertions.**
|
||||
The M2 note above applies with more force after M3: `test_head_cursor.py` adds
|
||||
roughly a dozen more tests that express position and rollback through the
|
||||
inherited gold counter. What they measure is *positional* — that the state
|
||||
belonging to a story position is restored when the head moves to it, in either
|
||||
direction, and that an abandoned line's state does not survive a divergence.
|
||||
Those properties must still hold over whatever carries state after M5. The file's
|
||||
own docstring says so.
|
||||
|
||||
**3. Complete the narrator edit.**
|
||||
`STORY-BRANCH-SEMANTICS.md` §14-15 requires that a narrator edit become
|
||||
authoritative and that the state it implies be re-evaluated. That requirement is
|
||||
intact and unimplemented: re-evaluating state from prose a user typed needs this
|
||||
milestone's extraction pass. M3 shipped the safe interim behavior only — an
|
||||
in-place edit is refused when story descends from the turn and is off screen
|
||||
(§14A), so retained history cannot be made to disagree with itself unseen.
|
||||
M5 is where §14-15 is finished: return to the state before the edited narration,
|
||||
treat the edited text as accepted output, re-evaluate the implied state, create a
|
||||
new continuation, and retain the original. The refusal in §14A is then replaced
|
||||
by that behavior rather than kept alongside it.
|
||||
|
||||
---
|
||||
|
||||
# M6 — Branch-Safe Context, Summaries, and Long-Term Story Memory
|
||||
|
||||
Reference in New Issue
Block a user