Skip to content

fix(renderer): token counts near 1M render as "1000.0k" instead of "1.0M" - #444

Merged
sirmalloc merged 2 commits into
sirmalloc:mainfrom
CorticalCode:fix/format-tokens-rounding
Jun 15, 2026
Merged

sirmalloc merged 2 commits into
sirmalloc:mainfrom
CorticalCode:fix/format-tokens-rounding

Conversation

@CorticalCode

Copy link
Copy Markdown
Contributor

Problem

formatTokens renders counts in [999950, 999999] as 1000.0k. Since 1000.0k denotes one thousand thousand — i.e. one million — these values should render as 1.0M.

Cause

Those counts fall into the thousands branch, where (count / 1000).toFixed(1) rounds 999.95… up to 1000.0. The millions branch (count >= 1000000) is never reached for them.

Fix

Lower the millions threshold to 999950 — the point at which the rounded thousands value reaches 1000.0 — so the affected range renders as 1.0M. One-line change, with a comment explaining the boundary.

This is a correctness fix rather than a display preference, so it isn't configurable. It applies to every token widget: Tokens Input/Output/Cached/Total, Context Length/Window, and the Cache widgets (via formatTokensWithPercentage).

Tests

Adds renderer-format-tokens.test.ts — the first direct unit test for formatTokens (bytes/k/M cases, the 999950/999999 boundary, and 999949 just below it). bun test and bun run lint pass.

Values in [999950, 999999] hit the thousands branch where (count/1000).toFixed(1) rounds up to '1000.0', producing '1000.0k'. Lower the millions threshold to that exact rounding boundary so they render as '1.0M'. Adds a direct unit test for formatTokens (previously exercised only indirectly via widget spies).

Co-Authored-By: Claude <noreply@anthropic.com>
CorticalCode added a commit to CorticalCode/ccstatusline that referenced this pull request Jun 15, 2026
…ed leaf

The reclaimed display calls formatTokens; the extracted format-tokens.ts held the
pre-fix version, so a reclaimed total in [999950, 999999] rendered '1000.0k'
instead of '1.0M'. Apply the same threshold fix as PR sirmalloc#444 here, so the reclaimed
figure is correct and merging both PRs in either order cannot regress it.

Co-Authored-By: Claude <noreply@anthropic.com>
sirmalloc pushed a commit that referenced this pull request Jun 15, 2026
* refactor(compaction): compute stats struct (count, byTrigger, tokensReclaimed) from markers

Parse compactMetadata.{trigger,preTokens,postTokens} during the existing
compact_boundary scan. No widget behavior change yet.

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(compaction): freeze zero stats, floor reclaimed at 0, clarify naming

Address review feedback on the data layer: freeze the shared
ZERO_COMPACTION_STATS (incl. nested byTrigger); floor each marker's reclaimed
contribution with Math.max(0, ...) so postTokens>preTokens can't go negative;
rename metaObj -> metaRecord; add unknown-trigger and negative-reclaim tests.

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(compaction): opt-in trigger split via (s) keybind

Adds a per-item showTriggers toggle that appends '(2 auto, 1 manual)';
unknown bucket shown only when > 0; omitted at count 0. Rendering now flows
through formatStats, shared by live render and preview.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(compaction): clarify zero-suffix test name; cover manual-only bucket

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(compaction): opt-in tokens reclaimed via (t) keybind

Adds a per-item showReclaimed toggle that appends the reclaimed token total
(e.g. 887.0k) via the shared formatTokens humanizer; omitted when 0. Stacks
independently with the trigger split.

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: extract formatTokens to a leaf module to avoid a circular import

CompactionCounter importing formatTokens from renderer.ts created a cycle
(renderer -> widget registry -> CompactionCounter) that broke isolated runs of
its test file with a TDZ error. Move formatTokens into src/utils/format-tokens.ts;
renderer re-exports it so existing importers are unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(compaction): cover tokens reclaimed with a non-default format

Co-Authored-By: Claude <noreply@anthropic.com>

* docs(compaction): document trigger-split and tokens-reclaimed toggles

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(compaction): apply code-review feedback

- use shared isMetadataFlagEnabled/toggleMetadataFlag for the trigger/reclaimed
  flags instead of local copies
- freeze SAMPLE_STATS to match ZERO_COMPACTION_STATS
- guard tokensReclaimed against non-finite (overflow) marker differences
- docs: note the unknown trigger bucket and that reclaimed is floored/omitted at 0

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(compaction): carry the formatTokens rounding fix into the extracted leaf

The reclaimed display calls formatTokens; the extracted format-tokens.ts held the
pre-fix version, so a reclaimed total in [999950, 999999] rendered '1000.0k'
instead of '1.0M'. Apply the same threshold fix as PR #444 here, so the reclaimed
figure is correct and merging both PRs in either order cannot regress it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
# Conflicts:
#	src/utils/renderer.ts
@sirmalloc
sirmalloc merged commit bfe86ab into sirmalloc:main Jun 15, 2026
3 checks passed
tahodev pushed a commit to tahodev/ccstatusline that referenced this pull request Oct 20, 2026
…c#445)

* refactor(compaction): compute stats struct (count, byTrigger, tokensReclaimed) from markers

Parse compactMetadata.{trigger,preTokens,postTokens} during the existing
compact_boundary scan. No widget behavior change yet.

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(compaction): freeze zero stats, floor reclaimed at 0, clarify naming

Address review feedback on the data layer: freeze the shared
ZERO_COMPACTION_STATS (incl. nested byTrigger); floor each marker's reclaimed
contribution with Math.max(0, ...) so postTokens>preTokens can't go negative;
rename metaObj -> metaRecord; add unknown-trigger and negative-reclaim tests.

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(compaction): opt-in trigger split via (s) keybind

Adds a per-item showTriggers toggle that appends '(2 auto, 1 manual)';
unknown bucket shown only when > 0; omitted at count 0. Rendering now flows
through formatStats, shared by live render and preview.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(compaction): clarify zero-suffix test name; cover manual-only bucket

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(compaction): opt-in tokens reclaimed via (t) keybind

Adds a per-item showReclaimed toggle that appends the reclaimed token total
(e.g. 887.0k) via the shared formatTokens humanizer; omitted when 0. Stacks
independently with the trigger split.

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: extract formatTokens to a leaf module to avoid a circular import

CompactionCounter importing formatTokens from renderer.ts created a cycle
(renderer -> widget registry -> CompactionCounter) that broke isolated runs of
its test file with a TDZ error. Move formatTokens into src/utils/format-tokens.ts;
renderer re-exports it so existing importers are unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(compaction): cover tokens reclaimed with a non-default format

Co-Authored-By: Claude <noreply@anthropic.com>

* docs(compaction): document trigger-split and tokens-reclaimed toggles

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(compaction): apply code-review feedback

- use shared isMetadataFlagEnabled/toggleMetadataFlag for the trigger/reclaimed
  flags instead of local copies
- freeze SAMPLE_STATS to match ZERO_COMPACTION_STATS
- guard tokensReclaimed against non-finite (overflow) marker differences
- docs: note the unknown trigger bucket and that reclaimed is floored/omitted at 0

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(compaction): carry the formatTokens rounding fix into the extracted leaf

The reclaimed display calls formatTokens; the extracted format-tokens.ts held the
pre-fix version, so a reclaimed total in [999950, 999999] rendered '1000.0k'
instead of '1.0M'. Apply the same threshold fix as PR sirmalloc#444 here, so the reclaimed
figure is correct and merging both PRs in either order cannot regress it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
tahodev pushed a commit to tahodev/ccstatusline that referenced this pull request Oct 20, 2026
…irmalloc#444)

Values in [999950, 999999] hit the thousands branch where (count/1000).toFixed(1) rounds up to '1000.0', producing '1000.0k'. Lower the millions threshold to that exact rounding boundary so they render as '1.0M'. Adds a direct unit test for formatTokens (previously exercised only indirectly via widget spies).

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
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.

2 participants