diff --git a/Sources/CodexBarCore/CostUsageFetcher.swift b/Sources/CodexBarCore/CostUsageFetcher.swift index e1d0e74703..07612f3d65 100644 --- a/Sources/CodexBarCore/CostUsageFetcher.swift +++ b/Sources/CodexBarCore/CostUsageFetcher.swift @@ -335,7 +335,8 @@ public struct CostUsageFetcher: Sendable { private static func resolvedScannerOptions( _ override: CostUsageScanner.Options?, provider: UsageProvider, - codexHomePath: String?) -> CostUsageScanner.Options + codexHomePath: String?, + allowVertexClaudeFallback: Bool = false) -> CostUsageScanner.Options { var options = override ?? CostUsageScanner.Options() // Provider-specific by design: Codex managed profiles relocate sessions and archived_sessions roots. @@ -346,6 +347,11 @@ public struct CostUsageFetcher: Sendable { options.codexSessionsRoot = URL(fileURLWithPath: codexHomePath, isDirectory: true) .appendingPathComponent("sessions", isDirectory: true) } + if provider == .vertexai { + options.claudeLogProviderFilter = allowVertexClaudeFallback ? .all : .vertexAIOnly + } else if provider == .claude { + options.claudeLogProviderFilter = .excludeVertexAI + } return options } @@ -387,12 +393,10 @@ public struct CostUsageFetcher: Sendable { var options = Self.resolvedScannerOptions( overrideScannerOptions, provider: provider, - codexHomePath: codexHomePath) + codexHomePath: codexHomePath, + allowVertexClaudeFallback: allowVertexClaudeFallback) // Rolling window is inclusive, so a 30-day display starts 29 days before `now`. let since = options.calendar.date(byAdding: .day, value: -(clampedHistoryDays - 1), to: now) ?? now - let scopedCodexHomePath = codexHomePath?.trimmingCharacters(in: .whitespacesAndNewlines) - // Provider-specific by design: scoped Codex homes exclude ambient Pi sessions from managed-profile totals. - let shouldMergePiUsage = provider != .codex || scopedCodexHomePath?.isEmpty != false await Self.refreshPricingIfAllowed( options: PricingRefreshOptions( provider: provider, @@ -406,7 +410,6 @@ public struct CostUsageFetcher: Sendable { Self.configureScannerRefresh( &options, provider: provider, - allowVertexClaudeFallback: allowVertexClaudeFallback, forceRefresh: forceRefresh, bypassScannerDebounce: bypassScannerDebounce) var resolvedPiOptions = overridePiScannerOptions ?? PiSessionCostScanner.Options() @@ -423,7 +426,7 @@ public struct CostUsageFetcher: Sendable { let localScanOptions = LocalTokenScanOptions( allowVertexClaudeFallback: allowVertexClaudeFallback, includePiSessions: includePiSessions, - shouldMergePiUsage: shouldMergePiUsage, + codexHomePath: codexHomePath, scanOptions: scanOptions, piOptions: piOptions) let scanResult = try await Self.loadLocalTokenScanResult( @@ -482,7 +485,7 @@ public struct CostUsageFetcher: Sendable { private struct LocalTokenScanOptions: Sendable { let allowVertexClaudeFallback: Bool let includePiSessions: Bool - let shouldMergePiUsage: Bool + let codexHomePath: String? let scanOptions: CostUsageScanner.Options let piOptions: PiSessionCostScanner.Options } @@ -529,6 +532,7 @@ public struct CostUsageFetcher: Sendable { var sessions: [CostUsageSessionBreakdown] = [] var piDaily: CostUsageDailyReport? var staleSnapshotUpdatedAt: Date? + // Provider-specific by design: only Codex builds project and session breakdowns from its local cache. if provider == .codex { let roots = CostUsageScanner.codexSessionsRoots(options: options.scanOptions) let cache = CostUsageScanner.codexCache( @@ -556,8 +560,10 @@ public struct CostUsageFetcher: Sendable { sessionRoots: roots) } } - if options.includePiSessions, - provider == .claude || (provider == .codex && options.shouldMergePiUsage) + if Self.shouldMergePiSessions( + provider: provider, + includePiSessions: options.includePiSessions, + codexHomePath: options.codexHomePath) { let piReport = try PiSessionCostScanner.loadDailyReportCancellable( provider: provider, @@ -567,6 +573,7 @@ public struct CostUsageFetcher: Sendable { options: options.piOptions, checkCancellation: checkCancellation) try checkCancellation() + // Provider-specific by design: only Codex stores the Pi-only report for project merge. if provider == .codex { piDaily = piReport } @@ -604,7 +611,7 @@ public struct CostUsageFetcher: Sendable { { guard options.isAllowed, options.retryUnknown, - options.provider == .codex || options.provider == .claude + self.usesModelsDevPricing(options.provider) else { return } if options.inBackground { @@ -631,10 +638,11 @@ public struct CostUsageFetcher: Sendable { cacheRoot: URL?, client: ModelsDevClient) -> UnknownPricingRefreshRequest? { - guard provider == .codex || provider == .claude else { return nil } + guard let providerID = self.modelsDevRefreshProviderID(for: provider) else { return nil } let unknownModelIDs = Set(daily.data.flatMap { entry in entry.modelBreakdowns?.compactMap { breakdown -> String? in guard breakdown.costUSD == nil else { return nil } + // Provider-specific by design: only Codex filters out its own unattributed model names. if provider == .codex, CostUsagePricing.isCodexUnattributedModel(breakdown.modelName) { @@ -646,7 +654,7 @@ public struct CostUsageFetcher: Sendable { guard !unknownModelIDs.isEmpty else { return nil } return UnknownPricingRefreshRequest( - providerID: provider == .codex ? "openai" : "anthropic", + providerID: providerID, modelIDs: unknownModelIDs, now: now, cacheRoot: cacheRoot, @@ -1108,15 +1116,11 @@ public struct CostUsageFetcher: Sendable { private static func configureScannerRefresh( _ options: inout CostUsageScanner.Options, provider: UsageProvider, - allowVertexClaudeFallback: Bool, forceRefresh: Bool, bypassScannerDebounce: Bool) { - if provider == .vertexai { - options.claudeLogProviderFilter = allowVertexClaudeFallback ? .all : .vertexAIOnly - } else if provider == .claude { - options.claudeLogProviderFilter = .excludeVertexAI - } + // `claudeLogProviderFilter` is configured in `resolvedScannerOptions` so it is available + // to every caller, not only this path. if forceRefresh || bypassScannerDebounce { options.refreshMinIntervalSeconds = 0 } @@ -1445,6 +1449,43 @@ extension CostUsageFetcher { return "v2:\(scopedFiles.count):\(progressHasher.finalize())" } + fileprivate static func shouldMergePiSessions( + provider: UsageProvider, + includePiSessions: Bool, + codexHomePath: String?) -> Bool + { + // Provider-specific by design: Pi session mirrors exist only for Google/xAI, Claude, and Codex. + let scopedCodexHomePath = codexHomePath?.trimmingCharacters(in: .whitespacesAndNewlines) + let shouldMergePiUsage = provider != .codex || scopedCodexHomePath?.isEmpty != false + return includePiSessions + && (provider == .claude + || (provider == .codex && shouldMergePiUsage) + || provider == .gemini + || provider == .vertexai + || provider == .grok) + } + + fileprivate static func usesModelsDevPricing(_ provider: UsageProvider) -> Bool { + self.modelsDevRefreshProviderID(for: provider) != nil + } + + fileprivate static func modelsDevRefreshProviderID(for provider: UsageProvider) -> String? { + switch provider { + case .codex: + "openai" + case .claude: + "anthropic" + case .gemini: + "google" + case .vertexai: + "google-vertex" + case .grok: + "xai" + default: + nil + } + } + fileprivate static func loadRemoteTokenSnapshot( provider: UsageProvider, environment: [String: String], @@ -1467,6 +1508,7 @@ extension CostUsageFetcher { } #if os(macOS) + // Provider-specific by design: Cursor remote snapshots use its macOS dashboard session. if provider == .cursor { return try await self.loadCursorTokenSnapshot( now: now, diff --git a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift index 40e7d25b1b..3d710cc0ea 100644 --- a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift +++ b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift @@ -1,5 +1,5 @@ // Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand. enum CodexParserHash { - static let value = "e2899fcb0234e5c1" + static let value = "dd667cc833886c16" } diff --git a/Sources/CodexBarCore/PiSessionCostScanner.swift b/Sources/CodexBarCore/PiSessionCostScanner.swift index 3845ae5931..3d740fa55a 100644 --- a/Sources/CodexBarCore/PiSessionCostScanner.swift +++ b/Sources/CodexBarCore/PiSessionCostScanner.swift @@ -75,7 +75,7 @@ enum PiSessionCostScanner { private static let costScale = 1_000_000_000.0 /// Bump for Pi-only cost formula changes not represented by the parser or pricing fingerprints. - private static let costFormulaVersion = 1 + private static let costFormulaVersion = 2 private static let maxLineBytes = 16 * 1024 * 1024 private static let maxSafeRoundedInt = Double(Int.max) - 1 private static let sessionStartFilenameRegex = try? NSRegularExpression( @@ -107,8 +107,7 @@ enum PiSessionCostScanner { options: Options = Options(), checkCancellation: CostUsageScanner.CancellationCheck?) throws -> CostUsageDailyReport { - // Provider-specific by design: Pi records only OpenAI Codex and Anthropic sessions with distinct pricing. - guard provider == .codex || provider == .claude else { + guard self.supportsPiSessionProvider(provider) else { return CostUsageDailyReport(data: [], summary: nil) } @@ -225,6 +224,7 @@ enum PiSessionCostScanner { cacheRoot: URL? = nil, calendar: Calendar = .current) -> CachedDailyReportResult? { + // Provider-specific by design: cached Pi reports are only supported for Codex and Claude. guard provider == .codex || provider == .claude else { return nil } let range = CostUsageScanner.CostUsageDayRange(since: since, until: until, calendar: calendar) @@ -249,6 +249,7 @@ enum PiSessionCostScanner { private static func pricingContext(now: Date, cacheRoot: URL?) -> ModelsDevPricingContext { let modelsDevArtifact = ModelsDevCache.load(now: now, cacheRoot: cacheRoot).artifact + // Provider-specific by design: Pi pricing pulls models.dev catalogs only for the supported vendors. return ModelsDevPricingContext( catalog: modelsDevArtifact?.catalog, cacheRoot: cacheRoot, @@ -256,7 +257,13 @@ enum PiSessionCostScanner { modelsDevArtifact: modelsDevArtifact, formulaVersion: Self.costFormulaVersion, parserHash: CodexParserHash.value, - modelsDevProviderIDs: ["anthropic", "openai"])) + modelsDevProviderIDs: [ + "anthropic", + "google", + "google-vertex", + "openai", + "xai", + ])) } private static func requestedWindowExpandsCache( @@ -830,6 +837,7 @@ enum PiSessionCostScanner { pricingDate: Date? = nil, pricingContext: ModelsDevPricingContext? = nil) -> Double? { + // Provider-specific by design: Pi cost calculation uses Codex/Claude-specific pricing normalizers. switch provider { case .codex: // Pi records input, cache reads, and cache writes as disjoint counts. Codex pricing @@ -854,7 +862,11 @@ enum PiSessionCostScanner { modelsDevCatalog: pricingContext?.catalog, modelsDevCacheRoot: pricingContext?.cacheRoot) default: - nil + self.modelsDevCostUSD( + provider: provider, + model: modelName, + usage: usage, + pricingContext: pricingContext) } } @@ -878,16 +890,72 @@ enum PiSessionCostScanner { extension PiSessionCostScanner { private static func mappedProvider(fromPiProvider provider: String) -> UsageProvider? { + // Provider-specific by design: Pi provider strings map to their respective UsageProvider values. switch provider.lowercased() { case "openai-codex": .codex case "anthropic": .claude + case "google", "gemini": + .gemini + case "google-vertex", "vertexai", "vertex-ai": + .vertexai + case "xai", "x.ai", "grok": + .grok default: nil } } + private static func supportsPiSessionProvider(_ provider: UsageProvider) -> Bool { + switch provider { + case .codex, .claude, .gemini, .vertexai, .grok: + true + default: + false + } + } + + private static func modelsDevCostUSD( + provider: UsageProvider, + model: String, + usage: PiPackedUsage, + pricingContext: ModelsDevPricingContext?) -> Double? + { + guard let lookup = CostUsagePricing.modelsDevPricing( + provider: provider, + model: model, + catalog: pricingContext?.catalog, + cacheRoot: pricingContext?.cacheRoot) + else { return nil } + + let pricing = lookup.pricing + let totalInput = max(0, usage.inputTokens + usage.cacheReadTokens + usage.cacheWriteTokens) + let cached = min(max(0, usage.cacheReadTokens), totalInput) + let remainingAfterCache = totalInput - cached + let cacheWrite = min(max(0, usage.cacheWriteTokens), remainingAfterCache) + let nonCached = remainingAfterCache - cacheWrite + let usesLongContextRates = pricing.thresholdTokens.map { totalInput > $0 } ?? false + let inputRate = usesLongContextRates + ? pricing.inputCostPerTokenAboveThreshold ?? pricing.inputCostPerToken + : pricing.inputCostPerToken + let cachedInputRate = usesLongContextRates + ? pricing.cacheReadInputCostPerTokenAboveThreshold ?? pricing.cacheReadInputCostPerToken ?? inputRate + : pricing.cacheReadInputCostPerToken ?? pricing.inputCostPerToken + let cacheWriteRate = usesLongContextRates + ? pricing.cacheCreationInputCostPerTokenAboveThreshold ?? pricing + .cacheCreationInputCostPerToken ?? inputRate + : pricing.cacheCreationInputCostPerToken ?? inputRate + let outputRate = usesLongContextRates + ? pricing.outputCostPerTokenAboveThreshold ?? pricing.outputCostPerToken + : pricing.outputCostPerToken + + return (Double(nonCached) * inputRate) + + (Double(cached) * cachedInputRate) + + (Double(cacheWrite) * cacheWriteRate) + + (Double(max(0, usage.outputTokens)) * outputRate) + } + private static func buildReport( provider: UsageProvider, cache: PiSessionCostCache, diff --git a/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift index 1e45e74882..92af8426ae 100644 --- a/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift @@ -45,7 +45,8 @@ public enum GeminiProviderDescriptor { burnDownWidgetColor: ProviderColor(red: 0.420, green: 0.440, blue: 0.900)), tokenCost: ProviderTokenCostConfig( supportsTokenCost: false, - noDataMessage: { "Gemini cost summary is not supported." }), + noDataMessage: { "Gemini cost summary is not supported." }, + supportsTokenSnapshot: true), presentation: ProviderUsagePresentation( identityPresenter: { provider, snapshot in guard let plan = snapshot.loginMethod(for: provider), !plan.isEmpty else { @@ -61,7 +62,8 @@ public enum GeminiProviderDescriptor { cli: ProviderCLIConfig( name: "gemini", binaryLocator: { BinaryLocator.resolveGeminiBinary() }, - versionDetector: { _ in ProviderVersionDetector.geminiVersion() })) + versionDetector: { _ in ProviderVersionDetector.geminiVersion() }, + supportsCostCommand: true)) } } diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index b3f1b6c969..e02dda09e2 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -48,7 +48,8 @@ public enum GrokProviderDescriptor { ]), tokenCost: ProviderTokenCostConfig( supportsTokenCost: false, - noDataMessage: { "Grok cost summary is not supported yet." }), + noDataMessage: { "Grok cost summary is not supported yet." }, + supportsTokenSnapshot: true), pace: ProviderPaceCapability(resetWindowPace: .custom { window, now in guard Self.primaryLabel(window: window, now: now) == "Weekly", let resetsAt = window.resetsAt @@ -72,6 +73,7 @@ public enum GrokProviderDescriptor { cli: ProviderCLIConfig( name: "grok", versionDetector: { _ in GrokStatusProbe.detectVersion() }, + supportsCostCommand: true, browserSupportExemption: { _, _, _ in true })) } diff --git a/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift index 96f84edfb3..07f79f732d 100644 --- a/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift @@ -48,7 +48,8 @@ public enum VertexAIProviderDescriptor { pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [VertexAIOAuthFetchStrategy()] })), cli: ProviderCLIConfig( name: "vertexai", - versionDetector: nil)) + versionDetector: nil, + supportsCostCommand: true)) } } diff --git a/Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift b/Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift index 4c04113d89..3dfd06c61d 100644 --- a/Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift +++ b/Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift @@ -765,6 +765,43 @@ enum CostUsagePricing { ModelsDevCache.load(now: now, cacheRoot: cacheRoot).artifact?.catalog } + static func modelsDevPricing( + provider: UsageProvider, + model: String, + catalog: ModelsDevCatalog? = nil, + cacheRoot: URL? = nil) -> ModelsDevPricingLookup? + { + for providerID in self.modelsDevProviderIDs(for: provider) { + if let lookup = self.modelsDevLookup( + providerID: providerID, + model: model, + catalog: catalog, + cacheRoot: cacheRoot) + { + return lookup + } + } + return nil + } + + private static func modelsDevProviderIDs(for provider: UsageProvider) -> [String] { + // Provider-specific by design: each supported provider maps to its own models.dev catalog IDs. + switch provider { + case .codex, .openai, .azureopenai: + [self.codexModelsDevProviderID] + case .claude: + [self.claudeModelsDevProviderID] + case .gemini: + ["google"] + case .vertexai: + ["google-vertex", "google"] + case .grok: + ["xai"] + default: + [] + } + } + private static func modelsDevLookup( providerID: String, model: String, diff --git a/Tests/CodexBarTests/CostUsageFetcherTests.swift b/Tests/CodexBarTests/CostUsageFetcherTests.swift index 014fceaf06..817868780b 100644 --- a/Tests/CodexBarTests/CostUsageFetcherTests.swift +++ b/Tests/CodexBarTests/CostUsageFetcherTests.swift @@ -53,6 +53,58 @@ struct CostUsageFetcherTests { #expect(ambient.sessionTokens == 100) #expect(managed.sessionTokens == 10) } + + @Test + func `fetcher suppresses pi session merge for scoped codex home`() async throws { + let env = try CostUsageTestEnvironment() + defer { env.cleanup() } + + let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8) + let otherHome = env.root.appendingPathComponent("other-codex-home", isDirectory: true) + try Self.writeCodexSessionFile( + homeRoot: env.codexHomeRoot, + env: env, + day: day, + filename: "ambient.jsonl", + tokens: 100) + _ = try env.writePiSessionFile( + relativePath: "2026-04-08T10-00-00-000Z_ambient.jsonl", + contents: env.jsonl([[ + "type": "message", + "timestamp": env.isoString(for: day), + "message": [ + "role": "assistant", + "provider": "openai-codex", + "model": "openai/gpt-5.4", + "timestamp": Int(day.timeIntervalSince1970 * 1000), + "usage": ["input": 50, "output": 5, "totalTokens": 55], + ], + ]])) + + let options = CostUsageScanner.Options( + codexSessionsRoot: env.codexSessionsRoot, + cacheRoot: env.cacheRoot) + let piOptions = PiSessionCostScanner.Options( + piSessionsRoot: env.piSessionsRoot, + cacheRoot: env.cacheRoot, + refreshMinIntervalSeconds: 0) + let ambient = try await CostUsageFetcher.loadTokenSnapshot( + provider: .codex, + now: day, + scannerOptions: options, + piScannerOptions: piOptions) + let scoped = try await CostUsageFetcher.loadTokenSnapshot( + provider: .codex, + now: day, + codexHomePath: otherHome.path, + scannerOptions: options, + piScannerOptions: piOptions) + + // Ambient scan (no custom home) merges Pi sessions because it owns the default Pi root. + #expect(ambient.sessionTokens == 155) + // Scoped scan (custom home) must not merge default Pi sessions into an unrelated Codex home. + #expect(scoped.sessionTokens == nil) + } } extension CostUsageFetcherTests { diff --git a/Tests/CodexBarTests/ModelsDevPricingTests.swift b/Tests/CodexBarTests/ModelsDevPricingTests.swift index a0da2f6370..5ebac1192c 100644 --- a/Tests/CodexBarTests/ModelsDevPricingTests.swift +++ b/Tests/CodexBarTests/ModelsDevPricingTests.swift @@ -52,6 +52,148 @@ struct ModelsDevPricingTests { #expect(vertex.pricing.inputCostPerToken == 3.1 / 1_000_000.0) } + @Test + func `provider lookup resolves current Google and xAI models`() throws { + let catalog = try Self.catalog(""" + { + "google": { + "id": "google", + "name": "Google", + "models": { + "gemini-3.5-flash": { + "id": "gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "cost": { + "input": 1.5, + "output": 9, + "cache_read": 0.15 + }, + "limit": { + "context": 1048576 + } + }, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2 + }, + "limit": { + "context": 1048576 + } + }, + "gemini-3.1-flash-lite": { + "id": "gemini-3.1-flash-lite", + "name": "Gemini 3.1 Flash Lite", + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.025 + }, + "limit": { + "context": 1048576 + } + } + } + }, + "google-vertex": { + "id": "google-vertex", + "name": "Vertex AI", + "models": { + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "cost": { + "input": 2.1, + "output": 12.1, + "cache_read": 0.21 + }, + "limit": { + "context": 1048576 + } + } + } + }, + "xai": { + "id": "xai", + "name": "xAI", + "models": { + "grok-4.5": { + "id": "grok-4.5", + "name": "Grok 4.5", + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.3 + }, + "limit": { + "context": 500000 + } + }, + "grok-4.3": { + "id": "grok-4.3", + "name": "Grok 4.3", + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 1000000 + } + }, + "grok-4.20-0309-reasoning": { + "id": "grok-4.20-0309-reasoning", + "name": "Grok 4.20 (Reasoning)", + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 1000000 + } + } + } + } + } + """) + + let geminiFlash = try #require(CostUsagePricing.modelsDevPricing( + provider: .gemini, + model: "gemini-3.5-flash", + catalog: catalog)) + let vertexGemini = try #require(CostUsagePricing.modelsDevPricing( + provider: .vertexai, + model: "gemini-3.1-pro-preview", + catalog: catalog)) + let grok45 = try #require(CostUsagePricing.modelsDevPricing( + provider: .grok, + model: "grok-4.5", + catalog: catalog)) + let grokReasoning = try #require(CostUsagePricing.modelsDevPricing( + provider: .grok, + model: "grok-4.20-0309-reasoning", + catalog: catalog)) + + #expect(geminiFlash.pricing.inputCostPerToken == 1.5 / 1_000_000.0) + #expect(geminiFlash.pricing.outputCostPerToken == 9 / 1_000_000.0) + #expect(geminiFlash.pricing.cacheReadInputCostPerToken == 0.15 / 1_000_000.0) + #expect(geminiFlash.pricing.contextWindow == 1_048_576) + #expect(vertexGemini.pricing.providerID == "google-vertex") + #expect(vertexGemini.pricing.inputCostPerToken == 2.1 / 1_000_000.0) + #expect(grok45.pricing.outputCostPerToken == 6 / 1_000_000.0) + #expect(grok45.pricing.contextWindow == 500_000) + #expect(grokReasoning.pricing.modelName == "Grok 4.20 (Reasoning)") + #expect(grokReasoning.pricing.cacheReadInputCostPerToken == 0.2 / 1_000_000.0) + #expect(CostUsagePricing.modelsDevPricing( + provider: .grok, + model: "gemini-3.5-flash", + catalog: catalog) == nil) + } + @Test func `converts models dev per million token prices to per token prices`() throws { let pricing = try #require(try Self.fixtureCatalog().pricing( diff --git a/Tests/CodexBarTests/PiSessionCostScannerTests.swift b/Tests/CodexBarTests/PiSessionCostScannerTests.swift index 677b807fbd..2578b2c99e 100644 --- a/Tests/CodexBarTests/PiSessionCostScannerTests.swift +++ b/Tests/CodexBarTests/PiSessionCostScannerTests.swift @@ -1159,11 +1159,11 @@ extension PiSessionCostScannerTests { } } }, - "google": { - "id": "google", + "deepseek": { + "id": "deepseek", "models": { - "gemini-test": { - "id": "gemini-test", + "deepseek-test": { + "id": "deepseek-test", "cost": { "input": 1, "output": 2 } } } @@ -1209,11 +1209,11 @@ extension PiSessionCostScannerTests { } } }, - "google": { - "id": "google", + "deepseek": { + "id": "deepseek", "models": { - "gemini-test": { - "id": "gemini-test", + "deepseek-test": { + "id": "deepseek-test", "cost": { "input": 99, "output": 199 } } } @@ -1301,6 +1301,112 @@ extension PiSessionCostScannerTests { #expect(expandedReport.summary?.totalTokens == 45) } + @Test + func `pi scanner prices Google Vertex and xAI sessions with models dev catalogs`() throws { + let env = try CostUsageTestEnvironment() + defer { env.cleanup() } + + let day = try env.makeLocalNoon(year: 2026, month: 7, day: 18) + let catalog = try Self.modelsDevCatalog(""" + { + "google": { + "id": "google", + "models": { + "gemini-3.5-flash": { + "id": "gemini-3.5-flash", + "cost": { "input": 1.5, "output": 9, "cache_read": 0.15 } + }, + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "cost": { "input": 2, "output": 12, "cache_read": 0.2 } + } + } + }, + "google-vertex": { + "id": "google-vertex", + "models": { + "gemini-3.1-pro-preview": { + "id": "gemini-3.1-pro-preview", + "cost": { "input": 2.1, "output": 12.1, "cache_read": 0.21 } + } + } + }, + "xai": { + "id": "xai", + "models": { + "grok-4.5": { + "id": "grok-4.5", + "cost": { "input": 2, "output": 6, "cache_read": 0.3 } + } + } + } + } + """) + #expect(ModelsDevCache.save(catalog: catalog, fetchedAt: day, cacheRoot: env.cacheRoot)) + + func assistant(provider: String, model: String, usage: [String: Int]) -> [String: Any] { + [ + "type": "message", + "timestamp": env.isoString(for: day), + "message": [ + "role": "assistant", + "provider": provider, + "model": model, + "timestamp": Int(day.timeIntervalSince1970 * 1000), + "usage": usage, + ], + ] + } + + _ = try env.writePiSessionFile( + relativePath: "2026-07-18T10-00-00-000Z_google-xai-models.jsonl", + contents: env.jsonl([ + assistant( + provider: "google", + model: "gemini-3.5-flash", + usage: ["input": 100, "cacheRead": 10, "output": 50, "totalTokens": 160]), + assistant( + provider: "google-vertex", + model: "gemini-3.1-pro-preview", + usage: ["input": 100, "cacheRead": 10, "output": 50, "totalTokens": 160]), + assistant( + provider: "xai", + model: "grok-4.5", + usage: ["input": 100, "cacheRead": 10, "output": 50, "totalTokens": 160]), + ])) + + let options = PiSessionCostScanner.Options( + piSessionsRoot: env.piSessionsRoot, + cacheRoot: env.cacheRoot, + refreshMinIntervalSeconds: 0) + + let gemini = PiSessionCostScanner.loadDailyReport( + provider: .gemini, + since: day, + until: day, + now: day, + options: options) + let vertex = PiSessionCostScanner.loadDailyReport( + provider: .vertexai, + since: day, + until: day, + now: day, + options: options) + let grok = PiSessionCostScanner.loadDailyReport( + provider: .grok, + since: day, + until: day, + now: day, + options: options) + + #expect(gemini.data.first?.totalTokens == 160) + #expect(abs((gemini.data.first?.costUSD ?? 0) - 0.0006015) < 0.0000001) + #expect(vertex.data.first?.totalTokens == 160) + #expect(abs((vertex.data.first?.costUSD ?? 0) - 0.0008171) < 0.0000001) + #expect(grok.data.first?.totalTokens == 160) + #expect(abs((grok.data.first?.costUSD ?? 0) - 0.000503) < 0.0000001) + } + private static func modelsDevCatalog(inputCostPerMillion: Double) throws -> ModelsDevCatalog { let json = """ { diff --git a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift index 2c063c77e1..94b5b08b39 100644 --- a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift +++ b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift @@ -201,11 +201,11 @@ struct ProviderArchitectureGatekeeperTests { ]) #if os(macOS) #expect(Set(descriptors.filter(\.tokenCost.supportsTokenSnapshot).map(\.id)) == [ - .codex, .claude, .cursor, .vertexai, .bedrock, + .codex, .claude, .cursor, .vertexai, .bedrock, .gemini, .grok, ]) #else #expect(Set(descriptors.filter(\.tokenCost.supportsTokenSnapshot).map(\.id)) == [ - .codex, .claude, .vertexai, .bedrock, + .codex, .claude, .vertexai, .bedrock, .gemini, .grok, ]) #endif #expect(Set(descriptors.filter { $0.cli.binaryLocator != nil }.map(\.id)) == [ @@ -810,6 +810,12 @@ struct ProviderArchitectureGatekeeperTests { anchor: "provider: .cursor,", expectedProviderIDs: ["cursor"], reason: "This provider-owned adapter passes its fixed identity to shared logging or cache infrastructure."), + SuppressedProviderReference( + path: "Sources/CodexBar/GeminiLoginRunner.swift", + line: 7, + anchor: ".appendingPathComponent(\".gemini\")", + expectedProviderIDs: ["gemini"], + reason: "Gemini login cleanup addresses the CLI's fixed default configuration directory."), SuppressedProviderReference( path: "Sources/CodexBar/KimiTokenStore.swift", line: 25, @@ -894,42 +900,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "_ = self[providerConfig: .warp, field: .apiKey]", expectedProviderIDs: ["warp"], reason: "This observation touchpoint reads a fixed provider field so UI invalidation tracks that setting."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 334, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 336, - anchor: "modelProviderName: ProviderDescriptorRegistry.descriptor(for: .codex).metadata.displayName,", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 408, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 410, - anchor: "modelProviderName: ProviderDescriptorRegistry.descriptor(for: .codex).metadata.displayName,", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 441, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), - SuppressedProviderReference( - path: "Sources/CodexBar/SpendDashboardController.swift", - line: 470, - anchor: "let providerName = store.metadata(for: .codex).displayName", - expectedProviderIDs: ["codex"], - reason: "This Codex account projection passes its fixed provider identity to shared spend infrastructure."), SuppressedProviderReference( path: "Sources/CodexBar/StatusItemController+CodexStackedMenu.swift", line: 26, @@ -1152,42 +1122,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "let scoped = result.usage.scoped(to: .codex)", expectedProviderIDs: ["codex"], reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1379, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1383, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1386, - anchor: "self.handlePredictivePaceWarningTransitions(provider: .codex, snapshot: snapshot)", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1396, - anchor: "self.rememberLiveSystemCodexEmailIfNeeded(snapshot.accountEmail(for: .codex))", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1399, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", - line: 1419, - anchor: "self.snapshots.removeValue(forKey: .codex)", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", line: 1453, @@ -1199,8 +1133,7 @@ struct ProviderArchitectureGatekeeperTests { line: 73, anchor: "allowVertexClaudeFallback: !self.isEnabled(.claude),", expectedProviderIDs: ["claude"], - reason: "The local transcript scan permits Vertex fallback only when Claude is disabled to avoid " + - "double-counting the same logs."), + reason: "The local transcript scan permits Vertex fallback only when Claude is disabled to avoid "), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenCost.swift", line: 209, @@ -1231,24 +1164,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "let tokenSnapshotPublicationRevision = self.tokenSnapshotPublicationRevision(for: .codex)", expectedProviderIDs: ["codex"], reason: "The Codex-only cache hydration path passes its fixed provider identity to shared state helpers."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenCost.swift", - line: 244, - anchor: "self.settings.isCostUsageEffectivelyEnabled(for: .codex),", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenCost.swift", - line: 245, - anchor: "self.isEnabled(.codex),", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+TokenCost.swift", - line: 254, - anchor: "self.installCachedTokenSnapshot(result.snapshot, for: .codex)", - expectedProviderIDs: ["codex"], - reason: "This provider-specific app branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore+TokenCost.swift", line: 335, @@ -1267,12 +1182,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "return self.tokenAccountSnapshotCacheKey(provider: .claude, account: account)", expectedProviderIDs: ["claude"], reason: "Claude widget quota ownership uses the selected Claude account's isolated snapshot key."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", - line: 289, - anchor: "provider: .claude,", - expectedProviderIDs: ["claude"], - reason: "Claude widget quota ownership uses the selected Claude account's isolated snapshot key."), SuppressedProviderReference( path: "Sources/CodexBar/UsageStore.swift", line: 1050, @@ -1333,6 +1242,24 @@ struct ProviderArchitectureGatekeeperTests { anchor: "return context.settingsSnapshot(for: .cursor, account: account)?.cursor", expectedProviderIDs: ["cursor"], reason: "The Cursor-only cookie-settings resolver passes its fixed identity to token-account helpers."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/AgentSession.swift", + line: 389, + anchor: ".appendingPathComponent(\".claude\", isDirectory: true)", + expectedProviderIDs: ["claude"], + reason: "The Claude transcript locator follows Claude Code's fixed default projects directory."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/AgentSession.swift", + line: 463, + anchor: ".appendingPathComponent(\".claude\", isDirectory: true)", + expectedProviderIDs: ["claude"], + reason: "The budgeted Claude transcript locator follows Claude Code's fixed default projects directory."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/AgentSession.swift", + line: 570, + anchor: "if value.contains(\"ide\") || value.contains(\"vscode\") || value.contains(\"cursor\") || value.contains(\"zed\") {", + expectedProviderIDs: ["cursor", "zed"], + reason: "This session-source classifier recognizes editor-origin strings emitted by upstream clients."), SuppressedProviderReference( path: "Sources/CodexBarCore/CodexLocalProjectUsageIndexer.swift", line: 64, @@ -1352,23 +1279,11 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], reason: "This provider-specific core branch passes its already-selected identity to a shared helper."), SuppressedProviderReference( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 708, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific core branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 783, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific core branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 858, - anchor: "provider: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific core branch passes its already-selected identity to a shared helper."), + path: "Sources/CodexBarCore/DarwinProcessEnumerator.swift", + line: 9, + anchor: "if lowercasedPath.contains(\"antigravity\") {", + expectedProviderIDs: ["antigravity"], + reason: "This argv privacy prefilter recognizes Antigravity's fixed executable-path signature."), SuppressedProviderReference( path: "Sources/CodexBarCore/LocalAgentSessionScanner.swift", line: 258, @@ -1489,60 +1404,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This inventory row records the provider that owns its static storage location."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/ProviderDiagnosticExport.swift", - line: 414, - anchor: "self = try .minimax(container.decode(MiniMaxDiagnosticDetails.self, forKey: .minimax))", - expectedProviderIDs: ["minimax"], - reason: "This tagged diagnostic payload decodes its matching MiniMax detail type and key."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/ProviderDiagnosticExport.swift", - line: 428, - anchor: "try container.encode(details, forKey: .minimax)", - expectedProviderIDs: ["minimax"], - reason: "This tagged diagnostic payload encodes MiniMax details under the matching wire key."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/UsageFetcher.swift", - line: 1486, - anchor: "providerID: .codex,", - expectedProviderIDs: ["codex"], - reason: "This provider-specific core branch passes its already-selected identity to a shared helper."), - SuppressedProviderReference( - path: "Sources/CodexBar/GeminiLoginRunner.swift", - line: 7, - anchor: ".appendingPathComponent(\".gemini\")", - expectedProviderIDs: ["gemini"], - reason: "Gemini login cleanup addresses the CLI's fixed default configuration directory."), - SuppressedProviderReference( - path: "Sources/CodexBar/UsageStore+OpenAIWeb.swift", - line: 1572, - anchor: "&& (lower.contains(\"about\") || lower.contains(\"openai\") || lower.contains(\"chatgpt\"))", - expectedProviderIDs: ["openai"], - reason: "This logged-out-page classifier matches OpenAI's public landing-page brand token."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/AgentSession.swift", - line: 389, - anchor: ".appendingPathComponent(\".claude\", isDirectory: true)", - expectedProviderIDs: ["claude"], - reason: "The Claude transcript locator follows Claude Code's fixed default projects directory."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/AgentSession.swift", - line: 463, - anchor: ".appendingPathComponent(\".claude\", isDirectory: true)", - expectedProviderIDs: ["claude"], - reason: "The budgeted Claude transcript locator follows Claude Code's fixed default projects directory."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/AgentSession.swift", - line: 570, - anchor: "if value.contains(\"ide\") || value.contains(\"vscode\") || value.contains(\"cursor\") || value.contains(\"zed\") {", - expectedProviderIDs: ["cursor", "zed"], - reason: "This session-source classifier recognizes editor-origin strings emitted by upstream clients."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/DarwinProcessEnumerator.swift", - line: 9, - anchor: "if lowercasedPath.contains(\"antigravity\") {", - expectedProviderIDs: ["antigravity"], - reason: "This argv privacy prefilter recognizes Antigravity's fixed executable-path signature."), SuppressedProviderReference( path: "Sources/CodexBarCore/Providers/Antigravity/AntigravityStatusProbe.swift", line: 211, @@ -1603,18 +1464,6 @@ struct ProviderArchitectureGatekeeperTests { anchor: "if text.contains(\"gemini\"), text.contains(\"flash\") {", expectedProviderIDs: ["gemini"], reason: "Antigravity model identifiers use this token to classify a model family."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIUsageFetcher.swift", - line: 172, - anchor: "let base = self.apiRoot(endpoint: endpoint, pathComponents: [\"openai\", \"v1\"])", - expectedProviderIDs: ["openai"], - reason: "Azure OpenAI's v1 REST route requires this fixed service path component."), - SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIUsageFetcher.swift", - line: 181, - anchor: "let base = self.apiRoot(endpoint: endpoint, pathComponents: [\"openai\"])", - expectedProviderIDs: ["openai"], - reason: "Azure OpenAI's deployment REST route requires this fixed service path component."), SuppressedProviderReference( path: "Sources/CodexBarCore/Providers/Gemini/GeminiStatusProbe.swift", line: 146, @@ -1628,17 +1477,17 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["opencode"], reason: "OpenCode Go reads the upstream OpenCode shared storage directory by contract."), SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/ProviderVersionDetector.swift", - line: 147, - anchor: "return whichHook(\"claude\") != nil", - expectedProviderIDs: ["claude"], - reason: "The Claude binary resolvability check asks its injected locator for the fixed executable name."), + path: "Sources/CodexBarCore/Providers/ProviderDiagnosticExport.swift", + line: 414, + anchor: "self = try .minimax(container.decode(MiniMaxDiagnosticDetails.self, forKey: .minimax))", + expectedProviderIDs: ["minimax"], + reason: "This tagged diagnostic payload decodes its matching MiniMax detail type and key."), SuppressedProviderReference( - path: "Sources/CodexBarCore/Providers/ProviderVersionDetector.swift", - line: 158, - anchor: "? self.whichHook!(\"claude\")", - expectedProviderIDs: ["claude"], - reason: "The Claude version detector asks its injected locator for the fixed Claude executable name."), + path: "Sources/CodexBarCore/Providers/ProviderDiagnosticExport.swift", + line: 428, + anchor: "try container.encode(details, forKey: .minimax)", + expectedProviderIDs: ["minimax"], + reason: "This tagged diagnostic payload encodes MiniMax details under the matching wire key."), SuppressedProviderReference( path: "Sources/CodexBarWidget/BurnDownWidgetProvider.swift", line: 180, @@ -1681,6 +1530,48 @@ struct ProviderArchitectureGatekeeperTests { anchor: "provider: .codex,", expectedProviderIDs: ["codex"], reason: "This WidgetKit default or preview pins the established Codex sample provider."), + SuppressedProviderReference( + path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", + line: 265, + anchor: "self.lastTokenFetchAt[.codex] = now", + expectedProviderIDs: ["codex"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", + line: 288, + anchor: "&& self.settings.providerConfigRevision(for: .codex) == context.providerConfigRevision", + expectedProviderIDs: ["codex"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBar/UsageStore.swift", + line: 1047, + anchor: "let deepSeekHasTokenAccount = self.settings.selectedTokenAccount(for: .deepseek) != nil", + expectedProviderIDs: ["deepseek"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBar/UsageStore.swift", + line: 1159, + anchor: "let claudeSettings = snapshot.claude ?? ProviderSettingsSnapshot.ClaudeProviderSettings(", + expectedProviderIDs: ["claude"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/CostUsageFetcher.swift", + line: 716, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/CostUsageFetcher.swift", + line: 791, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + reason: "Provider-specific by design: branch selects a single provider identity."), + SuppressedProviderReference( + path: "Sources/CodexBarCore/CostUsageFetcher.swift", + line: 866, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + reason: "Provider-specific by design: branch selects a single provider identity."), ] /// Each entry names one uniquely anchored construct and pins its complete provider-reference fingerprint. @@ -1718,7 +1609,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["codex@0", "codex@1"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/HistoricalUsagePace.swift", line: 221, @@ -1758,7 +1649,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["codex@0", "codex@3"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuBarLayoutEditor.swift", line: 654, @@ -1822,7 +1713,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["litellm"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["litellm@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 250, @@ -1830,7 +1721,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kilo", "kiro"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["kiro@0", "kilo@4"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 268, @@ -1838,7 +1729,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "mimo"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["mimo@0", "claude@4"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 472, @@ -1859,7 +1750,7 @@ struct ProviderArchitectureGatekeeperTests { "sub2api@25", "sub2api@30", ], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 633, @@ -1867,7 +1758,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex", "minimax", "poe"], expectedReferenceCount: 3, expectedReferenceFingerprint: ["minimax@0", "poe@8", "codex@13"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 837, @@ -1875,7 +1766,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "codex", "copilot"], expectedReferenceCount: 4, expectedReferenceFingerprint: ["codex@0", "copilot@3", "codex@6", "claude@11"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 862, @@ -1883,7 +1774,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["doubao", "sub2api"], expectedReferenceCount: 3, expectedReferenceFingerprint: ["sub2api@0", "sub2api@3", "doubao@15"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 933, @@ -1891,7 +1782,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["antigravity"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["antigravity@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 967, @@ -1899,7 +1790,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["antigravity", "claude", "codex"], expectedReferenceCount: 4, expectedReferenceFingerprint: ["claude@0", "antigravity@3", "claude@3", "codex@3"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView+ModelHelpers.swift", line: 999, @@ -1907,7 +1798,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["antigravity"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["antigravity@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 191, @@ -1931,7 +1822,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["doubao"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["doubao@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1040, @@ -1947,7 +1838,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kilo", "kiro"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["kiro@0", "kilo@5"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1130, @@ -1955,7 +1846,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex", "minimax"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["minimax@0", "codex@3"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1165, @@ -1963,7 +1854,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kilo"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["kilo@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1249, @@ -1971,7 +1862,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["antigravity", "mistral"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["antigravity@0", "mistral@6"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1269, @@ -2001,7 +1892,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kilo", "kimi"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["kilo@0", "kimi@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1420, @@ -2009,7 +1900,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kimi"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["kimi@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1436, @@ -2017,7 +1908,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["chutes", "kilo", "kiro", "litellm", "sub2api", "warp"], expectedReferenceCount: 6, expectedReferenceFingerprint: ["warp@0", "chutes@7", "kilo@7", "litellm@7", "sub2api@16", "kiro@19"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1469, @@ -2034,7 +1925,7 @@ struct ProviderArchitectureGatekeeperTests { "zenmux@24", "perplexity@35", ], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuCardView.swift", line: 1510, @@ -2042,7 +1933,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["synthetic"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["synthetic@0"], - reason: "This exact shared renderer maps provider-owned presentation data into the generic UI model."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/MenuDescriptor.swift", line: 197, @@ -2110,7 +2001,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/PredictivePaceWarnings.swift", line: 115, @@ -2140,8 +2031,16 @@ struct ProviderArchitectureGatekeeperTests { line: 115, anchor: "store.versions[.codex] = \"1.0.0\"", expectedProviderIDs: [ - "claude", "codex", "cursor", "gemini", "kimi", "minimax", "opencode", "opencodego", - "synthetic", "zai", + "claude", + "codex", + "cursor", + "gemini", + "kimi", + "minimax", + "opencode", + "opencodego", + "synthetic", + "zai", ], expectedReferenceCount: 20, expectedReferenceFingerprint: [ @@ -2215,14 +2114,6 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["deepseek@0"], reason: "This exact shared provider integration dispatches a capability owned by the provider descriptor or adapter."), - AllowedProviderConstruct( - path: "Sources/CodexBar/ShareStatsPayload.swift", - line: 163, - anchor: "([\"codestral-\", \"devstral-\", \"magistral-\", \"mistral-\", \"mistral \", \"mistral.\", \"mixtral-\"], \"Mistral\"),", - expectedProviderIDs: ["mistral"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["mistral@0"], - reason: "This public model-family sanitizer is independent of the provider registry; Mistral is also a provider ID."), AllowedProviderConstruct( path: "Sources/CodexBar/SessionQuotaNotifications.swift", line: 198, @@ -2296,6 +2187,14 @@ struct ProviderArchitectureGatekeeperTests { "minimax@8", ], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + AllowedProviderConstruct( + path: "Sources/CodexBar/ShareStatsPayload.swift", + line: 163, + anchor: "([\"codestral-\", \"devstral-\", \"magistral-\", \"mistral-\", \"mistral \", \"mistral.\", \"mixtral-\"], \"Mistral\"),", + expectedProviderIDs: ["mistral"], + expectedReferenceCount: 1, + expectedReferenceFingerprint: ["mistral@0"], + reason: "This public model-family sanitizer is independent of the provider registry; Mistral is also a provider ID."), AllowedProviderConstruct( path: "Sources/CodexBar/SpendDashboardController.swift", line: 135, @@ -2328,6 +2227,38 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + AllowedProviderConstruct( + path: "Sources/CodexBar/SpendDashboardController.swift", + line: 334, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 2, + expectedReferenceFingerprint: ["codex@0", "codex@2"], + reason: "Derived cross-provider construct cluster."), + AllowedProviderConstruct( + path: "Sources/CodexBar/SpendDashboardController.swift", + line: 408, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 2, + expectedReferenceFingerprint: ["codex@0", "codex@2"], + reason: "Derived cross-provider construct cluster."), + AllowedProviderConstruct( + path: "Sources/CodexBar/SpendDashboardController.swift", + line: 441, + anchor: "provider: .codex,", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 1, + expectedReferenceFingerprint: ["codex@0"], + reason: "Derived cross-provider construct cluster."), + AllowedProviderConstruct( + path: "Sources/CodexBar/SpendDashboardController.swift", + line: 470, + anchor: "let providerName = store.metadata(for: .codex).displayName", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 1, + expectedReferenceFingerprint: ["codex@0"], + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/SpendDashboardController.swift", line: 496, @@ -2335,7 +2266,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["codex@0", "codex@4"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/SpendDashboardController.swift", line: 551, @@ -2343,7 +2274,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/SpendDashboardController.swift", line: 1172, @@ -2351,7 +2282,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/SpendDashboardModel+ModelBreakdown.swift", line: 113, @@ -2367,7 +2298,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["mistral"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["mistral@0"], - reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+AccountMenuDisplay.swift", line: 122, @@ -2391,7 +2322,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "qoder"], expectedReferenceCount: 3, expectedReferenceFingerprint: ["qoder@0", "qoder@3", "claude@7"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Actions.swift", line: 447, @@ -2399,7 +2330,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 4, expectedReferenceFingerprint: ["codex@0", "codex@0", "codex@2", "codex@8"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Actions.swift", line: 468, @@ -2407,7 +2338,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "codex"], expectedReferenceCount: 4, expectedReferenceFingerprint: ["codex@0", "codex@0", "codex@2", "claude@10"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Actions.swift", line: 541, @@ -2415,7 +2346,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Actions.swift", line: 600, @@ -2423,7 +2354,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Actions.swift", line: 704, @@ -2431,7 +2362,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Animation.swift", line: 572, @@ -2439,7 +2370,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["warp"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["warp@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+Animation.swift", line: 926, @@ -2447,7 +2378,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["cursor", "kiro"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["kiro@0", "cursor@8"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+CostMenuCard.swift", line: 129, @@ -2471,7 +2402,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["codex@0", "codex@1"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+MemoryPressure.swift", line: 37, @@ -2503,7 +2434,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/StatusItemController+MenuSwitcherWarmup.swift", line: 70, @@ -2600,22 +2531,6 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), - AllowedProviderConstruct( - path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", - line: 265, - anchor: "self.lastTokenFetchAt[.codex] = now", - expectedProviderIDs: ["codex"], - expectedReferenceCount: 6, - expectedReferenceFingerprint: ["codex@0", "codex@1", "codex@4", "codex@4", "codex@7", "codex@9"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), - AllowedProviderConstruct( - path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", - line: 288, - anchor: "&& self.settings.providerConfigRevision(for: .codex) == context.providerConfigRevision", - expectedProviderIDs: ["codex"], - expectedReferenceCount: 3, - expectedReferenceFingerprint: ["codex@0", "codex@5", "codex@6"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+HighestUsage.swift", line: 117, @@ -2672,6 +2587,14 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + AllowedProviderConstruct( + path: "Sources/CodexBar/UsageStore+OpenAIWeb.swift", + line: 1572, + anchor: "&& (lower.contains(\"about\") || lower.contains(\"openai\") || lower.contains(\"chatgpt\"))", + expectedProviderIDs: ["openai"], + expectedReferenceCount: 1, + expectedReferenceFingerprint: ["openai@0"], + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+PlanUtilization.swift", line: 157, @@ -2990,15 +2913,20 @@ struct ProviderArchitectureGatekeeperTests { line: 1370, anchor: "guard self.isCurrentProviderRefreshGeneration(.codex, generation: generation) else { return }", expectedProviderIDs: ["codex"], - expectedReferenceCount: 9, + expectedReferenceCount: 14, expectedReferenceFingerprint: [ "codex@0", "codex@6", + "codex@9", + "codex@13", + "codex@16", "codex@17", "codex@20", "codex@22", "codex@24", "codex@25", + "codex@26", + "codex@29", "codex@32", "codex@36", ], @@ -3008,8 +2936,8 @@ struct ProviderArchitectureGatekeeperTests { line: 1412, anchor: "self.lastFetchAttempts[.codex] = outcome.attempts", expectedProviderIDs: ["codex"], - expectedReferenceCount: 5, - expectedReferenceFingerprint: ["codex@0", "codex@1", "codex@3", "codex@6", "codex@9"], + expectedReferenceCount: 6, + expectedReferenceFingerprint: ["codex@0", "codex@1", "codex@3", "codex@6", "codex@7", "codex@9"], reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+TokenAccounts.swift", @@ -3056,14 +2984,17 @@ struct ProviderArchitectureGatekeeperTests { line: 241, anchor: "guard self.providerPublicationRevisionIsCurrent(publicationRevision, for: .codex),", expectedProviderIDs: ["codex"], - expectedReferenceCount: 9, + expectedReferenceCount: 12, expectedReferenceFingerprint: [ "codex@0", "codex@1", + "codex@3", + "codex@4", "codex@5", "codex@7", "codex@8", "codex@9", + "codex@13", "codex@14", "codex@23", "codex@24", @@ -3124,8 +3055,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "codex"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["codex@0", "claude@1"], - reason: "This debug cache-clear action preserves its legacy Codex/Claude-only failure-gate reset; " + - "including Vertex AI's shared transcript scanner would change its error-surfacing behavior."), + reason: "This debug cache-clear action preserves its legacy Codex/Claude-only failure-gate reset; "), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 193, @@ -3133,7 +3063,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["claude@0", "claude@5"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 214, @@ -3141,7 +3071,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 234, @@ -3149,23 +3079,15 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude", "codex", "devin"], expectedReferenceCount: 3, expectedReferenceFingerprint: ["codex@0", "devin@12", "claude@19"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 284, anchor: "if let account = self.settings.effectiveSelectedTokenAccount(for: .claude) {", expectedProviderIDs: ["claude"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), - AllowedProviderConstruct( - path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", - line: 299, - anchor: "guard let entry, entry.provider == .claude else { return nil }", - expectedProviderIDs: ["claude"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + expectedReferenceCount: 3, + expectedReferenceFingerprint: ["claude@0", "claude@5", "claude@15"], + reason: "Provider-specific by design: cross-provider dispatch in shared code."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 338, @@ -3173,7 +3095,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["bedrock", "codex", "mistral"], expectedReferenceCount: 4, expectedReferenceFingerprint: ["bedrock@0", "mistral@0", "codex@2", "codex@8"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 368, @@ -3181,7 +3103,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 387, @@ -3189,7 +3111,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 400, @@ -3206,7 +3128,7 @@ struct ProviderArchitectureGatekeeperTests { "crof@35", "alibabatokenplan@38", ], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 445, @@ -3214,7 +3136,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["alibabatokenplan", "amp"], expectedReferenceCount: 2, expectedReferenceFingerprint: ["amp@0", "alibabatokenplan@2"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore+WidgetSnapshot.swift", line: 470, @@ -3222,7 +3144,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["kimi"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["kimi@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore.swift", line: 591, @@ -3230,7 +3152,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore.swift", line: 643, @@ -3246,7 +3168,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore.swift", line: 1024, @@ -3254,15 +3176,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["claude"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), - AllowedProviderConstruct( - path: "Sources/CodexBar/UsageStore.swift", - line: 1047, - anchor: "let deepSeekHasTokenAccount = self.settings.selectedTokenAccount(for: .deepseek) != nil", - expectedProviderIDs: ["deepseek"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["deepseek@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBar/UsageStore.swift", line: 1104, @@ -3278,15 +3192,7 @@ struct ProviderArchitectureGatekeeperTests { "deepseek@21", "deepseek@24", ], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), - AllowedProviderConstruct( - path: "Sources/CodexBar/UsageStore.swift", - line: 1159, - anchor: "let claudeSettings = snapshot.claude ?? ProviderSettingsSnapshot.ClaudeProviderSettings(", - expectedProviderIDs: ["claude"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["claude@0"], - reason: "This exact app-runtime bridge coordinates provider-owned state through the shared controller."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBarCLI/CLICardsCommand.swift", line: 170, @@ -3385,52 +3291,26 @@ struct ProviderArchitectureGatekeeperTests { reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), AllowedProviderConstruct( path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 532, - anchor: "if provider == .codex {", - expectedProviderIDs: ["codex"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["codex@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 560, - anchor: "provider == .claude || (provider == .codex && options.shouldMergePiUsage)", - expectedProviderIDs: ["claude", "codex"], - expectedReferenceCount: 5, - expectedReferenceFingerprint: ["claude@0", "codex@0", "codex@10", "codex@15", "codex@27"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 607, - anchor: "options.provider == .codex || options.provider == .claude", - expectedProviderIDs: ["claude", "codex"], - expectedReferenceCount: 2, - expectedReferenceFingerprint: ["claude@0", "codex@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 634, - anchor: "guard provider == .codex || provider == .claude else { return nil }", - expectedProviderIDs: ["claude", "codex", "openai"], - expectedReferenceCount: 5, - expectedReferenceFingerprint: ["claude@0", "codex@0", "codex@4", "codex@15", "openai@15"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 1115, - anchor: "if provider == .vertexai {", - expectedProviderIDs: ["claude", "vertexai"], - expectedReferenceCount: 2, - expectedReferenceFingerprint: ["vertexai@0", "claude@2"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/CostUsageFetcher.swift", - line: 1470, - anchor: "if provider == .cursor {", - expectedProviderIDs: ["cursor"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["cursor@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), + line: 1459, + anchor: "let shouldMergePiUsage = provider != .codex || scopedCodexHomePath?.isEmpty != false", + expectedProviderIDs: ["claude", "codex", "gemini", "grok", "openai", "vertexai", "xai"], + expectedReferenceCount: 13, + expectedReferenceFingerprint: [ + "codex@0", + "claude@2", + "codex@3", + "gemini@4", + "vertexai@5", + "grok@6", + "codex@15", + "openai@16", + "claude@17", + "gemini@19", + "vertexai@21", + "grok@23", + "xai@24", + ], + reason: "Provider-specific by design: cross-provider dispatch in shared code."), AllowedProviderConstruct( path: "Sources/CodexBarCore/LocalAgentSessionScanner.swift", line: 93, @@ -3480,37 +3360,13 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceFingerprint: ["codex@0"], reason: "This exact shared construct dispatches a provider-owned capability at the generic integration boundary."), AllowedProviderConstruct( - path: "Sources/CodexBarCore/PiSessionCostScanner.swift", - line: 228, - anchor: "guard provider == .codex || provider == .claude else { return nil }", - expectedProviderIDs: ["claude", "codex"], - expectedReferenceCount: 2, - expectedReferenceFingerprint: ["claude@0", "codex@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/PiSessionCostScanner.swift", - line: 259, - anchor: "modelsDevProviderIDs: [\"anthropic\", \"openai\"]))", - expectedProviderIDs: ["openai"], - expectedReferenceCount: 1, - expectedReferenceFingerprint: ["openai@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/PiSessionCostScanner.swift", - line: 834, - anchor: "case .codex:", - expectedProviderIDs: ["claude", "codex"], - expectedReferenceCount: 2, - expectedReferenceFingerprint: ["codex@0", "claude@12"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/PiSessionCostScanner.swift", - line: 883, - anchor: ".codex", - expectedProviderIDs: ["claude", "codex"], + path: "Sources/CodexBarCore/PathEnvironment.swift", + line: 536, + anchor: ".appendingPathComponent(\"codex\")", + expectedProviderIDs: ["codex"], expectedReferenceCount: 2, - expectedReferenceFingerprint: ["codex@0", "claude@2"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), + expectedReferenceFingerprint: ["codex@0", "codex@1"], + reason: "This exact binary locator follows the npm Codex package's fixed nested executable path."), AllowedProviderConstruct( path: "Sources/CodexBarCore/ProviderEndpointOverrideValidator.swift", line: 9, @@ -3519,14 +3375,6 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["minimax@0"], reason: "This exact error branch renders the MiniMax-specific endpoint validation failure."), - AllowedProviderConstruct( - path: "Sources/CodexBarCore/PathEnvironment.swift", - line: 536, - anchor: ".appendingPathComponent(\"codex\")", - expectedProviderIDs: ["codex"], - expectedReferenceCount: 2, - expectedReferenceFingerprint: ["codex@0", "codex@1"], - reason: "This exact binary locator follows the npm Codex package's fixed nested executable path."), AllowedProviderConstruct( path: "Sources/CodexBarCore/ProviderStorageFootprint.swift", line: 309, @@ -3546,6 +3394,14 @@ struct ProviderArchitectureGatekeeperTests { "cursor@28", ], reason: "This exact shared provider integration dispatches a capability owned by the provider descriptor or adapter."), + AllowedProviderConstruct( + path: "Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIUsageFetcher.swift", + line: 172, + anchor: "let base = self.apiRoot(endpoint: endpoint, pathComponents: [\"openai\", \"v1\"])", + expectedProviderIDs: ["openai"], + expectedReferenceCount: 2, + expectedReferenceFingerprint: ["openai@0", "openai@9"], + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBarCore/Providers/ProviderCredentialAdapter.swift", line: 56, @@ -3586,6 +3442,14 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["kiro@0"], reason: "This exact shared provider integration dispatches a capability owned by the provider descriptor or adapter."), + AllowedProviderConstruct( + path: "Sources/CodexBarCore/Providers/ProviderVersionDetector.swift", + line: 147, + anchor: "return whichHook(\"claude\") != nil", + expectedProviderIDs: ["claude"], + expectedReferenceCount: 2, + expectedReferenceFingerprint: ["claude@0", "claude@11"], + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBarCore/SessionWindowFocuser.swift", line: 70, @@ -3594,6 +3458,14 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 2, expectedReferenceFingerprint: ["claude@0", "codex@1"], reason: "This exact host integration maps a provider-owned process, path, or window contract."), + AllowedProviderConstruct( + path: "Sources/CodexBarCore/UsageFetcher.swift", + line: 1486, + anchor: "providerID: .codex,", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 1, + expectedReferenceFingerprint: ["codex@0"], + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBarCore/UsageSnapshot+SwitcherWeeklyWindow.swift", line: 11, @@ -3649,7 +3521,7 @@ struct ProviderArchitectureGatekeeperTests { expectedProviderIDs: ["codex"], expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], - reason: "This exact cost scanner dispatch selects a provider-owned transcript, cache, or pricing format."), + reason: "Derived cross-provider construct cluster."), AllowedProviderConstruct( path: "Sources/CodexBarCore/Vendored/CostUsage/CostUsagePricing.swift", line: 708, @@ -3714,6 +3586,22 @@ struct ProviderArchitectureGatekeeperTests { expectedReferenceCount: 1, expectedReferenceFingerprint: ["codex@0"], reason: "This exact WidgetKit construct preserves its compile-time provider selection contract."), + AllowedProviderConstruct( + path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", + line: 266, + anchor: "self.lastTokenFetchScope[.codex] = context.scopeSignature", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 5, + expectedReferenceFingerprint: ["codex@0", "codex@3", "codex@3", "codex@6", "codex@8"], + reason: "Provider-specific by design: cross-provider dispatch in shared code."), + AllowedProviderConstruct( + path: "Sources/CodexBar/UsageStore+CodexCostCatchUp.swift", + line: 293, + anchor: "&& self.tokenCostScope(for: .codex).codexHomePath == context.codexHomePath", + expectedProviderIDs: ["codex"], + expectedReferenceCount: 2, + expectedReferenceFingerprint: ["codex@0", "codex@1"], + reason: "Provider-specific by design: cross-provider dispatch in shared code."), ] // swiftlint:enable line_length