The first time a client asked me to make something “fully autonomous,” I built it. It was a cold-call agent named HANNA, and the demo was clean: it dialed a list, qualified leads, booked meetings through a scheduling API, and logged everything. Then it ran for a week against real numbers and made a decision no one anticipated. A prospect said “call me back next quarter,” and HANNA, trying to be helpful, scheduled a follow-up. Fine. Except it also fired a pre-authorization hold on the payments API because a downstream rule treated a booked slot as intent to buy. Nobody got charged. But the path existed, and the agent walked it, and no human was anywhere near the loop.
That is the moment the word “autonomous” stopped being a feature I could sell and became a question I had to answer carefully. Because the system was not too dumb. It did exactly what I told it. The problem was that I had wired a single switch labeled ON, when what the situation demanded was a dial.
The switch is a lie we tell in demos
Autonomy gets pitched as binary. Either the human drives or the machine drives. Self-driving versus driving. Agent versus assistant. It makes for a clean slide and a terrible engineering model.
Real systems are never binary, because the decisions inside them are not all the same. Inside HANNA there are dozens of distinct decisions: which number to dial, what to say, when to hang up, when to book, when to escalate, whether to touch money. Lumping those under one ON switch means the agent is as free to move money as it is to choose a greeting. That is insane, and yet it is the default shape of almost every “autonomous” thing I see shipped.
The useful reframing, the one I now start every build with: autonomy is not a property of the system. It is a property of each decision the system makes. You do not grant autonomy. You grant it per decision, in a measured amount, with a defined way to claw it back.
Autonomy is a cost you pay per decision, not a feature you switch on for the whole system.
Three questions, asked decision by decision
When I scope an agent now, I do not ask whether it should be autonomous. I take the list of decisions it will make and ask three things of each:
How much? What is the magnitude of this decision if it goes wrong? Picking a greeting has a magnitude near zero. Issuing a refund has a magnitude equal to the refund. Deleting a customer record has a magnitude that includes a possible lawsuit.
Over which decisions? Where exactly does the agent’s authority start and stop? “Handle billing” is not a boundary. “Apply credits up to $200 against invoices already marked overdue, never against draft invoices, never as cash refunds” is a boundary.
With what recourse? When the agent is wrong, how does a human find out, how fast, and can the action be undone? An action that can be replayed and reversed can tolerate far more autonomy than one that fires an irreversible side effect into a third-party system.
Those three questions produce a grid, not a switch. Here is the one I actually used when scoping the billing piece of the AI-native ERP I’m building (Vontra):
| Decision | Magnitude | Granted autonomy | Recourse |
|---|---|---|---|
| Draft a payment reminder email | Trivial | Act freely | Logged, editable before send |
| Send a reminder for a confirmed overdue invoice | Low | Act, then notify | Sent-items audit, recall window |
| Apply a credit under $200 | Medium | Act, notify, daily cap | Reversible ledger entry |
| Issue a cash refund | High | Propose only | Human approves, then reversible |
| Delete a client and their history | Severe | Forbidden to the agent | Human-only, soft-delete + restore |
Notice that nothing on that table says ON or OFF. Each row is a different rung. The agent is trusted to ship an email tomorrow at 3am with nobody watching, and simultaneously forbidden from ever deleting anything. Same agent. Different decisions. Different autonomy.
The ladder of autonomy
I think in rungs. Most decisions in a real system live on one of five:
- Suggest. The agent produces an option. A human does everything. This is autocomplete. It is also where I start every new capability, even ones I intend to push higher, because it lets me watch the agent’s judgment without letting it touch anything.
- Recommend with a default. The agent picks one option and pre-fills it. A human confirms. The agent is steering, the human is braking.
- Act, then notify. The agent does the thing and tells you after. This is the first genuinely autonomous rung, and the jump to it is the biggest one in the whole ladder, because it is the first time the system moves without a human in the path.
- Act within an envelope. The agent acts freely as long as it stays inside explicit limits: dollar caps, rate limits, allowed object types, time windows. It only surfaces to a human when it wants to step outside the envelope. I have written a whole separate piece on how to draw those envelopes well, because getting the boundary tight is most of the work.
- Initiate. The agent decides whether to act at all, with no request triggering it. This is the top rung and the rarest. An agent that wakes up, notices the AR aging report drifted, and starts a collections sequence on its own is operating here. Almost nothing should live this high, and the things that do need the strongest instrumentation underneath them.
The mistake I see constantly is teams trying to ship rung 4 or 5 on day one because the demo looked good at rung 1. The demo always looks good. The demo runs for thirty seconds against a happy path. Production runs for a week against an adversary, a flaky API, and a customer who does something nobody modeled.
Recourse is the load-bearing column
Of the three questions, recourse is the one people skip, and it is the one that actually determines how high you can safely climb.
Here is the asymmetry that runs my whole approach: the cost of an autonomous mistake is not the mistake. It is the mistake times how long it runs undetected times how hard it is to undo. An agent that misclassifies a lead and you catch it in the next replay and reverse it with one ledger entry cost you nearly nothing. An agent that quietly emailed the wrong 4,000 customers and you found out from social media cost you your reputation.
This is why I refuse to push any decision above rung 3 until two things exist underneath it. First, every action the agent takes is logged as a replayable trajectory, so I can reconstruct exactly what it saw and why it chose what it chose. Second, the action is reversible, or if it genuinely cannot be reversed, it is gated to a human no matter how trivial it looks. Irreversibility is the one property that overrides magnitude. A $3 charge to a real card is irreversible enough that it does not get to be autonomous, even though $3 is trivial.
# The shape of every gated action in my systems.
def execute(action, envelope):
if action.irreversible and not action.human_approved:
return propose(action) # bump it down a rung
if not envelope.permits(action):
return propose(action) # outside the boundary, ask
record_trajectory(action) # so it can be replayed
result = action.run()
if action.magnitude >= NOTIFY_FLOOR:
notify_humans(action, result) # act-then-notify
return result
That function is not sophisticated. That is the point. The intelligence of the model is upstream of it. This gate does not care how smart the agent is. It cares about magnitude, reversibility, and whether the action sits inside the granted envelope. The smartest model in the world still goes through it.
Why mid-market and federal force this on you
I build for US mid-market companies and for GovCon. Both environments make the spectrum unavoidable, for opposite reasons.
Mid-market clients have no slack for a runaway agent. They are running on twelve people and a thin margin. An autonomous system that does something expensive once erases the quarter’s savings and the trust in one stroke. So with them I climb the ladder slowly, capability by capability, watching at rung 1 before granting rung 3.
Federal is stricter still. In that world “the agent decided” is not an acceptable sentence. Every consequential action needs an attributable, auditable chain back to an authority that permitted it. The GovCon intelligence engine I architected on public federal award and spending data reads aggressively and acts almost never. It will surface that a contract vehicle is expiring and a competitor just won an adjacent award. It will not send anything, file anything, or move money. The reading is high autonomy. The acting is rung 1, by design, because the recourse story in a federal context has to be airtight.
What this changes about how you build
Once you stop treating autonomy as a switch, the build order inverts. You do not build the autonomous system and then bolt on guardrails. You build the spectrum first: the decision inventory, the magnitude rating, the envelope, the trajectory log, the reversal path. Then you let each decision climb the ladder only as fast as its recourse allows.
It feels slower. It is slower, at the start. But every catastrophic agent story I have heard, and a few I caused myself, has the same root: a decision was granted more autonomy than its recourse could justify. HANNA’s phantom pre-auth hold was that exact failure, scoped down to a near miss.
The agents I trust in production are not the most autonomous ones. They are the ones where I can point at any single decision and tell you precisely how much freedom it has, where that freedom ends, and how I undo it when it is wrong. That is not a smaller ambition than full autonomy. It is the only version of autonomy that survives contact with a real business.