Blog · 2026-11-02
Why agent permissions break at scale
Five kinds of gate, five systems, and nobody can say why the agent did that
The first version of an agent's permission system is one if statement. Should the agent send this email? Ask the user. Done.
A year later the same question runs through five systems. The flag tool says the new mailer is on for 20% of users. The experiment tool has this user in the arm that drafts but never sends. Billing says they were on Pro until last Tuesday. A permissions table someone wrote in a weekend says email needs approval unless the user ticked "send automatically". And the user has approved the last forty emails without changing a word, which nothing records at all.
Each system answers its own question well. None of them knows the others exist. That is the point at which agent permissions break, and almost every team building agents for real users gets there.
The five gates
Agents that act for people (send, book, pay, merge, file) end up gated in five ways.
- Feature flags and ramps. Is this capability on for this user yet? Ramp it from 5% to 50%, and kill it if it misbehaves.
- Experiments and holdouts. Which variant of the behaviour does this user get? And who is held out so we can measure the effect?
- Plans and entitlements. Has this user paid for the action? What happens to it when they stop paying?
- Policy. Never move money to a new payee without asking. Never email more than fifty people at once. Never act at 3am in the user's time zone.
- Earned autonomy. Has the agent done this well enough, for this user and this kind of action, to stop asking?
The first four have mature tools. LaunchDarkly, Statsig and GrowthBook do flags and experiments. Stripe and Stigg do entitlements. Oso and Cerbos do policy. The fifth has no tool at all, so it lives in a JSON column, a Slack thread, or nowhere.
Why five systems fail
Separately, each gate is fine. Together they create three problems that only show up at scale.
Nobody can explain a decision. A user writes in to say the agent sent a message without asking. Support checks the flag tool (on), the experiment (treatment arm), billing (active), and the permissions table (approval required). So why did it send? It takes an engineer half a day to find that a holdout ended, which flipped the user to a default that skipped the check. Every gate was right, and the outcome was still wrong. When the answer to "why did it do that?" lives in five dashboards, nobody answers it.
The order of the gates is everywhere and nowhere. Does a lapsed subscriber keep the autonomy they earned? Does an experiment arm override a user's own preference? Can a kill switch stop an action the user explicitly approved? Each team answers these in code, in a different place, often by accident. The rules are real, but they are never written down, so they drift.
Trust has nowhere to live. People say they want control over what agents do. In a recent consumer survey by Global Payments, 59% of people open to agents would require approval for every purchase. Yet developers approve 93% of Claude Code's permission prompts, according to Anthropic. Those are different groups, but both are true of most of us. We want to be asked, and then we stop reading the question.
The fix is an agent that asks less and asks better. It should earn the right to act from its own record with a particular person, then drop back to asking when something looks unusual (a new payee, an amount ten times the usual, a tool it has never used). Doing that needs every approval, edit, rejection and undo to land somewhere, per user and per action. A flag tool has no column for it. A billing system has no idea it exists.
What a single decision looks like
The alternative is boring on purpose. Put every gate behind one call that returns one of three answers (allow, ask or deny) and the reason.
// npm i @throtl/sdk
import throttle from "@throtl/sdk";
const d = await throttle.can({
agent: "mailer",
subject: user.id,
action: "email.send",
context: { to, recipients: 1 },
});
if (d.decision === "allow") await send();
if (d.decision === "ask") await askUser(d.message);
The reason chain comes back with it. The plan allowed email, the rollout included this user, the experiment arm capped autonomy at Ask, and so the deciding gate was the experiment. Support can read that. So can the agent.
The gates run in a fixed, published order, which settles the questions above once and for everyone.
- Hard denies first (kill switches, policy that says never, and plans the user doesn't have).
- Then forced asks, for action classes you've marked as always ask.
- Then rollouts and experiments, which can cap autonomy but never raise it.
- Then earned trust, which lifts an action only as far as the lowest cap above.
- Then tripwires, which drop that one call back to Ask when something is unusual.
A few properties fall out of that order. Nothing defaults to allow. An agent can ask for more autonomy but can never grant it to itself. A lapsed subscriber's earned trust is frozen rather than lost, the actions their old plan covered follow a declared policy (ask for the safe ones, deny the rest), and the trust comes back when they resubscribe. And because every human answer is recorded against the decision that prompted it, approval rates, undo rates and the effect of each experiment arm come for free.
Config, not code
The other half of the problem is who gets to change the rules. When gates live in code, ramping from 20% to 50% is a pull request, ending a test is a deploy, and changing what the Free plan can do is a sprint ticket.
Agent behaviour changes too often for that. Treat flags, ramps, experiments, plans and policy as data, audit every change (who, what, when and why), and let the people who own the product change them without a deploy. Agent builders already accept this for prompts. Permissions deserve the same.
Where to start
You don't need a new system to get most of the benefit. Three changes help on their own.
- Write down the order. List your gates and the order they run in. If two of them disagree, which wins? Most teams find at least one rule they never meant to have.
- Return a reason with every decision. Even a string array of which checks passed turns a half-day investigation into a log search.
- Record the human's answer. Approved, edited, rejected or undone, against the action and the user. That record is the raw material for asking less, and you can't recover it later.
We're building Throtl to do all three in one call, with the five gates, the published order and earned autonomy (six levels, L0 to L5) in one place. The levels and the decision order are an open spec, so you can use them with or without us.
Matt has run experiments and live-ops for games with 200M+ players, subscriptions at Cleo, and an AI agent platform at Plum. Every one of those had this problem. Agents make it urgent.
Join the waitlist at throtl.ai for a design-partner key.
Sources
- Global Payments 2026 Agentic Commerce Report, via FashionUnited. 16,000+ consumers in 7 markets, asked only of people open to using agents.
- Anthropic engineering, Claude Code auto mode. 93% of permission prompts approved.