The July 2 outage function had a unit test — with a populated fixture
only. This encodes the missing net:
- fresh-minimal-box fixture loop: every observation/discovery/snapshot/
report function in update.sh (17) and install.sh (16) must survive a
zero-app box under set -Eeuo pipefail; policy comment requires new
functions to be appended
- scripts/test/stubs.sh: shared failing-stub factories (systemctl exit
5, docker fail/empty, curl exit 22, GitHub rate-limit JSON)
- assertion sweep: 43 bare capture-then-compare sites now include exit
status in the assertion
- best-effort contract test: the run block's real APP_BASELINE ->
report chain exits 0 on a fresh box AND on a genuine regression
- fix(update): the loop immediately caught report_failure's trailing
'[ -n $UPDATE_LOG ] && printf' returning 1 when UPDATE_LOG is empty
(same species as the outage) — converted to if, added to the loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- update --branch without a value errors loudly instead of dying
silently on shift (L5)
- cmd_start/cmd_restart mirror cmd_stop's systemctl/docker guards (L6)
- cmd_logs degrades past a dead/missing journalctl to docker logs (L7)
- cmd_add_site/remove_site/list_sites detect Debian vs conf.d nginx
layout; reloads are guarded with a manual-reload hint (L9)
- uninstall on a remnants-only box prints exact recovery commands (L4)
- portable port extraction (no grep -oP), read-at-EOF defaults,
compare_versions rejects unparsable as update-available, doctor
exits nonzero on issues (L12); BASH_SOURCE guard for testability
- new scripts/test/test_cli.sh (22 passed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>