Escalation Ladder
Defined rungs from autonomous to assisted to human-owned.
Problem
An agent has two states: it handled the ticket, or it did not.
That binary produces two bad outcomes and no good ones. When it cannot finish, the customer gets "I'm unable to help with this" and a human gets a ticket with none of the work attached, an escalation that followed the rule and failed the contract. When it can almost finish, it finishes anyway, because the alternative is giving up entirely.
The same binary shows up on the other side. The system is either fully autonomous on a path or fully gated, so the response to any doubt is to add an approval, and approvals spend a finite budget of attention that an adversary can also draw against.
What is missing is the middle: an agent that does the parts it can, hands over the parts it cannot, and degrades one rung at a time rather than falling off a cliff.
Forces
- Confidence is continuous and the response to it usually is not.
- Partial work has real value, the retrieval, the computed amount, the drafted reply, and it is discarded by a binary failure.
- Each rung up costs human attention, which is the scarcest resource in the system.
- Different triggers mean different rungs. Running out of budget is not the same problem as reading a hostile document.
- Handing over needs a package, not a notification.
- Rungs nobody services are hangs, so every rung needs an owner.
Solution
Define named rungs with explicit triggers, and let any bound, guard, or check move the run to a specific one rather than to failure.
┌───────────────────────────────────────────────────────────────┐
│ 0 · AUTONOMOUS agent acts, no one is told │
│ tier 0 · reversible · within scope │
├───────────────────────────────────────────────────────────────┤
│ 1 · NOTIFY agent acts, a person is told after │
│ tier 1 · reversible, wider · they can undo │
├───────────────────────────────────────────────────────────────┤
│ 2 · ASSISTED agent drafts, a person sends │
│ the work is done; the judgement is not │
├───────────────────────────────────────────────────────────────┤
│ 3 · APPROVAL agent proposes, a person authorizes │
│ tier 2 · irreversible · card + wait │
├───────────────────────────────────────────────────────────────┤
│ 4 · HUMAN-OWNED person owns it; agent supplies the package │
│ unroutable · hostile · no progress · policy │
└───────────────────────────────────────────────────────────────┘
triggers move a run UP the ladder — never silently downFour rules:
Rung 2 is the one that pays for the ladder. Assisted, the agent does the retrieval, the computation, and the draft, and a person presses send. It converts an eight-minute human task into a twenty-second review, it needs no approval infrastructure, and it is the rung most systems skip on the way from autonomous to escalation.
Every rung carries the work forward. Whatever rung a run lands on, the human receives the original request, everything retrieved with citations, every tool call and result, and the reason for the rung. A rung that hands over a bare ticket has thrown away the run.
Map triggers to rungs deliberately. Budget exhaustion and no progress are different failures and belong on different rungs: the first has usable partial work and goes to assisted; the second is stuck and goes to human-owned. A taint ceiling or a policy refusal goes straight to approval or human-owned regardless of how much work was done.
Never move down silently. A run may be promoted to a higher rung mid-flight; it may not lower its own rung. The rung is set by code from the run's state, and the model has no way to argue itself into more autonomy.
Code
export enum Rung { Autonomous = 0, Notify = 1, Assisted = 2, Approval = 3, HumanOwned = 4 }
// Triggers map to rungs deliberately. Budget-exhausted still has usable
// work; no-progress does not; a policy refusal skips both.
const TRIGGER_RUNG: Record<string, Rung> = {
tier_1: Rung.Notify,
budget_soft: Rung.Assisted,
budget_exhausted: Rung.Assisted,
turn_cap: Rung.Assisted,
tier_2: Rung.Approval,
taint_ceiling: Rung.Approval,
no_progress: Rung.HumanOwned,
policy_refusal: Rung.HumanOwned,
unroutable: Rung.HumanOwned,
};
export function promote(state: RunState, trigger: string): Rung {
const target = TRIGGER_RUNG[trigger] ?? Rung.HumanOwned; // unknown → highest
// Monotonic: a run climbs, never descends. The model cannot argue
// its way back down to more autonomy.
state.rung = Math.max(state.rung, target);
state.rungReasons.push(trigger);
return state.rung;
}
// Every rung above 0 hands over the SAME package. The rung decides who
// acts; it never decides how much work is thrown away.
export function handover(state: RunState, ctx: RunContext): Handover {
return {
rung: state.rung,
reasons: state.rungReasons,
originalRequest: ctx.ticket.body,
retrieved: ctx.retrievedIds.map(id => ({ id, cite: citeFor(id) })),
toolCalls: state.toolHistory,
established: state.scratchpad.values(), // with provenance
draft: state.draft, // rung 2 sends this as-is
};
}The unknown-trigger default is HumanOwned. A trigger nobody mapped is a situation nobody anticipated, and the safe rung for an unanticipated situation is the one where a person owns the outcome.
Trade-offs
Every rung needs an owner and an SLA. Rung 2 with no queue and rung 3 with no approver are both hangs. This is organizational work, and it is the part that determines whether the ladder functions, the capstone's first month found six of eight backlog items were about exactly this.
The middle rungs shift load rather than removing it. Assisted work still costs a person twenty seconds. At high volume that is a real headcount number, and it is the honest comparison: twenty seconds of review against eight minutes of doing it, not against zero.
Rung distribution is a metric that must be watched. A system where 40% of runs land on rung 4 is not automating anything; one where 99% land on rung 0 may have thresholds set too loosely. Track the distribution by route, and treat a shift as a signal.
More rungs is more to explain. Five is already a lot to hold in your head. Three, autonomous, assisted, human-owned, covers most systems and is easier to operate correctly than five that nobody can distinguish.
When not to use it
When there is nowhere to escalate to. A consumer product with no support team has no rungs above the agent. The available controls are a lower autonomous cap and an honest "I can't help with this," and building a ladder into a queue nobody reads is worse, because it looks like oversight.
When every action is tier 0. A read-only assistant needs a way to say it does not know and nothing else.
When partial work has no value. If the deliverable is atomic, it either happened or it did not, rung 2 has nothing to hand over and the ladder collapses to autonomous or not.
When the rungs cannot be distinguished in practice. If nobody can tell you the difference between rung 1 and rung 2 in your product, you have four rungs and two behaviors. Collapse them.
Two different ladders, and they are easy to conflate
The map has an escalation ladder too, and it answers a different question. That one is about architecture: function, workflow, rules, agent, multi-agent, each rung more capable and less predictable, chosen once at design time.
This one is about autonomy at runtime: how much of this particular run's outcome the agent owns, decided per run from what actually happened.
They interact in one place worth noticing. A system that cannot build the runtime ladder, no queue, no reviewers, no approval surface, usually should not climb the architecture ladder either, because the rungs above workflow all produce outcomes that sometimes need a person. Autonomy you cannot hand back is autonomy you cannot safely grant.
Related
- Risk Tiers: the tier of a call, which is one of the triggers that sets a rung
- Bounded Autonomy: the caps whose exhaustion promotes a run
- Approval Gate: the mechanism behind rung 3
- Escalation and Audit: what the handover package must contain
- Fallbacks and Circuit Breakers: the human rung as a fallback when infrastructure degrades