Repository navigation
feat(framework): a price table for the tools that export no amount #654
Description
Activity
- added a parent issue
on Aug 14, 2026 Promoted from optional to load-bearing by #684.
The decision there is that the reader takes its figures from the files the tools already write, and none of those files carries a dollar amount. Measured 2026-08-20:
- Claude Code's transcript has
message.usagewith all four token counters, and nocostUSD. - Codex's rollouts have
token_countevents, no amount. - OpenCode's SQLite has a
costfield that is0in all 35 rows on disk, and its USD denomination is not established. - Copilot has a
costfield denominated in premium requests, not dollars - it stays pinned at0.33whiletotalNanoAiuranges from 0.99 to 2.95 billion across sessions, so it is requests × multiplier and invariant to consumption.
So this table is now what turns tokens into money for every tool, not just for the ones that export no amount. Two consequences for its shape:
- It should live behind an endpoint rather than inside a released binary. A table shipped in a version goes stale the day a provider changes prices; the same table served does not. spike(framework): does the local collector need to exist, or does the governor expose the endpoint #684 records that argument.
- Every amount it produces is computed, not billed, and the report has to say so on each figure. That marker has to be in the output format from the first version or it never will be.
- Claude Code's transcript has
Moved to the SaaS. Closing here rather than leaving it to look pending.
The rates belong in the governor, and once they do, this repository has no pricing code to write. Its job is upstream of pricing: emit metrics that are complete, identified and not double-counted, and let the service that holds the rates turn them into money.
That also removes the objection #684 raised against a shipped table — a table inside a released binary goes stale the day a provider changes prices. Served, it does not. The argument survives; only the location changes.
What was actually load-bearing in this ticket, and where it went:
- A reader can tell whether a figure was reported by the vendor or computed by us. Already shipped, by a different mechanism: every stored record carries
provenance(exportorlocal-read), andcost_usdis present only when a vendor actually reported one. Nothing local computes an amount, so nothing can be mistaken for one. - No inferred rate, no stale table, no unpriced share silently dropped. All now the governor's constraints, not this repository's.
What replaces it here is the gap that surfaced while planning this: a stored record does not say which tool produced it, and does not carry the skill. See the follow-up ticket, which is the real prerequisite for anything downstream pricing or aggregating these figures.
- A reader can tell whether a figure was reported by the vendor or computed by us. Already shipped, by a different mechanism: every stored record carries
Reopening: this was closed as completed on 2026-08-20 with all four
Done Whenboxes unticked, and nothing in the codebase implements it.Verified on
claude/aidd-telemetry-layer-e403ufat9f88c36d:- No price table exists.
grep -rln "price\|rate_per\|per_million\|pricing" cli/srcreturns nothing. - The two notions this issue is built on are absent:
grep -rn "computed\|reported" cli/src/domain/models/cost-report*.tsreturns nothing, so nothing can be "visually distinct" from anything. - A real run prints the consequence. From the committed fixture,
aidd telemetry report --from 2026-01-01 --to 2026-01-31:
tokens 16,625 85% cache cost amount unknownWhat does exist is the ingestion half, and only through the export route:
cost_usdis on the sink record and in the OTLP attribute allowlist (cli/src/domain/models/telemetry-sink-record.ts:136). So a tool that reports an amount can have it stored. Nothing computes an amount for a tool that reports none, which is this issue's entire subject.The local read path — the one
01-costuses, and the only one that needs no endpoint — has no route to a cost figure at all today.No blame attached: the closing looks like a batch tidy-up of the telemetry milestone rather than a claim of delivery. Flagging it because a closed issue reads as done, and this one's absence is the reason the
costline saysamount unknownon every report.- No price table exists.
Rescoped, and moved down
A product decision taken 2026-08-28 narrows this and drops its priority. Recording it here rather than leaving the issue reading as it did.
What the local layer can honestly produce is not "cost". Multiplying tokens by a public rate gives a value at list price — a measure of intensity. For anyone on a subscription (Claude Max, Copilot seats) that figure is not what they paid, and presenting it as cost produces a wrong number with the confidence of a right one. What someone actually paid depends on their plan, their seats and any negotiated rate: the organisation's contract, which a single machine does not hold and cannot infer.
So the split is not "tokens free, money paid". It is which currency:
- Local: tokens, plus a value at list price, named as such. Withholding the multiplication would be arbitrary — the counters are already there and the rates are public.
- Elsewhere: real cost, where the contract is known.
Two things this issue's scope gets wrong, both measured.
Its outcome says "a cost figure exists for every supported tool". It cannot. Cursor writes no token counter in any file it produces — with no tokens, no table yields an amount. The honest scope is a figure for every tool that counts tokens, and a named "not covered" for the rest. Copilot is its own case:
totalPremiumRequestsis a denomination, not a currency, and converting it needs a plan this layer does not know.And the table must be keyed by (model, counter), not by model. The four counters price differently — cache write above input, cache read a fraction of it, output highest. On a real report from this repository's own fixture (
16,625 tokens — 85% cache) a single blended rate would not be a rounding error; most of the figure would be wrong.What stays in scope, and it is small: a dated table for the two or three models that actually appear (
claude-opus-5,gpt-5.4are the only ones in a real sink today), overridable locally for a negotiated rate,unknownfor any model absent, and the total saying how much of the period is unpriced. No currency conversion, no inferred rate, no summing a reported amount with a computed one.Why it moves down. It opens nothing. The figures that need a key nobody has yet — per person, per task, per epic — are gated on #661 and #649, and those are what a team view rests on. This is a lookup table; it can land any time.
Reopened earlier today because it was closed as completed with all four
Done Whenboxes unticked and nothing implemented: no price table exists incli/src, and neither does the reported-versus-computed distinction it is built on.Closing as Not Planned for the framework.
The subsequent decision record settles the conflict in this issue: a local price table would assert a price, not measure one; it would go stale in a released binary and cannot represent account-specific cost. The framework keeps token and model counters and reports
amount unknown. A chosen destination may apply a dated rate or billing contract.No framework implementation remains.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Projects
- StatusShow more project fieldsDone
Outcome
A cost figure exists for every supported tool, and a reader can always tell whether it was reported by the vendor or computed by us.
Scope
Measured: Claude Code exports cost in USD; Copilot exports a cost attribute with no documented currency; Codex and Cursor export none, and Cursor's cost metric carries no identifier so it cannot be attached to a session even though it exists.
Done When
Relations