fix(control-ui): keep the dashboard mounted with a reconnect banner on gateway drops#100479
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 8:36 PM ET / 00:36 UTC. Summary Reproducibility: yes. source-reproducible: current main sets Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. 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:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this after exact-head CI is complete and the PR body includes redacted real browser proof for both transient reconnect and explicit credential-failure behavior. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: current main sets Is this the best way to solve the issue? Yes, this appears to be the best code shape: carry the Gateway client's retry decision into the app snapshot, keep the shell only for post-hello recoverable drops, and return to the gate for first-connect or credential failures. The remaining gap is real behavior proof, not a different implementation layer. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 7e7fc0075e3c. 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 (3 earlier review cycles)
|
c26abd2 to
a7be62e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6ae0d502b
ℹ️ 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".
|
|
||
| ### Changes | ||
|
|
||
| - **iOS offline chat:** pre-paint recent sessions and canonical transcripts from a protected, bounded per-gateway cache, keep sending disabled offline, and purge cached conversation text when pairing is reset. (#100194) |
There was a problem hiding this comment.
Remove hand-edited changelog entries
For normal commits like this one, the repo instructions say CHANGELOG.md is release-only and release generation derives entries from merged PRs/direct commits. Adding Unreleased bullets here means the generated release changelog can be overwritten or conflict with hand-authored text, and this commit also adds release-note content outside the control-ui fix it claims to make. Please keep release-note context in the PR/commit body and leave CHANGELOG.md to the release generator.
Useful? React with 👍 / 👎.
…n gateway drops Once a session is established, a dropped gateway WebSocket no longer unmounts the dashboard into the login gate. The client's close handler now reports willRetry (the same fact that drives its reconnect scheduling), the gateway store derives a `reconnecting` snapshot state from it (everConnected && willRetry), and the app shell stays mounted with an amber "Gateway connection lost - reconnecting" banner plus a Retry now action while the client retries with backoff. The login gate is reserved for first connects, credential rejections, and manual gate submissions; event-gap recovery also no longer flashes the gate. createApplicationGateway moved from bootstrap.ts to gateway-store.ts with an injectable client factory for direct behavior tests. Adds the previously unstyled `.callout.warn` variant and a "Connection loss and reconnect" docs section. Fixes #100475
… fixture, regenerate docs map
b6ae0d5 to
211240c
Compare
…n gateway drops (openclaw#100479) * fix(control-ui): keep the dashboard mounted with a reconnect banner on gateway drops Once a session is established, a dropped gateway WebSocket no longer unmounts the dashboard into the login gate. The client's close handler now reports willRetry (the same fact that drives its reconnect scheduling), the gateway store derives a `reconnecting` snapshot state from it (everConnected && willRetry), and the app shell stays mounted with an amber "Gateway connection lost - reconnecting" banner plus a Retry now action while the client retries with backoff. The login gate is reserved for first connects, credential rejections, and manual gate submissions; event-gap recovery also no longer flashes the gate. createApplicationGateway moved from bootstrap.ts to gateway-store.ts with an injectable client factory for direct behavior tests. Adds the previously unstyled `.callout.warn` variant and a "Connection loss and reconnect" docs section. Fixes openclaw#100475 * chore(i18n): regenerate control-ui locale bundles for connection banner strings * chore(control-ui): unbreak CI - add reconnecting to overlays snapshot fixture, regenerate docs map * chore(i18n): re-sync locale metadata after rebase onto refreshed main locales * chore(i18n): refresh raw-copy baseline after rebase
…n gateway drops (openclaw#100479) * fix(control-ui): keep the dashboard mounted with a reconnect banner on gateway drops Once a session is established, a dropped gateway WebSocket no longer unmounts the dashboard into the login gate. The client's close handler now reports willRetry (the same fact that drives its reconnect scheduling), the gateway store derives a `reconnecting` snapshot state from it (everConnected && willRetry), and the app shell stays mounted with an amber "Gateway connection lost - reconnecting" banner plus a Retry now action while the client retries with backoff. The login gate is reserved for first connects, credential rejections, and manual gate submissions; event-gap recovery also no longer flashes the gate. createApplicationGateway moved from bootstrap.ts to gateway-store.ts with an injectable client factory for direct behavior tests. Adds the previously unstyled `.callout.warn` variant and a "Connection loss and reconnect" docs section. Fixes openclaw#100475 * chore(i18n): regenerate control-ui locale bundles for connection banner strings * chore(control-ui): unbreak CI - add reconnecting to overlays snapshot fixture, regenerate docs map * chore(i18n): re-sync locale metadata after rebase onto refreshed main locales * chore(i18n): refresh raw-copy baseline after rebase
What Problem This Solves
Once the Control UI has an authenticated session, any WebSocket drop (gateway restart, laptop sleep, SSH-tunnel churn in the macOS app's remote mode, network blip) unmounts the entire dashboard and replaces it with the login gate showing a red "Could not connect" card — even though
GatewayBrowserClientkeeps auto-reconnecting with backoff underneath. The operator is visually "logged out" by a transient transport failure and loses all page context; when the retry succeeds the gate flashes back to the dashboard.The disconnect-resilient machinery already existed but was unreachable:
chat-page.applyGatewaySnapshothandles in-place disconnect/reconnect (queued sends marked "waiting for reconnect", history re-sync on reconnect) and the sidebar has an offline status dot — none of it ever visible becauseOpenClawApp.renderswapped the shell for the gate the momentconnectedflipped false.Fixes #100475.
Why This Change Was Made
Failing gracefully requires distinguishing "transport interrupted, client is retrying" from "credentials rejected, operator input needed". That fact lives in the client's close handler, so it is now exported instead of re-derived:
ui/src/api/gateway.ts: the close path computeswillRetryonce and uses it both to schedule the reconnect and to report it inonClose— a single source of truth for retry policy. Constructor failures (bad URL, mixed content) reportwillRetry: false.ui/src/app/gateway-store.ts(extracted frombootstrap.tsfor a test seam, with an injectable client factory): the snapshot gainsreconnecting = everConnected && willRetry, whereeverConnectedtracks whether a hello succeeded this page lifetime. First-connect failures stay on the gate; established sessions degrade to reconnecting.ui/src/app/app-host.ts: the shell stays mounted whilereconnecting, rendering an amber "Gateway connection lost — reconnecting…" banner (newopenclaw-connection-banner) with a Retry now action. The login gate is reserved for first connects, credential rejections, and manual gate submissions (aloginGatePinnedflag prevents shell flicker while a gate-submitted attempt is in flight).onGap→ fresh client) now also keeps the shell mounted instead of flashing the gate..callout.warnCSS variant added — previously referenced by update status banners but never styled.Keeping the shell mounted makes the existing per-page reconnect handling load-bearing (chat re-syncs subscriptions/queued sends on reconnect; the shell's
ensureAgentsListalready re-fetches on client change).User Impact
connection.*, translated for all locales viapnpm ui:i18n:sync(0 fallbacks).docs/web/control-ui.mdgains a "Connection loss and reconnect" section.Evidence
ui/src/app/gateway-store.test.ts(7 tests): first-connect failures stay on the gate; transport drop after hello →reconnecting: true; credential rejection → gate; event-gap recovery keepsreconnecting;stop()resets lineage; superseded clients cannot demote the snapshot.ui/src/api/gateway.node.test.ts:willRetryasserted on recoverable close, credential rejection (AUTH_PASSWORD_MISMATCH), both token-mismatch no-retry paths, and both constructor-failure paths.pnpm check:changedgreen on Blacksmith Testbox (run: https://github.com/openclaw/openclaw/actions/runs/28756286250).vitest run --config test/vitest/vitest.ui.config.ts ui/src/app/gateway-store.test.ts ui/src/api/gateway.node.test.ts ui/src/pages/skill-workshop/proposals.test.ts→ green. (ui/src/pages/plugin/plugin-page.test.tsfails identically on pristine HEAD in this macOS environment and is green on the Linux Testbox run above — pre-existing local flake, unrelated.)