CLAUDE.md is delivered as a <system_reminder> that tells the model the contents “may or may not be relevant.” Adherence is probabilistic and decays as the session grows. A PreToolUse hook that exits 2 is the opposite: the harness runs it as code and blocks the action regardless of model state or context drift.
Rule
- State the constraint as a check (path deny, command matcher, exit code) when you can.
- Put that check in a hook - not in always-loaded prose.
- Leave in CLAUDE.md only what genuinely needs prose: non-guessable commands, architecture decisions, quirks a newcomer cannot infer.
- Keep the file short enough to survive the relevance filter (under 300 lines; production target under 60).
Hooks cost nothing in the context window - they never load as instructions. Take one rule, put it in CLAUDE.md, and run the same task repeatedly: it gets violated more often the longer the session runs. Move the identical rule into a PreToolUse hook that exits 2 and the violations stop permanently - not because the model improved, but because the model is no longer the thing being asked.
Playbook: Why agents ignore your CLAUDE.md. Starter harness that ships this pattern: How to set up a minimal Claude Code harness.