Show metered vs subscription spend, and slice by billing route, on every surface (CLI --billing/--route, payload, tab strip)
The problem, in your words
The question this series exists to answer (#1449): I pay fixed fees for Claude Max and ChatGPT and dip into metered API usage on top — Bedrock, OpenRouter. How much metered spend did I incur this month, through which door, in which tool? No surface can say.
Every surface CodeBurn has slices spend by the coding harness — the tool that produced the session: --provider claude|codex|hermes… on the CLI, the agent tab strip in the macOS menubar (AgentTabStrip.swift) and Windows dock (AgentTabStrip.tsx), providers / providerDetails in the menubar payload, the provider picker in the desktop and web dashboards. None of them can slice by who billed the call.
Those are different axes and a user's invoices follow the second one. One Claude Code session on this machine is billed by Anthropic, the next by AWS (Bedrock), a Hermes session by AWS or by OpenAI's ChatGPT subscription (openai-codex), an OpenCode session by OpenRouter — and the only place any of that shows today is a (Bedrock) suffix on a model-row label (#1448), with nothing to click on and nothing to filter by. "How much did I put on the AWS account this month?" has no answer in the UI.
What you tried
--provider bedrock— rejected by design;--provideris the harness.- Reading the
(Bedrock)suffix offcodeburn modelsand adding rows by hand. - Grepping
codeburn exportfor the label — string-matching a display name, and blind to routes that are only in a provider column (#1450).
Proposed change
Treat the billing route as a first-class dimension next to the harness, on every surface, without replacing the harness view.
Headline: metered vs subscription. Before any per-route detail, one split on the overview and the menubar: This month · metered $X (Bedrock $a · OpenRouter $b · Anthropic API $c) · subscription-covered usage $Y API-equivalent (Claude Max, ChatGPT). The metered figure is the one that varies with behaviour and is the number the series is for; the subscription figure keeps the existing "what this would have cost" semantics and feeds codeburn plan's overage line unchanged.
CLI
--billing metered|subscriptionalongside--route <id>ontoday/models/sessions/export/audit/compare, applied at the call level so a session that mixed routes contributes only its matching calls;--route directfor the complement;codeburn routes(ormodels --by-route) listing spend per route with the harnesses that fed it.- The interactive dashboard's By Model rows become drill-through targets for their route, the same way a project or task row drills today.
Menubar payload (src/menubar-json.ts, contract test tests/menubar-json.test.ts)
current.billing: { metered: number, subscription: number },current.routes: Record<string, number>androuteDetails: Array<{ id, label, billing, hasUsage, cost, calls, inputTokens, outputTokens, sessions }>mirroringproviders/providerDetails;route?: stringontopModelsentries so clients group without parsing the suffix back out. Add-only and optional so older menubar builds keep working.
macOS menubar / Windows dock / desktop / web dash
- A second grouping on the tab strip — "By tool" (today's) / "By billing" — with a Metered / Subscription pair at the top and one tab per active route beneath: AWS Bedrock, OpenRouter, Anthropic API, OpenAI API · Claude Max, ChatGPT, … Each tab reuses the existing period tiles, model list and quota card where the route has one (Bedrock has none; Anthropic and Codex already do via the Capacity Dock adapters).
- Telemetry snapshot (
src/telemetry-snapshot.ts) gains the route split alongside the model × task cross so the anonymous aggregate can show how much usage runs through cloud marketplaces vs first-party.
Order: CLI filter and payload fields first (they are what the desktop surfaces consume), then the tab strip.
Related:
- #725 Part 5 (invoice reconciliation against the Anthropic cost API / OpenRouter key endpoint) needs spend split per billing account before any per-account reconciliation is possible; this is its prerequisite.
- #1318: Overview's all-provider model table reads
topModels, capped at 20. Splitting rows by route consumes slots; aroutefield on entries lets a consumer collapse or expand rather than the cap deciding. - #890's provenance principle (a computed number and a reported one are different things and both must be visible) applies to routes: a "By billing" tile should say whether the route came from the id shape or the provider's own column.
Depends on #1450 (route carried on the call) which depends on #1448.
Source: getagentseal/codeburn