cd /news/ai-agents/replay-your-history-before-you-trust… · home › topics › ai-agents › article
[ARTICLE · art-154412] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

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.

by read4 min views3 publishedOct 21, 2026

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 <feature-branch>) and open a PR into main.
Ask the user: only when a direct push is truly needed; they push it themselves. Do not work around this block.

The first line names the tool, the rule and a category, and the category does most of the work:

fix-and-retry: change the command and run it again use-instead: there's another route, like a branch and a PR env-setup: something on the machine needs fixing first (VPN down, expired SSO session)held: stop, and ask the user The Ask the user: line is optional. It's there when the next step isn't the agent's call, and it tells the agent not to look for a way around the block.

Since the switch, the agent reads the Fix line and follows it. A blocked push turns into a branch and a PR without me stepping in.

I still keep the rules in my instructions file. That's where the reason lives, and it shapes what the agent tries in the first place. But in a long session, with a failing check and a fix ready, pushing looks helpful, and one line of markdown is competing with everything else in the context.

A hook isn't competing with anything. It sees the actual call. So anything that must never happen gets a hook, and the hook gets tested against real history before I trust it.

rules-guard is MIT licensed and part of a small set of Claude Code mods:

/plugin marketplace add AbdAlrahmanAlQaroot/claude-code-mods
/plugin install rules-guard@claude-code-mods

Protected-branch pushes, plain force pushes and production kubectl/AWS calls are blocked by default. The stricter personal rules (AI attribution lines, PR description format, new code comments, test files in commits) are options, off by default. It makes no network calls.

Source: https://github.com/AbdAlrahmanAlQaroot/claude-code-mods

If you run a guard between an agent and your shell, what do you test it against?

── more in #ai-agents 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/replay-your-history-…] indexed:0 read:4min 2026-10-21 · —