fix(tools): skip degenerate identical hunks in V4A validation

The apply phase already skips a hunk whose -/+ lines are identical
(patch_parser.py '(search_lines == replace_lines): continue'), but the
validation phase lacked the guard: such a hunk reached
fuzzy_find_and_replace, whose identical-strings error names
old_string/new_string — parameters that don't exist in patch mode — and
failed the whole atomic patch that apply would have accepted. Mirror
the apply-phase skip in validation; regression test drives a mixed
degenerate+live patch end-to-end (short text dodges the
is_already_applied >=8-char rescue).
This commit is contained in:
kshitij
2026-08-12 15:17:49 +05:30
parent 6061377bbf
commit 8d0d908bef
2 changed files with 32 additions and 0 deletions
+24
View File
@@ -140,3 +140,27 @@ class TestV4AAlreadyApplied:
r = _patch_tool(mode="patch", patch=patch_content, task_id="t-v4a")
assert r["success"] is True, r
assert f.read_text() == "STATUS = 'migrated_to_v2_schema'\n"
def test_degenerate_identical_hunk_skipped_in_validation(self, workdir):
"""A hunk whose -/+ lines are identical is a no-op: the apply phase
skips it, so validation must not fail the patch (previously it
reached fuzzy_find_and_replace, whose identical-strings error names
old_string/new_string — parameters that don't exist in patch mode).
The short text also dodges is_already_applied's >=8-char rescue."""
f = workdir / "degen.py"
f.write_text("A = 1\nB = 2\n")
patch_content = (
"*** Begin Patch\n"
f"*** Update File: {f}\n"
"-A = 1\n"
"+A = 1\n"
"@@ B @@\n"
"-B = 2\n"
"+B = 3\n"
"*** End Patch\n"
)
r = _patch_tool(mode="patch", patch=patch_content, task_id="t-v4a")
assert r["success"] is True, r
text = f.read_text()
assert "A = 1" in text # degenerate hunk left intact
assert "B = 3" in text # live hunk applied
+8
View File
@@ -323,6 +323,14 @@ def _validate_operations(
replace_lines = [l.content for l in hunk.lines if l.prefix in {' ', '+'}]
replacement = '\n'.join(replace_lines)
if search_lines == replace_lines:
# Degenerate hunk whose -/+ lines are identical: the apply
# phase skips it as a no-op, so validation must not fail it
# — fuzzy_find_and_replace would reject the identical
# search/replacement with old_string/new_string guidance
# that has no meaning in V4A patch mode.
continue
new_simulated, count, _strategy, match_error = fuzzy_find_and_replace(
simulated, search_pattern, replacement, replace_all=False
)