Show validated cached spend while refreshing#2397
Conversation
Prime Usage & Spend from validated scoped Codex caches before the live refresh completes. Reuse the ambient cache for live-system history while keeping managed and profile homes isolated.
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: Show validated cached spend while refreshing This is item 1/1 in the current shard. Shard 0/1. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |
There was a problem hiding this comment.
Pull request overview
This PR updates the Usage & Spend dashboard flow to render validated cached Codex spend immediately (when available) while the normal live validation refresh is still running, and to avoid redundant indexing by reusing the ambient cache for .liveSystem.
Changes:
- Add a cache-priming load phase in
SpendDashboardControllerand a newSpendDashboardSource.loadCached(...)path to hydrate validated cached Codex data before the ordinary refresh completes. - Extend cached Codex snapshot loading to support lightweight hydration options (skip Pi merge, skip project/session reconstruction) and add a package-scoped API for scoped homes.
- Update the dashboard empty-state presentation to show a “Refreshing” state while validation is pending, and add targeted test coverage for cache-first rendering and cache-root routing.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| Tests/CodexBarTests/SpendDashboardScopedCacheTests.swift | Adds a production-like test ensuring the cached dashboard loader can read a validated scoped cache. |
| Tests/CodexBarTests/SpendDashboardPresentationTests.swift | Adds coverage for the “Refreshing” vs “No local cost history yet” empty-state titles. |
| Tests/CodexBarTests/SpendDashboardCachedRefreshTestSupport.swift | Adds test gates/recorders to deterministically control priming vs live refresh sequencing. |
| Tests/CodexBarTests/SpendDashboardCachedRefreshTests.swift | Validates cache-first rendering, generation staleness rejection, cache-miss behavior, auth rotation rejection, and cache-root routing. |
| Tests/CodexBarTests/CostUsageFetcherCacheSnapshotTests.swift | Expands cached snapshot tests to cover “native-only” hydration and scoped-home opt-in + root validation. |
| Sources/CodexBarCore/CostUsageFetcher.swift | Adds cached-snapshot options (scoped opt-in, Pi inclusion, breakdown inclusion) and package-scoped scoped-home cached loader. |
| Sources/CodexBar/SpendDashboardController.swift | Implements cache priming in the controller and routes .liveSystem to the ambient cache root; adds SpendDashboardSource.loadCached. |
| Sources/CodexBar/PreferencesSpendDashboardPane.swift | Wires the cached loader into the pane and adds a refresh-aware empty-state model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // A cache miss is still pending fresh validation, not a provider failure. | ||
| return SpendDashboardLoadResult(inputs: inputs, failedSourceIDs: []) |
Summary
.liveSystem.UsageStorerule that scoped cache hydration is opt-in.Refs #2116.
Evidence
Current first-open behavior can remain blank while the local corpus is indexed:
Before this patch, the same live session corpus existed in two v10 cache files on this machine:
That duplicate cache identity forced the dashboard to build a second index instead of reusing the provider-level local telemetry already maintained by
UsageStore.Changes
SpendDashboardController:.liveSystemreuses the ambient CodexBar cache;cost-usage/accounts/<identity>.UsageStorecannot accidentally hydrate managed data.Refreshingstate while the first cache/live validation pass is pending.Scope boundaries
This PR does not change fork attribution, migration selection, same-day activation behavior, model-label localization, or the meaning of provider-owned telemetry. Those remain separate concerns.
Validation
make check— passed; SwiftFormat and SwiftLint clean.origin/mainbecause fixed2026-07-16data crossed out of the default 7-day window on July 23. Existing Keychain-suppression and date-fixture baseline suites were kept out of the no-prompt run.