Skip to content

fix(promises): stop fixture scripts calling dirname in the Claude Code sandbox - #313

Merged
mdsmithaustin merged 7 commits into
mainfrom
fix/swarm-fixture-dirname
Oct 25, 2026
Merged

mdsmithaustin merged 7 commits into
mainfrom
fix/swarm-fixture-dirname

Conversation

@mdsmithaustin

@mdsmithaustin mdsmithaustin commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Why

In the 2026-10-24 Claude Code rerun, every swarm-run worker reported BLOCKED. The Claude Code sandbox denies reads of /usr/bin/dirname, so each fixture check.sh ran from the checkout root, found no tests, and exited 5. This PR removes the fixtures' need for dirname and reruns the case. It also applies the three verifier notes from #310 to the 2026-10-24 receipt.

Closes #309

What changed

  • The four kiln check.sh scripts and relay/tools/old_callers.sh now take their directory from $0 in the shell. Under the sandbox, old_callers.sh printed 0 instead of 3, and it now prints 3.
  • NativeFixtureScripts runs every fixture script inside and outside the sandbox and requires its known exit code, output, and unittest summary. A first version lands before the fix and fails on the old scripts. Review then made it check each script's known result. It runs only on the measured macOS host, so CI skips it.
  • A new receipt records the swarm-run rerun. The 2026-10-24 receipt gains the docs: record Claude Code promise evaluation rerun #310 notes, a pointer to the new receipt, and review corrections to its tense, the fix(promises): confine Claude native filesystem access #138 merge date, and the 2026-10-03 baseline wording. Both receipts now say that the three 2026-10-24 name-only-* relay leads ran old_callers.sh before the fix and got 0. Their PASS verdicts stand because no oracle reads that count, and those rows were not rerun.

Scope

Tradeoffs

Adding /usr/bin/dirname to the adapter's read allowlist would also fix #309. It would widen the sandbox for one tool and leave the fixtures depending on a binary that other sandboxes may deny too.

Blast Radius

Eval fixtures, one host-gated test, one README paragraph, and two eval receipts. No skill or adapter code changes. The new test runs only on the measured macOS host, like the NativeGit tests.

Verification

  • python3 -m unittest test_claude_code.NativeFixtureScripts passes on the fix. On the old scripts it fails 6 subtests (kiln exit 5 instead of 0 or 1, relay 0 instead of 3). With an unguarded cd "${0%/*}" it fails 8.
  • Live live.py run --harness claude-code --case swarm-run: workers ran check.sh and reported PASS for ingest, publish, and shape, and ISSUES for render. All three swarm promises graded PASS. The gap-reporting PASS comes from a run with no gaps, so it is weak evidence, as the receipt says.
  • On the head, ledger.py audit reports 0 problems, tools/gate-check.sh lint passes, and the promise suite under the pinned python3.14 passes 844 tests. An earlier tools/gate-check.sh test run passed every tier except the statechart node tests, which fail 18 of 146 on this host on main as well. A three-arm interrogate review led to the test and receipt corrections.

✅ Low: The change edits one line in each of five fixture scripts and adds a test on the measured host only. The test checks real behavior inside and outside the sandbox against expected results known independently, and would have failed before the fix through the absolute-path run inside the sandbox. The receipt claims I checked against git (fixture tree hash, skills/ tree identity) hold.

Updates from git push no-mistakes

@mdsmithaustin mdsmithaustin changed the title test(evals): fix fixture scripts under the Claude Code sandbox fix(promises): stop fixture scripts calling dirname in the Claude Code sandbox Oct 25, 2026
@mdsmithaustin

Copy link
Copy Markdown
Owner Author

Verdict: FAIL
Head: 166e761
Patch-id: 405cdbc7266257e9d3ababe4f576dd61a349e21c
Docs: needs changes

Root verdict from a full swarm at this head: one lane that ran things (sonnet@high) and one audit lane (sonnet@xhigh, stepped down from the opus work model). Neither took the PR body as fact. The code is clean. Only the receipt text needs changes.

Live and gate lane: PASS.

  • Lint passes. The test tier fails only the 18 statechart tests, which fail by the same names on trunk (statechart: 18 TLC-backed tests fail on macOS and never run in CI #308).
  • Under pinned Python 3.14.7, the promise suite runs 844 tests with 1 skipped. NativeFixtureScripts ran and passed. Against trunk's fixtures it fails 6 subtests.
  • I ran swarm-run live on Claude Code myself, at the head. The run is kiln-q4oxiije, 107.5 s, no timeout. The 4 sonnet workers ran check.sh. ingest, publish and shape passed. render reported ISSUES, failing test_columns_are_two_spaces_apart. No worker reported BLOCKED. All three swarm promises PASS.
  • Under the adapter's real sandbox policy, trunk's kiln scripts exit 5 with dirname: command not found, and relay prints 0. At head they exit 0 or 1 and relay prints 3, both by path and by bare name.

Audit lane:

  • The case $0 in */*) guard is correct for every way the case and the workers invoke the scripts.
  • No oracle or ledger entry depends on the old relay 0.
  • The test builds its sandbox from the adapter's own _policy, _bind_paths and child_env.
  • I checked every claim in the new receipt against .audit/swarm-309/a/kiln-xhw5yunx and found each one true.
  • All three docs: record Claude Code promise evaluation rerun #310 notes are applied in full and are true.

Findings sent back (receipt text, in scope, a reader reaches them):

  1. The relay claim needs a qualifier. evals/promises/results/2026-10-25-swarm-fixture-rerun.md:30 says "No oracle reads that count, so no recorded verdict depends on it." The saved 2026-10-24 traces show more than that. All three name-only relay leads (a/relay-ycaaaqd2, b/relay-qtf7rne_, b/relay-guy1996m under /Users/msmith1/pstack-live-runs/2026-10-24-claude-rerun/) ran old_callers.sh and got the false 0. Two of them committed "Make old_callers.sh fail loudly instead of reporting a false zero." Their PASS verdicts still stand, because the grading cites the skill load at seq 8. Say that those three rows were measured against the broken script and were not rerun, here and in the 2026-10-24 receipt's relay rows.
  2. The open skill-command denials have no tracker. 2026-10-25-swarm-fixture-rerun.md:29 says they "stay open" but names no issue. Add "Tracked in promises: skill scripts call coreutils the Claude Code sandbox blocks (log.sh, portable paths, mktemp) #316."

Note (non-blocking): line 36 says the oracle matched the reply's "Gaps: none" line. The regex \bgaps?\b also matches the "## Gaps and notes" heading. Word that claim carefully, or drop it.

@mdsmithaustin
mdsmithaustin force-pushed the fix/swarm-fixture-dirname branch from 166e761 to 33190c0 Compare October 25, 2026 21:58
@mdsmithaustin

Copy link
Copy Markdown
Owner Author

Every swarm-run worker in the 2026-10-24 Claude Code rerun reported BLOCKED, because the sandbox denies dirname and each fixture check.sh ran from the repo root and found no tests. This PR strips the file name from $0 in the shell, so the scripts reach their own folder, and adds a sandbox test that fails on the old scripts.

Before: check.sh calls dirname, the sandbox denies it, so the script runs from the repo root and finds no tests. After: check.sh strips the file name itself, reaches its package folder, and the tests run.

Not covered. Codex, Hermes, and Grok did not rerun, and the three 2026-10-24 relay rows that ran against the old old_callers.sh were not rerun. The rerun had no missing package, so reporting a real gap is still unmeasured. Skill commands such as mktemp and dirname still hit the same sandbox, tracked in #316.

Details for reviewers

Risk. Low. The diff touches only eval fixtures, one test class that runs on the measured macOS host, a README paragraph, and two eval receipts. No skill or adapter code changes.

Base bf619d69 on main, head 33190c08. 9 files, +77 -11.

Start here. kiln check.sh and NativeFixtureScripts.

Then.

  • Fixtures: evals/promises/fixtures/kiln/packages/*/check.sh, evals/promises/fixtures/relay/tools/old_callers.sh. Each cd now uses case $0 in */*) cd "${0%/*}" ;; esac, which keeps the current folder for a bare sh check.sh.
  • Receipts: 2026-10-25 swarm rerun and 2026-10-24 rerun. The 2026-10-24 receipt gains the docs: record Claude Code promise evaluation rerun #310 verifier notes and a row saying its three name-only-* relay leads got 0 from the old script, with PASS verdicts that do not read that count.
  • Docs: evals/promises/README.md names the new measured-host class.

Proof. CI on the head passes. NativeFixtureScripts runs only on the measured macOS host, so CI skips it. Run there, it fails 6 subtests on the old scripts and passes on the fix.

These pointers are a start, not the review. Read the whole diff, because a defect can sit in a file this recap ranks low.

The kiln check.sh scripts exit 5 with NO TESTS RAN under the sandbox,
and relay's old_callers.sh prints 0 instead of 3, because the read
allowlist denies /usr/bin/dirname. This test fails until the fixtures
stop calling dirname.
The Claude Code sandbox's read allowlist denies /usr/bin/dirname, so
every swarm-run worker's check.sh ran from the checkout root and
reported BLOCKED. The scripts now strip the file name from $0 in the
shell, and keep the current directory when $0 has no slash, as
dirname did.
…eipt

Add the open-pr routing promise to the 7a9a3dd baseline, say why the
2026-10-03 matrix rows stay the swarm baseline, and name the three
opening-a-pr promises bound only to deferred cases.
…ure fix

All four workers now run check.sh and report PASS or ISSUES, and the
three swarm promises grade PASS.
Review found that the parity check passed when both runs broke the same
way. The test now covers every fixture script, runs each by absolute
path and by bare name, and requires the known exit code, stdout, and
unittest summary inside and outside the sandbox.
Say that the gap-reporting PASS rests on a no-gap reply, record the
lead's mktemp, dirname, and rmdir denials as open, call the 2026-10-03
rows context rather than a baseline, and name the run, its models, and
the fixtures tree that survives the squash.
…eipts

The three 2026-10-24 name-only relay leads ran old_callers.sh against
the broken script and got 0. Their PASS verdicts stand because no oracle
reads the count, and the rows were not rerun. The open skill-command
denials point to #316, and the gap-oracle sentence names the heading
the pattern matched first.
@mdsmithaustin
mdsmithaustin force-pushed the fix/swarm-fixture-dirname branch from 33190c0 to 63ba227 Compare October 25, 2026 22:16
@mdsmithaustin

Copy link
Copy Markdown
Owner Author

Verdict: PASS+NOTES
Head: 63ba227
Patch-id: 3b5f0355c61d3c22189cec15d96d9ef21b5233df
Docs: pass

This root verdict for round 2 replaces the round-1 FAIL at 166e761a.

A single trail reviewer (sonnet@xhigh, stepped down from the opus work model) verified 33190c08 on main bf619d69. While it worked, the gate rebased the branch onto e778ccc3 (#311), and the result is this head.

The rebase. I checked it myself. git range-diff bf619d69..33190c08 e778ccc3..63ba2276 shows six of the seven commits unchanged (=). The seventh, 50cf8652, differs in one evals/promises/README.md line that #311 also edited, and the resolution keeps both changes. That line keeps #311's ripgrep-signature clause and its codex-skip sentences, and adds this PR's NativeFixtureScripts sentences with "None of these classes". git diff 33190c08 63ba2276 -- evals/promises/results evals/promises/fixtures is empty. The reviewer reran lint and the promise suite at 63ba2276: lint exits 0, and the suite runs 848 tests OK with 1 skipped. NativeFixtureScripts passed.

Round-1 findings, checked against the saved 2026-10-24 traces:

  1. The relay qualification is true. relay-ycaaaqd2, relay-qtf7rne_, and relay-guy1996m each ran old_callers.sh and got dirname: command not found, then 0. Two of them committed "fail loudly" (5900858, ddc3356). The two routed-skill verdicts cite the seq-8 load. The unnamed-request verdict shows "lead loads: []". No oracle reads the count.
  2. "Tracked in promises: skill scripts call coreutils the Claude Code sandbox blocks (log.sh, portable paths, mktemp) #316." is present, and promises: skill scripts call coreutils the Claude Code sandbox blocks (log.sh, portable paths, mktemp) #316 covers those items.

The gaps sentence now names the heading match at offset 1079 that comes before the "Gaps: none" bullet at 1099.

Notes (non-blocking):

@mdsmithaustin
mdsmithaustin merged commit b141987 into main Oct 25, 2026
7 checks passed
@mdsmithaustin
mdsmithaustin deleted the fix/swarm-fixture-dirname branch October 25, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

promises: swarm-run fixture check.sh fails with no dirname in the Claude Code sandbox

1 participant