fix(usage): let transcript parsers upgrade cached history#21516

open1
main
fix/usage-parser-cache-upgrade
opened17d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 40m ago

Transcript parsers could not change their persisted scan state without either resuming obsolete state or discarding cached history from deleted transcripts. Add an optional parser-owned cache upgrade hook: it receives committed and tail records, retains the rows it returns, and resets the scan position for a cold replay of live sources. Unchanged formats retain their existing behavior. Cache filenames and version stay unchanged.

Lower layer rebuilding #19971, credit @coffee100percnt for identifying stale cached faux usage and proposing the hook. This layer contains the generic provider-core contract, cache decoder, and generic behavior tests. The Pi policy lives in #21517.

Verification on 6e8e526:

  • Two executions of one generic fake-format cache case (v5 and v4) fail without the hook and pass with it. They exercise retained committed/tail rows, empty upgrades, current-entry resume, and legacy state reset without Pi-specific server branching.
  • The integrated stack passes 59 tests in the two changed suites; pinned main passes 56. Initial test phase on an earlier main passed 55 before edits and failed four tests after the new coverage; that implementation passed 58. One existing test arrived on main during the stack rebase. Four selected scanner/service/cache suites pass 85 tests on the reviewed head.
  • Scoped server/provider-core/Pi typechecks, targeted lint, formatting, and whitespace checks passed. One fresh parallel whole-stack and per-layer review round by Claude Opus 5.5 and GPT-6 Sol found no actionable issues.
  • Actual Linux server and web client ran the pinned main and stack head with matching local transcript fixtures. The head migrated a cache warmed by the actual base server without editing the cache; the $2.28 total became $0.48 while preserving $0.33 of real usage from a source deleted between runs. Evidence below demonstrates the generic hook together with its Pi consumer.
  • Current-head CI jobs passed, including all eight server shards, web tests, build/typecheck, lint, and the aggregate check. CodeRabbit reported no actionable code comments. Macroscope’s approvability check is neutral and explicitly requires human review of the usage-accounting change. No real provider account, macOS, Windows, iOS, Android, Electron desktop, or remote/relay/tunnel verification.

Before — actual main, same isolated Pi fixtures:

Before: faux usage inflates Pi spend to $2.28

After — head migrates the warmed cache, with one source transcript deleted:

After: Pi shows $0.48 and retains real deleted-transcript usage

Implementation: GPT-6.1 Sol / Codex. Tests: GPT-6 Astra / Codex. Maintainer integration and live verification: GPT-6.1 Sol / Codex. Independent reviews: Claude Opus 5.5 / Claude Code and GPT-6 Sol / Codex.

Checks

22 successful · 9 skipped · 1 neutral
All checks passed
6e8e526

Added 1 commit

  • fix(usage): let transcript parsers upgrade cached history6e8e526
Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — The PR adds an opt-in parser hook that can rewrite cached usage records and force cold transcript replay, affecting the token and cost data produced by the usage pipeline. Because this is production metering behavior, the change warrants human review.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded20.9 KiB20.9 KiB0 B (0.0%)29.3 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB−24 B (−0.5%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB−24 B (−2.0%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.2 KiB21.2 KiB−41 B (−0.2%)29.3 KiB✅
ClaudeLive turn messages21−1 (−50.0%)8✅

Baseline: 9f52dc2 · PR result: 6e8e526 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: dae22e8b-58d7-4646-8546-f27ded88c4ca
📥 Commits

Reviewing files that changed from the base of the PR and between 9f52dc28a9423e6b3201ed65b9820e5b8b52b887 and 6e8e526ebffd70f57611226765c84cda87d7793f.

📒 Files selected for processing: 3
  • apps/server/src/usage/usageScanCache.test.ts
  • apps/server/src/usage/usageScanCache.ts
  • packages/provider-core/src/server/usage.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

📝 Walkthrough

The usage format API adds an optional callback to upgrade cached records. Cache decoding passes it combined records and parser state, then marks legacy or upgraded entries for replay. Tests cover version 4 and version 5 cache entries.

Changes: Usage scan cache upgrades

Layer / File(s)Summary
Upgrade callback contract
packages/provider-core/src/server/usage.ts
TranscriptUsageFormat adds an optional callback that receives cached records and state. It can return replacement records or undefined.
Cache upgrade decoding
apps/server/src/usage/usageScanCache.ts, apps/server/src/usage/usageScanCache.test.ts
decodeScanCache passes combined records and state to the callback. Legacy entries and entries with returned replacements are marked for replay; replacements clear tail records. Tests cover unchanged, filtered, and fully removed records.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: maria-rcks

Merge Risk: ⚪ Minimal · up to 6e8e5

This change adds an optional hook that lets transcript parsers upgrade cached usage history. Formats that do not use the hook keep their existing behavior, and no merge-blocking risk was found.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Title checkPassedThe title is concise, specific, and accurately describes the main change: adding a cache upgrade hook for transcript parsers.
Description checkPassedThe description clearly explains the problem, implementation, scope, related pull requests, verification results, limitations, and observed behavior. It provides substantially complete information for…
Full details: Description check

Explanation

The description clearly explains the problem, implementation, scope, related pull requests, verification results, limitations, and observed behavior. It provides substantially complete information for the required template, including detailed test and manual verification evidence.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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