diff --git a/src/renderer/src/runtime/sync-runtime-graph-workspace-publication.test.ts b/src/renderer/src/runtime/sync-runtime-graph-workspace-publication.test.ts index 9a5116400c0..53ba6ef999e 100644 --- a/src/renderer/src/runtime/sync-runtime-graph-workspace-publication.test.ts +++ b/src/renderer/src/runtime/sync-runtime-graph-workspace-publication.test.ts @@ -79,6 +79,27 @@ describe('buildMobileSessionTabSnapshots', () => { expect(next.snapshotVersion).toBeGreaterThan(initial.snapshotVersion) }) + it('does not publish an ambiguous worktree id across hosts', () => { + const state = makeState({ + worktreesByRepo: { + 'repo-1': [ + { id: 'wt-duplicate', repoId: 'repo-1', hostId: 'local', instanceId: 'local-instance' }, + { + id: 'wt-duplicate', + repoId: 'repo-1', + hostId: 'ssh:ssh-1', + instanceId: 'ssh-instance' + } + ] + } as unknown as AppState['worktreesByRepo'], + tabsByWorktree: { + 'wt-duplicate': [{ id: 'term-1', title: 'Terminal 1' }] + } as unknown as AppState['tabsByWorktree'] + }) + + expect(buildMobileSessionTabSnapshots(state)).toEqual([]) + }) + it('publishes browser and editor color + pin state from unified tabs', () => { const fileId = '/repo/README.md' const state = makeState({ diff --git a/src/renderer/src/runtime/sync-runtime-graph/mobile-session-snapshots.ts b/src/renderer/src/runtime/sync-runtime-graph/mobile-session-snapshots.ts index 401323a43ca..a4a1d7f8824 100644 --- a/src/renderer/src/runtime/sync-runtime-graph/mobile-session-snapshots.ts +++ b/src/renderer/src/runtime/sync-runtime-graph/mobile-session-snapshots.ts @@ -1,5 +1,6 @@ import { getSystemPrefersDark } from '@/lib/terminal-theme' import type { AppState } from '@/store/types' +import { getIndexedWorktreesById } from '@/store/worktree-repo-index' import type { RuntimeMobileSessionSnapshotTab, RuntimeMobileSessionTabsSnapshot @@ -67,6 +68,11 @@ export function buildMobileSessionTabSnapshots( const snapshots: RuntimeMobileSessionTabsSnapshot[] = [] for (const worktreeId of worktreeIds) { + // A bare id cannot identify the host when local and remote rows collide; fail closed. + if (getIndexedWorktreesById(state.worktreesByRepo ?? {}, worktreeId).length > 1) { + graphState.mobileSessionSnapshotCacheByWorktree.delete(worktreeId) + continue + } const workspaceScope = parseWorkspaceKey(worktreeId) if ( workspaceScope?.type === 'folder' &&