Back to News & Insights
Artificial Intelligence August 20, 2026 · 8 min read

Your agent isn't reckless. It just can't see the blast radius.

I've been running Claude Code as a daily driver for about three months now. It writes Ansible I'd...

Your agent isn't reckless. It just can't see the blast radius.

I've been running Claude Code as a daily driver for about three months now. It writes Ansible I'd have taken a week to write. It reads a codebase faster than I do. It is, genuinely, very good.

It also once wanted to force-push to main, and it wanted to for an extremely good reason.

The rebase was stuck. Force-pushing would have unstuck it. Every link in that chain of reasoning is sound. The agent wasn't being careless, wasn't hallucinating, wasn't "drifting" or whatever we're calling it this month. It made a locally correct decision with a non-local consequence, which is the exact category of mistake that human code review is worst at catching — because the diff looks fine.

For a while my answer was to read everything. Every diff, every command, eyes on the screen, hand hovering over Ctrl-C like a man watching a toddler near a staircase.

This does not scale, and the reason it doesn't is embarrassing when you say it out loud: reviewing output scales with how much the agent writes. That number is going exactly one direction, and it isn't down.

So I flipped it. Instead of reviewing what it produces, I started writing down what it must never do.

And here's the good news that took me way too long to notice: that list is short. Not "short for a security policy" short. Short like you can fit it on a napkin.

Here's mine: A credential it read an hour ago gets inlined into a source file. A rebase gets stuck, and the fastest route to a green terminal is git push --force origin main. rm -rf "$BUILDDIR/" runs on the one machine where BUILDDIR never got set. A version bump gets typed straight into package-lock.json, because that's the file the version number is visibly in. A failing test quietly grows a .skip and CI goes green. Someone runs cat .env "just to see which variables exist."

None of these are the agent being stupid. Every single one is a reasonable move by something that can't see two feet past the command it's about to run.

PreToolUse is a hook that fires before any tool call. Your script gets the whole thing on stdin:

Say "blocked" and it shrugs and retries with slightly different syntax, like a cat testing a closed door. Say "change the manifest and run pnpm add" and it goes and does that, first try, no argument.

Which reframes the whole thing. A denial isn't just a fence. It's the highest signal-to-noise teaching moment you will ever get, because it lands at the precise second the agent was about to be wrong. Nobody reads documentation at that moment. Everybody reads an error.

So every guard I wrote has to answer two questions, not one: what's wrong, and what to do instead.

| Guard | Blocks | |---|---| | secrets-never-land-in-source | Credential-shaped literals written into source | | secret-files-stay-out-of-context | Reading .env, .pem, ~/.aws/credentials into the session | | secrets-are-not-staged | git add -A in a repo where .env was never gitignored | | shared-branches-are-not-rewritten | git push --force to main, develop, release/ | | uncommitted-work-is-not-discarded | git reset --hard, git clean -fd, git stash drop | | verification-hooks-are-not-bypassed | --no-verify, HUSKY=0, --no-gpg-sign | | unexpanded-variables-in-destructive-paths | rm -rf "$DIR/" where $DIR could be empty | | remote-code-is-not-piped-to-a-shell | curl … \| sh | | committed-migrations-are-immutable | Editing a migration that's already committed | | destructive-sql-needs-a-where | Unbounded DELETE/UPDATE, ad-hoc TRUNCATE | | cluster-targets-are-explicit | Destructive kubectl with no --context | | tests-are-not-silenced | Introducing .skip, @Disabled, continue-on-error: true | | lockfiles-are-generated-not-edited | Hand-editing package-lock.json and friends |

Zero dependencies. Nothing to configure. Node reading a JSON payload and occasionally saying no.

Four of them turned out more interesting than I expected when I started writing them. Reading a secret is worse than writing one

The write path has a code review in front of it. Someone, eventually, looks at that diff. The read path has nothing. When an agent runs cat .env to check which variables exist, it gets a completely reasonable answer to a completely reasonable question — and every value in that file is now sitting in a transcript. Transcripts get stored. Synced. Occasionally pasted into a bug report by someone being helpful.

Nothing changed on disk. git diff is empty. And your credentials have left the building.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation