Why our dashboard shows a dash instead of $0.00
A zero is a claim and an absence is an absence. Every dash on the dashboard marks a place where a plausible number would have been a lie — the specific cases, and the four provenance labels.
There is a dash on our dashboard where a dollar figure could be, and it is the most argued-over character in the product. A dash looks unfinished. A $0.00 looks professional. Every instinct — design, sales, ego — pushes toward printing the number. Here is why the dash wins, case by concrete case.
Zero is a claim. Null is an absence. They must not share a glyph.
$0.00 asserts: this call cost nothing. When the truth is "we could not determine the cost", printing zero replaces a fact you do not have with a fact that is false — and falsely low, which for a cost product is the worse direction: your dashboard under-reports, the invoice arrives, and the invoice wins. Once a customer catches one confident wrong number, every other number on the screen loses its authority — trust in a metrics product does not degrade gracefully.
So the rule runs the whole depth of the stack, because it only takes one layer to break it. A call on a model with no verified rate gets cost_usd = NULL in the database, never zero. The aggregates must then not launder it: coalesce(sum(cost_usd), 0) quietly turns absences back into zeros, which is exactly the defect we shipped — Postgres skips NULLs in sum(), so unpriced calls simply vanished from totals, and real spend rendered as a confident $0.00. And the UI must render the honest NULL as a dash with a reason, not as an empty cell someone will read as zero anyway.
The cases where a plausible number was available, and refused
Each dash below marks a place where we could print something defensible-sounding.
The unknown model. A model shipped yesterday has no verified rate. The tempting substitutes — a sibling model's rate, a family average, the old rate of the model it replaced — are all guesses wearing precision. We once had a substring fallback that priced every current Opus at a retired Opus's 3x-higher rate; the lesson is tattooed on the rate table now: no default, no sibling, no substring. Dash, plus "we do not have a verified rate for this model".
The write we cannot attribute. Anthropic bills 5-minute cache writes at 1.25x and 1-hour writes at 2x, and sometimes reports the total without the split. When a 1h write was genuinely possible and the breakdown is absent, either assumption is a potential 37.5% error in a known direction. Dash — and a spike in those dashes is itself a signal that the response shape moved where money is at stake.
The saving that does not exist. One provider's cache charges cached tokens at the full input rate — a real cache, zero discount. "$0.00 saved" there would still be a claim about a discount that does not exist. Where the discount is unpublished or zero, the saving is not zero; it is not a thing, and it renders as nothing.
The total with missing rows. When some calls in a window are unpriced, the honest total is not the sum of the priced ones presented as the sum of everything. Below a threshold, the total states its denominator — measured over N of M calls. Past the threshold, the priced remainder stops being a fair sample of the whole, and the total itself becomes a dash. A number that omits rows without saying so is how averages lie.
The four provenance labels
The dash's positive counterpart: every figure that does render carries where it came from. VERIFIED — read off the vendor's published page by a human, on a recorded date. CONTRACT_DESIGNATED — fixed by contractual terms naming an authoritative source (how some Bedrock rates resolve). INFERRED — derived from a published rule, like "cached input is 10% of input": correct by construction, but a derivation, and labelling it VERIFIED would pass a derivation off as a reading. unverified — lowercase on purpose, because it is not a grade of claim but the absence of one, and it never resolves to a number at all.
Two rules give the labels teeth. An aggregate is labelled by its weakest component — one INFERRED row taints the total, because a chain of provenance is as strong as its worst link. And anything irreversible — invoicing, hard budget enforcement — gates on the strong labels only. VERIFIED and INFERRED must also look different on screen; a label that renders identically to a stronger one is the lie relocated into typography.
"Insufficient data" is a feature
The objection writes itself: customers want numbers, dashes look broken. The observed opposite: the dash is why the numbers get believed. A dashboard that visibly refuses to guess, in the cases where guessing would be easy, is demonstrating the property that makes its other numbers worth acting on — every figure it does show survived a pipeline that prefers silence to plausibility. The dash is the evidence. And it is honest in the useful direction: each dash carries its reason — unpriced model, missing breakdown, unpublished discount — so it is not a shrug but a pointer at exactly what is unknown and what would make it known. The same discipline in the pricing behind the product, and in how the FAQ answers accuracy questions, is one rule applied everywhere: a number you cannot stand behind is not a number, whatever it looks like.
What to do
Find the NULLs in your own metrics pipeline and trace what each becomes by the time it reaches a screen — every coalesce(x, 0), every ?? 0, every chart library defaulting missing points to zero is a place absence becomes claim. Then pick your most-trusted dashboard total and ask what it does when an input row cannot be computed: silently omit, silently zero, or say so. If you cannot answer from the code, the answer is one of the first two.