Files
orca/src/main/runtime
Damir Vandic e21ddd378c Fix pending remote terminal tab creation (#6353)
* Adopt pending terminal tab reveals

* Materialize pending remote terminal tabs

Wait for mobile terminal creates to produce a ready PTY handle before resolving. If the renderer only publishes a pending tab shell, materialize a headless PTY into the same tab/leaf identity so later renderer focus adopts it instead of duplicating the tab.

Add a pane-key spawn reservation shared by runtime materialization and renderer pty:spawn so focus racing fallback reuses the winning PTY, including when SSH env stripping removes ORCA_PANE_KEY. Cover the pending-surface fallback and both overlapping spawn race directions with regression tests.

* Settle pane spawn reservation on any post-spawn failure

The per-paneKey spawn reservation was only settled on spawn error, persist
error, or final resolve. A throw in the post-spawn region (seedHeadlessTerminal,
registerPty, rememberPaneKeyForPty, track) rejected the IPC promise but left the
reservation in paneSpawnReservationsByPaneKey forever, so every later spawn for
that pane awaited a promise that never settled and hung.

Wrap each post-reservation body (both spawn paths) in a catch-all that rejects
the reservation with the real error and rethrows; reject is a no-op once
resolved. Remove the now-redundant inner reject calls. Add regression tests for
both paths.

* Tear down runtime-owned headless tabs on mobile session close

The renderer-attached branch of closeMobileSessionTab closed the renderer's
pane but never pruned mobileSessionTabsByWorktree or dropped the persisted
binding. For runtime-owned headless terminals — serve/SSH materialized, or a
pending shell the renderer never adopted, all introduced by the pending-surface
materialize fallback — syncMobileSessionTabs then re-hydrates the closed tab
from the persisted session, resurrecting it with a live PTY. "Close others" on
a paired client showed closed remote tabs reappearing with their sessions still
running.

Add isRuntimeOwnedHeadlessMobileTab (serve/SSH-owned, or no live PTY and absent
from the renderer graph). When the whole parent of such a tab is closed, route
through closeHeadlessMobileTerminalTab to kill the PTY, remove the persisted
binding, and prune+emit, then best-effort notify the renderer so no adopted
pane is left dead. Genuinely renderer-owned tabs and exact split-leaf closes
keep the existing path. Cover the serve parent close, renderer-published vs
runtime-unadopted pending tabs, and the serve split-leaf case with regressions.

* Tear down only headless tabs the renderer never published on mobile close

The close-others teardown discriminator returned false for any resolved
non-serve/ssh PTY, so headless tabs carrying a daemon session id
(<worktreeId>@@<shortUuid>) that the host materialized but the renderer never
published leaked into the session snapshot and the client mirrored them
(confirmed live: host published 5 tabs for a worktree the renderer showed 1 for).

That id shape is ALSO minted for ordinary renderer-owned daemon-backed local
terminals, so it can't classify ownership by shape — the renderer graph
(this.tabs) does. Classify a tab as runtime-owned when it is serve/ssh (always,
they're preserved + re-hydrated) or when the renderer graph never published it
(a leaked/unadopted shell); delegate everything the renderer graph lists,
including ordinary @@ local terminals and pending tabs.

Regression tests: renderer-owned @@ tab is delegated (not de-persisted); leaked
@@ tab the renderer never published is torn down + de-persisted.
2026-06-25 14:36:35 -07:00
..