From d05ab1cf63ef321a52393a4bd07e3b4d4f0ca054 Mon Sep 17 00:00:00 2001 From: Guido Berning <30648231+SaJaToGu@users.noreply.github.com> Date: Fri, 26 Jun 2026 00:07:22 +0200 Subject: [PATCH] =?UTF-8?q?fix(worker):=20hard-stop=20reject-artifact=20pa?= =?UTF-8?q?tch=20failures=20(=C2=A760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/solve_issues.py | 1 + tests/test_worker_adapters.py | 11 +++++++++++ tests/test_worker_execution.py | 13 +++++++++++++ workers/base.py | 6 ++++-- workers/execution.py | 3 +++ workers/openrouter_worker.py | 10 +++++++--- 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/scripts/solve_issues.py b/scripts/solve_issues.py index 1d787fd..60cf2d8 100644 --- a/scripts/solve_issues.py +++ b/scripts/solve_issues.py @@ -1953,6 +1953,7 @@ def run_openrouter_direct_worker( 0 — Alle gefundenen Patches erfolgreich angewendet. 1 — Patches gefunden, aber alle fehlgeschlagen oder API-Fehler. 2 — Modell hat Prosa ohne auswertbare Diffs zurueckgegeben. + 5 — Patch-Anwendung hat Reject-Artefakte erzeugt; harter Fehler. 6 — Nur ein Teil der Patches wurde angewendet; harter Fehler. Args: diff --git a/tests/test_worker_adapters.py b/tests/test_worker_adapters.py index 9776830..1b7d6dc 100644 --- a/tests/test_worker_adapters.py +++ b/tests/test_worker_adapters.py @@ -1019,6 +1019,17 @@ def test_partial_patch_failure_with_changes_stops(self): True, ) + def test_patch_validation_failed_with_changes_stops(self): + """Reject artifacts are a hard stop, even with file changes.""" + from workers.base import PATCH_VALIDATION_FAILED_RETURN_CODE + self._assert_consistent( + PATCH_VALIDATION_FAILED_RETURN_CODE, + " M scripts/solve_issues.py\n", + "patch_validation_failed", + False, + True, + ) + def test_aider_side_effects_only_stops(self): """Nur Aider-Nebenwirkungen ohne Änderungen → stoppt.""" self._assert_consistent(1, "?? .aider.chat.history.md\n", diff --git a/tests/test_worker_execution.py b/tests/test_worker_execution.py index cd2ba00..d77fe17 100644 --- a/tests/test_worker_execution.py +++ b/tests/test_worker_execution.py @@ -32,6 +32,7 @@ run_worker_subprocess, ) from workers.base import ( + PATCH_VALIDATION_FAILED_RETURN_CODE, PARTIAL_PATCH_FAILURE_RETURN_CODE, WorkerOutcome, WorkerRunResult, @@ -113,6 +114,18 @@ def test_partial_patch_failure_with_changes_stops(self): self.assertFalse(outcome.should_continue) self.assertTrue(outcome.has_changes) + def test_patch_validation_failed_with_changes_stops(self): + outcome = classify_worker_outcome( + self._make_result( + PATCH_VALIDATION_FAILED_RETURN_CODE, + "VALIDATION-FAILED: Reject-Artifakte wurden erkannt", + ), + " M scripts/solve_issues.py\n", + ) + self.assertEqual(outcome.reason, "patch_validation_failed") + self.assertFalse(outcome.should_continue) + self.assertTrue(outcome.has_changes) + def test_nonzero_without_changes_stops(self): outcome = classify_worker_outcome(self._make_result(1), "") self.assertEqual(outcome.reason, "nonzero_without_changes") diff --git a/workers/base.py b/workers/base.py index 0f0521f..74f80af 100644 --- a/workers/base.py +++ b/workers/base.py @@ -15,6 +15,7 @@ no_changes → Worker erfolgreich, keine Änderungen nonzero_with_changes → Worker mit Fehlercode, aber Änderungen vorhanden nonzero_without_changes → Worker mit Fehlercode, keine Änderungen + patch_validation_failed → Patch-Anwendung erzeugte Reject-Artefakte partial_patch_failure → Worker hat nur einen Teil der Patches angewendet rate_limit_deferred → Codex Rate-Limit, kein sofortiger Retry failed_worker → Worker nicht gefunden oder nicht startbar @@ -28,8 +29,9 @@ from typing import Any -# Return code for workers that applied only part of a generated patch set. -# This must be treated as a hard failure even when the working tree changed. +# Return codes for patch-applying workers that must hard-stop even when +# the working tree changed. +PATCH_VALIDATION_FAILED_RETURN_CODE = 5 PARTIAL_PATCH_FAILURE_RETURN_CODE = 6 diff --git a/workers/execution.py b/workers/execution.py index dd0991f..5df2311 100644 --- a/workers/execution.py +++ b/workers/execution.py @@ -31,6 +31,7 @@ from typing import Any, Callable from workers.base import ( + PATCH_VALIDATION_FAILED_RETURN_CODE, PARTIAL_PATCH_FAILURE_RETURN_CODE, WorkerOutcome, WorkerRunResult, @@ -309,6 +310,8 @@ def classify_worker_outcome( if result.returncode == PARTIAL_PATCH_FAILURE_RETURN_CODE: return WorkerOutcome(False, has_changes, "partial_patch_failure") + if result.returncode == PATCH_VALIDATION_FAILED_RETURN_CODE: + return WorkerOutcome(False, has_changes, "patch_validation_failed") if result.returncode == 0 and has_changes: return WorkerOutcome(True, True, "changed") if result.returncode == 0: diff --git a/workers/openrouter_worker.py b/workers/openrouter_worker.py index a26c701..b61aecc 100644 --- a/workers/openrouter_worker.py +++ b/workers/openrouter_worker.py @@ -27,7 +27,10 @@ import requests import json -from workers.base import PARTIAL_PATCH_FAILURE_RETURN_CODE +from workers.base import ( + PATCH_VALIDATION_FAILED_RETURN_CODE, + PARTIAL_PATCH_FAILURE_RETURN_CODE, +) # JSON-Schema für strukturierten Output (OpenRouter response_format). @@ -804,7 +807,8 @@ def run_direct( oder API-Fehler. 2 — Modell hat Prosa ohne auswertbare Diffs zurückgegeben. 3 — Request-Timeout überschritten. - 5 — Patch-Anwendung hat Reject-Artifakte (.orig/.rej) erzeugt. + 5 — Patch-Anwendung hat Reject-Artifakte (.orig/.rej) erzeugt; + der Lauf ist nicht lieferbar und muss als Fehler gelten. 6 — Nur ein Teil der Patches wurde angewendet; der Lauf ist nicht lieferbar und muss als Fehler gelten. @@ -927,7 +931,7 @@ def run_direct( ) if has_rejects: - returncode = 5 + returncode = PATCH_VALIDATION_FAILED_RETURN_CODE log_lines.append( f"[openrouter_direct] VALIDATION-FAILED: Reject-Artifakte wurden erkannt " f"und bereinigt. Der gesamte Lauf gilt als fehlgeschlagen."