Repository navigation
test: a golden test pins what the runners start, before the runner seam moves it (part of #701) - #856
Merged
Merged
Conversation
…am moves it (#701 C3) The runner seam (#701) and the executor seam (#702 step 5) change how an agent run is started, in src/runner.ts and src/cli-agents.ts. They must not change what it is started with. Nothing pinned that as a whole: tests checked pieces (the settings check, the API key, the effort flag), each on its own stand-in. test/runner-golden.test.ts records every process the runners start for fixed requests, and compares it with values recorded on main. A record holds the argv (with --settings parsed), the whole environment, the working directory and the first line on stdin. It covers six cases: - claude as this user: cli runtime with guardrails, a lane sandbox and every request field; api_key runtime with a key file; a hosted provider; - claude as the agent OS user, through sudo, where the `env -i` list is pinned in order; - codex and gemini, both through sudo. The engine environment it starts from holds a GitHub token, an API key, a proxy and the SSH agent, so the golden values also show none of them reaching an agent. No host tool can run. The stand-ins for claude, sudo, codex and gemini sit in one directory. That directory is the test process's whole PATH and every run's. The stand-ins record and answer, and exec nothing. Test (fails without): this test passes on main by design. On scratch copies of the build outside the repo, it fails 6 of 6 with GITHUB_TOKEN added to agentEnv's pass-through list, and 4 of 6 with --strict-mcp-config dropped from claude's argv (codex and gemini are unaffected). Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com>
…se lists it from the PR title Main's v0.5.3 section is unreleased again (#850). It holds only hand-written upgrade and guardrail notes, and the generated PR lists come back at the release. A test-only change has no upgrade or guardrail note to write. Test (fails without): none; this changes CHANGELOG.md only, back to main's text before #850. Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com>
Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com>
Owner
Author
Evaluator verdict: PASSFinal review of head Host tools (checked before running)
Is the golden data pinned (not vacuous)?
Mutations (on scratch copies of
|
| Mutation | Result (measured) |
|---|---|
| Control, unmutated copy | 6 pass / 0 fail |
GITHUB_TOKEN added to PASS_THROUGH |
0 pass / 6 fail (matches the builder's claim) |
--strict-mcp-config dropped from claude argv |
2 pass / 4 fail (matches; codex and gemini pass) |
Mine: SSH_AUTH_SOCK added in runAsEnv (sudo path only) |
3 pass / 3 fail (exactly the 3 sudo cases) |
| Mine: prompt written to stdin changed by one trailing space | 2 pass / 4 fail (claude cases; the diff shows content":"do the task ") |
Other checks
- Local run on the head:
node --import ./dist/test/isolate.js --test dist/test/runner-golden.test.jsgives# pass 6,# fail 0(measured). The head's src is identical to origin/main (git diff --stat origin/main HEADlists only the test file), so this is the "passes on main by design" result (measured). - CI: all checks green. The golden test ran on Node 22 (shard 4/4,
ok 239-ok 244) and on Node 24 (shard 4/4, all three sudo cases shown passing). That covers both Node versions and CI's shard environment (measured: CI logs of run 39353640186). - Brittleness: the test passes from a different cwd (a scratch copy outside the repo). It reads nothing from the test process's environment: the base env is explicit and sudo gets
{PATH}only. Node's default per-file process isolation keepsPATH = binfrom leaking into other files (measured: control run; inferred: from the code and ci.yml line 106). - Real state: every write goes under
tmpdir(), which is the isolated test machine. The base env'sHOMEis the case dir and<PREFIX>_STATE_DIRis a fake path that is filtered out.req.stateDiris a temp dir (inferred: from the code).test/machine-path-writes.test.tsgives# pass 9with this file in the tree (measured). - Durability: no change to src/. The test adds no parsing of Claude Code output to src/ (measured: diff).
npm run typecheck: clean.npm run check:denylist: "1320 files clean" (measured).- Size delta: 0. Only
test/changed, and the package'sfilesisdist/src,dist/weband the like, notdist/test(inferred: from thefilesfield I read). - Protection: no protected path touched. Risk class "logic" is correct (measured: diff).
- Commits: all 3 are signed off and carry no attribution trailers (measured). The PR body labels its claims and has an Adversarial pass and a Package size section.
Notes (not blocking)
- Stdin is pinned only up to its first line (
STDIN_LINE). A refactor that adds extra stdin lines after the prompt would not show (inferred). This is acceptable: claude's stdin stays open by design for console messages. - A new spawn by absolute path would not be recorded. The body already names this known gap.
- The file isn't locked yet. The Runner: agent backends declare the guard guarantees they provide, and the coordinator refuses a backend that lacks one a route needs (Claude CLI first) #701 L0 slice adds
test/runner*.test.tsto the self-modification locks, as the body says.
jayden1711
added a commit
that referenced
this pull request
Oct 20, 2026
…g-hold Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com> # Conflicts: # CHANGELOG.md
jayden1711
added a commit
that referenced
this pull request
Oct 20, 2026
) Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com> #856's golden runner settings: the deny list and hooks a run's --settings must carry. The backstop asks every check-shaped table to be registered; shrinking it weakens what a run is held to: never-shrink.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent: pin exactly what the runners start before the runner seam (#701) and the executor seam (#702 step 5) move how they start it. For fixed requests, a new test records every process
CliRunnerandCliAgentRunnerstart: claude, sudo, codex and gemini. For each one it compares four things with values recorded on main: the argv (--settingsand--json-schemaparsed), the whole environment, the working directory and the first line on stdin. A refactor that changes any of them fails here. This is slice C3 of the #701 plan, and it adds only a test file.Risk class: logic. It changes
test/only. It touches nosrc/**,scripts/**,.github/**,.worklane/**or guard file (measured:git diff --stat origin/main...HEADlists onlytest/runner-golden.test.ts). It isn't locked yet. The #701 plan's L0 slice (Lane A, after #839) addstest/runner*.test.tsto the self-modification locks, so a later tests-only PR can't weaken this file at L0.Part of #701
What it pins (six cases)
cliruntime: instance guardrails, a lane sandbox, and every request field the command line or environment reads (effort, tools, system prompt file, JSON schema, resume, task file, state dir, allow-once host, project variable)api_keyruntime with a key file: the key reaches claude only asapiKeyHelperANTHROPIC_BASE_URL, its key helper, an empty config dir, no--effort, no auth probesudo -n -u … -- /usr/bin/env -i …; theenv -ilist is pinned in order; sudo itself gets{PATH}onlyThe engine environment the cases start from holds a GitHub token, an API key, an auth token, a proxy, the SSH agent, an AWS secret and an engine state-dir variable, all fake values. The golden values show none of them reaching any process (measured: the recorded environments).
Order is pinned for argv and for sudo's
env -ilist. Object key order is not pinned:--settingsJSON and a spawn's environment are both read by name (inferred).How it was verified
node --import ./dist/test/isolate.js --test dist/test/runner-golden.test.jsgives# pass 6,# fail 0. It passed on each of three runs: two before a fixture rename for the denylist, and one after (measured).dist/outside the repo (in the session scratchpad), edited one line ofdist/src/runner.jsin each, and ran the same test from each copy (measured):GITHUB_TOKENadded toagentEnv'sPASS_THROUGH:# pass 0,# fail 6. Every case fails, showing the token in the environment or in sudo'senv -ilist.--strict-mcp-configdropped fromcliArgs:# pass 2,# fail 4. The four claude cases fail; codex and gemini don't use that flag.npm ci, built, and ran it: Engine: security reads of the environment see only variables that are set, never Object.prototype's (#786) [PROTECTED: owner merges] #844 at 045d62a gives# pass 6, and Wizard: the Claude step matches the login to agent_runtime and offers an API key (#738) [PROTECTED: owner merges] #819 at a39945b gives# pass 6. So both keep their "no output change" claims for these cases.npm run typecheck: clean (measured).npm run check:denylist: "1320 files clean" (measured, after renaming two fixtures the first run flagged).npm run check:dco -- origin/main HEAD: "all commits signed off" (measured).test/machine-path-writes.test.ts(Tests: stateDir() refuses the real state dir in a test run, like slotsDir() (#806 follow-up) #815's lint over test files):# pass 9with this file in the tree (measured).test/changelog.test.ts:# pass 10(measured, before the main merge).fullsuite.sh, which excludes mcp-* and plugin-fetch) was still running at hand-back. CI runs the whole suite.How it stays off the host
claude,sudo,codex,gemini) are one node script with an absoluteprocess.execPathshebang. They record and answer, and exec nothing. sudo's stand-in reads which program it would start and answers as that program.PATH, set at the top of the file, and every run's.killTreeAscallssudowith the process's own PATH, so it also reaches only the stand-in (inferred: fromsrc/os/index.ts)./usr/binor/binby PATH./usr/bin/envand/bin/kill, which appear only as arguments to sudo (inferred: from readingasUserandkillTreeAs).test/isolate.ts: a temp HOME, state dir and system root.Adversarial pass
mkdtempsuffix after<tmp>/name-, the fresh settings check value, and the product name and env prefix.<test-cwd>, so a refactor that started the probe in the worktree would fail (inferred).BRANDwould not show here.test/brand.test.tscovers that class.duration_msin the TAP output). No timer matters: the stall and timeout caps are 60 s. It passed on all three runs (measured).allowProviderHosts, which writes into the test's temp dir (measured:test/helpers.ts:80-86).New dependencies
None.
Package size
Delta against main: 0 bytes.
npm pack --dry-run --jsonlists neithertest/runner-golden.test.tsnorCHANGELOG.md(measured: a grep of its output found neither). The package'sfilesaredist/src,dist/web, templates, examples and notices (package.json:17-30). Per file:test/runner-golden.test.tsis +361 lines, not packaged.CHANGELOG
No line. Main's v0.5.3 section went back to unreleased in #850 and now holds only hand-written upgrade and guardrail notes. The release generates the PR lists, so this PR is listed from its title. A test-only change has no upgrade or guardrail note to add. My first commit had a bullet; the second removes it, so the branch's net diff touches
test/only.