I don't necessarily think this is a new or unique insight, but I thought I'd write out what seems to have worked for me.
TL;DR
- Markdown rules files (.mdc, CLAUDE.md, GEMINI.md) are suggestions, not enforcement. My agents forgot ~20-40% of compliance tasks.
- Hook-based enforcement at action boundaries (pre-edit, pre-shell, pre-MCP) is what changed that.
- Three layers: hooks (WHEN) + validators (HOW) + guard YAMLs (WHAT). Separation lets you update rules without touching code.
- Gradual rollout: monitor → warn → enforce. Be aggressive about creating hooks, conservative about enforcing them.
- Before/after: UEAH compliance went from ~40% to 100%. Credential exposure incidents: 3 → 0.
I’m often managing quite a few simultaneous AI agents across three different platforms (Cursor, Claude Code, Gemini CLI) in a production enterprise environment, and manual-enforcement / micromanaging that many just doesn’t scale.
Now you can’t fully automate it, you still have to manage this system.
The click-baity headlines that promise you or claim they have fully abstracted the agent systems are just “jesus-take-the-wheel,” fluff.
But what’s worse is what I’ve seen out there: AI Slop articles that promise this angelic future with only markdown files.
Yes, markdown is code now.
No, markdown does not provide deterministic stability or rails; that’s not how prompts —> LLMs works.
You need more than the best ever prompts and rules, per-agent files, agents.md files, commands, etc.
So many of these tedious agentic engineering tutorial starts the same way:
Initially, I took that advice just like many others.
I wrote beautiful, detailed rules files. I added numerous skills.md files. I even created systems to have agents print into the context window when/if whatever rule they were currently reading / cooperating with. I had a curated set of carefully crafted, ALL-CAPS warnings. "CRITICAL: NEVER expose credentials." "YOU MUST update CHANGELOG.md with UEAH attribution — No exceptions."
I built scaffolds around those prompts, more reinforcement of discipline, experimented with / commands.
And then I watched my agents ignore every single one of them, sometimes often, sometimes only on edge-cases, but every time it was a problem.
What I Actually Observed
I'm not exaggerating for effect here (I think most humans reading this who are trying to do real things with their agents will understand).
I tracked compliance across my agent fleet for months and this is what the data showed:
| What I Tried to Enforce (via Markdown ONLY) | What I Expected | What Actually Happened |
|---|---|---|
UEAH attribution in every CHANGELOG.md entry |
Agent ID + timestamp on every line | Plain text. No attribution. "Who made this change?" became unanswerable. |
| PARA footers in Jira comments | Cross-platform context links | Bare text updates. Zero traceability. |
| Sound notification when work starts | Audio acknowledgment | Dead silence. Agent starts working, I have no idea. |
| Unicode/injection scanning on MCP inputs | Security check before every call | Raw, unsanitized content passing straight through. |
The root cause was embarrassingly simple once I saw it: declarative rules without procedural enforcement are just suggestions.
I'd built this gorgeous governance library; 20+ Cursor rules, a multi-page CLAUDE.md, carefully organized security guidelines; and none of it mattered because nothing enforced it at the moment the agent was about to do something.
The agents profusely and eloquently praised these systems, and were HORRIBLE at diagnosing the problem.
They had these rules in their context window when diagnosing the problem, so it was difficult to imagine situations where they would disregard and/or their outputs would obviate these markdown files, rules, skills, etc.
But that’s what happens, these things get lost.
The agents’ rules live in the agent's context window. Context gets compacted. Attention drifts. The agent's "CRITICAL: NEVER do X" instruction is sitting somewhere below the fold, exerting zero influence on the next tool call.
I was building a Potemkin village — beautiful governance documentation with no police force behind it.
Hooks as Payload Injection + Script Invocation
So, because every agent operation — every single one — passes through a finite set of action boundaries, we have options.
Moments where the agent transitions from thinking to doing:
- About to execute a shell command? That's a boundary.
- Just modified a file on disk? Boundary.
- About to call the Atlassian MCP server? Boundary.
- Starting a new session? Boundary.
- Spawning a sub-agent? Boundary.
These are the enforcement event points in the agents' workflows.
This pattern is becoming standard. Oculi ships a commercial product doing exactly this; IDE-to-agent policy enforcement via YAML. The Everything Claude Code project (118K+ stars) includes hook-based security enforcement with cross-platform adapters. What follows is my version + the compliance data I've collected.
If you can intercept the agent's action at the boundary — before it actually executes (or right after) — you can validate, block, warn, or augment in real time.
The agent doesn't get to choose whether to comply.
The hook fires whether the agent remembers the rule or not.
I spent about 6 weeks building this out and honing it, and the difference is night and day.
Four Families of Hooks
I ended up grouping all enforcement into four families, each handling a different class of problem:
- Compliance hooks are the attribution police. The CHANGELOG-UEAH hook is a hard block — if an agent writes to CHANGELOG without a proper UEAH tag (
UEAH-CUR-YYYYMMDD-HHMMSS-rand4), the write is rejected. Not warned. Rejected. The PARA-links hook warns when Jira or Confluence writes are missing cross-platform context footers. - Security hooks handle the scary stuff. Injection scanning on incoming context. Unicode sanitization that strips zero-width joiners and bidirectional text markers (the kind that can hide malicious instructions from human review). A token exposure guard that scans all outputs for API keys and secrets before they reach any external endpoint.
- Quality hooks keep things operational. Sound notifications at action boundaries (so I actually know when agents are working). Context drift detection that warns when an agent has wandered way off task. Skill structure validation for new skills.
- Orchestration hooks coordinate the multi-agent chaos. Anti-spiral detection catches agents stuck in recursive loops (this was a weekly occurrence before hooks). Handoff validation ensures structured handoffs between agents. Task pickup authorization prevents agents from claiming work they're not qualified for.
The Enforcement Router: What Gives This "Teeth"
At the center of everything is a Python script that receives events from any agent platform via JSON stdin, runs the appropriate validators, and returns a decision via JSON stdout.
Here's the flow for every agent action:
- Agent initiates action (write a file, call an MCP server, run a shell command)
- The platform hook fires (each IDE/CLI has its own hook config)
- The enforcement router receives the event as JSON — event type, tool name, arguments, context
- Validators run across all four families
- The adjudication engine decides: enforce, warn, or monitor
- Result goes back to the platform: exit 0 (allow), exit 1 (warn), exit 2 (block)
That adjudication engine piece is important — it supports gradual rollout.
A new hook starts in monitor mode (just logs, never blocks), graduates to warn (shows a warning but lets it through), and finally reaches enforce (hard block).
This saved me from breaking all my agent workflows when I was iterating on new hooks.
I suspect a lot of people experiment with warn and give up and/or sleep on the opportunities it provides.
Reinforcement Learning training on these LLMs means that even a subtle warn payload reminding it that it just failed to properly document fully or write an ADR ends up being catnip automatically supplied to the agent; it will take that prompt payload and address the warning to please the human operating it.
I also suspect a fair number of developers deploy hooks with the enforce mode and blow up their workloads and grind their agents to a halt and give up.
It’s more of an art vs. a science and can vary across model families given their various different characteristics / behaviors.
This enables you to be aggressive about creating hooks but conservative about enforcing them.
But hooks alone won't save you
Here's where I think most devs stop short. They see that Cursor supports hooks.json, they wire a bash one-liner to an event, and they call it a day. Maybe a grep for a bad pattern, maybe an echo to a log file. And then they're confused when it doesn't scale.
A hook by itself is just an event trigger.
The hook fires when something happens. That's it.
It's like wiring a smoke detector that goes off but isn't connected to a sprinkler system.
The detection is worthless without the response, and in-particular, without a contextual response.
Smoke alarms going off constantly without context gets ignored and/or the battery removed.
If someone tells you a smoke alarm is going off and they saw smoke + flames, you’d listen to them, you’d call the fire-department, you’d evacuate the building.
Our hooks need to do the same thing, they have to have a payload that’s event-driven + context-driven so that we’re supplying agents with context when/if a hook fires.
What makes this actually work is a three-layer architecture, and each layer has a different job:
This separation matters more than it might seem at first. Last month I needed to block a new class of shell command — agents were running curl | bash patterns to install packages without approval. Without the three-layer split, I would have had to edit Python code, test it, hope I didn't break the regex for the 15 other patterns already in there. Instead, I opened a guard YAML, added two lines, and it was live. No code changes. No testing the validator module.
I currently have over 20 guard YAML configs spread across the four families, and the Python validators that consume them total maybe 500 lines across four modules. That's the whole enforcement engine.
I'll go much deeper into the validator internals and YAML schemas in Part 2. But the point I want to make here is: if you deploy hooks without this layered structure, you're going to hit a wall fast. The hooks are necessary. But the validators and the guard YAMLs are what make them real.
Before and After
I'll let the numbers speak for themselves:
| Metric | Before Hooks | After Hooks |
|---|---|---|
CHANGELOG.md entries with UEAH |
~40% (agents forgot) | 100% (hook blocks without it) |
| Jira comments with PARA footers | ~20% | 95%+ |
| Credential exposure incidents | 3 in first month | 0 since deployment |
| Session state lost on crash | Every time | Never (session-end hook saves state) |
| Agent recursive spirals | Weekly | Auto-detected and terminated |
But the real change wasn't any single metric. It was the shift from hoping agents comply to knowing they do. When changelog-ueah is in enforce mode, the agent physically cannot write to CHANGELOG without attribution. It's not a suggestion anymore.
One System, Three Platforms
Every major agentic IDE now supports hooks natively, which makes this whole approach viable:
- Cursor uses
.cursor/hooks.jsonwith events likebeforeShellExecution,afterFileEdit,beforeMCPExecution, andstop. - Claude Code uses
.claude/settings.jsonwithPreToolUse,PostToolUse, andSessionStart. - Gemini CLI uses
.gemini/settings.jsonwithBeforeTool,AfterAgent, andSessionStart.
All three route through the same executor.py. A security rule I define once protects all agents equally. No "this agent has different rules" drift — the guard YAMLs are the single source of truth.
(Side note: getting the event names right was its own adventure — I initially documented Cursor events as preToolUse and sessionStart which don't exist in Cursor's schema. They're Claude Code event names. The actual Cursor events are beforeShellExecution, beforeMCPExecution, etc. I wrote a whole ADR about this after it caused a silent security gate bypass. Does that make sense? The documentation of the enforcement system had a bug that defeated the enforcement system. Turtles all the way down.)
What I'd Tell You If You're Starting From Zero
If you're managing AI agents in production — even just one Cursor instance — here's what I'd suggest:
- Figure out your action boundaries. What do you care about? Where does the agent go from thinking to doing? Those are your enforcement points.
- Write validators. Python or Bash scripts that check inputs/outputs at those boundaries. They don't have to be fancy.
- Define rules as data. YAML configs that validators read at runtime. Separate the "what" from the "how."
- Wire hooks to your IDE. Every major platform supports this now. It's not experimental anymore.
- Roll out gradually. Monitor → warn → enforce. Don't hard-block on day one.
Your rules files still matter — they teach the agent what to do. But hooks ensure it actually does it. Suggestions become constraints. Theater becomes enforcement.
And you can stop worrying about whether "CRITICAL: NEVER expose credentials" is landing in the right part of the context window, because there's a Python script that blocks the action before it happens regardless.
What's Next
This post covered the why and the what — why rules files alone fail, what enforcement at action boundaries looks like, and how hooks + validators + guard YAMLs compose into a system that actually works.
In the next post, I'm going to go deep on the how. The actual Python validator modules. The YAML guard schema I've converged on after months of iteration. The adjudication engine that supports gradual rollout. The federation pipeline that compiles all of this into per-platform configs so one set of rules governs Cursor, Claude Code, and Gemini CLI identically. And the health checks that catch when a hook or validator has drifted out of spec.
If that sounds interesting — or if you've built something similar and want to compare notes — I'm at johnclick.ai and johnclick.dev.
This is based on T-ADR-038 from my Agentic Developer Toolkit. The full enforcement engine internals are in Part 2. The ADR-first protocol that governs all of this is documented in ADR-First Development.
I'm a DevOps / IT Platform Engineer building agentic governance infrastructure for enterprise AI agent deployments.
