#1451·codeburn

Show metered vs subscription spend, and slice by billing route, on every surface (CLI --billing/--route, payload, tab strip)

Author: ulises-cCreated Sep 15, 2026Updated Sep 18, 2026
Labelsenhancementarea: cliarea: desktop

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; --provider is the harness.
  • Reading the (Bedrock) suffix off codeburn models and adding rows by hand.
  • Grepping codeburn export for 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|subscription alongside --route <id> on today / models / sessions / export / audit / compare, applied at the call level so a session that mixed routes contributes only its matching calls; --route direct for the complement; codeburn routes (or models --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> and routeDetails: Array<{ id, label, billing, hasUsage, cost, calls, inputTokens, outputTokens, sessions }> mirroring providers / providerDetails; route?: string on topModels entries 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; a route field 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.