fix(core): context tokens from exact totals, not the rounded percentage - #37
Conversation
Claude Code reports `context_window.used_percentage` as a whole number and `total_input_tokens` / `total_output_tokens` exactly. `_context_window_usage` derived the token count from the percentage, so the readout quantised to 1% of the window. On a 200k model that is a 2k error — invisible. On a 1M-context model it is 10k: the bar steps 60.0k → 70.0k → 80.0k and can sit ~10k off the truth. Observed on 1M sessions (real payloads, v3.32.0): reported tokens | bar showed 354,031 | 350.0k 75,354 | 70.0k 63,824 | 60.0k Use the reported totals when present, keep the percentage-derived value as the fallback for payloads that omit them (older Claude Code, some relays). `ctx_pct` is deliberately left as reported, so the percentage and its severity colour stay identical to what Claude Code itself shows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughContext window usage now derives used tokens from valid exact input and output totals, falling back to percentage-based calculation when totals are missing or malformed. Tests cover exact totals, absent totals, malformed totals, and preservation of the reported percentage. ChangesContext token usage
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/claude_statusbar/core.py`:
- Around line 730-743: Update _exact_used_tokens to require both
total_input_tokens and total_output_tokens to be present, valid non-negative
integers before summing; reject missing, empty, malformed, negative, and
fractional values by returning None so callers use the percentage fallback. Add
a regression test covering a payload with one missing total.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ac998013-40f0-4196-b63a-b2fecfc0d1ce
📒 Files selected for processing (2)
src/claude_statusbar/core.pytests/test_core_ctx_pct.py
| def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]: | ||
| """Context tokens as Claude Code reported them, or None when unavailable. | ||
|
|
||
| None means "no usable signal" — an older Claude Code that omits the | ||
| totals, a relay payload that zeroes them, or a malformed value. Callers | ||
| fall back to deriving the count from the percentage. | ||
| """ | ||
| total = 0 | ||
| for key in ('total_input_tokens', 'total_output_tokens'): | ||
| try: | ||
| total += int(stdin_data.get(key, 0) or 0) | ||
| except (TypeError, ValueError): | ||
| return None | ||
| return total or None |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Require both complete, non-negative token totals.
.get(key, 0) or 0 silently treats a missing or empty field as zero, so a payload with only one total is used as an exact count instead of falling back to the percentage. Negative values and fractional floats are also accepted by int(). Require both fields and reject non-integer or negative values before summing; add a regression test for a single missing total.
As per path instructions, malformed Claude JSONL must not produce incorrect token calculations.
Proposed fix
- total = 0
+ totals = []
for key in ('total_input_tokens', 'total_output_tokens'):
+ if key not in stdin_data:
+ return None
+ raw = stdin_data[key]
+ if raw is None or isinstance(raw, bool):
+ return None
try:
- total += int(stdin_data.get(key, 0) or 0)
+ value = int(raw)
except (TypeError, ValueError):
return None
- return total or None
+ if value < 0 or (isinstance(raw, float) and not raw.is_integer()):
+ return None
+ totals.append(value)
+ total = sum(totals)
+ return total or None📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]: | |
| """Context tokens as Claude Code reported them, or None when unavailable. | |
| None means "no usable signal" — an older Claude Code that omits the | |
| totals, a relay payload that zeroes them, or a malformed value. Callers | |
| fall back to deriving the count from the percentage. | |
| """ | |
| total = 0 | |
| for key in ('total_input_tokens', 'total_output_tokens'): | |
| try: | |
| total += int(stdin_data.get(key, 0) or 0) | |
| except (TypeError, ValueError): | |
| return None | |
| return total or None | |
| def _exact_used_tokens(stdin_data: Dict[str, Any]) -> Optional[int]: | |
| """Context tokens as Claude Code reported them, or None when unavailable. | |
| None means "no usable signal" — an older Claude Code that omits the | |
| totals, a relay payload that zeroes them, or a malformed value. Callers | |
| fall back to deriving the count from the percentage. | |
| """ | |
| totals = [] | |
| for key in ('total_input_tokens', 'total_output_tokens'): | |
| if key not in stdin_data: | |
| return None | |
| raw = stdin_data[key] | |
| if raw is None or isinstance(raw, bool): | |
| return None | |
| try: | |
| value = int(raw) | |
| except (TypeError, ValueError): | |
| return None | |
| if value < 0 or (isinstance(raw, float) and not raw.is_integer()): | |
| return None | |
| totals.append(value) | |
| total = sum(totals) | |
| return total or None |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/claude_statusbar/core.py` around lines 730 - 743, Update
_exact_used_tokens to require both total_input_tokens and total_output_tokens to
be present, valid non-negative integers before summing; reject missing, empty,
malformed, negative, and fractional values by returning None so callers use the
percentage fallback. Add a regression test covering a payload with one missing
total.
Source: Path instructions
Fork-local. This machine is Windows and installs with `pip install -e`, so
none of this is reachable here:
install.sh, web-install.sh, uninstall.sh POSIX shell installers
publish.sh upstream's PyPI release script
packaging/ PyInstaller spec for the
standalone macOS/Linux binary
.github/workflows/release-binaries.yml builds that binary; would break
once packaging/ is gone
Nothing in `src/` imports any of them. `updater.py`'s INSTALL_SH_URL points at
upstream's raw copy on GitHub, not the local file, so the frozen-binary hint
still resolves.
ci.yml is kept on purpose — it is the only signal that changes here still work
on the platform upstream targets, which matters while PRs leeguooooo#37-leeguooooo#40 are open.
Everything else non-Windows (service.py, the macOS HUD, the /proc + ps
branches in daemon.py, darwin paths, POSIX docs) is recorded in TODO.md with
the reason each was deferred: they all sit inside files upstream edits often,
so removing them makes every future merge a conflict inside live code.
923 passed, 3 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem
_context_window_usagederives the displayed token count fromcontext_window.used_percentage, which Claude Code sends as a wholenumber — while
total_input_tokens/total_output_tokensin the samepayload are exact.
That makes the readout quantise to 1% of the window. On a 200k model that is
a 2k error and nobody notices. On a 1M-context model it is 10k: the bar steps
60.0k → 70.0k → 80.0kand can sit ~10k off the truth.Real payloads captured from v3.32.0 session buckets:
total_input_tokensFix
Prefer the exact totals; keep the percentage-derived value as the fallback
for payloads that omit them (older Claude Code, some relays) or send them
malformed.
ctx_pctis deliberately left as reported — the percentage and itsseverity colour stay byte-identical to what Claude Code itself shows. Only
the token count changes.
Before / after
Tests
Three cases added to
tests/test_core_ctx_pct.py: exact totals win, absenttotals fall back to the percentage, malformed totals fall back to the
percentage. Full suite run before and after — identical pass/fail sets (the
18 pre-existing failures on this Windows checkout are untouched by the diff).
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests