Skip to content

Two edit blocks naming one file by different path strings render its diff twice, and the second copy cannot apply #219

Description

@thedancingdeveloper

Found during the end-to-end review of the direct executor (#216), deliberately not fixed there — the fix changes plan_edits' public keying, which is not something to smuggle into a PR about rendering defects.

The mechanism

plan_edits keys its order list on the path as the model wrote it. So a reply containing:

a.txt
<<<<<<< SEARCH
one
=======
1
>>>>>>> REPLACE

./a.txt
<<<<<<< SEARCH
two
=======
2
>>>>>>> REPLACE

produces two entries — a.txt and ./a.txt — for one file. to_diff then renders that file's diff twice. The first hunk applies; the second is computed against the same "before" content and no longer matches the tree, so git apply refuses it.

_resolve already collapses both to the same Path, so the validation is correct: the second edit is checked against the first's pending content. Only the keying is by raw string.

Severity, honestly

Low, and it fails safely. It needs a model to write the same file two ways in one reply, which is unusual, and the outcome is a refused patch and a spent attempt — not a corrupt tree. It is filed because "unusual" is not "impossible", and because the failure message would blame the model for an edit it got right, which is the class of bug this repository has spent a day removing.

What the fix has to be careful about

plan_edits returns dict[str, tuple[str, str]] keyed by the model's path string, and apply_edits and to_diff both consume that. Keying by resolved path instead is the obvious answer, but it changes what callers see — including the order the diff is rendered in, which is currently "the order the model first named each file" and is deliberate.

Blind spots

  • Nobody has checked whether a model ever actually does this. If the answer is never, the right fix might be to refuse the duplicate outright rather than merge it — a refusal the model can act on beats a silently merged edit.
  • Other path spellings (sub/../a.txt, a trailing slash, a symlinked directory inside the tree) have not been enumerated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dispatchQueue, claims, worker supervision, admission controltype:taskUnit of implementation work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions