fix: keep h2 patch repair on direct codex
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user