A read-only agent that gets something wrong produces a wrong answer. That is bad, and it is bounded: a human reads the answer, notices it is wrong, and the cost is some wasted time. The error stays inside the conversation.
An acting agent that gets something wrong produces a wrong state. The email is sent. The branch is merged. The customer got the refund, or did not. The record is gone. The error has left the conversation and entered the world, where undoing it ranges from trivial to impossible depending on what it touched. Every security control we described in tool poisoning matters more here for exactly this reason: an injection that reaches a read-only agent leaks, and an injection that reaches an acting agent does.
The interesting question was never whether the model is trustworthy. It is what happens on the occasions it is wrong, and whether those occasions are survivable without a human in the loop.
Sensitivity is the wrong axis
The instinct when adding permissions is to sort actions by sensitivity. Reading public docs is fine, touching customer data needs care, anything involving money is locked down. It feels right and it produces a bad system, because sensitivity and recoverability are different properties and only one of them determines what happens after a mistake.
Consider two actions on the same customer record. Adding a note is sensitive: it touches customer data. It is also completely reversible, and if the agent adds a wrong note you delete it and nothing else has happened. Now consider sending that customer an email. Also sensitive, but the moment it leaves your server it is unrecoverable. You cannot unsend it. The recipient has read it. The two actions sit in the same sensitivity bucket and belong in completely different permission tiers.
Once we started sorting this way, the surprising finding was how much we could safely let agents do. A large fraction of the work a team wants to delegate is reversible: writing to a branch, drafting, staging, creating something in a non-production system, anything with an undo. Those can run unattended. It is a much smaller set that genuinely cannot be undone, and concentrating the controls there produces a system that is both safer and less annoying than gating everything sensitive.
The four tiers we use
Tier one: read. No state change. Runs freely, subject only to what the agent is allowed to see. The risk here is disclosure rather than damage, so it is governed by scope rather than approval.
Tier two: reversible write. Changes state, but the change can be undone mechanically and completely: a commit to a branch, a draft, a staged change, a record in a system with soft deletes and history. Runs unattended, with logging good enough to reconstruct and revert. This is where most delegated work should live, and moving work into this tier by making it reversible is usually a better investment than adding another approval step.
Tier three: externally visible. The action is technically reversible but somebody outside your system has already perceived it. Emails, messages, published content, webhooks to third parties, anything a customer sees. You can delete the record; you cannot delete the impression. These need a human, or a very narrow, well-tested policy in code.
Tier four: irreversible. Money moves, data is destroyed, permissions change, infrastructure is deleted. Human confirmation, always, with the specific consequence shown at the point of decision. There is no volume of successful runs that should promote an action out of this tier, because the argument for automating it gets stronger exactly as the operator's attention gets weaker.
Where the gate has to live
That last number is the one that gets built wrong most often. If your approval step is implemented as an instruction in the system prompt, telling the agent to ask before doing something consequential, you do not have a control. You have a preference, expressed in the same channel as everything else reaching the model, competing with whatever else is in that context.
A real gate lives in code the model cannot influence: a policy layer between the tool call and the effect, which checks the action against rules the agent has no access to and either executes, refuses, or escalates to a human. The model can request anything it likes. Whether the request becomes an effect is decided elsewhere. That distinction is the whole ballgame, and it is the same architectural principle as the execution boundary for agents that move money: capability is granted by the harness, not by the prompt.
The corollary is that credentials should be scoped per tier, not per agent. If the token your agent uses to read is the same token it uses to write, an injection that reaches the read path has the write path too. Splitting them is unglamorous and turns a class of total compromise into a class of nuisance.
Confirmation fatigue is the failure mode
The predictable way this goes wrong is not too few approvals. It is too many. A team that gates everything sensitive produces an agent that asks constantly, and an operator who clicks approve without reading, because forty of the last forty prompts were fine. You have built a system that generates the appearance of oversight and the reality of a rubber stamp, and you will discover this on the one occasion it mattered.
The defence is to make confirmations rare and informative. Rare, by pushing as much work as possible into the reversible tier rather than gating it. Informative, by showing the specific consequence at the moment of decision, not the action name. "Approve tool call: send_email" is not a decision anyone can make. "Send this message to 1,240 customers" is. The former trains people to click; the latter gives them something to actually evaluate, which is the same review-quality problem we described in the AI-native team: judgement is the scarce resource, and spending it on trivia means it is not available when it counts.
What we build for clients
In practice this comes down to three pieces. A policy layer in application code that every tool call passes through, with the tier and the rule for each action written down as configuration rather than living in a prompt. Separate credentials for read and write paths, so the blast radius of a compromise is bounded by which path it reached. And an action log that records what was requested, what was permitted, what actually happened, and who approved it if anyone did.
The log is the piece clients are most likely to skip and most likely to need. When something goes wrong with an autonomous system, the first question is always what did it actually do, and a team that cannot answer that in minutes ends up shutting the whole thing off rather than fixing the specific fault. That is how promising agent deployments die: not from a catastrophe, but from one unexplained incident and no way to bound it.
The bottom line
Write access is the point where agents stop being a productivity feature and start being an operational risk with a productivity upside. The teams handling it well are not the ones with the most restrictions. They are the ones who sorted their actions by whether a mistake can be undone, pushed as much work as possible into the reversible column, put the gate for the rest in code the model cannot argue with, and kept a log good enough to answer what happened. Do that and you can safely delegate far more than a sensitivity-based model would ever have let you.