Before a dollar is spent
Capacity checks, usage windows, the refusals that happen before a run spawns, and an honest statement of which ceiling is enforced by whom.
Two numbers ride on every dispatch: a prediction of what the work should cost, and a ceiling derived from it at twice that figure. What each of those is, who declares them, and what happens when a run reaches the ceiling is a topic of its own and is not repeated here.
This page is everything else that happens before the money moves — the capacity checks, the probes, and the refusals. They exist because the cheapest run is the one that never started, and the second cheapest is the one that was stopped a second before it did.
Which ceiling is actually enforced, and by whom
⚠ This is stated per backend, because it differs per backend, and a budget holder who assumes otherwise will be surprised exactly once.
| What holds the run | |
|---|---|
| Claude Code — today the only backend whose vendor CLI accepts a spend limit | The ceiling is composed onto the vendor's own budget option. A run that reaches it ends, under its own named status (budget-exhausted) — distinct from a vendor error, and distinct from a provider limit. |
| Every other backend — Codex, Droid, Qwen Code, plain commands, OpenAI-compatible endpoints | ⛔ There is no pre-enforcement. The ceiling is recorded on the run and nothing stops the run at a dollar figure. On an OpenAI-compatible endpoint the ceiling is compared against the actual after the run and the comparison is written on the record; elsewhere it is a recorded number only. |
| Every backend, unconditionally | The wall clock. Every run carries a deadline and is killed at it. This is the engine's own backstop and it does not depend on any vendor's cooperation. |
The remediation when a ceiling is hit is the same in both cases — re-predict and re-run, or split the work, as a deliberate decision — but the moment you learn differs, and on a backend in the second row you learn afterwards.
Capacity is checked before commitment, as a report
Given an expected cost (required) and optionally one model, this probes every applicable place on this host — a Claude Code or Codex harness — for room to run and reports what it found. It is a report and never a dispatch: there is no way to make it select a winner, and no way to chain it into a run.
⚠ The report is not free. Every probe is a real, billable run through the same door as any other run, booked like one. Two disciplines make the report worth reading:
- A place counts as free only on a positive assertion. A successful recorded probe with its own liveness marker present. ⛔ The absence of a failure is never read as a pass.
- "Could not measure" is a first-class answer, never collapsed into "not free". Those are opposite conclusions and they lead to opposite acts.
Subscription windows are measured, not assumed
A flat-rate plan is not unlimited — it is windowed, with capacity that refills on the plan's own cadence. This reads each seat's own usage and answers in three states: free, exhausted, or unknown, keeping the vendor's own reset wording verbatim rather than paraphrasing it into something that might be wrong. Claude Code and Codex seats are read; a seat of any other backend is reported as unsupported, by name.
The usage read itself costs nothing. ⚠ A Claude Code seat whose read comes back unusable
can cost one small model turn — at most one per seat per 30 minutes — and every row says
whether it did. A running server refreshes these readings on its own every
fleet.usage_refresh_minutes (15 by default, 0 disables), and that refresh never buys a turn.
Every reading is persisted, and an ordinary run that hits a provider limit extinguishes a stale "free" verdict at no extra cost — the fleet's own work keeps the report honest between probes.
⚠ A window nobody could put a number on is marked as unread on its own row, rather than left to read as a window with room. An absent percentage and a low percentage are opposite answers, and only the mark tells them apart.
The report itself blocks nothing. What the readings do at dispatch time is the next section.
The pre-flight gate: what is wired, and what is not
A real dispatch can opt in to a pre-flight that runs the capacity probe and refuses the spawn when no probed place is free. The refusal enumerates every place it probed with that place's own reason, so the answer is actionable rather than a verdict. The pre-flight's own cost is stated on the option itself — nothing about capacity checking is silent, including its price.
The option is --headroom-preflight on saphan run. ⚠ It costs roughly six cents per
probed place, plus that probe's own wall clock, which is why it is opt-in.
Independently of that, a prediction at or above a configurable threshold
(headroom.warn_usd, five dollars by default) flags the dispatch for visibility. A
warning, never a block.
A seat's own latest usage reading is read at every dispatch onto a seat. When that reading says the seat's window is spent and another seat on the same backend that would take the same role currently reads room, the dispatch is refused before the seat is reserved, naming the other seat and the reset time. When no other seat has room, the dispatch is admitted with a note on the record that it may die at the provider limit — capacity is not idled for want of an alternative. A seat with no reading at all is admitted with a note: absence of a measurement is not a refusal.
⚠ A spent weekly window is a fact about the subscription, not the seat. A seat funded by
vendor credits reads identically and still runs. For that case saphan run --force-usage-reading overrides the refusal as an owner act, and the refusal it overrode is
written onto the run's own record.
Two refusals that happen before the spawn
A dead session is refused before it burns capacity. Before casting work onto a Claude Code seat, that seat's credential is checked with the vendor's own free usage read — no model turn, and a good answer is reused for at most two minutes — and the spawn is refused if the session is already dead. A named refusal before the money moves, rather than a vendor error after it.
A step that asks for a model it does not need is refused outright. Asked through
saphan route <step> --request-client, a step your routing policy places on the
deterministic tier is refused, with its own exit code (3), and the refusal is booked in the
ledger at zero cost. ⚠ This is a separate check you run, not a gate inside saphan run:
a dispatch does not consult the routing policy today.
Declaring who pays for a seat
A seat is one identity on one machine slot, with its own profile and its own session — the
unit by which several vendor accounts are driven in parallel from one machine. Each seat
carries a billing class, set by an owner act on the seat's own signed row
(saphan machine seat … --billing-class <class>):
| Class | Meaning |
|---|---|
subscription | Flat-rate capacity somebody already paid for; the marginal run inside the window is effectively pre-paid |
metered | Invoiced per token, from the first token |
local-energy | A model you host yourself: no vendor bills anything, and what is scarce is time and the host's capacity |
⚠ An untagged seat reads back as unknown — treat unknown exactly like metered. The
conservative reading is the safe one: assuming a seat is free is how an unbudgeted invoice
arrives. ⚠ The class is a declaration on the seat and appears in reports; no dispatch check
reads it today, so it does not by itself stop or route anything.
The economic consequence is the point of the feature. When most work runs on flat-rate accounts, metered APIs become a deliberate overflow rather than the default meter — and a model behind an endpoint you run yourself extends the ladder down to electricity, with ordinary deterministic tooling closing it at zero.
Declared zeros live in one place
A zero that means "this genuinely has no marginal cost" and a zero that means "nobody knows" must never look the same in a report. So the rate for a backend and model — or the declaration that there is no marginal rate at all — is written by a human into the pricing registry, and the engine only ever reads it.
- A declared zero books a real, countable zero, with the declaration named on the row.
- A model with no declaration books
unknown, and is named in every rollup until somebody declares it. - One structural exception exists so an unregistered local model cannot masquerade as vendor spend: a run against a loopback endpoint with no declaration is classed as electricity from the observed fact of the loopback itself.
What is not built
- ⚠ Ceilings are per run only. There is no envelope above the run — no stream-level or portfolio-level budget with its own threshold, and no monthly number burned down over time. Tracking a larger envelope is done by hand against the reads.
- ⚠ Nothing warns you mid-run. A run's spend crossing a fraction of its ceiling raises no notification; you read the number at close.
Next
- One yardstick across vendors — why running several backends is a cost instrument and not just a preference.
- What a ledger row knows — where all of this lands afterwards.
The dimensions of cost
The question of what agent development costs has no single answer — it has axes. Which axes the record can answer along, and which ones need an export.
One yardstick across many vendors
Running several backends is a cost instrument, not a preference — because the same order can be given to two actors and judged by the same tests.