A paper out of Queen’s University, “To Add Is Machine, To Delete Is Human”, finds that the leading models on the SWE-bench Verified leaderboard leave in place a sizeable share of the code the original fix removed, even on tasks all of them solve. They reach the right file almost every time and still decline to cut the line. Instead of deleting, many passing patches keep the targeted logic alive behind a condition or fallback, a pattern the authors name Guard-and-Go.
I had the same gap logged in my own AI coding agent pipeline, with no way to check it. Their taxonomy caught a bug in a detector I had written for the deletion direction: my version only inspected the guarded path, while their dominant form, Retained Path as Live Fallback, guards the reported case and leaves the removed logic as the else default, so a detector has to look at every branch.
Fixing that one showed me how to build the other. Deletion avoidance and duplicate code are the same question asked in two directions:
- Does existing code already carry what this fix needs?
- Does this fix make existing code obsolete?
In my pipeline, answering either one takes the same inputs: the symbol registry, the call graph around the edit, and the peers declared in the same module.
The paper measures the deletion direction only. The mirror is my extension of their question, and the evidence for it is mine.
Why no test can tell reuse from reimplementation
A test only sees behavior, and a reuse behaves exactly like a faithful reimplementation, just as a deleted path behaves exactly like one still sitting behind a guard no test ever trips.
Checking the source is the only thing left. The paper does that after the fact, which measures the problem; a pipeline can do it at planning time, before any code is written.
The duplication direction already had a stage named for duplicate detection, and it ran on every ticket for 189 recorded invocations, flagged five duplicates, and never once changed an outcome.
Five unassisted agent runs, five duplicate implementations
The ticket is a per-source timeout fix in the ~200k line TypeScript monorepo the pipeline runs against, and its issue text points at a timeout helper that already exists elsewhere in the codebase. As a control for the pipeline’s code reuse enforcement, I ran the same raw issue five times against a bare CLI coding agent, Claude Sonnet 4.6, the same model the pipeline’s own builders run, with nothing but the issue text and the repo. All five came back the same way: each wrote its own inline Promise.race in the file it was editing, and none called, modified, or even named that helper. They were not weak patches either; every roll shipped a working fix that passed its own tests, and each one quietly sidestepped a flaw in the helper rather than fixing it.
The patches look right, nothing fails, and the codebase quietly gains another parallel implementation.

An LLM reviewer does not catch it either. On one of the rolls I ran an adversarial review pass; the reviewer spotted the sidestepped flaw and described it accurately, then rationalized it as intentional and approved the patch unchanged.
One ticket and one model tier, so this is a base rate for this ticket rather than a benchmark, and the paper finds this behavior swings hard from one model to the next.
What a code-reuse check has to assert
The first version of my reuse check asked whether the helper had changed. A run then did both things at once: it extended the helper exactly as instructed, and then wrote the same race inline at the call site anyway, never calling the helper it had just extended. The helper had changed, so the check read as honored. That helper was already one of several timeout wrappers in the codebase, and the run obliged to reuse it added one more.
The model obeyed the instruction and extended the helper, then wrote a duplicate of the same logic anyway. Proving the helper changed never proves the fix routes through it.
So the check stopped asking whether the helper was edited and started asking whether the committed code actually calls it. Editing a symbol says nothing about whether anything routes through it.
With the check asking about the call, the next run produced a change that routes through the extended helper, with no inline duplication. The difference from the five bare patches is blast radius, not code quality: they each fixed one call site and left the flawed helper for everyone else, while extending it fixes every caller at once.
Patch bloat and code duplication are the same disease
Retention makes the patch bigger; duplication makes the codebase bigger. One treatment covers both, and that claim is mine rather than the authors’: a machine-checkable obligation about what the change must and must not contain, set before the model writes a line.
The models may eventually learn this without help. A pipeline does not have to wait for that.
Paper: To Add Is Machine, To Delete Is Human: Measuring and Mitigating Deletion Avoidance in LLM Code Editing, Amir M. Ebrahimi, Mohammed Mehedi Hasan, Aaditya Bhatia, Gopi Krishnan Rajbahadur, and Ahmed E. Hassan, Queen’s University. Submitted 30 July 2026 (arXiv:2607.28887v1).
The pipeline runs inside Docker on real tickets against a ~200k line TypeScript monorepo. The reuse obligation described here has one compliant run and one recorded evasion, and it has not refused a duplicator live yet. Discovery does not surface every duplicate, so some never become a question at all. The deletion-direction detector has not fired on a live ticket. Still R&D.