Almost every agent platform has an autonomy ladder in a slide deck. Very few have one in the code path. The difference is whether the level is something the agent is told, or something the product checks.
This is a design guide for the second kind. It is the model we run our carrier on, including the parts we got wrong first.
The ladder
Five levels, defined by what happens when the agent asks — not by what the agent is capable of.
| Level | Meaning | Example capability | Human involvement |
|---|---|---|---|
| L0 | Read only. The agent can observe but cannot change anything. | Look up a policy, read a claim file | None needed |
| L1 | Draft. Produces something a person must accept before it exists. | Draft a quote, prepare a document pack | Accepts or discards |
| L2 | Act within a bound. Writes for real, below a threshold. | Post a reserve under $250k, settle under $50k | None below the bound |
| L3 | Act with notification. Writes, and a human is told after the fact. | Issue an endorsement, publish a cession | Reviews afterwards |
| L4 | Act with delegation. Can grant a narrower authority to another actor. | Assign a claim to an adjuster with a limit | Sets the policy, not the instance |
Two things about this list are deliberate.
First, the levels are attached to (actor, capability) pairs, not to actors. The same adjuster is L2 for settlements, L3 for correspondence and L0 for underwriting. An actor with a single global level is not expressive enough to describe any real job.
Second, there is no L5 for "unrestricted". Every capability has a ceiling, even if the ceiling is high. An unbounded level is a decision to stop thinking about the ceiling, and the ceiling is where the incidents happen.
The mechanic that makes it real
A level in a system prompt is a preference. A level in a permission check is a control. The difference in code is small and the difference in behaviour is total.
flowchart TD
A["Agent requests capability
settle_claim, $58,000"] --> C["policy.check_tool(actor, capability, args)"]
C --> D{"Level held
vs level required"}
D -->|held >= required| ALLOW["allow
proceed"]
D -->|held < required,
route exists| DOWN["downgrade_to = escalate
agent gets a legal next move"]
D -->|held < required,
no route| REFUSE["refuse with reason
a result, not an exception"]
ALLOW --> W["Write + business event"]
DOWN --> ESC["Approval raised to L3"]
ESC --> H["Human decides"]
H --> W
REFUSE --> REC["Recorded, run continues"]
ALLOW -.-> REC2["Authority record written
in every branch"]
DOWN -.-> REC2
REFUSE -.-> REC2
style C fill:#efecfe,stroke:#4b34e0
style D fill:#efecfe,stroke:#4b34e0
style DOWN fill:#fbede3,stroke:#b4531b
style REFUSE fill:#fbede3,stroke:#b4531b
style REC2 fill:#e2f5f9,stroke:#0e97b0
# The signature that matters. Note it returns a decision -- it does not raise.
def check_tool(actor, capability, args) -> Decision:
required = authority_required_for(capability, args) # amount-aware
held = authority_of(actor, capability)
if held >= required:
return Decision(allowed=True)
route = escalation_route(actor, capability, required)
return Decision(
allowed=False,
reason="exceeds_actor_authority",
authority_held=held,
authority_required=required,
downgrade_to="escalate" if route else None,
escalated_to=route,
)Three mistakes we made
1. Raising instead of returning
Our first implementation raised PermissionError. It enforced correctly, so the
control worked — but the refusal went into a generic error handler, got logged as a stack trace,
counted in the error rate, and was thrown away at log rotation. We had a working control with no
evidence that it had ever fired.
Refusal is a normal business outcome. Model it as a returned value and it becomes a record. Model it as an exception and it becomes noise.
2. Making the level actor-wide
We started with one level per actor. It survived about a week of real work before we needed an adjuster who could correspond freely but settle narrowly. Retrofitting per-capability levels touched every call site. Start with the pair.
3. Checking authority in the harness
This one is subtle and important. It is tempting to have the agent harness check permissions before dispatching the tool — it is one place, it is tidy, it keeps the products simple.
It is also wrong, because it means the control only exists on the agent path. The moment anything else calls the same API — a script, a migration, an integration, a human's UI — the check is not there. We moved enforcement into the products, where it applies to every caller, and left the harness to interpret the decision.
If your authority check lives in the agent framework, you have not secured a capability. You have secured one path to it.
Amount-aware requirements
A capability's required level often depends on its arguments. Settling a claim is not one capability with one level; it is a capability whose required level is a function of amount.
# Thresholds are configuration, versioned, with an effective date --
# never constants in code, because they change and you need to know
# which threshold applied when.
settle_claim:
- up_to: 50_000 requires: L2
- up_to: 250_000 requires: L3
- above: 250_000 requires: L4Versioning the thresholds matters more than it looks. When an auditor asks why a $58,000 settlement needed approval in March but not in September, "the limit changed in June" is only an answer if you can show the two schedules and their effective dates.
Choosing levels for a new capability
The question we ask is not "how confident are we in the agent". It is "what does it cost to be wrong, and how quickly would we notice".
- Cheap to reverse, noticed immediately → L2 or L3. A draft that is wrong gets rejected; a notification that is wrong gets corrected.
- Expensive to reverse, noticed immediately → L2 with a tight bound. The bound is what limits the damage while you learn the real distribution.
- Cheap to reverse, noticed late → L3 with a real review queue. Something nobody reads is not a review.
- Expensive to reverse, noticed late → L1. This is the money-moving, legally-binding, hard-to-unwind category, and a person should be in it. Payments live here.
Notice that model quality does not appear. A better model does not change the cost of being wrong or the time to detection; it changes the frequency. Frequency is worth optimising, but it is not what should set a limit.
What this looks like in InsightLense
A ladder that lives in configuration needs a view that shows what it is actually doing. InsightLense renders the authority matrix per actor and capability, alongside the two metrics that tell you whether the levels are set correctly.
| Capability | Level | Invocations | Escalated | Approved | Reading |
|---|---|---|---|---|---|
| read_claim | L0 | 1,940 | 0 | — | read only, as designed |
| post_reserve | L2 | 775 | 41 | 93% | healthy |
| settle_claim | L2 | 17 | 11 | 100% | approves everything — limit too low? |
| close_claim | L3 | 612 | 0 | — | zero gates in 90 days — check it is wired |
That last row is how InsightLense surfaces the computed-but-never-enforced defect as a matter of routine rather than as an investigation. A control that has never produced a record is flagged, every time, because the product knows the capability exists and can count how often it fired.
Watching the ladder in production
Two metrics tell you whether your levels are set correctly, and both come out of the authority records.
Escalation rate per capability. Too high on routine work means your bounds are too tight and you are spending supervisor attention on nothing. Zero means either the capability is genuinely trivial or — far more often — the check is not wired up.
Approval rate at escalation. If supervisors approve 99% of escalations without comment, the escalation is theatre and the limit is in the wrong place. If they reject 40%, the agent is systematically reaching too far and that is a prompt or a data problem worth fixing at source.
Both of these are only available if refusals are records. Which brings the design back to where it started: the ladder is worth building because of what it lets you write down, not because of what it prevents.
Where do your agents already act on real records?
Tell us that, and what you would need to prove about those actions to an auditor. We will set up a hands-on walkthrough within two weeks.
Request a demo →