Skip to content

Count Codex cached input once, and read archived rollouts in the report - #96

Merged
tsouth89 merged 1 commit into
mainfrom
fix/codex-cache-accounting-and-archives
Jul 21, 2026
Merged

Count Codex cached input once, and read archived rollouts in the report#96
tsouth89 merged 1 commit into
mainfrom
fix/codex-cache-accounting-and-archives

Conversation

@tsouth89

@tsouth89 tsouth89 commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Fixes SOU-295 and SOU-296. Both are Codex accounting bugs with the same shape: a second implementation that did not know what the first one knew.

SOU-295 — per-model cache read showed 49% instead of ~97%

Codex reports cached input inside input_tokens. model_breakdown used the provider-blind ModelTokenCounts::processed() (input + output + cache_read + cache_write), so cached tokens landed in the denominator twice and the ratio collapsed toward one half:

displayed: 97 / (100 + 97) = 49.2%
correct:   97 / (  3 + 97) = 97%      (fresh input = 100 - 97)

The top card was already right, because token_breakdown subtracts cached input first. Two copies of one rule, one of them wrong.

Both paths now go through a single NormalizedTokens type in the scanner that owns the convention (provider_folds_cache_into_input), exposes processed() and cache_read_percent(), and is reachable from both CostSummary and ModelTokenCounts. The provider-blind processed() is gone, so there is no longer a way to build the wrong ratio.

Scope note: the original report suggested fixing "model rows, effort rows, projects, CSV exports." Verified that is broader than reality — .processed() had exactly one production call site, and effort/project/CSV compute no cache percentage. One field, one surface.

SOU-296 — archiving a Codex task shrank every total

scan_codex_report walked only sessions/, never archived_sessions/. It is the path behind the Charts cards, the reset-aligned token and dollar windows shipped in 1.3.1, and the estimated API value card — while the older summary scanner did read archives. The two disagreed for the same account on the same machine.

It also had no dedup at all (unlike the Claude report path), so one rollout present under two homes or WSL roots could be counted twice.

Rather than copy the archive walk (a second implementation is how these drifted in the first place), the per-rollout accounting is extracted into CodexReportRollups::ingest with a shared seen set. Both the date-nested tree and the flat archive dir now run through it, the archive gated by the existing archived_rollout_day_in_range filename check.

Tests

  • codex_cache_percent_does_not_double_count_cached_input — 338.2M input / 329M cached reads ~97%, not 49%.
  • claude_cache_percent_leaves_input_untouched — the correction is Codex-only.
  • normalized_removes_cached_input_for_codex_so_cache_rate_is_honest / normalized_sums_every_bucket_for_a_provider_that_separates_cache / cache_read_percent_is_none_without_activity.
  • codex_report_counts_archived_rollouts_exactly_once — archived-only rollout counted, shared rollout counted once. Confirmed to fail without the fix (1 session instead of 2), so it is a real regression test.

Shared lib 658 passed, desktop 395 passed, frontend 278 passed. Clippy -D warnings clean on both crates, cargo fmt --check clean, tsc --noEmit clean.

Summary by CodeRabbit

  • Bug Fixes
    • Corrected token and cache-read percentage calculations across Codex and Claude usage reports.
    • Prevented cached input from being counted twice in Codex statistics.
    • Ensured Claude input metrics remain accurate.
    • Improved usage summaries and charts to consistently include archived sessions without duplicate counting.
  • Tests
    • Added coverage validating provider-specific token breakdowns, cache percentages, and archived-session reporting.

Two Codex accounting bugs, both from the same shape of mistake: a second
implementation that did not know what the first one knew.

SOU-295: the per-model row advertised "49% cache read" on a model that
was really ~97% cached. Codex reports cached input inside input_tokens,
so adding the cache bucket on top counts those tokens twice and the
ratio collapses toward one half: 97 / (100 + 97) is 49. The top card was
right because token_breakdown subtracts cached input first; the model
row used the provider-blind ModelTokenCounts::processed(). Both now go
through one NormalizedTokens type that owns the rule, so the two cannot
drift apart again.

SOU-296: scan_codex_report walked only sessions/ and never
archived_sessions/, so archiving a Codex task quietly shrank the charts,
the reset windows, and the estimated API value, while the summary
scanner kept counting it. That scanner also had no dedup at all, so the
same rollout under two homes could be counted twice. The per-rollout
accounting is now a single ingest path with a shared seen-set, and both
the date-nested tree and the flat archive run through it.

Tests: a 97%-cached Codex model reads ~97% and a Claude model is left
untouched; an archived-only rollout is counted and a rollout present in
both locations is counted once. The archive test was confirmed to fail
without the fix (1 session instead of 2).
@cloudflare-workers-and-pages

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ceiling 0e6d498 Commit Preview URL

Branch Preview URL
Jul 21 2026, 06:53 PM

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2433c3f3-76c6-4ac3-8d2b-edb34f50d06e

📥 Commits

Reviewing files that changed from the base of the PR and between 8ea0222 and 0e6d498.

📒 Files selected for processing (2)
  • apps/desktop-tauri/src-tauri/src/commands/chart.rs
  • rust/src/cost_scanner.rs

📝 Walkthrough

Walkthrough

Changes

Provider token normalization

Layer / File(s) Summary
Provider-aware token normalization
rust/src/cost_scanner.rs
Adds normalized token buckets and provider-specific cache-folding behavior, with tests for Codex, Claude, and empty activity.
Chart breakdown integration
apps/desktop-tauri/src-tauri/src/commands/chart.rs
Uses normalized tokens for token and model breakdowns, updates call sites, and validates provider-specific cache-read percentages.

Codex archived report rollups

Layer / File(s) Summary
Shared Codex rollout accumulation
rust/src/cost_scanner.rs
Consolidates nested and archived session ingestion through CodexReportRollups, including rollout deduplication and report finalization.
Archived rollout regression coverage
rust/src/cost_scanner.rs
Tests archived-only inclusion and prevents duplicate counting for rollouts present in both session trees.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Sessions as sessions/
  participant Archives as archived_sessions/
  participant Rollups as CodexReportRollups
  participant Report as scan_codex_report
  Sessions->>Rollups: ingest nested rollout JSONL
  Archives->>Rollups: ingest in-range archived rollout
  Rollups->>Rollups: deduplicate rollout filenames
  Rollups->>Report: finish(days)
Loading

Possibly related PRs

Suggested reviewers: finesssee, darknight105

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes both main fixes: Codex cached input accounting and archived rollout ingestion.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/codex-cache-accounting-and-archives

Comment @coderabbitai help to get the list of available commands.

@tsouth89
tsouth89 merged commit 4826478 into main Jul 21, 2026
11 checks passed
@tsouth89
tsouth89 deleted the fix/codex-cache-accounting-and-archives branch July 21, 2026 19:02
@tsouth89 tsouth89 mentioned this pull request Jul 21, 2026
tsouth89 added a commit that referenced this pull request Jul 21, 2026
Release prep. Version bumped in all five tracked locations
(`version.env` incl. BUILD_NUMBER 100→101, both Rust manifests,
`package.json`, `tauri.conf.json`) plus `Cargo.lock`.

## What ships

**Added** — multi-plan disclosure (SOU-297 step 1, #97/#98). Codex
records the plan behind each request, so when a machine's totals span
more than one plan the Charts page says so rather than letting them read
as the signed-in account's.

**Fixed**
- Codex cache rate double-counted cached input, showing ~49% for a model
that was really ~97% (SOU-295, #96).
- Archived Codex sessions were missing from the Charts page, reset
windows, and API value card (SOU-296, #96).
- Cache percentages now say which window they measure (SOU-300, #99).

Also folds in a one-line doc fix flagged during the #90 review:
`CostUsageReport.current_windows` said "token totals" after it started
carrying cost too.

## Note for release notes

The archived-sessions fix will **increase** existing users' totals if
they archive Codex tasks — on the maintainer's machine 81 of 97 rollouts
were archived and therefore invisible. That is a correction, not a
regression, and the notes say so.

## Validation

`local-check.ps1 -All -Version 1.4.0` passes; release-doctor reports all
five version locations at 1.4.0 and the changelog entry present, with
only the expected pre-tag warnings (no local tag, no local assets dir).

Co-authored-by: tsouth89 <tsouth89@users.noreply.github.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.

1 participant