Skip to content

fix: honor preferred currency in cost-history detail chart - #2887

Merged
steipete merged 5 commits into
steipete:mainfrom
vAhyThe:fix/cost-history-preferred-currency
Aug 13, 2026
Merged

fix: honor preferred currency in cost-history detail chart#2887
steipete merged 5 commits into
steipete:mainfrom
vAhyThe:fix/cost-history-preferred-currency

Conversation

@vAhyThe

@vAhyThe vAhyThe commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

The cost-history detail submenu ignored Settings → General → Currency and always rendered its labels in the snapshot's native currency. Other cost surfaces already use UsageFormatter.convertedCost, so users selecting GBP, EUR, CNY, or another supported currency saw converted values everywhere except this submenu.

Change

Display-time conversion only; stored snapshots and chart geometry stay in provider-native values.

  • CostHistoryChartMenuView receives one display currency and multiplier. Its existing formatting paths apply that multiplier to the total, day detail, model total and Standard/Fast rows, project/source rows, session rows, and Y-axis labels.
  • StatusItemController+HostedSubmenus resolves the display conversion through UsageFormatter.convertedCost, preserving the shared fallback contract: auto, same-currency, and unavailable exchange rates remain in the source currency with multiplier 1.
  • The hosted-view render fingerprint includes both the effective display currency and the exact Double.bitPattern of the multiplier. A rate refresh therefore invalidates an already-hydrated chart even when the selected currency code is unchanged.
  • Existing Codex-only project/session presentation remains unchanged and its architecture-gatekeeper anchors were updated only for shifted line numbers.

Deliberately unchanged: provider-native balances and other surfaces that intentionally keep native currencies separate.

Validation

  • swift test --filter CostHistoryChartMenuView — 30/30 passed.
  • Regression: identical provider, snapshot, and display currency with multipliers 21.0 and 21.5 produce different render fingerprints; the exact bit patterns are asserted.
  • swift test --filter ProviderArchitectureGatekeeperTests — 38/38 passed.
  • make check passed.
  • Full local matrix: 841 selections across 71 groups passed with zero retries or timeouts.
  • Structured Codex autoreview and TruffleHog passed with no accepted/actionable finding.

Runtime and visual proof

A Developer ID-signed debug bundle (com.steipete.codexbar.debug) was built from this head and launched with:

  • a disposable HOME and Codex home;
  • Keychain access disabled;
  • all network access denied;
  • a deterministic cached USD→CNY multiplier of 21;
  • synthetic Codex JSONL only.

The packaged CLI exercised the production scanner and loaded two days, two synthetic projects, three synthetic sessions, and two model rows for the latest day. The exact production CostHistoryChartMenuView was then rendered offscreen with that data. The image shows CNY conversion across every affected label path while bar proportions remain source-value based.

Synthetic production-view proof showing CNY axis, day/model rows, total, projects, and conversations

The host desktop was locked during proof capture, so this is explicitly an offscreen production-view render rather than a screenshot of the live popup. The signed app process, bundle signature, isolated scanner path, and deterministic refresh regression were verified separately.

Contributor authorship and co-author trailers are preserved in the reconstructed commits.

@clawsweeper

clawsweeper Bot commented Aug 12, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 12, 2026
@clawsweeper

clawsweeper Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed August 12, 2026, 10:25 PM ET / August 13, 2026, 02:25 UTC.

ClawSweeper review

What this changes

The PR converts cost-history chart labels to the selected display currency while preserving provider-native chart geometry and refreshing hydrated charts when the exchange rate changes.

Merge readiness

⚠️ Ready for maintainer review - 1 item remains

Keep open: this is a focused, correct fix for a current-main display inconsistency, with the prior cache-invalidation concern addressed and credible production-boundary proof provided.

Priority: P2
Reviewed head: d0fd6e197aa9e97fd18fd758cab9ce00d665eba2

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The patch is a well-scoped correction with real after-fix production-boundary evidence and a targeted cache-regression test.
Proof confidence 🐚 platinum hermit (4/6) ✨ media proof bonus Sufficient (screenshot): The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (screenshot): The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
Evidence reviewed 6 items Current-main gap: Current main passes the snapshot currency directly to the chart and formats Y-axis values without a display multiplier, so this chart does not yet follow the shared preferred-currency behavior.
Shared conversion contract: The branch derives a multiplier by converting one source-currency unit through the existing formatter; that formatter preserves the source amount and code when auto mode, same currency, or an unavailable rate applies.
Resolved cache finding: The render fingerprint now includes the display currency and exact multiplier bit pattern, so an exchange-rate-only refresh rebuilds an already hydrated chart.
Findings None None.
Security None None.

How this fits together

CodexBar scans provider usage snapshots and renders cost history in a hosted menu-bar chart. The chart combines native provider costs with the user’s currency preference to produce displayed labels without altering stored data or bar heights.

flowchart LR
A[Provider cost snapshot] --> B[Preferred currency setting]
B --> C[Shared conversion resolver]
A --> D[Native chart geometry]
C --> E[Display currency and multiplier]
D --> F[Cost-history chart]
E --> F
F --> G[Menu-bar labels and details]
Loading

Before merge

  • Complete next step (P2) - No repair action is needed; this PR is ready for ordinary maintainer merge review once the standard checks finish.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Focused code and test delta production +35/-7, tests +27/-2 across 4 files The implementation is narrowly limited to chart presentation, hosted-view invalidation, and matching regression coverage.

Technical review

Best possible solution:

Merge the display-only conversion after normal checks complete, retaining the shared formatter fallback behavior and multiplier-based cache invalidation.

Do we have a high-confidence way to reproduce the issue?

Yes, from source: render a cost-history snapshot with an explicit non-native preferred currency and compare the current-main raw-label path with the branch’s shared conversion path; the PR also supplies an isolated production-view run.

Is this the best way to solve the issue?

Yes. Reusing the existing formatter for a display-time multiplier preserves native snapshots and geometry while matching the established currency fallback contract.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ee29794b9a1b.

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
  • add proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This fixes a bounded but user-visible preference inconsistency in the cost-history menu.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
  • proof: sufficient: Contributor real behavior proof is sufficient. The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The attached production-view image and documented signed, isolated scanner run directly show the after-fix converted chart labels while preserving native-value geometry.

Evidence

What I checked:

  • Current-main gap: Current main passes the snapshot currency directly to the chart and formats Y-axis values without a display multiplier, so this chart does not yet follow the shared preferred-currency behavior. (Sources/CodexBar/CostHistoryChartMenuView.swift:109, ee29794b9a1b)
  • Shared conversion contract: The branch derives a multiplier by converting one source-currency unit through the existing formatter; that formatter preserves the source amount and code when auto mode, same currency, or an unavailable rate applies. (Sources/CodexBarCore/UsageFormatter.swift:284, ee29794b9a1b)
  • Resolved cache finding: The render fingerprint now includes the display currency and exact multiplier bit pattern, so an exchange-rate-only refresh rebuilds an already hydrated chart. (Sources/CodexBar/CostHistoryChartMenuView.swift:952, d0fd6e197aa9)
  • Feature-history provenance: The visible-content fingerprint behavior dates to the cost-history cache work, which makes the display-currency and multiplier inputs appropriate cache keys. (Sources/CodexBar/StatusItemController+HostedSubmenus.swift:285, 0bb874930d6a)
  • Real behavior proof: The PR body provides a signed isolated production scanner run and attached offscreen production-view image showing converted CNY labels across the chart, totals, breakdowns, projects, and sessions while bars retain source-value proportions. (d0fd6e197aa9)
  • Release status: The PR head is rebased on current main and is not contained by any local release tag; v0.49.3 predates this unmerged work. (d0fd6e197aa9)

Likely related people:

  • steipete: Introduced the visible-content cost-history fingerprint and has substantial recent history in both the chart and hosted-submenu paths. (role: feature-history owner and recent area contributor; confidence: high; commits: 0bb874930d6a, 1e49619e7330, 8314a74e8613; files: Sources/CodexBar/CostHistoryChartMenuView.swift, Sources/CodexBar/StatusItemController+HostedSubmenus.swift)
  • Zihaoqi: Contributed the recent cost-estimate-context work in the same chart surface. (role: adjacent cost-history contributor; confidence: medium; commits: b65d6156a9b1; files: Sources/CodexBar/CostHistoryChartMenuView.swift)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (2 earlier review cycles)
  • reviewed 2026-08-12T10:28:09.859Z sha 09d6cfb :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-12T19:31:07.155Z sha 3a40760 :: needs real behavior proof before merge. :: none

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09d6cfb1a7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +289 to +292
return .costHistory(CostHistoryChartMenuView.renderFingerprint(
from: snapshot,
provider: provider,
displayCurrencyCode: self.costHistoryDisplayConversion(for: snapshot).currencyCode))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include the exchange rate in the submenu fingerprint

When a non-USD preferred currency's rate changes while the app is running, an already-hydrated cost-history submenu can keep showing values converted with the old multiplier: refreshHostedSubviewMenu compares this fingerprint before rebuilding, but the fingerprint only records the display currency code, while the actual CostHistoryChartMenuView also depends on costHistoryDisplayConversion(...).multiplier. Reopening the submenu after CurrencyExchange refreshes rates will therefore return early as “unchanged” whenever the code is still, for example, EUR, leaving stale converted cost labels until some unrelated rebuild occurs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — addressed in 3a40760. RenderFingerprint now carries costMultiplierBitPattern alongside the display currency code, and the fingerprint call site passes the resolved multiplier, so a mid-session exchange-rate refresh invalidates an already-hydrated submenu. The added test asserts that a rate-only change (same currency code, different multiplier) produces a different fingerprint.

vAhyThe and others added 5 commits August 12, 2026 12:58
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A hosted cost-history submenu is only rebuilt when its render fingerprint
changes. The fingerprint carried the display currency code but not the
conversion multiplier, so a live exchange-rate refresh mid-session left
already-hydrated charts showing values converted with the stale rate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@steipete
steipete force-pushed the fix/cost-history-preferred-currency branch from 3a40760 to d0fd6e1 Compare August 13, 2026 02:22
@steipete

Copy link
Copy Markdown
Owner

@clawsweeper re-review

The contributor branch is now rebuilt on current main at d0fd6e197aa9e97fd18fd758cab9ce00d665eba2. The PR body includes the signed isolated runtime/scanner proof, sanitized synthetic production-view image, full 841-selection matrix, and the same-currency/different-rate fingerprint regression.

@clawsweeper

clawsweeper Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

@clawsweeper clawsweeper Bot added proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 13, 2026
@steipete
steipete merged commit af5a67e into steipete:main Aug 13, 2026
15 of 17 checks passed
@steipete

Copy link
Copy Markdown
Owner

Landed and verified.

Testing and proof:

  • swift test --filter CostHistoryChartMenuView — 30/30 passed, including the same-currency 21.021.5 multiplier fingerprint regression.
  • swift test --filter ProviderArchitectureGatekeeperTests — 38/38 passed.
  • make check passed.
  • Full local matrix: 841 selections across 71 groups passed with zero retries or timeouts.
  • Structured Codex autoreview and TruffleHog reported no accepted/actionable finding.
  • A Developer ID-signed debug bundle was built and launched with a disposable home, Keychain disabled, network denied, deterministic CNY rates, and synthetic Codex JSONL. The packaged CLI exercised the production scanner; the attached offscreen production-view image covers the converted axis, day/model rows, total, projects, and conversations. The host was locked, so no claim is made that the image is a live-popup screenshot.
  • Exact-head CI passed after rerunning one unrelated Swift SDK installer crash: https://github.com/steipete/CodexBar/actions/runs/31660606259
  • ClawSweeper reviewed exact head d0fd6e197aa9e97fd18fd758cab9ce00d665eba2, accepted the proof, and found no actionable issue.

Merged as af5a67ec968de739ec038377440c239aff3110b7. The 0.49.4 changelog entry was added in 2080166870985748706efca6e200b3a53c065e3f; final-main CI passed: https://github.com/steipete/CodexBar/actions/runs/31662711748

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants