fix(ssh): make the partition hoist consume its source

A migration that leaves its source on disk runs again every launch, and its
hoist condition is 'local has no such tab' — which is exactly the state after
the user closes one. Closed panes came back on the next launch.

Deleting the partition once hoisted also completes the consolidation: local is
now the single plane for SSH pane membership, which is what the reattach binder
and the lease arbitration were already assuming.

Mutation-proofed: leaving the source behind reddens the new no-resurrection
test and nothing else.
This commit is contained in:
Neil
2026-08-13 19:38:42 -07:00
parent a8e8c13a73
commit 32fa11829a
2 changed files with 33 additions and 0 deletions
+9
View File
@@ -2913,6 +2913,15 @@ export function hoistSshPartitionsIntoLocalSession(
local.activeTabIdByWorktree = { ...local.activeTabIdByWorktree, [worktreeId]: tabId }
changed = true
}
// Consume the source. A migration that leaves it behind runs again every launch and
// resurrects panes the user has since closed, because "local has no such tab" is exactly
// what this function treats as "needs hoisting".
if (state.workspaceSessionsByHostId?.[hostId]) {
const remaining = { ...state.workspaceSessionsByHostId }
delete remaining[hostId]
state.workspaceSessionsByHostId = remaining
changed = true
}
}
return changed
}
@@ -157,4 +157,28 @@ describe('an SSH pane whose membership was persisted into the ssh partition', ()
expect(changed, 'the function reported no change').toBe(true)
expect(state.workspaceSession?.tabsByWorktree?.[WORKTREE] ?? []).toHaveLength(1)
})
// A migration that leaves its source behind runs again on the next launch and re-adds panes the
// user has since closed, because "local has no such tab" is precisely its hoist condition.
it('does not resurrect a tab the user closed after upgrading', async () => {
const before = await createStore()
before.setWorkspaceSession(paneSession() as never, SSH_PARTITION)
before.flushOrThrow()
// The upgrade launch hoists the pane.
const upgraded = await reopenStore()
expect(upgraded.getWorkspaceSession().tabsByWorktree?.[WORKTREE] ?? []).toHaveLength(1)
// The user closes it, and that sticks.
upgraded.setWorkspaceSession(
{ ...upgraded.getWorkspaceSession(), tabsByWorktree: { [WORKTREE]: [] } } as never
)
upgraded.flushOrThrow()
const relaunched = await reopenStore()
expect(
relaunched.getWorkspaceSession().tabsByWorktree?.[WORKTREE] ?? [],
'the migration re-added a tab the user had closed'
).toHaveLength(0)
})
})