refactor(renderer): fold four lib callers off the deprecated runtime-owner resolver (#30458)

Open-in owner, execution-host platform facts, doc-preview grants and the activation PTY inventory
read the owner through resolveOwnerWithLegacyFallback. Open in now refuses a twin instead of
launching it as this desktop's checkout; the others keep reading a twin as not server-owned. A row
whose only repo row is a server proxying its SSH target routes through that server. The
creation-action policy stays until browser creation folds with it. Deprecated importers 57 -> 53.
This commit is contained in:
Neil
2026-10-18 18:09:07 -07:00
committed by GitHub
parent cf460aba5a
commit 1ad8755fa8
11 changed files with 76 additions and 24 deletions
@@ -34,9 +34,7 @@ src/renderer/src/lib/active-agent-note-send.ts
src/renderer/src/lib/active-agent-note-target.ts
src/renderer/src/lib/agent-hibernation-coordinator.ts
src/renderer/src/lib/client-creation-action-policy.ts
src/renderer/src/lib/doc-preview-grants.ts
src/renderer/src/lib/execute-open-editor-path-move.ts
src/renderer/src/lib/execution-host-facts.ts
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
@@ -47,10 +45,8 @@ 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-pty-inventory.ts
src/renderer/src/lib/worktree-activation.ts
src/renderer/src/lib/worktree-creation-agent-seeds.ts
src/renderer/src/lib/worktree-row-open-in-owner.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
+1 -5
View File
@@ -164,7 +164,6 @@
1 src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx
1 src/renderer/src/components/sidebar/WorktreeCardPorts.tsx
2 src/renderer/src/components/sidebar/WorktreeList.tsx
1 src/renderer/src/components/sidebar/WorktreeRowOpenInSubMenu.tsx
1 src/renderer/src/components/sidebar/WorktreeVisibilityDialog.tsx
2 src/renderer/src/components/sidebar/delete-worktree-flow.ts
1 src/renderer/src/components/sidebar/linear-agent-skill-runtime.ts
@@ -312,11 +311,10 @@
1 src/renderer/src/lib/client-creation-action-error.ts
3 src/renderer/src/lib/client-creation-action-policy.ts
1 src/renderer/src/lib/default-creation-host.ts # the sanctioned reader of the default host
1 src/renderer/src/lib/doc-preview-grants.ts
1 src/renderer/src/lib/empty-workspace-default-agent-chat.ts
3 src/renderer/src/lib/execute-open-editor-path-move.ts
1 src/renderer/src/lib/execution-host-display-label.ts
2 src/renderer/src/lib/execution-host-facts.ts
1 src/renderer/src/lib/execution-host-facts.ts
6 src/renderer/src/lib/file-preview.ts
2 src/renderer/src/lib/folder-workspace-runtime-owner.ts
6 src/renderer/src/lib/http-link-destinations.ts
@@ -351,12 +349,10 @@
5 src/renderer/src/lib/workspace-browser-tab-open.ts
1 src/renderer/src/lib/workspace-file-drag-source.ts
1 src/renderer/src/lib/workspace-tab-palette-activation.ts
1 src/renderer/src/lib/worktree-activation-pty-inventory.ts
3 src/renderer/src/lib/worktree-activation.ts
1 src/renderer/src/lib/worktree-creation-agent-seeds.ts
1 src/renderer/src/lib/worktree-jump-navigation.ts
2 src/renderer/src/lib/worktree-operation-route.ts
1 src/renderer/src/lib/worktree-row-open-in-owner.ts
7 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/runtime/local-structured-session-tabs-sync/snapshot-apply.ts
@@ -22,7 +22,8 @@ vi.mock('@/lib/connection-owner-resolution', async (importOriginal) => ({
}))
vi.mock('@/lib/worktree-runtime-owner', async (importOriginal) => ({
...(await importOriginal<typeof WorktreeRuntimeOwnerModule>()),
getRuntimeEnvironmentIdForWorktree: () => 'env-1'
getRuntimeEnvironmentIdForWorktree: () => 'env-1',
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'env-1'
}))
import { NativeChatInlineVisual } from './NativeChatInlineVisual'
@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
connectionId: null as string | null | undefined,
environmentId: null as string | null,
ambiguous: false,
mintGrant: vi.fn(),
revokeGrant: vi.fn()
}))
@@ -11,7 +12,8 @@ vi.mock('@/lib/connection-owner-resolution', () => ({
getConnectionIdForFileFromState: () => mocks.connectionId
}))
vi.mock('@/lib/worktree-runtime-owner', () => ({
getRuntimeEnvironmentIdForWorktree: () => mocks.environmentId
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () =>
mocks.ambiguous ? null : mocks.environmentId
}))
import {
@@ -26,6 +28,7 @@ beforeEach(() => {
vi.clearAllMocks()
mocks.connectionId = null
mocks.environmentId = null
mocks.ambiguous = false
mocks.mintGrant.mockResolvedValue({ grantId: 'grant-1', url: 'orca-preview://grant-1/a.html' })
vi.stubGlobal('window', {
api: { docPreview: { mintGrant: mocks.mintGrant, revokeGrant: mocks.revokeGrant } }
@@ -33,6 +36,11 @@ beforeEach(() => {
})
describe('buildDocPreviewGrantRequest', () => {
it('mints no grant for a twin, which names no server to mint against', () => {
mocks.ambiguous = true
expect(buildDocPreviewGrantRequest(state, 'wt-1', '/srv/repo/docs/a.html')).toBeNull()
})
// A document outside every workspace resolves and authorizes from its own directory.
it('roots an SSH grant outside the workspace at the document directory', () => {
mocks.connectionId = 'ssh-1'
+3 -2
View File
@@ -1,7 +1,7 @@
import type { DocPreviewGrantRequest } from '../../../preload/api/doc-preview-api'
import { basename, dirname, getRelativePathInsideRoot } from '@/lib/path'
import { getConnectionIdForFileFromState } from '@/lib/connection-owner-resolution'
import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner'
import { toRuntimeWorktreeSelector } from '@/runtime/runtime-worktree-selector'
import { chatScratchFileAddress } from '@/runtime/runtime-chat-scratch-file-address'
import type { AppState } from '@/store/types'
@@ -43,7 +43,8 @@ export function buildDocPreviewGrantRequest(
// directory is often a home directory, which is where secrets live.
return { owner: { kind: 'ssh', connectionId }, requestBase, root, entryRelativePath }
}
const environmentId = getRuntimeEnvironmentIdForWorktree(state, worktreeId)
// Why: a twin names no server to mint against, so it gets no grant.
const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId)
// Outside the workspace, a chat's scratch folder stands in for it: its host serves it as a root.
const base =
worktreeRoot && worktreeRelativePath
+4 -2
View File
@@ -14,7 +14,7 @@ import { getLocalProjectExecutionRuntimeContext } from './local-preflight-contex
import { CLIENT_PLATFORM } from './new-workspace'
import {
getExecutionHostIdForWorktree,
getRuntimeEnvironmentIdForWorktree
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin
} from './worktree-runtime-owner'
/**
@@ -148,7 +148,9 @@ export function resolveWorktreeExecutionHostPlatform(
nestedSshTargetId:
host?.kind === 'runtime' ? (getConnectionIdFromState(state, worktreeId) ?? null) : null,
sshOwnerEnvironmentId:
host?.kind === 'ssh' ? getRuntimeEnvironmentIdForWorktree(state, worktreeId) : null,
host?.kind === 'ssh'
? runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId)
: null,
workspacePath,
projectRuntime: () => getLocalProjectExecutionRuntimeContext(state, worktreeId)
})
@@ -178,7 +178,8 @@ vi.mock('@/runtime/local-runtime-capabilities', () => ({
vi.mock('@/lib/worktree-runtime-owner', () => ({
getExecutionHostIdForWorktree: () =>
store.repos[0]?.connectionId ? `ssh:${store.repos[0].connectionId}` : 'local',
getRuntimeEnvironmentIdForWorktree: () => null
getRuntimeEnvironmentIdForWorktree: () => null,
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null
}))
vi.mock('@/runtime/structured-agent-session-client', () => ({
callStructuredAgentSession: (target: unknown, method: string, params: unknown) =>
@@ -2,7 +2,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
import { parseExecutionHostId } from '../../../shared/execution-host'
import type { PtyListedSession, PtySessionListScope } from '../../../shared/pty-listed-session'
import { getRepoIdFromWorktreeId } from '../../../shared/worktree/id'
import { getRuntimeEnvironmentIdForWorktree } from './worktree-runtime-owner'
import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner'
import {
resolveIndexedRepoOwner,
resolveIndexedWorktreeOwner
@@ -21,7 +21,10 @@ export function resolveActivationPtyListScope(
return { connectionId: null }
}
const resolution = resolveWorktreeOperationRouteResult(state, worktreeId)
if (resolution.kind === 'missing' && !getRuntimeEnvironmentIdForWorktree(state, worktreeId)) {
if (
resolution.kind === 'missing' &&
!runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId)
) {
const repo = resolveIndexedRepoOwner(state.repos, getRepoIdFromWorktreeId(worktreeId))
const worktree = resolveIndexedWorktreeOwner(state.worktreesByRepo, worktreeId)
// A known native repo remains usable while the unrelated runtime catalog hydrates.
@@ -3,6 +3,30 @@ import type { Worktree } from '../../../shared/worktree/types'
import { resolveWorktreeRowOpenInRuntimeOwner } from './worktree-row-open-in-owner'
import { getExternalEditorOpenCapability } from './external-editor-open-capability'
import { getLocalPathOpenOwnerForRoute, isLocalPathOpenBlocked } from './local-path-open-guard'
import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture'
describe('an unhosted row resolves through the fold adapter', () => {
const caseState = (name: string) => OWNER_RESOLUTION_CASES.find((c) => c.name === name)!.state
const row = (id: string, repoId: string) => ({ id, repoId, hostId: undefined })
it('leaves a git twin unresolved instead of launching it as this desktop checkout', () => {
expect(
resolveWorktreeRowOpenInRuntimeOwner(
caseState('focused | projected-twin::wt'),
row('projected-twin::wt', 'projected-twin')
).ownerUnresolved
).toBe(true)
})
it('routes a row whose only repo row is a server proxying its target through that server', () => {
expect(
resolveWorktreeRowOpenInRuntimeOwner(
caseState('focused | stale-hub-repo::wt'),
row('stale-hub-repo::wt', 'stale-hub-repo')
)
).toEqual({ runtimeEnvironmentId: 'hub-env', connectionId: null, ownerUnresolved: false })
})
})
const WORKTREE_ID = 'repo-1::/srv/worktree'
@@ -1,7 +1,6 @@
import { parseExecutionHostId } from '../../../shared/execution-host'
import type { Worktree } from '../../../shared/worktree/types'
import { getResolvedExecutionHostIdForWorktree } from './resolved-worktree-execution-host'
import { getRuntimeEnvironmentIdForWorktree } from './worktree-runtime-owner'
import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner'
import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state'
import {
resolveStrictWorktreeOperationRouteResultForHost,
@@ -46,10 +45,17 @@ export function resolveWorktreeRowOpenInRuntimeOwner(
return runtimeEnvironmentId ? resolved(runtimeEnvironmentId) : UNRESOLVED
}
if (!worktree.hostId) {
return resolved(
getRuntimeEnvironmentIdForWorktree(state, worktree.id),
getResolvedExecutionHostIdForWorktree(state, worktree.id)
)
// Why: a twin names no owner, and Open in must not launch it as this desktop's checkout.
const owner = resolveOwnerWithLegacyFallback(state, { workspaceId: worktree.id })
if (owner.kind === 'ambiguous') {
return UNRESOLVED
}
// Why: an owner no row places was read as this desktop before the fold, and still is.
if (owner.kind === 'missing') {
return resolved(null)
}
const { endpoint, at } = owner.owner
return resolved(endpoint.kind === 'environment' ? endpoint.environmentId : null, at)
}
// Why: an inactive row can name its host before the catalog has it, so the explicit host wins.
const resolution = resolveStrictWorktreeOperationRouteResultForHost(
@@ -288,3 +288,17 @@ export function resolveOwnerWithLegacyFallback(
}
return match.kind === 'resolved' ? match : { kind: 'resolved', owner: SELF_LOCAL_AUTHORITY }
}
/**
* The paired server behind {@link resolveOwnerWithLegacyFallback}, or `null` for this app, a
* directly dialed SSH target or a twin: for readers whose twin answer is "no server", as before.
*/
export function runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(
state: WorktreeRuntimeOwnerState,
workspaceId: string
): string | null {
const match = resolveOwnerWithLegacyFallback(state, { workspaceId })
return match.kind === 'resolved' && match.owner.endpoint.kind === 'environment'
? match.owner.endpoint.environmentId
: null
}