Skip to content

feat: add AED to the preferred currency picker - #3186

Merged
steipete merged 2 commits into
steipete:mainfrom
samiashi:feat/aed-preferred-currency
Aug 25, 2026
Merged

feat: add AED to the preferred currency picker#3186
steipete merged 2 commits into
steipete:mainfrom
samiashi:feat/aed-preferred-currency

Conversation

@samiashi

@samiashi samiashi commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds AED (د.إ) to the preferred currency picker in Settings → General. Dirham-billed users currently convert the USD estimates by hand to compare them against a card statement.

Change

Three lines of source, no new exchange-rate logic:

  • CurrencyExchange.swift"AED" joins supportedCurrencies. Listing the code is what opens the requiresLiveRates gate (it returns true for any supported non-USD code), so selecting AED triggers the same daily open.er-api.com/v6/latest/USD live fetch + 24h cache path as every existing non-USD option; no fetch logic changes.
  • CurrencyExchange.swift"AED": 3.67 joins the hardcoded fallback table, read only before the first successful fetch. The dirham has been pegged to the dollar at ~3.6725 since 1997, so the fallback is unusually accurate. Existing fallback values are untouched.
  • PreferencesGeneralPane.swiftPreferredCurrencyOption.aed and its "AED (د.إ)" label.

No public API surface change: supportedCurrencies gains an element, its signature is unchanged.

Symbol choice

The Central Bank of the UAE published a national currency symbol in 2025 (a "D" with two horizontal bars), but it has no Unicode code point on shipping Apple platforms yet — U+20C0 is SOM SIGN and U+20C1 is SAUDI RIYAL SIGN; there is no DIRHAM SIGN scalar, so the glyph would render as tofu. The label therefore uses the traditional Arabic abbreviation د.إ, which stays renderable via the system font cascade. If/when Unicode assigns a code point, the label is a one-line update.

Per CBUAE guidance ("use one or the other"), rendered amounts show only the code — never symbol and code together.

Formatting

AED is a two-decimal currency and UsageFormatter.currencyString pins Locale(identifier: "en_US"). ICU has no د.إ symbol under that locale, so amounts render with the code prefix and two fraction digits:

Double(10).formatted(.currency(code: "AED").locale(Locale(identifier: "en_US")))     // "AED 10.00"
Double(0.42).formatted(.currency(code: "AED").locale(Locale(identifier: "en_US")))   // "AED 0.42"
Double(988.31).formatted(.currency(code: "AED").locale(Locale(identifier: "en_US"))) // "AED 988.31"

(These format an amount that is already in dirhams; conversion happens upstream in CurrencyExchange.) Because the locale is pinned, rendering is host-locale independent, and this matches how other currencies without a Latin symbol degrade under that same pinned locale — CZK 210.11. The menu-card screenshot below shows exactly this shape at runtime.

Validation

  • ./Scripts/lint.sh — swiftformat clean, swiftlint 0 violations, 0 serious in 1990 files
  • swift test --filter UsageFormatter46 tests in 2 suites passed
  • make test (full sharded suite) — every group that ran is green except one that is pre-existing and unrelated: CostUsagePricingTests records two 1e-12 float-tolerance issues in Codex pricing math, at CostUsagePricingTests.swift:424 and :471. Checking out clean main (b6a4ce968, no AED) and running swift test --filter CostUsagePricingTests reproduces both with identical deltas — 0.5640000000000001 and 0.0002920000000000004 — so it predates this branch and is untouched by it.
  • New coverage in UsageFormatterTests.swift:
    • USD → AED conversion equals the current table rate within epsilon, so convert(usdAmount:to:) resolves the dirham instead of returning nil (which would silently drop converted amounts)
    • convertedCostString(preferredCurrency: "AED") renders the AED code plus a decimal separator — the two-decimal behavior shown above
    • requiresLiveRates("AED") == true, proving selection actually opens the live-rate path rather than silently sitting on the hardcoded fallback

Real behavior proof

Fresh bundle built from this branch and relaunched — CodexBar 0.55.0 (129) on macOS 26.6.2 (arm64).

1. Settings → General — Preferred Currency resolves to AED (د.إ)

The new option appears in the picker (checked, last in the list) and the row value updates to it.

CodexBar Settings → General with Preferred Currency set to AED (د.إ), picker open with AED checked

2. Menu card — provider costs render as dirhams

Today AED 988.31, 30d AED 11,961.84, daily-chart peak AED 2,060 — code prefix, thousands separated, two decimals, matching the pinned-en_US expectation asserted in UsageFormatterTests.

CodexBar menu card showing today and 30-day Codex costs rendered in UAE dirhams

Rate provenance. Selecting AED opens the requiresLiveRates gate, and the app then fetched and cached a live dirham rate rather than resting on the fallback:

CodexBar.CurrencyExchangeRates["AED"] = 3.6725   (166 rates cached)
CodexBar.CurrencyExchangeLastFetch    = 2026-08-25 07:29:07 +0000

3.6725 is the live value from the rates payload, distinct from the hardcoded 3.67 fallback — so the amounts above came through the live path. Because the dirham is pegged, the two rates agree closely (which is what makes the fallback a safe pre-fetch default); the cached value and fetch timestamp, not the rendered figures, are what confirm the live fetch actually ran.

@clawsweeper

clawsweeper Bot commented Aug 25, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

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

@samiashi
samiashi force-pushed the feat/aed-preferred-currency branch from 3307310 to b3fad01 Compare August 25, 2026 07:03
@clawsweeper clawsweeper Bot added P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. 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 25, 2026
@clawsweeper

clawsweeper Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed August 25, 2026, 6:34 AM ET / 10:34 UTC.

ClawSweeper review

What this changes

The PR makes UAE dirham (AED) selectable as CodexBar’s display currency, adds its fallback rate and regression coverage, and includes a release-note entry.

Merge readiness

Ready for maintainer review

The PR is a focused extension of the existing currency path with targeted coverage and convincing after-fix runtime proof; no blocking patch defect was found.

Priority: P3
Reviewed head: 88f8a66038d724c3880048893bf3109defe4af00

Review scores

Measure Result What it means
Overall readiness 🦞 diamond lobster (5/6) The patch is focused, covered at the relevant seams, and supported by direct runtime evidence.
Proof confidence 🦞 diamond lobster (5/6) ✨ media proof bonus Sufficient (screenshot): After-fix screenshots directly show AED in the picker and rendered costs, while the included live cache value shows the shared rate-fetch path completed.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (screenshot): After-fix screenshots directly show AED in the picker and rendered costs, while the included live cache value shows the shared rate-fetch path completed.
Evidence reviewed 5 items AED follows the existing conversion contract: The branch adds AED to the supported-code list and fallback-rate table; the existing non-USD gate then makes it eligible for the shared cached live-rate path.
Picker and persistence stay aligned: AED is added to the existing preference enum, and focused tests assert the picker’s non-Auto values match the converter’s supported currencies and persist AED.
Conversion and live-rate regression coverage: The formatter tests cover USD/AED and cross-currency conversion, AED code formatting, and normalized AED live-rate eligibility.
Findings None None.
Security None None.

Live Verification

Command: swift test --filter UsageFormatterTests

Result: FAIL (failed) — execution before step 1 run: sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.23.0.tgz

sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.23.0.tgz

Assertions:

  • FAIL expect_output: Test run

How this fits together

CodexBar converts provider cost estimates into a user-selected display currency. The Settings picker persists that choice, then shared conversion and formatting logic presents the result in menu and dashboard cost views.

flowchart LR
  A[Provider cost] --> B[Currency conversion]
  C[Preferred currency setting] --> B
  B --> D[Live or fallback rate]
  D --> E[Formatted cost]
  E --> F[Menu and dashboard]
  C --> G[Settings picker]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Change surface 2 production files, 2 test files, 1 release-notes file The implementation remains narrowly scoped to the existing currency selection and conversion path.
Code versus test delta production +4/-1, tests +27/-1 The small production extension is accompanied by conversion, formatting, live-rate, picker-alignment, and persistence coverage.

Technical review

Best possible solution:

Merge the narrow AED addition through the established picker, conversion, live-rate cache, and formatter path after normal check and maintainer review.

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

Not applicable: this is a preference enhancement rather than a defect report, and the PR supplies direct after-fix picker and menu-card evidence.

Is this the best way to solve the issue?

Yes. Reusing the existing supported-currency list, live-rate gate, fallback table, and formatter is the narrowest maintainable implementation.

AGENTS.md: found and applied where relevant.

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

Labels

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.

Label justifications:

  • P3: This is a small display-currency enhancement with limited blast radius.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): After-fix screenshots directly show AED in the picker and rendered costs, while the included live cache value shows the shared rate-fetch path completed.
  • proof: sufficient: Contributor real behavior proof is sufficient. After-fix screenshots directly show AED in the picker and rendered costs, while the included live cache value shows the shared rate-fetch path completed.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. After-fix screenshots directly show AED in the picker and rendered costs, while the included live cache value shows the shared rate-fetch path completed.

Evidence

What I checked:

Likely related people:

  • steipete: Current-main blame attributes both the converter and preferred-currency picker to this author, who also authored the latest PR-head commit. (role: currency and settings feature owner; confidence: high; commits: 061593ca15d9, 88f8a66038d7; files: Sources/CodexBarCore/CurrencyExchange.swift, Sources/CodexBar/PreferencesGeneralPane.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 (3 earlier review cycles)
  • reviewed 2026-08-25T07:07:27.053Z sha b3fad01 :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-25T07:57:41.795Z sha c04a3ab :: needs real behavior proof before merge. :: none
  • reviewed 2026-08-25T08:07:47.867Z sha c04a3ab :: needs maintainer review before merge. :: none

Add the UAE dirham to Settings → General → Preferred Currency so
AED-billed users see converted spend estimates instead of converting
the USD figures by hand. Follows the established shape for adding a
currency: three source/test touch points, no new exchange-rate logic.

- Sources/CodexBar/PreferencesGeneralPane.swift — PreferredCurrencyOption.aed
  joins the picker with the label "AED (د.إ)". The 2025 CBUAE national
  dirham symbol has no Unicode code point on shipping Apple platforms
  yet (U+20C0 is SOM SIGN, U+20C1 is SAUDI RIYAL SIGN), so the label
  uses the traditional Arabic abbreviation to stay renderable in system
  fonts everywhere.
- Sources/CodexBarCore/CurrencyExchange.swift — "AED" joins
  supportedCurrencies, which is what opens the requiresLiveRates gate;
  selecting it triggers the same daily open.er-api.com live fetch with a
  24h cache used by every other non-USD option. Adds the hardcoded
  fallback "AED": 3.67 (USD peg), read only before the first successful
  fetch.
- Tests/CodexBarTests/UsageFormatterTests.swift — assert
  requiresLiveRates("AED"), proving selection actually triggers the
  live-rate path rather than silently sitting on the fallback.

Formatting needs no changes: UsageFormatter.currencyString pins
Locale(identifier: "en_US"), where ICU renders two-decimal AED, so
$10 becomes "AED 10.00", host-locale independent like CZK and KRW.
@samiashi
samiashi force-pushed the feat/aed-preferred-currency branch from b3fad01 to c04a3ab Compare August 25, 2026 07:53
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. 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 rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 25, 2026
Co-authored-by: Sami El Achi <mohamadashi7@gmail.com>

@steipete steipete left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The dirham option now uses its exact 3.6725 USD peg when offline, preserves native two-decimal AED formatting, supports both conversion directions and cross-currency spend, persists across settings reloads, and includes a regression ensuring every picker option matches the supported exchange-rate currencies. Contributor screenshots demonstrate the real picker and converted menu amounts. Focused formatter and settings tests plus repository lint all pass.

@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Aug 25, 2026
@steipete
steipete merged commit f10b605 into steipete:main Aug 25, 2026
9 checks passed
@steipete

Copy link
Copy Markdown
Owner

Landed in f10b605 after improving the offline fallback to the exact 3.6725 USD-to-AED peg and adding broader product coverage. The settings picker now offers the existing system-renderable Arabic dirham abbreviation, while actual amounts retain native AED code formatting with two fraction digits. Regression coverage verifies USD-to-AED, AED-to-USD and GBP-to-AED conversion, lowercase live-rate eligibility, persisted AED selection, exact formatted output, and parity between every picker option and the supported exchange currencies. Verification: 90 focused formatter and settings tests passed; make check passed with zero violations; the full local suite passed all 930 selections across 78 groups with zero failures, retries, or timeouts; and exact-head GitHub Actions run 32837620486 passed both macOS shards, Linux x64, arm64 and musl builds, lint, and security checks. Contributor credit and changelog included.

@samiashi
samiashi deleted the feat/aed-preferred-currency branch August 25, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦞 diamond lobster Very strong PR readiness with only minor 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