Files
orca/config/scripts/ci-e2e-job-selection.mjs
T
Neilandm4air 51e7181850 Keep remote terminal path links on their own host (#26696)
* Keep terminal workspace root links on their execution host

* Resolve terminal root-link source host from the owner, not a missing connection id

A direct SSH pane whose worktree id also exists on the desktop has no
connectionId (ambiguous owner), which was read as local and offered the
desktop workspace. Resolve the source via the owner-route resolver and
skip the root shortcut when the source host is unknown.

* Resolve terminal file links against the source owner's host

A same-id SSH workspace left connectionId unset, so a rejected root link,
the default-app row and Shift-click fell through to this machine's files.
The terminal file context now names the resolved direct SSH host and
refuses file access when no owner can be determined.

* Keep an ambiguous terminal link owner unresolved instead of local

The connection lookup ignores detected worktree rows, so a registered local
row could override an owner the full resolver found ambiguous (registered
local, detected SSH) and send link stat/open to this machine. Only a missing
route may now defer to the connection lookup.

* Re-resolve a held chat link's owner before rechecking it

Native chat rechecks held link targets when a turn ends. A target resolved
while its owner was local kept that context after a same-id worktree was
detected on an SSH host, so the recheck still asked this machine. Each
existence ask now re-resolves the workspace owner and refuses when it is
unresolved or names a different host.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-09 10:33:42 -07:00

177 lines
7.7 KiB
JavaScript

import { pathToFileURL } from 'node:url'
export const DOCKER_SSH_E2E_SPECS = [
'tests/e2e/local-ssh-browser-routing.spec.ts',
'tests/e2e/ssh-client-hosted-browser-drop-reconnect.spec.ts',
'tests/e2e/pty-input-write-queue-ssh.spec.ts',
'tests/e2e/ssh-ai-vault-session-history.spec.ts',
'tests/e2e/ssh-codex-display-artifacts-repro.spec.ts',
'tests/e2e/ssh-cold-activation-restore.spec.ts',
'tests/e2e/ssh-cold-hydration-gap-tab-seeding.spec.ts',
'tests/e2e/ssh-emptied-worktree-reactivation.spec.ts',
'tests/e2e/ssh-docker-five-pane-input-under-flood.spec.ts',
'tests/e2e/ssh-docker-bulk-open-freeze-repro.spec.ts',
'tests/e2e/ssh-docker-half-open-link.spec.ts',
'tests/e2e/ssh-docker-quick-open-large-listing.spec.ts',
'tests/e2e/ssh-docker-reconnect-pane-restore.spec.ts',
'tests/e2e/ssh-docker-relay-stall-credential.spec.ts',
'tests/e2e/ssh-docker-resource-accumulation.spec.ts',
'tests/e2e/ssh-docker-transport-drop-recovery.spec.ts',
'tests/e2e/ssh-external-image-preview.spec.ts',
'tests/e2e/ssh-lost-kill-tab-resurrection.spec.ts',
'tests/e2e/ssh-pi-compatible-agent-title.spec.ts',
'tests/e2e/ssh-port-forward-lifecycle.spec.ts',
'tests/e2e/ssh-reattach-home-partition.spec.ts',
'tests/e2e/ssh-reconnect-tab-destruction.spec.ts',
'tests/e2e/ssh-restart-tab-accumulation.spec.ts',
'tests/e2e/ssh-skill-installation.spec.ts',
'tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts',
'tests/e2e/ssh-startup-local-shadow.spec.ts',
'tests/e2e/ssh-terminal-window-wake-stale-grid-repro.spec.ts',
'tests/e2e/terminal-inline-images-ssh.spec.ts',
'tests/e2e/ssh-docker-watcher-isolation.spec.ts',
'tests/e2e/ssh-terminal-parking.spec.ts',
'tests/e2e/terminal-retention-budget.spec.ts',
'tests/e2e/ssh-startup-exec-readiness.spec.ts',
'tests/e2e/paired-startup-exec-readiness.spec.ts',
'tests/e2e/workspace-layout-oracle-ssh.spec.ts'
]
export const NODE_NETWORK_E2E_SPEC =
'tests/e2e/ssh-browser-network-execution-route.docker.unit.test.ts'
export const LOCALHOST_SSH_E2E_SPEC = 'tests/e2e/ssh-localhost.spec.ts'
export const NATIVE_IME_E2E_SPEC = 'tests/e2e/terminal-ibus-hangul-native.spec.ts'
// Needs the packaged orcad slot, which only its own job builds.
export const ORCAD_SERVE_MODE_SWITCH_E2E_SPEC = 'tests/e2e/orcad-serve-mode-switch.spec.ts'
// Needs the orcad template for its host's target, which only its own job builds.
export const ORCAD_AUTO_CONVERT_E2E_SPEC = 'tests/e2e/ssh-orcad-auto-convert.spec.ts'
// Windows-only; its own job runs it on a Windows runner.
export const WINDOWS_MISSING_APPDATA_E2E_SPEC = 'tests/e2e/windows-missing-appdata-startup.spec.ts'
// Needs out/orcad, which only the mode-switch job builds; it runs there beside that spec.
export const LAYOUT_ORACLE_HEADLESS_E2E_SPEC = 'tests/e2e/workspace-layout-oracle-headless.spec.ts'
// Runs in the auto-convert job, which builds the template it needs.
export const ORCAD_IDLE_EXIT_E2E_SPEC = 'tests/e2e/ssh-orcad-idle-exit.spec.ts'
export const ORCAD_BROWSER_CAPABILITIES_E2E_SPEC =
'tests/e2e/ssh-orcad-browser-capabilities.spec.ts'
export const ORCAD_BROWSER_SERVICE_STATUS_E2E_SPEC =
'tests/e2e/ssh-orcad-browser-service-status.spec.ts'
export const ORCAD_BROWSER_ROUTING_E2E_SPEC = 'tests/e2e/ssh-orcad-browser-routing.spec.ts'
export const ORCAD_EDITOR_OWNERSHIP_E2E_SPEC = 'tests/e2e/ssh-orcad-editor-ownership.spec.ts'
export const ORCAD_MARKDOWN_CONVERSION_E2E_SPEC = 'tests/e2e/ssh-orcad-markdown-conversion.spec.ts'
export const ORCAD_MARKDOWN_LINK_REFRESH_E2E_SPEC =
'tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts'
export const ORCAD_MARKDOWN_LIVE_DOCUMENTS_E2E_SPEC =
'tests/e2e/ssh-orcad-markdown-live-documents.spec.ts'
export const ORCAD_BROWSER_DROP_OWNER_E2E_SPEC = 'tests/e2e/ssh-orcad-browser-drop-owner.spec.ts'
export const ORCAD_OPEN_IN_OWNER_E2E_SPEC = 'tests/e2e/ssh-orcad-open-in-owner.spec.ts'
export const ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC =
'tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts'
export const ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC =
'tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts'
export const ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC =
'tests/e2e/ssh-orcad-explorer-selected-host.spec.ts'
export const ORCAD_TERMINAL_ROOT_OWNER_E2E_SPEC = 'tests/e2e/ssh-orcad-terminal-root-owner.spec.ts'
export const DEDICATED_E2E_SPECS = [
...DOCKER_SSH_E2E_SPECS,
NODE_NETWORK_E2E_SPEC,
LOCALHOST_SSH_E2E_SPEC,
NATIVE_IME_E2E_SPEC,
ORCAD_SERVE_MODE_SWITCH_E2E_SPEC,
LAYOUT_ORACLE_HEADLESS_E2E_SPEC,
ORCAD_AUTO_CONVERT_E2E_SPEC,
WINDOWS_MISSING_APPDATA_E2E_SPEC,
ORCAD_IDLE_EXIT_E2E_SPEC,
ORCAD_BROWSER_CAPABILITIES_E2E_SPEC,
ORCAD_BROWSER_SERVICE_STATUS_E2E_SPEC,
ORCAD_BROWSER_ROUTING_E2E_SPEC,
ORCAD_EDITOR_OWNERSHIP_E2E_SPEC,
ORCAD_MARKDOWN_CONVERSION_E2E_SPEC,
ORCAD_MARKDOWN_LINK_REFRESH_E2E_SPEC,
ORCAD_MARKDOWN_LIVE_DOCUMENTS_E2E_SPEC,
ORCAD_OPEN_IN_OWNER_E2E_SPEC,
ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC,
ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC,
ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC,
ORCAD_BROWSER_DROP_OWNER_E2E_SPEC,
ORCAD_TERMINAL_ROOT_OWNER_E2E_SPEC
]
const dedicatedSpecs = new Set(DEDICATED_E2E_SPECS)
const dockerSpecs = new Set(DOCKER_SSH_E2E_SPECS)
export function selectGeneralE2eSpecs(specs) {
return specs.filter((spec) => !dedicatedSpecs.has(spec))
}
function parseSpecs(input) {
const specs = JSON.parse(input)
if (!Array.isArray(specs) || specs.some((spec) => typeof spec !== 'string' || !spec)) {
throw new Error('Expected a JSON array of nonempty E2E spec paths')
}
return specs
}
export function classifyE2eJobs(input, sshSourceChanged = 'false') {
const conservative = { e2e_run_changed: true, e2e_needs_build: true }
let specs
try {
specs = parseSpecs(input)
} catch {
return conservative
}
// Empty evidence keeps allocations; the consumer still validates its input.
if (specs.length === 0) {
return conservative
}
const runChanged = selectGeneralE2eSpecs(specs).length > 0
return {
e2e_run_changed: runChanged,
e2e_needs_build:
runChanged ||
sshSourceChanged !== 'false' ||
specs.some(
(spec) =>
dockerSpecs.has(spec) ||
spec === LOCALHOST_SSH_E2E_SPEC ||
spec === ORCAD_SERVE_MODE_SWITCH_E2E_SPEC ||
spec === LAYOUT_ORACLE_HEADLESS_E2E_SPEC ||
spec === ORCAD_AUTO_CONVERT_E2E_SPEC ||
spec === ORCAD_IDLE_EXIT_E2E_SPEC ||
spec === ORCAD_BROWSER_CAPABILITIES_E2E_SPEC ||
spec === ORCAD_BROWSER_SERVICE_STATUS_E2E_SPEC ||
spec === ORCAD_BROWSER_ROUTING_E2E_SPEC ||
spec === ORCAD_EDITOR_OWNERSHIP_E2E_SPEC ||
spec === ORCAD_MARKDOWN_CONVERSION_E2E_SPEC ||
spec === ORCAD_MARKDOWN_LINK_REFRESH_E2E_SPEC ||
spec === ORCAD_MARKDOWN_LIVE_DOCUMENTS_E2E_SPEC ||
spec === ORCAD_OPEN_IN_OWNER_E2E_SPEC ||
spec === ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC ||
spec === ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC ||
spec === ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC ||
spec === ORCAD_BROWSER_DROP_OWNER_E2E_SPEC ||
spec === ORCAD_TERMINAL_ROOT_OWNER_E2E_SPEC
)
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
let input = ''
process.stdin.setEncoding('utf8')
for await (const chunk of process.stdin) {
input += chunk
}
if (process.argv.includes('--job-outputs')) {
for (const [name, value] of Object.entries(
classifyE2eJobs(input, process.env.E2E_SSH_SOURCE_CHANGED ?? 'false')
)) {
process.stdout.write(`${name}=${value}\n`)
}
} else {
for (const spec of selectGeneralE2eSpecs(parseSpecs(input))) {
if (/[\r\n]/.test(spec)) {
throw new Error('E2E spec paths cannot contain newlines')
}
process.stdout.write(`${spec}\n`)
}
}
}