Replay your history before you trust an agent guardrail A developer built rules-guard, an MIT-licensed Claude Code plugin that enforces agent guardrails through PreToolUse hooks, and validated it by replaying every Bash command from local session history against the rules without executing them. The replay of 11,628 calls produced zero missed blocks and zero wrong blocks, after an earlier shell-script version blocked 33 commands of which 27 were false positives, mostly legitimate `gh pr create --base staging` calls. The plugin now emits structured block messages with a category (fix-and-retry, use-instead, env-setup, held) and a Fix line, which the developer reports the agent follows without intervention. My coding agent has one rule it must never break: don't push straight to main , master or staging . The instructions file says so. A hook on the push command still had to stop it six times. The hook was the easy part. Trusting it took longer. My first version was a shell script on Claude Code's PreToolUse hook. It looked for the word "staging" near a push and blocked the command. When I finally counted, it had blocked 33 commands. 27 of them should have gone through. Almost all were the same thing: gh pr create --base staging , which is exactly what I wanted the agent to do instead of pushing. It had a second bug. It printed its reason to stdout. The agent got the block and the message "No stderr output", so it had nothing to act on. It retried, or guessed, or asked me. So the guard was stopping the right move and explaining nothing. I only found out because I went looking. When I rewrote it as a proper plugin rules-guard , I didn't want to find the next false positive in the middle of real work. Unit tests helped, but I write tests for the cases I already thought about. The better test set was already on disk: every shell command the agent had run on my machine. Claude Code keeps each session's history locally, and every Bash call in it has the exact command and the folder it ran in. So I pulled out the Bash calls and ran each one through the new rules, without executing anything: That gave me two numbers I care about. Missed blocks: 0. Wrong blocks: 0. And I knew which 15 commands the decision rested on. A guard drifts. Every new rule, every refactor and every message tweak can change a decision you didn't mean to touch. So the replay became part of every change. When I rewrote all the block messages later, the replay ran over 11,628 calls. The wording changed on every block. Not one allow or block decision did. That's the result I wanted, and I didn't have to argue it from reading the diff. The setup is small: decision + rule per call. The other lesson from "No stderr output": the one reading the block message is the agent. If the message doesn't tell it what to do, it guesses. Every rules-guard block now has the same shape: rules-guard · protected-push · use-instead Problem: pushing to main directly is not allowed user rule: branch + PR only . Where: git push origin main in ~/code/app Fix: push a feature branch git push -u origin