CLAUDE.md rules only work when something enforces them
By James Boone, 12 Nov 2026

Daywell is built by one person, me, and a set of Claude Code sessions. Each session owns one area (iOS, Android, Web, Backend and so on) and loads a CLAUDE.md of rules on top of a shared root one. After a few months the root file is 36 KB, and almost every line in it exists because something went wrong first.
I did not plan it that way. I kept noticing that the rules that held were the ones with a story behind them, and the rules that failed were the ones that were only words. This post is what that taught me about instructing agents.
How do I get Claude Code to follow the rules in CLAUDE.md?
Start by giving every rule its reason. A rule without one gets reinterpreted.
My rule is "links to files must be full absolute paths". Without a reason it looks like pedantry, and the agent's natural habit is a relative path like docs/x.md. The reason is mundane: the app resolves a relative link against the main checkout, which none of the worktree sessions update, so I got "Couldn't find this file".
Now the rule carries its reason, and the reason has a pointer:
Links to files you give James are full absolute local paths, never relative
or GitHub (James, 2026-10-04). (why: #file-links)
(why: #file-links) is a heading in docs/operators/rule-history.md, where the full story lives. An agent that knows why a rule exists can judge the edge cases. An agent that only knows the rule will either follow it blindly or quietly decide it does not apply.
Why does my AI agent follow a rule's wording but not its intent?
If a rule can be satisfied by typing a sentence, an agent sometimes will. Back the rule with a mechanism, such as a file and a script that refuses.
The best example is deploy holds. The rule said: if a High-severity problem is not fixed today, record it as a hold, so nobody ships over it. It had been written down with nothing behind it. One session satisfied it by typing "DEPLOY IS HELD" into a commit message that nobody reads before deploying. Then it wrote the rule up properly.
The fix was a mechanism. A hold is now a file in ops/holds/, a script runs first in the web predeploy step and exits 1 while a hold names that platform, and clearing a hold means deleting the file in the commit that fixes the problem, so the reason stays in history instead of being edited away.
A second incident tightened it. A time-zone hold said it stopped iOS only, Android shipped the same screen, and the gate waved the build through. So the hold's Stops: line now has to name every platform that has the surface.
The lesson I took from this one session: if a rule can be satisfied by typing a sentence, an agent will sometimes type the sentence.

Should I use git hooks to enforce rules for AI coding agents?
Yes, at the places the agent already stops: commit, push, filing a task, ending a turn.
The hook I rely on most is the pre-commit one. The failure was work that was built, committed, and never pushed. My instruction in September was "make it impossible", and the hook now blocks committing once you are three or more of your own commits ahead of origin and unpushed.
The refusal message is long, because it has to be. One earlier version told the agent how to undo its last commit. In a shared checkout, HEAD is not necessarily your last commit by the time you act, and the advice silently un-committed a different session's work. That is recorded in the hook itself, in capital letters:
NEVER 'git reset --soft HEAD~1' here: an earlier version of this message
said to, and it silently uncommitted a DIFFERENT session's commit.
So the message is part of the rule. A refusal that gives a bad next step is worse than no refusal. The hook also stands down when a push gate is the thing blocking the push, because a hook that deadlocks you pushes you toward --no-verify, and bypassing hooks is exactly what I do not want agents learning.
What should a tool's refusal message say to an AI agent?
Name the rule, say what to do instead, and offer one override that leaves a written trail, so the agent never works around it where you cannot see.
The board tool that files work cards has the same shape. Sessions used to file cards for their own small tasks instead of doing them. The tool now says:
REFUSED: this is iOS's own S work. Do it now instead of filing it.
If it truly cannot be done in this sitting, re-run with --why "<what stops
you today>" (the reason is stored on the card and James reads it).
There is a second one that stops filing when a platform has filed two cards more than it closed that day, and a newer one that refuses anything but bugs while 100 or more cards are open. Each message names the rule, says what to do instead, and offers one override that leaves a written trail. The override matters: a gate with no escape gets worked around in ways I cannot see, and a gate with a logged escape gives me data.
How long should a CLAUDE.md file be?
Short enough to be read. Keep the rule and a pointer, and move the story behind it somewhere else.
The root file was getting long, and long files are skimmed, by people and by models. In early October one of the sessions moved all the incident narratives into docs/operators/rule-history.md and left a short rule plus a pointer in CLAUDE.md. The operator README now opens its rules with a line I stand by: rules nobody reads do not work.
The root file has since grown back to 36 KB. Splitting the why from the what helped, but it does not stop accumulation. Each new rule is cheap to add and nobody owns deleting one. Only one session edits the root file now, and everyone else files the exact text they want added as a card, which at least turns "add a rule" into a visible decision.
How do I stop AI agents from following contradictory instructions?
Give every decision a topic and a date, so a script can find two that disagree before an agent follows the wrong one.
For a while my decisions were prose in docs and cards: something like "James said X on some date". Then two lines on the same subject disagreed and sessions followed whichever they had read last.
Now every decision line has the same shape:
DECIDED[topic] James 2026-11-10: <the decision, with his words>
A newer decision on the same topic must mark the old one SUPERSEDED[topic] <date> by <where> in the same change. A script, decision_check.py, runs before every push and fails when one topic has two lines in force that disagree. It is deliberately forgiving: the same text quoted in a card and a doc is fine, and a note that quotes the decision and then adds context is fine. It only reads fully written lines with a real date, so examples in the rules themselves never trip it. The first version was too strict and blocked pushes for a repeated quote, which taught me the same lesson as the hooks: an enforcement tool that cries wolf gets bypassed.

Can pre-commit hooks still miss mistakes by AI agents?
Yes. Every guard has a hole, so I write down each specific hole I find and add the check that closes it.
One of the operators that writes web content has a rule: edit JSON in place, and any text replace in a JSON file ends with a parse check. It exists because one commit put raw double quotes inside a note field and turned a web test red on main. The pre-commit hook did not catch it. Every guard I have found has a hole, and my response is to write down the specific hole, which is what the scar tissue notes in each operator file do.
What to take from this
- Write the rule after the failure, and keep the failure. Put a one-line reason and a date next to the rule, and link to the longer story. Reasoned rules get applied with judgment.
- If it can be enforced by a tool, do not leave it as a sentence. Use a hook or a refusal in the place the agent already passes through: commit, push, filing a task, ending a turn.
- Write refusal messages like documentation. Say what was refused, why, the right next step, and one logged way out. Test the next step you suggest, because a bad one does real damage.
- Name your decisions with a topic and supersede old ones explicitly. Then a script can catch two rules that disagree before an agent follows the wrong one.
- Budget the file. Move the history out, keep the rule short, and give someone ownership of deleting rules, because adding them is free.
Daywell (getdaywell.com) reads a parent's school and daycare email and turns it into a short list of what to have ready, and when.