The local coding harness has one job it needs from a coder model: read a bug, produce a unified diff, apply it, watch the tests go green. The first half of that loop kept working. A model would read a failing test, correctly identify the broken line, and describe exactly what should change. Then it would try to write that change as a diff, and the diff would be garbage.
Not wrong. Garbage. apply_patch_dry_run, the harness’s fail-closed guard before anything real ever gets touched, kept rejecting the output, and every rejection looked different.
The actual problem: diagnosis and diff-authoring are different skills
The first sighting was narrow. docs.update’s fallback model was producing diffs missing full unified-diff headers, live-verified three separate times. It read like a fallback-model problem, the kind of thing you’d expect from the model that only runs when the primary isn’t available. Then the same check widened to coding.bugfix’s actual primary coder role, and the same failure class showed up there too: a diff whose def add(a, b): context line was missing its required leading space character. apply_patch_dry_run correctly rejected it as corrupt. Nothing broke. But the pattern had just stopped being one fallback model’s quirk.
What got weird: four runs, three different ways to fail
coding.refactor shares the same patch-generation loop as coding.bugfix, which meant it inherited the same risk through a third call site. Live verification made the pattern impossible to write off as noise: four real runs, against two different coder models (devstral-small-2:24b and qwen2.5-coder:7b) on two different scratch scenarios, and every single one failed apply_patch_dry_run. Three genuinely different malformed-diff defects showed up across those four runs: one run got the @@ hunk header’s line-count math wrong, another produced a diff line missing its required +/-/space prefix character entirely, and a third generated a hunk with no ---/+++ file-header lines at all.
Four attempts. Three distinct ways to be wrong, not four, since one defect type showed up more than once. If this had been the same mistake repeating every time, it would suggest a fixable prompt gap, tell the model not to do that one thing. Three different mistakes across four attempts and two different models suggested something closer to a structural ceiling: hand-authoring unified-diff syntax character-for-character is a harder, more brittle skill than the models were bringing to the table, independent of which model was asked.
What worked: the guard, not the model
Here is the part worth sitting with before reaching for a bigger model or a cleverer prompt: nothing dangerous happened. Every one of the four failures was caught, cleanly, by mechanical verification that never trusted the model’s own claim that its diff was valid. The harness didn’t need the model to be right. It needed the model to be checkable, and it was.
That’s the reusable pattern, ahead of any fix: when you’re trusting a local model to produce a structured, syntax-sensitive artifact, the artifact’s validity should never depend on the model getting the syntax right on the first try. It should depend on a mechanical check that can fail closed as many times as it needs to, while you go find out why.
What next
This is deliberately where the post stops. The harness had, at this point, a confirmed failure pattern, a guard that was doing its job, and no fix yet. The next move wasn’t to try a bigger model or a more detailed prompt, it was to actually investigate why the failures kept varying, instead of assuming the answer and building around a guess.
What Next
Falsify Your Favorite Hypothesis First
Next in the Local Coding Harness arc: two plausible explanations for the malformed-diff pattern, both killed by the harness's own stored event logs before a single line of fix code gets written.