What a ledger row knows
Why agent spend needs its own bookkeeping, what is recorded for every run, and why a run nobody can price is reported as unknown instead of as zero.
Agent development has a real price per unit of work, and in most organisations nobody can name it. The reason is not carelessness. It is that agent spend does not behave like cloud spend or like payroll, so the instruments built for those two report it badly.
Three properties that break ordinary cost reporting
The unit of cost is a run, and runs vary wildly. The same class of task can cost cents or tens of dollars depending on the model, the size of the context, how many retries it took, and how well the work was specified before it started. An average over that spread tells you almost nothing; only per-run bookkeeping, with a prediction to compare against, does.
The meter is not the work. Tokens burn on failed attempts, on network flaps, on runs that produce no output at all, and on rework after a defect. A cost system that sums vendor invoices cannot tell work delivered on the first pass from the same work paid for three times — and that distinction, not the token price, is where the money goes.
Several billing worlds coexist. A subscription seat is flat-rate: inside the plan's window, the marginal run is effectively pre-paid. An API key is metered: every token invoiced. A model you run yourself costs electricity, not dollars per token. A fleet that treats those three as the same thing over-manages one and ignores another.
⇒ So the record keeps its own books, per run, and normalises what happened into one currency column at the moment the row is written.
What is recorded for every run
| Recorded | Why a budget holder cares |
|---|---|
| Tokens in, out, and cached | Cached tokens are billed differently from fresh ones, so they are counted separately rather than folded in |
| Normalised cost in one currency column | Comparable across vendors and billing models without a spreadsheet step |
| The actor and the session that spent it | "Who spent this" is a query, not an investigation |
| The step within the piece of work | Separates the work itself from the coordination around it |
| The model and the backend that actually executed | A per-model cost report means nothing if the executing model can differ from the one named |
| The stream of work, and the timing | Lets one merged change carry a total |
| The outcome | Distinguishes money spent on delivery from money spent on failure |
Five kinds of row, because not all spend is the same act
| Kind | What it books |
|---|---|
| A run | Agent work |
| A call | A model call made outside a run |
| A deterministic act | Booked at zero — deliberately recorded, so the share of work done by ordinary code is visible rather than merely free |
| A refusal | Near-zero, carrying the reason the act was refused |
| An authorised exception | An override somebody explicitly allowed, on the books by name |
The third and fourth rows are the ones most cost tools omit, and they are the ones that make the account readable. A ledger that records only what was charged cannot show you the work that cost nothing, and cannot show you what the guardrails stopped.
Outcomes are honest
Only a run that actually ended well is booked as a success. Every named failure — a run stopped by its own spending ceiling, a run ended by a provider limit, a vendor error, an authentication failure — books an error outcome, under its own name.
⚠ The distinction is operational, not cosmetic. A ceiling hit sends you to your own budget table; a provider limit sends you to capacity; a vendor error sends you to the vendor. One undifferentiated "failed" sends you to all three, in the wrong order. You cannot manage a cost you cannot name.
One currency column, whatever the source
Four different kinds of evidence arrive about what a run cost: a vendor's own reported figure, a cost header returned by a proxy, a token count priced against a declared rate, and a duration priced per hour for work that ran on a machine with a stated price. All four land in the same column, and each row names exactly which declaration priced it — so a figure can be re-derived later instead of taken on trust.
⛔ A rate is never invented. Where no declaration applies, the row does not receive a guessed price.
Unknown is a first-class answer
A run the engine cannot price books the honest string unknown. The cost reads report
unknown-cost runs as a named count with the models listed, excluded from the total and
never counted as zero.
This matters more than it sounds. A blind spot silently priced at zero deflates your spend and looks like good news; a blind spot that leaves a trace in the rollup is an item on somebody's list. A zero is declared, never guessed — a genuine zero, such as work covered by a subscription or run on your own hardware, is a human declaration with the declaration's reference pinned on the row.
Cost classes keep the billing worlds apart
Every row carries a class, assigned from the pricing registry's own declaration:
- vendor spend — metered, invoiced per token;
- subscription-covered — run on flat-rate capacity somebody already paid for;
- electricity — a model you host yourself;
- unclassified — nothing has declared this one yet.
So "what did the API bill us", "what ran on pre-paid capacity", and "what cost electricity" are three columns of the same day rather than one blended number. The console's ledger screen draws exactly that comparison, day by day.
The read surface
saphan cost names a stream and reports every run in it — what it actually cost, what it
was quoted at when a prediction was recorded, and the difference between the two when both
are known — plus totals across continuations. Because a stream of work closes into exactly
one observed merge, that rollup is the cost of a merged change, including the review and
coordination runs that shipped it.
The export exists so that any slice the built-in reads do not pre-compute is one import away in the tooling your finance team already uses.
What this does not do yet, stated plainly
- ⚠ There are no built-in time-window or group-by reads. Totals are per stream, per session and per workspace; anything else — per actor, per repository, per month, across a portfolio — is the export aggregated in your own reporting tool.
- ⚠ No reconciliation surface reports runs the ledger never saw. The books are append-only and referenced, but their completeness is not itself checkable by a command today.
- ⛔ A cost figure with no reference does not enter the books at all. Every recorded prediction-and-actual pair carries the run or commit it came from, and every priced row pins the version of the pricing table it was priced under. A number nobody can trace is a rumour, and it is kept out rather than caveated.
Next
- The dimensions of cost — the axes this record can answer along, and the one answer a finance reader usually wants.
- Before a dollar is spent — the checks that run ahead of a dispatch, and the ones that do not.
Budgeting a run: the quote and the cap
What a cost estimate is, what a budget is, who decides them and on what basis, what happens when a run reaches the ceiling, and how quotes get better over time.
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.