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
The agent announces work and then just stops — the turn ends with stop_reason "end_turn" and nothing happens. Observed repeatedly in one session (BUILD mode, claude-opus-4-8, local):
Turn A ends with: "Enough exploration — I have the wiring points. Writing the CLI command now. First the self-contained module…" → no tool calls, turn over.
User: "continue until completion" → model runs one Bash(cd …) round, then narrates "Now writing the CLI uninstall module." across rounds and ends with "Writing the CLI uninstall module now." → no Write/Edit calls ever happen, turn shows "done".
Session evidence: ~/.coven-code/sessions/d0bca548-…json (assistant messages [7], [9], [11] all end end_turn with announced-but-unexecuted work; [9] is fully empty).
Root cause
run_query_loop treats end_turn as authoritative in both provider paths (crates/query/src/lib.rs — the Anthropic match stop arm and the OpenAI-compat round loop). When the model emits intent-narration text but no tool calls and stops, the loop fires Stop hooks and returns QueryOutcome::EndTurn. The #149 follow-up made the empty variant visible (placeholder text) but not recoverable, and the announce-then-stop variant isn't even surfaced — the turn just reads as if the agent chose to stop mid-task.
There is already a working in-loop recovery precedent: the max_tokens arm injects MAX_TOKENS_RECOVERY_MSG and continues, bounded by MAX_TOKENS_RECOVERY_LIMIT, with should_emit_turn_complete suppressing the intermediate TurnComplete.
Proposed fix
Bounded stall recovery mirroring the max_tokens pattern, in both provider paths:
When a round ends end_turn with no tool calls and the text is empty or its closing sentences are an imminent-action announcement ("Writing the CLI uninstall module now.", "Let me confirm deps, then write the module.") — inject a continuation nudge as a user message and continue instead of ending the turn.
Bounded at 2 nudges per user turn; counter resets on any tool round. On exhaustion, fall through to today's behavior (placeholder for the empty case).
Deliberate handoffs never trigger recovery: questions and wait-for-user phrasing ("Should I…?", "Let me know…", "I'll wait for your review.") end the turn as before.
Suppress the intermediate TurnComplete while recovering (same as max_tokens recovery).
Kill switch: COVEN_CODE_DISABLE_STALL_RECOVERY=1.
Worst case for a false positive is one extra bounded round-trip in which the model states the outcome and stops — the nudge text explicitly allows "if the work is complete, state the final outcome".
Symptom
The agent announces work and then just stops — the turn ends with
stop_reason "end_turn"and nothing happens. Observed repeatedly in one session (BUILD mode, claude-opus-4-8, local):(no response — model ended the turn with stop_reason "end_turn").Bash(cd …)round, then narrates "Now writing the CLI uninstall module." across rounds and ends with "Writing the CLI uninstall module now." → no Write/Edit calls ever happen, turn shows "done".Session evidence:
~/.coven-code/sessions/d0bca548-…json(assistant messages [7], [9], [11] all endend_turnwith announced-but-unexecuted work; [9] is fully empty).Root cause
run_query_looptreatsend_turnas authoritative in both provider paths (crates/query/src/lib.rs— the Anthropicmatch stoparm and the OpenAI-compat round loop). When the model emits intent-narration text but no tool calls and stops, the loop fires Stop hooks and returnsQueryOutcome::EndTurn. The #149 follow-up made the empty variant visible (placeholder text) but not recoverable, and the announce-then-stop variant isn't even surfaced — the turn just reads as if the agent chose to stop mid-task.There is already a working in-loop recovery precedent: the
max_tokensarm injectsMAX_TOKENS_RECOVERY_MSGand continues, bounded byMAX_TOKENS_RECOVERY_LIMIT, withshould_emit_turn_completesuppressing the intermediate TurnComplete.Proposed fix
Bounded stall recovery mirroring the max_tokens pattern, in both provider paths:
end_turnwith no tool calls and the text is empty or its closing sentences are an imminent-action announcement ("Writing the CLI uninstall module now.", "Let me confirm deps, then write the module.") — inject a continuation nudge as a user message andcontinueinstead of ending the turn.TurnCompletewhile recovering (same as max_tokens recovery).COVEN_CODE_DISABLE_STALL_RECOVERY=1.Worst case for a false positive is one extra bounded round-trip in which the model states the outcome and stops — the nudge text explicitly allows "if the work is complete, state the final outcome".