Skip to content

fix(remote-runtime): wait for the desktop's terminal list before opening a terminal - #28154

Merged
Jinwoo-H merged 3 commits into
mainfrom
fix/web-client-unknown-terminal-count
Oct 13, 2026
Merged

Jinwoo-H merged 3 commits into
mainfrom
fix/web-client-unknown-terminal-count

Conversation

@Jinwoo-H

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

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​230 $\color{#cf222e}{\Huge{\mathbf{−}}}$​10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​220
Prod 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​83 $\color{#cf222e}{\Huge{\mathbf{−}}}$​12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​71

ELI5

When a browser, or another desktop, paired with a desktop opened a workspace, it sometimes asked the desktop for a new terminal before the desktop had told it which terminals already existed. "I haven't heard yet" was read as "there are none", so the workspace could get an extra terminal, or two. Now the client waits for the desktop's list and decides from that.

What Changed

Before: opening (or switching to) a workspace that the desktop owns, right after the client connected or reconnected, could add a terminal the user did not ask for. If the desktop already had terminals, the workspace gained one more. If it had none, two were created: one by the "open workspace" step and one by the step that seeds an empty workspace when the desktop's first list arrives.

After: the workspace shows exactly the desktop's terminals. If the desktop answers that the workspace is empty, exactly one terminal is opened, as before.

Mechanism:

  • The client's remembered "how many terminals does the desktop have here" now returns unknown until the desktop has sent a list for that workspace, instead of 0 (tracking-lifecycle.ts).
  • When opening a workspace without a specific command to run, an unknown count makes the client wait. The existing seeding step that runs on the desktop's first list for the active workspace (active-session-subscription.ts) creates the terminal if the workspace really is empty. Opens that carry a startup command keep creating directly, because the seeding step can only open a plain terminal and would drop the command.
  • Older desktops: desktops from before fix(remote-runtime): a paired terminal accepts input again after its host app relaunches #25736 (v1.4.223) answer an empty workspace with "ask me later" and never follow it with a real answer, so the client cannot wait for them. A desktop now advertises session-tabs.empty-worktree-answer.v1 (new module session-tabs-runtime-capabilities.ts, through the existing runtime capability list) to say it does send that answer. The client waits only for desktops that advertise it. For any other desktop, or if the check fails, an unknown count is treated as 0, exactly as on main. Desktops from 1.4.223 up to this release do not advertise it, so they also keep main's behaviour. Delete the check once every supported desktop advertises the capability.
  • A workspace this client just created is known to be empty, so the create flow passes unknownHostCountIsEmpty and opens its first terminal right away, as on main. That includes a blank workspace created in the background. Waiting there would leave the desktop window and the phone with no terminal, which is a visible change.
  • A desktop that has just started replies "ask me later" before it has published its tabs. That reply no longer counts as "0 terminals" (tracking.ts), and the desktop's later real answer, which arrives as an update rather than a first snapshot, is accepted as the first answer for seeding (tracking-decisions.ts). Without this, a workspace opened while the desktop was still starting would end up with no terminal.

Why

This rule was added in #4825 (fix for a workspace-switching loop), and #21630 narrowed the field-reported "a terminal every time, sometimes two" duplicate, but left the gap between connecting and the desktop's first list. The client was guessing from a partial copy; the desktop owns the answer. Two alternatives were set aside:

  • A new "make sure a terminal exists" request to the desktop would let the desktop decide, but older desktops do not have it, so it would need version negotiation.
  • A timer that gives up waiting and creates anyway would bring the guess back.
    The existing first-list seeding step already makes this decision from the desktop's real answer, so this change routes the unknown case to it. The only wire addition is one capability string a desktop advertises, which older clients ignore. Desktops without it keep main's behaviour.

Linked Issue

N/A (internal legacy-rules audit, rule 10).

Visual Proof

N/A: the change removes a duplicate terminal that only appears in a race between connecting and the desktop's first tab list. The unit tests below drive that exact ordering.

Testing

  • New tests in src/renderer/src/runtime/web-session-tabs-sync-initial-terminal-relaunch.test.tsx drive the real tab-sync subscription with the "open workspace" step called before the desktop's first list:

    • desktop already has a terminal: no terminal is created (main: 1 created);
    • desktop answers empty: exactly one terminal is created (main: 2 created);
    • "ask me later" followed by a terminal list: none created (main: 1 created);
    • "ask me later" followed by an empty answer: exactly one created (passes on main too; guards the case the change could otherwise break).
    • an older desktop whose "ask me later" is never followed by an answer: exactly one created (fails on this PR's first version, passes now);
    • a workspace this client just created: one created right away, with no capability check. worktree-creation-agent-seeding.test.ts checks that the background blank create passes the flag.
  • Existing activation tests that model "the desktop already said the workspace is empty" now record that answer explicitly.

  • pnpm tc, oxlint, pnpm run check:code-quality:changed, and the full renderer unit suite pass (one unrelated terminal-snapshot file failed under load and passes alone).

  • e2e: full suite and the workspace-layout oracle specs dispatched on this head.

  • I manually tested these changes locally

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

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • SSH/remote: this is the paired-client path. The only host change is one advertised capability string. Mixed versions: an old client with a new desktop is unchanged, and a new client with an older desktop gets main's behaviour.
  • Known edges, not regressions:
    • If the capability check fails on a new desktop, the client falls back to main's behaviour, including the possible duplicate this PR otherwise removes.
    • The "first real answer" flag changes only on updates the client applies. If that first answer is dropped as stale, a later empty update can still open a terminal. That is correct when the workspace really is empty.
    • The fallback create keeps the caller's activate setting. It does not change which workspace is selected.
  • Folder workspaces use the same path and are covered by the updated folder activation test.
  • Cross-platform: no platform-specific code.

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)

… a terminal

A paired client opening a workspace whose terminals it had not heard about
yet read "no answer from the host" as "zero terminals" and asked the host for
one. When the host's list then arrived with terminals, the workspace gained an
extra terminal; when it arrived empty, the first-frame bootstrap created a
second one.

The client now treats an unknown host count as unknown. Activation without
explicit startup work waits, and the active subscription's first real answer
seeds the workspace if it is empty. An "ask me later" frame from a host that
has not published yet no longer records a count of zero, and the host's later
answer (sent as an update) is accepted as the first answer for seeding.
@coderabbitai

coderabbitai Bot commented Oct 13, 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: 8882325a-e5fd-4401-9cd5-95e34de21a25
📥 Commits

Reviewing files that changed from the base of the PR and between 15cf402 and 672bfd8.

📒 Files selected for processing (7)
  • src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts
  • src/renderer/src/lib/worktree-activation-empty-remote.test.ts
  • src/renderer/src/lib/worktree-creation-agent-seeding.test.ts
  • src/renderer/src/lib/worktree-creation-flow-execute.ts
  • src/renderer/src/runtime/web-session-tabs-sync-initial-terminal-relaunch.test.tsx
  • src/shared/protocol-version.ts
  • src/shared/session-tabs-runtime-capabilities.ts

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

📝 Walkthrough

The changes distinguish an unknown host terminal count from a confirmed count of zero. Initial-terminal bootstrap decisions now use whether the host has provided an answer. Wake-terminal recovery checks the count and startup payload before returning. Runtime capability identifiers now include empty-worktree answers. Tests cover host snapshots, bootstrap decisions, wake recovery, and creation seeding.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 672bf

The desktop’s delayed terminal answer is handled without immediately opening an extra terminal. No actionable merge-blocking issue remains after normal checks.

Pre-merge checks · Passed 4 · Failed 1

Failed Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 15 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: waiting for the desktop terminal list before creating a terminal.
Description check Passed The description is detailed and covers the user impact, implementation, compatibility behavior, alternatives, testing, and scope considerations. The Linked Issue section says N/A despite the template …
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · 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.

Jinwoo-H and others added 2 commits October 13, 2026 03:23
… later

Hosts before #25736 answer an empty workspace with "ask me later" and
never follow it with a real answer, so waiting for that answer left a
blank workspace with no terminal against them. Wait only on hosts that
advertise session-tabs.empty-worktree-answer.v1; older hosts keep
main's behaviour. Workspaces this client just created are known empty
and open their first terminal without waiting.

Co-Authored-By: Claude <noreply@anthropic.com>
…t flag

Co-Authored-By: Claude <noreply@anthropic.com>
@Jinwoo-H
Jinwoo-H merged commit b8e4922 into main Oct 13, 2026
46 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