Skip to content

fix(jsonl): dedup cumulative token totals by message.id - #725

Merged
sirmalloc merged 2 commits into
sirmalloc:mainfrom
axisrow:fix/549-jsonl-token-dedup
Oct 29, 2026
Merged

sirmalloc merged 2 commits into
sirmalloc:mainfrom
axisrow:fix/549-jsonl-token-dedup

Conversation

@axisrow

@axisrow axisrow commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Why

Closes #549. Claude Code writes one JSONL entry per content block of a single API call, all sharing one message.id; the cumulative token path summed every entry, inflating Tokens Input/Output/Total (measured ~1.84×, independently confirmed at ~2.02× in the issue thread).

What this does

Group consecutive counted entries that share a message.id: the prompt side counts once, output_tokens as the running max (the approach endorsed in the issue thread). Entries without an id each start a new group — transcripts without duplicate-id entries are unchanged. Sidechain rows keep their treatment in sums and recency and group by their own id, so one subagent call counts once instead of once per content block.

Recency snapshots (contextLength, latest-usage) stay pinned to the group's first counted entry: duplicates only grow output_tokens and repeat identical prompt-side usage, so the early return deliberately keeps the first snapshot.

Verification

  • New tests on a Token widgets over-report ~1.84x: one JSONL entry per content block is counted per API call #549-shaped transcript assert exact cumulative totals (12 in / 292 out / 16660 total vs the inflated 16/393/...), plus streaming partials with ids and a sidechain per-content-block group; they fail with the inflated numbers on main.
  • Full suite: 3114 pass / 1 fail (the pre-existing git-hardening-exec LFS failure under Bun on macOS, unrelated — filing separately); target file green under both bun test and vitest; lint/type-check clean.

🤖 Generated with Claude Code

Claude Code writes one JSONL entry per content block of a single API
call, all sharing one message.id. The cumulative token path summed
every entry, inflating Tokens Input/Output/Total by up to ~1.84x
(2.02x in the independent report on sirmalloc#549). Group consecutive counted
entries sharing an id: the prompt side counts once, output_tokens as
the running max.

- transcripts without duplicate-id entries are unchanged (existing tests)
- streaming partials resolve to the finalized value, counted once
- sidechain rows keep their treatment in sums and recency; grouped by
  their own id so one subagent call counts once per content block

Closes sirmalloc#549

Co-Authored-By: Claude Code <[email protected]>
@eric-engberg

Copy link
Copy Markdown
Contributor

Isn't there already an open PR for this at #595 ?

@axisrow

axisrow commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor Author

Good catch, and thanks for the pointer — I hadn't cross-linked the issue and the PRs when I opened mine, so the overlap wasn't visible: both #725 and #595 address the same bug (#549).

Fix direction

Dedup consecutive counted entries by message.id as described above (which also matches the grouping discussed earlier in this thread). I've implemented exactly this with red→green tests on the #549 shape — see #725. If the approach taken in #595 is preferred, happy to adjust mine to it or withdraw it — either way works for me.

@eric-engberg

Copy link
Copy Markdown
Contributor

For what it's worth, a few more open PRs are working on #549 too, and not all of them link the issue: #591 and #622 cover both the token totals and the speed widgets like #595, #735 covers just the speed widgets (so it fits together with this one), and #587 includes its own version alongside its cost breakdown widgets. Might help to compare notes across all of them so there's one version to review.

@eric-engberg

Copy link
Copy Markdown
Contributor

My comment just above was done by Claude... didn't mean to double post about it here.

@sirmalloc
sirmalloc merged commit a7703e7 into sirmalloc:main Oct 29, 2026
3 checks passed
sirmalloc added a commit to iy88/ccstatusline that referenced this pull request Oct 29, 2026
Preserve maximum-output accounting and context snapshots from sirmalloc#725 and sirmalloc#735 while deduplicating nonconsecutive message IDs. Keep independent timing intervals and subagent collectors, retain both sets of regression tests, and compute speed window bounds once.
pcvelz added a commit to pcvelz/ccstatusline-usage that referenced this pull request Nov 2, 2026
Upstream: weekly limit timers (sirmalloc#768), prompt cache miss widgets (sirmalloc#770), context window size env override (sirmalloc#352, sirmalloc#407), windowed speed fixes (sirmalloc#769, sirmalloc#771), separator/renderer fixes (sirmalloc#773), custom-command styling (sirmalloc#775), clickable cwd links (sirmalloc#744), account email from live credentials (sirmalloc#724), cache timer reported expiry + short toggle (sirmalloc#576, sirmalloc#592), claude-status lock (sirmalloc#635, sirmalloc#772), dedup token metrics (sirmalloc#622, sirmalloc#725, sirmalloc#735).
Not adopted: sirmalloc#765 terminal width from COLUMNS with fixed reserve - the fork keeps flexMode/compactThreshold and tmux pane priority.

Hand edits outside conflicts:
- README.md: kept fork Configure flex separator behavior bullet and older-updates details wrapper; dropped upstream Related Projects additions (claude-eta)
- AGENTS.md: kept fork terminal width wording (sirmalloc#765 not adopted)
- src/types/FlexMode.ts, src/tui/components/TerminalWidthMenu.tsx, src/tui/components/__tests__/TerminalWidthMenu.test.ts, src/tui/components/TerminalOptionsMenu.tsx, src/tui/components/__tests__/TerminalOptionsMenu.test.ts: restored, fork keeps terminal width settings
- src/tui/App.tsx, src/tui/components/index.ts, src/types/index.ts: re-wired terminal width menu and FlexMode export
- src/types/StatusJSON.ts: dropped fork duplicate prompt_cache key, upstream schema is a superset
- src/utils/terminal.ts: COLUMNS source gated on non-tmux so tmux pane width keeps priority
- src/widgets/WeeklyLimitTimer.ts, src/utils/usage-prefetch.ts, src/widgets/__tests__/WeeklyLimitTimer.test.ts: compat fix - fableUsage/fableResetAt -> weeklyFableUsage/weeklyFableResetAt
- src/widgets/ApiUsage.tsx: compat fix - Context Bar honors effective context window (override, clamp, 1.0M format); Weekly renders a valid value despite a session error
- src/widgets/__tests__/ApiUsage.localModelFallback.test.ts: output tokens no longer counted in context (upstream sirmalloc#730)
- src/utils/__tests__/renderer-flex-width.test.ts, src/utils/__tests__/renderer-ansi.test.ts, src/utils/__tests__/gradient.test.ts, src/utils/__tests__/renderer-separator-collapse.test.ts, src/utils/__tests__/config.test.ts, src/tui/__tests__/App.test.ts: fork deviation - fork width model (flexMode, 6-column full reserve, kept settings, Terminal Width menu entry)
- src/utils/__tests__/default-colors.test.ts: fork deviation - accept ansi256:/hex: defaults
- src/utils/__tests__/usage-prefetch.test.ts: fork deviation - weekly usage comes from the API, fork widget imports from ApiUsage
- src/widgets/__tests__/CurrentWorkingDir.test.ts, src/widgets/__tests__/CustomCommand.test.ts: Settings literal gains flexMode/compactThreshold
- src/utils/__tests__/context-window.test.ts, src/utils/__tests__/jsonl-metrics.test.ts: restored fork tests upstream dropped
- docs/test-retirements.md: ledger for the dev-reload snapshot-path and RemoteControlStatus glyph (sirmalloc#605) tests upstream replaced
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.

Token widgets over-report ~1.84x: one JSONL entry per content block is counted per API call

3 participants