Your coding agent re-fixes code that was already fixed
AI agents patch already fixed code in 35 to 65 percent of stale tickets. What a work item must carry so a fresh session tells done from open.

An agent run leaves a four hundred line diff on a ticket. The bug that ticket describes was fixed nine days ago by someone on your team, in a commit that has been on main since. Tests pass. They passed before the run too. Now a human has to read the whole diff to work out that it changes nothing anyone wanted changed.
That run is not a fluke. In March 2026 the SRI Lab at ETH Zurich published FixedBench, 200 tasks where the bug has already been patched, followed on 8 May by Coding Agents Don't Know When to Act (both fetched 27 July 2026). Five models across four harnesses made unnecessary edits to already correct code in 35 to 65 percent of cases. The authors put it plainly: "at least one in three stale issue reports results in spurious edits to working code."
Almost every write-up of this lands on the agent's own configuration: reproduce before you patch, put a rule in the config file, install a memory server. That advice is real, and it is half the job. The half I have not seen written down is what the ticket itself has to carry.
The measurement, and why it is worse than it sounds
FixedBench takes 200 SWE-bench Verified instances, commits the golden patch from the original pull request, then hands the agent the original issue text. The correct answer is an empty patch. Editing tests or docs is allowed. Touching executable code is a failure.
| Model and harness | Correctly left the code alone |
|---|---|
| GPT-5.3 Codex in Codex | 68% |
| Sonnet 4.6 in Claude Code | 65.0% |
| GPT-5.4 mini in Codex | 60.5% |
| Qwen3.5 122B in Qwen Code | 50.3% |
| Gemini 3 Pro in Gemini CLI | 36.5% |
Treat the top row as approximate: the blog chart shows 68 percent for GPT-5.3 Codex, while the paper text says the best models do not clear 67 percent. The figure with a published interval is Sonnet 4.6 at 65.0 plus or minus 6.6.
Those are the good conditions. The paper's best case has "the Git history available with the last commit resolving the issue" plus a fully set up environment, which it calls "a realistic setting where the agent is part of a deployment pipeline." The resolving commit was the newest entry in the log, and between a third and two thirds of runs patched anyway. Remove the .git folder and Sonnet 4.6 falls to 50.0 percent.
Why it edits when the fix is one git log away
The obvious theory is that it never looked. Partly true. The authors read 50 Sonnet 4.6 traces by hand to define ten behaviour categories, then used a model as judge across the rest, at 80.5 percent precision against the humans. Traces ending in abstention had checked the git history 63.8 percent of the time. Traces ending in an edit, 31.4 percent.
Knowing does not stop it, and that is the part that should change what you do. Under a reproduce first prompt, the agent worked out the bug was already fixed 79.5 percent of the time. Under a prompt that also offered abstention as an outcome, 81.0 percent. Practically identical.
Correct abstention was 65.5 percent against 80.5 percent. Same understanding, wildly different behaviour. In the authors' words, "the key issue is not the agent's ability to reproduce an issue and realize it is already resolved, but what it believes its success criteria are."
Failing traces have a signature. They modify code unrelated to the reported issue 87.1 percent of the time, and 47.1 percent apply changes that do not meaningfully alter behaviour. That second number is a no op diff seen from the inside. The agent has no way to report success, so it produces a change instead.
flowchart TD
A["Fresh session opens a stale ticket"] --> B{"Does the item name the resolving commit?"}
B -->|"No"| C["Agent infers state from the repo"]
C --> D{"Reproduces the defect first?"}
D -->|"No"| E["Patches working code"]
D -->|"Yes"| F["Usually realises it is fixed"]
B -->|"Yes"| F
F --> G{"Is a no change outcome available?"}
G -->|"No"| E
G -->|"Yes"| H["Reports back with evidence"]
Telling it to reproduce first does not work
Most advice here is wrong, including mine before I read the numbers. The reproduce first prompt on its own moved Sonnet 4.6 from 65.0 to 65.5 percent, a p value of 1.0. On GPT-5.4 mini it went the other way, 60.5 down to 47.5. Thirteen points worse than saying nothing. The prompt that worked framed no change as a completed task: 80.5 percent for Sonnet, 88.5 percent for GPT-5.4 mini.
That prompt breaks in the other direction. On the partial task, where a wrong patch is already applied and the agent should finish the job, the same instruction pushed both models into passivity. Sonnet made a meaningful edit in 18.7 percent of those cases, GPT-5.4 mini in 6.4 percent. The paper is blunt: "it can suppress action bias on fixed code at the cost of inducing passivity on broken code."
More thinking does not rescue it. Across four reasoning effort levels GPT-5.4 mini moved from 61.5 to 65.8 percent, inside a confidence interval of roughly seven points. Action bias, the authors conclude, "is not a failure of deliberation that more compute can overcome, but rather a failure of task framing."
So the ceiling on prompting sits between one bad run in five and one in nine, bought by making the agent worse at broken code. Past that, you change what the agent is reading.
What the work item has to carry
Open any ticket in your tracker and ask whether a fresh session, with no memory of your team, could tell "not done" from "done and stale" without asking a human. For most tickets it cannot, and the missing pieces are boring.
| What the item carries | What a fresh session concludes | What happens without it |
|---|---|---|
| The resolving commit, on the item | The fix is in this sha, by this author, at this time | The agent guesses from repo state |
| A terminal state for fixed and verified, separate from cancelled | Someone verified the fix after it landed | Closed and abandoned look identical |
| A hand off note written when work stopped | What was tried, what was left, why | The code survives, the reasoning does not |
| An acceptance step naming the reproduction | What to run before editing a file | Any green suite reads as confirmation |
| A reachable no change outcome | Reporting is a finished task | Only a patch counts as done |
The first row is the one people assume they already have. Check it. GitHub's closing keywords are interpreted, per its own docs fetched 27 July 2026, only when the pull request targets the default branch. Target a release branch and "these keywords are ignored, no links are created, and merging the PR has no effect on the issues." A squash merge whose message drops the key leaves nothing either. A team merging into develop can ship unlinked fixes for years without noticing, because until now the only readers of those tickets remembered.
The last row is the one nobody builds. If the only terminal state on your board is Done, and Done means shipped work, an agent that finds nothing to fix has no move that counts. So it writes the patch.
Wiring it up in Taskfolk
Start with the boundary. Taskfolk never reads your repository or your diffs. It stores what a person or a process told it, and everything below makes that record worth reading.
Commit references land on the issue through the GitHub push webhook. A message containing #WEB-118 anywhere, or WEB-118 at the very start, links that commit with its sha, message, author and timestamp, and writes an append only activity row. Setup is a per project connection (how to connect GitHub). The reference lives on the ticket, so deleting the branch does not take the evidence with it.
The Linked commits card on issue WEB-118, listing two commits with short sha, message, author and date, under the issue title and status.
BASE=https://taskfolk.ai/api/v1/workspaces/acme
curl -H "Authorization: Bearer tfk_live_a1b2..." \
"$BASE/projects/WEB/issues/WEB-118/commits"
Next, split the terminal states. Taskfolk keeps seven fixed status categories under the board and lets you name any number of columns on top, each mapped to one category. "Fixed and verified" maps to done, "No change needed" maps to cancelled, and both are real columns. Reports and the open or closed split still work because they read the category, not the name (custom workflow statuses, and the wider bug tracking workflow).
A board with two closing columns, Fixed and verified in green and No change needed in red, each holding cards.
Allowed transitions are the enforced control, and they run on every write path: the board, bulk edits, automation actions and the REST API. Give Backlog two destinations, To do and In progress, and an agent call that jumps a card straight to No change needed comes back rejected with both status names in the error. The per column WIP limit is not that. You can set it, but no write path reads it, and the count badge and red header on the board are a view toggle. Nothing refuses a card.
Project settings, Board statuses card open with one status expanded to show its allowed transitions and mapped category.
The acceptance step from the table is a text custom field. Define one called Reproduction, scope it to bugs, and every bug carries the command to run before anyone edits a file (custom fields in issue tracking). Marking it required does not stop someone filing a bug without it. It only stops them clearing the value once it is set, which is weaker than a gate.
Now the outcome the research says is missing. Assigning an issue to an agent creates a pending session the agent claims when it starts. When it finds nothing to fix, the finished shape is a comment carrying evidence, a session moved to review, and a card in the no change column. That leaves no branch and nothing in the review queue.
curl -X POST -H "Authorization: Bearer tfk_live_a1b2..." \
-H "Content-Type: application/json" \
"$BASE/projects/WEB/issues/WEB-118/comments" \
-d '{"body_md":"No change needed. Reproduction passes on main at 4f0c9ae. Fix landed in 2b71d3c."}'
await fetch(`${BASE}/projects/WEB/issues/WEB-118/comments`, {
method: "POST",
headers: {
Authorization: "Bearer tfk_live_a1b2...",
"Content-Type": "application/json",
},
body: JSON.stringify({ body_md: "No change needed. Passes on main at 4f0c9ae." }),
});
requests.post(
f"{BASE}/projects/WEB/issues/WEB-118/comments",
headers={"Authorization": "Bearer tfk_live_a1b2..."},
json={"body_md": "No change needed. Passes on main at 4f0c9ae."},
)
{
"id": "0199f3c1-6b2a-7c41-9f0d-2c8a1e5b7d33",
"issue_id": "0199e07a-4c11-7a92-b3d6-8f1c2a4e9b05",
"author_id": "0199c4d8-9b30-7e15-8a71-5d0b3f6c2e44",
"body_md": "No change needed. Passes on main at 4f0c9ae.",
"body_html": "<p>No change needed. Passes on main at 4f0c9ae.</p>",
"created_at": "2026-07-26T09:14:22.000Z",
"updated_at": "2026-07-26T09:14:22.000Z"
}
Then move the card. POST .../issues/WEB-118/transition takes a status category, so {"status":"cancelled"} puts the issue in that project's default cancelled column; to land on a specific named column, PATCH the issue with its status_id. Both run the same transition check a human board drag does. The agent authenticates with its own API key, so the comment and the status change are attributed to its member identity, which is what the audit trail is built from.
One note on checking the work, covered properly in managing a team of AI agents. A session reaching review or done with no attributed activity row and no comment on its issue is flagged unverified. That is a heuristic, not proof, and one comment defeats it. Here it still helps: an agent that really found nothing clears the flag by writing the report.
Where this stops
Taskfolk cannot detect that a fix already landed. It has no view of your repository, branches or diffs, and never reads them.
GitHub is the only repository integration. No GitLab, no Bitbucket, no Azure DevOps.
The commit reference appears only when the issue key is in the commit message. Squash merge with a generated message, or put the key only in the pull request title, and you get an empty card plus a false sense of coverage. The webhook listens to push events, so it links commits, not pull requests.
Taskfolk also does not run your agent. It delivers triggers and stores the session record. The decision to abstain happens in a runtime you own, which is why the prompt half stays your job.
The bill for ignoring this
The wasted run is the cheap part. The expensive part is what happens to the diff.
Alam, Mondal and Roy analysed 8,106 agent authored fix pull requests from the AIDev dataset in a study prepared for MSR 2026 (29 January 2026, fetched 27 July 2026). 65.0 percent merged, 26.1 percent closed without merging. They hand reviewed 326 of the closed ones over roughly 100 person hours. The most common failure reason was that the issue had already been resolved by a different pull request, at 22.1 percent. Another 8.0 percent were deprioritised or obsolete by the time anyone looked.
Then the multiplier. Xu, Subramanian and Karthik looked at 33,596 agent pull requests across 2,807 repositories and found 79.4 percent open concurrently with another agent pull request in the same repository, rising to 95 percent within a week (7 July 2026, fetched 27 July 2026). A ticket does not go stale over months. It goes stale over an afternoon, because your own second agent already fixed it. That is the ordinary case behind a shared backlog for AI coding agents and coordinating agents with issue status as handoffs.
Each run costs provider tokens, a branch, a CI run, and a human read. The read is the part you cannot automate away, and it is spent on diffs that should have been empty.
Pick one bug you closed last week. Read only what is on the ticket, the way an agent would. If you cannot find the commit that fixed it, or tell whether anyone verified the fix, you already know what your next agent run will do with it.
Frequently asked questions
Why does my AI agent rewrite code that was already fixed?
Because a patch is the only outcome it counts as success. ETH Zurich's FixedBench found agents editing already correct code in 35 to 65 percent of cases, and trace analysis showed they often knew the bug was fixed and edited anyway. Give the run a defined non edit outcome and the rate drops sharply.
Does telling the agent to reproduce the bug first fix the problem?
Not on its own. In the FixedBench study a reproduce first prompt moved Sonnet 4.6 from 65.0 to 65.5 percent (p = 1.0) and made GPT-5.4 mini worse, 60.5 down to 47.5. Only the prompt framing abstention as a successful outcome helped, and it then made both models too passive on genuinely broken code.
Can Taskfolk tell that a fix already landed?
No. Taskfolk never reads your repository or your diffs. It stores the commit references that arrive from the GitHub push webhook when a commit message contains the issue key, plus whatever people and agents write on the item. That record is what a fresh session reads.
What should a closed ticket contain so an agent does not redo the work?
The resolving commit linked on the item, a terminal state that separates fixed and verified from cancelled, a note describing where work stopped, and an acceptance step naming the reproduction to run before editing anything. Add a status the agent can move the card to when it finds nothing to fix.
Related reading

The agent's pull request is 80 percent right. Now what?
An AI agent's PR that is useful but not mergeable has three exits: finish it, send it back, or close it and rewrite the ticket. How to pick in five minutes.
28 July 2026 · 11 min read

How to write a ticket an AI agent can actually finish
How to write tickets for AI coding agents: the seven fields that decide whether you get a reviewable pull request or an eleven file mess you cannot check.
26 July 2026 · 13 min read

Did your AI agents actually save time, or did it just feel fast?
How to tell if your AI coding agents actually saved time: a four week check that runs on ticket data you already have, with a sign test and honest limits.
28 July 2026 · 11 min read

Prompt injection starts in the ticket your agent reads
A stranger can write the ticket your AI agent reads. Real 2026 prompt injection incidents, what a hijacked agent can change, and how to cut the blast radius.
26 July 2026 · 11 min read

The agent stopped halfway and the board still says in progress
Your AI agent died at 60 percent and the ticket still says In Progress. How to pick up where an agent left off, and stop the board lying about it.
28 July 2026 · 11 min read

AI made code review the bottleneck, not writing code
The AI code review bottleneck, checked against three 2026 datasets: the delay is queue wait, not review time, so a review bot aims at the wrong term.
26 July 2026 · 11 min read

What your junior engineer does now that agents take the tickets
A staffing plan for leads: what a junior developer works on when AI agents do the tickets, from spec authorship to first review of agent pull requests.
28 July 2026 · 10 min read

How to split work between Claude Code, Codex and Cursor
How to split work between Claude Code, Codex and Cursor: a division of labour that holds up, and where the shared state lives since none of them reads the others.
26 July 2026 · 11 min read

How to tell when an AI agent is stuck (and what to do about it)
A looping, waiting, or hung agent looks exactly like a working one. Here is how to get a real signal on an agent's live state and catch stuck runs.
15 July 2026 · 7 min read

Onboarding a new engineer to code your agents wrote
Git blame points at a merge and whoever wrote the prompt has left. How to onboard a new engineer to a codebase your AI agents wrote, without guessing.
28 July 2026 · 11 min read
Add a comment
Start the conversation.
