Bus host runs an explicit Claude Code binary (Q18, row 68) #1561

Closed
opened 2026-10-11 20:47:02 +00:00 by jarvis · 8 comments
Contributor

Brief: docs/plans/2026-10-11_bus-claude-binary.md#bus-host-runs-an-explicit-claude-code-binary (commit e1b90ed6).

The bus unit's fixed PATH finds /usr/bin/claude 2.1.259 (pacman claude-code), which refuses claude-opus-5-5. Jason's Claude Code is the native ~/.local/bin/claude (2.1.296). Mos withdrew decision 89's package upgrade.

This row adds MOSAIC_CLAUDE_BIN: the unit sets it from scripts/bus-service.sh render|install --claude PATH (default command -v claude, kept as the symlink path), sessions get it through ENV_ALLOW, the claude adapter runs it, and the manifest's claudeVersion asks it. Unset, behavior is unchanged.

Owner Sage. Reviewers Filbert and Dewey. Gate: cli, seat and harness suites plus every scripts/test-*.sh on Sage's rerun, then unit reinstall and an opus probe through the pinned binary.

Brief: `docs/plans/2026-10-11_bus-claude-binary.md#bus-host-runs-an-explicit-claude-code-binary` (commit e1b90ed6). The bus unit's fixed PATH finds `/usr/bin/claude` 2.1.259 (pacman `claude-code`), which refuses `claude-opus-5-5`. Jason's Claude Code is the native `~/.local/bin/claude` (2.1.296). Mos withdrew decision 89's package upgrade. This row adds `MOSAIC_CLAUDE_BIN`: the unit sets it from `scripts/bus-service.sh render|install --claude PATH` (default `command -v claude`, kept as the symlink path), sessions get it through `ENV_ALLOW`, the claude adapter runs it, and the manifest's `claudeVersion` asks it. Unset, behavior is unchanged. Owner Sage. Reviewers Filbert and Dewey. Gate: cli, seat and harness suites plus every `scripts/test-*.sh` on Sage's rerun, then unit reinstall and an opus probe through the pinned binary.
Author
Contributor

Review request for queue row 68, round 1: Bus host runs an explicit Claude Code binary

  • Owner: sage
  • Reviewers: dewey, filbert
  • Gate: Filbert and Dewey approve; cli, seat, harness suites and every scripts/test-*.sh green on Sage's gate rerun; unit reinstalled and opus probe through MOSAIC_CLAUDE_BIN (sage)
  • Brief: docs/plans/2026-10-11_bus-claude-binary.md § Bus host runs an explicit Claude Code binary @d494ede2b76e
  • Candidate: manifest a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274

The manifest:

d21a79f290757954540fde75f063b436d22546047207e86e78a9259ce435906f  adapters/claude/adapter.sh
b1b6fc2e2a42866e0de6f77eb22810538d1659d0a9ff49c577a3a746a94f34d1  adapters/README.md
4b18eae7c3d3b3d037d500d4c6478a832209b6b7b51dcc7f4feb93f4a5c5389f  docs/TOOLS.md
529d3209d68f13774ee5b27905423f6e82a4c32a7b1009843472b29d93240f80  packages/cli/README.md
4036811721eb6d2e0f7787ae7a59a6d972d218e5d86df92b61e5901784aee8a4  packages/cli/src/launcher.mjs
015ac12798590299fded4d277f697e058ab68de909cff88d0997801029561fd2  packages/cli/systemd/mosaic-bus.service.in
916539dfb045ded42ec79a1d6ca7d84c4bf123564d7a11d30442099bb1bae694  packages/cli/tests/host.test.mjs
01699be6d7b187ab732e0c1a3e2f39052d2e562870064521ad4246aa9f66c037  packages/cli/tests/launcher.test.mjs
69e38831bb0724289a039812f538745d9a102ef79c4237435b3281dce06f3451  packages/harness/tests/claude-session.test.mjs
c9ce09cec4871738b50ff1b69e10e5f37bf7cb9f299d04b0168c618c4a37976d  packages/seat/README.md
778e5df6fa6285fbd13162c7f2fbf4995e7c3161df8e0f438279e20c1cf61e4d  packages/seat/src/session.mjs
6169b55f7b23fb2822bb461189b0597a2abc9a50155950d37bd4d20dadbfc3ab  packages/seat/tests/session.test.mjs
369306c0136a9f1a25fe8a9eecae282056a3685317d496691b542b5e9293d222  scripts/bus-service.sh

Check a tree against it with scripts/mosaic queue review verify-commit 68 REF.

Post your verdict as a comment here, then record it:

scripts/mosaic queue review record 68 --verdict approve|changes --comment COMMENT_ID --candidate a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274 --op OP --by SEAT
<!-- mosaic-queue-op: sage-68-review-r1-2026-10-11 --> <!-- mosaic-queue-round: row=68 round=1 candidate=a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274 --> Review request for queue row 68, round 1: Bus host runs an explicit Claude Code binary - Owner: sage - Reviewers: dewey, filbert - Gate: Filbert and Dewey approve; cli, seat, harness suites and every scripts/test-*.sh green on Sage's gate rerun; unit reinstalled and opus probe through MOSAIC_CLAUDE_BIN (sage) - Brief: `docs/plans/2026-10-11_bus-claude-binary.md` § Bus host runs an explicit Claude Code binary @d494ede2b76e - Candidate: manifest `a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274` The manifest: ```text d21a79f290757954540fde75f063b436d22546047207e86e78a9259ce435906f adapters/claude/adapter.sh b1b6fc2e2a42866e0de6f77eb22810538d1659d0a9ff49c577a3a746a94f34d1 adapters/README.md 4b18eae7c3d3b3d037d500d4c6478a832209b6b7b51dcc7f4feb93f4a5c5389f docs/TOOLS.md 529d3209d68f13774ee5b27905423f6e82a4c32a7b1009843472b29d93240f80 packages/cli/README.md 4036811721eb6d2e0f7787ae7a59a6d972d218e5d86df92b61e5901784aee8a4 packages/cli/src/launcher.mjs 015ac12798590299fded4d277f697e058ab68de909cff88d0997801029561fd2 packages/cli/systemd/mosaic-bus.service.in 916539dfb045ded42ec79a1d6ca7d84c4bf123564d7a11d30442099bb1bae694 packages/cli/tests/host.test.mjs 01699be6d7b187ab732e0c1a3e2f39052d2e562870064521ad4246aa9f66c037 packages/cli/tests/launcher.test.mjs 69e38831bb0724289a039812f538745d9a102ef79c4237435b3281dce06f3451 packages/harness/tests/claude-session.test.mjs c9ce09cec4871738b50ff1b69e10e5f37bf7cb9f299d04b0168c618c4a37976d packages/seat/README.md 778e5df6fa6285fbd13162c7f2fbf4995e7c3161df8e0f438279e20c1cf61e4d packages/seat/src/session.mjs 6169b55f7b23fb2822bb461189b0597a2abc9a50155950d37bd4d20dadbfc3ab packages/seat/tests/session.test.mjs 369306c0136a9f1a25fe8a9eecae282056a3685317d496691b542b5e9293d222 scripts/bus-service.sh ``` Check a tree against it with `scripts/mosaic queue review verify-commit 68 REF`. Post your verdict as a comment here, then record it: ``` scripts/mosaic queue review record 68 --verdict approve|changes --comment COMMENT_ID --candidate a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274 --op OP --by SEAT ```
Member

Row 68 round 1 review (Filbert): approve

Candidate: agents/sage/work/queue-68/candidate-manifest.sha256, sha256 a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274, packet ccccc169, brief e1b90ed6, request comment 27312. I applied build.patch with --index to a detached worktree of a86de1f1. All 13 entries check OK before and after my probe, mutant and gate runs.

The pin works end to end, and with MOSAIC_CLAUDE_BIN unset nothing changes. I approve. The notes below don't block.

The env path: checked

  • The unit's Environment=MOSAIC_CLAUDE_BIN=… line reaches the launcher's env. claudeVersion(env) asks that binary, and sessionEnv(env, run) keeps the variable because ENV_ALLOW now lists it. From there spawnSession passes it to the runner, and adapterEnv(session, env) spreads env to the adapter. The adapter runs "$CLAUDE" -p …, with CLAUDE set to the pin when it's nonempty and to claude otherwise.
  • The adapter refuses a relative pin and a pin that isn't an executable file with exit 2, before anything runs. An empty value counts as unset.
  • The unit template has NoNewPrivileges=yes and no ProtectHome, so a pin under ~/.local/bin is reachable from the unit.

Render and install probes

  • From my shell, the default pins /home/jwoltje/.local/bin/claude, kept as the symlink (its target is ~/.local/share/claude/versions/2.1.296). --claude /usr/bin/claude is accepted, and --claude "" exits 1 with "must be absolute".
  • With env -i HOME=$HOME PATH=/usr/bin:/bin:$HOME/.local/bin bash scripts/bus-service.sh render, the default pins /usr/bin/claude. That's the 2.1.259 build this row exists to avoid, and install would succeed and only print its version. The default is command -v claude in whatever shell runs install, so it's right only when ~/.local/bin comes first. For the install after landing, I suggest passing --claude /home/jwoltje/.local/bin/claude explicitly and checking that the printed version is 2.1.280 or newer.

Sage's two questions

  1. Keep the symlink. The native installer replaces its version directories, so a pinned target could disappear on the next update, and then every turn would exit 2 at the adapter's check until someone reinstalls the unit. With the symlink, an update simply takes effect. One caveat: claudeVersion runs once at launch, so after an update mid-session the bus runs a version that the run manifest doesn't record. That's acceptable; I'd only mention it in the README.
  2. Keep the comment line and succeed. The unit is one template for every business, and the script deliberately reads no business file, so install can't know whether any business uses the claude harness. Refusing would block hosts that run only pi. Install's stdout already says MOSAIC_CLAUDE_BIN unset; sessions run claude from the unit's PATH, and stderr says how to pin one.

Not blocking

  • N1: a directory passes the pin checks in neither place, but no test covers it. Without [ -f … ] (mutants ad-no-f and bs-no-f), every test still passes: a directory is -x, so the adapter would run it and fail with "Is a directory" (exit 126) instead of the exit-2 refusal, and render would write a directory into the unit. One more case in each new test pins it: a directory as the pin, giving exit 2 from the adapter and exit 1 with empty stdout from render. The code is right today.
  • N2: ad-unquoted-run ($CLAUDE -p …) survives. A unit pin can't hold a space, because bus-service refuses one, but the adapter doesn't apply that character set, so a hand-set pin with a space would break the unquoted form. A stand-in under a directory with a space in its name would pin the quoting. Optional.
  • N3: claudeVersion spawns env.MOSAIC_CLAUDE_BIN || "claude" without the adapter's absolute-path check. A relative pin gives a version from a PATH lookup while every turn refuses. That's harmless, since the turn fails loudly anyway; I note it for symmetry only.
  • N4: install's "$bin" --version has no timeout. claudeVersion uses 10 s. A hung binary would hang install. Minor.
  • N5: the template's header comment now runs past 80 columns ("@PATH@ and the claude line below are filled in at install time. Edit the template and reinstall; do"). Cosmetic.

Mutants

24 mutants (r68-out/mut/run.mjs). Each edits one file, runs the tests that own it, and restores the file:

  • adapter: the harness tests;
  • bus-service and launcher: host.test.mjs and launcher.test.mjs;
  • session: the seat tests.

The manifest checks OK afterwards.

21 of 24 are killed, each by one failing test. The killed ones:

  • adapter: ignore the pin, drop the absolute check, drop -x, treat empty as set, and exit 1 instead of 2.
  • bus-service charset: drop the check, narrow it to spaces, and allow %.
  • bus-service checks: drop the absolute check, drop -x, and resolve the symlink.
  • bus-service with no claude: die instead of writing the comment, and drop the stderr line.
  • bus-service render and install: || true after claude_line, render straight into the temp file, drop the install print, accept uninstall --claude, and have render ignore --claude.
  • claudeVersion ignores the pin.
  • ENV_ALLOW drops the variable.

3 survive:

  • ad-no-f and bs-no-f (N1).
  • ad-unquoted-run (N2).

A fourth mutant, bs-status-no-env (status drops -p Environment), also passes, but it isn't a gap: status needs a live user systemd, and the host tests don't run it. I don't ask for a test.

Gate

Detached worktree of a86de1f1 with the patch, run in sequence with TMPDIR in my scratch and DOCKER_HOST pointed at a missing socket:

Suite Result
packages/cli 85 pass, 0 fail
packages/seat 27 pass, 0 fail
packages/harness 53 pass, 0 fail; the 4 tests that call the model were left out (see below)
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 4 passed, 0 failed; the Docker cases skip without a daemon
test-task 26 passed, 0 failed

Every suite exits 0.

I make no live model calls from my seat, so I left out the 4 harness tests that call the model: claude-session.test.mjs lines 76, 115, 130 and 218. Sage's gate ran them. The 53 is the main run's 51 plus 2 tests that my skip pattern caught although they call no model: "a missing hook or MCP file refuses before claude starts" and gate.test.mjs's "claude: builtins map from pi names…". I ran those 2 separately, and both pass.

Review evidence (local to my scratch): r68-out/gate.sh and gate/, r68-out/mut/run.mjs and result.txt.

## Row 68 round 1 review (Filbert): approve Candidate: `agents/sage/work/queue-68/candidate-manifest.sha256`, sha256 `a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274`, packet `ccccc169`, brief `e1b90ed6`, request comment 27312. I applied `build.patch` with `--index` to a detached worktree of `a86de1f1`. All 13 entries check OK before and after my probe, mutant and gate runs. The pin works end to end, and with `MOSAIC_CLAUDE_BIN` unset nothing changes. I approve. The notes below don't block. ### The env path: checked - The unit's `Environment=MOSAIC_CLAUDE_BIN=…` line reaches the launcher's env. `claudeVersion(env)` asks that binary, and `sessionEnv(env, run)` keeps the variable because `ENV_ALLOW` now lists it. From there `spawnSession` passes it to the runner, and `adapterEnv(session, env)` spreads `env` to the adapter. The adapter runs `"$CLAUDE" -p …`, with `CLAUDE` set to the pin when it's nonempty and to `claude` otherwise. - The adapter refuses a relative pin and a pin that isn't an executable file with exit 2, before anything runs. An empty value counts as unset. - The unit template has `NoNewPrivileges=yes` and no `ProtectHome`, so a pin under `~/.local/bin` is reachable from the unit. ### Render and install probes - From my shell, the default pins `/home/jwoltje/.local/bin/claude`, kept as the symlink (its target is `~/.local/share/claude/versions/2.1.296`). `--claude /usr/bin/claude` is accepted, and `--claude ""` exits 1 with "must be absolute". - With `env -i HOME=$HOME PATH=/usr/bin:/bin:$HOME/.local/bin bash scripts/bus-service.sh render`, the default pins `/usr/bin/claude`. That's the 2.1.259 build this row exists to avoid, and install would succeed and only print its version. The default is `command -v claude` in whatever shell runs install, so it's right only when `~/.local/bin` comes first. For the install after landing, I suggest passing `--claude /home/jwoltje/.local/bin/claude` explicitly and checking that the printed version is 2.1.280 or newer. ### Sage's two questions 1. Keep the symlink. The native installer replaces its version directories, so a pinned target could disappear on the next update, and then every turn would exit 2 at the adapter's check until someone reinstalls the unit. With the symlink, an update simply takes effect. One caveat: `claudeVersion` runs once at launch, so after an update mid-session the bus runs a version that the run manifest doesn't record. That's acceptable; I'd only mention it in the README. 2. Keep the comment line and succeed. The unit is one template for every business, and the script deliberately reads no business file, so install can't know whether any business uses the claude harness. Refusing would block hosts that run only pi. Install's stdout already says `MOSAIC_CLAUDE_BIN unset; sessions run claude from the unit's PATH`, and stderr says how to pin one. ### Not blocking - N1: a directory passes the pin checks in neither place, but no test covers it. Without `[ -f … ]` (mutants `ad-no-f` and `bs-no-f`), every test still passes: a directory is `-x`, so the adapter would run it and fail with "Is a directory" (exit 126) instead of the exit-2 refusal, and render would write a directory into the unit. One more case in each new test pins it: a directory as the pin, giving exit 2 from the adapter and exit 1 with empty stdout from render. The code is right today. - N2: `ad-unquoted-run` (`$CLAUDE -p …`) survives. A unit pin can't hold a space, because bus-service refuses one, but the adapter doesn't apply that character set, so a hand-set pin with a space would break the unquoted form. A stand-in under a directory with a space in its name would pin the quoting. Optional. - N3: `claudeVersion` spawns `env.MOSAIC_CLAUDE_BIN || "claude"` without the adapter's absolute-path check. A relative pin gives a version from a PATH lookup while every turn refuses. That's harmless, since the turn fails loudly anyway; I note it for symmetry only. - N4: install's `"$bin" --version` has no timeout. `claudeVersion` uses 10 s. A hung binary would hang install. Minor. - N5: the template's header comment now runs past 80 columns ("@PATH@ and the claude line below are filled in at install time. Edit the template and reinstall; do"). Cosmetic. ### Mutants 24 mutants (`r68-out/mut/run.mjs`). Each edits one file, runs the tests that own it, and restores the file: - adapter: the harness tests; - bus-service and launcher: `host.test.mjs` and `launcher.test.mjs`; - session: the seat tests. The manifest checks OK afterwards. 21 of 24 are killed, each by one failing test. The killed ones: - adapter: ignore the pin, drop the absolute check, drop `-x`, treat empty as set, and exit 1 instead of 2. - bus-service charset: drop the check, narrow it to spaces, and allow `%`. - bus-service checks: drop the absolute check, drop `-x`, and resolve the symlink. - bus-service with no claude: die instead of writing the comment, and drop the stderr line. - bus-service render and install: `|| true` after `claude_line`, render straight into the temp file, drop the install print, accept `uninstall --claude`, and have render ignore `--claude`. - `claudeVersion` ignores the pin. - `ENV_ALLOW` drops the variable. 3 survive: - `ad-no-f` and `bs-no-f` (N1). - `ad-unquoted-run` (N2). A fourth mutant, `bs-status-no-env` (status drops `-p Environment`), also passes, but it isn't a gap: `status` needs a live user systemd, and the host tests don't run it. I don't ask for a test. ### Gate Detached worktree of `a86de1f1` with the patch, run in sequence with `TMPDIR` in my scratch and `DOCKER_HOST` pointed at a missing socket: | Suite | Result | |---|---| | `packages/cli` | 85 pass, 0 fail | | `packages/seat` | 27 pass, 0 fail | | `packages/harness` | 53 pass, 0 fail; the 4 tests that call the model were left out (see below) | | 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 | 4 passed, 0 failed; the Docker cases skip without a daemon | | test-task | 26 passed, 0 failed | Every suite exits 0. I make no live model calls from my seat, so I left out the 4 harness tests that call the model: `claude-session.test.mjs` lines 76, 115, 130 and 218. Sage's gate ran them. The 53 is the main run's 51 plus 2 tests that my skip pattern caught although they call no model: "a missing hook or MCP file refuses before claude starts" and `gate.test.mjs`'s "claude: builtins map from pi names…". I ran those 2 separately, and both pass. Review evidence (local to my scratch): `r68-out/gate.sh` and `gate/`, `r68-out/mut/run.mjs` and `result.txt`.
Member

Row 68 round 1 review (Dewey): approve

Candidate: agents/sage/work/queue-68/candidate-manifest.sha256, sha256 a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274, packet ccccc169. I applied build.patch with git apply --index to a detached worktree of a86de1f1. All 13 manifest entries check OK against the committed copy, before and after the gate.

The design does what the brief asks. The unit names the binary in MOSAIC_CLAUDE_BIN, ENV_ALLOW passes it to sessions, the adapter runs it, and claudeVersion asks the same binary, so each session's manifest records what ran. Unset or empty, behavior is the same as at base. The only places that run claude by name are launcher.mjs and adapters/claude/adapter.sh, and both take the pin. I have no blocking items.

Sage's questions

  1. Keep the symlink. claudeVersion runs per launch (launcher.mjs 165) with the same env that sessionEnv gets (197), so the manifest records the version that actually ran after an update. That gives the evidence a pinned target would give, without a reinstall per update. A pinned target under the native installer's versions directory may also be pruned by a later update, which would break the unit. And it matches what Jason's own sessions run.
  2. Succeed with the comment. bus-service.sh deliberately reads no business file, so it can't know whether any role needs claude. Refusing would make a pi-only host need a fake --claude. The stderr line and the install print say plainly that the pin is unset, and a claude role on that host then fails at launch the same way it does at base. I'd keep it.

What I checked

Probes on the review tree (stand-in binaries in my scratch, no real claude run):

Input Result
--claude a symlink pinned as the link, not resolved
--claude "", a directory, /, a dangling link exit 1 ("must be absolute" for the empty one, "not an executable file" for the rest), nothing on stdout
a newline, & or a backslash in the path exit 1, "cannot carry"
.. in the path accepted (harmless: absolute, carried as given)
--claude twice the last one wins
render --dir, render --claude with no value exit 4
no --claude, an exported shell function claude exit 1, "must be absolute: claude" (fails closed)
install twice second reports unchanged
a refused install over an existing unit unit unchanged, no temp file left
a pin whose --version exits 3 install exit 0, prints claude: <path> ()
no claude: PATH holding only node, dirname and sed exit 0, the comment line, and the stderr line
systemd-analyze --user verify of the rendered unit exit 0, with the pin and with the no-claude comment line

The adapter, with the contract's variables pointed at harmless files: a pin containing a space runs (the variable is quoted), a symlink pin runs, and a directory, dangling, relative or non-executable pin exits 2 before anything runs. Unset or empty runs the claude on PATH.

Mutation run

35 single-site mutants over bus-service.sh (claude_line, render, install, the argument parsing, status), adapter.sh, launcher.mjs and session.mjs. Tests: host.test.mjs, launcher.test.mjs, session.test.mjs and claude-session.test.mjs, filtered to bus-service|claudeVersion|sessionEnv|claude adapter so the live claude tests don't run. The baseline passed (6/6). 30 were killed and 5 survived.

Killed include: each check in claude_line removed; the charset widened by space, %, & or |; -f alone; the no-claude case refusing, writing an empty line, or staying quiet; the named pin resolved; render ignoring a refusal; install writing the temp file before rendering; uninstall --claude accepted; render taking any argument; --claude ignored; the install print dropped or without the version; the sed substitution missing; and in the adapter, each check removed, -f alone, the pin ignored, claude run by name, and an empty pin refused. claudeVersion ignoring the pin, requiring it, or reading process.env, and sessionEnv dropping it, are killed.

Survivors, none blocking:

  • bs-x-only and ad-x-only: [ -x ] alone, without [ -f ], in claude_line and in the adapter. A directory is -x, so a directory pin would then be accepted: the unit would carry it, and the adapter would fail at exec instead of with its exit-2 message. No test passes a directory. One directory case in each refusal list kills both.
  • bs-resolve-default: resolving the default with readlink -f. The test's claude on PATH is a plain file, so resolving it changes nothing. The real case, ~/.local/bin/claude, is a symlink. Making the test's bin/claude a symlink kills it and tests the case the design exists for.
  • bs-status-no-env: status without -p Environment. status needs a user systemd, and no test runs it. Fine as is.
  • bs-sed-unanchored: dropping the ^…$ anchors. The template holds @CLAUDE_ENV@ once, on its own line, so this is equivalent today. The anchors are right to keep.

Not blocking

  • N1, the default is the first claude on the installing shell's PATH. On this host /usr/bin/claude (2.1.259) exists too. A shell with /usr/bin ahead of ~/.local/bin (a sudo -u, a cron line, a login shell with a different profile) would pin the version this row exists to avoid. The install print shows the path and version, which is a good mitigation. For the after-landing install I suggest passing --claude "$HOME/.local/bin/claude" explicitly, so the result doesn't depend on that shell's PATH.
  • N2, the --version in the install print has no timeout and no hint when it fails. A pin whose --version fails prints claude: <path> (). A short note such as "(version unknown)" would read better. Minor.
  • N3, test gaps. The directory case and the symlink on PATH, as in the survivors above.
  • N4, the gate logs. The packet's logs were in /tmp/sage-r68-out/ and weren't kept, so the gate table has no output behind it. My gate below covers the same suites with the output kept.

Checked and right

  • claude_line's order (absolute, charset, executable file) gives the clearest message first, and the charset excludes % (systemd specifiers), space (Environment= splitting), quotes, backslash and newline.
  • render fails with || exit 1 rather than inside || { … }, so set -e isn't switched off. install renders into a variable before mktemp, so a refusal leaves nothing in the unit directory.
  • session.mjs adds only MOSAIC_CLAUDE_BIN to ENV_ALLOW. It is a path, not a credential, and the unit holds no secret.
  • The rejected alternative (prepending ~/.local/bin to the unit's PATH) would have shadowed system binaries for the host and every session. Naming one binary is narrower.
  • The docs (cli README "Unit", adapters README, seat README, TOOLS.md) match the code.

Gate

All suites ran one after another in the review worktree, with TMPDIR in my scratch and Docker on, from 2026-10-11T21:12:56Z to 21:20:43Z.

Suite Result
packages/cli 85 pass, 0 fail
packages/seat 27 pass, 0 fail
packages/harness 57 pass, 0 fail, nothing skipped. The live claude tests ran against /home/jwoltje/.local/bin/claude 2.1.296 on my PATH.
packages/tasks, business, bus, webui 0 fail (51, 60, 80, 40 pass)
control-board, conversation, discord, ledger, mosaic, queue, runs all 0 fail (124, 182, 178, 78, 69, 149, 41 pass)
build-tokens --check exit 0
test-auth, test-conductor, test-config, test-discord, test-extension-package, test-foundation, test-queue exit 0 (15, 17, 24, 66, 18, 44, 27 passed)
test-release 14 passed, 0 failed (Docker, nothing skipped)
test-task 98 passed, 0 failed (Docker, nothing skipped)

My counts match the packet's table. After the gate the manifest checked 13/13 OK against the committed copy at ccccc169, and core.hooksPath stayed unset.

Evidence (local to my scratch, ~/dewey-scratch/r68/): probe/p.sh with out/probe.txt, and probe/p2.sh with out/probe2.txt (p.sh's no-claude render ran under env -i without HOME and stopped on set -u; p2.sh reruns it with HOME set, and holds the adapter cases), mutants.py with out/mutants-r1.txt and out/mut-*.txt, and gate.sh with gate-out/.

## Row 68 round 1 review (Dewey): approve Candidate: `agents/sage/work/queue-68/candidate-manifest.sha256`, sha256 `a2008d8b9e534d35007fc79a4f6578bc04bb22170da7121ed0bf3c7dbcbaa274`, packet `ccccc169`. I applied `build.patch` with `git apply --index` to a detached worktree of `a86de1f1`. All 13 manifest entries check OK against the committed copy, before and after the gate. The design does what the brief asks. The unit names the binary in `MOSAIC_CLAUDE_BIN`, `ENV_ALLOW` passes it to sessions, the adapter runs it, and `claudeVersion` asks the same binary, so each session's manifest records what ran. Unset or empty, behavior is the same as at base. The only places that run claude by name are `launcher.mjs` and `adapters/claude/adapter.sh`, and both take the pin. I have no blocking items. ### Sage's questions 1. **Keep the symlink.** `claudeVersion` runs per launch (`launcher.mjs` 165) with the same `env` that `sessionEnv` gets (197), so the manifest records the version that actually ran after an update. That gives the evidence a pinned target would give, without a reinstall per update. A pinned target under the native installer's versions directory may also be pruned by a later update, which would break the unit. And it matches what Jason's own sessions run. 2. **Succeed with the comment.** `bus-service.sh` deliberately reads no business file, so it can't know whether any role needs claude. Refusing would make a pi-only host need a fake `--claude`. The stderr line and the install print say plainly that the pin is unset, and a claude role on that host then fails at launch the same way it does at base. I'd keep it. ### What I checked Probes on the review tree (stand-in binaries in my scratch, no real claude run): | Input | Result | |---|---| | `--claude` a symlink | pinned as the link, not resolved | | `--claude ""`, a directory, `/`, a dangling link | exit 1 ("must be absolute" for the empty one, "not an executable file" for the rest), nothing on stdout | | a newline, `&` or a backslash in the path | exit 1, "cannot carry" | | `..` in the path | accepted (harmless: absolute, carried as given) | | `--claude` twice | the last one wins | | `render --dir`, `render --claude` with no value | exit 4 | | no `--claude`, an exported shell function `claude` | exit 1, "must be absolute: claude" (fails closed) | | install twice | second reports unchanged | | a refused install over an existing unit | unit unchanged, no temp file left | | a pin whose `--version` exits 3 | install exit 0, prints `claude: <path> ()` | | no claude: `PATH` holding only node, dirname and sed | exit 0, the comment line, and the stderr line | | `systemd-analyze --user verify` of the rendered unit | exit 0, with the pin and with the no-claude comment line | The adapter, with the contract's variables pointed at harmless files: a pin containing a space runs (the variable is quoted), a symlink pin runs, and a directory, dangling, relative or non-executable pin exits 2 before anything runs. Unset or empty runs the `claude` on `PATH`. ### Mutation run 35 single-site mutants over `bus-service.sh` (`claude_line`, render, install, the argument parsing, status), `adapter.sh`, `launcher.mjs` and `session.mjs`. Tests: `host.test.mjs`, `launcher.test.mjs`, `session.test.mjs` and `claude-session.test.mjs`, filtered to `bus-service|claudeVersion|sessionEnv|claude adapter` so the live claude tests don't run. The baseline passed (6/6). **30 were killed and 5 survived.** Killed include: each check in `claude_line` removed; the charset widened by space, `%`, `&` or `|`; `-f` alone; the no-claude case refusing, writing an empty line, or staying quiet; the named pin resolved; render ignoring a refusal; install writing the temp file before rendering; `uninstall --claude` accepted; render taking any argument; `--claude` ignored; the install print dropped or without the version; the sed substitution missing; and in the adapter, each check removed, `-f` alone, the pin ignored, `claude` run by name, and an empty pin refused. `claudeVersion` ignoring the pin, requiring it, or reading `process.env`, and `sessionEnv` dropping it, are killed. Survivors, none blocking: - **`bs-x-only` and `ad-x-only`**: `[ -x ]` alone, without `[ -f ]`, in `claude_line` and in the adapter. A directory is `-x`, so a directory pin would then be accepted: the unit would carry it, and the adapter would fail at exec instead of with its exit-2 message. No test passes a directory. One directory case in each refusal list kills both. - **`bs-resolve-default`**: resolving the default with `readlink -f`. The test's claude on `PATH` is a plain file, so resolving it changes nothing. The real case, `~/.local/bin/claude`, is a symlink. Making the test's `bin/claude` a symlink kills it and tests the case the design exists for. - **`bs-status-no-env`**: `status` without `-p Environment`. `status` needs a user systemd, and no test runs it. Fine as is. - **`bs-sed-unanchored`**: dropping the `^…$` anchors. The template holds `@CLAUDE_ENV@` once, on its own line, so this is equivalent today. The anchors are right to keep. ### Not blocking - **N1, the default is the first claude on the installing shell's `PATH`.** On this host `/usr/bin/claude` (2.1.259) exists too. A shell with `/usr/bin` ahead of `~/.local/bin` (a `sudo -u`, a cron line, a login shell with a different profile) would pin the version this row exists to avoid. The install print shows the path and version, which is a good mitigation. For the after-landing install I suggest passing `--claude "$HOME/.local/bin/claude"` explicitly, so the result doesn't depend on that shell's `PATH`. - **N2, the `--version` in the install print has no timeout and no hint when it fails.** A pin whose `--version` fails prints `claude: <path> ()`. A short note such as "(version unknown)" would read better. Minor. - **N3, test gaps.** The directory case and the symlink on `PATH`, as in the survivors above. - **N4, the gate logs.** The packet's logs were in `/tmp/sage-r68-out/` and weren't kept, so the gate table has no output behind it. My gate below covers the same suites with the output kept. ### Checked and right - `claude_line`'s order (absolute, charset, executable file) gives the clearest message first, and the charset excludes `%` (systemd specifiers), space (`Environment=` splitting), quotes, backslash and newline. - `render` fails with `|| exit 1` rather than inside `|| { … }`, so `set -e` isn't switched off. `install` renders into a variable before `mktemp`, so a refusal leaves nothing in the unit directory. - `session.mjs` adds only `MOSAIC_CLAUDE_BIN` to `ENV_ALLOW`. It is a path, not a credential, and the unit holds no secret. - The rejected alternative (prepending `~/.local/bin` to the unit's `PATH`) would have shadowed system binaries for the host and every session. Naming one binary is narrower. - The docs (cli README "Unit", adapters README, seat README, TOOLS.md) match the code. ### Gate All suites ran one after another in the review worktree, with `TMPDIR` in my scratch and Docker on, from 2026-10-11T21:12:56Z to 21:20:43Z. | Suite | Result | |---|---| | `packages/cli` | 85 pass, 0 fail | | `packages/seat` | 27 pass, 0 fail | | `packages/harness` | 57 pass, 0 fail, nothing skipped. The live claude tests ran against `/home/jwoltje/.local/bin/claude` 2.1.296 on my `PATH`. | | `packages/tasks`, business, bus, webui | 0 fail (51, 60, 80, 40 pass) | | control-board, conversation, discord, ledger, mosaic, queue, runs | all 0 fail (124, 182, 178, 78, 69, 149, 41 pass) | | build-tokens `--check` | exit 0 | | test-auth, test-conductor, test-config, test-discord, test-extension-package, test-foundation, test-queue | exit 0 (15, 17, 24, 66, 18, 44, 27 passed) | | test-release | 14 passed, 0 failed (Docker, nothing skipped) | | test-task | 98 passed, 0 failed (Docker, nothing skipped) | My counts match the packet's table. After the gate the manifest checked 13/13 OK against the committed copy at `ccccc169`, and `core.hooksPath` stayed unset. Evidence (local to my scratch, `~/dewey-scratch/r68/`): `probe/p.sh` with `out/probe.txt`, and `probe/p2.sh` with `out/probe2.txt` (p.sh's no-claude render ran under `env -i` without `HOME` and stopped on `set -u`; p2.sh reruns it with `HOME` set, and holds the adapter cases), `mutants.py` with `out/mutants-r1.txt` and `out/mut-*.txt`, and `gate.sh` with `gate-out/`.
Author
Contributor

Review request for queue row 68, round 2: Bus host runs an explicit Claude Code binary

  • Owner: sage
  • Reviewers: dewey, filbert
  • Gate: Filbert and Dewey approve; cli, seat, harness suites and every scripts/test-*.sh green on Sage's gate rerun; unit reinstalled and opus probe through MOSAIC_CLAUDE_BIN (sage)
  • Brief: docs/plans/2026-10-11_bus-claude-binary.md § Bus host runs an explicit Claude Code binary @d494ede2b76e
  • Candidate: manifest 21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786

The manifest:

d21a79f290757954540fde75f063b436d22546047207e86e78a9259ce435906f  adapters/claude/adapter.sh
b1b6fc2e2a42866e0de6f77eb22810538d1659d0a9ff49c577a3a746a94f34d1  adapters/README.md
b8715347f13ae77507356fdd8de955271d63e7eecb627adc3ab72bae2ba27936  docs/TOOLS.md
76f80e882d3122944c38721baa2261ab1a80057260a68df01309f86237a94a5c  packages/cli/README.md
4036811721eb6d2e0f7787ae7a59a6d972d218e5d86df92b61e5901784aee8a4  packages/cli/src/launcher.mjs
015ac12798590299fded4d277f697e058ab68de909cff88d0997801029561fd2  packages/cli/systemd/mosaic-bus.service.in
916539dfb045ded42ec79a1d6ca7d84c4bf123564d7a11d30442099bb1bae694  packages/cli/tests/host.test.mjs
01699be6d7b187ab732e0c1a3e2f39052d2e562870064521ad4246aa9f66c037  packages/cli/tests/launcher.test.mjs
69e38831bb0724289a039812f538745d9a102ef79c4237435b3281dce06f3451  packages/harness/tests/claude-session.test.mjs
c9ce09cec4871738b50ff1b69e10e5f37bf7cb9f299d04b0168c618c4a37976d  packages/seat/README.md
778e5df6fa6285fbd13162c7f2fbf4995e7c3161df8e0f438279e20c1cf61e4d  packages/seat/src/session.mjs
6169b55f7b23fb2822bb461189b0597a2abc9a50155950d37bd4d20dadbfc3ab  packages/seat/tests/session.test.mjs
369306c0136a9f1a25fe8a9eecae282056a3685317d496691b542b5e9293d222  scripts/bus-service.sh

Check a tree against it with scripts/mosaic queue review verify-commit 68 REF.

Post your verdict as a comment here, then record it:

scripts/mosaic queue review record 68 --verdict approve|changes --comment COMMENT_ID --candidate 21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786 --op OP --by SEAT
<!-- mosaic-queue-op: sage-68-review-r2-2026-10-11 --> <!-- mosaic-queue-round: row=68 round=2 candidate=21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786 --> Review request for queue row 68, round 2: Bus host runs an explicit Claude Code binary - Owner: sage - Reviewers: dewey, filbert - Gate: Filbert and Dewey approve; cli, seat, harness suites and every scripts/test-*.sh green on Sage's gate rerun; unit reinstalled and opus probe through MOSAIC_CLAUDE_BIN (sage) - Brief: `docs/plans/2026-10-11_bus-claude-binary.md` § Bus host runs an explicit Claude Code binary @d494ede2b76e - Candidate: manifest `21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786` The manifest: ```text d21a79f290757954540fde75f063b436d22546047207e86e78a9259ce435906f adapters/claude/adapter.sh b1b6fc2e2a42866e0de6f77eb22810538d1659d0a9ff49c577a3a746a94f34d1 adapters/README.md b8715347f13ae77507356fdd8de955271d63e7eecb627adc3ab72bae2ba27936 docs/TOOLS.md 76f80e882d3122944c38721baa2261ab1a80057260a68df01309f86237a94a5c packages/cli/README.md 4036811721eb6d2e0f7787ae7a59a6d972d218e5d86df92b61e5901784aee8a4 packages/cli/src/launcher.mjs 015ac12798590299fded4d277f697e058ab68de909cff88d0997801029561fd2 packages/cli/systemd/mosaic-bus.service.in 916539dfb045ded42ec79a1d6ca7d84c4bf123564d7a11d30442099bb1bae694 packages/cli/tests/host.test.mjs 01699be6d7b187ab732e0c1a3e2f39052d2e562870064521ad4246aa9f66c037 packages/cli/tests/launcher.test.mjs 69e38831bb0724289a039812f538745d9a102ef79c4237435b3281dce06f3451 packages/harness/tests/claude-session.test.mjs c9ce09cec4871738b50ff1b69e10e5f37bf7cb9f299d04b0168c618c4a37976d packages/seat/README.md 778e5df6fa6285fbd13162c7f2fbf4995e7c3161df8e0f438279e20c1cf61e4d packages/seat/src/session.mjs 6169b55f7b23fb2822bb461189b0597a2abc9a50155950d37bd4d20dadbfc3ab packages/seat/tests/session.test.mjs 369306c0136a9f1a25fe8a9eecae282056a3685317d496691b542b5e9293d222 scripts/bus-service.sh ``` Check a tree against it with `scripts/mosaic queue review verify-commit 68 REF`. Post your verdict as a comment here, then record it: ``` scripts/mosaic queue review record 68 --verdict approve|changes --comment COMMENT_ID --candidate 21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786 --op OP --by SEAT ```
Author
Contributor

Round 2 is round 1 rebased, nothing else (Sage).

Round 1 (manifest a2008d8b on a86de1f1) has both approvals, 27349 and 27350, but it can't land as approved. Row 55 (79d9fc2d) landed after a86de1f1 and changed two of the 13 paths, docs/TOOLS.md and packages/cli/README.md, so no commit on refactor can hash to round 1's manifest.

The round 2 packet is agents/sage/work/queue-68/r2/ (commit b31cc8a9), base f2f2861b. The + and - lines of the two patches, file headers dropped, are byte-identical. Eleven hashes are round 1's; the two that differ are the files row 55 changed, and row 55's additions there don't mention the claude binary, MOSAIC_CLAUDE_BIN, bus-service.sh or systemd. build.md there has the check.

Gate on f2f2861b plus the patch, finished 21:37:18Z: cli 112, seat 27, harness 57, and the nine scripts/test-*.sh all 0 failed, Docker on, manifest OK before and after.

A confirmation that the diff is only context is enough for round 2. Round 1's notes (the explicit --claude install, the version check, the mutant survivors) carry over unchanged.

Round 2 is round 1 rebased, nothing else (Sage). Round 1 (manifest a2008d8b on a86de1f1) has both approvals, 27349 and 27350, but it can't land as approved. Row 55 (79d9fc2d) landed after a86de1f1 and changed two of the 13 paths, `docs/TOOLS.md` and `packages/cli/README.md`, so no commit on `refactor` can hash to round 1's manifest. The round 2 packet is `agents/sage/work/queue-68/r2/` (commit b31cc8a9), base f2f2861b. The `+` and `-` lines of the two patches, file headers dropped, are byte-identical. Eleven hashes are round 1's; the two that differ are the files row 55 changed, and row 55's additions there don't mention the claude binary, `MOSAIC_CLAUDE_BIN`, `bus-service.sh` or systemd. `build.md` there has the check. Gate on f2f2861b plus the patch, finished 21:37:18Z: cli 112, seat 27, harness 57, and the nine `scripts/test-*.sh` all 0 failed, Docker on, manifest OK before and after. A confirmation that the diff is only context is enough for round 2. Round 1's notes (the explicit `--claude` install, the version check, the mutant survivors) carry over unchanged.
Member

Filbert, round 2 review of row 68 (#1561). Approve: manifest 21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786.

Round 2 is round 1 rebased onto f2f2861b. I checked that, and only that. Round 1's review (27349) carries over, its notes included.

What I ran:

  • In a fresh worktree of f2f2861b, git apply --index agents/sage/work/queue-68/r2/build.patch applies cleanly. sha256sum -c on the r2 manifest passes all 13 files. The staged file set equals the manifest's 13 paths.
  • I extracted the +/- lines (file headers dropped) from round 1's and round 2's patches: 273 lines each, byte-identical (cmp). The diff --git/---/+++ headers are identical too.
  • The two patches differ only in 10 @@ lines, 4 index lines and no context-body lines. Per file, only docs/TOOLS.md and packages/cli/README.md differ.
  • Comparing manifests: 11 of 13 hashes equal round 1's. The two that differ are those same two files, and they are the only candidate paths that git diff a86de1f1 f2f2861b touches (row 55: TOOLS.md +5, cli README +76/-2).
  • I read the moved hunks in both docs. Each still lands in its own section: the bus-service.sh usage line and the MOSAIC_CLAUDE_BIN paragraph in TOOLS.md, and the install, render and status bullets and the unit settings in the cli README's "Unit" section. None of them falls inside row 55's added text.

Per Sage's ask, I didn't rerun mutants or the gate. Sage's gate on r2 is green.

Filbert, round 2 review of row 68 (#1561). Approve: manifest `21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786`. Round 2 is round 1 rebased onto f2f2861b. I checked that, and only that. Round 1's review (27349) carries over, its notes included. What I ran: - In a fresh worktree of f2f2861b, `git apply --index agents/sage/work/queue-68/r2/build.patch` applies cleanly. `sha256sum -c` on the r2 manifest passes all 13 files. The staged file set equals the manifest's 13 paths. - I extracted the `+`/`-` lines (file headers dropped) from round 1's and round 2's patches: 273 lines each, byte-identical (`cmp`). The `diff --git`/`---`/`+++` headers are identical too. - The two patches differ only in 10 `@@` lines, 4 `index` lines and no context-body lines. Per file, only `docs/TOOLS.md` and `packages/cli/README.md` differ. - Comparing manifests: 11 of 13 hashes equal round 1's. The two that differ are those same two files, and they are the only candidate paths that `git diff a86de1f1 f2f2861b` touches (row 55: TOOLS.md +5, cli README +76/-2). - I read the moved hunks in both docs. Each still lands in its own section: the `bus-service.sh` usage line and the `MOSAIC_CLAUDE_BIN` paragraph in TOOLS.md, and the install, render and status bullets and the unit settings in the cli README's "Unit" section. None of them falls inside row 55's added text. Per Sage's ask, I didn't rerun mutants or the gate. Sage's gate on r2 is green.
Member

Row 68 round 2 review (Dewey): approve

Candidate: agents/sage/work/queue-68/r2/candidate-manifest.sha256, sha256 21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786, packet b31cc8a9. I applied r2/build.patch with git apply --index to a detached worktree of f2f2861b. All 13 manifest entries check OK against the committed copy, before and after the tests.

Round 2 is round 1 rebased and nothing else. My round 1 approval (27350) and its notes carry over. I have no blocking items.

What I checked

  • Changed lines. The + and - lines of round 1's patch (ccccc169) and round 2's, with the +++/--- headers dropped, are byte-identical (273 lines each). Both patches touch the same 13 files in the same order.
  • Whole-patch diff. A plain diff of the two patches shows only the index lines and hunk offsets of docs/TOOLS.md (one line) and packages/cli/README.md (72 lines). The context lines are identical too.
  • Manifests. 11 of the 13 hashes are round 1's. The two that differ are those two files.
  • Row 55's change to them. git diff a86de1f1 f2f2861b -- docs/TOOLS.md packages/cli/README.md has no added or removed line mentioning claude, bus-service, systemd or MOSAIC_CLAUDE_BIN.
  • New callers. Row 55 added app.mjs and screens.mjs and changed cli.mjs and scripts/mosaic. None of them runs claude. In packages/cli/src, packages/seat/src, packages/harness/src and scripts/mosaic, the only place that runs claude is still launcher.mjs, and it takes the pin.

Tests

Run on the review tree, with TMPDIR in my scratch, between 2026-10-11T21:40:44Z and 21:41:39Z. I ran the three packages the patch touches, not the full gate. Sage's packet records a full gate with Docker.

Suite Result
packages/cli 112 pass, 0 fail
packages/seat 27 pass, 0 fail
packages/harness 57 pass, 0 fail, nothing skipped

Correction on my own run: the first harness run failed 5 pi tests with exec: pi: not found, because my worktree had no node_modules link. With the link in place, the rerun passed 57 of 57. I kept both logs. This was my setup error, not the candidate's.

The counts match the packet's. After the tests the manifest checked 13/13 OK, and core.hooksPath stayed unset.

Round 1 notes

These carry over, as build.md says:

  • N1: install with an explicit --claude "$HOME/.local/bin/claude".
  • N2: the version print has no hint when it fails.
  • N3 and the survivors: the directory case, and the symlink on PATH. These go to the follow-up row.
  • N4: the round 1 gate logs.

Evidence (local to my scratch, ~/dewey-scratch/r68b/): out/build.changed and out/build-r1.changed, out/patch-diff.txt, out/manifest.sha256 and out/manifest-r1.sha256, and gate-out/, which includes node-harness-no-node-modules.txt from the first run.

## Row 68 round 2 review (Dewey): approve Candidate: `agents/sage/work/queue-68/r2/candidate-manifest.sha256`, sha256 `21c1ab05aa380539ab71a3b4abbb018be462d6d9d812544a67e4d311d24d6786`, packet `b31cc8a9`. I applied `r2/build.patch` with `git apply --index` to a detached worktree of `f2f2861b`. All 13 manifest entries check OK against the committed copy, before and after the tests. Round 2 is round 1 rebased and nothing else. My round 1 approval (27350) and its notes carry over. I have no blocking items. ### What I checked - **Changed lines.** The `+` and `-` lines of round 1's patch (`ccccc169`) and round 2's, with the `+++`/`---` headers dropped, are byte-identical (273 lines each). Both patches touch the same 13 files in the same order. - **Whole-patch diff.** A plain `diff` of the two patches shows only the `index` lines and hunk offsets of `docs/TOOLS.md` (one line) and `packages/cli/README.md` (72 lines). The context lines are identical too. - **Manifests.** 11 of the 13 hashes are round 1's. The two that differ are those two files. - **Row 55's change to them.** `git diff a86de1f1 f2f2861b -- docs/TOOLS.md packages/cli/README.md` has no added or removed line mentioning claude, `bus-service`, systemd or `MOSAIC_CLAUDE_BIN`. - **New callers.** Row 55 added `app.mjs` and `screens.mjs` and changed `cli.mjs` and `scripts/mosaic`. None of them runs claude. In `packages/cli/src`, `packages/seat/src`, `packages/harness/src` and `scripts/mosaic`, the only place that runs claude is still `launcher.mjs`, and it takes the pin. ### Tests Run on the review tree, with `TMPDIR` in my scratch, between 2026-10-11T21:40:44Z and 21:41:39Z. I ran the three packages the patch touches, not the full gate. Sage's packet records a full gate with Docker. | Suite | Result | |---|---| | `packages/cli` | 112 pass, 0 fail | | `packages/seat` | 27 pass, 0 fail | | `packages/harness` | 57 pass, 0 fail, nothing skipped | Correction on my own run: the first harness run failed 5 pi tests with `exec: pi: not found`, because my worktree had no `node_modules` link. With the link in place, the rerun passed 57 of 57. I kept both logs. This was my setup error, not the candidate's. The counts match the packet's. After the tests the manifest checked 13/13 OK, and `core.hooksPath` stayed unset. ### Round 1 notes These carry over, as `build.md` says: - N1: install with an explicit `--claude "$HOME/.local/bin/claude"`. - N2: the version print has no hint when it fails. - N3 and the survivors: the directory case, and the symlink on `PATH`. These go to the follow-up row. - N4: the round 1 gate logs. Evidence (local to my scratch, `~/dewey-scratch/r68b/`): `out/build.changed` and `out/build-r1.changed`, `out/patch-diff.txt`, `out/manifest.sha256` and `out/manifest-r1.sha256`, and `gate-out/`, which includes `node-harness-no-node-modules.txt` from the first run.
Author
Contributor

Landed as 11dae0c7 (round 2, manifest 21c1ab05; Filbert 27374, Dewey 27375). queue review verify-commit 68 11dae0c7: 13 paths match.

Installed 2026-10-11T21:42:39Z: scripts/bus-service.sh install --claude /home/jwoltje/.local/bin/claude (symlink to 2.1.296). The unit diff was its comments and Environment=MOSAIC_CLAUDE_BIN=/home/jwoltje/.local/bin/claude; daemon-reload only.

Probe 21:43:03Z, the adapter under env -i with the unit PATH and claude-opus-5-5: the pin answered OK, rc 0; /usr/bin/claude (2.1.259) still refuses the model, rc 1. The running bus was not restarted; it picks up the pin at the Q18 --pm restart.

Lead decision 91 amends 89 (32aa5727). Row 68 done at rev 463 (4100fa68). Follow-ups: #1566 (row 72, the surviving mutants) and #1567 (row 73, the dead coder grant).

Landed as 11dae0c7 (round 2, manifest 21c1ab05; Filbert 27374, Dewey 27375). `queue review verify-commit 68 11dae0c7`: 13 paths match. Installed 2026-10-11T21:42:39Z: `scripts/bus-service.sh install --claude /home/jwoltje/.local/bin/claude` (symlink to 2.1.296). The unit diff was its comments and `Environment=MOSAIC_CLAUDE_BIN=/home/jwoltje/.local/bin/claude`; daemon-reload only. Probe 21:43:03Z, the adapter under `env -i` with the unit PATH and claude-opus-5-5: the pin answered OK, rc 0; `/usr/bin/claude` (2.1.259) still refuses the model, rc 1. The running bus was not restarted; it picks up the pin at the Q18 `--pm` restart. Lead decision 91 amends 89 (32aa5727). Row 68 done at rev 463 (4100fa68). Follow-ups: #1566 (row 72, the surviving mutants) and #1567 (row 73, the dead coder grant).
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1561