The quick checklist
When a rule seems to be ignored, go down this list before you rewrite anything:
- Is the file name exactly right, including case?
- Is it at the project root?
- Is the tool you are using reading that name at all, or a different one?
- Is the file long enough that the rule is buried in the middle?
- Has the session been compacted since the file was read? Long sessions lose fidelity to instructions read early, and a fresh session is a faster diagnosis than another rewrite of the file.
- Is this a rule that should not be prose at all? If it must hold every time, a hook enforces it and a paragraph only asks.
Check the boring causes first
- The file name is case-sensitive on Linux and on case-sensitive macOS volumes.
Claude.mdis notCLAUDE.md, and it is silently ignored. If your rules seem to do nothing, check this first. - Location matters. The project root is the reliable place. Nested files and home-directory files behave differently per tool, so do not assume a deep file is being read.
- Other tools read other files. Claude Code reads
CLAUDE.md. Codex and a growing set of other tools readAGENTS.md. When an agent "forgot the rules after I switched tools", it is usually a harness difference, not a model failure. The fix is one real file with the other name pointing at it.
When it stops following the rules halfway through a long session
Early in a session it followed my handbook. Several hours in it started drifting: skipping the verification step, committing in a way I had explicitly ruled out. Nothing dramatic, just a slow return to defaults. I spent longer than I want to admit rewriting the handbook, adding emphasis, making rules louder. That is the wrong fix.
Long sessions get compacted. Compaction summarizes earlier context to free space, and instructions read at the very beginning are exactly what gets thinned. The rule is not being defied, it has faded out of view. Making the file longer makes this worse, because a longer handbook is more to thin and more to skim. I caught it when I noticed that the drift tracked session length, not task difficulty.
What helps, in order:
/clearbetween unrelated tasks. Most sessions should be shorter than they are.- Move must-hold rules out of prose and into hooks. A documented rule is followed most of the time. A hook runs every time, because the harness executes it and the model does not get a vote.
- Keep the handbook short and the non-negotiables near the top. The middle of a long file is where rules go to be ignored.
When it says it is done, and it is not
The last cause is not an ignored rule but a false report. The agent is not lying. It is reporting on a world it believes in. My job stopped being "write the code" and became "verify the report". Two failures taught me this.
It said the tests passed. It had changed the tests.
I asked it to fix a failing test in a module I had not touched in a while. It told me: fixed, suite green, all passing. The test had been asserting real behavior. Rather than fix the code, it relaxed the assertion until the existing behavior satisfied it. The suite was green because the test no longer tested anything.
Every signal you normally trust says things are fine: a green suite, a confident summary, clean output. I caught it because I read the diff, not the summary. A test file in a diff you did not expect to see one in is a stop sign.
The rule that goes in the handbook: if the task was to fix the code, the tests should not change. If they must, that is a separate, deliberate, explained change. I also review test-file changes separately from source changes, and I use a reviewer subagent with no write access, so it cannot fix anything into looking correct.
"Done" that never ran
I asked it to run the suite and confirm before finishing. It said everything passes. The command had failed early on an unrelated setup error. The failure scrolled past, the model summarized optimistically, and I got a confident completion message for work that had not executed.
A model summarizing a long, noisy transcript tends to report the intent of the last few steps rather than their result. It is lossy compression pointed at your build output. The fix is to ask for evidence that cannot be paraphrased: exit codes, line counts, the actual last lines. "Paste the last 20 lines of the test run" cannot be answered agreeably. "Did it work?" can.
The guardrail is to make completion something the harness proves, not something the model claims. That is what hooks are for.
Where hooks beat instructions
Hooks are shell commands the harness runs at defined moments. They turn "I keep reminding it to do X" into "X happens automatically". Anything you have written into CLAUDE.md as "always do X" and still find yourself repeating is a candidate. A documented rule gets followed most of the time. A hook runs every time, because the harness executes it rather than the model choosing to.
They live in .claude/settings.json (committed and shared) or .claude/settings.local.json (just you). This is the shape:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{ "type": "command", "command": "your-command-here", "timeout": 10 }
]
}
]
}
}
The events worth knowing:
- SessionStart sets up context at the top of every session: the current branch and open work, a warm cache, a status check.
- PreToolUse runs before a tool call and can block it. This is how you enforce a rule instead of documenting it and hoping. The strongest use is blocking a command that must never run in this repo.
- PostToolUse runs after a tool call: auto-format after every edit, or run the affected tests without being asked.
- Stop fires when the turn ends. It is useful for a notification when a long task finishes, and it is where I put a check that blocks the turn from ending if it fails, so "done" means the command exited zero. Read the hooks documentation for your Claude Code version for the exact fields.
Cautions worth taking seriously
- Hooks run with your permissions. Read any hook you copy from the internet before you enable it, including mine.
- Set a
timeout. A hanging hook hangs your session. - Keep them fast and quiet. A hook that prints 200 lines at every session start is noise you will learn to ignore.
- Block the genuinely dangerous, not the merely untidy. A
PreToolUsehook that blocks too aggressively makes the agent fight the hook instead of doing the work.
Get the files
The war stories, the reviewer subagent, the settings and hooks notes and the handbook template are all in the free Claude Workflow Pack, and the repo is open on GitHub. If you would like this set up for your team, see work with me.