Skip to content

Codex: distinguish API estimates from subscription bills - #2181

Merged
steipete merged 14 commits into
steipete:mainfrom
Yuxin-Qiao:fix/codex-api-estimate-disclosure
Jul 16, 2026
Merged

Codex: distinguish API estimates from subscription bills#2181
steipete merged 14 commits into
steipete:mainfrom
Yuxin-Qiao:fix/codex-api-estimate-disclosure

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • label Codex local token-derived USD totals as API-equivalent estimates, not a bill or subscription value
  • apply the disclosure consistently to menu, inline dashboard, Settings, history submenu, Widget, CLI, and accessibility labels
  • keep non-Codex provider billing labels unchanged
  • provide the three required strings in every app localization

Why

CodexBar derives this number from local Codex JSONL token usage and public API prices. It is not the user's ChatGPT/Codex subscription bill and cannot measure the value of a Plus, Pro, or other flat-rate plan.

Scope

  • no authentication, pricing, token-accounting, cache, or scanner changes
  • no unrelated Linux platform-test or CI workflow changes
  • website/docs mockup churn removed from the merge path

Live CLI proof (redacted)

Ran the PR head against real local Codex and Claude history. Amounts and token totals are redacted; provider names and the changed disclosure text are preserved:

$ swift run --disable-sandbox --scratch-path /private/tmp/codexbar-pr2191-proof \
    CodexBarCLI cost --provider both --days 1

Codex API-equivalent estimate (not billed)
Today: $<redacted> · <redacted> tokens
Not a subscription bill or plan value · local usage × public API prices

Claude Cost (API-rate estimate)
Today: —
Estimated from local Claude logs at API rates; token totals include cache read/write tokens and may differ from Claude Code /status.

This demonstrates that the Codex section carries the new not-billed subscription disclosure while the non-Codex provider keeps its existing provider-specific API-rate wording. No project path, account identifier, session content, prompt, or credential is included.

Validation

  • live CodexBarCLI cost --provider both --days 1 output above
  • focused menu, inline-dashboard, Widget, CLI, and model regression suites
  • current-head GitHub Actions: both macOS shards, both Linux builds, lint, aggregate, and GitGuardian passed
  • SwiftFormat on all changed Swift sources and tests
  • git diff --check upstream/main...HEAD

@Yuxin-Qiao
Yuxin-Qiao marked this pull request as ready for review July 15, 2026 03:45

@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: 966c444d28

ℹ️ 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".

Comment on lines +174 to +177
[
L("Estimated from local Codex logs for the selected account."),
L("cost_estimate_hint"),
].joined(separator: "\n")

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 Show the bill disclaimer in inline summaries

When the user selects the Inline summary cost display style, Codex has no separate token-usage section, so this hint is only shown through costHistoryInlineDashboard, which appends the entire hint as one detailLines entry and InlineUsageDashboardContent renders each entry with .lineLimit(1). Because the new cost_estimate_hint is placed after a newline in the same string, the “may differ from your bill” disclaimer is clipped in that mode; split the Codex hints into separate dashboard detail rows or otherwise render this hint multiline.

Useful? React with 👍 / 👎.

@Yuxin-Qiao Yuxin-Qiao changed the title Clarify Codex cost estimates Codex: distinguish API estimates from subscription bills Jul 15, 2026

@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: 943b3d1e35

ℹ️ 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".

/* Cost estimation */
"cost_header_estimated" = "Cost (estimated)";
"cost_estimate_hint" = "Estimated from local logs · may differ from your bill";
"cost_header_estimated" = "API-equivalent estimate";

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 Keep API-estimate header scoped to Codex

This key is rendered unconditionally by the SwiftUI token-cost sections (Sources/CodexBar/MenuCardView.swift lines 216 and 761) for every provider with token-cost data, not just Codex. After changing the shared header to “API-equivalent estimate”, non-Codex cards such as OpenAI, Mistral, and Bedrock also get that label even though their hints describe Admin API, Mistral billing, or AWS Cost Explorer data; this contradicts the stated scope of keeping non-Codex labels unchanged. Please make the header provider-specific like the status-menu title or keep this shared key generic.

Useful? React with 👍 / 👎.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Addressed the two inline review findings in 3e6e63c.

  • scoped the stronger terminology to Codex only; shared cost copy remains unchanged for other providers
  • split the Codex inline disclosure into separate detail rows, with not a subscription bill or plan value visible independently rather than inside a single-line-clamped string
  • applied the same wording to both normal and --group-by project CLI cost output

Verification: focused menu/inline/CLI/widget Swift regression suite and make check both pass locally. The preceding Linux ARM64 failure is in an unrelated Claude CLI platform-gating test; this commit has a fresh CI run queued.

@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: 3e6e63c638

ℹ️ 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 sessionLabel = if provider == .bedrock || provider == .mistral {
"Latest billing day"
} else if provider == .codex {
"Today API est. · not billed"

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 Keep compact widget labels from appending cost

For Codex widget snapshots this new label is later used by CompactMetricFormatter for the compact .todayCost metric, which builds the title as "\($0.sessionLabel) cost" in Sources/CodexBarWidget/CodexBarWidgetViews.swift:211-213. With this value the compact widget renders Today API est. · not billed cost, reintroducing the cost wording immediately after the not-billed disclaimer and making the Codex estimate disclosure confusing; please either make the compact widget avoid appending cost for Codex estimates or store a separate compact label.

Useful? React with 👍 / 👎.

}

static func costMenuTitleForProvider(_ provider: UsageProvider) -> String {
provider == .codex ? L("codex_api_estimate_header") : self.costMenuTitle

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 Disclose Codex estimates inside the history submenu

When a Codex cost history submenu is available, costMenuVisibleDetailLines(..., hasSubmenu: true) returns no detail rows and native items omit the tooltip, so this title is the only changed top-level text before opening a submenu whose CostHistoryChartMenuView still labels the chart/axis and total as Cost/Est. total without the not-billed hint. In the submenu style, the dollar totals are still presented as cost, so users can miss the API-equivalent/not-billed boundary; pass provider-specific copy into the chart or add the disclaimer there.

Useful? React with 👍 / 👎.

Comment on lines +176 to +177
static func tokenUsageHeader(provider: UsageProvider) -> String {
provider == .codex ? L("codex_api_estimate_header") : L("cost_header_estimated")

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 Use the Codex estimate title in provider details

This adds a provider-specific token-usage header, but the Settings provider detail still renders Codex token USD as ProviderMetricInlineTextRow(title: L("Cost"), value: tokenUsage.sessionLine) and does not show tokenUsage.hintLine, so Settings continues to show the local Codex API-rate total as Cost with no not-billed disclosure. Please route that provider-detail row through the same provider-specific title/disclaimer path so the in-app Settings surface does not contradict the new Codex wording.

Useful? React with 👍 / 👎.

Comment on lines +384 to +386
let hintLines = Self.tokenUsageHintLines(provider: provider)
if hintLines.isEmpty == false {
details.append(contentsOf: hintLines)

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 Relabel inline Codex totals as estimates

When the user selects the Inline summary display, adding the Codex hint lines here still leaves the KPI title built above as 30d cost or Last N days Cost, so the visible dollar total in that summary is still labeled as cost rather than an API-equivalent estimate. Please make the historyTitle provider-specific for Codex in this inline dashboard, otherwise this surface still contradicts the new estimate/not-billed wording.

Useful? React with 👍 / 👎.

@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: 32693b4442

ℹ️ 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".

}

static func costMetricLabel(_ label: String, provider: UsageProvider) -> String {
provider == .codex ? label : "\(label) cost"

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 Normalize bare Codex widget labels

When entry.tokenUsage does not come from the new UsageStore labels—for example WidgetPreviewData.snapshot() and any persisted pre-upgrade WidgetSnapshot, whose TokenUsageSummary defaults are still Today/30d—this Codex branch returns the bare label unchanged. A compact Codex metric can then show a dollar value labelled only Today or 30d, with no API-estimate/not-billed disclosure until the main app rewrites the snapshot; please normalize bare Codex labels here or migrate all producers before suppressing the cost suffix.

Useful? React with 👍 / 👎.

.init(
title: usesLatestPrimary ? L("Latest") : L("Today"),
title: provider == .codex
? "\(L("Today")) · \(L("codex_api_estimate_header"))"

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 Relabel Codex inline chart accessibility

When provider == .codex and the inline dashboard is rendered, this branch relabels the visible KPI as an API-equivalent estimate, but the same model still sets the mini chart accessibility label to a string ending in cost trend, which MiniUsageBars exposes via .accessibilityLabel(...). VoiceOver users therefore still hear the Codex dollar chart described as a cost trend even though the visible UI says it is not a bill; make the accessibility label provider-specific too.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added 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. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Jul 16, 2026
@clawsweeper

clawsweeper Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed July 16, 2026, 10:29 AM ET / 14:29 UTC.

Summary
The PR relabels Codex token-derived dollar totals as API-equivalent estimates across the macOS app, widget, CLI, accessibility output, localizations, and focused tests while preserving other providers’ terminology.

Reproducibility: yes. at the source level: construct a Codex widget entry using a legacy TokenUsageSummary label such as Today, 7d, or 30d and render a non-compact switcher or history layout; those paths bypass the new CompactMetricFormatter normalization. A live widget reproduction was not supplied.

Review metrics: 2 noteworthy metrics.

  • Changed surface: 41 files; 279 added, 47 removed. The wording change spans multiple native UI, widget, CLI, test, and localization paths, so cross-surface consistency matters before merge.
  • Localization coverage: 23 localization catalogs changed. All added disclosure keys must remain semantically consistent with the Codex-only provider boundary.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦐 gold shrimp
Patch quality: 🦐 gold shrimp
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Normalize persisted Codex labels through every widget layout and add regression coverage for non-compact legacy snapshots.
  • [P1] Add redacted native menu or Settings and widget screenshots or a short recording showing the disclosure without truncation; redact private account and usage information.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The redacted live CLI output proves the CLI wording, but the native app and widget changes still need redacted screenshots or a recording from a real setup; after adding them, update the PR body to trigger review or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P1] Existing users with persisted pre-upgrade Codex widget snapshots can still see dollar values labeled only by a period in non-compact widget layouts, undermining the disclosure during upgrade until the main app republishes the snapshot.
  • [P1] The CLI proof establishes only one changed surface; native menu, Settings, accessibility, and widget presentation could still truncate, omit, or render the new wording poorly on a real macOS setup.

Maintainer options:

  1. Normalize labels before merge (recommended)
    Move legacy Codex period-label normalization into a shared path used by every widget layout and cover persisted Today/Nd snapshots with focused tests.
  2. Accept temporary upgrade inconsistency
    Merge while accepting that some existing non-compact widgets may lack the disclosure until CodexBar republishes their persisted snapshot.

Next step before merge

  • [P1] The contributor should repair the remaining shared widget compatibility path and add native real-behavior proof; automation cannot produce proof from the contributor’s macOS setup.

Security
Cleared: The diff changes presentation, localization, and tests without modifying authentication, secrets, dependencies, workflows, downloaded code, package resolution, or privileged execution paths.

Review findings

  • [P2] Normalize Codex labels in every widget layout — Sources/CodexBarWidget/CodexBarWidgetViews.swift:230-243
Review details

Best possible solution:

Normalize legacy Codex TokenUsageSummary labels at a shared snapshot/model or formatting boundary used by every widget layout, retain unchanged behavior for other providers, add coverage for legacy numeric windows across compact and non-compact layouts, and provide redacted native app and widget screenshots or a short recording.

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

Yes at the source level: construct a Codex widget entry using a legacy TokenUsageSummary label such as Today, 7d, or 30d and render a non-compact switcher or history layout; those paths bypass the new CompactMetricFormatter normalization. A live widget reproduction was not supplied.

Is this the best way to solve the issue?

No, not yet: provider-specific copy and compact compatibility handling are appropriate, but normalization belongs in a shared widget boundary so every layout handles persisted snapshots consistently.

Full review comments:

  • [P2] Normalize Codex labels in every widget layout — Sources/CodexBarWidget/CodexBarWidgetViews.swift:230-243
    The new compatibility helper is only used by CompactMetricFormatter. Regular provider, switcher, and history widget views still pass persisted TokenUsageSummary labels directly to tokenRowTitle, so an upgraded Codex widget with legacy Today, 7d, 30d, or 90d labels can continue displaying a dollar value without the API-estimate/not-billed disclosure. Apply the normalization at a shared snapshot/format boundary or route every widget layout through this helper.
    Confidence: 0.98

Overall correctness: patch is incorrect
Overall confidence: 0.97

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🦐 gold shrimp.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.

Label justifications:

  • P2: This is a bounded user-facing accuracy and upgrade-compatibility issue rather than an outage, data loss, or blocked setup path.
  • merge-risk: 🚨 compatibility: Persisted pre-upgrade widget snapshots can render differently after merge because only compact layouts normalize their legacy Codex period labels.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The redacted live CLI output proves the CLI wording, but the native app and widget changes still need redacted screenshots or a recording from a real setup; after adding them, update the PR body to trigger review or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

What I checked:

  • Remaining widget compatibility gap: The new Codex label normalization is implemented in CompactMetricFormatter, while regular provider, switcher, and history widget layouts continue passing TokenUsageSummary period labels directly to WidgetFormat.tokenRowTitle; persisted snapshots with labels such as Today, 7d, 30d, or 90d therefore remain undisclosed outside compact layouts. (Sources/CodexBarWidget/CodexBarWidgetViews.swift:230, 09f0fbec0b4f)
  • Prior finding continuity: The current commit expands compact normalization to arbitrary numeric day windows, resolving the previous review’s 7d/90d concern, but does not apply that normalization to the other widget rendering paths identified in the latest review discussion. (Sources/CodexBarWidget/CodexBarWidgetViews.swift:230, 09f0fbec0b4f)
  • Real CLI proof: The PR body includes redacted live output showing the changed Codex disclosure alongside unchanged Claude wording, which is credible after-fix proof for the CLI surface. (Sources/CodexBarCLI/CLICostCommand.swift:103, 09f0fbec0b4f)
  • Native proof remains incomplete: No screenshot or recording demonstrates the changed menu, inline dashboard, Settings, history submenu, accessibility presentation, or widget behavior on the contributor’s real macOS setup. (09f0fbec0b4f)
  • Automated validation: GitHub reports both macOS test shards, both Linux CLI builds, lint, aggregate validation, and GitGuardian successful on the current head; these checks support implementation quality but do not replace real native-app and widget proof. (09f0fbec0b4f)
  • Repository policy applied: The repository’s provider-silo guidance supports scoping the stronger estimate wording to Codex, and the current patch preserves provider-specific wording for Claude and other providers. (AGENTS.md, 6d71af30b84d)

Likely related people:

  • steipete: The repository owner is the strongest routing candidate for a cross-surface CodexBar terminology and upgrade-compatibility change spanning the menu app and widget. (role: recent merger and release owner; confidence: medium; commits: 5a0cbc07119a, 6d71af30b84d; files: Sources/CodexBarWidget/CodexBarWidgetViews.swift, Sources/CodexBar/UsageStore+WidgetSnapshot.swift, Sources/CodexBar/MenuCardView+Costs.swift)
  • ryoppippi: Release history credits ryoppippi with the local cost-usage scanner and original widget work, making them relevant to token-derived cost semantics and widget snapshot behavior. (role: feature introducer; confidence: medium; files: Sources/CodexBar/UsageStore+WidgetSnapshot.swift, Sources/CodexBarWidget/CodexBarWidgetViews.swift)
  • getogrand: Release history credits getogrand with stabilizing the Cost submenu, one of the primary UI surfaces modified by this PR. (role: adjacent feature contributor; confidence: medium; files: Sources/CodexBar/StatusItemController+CostMenuCard.swift, Sources/CodexBar/CostHistoryChartMenuView.swift)
  • Yuxin-Qiao: Beyond proposing this PR, Yuxin-Qiao has prior merged history in widget/provider-switcher behavior and localization infrastructure that overlaps the affected current-main surfaces. (role: recent adjacent contributor; confidence: medium; files: Sources/CodexBarWidget/CodexBarWidgetViews.swift, Sources/CodexBar/Resources/en.lproj/Localizable.strings)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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.

How this review workflow works
  • 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.
Review history (3 earlier review cycles)
  • reviewed 2026-07-16T02:12:53.792Z sha 5012166 :: needs real behavior proof before merge. :: [P1] Normalize legacy Codex labels before dropping the cost suffix | [P2] Make the inline chart accessibility label provider-aware
  • reviewed 2026-07-16T11:01:02.406Z sha 288e823 :: needs real behavior proof before merge. :: [P2] Normalize every legacy Codex day-window label
  • reviewed 2026-07-16T13:47:16.327Z sha 09f0fbe :: needs real behavior proof before merge. :: [P2] Normalize legacy labels in every widget layout

@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: f335ddc68e

ℹ️ 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".

var model = InlineUsageDashboardModel(
accessibilityLabel: "\(providerName) \(periodLabel) cost trend",
accessibilityLabel: provider == .codex
? "\(providerName) \(periodLabel) \(L(\"codex_api_estimate_header\")) trend"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove escaped quotes from interpolation

This string literal does not compile because the escaped quotes are inside a Swift string interpolation expression; Swift parses L(\"...\") as an unterminated interpolation/string. Use normal quotes inside the interpolation (for example L("codex_api_estimate_header")), otherwise any target that builds InlineUsageDashboardContent.swift fails before tests can run.

Useful? React with 👍 / 👎.

Comment on lines +24 to +27
let fallbackTitle = StatusItemController.costMenuFallbackAttributedTitle(
title: "API-equivalent estimate",
visibleDetailLines: visibleLines)
#expect(fallbackTitle.string == "API-equivalent estimate")

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 Update the submenu fallback title expectation

With the new hasSubmenu branch, a token section that has a non-empty hintLine now makes visibleLines contain that hint, so the fallback title built here becomes API-equivalent estimate <hint> instead of the bare title. This assertion still expects the old empty-detail result and will fail for exactly the fixture set up above; either update the expected string or make the fixture match the no-detail case.

Useful? React with 👍 / 👎.

Comment on lines +948 to +949
#expect(todayMetric.label.hasPrefix("Today · "))
#expect(historyMetric.label.hasPrefix("30d · "))

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 Expect the normalized Codex metric labels

For this .codex entry, staleToken uses the default Today/30d labels, and CompactMetricFormatter.costMetricLabel now rewrites those to Today API est. · not billed and 30d API est. · not billed before tokenRowTitle appends the stale-age suffix. These expectations still look for the old bare prefixes, so this widget test will fail once the production formatter exercises the new normalization.

Useful? React with 👍 / 👎.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the fix/codex-api-estimate-disclosure branch from 5ba5044 to 288e823 Compare July 16, 2026 09:48

@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: 288e82360d

ℹ️ 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".

Comment on lines +237 to +240
return switch label {
case "Today": "Today API est. · not billed"
case "30d": "30d API est. · not billed"
default: label

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 Normalize all bare Codex day-window labels

Fresh evidence in this revision is that the new fallback only rewrites Today and 30d, but pre-upgrade widget snapshots were produced from the configurable costUsageHistoryDays value, so Codex users who had a 7/90/etc. day window can have persisted labels like 7d or 90d. In that case the compact .last30DaysCost metric still renders a bare dollar value labelled only 7d until the main app republishes the snapshot, with no API-estimate/not-billed disclosure; please normalize other bare Nd window labels (or migrate decoded snapshots) as well.

Useful? React with 👍 / 👎.

@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: 09f0fbec0b

ℹ️ 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".

Comment on lines +233 to +234
static func costMetricLabel(_ label: String, provider: UsageProvider) -> String {
guard provider == .codex else { return "\(label) cost" }

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 Normalize Codex labels across widget layouts

Fresh evidence after the compact fix is that this normalization is confined to CompactMetricFormatter; the regular provider/switcher/history widget layouts still pass token.sessionLabel and token.last30DaysLabel directly to WidgetFormat.tokenRowTitle (for example SwitcherSmallUsageView and HistoryView). When the widget extension renders WidgetPreviewData.snapshot() or a persisted pre-upgrade Codex WidgetSnapshot whose TokenUsageSummary defaults are still Today/30d, those non-compact layouts continue to show dollar values with bare labels and no API-estimate/not-billed disclosure until the app rewrites the snapshot. Please normalize Codex token labels at decode/format time or in all widget render paths.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Added redacted live codexbar cost --provider both --days 1 proof to the PR body, showing the Codex not-billed disclosure alongside the unchanged Claude API-rate wording. @clawsweeper re-review

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 16, 2026

@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: 96363a7f57

ℹ️ 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".

Comment on lines +120 to +125
guard !hasSubmenu else {
guard provider == .codex else { return [] }
return tokenUsage?.hintLine?
.split(separator: "\n")
.map(String.init)
.filter { !$0.isEmpty } ?? []

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 Codex disclaimer without a submenu

When Codex has token-cost data but no history submenu (for example the cost-menu display is enabled while tokenSnapshot.daily is empty, so webItems.hasCostHistory is false), this submenu-only branch is skipped and the method falls through to the primary session/month lines, which omit hintLine whenever those lines exist. That leaves the visible menu row showing Codex dollar amounts under API-equivalent estimate without the new not a subscription bill or plan value disclosure unless the user discovers the tooltip; include the Codex hint in the no-submenu visible rows too.

Useful? React with 👍 / 👎.

…e-disclosure

# Conflicts:
#	Sources/CodexBar/InlineUsageDashboardContent.swift

@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: e5d2b76c9c

ℹ️ 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".

?? (historyDays == 1
? L("Today")
: historyDays == 30
? "30d"

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 Localize the Codex 30-day inline label

When the app is running in a non-English locale and the Codex inline dashboard uses the default 30-day window, this literal bypasses the existing localized 30d key, so the second KPI becomes mixed-language like 30d · API-equivalent estimate even though the previous 30-day cost label was localized through L("30d cost"). Use L("30d") for this period before appending the Codex-specific header.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit c613397 into steipete:main Jul 16, 2026
8 checks passed
@steipete

Copy link
Copy Markdown
Owner

Merged as c613397.

Verification:

  • Audited the complete 41-file PR diff and all 23 locale changes. No network, credential, process, or unrelated provider behavior was introduced.
  • Scoped menu and preferences disclosure changes to Codex at 31c8e34, and covered cached custom widget periods at 96363a7.
  • Initial focused proof passed 188 tests across 28 suites; CodexBarWidgetProviderTests then passed 46/46.
  • After Add high-contrast mode for inactive displays #2210 and Block background Chromium Keychain access #2225 changed main, merged origin/main non-force at e5d2b76. Resolved the single dashboard conflict by preserving both the new main Groq exclusion and the Codex multi-line disclosure.
  • Merged-branch focused proof passed 126 tests across 21 suites. make check passed locales, package checks, SwiftFormat, and SwiftLint with 0 violations.
  • Autoreview of the exact merged-result diff reported no findings and patch correct with 0.94 confidence; it specifically confirmed Codex-only scope and preserved Groq/non-Codex behavior.
  • Fresh exact head e5d2b76 passed GitGuardian, lint, changes, both Linux CLI jobs, lint-build-test, and both macOS shards. macOS shard durations: 40m18s and 21m44s.
  • GitHub reported MERGEABLE/CLEAN immediately before merge.

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

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants