# Row 72 (#1566): Claude pin tests, candidate packet, round 1 Author: Rocko. Reviewer: Filbert. Brief: `docs/plans/2026-10-11_claude-pin-and-coder-grant.md`, section "Claude pin tests: a directory pin, a symlink default, a pin with a space". Base: `a0080da8` (`base.txt`). At 2026-10-11T23:03Z the checkout HEAD `ebd90554` has no change since the base to either test file, `bus-service.sh` or `adapter.sh`, and `build.patch` applies to it cleanly. Tests only: no change to `bus-service.sh`, `adapter.sh`, the launcher or the session code. The packet is uncommitted. I made no commits, pushes or Gitea calls and read no token or private binding. ## Files (`files.txt`, `candidate-manifest.sha256`) | File | Change | |---|---| | `packages/cli/tests/host.test.mjs` | +1 test: "bus-service.sh refuses a directory pin and keeps a symlink default unresolved" | | `packages/harness/tests/claude-session.test.mjs` | +1 test: "claude adapter: a directory pin exits 2 before it runs; a pin with a space runs" | | `agents/rocko/work/queue-72/mutants.sh` | the mutant rerun, so a reviewer can repeat it: `mutants.sh ` | ## What the tests check - `render --claude DIR` (a 0755 directory) exits 1 with "not an executable file" and prints nothing on stdout. - **Deviation from the brief: the default with a directory named `claude` on `PATH`.** The brief says it refuses with "not an executable file". That can't happen: bash's `command -v` never returns a directory. With only a directory named `claude` on PATH it finds nothing (rc 1); with `/usr/bin` behind it, it returns `/usr/bin/claude`. So the default goes down the "no claude on PATH" branch: exit 0, a comment, no pin. The test asserts that, and that the directory's path never reaches the unit. The code is not wrong. The hazard the brief names, a unit carrying a directory pin, can't come from the default, only from `--claude`, and the first case covers that. I checked this by hand before writing the test (`env -i`, PATH of only the directory plus the tools). - The default with a symlink named `claude` on PATH, pointing to an executable in another directory: `MOSAIC_CLAUDE_BIN` is the link's path, and the target's path appears nowhere in the unit. - Adapter, `MOSAIC_CLAUDE_BIN` a directory: exit 2 with "MOSAIC_CLAUDE_BIN is not an executable file", and the stand-in on PATH never runs. - Adapter, a stand-in at `/Claude Code/claude`: exit 0, and that stand-in is the one that ran. The host test runs bash with a PATH of only the test directory and a tools directory (node, dirname, sed, readlink), as row 68's no-claude case does, so the host's own claude can't stand in. No test spawns a real claude. ## Mutants (`out/mutants-candidate.txt`, `out/mutants-base.txt`) The reviewers' comments name the mutants but don't write them out on the checkout, so `mutants.sh` defines each from the brief's description: | Mutant | Edit | Base `a0080da8` | Candidate | |---|---|---|---| | `bs-no-f` | bus-service `claude_line`: `[ -f "$bin" ]` → `[ -e "$bin" ]` | survived | killed (host directory-pin test) | | `bs-x-only` | bus-service: only `[ -x "$bin" ]` | survived | killed (host) | | `ad-no-f` | adapter: `[ -f "$MOSAIC_CLAUDE_BIN" ]` → `[ -e … ]` | survived | killed (adapter test) | | `ad-x-only` | adapter: only `[ -x "$MOSAIC_CLAUDE_BIN" ]` | survived | killed (adapter) | | `bs-resolve-default` | bus-service: default `bin` passed through `readlink -f` | survived | killed (host symlink case) | | `ad-unquoted-run` | adapter: `"$CLAUDE" -p` → `$CLAUDE -p` | survived | killed (adapter space case) | Per mutant, the script checks that the sed changed the file, saves the diff, runs the five pin tests, then restores the file from git. Before mutating, it refuses if either file is already modified or if the unmutated run fails. `out/mutants-candidate/` and `out/mutants-base/` hold each mutant's diff and log. ## Gate (`out/gate/`, sequential, scratch worktree at a0080da8 with the patch, `node_modules` linked) | Suite | Result | |---|---| | cli `node --test 'packages/cli/tests/*.test.mjs'` | 113 pass, 0 fail | | harness `node --test 'packages/harness/tests/*.test.mjs'` | 58 pass, 0 fail, 0 skipped (claude on PATH) | | test-auth | 15 passed, 0 failed | | test-conductor | 17 passed, 0 failed | | test-config | 24 passed, 0 failed | | test-discord | 66 passed, 0 failed | | test-extension-package | 18 passed, 0 failed | | test-foundation | 44 passed, 0 failed | | test-queue | 27 passed, 0 failed | | test-release | 14 passed, 0 failed | | test-task | 98 passed, 0 failed (Docker) | After the gate I removed the `rocko-r72_default` network.