Skip to content

feat: model-centric usage & spend view across providers (Scope B from #2257)#2322

Open
Yuxin-Qiao wants to merge 1 commit into
steipete:mainfrom
Yuxin-Qiao:feat/model-centric-usage-spend
Open

feat: model-centric usage & spend view across providers (Scope B from #2257)#2322
Yuxin-Qiao wants to merge 1 commit into
steipete:mainfrom
Yuxin-Qiao:feat/model-centric-usage-spend

Conversation

@Yuxin-Qiao

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

Copy link
Copy Markdown
Contributor

Summary

This PR implements Scope B from #2257: a model-centric usage & spend view embedded in the existing Usage & Spend dashboard.

Instead of the previous per-currency "Model breakdown unavailable" placeholder, users now see a Models card with:

  • Daily stacked bar chart of total tokens across all providers, with an independent 7d / 30d / All range control.
  • Full model ranking showing every captured model, its total tokens, provider provenance, and share.
  • Cross-provider merging using trimmed, case-insensitive exact model-name matching. Token counts are aggregated across providers; cost remains grouped by currency to avoid mixing USD/EUR.
  • Kimi Code session scanner so Kimi token-only history can contribute to the model analysis without creating a fake currency group.

The card sits between "By subscription" and "Daily estimated spend" and does not add a new tab, route, or Overview navigation.

Design notes

  • All = up to the existing 365-day local-history contract.
  • The chart uses Top 5 + Other for legibility; the ranking still lists every model.
  • Model identity is intentionally conservative: only exact normalized names are merged. No fuzzy alias resolution yet.
  • New UI strings are localized across all supported languages.

Test plan

  • swift test --filter "SpendModelsPresentationTests|SpendDashboardModelTests|SpendDashboardKimiModelTests|KimiCodeSessionScannerTests|CostUsageDailyReportMergeTests|ShareStatsTests|UserFacingLocalizationCoverageTests|LocalizationLanguageCatalogTests|CostUsageFetcherTests" — 106 tests pass.
  • swift build succeeds.
  • Manual verification on macOS with Claude, Codex, and Kimi providers enabled.

Screenshots

7d

7d

30d

30d

All

All

Fixes #2257

@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: 0bb85adb5a

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

.frame(maxWidth: .infinity, minHeight: 220)
}
} else {
let modelHostGroupID = self.controller.model.groups.first?.id

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 Render token-only model history without a currency group

When local Kimi history is the only available source, KimiCodeSessionScanner emits currencyCode: "XXX" and SpendDashboardModel.currencyCode filters that out of groups, so this host-group path is never reached and the groups.isEmpty branch above shows “No local cost history yet” despite model.modelAnalysis containing Kimi rows. Kimi-only users therefore cannot see the new model history unless there is also a priced currency group; render the global model section outside currency groups or synthesize a host for token-only sources.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. labels Jul 19, 2026
@clawsweeper

clawsweeper Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed July 19, 2026, 6:00 AM ET / 10:00 UTC.

Summary
The branch replaces the per-currency model placeholder with an embedded Models card, aggregates model usage across providers, adds Kimi Code local-history scanning, expands model-token parsing, localizes the UI, and adds focused tests.

Reproducibility: not applicable. this is a new dashboard capability rather than a report of broken existing behavior. The contributor supplied direct screenshots of the changed 7-day, 30-day, and All states plus a manual macOS verification claim.

Review metrics: 2 noteworthy metrics.

  • Patch surface: 46 files affected; 2,328 additions and 83 deletions. The feature spans dashboard UI, shared parsing models, a new Kimi scanner, tests, generated metadata, and every localization catalog.
  • Focused coverage: 4 new focused test files; multiple existing model/parser suites extended. The implementation introduces new presentation, aggregation, local-history, and parser behavior that needs review as one cohesive feature.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

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

Rank-up moves:

  • Obtain explicit maintainer approval for the exact-name, non-deduplicated aggregation contract or narrow the feature to avoid presenting it as a canonical total.

Risk before merge

  • [P1] Merging makes exact normalized-name aggregation a user-visible interpretation of cross-provider usage even though the linked discussion identifies canonical identity and double-counting as unresolved; users may treat duplicated or provider-specific records as a single model total.

Maintainer options:

  1. Approve the source-additive contract (recommended)
    Merge only after a maintainer explicitly accepts exact-name, non-deduplicated aggregation as the intended meaning of the displayed totals.
  2. Narrow before merge
    Remove or provider-scope the aggregate totals until canonical identity and duplicate-source handling have an approved design.
  3. Pause for design resolution
    Keep the feature discussion open and defer this broad dashboard change until the aggregation policy is settled.

Next step before merge

  • [P2] The remaining blocker is approval of a user-facing aggregation policy; there is no narrowly defined mechanical repair to dispatch.

Maintainer decision needed

  • Question: Should CodexBar ship an embedded cross-provider Models card that sums exact normalized model names without cross-source deduplication or canonical alias mapping?
  • Rationale: The implementation is technically bounded and has direct UI proof, but the linked feature discussion identifies this aggregation rule as a user-facing policy decision rather than an established existing contract.
  • Likely owner: steipete — The remaining question is product acceptance of a new dashboard interpretation, not a narrow mechanical repair.
  • Options:
    • Approve the conservative MVP (recommended): Accept exact normalized-name grouping as source-additive reporting and require clear user-facing wording that it is not canonicalized or deduplicated.
    • Narrow the feature: Keep model history provider-scoped until canonical identity and double-counting rules are designed.
    • Defer Scope B: Pause this PR until the linked discussion establishes a durable aggregation and cost-display contract.

Security
Cleared: The patch adds local parsing and dashboard presentation code but does not introduce new dependency sources, secret handling, privileged execution, workflow changes, or supply-chain behavior.

Review details

Best possible solution:

Decide and document the supported aggregation contract first: either land this as clearly source-additive exact-name reporting with prominent limits, or narrow it until canonical identity and double-counting handling have a maintainer-approved design.

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

Not applicable: this is a new dashboard capability rather than a report of broken existing behavior. The contributor supplied direct screenshots of the changed 7-day, 30-day, and All states plus a manual macOS verification claim.

Is this the best way to solve the issue?

Unclear. The UI and data path are coherent, but the linked discussion shows that exact-name aggregation without canonical aliases or source deduplication is a product-policy choice that needs explicit approval.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The PR provides direct screenshots of the changed native dashboard across all three model-card ranges and describes manual verification with the relevant providers enabled.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a substantial but non-emergency dashboard feature whose acceptance depends on a bounded product decision.
  • merge-risk: 🚨 other: The PR introduces a new user-facing interpretation of cross-provider model totals that can be misleading without an approved identity and deduplication contract.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (screenshot): The PR provides direct screenshots of the changed native dashboard across all three model-card ranges and describes manual verification with the relevant providers enabled.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR provides direct screenshots of the changed native dashboard across all three model-card ranges and describes manual verification with the relevant providers enabled.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR provides direct screenshots of the changed native dashboard across all three model-card ranges and describes manual verification with the relevant providers enabled.
Evidence reviewed

What I checked:

  • Feature scope: The branch adds a global model-analysis surface and embeds it in the existing Usage & Spend dashboard rather than adding a new route or tab. (Sources/CodexBar/PreferencesSpendDashboardPane.swift:49, 73219498dc79)
  • Aggregation policy: The new model-analysis model aggregates cross-provider rows using normalized model names while retaining source provenance, which is the central product-policy surface of this PR. (Sources/CodexBar/SpendDashboardModel.swift:66, 73219498dc79)
  • Linked design discussion: The linked canonical discussion remains open and explicitly identifies canonical model identity, cross-currency interpretation, and double-counting as decisions to settle before a model-centric aggregate view is accepted.
  • Behavior proof: The PR body includes direct macOS screenshots for the 7-day, 30-day, and All model-card states and reports manual verification with Claude, Codex, and Kimi enabled. (73219498dc79)
  • Repository policy: The repository guidance calls for focused parser/provider validation and stable model-state seams; this PR includes model, presentation, Kimi scanner, merge, and localization coverage, but its product direction still needs a human decision. (AGENTS.md:1, 1d307430a13d)

Likely related people:

  • steipete: The linked feature discussion is explicitly marked for maintainer review and product direction in this repository; no stronger file-history owner could be established from the available read-only inspection. (role: likely product decision owner; confidence: medium; files: Sources/CodexBar/PreferencesSpendDashboardPane.swift, Sources/CodexBar/SpendDashboardModel.swift)
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 (6 earlier review cycles)
  • reviewed 2026-07-19T07:54:52.652Z sha 0bb85ad :: found issues before merge. :: [P2] Render token-only model history without a currency group
  • reviewed 2026-07-19T08:04:22.394Z sha 0bb85ad :: found issues before merge. :: [P2] Render token-only model history without a currency group
  • reviewed 2026-07-19T08:09:43.918Z sha 0bb85ad :: found issues before merge. :: [P2] Render token-only model history without a currency group
  • reviewed 2026-07-19T08:43:53.308Z sha 92714de :: found issues before merge. :: [P2] Render the Models card when no priced group exists | [P2] Choose a visible metric for spend-only model history
  • reviewed 2026-07-19T09:22:31.848Z sha 37f7807 :: found issues before merge. :: [P2] Render the Models card without a priced currency group
  • reviewed 2026-07-19T09:35:24.879Z sha ff057d5 :: needs changes before merge. :: [P2] Render the Models card without a priced currency group

@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: 18a58d70fb

ℹ️ 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 +332 to +337
inputs.append(SpendDashboardModel.ProviderInput(
id: "kimi:local",
provider: .kimi,
displayName: "Kimi Code CLI",
modelProviderName: ProviderDescriptorRegistry.descriptor(for: .kimi).metadata.displayName,
snapshot: snapshot))

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 Preserve Kimi results through forced refresh reconciliation

When the user clicks Refresh, the controller runs a .forceRefresh load and then a .captureOnly reconciliation; merge(outcome:capture:) only carries forced loader outputs whose ids are in forcedCodexIDs or barrierFailed, so this new kimi:local input is dropped because Kimi has no captured publication and is not a Codex account. In a Kimi-enabled dashboard, a successful manual refresh therefore removes the freshly scanned Kimi model history from loadedInputs instead of displaying it.

Useful? React with 👍 / 👎.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the feat/model-centric-usage-spend branch 2 times, most recently from 811bc6f to 92714de Compare July 19, 2026 08:36

@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: 92714def17

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

@State private var selectedDay: Date?

private var presentation: SpendModelsPresentation {
SpendModelsPresentation(analysis: self.analysis, metric: .tokens)

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 Render spend-only model history

When the model data comes from a cost-only source such as Bedrock, the breakdowns carry costUSD but no totalTokens, so hard-wiring the section to .tokens makes every row value nil and presentation.points empty; the card then renders “No model-level history” even though analysis.dailyValues contains spend values. Choose .estimatedSpend when token coverage is unavailable, or expose the intended metric switch, so spend-only providers remain visible.

Useful? React with 👍 / 👎.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the feat/model-centric-usage-spend branch 2 times, most recently from 37f7807 to fe82c42 Compare July 19, 2026 09:25

@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: 37f78075d2

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

for line in data.split(separator: 0x0A, omittingEmptySubsequences: true) {
guard let event = try? decoder.decode(WireEvent.self, from: Data(line)),
event.type == "usage.record",
event.usageScope == "turn",

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 Accept unscoped Kimi usage records

Kimi Code 0.14 wire.jsonl rows in the same ~/.kimi-code/sessions/.../agents/... layout can omit usageScope (see ccusage#1261), while scoped session rows are the cumulative ones to skip. With the strict event.usageScope == "turn" guard, those installs have every valid usage row ignored and scan returns nil/no local model history; please treat a missing scope as a turn record and continue excluding explicit session rows.

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: fe82c42ca0

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

fileManager: FileManager = .default,
historyDays: Int = defaultHistoryDays,
now: Date = Date(),
calendar: Calendar = .current) -> CostUsageTokenSnapshot?

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 Gregorian dates for Kimi history keys

When the user’s macOS calendar is non-Gregorian (for example Buddhist), this default calendar makes CostUsageLocalDay.key write Kimi daily entries with years such as 2569. The spend dashboard later normalizes and parses provider dates with a Gregorian calendar, so those Kimi entries fall outside the current 2026 window and the local model history disappears. Please generate Kimi snapshot day keys with a Gregorian calendar that preserves the current time zone, as the dashboard model does.

Useful? React with 👍 / 👎.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the feat/model-centric-usage-spend branch from fe82c42 to ff057d5 Compare July 19, 2026 09:32

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

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


private static func normalizedDays(_ value: Int) -> Int {
value == 7 ? 7 : 30
[7, 30, 365].contains(value) ? value : 30

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 the overview range in sync with the picker

When settingsSpendDashboardDays is already 365 (or any caller invokes selectDays(365), which this now persists), the controller builds 365-day currency/provider totals, but the header picker in PreferencesSpendDashboardPane still only offers 7 and 30 tags. The pane can therefore open with no matching segmented selection while silently showing an unsupported 365-day overview; either keep this normalization to the picker’s 7/30 options or add the All option to that picker.

Useful? React with 👍 / 👎.

Implements Scope B from steipete#2257.

- Replace per-currency "model breakdown unavailable" panel with an
  embedded Models card showing daily stacked usage bars and a full
  model ranking.
- Merge models across providers by trimmed, case-insensitive exact
  name matching; keep token aggregation separate from cost to avoid
  cross-currency sums.
- Add independent 7d / 30d / All range control for the model card
  without affecting the existing Overview 30d spend semantics.
- Add Kimi Code session scanner so Kimi token-only history can
  contribute to the cross-provider model analysis.
- Localize new UI strings across all supported languages.
- Adopt upstream completeModelSummaries filtering for models list while
  keeping modelAnalysis built from all summaries for partial coverage.
- Regenerate CodexParserHash for merged Vendored/CostUsage sources.

Tests: SpendModelsPresentationTests, SpendDashboardModelTests,
SpendDashboardKimiModelTests, KimiCodeSessionScannerTests,
CostUsageDailyReportMergeTests, ShareStatsTests,
UserFacingLocalizationCoverageTests (63 tests).

Co-authored-by: Cursor <cursoragent@cursor.com>
@Yuxin-Qiao
Yuxin-Qiao force-pushed the feat/model-centric-usage-spend branch from ff057d5 to 7321949 Compare July 19, 2026 09:57
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. 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: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. 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.

Feature discussion: model-centric token / cost view across providers

1 participant