Skip to content

Renderer and runtime disagree on which partition owns SSH worktree session state #12723

Closed

Description

@robertnisipeanu

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 to ssh:<targetId> via getRepoExecutionHostId / 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:

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

Activity

  1. self-assigned this
    on Aug 5, 2026
  2. AmethystLiang commented on Aug 5, 2026

    @AmethystLiang
    Contributor

    Looking into it

  3. nwparker commented on Sep 8, 2026

    @nwparker
    Contributor

    Partially resolved by #19572. Recording exactly which half, so the remainder is not lost.

    Closed there: repo-backed worktrees. workspaceSessionPartitionHostId no longer takes the sshOwnership argument that made one function give the renderer and the runtime different answers for the same workspace. Both sides now resolve an SSH worktree to ssh:<targetId>, boot hydration and the remoteWorkspace:setForConnectedTargets export fallback both read that partition, and rows a shipping build left in local are 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. getFolderWorkspacePartitionHostId deliberately still answers local for an SSH-owned folder workspace, while RuntimeWorkspaceSessionController.getPreferredHostId continues to route the same workspace to ssh:<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 to ssh:<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 in Store.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:

    • A stranded folder workspace is adopted back out of an 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 adoption is strictly gap-filling, so a folder workspace whose local row has tabs is never re-merged from the stale partition — the recurring-resurrection shape does not apply to it.

    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.

  4. added 2 commits that reference this issue on Oct 8, 2026
  5. OrcaWin commented on Oct 9, 2026

    @OrcaWin
    Collaborator

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions