Skip to content

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

Description

@thedancingdeveloper

Problem

When the implementer's reply is cut off mid-hunk, the harness correctly
detects it — and then fails the whole item. The model call itself returned
200, so the retry ladder never engages; validate_diff reports a fatal
patch problem and _execute returns.

One truncated response therefore costs a full item: the planner call, the
implementer call, and an attempt off the item's budget. Recovering it takes a
manual requeue, which re-plans and re-implements from scratch.

Why that is the wrong call

Truncation is the most obviously retryable failure there is. It says nothing
about the prompt and nothing about the model's suitability — the reply simply
stopped. It is not the same class of thing as "the model returned prose
instead of a diff", which is what the fatal path was built for.

Compare what happens either side of it: an HTTP 503 is retried up to six times
with backoff, and now also falls through to the next model in the chain
(#141). A reply that arrives complete but unusable fails once and stops. The
harness is most patient with the failure it can do least about, and least
patient with the one a single re-ask usually fixes.

Observed

Three times in one evening's runs, on the same item, across two different
models:

T3 patch_malformed — line 21: the patch ends mid-hunk — the reply was truncated
   FAIL T3: plan -> implement -> parse
   the implementer did not produce a usable diff — line 21: the patch ends mid-hunk

T2 patch_malformed — line 17: the patch ends mid-hunk — the reply was truncated
   FAIL T2: plan -> implement -> parse

Each was recovered by requeue + a fresh run, i.e. by paying for the whole
item again. The second and third occurrences were on gpt-5.4 reached through
a fallback chain, so this is not one bad model.

What would fix it

Treat "the reply was truncated" as a retryable call failure rather than a
fatal item failure:

  • re-ask the implementer, at the call level, so the planner's output is not
    thrown away;
  • with a chain configured, prefer the next route on the retry — a model that
    truncated once on a given prompt is a reasonable thing to route around;
  • keep the existing fatal path for replies that are malformed in ways a
    re-ask will not fix (no hunk header at all, prose instead of a diff).

PatchProblem already carries fatal, and the truncation case is already
detected and named precisely — the classification simply sits on the wrong
side of the line. Worth also capping how many re-asks one item may spend, so a
model that truncates every time still terminates.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions