diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f02ca107..5b121bddb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Packaging: skip slow widget App Intents metadata during dev restarts and preserve the previous app bundle if required metadata generation times out. - Claude: keep Team and Personal Max plan-utilization history separate when the same email appears on multiple Claude accounts (#213). - Claude: keep the last successful usage card visible across transient probe timeouts while still clearing stale data after Claude auth changes. +- Claude: label Extra usage denominators as the monthly cap so recharge balances are not confused with the maximum spend limit (#975). - Claude: wait for the CLI usage panel to finish rendering after the Current session label so slow Claude Code builds do not produce false "Missing Current session" errors (#959). - Claude: label five-hour session pace as "Projected empty" so it is not confused with the reset countdown (#960). - Claude: show Enterprise spend-limit usage in automatic menu bar metrics and expose the Extra usage metric picker when spend data is available (#964). diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift index cd28ca9eb2..6e2b4eb964 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift @@ -931,7 +931,7 @@ extension ClaudeUsageFetcher { used: normalized.used, limit: normalized.limit, currencyCode: code, - period: isSpendLimit ? "Spend limit" : "Monthly", + period: isSpendLimit ? "Spend limit" : "Monthly cap", resetsAt: nil, updatedAt: Date()) } diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift index 6bd41e8068..f270d96159 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift @@ -957,7 +957,7 @@ private enum ClaudeWebExtraUsageCost { used: usedAmount, limit: limitAmount, currencyCode: currencyCode, - period: "Monthly", + period: "Monthly cap", resetsAt: nil, updatedAt: Date()) } diff --git a/Tests/CodexBarTests/ClaudeOAuthTests.swift b/Tests/CodexBarTests/ClaudeOAuthTests.swift index 654318531b..e180075db3 100644 --- a/Tests/CodexBarTests/ClaudeOAuthTests.swift +++ b/Tests/CodexBarTests/ClaudeOAuthTests.swift @@ -176,6 +176,7 @@ struct ClaudeOAuthTests { #expect(snap.providerCost?.currencyCode == "USD") #expect(snap.providerCost?.limit == 20.5) #expect(snap.providerCost?.used == 3.25) + #expect(snap.providerCost?.period == "Monthly cap") } @Test @@ -195,6 +196,7 @@ struct ClaudeOAuthTests { #expect(snap.providerCost?.currencyCode == "USD") #expect(snap.providerCost?.limit == 20) #expect(snap.providerCost?.used == 5.2) + #expect(snap.providerCost?.period == "Monthly cap") } @Test diff --git a/Tests/CodexBarTests/ClaudeUsageTests.swift b/Tests/CodexBarTests/ClaudeUsageTests.swift index 8f01e9c777..b52ed973d7 100644 --- a/Tests/CodexBarTests/ClaudeUsageTests.swift +++ b/Tests/CodexBarTests/ClaudeUsageTests.swift @@ -721,7 +721,7 @@ struct ClaudeUsageTests { #expect(cost?.currencyCode == "EUR") #expect(cost?.limit == 20) #expect(cost?.used == 0) - #expect(cost?.period == "Monthly") + #expect(cost?.period == "Monthly cap") } @Test diff --git a/Tests/CodexBarTests/ClaudeWebEnterpriseUsageTests.swift b/Tests/CodexBarTests/ClaudeWebEnterpriseUsageTests.swift index b11ffa6018..dcd5e1dddc 100644 --- a/Tests/CodexBarTests/ClaudeWebEnterpriseUsageTests.swift +++ b/Tests/CodexBarTests/ClaudeWebEnterpriseUsageTests.swift @@ -38,6 +38,6 @@ struct ClaudeWebEnterpriseUsageTests { #expect(parsed.extraUsageCost?.used == 41.32) #expect(parsed.extraUsageCost?.limit == 1000) #expect(parsed.extraUsageCost?.currencyCode == "USD") - #expect(parsed.extraUsageCost?.period == "Monthly") + #expect(parsed.extraUsageCost?.period == "Monthly cap") } } diff --git a/Tests/CodexBarTests/MenuCardModelTests.swift b/Tests/CodexBarTests/MenuCardModelTests.swift index 305467631b..fcc8e0c5f0 100644 --- a/Tests/CodexBarTests/MenuCardModelTests.swift +++ b/Tests/CodexBarTests/MenuCardModelTests.swift @@ -674,6 +674,48 @@ struct MiniMaxMenuCardModelTests { } } +struct ClaudeMenuCardCostTests { + @Test + func `claude extra usage labels monthly denominator as cap`() throws { + let now = Date() + let metadata = try #require(ProviderDefaults.metadata[.claude]) + let snapshot = UsageSnapshot( + primary: RateWindow(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil), + secondary: nil, + tertiary: nil, + providerCost: ProviderCostSnapshot( + used: 5, + limit: 20, + currencyCode: "USD", + period: "Monthly cap", + updatedAt: now), + updatedAt: now, + identity: nil) + + let model = UsageMenuCardView.Model.make(.init( + provider: .claude, + metadata: metadata, + snapshot: snapshot, + credits: nil, + creditsError: nil, + dashboard: nil, + dashboardError: nil, + tokenSnapshot: nil, + tokenError: nil, + account: AccountInfo(email: nil, plan: nil), + isRefreshing: false, + lastError: nil, + usageBarsShowUsed: false, + resetTimeDisplayStyle: .countdown, + tokenCostUsageEnabled: false, + showOptionalCreditsAndExtraUsage: true, + hidePersonalInfo: false, + now: now)) + + #expect(model.providerCost?.spendLine == "Monthly cap: $5.00 / $20.00") + } +} + struct MenuCardModelTests { @Test func `builds metrics using remaining percent`() throws {