Repository navigation
Renderer and runtime disagree on which partition owns SSH worktree session state #12723
Description
Summary
The renderer and the main-process runtime use two different worktree→session-partition owner maps for SSH-owned worktrees:
- Renderer persistence:
buildHostIdByWorktreeId(src/renderer/src/lib/workspace-session-host-persistence.ts) maps SSH worktrees to the local partition. The function's doc comment states the invariant explicitly: "SSH-owned worktrees stay in the 'local' partition ... partitioning them too would double-own that data." The field-ownership table (workspace-session-host-field-ownership.ts) repeats it: "SSH remains local-owned." - Main-process runtime:
tryGetWorkspaceSessionHostIdForWorktree(src/main/runtime/orca-runtime.ts) maps the same worktrees tossh:<targetId>viagetRepoExecutionHostId/toSshExecutionHostId, and runtime flows (pane retirement, windowless PTY handoff, orchestration fences) read-modify-write that partition. fix(runtime): retire an exited pane's surface in its owning host partition (STA-3463) #12671 deliberately routed more retirement writes there, calling it the pane's "owning host partition".
Both maps are load-bearing today, so the same worktree's session state is double-owned: the renderer's interactive writes land in the local partition while runtime writes land in the ssh: partition, and the two copies diverge into independent forks. #12671's own commit message documents one consequence (a resurrected leaf hydrated back from a stale SSH partition); #12721 documents the worst one (tab lists round-tripping as deletions through the remote workspace snapshot).
Why this deserves its own decision
The read-side adoption fix for #12721 makes the divergence recoverable instead of destructive, but it does not remove the fork: runtime writes keep landing in a partition the renderer never writes, and every new runtime flow that touches session state has to choose a side. The two documented invariants directly contradict each other, so this is an architectural decision, not a bug fix:
- Option A — local owns SSH worktrees (renderer's documented model): runtime flows coerce
ssh-kind host ids to the local partition for session persistence, and thessh:partition remains at most a runtime-facing projection that ordinary hydration ignores. Requires revisiting fix(runtime): retire an exited pane's surface in its owning host partition (STA-3463) #12671's routing. - Option B —
ssh:partitions own SSH worktrees (runtime's model, fix(runtime): retire an exited pane's surface in its owning host partition (STA-3463) #12671's direction): renderer split/merge, boot hydration, quit snapshots, and the remote-workspace export all move to thessh:partition for those worktrees, with a one-time migration of existing users' local-partition SSH state.
Either way, the end state should be a single shared owner-map function used by renderer persistence, runtime persistence, and the partition sweeps (workspaceSessionPartitionIdsForHost), so a worktree's session state has exactly one durable home.
References
- SSH host session partitions are written but never read — tab lists round-trip as empty and remote sync permanently deletes them #12721 (data-loss consequence and read-side mitigation)
- fix(runtime): retire an exited pane's surface in its owning host partition (STA-3463) #12671 (runtime-side routing into
ssh:partitions, and the resurrection pathology it measured)
Activity
AmethystLiang commentedon Aug 5, 2026
Looking into it
fix(ssh): a reattach may bind a pane but must never create one (stabl…
nwparker commentedon Sep 8, 2026
Partially resolved by #19572. Recording exactly which half, so the remainder is not lost.
Closed there: repo-backed worktrees.
workspaceSessionPartitionHostIdno longer takes thesshOwnershipargument that made one function give the renderer and the runtime different answers for the same workspace. Both sides now resolve an SSH worktree tossh:<targetId>, boot hydration and theremoteWorkspace:setForConnectedTargetsexport fallback both read that partition, and rows a shipping build left inlocalare folded back in once by a gap-filling read. The data-loss consequence this issue points at (#12721) is fixed and regression-tested.Still open here: folder workspaces.
getFolderWorkspacePartitionHostIddeliberately still answerslocalfor an SSH-owned folder workspace, whileRuntimeWorkspaceSessionController.getPreferredHostIdcontinues to route the same workspace tossh:<targetId>. So the two owner maps still disagree for this one workspace kind.The reason it was not converged with the worktree half: boot hydration discovers which SSH partitions to read from the repo catalog (
listKnownSshHostIds). A folder workspace can be the only thing an SSH target owns, and in that case routing its writes tossh:<targetId>would put them behind a partition no reader enumerates — trading this bug for a worse one. Converging it needs a partition census the renderer can consult at boot (main already has the answer inStore.getWorkspaceSessionHostIds(); it is simply not exposed), rather than inferring the host set from repos.Two things reduce the impact in the meantime, and neither closes it:
ssh:partition whenever some repo also names that host, because the adoption is keyed by workspace id and does not care which kind it is.The residual defect is therefore: an SSH target that owns no repo, with a folder workspace whose session the runtime has written to
ssh:<targetId>. That workspace's runtime-authored rows stay unread, exactly as today.fix(session): give an SSH folder workspace one owning partition boot …
fix(session): give an SSH workspace one owning partition so its tabs …
fix(session): give an SSH workspace one owning partition so its tabs …
fix(GAP-03): stop reconnect from re-adopting server-closed tabs
fix(GAP-03): resolve multi-agent review findings across write-queue, …
test(e2e): #12723 SSH folder scenario, CI lane for the SSH oracle spe…
test(e2e): layout oracle scenarios with window, headless serve and SSH (
OrcaWin commentedon Oct 9, 2026
Resolved by #19572. The renderer and the runtime now agree that an SSH workspace, repo-backed or folder, owns a single ssh: partition, and the SSH layout e2e test from #26674 checks that it stays one partition across a relaunch. If you find a path that still writes to two partitions, please reopen with the details.
fix(GAP-03): stop reconnect from re-adopting server-closed tabs
fix(GAP-03): resolve multi-agent review findings across write-queue, …
fix(GAP-03): default carriesParkedShadowRows to false
fix(ssh): a reattach may bind a pane but must never create one (stabl…