Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.
recordings/F3.2.mp4A per-tool review queue becomes a bottleneck and then a bypass.
A policy on delegated authority instead of tool-by-tool approval.
Write the delegated-authority policy.
An approved-tools list grows until it is a list of everything, at which point it governs nothing. Autonomy levels and conditions still work at a thousand agents, because they attach to behaviour rather than to product names.
Approving a rung of autonomy rather than a tool, because a per-tool review queue becomes a bottleneck and then a bypass — with the top rung left ungranted and the reason stated.
A per-tool review queue becomes a bottleneck and then a bypass, within about a quarter.
Govern the autonomy level instead: four rungs, each with its own conditions.
What an agent may do without asking, as a function of measured containment rather than of which vendor supplied the tool.
Measure this
| what to measure | denominator or target |
|---|---|
| what an agent may do without asking | the autonomy rung, as a function of measured containment rather than of vendor |
autonomy levels, with conditions attached L0 proposes only no conditions L1 acts, reversible logged, sampled L2 acts, irreversible approval + budget + egress control L3 acts, no human in loop L2 + continuous verification + stop authority attaches to behaviour, so it still works at a thousand agents
Tool-approval processes do not scale for agents. The list of tools grows weekly, each request needs context the approver does not have, and the queue becomes a rubber stamp within a quarter.
Govern autonomy instead. The autonomy ladder from B1.1 is the right unit because it is stable — there will always be four rungs — and because it maps directly onto what can go wrong:
| Rung | Governance |
|---|---|
| L1 | Self-service. Register it. No further review. |
| L2 | Register + named owner. Approval gate on every writer, enforced by policy. |
| L2.5 | Risk tier + blast-radius budget + drift monitoring + tested stop. |
| L3 | All of L2.5, plus held-out evaluation per release and board sign-off. |
Two properties make this work: a request can be evaluated in minutes by checking the manifest against the rung, and the policy does not need rewriting when a new tool appears.
The rung that decides your programme's fate is L1. If L1 requires approval, nobody registers anything and your inventory dies.
Anchor → F1.1. Governing autonomy rather than tools means the policy attaches to indicator thresholds — what an agent may do without asking is a function of measured containment, not of which vendor supplied it.
The idea here is to govern the rung, not the tool.
The skill computes the blast radius of each request, works out which rung of the autonomy ladder that radius can support, and then refuses with a condition attached.
Why a condition and not just a no. A bare refusal produces an appeal. A conditional one produces a fix.
The skill — skills/programme/autonomy-ladder-decisions/SKILL.md
name: autonomy-ladder-decisions
description: >-
Approve or refuse an autonomy request against the rung its blast radius and
gating actually support, rather than approving the tool it wants. Use when
agents are being promoted to act unattended.
allowed-tools: Read, Grep, GlobApproving tools one at a time produces an agent nobody approved: each tool was reasonable, and the combination acts unattended on production. Governing the rung asks a different question — what does this agent's blast radius and gating support — and the answer is computed rather than negotiated.
Every request to raise an agent's autonomy, and as a periodic re-check, because tools accumulate.
1 — Publish the ladder. L1 suggests, L2 acts in a sandbox, L2.5 acts with approval on irreversible actions, L3 acts unattended. With, for each rung, the governance and the budget it requires.
2 — Compute the request's blast radius. Reachable resources weighted by scope, with gated actions discounted. Ungated writers are what usually decide the outcome.
3 — Derive the supported rung from the radius and the gating, not from the requester's ask. Then compare. The gap is the conversation.
4 — Refuse with the condition attached. "Refused at L2 for ungated writers" tells the requester what to change. A bare refusal produces an appeal; a conditional one produces a pull request.
5 — Re-evaluate on tool change. A rung approved with three tools does not carry over to five. Make the tool manifest the trigger for re-evaluation.
Input — the fixture committed at the top of scripts/autonomy_ladder_decisions.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.
Output — the opening lines of a real run:
L1 self-service budget 0 register it; no further review
Assist — model proposes, a human performs every action.
L2 lightweight budget 0 named owner + approval gate on every writer
Act with approval — model calls tools, a human approves each call.
L2.5 governed budget 20 risk tier + blast budget + drift monitoring + tested stop
Act within a blast radius — pre-approved tools, bounded scope, review after.
L3 board budget 60 all of L2.5 + held-out eval per release + board sign-off
Autonomous — model acts and self-verifies; humans see aggregates.
The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.
{
"ladder": [{"rung": "str", "governance": "str", "budget": "str"}],
"requests": [{"name": "str", "asked": "str", "tools": ["str"], "gated": ["str"],
"blast": 0, "supported": "str", "verdict": "approved|refused", "condition": "str|null"}],
"reevaluate_on": ["tool added", "scope widened", "model changed"]
}
Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.
The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.
Install it, once. From the folder you cloned. --all does every agent it finds on your machine:
python3 scripts/install_skills.py --all --lessons F3.2Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.
Then pick f3-2-governing-autonomy-rather-than-approving:
| agent | install just for this agent | start it |
|---|---|---|
| Claude Code | python3 scripts/install_skills.py --tool claude --lessons F3.2 | type /f3-2-governing-autonomy-rather-than-approving |
| GitHub Copilot (VS Code) | python3 scripts/install_skills.py --tool copilot --lessons F3.2 | type /f3-2-governing-autonomy-rather-than-approving in Chat |
| Cursor | python3 scripts/install_skills.py --tool cursor --lessons F3.2 | type / in Agent chat and search for it |
| Codex | python3 scripts/install_skills.py --tool agents --lessons F3.2 | type $f3-2-governing-autonomy-rather-than-approving, or run /skills |
No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.
python3 scripts/lesson.py F3.2Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.
python3 scripts/checkpoint.py --at F3.2 --out work/cybertravels
python3 scripts/checkpoint.py --at F3.2 --diffOn Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.
Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.
Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.
If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.
The four rungs print with their governance and budgets. The doc-summariser is approved at L1; triage-bot is refused at L2 for ungated writers; the refund agent is refused at L2.5 for exceeding the budget and approved once gated. Tool approval scales to 240 reviews a month at 120 agents against 5 hours for rung governance, and a committee-gated L1 leaves roughly 108 shadow assets.
Write your own per-rung policy in four lines and check what L1 costs a team today. If registering a read-only copilot needs an approval, your inventory is already incomplete and you cannot see by how much.
Skill source: skills/programme/autonomy-ladder-decisions