Count Codex cached input once, and read archived rollouts in the report - #96
Merged
Merged
Conversation
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).
Contributor
Deploying with
|
| 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 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughChangesProvider token normalization
Codex archived report rollups
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)
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Merged
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>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_breakdownused the provider-blindModelTokenCounts::processed()(input + output + cache_read + cache_write), so cached tokens landed in the denominator twice and the ratio collapsed toward one half:The top card was already right, because
token_breakdownsubtracts cached input first. Two copies of one rule, one of them wrong.Both paths now go through a single
NormalizedTokenstype in the scanner that owns the convention (provider_folds_cache_into_input), exposesprocessed()andcache_read_percent(), and is reachable from bothCostSummaryandModelTokenCounts. The provider-blindprocessed()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_reportwalked onlysessions/, neverarchived_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::ingestwith a sharedseenset. Both the date-nested tree and the flat archive dir now run through it, the archive gated by the existingarchived_rollout_day_in_rangefilename 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 warningsclean on both crates,cargo fmt --checkclean,tsc --noEmitclean.Summary by CodeRabbit