Skip to content

Fix context ring accuracy: count cache tokens, document Claude windows, add a per-thread breakdown - #447

Merged
currentbits merged 6 commits into
mainfrom
coder/context-visibility-accurate-per-thread-c-a8891f
Aug 18, 2026
Merged

currentbits merged 6 commits into
mainfrom
coder/context-visibility-accurate-per-thread-c-a8891f

Conversation

@currentbits

Copy link
Copy Markdown
Owner

Closes #317.

The context ring was reading ~0% on every Claude thread and never rendering at all on most of them. Both causes were measurement bugs, not display bugs.

The ring was dividing by the wrong numerator

Claude's usage.input_tokens counts only uncached input. With prompt caching on — which is always, in practice — a real result event from the CLI looks like this:

{"input_tokens": 10, "cache_creation_input_tokens": 10203,
 "cache_read_input_tokens": 17748, "output_tokens": 175}

runner.js computed context fill as input + output, so it read 185 tokens against a true prompt of 28,136 — a 152x under-read. A mid-session thread pulled from a real transcript showed input_tokens: 2 against cache_read_input_tokens: 154815. That is exactly the "meter stuck near zero, then jumps to 100% at compaction" failure the issue describes, and it was in our own code rather than a provider quirk.

contextTokens now sums input + cache-creation + cache-read + output. The billable inputTokens/outputTokens counters and recordUsage are deliberately left alone — cache reads bill differently, so folding them into the cost path would trade one wrong number for another. grok runs the same claude-stream path and inherits the fix.

The ring never rendered for Claude at all

Found by scanning the live coder-store.json rather than by a test: every claude entry in the provider catalog was missing contextTokens, so contextWindowFor returned null and the ring hid itself. 161 stored usage rows had a measured numerator and no denominator to divide by. Added the documented windows — 1M for Fable 5 / Opus 5 / Sonnet 5, 200k for Haiku 4.5.

Per-provider accuracy

  • codex — token_count.info.total_token_usage is session-cumulative but was being added as if it were a per-turn delta, inflating totals on every event. Now uses last_token_usage for the delta and treats totals-only events as a snapshot to replace rather than add. info.model_context_window flows into the new SessionUsage.contextWindow and takes precedence over the static catalog, which goes stale when a thread changes model.
  • kimi — reports too little to reconstruct a full prompt, so contextTokens stays unset and the ring hides.

The rule applied throughout: if a provider cannot be measured accurately, show no number. An inaccurate meter is worse than none.

Breakdown UI

New pure src/contextBreakdown.ts splits the measured total into tool output, transcript, and a remainder attributed to the system prompt and tool definitions. Segments are estimated at chars/4 with no tokenizer dependency; the provider's measured total stays authoritative, segments never sum above it, and the popover labels itself as an estimate rather than implying precision it doesn't have. Hovering or focusing the ring opens it; fill at or above 85% turns the ring amber and warns that compaction is close.

Testing

Renderer 926 pass / 0 fail, electron 1166 pass / 1 fail — that one failure is the pre-existing web-integration reconnect test, which fails on main too. tsc --noEmit clean.

The electron and renderer halves were built in parallel, each against a mock of the other, so test/contextSeam.test.ts covers the join they couldn't: a verbatim result.usage payload captured from the live CLI, driven through the real ring and breakdown functions. It pins the regression directly — the same turn reads 14% now and 0% under the old formula.

Not included

The account-level rate-limit and reset-time popover from the issue comments. That needs provider quota endpoints (Claude OAuth usage, ChatGPT wham/usage), which is a different data source from per-thread token accounting and belongs in its own change.

Also filed #441 separately: electron/providers.js contains a literal NUL byte, which makes grep silently return zero matches on that file. Left unfixed here to keep this diff scoped.

  • PR created by the Claude Code agent

Willem and others added 6 commits August 18, 2026 08:56
Hover/focus the header ring for an estimated split of the last
turn (tool output, transcript, system+tool defs). Prefer the
CLI-reported window over the static catalog, and warn in amber
when fill crosses 85%.
Claude's input_tokens excludes cache_read/cache_creation, so the ring
read ~0% then jumped. Sum those fields into contextTokens only; leave
the billable in/out counters alone. Codex token_count.last_token_usage
is the turn delta and model_context_window is the live window; never
add the cumulative total. Kimi cannot be measured, so contextTokens
stays unset.
Every claude modelInfo entry lacked contextTokens, so contextWindowFor
returned null and the ring never rendered for claude threads — 161
usage rows in the live store all had a measured contextTokens and no
denominator to divide by. Values from Anthropic model docs: 1M for
Fable 5 / Opus 5 / Sonnet 5, 200k for Haiku 4.5.

Refs #317

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The runner slice and the renderer slice were built in parallel against
mocks of each other. This drives real provider payloads — a verbatim
claude result.usage captured from the live CLI — through the actual
ring and breakdown functions, and pins the regression: the same turn
reads 14% now and 0% under the pre-fix input+output formula.

Refs #317

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@currentbits
currentbits merged commit 0f429b3 into main Aug 18, 2026
1 check failed
@currentbits
currentbits deleted the coder/context-visibility-accurate-per-thread-c-a8891f branch October 3, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Context visibility: accurate per-thread context breakdown ('what is eating my context')

1 participant