Keep task reminders transactional across interrupts - #721
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an interruption edge case in fabro-agent session handling by making task-reminder system messages “transactional”: reminders are staged per request and only written to durable history once the assistant turn successfully commits, preventing invalid message role sequences after an interrupt + steer.
Changes:
- Stage task reminders per-round (
task_reminder_if_needed) and inject them into the outbound LLM request without persisting them immediately. - Commit the reminder into
Session.historyonly when the assistant response is recorded, so interrupted rounds don’t leave stray system messages behind. - Add a regression test that reproduces “interrupt after reminder injection, then steer” and asserts request ordering and durable history correctness.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Stage the pending task reminder as a Message and add Message::to_llm_message so durable history and the round-staged turn share one turn-to-wire conversion. Replace the one-off BlockingAfterFirstOutputProvider with request capture and an EventsThenPending variant on ScriptedStreamProvider, add a shared make_session_with_provider_and_tools helper, and assert the reminder tests against task_reminder::TASK_REMINDER_TEXT instead of a substring. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Ran a three-way simplification review (reuse, quality, efficiency) on this branch and pushed one cleanup commit (9c152cc):
Net −16 lines. Verified: Noted but intentionally skipped: the double 🤖 Generated with Claude Code |
Summary
Root cause
An interrupted assistant turn was discarded, but its pre-round system reminder remained in durable history. The steering message then followed that system message, producing an invalid message sequence that OpenRouter rejected.
Test plan