Idiomatic Minimal Patch Preference
Prefer the smallest framework hook that fixes the bug over reimplementing larger APIs — agents often localize correctly then over-build the edit.
Software-engineering agents frequently localize the right symbols yet
submit an over-specific patch: reimplement a higher-level API instead of
the one-line framework hook maintainers expect. Example
(django readonly password hash label): the agent overrides label_tag on the
field (custom HTML, mid-file imports) while the gold fix is
id_for_label → None on the widget so Django’s label machinery omits
for — smaller surface, idiomatic extension point.
The same over-specific-patch pattern recurs across models and languages under gold-standard matching qualitative review, and takes two related shapes:
- Invented primitive over existing helper. Claude 2 on a bool-input
TypeError(scikit-learnHuberRegressor) adds a hand-rolled_validate_datathatastypes booleans tofloat64, instead of routing through the framework's existingcheck_X_y(..., dtype=[...])validator the gold patch uses — passes both fail-to-pass and pass-to-pass tests, but is stylistically and organizationally inconsistent with the codebase. Similarly, extending anisinstance(..., bool)check with manual== 0 or == 1branches (matplotlibsharex/sharey) where the gold patch instead folds the cases into an existingcheck_in_listhelper and deletes now-dead related code — the agent's ad hoc branch neither generalizes nor cleans up after itself. - Generic/popular answer over codebase-specific mechanism. Claude 2 fixes
a cyclic-import false positive by adding a bare
TYPE_CHECKINGimport guard — the common "Stack-Overflow-style" answer to that class of Python error — when the gold fix detects the guard via the codebase's ownastroid.If/is_typing_guard()dependency-graph machinery. A popular general-purpose answer can be syntactically the idiomatic-looking fix while still being the wrong idiom for this codebase; prefer prompts and demos that push the agent to search for an existing project-specific mechanism before reaching for a generically "correct" pattern it has seen often in pretraining.
A third, opposite-looking but related shape is missing a branch that an
analogous sibling already implements. Fixing sphinx's napoleon extension
to honor a napoleon_use_param config flag in one section-parsing function,
the agent edits the right function but hardcodes the True-branch behavior
instead of branching on the config value — while a sibling function in the
same file (_parse_parameters_section) already implements exactly that
branch for a near-identical section type. The bug was locally correct-looking
(tests without the flag pass) but wrong under the flag's False setting, which
a same-file analogous implementation would have revealed as the expected
shape. Before treating localization as done, search the file (or module) for
a sibling function handling a structurally similar case and mirror its
branching, not only its target symbol — an existing parallel implementation
is often a more reliable template than reasoning about the config surface
from scratch. Quantitatively, applied model patches tend to run noticeably
shorter and touch fewer files/functions than gold across the board (SWE-bench
oracle: ~19.6 lines / 1.0 files for applied Claude 2 patches vs ~44.1 lines /
1.2 files for gold on the same instances; gold averages 3.0 functions touched
overall) — models default to the smallest change that makes the target test
pass rather than the structurally complete one, which is this same
missing-branch pattern at scale, not only isolated case studies.
The under-sized-patch tendency above is specific to prompted general models. LoRA-fine-tuned code models trained on gold patches show the opposite asymmetry: patches averaging longer than gold, not shorter, with more hallucinated content, style mismatches, and long-range-dependency errors as patch length grows. Which direction to expect is a property of how the model was built, not a universal law — prompted models tend to stop once tests pass, fine-tuned models tend to over-produce plausible-looking code toward the training distribution's typical patch length. Size the specific offline harness test checks (diff-size delta from gold, novel-symbol rate) to the failure direction your model class actually exhibits rather than assuming under-building is the only risk.
Design implications for the harness:
- Failure-derived prompt tips: prefer
existing override hooks (
id_for_*, empty returns that disable defaults) and already-imported helpers (to_native_stringvs decode-then-builtin_str) before inventing new HTML/render or type-branch paths. - After localization, prompt a short “what is the smallest API that controls this behavior?” thought before guardrailed edit.
- Score patches not only on tests but on diff size / API novelty in offline harness tests when gold patches exist — overbuild is a dominant unresolved-task mode under compound mistake amplification. Agent patches are often larger than gold partly from leftover reproduction code; require cleanup before submit under agent episode termination modes.
Guardrails still help: lint catching missing imports blocked a bad first edit
and forced a retry — but they do not catch semantically heavy correct-syntax
patches. Pair with runtime checks of the reported behavior before submit
when the environment allows; submitting without verifying accessibility/for
output is a goal-failure under
agent planning failure modes.
Sources
- SWE-agent (Yang et al.), pp. 106–118
- SWE-bench (Jimenez et al.), pp. 31–45 (Appendix F)