cap-mode budget auto-pause a run the moment it crosses 100%, and resolve a risky-delegation approval. Along the way you will see how the always-on circuit breakers and depth/fan-out caps already protect you for free.
It composes existing surfaces. The mechanics live in Governance; the human-facing panel is Use the Governance dashboard; the raw shapes are in the Governance API reference. This guide ties them into one operator task and links out for detail rather than restating it.
If you have not deployed a team yet, do Deploy your first team first. This guide assumes you have a team whose agents incur cost; governance only does something once runs produce
cost events.What you are setting up
Clawboo has four governance mechanisms, and they sit at two different layers:
Every one of these keys on a typed
RuntimeEvent: a cost event’s dollar delta, a tool-call/tool-result pair, a typed error code, never on the model’s rendered prose. That is the same no-prose-as-control-signal rule the whole orchestration layer follows. You cannot turn governance off; you can only opt into harder enforcement (a cap budget, a per-run cost ceiling). Full reasoning in Governance § design rationale.
Prerequisites
- A running dashboard. The Governance panel polls
/api/governance/budgetsevery 5 seconds. See Installation. - The Governance nav view open (
GovernancePanel). There is no flag to enable it. - Your resolved API port for any
curlexamples; the default is18790, with auto-fallback through18809. See Deployment § ports. - A team id (or agent / root-task id) to scope the budget to. Grab a team id from
GET /api/teamsor the team header.
Steps
1. Confirm the default posture (nothing pauses yet)
Out of the box no budget row exists, so the kill-switch enforces nothing. Confirm it:{ "budgets": [] }. An empty list means uncapped, not zero; “uncapped” is the absence of a budget row, never a $0 limit. You still get the always-on tracking, the audit log, and the circuit breakers for free; budgets are the one piece you opt into.
2. Create a budget in cap mode
A budget is a USD cap on a scope. There are four:
The mode is the difference between watching a budget and enforcing one:
warn(the shipped default). Records spend and emits a warning at the 80% and 100% crossings, but its status never readspaused, so the kill-switch leaves the run alone.cap. Identical tracking, but the 100% crossing persistsstatus = 'paused'and the kill-switch aborts the live run.
team), type the scope id (your team id), enter the limit in dollars, pick hard cap, and click Set budget. The form converts dollars to cents (Math.round(v * 100)) and POSTs { scope, scopeId, limitUsdCents, mode }.
The equivalent REST call: a $5.00 hard cap on a team (500 cents):
spent 0 / active. Note two validation rules baked into the schema:
limitUsdCents must be a positive integer (z.number().int().positive()). A cap of $0 is rejected with 400 { "error": "invalid body" }. If you omit mode, the budget defaults to warn; you must pass "mode":"cap" to get the auto-pause. Full body shape: POST /api/governance/budgets.3. Watch the kill-switch pause at the cap
Now dispatch work that spends past the cap (run a task on the team, or let the team chat drive a delegation). Spend is recorded inside the executor’s cost loop, not over REST: on everycost event the runner records the dollar delta against all three concrete scopes atomically: agent, the mission (the root task of the delegation tree), and team.
recordSpend is an atomic read-modify-write under BEGIN IMMEDIATE (the board’s contention recipe), so two concurrent cost events can never lose an update. The moment a cap-mode scope’s recorded spend crosses 100%, the runner sets stopForBudget, aborts the live run, and runs one teardown:
- An
auto_pauseentry lands in the governance audit log (eventType: 'budget'). - A system comment is added to the board task: “Auto-paused:
<team>budget reached. Raise the cap (or resume) to continue.” - The execution is completed as
cancelledwith errorbudget_paused:<scope>, and anexecution_completedobservability event fires. - The task is released to
todo, retryable once you raise the cap or resume.
paused. In the dashboard its status pill turns red; over REST:
The runner double-guards this: the DB layer clamps a
warn budget’s status so it can never read paused, and the kill-switch additionally checks mode === 'cap' explicitly. A warn budget can never auto-pause even if the DB clamp regressed.- Sub-cent carry. A cost event can be a fraction of a cent. Spend accumulates in micro-cents (
MICRO_CENTS_PER_CENT = 10000) so repeated tiny amounts are not floored to zero; the displayedspentUsdCentsisfloor(spentMicroCents / 10000). - Estimated cost for runtimes without USD. A runtime that reports tokens but no dollar figure (Codex, Hermes, an unpinned native model) emits
costUsd: null; the runner estimates spend from exact token usage × the model rate so the cap still engages. A realcostUsd(Claude Code, a pinned native model) is used as-is.
4. Resume the paused scope (raise the cap, don’t just un-pause)
Apaused budget shows a Resume button. Clicking it forces the scope back to active, but a bare resume of a scope whose spend already meets its limit re-pauses on the very next cost event.
Two ways to make progress:
- Raise the cap. Re-
POSTthe same/api/governance/budgetsroute with a higherlimitUsdCents(the dashboard’s per-row Set cap button). Re-setting the limit recomputes status from the existing spend, so raising the cap above current spend un-pauses the scope. - Resume with grace. The resume route takes an optional
graceUsdCentsbody that raises the cap tospent + gracein one call:
POST .../resume.
5. Understand the circuit breakers (no setup needed)
The breakers are a deterministic, cross-runtime backstop for a run that is going nowhere, distinct from the budget kill-switch (which stops on dollars) and from a runtime’s own max-turns limit. They halt a run that burns turns or tokens making no progress or repeating a failing call, before the dollar ceiling is reached. The breaker is a pure stateful reducer (stepBreaker) over run-local state; it does no I/O and reads no wall clock.
There are five trip reasons, all with conservative defaults a healthy run never trips:
When a breaker trips, the teardown mirrors the budget teardown exactly: a
circuit_break audit entry, a [stopped: <reason>] … Released to todo for re-planning. board comment so the leader can re-plan, a cancelled execution with error circuit_broken:<reason>, and a release to todo. The worktree is left intact, so the handoff stays writable and a retry resumes from clean state.
You can override the breaker thresholds per run via the breakerConfig field on POST /api/runtimes/:id/run (validated by a zod schema; each field optional, falling back to the conservative default). A per-team or per-agent override table is a noted future seam. Full nuance, why no-progress only counts failures, why token-velocity needs two cost events, is in Governance § the circuit breakers.
6. Read the enforced-in-code caps
The Caps (enforced in code) section of the dashboard is informational; these are stateless predicates enforced at the orchestrator boundary, refusing a delegation before it becomes a board task and a run. They are not editable from the UI.- The depth cap is the single-reduce-point rule: a leader delegates to a specialist who can delegate once more, and no further. The orchestrator computes depth from the board’s
parent_task_idancestor chain, not from a prompt, and refuses a delegation that would exceed it (the runner refuses with reasontoo_deep), leaving a system comment and a reflection telling the delegator to handle the work directly. - The fan-out cap counts the delegations spawned in one turn. At the max, the overflow is dropped with a comment naming how many were not started.
- The cost cap (
maxNodeCents) is a per-run cent ceiling. Crossing it setsstopForBudget = 'node', the same teardown as a budget auto-pause. It accumulates the same estimated/real cents the budget ledger sees.
cap_hit.
7. Resolve a risky-delegation approval
Some delegations should not run until a human signs off, a destructive or external action a leader is about to hand to a specialist. The orchestrator gates these on the leader’s approval queue. A client-side heuristic decides which delegations are risky (matching obviously destructive or external verbs likedelete, deploy, publish, rm -rf, prod, secret, force-push).
When a risky delegation fires, it calls the delegation-approval endpoint, which reuses the existing DB-mediated tool_call_approvals handshake:
- Sticky scope. If the leader has previously resolved an
allow_alwaysfor this scope key (delegate:<kind>), the prompt is skipped and the approval returns immediately. - Otherwise, a pending approval is created and the handler blocks on a poll loop until the leader resolves it via the Approvals UI, or until the TTL or the waiter deadline expires.
- The resolution decides.
allow_onceorallow_alwayslets the delegation proceed; anything else (deny,expired,timeout) skips it, leaving a system comment and a reflection so the leader can revise or reassign.
ToolApprovalQueue as the Approvals panel, so there is one resolve path). Each pending row shows the tool name, an expiry countdown, the reason, and an args summary, with Allow Once / Always / Deny. These POST to /api/tools/approvals/:id/resolve.
The client-side delegation-approval call fails closed. If the approval endpoint is unreachable, the request maps to
timeout, a non-approving resolution, never allow_once. The whole point of the gate is human sign-off for a destructive action, so an unreachable endpoint must not auto-approve. Only risky delegations reach this path, so the strictness can never deadlock ordinary team work.expiresAt, the waiter has its own deadline, and a durable TTL reaper atomically expires abandoned pending approvals on an interval (and unblocks the linked board task, unless a non-promotable verification verdict is what holds it blocked). The TTL is configurable via CLAWBOO_APPROVAL_TTL_MS; see environment variables.
Verify it worked
- A new or raised budget appears (or updates) in the Budgets list within the 5-second poll. Confirm over REST with
curl http://localhost:18790/api/governance/budgets. - A
cap-mode scope that crossed 100% reads"status": "paused"and shows a red status pill plus the auto-pause audit entry and thebudget_paused:<scope>board comment. - A resumed scope flips its status back to
active. If it showswill re-pause, raise the cap. - A resolved approval disappears from the Approval queue on the next 3-second refetch, and the gated delegation proceeds (or is skipped on a deny).
- The audit log carries the trail: filter
GET /api/governance/audit?eventType=budgetfor the budget events,eventType=cap_hitfor a cap hit,eventType=circuit_breakfor a tripped breaker.
Troubleshooting
Setting a
$0 budget does nothing. The POST is rejected with 400 invalid body; a cap must be a positive cent amount. “Uncapped” is the absence of a budget row, not a $0 limit. There is no delete route; to make a scope uncapped again, leave it without a row.The OpenClaw path records spend at the terminal, so it cannot auto-abort mid-run. OpenClaw emits no incremental cost events (only a final cost on
done), so there is no per-event crossing signal for the budget kill-switch to fire on during a run. Its budgets are instead enforced by a pre-flight gate on the next dispatch (the runner refuses a dispatch with reason budget_paused before the claim when a relevant cap-paused budget already exists), not a mid-run kill. This is a documented asymmetry with runtimes that stream per-turn cost (like the native runtime). The tenant scope and tenantId column are a dormant future seam; Clawboo runs as a single implicit tenant in v0.3.1.Related
- Governance: budgets, the kill-switch, circuit breakers, caps, and approvals explained
- Use the Governance dashboard: the panel, step by step
- Governance API reference: full request/response shapes for budgets, resume, audit, and delegation approval
- Verification: the builder-≠-judge gate that makes “done” mean verified
- Cost and budgets: the cost dashboard alongside budgets
- Production defaults: why track-and-warn is the default posture
- Approvals: the same tool-approval queue surfaced standalone
- Build a multi-runtime team: the team this governance protects
See also
- Worktrees and handoff: the isolation boundary governance complements
- Observability: the event log and audit trail governance writes into
- Environment variables:
CLAWBOO_APPROVAL_TTL_MSand the reaper interval - Glossary: canonical term definitions