fix(repo-explorer): recover from a blank survey turn instead of aborting - #222
Open
gadievron wants to merge 1 commit into
Open
fix(repo-explorer): recover from a blank survey turn instead of aborting#222gadievron wants to merge 1 commit into
gadievron wants to merge 1 commit into
Conversation
gadievron
requested review from
dgeyshis,
shahar-davidson and
sounil
as code owners
August 8, 2026 08:18
An empty/malformed completion raises LLMResponseError in every adapter (anthropic/google/openai-responses, and openai-chat once its empty-content guard lands). The exploration loop called complete() unguarded, so a single blank turn aborted a survey that may already have read useful context -- turning a transient into a total failure of application-context / threat-model generation. Catch it and retry the same messages (appending nothing keeps the user/ assistant roles alternating), bounded by MAX_CONSECUTIVE_EMPTY_TURNS so a persistently-empty model still fails loudly and well short of the MAX_TURNS budget. A refusal (LLMRefusalError, a subclass) is caught first and re-raised so a safety signal is never churned past. Tests: recover-on-transient, fail-loud-and-bounded-on-persistent, and refusal-propagates. The tests raise repo_explorer's own bound exception class so they stay stable when another test purges utilities.* from sys.modules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gadievron
force-pushed
the
fix/repo-explorer-empty-turn-resilient
branch
from
August 8, 2026 08:31
80e8a47 to
7c4d2c4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
context/repo_explorer.py::explore_repositoryruns a multi-turn agentic survey (application-context / threat-model generation). Itsbinding.adapter.complete()call was unguarded. An empty/malformed completion raisesLLMResponseErrorin every adapter — anthropic, google, openai-responses, and openai-chat once #208 lands — so a single blank turn aborted a survey that may already have read useful context, turning a transient into a total failure. (Pre-existing: the "nudge and continue" recovery only ever worked for the one provider that returned an emptyend_turninstead of raising; surfaced by the #206/#207/#208 collision review.)Fix
Catch
LLMResponseErroraroundcomplete()and retry the same messages (appending nothing keeps the user/assistant roles alternating). Bound consecutive blanks withMAX_CONSECUTIVE_EMPTY_TURNS = 2so a persistently-empty model still fails loudly and well short of theMAX_TURNSbudget. A refusal (LLMRefusalError, a subclass) is caught first and re-raised — a deliberate safety signal must never be churned past.Reproduction
A survey where turn 1's completion is empty (adapter raises
LLMResponseError) and turn 2 is a validfinish.Regression tests (
tests/test_repo_explorer_loop.py)test_empty_turn_is_recovered_not_fatal— transient blank → survey recovers and finishes.test_persistent_empty_turns_fail_loud_and_bounded— every turn blank → raises, and does not burn the whole budget.test_refusal_is_not_retried_propagates— a refusal propagates immediately, is not retried.The tests raise repo_explorer's own bound exception class so they stay stable when another test purges
utilities.*fromsys.modules(a pre-existing test-isolation hazard).Compatibility
No API/signature change. Behavior change: a blank turn is retried (bounded) instead of aborting; a persistently-empty model still fails loudly. Independent of #208 (anthropic/google already raised on empty), and composes with it.