fix(fleet): tell the operator when the fleet transport is missing (#1240)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
`mosaic fleet --help` reads "Manage the local Mosaic tmux fleet" and every roster the CLI scaffolds sets `transport: tmux`, but neither `tools/install.sh` nor `tools/_scripts/mosaic-doctor` contained the string "tmux" at all. A greenfield host therefore came out of the installer able to install a fleet, start a fleet, and run no seat, with `mosaic fleet ps` as the operator's first and only signal. Measured on mosaic-sbx-dev (Debian, no tmux, framework installed): `mosaic-doctor` reported 11 warnings and not one of them named the reason no seat could launch. The installer gets a warning, not a `require_cmd` hard failure: tmux is required by the fleet, not by mosaic. Hosts that install this to run `mosaic claude` and never scaffold a roster are common, and failing their install over a binary they do not need would be wrong. The check runs in `--check` mode too — "what is the state of this host" is the question `--check` is asked. Both checks read the roster's own `transport:` rather than assuming tmux, so a host declaring something else is pointed at the binary it actually needs instead of at the wrong package. The two implementations are deliberately parallel and each carries a comment pointing at the other. They are separate because the installer must answer this before the framework's own scripts are guaranteed to be on disk. One harness drives BOTH from the shipped text — the functions are extracted from the scripts by awk rather than copied — so the pair cannot drift silently, and the test cannot keep passing after the shipped copy changes. The harness is wired into `test:framework-shell`. Without that it would have tripped the #1017 enumeration guard as UNENUMERATED, which is the guard doing its job: a check nothing runs is not a check. Evidence: - red: the harness fails against origin/next ("could not extract fleet_declared_transport"); `grep -ci tmux` on both files at origin/next = 0. - green on real hosts, all four branches: - dev (no tmux, no roster) -> WARN naming tmux, points at `mosaic fleet init` - dev (no tmux, v2 roster) -> WARN naming the roster, points at `mosaic fleet start` - dev installer --check -> WARN saying start "reports success and no seat comes up" - canary (tmux present) -> `[OK] Fleet transport available: tmux` under --verbose, silent by default (pass() is verbose-gated), installer silent - harness green on node:24-alpine/busybox, the CI base image. - `bash -n` x3, `pnpm typecheck` 45/45, fleet specs 342 passed, enumeration guard OK, its self-test OK, prettier clean. Refs #1240. Upstream of #1237/#1243 and #1241/#1244: a correct fix for either of those still leaves this host with no live seat.
This commit is contained in:
@@ -309,6 +309,43 @@ require_cmd() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Fleet transport binary (#1240).
|
||||
#
|
||||
# `mosaic fleet --help` reads "Manage the local Mosaic tmux fleet" and every
|
||||
# roster the CLI scaffolds sets `transport: tmux`, but nothing in this script
|
||||
# provides tmux and, until now, nothing in it mentioned tmux at all. A
|
||||
# greenfield host came out of this installer able to install a fleet, start a
|
||||
# fleet, and run no seat — the operator's first signal was `mosaic fleet ps`.
|
||||
#
|
||||
# Not a `require_cmd`: tmux is required by the fleet, not by mosaic. Plenty of
|
||||
# hosts install this to run `mosaic claude` and will never scaffold a roster,
|
||||
# and failing their install over a binary they do not need would be wrong. It
|
||||
# is a warning that names precisely what it blocks.
|
||||
#
|
||||
# `tools/_scripts/mosaic-doctor` carries a deliberately parallel check, so the
|
||||
# same host state gets the same answer from an audit as from an install. They
|
||||
# are separate implementations because this one has to work before the
|
||||
# framework's scripts are guaranteed to be on disk; keep their wording in step.
|
||||
check_fleet_transport() {
|
||||
local transport=tmux
|
||||
local roster="$MOSAIC_HOME/fleet/roster.yaml"
|
||||
local declared=""
|
||||
|
||||
if [[ -f "$roster" ]]; then
|
||||
declared="$(sed -n 's/^[[:space:]]*transport:[[:space:]]*//p' "$roster" | head -1 |
|
||||
tr -d '"'\''' | tr -d '\r' | awk '{print $1}')"
|
||||
[[ -n "$declared" ]] && transport="$declared"
|
||||
fi
|
||||
|
||||
command -v "$transport" &>/dev/null && return 0
|
||||
|
||||
warn "Fleet transport '$transport' is not installed."
|
||||
echo " The Mosaic fleet runs its agent seats inside $transport. Without it,"
|
||||
echo " ${C}mosaic fleet start${RESET} reports success and no seat comes up."
|
||||
echo " Install it before using the fleet, e.g. ${C}sudo apt-get install -y $transport${RESET}"
|
||||
echo " (this does not affect ${C}mosaic claude${RESET} or the other single-runtime commands)."
|
||||
}
|
||||
|
||||
installed_cli_version() {
|
||||
local json
|
||||
json="$(npm ls -g --depth=0 --json --prefix="$PREFIX" 2>/dev/null)" || true
|
||||
@@ -870,6 +907,11 @@ if [[ "$FLAG_CHECK" == "false" ]]; then
|
||||
ok "Done."
|
||||
fi
|
||||
|
||||
# Fleet readiness (#1240). Runs in both normal and --check mode: "what is the
|
||||
# state of this host" is exactly the question --check is asked, and a host that
|
||||
# cannot run a seat should not have to discover it from `fleet ps`.
|
||||
check_fleet_transport
|
||||
|
||||
} # end main
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user