# #1256 — Fleet runtime preflight **Agent:** tiny **Branch:** `fix/1256-fleet-runtime-preflight` from `origin/next@476db12b92971634b67fd2057b7577ee5894e449` **Issue:** `mosaicstack/stack#1256` blocker 1 **Adjacent PR:** `#1258` (`fix/1256-fleet-pane-path-node`) owns the bootstrapped-Node candidate and must remain a separate change **Budget:** 30K-token soft cap; one bounded implementation lane ## Objective Make fleet provisioning fail before installation effects when the roster names a runtime binary absent from the exact PATH the tmux pane will receive. Make `mosaic fleet doctor` report the same named runtime check. Diagnostics must name the runtime, every requesting roster row, the pane PATH searched, and an exact install command. For Pi the exact command is: ```text npm install -g @earendil-works/pi-coding-agent ``` ## Constraints - TDD: add the failing behavior test and capture RED before implementation. - Runtime resolution uses the launcher's pane-PATH construction; a second PATH model is forbidden. - Operator PATH is non-authoritative and must not cause a false pass. - Tests use an isolated `--mosaic-home`/temporary HOME and never mutate host runtime binaries. - No install, removal, or binary-resolution changes on sb-it-1-dt. - PR targets `next` and requires a reviewer other than fred. - Commit identity is `tiny `. - #1258's Node candidate is a dependency/adjacent change, never reimplemented here. ## Planned seam 1. Factor the shell pane-path builder/resolver into one sourceable and executable fleet helper. 2. Have `start-agent-session.sh` source that helper, preserving one definition of the pane PATH. 3. Have the TypeScript fleet command invoke the same helper under the unit-equivalent clean launcher environment. 4. Group roster rows by distinct runtime and effective pane PATH, then report requesting row names. 5. Run the preflight before `installFleet` performs any write. 6. Add the named result to roster-v2 `fleet doctor` JSON and set a failing exit when a runtime is absent. 7. Install/copy the helper alongside `start-agent-session.sh` and update framework manifest/docs as required. This seam overlaps #1258 only at the location of the existing shell function. Development may use #1258 as a local dependency, but the final PR diff must exclude #1258's separately owned Node change after that PR lands or after an agreed rebase order. ## Acceptance evidence | Requirement | Evidence | |---|---| | Missing Pi blocks install before effects | isolated CLI test: nonzero + no installed files/runner effects | | Operator PATH cannot create false green | test puts Pi only on operator PATH and omits it from constructed pane PATH | | Exact pane PATH reused | launcher and CLI call one shared shell helper; contract test exercises both | | Actionable diagnosis | runtime + roster rows + searched PATH + exact install command assertions | | Distinct runtimes | repeated rows produce one check with all row names | | Doctor reports named check | JSON assertion + nonzero exit for missing runtime | | Present runtime passes | isolated pane-path fixture with executable binary | | No host mutation | tests use temporary HOME/Mosaic home and fixture binaries only | | Baseline safety | focused tests, package typecheck/lint/format, full relevant suite, CI | ## Progress log - 2026-08-16: Dispatch received from fred; issue #1256 and PR #1258 measured. - 2026-08-16: Fresh clone created under `~/agent-work/tiny-fleet-runtime-preflight`; local Git identity pinned to tiny so retired global `mos-dt-0` identity cannot win. - 2026-08-16: Design inspection found the pane PATH exists only inside `start-agent-session.sh`; the right seam is a shared shell helper rather than a parallel TypeScript reconstruction. - 2026-08-16: RED measured on `origin/next@476db12b`: focused `fleet-roster-v2-dispatch.spec.ts` ran 11 tests; the new case failed because install returned success, wrote units for two agents, and emitted no `runtime=pi` diagnosis while Pi existed only on operator PATH. - 2026-08-16: Factored pane home/PATH/resolution into sourceable and executable `pane-runtime-path.sh`; install invokes it before the first effect, doctor emits the same named checks, and the launcher sources it. - 2026-08-16: Fred/rhodey review exposed the #1241 name-resolution blind spot: `mosaic` can resolve while its `#!/usr/bin/env node` interpreter cannot. Measurement confirmed every supported current Mosaic package shape is a Node-shebang script, but executing `mosaic --version` is not observational because CLI startup runs the cache-writing/network update checker before Commander handles the flag. - 2026-08-16: Final executable check reads and unwraps direct and `/usr/bin/env` shebangs (including `env -S`), resolves the declared dependency against pane PATH, and runs only side-effect-free `node --version` when Node is declared. Native binaries do not inherit a permanent Node requirement. Install, doctor, and launcher share this implementation. - 2026-08-16: Isolated greenfield fixture places resolved Mosaic and Pi Node-shebang scripts in pane-visible npm-global bin while using an empty system suffix; both checks become `unexecutable` with `dependency=node`, and install leaves holder/tools/units absent. No host binary or HOME is changed. - 2026-08-16: GREEN evidence before #1258 rebase: focused install/doctor/preflight suites pass; `fleet.spec.ts` 209/209; full Vitest 87 files / 1,557 tests; launcher shell suite, typecheck, lint, build, and focused format check pass. Full framework-shell reaches an unrelated host-measurement drift in unchanged `invariant_r_unittest.py` (expected Pi 0.84.1, host resolves 0.84.2); no invariant was changed in this lane. - 2026-08-16: Merge-order gate remains: `origin/next` is still `476db12b`; #1258 is unmerged at `6dc35e5`. Rebase after it lands, relocate its Node candidate into the helper with explicit provenance, rerun gates, then open the PR to `next` for an independent non-fred review.