fix(mobile): background-mount never-mounted terminals on subscribe so they don't render blank (STA-1840)#8811
Conversation
… they don't render blank (STA-1840) A mobile terminal.subscribe to a tab the desktop never mounted this session — a workspace the desktop isn't currently showing, a cold-activation-deferred tab (#8597), or a cold-parked tab — has no attached PTY, so the runtime has no headless emulator and the initial snapshot is empty. The mobile terminal then renders blank (repro: on the phone, scroll to an old workspace whose terminals the desktop unmounted; they stay blank). When a mobile subscribe hits an empty snapshot (or resolves no PTY), the runtime now asks the renderer to background-mount that tab so the PTY attaches and the live data stream — already subscribed on that path — delivers its output. A never-mounted workspace has no renderer-graph leaf, so its terminals are surfaced to mobile via synthetic pty:<ptyId> handles that carry no real UI tabId. The runtime passes the ptyId through the mount request and the renderer resolves the owning tab from the persisted tab model (resolveTerminalTabIdForPtyId), falling back to a whole-worktree background mount if it cannot. The request is idempotent for already-mounted tabs and never unmounts a live pane, so the worst case is the same blank as before — no regression. Validated on an iOS simulator with a two-workspace desktop: opening the workspace the desktop was not showing rendered a fully blank terminal without the fix and the live shell prompt with it. Tests: runtime mount-request gating (real-tab / pty-form / no-ptyId / unknown), subscribe-handler empty-snapshot-vs-content, and the ptyId->tabId resolver; all 608 RPC method tests pass.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds runtime-to-renderer terminal tab mount requests through a new IPC event. Mobile terminal subscriptions invoke the request when no PTY leaf or initial terminal content is available. The preload and web APIs expose the subscription, while the renderer resolves tabs from direct or split-layout PTY ownership and triggers background mounting. Tests cover request payloads, failure handling, empty snapshots, existing content, PTY-to-tab resolution, and updated IPC mocks. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
…le-terminal-blank
…le-terminal-blank
Problem (STA-1840 / regression #8597)
Mobile terminals can render blank—or show only a stale retained snapshot—when the phone opens a workspace whose terminal pane the desktop never mounted this session. The desktop does not follow the phone into that workspace, so nothing attaches the live PTY stream.
This affects never-visited workspaces, cold-activation-deferred tabs from #8597, and other panes with a persisted terminal identity but no renderer attachment.
ELI5
Orca remembers your terminal tabs, but it normally reconnects a terminal only when the desktop displays it. If you open a different workspace from your phone before the desktop has visited it, the phone can ask for a remembered terminal whose connection is still “asleep.” That is why the terminal can look blank or frozen even though the shell still exists.
This change lets the phone wake up exactly that one terminal in the background. It does not switch the desktop to another workspace or start every saved terminal.
For users, the difference is simple:
Root cause
A mobile
terminal.subscribecan resolve a persisted PTY through a syntheticpty:<ptyId>handle even though the renderer graph has no leaf for it. On currentmain, provider-owned snapshot fallback (#8768) may still return retained output, but without a headless runtime model the PTY is not live-attached; the phone can therefore remain blank or frozen on stale output.Fix
When a mobile subscribe has no connected leaf, or resolves a PTY with no headless runtime model, the runtime asks the renderer to background-mount the owning terminal tab. The subscribe installs its live-data listener before this request, so attach output is buffered and delivered without navigating the desktop.
Synthetic handles carry the
ptyId; the renderer resolves it through the persisted tab model (tab.ptyIdor a split layout binding). Mount planning is deliberately bounded:ptyIdmounts nothing;That last rule preserves #8597's activation performance: a bad mapping cannot instantiate every saved xterm/PTY pane in a large hidden workspace.
User validation
Validated on an iOS simulator with two desktop workspaces:
brennanbenson ~/orca/demo-project [main] $.Also validated through a disposable Linux SSH target using the real encrypted mobile WebSocket runtime:
Reliability contract
terminal-session.mobile-live-attachment): a mobile subscription to a persisted terminal either attaches that terminal's live PTY without desktop navigation or safely remains on its existing fallback; it never mounts unrelated tabs.terminal-runtime.mobile-stream-budgetgate passes, plus the PR-specific focused tests below. Accepted gap: there is not yet a dedicated manifest entry or automated real-daemon iOS end-to-end gate for this exact navigation path.ptyId, recovery remains a safe no-op rather than risking a workspace-wide mount.Validation
pnpm run typecheck— passed on merged currentmain.pnpm run lint— passed, including reliability manifest and max-lines ratchet.terminal-runtime.mobile-stream-budgetcommand — 3 files, 40 tests passed.window.localStorage, 2 inherited Git credential env duplication). CI uses the repository-required Node 24 runtime and is the authoritative full-suite gate.Refs: STA-1840, #8597, #8768