Mid · Core path · 7 chapters
Patch application and conflict handling
Validating an edit before it lands
Patch handling is a senior-level reliability boundary: a superficially successful edit can silently corrupt code or waste an entire agent run.
Read these first
What you will know
Apply a patch and detect a conflict rather than corrupting a file
Context drift, duplicate matches, and offset shifts can make a syntactically valid result semantically wrong.
Validate an edit before it lands
Parsing catches syntax, while linting and compilation expose unresolved names, type errors, and generated-file mismatches.
Roll back a bad edit cleanly
A snapshot, inverse patch, or clean checkout must restore both contents and the index without discarding unrelated work.
Handle a patch that applies but breaks the build
A successful hunk match says nothing about tests, imports, types, or behavior exercised only at runtime.
Reproduce a blind-apply corruption case and add a guard that prevents it
Fuzzy context matching and partial writes can leave plausible-looking code that passes superficial inspection.
Chapters
- 1
A non-zero offset is a conflict wearing a success message
"Hunk #2 succeeded at 812 (offset 47 lines)" should stop the loop. The anchor matched somewhere other than where the patch aimed, and in a file full of copy-pasted retry blocks the applier resolves that ambiguity by proximity — a coin flip that put the payments edit into the refunds path. Compares a strict applier that rejects on any fuzz with a permissive one, and prices the failure each turns into. Build the fixture that proves it: two near-identical blocks and a patch whose anchor is ambiguous corrupts on demand, and the same file is what shows the guard refusing to apply.
- 2
The base moved between the read and the apply
The agent read the file at turn 3 and emits its edit at turn 9. In between, its own earlier hunk landed, a formatter ran, a generator rewrote the file, or the engineer typed in another window — and a byte-exact anchor can still match a file that no longer means what the model believed, so zero offset and zero fuzz prove nothing here. Capture a content hash when the file is read, compare it immediately before applying, and re-read and re-plan rather than reapplying when it differs. Otherwise the in-memory validation that follows is checking a candidate assembled on a base that no longer exists.
- 3
Parse the candidate in memory, then write by rename
One run in seven comes back after six minutes with a collection error and nothing ran, because a syntax error reached disk. Parse the candidate content in memory first — milliseconds against six minutes, and every downstream result is meaningless until it passes. Then write through a temporary file in the same directory and rename over the target, so a cancelled run cannot leave a half-written module that happens to parse at a function boundary.
- 4
A clean apply plus a parse is a claim about one file
A patch applying with no offset and no fuzz, on a file that then parses, has established exactly two things: the text landed where it was aimed, and the result is syntactically valid. Neither reaches unresolved names, types, imports or behaviour, and the module's own green suite does not extend them beyond the file. Naming that boundary is what stops a loop accepting an edit on the strength of a clean apply.
- 5
Validation depth has to match the blast radius of the edit
An edit to a shared response model applies, parses and passes its module's suite, and CI then fails: a checked-in generated client still declares the old field, and two dependent services no longer compile. Every check the loop ran was local and the change was not. Running the full build on every iteration prices the loop out of existence, so the rule must recognise which paths carry a wider obligation.
- 6
Roll back exactly the paths the patch touched
git checkout -- . at the repository root restores the broken file and takes forty minutes of unrelated uncommitted work with it; git stash hides the human's changes while the agent runs and skips untracked files. Restore instead from a snapshot taken immediately before the edit, covering only the touched paths and leaving the rest of the tree and the staged index as the engineer left them. Includes when to refuse and hand it to a person.
- 7
Order the validation ladder around the moment of writing
Parse, format, lint, type-check, compile, module tests, affected-target build: sequencing them is the design, and the file is written only once the cheap checks pass, so a failure is a no-op rather than a rollback. Writing first means an unparseable file exists on disk while formatters, language servers and test runners observe it. Answers the objection that CI already runs this, and names the check to drop first under pressure.
External resources
- Apply Patch | OpenAI APIdevelopers.openai.com
The V4A patch format spec and the contract for accepting or rejecting an edit
- PatchPilot: A Cost-Efficient Software Engineering Agent with Early Attempts on Formal Verificationarxiv.org
PatchPilot’s cost-efficient loop for generating, applying, validating and refining patches after failures.
- ClaudeLike: How to Apply a Tool-Based Agent to Patch Generationteam-atlanta.github.io
Field report on validating generated patches before they land in a repo
- Git Internals - Diff and Patch (merge & rebase series - part 1)Brief
The underlying diff/patch mechanics agents rely on, including hunk matching