Skip to content

fix(tui): roll the compact form over to M at the rounding boundary - #55871

Merged
Tarquinen merged 2 commits into
anomalyco:devfrom
argszero:compact-number-rollover
Oct 15, 2026
Merged

Tarquinen merged 2 commits into
anomalyco:devfrom
argszero:compact-number-rollover

Conversation

@argszero

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55256

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Locale.number picks its unit before it rounds, so a value that the K branch rounds up to four digits prints as four digits. number(999_950) is "1000.0K" where "1.0M" is meant, and the status bar's context counter shows the same thing through formatContextUsage.

The K branch is (num / 1000).toFixed(1), so its result first reaches "1000.0" at 999_950 (999_949 gives "999.9"). That is the whole fix:

export function number(num: number): string {
  // One decimal place means the K branch reaches "1000.0K" at 999950, so that range belongs to M.
  if (num >= 999950) {
    return (num / 1000000).toFixed(1) + "M"
  } else if (num >= 1000) {
    return (num / 1000).toFixed(1) + "K"
  }
  return num.toString()
}

Everything below the boundary is unchanged: 999_949 still reads 999.9K, 1000 still reads 1.0K, and 1_000_000 still reads 1.0M, which it already did.

number has two callers, both fixed by the same line: the token line in routes/session/index.tsx (N in · N out) and formatContextUsage in util/session.ts, which is what the reporter and the bot's reproduction script saw in the footer (mini/footer.view.tsx and feature-plugins/prompt/footer.tsx both render it through that helper).

Not in this PR: the same shape at the top of the scale. There is no unit above M, so number(999_999_999) still prints 1000.0M. That is the end of the scale rather than a missing rollover — adding a B unit is a formatting decision the issue does not ask for, and this PR stays on the reported boundary.

The two earlier attempts at this, #48969 and #47523, were both closed by the periodic automated stale-PR sweep rather than on their merits, and dev still carries the bug, so this is not a resurrection of a rejected fix.

How did you verify your code works?

Two tests, both of which fail on the unmodified source with exactly the reported string:

  • packages/tui/test/util/locale.test.ts — rolls the compact form over at the rounding boundary pins 999, 1000, 999949, 999950, 999999, 1000000, 2500000. Without the change it fails at 999949 → 999.9K being fine but 999950 coming back as "1000.0K".
  • packages/tui/test/util/session.test.ts — reports the context counter a million tokens in as 1.0M covers the path the reporter actually sees, through formatContextUsage. Without the change it returns "1000.0K (101%)" instead of "1.0M (101%)".

Both directions were measured by reverting only locale.ts and re-running: 2 fail on the old source, 5 pass on the new one. tsgo -b in packages/tui exits 0, and is a real gate here (it was confirmed to reject a deliberate type error first). oxlint reports 0 warnings and 0 errors on the three files — note that packages/tui is not in the anti-slop override list in .oxlintrc.json, so the only rule reaching these files is the global no-restricted-globals, which was shown to fire before being trusted.

Full packages/tui suite: 1487 tests, 7 failures, all in the plugin/watcher family (a component layer ends …, failed setup reports batched layer errors …, Node reloads the local ESM graph …, setup layers before and after await …). A pristine upstream/dev checkout of the same tree fails 8, including every one of those, plus real watchers observe atomic saves to nested, outside-root, and symlinked helpers — so the failures are pre-existing and environment-dependent, this branch's set is a strict subset of the pristine one, and nothing in util/locale.ts is reachable from those files.

Screenshots / recordings

Not a layout change — this changes the text a counter renders, and the exact string is pinned by the tests above (1000.0K → 1.0M at the reported boundary). The reproduction the bot posted on the issue shows the same counter.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bug is fixed. I ran the #55256 reproduction from source with the simulated model reporting 999,950 prompt tokens. The status bar context counter shows 1000.0K (781%) on the base and 1.0M (781%) with this change (screenshots below). The new tests fail on the base and pass here, and the packages/tui type check passes.

The 999,950 threshold is exactly where (n / 1000).toFixed(1) first reaches "1000.0". Both callers (formatContextUsage and the session in · out line) only get token counts, so no fractional value lands on the boundary.

The public API is unchanged: this only affects TUI display formatting.

I found no problems with the change.

Before

After

@opencode-agent
opencode-agent Bot force-pushed the compact-number-rollover branch from 0ad9e52 to 72720b3 Compare October 15, 2026 23:27
@Tarquinen
Tarquinen merged commit 2e7ac8c into anomalyco:dev Oct 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tui: compact number formatter shows "1000.0K" instead of "1.0M" between 999,950 and 999,999

2 participants