Google's developer blog published a study called Agent Distraction last week, and it puts hard numbers on something most of us have watched happen: an agent fixes your bug, then wanders off to investigate somebody else's. The team cross-referenced inline TODO(bug/...) comments against tracker status across hundreds of millions of internal agent steps, and the price of those wanderings is higher than I expected.
I have written before about config drift and about where token bills actually come from. This one is different, because the distracting instruction is not in any config file. It sits in your code, in a comment nobody has read in three years.
What Google actually measured
The unit of analysis is the file-reading session. Google used distributed traces of agent interactions from thousands of engineers, covering hundreds of millions of individual agent steps. Their proxy for distraction is precise: inline TODO(bug/...) or FIXME(bug/...) comments, where the linked ticket is the canonical record of whether the work still exists. Cross-referencing every TODO an agent encounters against ticket status and the user's prompt separates valid work from wasted detours, automatically, at a scale no manual review could cover.
The numbers
Of the millions of file-reading sessions, 13% exposed the agent to a TODO(bug/...) or FIXME(bug/...) reference. Agents ignored the comment in 94% of encounters. In 6% the agent took secondary action targeting that bug ID, and 83% of those actions were completely unprompted: the developer never asked for the bug.
To price the detours, the team ran a multi-way fixed-effects regression with developer-clustered standard errors, controlling for developer, codebase complexity, task type, model tier, number of files read, initial prompt length, and the step where the bug reference appeared. Sessions with unprompted detours took 70% more steps, used 77% more tokens, and spent 105% more time waiting, all significant at p<0.0001.
The two failure shapes
Cross-referencing the unprompted detours against live ticket status splits them into two clean groups.
Dead ends. 41% chased tickets that were already closed: 32% were fixed and verified bugs, 9% were obsolete or won't-fix decisions, and 2% hit duplicate or restricted tickets. The agent investigated work that no longer exists.
Scope creep. 57% landed on tickets that were still open, and the agent widened the task to investigate or modify an issue the developer never asked it to touch. Of those, 61% were lookups and 39% actually edited code.
The blog walks through one example where an agent hit a TODO in a model downloader, ran code search and internal knowledge search against the bug, and ended up changing the ModelDownloaderV1 code. Later in the trajectory the human challenged the scope increase, and the model admitted it was an incorrect increase in scope. Google notes both documented cases were trivial to minor detours, and they still cost time, tokens, and human intervention.
Why a comment reads like an assignment
A human skimming a file treats a stale TODO as archaeology. An agent treats it as a call to action. The blog says it directly: what is easy for humans to skim past in code, agents see as a call to action.
The mechanism is mundane. The comment is in context. It carries an identifier. The identifier resolves through a tool call, and the tool call is cheap. Nothing in the agent's instructions says the comment is four years old, so nothing stops the detour. Recency is implicit for humans and absent for machines.
What Google did about it
Google built cleaning agents that scrubbed their codebases for bug references already marked fixed or obsolete, then dug into the history of the bug, the file, and the surrounding changes, sometimes spanning years, to confirm the TODO's intent was truly gone. Only confirmed-obsolete references went to review as deletions. Over the last six months the cleaner removed thousands of obsolete references, and human reviewers accepted more than 95% of the changes.
That acceptance rate is the quiet headline. Reviewers agreed with the machine 95 times out of 100 that a TODO was dead weight. The signal was real, and everyone could see it once it was surfaced.
Audit your own repo first
You do not need Google's infrastructure to find your own exposure. Inventory the references, then check them against the tracker:
# how many ticket-referencing TODOs are in the tree?
git grep -nE '(TODO|FIXME)\((bug:)?[A-Za-z]+-?[0-9]+\)' | wc -l
# list them with file and line
git grep -nE '(TODO|FIXME)\((bug:)?[A-Za-z]+-?[0-9]+\)'
For every hit, one of three things is true: the ticket is open and the TODO is honest, the ticket is closed and the TODO is lying, or the ticket is gone and the reference is noise your agent will chew on. Fix the second and third cases on sight. Deleting a dead TODO is the cheapest context optimization available, and unlike most context advice, the win here is measurable.
Add a scope rule to your agent config
Cleaning the repo removes the trigger. A rule in your agent config can catch the behavior class, including stale references you have not found yet:
## Scope discipline
- Comments that reference bug tickets are context, not tasks.
- If work on a referenced ticket seems necessary, ask before starting it.
- Never edit files outside the current task without confirming first.
Be honest about what this buys you. Google's agents ignored 94% of references with no rule at all, so the baseline is already decent. A rule helps at the margin, on the 6% that trigger action, and it will not fire every time. I wrote about why agents ignore rules in detail, and the same limits apply here. Think of the rule as a seatbelt and the cleanup as the brakes.
The limits of the study
Three caveats before you rebuild your sprint around this. The data is Google's internal codebase, internal agents, and internal tracker, so it extrapolates only so far. The regression controls for confounders but is still correlation, not causation. And the documented detours were trivial to minor; nobody shipped a disaster in the examples. The cheap response is still worth it: grep the repo, delete the dead references, keep the live ones honest.
The framing I keep returning to is the study's own: obsolete specs and resolved bugs no longer rest in forgotten corners. They get ingested as context. Your repository was always documentation for humans first. For an agent, it is the prompt.
Top comments (3)
The 77% token figure is what makes this land for me — a stale TODO isn't just noise, it's an invitation for a run to keep going. Every run of mine has a stop condition written before it starts: a number of attempts, or a wall-clock limit, after which it stops and reports instead of improvising. Most runaway costs I see in other people's setups trace back to a missing stop line, not a bad model.
Agreed, and I like the phrasing that a stale TODO is an invitation. Your stop-line habit matches what the study's numbers imply: the detours themselves are cheap individually, it's the run continuing on its own that compounds the token bill. Two small things I'd add from the paper and from my own runs. The regression controlled for task type and model tier, so this isn't just hard tasks or weak models wandering, which makes prevention cheaper than better prompting. And for stop conditions, a wall-clock limit plus a step cap has caught more runaway loops for me than any instruction in the prompt, because the agent can talk itself past instructions but not past a timer. The report-then-stop shape you describe also leaves you a clean trail of where it went off, which is exactly the signal you need to fix the underlying TODO.
The control for task type and model tier is what makes the finding hard to wave away — it strips out the two excuses everyone reaches for first ('my tasks are just harder', 'the model was the problem'). What's left is the uncomfortable variable: the run's own momentum. That matches the stop-line logic from the other side. A detour is cheap; a detour that teaches the run to keep detouring is the compounding part. The stop-line isn't really about saving the current run's tokens. It's about not letting one run's habits become the template the next run inherits.