Skip to content

fix: recount a hunk header the model miscounted, rather than discarding it - #143

Merged
thedancingdeveloper merged 1 commit into
mainfrom
fix/recount-hunk-headers
Aug 4, 2026
Merged

fix: recount a hunk header the model miscounted, rather than discarding it#143
thedancingdeveloper merged 1 commit into
mainfrom
fix/recount-hunk-headers

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

Closes #142.

What was actually wrong

The implementer produced this — complete, correct, applies-in-spirit:

@@ -1,10 +1,21 @@
-from calc import add, subtract
+import pytest
+
+from calc import add, divide, subtract
 ...
+def test_divide_by_zero():
+    with pytest.raises(ZeroDivisionError, match="cannot divide by zero"):
+        divide(1, 0)

test_calc.py has 9 lines; the header declares 10. The body supplies 9
old / 20 new against a declared 10 / 21 — one too many on each side, because
the model counted the trailing newline. The patch is 38 lines and git reports
corrupt patch at line 39: it parsed by the declared counts and ran out of
input.

No rung rescued it. --unidiff-zero fails identically to strict git apply
tolerance needs something to be tolerant with, and the parser has already hit
EOF. So a correct body was discarded over arithmetic, costing the planner call,
the implementer call, and an attempt.

Four consecutive runs, two different models. A common model error.

The fix, and why it is safe

Recompute the counts from the body. They are a derivable property of it, so
this guesses at nothing — which is precisely what separates it from #133, where
a -0,0 header carried the only statement about where the lines belonged and
there was nothing to recompute. Refused there; repaired here.

Recounting runs as a second variant of each rung, after the patch as written,
so a well-formed diff is still applied byte-for-byte as the model produced it.

Telling two faults apart

They wore one symptom. The rule is principled rather than a threshold:

  • over-counted context — a context line counts on both sides, so the
    shortfall is equal on each. Body complete. Recountable.
  • cut off mid-hunk — loses a mix of +, - and context, so the shortfalls
    differ. Recounting would make the header agree with a partial body and
    apply, say, a deletion whose replacement never arrived. Stays fatal.

The existing TRUNCATED_DIFF fixture is exactly that dangerous shape and is
still refused — its test now asserts the distinction rather than the wording.

Also: the diagnostic lied

It said the reply was truncated for a reply that was complete at 266 output
tokens
. That sent me looking for a token limit that did not exist, and I filed
#142 against the wrong cause before reading the preserved patch. It now states
what is true: how many lines short, on which side.

Result

The backlog that failed 2/3 on four consecutive runs now completes 3/3, one
item via git apply (recounted). Tests pass independently on each branch (4, 4
and 6). Suite, ruff and mypy . green.

…ng it

An implementer produced a byte-for-byte correct body under `@@ -1,10 +1,21 @@`
for a nine-line file — one line too many on each side, because it counted the
trailing newline. `git apply` parses by the declared counts, reaches the end of
input a line early, and reports `corrupt patch`. No rung of the ladder helped:
tolerance needs something to be tolerant *with*, and the parser has already run
out of input. The item failed, throwing away the planner's work and an attempt.

This happened on four consecutive runs of the same item, across two models. It
is a common model error, not one bad reply.

The counts are a derivable property of the body, so recomputing them guesses at
nothing. That is what separates this from a `-0,0` header against a file with
content (#133), where the header carried the only statement about *where* the
lines belong and there was nothing to recompute — refused there, repaired here.
Recounting is tried as a second variant of each rung, after the patch as
written, so a correct diff is still applied exactly as the model produced it.

Two faults wore one symptom, and they are now told apart. A header that
over-counted trailing context is short by the SAME amount on both sides,
because a context line counts on each; its body is complete, and it is
repaired. A reply genuinely cut off mid-hunk loses a mix of `+`, `-` and
context, so the shortfalls differ — recounting that would make the header agree
with a partial body and land, say, a deletion whose replacement never arrived.
That stays fatal.

Also fixes the diagnostic, which said "the reply was truncated" for a reply
that was complete at 266 output tokens. It sent me looking for a token limit
that did not exist, and I filed an issue against the wrong thing before reading
the preserved patch.

Live: the three-item backlog that had been failing 2/3 for four runs now
completes 3/3, one of them via `git apply (recounted)`.
@thedancingdeveloper
thedancingdeveloper merged commit cfc4ccd into main Aug 4, 2026
2 checks passed
@thedancingdeveloper
thedancingdeveloper deleted the fix/recount-hunk-headers branch August 4, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: a hunk header that over-declares its line counts is discarded, and misreported as a truncated reply

1 participant