Skip to content

Fix paired remote HTML browser ownership and focus - #13876

Merged
Jinwoo-H merged 16 commits into
mainfrom
remote-server-html-files
Aug 12, 2026
Merged

Jinwoo-H merged 16 commits into
mainfrom
remote-server-html-files

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Route paired HTML browser creation to the runtime that owns the worktree, preventing a competing local browser identity.
  • Preserve the requesting client's right-side preview placement while mirroring the host-authoritative browser page.
  • Open the HTML eye-button preview in the background: the editor remains active until the user explicitly clicks the browser.
  • Keep ordinary New Browser Tab creation focused with its existing bounded focus guard.

Issue

https://linear.app/stably/issue/STA-3872/unable-to-open-html-files-in-remote-server

Reproduction and authority

Invariant: one paired-client HTML preview creates one browser page on the worktree-owning runtime and one client mirror. It remains in the requested split without taking editor focus; an explicit browser click activates it on client and host; closing it preserves the editor and terminal.

Observed failure: the split appeared briefly and collapsed, two blank browser tabs could appear, and clicking them could return to the Claude/terminal tab.

Authority: the worktree-owning runtime owns browser creation and page identity. The requesting client owns only its local split placement and focus choice.

Current byte-identical Electron oracle: tests/e2e/paired-remote-html-browser-focus.spec.ts (SHA-256 2ec1dcf101f922c86a9e1e20c5a0cb785e478fd2c1fe0c6c9d89b43dd4048dd1).

  • Latest main 346e59c879: red in 11.8s; two client HTML browser workspaces appeared.
  • Candidate: green; one host page, one client workspace/tab, stable split, rendered DOM, editor retained focus after creation, explicit click focused the browser, and browser-only close converged.
  • Candidate with only focusOnCreate: false disabled: red in 12.3s; preview creation activated the browser instead of retaining editor focus.

The earlier byte-identical oracle (5806b23f…) separately proved the owning-runtime seam: affected main was red, the candidate was green, and disabling only owning-runtime routing was red with duplicate client workspaces.

Root cause and design

The paired client previously opened/staged browser state locally even though the remote runtime owned the worktree and browser page. Host publication then introduced a second identity and host group placement, allowing reconciliation to duplicate, collapse, or refocus the client surface.

The fix follows ownership:

  1. Paired HTML opens execute browser.tabCreate on the worktree owner.
  2. A bounded client placement record maps that host page into the viewer's requested split.
  3. Side previews pass focusOnCreate: false, so the host creates them inactive and the client records no focus intent or temporary listener.
  4. Exact-snapshot replay handles publication arriving before the create response reveals the canonical page ID.
  5. An explicit browser click uses the existing activation path and becomes authoritative on client and host.

Ordinary New Browser Tab actions retain their existing focused-create behavior and bounded listener. No new wire field, opcode, RPC method, retry protocol, deterministic page ID, receipt ledger, TTL, timer, or polling loop is introduced.

Alternatives considered

  • Keep provisional local creation and deduplicate after host adoption: rejected because two authorities still exist during the race.
  • Auto-focus side previews and guard every intervening navigation: rejected for this path because background creation is simpler and matches the approved UX.
  • Add durable create receipts/retries or deterministic requested page IDs: rejected as broader protocol machinery with ambiguous-success and collision risks.
  • Remove focus from every browser creation: rejected because normal New Browser Tab should remain focused; the option is scoped to the eye-button preview.

Risks and compatibility

  • Behavior change: paired remote eye-button previews no longer auto-focus. Users must click the preview to interact with it; this is intentional.
  • Local HTML previews retain their existing auto-focus behavior; this PR scopes the UX change to the reported paired-remote path.
  • Placement state is capped at 128 and cleared on adoption, failure, worktree teardown, or environment teardown.
  • No OS path/shell behavior or wire shape changes. macOS, Linux, Windows, folder workspaces, Git worktrees, SSH, WSL, and mixed client/host versions keep their existing protocol surface.
  • Live paired validation covers macOS. Linux, physical Windows, and rendererless orca serve remain explicit topology gaps; no Windows-specific primitive is causal here.

Validation

Final merge base: origin/main@346e59c879ad

  • Focused host/renderer suite: 183/183 passed
  • Final paired Electron/web-client oracle: 1/1 passed (7.1s product time)
  • pnpm typecheck: passed
  • pnpm run check:code-quality:changed: passed with zero findings
  • pnpm run check:max-lines-ratchet: passed
  • pnpm run check:reliability-gates: passed (75 gates)
  • git diff --check: passed
  • Internal correctness, reproduce/readiness, architecture, performance/resource, platform, security, and compatibility reviews: clean after one bounded test/evidence fix round

Visual proof

N/A. The paired Electron/CDP oracle validates rendered HTML, split visibility, client/host state, focus transitions, identity cardinality, and close convergence; no screenshot is used as the sole oracle.

Do not merge automatically; this PR is ready for review.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Runtime-owned file previews now open through the owning web runtime and preserve split placement metadata. Browser creation uses provisional IDs, placement reservations, guarded focus intents, host-ID reconciliation, snapshot refresh, and failure cleanup. Session synchronization preserves client-selected browser groups and rejects stale focus requests. Tests cover races, placement limits, cleanup, routing, and paired remote HTML browser focus and lifecycle behavior. A reliability gate records validation commands, evidence, budgets, promotion criteria, and known gaps.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. 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 and concisely describes the primary change: fixing ownership and focus for paired remote HTML browsers.
Description check ✅ Passed The description explains the problem, solution, risks, issue, visual proof, testing, compatibility, and validation results in sufficient detail.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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.

@Jinwoo-H
Jinwoo-H force-pushed the remote-server-html-files branch from 4e2550d to 4c9c025 Compare August 11, 2026 23:00
@Jinwoo-H Jinwoo-H changed the title Fix paired remote HTML tab focus ordering Fix paired remote HTML browser ownership and focus Aug 11, 2026
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

🧹 Nitpick comments (3)
src/renderer/src/runtime/web-session-intent-owner.test.ts (1)

100-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Seed executed as empty and push from inside the first operation.

Line 100 pre-seeds executed with 'first', but the first operation body does not push. The final assertion at Line 135 then confirms 'first' even if the first operation never runs. Push from inside the operation so the assertion proves execution.

♻️ Proposed change
-    const executed = ['first']
+    const executed: string[] = []
     const first = runSerializedWebSessionFocusOperation(OWNER_A, WORKTREE_ID, async () => {
       await firstBarrier
+      executed.push('first')
       return 'first'
     })
src/renderer/src/runtime/web-session-focus-intent.ts (1)

47-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Return void from recordWebSessionFocusIntent. No caller consumes its returned token, and the token always fails isWebSessionFocusIntentTokenValid and isWebSessionFocusIntentTokenCurrent.

tests/e2e/paired-remote-html-browser-focus.spec.ts (1)

70-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated session.tabs.list call into a shared helper.

The same session.tabs.list RPC-call shape (selector, method: 'session.tabs.list', params: { worktree: id:${worktreeId} }, timeoutMs: 15_000) is built inline inside page.evaluate() five separate times in this file. Extract a shared function passed to page.evaluate() so the request shape has one source of truth.

♻️ Proposed helper extraction
+async function listSessionTabs(
+  page: Page,
+  environmentId: string,
+  worktreeId: string
+): Promise<Awaited<ReturnType<typeof window.api.runtimeEnvironments.call>>> {
+  return page.evaluate(
+    async ({ environmentId, worktreeId }) => {
+      return window.api.runtimeEnvironments.call({
+        selector: environmentId,
+        method: 'session.tabs.list',
+        params: { worktree: `id:${worktreeId}` },
+        timeoutMs: 15_000
+      })
+    },
+    { environmentId, worktreeId }
+  )
+}

Then replace each inline construction, e.g.:

-              const response = await window.api.runtimeEnvironments.call({
-                selector: environmentId,
-                method: 'session.tabs.list',
-                params: { worktree: `id:${worktreeId}` },
-                timeoutMs: 15_000
-              })
+              const response = await listSessionTabs(page, environmentId, worktreeId)

Also applies to: 121-129, 187-194, 209-215, 251-262


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d5bd2e2-11fa-483d-817d-b34159f6d5cc

📥 Commits

Reviewing files that changed from the base of the PR and between 648d173 and 4c9c025.

📒 Files selected for processing (10)
  • config/reliability-gates.jsonc
  • src/renderer/src/lib/file-preview.test.ts
  • src/renderer/src/lib/file-preview.ts
  • src/renderer/src/runtime/web-runtime-session.test.ts
  • src/renderer/src/runtime/web-runtime-session.ts
  • src/renderer/src/runtime/web-session-focus-intent.ts
  • src/renderer/src/runtime/web-session-intent-owner.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync.ts
  • tests/e2e/paired-remote-html-browser-focus.spec.ts

Comment thread config/reliability-gates.jsonc Resolved
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config/reliability-gates.jsonc (1)

3249-3249: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the STA-3872 known gap. The new electron-headless run is a hidden-window Electron test, not rendererless orca serve, but Playwright still drives the real HTML DOM through CDP. It is therefore a second HTML UI journey. Distinguish hidden-window parity from the remaining rendererless headless-serve gap.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0db9159b-d124-45df-88b2-7e9172c512de

📥 Commits

Reviewing files that changed from the base of the PR and between 4c9c025 and 0be3801.

📒 Files selected for processing (4)
  • config/reliability-gates.jsonc
  • src/renderer/src/runtime/web-runtime-session.test.ts
  • src/renderer/src/runtime/web-runtime-session.ts
  • src/renderer/src/runtime/web-session-intent-owner.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/src/runtime/web-session-intent-owner.test.ts
  • src/renderer/src/runtime/web-runtime-session.ts

@Jinwoo-H
Jinwoo-H force-pushed the remote-server-html-files branch 2 times, most recently from 25188e1 to 33480dd Compare August 12, 2026 01:14
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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)
src/renderer/src/runtime/web-runtime-session.test.ts (1)

1845-1860: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the duplicated store fixture into a local factory.

Both race tests repeat the same mocks.getState.mockReturnValue({...}) block, the same createBrowserTab return value, and the same applyFreshWebSessionTabsSnapshot pass-through. A single helper keeps the two tests aligned when the store shape changes.

♻️ Proposed helper
function stubStoreForBrowserFocusRace(): void {
  mocks.getState.mockReturnValue({
    settings: { activeRuntimeEnvironmentId: ENVIRONMENT_ID },
    activeWorktreeId: WORKTREE_ID,
    browserPagesByWorkspace: {},
    remoteBrowserPageHandlesByPageId: {},
    createBrowserTab: mocks.createBrowserTab,
    setRemoteBrowserPageHandle: mocks.setRemoteBrowserPageHandle,
    focusBrowserTabInWorktree: mocks.focusBrowserTabInWorktree,
    setActiveWorktree: mocks.setActiveWorktree
  })
  mocks.createBrowserTab.mockReturnValue({
    id: 'local-browser-workspace-1',
    activePageId: 'local-page-1',
    pageIds: ['local-page-1']
  })
  mocks.applyFreshWebSessionTabsSnapshot.mockImplementation((state) => state)
}

Also applies to: 1922-1937


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15983f54-5736-447c-977d-f36b2b99c34e

📥 Commits

Reviewing files that changed from the base of the PR and between 25188e10d267d057401b415ec40b84e8e4dcbaf9 and 33480ddc186bdb7dcd7a1c21d1017acfd24ddc31.

📒 Files selected for processing (4)
  • src/renderer/src/runtime/web-runtime-session.test.ts
  • src/renderer/src/runtime/web-runtime-session.ts
  • src/renderer/src/runtime/web-session-tabs-sync.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/renderer/src/runtime/web-session-tabs-sync.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync.ts
  • src/renderer/src/runtime/web-runtime-session.ts

@Jinwoo-H
Jinwoo-H force-pushed the remote-server-html-files branch from 33480dd to 480cc0d Compare August 12, 2026 01:21
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Jinwoo-H
Jinwoo-H force-pushed the remote-server-html-files branch from 480cc0d to d24c605 Compare August 12, 2026 08:12
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Jinwoo-H
Jinwoo-H force-pushed the remote-server-html-files branch from d24c605 to 3df3d10 Compare August 12, 2026 08:42
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/runtime/web-runtime-session.ts (1)

962-1007: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reconcile a successful activation in the caller.

Line 972 sets notifyClients: false. Lines 991-1007 discard the activation result and do not refresh session.tabs. The host can activate the tab while the requesting client remains on its prior tab until an unrelated publication arrives.

After successful activation, apply a validated acknowledgment snapshot or call refreshWebRuntimeSessionTabsSnapshot(). Keep the focus intent until that reconciliation completes.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f2e7e42-1427-4c3b-bcba-f6712d5261d0

📥 Commits

Reviewing files that changed from the base of the PR and between d24c60519a1c5761c8e480a42edd6299613a8fb0 and 3df3d10.

📒 Files selected for processing (6)
  • config/reliability-gates.jsonc
  • src/renderer/src/lib/file-preview.test.ts
  • src/renderer/src/lib/file-preview.ts
  • src/renderer/src/runtime/web-runtime-session.test.ts
  • src/renderer/src/runtime/web-runtime-session.ts
  • tests/e2e/paired-remote-html-browser-focus.spec.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/renderer/src/lib/file-preview.ts
  • src/renderer/src/lib/file-preview.test.ts
  • tests/e2e/paired-remote-html-browser-focus.spec.ts
  • src/renderer/src/runtime/web-runtime-session.test.ts

Comment thread config/reliability-gates.jsonc Resolved
@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

Latest-head review follow-up: the outside-diff activation concern is not reproducible. Every production activateWebRuntimeSessionTab caller synchronously updates the requesting client’s local tab/group/type; notifyClients:false intentionally prevents steering other clients. The paired Electron oracle explicitly proves an HTML browser click activates both client and host. Adding a redundant list/reconcile after activation would add remote work and could replay stale focus. The two inline threads are addressed/resolved; production code is unchanged.

@Jinwoo-H
Jinwoo-H merged commit 70ad65f into main Aug 12, 2026
47 checks passed
AmethystLiang pushed a commit that referenced this pull request Aug 12, 2026
* Fix paired remote HTML browser preview

* Register paired HTML preview reliability gate

* Scope browser worktree selection guard

* Harden paired HTML preview ordering

* Simplify paired HTML preview ownership

* Record final paired HTML preview evidence

* fix(remote): simplify browser preview ownership

* test(remote): refresh HTML preview evidence

* fix(remote): reconcile browser create ordering

* fix(remote): guard browser focus through reconcile

* fix(remote): bound delayed browser focus

* fix(remote): guard browser focus context

* test(remote): record final HTML preview evidence

* fix(remote): keep HTML previews in background

* test(remote): cover browser focus modes

* test(remote): record focused browser contracts
AmethystLiang pushed a commit that referenced this pull request Aug 13, 2026
* Fix paired remote HTML browser preview

* Register paired HTML preview reliability gate

* Scope browser worktree selection guard

* Harden paired HTML preview ordering

* Simplify paired HTML preview ownership

* Record final paired HTML preview evidence

* fix(remote): simplify browser preview ownership

* test(remote): refresh HTML preview evidence

* fix(remote): reconcile browser create ordering

* fix(remote): guard browser focus through reconcile

* fix(remote): bound delayed browser focus

* fix(remote): guard browser focus context

* test(remote): record final HTML preview evidence

* fix(remote): keep HTML previews in background

* test(remote): cover browser focus modes

* test(remote): record focused browser contracts

(cherry picked from commit 70ad65f)
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
* Fix paired remote HTML browser preview

* Register paired HTML preview reliability gate

* Scope browser worktree selection guard

* Harden paired HTML preview ordering

* Simplify paired HTML preview ownership

* Record final paired HTML preview evidence

* fix(remote): simplify browser preview ownership

* test(remote): refresh HTML preview evidence

* fix(remote): reconcile browser create ordering

* fix(remote): guard browser focus through reconcile

* fix(remote): bound delayed browser focus

* fix(remote): guard browser focus context

* test(remote): record final HTML preview evidence

* fix(remote): keep HTML previews in background

* test(remote): cover browser focus modes

* test(remote): record focused browser contracts
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