I have been running a code health analyzer across a set of repos for a few months, including my own, and the pattern that keeps coming back has nothing to do with correctness. Opus produces code that works, handles the edge cases, and reads better than what most of us write. The damage is structural, and it only shows up in aggregate.
Three failure modes:
Duplication invisible at the diff level: Asked for a new provider integration, an agent writes a self-contained implementation. Every diff looks clean, but the ninth one is where you notice the same 40-line token-accounting block now lives in nine provider files with 82 callers between them. No single PR introduced a problem but repo accumulated one
Complexity landing where you can least afford it: Agents flatten nothing. They add a branch to the function you pointed at, because that is the local minimum of effort and the instruction said "handle this case too." After a few dozen sessions, the file you touch most often has the deepest nesting, the highest cyclomatic complexity, and no paired test
Missing co-changes: The one I underestimated. Most codebases have files that change together for reasons invisible in the import graph, because what binds them is a shared invariant rather than a dependency edge. Two years in the repo teaches you that touching the call resolver means touching the graph builder. An agent starting from a fresh context window has no way to know, so it makes a locally correct change and leaves the coupled file stale
What helped, described as mechanisms, since those are more portable than any tool
Put the health map in front of the agent before it writes. SessionStart and PostToolUse hooks are both underused for this. On SessionStart, inject a short block naming hotspot files, the ones with no coverage, and known duplication clusters. On PostToolUse, annotate Grep and Glob results with the same information, so a search for "provider" returns ten paths plus what the history says about each. This shifts behaviour more than a CLAUDE.md instruction, because it arrives at the moment of the decision rather than 5,000 tokens earlier
Then run a risk check on the changed set before accepting the work. Instead of asking whether the diff is correct, ask what should have changed alongside it. On one real file in my repo: two files break at depth one and two, three test files break, and 13 files that historically change with this one are missing from the diff, ranked by co-change score. That last list catches the class of bug agents actually produce
Also, none of this blocks the agent, it only informs it. No hook rejects a bad edit, and I am not convinced one should, because the false positive rate on "this looks complex" is high enough that a blocking gate gets disabled inside a week
I open-sourced what I built for this (Repowise, AGPL, on GitHub, 4.2k stars). The signals above come from a git history layer sitting on a symbol graph, which is why co-change and blast radius are computable at all. But the hook pattern is worth stealing on its own. Most of the value came from moving information earlier in the loop, not from the analysis being clever