Skip to content

fix(channel): roll back history on PromptCancelled to prevent poisoned turns#114

Merged
jamiepine merged 3 commits intospacedriveapp:mainfrom
Marenz:fix/history-rollback-promptcancelled
Feb 22, 2026
Merged

fix(channel): roll back history on PromptCancelled to prevent poisoned turns#114
jamiepine merged 3 commits intospacedriveapp:mainfrom
Marenz:fix/history-rollback-promptcancelled

Conversation

@Marenz
Copy link
Collaborator

@Marenz Marenz commented Feb 21, 2026

The previous rollback fix (PR #104) correctly handles hard errors but misses PromptCancelled. This causes channels to permanently break after any cancelled turn (e.g. the reply tool fires and the hook terminates the turn).

Root cause: When Rig raises PromptCancelled, the history it carries was snapshotted before the tool batch executed. It contains the assistant's tool-call message but not the tool results. Writing that back leaves a dangling tool-call that poisons every subsequent turn with:

tool call result does not follow tool call (2013)

Concrete trace: reply tool fires → hook returns Terminate { "reply delivered" }PromptCancelled raised → carried history has assistant(tool_call: reply) but no tool result → written to self.state.history → next turn fails permanently.

Fix: extract the history write-back into apply_history_after_turn and treat PromptCancelled like hard errors — truncate to history_len_before. The side-effects (reply sent, worker spawned, etc.) have already happened; we don't need the history to reflect them.

MaxTurnsError continues to write back — Rig pushes all tool results into a User message before raising it, so history is consistent.

Tests: five unit tests covering all four match arms plus an end-to-end scenario verifying the next turn is clean after a PromptCancelled rollback.

…d turns

When Rig raises PromptCancelled (e.g. reply tool fires and hook terminates
the turn), the carried history snapshot was captured *before* the tool batch
executed. It contains the assistant's tool-call message but not the tool
results, leaving a dangling tool-call that causes every subsequent turn to
fail with 'tool call result does not follow tool call (2013)'.

Also roll back on hard errors — Rig mutates history in-place but does not
carry it back in error variants, which can leave dangling tool-call messages.

MaxTurnsError is safe to write back — Rig pushes all tool results into a
User message before raising it, so history remains consistent.
@Marenz Marenz marked this pull request as draft February 21, 2026 23:20
Tests cover all four cases of apply_history_after_turn:
- Ok: writes full post-turn history back
- MaxTurnsError: writes back (Rig includes tool results)
- PromptCancelled: rolls back to pre-turn snapshot
- Hard error (CompletionError): rolls back to pre-turn snapshot

Also includes an end-to-end scenario test verifying the next turn
is clean after a PromptCancelled rollback (no dangling tool-calls).
@Marenz Marenz marked this pull request as ready for review February 22, 2026 00:16
- ToolError (ToolNotFoundError) rolls back via catch-all arm
- Rollback on empty history is a no-op and doesn't panic
- Rollback when nothing was appended leaves history unchanged
Copy link
Member

@jamiepine jamiepine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect

@jamiepine jamiepine merged commit 3444aeb into spacedriveapp:main Feb 22, 2026
1 of 3 checks passed
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.

2 participants