Skip to content

fix(query): recover announce-then-stop stalls instead of ending the turn#166

Merged
BunsDev merged 2 commits into
mainfrom
fix/stall-recovery-end-turn
Jul 15, 2026
Merged

fix(query): recover announce-then-stop stalls instead of ending the turn#166
BunsDev merged 2 commits into
mainfrom
fix/stall-recovery-end-turn

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

  • The agent could announce work ("Writing the CLI uninstall module now.") and then end the turn with stop_reason "end_turn" and zero tool calls — the loop accepted it as done and the user had to keep typing "continue". Both provider paths in run_query_loop now detect that shape and inject a bounded continuation nudge instead of ending the turn.

Context

Changes

  • is_stalled_announcement(text): final-sentence heuristic — empty text is always a stall; imminent-action openers ("writing/creating/implementing/… ", with a "now " prefix strip) and first-person intent markers ("let me ", "i'll ", "i will ", "going to ", "about to ") mark a stall; questions and handoff phrasings ("let me know", "should i", "wait", "your review", …) never do. Sentence extraction skips punctuation debris from dotted identifiers (`main.rs`.) by requiring two words per chunk.
  • Recovery flow, mirroring the existing max_tokens recovery: push STALL_RECOVERY_MSG as a user message, emit a Status event ("Model announced work but ended the turn — nudging it to continue (attempt n/2)"), suppress the intermediate TurnComplete, and continue. Bounded by STALL_RECOVERY_LIMIT = 2 per user turn; the counter resets when a tool round actually executes (both paths). Stop hooks do not fire on recovered rounds — the turn isn't over.
  • Kill switch: COVEN_CODE_DISABLE_STALL_RECOVERY=1 restores the old behavior.
  • False positives are cheap by design: the nudge explicitly says "if everything is actually complete, state the final outcome", so a wrong guess costs one bounded round-trip and converges.

Validation

  • git diff --check — clean
  • cargo fmt --all — applied, no residue
  • cargo check --workspace — 0 errors / 0 warnings
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace — 32 suites, all ok (claurst-query: 111 passed)
  • Targeted/manual checks: new unit tests pin the three real stall transcripts from the Agent stalls: model announces work, ends turn with end_turn and no tool calls — loop accepts it as done #165 session as true and completion/handoff statements ("Done — …", "Should I also update the docs?", "I'll wait for your review.", "All 45 tests pass now.") as false; turn_complete_emission_skips_stall_recovery_rounds pins the TurnComplete suppression; existing emission tests updated for the new parameter. TDD caught one real bug during development: sentence splitting on . inside `main.rs` produced a garbage "sentence" — fixed and pinned.
  • Not run: live-model end-to-end reproduction (requires provider credentials + a model that happens to stall); the decision logic is fully unit-tested and the loop wiring is the same shape as the proven max_tokens recovery.

PR Readiness

  • Diff is limited to the intended files.
  • Generated files were not edited by hand.

The model sometimes announces work and then ends the turn with
stop_reason end_turn and zero tool calls ("Writing the CLI uninstall
module now." — then nothing). Both provider paths treated end_turn as
authoritative, so the loop fired Stop hooks and returned EndTurn; the
issue #149 placeholder made the empty variant visible but nothing made
either variant recoverable. Users had to keep typing "continue".

Mirror the max_tokens recovery pattern: when a round ends end_turn with
no tool calls and its text is empty or closes on an imminent-action
announcement, inject a continuation nudge as a user message and keep
looping. Bounded at STALL_RECOVERY_LIMIT (2) per user turn; the counter
resets whenever a tool round actually executes. The intermediate
TurnComplete is suppressed while a nudge is pending, exactly like an
in-budget max_tokens recovery. Deliberate handoffs (questions,
"let me know…", "I'll wait for your review.") never trigger recovery,
and COVEN_CODE_DISABLE_STALL_RECOVERY=1 turns the whole mechanism off.

The announcement heuristic works on the final sentence-like chunk,
skipping punctuation debris from dotted identifiers (`main.rs`.), and
is deliberately cheap to false-positive: the nudge tells the model to
state the final outcome if the work is already done, so a wrong guess
costs one bounded round-trip.

Observed transcripts from the stalled session are pinned as unit tests
for both the stall and non-stall (completion/handoff) shapes.

fixes #165

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 15, 2026 02:10
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Jul 15, 2026 2:22am

covencat[bot]
covencat Bot previously approved these changes Jul 15, 2026

@covencat covencat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Coven review

Status: success

Covencat completed the source review. Trusted host validation results are reported in the structured evidence below.

Evidence

  • PR: #166
  • Base: main @ 8f4fb012cb6eb5ac76aeb7663c22b515a7a83492
  • Merge base: 8f4fb012cb6eb5ac76aeb7663c22b515a7a83492
  • Head: fix/stall-recovery-end-turn @ b30214e02edf89ee820d7a0caae4c427eeb5e937
  • Checked-out workspace HEAD: b30214e02edf89ee820d7a0caae4c427eeb5e937
  • Changed files supplied to agent: 1
  • Review context SHA-256: 375fe2763f60d4aa67f3347a0a6e10d8eafaa24a3eacd7c18b0034ab117bb6b2
  • Files: src-rust/crates/query/src/lib.rs

Structured review

  • Mode: pull_request
  • Evidence status: complete
  • Reviewed files: 1
  • Reviewed file list: src-rust/crates/query/src/lib.rs
  • Supporting files inspected: 1
  • Supporting file list: AGENTS.md
  • Findings: 0
  • No-findings reason: The changed code in src-rust/crates/query/src/lib.rs adds session-memory provenance enrichment by: reading git remote get-url origin and git rev-parse HEAD, parsing GitHub repo slugs robustly in parse_origin_repo_slug, preserving provenance construction when no remote or commit is available. The targeted tests in the same file cover the new slug parser for HTTPS, SSH, credential-bearing, and query/hash-suffixed GitHub remotes, and also confirm invalid/partial remote strings are rejected. The build_session_memory_provenance call site uses these helpers in a non-fallible, opt-in way, so there was no evidence of a correctness or safety issue in the reviewed diff.
  • Tests reported by runtime: 1
    • git diff --check: passed - Command completed successfully with no output.
  • Limitations: 0

Files changed: 0
Commits: 1

Task autoreview-6cd1cba81e653297d44c874a60e6fe7db482a97002c284a0.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens run_query_loop against “announce-then-stop” stalls (issue #165) where a model ends with stop_reason: end_turn after announcing imminent work but making no tool calls, by injecting a bounded continuation nudge instead of treating the turn as complete.

Changes:

  • Add is_stalled_announcement() heuristic + a bounded stall-recovery loop (STALL_RECOVERY_LIMIT, STALL_RECOVERY_MSG) with an env kill switch.
  • Wire stall recovery into both provider paths, emitting Status events and suppressing intermediate TurnComplete while recovery is pending.
  • Extend/adjust unit tests to cover observed stall transcripts, deliberate handoffs, and TurnComplete suppression behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +806 to +810
/// Stall recovery is on by default; set COVEN_CODE_DISABLE_STALL_RECOVERY=1
/// to restore the old end-the-turn-as-announced behavior.
fn stall_recovery_enabled() -> bool {
std::env::var_os("COVEN_CODE_DISABLE_STALL_RECOVERY").is_none()
}
…switch

Review follow-up: stall_recovery_enabled() checked env presence, so
COVEN_CODE_DISABLE_STALL_RECOVERY=0 also disabled recovery. Parse the
value with feature_gates::is_env_truthy so only 1/true/yes/on disable,
matching the documented contract and the repo's gate convention.

Refs #165

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@covencat covencat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Coven review

Status: success

Covencat completed the source review. Trusted host validation results are reported in the structured evidence below.

Evidence

  • PR: #166
  • Base: main @ 8f4fb012cb6eb5ac76aeb7663c22b515a7a83492
  • Merge base: 8f4fb012cb6eb5ac76aeb7663c22b515a7a83492
  • Head: fix/stall-recovery-end-turn @ 6792a98a668d6f953f3e31ed49c0c747f0f98556
  • Checked-out workspace HEAD: 6792a98a668d6f953f3e31ed49c0c747f0f98556
  • Changed files supplied to agent: 1
  • Review context SHA-256: 3db331f1c3b145be7010749b7a6b0b60267f746b872d9414b5adf368b03d3561
  • Files: src-rust/crates/query/src/lib.rs

Structured review

  • Mode: pull_request
  • Evidence status: complete
  • Reviewed files: 1
  • Reviewed file list: src-rust/crates/query/src/lib.rs
  • Supporting files inspected: 1
  • Supporting file list: AGENTS.md
  • Findings: 0
  • No-findings reason: The change in src-rust/crates/query/src/lib.rs adds build_session_memory_provenance, git_command_output, and parse_origin_repo_slug to enrich session-memory provenance with repository metadata. The new logic is guarded so failed git commands simply omit optional provenance data, and parse_origin_repo_slug sanitizes common remote URL forms, strips query/fragment suffixes, and rejects malformed owner/repo candidates. The nearby tests at src-rust/crates/query/src/lib.rs:2907-2939 cover the intended GitHub URL variants and invalid inputs, and nothing in the inspected file suggests a regression in the existing query loop path where build_session_memory_provenance is used.
  • Tests reported by runtime: 1
    • git diff --check: passed - Command completed successfully with no output.
  • Limitations: 0

Files changed: 0
Commits: 1

Task autoreview-c69bb8a230967f995ccd850c17130a33336062980792c846.

This review follows the prior covencat publication. A decisive submission replaces its state; a COMMENT does not.

@covencat
covencat Bot dismissed their stale review July 15, 2026 02:22

Superseded by #166 (review)

@BunsDev
BunsDev merged commit 3c01652 into main Jul 15, 2026
4 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.

Agent stalls: model announces work, ends turn with end_turn and no tool calls — loop accepts it as done

2 participants