diff --git a/src/renderer/src/hooks/ipc-events/ssh-managed-server-converted-catalogs.test.ts b/src/renderer/src/hooks/ipc-events/ssh-managed-server-converted-catalogs.test.ts index aa0089b9588..86b00c67a67 100644 --- a/src/renderer/src/hooks/ipc-events/ssh-managed-server-converted-catalogs.test.ts +++ b/src/renderer/src/hooks/ipc-events/ssh-managed-server-converted-catalogs.test.ts @@ -176,6 +176,38 @@ describe('a host that just moved to its managed server', () => { expect(store.getState().activeWorktreeId).toBe(worktreeId) }) + it('moves the open workspace once the cold-start scan delivers the server rows', async () => { + store.setState({ + activeRepoId: 'repo-1', + activeWorktreeId: worktreeId, + activeWorkspaceExecutionHostId: 'ssh:ssh-1' + }) + applySshManagedServerTransition('ssh-1', undefined, { + kind: 'managed', + environmentId: 'env-4' + }) + await vi.waitFor(() => expect(store.getState().worktreesByRepo['repo-1']).toEqual([])) + // Startup's own scan has not run yet, so the server's row is not known. + expect(store.getState().activeWorkspaceExecutionHostId).toBe('ssh:ssh-1') + + store.setState({ + worktreesByRepo: { + 'repo-1': [ + makeWorktree({ + id: worktreeId, + repoId: 'repo-1', + path: '/root/repo', + hostId: 'runtime:env-4' + }) + ] + }, + startupWorktreeRefreshCompleted: true + }) + + expect(store.getState().activeWorkspaceExecutionHostId).toBe('runtime:env-4') + expect(store.getState().activeWorktreeId).toBe(worktreeId) + }) + it('leaves a workspace open on another host where it is', async () => { store.setState({ activeRepoId: 'repo-1', diff --git a/src/renderer/src/hooks/ipc-events/ssh-managed-server-state-effects.ts b/src/renderer/src/hooks/ipc-events/ssh-managed-server-state-effects.ts index 545274c4b07..d3f5b5b31bc 100644 --- a/src/renderer/src/hooks/ipc-events/ssh-managed-server-state-effects.ts +++ b/src/renderer/src/hooks/ipc-events/ssh-managed-server-state-effects.ts @@ -156,6 +156,27 @@ async function loadManagedServerCatalogs(targetId: string, load: CatalogLoad): P } useAppStore.setState((state) => withoutConvertedSshHostRows(state, targetId)) rehomeActiveWorkspace(targetId, environmentId) + if (!useAppStore.getState().startupWorktreeRefreshCompleted) { + rehomeAfterStartupScan(targetId, environmentId, isCurrent) + } +} + +/** Why: on cold start the server's worktree rows arrive with startup's scan, after the attempt above. */ +function rehomeAfterStartupScan( + targetId: string, + environmentId: string, + isCurrent: () => boolean +): void { + const unsubscribe = useAppStore.subscribe((state) => { + if (!isCurrent()) { + unsubscribe() + return + } + if (state.startupWorktreeRefreshCompleted) { + unsubscribe() + rehomeActiveWorkspace(targetId, environmentId) + } + }) } /** Why: an active workspace left on the SSH host keeps showing panes that dial the stopped relay. */