Skip to content
GEOstack
spend-caps observability

Usage dashboards vs. enforced spend caps

Dashboards report spend after it happens. An enforced cap needs a reserve-then-commit ledger — and naive balance checks race under concurrent agents.

GEOstack · · 5 min read

reserve → execute → commit: the cap that can't be raced past

The short answer

A usage dashboard is posterior to spend: it tells you what your agents cost after they cost it. An enforced cap is prior: each action atomically reserves estimated cost against a ledger before executing, commits the actual cost after, and blocks or escalates when the reservation would cross the budget. Dashboards inform the postmortem; ledgers prevent it.

Everyone who got burned had a dashboard

The 2026 incident record is not a story of missing data. Uber could see adoption climb from 32% to 84% of its ~5,000 engineers by February 2026, and could see per-engineer bills of $500–$2,000/month — and still exhausted its full-year agentic-tools budget by April, announcing a $1,500/engineer/month/tool cap only in June. Microsoft could see the same per-engineer range internally and responded in May 2026 by cancelling most Claude Code licenses in Experiences + Devices outright. And the unverified headline case — a reported ~$500M Claude bill in one month, per an AI consultant’s account reported by Axios (company unnamed, figure unconfirmed) — is, if you believe it at all, precisely a story about spend that was measurable and unenforced.

Observability is necessary. It is also, structurally, too late: by the time a human reads the chart, the loop has already billed the next thousand iterations. Agents compress the gap between “anomaly visible” and “budget gone” from weeks to hours. Alert → page → human → decision is a control loop with a human in the hot path; an agent loop runs at machine speed against it and wins.

The naive cap, and why it races

The obvious fix looks like this, and it’s wrong:

// DON'T: check-then-act races under concurrency
const spent = await getSpend(agentId)
if (spent + estimate > budget) throw new BudgetExceeded()
await executeAction()
await recordSpend(agentId, actualCost)

Two agent loops (or two steps of one loop running tools in parallel) read spent at the same moment. Both see headroom. Both pass. Both execute. You’re over budget — by up to N × estimate for N concurrent actions, and the dashboard will faithfully report the overage after it settles. This is a textbook TOCTOU bug, and floating-point dollar arithmetic on top of it adds its own off-by-fractions drift.

The correct shape is reserve-then-commit, the same pattern card networks use (auth hold, then capture):

  1. Reserve: atomically increment a reserved counter by the estimated cost, in integer minor units (cents), in the same operation that checks the limit — a conditional atomic update, not a read followed by a write.
  2. Execute only if the reservation succeeded.
  3. Commit: replace the reservation with actual cost (actuals often differ from estimates — token counts, refund amounts).
  4. Release the reservation if the action fails or times out, so crashed actions don’t leak budget. Expire stale reservations.

budget = committed + reserved is the invariant. The cap can never be crossed because crossing it requires a reservation that, by construction, cannot be granted.

Arc implements this ledger per agent; an action’s cost can be a token estimate, a dollar field from its own parameters (amount on a refund), or a fixed weight:

arc.defineActions({
  issue_refund: {
    defaultDecision: "ask",
    cost: { mode: "field", field: "amount" },  // reserved before, committed after
  },
})
// budget $2,000/day: the action that would cross it is blocked or held
// for approval — and the attempt lands in the hash-chained audit log.

When the cap is hit, behavior matters as much as enforcement: the agent should pause and escalate (Arc holds the action for human approval), not crash, and not retry — a retry loop against a hard cap is a self-inflicted denial of service on your own approvers.

Honest limits of an enforced cap

  • Estimates are estimates. If actuals routinely exceed reservations, you can still drift over within a single in-flight batch. Mitigation: reserve pessimistically, reconcile on commit, and alarm on estimate error — a dashboard job, usefully.
  • A cap bounds spend, not value. Uber’s COO Andrew Macdonald noted token spend doesn’t map cleanly to shipped features. A ledger guarantees you won’t spend more than $2,000/day; it cannot tell you whether $2,000 was worth it.
  • Coverage is what you route. The ledger governs actions that pass through it. Token spend from a model call your gateway sees but Arc doesn’t belongs to the gateway’s budget. Run both; they compose.
  • Throughput. An atomic ledger is a serialization point per agent. At thousands of actions/sec per agent you’ll want sharded budgets with bounded overshoot. Most teams are nowhere near this; measure before architecting around it.

Keep the dashboard. Its correct job is calibration (are estimates accurate? which agents bump their caps?), anomaly narrative, and the CFO conversation — sitting on top of a cap that’s enforced upstream, not substituting for one.

FAQ

Why isn’t a billing alert enough to control AI agent costs? Alerts are posterior and human-gated: by the time someone acts, an agent loop has executed thousands more billable steps. Uber’s spend was fully visible on dashboards and the 2026 budget was still gone by April. An enforced cap blocks the breaching action itself, with no human in the hot path.

How do I stop two concurrent agents from both passing the budget check? Don’t check-then-act. Use an atomic conditional reservation: one operation that both verifies headroom and holds the estimated amount (integer cents), then commits actual cost after execution and releases on failure. Any read-balance-then-write implementation will eventually double-spend under concurrency.

Should the cap be on tokens or dollars? Dollars, at the action layer. Tokens only price the model call; an agent action can move $40,000 while costing a fraction of a cent in tokens. Cap token spend at your gateway too — the two budgets answer different questions.


Arc is the spend-and-action guardrail for AI agents — free Developer tier, no credit card: npm i @geostack/arc. Quickstart →

Written by the GEOstack team. We build Arc — an allow / ask / block guardrail for autonomous agents. Spot something off? Tell us.