Merge origin/main into the SSH host-key + reconnect branch

One conflict, and it was structural rather than textual: main split
ssh-connection.test.ts from 2904 lines into seventeen files with a shared harness
and fixtures (#14728), taking it off the max-lines suppression list, while this
branch had added host key verification tests to the old monolith.

Resolved by adopting main's split rather than reinstating the big file, since
restoring it would put back the suppression the refactor just removed. The seven
host key tests move to ssh-connection-host-key-verification.test.ts unchanged,
including their comments — they are the reasoning, not decoration.

The load-bearing piece was the ssh2 mock, which now lives in the shared harness
and is therefore used by all seventeen files. It has to keep the callback form:
ssh2 calls hostVerifier(key, verify) and only accepts synchronously when the
return is not undefined, so the old single-argument mock would pass against a
verifier that never decides — exactly the regression this work exists to prevent.
It also has to abort on denial, or a refused key looks like a successful connect.
Defaulting the presented key to a valid ed25519 blob keeps the other sixteen
files unaffected, which the full run confirms: 1549 passed, 13 skipped, zero
failures, typecheck clean.

The one remaining caller of the old signature — the obsolete-generation
fingerprint test — moved to real key blobs, because the verifier now identifies a
key before recording a fingerprint and a placeholder string would be refused
before reaching the generation check that test covers.
This commit is contained in:
Neil
2026-08-15 21:17:46 -07:00
2413 changed files with 437534 additions and 354294 deletions
+228
View File
@@ -0,0 +1,228 @@
import { appendFileSync, readFileSync } from 'node:fs'
import { pathToFileURL } from 'node:url'
import { mergeLocBlock, renderLocBlock, sumChangedFiles } from './pr-test-loc-table.mjs'
export const PR_FILES_PAGE_LIMIT = 3000
export function nextLink(linkHeader) {
if (linkHeader == null || linkHeader.length === 0) {
return undefined
}
for (const part of linkHeader.split(',')) {
const match = part.match(/<([^>]+)>\s*;\s*rel="next"/)
if (match != null) {
return match[1]
}
}
return undefined
}
function githubHeaders(token) {
return {
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${token}`,
'User-Agent': 'orca-pr-test-loc',
'X-GitHub-Api-Version': '2022-11-28'
}
}
export async function listPullFiles({ owner, repo, pullNumber, token, fetchImpl = fetch }) {
const files = []
let url = `https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/files?per_page=100`
while (url != null) {
const response = await fetchImpl(url, { headers: githubHeaders(token) })
if (!response.ok) {
throw new Error(
`Failed to list PR #${pullNumber} files: ${response.status} ${response.statusText}`
)
}
const page = await response.json()
if (!Array.isArray(page)) {
throw new Error(`Unexpected PR files payload for #${pullNumber}`)
}
files.push(...page)
if (files.length >= PR_FILES_PAGE_LIMIT) {
console.log(
`PR #${pullNumber} file list hit GitHub's ${PR_FILES_PAGE_LIMIT}-file cap; totals may be short.`
)
return files.slice(0, PR_FILES_PAGE_LIMIT)
}
url = nextLink(response.headers.get('link'))
}
return files
}
function writeGithubOutput(totals) {
const block = `${renderLocBlock(totals)}\n`
const outputPath = process.env.GITHUB_OUTPUT
if (outputPath != null) {
appendFileSync(outputPath, `summary<<ORCA_PR_LOC_EOF\n${block}ORCA_PR_LOC_EOF\n`)
}
const summaryPath = process.env.GITHUB_STEP_SUMMARY
if (summaryPath != null) {
appendFileSync(summaryPath, `${block}\n`)
}
}
async function updatePullRequest({ owner, repo, pullNumber, token, totals, fetchImpl = fetch }) {
const headers = githubHeaders(token)
const url = `https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}`
const response = await fetchImpl(url, { headers })
if (response.status === 403) {
console.log('Skipping PR body update: token cannot write (likely a fork PR).')
return 0
}
if (!response.ok) {
throw new Error(`Failed to read PR #${pullNumber}: ${response.status} ${response.statusText}`)
}
const pull = await response.json()
const nextBody = mergeLocBlock(pull.body, totals)
if (nextBody === (pull.body ?? '')) {
console.log(`PR #${pullNumber} LoC header already current.`)
return 0
}
const update = await fetchImpl(url, {
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ body: nextBody })
})
if (update.status === 403) {
console.log('Skipping PR body update: token cannot write (likely a fork PR).')
return 0
}
if (!update.ok) {
throw new Error(`Failed to update PR #${pullNumber}: ${update.status} ${update.statusText}`)
}
console.log(`Updated LoC header on PR #${pullNumber}.`)
return 0
}
function resolveRepository() {
const repository = process.env.GITHUB_REPOSITORY
if (repository == null || !repository.includes('/')) {
return undefined
}
const slash = repository.indexOf('/')
return { owner: repository.slice(0, slash), repo: repository.slice(slash + 1) }
}
function resolveToken() {
return process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN
}
function printUsage() {
console.error(
`Usage: ${process.argv[1]} --from-pr <number> | --update-pr <number> [--files-json <file>] [--merge-body <file>]`
)
}
async function main(argv) {
let filesJsonPath
let mergeBodyPath
let fromPrNumber
let updatePrNumber
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i]
if (arg === '--files-json') {
filesJsonPath = argv[i + 1]
i += 1
continue
}
if (arg === '--merge-body') {
mergeBodyPath = argv[i + 1]
i += 1
continue
}
if (arg === '--from-pr') {
fromPrNumber = argv[i + 1]
i += 1
continue
}
if (arg === '--update-pr') {
updatePrNumber = argv[i + 1]
i += 1
continue
}
printUsage()
return 2
}
const pullNumber = updatePrNumber ?? fromPrNumber
if (
(argv.includes('--files-json') && filesJsonPath == null) ||
(argv.includes('--merge-body') && mergeBodyPath == null) ||
(argv.includes('--from-pr') && fromPrNumber == null) ||
(argv.includes('--update-pr') && updatePrNumber == null) ||
(filesJsonPath == null && pullNumber == null)
) {
printUsage()
return 2
}
let files
if (filesJsonPath != null) {
files = JSON.parse(readFileSync(filesJsonPath, 'utf8'))
} else {
const repository = resolveRepository()
const token = resolveToken()
if (repository == null || token == null) {
console.error('GITHUB_REPOSITORY and GITHUB_TOKEN are required to read a pull request.')
return 2
}
files = await listPullFiles({
...repository,
pullNumber: Number(pullNumber),
token
})
}
const totals = sumChangedFiles(files)
writeGithubOutput(totals)
if (mergeBodyPath != null) {
process.stdout.write(mergeLocBlock(readFileSync(mergeBodyPath, 'utf8'), totals))
return 0
}
console.log(renderLocBlock(totals))
if (updatePrNumber == null) {
return 0
}
const repository = resolveRepository()
const token = resolveToken()
if (repository == null || token == null) {
console.error('GITHUB_REPOSITORY and GITHUB_TOKEN are required with --update-pr.')
return 2
}
return updatePullRequest({
...repository,
pullNumber: Number(updatePrNumber),
token,
totals
})
}
if (process.argv[1] != null && import.meta.url === pathToFileURL(process.argv[1]).href) {
main(process.argv.slice(2))
.then((code) => {
process.exitCode = code
})
.catch((error) => {
console.error(error instanceof Error ? error.message : error)
process.exitCode = 1
})
}
+86
View File
@@ -0,0 +1,86 @@
export const LOC_BLOCK_START = '<!-- orca-pr-loc -->'
export const LOC_BLOCK_END = '<!-- /orca-pr-loc -->'
export const LOC_HANDS_OFF_COMMENT =
'<!-- Programmatic LoC summary. Do not edit by hand; rewritten on every commit. -->'
const TEST_DIR_SEGMENT = /(?:^|\/)(?:__tests__|e2e|tests)(?:\/|$)/i
const TEST_FILENAME = /\.(?:test|spec|e2e)\.[^/]+$/i
export function isTestPath(path) {
const normalized = path.replaceAll('\\', '/')
return TEST_DIR_SEGMENT.test(normalized) || TEST_FILENAME.test(normalized)
}
export function emptyLocTotals() {
return {
test: { files: 0, added: 0, deleted: 0 },
nonTest: { files: 0, added: 0, deleted: 0 }
}
}
export function sumChangedFiles(files) {
const totals = emptyLocTotals()
for (const file of files) {
const path = file.filename
if (path == null) {
continue
}
const bucket = isTestPath(path) ? totals.test : totals.nonTest
bucket.files += 1
bucket.added += Number(file.additions ?? 0)
bucket.deleted += Number(file.deletions ?? 0)
}
return totals
}
function signed(count) {
if (count === 0) {
return '0'
}
return count > 0 ? `+${count}` : `−${Math.abs(count)}`
}
function locTableRow(label, bucket) {
return `| ${label} | ${bucket.files ?? 0} | ${signed(bucket.added)} | ${signed(-(bucket.deleted ?? 0))} | ${signed((bucket.added ?? 0) - (bucket.deleted ?? 0))} |`
}
export function formatLocTable({ test, nonTest }) {
return [
'| | Files | Added | Deleted | Net |',
'| :--- | ---: | ---: | ---: | ---: |',
locTableRow('Test', test),
locTableRow('Prod', nonTest)
].join('\n')
}
export function renderLocBlock(totals) {
return [
LOC_BLOCK_START,
LOC_HANDS_OFF_COMMENT,
'',
formatLocTable(totals),
'',
LOC_BLOCK_END
].join('\n')
}
export function mergeLocBlock(body, totals) {
const block = renderLocBlock(totals)
const current = body ?? ''
const start = current.indexOf(LOC_BLOCK_START)
const end = current.indexOf(LOC_BLOCK_END)
if (start !== -1 && end !== -1 && end > start) {
const rest = current.slice(end + LOC_BLOCK_END.length).replace(/^\r?\n/, '')
if (rest.trim().length === 0) {
return `${current.slice(0, start)}${block}\n`
}
return `${current.slice(0, start)}${block}\n\n${rest.replace(/^\r?\n+/, '')}`
}
if (current.trim().length === 0) {
return `${block}\n`
}
return `${block}\n\n${current.replace(/^\r?\n+/, '')}`
}
+4
View File
@@ -29,6 +29,9 @@ on:
- 'electron.vite.config.ts'
- 'config/build-plugins/**'
- 'src/main/daemon/**'
- 'src/main/providers/local-pty-provider.ts'
- 'src/main/providers/windows-shell-args.ts'
- 'src/main/providers/windows-shell-preflight-runtime.windows.test.ts'
- 'native/computer-use-macos/**'
- 'native/computer-use-linux/**'
- 'native/computer-use-windows/**'
@@ -106,6 +109,7 @@ jobs:
src/main/computer/macos-native-provider-socket.test.ts
src/main/computer/macos-computer-use-permissions.test.ts
src/main/computer/macos-computer-use-permission-status.test.ts
src/main/providers/windows-shell-preflight-runtime.windows.test.ts
src/main/computer/desktop-script-provider-client.test.ts
src/main/computer/desktop-script-provider-cache.test.ts
src/main/computer/desktop-script-provider-actions.test.ts
+36
View File
@@ -0,0 +1,36 @@
name: PR test LoC
on:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
concurrency:
group: pr-test-loc-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
loc:
name: test vs non-test LoC
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
# Why no checkout: the Files API already has per-file additions/deletions.
- name: Count test vs non-test LoC
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
for script in pr-test-loc-table.mjs pr-test-loc-summary.mjs; do
gh api "repos/${GITHUB_REPOSITORY}/contents/.github/scripts/${script}?ref=pull/${{ github.event.pull_request.number }}/head" \
--jq .content | base64 --decode > "$RUNNER_TEMP/${script}"
done
node "$RUNNER_TEMP/pr-test-loc-summary.mjs" --update-pr "${{ github.event.pull_request.number }}"
+32 -2
View File
@@ -50,6 +50,27 @@ jobs:
- name: Check reliability gate manifest
run: pnpm run check:reliability-gates
- name: Check VM runtime rollback compatibility
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if git diff --quiet --merge-base "$BASE_SHA" "$HEAD_SHA" -- \
src/shared/ephemeral-vm-runtime-store.ts \
src/shared/ephemeral-vm-runtime-feature-store.ts \
src/shared/ephemeral-vm-runtime-rollback-projection.ts \
src/shared/ephemeral-vm-runtimes.ts \
src/shared/ephemeral-vm-recipes.ts \
src/shared/orca-yaml-hook-types.ts \
src/main/ephemeral-vm-runtime-service.ts \
src/main/ephemeral-vm-runtime-provisioning-persistence.ts \
src/main/ephemeral-vm-failed-start-cleanup.ts; then
echo "VM runtime persistence is unchanged."
exit 0
fi
node config/scripts/run-ephemeral-vm-runtime-store-rollback-repro.mjs \
config/scripts/ephemeral-vm-runtime-store-cross-version.test.ts
- name: Enforce max-lines ratchet
run: pnpm run check:max-lines-ratchet
@@ -243,7 +264,10 @@ jobs:
src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts \
src/main/daemon/shell-ready.test.ts \
src/main/daemon/node-pty-fd-leak.test.ts \
src/main/providers/local-pty-shell-ready.test.ts \
src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts \
src/main/providers/local-pty-shell-ready-zsh-startup-file-behavior.test.ts \
src/main/providers/local-pty-shell-ready-zsh-zdotdir-discovery.test.ts \
src/main/providers/local-pty-shell-ready-zsh-zdotdir-normalization.test.ts \
src/main/providers/__tests__/shell-ready-framework-example.test.ts \
src/main/pty/omp-shell-wrapper.node-pty.test.ts \
src/renderer/src/components/terminal-pane/fish-color-scheme-child-stdin.node-pty.test.ts \
@@ -279,7 +303,10 @@ jobs:
--exclude=src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts \
--exclude=src/main/daemon/shell-ready.test.ts \
--exclude=src/main/daemon/node-pty-fd-leak.test.ts \
--exclude=src/main/providers/local-pty-shell-ready.test.ts \
--exclude=src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts \
--exclude=src/main/providers/local-pty-shell-ready-zsh-startup-file-behavior.test.ts \
--exclude=src/main/providers/local-pty-shell-ready-zsh-zdotdir-discovery.test.ts \
--exclude=src/main/providers/local-pty-shell-ready-zsh-zdotdir-normalization.test.ts \
--exclude=src/main/providers/__tests__/shell-ready-framework-example.test.ts \
--exclude=src/main/pty/omp-shell-wrapper.node-pty.test.ts \
--exclude=src/renderer/src/components/terminal-pane/fish-color-scheme-child-stdin.node-pty.test.ts \
@@ -479,6 +506,9 @@ jobs:
HEAD="${{ github.event.pull_request.head.sha }}"
CHANGED="$(git diff --name-only --diff-filter=AMCR --merge-base "$BASE" "$HEAD")"
TEST_FILES="$(printf '%s\n' "$CHANGED" | grep -E '^tests/e2e/.*\.spec\.ts$' || true)"
if printf '%s\n' "$CHANGED" | grep -Eq '^src/(main/ephemeral-vm-(runtime-(service|provisioning-persistence)|failed-start-cleanup)|shared/(ephemeral-vm-runtime-(store|feature-store|rollback-projection|runtimes)|ephemeral-vm-recipes|orca-yaml-hook-types))\.ts$'; then
TEST_FILES="$(printf '%s\n%s\n' "$TEST_FILES" 'tests/e2e/ephemeral-vm-provisioned-root.spec.ts' | sort -u)"
fi
TEST_FILES_JSON="$(printf '%s\n' "$TEST_FILES" | jq --raw-input --slurp --compact-output 'split("\n") | map(select(length > 0))')"
echo "test_files=$TEST_FILES_JSON" >> "$GITHUB_OUTPUT"
if [ "$TEST_FILES_JSON" != '[]' ]; then