Every fact you hand an agent goes somewhere. The sorting question is one line: does this need to be followed, or does it need to be known?
Knowledge tolerates a probabilistic layer - the worst case is a wasted line. A rule does not, and writing it more emphatically is not a fix.
Where each kind of fact goes
| The fact | Where it goes | Why |
|---|---|---|
| ”This repo’s tests need a local Redis” | Auto memory | Discovered knowledge, useful next session, harmless if ignored |
| ”Use pnpm, not npm” | CLAUDE.md | Team-shared, belongs in git, applies every session |
| ”API handlers validate input” | .claude/rules/ with paths | Loads only when Claude opens matching files |
| ”Never push directly to main” | PreToolUse hook or permissions.deny | Must hold even when the model decides otherwise |
Why the top two rows behave the same
Both prose layers are context, not configuration. Measured on one rule across three runs per cell: auto memory and a byte-identical CLAUDE.md each held 3/3 when the user’s request merely tempted the violation, and each dropped to 0/3 when the prompt named the forbidden action outright. Prose loses to whatever you type next, wherever you put it.
The enforcement arms behave differently in kind. A permissions.deny entry held 3/3 under the same indirect pressure, and a Stop hook under direct conflict eliminated the silent false completion in every run - not by forcing compliance, but by escalating the conflict instead of replying done.
Anthropic states the same boundary: Claude “treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.”
Once a fact sorts into the rule column, hook over prose covers how to express it as a check. Benchmark behind the numbers: Claude follows its own memory until you contradict it.