fix(x): validate turn event batches before they become durable

The commit ritual persisted first and reduced second, so an illegal
append (e.g. a misbehaving tool reporting progress after its terminal
result) became durable before the reducer rejected it — permanently
corrupting the turn file and, through context references, blocking
every later turn in the session. Reduce the batch against the in-memory
history first: illegal batches now reject in memory for their caller
only, the file stays legal, and a failed commit no longer poisons
this.events for subsequent appends in the same invocation.

Adds a test that fires a stashed reportProgress after the tool's result
is durable (while a sibling keeps the invocation alive) and asserts the
late append rejects, nothing illegal reaches the file/stream/bus, and
the turn completes and stays re-advanceable. Updates the design doc
(§4.5, §24) to the reduce → persist → stream order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-07-10 22:32:33 +05:30
parent a1f03d0a69
commit 21c93913f4
3 changed files with 111 additions and 7 deletions

View file

@ -164,10 +164,18 @@ naturally complete independently. No behavior may rely on physical completion
order.
Durable appends are serialized through a single internal queue per invocation:
the persist → reduce → stream ritual runs to completion for one batch of
the reduce → persist → stream ritual runs to completion for one batch of
events before the next begins, so file order, in-memory order, and stream
order are identical by construction even while executions overlap.
The reduce step comes first as a validation gate: the batch is reduced
against the in-memory history before anything is written, so an illegal
append (for example, a misbehaving tool reporting progress after its
terminal result) rejects in memory for its caller only and never becomes
durable. A persisted illegal event would make every future read of the
file fail and, through context references (section 6.6), block the whole
session chain behind it.
## 5. Storage design
### 5.1 File location
@ -1656,6 +1664,11 @@ but the first turn implementation does not enforce it.
The reducer validates `context` structurally but treats it as opaque; it
never resolves references (section 6.6).
The runtime also uses the reducer as its append gate: every batch is
reduced against the existing history before it is persisted (section 4.5),
so a history that violates these invariants cannot become durable through
the loop.
## 25. Historical and live UI behavior
### 25.1 Historical load