134 lines
6.5 KiB
Markdown
134 lines
6.5 KiB
Markdown
# Row 68 (#1561): the bus host runs an explicit Claude Code binary, round 1
|
|
|
|
Sage, 2026-10-11. Brief:
|
|
`docs/plans/2026-10-11_bus-claude-binary.md`, "Bus host runs an explicit
|
|
Claude Code binary" (e1b90ed6). Mos's Q18 ask: the bus finds
|
|
`/usr/bin/claude` 2.1.259 (pacman `claude-code`), which refuses
|
|
`claude-opus-5-5`. Jason runs the native `~/.local/bin/claude` (2.1.296).
|
|
Filbert and Dewey review. Sage commits after the gate; nothing is
|
|
committed, staged or pushed yet.
|
|
|
|
Base a86de1f1. `build.patch` applies there with `git apply --index`;
|
|
`candidate-manifest.sha256` pins all 13 files after the patch.
|
|
|
|
## Design
|
|
|
|
The binary is named, not found. `MOSAIC_CLAUDE_BIN` is an absolute path:
|
|
|
|
1. The unit sets it. `scripts/bus-service.sh render|install --claude PATH`;
|
|
without `--claude`, `command -v claude` in the installing shell. The
|
|
path is kept as given, not resolved: `~/.local/bin/claude` is a symlink
|
|
the native installer moves to each new version, so the unit follows
|
|
updates without a reinstall.
|
|
2. `ENV_ALLOW` passes it to the session. It's a path, not a credential.
|
|
3. `adapters/claude/adapter.sh` runs `$MOSAIC_CLAUDE_BIN` when it is set
|
|
and nonempty, else `claude` as before.
|
|
4. `claudeVersion`, which fills the manifest's `claudeVersion`, asks the
|
|
same binary, so the manifest records what actually ran.
|
|
|
|
Rejected: prepending `~/.local/bin` to the unit's `PATH`. Every binary
|
|
there (pip and npm installs, personal scripts) would then shadow the
|
|
system's, for the host and for every session. Also rejected: a field in
|
|
the business file or system config. Which binary runs is a property of the
|
|
host install, like node's directory, which the unit already carries.
|
|
|
|
## Refusals
|
|
|
|
| Where | Input | Result |
|
|
|---|---|---|
|
|
| `bus-service.sh` | `--claude` relative | exit 1, "must be absolute", nothing written |
|
|
| `bus-service.sh` | not an executable file, or missing | exit 1, "not an executable file" |
|
|
| `bus-service.sh` | a character outside `A-Za-z0-9/._+-` (`%`, space, ...) | exit 1, "cannot carry". systemd expands `%` and splits `Environment=` on spaces. |
|
|
| `bus-service.sh` | no `--claude` and no claude on `PATH` | exit 0; the unit gets `# MOSAIC_CLAUDE_BIN unset: ...` and stderr says so. A pi-only host installs as before. |
|
|
| `bus-service.sh` | `uninstall --claude`, `render --dir` | exit 4 |
|
|
| adapter | `MOSAIC_CLAUDE_BIN` relative | exit 2 before claude runs |
|
|
| adapter | not an executable file, or missing | exit 2 before claude runs |
|
|
| adapter | unset or empty | `claude` from `PATH`, as today |
|
|
|
|
`install` now renders into a variable before `mktemp`, so a refused pin
|
|
leaves no temp file in the unit directory. Calling `render` inside
|
|
`|| { ... }` would have switched `set -e` off in the function and written
|
|
a unit with an empty line. `claude_line` failing is handled with an
|
|
explicit `|| exit 1`.
|
|
|
|
`install` prints `claude: <path> (<--version>)` or the unset line.
|
|
`status` adds `-p Environment` to its `systemctl show`, which shows `PATH`
|
|
and the pin. The unit holds no secret.
|
|
|
|
## Files
|
|
|
|
- `packages/cli/systemd/mosaic-bus.service.in`: a comment and an
|
|
`@CLAUDE_ENV@` line after `Environment=PATH=`. The header names "the
|
|
claude line below" rather than the token, because header tokens render
|
|
into the comment (as `@REPO@` and `@PATH@` already do) and the claude
|
|
substitution only matches its own line.
|
|
- `scripts/bus-service.sh`: `claude_line`, `--claude` for render and
|
|
install, the install print, `status` shows `Environment`, usage.
|
|
- `adapters/claude/adapter.sh`: the `CLAUDE` variable and its checks.
|
|
- `packages/seat/src/session.mjs`: `MOSAIC_CLAUDE_BIN` in `ENV_ALLOW`.
|
|
- `packages/cli/src/launcher.mjs`: `claudeVersion` asks
|
|
`env.MOSAIC_CLAUDE_BIN || "claude"`.
|
|
- Docs: `packages/cli/README.md` ("Unit" and the manifest note),
|
|
`adapters/README.md` (adapter list and variable table),
|
|
`packages/seat/README.md` (`sessionEnv`), `docs/TOOLS.md` (usage line
|
|
and one paragraph). The brief didn't list `docs/TOOLS.md`. I added it
|
|
because its usage line would otherwise be wrong, and it is my file.
|
|
- Tests:
|
|
- `host.test.mjs`, new test "bus-service.sh pins the claude binary...":
|
|
- the pin found on `PATH`, from `--claude`, and through a symlink
|
|
kept unresolved
|
|
- five refusals with exit 1 and empty stdout
|
|
- install printing the pin and the stand-in's version
|
|
- a refused install writing nothing
|
|
- `uninstall --claude` and `render --dir` exiting 4
|
|
- no claude anywhere: `PATH` is a directory holding only node,
|
|
dirname and sed, and the unit gets the comment line
|
|
- The existing render test also asserts no `@CLAUDE_ENV@`.
|
|
- `launcher.test.mjs`: `claudeVersion` with the pin, from `PATH`, and
|
|
with none (null).
|
|
- `session.test.mjs`: `sessionEnv` passes `MOSAIC_CLAUDE_BIN`.
|
|
- `claude-session.test.mjs`, new test:
|
|
- the pin wins over a `claude` on `PATH`
|
|
- unset or empty uses `PATH`
|
|
- relative, missing and non-executable pins exit 2 without running
|
|
anything
|
|
|
|
Each new test fails against base: unpinned `claudeVersion` returns the
|
|
`PATH` stand-in's 1.0.0, the base adapter runs the `PATH` stand-in, base
|
|
`sessionEnv` drops the variable, and base `render` rejects `--claude` with
|
|
exit 4.
|
|
|
|
## Gate (my run on the candidate)
|
|
|
|
| Suite | Result |
|
|
|---|---|
|
|
| packages/cli | 85/0 |
|
|
| packages/seat | 27/0 |
|
|
| packages/harness | 57/0, nothing skipped. The live claude tests ran against the 2.1.296 on my PATH. |
|
|
| scripts/test-*.sh | all nine exit 0: auth 15, conductor 17, config 24, discord 66, extension-package 18, foundation 44, queue 27, release 14, task 98 (Docker on) |
|
|
|
|
Logs: `/tmp/sage-r68-out/` (not kept).
|
|
|
|
## After landing (not in the candidate)
|
|
|
|
1. `scripts/bus-service.sh install` from my shell, which resolves
|
|
`/home/jwoltje/.local/bin/claude`. `install` prints the pin and its
|
|
version.
|
|
2. The opus probe: the adapter's flags with `MOSAIC_CLAUDE_BIN` and `PATH`
|
|
taken from the installed unit, as decision 89's probe did.
|
|
3. Report the path and version to Mos.
|
|
|
|
The running host (MainPID 1242258) keeps its old environment until it
|
|
restarts. That restart is the Q18 `--pm` start, which waits for Mos's
|
|
watched window. Nothing here restarts it.
|
|
|
|
## Questions for reviewers
|
|
|
|
- Is keeping the symlink, not its target, right? The other choice pins an
|
|
exact version and needs a reinstall per update. I chose to follow
|
|
updates because that's what Jason's own sessions do.
|
|
- With no claude found, `install` succeeds with a comment rather than
|
|
refusing, because a pi-only business needs no claude. Is that the right
|
|
default, or should `install` refuse unless the business's roles are all
|
|
pi?
|