▲ Agents in production
Human approval without the bottleneck.
By César Soto · 6 min read
▲ In this article
▲ In short
- Human approval becomes a bottleneck when it is applied to the whole agent; I apply it per tool, based on the action's impact and how far it reaches.
- Actions that wait for a person go into a queue with their context while the agent keeps working, so waiting never stops the flow.
- Every approval is recorded, and that history is the evidence the process owner uses to decide when a tool can run on its own.
On the post about the ways an agent fails, an operations executive proposed one more case: valid actions that are irreversible and reach the outside world, like sending, deleting or publishing. Then he asked what every operation asks next: how do you keep human approval from becoming the bottleneck? Here is the full answer, with the same support agent from the previous article.
When does human approval become a bottleneck?
It becomes a bottleneck when it is decided at the agent level: "this agent needs approval". Then a person reviews every lookup, every draft and every send, and the agent moves at the pace of their inbox. The right decision is made further down, on each action the agent can take.
How do I decide which actions need a person?
I classify each tool with two questions: how expensive is it if it goes wrong (impact), and does the effect stay inside the system or reach a customer (reach). With those two answers, each tool lands on an autonomy level.
↙ with evidence, the tool moves down a level
| Level | What the agent does | What the person does | Example |
|---|---|---|---|
| Automatic | Executes | Nothing at that moment | Look up an order, write a draft |
| Cancel window | Schedules the action a few minutes out | Stops it if something looks wrong | Tell the customer their case moved forward |
| Batch approval | Prepares the full set | Reviews and approves it all at once | Close the day's resolved tickets |
| Prior approval | Leaves the action pending and moves on | Approves, corrects or rejects it from a queue | A refund, deleting a customer's data |
The cancel window solves a good part of the case the comment raised. A send is irreversible the moment it goes out; scheduled a few minutes ahead, it can be stopped. That makes it reversible during the window without anyone approving sends one by one.
Where does that classification live?
It lives in the backend, in a policy declared per tool. The prompt can ask the agent to be careful; the policy decides what runs and what waits.
// One entry per tool. It lives in the backend, outside the prompt.
export const toolPolicy = {
getOrder: { level: "auto" },
draftReply: { level: "auto" },
notifyCustomer: { level: "cancel_window", delayMinutes: 10 },
closeTickets: { level: "batch" },
proposeRefund: {
level: "approve",
autoBelowCents: 0, // set by the process owner
},
} satisfies Record<ToolName, ToolPolicy>;
The autoBelowCents threshold starts at zero: every refund waits. It is the lever that moves later, once there is evidence. The agent has no way to read or change this policy, just as in the previous article it could not choose the tenant.
How does an approval wait without stopping the agent?
Approval is asynchronous: the action goes into a queue with its context and the agent carries on with the rest of the work. The person approves when they get to it, and execution happens at that moment, on the deterministic side.
export async function runTool(
name: ToolName,
input: unknown,
ctx: ServerContext,
) {
const policy = toolPolicy[name];
if (needsApproval(policy, input)) {
// The agent does not wait: the action goes to the queue.
return approvals.enqueue({
tool: name,
input,
tenantId: ctx.tenantId,
reason: ctx.agentReason, // the reason, for whoever approves
expiresAt: hoursFromNow(24), // if nobody decides, it does not run
});
}
if (policy.level === "cancel_window") {
// Runs on its own unless someone cancels it first.
const at = minutesFromNow(policy.delayMinutes);
return scheduler.runAt(at, name, input, ctx);
}
return tools[name].execute(input, ctx);
}
Two details matter. Every approval arrives with what the person needs to decide in seconds: what will happen, for whom, why the agent proposed it and what changes. And an approval that expires is dropped: the system never runs an action by default when it was waiting for someone.
How is autonomy earned over time?
It is earned with the approval history. Each decision is recorded as approved unchanged, corrected or rejected, and that record per tool is the evidence of how far it can be trusted.
- Start at the strictest level that makes sense. A new tool with high impact and external reach starts at prior approval.
- Watch the corrections, not just the approvals. A tool that is often approved but with changes still needs the person; the correction is the sign that the agent lacks context.
- Move one lever at a time. Raise the amount threshold or go from prior approval to a cancel window, never straight to automatic.
- The process owner makes the call. With the history in view, the person who was approving decides when to stop.
- Moving down a level is also a one-line change. If corrections come back, the tool returns to the previous level without touching the agent.
“An agent earns autonomy tool by tool, with evidence, and the decision is made outside the agent.”
Does the same apply to software development with agents?
Yes. In agent-driven development, human review of a PR is proportional to risk: a text change on a screen does not get the same review as a change in the payments module. The classification by impact and reach is the same, and so is the way trust is earned.
I describe those gates in what a harness is and in the PeakSyn method.
Where do you start?
- List your agent's tools. All of them, including the ones that only read.
- Answer impact and reach for each one. One line per tool is enough.
- Write the policy in the backend. Outside the prompt, versioned like any configuration.
- Build the queue with context and expiry. And assign each kind of approval to its process owner.
- Record every decision. Approved, corrected or rejected: it is what you will need to let go of control.
If you want to review which of your agent's actions should wait for a person, you can book a conversation of 30 minutes.
Frequently asked questions
Can the agent decide when to ask for approval?
No. The agent can explain why it proposes an action, and that reason reaches whoever approves. The decision about what waits and what runs lives in a backend policy the agent cannot read or change.
What happens if nobody approves in time?
The approval expires and the action is dropped. The agent or the system reports that it did not run. An action that was waiting for a person never runs by default.
Isn't a cancel window the same as having no control?
It is control without waiting. It fits low-impact actions that reach a customer, like a notification. For high impact, like a refund, the action waits for prior approval.
How many unchanged approvals does it take to raise autonomy?
There is no universal number. The process owner sets it based on what an error costs in that action. What matters is that the decision is made with the history in view and that moving back down is just as easy.
Does this replace backend validation?
No. Human approval decides whether a valid action is a good idea; the domain service's validations decide whether it is valid. An approved action goes through the same rules as any other input.
References
Want to apply it in your team? Let's talk for 30 minutes.
Free, on video, with César Soto.
Book a conversation