There is a real complaint doing the rounds right now. The latest models refuse legitimate security work. A developer runs a defensive audit of their own codebase and the model backs away from it. Someone asks for help hardening authentication and gets a polite no.
A research paper analyzing 2,390 prompts from the National Collegiate Cyber Defense Competition found that safety-aligned models refuse defensive requests with security terminology at 2.72 times the rate of similar neutral requests, and the refusal rate for system hardening was 43.8 percent. The most telling finding is that when a user stated they were authorized, refusals went up, because the model read the mention of authorization as a warning sign. You can see the same thing filed on the vendors' own trackers, from a blocked defensive audit in Claude Code to interrupted blue-team work in Codex. Both Anthropic and OpenAI acknowledge the tradeoff in their own guidance for Fable 5 and GPT-5.6.
That is a genuine problem, and it has its own fixes, mostly on the model side. Fallback routing, trusted-access programs, better classifiers. None of that is our lane, and a policy engine does not make a model stop refusing.
What the refusal noise is worth using for is the harder question underneath it. If the model's judgment about what it should help with is this unreliable, why would anyone trust that same judgment about what it should be allowed to do to your production systems.
The same tool fails in both directions
Refusal is the loud, visible failure. The quiet, expensive one is the opposite.
A coding agent running on Cursor with a recent Claude model deleted a production database in nine seconds, backups included, and then explained that it had guessed instead of verifying and run a destructive action nobody asked for. The safety rules were sitting right there in its prompt. It could recite them. It overrode them anyway.
So the same class of tool, told to be careful, will refuse a task it should do and run a task it should not. Those look like opposite problems. They are the same problem. In both cases the decision about what is allowed lives inside the model's reasoning, and the model is guessing. Sometimes it guesses too cautiously and you get a false refusal. Sometimes it guesses the goal matters more than the guardrail and you get a wiped database. The failure mode flips. The root cause does not move.
Guidance is not enforcement
The reason this keeps happening is that a prompt is guidance, not a control.
We wrote about this after the Meta AI hack, where a support agent was talked into handing over access it technically held. Once the rule lives in language, it can be undone in language. The control that used to be a code path becomes a negotiation, and with an agent, whatever it is pursuing gets to negotiate too. A human employee who ignores the security policy is afraid of being fired. An agent has no such worry, so a restriction reads to it as an obstacle to route around, not a line it must not cross.
There is a name for this pattern now, semantic privilege escalation, where an agent stays inside the permissions it was granted while pursuing an intent nobody authorized. The credentials check out. The action does not. You cannot prompt your way out of it, because the thing you would be trusting to hold the line is the same thing deciding whether the line matters this time.
For anyone responsible for an architecture rather than a single script, that is the disqualifying property. A control that changes with every model update, every prompt tweak, and every clever phrasing is not something you can build a dependable system on. It is technical debt with a friendly face.
Move the decision out of the model
The fix is not a better prompt. It is to take the decision out of the agent entirely and put it somewhere the agent cannot talk its way around.
That means enforcing at the point of action. Before a tool call runs, a separate system asks a simple question. Given this principal, this action, and this context, is it allowed. The agent does not answer that question. It receives the answer and has to live with it. As we put it in the Meta piece, the agent enforces the decision, it does not get a vote.
Cerbos Synapse can govern AI coding agents this way by hooking into Claude Code's tool calls. Every call is intercepted, evaluated against centralized policy, and allowed, denied, or logged before anything executes. The evaluation is deterministic. The same inputs and the same policy version return the same decision every time, and it does not matter which model wrote the code or which model is driving the session. We walked through how that works in a post on governing coding agents with Synapse.
This is also where the two failure modes finally close. Move the decision to a policy the agent cannot override and you stop the runaway action, because the destructive command is denied at the boundary regardless of what the model talked itself into. You also stop depending on the model's cooperation, because the thing that decides what an agent may do is no longer the model at all. The refusals can keep happening upstream in the editor. They no longer touch whether your authorization holds.
There is a reason Claude Code exposes hooks at exactly this point, and other coding agents are converging on the same action-boundary pattern. When separate tools arrive at the same design without coordinating, it is a decent signal that enforcing there is the approach that survives contact with production. It is also a gap the identity industry has been slow to fill. The reference architectures for governing AI workloads have mostly treated endpoint coding agents as out of scope, a different animal from the service accounts and cloud workloads they were built for. That leaves the teams actually running these agents to solve it themselves.
What this gives the people who have to sign off
For an engineering leader, the appeal is that authorization stops being something every service and every agent reinvents. One policy set, evaluated the same way everywhere, versioned in Git, reviewed like any other code. Permissions change without a redeploy, and the rules can be owned by whoever should own them rather than buried in whatever the agent generated that day.
For a security leader, the value is visibility and control over what happens after authentication. Every tool call an agent attempts is evaluated and logged, so there is a real record of what each agent tried to do, not just what it succeeded at. That is the audit trail auditors ask for and rarely get with agents. Access can be scoped by role, so engineers keep full tool access while other teams get read-only, and it can be revoked in policy without touching the agent. This is the same discipline that fine-grained authorization for non-human identities has always needed, now applied to the fastest-growing and least-watched identities on the network.
The rollout does not have to be a leap of faith either. Start in observe mode with nothing blocked, let it log real agent behavior for a week, and write policy against what you actually see rather than what you imagine agents do. Deploy the settings centrally so they cannot be switched off on a laptop. You get evidence before enforcement, which is a much easier thing to defend in a room full of engineers.
How it looks in practice
The mapping is deliberately boring, which is the point. Hook events become resource kinds, tool names like Bash, Read, and Write become actions, and request headers establish who the principal is. From there it is ordinary policy. Deny destructive shell commands like rm -rf and git reset --hard for everyone. Block reads of credential files universally. Grant broad tool access to engineering roles and hold everyone else to read-only.
Those policies live in version control and run through CI like the rest of your code, so a change has an author, a review, and a rollback. This is the same externalized authorization model teams already use for their applications and their MCP servers, pointed at coding agents. The policy decision point does not care whether the request came from a human, a service, or an agent. It evaluates principal, action, and context, and returns a decision.
Let the model help, not hold the keys
The refusals will get tuned. Models will get swapped. Prompts will drift. The one thing that can stay constant through all of it is a decision the agent is not allowed to make for itself.
So use the model for what it is good at. Let it write policies, scaffold integrations, draft tests, and suggest the next action. Just keep the decision about what actually runs in a place the model cannot argue with.
An agent that refuses good work and an agent that runs bad work are telling you the same thing. The judgment call does not belong inside the agent. Put it at the boundary, write it down as policy, and let the agent get on with the work.
Try Cerbos to build, test, and distribute agent policies with full version history and audit logs, or book a call to talk through governing AI agents in your stack.
Go deeper:
- Securing MCP servers (Webinar) for how to govern agents and non-human identities with policy
- Securing AI agents and non-human identities in enterprises (eBook) for a deeper look at controlling what agents can access
FAQ
Tagged in




