Add durable All Time spend history - #2881
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. |
|
Codex review: needs maintainer review before merge. Reviewed August 13, 2026, 1:28 AM ET / 05:28 UTC. ClawSweeper reviewWhat this changesThe PR persists validated provider cost buckets in a local ledger and adds an All Time Usage & Spend range plus coverage-aware Share Stats output. Merge readinessKeep open for a maintainer decision on the retention lifecycle. The implementation and packaged restart proof are strong, but turning existing cost tracking into a durable local usage archive needs an explicit product/privacy policy before merge. Priority: P2 Review scores
Verification
How this fits togetherCodexBar converts provider usage snapshots into the Usage & Spend dashboard. This PR adds a local ledger between validated snapshots and the dashboard so an All Time view can survive provider-window expiry and app restarts. flowchart LR
A[Provider usage snapshots] --> B[Validated daily buckets]
B --> C[Local spend ledger]
C --> D[All Time aggregation]
D --> E[Usage and Spend dashboard]
D --> F[Share Stats output]
Decision needed
Why: The code can safely implement either retention policy, but choosing whether historical local usage data survives preference changes is a product and privacy commitment. Before merge
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Adopt All Time only with a maintainer-approved local-history lifecycle: clearly disclose retention, provide a user-visible way to remove the archive, and test the selected disable/re-enable behavior. Do we have a high-confidence way to reproduce the issue? Not applicable as a bug report: this is a new feature, and its proposed restart behavior is demonstrated by the supplied two-process packaged-app proof. Is this the best way to solve the issue? Unclear: the ledger and restart proof are credible, but the retention and deletion policy is not a purely technical choice. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 208016687098. LabelsLabel justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (14 earlier review cycles; latest 8 shown)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 399aa5e8a6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let coveredInterval = if range == .allTime, let trackedCoverage = input.trackedCoverage { | ||
| max(bounds.lowerBound, trackedCoverage.start)...min(bounds.upperBound, trackedCoverage.end) |
There was a problem hiding this comment.
Normalize tracked coverage to display-day boundaries
When All Time includes Mistral history and the user's time zone is not UTC, trackedCoverage.start is a UTC bucket boundary while each daily entry is converted to the containing local day's start. The first entry therefore falls before coveredInterval and is discarded, silently undercounting spend and tokens while the displayed covered-day count remains unchanged; normalize the tracked bounds through the display calendar as the regular coverage path does.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| private func rebuildModel() { | ||
| let inputs = self.selectedRange == .allTime ? self.ledgerInputs : self.loadedInputs |
There was a problem hiding this comment.
Clear ledger inputs when configured ownership disappears
When the user is viewing All Time and disables/removes every cost-capable source, the empty-configuration branch clears only loadedInputs; this line continues rebuilding from the old ledgerInputs. Because no subsequent load runs to filter the ledger by the new ownership map, the dashboard and share action can indefinitely expose spend rows from the now-disabled account/provider. The same stale data is shown transiently during ownership changes, so ledger inputs should be cleared or filtered synchronously with configuration invalidation.
Useful? React with 👍 / 👎.
|
@clawsweeper re-review Addressed the multi-provider All Time coverage finding in signed commit The currency-group disclosure now counts the intersection of the actual persisted daily buckets across every provider, instead of taking the minimum of each provider's count. A focused regression fixture gives Codex and Claude different gaps and proves that only their shared day is reported as covered. Local verification:
— Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Fixed the failing macOS shard in signed commit The only failure was an exact architecture-gate source-line anchor shifted by the new all-time coverage helper; production behavior was unaffected. Verification: @clawsweeper re-review — Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Addressed both ClawSweeper correctness findings in signed commit
Focused proof:
I am preparing the requested isolated, redacted packaged-app record → restart → reload proof separately. @clawsweeper re-review — Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Fixed the final macOS shard gate in signed commit Both exact allowlist anchors had shifted when the reviewed controller/model fixes added lines. No production behavior changed in this commit. Verification: — Akshay / Codex |
|
Addressed the remaining failed-write finding in signed commit The ledger now distinguishes the last durable document from an in-memory candidate. A failed atomic write returns and retains only the prior persisted history, so the UI cannot claim history that will vanish after restart. Verification:
The local @clawsweeper re-review — Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Fixed the macOS shard failure in signed commit Root cause: the parameterized All Time disablement test used Verification:
GitHub CI has restarted on the signed head. — Akshay / Codex |
|
Fixed the remaining CI-only date fixture mismatch in signed commit The fixture instant was July 15 in Pacific time but July 16 on GitHub’s UTC runner. Because the test deliberately models a one-day complete window, CI correctly rejected its July 15 bucket. The fixture now uses noon UTC on July 15, which remains July 15 in both zones. Verification:
— Akshay / Codex |
|
Exact-head packaged persistence proof is now attached in signed commits The first packaged process records 30 covered days and exits. The second process uses the same executable SHA, opens the exact first-process ledger SHA, and renders 60 covered All Time days. The final dashboard and Share Stats images were inspected at original resolution: no PII, local paths, UUIDs, account labels, prompts, desktop context, or permission dialogs. @clawsweeper re-review — Akshay / Codex |
|
🦞👀 Re-review progress:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af02d8fd8f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| cacheReadTokens: entry?.cacheReadTokens ?? 0, | ||
| cacheCreationTokens: entry?.cacheCreationTokens ?? 0, | ||
| outputTokens: entry?.outputTokens ?? 0, | ||
| totalTokens: entry?.totalTokens ?? 0, |
There was a problem hiding this comment.
Preserve unknown tokens when writing ledger days
For cost-only sources such as OpenCode Go, daily entries legitimately contain a cost while totalTokens is nil; replacing that value with 0 makes the reconstructed ledger snapshot report a complete zero-token total. Selecting All Time then displays and shares 0 tracked tokens instead of leaving tokens unavailable, so absent token fields must remain unknown rather than being coerced to zero.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| private func rebuildModel() { | ||
| let inputs = self.selectedRange == .allTime ? self.ledgerInputs : self.loadedInputs |
There was a problem hiding this comment.
Keep ledger gaps unknown in the activity heatmap
When two recorded windows have a calendar gap, selecting All Time switches the 365-day token activity calculation to ledgerInputs. The ledger represents that gap by omitting its days but emits a snapshot whose historyDays spans from the first capture to the last and whose coverage is established; tokenActivityInputSummary consequently treats every omitted gap day as a proven zero. The heatmap therefore reports those unobserved days as covered inactivity, so token activity must retain discontinuous coverage or continue using the live/cache inputs.
Useful? React with 👍 / 👎.
|
Current-head CI failure fixed in signed commit The only failure was Verification:
@clawsweeper re-review — Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
All three contributor-actionable findings are fixed in signed commits
Focused verification:
The hardened packaged verifier then passed two separate processes from executable head @clawsweeper re-review — Akshay / Codex |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Exact-head lint repair is pushed in signed commit
Please re-review the current head. — Akshay / Codex |
Summary
Why
The dashboard currently offers only rolling 7-day and 30-day views. Extending that window to 365 days is not true lifetime tracking because provider snapshots are bounded and older local source history can disappear. This change records proven daily buckets as CodexBar observes them, then builds All Time from that durable history.
This is intentionally separate from #2759: it does not add a 365-day approximation or change provider/RPC behavior.
Data truth and safety
0600on macOS), and fails empty on corrupt or unknown schemas.Packaged runtime proof
The checked-in evidence was produced by the packaged debug CodexBar app at signed harness commit
392eb52c6, using two separate processes and the production Codex scanner, ledger, dashboard controller, SwiftUI views, and Share Stats renderer.Both manifests report executable SHA-256
838af0bb9a7a999dcccb2481f46e2425b8f16e0262b451bf74d49871d28fe039. The record manifest's ledger SHA-25653691e15efae58b5cb2022b857249d1a281e853654c4669359db08fa6d4df0aeexactly matches the reload manifest'sledgerBeforeSHA256.The DEBUG-only proof mode resolves before normal app initialization, requires a current-user-owned
0700sentinel directory under/private/tmp, rejects root and sentinel symlinks withlstat, uses an empty isolated environment, and constructs noSettingsStore,UsageStore, browser importer, Keychain service, or network client. It is compiled out of release builds.All screenshot inputs are fixed synthetic fixtures. The final images were inspected at original resolution and contain no names, emails, usernames, paths, UUIDs, account labels, prompts, keys, desktop/menu-bar context, permission dialogs, or live usage data.
All Time dashboard after restart
All Time Share Stats card after restart
Evidence and reproduction details:
Verification
git diff --checkpassAddresses #1708