Skip to content

framework: a turn's continuation records no undo checkpoint, so an undo after a budget restart takes the whole turn - #1017

Open
rufalupa666-netizen wants to merge 1 commit into
anima-research:mainfrom
rufalupa666-netizen:fix/undo-whole-turn-across-restart
Open

rufalupa666-netizen wants to merge 1 commit into
anima-research:mainfrom
rufalupa666-netizen:fix/undo-whole-turn-across-restart

Conversation

@rufalupa666-netizen

Copy link
Copy Markdown

Fixes #1013.

A budget restart at a tool boundary, a tool-result-guard retry, the direct API's tool_results_ready and a held call's retry (TURN_RETRY) each start a stream of their own at attempt === 0, and each recorded a turn checkpoint; only the guard retry was excepted. An undo after one took back the rows since that start, the reply, and left the round's tool call and its result in the window, which the next request still carried: William's reproduction on main, and ours on connectome-host 0.24.1 / openai-codex, where every tool-calling turn restarts at every boundary against the 150k stream budget.

Change. A start that carries the logical turn on (isTurnContinuation(reason), or isTurnRetry(trigger)) records no checkpoint, so undoLastTurn walks back to the turn's first. The redo stack is still cleared on every attempt-0 start, as new work. A budget refusal of a continuation has no checkpoint of its own to drop, which holdForBudgetRefusal already allows for (checkpoint !== undefined).

This takes the first of the two shapes we asked about in #1013 (the restart's start as a continuation of the logical turn for undo). If "/undo took the last round only" is the behaviour you'd rather keep and say, this PR is easy to turn into that instead.

Test, on turn-continuation.test.ts's restartTurn harness (wait: false, run to inference:turn_ended): two streams, one checkpoint; the undo leaves the window at its pre-turn length with three or more rows gone; the next request carries neither tool_use nor tool_result nor the reply. Without the change the same test reads undoDepth: 2 and one row retracted.

Changelog fragment included.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SkepYxcPem6pmgRxKd2RBY

…do after a restart takes the whole turn

A budget restart at a tool boundary, a tool-result-guard retry, the
direct API's tool_results_ready and a held call's retry (TURN_RETRY)
each start a stream of their own at attempt 0, and each recorded a turn
checkpoint; only the guard retry was excepted. An undo after one took
back the rows since that start, the reply, and left the round's tool
call and its result in the window, which the next request still carried
(anima-research#1013, reproduced by nissa-seru on main and on our host).

A start that carries the logical turn on (isTurnContinuation, or
isTurnRetry) records none now, so undoLastTurn walks back to the turn's
first checkpoint. The redo stack is still cleared on every attempt-0
start, as new work. A budget refusal of a continuation has no checkpoint
of its own to drop, which holdForBudgetRefusal already allows for.

Test on turn-continuation's restartTurn harness: after the restart and
the reply, one checkpoint; the undo leaves the window where it stood
before the turn, three or more rows gone, and the next request carries
neither the tool round nor the reply.

Fixes anima-research#1013.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkepYxcPem6pmgRxKd2RBY
@anima-review

anima-review Bot commented Nov 11, 2026

Copy link
Copy Markdown

Automatic review of 2eca88816e67.

About these findings

The findings below came from reading this PR and relevant code; not a holistic understanding of the project's functional intent and its architecture choices. Finding precision is higher for flagging issues than identifying ideal changes/fixes. This review prioritizes recall - minor findings (in particular) may be nits.

Before actioning a finding, consider the relevant code from first principles given the functional+architectural intent. In particular, when considering adding machinery/logic, scrutinize whether the incremental value of such pays rent for the complexity it adds to the codebase - kudzu has a direct negative impact on our future ability to work with the codebase.

Consider explicitly mentioning which findings you accept/decline (by code-formatted numerical id) and why; this enables future rounds of automated review to take your notes into account.

Findings, most severe first:

  • 1. should fix at src/framework.ts:28135, src/framework.ts:28130, src/framework.ts:19974, src/framework.ts:10112, changelog.d/undo-whole-turn-across-restart.fixed.md:13: After this change a tool_results_ready start records no checkpoint, but that start comes after a round made by the direct Agent API (agent.runInference), and that round never records a checkpoint either, so the work ends up with no checkpoint of its own and undoLastTurn pops the previous framework turn's checkpoint instead.
    • Failing case: A resident finishes a framework turn T1, which has checkpoint C1. The host then calls agent.runInference(...) directly; the reply has a tool call, and the agent waits for tools with no stream. The tool result arrives, and the non-streaming fallback queues tool_results_ready, whose start runs and replies. Before this change, an undo took back that reply, from the tool_results_ready start's checkpoint. Now there is no checkpoint for this work, so the undo pops C1 and takes T1's rows out of the window along with the direct round and the reply. With no earlier checkpoint, the undo returns { undone: false } and the reply can't be undone at all.
  • 2. should fix at test/turn-continuation.test.ts:57, src/framework.ts:28135: The new condition also stops a held call's retry (TURN_RETRY, including a merged start marked by framework.ts:23285) and a tool_results_ready start from recording a checkpoint, but the only new test covers the budget restart; none of the other paths whose behaviour changed is tested.
    • Failing case: If isTurnRetry(trigger) were removed from the condition, or tool_results_ready dropped from it, every test would still pass. A test of the tool_results_ready path after a direct agent.runInference would have exposed the missing checkpoint described in the other finding.
  • 3. minor at changelog.d/undo-whole-turn-across-restart.fixed.md:8, src/framework.ts:28125: Both the changelog and the new code comment list the tool-result-guard retry among the starts that "each recorded a turn checkpoint", but before this change the code already skipped the checkpoint for that retry.
    • Failing case: A maintainer or host operator reading the changelog would conclude that undo after a tool-result-guard retry was broken before this release and is fixed by it. In fact the base condition attempt === 0 && trigger?.reason !== 'tool_result_guard_retry' already recorded no checkpoint for it, so its behaviour doesn't change.

@anima-review

anima-review Bot commented Nov 11, 2026

Copy link
Copy Markdown

Automatic context view of 2eca88816e67 merged onto 36b6dfe9de39.

What a context view runs and what its checks judge: CHECKS.md.

The records of 1 of the 80 runs differ between the base and this head.

All 4 of this change's changed source lines that map to code ran in at least one profiled drive. 9 changed source lines map to nothing the host runs: 9 of src/framework.ts's. The whole list is in the fold below.

Parts left out where an arm's stack doesn't bring what they need: H1r/W16's kept-history on the base; H1r/W16's kept-history on the head.

Separately, 1 of the checks' flags differs between the base and the head, in 1 run; it's listed under "Checks' flags that differ", below.

Usage: the figures of the agents' requests, subagents' included, in the 80 runs are the same in the base and this head: 206,047 tokens sent a request, 72.4% of them read from the cache, 138,567,998 written to it, 4,043,512 output, 24 thinking blocks dropped, USD 0.5446 at list price a priced request and 123 requests with no list price. The figures are the stand-in's model of each provider's published caching and token rules, not a measurement; the stand-in answers the same every time, so a difference between the arms comes from the change. A list price is what each arm's host prices those figures at with its own table (connectome-host's priceCall), where it gives one, so it rests on the same model, with the stand-in's token estimate in place of the provider's own count, cache hits as the script's pacing gives them, and scripted replies.

Context in the 80 runs: in each run, the number of seams, the tokens in the parts they changed or took out, the most one agent request held and the number of summariser requests stayed the same. What these count.

No model read these records for this view, which started on its own. A view asked for with a /context-deltas comment by nissa-seru gives a model's account of the records of the pull request's head as it is when that view runs.

Runs whose records differ: 1 of 80

H3/W9.

Checks' flags that differ (1)
  • H3/W9 step 321741.13.redo, check 11 flagged on the base 1 more time than on the head: fixture:dry:shape: message 0: result 442693.51.call1 answers no call in the message before it
Upgraded runs beside their fresh twins
On the base requests tokens sent a request read from the cache written to it output thinking dropped last request dropped calibration
H1b/continuation/from-0.9.0 15 159,911 58.3% 857,419 20,328 24 0 1.28, confirmed after a reset
H1b/continuation/fresh 15 155,647 63.2% 715,495 20,328 0 0 1.29, confirmed
H1b/continuation/from-0.20.0 15 155,632 63.2% 715,480 20,328 0 0 1.29, confirmed
H1b/continuation/from-0.23.0 15 155,647 63.2% 715,495 20,328 0 0 1.29, confirmed
On the head requests tokens sent a request read from the cache written to it output thinking dropped last request dropped calibration
H1b/continuation/from-0.9.0 15 159,911 58.3% 857,419 20,328 24 0 1.28, confirmed after a reset
H1b/continuation/fresh 15 155,647 63.2% 715,495 20,328 0 0 1.29, confirmed
H1b/continuation/from-0.20.0 15 155,632 63.2% 715,480 20,328 0 0 1.29, confirmed
H1b/continuation/from-0.23.0 15 155,647 63.2% 715,495 20,328 0 0 1.29, confirmed

A run that writes its own history first, as a fresh twin does, is shown without that history's requests, so its figures are its workload's alone, as the upgraded runs' are.

Changed source lines and deletions not seen to run, by file

src/framework.ts: lines 28123–28131 map to nothing the host runs.

Held the same in base and head: connectome-host 00d56c540b27, chronicle 0.6.0, context-manager 0.27.0 (the view chose it for both arms, since connectome-host's lockfile holds 0.25.3, which doesn't meet ^0.27.0 from the base), membrane 0.8.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

undoLastTurn after a tool-calling turn takes out only the final round: the tool call and its result stay in the window, and the agent answers from them

1 participant