Files
orca/config/scripts/cli-runtime-client-deferral-equivalence.mjs
T
OrcaWinandOrcaWin 7d6603aa17 [comment audit] Complete repository-wide comment cleanup (#33249)
* [comment audit] Align legacy failure-read counts with cached display

* [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci]

* [comment audit] Condense shared generation, path and preview comments [skip ci]

* [comment audit] Condense host, file and Git contract rationale [skip ci]

* [comment audit] Tighten Git lifecycle and provider contract comments [skip ci]

* [comment audit] Clarify shared host authority and launch isolation [skip ci]

* [comment audit] simplify local access mobile and native chat contracts [skip ci]

* [comment audit] simplify native chat presentation and state contracts [skip ci]

* [comment audit] simplify native runtime and provider contracts [skip ci]

* [comment audit] simplify migration and orchestration contracts [skip ci]

* [comment audit] simplify plugin trust and process capture rationale [skip ci]

* [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci]

* [comment audit] clarify remote recovery and launch wire rationale [skip ci]

* [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci]

* [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci]

* [comment audit] clarify sidebar, skill and SSH invariants [skip ci]

* [comment audit] condense structured chat delivery and timing rationale [skip ci]

* [comment audit] condense terminal replay and telemetry invariants [skip ci]

* [comment audit] clarify launch permission and terminal delivery rationale [skip ci]

* [comment audit] clarify workspace ownership and layout contracts [skip ci]

* [comment audit] wait for fitted and painted SSH terminal links

* [comment audit] clarify layout persistence and session ownership [skip ci]

* [comment audit] clarify host routing and workspace lifecycle contracts [skip ci]

* [comment audit] clarify startup persistence and app lifetime comments [skip ci]

* [comment audit] clarify recovery and activity boundaries [skip ci]

* [comment audit] condense activity and artifact invariants [skip ci]

* [comment audit] condense automation authority contracts [skip ci]

* [comment audit] simplify automation scheduling and history contracts [skip ci]

* [comment audit] clarify browser chrome and annotation lifecycles [skip ci]

* [comment audit] clarify browser routing and retained guest ownership [skip ci]

* [comment audit] clarify browser recovery and document ownership comments [skip ci]

* [comment audit] restore hosted runners required by native IME fixtures

* [comment audit] shorten preview, palette and dashboard invariants [skip ci]

* [comment audit] clarify terminal preview, dashboard and retention comments [skip ci]

* [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci]

* [comment audit] shorten rich-text and combined-diff ownership comments [skip ci]

* [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci]

* [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci]

* [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci]

* [comment audit] clarify rich Markdown source and selection safeguards [skip ci]

* [comment audit] clarify editor loading and emulator stream lifetimes [skip ci]

* [comment audit] trim emulator and feature tour narration [skip ci]

* [comment audit] clarify floating workspace and GitHub cache ownership [skip ci]

* [comment audit] trim provider controls and Linear filter explanations [skip ci]

* [comment audit] clarify pairing and chat resume lifetimes [skip ci]

* [comment audit] clarify chat delivery and transcript lifetimes [skip ci]

* [comment audit] clarify session options and host-owned status [skip ci]

* [comment audit] clarify draft durability and live-status precedence [skip ci]

* [comment audit] Trim chat send and model ownership explanations [skip ci]

* [comment audit] Shorten transcript ownership and send recovery comments [skip ci]

* [comment audit] Simplify native chat composer and transcript hook comments [skip ci]

* [comment audit] Shorten native chat scroll and structured session explanations [skip ci]

* [comment audit] Trim workspace picker and onboarding commentary [skip ci]

* [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci]

* [comment audit] Trim sidebar and session history explanations [skip ci]

* [comment audit] Condense session resume and review checks comments [skip ci]

* [comment audit] Shorten checks state and file explorer refresh comments [skip ci]

* [comment audit] Trim plugin bridge and source-control action rationale [skip ci]

* [comment audit] Trim source-control listing and review rationale [skip ci]

* [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci]

* [comment audit] Condense Settings account and appearance rationale [skip ci]

* [comment audit] Trim pairing and repository Settings explanations [skip ci]

* [comment audit] Condense Settings search SSH and terminal safeguards [skip ci]

* [comment audit] Shorten Settings voice and provider ownership comments [skip ci]

* [comment audit] Condense Settings draft persistence and host-state rationale [skip ci]

* [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci]

* [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci]

* [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci]

* [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci]

* [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci]

* [comment audit] Prune focus-read baseline after comment cleanup

* [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci]

* [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci]

* [comment audit] Clarify viewport geometry and skill update safeguards [skip ci]

* [comment audit] Tighten skill inventory, usage and host status comments [skip ci]

* [comment audit] Clarify usage ownership and resource kill safeguards [skip ci]

* [comment audit] Tighten tab selection, rename and search contracts [skip ci]

* [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci]

* [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci]

* [comment audit] Tighten provider loading and terminal recovery comments [skip ci]

* [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci]

* [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci]

* [comment audit] Tighten PTY startup and replay explanations [skip ci]

* [comment audit] Clarify PTY identity and delivery ownership [skip ci]

* [comment audit] Shorten remote reconnect and input replay rationale [skip ci]

* [comment audit] Condense terminal parking clipboard and file link comments [skip ci]

* [comment audit] Clarify IME ownership and trim trace history [skip ci]

* [comment audit] Shorten pane recovery and parked watcher explanations [skip ci]

* [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci]

* [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci]

* [comment audit] Trim renderer lifecycle and menu rationale [skip ci]

* [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci]

* [comment audit] Clarify renderer hook evidence and source ownership [skip ci]

* [comment audit] Tighten IPC ownership and reconnect explanations [skip ci]

* [comment audit] Trim renderer focus, paste and attention comments [skip ci]

* [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci]

* [comment audit] Trim agent launch, resume and ownership commentary [skip ci]

* [comment audit] Shorten account, browser and editor comments [skip ci]

* [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci]

* [comment audit] Condense launch recovery and editor tokenization comments [skip ci]

* [comment audit] Condense workspace selection and terminal pane rationale [skip ci]

* [comment audit] Condense terminal fitting and rendering recovery comments [skip ci]

* [comment audit] Condense terminal input and host routing explanations [skip ci]

* [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci]

* [comment audit] Condense structured launch and terminal link explanations [skip ci]

* [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci]

* [comment audit] Condense session persistence and workspace activation rationale [skip ci]

* [comment audit] Condense workspace routing and browser ownership rationale [skip ci]

* [comment audit] Condense mirror recovery and layout authority rationale [skip ci]

* [comment audit] Condense runtime transport and file safety rationale [skip ci]

* [comment audit] Condense runtime graph and structured session rationale [skip ci]

* [comment audit] Condense browser creation and remote tab recovery rationale [skip ci]

* [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci]

* [comment audit] Condense frame, startup and provider cache rationale [skip ci]

* [comment audit] Condense host twin and agent lifecycle rationale [skip ci]

* [comment audit] Condense browser authority and editor cleanup rationale [skip ci]

* [comment audit] Condense editor buffer identity and review cache rationale [skip ci]

* [comment audit] Condense provider lifetime and repository ownership rationale [skip ci]

* [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci]

* [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci]

* [comment audit] Condense cleanup ownership and listing admission rationale [skip ci]

* [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci]

* [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci]

* [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci]

* [comment audit] Condense cloud rollout and assignment safety rationale [skip ci]

* [comment audit] Condense relay session and database recovery rationale [skip ci]

* [comment audit] Condense relay placement and protocol compatibility rationale [skip ci]

* [comment audit] Condense build and packaging constraints [skip ci]

* [comment audit] Condense CI gates and validation isolation rationale [skip ci]

* [comment audit] Condense catalog identity and browser validation rationale [skip ci]

* [comment audit] Condense mobile browser and frame-budget rationale [skip ci]

* [comment audit] Shorten mobile route and browser test explanations [skip ci]

* [comment audit] Condense editor and session seam test rationale [skip ci]

* [comment audit] Trim session and terminal closure history [skip ci]

* [comment audit] Shorten terminal browser and census explanations [skip ci]

* [comment audit] Preserve root-only E2E diagnostic baseline

* [comment audit] Condense mobile seam and HTML preview test rationale [skip ci]

* [comment audit] Condense preview instrumentation and render harness rationale [skip ci]

* [comment audit] Condense bundle contracts and native-build constraints [skip ci]

* [comment audit] Shorten native packaging and CI routing rationale [skip ci]

* [comment audit] Condense release tooling and benchmark constraints [skip ci]

* [comment audit] Condense packaging and platform gate rationale [skip ci]

* [comment audit] Shorten mobile route and history rationale [skip ci]

* [comment audit] Condense mobile browser and component rationale [skip ci]

* [comment audit] Shorten drawer and workspace-form rationale [skip ci]

* [comment audit] Trim mobile review and editor-document explanations [skip ci]

* [comment audit] Condense editor lifecycle and Markdown explanations [skip ci]

* [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci]

* [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci]

* [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci]

* [comment audit] Condense bridge stream, native request and audio rationale [skip ci]

* [comment audit] Reduce bridge schema and page-client commentary [skip ci]

* [comment audit] Tighten mobile page bridge contracts [skip ci]

* [comment audit] Clarify mobile cache and session constraints [skip ci]

* [comment audit] Condense mobile route and lifecycle rationale [skip ci]

* [comment audit] Tighten mobile shell hook timing rationale [skip ci]

* [comment audit] Clarify route handoff and notification compatibility [skip ci]

* [comment audit] Clarify push registration and dictation cleanup constraints [skip ci]

* [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci]

* [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci]

* [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci]

* [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci]

* [comment audit] Tighten stream replay and queued session ownership rationale [skip ci]

* [comment audit] Condense mobile composer and reply-schema constraints [skip ci]

* [comment audit] Clarify mobile transcript recovery and session ownership [skip ci]

* [comment audit] Clarify mobile queue and source-control compatibility [skip ci]

* [comment audit] Condense storage and task launch constraints [skip ci]

* [comment audit] Clarify task schemas and terminal document ownership [skip ci]

* [comment audit] Tighten terminal input and viewport rationale [skip ci]

* [comment audit] Shorten terminal and RPC recorder contracts [skip ci]

* [comment audit] Condense recorder engine and connection contracts [skip ci]

* [comment audit] Shorten host credential and relay recovery comments [skip ci]

* [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci]

* [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci]

* [comment audit] Condense catalog and relay entry contracts [skip ci]

* [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci]

* [comment audit] Condense WSL relay and E2E regression rationale [skip ci]

* [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci]

* [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci]

* [comment audit] Shorten layout oracle and regression explanations [skip ci]

* [comment audit] Condense paired recovery and ownership regression contracts [skip ci]

* [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci]

* [comment audit] Condense terminal rendering and IME regression rationale [skip ci]

* [comment audit] Shorten terminal recovery and benchmark contracts [skip ci]

* [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci]

* [comment audit] Condense workflow routing and release rationale [skip ci]

* [comment audit] Condense mobile security and installer contracts [skip ci]

* [comment audit] Shorten native rationale and stylesheet workarounds [skip ci]

* [comment audit] Shorten Windows visibility probe rationale [skip ci]

* [comment audit] Condense embedded release and signing rationale [skip ci]

* [comment audit] Retain current main test behavior in final reconciliation [skip ci]

* [comment audit] Complete final comment review across current source inventory

* [comment audit] Handle consolidated audit reports before final CI

* [comment audit] Preserve credential storage census detail [skip ci]

* [comment audit] Scale broad changed E2E selections with verified timing shards

* [comment audit] Align E2E assertions with browser close and settled session metadata

* [comment audit] Match existing E2Es to current resource and windowing contracts

* [comment audit] Observe the shared server outage notice in reconnect coverage

* [comment audit] Make existing renderer probes measure settled tab and PTY work

* [comment audit] Align palette E2E selector with task search

* [comment audit] Refresh remaining palette search selectors

* [comment audit] Keep selector correction within selected audit specs

* [comment audit] Await emoji fixture settings inside renderer evaluation

* [comment audit] Cancel fixture fit retries between terminal tests

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-25 04:05:46 -07:00

449 lines
14 KiB
JavaScript

#!/usr/bin/env node
// Compare stdout/stderr/exit byte-for-byte across two real CLI emits: current tree and selected files restored from baseline (default HEAD~).
// Full-tree compilation stays outside fast Vitest invariants (runtime-client-deferral.test.ts).
// Usage: node config/scripts/cli-runtime-client-deferral-equivalence.mjs [--baseline <rev>]
import { execFileSync, spawnSync } from 'node:child_process'
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const REPO = fileURLToPath(new URL('../..', import.meta.url))
// Restore only these baseline files; remove baseline-absent files (e.g. cli-error.ts), then restore the working tree.
const TOUCHED = [
'src/cli/args.ts',
'src/cli/cli-error.ts',
'src/cli/dispatch.ts',
'src/cli/flags.ts',
'src/cli/format.ts',
'src/cli/index.ts',
'src/cli/runtime/client.ts',
'src/cli/selectors.ts'
]
// Fail if real deferral call shapes change or baseline/current could become identical and pass vacuously.
function assertMarkersFresh() {
const index = readFileSync(join(REPO, 'src/cli/index.ts'), 'utf8')
const client = readFileSync(join(REPO, 'src/cli/runtime/client.ts'), 'utf8')
const checks = [
['src/cli/index.ts', index, 'await loadRuntimeClientClass()'],
['src/cli/index.ts', index, "await import('./runtime-client.js')"],
['src/cli/index.ts', index, "import type { RuntimeClient } from './runtime-client'"],
['src/cli/runtime/client.ts', client, 'await loadSendWebSocketRequest()'],
['src/cli/runtime/client.ts', client, "await import('./websocket-transport.js')"]
]
for (const [file, source, marker] of checks) {
// Match calls so prose mentions cannot satisfy the guard.
if (!source.includes(marker)) {
throw new Error(
`${file} no longer contains \`${marker}\` — cli-runtime-client-deferral-equivalence.mjs is stale`
)
}
}
const repointed = ['src/cli/args.ts', 'src/cli/flags.ts', 'src/cli/dispatch.ts']
for (const file of repointed) {
const source = readFileSync(join(REPO, file), 'utf8')
if (!source.includes("import { RuntimeClientError } from './runtime/types'")) {
throw new Error(`${file} no longer repoints RuntimeClientError at ./runtime/types — stale`)
}
}
}
function buildTree(label, baselineRev) {
// Build inside repo so emitted modules resolve its node_modules.
const outDir = join(REPO, `.equiv-out-${label}`)
rmSync(outDir, { recursive: true, force: true })
const restored = []
try {
if (baselineRev) {
for (const file of TOUCHED) {
const path = join(REPO, file)
restored.push([path, existsSync(path) ? readFileSync(path) : null])
const old = spawnSync('git', ['show', `${baselineRev}:${file}`], {
cwd: REPO,
maxBuffer: 64 * 1024 * 1024
})
if (old.status === 0) {
writeFileSync(path, old.stdout)
} else {
rmSync(path, { force: true })
}
}
}
execFileSync(
'npx',
[
'tsc',
'-p',
'config/tsconfig.cli.json',
'--outDir',
outDir,
'--composite',
'false',
'--incremental',
'false'
],
{ cwd: REPO, stdio: 'inherit' }
)
} finally {
for (const [path, contents] of restored) {
if (contents === null) {
rmSync(path, { force: true })
} else {
writeFileSync(path, contents)
}
}
}
return join(outDir, 'cli/index.js')
}
// Every case must exit itself; bound spawnSync so reaching a blocking command fails instead of stalling forever.
const RUN_TIMEOUT_MS = 30_000
function run(entry, argv, env) {
const result = spawnSync(process.execPath, [entry, ...argv], {
cwd: REPO,
env: { ...process.env, ...env },
encoding: 'buffer',
timeout: RUN_TIMEOUT_MS,
killSignal: 'SIGKILL'
})
if (result.error) {
if (result.error.code === 'ETIMEDOUT') {
throw new Error(
`orca ${argv.join(' ')} did not exit within ${RUN_TIMEOUT_MS} ms — it reached a blocking command`
)
}
throw result.error
}
return {
status: result.status,
stdout: result.stdout.toString('utf8'),
stderr: result.stderr.toString('utf8')
}
}
// Require identical bytes; empty user-data dirs make runtime-dependent cases deterministically report not running.
function buildCases(isolatedUserData) {
const isolated = { ORCA_USER_DATA_PATH: isolatedUserData }
const cases = [
// Paths that must never load the runtime client at all.
[[], {}],
[['--help'], {}],
[['help'], {}],
[['help', 'worktree'], {}],
[['help', 'browser'], {}],
[['worktree', '--help'], {}],
[['help', 'no-such-command'], {}],
[['no-such-command'], {}],
[['no-such-command'], { ORCA_PAIRING_CODE: 'garbage' }],
[['wrktree', 'list'], {}],
[['agent-context'], {}],
[['agent-context', '--json'], {}],
// Flag validation must still fire before any runtime lookup.
[['worktree', 'list', '--nonexistent-flag'], {}],
[['worktree', 'list', '--nonexistent-flag', '--json'], {}],
[['browser', 'snapshot', '--nonexistent-flag'], {}],
// resolveRemotePairing throws from the RuntimeClient CONSTRUCTOR.
[['status', '--pairing-code', 'x', '--environment', 'y'], isolated],
[['status', '--pairing-code', 'x', '--environment', 'y', '--json'], isolated],
[['status', '--pairing-code', 'not-a-pairing-code'], isolated],
[['status', '--pairing-code', 'not-a-pairing-code', '--json'], isolated],
[['status', '--pairing-code', 'orca://pair?code=zzzz'], isolated],
[['status', '--environment', 'no-such-environment'], isolated],
[['status', '--environment', 'no-such-environment', '--json'], isolated],
[['worktree', 'list', '--environment', 'no-such-environment', '--json'], isolated],
// The env-var fallback must stay live for non-suppressed commands...
[['status', '--json'], { ...isolated, ORCA_PAIRING_CODE: 'not-a-pairing-code' }],
[['status', '--json'], { ...isolated, ORCA_REMOTE_PAIRING: 'not-a-pairing-code' }],
[['status', '--json'], { ...isolated, ORCA_ENVIRONMENT: 'no-such-environment' }],
// Keep suppression for local-only groups. Do not invoke agent hooks on/off: they modify real ~/.claude configuration outside ORCA_USER_DATA_PATH.
// Constructor-argument assertions in runtime-client-deferral.test.ts cover null/undefined suppression without those side effects.
[['environment', 'list', '--json'], { ...isolated, ORCA_ENVIRONMENT: 'no-such-environment' }],
[['environment', 'list', '--json'], { ...isolated, ORCA_PAIRING_CODE: 'not-a-pairing-code' }],
[['agent-context', '--json'], { ...isolated, ORCA_PAIRING_CODE: 'not-a-pairing-code' }],
// Runtime-unavailable reporting (RuntimeClientError formatting).
[['status'], isolated],
[['status', '--json'], isolated],
[['worktree', 'list', '--json'], isolated],
[['terminal', 'list', '--json'], isolated]
]
// Seed fuzzing for reproducibility; all pool tokens must be safe to execute, checked against UNSAFE_TOKENS before running any case.
const tokens = [
'worktree',
'list',
'status',
'browser',
'snapshot',
'terminal',
'environment',
'agent',
'agent-context',
'vm',
'--json',
'--help',
'--pairing-code',
'--environment',
'--worktree',
'orca://pair?code=!!!',
'',
'-',
'--',
'--=',
'a'.repeat(300),
'näme-ünicode',
'💥',
'../..',
'x\ty'
]
let seed = 0x9e3779b9
const next = () => {
seed ^= seed << 13
seed ^= seed >>> 17
seed ^= seed << 5
return (seed >>> 0) / 0x100000000
}
// Check entire pool, not just sampled cases: serve once survived review here and escaped the per-case scan by chance.
assertTokensSafe(tokens, 'fuzz token pool')
assertFuzzPoolDeclaredReadOnly(tokens)
for (let index = 0; index < 400; index += 1) {
const length = 1 + Math.floor(next() * 4)
const argv = []
for (let part = 0; part < length; part += 1) {
argv.push(tokens[Math.floor(next() * tokens.length)])
}
cases.push([argv, isolated])
}
for (const [argv] of cases) {
assertTokensSafe(argv, `argv ${JSON.stringify(argv)}`)
}
return cases
}
// Cases run real CLI with developer HOME/PATH: deny foreground commands, surviving children, config/registry writes and real UI input.
// Deny mixed read/write groups (capture/intercept/label/relation) wholesale; fuzzing cannot distinguish their subcommands.
const FOREGROUND_TOKENS = ['serve', 'open', 'claude-teams', 'exec', 'eval', 'launch', 'attach']
const MUTATING_TOKENS = [
// persistent config and registry state
'on',
'off',
'hooks',
'create',
'remove',
'rm',
'delete',
'add',
'edit',
'set',
'set-value',
'set-base-ref',
'setup-clone',
'setup-create',
'setup-update',
'setup-delete',
'setup-existing-folder',
'install',
'uninstall',
'reinstall',
'update',
'clone',
'apply',
'write',
'reset',
'clear',
'enable',
'disable',
'use-default',
'permissions',
// process and pane lifecycle
'run',
'run-stop',
'start',
'stop',
'kill',
'shutdown',
'close',
'split',
'switch',
'rename',
'focus',
// orchestration writes
'send',
'reply',
'dispatch',
'task-create',
'task-update',
'gate-create',
'gate-resolve',
// issue-tracker writes
'save-issue',
'comment',
'label',
'relation',
'assignee',
'priority',
'estimate',
'due-date',
// browser / computer / emulator input and navigation
'goto',
'back',
'forward',
'reload',
'click',
'dblclick',
'hover',
'fill',
'type',
'type-text',
'select',
'select-all',
'uncheck',
'keypress',
'press-key',
'inserttext',
'hotkey',
'paste-text',
'perform-secondary-action',
'drag',
'scroll',
'scrollintoview',
'wheel',
'move',
'up',
'down',
'tap',
'gesture',
'button',
'rotate',
'upload',
'download',
'dismiss',
'accept',
'highlight',
'viewport',
'geolocation',
'headers',
'credentials',
'offline',
'media',
'device',
'capture',
'intercept'
]
const UNSAFE_TOKENS = new Map([
...FOREGROUND_TOKENS.map((token) => [token, 'runs in the foreground or spawns a process']),
...MUTATING_TOKENS.map((token) => [token, 'can write outside ORCA_USER_DATA_PATH'])
])
// Deny lists miss new verbs (serve once escaped); require explicit read-only allowlisting for every pool token too.
const READ_ONLY_FUZZ_TOKENS = new Set([
// command tokens: every path they can form is a list/show or a parse error
'agent',
'agent-context',
'browser',
'environment',
'list',
'snapshot',
'status',
'terminal',
'vm',
'worktree',
// global flags and hostile strings, which reach no handler at all
'--json',
'--help',
'--pairing-code',
'--environment',
'--worktree',
'orca://pair?code=!!!',
'',
'-',
'--',
'--=',
'a'.repeat(300),
'näme-ünicode',
'💥',
'../..',
'x\ty'
])
function assertTokensSafe(tokens, context) {
for (const token of tokens) {
const reason = UNSAFE_TOKENS.get(token)
if (reason) {
throw new Error(`Refusing to run ${context}: "${token}" ${reason}`)
}
}
}
// Reject neither/both-list membership before any case runs; sampling cannot prove pool safety.
function assertFuzzPoolDeclaredReadOnly(tokens) {
for (const token of tokens) {
if (!READ_ONLY_FUZZ_TOKENS.has(token)) {
throw new Error(
`Fuzz token ${JSON.stringify(token)} is not declared in READ_ONLY_FUZZ_TOKENS — declare it read-only or drop it`
)
}
}
for (const token of READ_ONLY_FUZZ_TOKENS) {
if (UNSAFE_TOKENS.has(token)) {
throw new Error(
`Token ${JSON.stringify(token)} is declared both read-only and unsafe — the two lists disagree`
)
}
}
}
const baselineIndex = process.argv.indexOf('--baseline')
const baselineRev = baselineIndex === -1 ? 'HEAD' : process.argv[baselineIndex + 1]
assertMarkersFresh()
const isolatedUserData = mkdtempSync(join(REPO, '.equiv-userdata-'))
mkdirSync(join(isolatedUserData, 'empty'), { recursive: true })
let oldEntry
let newEntry
try {
// Build cases and check safety before expensive tsc compiles so unsafe tokens fail immediately.
const cases = buildCases(isolatedUserData)
console.log(`Building baseline (${baselineRev}) …`)
oldEntry = buildTree('old', baselineRev)
console.log('Building working tree …')
newEntry = buildTree('new', null)
console.log(`Comparing ${cases.length} invocations byte for byte …`)
let mismatches = 0
for (const [argv, env] of cases) {
const before = run(oldEntry, argv, env)
const after = run(newEntry, argv, env)
if (
before.status !== after.status ||
before.stdout !== after.stdout ||
before.stderr !== after.stderr
) {
mismatches += 1
console.error(`\nMISMATCH argv=${JSON.stringify(argv)} env=${JSON.stringify(env)}`)
console.error(` exit before=${before.status} after=${after.status}`)
if (before.stdout !== after.stdout) {
console.error(` stdout before=${JSON.stringify(before.stdout.slice(0, 400))}`)
console.error(` after =${JSON.stringify(after.stdout.slice(0, 400))}`)
}
if (before.stderr !== after.stderr) {
console.error(` stderr before=${JSON.stringify(before.stderr.slice(0, 400))}`)
console.error(` after =${JSON.stringify(after.stderr.slice(0, 400))}`)
}
}
}
if (mismatches > 0) {
throw new Error(`${mismatches} of ${cases.length} invocations differ`)
}
console.log(`\nAll ${cases.length} invocations byte-identical (stdout, stderr, exit code).`)
} finally {
rmSync(isolatedUserData, { recursive: true, force: true })
for (const label of ['old', 'new']) {
rmSync(resolve(REPO, `.equiv-out-${label}`), { recursive: true, force: true })
}
}