You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
An operator's undo leaves a pending wake to run on the rewound branch, and that run clears the redo stack #381
After undoLastTurn rewinds the store, a wake that was pending before it still runs, on the rewound branch. The turn it starts can answer an input the undo took back, after the message that caused the wake has been rewound away. Its start also clears the redo stack, so the operator can't redo the undo. undoLastTurn checks that the agent is idle (framework.ts:7647), and an agent whose next turn waits on a provider hold or a gate debounce is idle, so the window lasts as long as the hold or the debounce.
A probe at 91e70eda, on Linux with Node 24.21.0, used a mock provider and a module that stores each external message and asks for inference:
The pending wake
The operator
What the wake's request holds
redo() afterwards
An organization acceleration 429 holds the turn for SECOND
undoes twice
FIRST alone. SECOND and the reply to FIRST are gone, and the agent answers FIRST again.
{ redone: false }
A gate policy with { debounce: 600 } holds the wake for SECOND
undoes once
FIRST and the gate's line for it, without the reply, then a new gate line quoting SECOND. The agent answers.
{ redone: false }
With one undo under the provider hold, the undo takes the held turn's own checkpoint. The branch's messages stay as they were, and the released turn answers SECOND as it would have. Its start still clears the redo stack, and redo() returns { redone: false }.
The redo stack goes because a turn's first start records a checkpoint and clears it (framework.ts:11743-11746). That code looks at the attempt and the trigger's reason, not at when the wake was queued.
A fix. One rule for every kind of pending wake when an operator moves the store's branch (an undo, a redo, a rollback or a branch switch): the move withdraws every wake pending at that moment, the trace gets one outcome for a withdrawn wake, and the operator command's answer names what it withdrew. The agent then stays idle until its next input or an operator's request. That costs the one-undo case above: SECOND stays on the branch, unanswered until the next wake.
Activity
docs(inference): the bound's comment and the changelog claim only wha…