Let's be precise about the problem, because "agent code is bad" isn't it. Agent code is mostly fine, which is why the slop gets through.
Slop, as I'm defining it: code that is correct, passes review individually, and makes the codebase worse collectively. The specific strains I keep merging and regretting:
- Abstraction on arrival. A helper used once, an interface with one implementation, a config option nobody asked for. Agents pattern-match "good code has abstractions" without the taste to know when.
- Defensive ceremony. try/catch around code that can't throw, null checks on values the type system already guarantees, fallbacks that mask real failures instead of surfacing them.
- Narration comments.
// Iterate over the users above a for loop over users. Individually harmless. Two hundred of them and the codebase reads like it's explaining itself to a jury. - Style archaeology. The agent matches the style of whatever file it read last, so old-style patterns metastasize into new code. Our deprecated error-handling pattern has spread since agents arrived because it's well-represented in the corpus they read.
The insidious part: none of these fail review. Each one survives "is this line wrong?" It's the aggregate that's wrong, and aggregate review isn't a thing.
What has actually moved the needle for people — instruction rules, lint enforcement, review checklists, something else? Bonus points for the style-archaeology problem specifically, because I think it's the one that compounds.
The only thing that durably worked for me: convert taste into lint rules, ruthlessly, even when the rule feels petty.
eslintno-single-use-abstraction isn't a real rule, but max-lines-per-function plus a custom rule flagging exported symbols with one call site gets 80% of it.The general principle: agents respect machine feedback and negotiate with prose feedback. Anything in AGENTS.md is a suggestion; anything in CI is physics.
'Anything in CI is physics' is the thesis of this whole site, honestly. Do you publish the comment-overlap hook anywhere? That's the strain I have no answer for.
Gist linked from my profile. Fair warning: threshold tuning took longer than writing it — 0.6 overlap flagged docstrings, 0.8 missed half the narration. We run 0.72 and review its rejections weekly like the tiny bureaucracy it is.