fix(agent): clamp _last_flushed_db_idx after scaffolding drop (#31507) - #40714
fix(agent): clamp _last_flushed_db_idx after scaffolding drop (#31507)#40714andrebrassfield wants to merge 1 commit into
Conversation
Per issue #31507: intermediate _persist_session calls during tool processing set _last_flushed_db_idx = len(messages) at the old (higher) count. When _drop_trailing_empty_response_scaffolding pops trailing messages, len(messages) drops below _last_flushed_db_idx. The next _flush_messages_to_session_db computes flush_from = max(start_idx, old_high_idx) >= len(messages) → empty slice → final assistant responses never written to state.db. Fix: clamp _last_flushed_db_idx = min(_last_flushed_db_idx, len(messages)) after the scaffolding drop in _persist_session. 7 regression tests covering: - Index clamped after scaffolding drop (core regression) - Index unchanged when no scaffolding dropped - Index clamped to zero on full wipe - Index clamped after multi-message scaffolding drop - Post-scaffold invariant (randomized) - Final assistant response written after intermediate persist - Scaffolding drop then new messages written Closes #31507
|
Verified: _last_flushed_db_idx clamping after scaffolding drop is correct. Reviewed the full diff — this is a precise one-line fix for a real data-loss bug in the agent session persistence layer.
LGTM. |
Summary
Intermediate
_persist_sessioncalls during tool processing set_last_flushed_db_idxto the old (higher) message count. When_drop_trailing_empty_response_scaffoldingpops trailing messages, the index overshootslen(messages), causing_flush_messages_to_session_dbto compute an empty slice — final assistant responses are never written to state.db.Root Cause
In
_persist_session(run_agent.py, line ~1463):_persist_sessionsets_last_flushed_db_idx = len(messages)(e.g., 98)_persist_session→_drop_trailing_empty_response_scaffoldingpops 1-2 messages (len drops to 97)_flush_messages_to_session_dbcomputesflush_from = max(start_idx, 98) >= 97→ empty slice → no writesFix
Clamp
_last_flushed_db_idx = min(_last_flushed_db_idx, len(messages))after the scaffolding drop. One line, no persistence semantics changed.Option B (clamp) chosen over Option A (flush-before-drop) because the bug is index overshoot, not wrong order. The scaffolding drop is doing its job — what's wrong is the index getting ahead of the array.
Closes
_drop_trailing_empty_response_scaffoldingcauses_last_flushed_db_idxto overshootTesting
7 regression tests covering:
_last_flushed_db_idx <= len(messages)(50 randomized iterations)All 25 existing tests pass (
test_860_dedup.py,test_message_sequence_repair.py,test_compression_persistence.py).