Skip to content

Usage view: kimi is invisible, Claude's tokens are ~1000x under, and the breakdown should go past model #556

Closed

Description

@currentbits

Follow-up to #152, which shipped the current view (range presets, cost/tokens toggle, daily bars, provider rows, model table). Two data defects make the numbers untrustworthy today, and the view itself has room to become the surface that actually answers "where did the money go".

1. Providers that report no usage vanish entirely

electron/store.js:1359:

if (costUsd === 0 && inputTokens === 0 && outputTokens === 0) return;

Any turn that reports neither cost nor tokens is dropped on the floor, so the provider never appears in the view at all. Read-only scan of the live coder-store.json today:

claude/claude-opus-5   $229.34   in 3,001       out 438,289     turns 46
grok/grok-4.6          $69.67    in 23,615,038  out 2,973,407   turns 141
--- threads by provider ---
{ claude: 167, grok: 231, kimi: 41 }

41 kimi threads, zero kimi rows. The runner does call recordUsage on the kimi path (electron/runner.js:3629) — and its own comment two lines up says kimi "reports billable in/out at best, never a full prompt" (#317) — so the turns arrive with zeroes and get discarded.

Silently dropping is the wrong default. The view should show kimi · 41 turns · usage not reported rather than pretend the provider does not exist, because "this provider costs nothing" and "this provider does not tell us what it costs" are completely different facts and only one of them is good news. Record the turn whenever a provider is named; keep zeros as zeros and render them as unreported, not as $0.00.

2. Claude's token counts are undercounted by ~1000x

Same scan: 3,001 input tokens across 46 Claude Opus turns. That is not plausible — it is one prompt's worth spread over a month of work.

electron/runner.js:2768 records usage.input_tokens only. Under prompt caching, input_tokens counts just the uncached remainder; the real prompt is input_tokens + cache_read_input_tokens + cache_creation_input_tokens. The codebase already knows this — claudeContextTokens() at runner.js:85-103 sums exactly those three fields for the context ring, with a comment explaining that omitting them "reads as ~0% then jumps (#317)". The usage rollup never got the same fix.

Consequence: the Tokens toggle is meaningless across providers. Grok reports full input and shows 23.6M; Claude reports post-cache and shows 3K. Comparing them ranks a cache-heavy provider as ~7,800x cheaper on tokens than it is.

Keeping input_tokens as the billable number is defensible; reporting it as the token count is not. The fix is to record the split rather than pick one:

inputTokens        // uncached, billable at full rate  (today's number)
cachedInputTokens  // cache_read_input_tokens
cacheWriteTokens   // cache_creation_input_tokens
outputTokens

UsageEntry in src/shared/ipc.ts and normalizeUsageByDay need the two new fields, defaulting to 0 so existing store data still loads.

3. What the view should become

Reference (T3 Code's Usage screen, not to be copied 1:1):

  • A single headline figure with an honest caveat — "$12,739.09 * if billed at full API rate" — because almost everyone here is on a subscription and the raw-rate number is a counterfactual, not a bill.
  • Provider rows with share bars underneath the headline.
  • One overlaid daily area chart per provider with a legend, instead of a single merged bar per day.
  • A stat strip: processed tokens · cached input · uncached input · output · cache savings ("6.8x the raw token cost").
  • Breakdown table with a Model / Day toggle, sorted by cost, with share and tokens columns.

What Solenta should do differently, given it knows things a single-vendor tool cannot:

  1. Break down by project and thread, not just model. Make the table's toggle Model · Day · Project · Thread. This is the multi-provider tool's actual advantage, and it is the direct input to Per-client cost attribution with invoice-ready export #403's per-client attribution and Work receipts: per-thread claim-vs-proven split and plan-quota cost #339's work receipts — that issue wants exactly this rollup with a client label on top.
  2. A wasted-spend column. Solenta knows which runs failed, were stopped, or were superseded by a rerun. "$X on runs that ended in failed/stopped" is a number nobody else can show, and it is the one most likely to change behaviour. Ties to Thrash detector: interrupt same-error retry loops with rollback and a provider switch #391 (thrash detector) and Failure taxonomy: classify before retry, quarantine flakes #297 (failure taxonomy).
  3. Subscription reality first. Where the CLI reports plan quota (Claude's usage ring, plan usage for the period), show quota consumed next to the counterfactual dollar figure. Dollars are the wrong unit for the provider you cannot exceed but can exhaust.
  4. Be explicit about unreported providers rather than letting them read as free — the whole point of item 1.
  5. Cache savings only where it is real. The 6.8x line is meaningful for Claude and meaningless for a provider that reports no cache fields; render it per-provider or not at all, never as a single global multiplier.

Deliberately not adopting: the exact typography and colour treatment, and the 24h range preset (the current 7/30/90 is fine; note the 90-day view will look sparse for a while — SPEND_RETENTION_DAYS = 90 is correct, there is simply only three days of history in the store so far).

Tests

  • test/usage.test.ts (or wherever summarizeUsage is covered): a provider whose entries are all-zero still appears in providers with its turn count, and is flagged unreported.
  • electron/test/store.test.js: recordUsage with {provider:"kimi", costUsd:0, inputTokens:0, outputTokens:0} persists a row with turns: 1.
  • A rollup case with cachedInputTokens/cacheWriteTokens set, asserting processed-vs-billable totals differ and old entries without the fields still load.

Related

#152 (the view this extends), #317 (the cache-token issue this is the sibling of), #403, #339, #401, #405, #323.

Activity

  1. added
    bugSomething isn't working
    enhancementNew feature or request
    and removed on Aug 19, 2026
  2. added and removed on Aug 20, 2026
  3. currentbits commented on Aug 20, 2026

    @currentbits
    OwnerAuthor

    Shipped on main (78fe3bf); verified in the tree. Moving to plan:done.

  4. added a commit that references this issue on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requestplan:donePlanboard status

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions