--- /opt/router-ops/steps/20260716-173824_STEP_050M07R15C_ROUTER_MACHINE_CLOSE_CONTRACT_FIX_R02/rollback-files/router-machine-close	2026-07-15 21:02:36.012527196 +0000
+++ /opt/router-ops/bin/router-machine-close	2026-07-16 17:38:24.685813795 +0000
@@ -1,9 +1,10 @@
 #!/usr/bin/env bash
 set -Eeuo pipefail
+umask 077
 
 MODE="--publish"
 
-if [ "${1:-}" = "--check" ]; then
+if [[ "${1:-}" == "--check" ]]; then
     MODE="--check"
     shift
 fi
@@ -13,8 +14,7 @@
 DESCRIPTION="${*:-current managed source}"
 
 case "$MACHINE" in
-    vm100|vm101|vm121)
-        ;;
+    vm100|vm101|vm121) ;;
     *)
         echo 'Usage: router-machine-close [--check] {vm100|vm101|vm121} "description"' >&2
         exit 2
@@ -24,13 +24,15 @@
 BASE="${ROUTER_OPS_BASE:-/opt/router-ops}"
 PUBLIC_BASE="${ROUTER_PUBLIC_BASE_URL:-https://reports.secret-studio.ru}"
 PUBLIC_BASE="${PUBLIC_BASE%/}"
-BACKEND="$BASE/bin/router-${MACHINE}-git-source-publish"
-STATE_FILE="$BASE/state/current-${MACHINE}-git-source.env"
+BACKEND="${ROUTER_MACHINE_CLOSE_BACKEND_OVERRIDE:-$BASE/bin/router-${MACHINE}-git-source-publish}"
+STATE_FILE="${ROUTER_MACHINE_CLOSE_STATE_FILE_OVERRIDE:-$BASE/state/current-${MACHINE}-git-source.env}"
 TOKEN_FILE="${ROUTER_PUBLIC_TOKEN_FILE:-$BASE/public/.router-public-token}"
-REINDEX="$BASE/bin/router-public-reindex"
+REINDEX="${ROUTER_MACHINE_CLOSE_REINDEX_OVERRIDE:-$BASE/bin/router-public-reindex}"
+CONTRACT_LIB="${ROUTER_MACHINE_CLOSE_CONTRACT_LIB_OVERRIDE:-$BASE/lib/router-machine-close-contract.sh}"
+RUN_ROOT="${ROUTER_MACHINE_CLOSE_RUN_ROOT_OVERRIDE:-$BASE/state/machine-close-runs}"
 
 TS="$(date -u +%Y%m%d-%H%M%S)"
-RUN_DIR="$BASE/state/machine-close-runs/${TS}_${MACHINE}"
+RUN_DIR="$RUN_ROOT/${TS}_${MACHINE}_$$"
 LOG="$RUN_DIR/backend.txt"
 DESCRIPTION_FILE="$RUN_DIR/description.txt"
 
@@ -39,32 +41,52 @@
 printf '%s\n' "$DESCRIPTION" > "$DESCRIPTION_FILE"
 chmod 600 "$DESCRIPTION_FILE"
 
-[ -x "$BACKEND" ]
-[ -x "$REINDEX" ]
-[ -f "$TOKEN_FILE" ]
-
-if ! "$BACKEND" "$MODE" > "$LOG" 2>&1; then
-    echo "STOP=ROUTER_MACHINE_CLOSE_BACKEND_FAILED"
+[[ -x "$BACKEND" ]] || { echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_BACKEND_MISSING"; exit 70; }
+[[ -x "$REINDEX" ]] || { echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_REINDEX_MISSING"; exit 70; }
+[[ -f "$TOKEN_FILE" ]] || { echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_TOKEN_MISSING"; exit 70; }
+[[ -r "$CONTRACT_LIB" ]] || { echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_CONTRACT_LIBRARY_MISSING"; exit 70; }
+
+# shellcheck source=/opt/router-ops/lib/router-machine-close-contract.sh
+source "$CONTRACT_LIB"
+
+echo ">>> [router-machine-close] machine=$MACHINE mode=$MODE"
+set +e
+"$BACKEND" "$MODE" > "$LOG" 2>&1
+BACKEND_RC=$?
+set -e
+cat "$LOG"
+
+if ! router_machine_close_contract_classify "$MODE" "$MACHINE" "$BACKEND_RC" "$LOG"; then
+    FINAL_RC="$BACKEND_RC"
+    [[ "$FINAL_RC" -ne 0 ]] || FINAL_RC=1
+    echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_BACKEND_OR_PUBLICATION"
     echo "MACHINE=$MACHINE"
     echo "MODE=$MODE"
-    cat "$LOG"
-    exit 1
+    echo "BACKEND_RC=$BACKEND_RC"
+    echo "CLASSIFICATION=$ROUTER_MACHINE_CLOSE_CLASSIFICATION"
+    echo "BACKEND_LOG=$LOG"
+    exit "$FINAL_RC"
 fi
 
-if [ "$MODE" = "--check" ]; then
-    RESULT="$(awk '/^RESULT=PASS_/ {value=$0} END {print value}' "$LOG")"
-    [ -n "$RESULT" ]
-    echo "$RESULT"
+if [[ "$MODE" == "--check" ]]; then
+    echo "RESULT=PASS_ROUTER_MACHINE_CLOSE_CHECK"
     echo "MACHINE=$MACHINE"
     echo "MODE=--check"
     echo "DESCRIPTION=$DESCRIPTION"
+    echo "BACKEND_RC=$BACKEND_RC"
+    echo "BACKEND_RC_NONZERO_SALVAGED=$([[ "$BACKEND_RC" -ne 0 ]] && echo true || echo false)"
+    echo "CLASSIFICATION=$ROUTER_MACHINE_CLOSE_CLASSIFICATION"
     echo "BACKEND_LOG=$LOG"
     exit 0
 fi
 
-[ -f "$STATE_FILE" ]
-MACHINE_UPPER="$(printf '%s' "$MACHINE" | tr '[:lower:]' '[:upper:]')"
+[[ -f "$STATE_FILE" ]] || {
+    echo "RESULT=STOP_ROUTER_MACHINE_CLOSE_STATE_FILE_MISSING"
+    echo "STATE_FILE=$STATE_FILE"
+    exit 1
+}
 
+MACHINE_UPPER="${MACHINE^^}"
 read_state() {
     local suffix="$1"
     local key="${MACHINE_UPPER}_GIT_SOURCE_${suffix}"
@@ -73,8 +95,7 @@
         $1 == key {
             print substr($0, index($0, "=") + 1)
         }
-    ' "$STATE_FILE" |
-    tail -n 1
+    ' "$STATE_FILE" | tail -n 1
 }
 
 PUBLIC_URL="$(read_state URL)"
@@ -86,36 +107,37 @@
 EXPECTED_DIR_PREFIX="$BASE/public/r/$TOKEN/latest/"
 EXPECTED_URL="${PUBLIC_BASE}/latest/$(basename "$PUBLIC_DIR")/"
 
-[ -n "$PUBLIC_URL" ]
-[ -n "$COMMIT" ]
-[ -n "$TREE" ]
-[ -n "$FILE_COUNT" ]
-[ -d "$PUBLIC_DIR" ]
+[[ -n "$PUBLIC_URL" && -n "$COMMIT" && -n "$TREE" && -n "$FILE_COUNT" ]]
+[[ -d "$PUBLIC_DIR" ]]
+[[ "$COMMIT" =~ ^[0-9a-f]{40}$ ]]
+[[ "$TREE" =~ ^[0-9a-f]{40}$ ]]
+[[ "$FILE_COUNT" =~ ^[0-9]+$ ]]
 
 case "$PUBLIC_DIR" in
     "$EXPECTED_DIR_PREFIX"*) ;;
     *)
-        echo "STOP=INVALID_MACHINE_PUBLIC_DIR"
+        echo "RESULT=STOP_INVALID_MACHINE_PUBLIC_DIR"
         echo "ACTUAL_PUBLIC_DIR=$PUBLIC_DIR"
         echo "EXPECTED_PREFIX=$EXPECTED_DIR_PREFIX"
         exit 1
         ;;
 esac
 
-if [ "$PUBLIC_URL" != "$EXPECTED_URL" ]; then
-    echo "STOP=INVALID_MACHINE_PUBLIC_URL"
+if [[ "$PUBLIC_URL" != "$EXPECTED_URL" ]]; then
+    echo "RESULT=STOP_INVALID_MACHINE_PUBLIC_URL"
     echo "ACTUAL_PUBLIC_URL=$PUBLIC_URL"
     echo "EXPECTED_PUBLIC_URL=$EXPECTED_URL"
     exit 1
 fi
 
-grep -Fqx "PERMANENT_HTTPS_PUBLISHED=true" "$LOG" || {
-    echo "STOP=PERMANENT_HTTPS_CONFIRMATION_MISSING"
+grep -Fqx 'PERMANENT_HTTPS_PUBLISHED=true' "$LOG" || {
+    echo "RESULT=STOP_PERMANENT_HTTPS_CONFIRMATION_MISSING"
     exit 1
 }
 
-grep -Fqx "${MACHINE_UPPER}_PUBLIC_URL=$PUBLIC_URL" "$LOG" || {
-    echo "STOP=CANONICAL_MACHINE_URL_MISSING"
+grep -Fqx "${MACHINE_UPPER}_PUBLIC_URL=$PUBLIC_URL" "$LOG" ||
+grep -Fqx "${MACHINE_UPPER}_GIT_SOURCE_URL=$PUBLIC_URL" "$LOG" || {
+    echo "RESULT=STOP_CANONICAL_MACHINE_URL_MISSING"
     exit 1
 }
 
@@ -133,7 +155,7 @@
         -printf '%f\n' |
     LC_ALL=C sort |
     while IFS= read -r NAME; do
-        [ -n "$NAME" ] || continue
+        [[ -n "$NAME" ]] || continue
         printf '<li><a href="%s">%s</a></li>\n' "$NAME" "$NAME"
     done
     echo '</ul>'
@@ -146,7 +168,7 @@
 
 CURRENT="$BASE/state/current-machine-close-${MACHINE}.env"
 CURRENT_TMP="${CURRENT}.tmp.$$"
-
+mkdir -p "$(dirname "$CURRENT")"
 {
     echo "ROUTER_MACHINE_CLOSE_UPDATED_AT_UTC=$TS"
     echo "ROUTER_MACHINE_CLOSE_RESULT=PASS"
@@ -158,8 +180,9 @@
     echo "ROUTER_MACHINE_CLOSE_PUBLIC_URL=$PUBLIC_URL"
     echo "ROUTER_MACHINE_CLOSE_PUBLIC_DIR=$PUBLIC_DIR"
     echo "ROUTER_MACHINE_CLOSE_BACKEND_LOG=$LOG"
+    echo "ROUTER_MACHINE_CLOSE_BACKEND_RC=$BACKEND_RC"
+    echo "ROUTER_MACHINE_CLOSE_CLASSIFICATION=$ROUTER_MACHINE_CLOSE_CLASSIFICATION"
 } > "$CURRENT_TMP"
-
 chmod 600 "$CURRENT_TMP"
 mv "$CURRENT_TMP" "$CURRENT"
 
@@ -171,4 +194,7 @@
 echo "GIT_FILE_COUNT=$FILE_COUNT"
 echo "SOURCE_PUBLISH_BACKEND=$BACKEND"
 echo "BACKEND_LOG=$LOG"
+echo "BACKEND_RC=$BACKEND_RC"
+echo "BACKEND_RC_NONZERO_SALVAGED=$([[ "$BACKEND_RC" -ne 0 ]] && echo true || echo false)"
+echo "CLASSIFICATION=$ROUTER_MACHINE_CLOSE_CLASSIFICATION"
 echo "${MACHINE_UPPER}_PUBLIC_URL=$PUBLIC_URL"
