Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Fixed
- Vertex AI: match Cloud Monitoring quota usage without a `limit_name` to its unambiguous same-metric, same-location limit, restoring quota percentages (#2958). Thanks @MachApple!
- Antigravity: render one lane per quota bucket on the serve dashboard and in `codexbar dashboard`, instead of repeating two buckets as extra "Gemini Models" and "Claude and GPT" rows.

## 0.50.0 — 2026-08-15

Expand Down
18 changes: 18 additions & 0 deletions Sources/CodexBarCLI/DashboardSnapshotBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ enum DashboardSnapshotBuilder {
usage: UsageSnapshot?) -> [DashboardWindowPayload]
{
guard let usage else { return [] }
// Provider-specific by design: Antigravity's primary and secondary are representatives
// copied out of its own quota-summary lanes so the icon and menu bar have standard slots
// to read. Emitting all three repeats two lanes under the generic session and weekly
// labels, so the dashboard renders the summary lanes alone, one per quota bucket.
if provider == .antigravity, let windows = self.antigravityQuotaSummaryWindows(usage) {
return windows
}
let labels = self.rateWindowLabels(provider: provider, metadata: metadata, usage: usage)
var windows: [DashboardWindowPayload] = []
// Provider-specific by design: Amp subscription payloads model balance and orb as non-time-window kinds.
Expand All @@ -309,6 +316,17 @@ enum DashboardSnapshotBuilder {
return windows
}

/// Display lanes for an Antigravity quota-summary snapshot, or `nil` when the snapshot has no
/// summary lanes and must keep the standard primary and secondary rows. Every family stays
/// visible: the dashboard is a detail surface, so only the duplicated representatives go away.
private static func antigravityQuotaSummaryWindows(_ usage: UsageSnapshot) -> [DashboardWindowPayload]? {
let extras = usage.extraRateWindows ?? []
guard extras.contains(where: { AntigravityStatusSnapshot.isQuotaSummaryWindowID($0.id) }) else {
return nil
}
return extras.map { self.makeWindow(kind: $0.id, label: $0.title, window: $0.window) }
}

private struct RateWindowLabels {
let primary: String
let secondary: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public struct AntigravityStatusSnapshot: Sendable {
}
}

static func isQuotaSummaryWindowID(_ id: String) -> Bool {
public static func isQuotaSummaryWindowID(_ id: String) -> Bool {
id.hasPrefix(self.quotaSummaryWindowIDPrefix)
}

Expand Down
131 changes: 131 additions & 0 deletions Tests/CodexBarTests/DashboardAntigravityWindowTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import CodexBarCore
import Foundation
import Testing
@testable import CodexBarCLI

/// The dashboard snapshot renders one Antigravity lane per quota bucket. The primary and secondary
/// representatives are copies of two of those buckets, kept only so the icon and menu bar have
/// standard slots to read, so the dashboard must not repeat them as rows of their own.
struct DashboardAntigravityWindowTests {
@Test
func `dashboard renders one Antigravity lane per quota bucket`() throws {
let windows = try self.antigravityWindows(geminiSessionPercent: 3.9, geminiWeeklyPercent: 8.1)

#expect(windows.map { $0["label"] as? String } == [
"Gemini 5-hour",
"Gemini weekly",
"Claude/GPT 5-hour",
"Claude/GPT weekly",
])
#expect(windows.map { $0["usedPercent"] as? Double } == [3.9, 8.1, 0, 0])
}

@Test
func `dashboard omits the Antigravity representative labels`() throws {
let windows = try self.antigravityWindows(geminiSessionPercent: 3.9, geminiWeeklyPercent: 8.1)
let labels = windows.compactMap { $0["label"] as? String }

#expect(!labels.contains("Gemini Models"))
#expect(!labels.contains("Claude and GPT"))
}

@Test
func `dashboard keeps standard lanes for an Antigravity snapshot without summary windows`() throws {
let now = Date(timeIntervalSince1970: 1_700_000_000)
let usage = UsageSnapshot(
primary: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: now, resetDescription: nil),
secondary: RateWindow(usedPercent: 34, windowMinutes: 7 * 24 * 60, resetsAt: now, resetDescription: nil),
tertiary: nil,
updatedAt: now)

let windows = try self.windows(for: usage, now: now)

#expect(windows.map { $0["kind"] as? String } == ["session", "weekly"])
#expect(windows.map { $0["label"] as? String } == ["Gemini Models", "Claude and GPT"])
}

// MARK: - Fixtures

/// Mirrors the shape the quota-summary probe produces: four lanes, with `primary` and
/// `secondary` set to the most-used lane of each family rather than to distinct windows.
private func antigravityWindows(
geminiSessionPercent: Double,
geminiWeeklyPercent: Double) throws -> [[String: Any]]
{
let now = Date(timeIntervalSince1970: 1_700_000_000)
let geminiSession = RateWindow(
usedPercent: geminiSessionPercent,
windowMinutes: 300,
resetsAt: now,
resetDescription: nil)
let geminiWeekly = RateWindow(
usedPercent: geminiWeeklyPercent,
windowMinutes: 7 * 24 * 60,
resetsAt: now,
resetDescription: nil)
let thirdPartySession = RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: now, resetDescription: nil)
let thirdPartyWeekly = RateWindow(
usedPercent: 0,
windowMinutes: 7 * 24 * 60,
resetsAt: now,
resetDescription: nil)
let extras = [
NamedRateWindow(
id: "antigravity-quota-summary-gemini-5h",
title: "Gemini 5-hour",
window: geminiSession,
usageKnown: true),
NamedRateWindow(
id: "antigravity-quota-summary-gemini-weekly",
title: "Gemini weekly",
window: geminiWeekly,
usageKnown: true),
NamedRateWindow(
id: "antigravity-quota-summary-3p-5h",
title: "Claude/GPT 5-hour",
window: thirdPartySession,
usageKnown: true),
NamedRateWindow(
id: "antigravity-quota-summary-3p-weekly",
title: "Claude/GPT weekly",
window: thirdPartyWeekly,
usageKnown: true),
]
let usage = UsageSnapshot(
primary: geminiWeeklyPercent >= geminiSessionPercent ? geminiWeekly : geminiSession,
secondary: thirdPartySession,
tertiary: nil,
extraRateWindows: extras,
updatedAt: now)

return try self.windows(for: usage, now: now)
}

private func windows(for usage: UsageSnapshot, now: Date) throws -> [[String: Any]] {
let payload = ProviderPayload(
provider: .antigravity,
account: nil,
version: nil,
source: "cli",
status: nil,
usage: usage,
credits: nil,
antigravityPlanInfo: nil,
openaiDashboard: nil,
error: nil)
let snapshot = DashboardSnapshotBuilder.makeSnapshot(
usagePayloads: [payload],
costPayloads: [],
config: CodexBarConfig(providers: [ProviderConfig(id: .antigravity, enabled: true)]),
identityMode: .redacted,
generatedAt: now,
refreshInterval: 60,
codexBarVersion: nil)

let json = try #require(CodexBarCLI.encodeJSON(snapshot, pretty: false))
let data = try #require(json.data(using: .utf8))
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
let provider = try #require((object["providers"] as? [[String: Any]])?.first)
return try #require(provider["windows"] as? [[String: Any]])
}
}