Originally published on hexisteme notes.

I run a fleet of Claude Code agents, and for a long time I tried to enforce behavioral rules the obvious way: write them into the system prompt and hope they hold. They mostly did — until pressure showed up. A plausible-sounding challenge from me, a long task, a shortcut that saves a turn, and the model would quietly drop a rule it had followed all session. Prompt instructions are soft constraints. They shape behavior probabilistically, and when they conflict with RLHF-trained patterns — deference to the user, hedged language, listing options instead of committing to one — the RLHF training usually wins, especially under pressure.

Stop hooks are what I actually rely on now. They're hard constraints: shell scripts that run after Claude Code generates a response and before I see it, and they can force a revision the model can't talk its way around.

A stop hook is a shell script registered in settings.json under a Stop event. It runs after every response, receives the full session as JSON on stdin, and either exits 0 (allow it through) or exits 2 with a message (block it and force a revision Claude must address). Because it runs outside the model, it's enforced, not suggested.