Skip to content

Control UI: keep slow reconnects out of the login gate#61345

Closed
coder-zhuzm wants to merge 5 commits into
openclaw:mainfrom
coder-zhuzm:zhuzm/dev
Closed

Control UI: keep slow reconnects out of the login gate#61345
coder-zhuzm wants to merge 5 commits into
openclaw:mainfrom
coder-zhuzm:zhuzm/dev

Conversation

@coder-zhuzm

@coder-zhuzm coder-zhuzm commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: When the Control UI was refreshed during a slow gateway reconnect, it could jump to the login gate even though the user still had a valid login state.
  • Why it matters: This makes a transient reconnect look like a logout or lost auth state, which is misleading and disruptive.
  • What changed: The Control UI now shows the login gate only for missing gateway configuration or high-confidence auth/config/device-auth failures, and otherwise keeps the dashboard shell visible with a topbar Connecting... status.
  • What did NOT change (scope boundary): This PR does not change gateway protocol behavior, reconnect mechanics, stored auth material, or backend auth rules.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: The Control UI treated every !connected state as a reason to render the login gate, even when the user still had valid auth state and the gateway was only reconnecting.
  • Missing detection / guardrail: There was no dedicated UI-side distinction between "needs re-auth" and "temporary reconnect in progress", and the gate logic did not fully use the gateway's structured connect error signals.
  • Contributing context (if known): The gateway already exposes structured connect error codes, but the UI gate logic was previously too coarse and did not distinguish reconnect states from true login-required states.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: N/A
  • Scenario the test should lock in: Refresh the Control UI during a slow reconnect and verify that the dashboard shell remains visible for reconnect states, while true auth/config/device-auth failures still route to the login gate.
  • Why this is the smallest reliable guardrail: The bug is a top-level UI state transition driven by gateway connection state and rendered layout, so it is best validated at the rendered app level.
  • Existing test that already covers this (if any): None known.
  • If no new test is added, why not: This change was validated manually in the local Control UI with a simulated slow reconnect, plus targeted review of the structured gateway error-code paths and normalized fallback error messages.

User-visible / Behavior Changes

  • Refreshing the Control UI during a slow reconnect no longer immediately sends the user to the login gate when auth is still valid.
  • A Connecting... status is shown in the topbar during reconnect states, including on mobile.
  • The login gate is still shown for true login-required states such as missing config, auth failures, pairing-required states, origin restrictions, and device-auth failures.

Diagram (if applicable)

Before:
[user refreshes during slow reconnect] -> [connected = false] -> [login gate shown]

After:
[user refreshes during slow reconnect] -> [connected = false] -> [UI checks config/error state] -> [dashboard shell stays visible + topbar Connecting...]

Auth/config failure path:
[user refreshes / reconnect fails with login-required signal] -> [UI checks config/error state] -> [login gate shown]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local gateway + built Control UI
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): local gateway with token auth and Control UI reconnect flow

Steps

  1. Open the Control UI and navigate to a dashboard page.
  2. Simulate a slow reconnect and refresh the page.
  3. Observe whether the UI stays in the dashboard shell or incorrectly switches to the login gate.

Expected

  • Slow reconnect states keep the dashboard shell visible and show a lightweight connecting indicator.
  • True auth/config/device-auth failures still route to the login gate.

Actual

  • Slow reconnect states now stay in the dashboard shell and show Connecting... in the topbar.
  • Login-required error states still render the login gate.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: Verified locally that slow reconnect states no longer jump to the login gate and instead show a topbar Connecting... status while preserving the dashboard shell.
  • Edge cases checked: Reviewed and verified login-gate routing for structured auth/config/device-auth error codes, plus fallback handling for high-confidence normalized auth error messages and generic non-auth gateway codes.
  • What you did not verify: Did not add automated test coverage; did not change or re-verify gateway-side reconnect semantics beyond the existing UI behavior and code-path review.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: A reconnect state without a structured auth/detail code could still be misclassified if future normalized error messages drift away from the current high-confidence fallback patterns.
    • Mitigation: The UI now relies primarily on structured gateway error codes, keeps the string fallback intentionally narrow, and aligns that fallback with normalized connect error messages.

@greptile-apps

greptile-apps Bot commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a UX bug where the Control UI would incorrectly display the login gate during a slow gateway reconnect, even when the user's auth state was still valid. Previously, any !connected state immediately triggered the login gate. The fix introduces shouldRenderLoginGate in overview-hints.ts, which checks structured gateway error codes (LOGIN_REQUIRED_CODES) and a narrow set of high-confidence string patterns before deciding to show the login gate — leaving all other disconnected states in the dashboard shell with a lightweight Connecting... topbar indicator. A companion CSS fix in layout.mobile.css ensures the Connecting... label is visible on mobile, overriding the generic pill-label display: none rule.

  • shouldRenderLoginGate cleanly separates "transient reconnect" from "auth/config failure" using structured error codes first and narrow string fallbacks second
  • The "Connecting..." topbar pill is only rendered inside the dashboard shell (past the login-gate early-return), so the !showLoginGate term in its conditional is redundant (though harmless)
  • The CSS specificity override for .topbar-connection-pill span:nth-child(2) is correctly ordered (equal specificity, later in file wins)
  • AUTH_RATE_LIMITED reaches LOGIN_REQUIRED_CODES via AUTH_FAILURE_CODES inheritance — rate-limit errors will still show the login gate rather than Connecting..., which is debatable for a transient condition, but mirrors pre-existing behavior and is not a regression introduced by this PR

Confidence Score: 4/5

Safe to merge — narrows an overly broad gate condition without touching gateway protocol, auth state, or stored credentials.

Well-scoped change using structured error codes as primary signal. The redundant !showLoginGate guard is harmless. CSS specificity is correct. AUTH_RATE_LIMITED classification is debatable but pre-existing. No new risks introduced.

ui/src/ui/views/overview-hints.ts — verify HIGH_CONFIDENCE_LOGIN_ERROR_PATTERNS stays aligned with normalized gateway error messages if those strings ever change in the future.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/ui/app-render.ts
Line: 537-542

Comment:
**Redundant `!showLoginGate` guard in topbar pill**

Since the dashboard render path is only reached after the early-return `if (showLoginGate) { return ... }` guard on line 364, `showLoginGate` is always `false` inside the dashboard template. The `!showLoginGate` term in the condition is a no-op and can be simplified to just `!state.connected`.

```suggestion
              ${!state.connected
                ? html`<div class="pill topbar-connection-pill" title="Gateway status: Connecting">
                    <span class="statusDot warn" aria-hidden="true"></span>
                    <span>Connecting...</span>
                  </div>`
                : nothing}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Control UI: keep slow reconnects out of ..." | Re-trigger Greptile

Re-review progress:

Comment thread ui/src/ui/app-render.ts Outdated
Comment on lines +537 to +542
${!state.connected && !showLoginGate
? html`<div class="pill topbar-connection-pill" title="Gateway status: Connecting">
<span class="statusDot warn" aria-hidden="true"></span>
<span>Connecting...</span>
</div>`
: nothing}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant !showLoginGate guard in topbar pill

Since the dashboard render path is only reached after the early-return if (showLoginGate) { return ... } guard on line 364, showLoginGate is always false inside the dashboard template. The !showLoginGate term in the condition is a no-op and can be simplified to just !state.connected.

Suggested change
${!state.connected && !showLoginGate
? html`<div class="pill topbar-connection-pill" title="Gateway status: Connecting">
<span class="statusDot warn" aria-hidden="true"></span>
<span>Connecting...</span>
</div>`
: nothing}
${!state.connected
? html`<div class="pill topbar-connection-pill" title="Gateway status: Connecting">
<span class="statusDot warn" aria-hidden="true"></span>
<span>Connecting...</span>
</div>`
: nothing}
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/app-render.ts
Line: 537-542

Comment:
**Redundant `!showLoginGate` guard in topbar pill**

Since the dashboard render path is only reached after the early-return `if (showLoginGate) { return ... }` guard on line 364, `showLoginGate` is always `false` inside the dashboard template. The `!showLoginGate` term in the condition is a no-op and can be simplified to just `!state.connected`.

```suggestion
              ${!state.connected
                ? html`<div class="pill topbar-connection-pill" title="Gateway status: Connecting">
                    <span class="statusDot warn" aria-hidden="true"></span>
                    <span>Connecting...</span>
                  </div>`
                : nothing}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. showLoginGate is already ruled out by the early return above, so this condition was redundant. I simplified it to !state.connected.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6fbb81b7d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ui/src/ui/views/overview-hints.ts Outdated
Comment on lines +132 to +133
if (params.lastErrorCode) {
return LOGIN_REQUIRED_CODES.has(params.lastErrorCode);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fall back to message matching for unknown error codes

shouldRenderLoginGate treats any non-null lastErrorCode as authoritative, but lastErrorCode can be a generic gateway code (for example INVALID_REQUEST) when structured details.code is absent. In that case this branch returns false and skips the high-confidence message patterns, so auth-required failures can be misclassified as reconnecting and the login gate never appears (the UI stays in a perpetual “Connecting...” shell).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. lastErrorCode can fall back to a generic gateway error code when structured auth details are absent, so treating any non-null code as authoritative was too strict. I updated the logic to return early only for known login-required codes and otherwise continue into the high-confidence message fallback.

@clawsweeper

clawsweeper Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: the same Control UI reconnect/login-gate problem is now tracked by the canonical open issue and replacement PR, while this branch still has a protocol-mismatch regression and no inspectable real behavior proof.

Canonical path: The related issue discussion identifies #72500 and #72522 as the active canonical path; the replacement PR is open, references the canonical issue with closing syntax, and implements a sticky prior-hello state rather than this branch's string-pattern gate.

So I’m closing this here and keeping the remaining discussion on #72500 and #72522.

Review details

Best possible solution:

Use the canonical issue and corrected replacement PR, or an equivalent successor, to preserve the Control UI shell during transient reconnects while keeping protocol-mismatch and auth/pairing/revocation failures routed to the login gate.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level. Current main gates every disconnected render, and this PR's predicate can be checked from source with connected=false, a configured gateway URL, lastError='protocol mismatch', and lastErrorCode=null.

Is this the best way to solve the issue?

No. The central direction is valid, but this branch is no longer the best path because the canonical issue/replacement PR track the same root cause more directly and this branch regresses existing protocol-mismatch guidance.

Security review:

Security review cleared: The diff changes Control UI render gating and CSS only, with no dependency, workflow, permission, backend auth, secret-storage, or new network-call changes.

What I checked:

  • PR head predicate omits protocol mismatch: At PR head, HIGH_CONFIDENCE_LOGIN_ERROR_PATTERNS includes auth, pairing, origin, rate-limit, and device identity patterns but not protocol mismatch; shouldRenderLoginGate then returns false for a configured disconnected state with lastError='protocol mismatch' and no structured detail code. (ui/src/ui/views/overview-hints.ts:52, 689514877268)
  • Current main preserves protocol-mismatch login guidance: Current main has an explicit protocol-mismatch login failure branch with update/reopen guidance and tests asserting that unstructured lastError: 'protocol mismatch' renders that panel. (ui/src/ui/views/login-gate.ts:181, 210ff7d318db)
  • Current main still has the reported reconnect gate behavior: Current renderApp still returns renderLoginGate(state) for every disconnected render, so the central slow/transient reconnect problem remains real on main and is suitable for one canonical fix path. (ui/src/ui/app-render.ts:698, 210ff7d318db)
  • Canonical issue and replacement PR own the remaining work: The related issue discussion identifies [Bug]: Control UI webchat flashes credentials gate on tab re-focus due to immediate connected=false render #72500 and fix(control-ui): keep chat UI mounted across transient reconnects #72522 as the active canonical path; the replacement PR is open, references the canonical issue with closing syntax, and implements a sticky prior-hello state rather than this branch's string-pattern gate. (aafc2455ea4c)
  • Prior review and live PR state still block this branch: The live PR body/comments claim local verification but provide no inspectable artifact, and the existing ClawSweeper review already flagged the protocol-mismatch regression plus missing real behavior proof. (689514877268)

Likely related people:

  • BunsDev: Authored the current protocol-mismatch login failure guidance and tests that this PR must preserve, and recently worked in adjacent Control UI responsiveness/auth surfaces. (role: recent Control UI login-gate contributor; confidence: high; commits: 79e31421225b, 6b3cd9043ee6, 5ae385b2f0f1; files: ui/src/ui/views/login-gate.ts, ui/src/ui/views/login-gate.test.ts, ui/src/ui/app-render.ts)
  • sallyom: Merged history restored Control UI auth bypass and classified connect failures in overview-hints, app-gateway, and the gateway protocol detail-code contract this PR relies on. (role: adjacent auth/connect classification contributor; confidence: medium; commits: e5fe818a7491, d37e3d582fd8; files: ui/src/ui/views/overview-hints.ts, ui/src/ui/app-gateway.ts, src/gateway/protocol/connect-error-details.ts)
  • gumadeiras: Recent Control UI commits addressed seq-gap reconnect and reconnect-state preservation near the transient disconnect behavior changed by this PR. (role: adjacent reconnect behavior contributor; confidence: medium; commits: 1600c1726e6b, c191dc992884, b952e404fa25; files: ui/src/ui/app-gateway.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 210ff7d318db.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant