From b894061a6fc78e469f3da35dccb1d839533f905a Mon Sep 17 00:00:00 2001 From: Neil Date: Fri, 23 Oct 2026 03:12:07 -0700 Subject: [PATCH] fix(wsl-mover): only the selected projects' session rows can refuse a WSL move The session eligibility check treated host local's whole partition as the move's, as the SSH conversion does. So an agent session or unsaved draft in any other project on this desktop, or in a project an earlier move retained, refused every later WSL move from that distro. Co-Authored-By: Claude --- ...ad-source-workspace-session-eligibility.ts | 9 ++-- .../wsl-move-manifest-export.test.ts | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/main/persistence/migrating-orcad-catalog/orcad-source-workspace-session-eligibility.ts b/src/main/persistence/migrating-orcad-catalog/orcad-source-workspace-session-eligibility.ts index 13a68c8020c..c839cf45aab 100644 --- a/src/main/persistence/migrating-orcad-catalog/orcad-source-workspace-session-eligibility.ts +++ b/src/main/persistence/migrating-orcad-catalog/orcad-source-workspace-session-eligibility.ts @@ -8,6 +8,7 @@ import type { import { orcadMigrationOwnerMatchesScope, orcadMigrationOwnsRepoId, + orcadMigrationScopeOwnsWholeHost, unqualifyOrcadMigrationOwnerKey, type OrcadMigrationSourceScope } from './orcad-source-scope' @@ -29,11 +30,13 @@ export function collectUnsupportedSessionItems( sourceTerminalTabIds: ReadonlySet ): string[] { const owns = (ownerKey: string): boolean => orcadMigrationOwnerMatchesScope(ownerKey, scope) - const matches = (ownerKey: string): boolean => - Boolean(ownerKey) && (sourceHostPartition || owns(ownerKey)) + // A WSL move takes some of host `local`'s projects; the others, and rows a past move retained, + // stay on this desktop untouched, so only the selected projects' rows can refuse it. + const wholeHost = sourceHostPartition && orcadMigrationScopeOwnsWholeHost(scope) + const matches = (ownerKey: string): boolean => Boolean(ownerKey) && (wholeHost || owns(ownerKey)) const items: string[] = [] // Another project's rows in this host's copy stay behind; only an unsaved draft there is work. - if (sourceHostPartition) { + if (wholeHost) { for (const [ownerKey, files] of Object.entries(session.openFilesByWorktree ?? {})) { if (!owns(ownerKey)) { items.push(...unsavedDraftItems(files, ownerKey)) diff --git a/src/main/persistence/moving-wsl-projects/wsl-move-manifest-export.test.ts b/src/main/persistence/moving-wsl-projects/wsl-move-manifest-export.test.ts index 3308a5f5c67..d6490619caa 100644 --- a/src/main/persistence/moving-wsl-projects/wsl-move-manifest-export.test.ts +++ b/src/main/persistence/moving-wsl-projects/wsl-move-manifest-export.test.ts @@ -234,6 +234,58 @@ describe('exportWslMoveManifest', () => { expect(exportWslMoveManifest(storeFor(state), REQUEST).blockers.length).toBeGreaterThan(0) }) + it("is not refused by another project's agent session or unsaved draft", () => { + // On awin, any agent or draft anywhere on this desktop refused every WSL move (D1b-10 run). + const state = sourceState() + const elsewhere: SleepingAgentSessionRecord = { + paneKey: 'windows-shell:missing-leaf', + tabId: 'windows-shell', + worktreeId: OTHER_WORKTREE, + agent: 'codex', + providerSession: { key: 'session_id', id: 'session-2' }, + prompt: 'Saved prompt', + state: 'done', + capturedAt: 1, + updatedAt: 1, + connectionId: null, + origin: 'quit' + } + state.workspaceSession.sleepingAgentSessionsByPaneKey = { [elsewhere.paneKey]: elsewhere } + state.workspaceSession.openFilesByWorktree = { + [OTHER_WORKTREE]: [ + { + filePath: 'C:\\code\\win\\notes.md', + relativePath: 'notes.md', + worktreeId: OTHER_WORKTREE, + language: 'markdown', + dirtyDraftContent: 'unsaved' + } + ] + } + expect(exportWslMoveManifest(storeFor(state), REQUEST).blockers).toEqual([]) + }) + + it("is not refused by a project an earlier move retained, whose agent stays here", () => { + const state = sourceState() + const retainedWorktree = 'old::\\\\wsl.localhost\\Ubuntu\\home\\me\\old' + state.repos.push(repo('old', '\\\\wsl.localhost\\Ubuntu\\home\\me\\old')) + const record: SleepingAgentSessionRecord = { + paneKey: 'old-shell:missing-leaf', + tabId: 'old-shell', + worktreeId: retainedWorktree, + agent: 'claude', + providerSession: { key: 'session_id', id: 'session-3' }, + prompt: 'Saved prompt', + state: 'done', + capturedAt: 1, + updatedAt: 1, + connectionId: null, + origin: 'quit' + } + state.workspaceSession.sleepingAgentSessionsByPaneKey = { [record.paneKey]: record } + expect(exportWslMoveManifest(storeFor(state), REQUEST).blockers).toEqual([]) + }) + it('refuses to sign a scope naming a project that is no longer here', () => { expect(() => exportWslMoveManifest(storeFor(sourceState()), { ...REQUEST, repoIds: ['app', 'gone'] })