Fix Antigravity, duplicate accounts, strip not-ready providers; prepare 1.5.12#156
Conversation
Antigravity 2.3+ on Windows advertises --https_server_port 0 and omits --extension_server_port, so process detection never accepted the language server. Accept CSRF + PID (or a real advertised port) and discover the API via listening-port enumeration. Opening Accounts auto-registers the ambient Codex home with a UUID, while the earlier ambient reading (account_id=None) stayed in the cache forever. Prune cache rows to the cycle's expected keys after refresh, and resync the UI authoritatively on refresh-complete so ghost cards leave the overview. Fixes #153, #155
Enabling a provider only put an error placeholder in the cache. The strip showed it as a blank dash pill, and Antigravity's session window label is "Claude", so the pill looked like a broken Claude seat with the wrong identity. Only error-free readings belong on the always-visible capacity strip. Overview and Settings still show setup failures so the user can finish auth. Error snapshots no longer carry session_label as primary_label.
Version bump to 1.5.12 (build 114). Ships Antigravity Windows detection, duplicate-account cache prune, and taskbar strip eligibility for not-ready providers. After merge, tag v1.5.12 on the squash commit on protected main.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ceiling | 0b05e32 | Commit Preview URL Branch Preview URL |
Jul 26 2026, 02:05 PM |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 45 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughCeiling 1.5.12 improves Antigravity IDE/CLI detection, removes stale provider account readings, hides unauthenticated providers from the float bar, updates provider glyph rendering, and bumps release metadata and package versions. ChangesProvider refresh and presentation updates
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant WindowsProcessEnumeration
participant AntigravityDetector
participant LocalConnectAPI
WindowsProcessEnumeration->>AntigravityDetector: enumerate matching IDE or CLI process
AntigravityDetector->>WindowsProcessEnumeration: discover PID listening ports
AntigravityDetector->>LocalConnectAPI: probe candidate API port
LocalConnectAPI-->>AntigravityDetector: return user status and quota data
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Antigravity CLI (agy) hosts the same local quota API as the IDE language server but without a CSRF token and under a different process name. Match agy.exe / antigravity-cli by image path only so shell scripts that merely mention agy are not mistaken for the server, and allow empty CSRF for CLI matches only. Native taskbar only had bitmaps for Codex/Claude/Cursor/Grok, so Gemini fell through to a hollow ring. Add Gemini and Antigravity 16x16 marks, brand colors, and stronger strip SVG tinting so first-class seats stay readable. Gemini remains a separate provider (oauth_creds) from Antigravity.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/desktop-tauri/src-tauri/src/commands/providers.rs (1)
250-253: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSilent skip when the state lock is poisoned.
Every other
state.lock()site in this module logs on failure; here a poisoned lock silently leaves ghost rows in the cache with no diagnostic. Atracing::warn!in theelsekeeps the failure debuggable.♻️ Proposed tweak
- if let Ok(mut guard) = state.lock() { - prune_stale_provider_readings(&mut guard.provider_cache, &expected, &inputs.enabled_ids); - } + match state.lock() { + Ok(mut guard) => { + prune_stale_provider_readings( + &mut guard.provider_cache, + &expected, + &inputs.enabled_ids, + ); + } + Err(_) => tracing::warn!("Provider state poisoned; skipped stale-reading pruning"), + }As per coding guidelines, "Use
tracingfor diagnostics".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop-tauri/src-tauri/src/commands/providers.rs` around lines 250 - 253, Update the state.lock() handling around prune_stale_provider_readings to add a tracing::warn! diagnostic when the lock is poisoned, matching the logging behavior of other state.lock() sites in this module while preserving the existing pruning path on successful locking.Source: Coding guidelines
apps/desktop-tauri/src/floatbar/FloatBar.css (1)
799-807: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueBrand tint is now forced regardless of the light-background contrast mode.
color: var(--brand …) !importantapplies infloatbar--light-bgtoo, so near-white brands (e.g. Grok's silver) sit on an 18%-tint badge over a light surface and can wash out. Consider scoping the override, or darkening viacolor-mix(… , currentColor)under.floatbar--light-bg.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop-tauri/src/floatbar/FloatBar.css` around lines 799 - 807, Scope the brand color, background, and inset box-shadow overrides in the `.floatbar__bar .floatbar__provider-icon .provider-icon` rule so they do not force low-contrast near-white brand colors in `.floatbar--light-bg`; preserve the existing branded styling for other modes and the SVG sizing in `.provider-icon--svg`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop-tauri/src/floatbar/FloatBar.tsx`:
- Around line 533-540: Update the eligibility logic around isFloatBarEligible in
the visible useMemo so transient fetch errors do not remove providers that have
previously produced a successful reading. Suppress only providers that have
never reached a ready/authenticated state, while retaining established error
snapshots for ProviderPill to render its existing stale or “—” state and
preserve pill layout.
In `@docs/HANDOFF.md`:
- Line 9: Update the release state entry in HANDOFF.md to mark Ceiling 1.5.12 as
the public release and advance the package/next-draft version to the appropriate
post-release version, preserving the existing release-link and description
format.
In `@rust/src/providers/antigravity/mod.rs`:
- Around line 199-226: Update parse_process_info to avoid returning a tokenless,
port-zero CLI candidate before stronger matches: retain the best candidate while
scanning, immediately prefer candidates with a non-zero advertised port and/or
CSRF token, and return the tokenless CLI only as a fallback after all processes
are examined. Preserve the existing eligibility checks and ProcessInfo fields.
---
Nitpick comments:
In `@apps/desktop-tauri/src-tauri/src/commands/providers.rs`:
- Around line 250-253: Update the state.lock() handling around
prune_stale_provider_readings to add a tracing::warn! diagnostic when the lock
is poisoned, matching the logging behavior of other state.lock() sites in this
module while preserving the existing pruning path on successful locking.
In `@apps/desktop-tauri/src/floatbar/FloatBar.css`:
- Around line 799-807: Scope the brand color, background, and inset box-shadow
overrides in the `.floatbar__bar .floatbar__provider-icon .provider-icon` rule
so they do not force low-contrast near-white brand colors in
`.floatbar--light-bg`; preserve the existing branded styling for other modes and
the SVG sizing in `.provider-icon--svg`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bed85bb9-0ef2-41d1-8ebb-391fce2c4683
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
.github/release-notes-1.5.12.mdCHANGELOG.mdapps/desktop-tauri/package.jsonapps/desktop-tauri/src-tauri/Cargo.tomlapps/desktop-tauri/src-tauri/src/commands/bridge.rsapps/desktop-tauri/src-tauri/src/commands/providers.rsapps/desktop-tauri/src-tauri/src/commands/tests.rsapps/desktop-tauri/src-tauri/src/taskbar_widget.rsapps/desktop-tauri/src-tauri/tauri.conf.jsonapps/desktop-tauri/src/components/providers/ProviderIcon.tsxapps/desktop-tauri/src/floatbar/FloatBar.cssapps/desktop-tauri/src/floatbar/FloatBar.test.tsxapps/desktop-tauri/src/floatbar/FloatBar.tsxapps/desktop-tauri/src/hooks/useProviders.test.tsxapps/desktop-tauri/src/hooks/useProviders.tsdocs/HANDOFF.mdrust/Cargo.tomlrust/src/providers/antigravity/mod.rsversion.env
| const visible = useMemo(() => { | ||
| const enabled = new Set(settings.enabledProviders); | ||
| const eligible = providers.filter((p) => enabled.has(p.providerId)); | ||
| // Enabled alone is not enough: skip not-yet-ready error placeholders so | ||
| // turning on Antigravity without the app installed does not mint a blank | ||
| // "Claude" pill on the taskbar. | ||
| const eligible = providers.filter( | ||
| (p) => enabled.has(p.providerId) && isFloatBarEligible(p), | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Transient fetch failures now make an established pill vanish, not just gray out.
The filter is on the current reading's error, not on "never authenticated". When a working provider's next refresh errors, its cached row becomes an error snapshot and the pill disappears — the capsule reflows and the number is lost until recovery, which is a bigger jolt than the previous stale/"—" pill. If the intent is only to suppress never-ready providers (as the new test's name suggests), consider retaining the last successful reading with a stale chip instead.
Also note ProviderPill's error branches (exhausted via provider.error, the "—" label) are now dead from this call site.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/desktop-tauri/src/floatbar/FloatBar.tsx` around lines 533 - 540, Update
the eligibility logic around isFloatBarEligible in the visible useMemo so
transient fetch errors do not remove providers that have previously produced a
successful reading. Suppress only providers that have never reached a
ready/authenticated state, while retaining established error snapshots for
ProviderPill to render its existing stale or “—” state and preserve pill layout.
| - Repository: `C:\projects\personal\ceiling` | ||
| - Trunk: protected `main`; create short-lived feature branches for new work. PRs squash-merge. | ||
| - Public release: [Ceiling 1.5.6](https://github.com/tsouth89/ceiling/releases/tag/v1.5.6). Package / next draft: **1.5.11** (everything since 1.5.6: multi-account strip pin, Charts trust/efficiency, strip polish, constraining-window taskbar meters, Grok API-equivalent $ charts). | ||
| - Public release: [Ceiling 1.5.11](https://github.com/tsouth89/ceiling/releases/tag/v1.5.11). Package / next draft: **1.5.12** (Antigravity detection, duplicate-account prune, strip hides not-ready providers). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update the handoff release state.
This PR releases 1.5.12, but the handoff still says 1.5.11 is public and 1.5.12 is the next draft. Update the public release and next-draft version to reflect the post-release state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/HANDOFF.md` at line 9, Update the release state entry in HANDOFF.md to
mark Ceiling 1.5.12 as the public release and advance the package/next-draft
version to the appropriate post-release version, preserving the existing
release-link and description format.
| let port = match extension_port { | ||
| Some(p) if p > 0 => p, | ||
| _ => match https_port { | ||
| Some(p) if p > 0 => p, | ||
| _ => 0, | ||
| }, | ||
| }; | ||
|
|
||
| // Need a real advertised port or a PID to enumerate listening ports. | ||
| if port == 0 && pid.is_none() { | ||
| continue; | ||
| } | ||
|
|
||
| // IDE language_server requires CSRF. Tokenless IDE matches are skipped so a | ||
| // later valid IDE (or CLI) candidate can still be found. | ||
| // CLI accepts an empty token — its local server does not check CSRF. | ||
| let token = match (is_cli, csrf_token) { | ||
| (_, Some(token)) => token, | ||
| (true, None) => String::new(), | ||
| (false, None) => continue, | ||
| }; | ||
|
|
||
| return Some(ProcessInfo { | ||
| csrf_token: token, | ||
| extension_server_csrf_token: ext_csrf_token, | ||
| extension_port: port, | ||
| pid, | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
First match wins, so a transient agy process can shadow a live IDE language server.
parse_process_info returns on the first accepted candidate. A short-lived CLI (agy -p …, port 0, no listening Connect port) enumerated before the IDE's language_server.exe will be selected, and find_api_port then fails with "Could not find Antigravity API port" even though the IDE was discoverable. Consider collecting candidates and preferring ones with a non-zero advertised port / CSRF token before falling back to the tokenless CLI.
♻️ Sketch
- return Some(ProcessInfo {
- csrf_token: token,
- extension_server_csrf_token: ext_csrf_token,
- extension_port: port,
- pid,
- });
+ let candidate = ProcessInfo {
+ csrf_token: token,
+ extension_server_csrf_token: ext_csrf_token,
+ extension_port: port,
+ pid,
+ };
+ // Prefer a candidate that advertises a real port or a CSRF token;
+ // keep a tokenless CLI only as a fallback.
+ if candidate.extension_port > 0 || !candidate.csrf_token.is_empty() {
+ return Some(candidate);
+ }
+ fallback.get_or_insert(candidate);(then return fallback; instead of None)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@rust/src/providers/antigravity/mod.rs` around lines 199 - 226, Update
parse_process_info to avoid returning a tokenless, port-zero CLI candidate
before stronger matches: retain the best candidate while scanning, immediately
prefer candidates with a non-zero advertised port and/or CSRF token, and return
the tokenless CLI only as a fallback after all processes are examined. Preserve
the existing eligibility checks and ProcessInfo fields.
Summary
Three reliability fixes plus the 1.5.12 version bump so we can ship them.
--https_server_port 0without--extension_server_port(Antigravity 2.3+ on Windows). Discover the API via PID listening ports.account_id=Noneghost rows from the provider cache and resync the UI on refresh-complete.Version bump to 1.5.12 (build 114).
Related issue
Fixes #153
Fixes #155
Affected areas
Validation
cargo test --manifest-path rust/Cargo.toml antigravity(11 passed)cargo test --manifest-path apps/desktop-tauri/src-tauri/Cargo.toml pruning_/expected_keys/provider_cache(passed)npm test -- --run src/hooks/useProviders.test.tsx(18 passed)npm test -- --run src/floatbar/FloatBar.test.tsx(22 passed)UI / tray proof
Notes for reviewers
After squash-merge to protected
main, tagv1.5.12on that commit only to start the signed Windows release workflow (tag must match version files and be an ancestor of main).Release notes:
.github/release-notes-1.5.12.mdSummary by CodeRabbit
Bug Fixes
Release