At some point in the last two modules, you told Claude Code how you wanted it to work. Perhaps you said “keep your answers to three lines”, “show me the change before you write it”, or “leave that file alone”. It listened at first. Then, twenty minutes into the same conversation, the instruction seemed to disappear.
The tool is not broken, and your wording may be fine. The real cause sits one layer below the conversation, which is why the fix most people try first does not help.
Everything in a session joins one growing pile of text: your questions, Claude’s answers, every file it opened, every command’s output, and the instruction you gave earlier. Before each reply, the model reads that entire pile and weighs the pieces together. Your sentence is one voice among many. It competes with newer material about the work directly in front of the model.
That pile is the context window, the fixed amount of text the model can hold at once. Your instruction does not stand outside the window and issue orders. It lives inside the window with everything else the model reads.
As the session grows, so does the competition around your sentence. This is the hidden part most people miss, and somebody measured it.
Someone measured which part matters
A controlled study tested the advice people tend to repeat. Damon McMillan’s “Instruction Adherence in Coding Agent Configuration Files: A Factorial Study of Four File-Structure Variables” (submitted 2026-05-11, arxiv.org/abs/2605.10039) covered 1,650 Claude Code sessions and 16,050 function-level observations. The study used two TypeScript codebases, three current top-end models and five tasks. It changed four features of the instruction file: its size, the instruction’s position inside it, the way files were split, and whether a neighbouring file contradicted the instruction.
The abstract reports: “None of the four structural variables or three two-way interactions produces a detectable contrast after multiple-testing correction.” For size and contradiction, the result was evidence of no effect rather than a failure to find one.
One factor did move the number: position within the session. Each additional function the agent generated was associated with roughly 5.6% lower odds of compliance per step.
That finding clashes with common advice, including Anthropic’s own best-practices page, which says “Bloated CLAUDE.md files cause Claude to ignore your actual instructions!” and recommends pruning. You may still want to prune. Shorter files save tokens, which cost real money and take real room in the window. The study says something narrower: pruning is unlikely to be the change that buys you obedience. Keep two limits in view. It measured compliance with one small target annotation, so you cannot conclude that “length never matters,” and this is one study.
Here is the part you can use: when an instruction keeps slipping, better wording is usually the wrong lever. Start a fresh session, and the same sentence works again.
Some things you want held every time
You should still write guidance. It is cheap, and it covers situations you could never list in advance. “Prefer small commits” can shape behavior across cases you would not have known how to encode as a pattern.
Some rules, though, cannot be left to weighing. Never commit to the main branch. Never write to that credentials file. Those need a second mechanism: a program instead of a sentence. A program does not interpret your intent. It runs and either lets the action through or stops it.
Where do you put that program? Start with a folder named .claude at the top of your project. Claude Code looks there whenever it starts in that directory. The leading dot hides the folder from a plain ls, which may be why you have not noticed it. Inside, a file named settings.json stores configuration as JSON, the format built from curly braces and quoted keys.
Before the file makes sense, you need one more piece. Every program returns a single number to the system when it finishes. Zero means it finished cleanly; any other number means it did not. That number is the program’s exit code, and Claude Code reads it. At most of the points where your program can run, exit code 2 stops the action. Exit code 1 stops nothing: Claude Code treats it as a problem in your program and continues with the original action.
Here is the same intent expressed through both mechanisms.
.claude/settings.json Guarantee. A program runs before the command does, and exit code 2 stops it.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "./scripts/no-main-commit.sh" }
]
}
]
}
} Read more
For most hook events, exit code 2 is the only exit code that blocks through the code alone. Exit 1 without valid JSON printed to stdout is treated as a non-blocking error and Claude proceeds anyway. Exit 0 with no output means the hook has no decision to report, so the call continues through the normal permission flow: the hook can deny the call, but staying silent does not approve it. Module 9 builds one of these.
CLAUDE.md Guidance. Loaded into context at session start; the model weighs it against everything else.
# Project conventions
Do not commit directly to main. Branch first. Read more
This is the whole file. CLAUDE.md is plain markdown with no required structure. It gets added to the context window at the start of the session. Nothing checks it, so nothing can reject it either. Module 3 covers which instruction files load and in what order.
The first file persuades; the second decides. Look at the second file again and notice how much more it requires: you must name the moment, the tool, and the script. That specificity is the cost of a guarantee. It is also why you would not build one for “prefer small commits.”
The second mechanism is a hook, a program Claude Code runs at a fixed point in its own loop. You name the hook in settings.json . In Module 9, you will write one and watch it fire.
The first mistake is writing exit 1, because 1 is the conventional failure code almost everywhere else. Then you see no error and no warning. Your message may even appear in the transcript, making the block look successful, while the commit lands on main anyway. Check whether the command actually ran; the appearance of your message proves nothing. The hooks reference is direct: without valid JSON on stdout, exit code 1 is a non-blocking error, so Claude Code continues with the action.
A hook is not a security wall. It is a program you chose to run, and whoever runs Claude Code can remove it. What you gain is a check that runs every time instead of most of the time. Module 11 covers permission rules, which still apply when the hook says yes.
The distinction travels because it comes from how language models work, not from Claude Code. Every tool of this kind weighs the text you give it. The files themselves travel further than you might expect: portability inverted over the past year as other tools adopted Claude Code’s layout rather than a neutral standard. Copilot CLI reads .claude/skills/, .claude/agents/, .claude/commands/, CLAUDE.md, and Claude-format hook JSON without modification. From here on, each module tells you where its artifact lands.
Every module ahead gives you a place for one mechanism or the other: instruction files, commands, skills, settings, output styles, subagents, hooks, external tool servers, and permissions. For each surface, you will see what it holds and whether that content is guidance the model weighs or a guarantee that runs.
Check yourself
- You cut your
CLAUDE.mdfrom 400 lines to 40, but the instruction still fades an hour into a long session. According to the study, what did the edit likely buy you, and what should you change instead? - You put both “Prefer small commits” and “never write to
.env.production” inCLAUDE.md. Which instruction belongs somewhere else, and how can you tell? - You write the same check as both a
PreToolUsehook and aPostToolUsehook. Both exit with code 2. Which one keeps the commit off main?