refactor(renderer): fold activation mirroring and sort-order persistence off the deprecated owner resolver (#30484)

The eight mirroring callers (#23-#30) read the owner through
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin, so a twin is still not mirrored. Sort-order
persistence (#2) leaves out ids that two hosts publish, and ids whose only server proof is a repo
row (isUnprovenServerSshRoute), instead of writing them to this desktop's or a guessed server's
meta. Deprecated importers 40 -> 31.
This commit is contained in:
Neil
2026-10-18 20:16:50 -07:00
committed by GitHub
parent 51530bb86c
commit f951cf44bb
17 changed files with 125 additions and 41 deletions
@@ -9,8 +9,6 @@ src/renderer/src/components/native-chat/native-chat-runtime-owner.ts
src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts
src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts
src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts
src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts
src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts
src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts
src/renderer/src/components/terminal-pane/terminal-pane-menu-paste.ts
src/renderer/src/components/terminal-pane/terminal-pane-paste-execution.ts
@@ -26,18 +24,11 @@ src/renderer/src/lib/file-preview.ts
src/renderer/src/lib/launch-agent-background-session.ts
src/renderer/src/lib/launch-agent-in-new-tab.ts
src/renderer/src/lib/launch-ai-vault-session.ts
src/renderer/src/lib/structured-agent-session-tab-activation.ts
src/renderer/src/lib/tab-number-shortcuts.ts
src/renderer/src/lib/terminal-tab-owner-activation.ts
src/renderer/src/lib/terminal-worktree-route.ts
src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts
src/renderer/src/lib/workspace-tab-palette-activation.ts
src/renderer/src/lib/worktree-activation.ts
src/renderer/src/lib/worktree-creation-agent-seeds.ts
src/renderer/src/lib/worktree-sort-order-host-split.ts
src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts
src/renderer/src/store/slices/browser/browser-tab-actions.ts
src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts
src/renderer/src/store/slices/terminal-tab-retirement.ts
src/renderer/src/store/terminals/terminal-active-workspace-creation.ts
src/renderer/src/store/terminals/terminal-pane-hibernation.ts
+1
View File
@@ -360,6 +360,7 @@
1 src/renderer/src/lib/worktree-row-open-in-owner.ts # reads focus through resolveOwnerWithLegacyFallback, newly counted (round 3 F2); F8 nets it down
9 src/renderer/src/lib/worktree-runtime-owner.ts # typed owner resolver, its null-collapsing wrapper, and the legacy-fallback adapter the folds move to (round 3 F0); deleting the deprecated pair (F8) nets it down
1 src/renderer/src/lib/worktree-sort-order-host-split.ts
1 src/renderer/src/lib/worktree-sort-order-persistence.ts # its split helper calls resolveOwnerWithLegacyFallback directly (round 3 F4); F8 nets it down
1 src/renderer/src/runtime/local-structured-session-tabs-sync/snapshot-apply.ts
17 src/renderer/src/runtime/runtime-jira-client.ts
2 src/renderer/src/runtime/runtime-jira-summary-client.ts
@@ -1,5 +1,5 @@
import type { RuntimeMobileSessionTabMove } from '../../../../shared/runtime-types'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { useAppStore } from '../../store'
import { isWebRuntimeSessionActive } from '../../runtime/web-runtime-session-environment'
import { moveWebRuntimeSessionTab } from '../../runtime/web-runtime-session-tab-move'
@@ -9,7 +9,10 @@ export function mirrorWebRuntimeTabMove(
worktreeId: string
}
): void {
const environmentId = getRuntimeEnvironmentIdForWorktree(useAppStore.getState(), args.worktreeId)
const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
useAppStore.getState(),
args.worktreeId
)
if (!isWebRuntimeSessionActive(environmentId)) {
return
}
@@ -5,7 +5,7 @@ import { focusTerminalTabSurface } from '../../lib/focus-terminal-tab-surface'
import { TOGGLE_TERMINAL_PANE_EXPAND_EVENT } from '@/constants/terminal'
import { activateWebRuntimeSessionTab } from '../../runtime/web-runtime-session-tab-lifecycle'
import { isWebRuntimeSessionActive } from '../../runtime/web-runtime-session-environment'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { browserWorkspaceHasRemoteOwner } from '@/runtime/remote-browser-tab-ownership'
import { activateStructuredAgentSessionTab } from '@/lib/structured-agent-session-tab-activation'
import type { TabGroupWorktreeSnapshot } from './useTabGroupItemProjections'
@@ -38,7 +38,7 @@ export function useTabGroupActivationCommands({
}
focusGroup(worktreeId, groupId)
activateTab(item.id)
const runtimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(
const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
useAppStore.getState(),
worktreeId
)
@@ -117,7 +117,7 @@ export function useTabGroupActivationCommands({
}
focusGroup(worktreeId, groupId)
activateTab(item.id)
const runtimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(
const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
useAppStore.getState(),
worktreeId
)
+30
View File
@@ -4,6 +4,7 @@
*/
import {
getRepoSshConnectionId,
parseExecutionHostId,
toRuntimeExecutionHostId,
toSshExecutionHostId,
type ExecutionHostId
@@ -250,3 +251,32 @@ export function runtimeEnvironmentIdUnlessTwin(
}
return null
}
/**
* A server route to `ssh:X` that only a repo row proves: no worktree row names that server. The
* row's `ssh:X` and the server's `connectionId: X` are different namespaces, so reads and spawn
* may follow it but a host write could land on another machine with the same path.
*/
export function isUnprovenServerSshRoute(
state: Pick<WorktreeOperationRouteState, 'worktreesByRepo' | 'detectedWorktreesByRepo'>,
workspaceId: string,
owner: HostAuthority
): boolean {
const { endpoint, at } = owner
if (
endpoint.kind !== 'environment' ||
at === 'local' ||
parseWorkspaceKey(workspaceId)?.type === 'folder'
) {
return false
}
const repoId = getRepoIdFromWorktreeId(workspaceId)
const namesServer = (row: { id: string; hostId?: string; runtimeOwnerEnvironmentId?: string }) =>
row.id === workspaceId &&
parseExecutionHostId(row.hostId)?.id === at &&
row.runtimeOwnerEnvironmentId?.trim() === endpoint.environmentId
return !(
state.worktreesByRepo?.[repoId]?.some(namesServer) ||
state.detectedWorktreesByRepo?.[repoId]?.worktrees.some(namesServer)
)
}
@@ -18,7 +18,9 @@ vi.mock('@/components/native-chat/use-native-chat-composer-reveal-focus', () =>
requestNativeChatComposerFocus: mocks.requestNativeChatComposerFocus
}))
vi.mock('./worktree-runtime-owner', () => ({ getRuntimeEnvironmentIdForWorktree: () => 'env-1' }))
vi.mock('./worktree-runtime-owner', () => ({
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'env-1'
}))
vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc }))
@@ -1,4 +1,4 @@
import { getRuntimeEnvironmentIdForWorktree } from './worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner'
import { requestNativeChatComposerFocus } from '@/components/native-chat/use-native-chat-composer-reveal-focus'
import { useAppStore } from '@/store'
import { callRuntimeRpc } from '@/runtime/runtime-rpc-client'
@@ -49,7 +49,7 @@ export function activateStructuredAgentSessionTab(args: {
state.activateTab(tab.id, { worktreeId: args.worktreeId })
state.setActiveTabType('agent-session', args.worktreeId)
requestNativeChatComposerFocus(tab.id)
const environmentId = getRuntimeEnvironmentIdForWorktree(state, args.worktreeId)
const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, args.worktreeId)
void callRuntimeRpc(
args.target ?? runtimeTargetForOwnerEnvironment(environmentId),
'session.tabs.activate',
+2 -2
View File
@@ -1,7 +1,7 @@
import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface'
import { useAppStore } from '@/store'
import type { AppState } from '@/store/types'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { dedupeTabOrder } from '../../../shared/workspace-layout/tab-order'
import type { Tab } from '../../../shared/tab-types'
import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle'
@@ -60,7 +60,7 @@ export function activateTabNumberShortcut(index: number): boolean {
}
const worktreeId = target.worktreeId
const runtimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(store, worktreeId)
const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, worktreeId)
store.focusGroup(worktreeId, target.groupId)
store.activateTab(target.id)
@@ -2,13 +2,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
activateWebRuntimeSessionTab: vi.fn(),
getRuntimeEnvironmentIdForWorktree: vi.fn(),
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(),
resolveWebSessionVisibleTabId: vi.fn()
}))
vi.mock('@/store', () => ({ useAppStore: { getState: () => ({}) } }))
vi.mock('@/lib/worktree-runtime-owner', () => ({
getRuntimeEnvironmentIdForWorktree: mocks.getRuntimeEnvironmentIdForWorktree
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin:
mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin
}))
vi.mock('@/runtime/web-runtime-session-tab-lifecycle', () => ({
activateWebRuntimeSessionTab: mocks.activateWebRuntimeSessionTab
@@ -30,12 +31,12 @@ describe('activateTerminalTabOnOwner', () => {
})
it('tells the paired server that owns the workspace, not the focused one', () => {
mocks.getRuntimeEnvironmentIdForWorktree.mockReturnValue('env-owner')
mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-owner')
mocks.resolveWebSessionVisibleTabId.mockReturnValue('unified-visible')
activateTerminalTabOnOwner('wt-1', 'web-terminal-host-tab', 'leaf-b')
expect(mocks.getRuntimeEnvironmentIdForWorktree).toHaveBeenCalledWith({}, 'wt-1')
expect(mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin).toHaveBeenCalledWith({}, 'wt-1')
expect(mocks.activateWebRuntimeSessionTab).toHaveBeenCalledWith({
worktreeId: 'wt-1',
tabId: 'web-terminal-host-tab',
@@ -47,7 +48,7 @@ describe('activateTerminalTabOnOwner', () => {
})
it('makes no owner round trip for a workspace this client runs', () => {
mocks.getRuntimeEnvironmentIdForWorktree.mockReturnValue(null)
mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null)
activateTerminalTabOnOwner('wt-local', 'tab-1', null)
@@ -1,5 +1,5 @@
import { useAppStore } from '@/store'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle'
import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment'
import { resolveWebSessionVisibleTabId } from '@/runtime/web-session-focus-intent'
@@ -17,7 +17,7 @@ export function activateTerminalTabOnOwner(
leafId: string | null
): void {
const state = useAppStore.getState()
const environmentId = getRuntimeEnvironmentIdForWorktree(state, worktreeId)
const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId)
if (!environmentId || !isWebRuntimeSessionActive(environmentId)) {
return
}
@@ -63,7 +63,7 @@ const mocks = vi.hoisted(() => {
activateAndRevealWorkspace: vi.fn(),
activateWebRuntimeSessionTab: vi.fn(),
focusTerminalTabSurface: vi.fn(),
getRuntimeEnvironmentIdForWorktree: vi.fn(),
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(),
isWebRuntimeSessionActive: vi.fn()
}
})
@@ -79,7 +79,8 @@ vi.mock('@/lib/focus-terminal-tab-surface', () => ({
}))
vi.mock('@/lib/worktree-runtime-owner', () => ({
getRuntimeEnvironmentIdForWorktree: mocks.getRuntimeEnvironmentIdForWorktree
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin:
mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin
}))
vi.mock('@/runtime/web-runtime-session-tab-lifecycle', () => ({
@@ -172,7 +173,7 @@ describe('activateWorkspaceTabPaletteResult', () => {
.find((worktree) => worktree.id === worktreeId)
)
mocks.activateAndRevealWorkspace.mockReturnValue(true)
mocks.getRuntimeEnvironmentIdForWorktree.mockReturnValue('runtime-1')
mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('runtime-1')
mocks.isWebRuntimeSessionActive.mockReturnValue(false)
})
@@ -1,5 +1,5 @@
import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle'
import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment'
import { useAppStore } from '@/store'
@@ -119,7 +119,10 @@ export function activateWorkspaceTabPaletteResult(
return { status: 'failed', reason: finalFailure }
}
const runtimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(state, result.worktreeId)
const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
state,
result.worktreeId
)
state.focusGroup(result.worktreeId, result.groupId)
state.activateTab(result.tabId, { worktreeId: result.worktreeId })
+5 -2
View File
@@ -7,7 +7,7 @@ import { registerWorktreeActivation } from '@/lib/worktree-activation-nav-regist
import { workspaceHasSleepingAgentSessions } from '@/lib/worktree-agent-activation-gate'
import { resumeSleepingAgentSessionsForWorktree } from '@/lib/resume-sleeping-agent-session'
import { shouldAutoCreateInitialTerminal } from '@/components/terminal/initial-terminal'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { folderWorkspaceKey, parseWorkspaceKey } from '../../../shared/workspace-scope'
import {
folderWorkspaceActivationBlocked,
@@ -210,7 +210,10 @@ export function activateAndRevealWorktree(
// 3. Core activation: setActiveWorktree also restores per-worktree state, clears unread, bumps dead PTY generations, refreshes GitHub
state.setActiveWorktree(worktreeId, opts?.executionHostId)
const postActivationState = useAppStore.getState()
const ownerRuntimeEnvironmentId = getRuntimeEnvironmentIdForWorktree(postActivationState, wt.id)
const ownerRuntimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
postActivationState,
wt.id
)
if (opts?.notifyHostRuntime !== false && isWebRuntimeSessionActive(ownerRuntimeEnvironmentId)) {
// Why: paired web clients own only local selection, so the desktop host publishes session surfaces without treating it as a nav command.
void activateWebRuntimeSessionWorktree({
@@ -34,4 +34,42 @@ describe('splitWorktreeSortOrderByHost', () => {
)
expect(groups).toEqual([{ hostId: 'runtime:focused-env', orderedIds: ['legacy::wt'] }])
})
it('leaves out a twin and a row only a repo row places on a server', () => {
const groups = splitWorktreeSortOrderByHost(
{
settings: { activeRuntimeEnvironmentId: null },
runtimeEnvironments: [{ id: 'hub-env' }],
runtimeEnvironmentCatalogHydrated: true,
repos: [
{ id: 'local-repo', connectionId: null, executionHostId: 'local' },
{ id: 'twin', connectionId: null, executionHostId: 'local' },
{ id: 'twin', connectionId: 'box', executionHostId: 'ssh:box' },
{ id: 'stale-hub', connectionId: 'target', executionHostId: 'runtime:hub-env' },
{ id: 'hub', connectionId: 'box', executionHostId: 'runtime:hub-env' }
],
worktreesByRepo: {
'local-repo': [{ id: 'local-repo::wt-a', repoId: 'local-repo' }],
twin: [
{ id: 'twin::wt', repoId: 'twin', hostId: 'local' },
{ id: 'twin::wt', repoId: 'twin', hostId: 'ssh:box' }
],
'stale-hub': [{ id: 'stale-hub::wt', repoId: 'stale-hub', hostId: 'ssh:target' }],
hub: [
{
id: 'hub::wt',
repoId: 'hub',
hostId: 'ssh:box',
runtimeOwnerEnvironmentId: 'hub-env'
}
]
}
},
['twin::wt', 'stale-hub::wt', 'hub::wt', 'local-repo::wt-a']
)
expect(groups).toEqual([
{ hostId: 'runtime:hub-env', orderedIds: ['hub::wt'] },
{ hostId: 'local', orderedIds: ['local-repo::wt-a'] }
])
})
})
@@ -1,5 +1,6 @@
import { LOCAL_EXECUTION_HOST_ID, toRuntimeExecutionHostId } from '../../../shared/execution-host'
import { getRuntimeEnvironmentIdForWorktree } from './worktree-runtime-owner'
import { isUnprovenServerSshRoute } from './resolve-owner'
import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner'
import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state'
export type WorktreeSortOrderHostGroup = {
@@ -13,7 +14,8 @@ export type WorktreeSortOrderHostGroup = {
* Why: persisted `sortOrder` lives in each host's `worktreeMeta` and is enriched
* onto worktrees from their owner host. Stamping the full cross-host id list on
* only the focused host loses other hosts' ordering and pollutes the focused
* host with foreign ids, so persist each host's ids on that host.
* host with foreign ids, so persist each host's ids on that host. Ids no host can be
* proven to own are left out.
*/
export function splitWorktreeSortOrderByHost(
state: WorktreeRuntimeOwnerState,
@@ -21,8 +23,16 @@ export function splitWorktreeSortOrderByHost(
): WorktreeSortOrderHostGroup[] {
const groups = new Map<string, string[]>()
for (const id of orderedIds) {
const environmentId = getRuntimeEnvironmentIdForWorktree(state, id)
const hostId = environmentId ? toRuntimeExecutionHostId(environmentId) : LOCAL_EXECUTION_HOST_ID
const owner = resolveOwnerWithLegacyFallback(state, { workspaceId: id })
// Why: a twin names no host to write to, and an unproven server route may be another machine.
if (owner.kind !== 'resolved' || isUnprovenServerSshRoute(state, id, owner.owner)) {
continue
}
const { endpoint } = owner.owner
const hostId =
endpoint.kind === 'environment'
? toRuntimeExecutionHostId(endpoint.environmentId)
: LOCAL_EXECUTION_HOST_ID
const existing = groups.get(hostId)
if (existing) {
existing.push(id)
@@ -76,7 +76,8 @@ vi.mock('@/lib/feature-education-telemetry', () => ({
}))
vi.mock('@/lib/worktree-runtime-owner', () => ({
getRuntimeEnvironmentIdForWorktree: mocks.getRuntimeEnvironmentIdForWorktree
getRuntimeEnvironmentIdForWorktree: mocks.getRuntimeEnvironmentIdForWorktree,
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: mocks.getRuntimeEnvironmentIdForWorktree
}))
vi.mock('@/lib/agent-launch-prompt-delivery', () => ({
@@ -1,7 +1,7 @@
import type { AppState } from '../../types'
import type { TerminalTab } from '../../../../../shared/terminal-tab-types'
import { findTabAndWorktree } from '../tab-group-state'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { locateTerminalTab } from '../../terminals/terminal-tab-location'
/**
@@ -33,7 +33,7 @@ export function mirrorTabPinnedToHost(state: AppState, tabId: string, isPinned:
if (
!found ||
found.tab.contentType !== 'terminal' ||
!getRuntimeEnvironmentIdForWorktree(state, found.worktreeId)
!runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, found.worktreeId)
) {
return
}
@@ -55,7 +55,7 @@ export function mirrorTabViewModeToHost(
if (
!found ||
found.tab.contentType !== 'terminal' ||
!getRuntimeEnvironmentIdForWorktree(state, found.worktreeId)
!runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, found.worktreeId)
) {
return
}