diff --git a/scripts/reconcile-containers.sh b/scripts/reconcile-containers.sh index a9bb2473..b19484f6 100755 --- a/scripts/reconcile-containers.sh +++ b/scripts/reconcile-containers.sh @@ -18,16 +18,25 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # ── Parse arguments ────────────────────────────────────────────────── CHECK_ONLY=false FORCE=false +CREATE_MISSING=false FILTER_TIER="" FILTER_CONTAINER="" for arg in "$@"; do case "$arg" in --check-only) CHECK_ONLY=true ;; --force) FORCE=true ;; + --create-missing) CREATE_MISSING=true ;; --tier=*) FILTER_TIER="${arg#*=}" ;; --container=*) FILTER_CONTAINER="${arg#*=}" ;; -h|--help) - echo "Usage: $0 [--check-only] [--force] [--tier=N] [--container=NAME]" + echo "Usage: $0 [--check-only] [--force] [--create-missing] [--tier=N] [--container=NAME]" + echo "" + echo " --check-only Audit only, no changes." + echo " --force Override user-stopped state." + echo " --create-missing Override SPEC_OPTIONAL for containers that have on-disk" + echo " data but no live container (recovery from failed updates)." + echo " --tier=N Only reconcile containers in tier N." + echo " --container=NAME Only reconcile the named container (spec key)." exit 0 ;; esac done @@ -213,7 +222,9 @@ reconcile() { # Optional apps: only reconcile if already installed (container exists). # The install RPC creates the container; the reconciler just keeps it running. - if [ "$SPEC_OPTIONAL" = "true" ] && ! container_exists "$name"; then + # --create-missing overrides this so we can recover from failed-update rollbacks + # that deleted a container without restoring it (on-disk data still present). + if [ "$SPEC_OPTIONAL" = "true" ] && ! container_exists "$name" && ! $CREATE_MISSING; then skip "$name — not installed" COUNT_SKIPPED=$((COUNT_SKIPPED + 1)) return