Fix automatic quota display priority for exhausted windows & Antigravity session preference#2352
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39bcea869d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return self.mostConstrainedWindow( | ||
| primary: snapshot.primary, | ||
| secondary: snapshot.secondary, | ||
| tertiary: snapshot.tertiary) |
There was a problem hiding this comment.
Keep partially exhausted providers rankable
When this returns an exhausted lane for default multi-window providers (for example Kimi weekly at 100% with the 5-hour lane still active, or LiteLLM personal at 100% with team budget still active), the merged "show highest usage" path calls this resolver in UsageStore+HighestUsage.swift:55 and then shouldExcludeFromHighestUsage drops any non-special provider with metricPercent >= 100 by falling through to return true at lines 71-126. That means the exact exhausted quota this change is meant to surface is hidden from the unified icon/rotation unless all lanes are exhausted; add all-lanes-exhausted handling for the newly generalized providers or avoid feeding partial 100% windows into that exclusion path.
Useful? React with 👍 / 👎.
| let numberText = String( | ||
| format: L("≈%d full 5h windows of weekly left · %d windows until reset"), | ||
| locale: codexBarLocalizedLocale(), |
There was a problem hiding this comment.
Keep stringsdict plural formatting intact
These strings are backed by Localizable.stringsdict entries with %#@...@ plural variables (en.lproj/Localizable.stringsdict:5-45). String.localizedStringWithFormat is the plural-aware path that expands those stringsdict variants; replacing it with plain String(format:locale:) no longer applies the singular/plural selections, so forecasts like 1 window regress from the expected singular text in SessionEquivalentForecastTests.swift:292-293 and localized plural forms are lost. Use a plural-aware formatting path while still controlling the app locale.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed July 20, 2026, 9:29 AM ET / 13:29 UTC. Summary Reproducibility: yes. from source inspection: construct a provider with an exhausted selected lane and another usable lane to reproduce the highest-usage exclusion, or migrate a legacy Pace layout for a snapshot without weekly pace to observe the unavailable placeholder. Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review findings
Review detailsBest possible solution: Preserve compatibility for existing quota layouts and highest-usage selection, then land either a fully migrated Gemini-only Antigravity contract or the existing multi-family contract after the product owner chooses it and the contributor provides redacted runtime proof from a freshly built app. Do we have a high-confidence way to reproduce the issue? Yes from source inspection: construct a provider with an exhausted selected lane and another usable lane to reproduce the highest-usage exclusion, or migrate a legacy Pace layout for a snapshot without weekly pace to observe the unavailable placeholder. Is this the best way to solve the issue? No, not yet: exhausted-window priority is a reasonable fix, but it must preserve merged-provider eligibility and legacy layout fallback behavior, while the Gemini-only Antigravity scope requires an explicit product decision. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f8636cb37eb0. Label changesLabel justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (5 earlier review cycles)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 289349ab10
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self) | ||
| if let exhausted = windows.filter({ $0.usedPercent >= 100 }).max(by: { $0.usedPercent < $1.usedPercent }) { | ||
| return exhausted |
There was a problem hiding this comment.
Keep default providers on supported automatic lanes
For default providers with independent lanes, such as Gemini's Pro/Flash/Flash Lite quotas, this exhausted-first branch now lets an exhausted lower-priority tertiary lane win automatic selection even though Gemini's automatic behavior is expected to stay on the primary lane (see StatusItemAnimationTests.swift:578-612, which only covers the 95% case). In merged-icon/highest-usage mode that chosen 100% metric then falls through to UsageStore+HighestUsage.swift:126 and excludes the provider entirely, so a user with Pro still usable but Flash Lite exhausted loses Gemini from the unified icon instead of seeing the usable primary quota; limit this default exhausted-first behavior to providers whose partial exhaustion is handled, or add matching all-lanes-exhausted exclusion logic.
Useful? React with 👍 / 👎.
When no quota is exhausted, the automatic metric now prefers 5-hour session windows (windowMinutes=300) over weekly windows for the Antigravity provider. The session limit is the immediate constraint users care about; showing a weekly reset countdown (e.g. 'in 6d 18h') is less actionable than the session window.
7d2a263 to
5939580
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5939580582
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let sessionWindows = usableWindows.filter { | ||
| $0.windowMinutes == Self.antigravitySessionCadence | ||
| } |
There was a problem hiding this comment.
Keep Antigravity ranking and rendering aligned
When Antigravity has a usable 5-hour quota at 0% and a usable weekly quota at 99% with the default antigravityPrioritizeExhaustedQuotas == false, this new session-window preference makes the rendered automatic metric choose the 5-hour window. However UsageStore.menuBarMetricWindowForHighestUsage still calls its own unchanged Antigravity helper in Sources/CodexBar/UsageStore+HighestUsage.swift:41-139, which ranks the highest-used usable quota, so highest-usage mode can select Antigravity based on 99% weekly usage while the status item renders 0% 5-hour usage. Keep the ranking and rendering paths on the same resolver/rule so unified selection does not display a different quota than the one it selected.
Useful? React with 👍 / 👎.
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a56c65ca6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ]]) | ||
| case .pace: | ||
| return MenuBarLayout(lines: [[icon, .runsOut]]) | ||
| return MenuBarLayout(lines: [[icon, .pacePercent]]) |
There was a problem hiding this comment.
Preserve pace fallback in migrated layouts
When a provider cannot compute weeklyPace (missing weekly/reset metadata; the legacy .pace path explicitly falls back to the selected percent in MenuBarDisplayText.displayText), this migrated layout now renders only .pacePercent, whose renderer shows – when data.pacePercent is nil. If a legacy Pace user opens the layout editor or changes size/gap, that migration is persisted and the menu bar regresses from icon + quota percent to icon + placeholder for providers without pace data; keep the percent fallback when translating legacy .pace into a stored layout.
Useful? React with 👍 / 👎.
| let pacePercent = pace | ||
| .flatMap { MenuBarDisplayText.paceText(pace: $0) } |
There was a problem hiding this comment.
Track Pace % in the stored-layout icon signature
This new stored-layout value depends on weeklyPace, but storeIconObservationSignature() only signs the legacy menuBarDisplayText and cost tokens. For a user with a custom layout containing Pace % while the legacy display mode remains Percent, historicalPaceRevision changes wake the icon observer but the signature is unchanged, so updateIcons is skipped and the menu bar keeps showing the old pace value until some unrelated signed field changes; add a layout pace signature like the existing cost signature path.
Useful? React with 👍 / 👎.
|
@clawsweeper Simplified Antigravity logic: the menu bar now only tracks Gemini's quota (primary = 5h, secondary = weekly). Claude/GPT limits are kept parsed under extraRateWindows so they still render in the dropdown menu card. Removed over 1,100 lines of complex cross-family merging code. Ready for review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eed654fe58
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| matching: { $0.lowercased().contains("claude") || $0.lowercased().contains("gpt") }, | ||
| in: namedWindows) | ||
| matching: { name in | ||
| name.lowercased().contains("gemini") |
There was a problem hiding this comment.
Restore Claude/GPT quota selection for Antigravity
When the quota-summary payload contains both Gemini and Claude/GPT weekly buckets, this secondary representative now filters for Gemini again, even though Antigravity's metadata still labels the secondary lane as Claude and GPT (AntigravityProviderDescriptor.swift:12-13). That makes the menu/status surfaces show Gemini weekly usage under the Claude/GPT label and completely ignore a more constrained Claude/GPT quota; keep this predicate on the Claude/GPT family unless the rest of the UI is migrated to session/weekly semantics too.
Useful? React with 👍 / 👎.
| } | ||
| return windows.max(by: { $0.usedPercent < $1.usedPercent }) | ||
| // 2. Otherwise, fall back to the default order | ||
| return snapshot.primary ?? snapshot.secondary |
There was a problem hiding this comment.
Preserve Antigravity compact fallback in Automatic
For Antigravity local probes with only an unrecognized selectable text model, AntigravityStatusProbe still creates an antigravity-compact-fallback-* extra window, but after deleting the provider-specific branch this default fallback only returns snapshot.primary ?? snapshot.secondary. In that scenario both are nil, so Automatic produces no metric and the provider disappears from menu-bar/Overview ranking despite having usable quota data; include the compact fallback extra window before returning nil.
Useful? React with 👍 / 👎.
…eep partially exhausted Antigravity eligible for highest-usage
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec48432cfc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Default path: | ||
| // 1. Prioritize any exhausted window (usedPercent >= 100) | ||
| let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self) | ||
| if let exhausted = windows.filter({ $0.usedPercent >= 100 }).max(by: { $0.usedPercent < $1.usedPercent }) { |
There was a problem hiding this comment.
Respect the Antigravity exhausted-quota opt-out
When Antigravity is in Automatic mode with the default antigravityPrioritizeExhaustedQuotas == false, this default branch is still reached and returns any 100% lane before the normal session/primary preference; the setting is no longer consulted. In accounts with one exhausted Antigravity lane and another still usable, the “Prioritize exhausted quotas” toggle now behaves as if it is always enabled for the menu bar and highest-usage ranking, so gate this exhausted-first return for Antigravity on the setting or restore the provider-specific non-exhausted selection.
Useful? React with 👍 / 👎.
| let pace = paceWindow | ||
| .flatMap { self.store.weeklyPace(provider: provider, window: $0, now: now) } | ||
| let runsOut = pace | ||
| .flatMap { UsagePaceText.weeklyDetail(provider: provider, pace: $0, now: now).rightLabel } | ||
| let pacePercent = pace |
There was a problem hiding this comment.
Compute Pace % from the selected legacy lane
For stored layouts migrated from legacy Both/Pace with an explicit lane selection, this new Pace % value uses the shared pace computed from windows.weekly ?? windows.automatic, but legacy menuBarPaceWindow used the selected percent window for non-combined providers/modes. For example, Claude with Primary selected can keep showing the session percent while this token shows weekly pace instead of the selected session pace after the user opens/saves the layout editor; make the token carry the intended window or reuse the legacy pace-window resolver during migration/rendering.
Useful? React with 👍 / 👎.
…to satisfy length limits, and fix formatting
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: Fix automatic quota display priority for exhausted windows & Antigravity session preference This is item 1/1 in the current shard. Shard 0/1. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d64334f000
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let primary = primaryQuota.map(Self.rateWindow(for:)) | ||
| let secondary = secondaryQuota.map(Self.rateWindow(for:)) | ||
| let secondary: RateWindow? = nil |
There was a problem hiding this comment.
Let Claude/GPT coexist with Antigravity fallback
When a local Antigravity payload has no recognized Gemini quota but does include an unrecognized selectable text model plus any Claude/GPT quota, secondaryQuota is non-nil so the fallback guard above never sets compactFallbackModelID; this line then drops the Claude/GPT lane anyway. Since Claude/GPT is no longer rendered as a menu-bar lane, its presence suppresses the only compact fallback for new/unclassified local Gemini-style quotas, leaving automatic display with no usable primary/secondary metric.
Useful? React with 👍 / 👎.
| .percent(window: Self.percentWindow(for: metricPreference, provider: provider)), | ||
| .separatorDot, | ||
| .runsOut, | ||
| .pacePercent, |
There was a problem hiding this comment.
Avoid duplicating percent in migrated Both layouts
For legacy Both users whose provider cannot compute pace, persisting this migrated layout (for example via the layout editor or size/gap controls) renders the percent token and then .pacePercent, whose renderer falls back to the automatic percent when pace is nil. That changes the legacy fallback from a single quota like 42% to 42% · 42%; keep the pace fallback out of the sibling token or omit the separator/pace token when no pace is available.
Useful? React with 👍 / 👎.
Fix automatic quota display priority for exhausted windows & Simplify Antigravity to Gemini-only
Problem
1. Exhausted Quota Display (all default-path providers)
When a provider has multiple quota windows (e.g., weekly + 5-hour rate limit), the menu bar should display the exhausted window when any quota is used up. Instead, the default resolution logic (
snapshot.primary ?? snapshot.secondary) blindly returned the primary window if it existed, hiding an exhausted secondary window from the user.Example: Kimi's weekly quota is exhausted (0% remaining), but the menu bar still shows the 5-hour rate limit (96% remaining), misleading users into thinking they can still use the service.
2. Antigravity Quota Display Complexity
Previously, Antigravity attempted to merge, rank, and prioritize multiple distinct quota families (Gemini, Claude, GPT) across different cadences (5-hour session, weekly) within the unified menu bar icon and text.
This resulted in over 1,000 lines of complex special-case logic that:
Solution
Commit 1-3: Base fixes
usedPercent >= 100) first, then fall back tosnapshot.primary ?? snapshot.secondary. Custom overrides for Kimi/Factory/LiteLLM, Copilot, and MiniMax are fully preserved.UsagePaceText.swiftto usecodexBarLocalizedLocale()instead of implicitLocale.current.Commit 4-5: Simplify Antigravity to Gemini-Only
We realized that trying to merge multiple different model families in the menu bar was over-engineering. Users primarily use Antigravity for Gemini (the Claude/GPT quota is tiny and runs out instantly).
Thus, we made a major simplification:
extraRateWindows), so the user does not lose visibility.primary ?? secondary).This significantly reduces maintenance overhead and makes the menu bar behavior predictable and robust.
Provider Audit
Providers affected by default-path fix
Providers NOT affected (custom overrides preserved)
automaticPerplexityWindow()mostConstrainedWindowVerification Evidence
make test— 673/673 tests passed, 0 failures ✅make check— 0 violations ✅Files changed
Sources/CodexBarCore/Providers/Antigravity/AntigravityStatusProbe.swift— Directly maps Gemini 5-hour to primary and Gemini weekly to secondary.Sources/CodexBar/IconRemainingResolver.swift— Reverted custom Antigravity code and signature modifications.Sources/CodexBar/MenuBarMetricWindowResolver.swift— Removed all complex custom Antigravity ranking and helper methods.Sources/CodexBar/UsageStore+HighestUsage.swift— Reverted all custom Antigravity automatic metrics checks and static helpers.Tests/CodexBarTests/MenuBarMetricWindowResolverTests.swift&Tests/CodexBarTests/UsageStoreHighestUsageTests.swift&Tests/CodexBarTests/CodexbarTests.swift— Cleaned up obsolete Antigravity tests and added standard Gemini session vs weekly checks.