fix: keep h2 patch repair on direct codex

This commit is contained in:
thanhnv
2026-07-18 12:20:22 +07:00
parent 1e5cfb341c
commit 850be145d7
3 changed files with 26 additions and 4 deletions
@@ -349,8 +349,13 @@ def repair_write_output(model: str, original_prompt: str, invalid_output: str, c
if patch_repair_attempts() == 0:
return False, "", {}, "goal_patch_missing"
def repair_prompt_for(previous_output: str, error: str) -> str:
hunk_instruction = (
"The previous diff is syntactically corrupt. Recompute every `@@ -old,count +new,count @@` header from the exact added/removed/context lines that follow it; do not omit or invent any hunk line. "
if "corrupt patch" in error.lower() else ""
)
return (
f"Your previous response violated the required write-output contract: {error}. "
f"Your previous response violated the required write-output contract: {error}. " +
hunk_instruction +
"Return ONLY one complete, applicable unified git diff inside a ```diff fence. The first non-empty line inside the fence MUST be `diff --git `. "
"Every hunk must be complete and the patch must pass `git apply --check`. Do not emit `index` lines, placeholder hashes, commentary, plans, summaries, or side effects. Preserve the original objective and workspace restrictions.\n\n"
f"ORIGINAL CONTRACT:\n{original_prompt}\n\nPREVIOUS INVALID RESPONSE:\n{previous_output[:8000]}"
@@ -390,7 +395,7 @@ def repair_write_output(model: str, original_prompt: str, invalid_output: str, c
# incomplete first diff. Give the same stronger direct model one
# corrective pass containing its own failed patch and git error
# before sending source context to a weaker gateway.
if candidate.endswith("-codex") and candidates.count(candidate) == 1:
if candidate.endswith("-codex") and attempt_number < patch_repair_attempts():
candidates.insert(attempt_number, candidate)
del candidates[patch_repair_attempts():]
repair_prompt = repair_prompt_for(output, last_reason)
@@ -122,6 +122,17 @@ class GoalPatchWorkflowTests(unittest.TestCase):
self.assertEqual([item.args[0] for item in call.call_args_list], ["openai:gpt-5.3-codex", "openai:gpt-5.3-codex"])
self.assertEqual(usage["repair_attempts"][-1]["status"], "pass")
def test_codex_keeps_all_repair_attempts_when_its_patch_headers_remain_corrupt(self):
with (
patch.dict(os.environ, {"CASAN_GOAL_PATCH_REPAIR_ATTEMPTS": "3", "CASAN_GOAL_PATCH_REPAIR_MODELS": "openai:gpt-5.3-codex,ollama:ornith"}, clear=False),
patch.object(ORCHESTRATOR, "call_model", return_value=(True, "corrupt", {}, "ok")) as call,
patch.object(ORCHESTRATOR, "validate_write_output", side_effect=ValueError("goal_patch_check_failed:error: corrupt patch at line 125")),
):
ok, _, usage, _ = ORCHESTRATOR.repair_write_output("ollama:ornith", "original", "invalid")
self.assertFalse(ok)
self.assertEqual([item.args[0] for item in call.call_args_list], ["openai:gpt-5.3-codex"] * 3)
self.assertEqual([attempt["provider"] for attempt in usage["repair_attempts"]], ["openai"] * 3)
def test_patch_outside_workspace_is_denied(self):
job = {"workspace": {"context_roots": ["apps/okr/frontend"]}}
content = "diff --git a/package.json b/package.json\n--- a/package.json\n+++ b/package.json\n@@ -1 +1 @@\n-a\n+b\n"