Conversation
…led providers (chattymin#200) chattymin#224 labeled every $ row from a Claude plan flag, so a Max+Grok user saw "(API-equiv.)" on a real bill. Keep the qualifier on the leverage row only and compute $Y from per-provider costIsEstimate.
shawnla90
left a comment
There was a problem hiding this comment.
Built and ran this same feature on my own machine last week (branch linked in #200), so two notes from that experience:
1. The multiple mixes subscriptions. monthAPIEquivalentCost sums every costIsEstimate provider (Claude, Codex, Gemini, ...) but subscriptionLeverage gates on the Claude plan and divides by one plan price. A Max user who also runs Codex CLI (covered by a ChatGPT subscription, not the Claude plan) gets an inflated 'leverage' number: on my machine, August is ~$2,783 of Claude API-equivalent, and Codex estimates on top would push the displayed multiple past what the Max plan actually delivered. Two clean fixes: scope the numerator to claude_code (the plan the gate reads), or keep the mixed sum and make the row's copy say what it covers. I'd do the former — the hint text says the price is for the Max/Pro/Team row, so users will type their Claude plan price.
2. The price field writes defaults per keystroke. TextField("0", value: $store.monthlyPlanPrice, ...) binds straight to the store, and the didSet writes to UserDefaults on every edit. The scan-roots field from #187 established the draft + commit-on-submit/blur pattern for exactly this reason, and CustomScanRootsTests.testSettingsUsesProviderPickerAndCommitsOnSubmit locks it there ('direct TextField binding to the store writes on every keystroke'). Worth matching that pattern here so the convention holds.
Neither blocks the shape — per-provider costIsEstimate is the right structure and matches the #224 close. Happy to test the branch on my dual-login setup once these land.
…ymin#200) Gemini/Codex estimates ride other subscriptions, so mixing them into the Max multiple inflates it. Settings was writing UserDefaults on every keystroke; match the chattymin#187 draft+commit field.
|
Landed in c8d7c71. 1. Mixed subscriptions. 2. Per-keystroke defaults. Plan price uses a local draft and commits on submit, blur, and leaving Settings — same pattern as custom scan roots (#187). Happy to have you run the dual-login setup against this commit. |
Summary
This is a new PR for #200, not a revival of #224.
#224 was closed because a Claude Max/Pro/Team flag stamped
(API-equiv.)on every$row and on the combined header. Six providers report$. Grok / OpenCode / Hermes$is a server bill, not a ModelPricing estimate. A Max user who also uses Grok would have seen “(API-equiv.)” on Grok — the opposite of the truth. The close comment also asked for the leverage ratio that motivated the issue ($100/moagainst$610API-equivalent → 6.1×). Caption-only paid seven localization columns with no teaching value.This PR takes the shape the close comment asked for:
Per-provider
costIsEstimate(defaulttrue= table-priced). Grok, OpenCode, and Hermes setfalse.No
(API-equiv.)caption on today / week / month / per-provider rows, or on the menu bar. Bare$stays the convention there.Optional Settings field Monthly plan price ($). Default
0= off. No hardcoded price table.When the Claude credential is Max/Pro/Team and the field is set and estimate
$Y > 0, the popover shows one extra line under week/month:plan $100.00/mo · API-equiv $610.00 · 6.1×API-key / Free keep today’s presentation. The API-equivalent number is still shown; it is not hidden or zeroed.
Closes nothing until you say so — #200 stays the tracking issue. @shawnla90 offered to take Settings/leverage after #224; I am taking that follow-up here so there is one PR, not two.
Why this is not #224 again
UsageProvider.costIsEstimateon each source(API-equiv.)on every cost row + combined headerZ×$X,$Yfrom estimates,Z× = Y/X$Yexcludes itOwner scenario from the close comment: Claude Max month
$610estimate + Grok$50bill + plan$100.$700.00(unlabeled — still includes the bill).plan $100.00/mo · API-equiv $610.00 · 6.1×.$, no API-equiv caption.$Yis not== "claude_code". Gemini (table-priced) and any future estimate source count. Codex still reports cost but zerostotalCostinfetchDaily(pre-existing), so it does not move$Ytoday.Type of change
UI changes
$unlabeled for everyone. No plan-price field.$on every cost row and in the menu bar. Max/Pro/Team only, and only if Settings has a price> 0: one caption under week/month with plan / API-equiv / multiplier.0to hide the row. Default0.$12.3(compact).API-equiv, no×.Canonical
assets/screenshots are release-time; not updated in this PR.Design notes (so review does not re-open #224 questions)
reportsCoststill means “draw dollars at all” (Cursor / Copilot / Kiro / Pi / Antigravity stay token-only).costIsEstimateonly matters when dollars exist.LimitStatus.isFlatRateSubscription(max/pro/team, case-insensitive) ∧monthlyPlanPrice > 0∧monthAPIEquivalentCost > 0. Plan lives onLimitStatusso generic totals never branch onproviderID.TextField(value:format:)does not reliably commit empty →0. The hint says set to 0 to hide, and negatives clamp to0.Test plan
swift test— 907 passed, 11 skipped, 0 failures./scripts/test-gate.sh— logic-core line coverage 91.30% (≥ 75%)$includes bills;$Yand6.1×do not (testMonthAPIEquivalentCostExcludesBilledProviders). Afuture_bill_xyzbilled source is in the same fixture so exclusion is the flag, notid == "grok".$Yis not a Claude allow-list: unknownfuture_tool_xyzestimate of$40enters$Yand the ratio (testMonthAPIEquivalentCostIncludesUnknownEstimateProvider).0/ Free / no limits /$Y = 0→ no row. Pro / Team /PROopen it (testSubscriptionLeverageGates,testLeverageOpensForProAndTeam).API/×(testLeverageDoesNotAppearInMenuBar).(API-equiv.)on every cost row (testPopoverLeverageDoesNotRelabelEveryCostRow).testCostIsEstimateIsPerProviderNotAClaudeFlag).LocalizationInterpolationTests).Checklist
swift buildandswift testpass locallyRelated