fix(fleet): boot-survival symmetry — disable-on-remove + add-enable + init-R5 #612

Merged
jason.woltje merged 1 commits from feat/fleet-polish-bundle into main 2026-06-22 08:12:59 +00:00
Owner

Fleet-polish bundle — boot-survival symmetry (closes #611)

The full fleet-polish bundle from your Codex symmetry-gap finding. Three fixes completing add/remove boot-survival symmetry.

1. disable-on-remove (the bug — TDD)

mosaic fleet remove stopped + deleted roster/env/heartbeat but never systemctl --user disable mosaic-agent@NAME.service — so a removed-but-enabled unit could resurrect on reboot pointing at deleted config. Added buildSystemdDisableCommand + a disable step in remove (best-effort, non-fatal, gated on !--keep-files so keeping config keeps the unit).

2. add-enable

mosaic fleet add now enables the new agent's unit for boot-survival (best-effort, independent of --start so a queued agent still survives a reboot).

3. init-R5 guarantee

mosaic fleet init --write now fails hard when a non-minimal profile lacks exactly one orchestrator (was a soft warning) — a corrupted preset can't silently produce a malformed fleet. The sanctioned no-orchestrator minimal preset is still allowed.

Verification

  • 4 new tests: disable-command builder; remove invokes disable (+ still stops); add invokes enable; init general → exactly 1 orchestrator. + 147 existing fleet tests green (151 total). tsc/eslint/prettier clean.
  • TDD on the disable bug per contract.

Note for the reviewer

disable/enable/init validations are best-effort + non-fatal (warn-and-continue) except init's R5 hard-fail. Ready for the batched release (F3-m3 + fleet-polish + F4).

🤖 Generated with Claude Code

## Fleet-polish bundle — boot-survival symmetry (closes #611) The full fleet-polish bundle from your Codex symmetry-gap finding. Three fixes completing add/remove boot-survival symmetry. ### 1. disable-on-remove (the bug — TDD) `mosaic fleet remove` stopped + deleted roster/env/heartbeat but never `systemctl --user disable mosaic-agent@NAME.service` — so a removed-but-**enabled** unit could **resurrect on reboot** pointing at deleted config. Added `buildSystemdDisableCommand` + a disable step in `remove` (best-effort, non-fatal, gated on `!--keep-files` so keeping config keeps the unit). ### 2. add-enable `mosaic fleet add` now enables the new agent's unit for boot-survival (best-effort, **independent of `--start`** so a queued agent still survives a reboot). ### 3. init-R5 guarantee `mosaic fleet init --write` now **fails hard** when a non-minimal profile lacks exactly one orchestrator (was a soft warning) — a corrupted preset can't silently produce a malformed fleet. The sanctioned no-orchestrator `minimal` preset is still allowed. ### Verification - **4 new tests**: disable-command builder; `remove` invokes disable (+ still stops); `add` invokes enable; `init general` → exactly 1 orchestrator. + **147 existing fleet tests** green (151 total). tsc/eslint/prettier clean. - TDD on the disable bug per contract. ### Note for the reviewer disable/enable/init validations are best-effort + non-fatal (warn-and-continue) except init's R5 hard-fail. Ready for the batched release (F3-m3 + fleet-polish + F4). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jason.woltje added 1 commit 2026-06-22 07:45:13 +00:00
fix(fleet): boot-survival symmetry — disable-on-remove + add-enable + init-R5 (#611)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
eb32df6cc5
Codex symmetry-gap finding. Three fixes completing add/remove boot-survival
symmetry:

1. disable-on-remove (BUG, TDD): fleet remove stopped + deleted roster/env/
   heartbeat but never disabled the systemd unit, so a removed-but-enabled
   mosaic-agent@NAME.service could resurrect on reboot pointing at deleted
   config. Add buildSystemdDisableCommand + disable in remove (best-effort,
   gated on !--keep-files).
2. add-enable: fleet add now enables the new agent's unit for boot-survival
   (best-effort, independent of --start).
3. init-R5 guarantee: fleet init --write now fails hard when a non-minimal
   profile lacks exactly one orchestrator (was a soft warning); the sanctioned
   no-orchestrator 'minimal' preset is still allowed.

Verified: 4 new tests (disable builder; remove-invokes-disable; add-invokes-
enable; init general → exactly 1 orchestrator) + 147 existing fleet tests green
(151 total); tsc/eslint/prettier clean.

Refs #611

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EsgTQzV5YUGk1JtCLP4B83
jason.woltje merged commit d46ac40890 into main 2026-06-22 08:12:59 +00:00
jason.woltje deleted branch feat/fleet-polish-bundle 2026-06-22 08:13:00 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#612