From c510f58ef3c980d78eaa85fb6348f0081314e594 Mon Sep 17 00:00:00 2001 From: yoni Date: Mon, 28 Sep 2026 01:53:04 +0000 Subject: [PATCH] fix: retry transient verifier inconclusive results --- strix/fix/prepare.py | 1 + tests/test_fix_preparation.py | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/strix/fix/prepare.py b/strix/fix/prepare.py index de135e6c4..ab4039743 100644 --- a/strix/fix/prepare.py +++ b/strix/fix/prepare.py @@ -660,6 +660,7 @@ async def prepare_fix( # noqa: PLR0915 if ( previous_workspace_digest == workspace_digest and previous_verification_fingerprint == verification_fingerprint + and verifier.decision is not VerificationDecision.INCONCLUSIVE ): manifest, summary, artifact_ref = await manifest_builder(workspace) return _result( diff --git a/tests/test_fix_preparation.py b/tests/test_fix_preparation.py index 616eaaaf6..9aa66a2a7 100644 --- a/tests/test_fix_preparation.py +++ b/tests/test_fix_preparation.py @@ -514,6 +514,44 @@ async def test_prepare_fix_retries_when_unchanged_state_has_new_feedback( assert len(result.attempt_history) == 3 +@pytest.mark.asyncio +async def test_prepare_fix_retries_transient_inconclusive_verification( + tmp_path: Path, +) -> None: + workspace, commit = _workspace(tmp_path) + verifier_calls = 0 + + async def verify( + context: PreparationContext, + checks: list[CheckResult], + reproduction: CheckResult | None, + ) -> VerifierResult: + nonlocal verifier_calls + verifier_calls += 1 + if verifier_calls < 3: + return VerifierResult( + decision=VerificationDecision.INCONCLUSIVE, + summary="The independent verifier reached its turn limit.", + gaps=["Independent verification did not complete within 30 turns."], + ) + return await _verified(context, checks, reproduction) + + result = await prepare_fix( + _request(_candidate(commit), attempts=4), + workspace, + repair=_noop_repair, + verify=verify, + ) + + assert result.state is PreparationState.READY + assert result.attempts == 3 + assert [attempt.verifier.decision for attempt in result.attempt_history] == [ + VerificationDecision.INCONCLUSIVE, + VerificationDecision.INCONCLUSIVE, + VerificationDecision.VERIFIED, + ] + + @pytest.mark.asyncio async def test_prepare_fix_evaluates_budget_exhausted_patch(tmp_path: Path) -> None: workspace, commit = _workspace(tmp_path)