Search “claude code best practices” and you get the vendor docs reprinted as a listicle. Nothing on that first page tells you what broke before the rule existed, so you can’t tell which line is load-bearing and which one is filler that sounded right in a README.
Here is a different filter. Every rule below has a date, a number, or a repo attached to it, because I only kept the ones I can point at a specific failure for. If I can’t source the incident, it isn’t on this list.
Verify the marker before you trust it
An agent that reports “done” is usually handing you a marker: a status line, an issue label, a closing comment. The marker is not the work. It’s a claim about the work, written by whoever closed it out, and it is exactly as reliable as they were that day.
I measured how often that claim was checked across one day of my own transcripts: 2292 marker-shaped assertions. Opening the artifact behind every single one would cost roughly 2292 reads to catch three real errors, and a rule that expensive gets ignored within a day. So I narrowed it to one moment: a foreign marker, at the point you’re about to act on it because of what it says.
Check every marker, always
Costs ~2292 reads for the same 3 errors caught below. Rejected on cost.
Check a foreign marker at a decision point
16 firings, 3 real errors prevented, one artifact read each.
Both versions catch the same three errors. The difference is 2292 reads versus 16.
The three: an agentic label that put three unfinishable tasks into an unattended queue, a commit whose subject claimed it closed an issue while all 22 rows in the table it touched still read 0, and a closing comment that cited a SHA which turned out to be an unrelated PDF fix. The counterexample matters as much as the catches: a fourth issue carried the identical agentic label and was genuinely complete. Markers aren’t usually wrong. They’re unvalidated, which is a different problem with a cheaper fix: open the thing before you act on what it says, and if it names nothing, say so instead of proceeding.
A green status is not proof
Related trap, one layer up: a status code that comes back clean tells you a server answered, not that the page exists or renders what you think. I’m still measuring this one properly (five triggering sessions since I started tracking it, want eight before I call it confirmed), so take it as a live hypothesis with early support, not a settled rule.
What’s already settled is the sibling problem: presence is not health. I hit seven instances of this in one day across four different layers of the same project, on 2026-09-04. A directory check ([ -d node_modules ]) passed while the actual dependencies lived one folder over and the checked root had zero packages. A verdict file existed and said nothing about what had actually run, or against which commit. A local gate was green while the project’s real CI had been red on three consecutive pushes. None of those checks were lying. They were answering “does something exist here,” which is a different question from “does this work,” and the two came apart in four separate places on the same day.
{{learn:builders-notes/dont-assume-ai-fixes-things-properly}} covers the sibling failure: an agent that can’t tell when its own fix is wrong. This is the same blind spot from the checker’s side instead of the fixer’s.
Before you trust a gate, make it fail
A test suite, a linter, a pre-commit hook: none of it is proven by passing. It’s proven by failing on the thing it’s supposed to catch, then passing again once you undo the damage. Name the edit that should break the rule, make it, confirm the gate goes red, restore the file, confirm the tree is byte-identical to before. A gate that has only ever been seen green is a light with no bulb behind it, and the restore step isn’t bookkeeping: a gate you proved by mutating a file and never reverting just shipped the mutation.
This one bites hardest on regression gates written after an incident, because there’s no naturally-occurring broken tree lying around to test against. You have to reintroduce the exact defect on purpose, watch the gate catch it, then take it back out.
Size the landmine, then route it, never just mention it
The failure this rule is named for happened on 2026-08-24, in a different project of mine. A session shipped its actual fix, then signed off with a line like “one thing I found and deliberately left alone” about seven URLs returning 404. That sentence is the tell. Four minutes of sizing turned those seven URLs into a real crawl seed-guard bug with 14 live page_loads against it, plus the same markdown-tail shape showing up on a second site.
“Worth a look separately” and “deliberately left alone” are the two phrases that mean a session is handing back work it should have finished. The fix is two steps, and the first one is never optional:
- Size it. One grep, one curl, one query. What does it actually touch, measured against real cases, not the one convenient one.
- Route it, exactly one of: fix it now if the fix costs less than describing it, get a second opinion if it’s a judgment call, or file it with the measurement already in the ticket if it’s real but bigger than the current session.
What never counts as a route: naming the problem in a summary and moving on. A finding that exists only in a chat transcript is gone the moment the session ends.
A permission denial is about the string, not your capability
I once watched a session get denied git merge four times, conclude from that that background agents can’t integrate work at all, and come within one message of proposing a permanent permission widening to work around it. Measured 2026-09-05. The bare form, git merge with nothing else attached, worked on the first try.
Permission rules are prefix matches on the literal string you send, so small, reasonable-looking additions break a match that would otherwise pass clean:
| What got typed | Why it misses an allow-listed git merge --no-ff:* |
|---|---|
git -C /path merge --no-ff … | Starts with git -C, not git merge |
git merge … | tail -6 | The pipe breaks the match |
gh run list …; other.sh … | The ; join breaks it |
A denial is telling you about the exact command you sent, not about what you’re allowed to do. Before concluding a capability is missing, strip the command back to its plainest form and try that first.
A dev server you start, you own
Measured 2026-09-21: one wedged astro dev process burned 120 CPU-hours over four days at roughly 480% CPU and 7.7GB of memory, with two more copies of the same project’s dev server running alongside it, none of them announced. A session that exits leaves whatever it started orphaned to launchd, and nothing on a normal machine reaps it. The port auto-increments silently when one is already bound, so a duplicate server never even throws an error to warn you it’s the second one.
The check costs one command before you start a new server for a project: ps -Ao pid,ppid,args | grep -E 'astro|vite|wrangler' | awk '$2==1'. If your session started a server, kill it before you finish, or say plainly that you left it running and why.
Practices that already have their own post
A few load-bearing habits are big enough that I’ve written the full case for them elsewhere. Linking instead of re-arguing them here is the point of a hub post:
- Memory files don’t stop a repeating agent. A keyword-triggered hook does, and I measured why: {{learn:claude-code/claude-code-repeats-same-mistakes}}
- CLAUDE.md is a suggestion the agent can and does ignore under specific conditions: {{learn:claude-code/why-claude-code-ignores-claude-md}}
- Deterministic enforcement through the four hook events, not written instructions: {{learn:claude-code/claude-code-hooks}}
- Running several agents against one repo without them colliding: {{learn:claude-code/claude-code-worktrees}}
- Why a second opinion has to come from a model that isn’t you, with the numbers on how often it actually disagrees: {{learn:claude-code/ai-second-opinion}}
And the discipline underneath all of it, checking the claim against the actual system instead of the doc that describes it, is its own habit worth building deliberately: {{learn:research/agent-memory-delivery-rate}} is where I instrumented exactly how often a written lesson reaches an agent mid-edit. The number was low enough to change how I use memory files at all.
Six lines for your own CLAUDE.md
Do
- Open the artifact a marker points to before acting on what it claims, never the marker alone
- Ask whether a check proves the thing works, not merely that it exists
- Before trusting a gate, break the rule on purpose, confirm it goes red, then restore and confirm byte-identical
- Size a landmine in one grep or curl before you route it: fix it, get a second opinion, or file it with the measurement attached
- Read a permission denial as feedback on the exact string you sent, not on what you’re allowed to do
- Kill or account for every dev server you start before the session ends
Related:
- Claude Code Hooks: Make the Agent’s Behavior Deterministic
- Claude Code Repeats the Same Mistakes: Memory Won’t Fix It
- Don’t Assume AI Fixes Things Properly
Sources
~/.claude/CLAUDE.md: my own global Claude Code instruction file, private. The marker-verification rule (2292 firings narrowed to 16, theagentic-label and commit-subject incidents), the landmine-sizing rule (the 2026-08-24 itbroke-v4 incident), and the permission-denial rule (the 2026-09-05git mergeincident) are documented there under “Golden Rules” and “Operating Rules,” each dated to the session that produced it./Applications/MAMP/htdocs/evolving-agent/seeds/superagent/ACTIVE-RULES.md, private, generated byscripts/field_trial.py: the status-code rule is listed there asTESTING, not yetCONFIRMED, with its current sample size stated plainly. I’m reporting it at the evidence grade it actually has.FINDINGS.md, my cross-session findings ledger, private:R-01(presence is not health, seven instances measured 2026-09-04 across four layers) andR-07(before claiming a gate works, make it fail).