fix(cli): implicit worktree selection is scoped to the caller's host (#27108)

* fix(cli): implicit worktree selection is scoped to the caller's host

A CLI run from a shell on SSH host D matched its cwd against every host's
worktrees, so a same-path twin on host A could win. Candidates are now
limited to the caller's execution host (the relay's SSH stamp, else this
machine), and a pane's forwarded ORCA_WORKTREE_ID is scoped the same way.

* fix(cli): a local caller keeps runtime-stamped checkouts this runtime holds

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
This commit is contained in:
OrcaWin
2026-10-09 20:36:36 -07:00
committed by GitHub
co-authored by m4air
parent 03408d8570
commit 4ec973f588
3 changed files with 203 additions and 32 deletions
+88
View File
@@ -0,0 +1,88 @@
import { resolve as resolvePath } from 'node:path'
import { isPathInsideOrEqual } from '../shared/cross-platform-path'
import {
LOCAL_EXECUTION_HOST_ID,
parseExecutionHostId,
toSshExecutionHostId,
type ExecutionHostId
} from '../shared/execution-host'
import { readOrchestrationCompatibilityEvidence } from '../shared/orchestration-compatibility-evidence'
import type { RuntimeWorktreeListResult, RuntimeWorktreeRecord } from '../shared/runtime-types'
import type { RuntimeClient } from './runtime-client'
type CatalogRow = Pick<RuntimeWorktreeRecord, 'id' | 'path' | 'hostId'> & {
identity?: { key: string }
}
/** The execution host this CLI process runs on: the SSH target its relay stamped, else this machine. */
export function getCallerExecutionHostId(
env: Readonly<Record<string, string | undefined>> = process.env
): ExecutionHostId {
const host = readOrchestrationCompatibilityEvidence(env)?.host
return host?.kind === 'ssh' ? toSshExecutionHostId(host.targetId) : LOCAL_EXECUTION_HOST_ID
}
// Why: the catalog lists SSH hosts' worktrees too and two hosts can hold the same path, so an SSH
// caller may only take its own host's rows and a local caller none of an SSH host's. `runtime:*`
// rows in this runtime's catalog are checkouts it holds itself, and unstamped rows predate stamping.
function isOnCallerHost(row: CatalogRow, callerHostId: ExecutionHostId): boolean {
const rowHostId = row.hostId ?? LOCAL_EXECUTION_HOST_ID
return parseExecutionHostId(callerHostId)?.kind === 'ssh'
? rowHostId === callerHostId
: parseExecutionHostId(rowHostId)?.kind !== 'ssh'
}
// Why: `id:` keeps repo inference and the runtime's scoped fast path; only a bare id that another
// host's row shares needs the exact identity, or the runtime would refuse it as ambiguous.
function toHostScopedSelector(row: CatalogRow, catalog: readonly CatalogRow[]): string {
const sharedId = catalog.some((other) => other !== row && other.id === row.id)
return sharedId && row.identity?.key ? `identity:${row.identity.key}` : `id:${row.id}`
}
/** The caller host's innermost worktree enclosing `cwd`, as a selector; undefined when none does. */
export function selectEnclosingWorktreeOnHost(
catalog: readonly CatalogRow[],
cwd: string,
hostId: ExecutionHostId
): string | undefined {
const currentPath = resolvePath(cwd)
let enclosing: CatalogRow | undefined
let enclosingPathLength = -1
for (const row of catalog) {
if (!isOnCallerHost(row, hostId)) {
continue
}
const rowPath = resolvePath(row.path)
if (!isPathInsideOrEqual(rowPath, currentPath) || rowPath.length <= enclosingPathLength) {
continue
}
enclosing = row
enclosingPathLength = rowPath.length
}
return enclosing ? toHostScopedSelector(enclosing, catalog) : undefined
}
/** Scopes a pane's forwarded bare worktree id to the caller's host; undefined when it has none. */
export function selectWorktreeIdOnHost(
catalog: readonly CatalogRow[],
worktreeId: string,
hostId: ExecutionHostId
): string | undefined {
const match = catalog.find((row) => row.id === worktreeId && isOnCallerHost(row, hostId))
return match ? toHostScopedSelector(match, catalog) : undefined
}
/** Scopes a pane's forwarded bare `ORCA_WORKTREE_ID` to the caller's host before it is used. */
export async function resolveCallerPaneWorktreeSelector(
worktreeId: string,
client: RuntimeClient
): Promise<string | undefined> {
const callerHostId = getCallerExecutionHostId()
if (callerHostId === LOCAL_EXECUTION_HOST_ID) {
return worktreeId
}
const worktrees = await client.call<RuntimeWorktreeListResult>('worktree.list', {
limit: 10_000
})
return selectWorktreeIdOnHost(worktrees.result.worktrees, worktreeId, callerHostId)
}
+28 -32
View File
@@ -1,11 +1,6 @@
import { resolve as resolvePath } from 'node:path'
import type {
ComputerAppQuery,
RuntimeWorktreeListResult,
RuntimeWorktreeRecord
} from '../shared/runtime-types'
import type { ComputerAppQuery, RuntimeWorktreeListResult } from '../shared/runtime-types'
import {
isPathInsideOrEqual,
isWslUncPathForCallerLinuxPath,
isWslUncPathForLinuxMountedPath
} from '../shared/cross-platform-path'
@@ -13,6 +8,11 @@ import { parseWslUncPath } from '../shared/wsl-paths'
import type { RuntimeClient } from './runtime-client'
import { RuntimeClientError } from './runtime/types'
import { getOptionalStringFlag, getRequiredStringFlag } from './flags'
import {
getCallerExecutionHostId,
resolveCallerPaneWorktreeSelector,
selectEnclosingWorktreeOnHost
} from './caller-host-worktree-selection'
export type BrowserCliTarget = {
worktree?: string
@@ -107,36 +107,25 @@ export async function resolveCurrentWorktreeSelector(
): Promise<string> {
assertLocalCwdWorktreeSelector('current', client)
const currentPath = resolvePath(cwd)
const worktrees = await client.call<RuntimeWorktreeListResult>('worktree.list', {
limit: 10_000
})
let enclosingWorktree: RuntimeWorktreeRecord | undefined
let enclosingPathLength = -1
for (const worktree of worktrees.result.worktrees) {
const worktreePath = resolvePath(worktree.path)
if (
!isPathInsideOrEqual(worktreePath, currentPath) ||
worktreePath.length <= enclosingPathLength
) {
continue
}
enclosingWorktree = worktree
enclosingPathLength = worktreePath.length
}
if (!enclosingWorktree) {
throw new RuntimeClientError(
'selector_not_found',
`No Orca-managed worktree contains the current directory: ${currentPath}`
)
}
// Why: users expect "active/current" to mean the enclosing managed worktree
// even from nested subdirectories. Resolve to the concrete runtime id here:
// even from nested subdirectories. Resolve to the concrete runtime row here:
// duplicate repo registrations can expose the same Git worktree path, and a
// path selector would throw selector_ambiguous after losing the repo id.
return `id:${enclosingWorktree.id}`
const selector = selectEnclosingWorktreeOnHost(
worktrees.result.worktrees,
cwd,
getCallerExecutionHostId()
)
if (!selector) {
throw new RuntimeClientError(
'selector_not_found',
`No Orca-managed worktree contains the current directory: ${resolvePath(cwd)}`
)
}
return selector
}
export async function getOptionalWorktreeSelector(
@@ -301,7 +290,10 @@ export async function getEmulatorWorktreeSelector(
}
const terminalWorktreeId = process.env.ORCA_WORKTREE_ID
if (terminalWorktreeId?.trim()) {
return terminalWorktreeId
const scoped = await resolveCallerPaneWorktreeSelector(terminalWorktreeId, client)
if (scoped) {
return scoped
}
}
const folderWorkspaceId = process.env.ORCA_WORKSPACE_ID?.trim()
if (folderWorkspaceId?.startsWith('folder:')) {
@@ -323,7 +315,11 @@ export async function getEmulatorCommandTarget(
const emulator = getOptionalStringFlag(flags, 'emulator')
const worktree = await getEmulatorWorktreeSelector(flags, cwd, client)
if (device || emulator) {
return { device: device || undefined, emulator: emulator || undefined, worktree }
return {
device: device || undefined,
emulator: emulator || undefined,
worktree
}
}
return { worktree }
}
@@ -0,0 +1,87 @@
/**
* #21210: several SSH hosts hold a worktree of one repo at the same path. A CLI on host D must
* resolve `current` (and a pane's forwarded worktree id) to host D's row, never another host's twin.
*/
import { describe, expect, it } from 'vitest'
import {
getCallerExecutionHostId,
selectEnclosingWorktreeOnHost,
selectWorktreeIdOnHost
} from './caller-host-worktree-selection'
const PATH = '/srv/app'
function row(hostId: 'local' | `ssh:${string}` | undefined, id: string, identityKey?: string) {
return {
id,
path: PATH,
...(hostId ? { hostId } : {}),
...(identityKey ? { identity: { key: identityKey } } : {})
}
}
const SSH_CALLER_D = {
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_KIND: 'ssh',
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_ID: 'host d',
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_INCARNATION: 'inc-1',
ORCA_ORCHESTRATION_COMPATIBILITY_ATTACHMENT: 'att-1'
}
const TWINS = [
row('ssh:host-a', `repo-a::${PATH}`),
row('ssh:host%20d', `repo-d::${PATH}`),
row('local', `repo-l::${PATH}`)
]
describe('implicit worktree selection is scoped to the caller host (#21210)', () => {
it('reads the caller host from the relay stamp, encoding the SSH target', () => {
expect(getCallerExecutionHostId(SSH_CALLER_D)).toBe('ssh:host%20d')
expect(getCallerExecutionHostId({})).toBe('local')
// A WSL stamp is still this machine.
expect(
getCallerExecutionHostId({
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_KIND: 'wsl',
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_ID: 'local',
ORCA_ORCHESTRATION_COMPATIBILITY_HOST_INCARNATION: 'Ubuntu'
})
).toBe('local')
})
it("resolves an SSH caller's cwd to its own host's twin", () => {
const hostId = getCallerExecutionHostId(SSH_CALLER_D)
expect(selectEnclosingWorktreeOnHost(TWINS, `${PATH}/src`, hostId)).toBe(`id:repo-d::${PATH}`)
})
it('resolves a caller with no host stamp to the local row only', () => {
expect(selectEnclosingWorktreeOnHost(TWINS, PATH, 'local')).toBe(`id:repo-l::${PATH}`)
})
// A runtime-stamped row in this runtime's own catalog is a checkout it holds locally.
it('keeps a runtime-stamped local checkout for a local caller', () => {
const rows = [
TWINS[0]!,
{ id: `repo-r::${PATH}`, path: PATH, hostId: 'runtime:env-1' as const }
]
expect(selectEnclosingWorktreeOnHost(rows, `${PATH}/src`, 'local')).toBe(`id:repo-r::${PATH}`)
expect(selectEnclosingWorktreeOnHost(rows, PATH, 'ssh:host%20d')).toBeUndefined()
})
it('never adopts another host or an unstamped row for an SSH caller', () => {
expect(
selectEnclosingWorktreeOnHost([TWINS[0]!, row(undefined, 'repo-u::x')], PATH, 'ssh:host-z')
).toBeUndefined()
})
it('names the exact identity when another host shares the bare id', () => {
const twins = [
row('ssh:host-a', `repo-1::${PATH}`, 'wt2:a'),
row('ssh:host%20d', `repo-1::${PATH}`, 'wt2:d')
]
expect(selectEnclosingWorktreeOnHost(twins, PATH, 'ssh:host%20d')).toBe('identity:wt2:d')
expect(selectWorktreeIdOnHost(twins, `repo-1::${PATH}`, 'ssh:host%20d')).toBe('identity:wt2:d')
expect(selectWorktreeIdOnHost(twins, `repo-1::${PATH}`, 'ssh:host-z')).toBeUndefined()
})
})