Skip to content

fix(remote-browser): host client browser pages again when a paired host comes back after an outage - #25827

Merged
Jinwoo-H merged 4 commits into
mainfrom
client-hosted-browser-rehost
Oct 6, 2026
Merged

Jinwoo-H merged 4 commits into
mainfrom
client-hosted-browser-rehost

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​461 $\color{#cf222e}{\Huge{\mathbf{−}}}$​18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​443
Prod 10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​90 $\color{#cf222e}{\Huge{\mathbf{−}}}$​26 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​64

ELI5

A remote browser tab in a paired workspace is drawn on your desktop. If the network went away for more than about 15 seconds (Wi‑Fi drop, laptop sleep), the desktop stopped hosting that page and never offered to host it again, so the tab said "Client-hosted browser unavailable … attached to a different desktop" until Orca was restarted. Now, when the remote comes back, the desktop hosts its pages again and they reload at their last address.

What Changed

Before: the desktop re-claimed browser hosting for a remote only when that remote restarted. Losing and regaining contact with the same remote did nothing, even though the outage had already torn the hosting down.

After: one rule in setRuntimeEnvironmentStatus: when the runtime restarts or contact with it is regained, run the existing ensure (renamed ensureBrowserClientHostOnRuntimeContact; its guards and coalescing are unchanged). If this desktop still has client-placed pages for that remote, it starts hosting again, and the remote's existing recovery re-places them, reloading at their last address. The pane picks up the new placement through its existing effect. The contact-regained condition is computed once and also drives the toast dismissal and the host-contact counter, replacing a duplicated expression.

Message: the notice names "another desktop" only when the page actually belongs to a different desktop. While the remote is unreachable it says " is offline. This page will reload here once it reconnects"; otherwise "This page isn't available on this desktop right now". Translated in all locales.

Why

The root cause was a missing trigger, not missing machinery: the restart path already knew how to re-host. Alternatives considered:

Trade-off: after a long outage the page reloads at its last address. You stay signed in, but scroll position and half-typed forms are lost.

Compatibility: client-only, with no host or wire change. Older clients are unchanged.

Linked Issue

N/A (internal report).

Visual Proof

To follow in a comment (the notice before, and the page re-hosted after a 25s outage).

Testing

  • E2E, tests/e2e/paired-client-hosted-browser-network-outage.spec.ts: a real paired host and desktop with isolated profiles, and a TCP proxy that cuts the link by reset (25s) or silently (100s, long enough for the host's heartbeat to reap the socket).

    • Red on main 2/2: the page's host generation never changes and the notice stays.
    • Green 2/2: the page is re-hosted, renders, and navigates to a new URL through the remote.
  • All tests/e2e/paired-client-hosted-browser* specs: 13/13.

  • Unit: a new runtime-status test re-attaches when the same runtime answers again after contact was lost, through the real store slice and the real ensure, and goes red without the change. Related suites: 3166 passed.

  • pnpm tc, oxlint, oxfmt, check:code-quality:changed and the localization checks pass.

  • Platforms: macOS desktop with a headless macOS remote. Windows host not exercised.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@Jinwoo-H
Jinwoo-H requested a review from brennanb2025 as a code owner October 6, 2026 07:42
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 782e1d1c-0032-462e-a799-1059b019a994
📥 Commits

Reviewing files that changed from the base of the PR and between 2d0b1c29d07e47a469873af482fc3b30fe1c2c11 and b2e29d1.

📒 Files selected for processing (4)
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Runtime status handling now prepares the client-hosted browser host after a restart or when runtime contact returns. End-to-end tests cover silent and reset network outages and verify recovery with a new host generation. The unavailable notice now distinguishes pages open on another desktop, offline hosts, and pages unavailable on the current desktop. Translations for these states were updated in six locales.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to b2e29

A page may remain unavailable after contact returns in the identified coalescing case. Resolve or explicitly accept that recovery risk before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 9 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: reclaiming client-browser hosting when a paired host returns after an outage.
Description check ✅ Passed The description covers the user-facing change, mechanism, rationale, trade-off, compatibility, and reported testing. It identifies the issue as an internal report and gives a visual-proof reference, t…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 9 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
tests/e2e/paired-client-hosted-browser-network-outage.spec.ts (1)

173-181: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cleanup failure can mask the real test failure and skip later cleanup.

Each cleanup step in finally is awaited in sequence without protection. If client.dispose() or proxy?.close() throws, the remaining steps (host.dispose(), fixture.close()) are skipped. This leaks the headless host process and its profile, and the cleanup error replaces the original assertion error. Make each step independent so every resource is released.

Proposed fix
     } finally {
-      if (client) {
-        await cleanupE2EDaemons(client.userDataDir).catch(() => undefined)
-        await client.dispose()
-      }
-      await proxy?.close()
-      await host.dispose()
-      await fixture.close()
+      await Promise.allSettled([
+        (async () => {
+          if (client) {
+            await cleanupE2EDaemons(client.userDataDir).catch(() => undefined)
+            await client.dispose()
+          }
+        })(),
+        proxy?.close(),
+        host.dispose(),
+        fixture.close()
+      ])
     }

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 24f77ebc-e334-4467-858b-c075ce4c18db
📥 Commits

Reviewing files that changed from the base of the PR and between 9905765 and c4c7057.

📒 Files selected for processing (14)
  • src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.tsx
  • src/renderer/src/components/browser-pane/client-hosted-browser-unavailable-notice.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/runtime/restored-client-hosted-browser-host-attach.ts
  • src/renderer/src/runtime/restored-client-hosted-browser-host-restart-attach.test.ts
  • src/renderer/src/store/slices/runtime-status-snapshot.test.ts
  • src/renderer/src/store/slices/runtime-status.ts
  • tests/e2e/helpers/freezable-tcp-proxy.ts
  • tests/e2e/paired-client-hosted-browser-network-outage.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b8d75095-0b34-453d-b7fd-ee6056c32a12
📥 Commits

Reviewing files that changed from the base of the PR and between c4c7057 and f1f0a82.

📒 Files selected for processing (5)
  • src/renderer/src/components/browser-pane/client-hosted-browser-unavailable-notice.test.tsx
  • src/renderer/src/components/browser-pane/client-hosted-browser-unavailable-notice.tsx
  • src/renderer/src/runtime/restored-client-hosted-browser-host-attach.ts
  • src/renderer/src/runtime/restored-client-hosted-browser-host-restart-attach.test.ts
  • src/renderer/src/store/slices/runtime-status.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment thread src/renderer/src/runtime/restored-client-hosted-browser-host-attach.ts Outdated
@Jinwoo-H

Jinwoo-H commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Visual proof and final regression run (head 2d0b1c29d07)

Captured over CDP from hidden windows: a real paired host and desktop with isolated profiles, and a 25s network cut through a TCP proxy. A first-run tooltip ("This page renders on your desktop") overlaps the top of the page in every shot.

Before (main), after the network returns: stuck on "unavailable … attached to a different desktop", with the host connected again ("1 host").
before.png

After (this PR), during the outage: " is offline. This page will reload here once it reconnects." The status bar shows "Connecting…". The host name here is the test's environment label.
after-offline.png

After (this PR), once the network returns: the page is re-hosted and rendered at its last address.
after.png

Regression run (fresh e2e build, ORCA_BACKGROUND_LAUNCH=1)

Suite Result
all paired-client-hosted-browser* (10 specs, including the reset and silent outage tests) 13/13
paired terminal host-restart, client-restart, host-quit-reconnect, materialization-reconnect, serve-restart-binding 6/6
paired browser stream-reconnect, ghost-subscriber-rejoin, split-pane host-retired 4/4
runtime-host-status-recovery (needs ORCA_E2E_WEB_CLIENT=1) 2/2
unit: runtime-status*, restored-client-hosted*, components/browser-pane 1055/1055

Skipped:

  • the SSH client-hosted drop/reconnect spec (needs Docker);
  • one serve-restart case that creates a Codex automation;
  • the headful-only probe-gap spec.

Typecheck on current main fails in structured-agent-session-live-turn.test.ts. That's unrelated to this PR and is fixed in #25936.

…ailable after a network outage

A paired desktop that loses its host for longer than the browser-host
lease grace (25s reset cut, or a 100s silent cut the host's heartbeat
reaps) tears down its client-hosted pages and never hosts them again,
leaving 'Client-hosted browser unavailable'. Red on main.

Extends the freezable TCP proxy with a full silent or reset link cut.
…st comes back after an outage

When the network between a paired desktop and its Orca host dropped for longer
than the browser-host lease grace, the desktop tore down its client-hosted
pages and never offered to host them again, so the tab stayed on 'Client-hosted
browser unavailable' until the app was relaunched.

Regaining contact with a runtime now re-claims browser hosting the same way a
runtime restart already did: if this desktop still has client-placed pages for
that host, it starts its browser client host again and the host's existing
recovery re-places them, reloading at their last URL. The notice now says the
host is offline (and that the page reloads when it reconnects), names another
desktop only when the placement belongs to one, and otherwise says the page is
not available here right now.
…, and re-host from the notice

The unavailable notice said the host was offline for any null status, which
also covers a retired or refused pairing and a host whose runtime did not
answer. It now uses the host connection state and excludes revoked verdicts.

When the control link returns while the old browser host is still inside its
lease reconnect, the contact-edge ensure is a no-op; if that reconnect then
fails, nothing re-hosts. The notice now re-claims hosting on mount while the
host is reachable, which is exactly that stranded state.

Plain regained contact no longer queues a second preparation behind one
already in flight; only a runtime restart does.
…tact

A preparation still in flight when contact returns started while the host
was unreachable, so it is the one most likely to fail. Coalescing the
contact edge into it dropped the only trigger that could succeed and left
the page unavailable. Always queue one follow-up preparation instead.
@Jinwoo-H
Jinwoo-H force-pushed the client-hosted-browser-rehost branch from 2d0b1c2 to b2e29d1 Compare October 6, 2026 19:37
@Jinwoo-H
Jinwoo-H merged commit 2f001fe into main Oct 6, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant