#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

[ "$(id -un)" = "ops" ] || {
  echo "STOP: запускать на VM130/router-ops пользователем ops"
  exit 1
}

STEP="STEP_050M06C1_DIAGNOSE_REMOTE_RC127_BEFORE_PATCH"
TOKEN="e94a0859747d7b96f29c7fdafc2d0351ba603bb0a7e9e5a4"
PUBLIC_BASE="https://helena-background-beam-harry.trycloudflare.com/r/${TOKEN}"

ARCHITECTURE_PLAN="${PUBLIC_BASE}/20260711-150958_local_architecture_plan_vm101_autonomous_hmn_recovery/"
XS_MAP="${PUBLIC_BASE}/20260711-120734_xs_map_audit_repair_publish/"
GLOBAL_PROJECT_PLAN="${PUBLIC_BASE}/20260711-123348_global_project_plan_wg_paid/"

ROOT="/opt/router-ops"
PUBROOT="${ROOT}/public/r/${TOKEN}"

M06C_DIR="${PUBROOT}/20260711-175545_step050m06c_enable_commit_threshold_aware_vm101"
M06A_DIR="${PUBROOT}/20260711-173308_step050m06a_compare_live_toolchain_to_frozen_map"

TS="$(date -u +%Y%m%d-%H%M%S)"
REPORT_SLUG="${TS}_step050m06c1_diagnose_remote_rc127_before_patch"
REPORT_DIR="${PUBROOT}/${REPORT_SLUG}"

TRYCF_REPORT="${PUBLIC_BASE}/${REPORT_SLUG}/"
REPORT_TXT="${TRYCF_REPORT}report.txt"
FACTS_JSON="${TRYCF_REPORT}facts.json"

mkdir -p \
  "$REPORT_DIR" \
  "$REPORT_DIR/sources"

# Точный пользовательский скрипт публикуется первым.
cp -a "$0" "$REPORT_DIR/step.sh"
chmod 600 "$REPORT_DIR/step.sh"

PROGRESS_LOG="$REPORT_DIR/progress.log"
: > "$PROGRESS_LOG"

stage() {
  echo
  echo ">>> [$1] $2" | tee -a "$PROGRESS_LOG"

  date -u '+%Y-%m-%dT%H:%M:%SZ' |
    sed 's/^/    utc=/' |
    tee -a "$PROGRESS_LOG"
}

print_links() {
  echo
  echo "TRYCF_REPORT=$TRYCF_REPORT"
  echo "REPORT_TXT=$REPORT_TXT"
  echo "FACTS_JSON=$FACTS_JSON"
  echo "ARCHITECTURE_PLAN=$ARCHITECTURE_PLAN"
  echo "XS_MAP=$XS_MAP"
  echo "GLOBAL_PROJECT_PLAN=$GLOBAL_PROJECT_PLAN"
}

write_stop() {
  local reason="$1"
  local rc="$2"
  local line="$3"

  cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=STOP_${STEP}_${reason}
all_ok=false
mode=READ_ONLY_RC127_DIAGNOSTIC
error_rc=${rc}
error_line=${line}
production_modified=false
vm101_modified=false
backup_created=false
commit_flag_changed=false
real_refresh_ran=false
rebalance_apply_ran=false
direct_failopen_changed=false

TRYCF_REPORT=${TRYCF_REPORT}
REPORT_TXT=${REPORT_TXT}
FACTS_JSON=${FACTS_JSON}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

  python3 - \
    "$STEP" \
    "$reason" \
    "$rc" \
    "$line" \
    "$TRYCF_REPORT" \
    "$REPORT_TXT" \
    "$FACTS_JSON" \
    "$ARCHITECTURE_PLAN" \
    "$XS_MAP" \
    "$GLOBAL_PROJECT_PLAN" \
    > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    step,
    reason,
    rc,
    line,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "assessment": {
        "decision": f"STOP_{step}_{reason}",
        "all_ok": False,
        "error_rc": int(rc),
        "error_line": int(line),
    },
    "mode": "READ_ONLY_RC127_DIAGNOSTIC",
    "safety": {
        "production_modified": False,
        "vm101_modified": False,
        "backup_created": False,
        "commit_flag_changed": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "publish": {
        "trycf_report": report,
        "report_txt": report_txt,
        "facts_json": facts_json,
        "architecture_plan": architecture,
        "xs_map": xs_map,
        "global_project_plan": global_plan,
    },
}, ensure_ascii=False, indent=2))
PY
}

on_error() {
  local rc="$?"
  local line="$1"

  trap - ERR
  write_stop "UNEXPECTED_ERROR" "$rc" "$line"
  print_links
  exit "$rc"
}

trap 'on_error "$LINENO"' ERR

stage "01/06" "Проверяю и сохраняю точные артефакты M06C"

for required in \
  "$M06C_DIR/report.txt" \
  "$M06C_DIR/facts.json" \
  "$M06C_DIR/step.sh" \
  "$M06C_DIR/vm101.sh" \
  "$M06C_DIR/vm101.txt" \
  "$M06C_DIR/vm101.stderr" \
  "$M06C_DIR/vm101-tool-hash-check.txt" \
  "$M06A_DIR/comparison.json"
do
  [ -e "$required" ] || {
    echo "MISSING_REQUIRED=$required"
    exit 1
  }
done

grep -Fq \
  "decision=STOP_STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101_VM101_REMOTE_FAILED" \
  "$M06C_DIR/report.txt"

grep -Fq \
  "error_rc=127" \
  "$M06C_DIR/report.txt"

grep -Fq \
  "production_modified=false" \
  "$M06C_DIR/report.txt"

grep -Fq \
  "__TRACE__ stage=pre_state" \
  "$M06C_DIR/vm101.txt"

if grep -Fq \
  "__TRACE__ stage=backup_and_patch" \
  "$M06C_DIR/vm101.txt"
then
  echo "STOP: M06C unexpectedly reached backup_and_patch"
  exit 1
fi

cp -a \
  "$M06C_DIR/report.txt" \
  "$REPORT_DIR/sources/step050m06c-report.txt"

cp -a \
  "$M06C_DIR/facts.json" \
  "$REPORT_DIR/sources/step050m06c-facts.json"

cp -a \
  "$M06C_DIR/step.sh" \
  "$REPORT_DIR/sources/step050m06c-step.sh"

cp -a \
  "$M06C_DIR/vm101.sh" \
  "$REPORT_DIR/sources/step050m06c-vm101.sh"

cp -a \
  "$M06C_DIR/vm101.txt" \
  "$REPORT_DIR/sources/step050m06c-vm101.txt"

cp -a \
  "$M06C_DIR/vm101.stderr" \
  "$REPORT_DIR/previous-vm101.stderr"

python3 - \
  "$M06C_DIR/vm101.stderr" \
  "$REPORT_DIR/previous-stderr.json" <<'PY'
import json
import sys
from pathlib import Path

path = Path(sys.argv[1])
data = path.read_bytes()

result = {
    "size_bytes": len(data),
    "text": data.decode("utf-8", errors="replace"),
    "hex": data.hex(),
}

Path(sys.argv[2]).write_text(
    json.dumps(
        result,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)
PY

stage "02/06" "Публикую точный read-only VM101 diagnostic"

cat > "$REPORT_DIR/vm101.sh" <<'VM101'
#!/bin/sh
set -u
umask 077

CONF="/etc/router-egress-emergency-refresh.conf"
RUNNER="/usr/local/sbin/router-egress-emergency-refresh.sh"
HOOK="/usr/local/sbin/router-egress-emergency-decision-hook.sh"
PLANNER="/usr/local/sbin/router-egress-hmn-plan-top5.sh"
POOL="/root/hmn/cache/ok-awg1-strict-foreign-latest.tsv"

fact() {
  printf '__FACT__ %s=%s\n' "$1" "$2"
}

result() {
  name="$1"
  rc="$2"
  value="$3"

  printf '__RESULT__ name=%s rc=%s value=%s\n' \
    "$name" \
    "$rc" \
    "$value"
}

check_command() {
  name="$1"

  if path="$(command -v "$name" 2>/dev/null)"; then
    fact "command.${name}.exists" true
    fact "command.${name}.path" "$path"
  else
    fact "command.${name}.exists" false
    fact "command.${name}.path" MISSING
  fi
}

run_capture() {
  name="$1"
  shift

  temporary="/tmp/step050m06c1.${$}.${name}"

  set +e
  "$@" > "${temporary}.out" 2> "${temporary}.err"
  rc="$?"
  set -e

  stdout="$(
    tr '\n' ' ' < "${temporary}.out" |
      sed 's/[[:space:]][[:space:]]*/ /g'
  )"

  stderr="$(
    tr '\n' ' ' < "${temporary}.err" |
      sed 's/[[:space:]][[:space:]]*/ /g'
  )"

  rm -f \
    "${temporary}.out" \
    "${temporary}.err"

  result "${name}.stdout" "$rc" "${stdout:-EMPTY}"
  result "${name}.stderr" "$rc" "${stderr:-EMPTY}"

  return 0
}

echo "__TRACE__ stage=command_inventory"

for command_name in \
  sh \
  sha256sum \
  sed \
  uci \
  stat \
  date \
  cp \
  awk \
  mv \
  chmod \
  chown \
  wg \
  ip \
  ping \
  grep
do
  check_command "$command_name"
done

echo "__TRACE__ stage=exact_expression_diagnostics"

run_capture \
  config_sha256 \
  sh -c '
    sha256sum "$1" |
      sed "s/[[:space:]].*$//"
  ' sh "$CONF"

run_capture \
  runner_sha256 \
  sh -c '
    sha256sum "$1" |
      sed "s/[[:space:]].*$//"
  ' sh "$RUNNER"

run_capture \
  hook_sha256 \
  sh -c '
    sha256sum "$1" |
      sed "s/[[:space:]].*$//"
  ' sh "$HOOK"

run_capture \
  network_sha256 \
  sh -c '
    uci export network |
      sha256sum |
      sed "s/[[:space:]].*$//"
  '

run_capture \
  pool_sha256 \
  sh -c '
    sha256sum "$1" |
      sed "s/[[:space:]].*$//"
  ' sh "$POOL"

run_capture \
  pool_stat_gnu \
  sh -c '
    stat -c "%Y" "$1"
  ' sh "$POOL"

run_capture \
  pool_stat_busybox \
  sh -c '
    busybox stat -c "%Y" "$1"
  ' sh "$POOL"

run_capture \
  pool_ls_epoch_capability \
  sh -c '
    date -r "$1" +%s
  ' sh "$POOL"

run_capture \
  runner_dryrun \
  "$RUNNER" --dry-run

run_capture \
  hook_dryrun \
  "$HOOK"

run_capture \
  planner_readonly \
  "$PLANNER"

echo "__TRACE__ stage=safety_confirmation"

fact production_modified false
fact backup_created false
fact commit_flag_changed false
fact watcher_invoked false
fact real_refresh_ran false
fact rebalance_apply_ran false
fact direct_failopen_changed false

echo "__TRACE__ stage=complete"
VM101

chmod 600 "$REPORT_DIR/vm101.sh"
sh -n "$REPORT_DIR/vm101.sh"

stage "03/06" "Выполняю read-only diagnostic на VM101"

if ssh pve-mgts \
  "ssh \
    -o BatchMode=yes \
    -o ConnectTimeout=8 \
    -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    -i /root/.ssh/pve_to_openwrt_mgts_ed25519 \
    root@10.71.100.2 \
    'sh -s'" \
  < "$REPORT_DIR/vm101.sh" \
  > "$REPORT_DIR/vm101-diagnostic.txt" \
  2> "$REPORT_DIR/vm101-diagnostic.stderr"
then
  VM101_RC=0
else
  VM101_RC=$?
fi

echo "vm101_rc=$VM101_RC" |
  tee -a "$PROGRESS_LOG"

[ "$VM101_RC" -eq 0 ] || {
  echo "STOP: read-only diagnostic failed rc=$VM101_RC"
  exit 1
}

grep -Fq \
  "__TRACE__ stage=complete" \
  "$REPORT_DIR/vm101-diagnostic.txt"

grep -Fq \
  "__FACT__ production_modified=false" \
  "$REPORT_DIR/vm101-diagnostic.txt"

stage "04/06" "Классифицирую точную причину rc=127"

python3 - \
  "$REPORT_DIR/previous-stderr.json" \
  "$REPORT_DIR/vm101-diagnostic.txt" \
  "$REPORT_DIR/vm101-diagnostic.stderr" \
  "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import re
import sys
from pathlib import Path

previous_stderr = json.loads(
    Path(sys.argv[1]).read_text(
        encoding="utf-8",
    )
)

diagnostic = Path(sys.argv[2]).read_text(
    encoding="utf-8",
    errors="replace",
)

diagnostic_stderr = Path(sys.argv[3]).read_text(
    encoding="utf-8",
    errors="replace",
).strip()

facts = {}
results = {}
traces = []

for line in diagnostic.splitlines():
    if line.startswith("__TRACE__ "):
        traces.append(line)

    elif line.startswith("__FACT__ "):
        payload = line[len("__FACT__ "):]

        if "=" in payload:
            key, value = payload.split("=", 1)
            facts[key] = value

    elif line.startswith("__RESULT__ "):
        payload = line[len("__RESULT__ "):]

        match = re.match(
            r"name=(\S+) rc=(\d+) value=(.*)$",
            payload,
        )

        if match:
            results[match.group(1)] = {
                "rc": int(match.group(2)),
                "value": match.group(3),
            }

required_expressions = [
    "config_sha256.stderr",
    "runner_sha256.stderr",
    "hook_sha256.stderr",
    "network_sha256.stderr",
    "pool_sha256.stderr",
    "pool_stat_gnu.stderr",
]

failed_expressions = {
    name: results.get(name)
    for name in required_expressions
    if (
        results.get(name) is None
        or results[name]["rc"] != 0
    )
}

missing_commands = [
    name.removeprefix("command.").removesuffix(".exists")
    for name, value in facts.items()
    if (
        name.startswith("command.")
        and name.endswith(".exists")
        and value == "false"
    )
]

previous_text = previous_stderr.get("text", "")

if "stat: not found" in previous_text.lower():
    cause = "STAT_COMMAND_MISSING"
elif "not found" in previous_text.lower():
    cause = "COMMAND_NOT_FOUND_FROM_PREVIOUS_STDERR"
elif failed_expressions:
    cause = "EXACT_PREPATCH_EXPRESSION_FAILED"
else:
    cause = "PREVIOUS_STDERR_REQUIRED_FOR_FINAL_CLASSIFICATION"

safe_fallback = None

gnu_stat = results.get("pool_stat_gnu.stderr", {})
busybox_stat = results.get("pool_stat_busybox.stderr", {})
date_r = results.get("pool_ls_epoch_capability.stderr", {})

if gnu_stat.get("rc") == 0:
    safe_fallback = "stat -c %Y"
elif busybox_stat.get("rc") == 0:
    safe_fallback = "busybox stat -c %Y"
elif date_r.get("rc") == 0:
    safe_fallback = "date -r FILE +%s"

all_core_readonly_ok = all(
    results.get(name, {}).get("rc") == 0
    for name in (
        "runner_dryrun.stderr",
        "hook_dryrun.stderr",
        "planner_readonly.stderr",
    )
)

diagnosis = {
    "all_ok": bool(
        "__TRACE__ stage=complete" in traces
        and facts.get("production_modified") == "false"
        and all_core_readonly_ok
        and safe_fallback
    ),
    "classification": cause,
    "previous_m06c": {
        "rc": 127,
        "stderr": previous_text,
        "stderr_size_bytes":
            previous_stderr.get("size_bytes"),
        "reached_backup_and_patch": False,
        "production_modified": False,
    },
    "missing_commands": missing_commands,
    "failed_prepatch_expressions": failed_expressions,
    "safe_pool_mtime_expression": safe_fallback,
    "core_readonly_tools_ok": all_core_readonly_ok,
    "diagnostic_stderr": diagnostic_stderr,
    "safety": {
        "production_modified": False,
        "vm101_modified": False,
        "backup_created": False,
        "commit_flag_changed": False,
        "watcher_invoked": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "next_step":
        "STEP_050M06D_ENABLE_COMMIT_WITH_PROVEN_OPENWRT_COMMANDS",
}

Path(sys.argv[4]).write_text(
    json.dumps(
        diagnosis,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)
PY

ALL_OK="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print("true" if data["all_ok"] else "false")
PY
)"

CLASSIFICATION="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(data["classification"])
PY
)"

SAFE_MTIME="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(data.get("safe_pool_mtime_expression") or "NONE")
PY
)"

MISSING_COMMANDS="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

values = data.get("missing_commands", [])
print(",".join(values) if values else "NONE")
PY
)"

stage "05/06" "Публикую диагностический отчёт"

if [ "$ALL_OK" = true ]; then
  DECISION="PASS_${STEP}"
else
  DECISION="STOP_${STEP}_DIAGNOSIS_INCOMPLETE"
fi

cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${DECISION}
all_ok=${ALL_OK}
mode=READ_ONLY_RC127_DIAGNOSTIC

previous_step:
  step=STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101
  remote_rc=127
  reached_backup_and_patch=false
  production_modified=false
  commit_flag_changed=false

diagnosis:
  classification=${CLASSIFICATION}
  missing_commands=${MISSING_COMMANDS}
  safe_pool_mtime_expression=${SAFE_MTIME}
  exact_prepatch_expressions_tested=true

safety:
  production_modified=false
  vm101_modified=false
  backup_created=false
  commit_flag_changed=false
  watcher_invoked=false
  real_refresh_ran=false
  rebalance_apply_ran=false
  direct_failopen_changed=false

plan:
  current_milestone=M06
  milestone_changed=false

next_step:
  STEP_050M06D_ENABLE_COMMIT_WITH_PROVEN_OPENWRT_COMMANDS

artifacts:
  step_script=step.sh
  vm101_script=vm101.sh
  previous_stderr=previous-vm101.stderr
  previous_stderr_json=previous-stderr.json
  vm101_diagnostic=vm101-diagnostic.txt
  diagnosis=diagnosis.json

TRYCF_REPORT=${TRYCF_REPORT}
REPORT_TXT=${REPORT_TXT}
FACTS_JSON=${FACTS_JSON}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

python3 - \
  "$STEP" \
  "$TS" \
  "$DECISION" \
  "$ALL_OK" \
  "$CLASSIFICATION" \
  "$MISSING_COMMANDS" \
  "$SAFE_MTIME" \
  "$TRYCF_REPORT" \
  "$REPORT_TXT" \
  "$FACTS_JSON" \
  "$ARCHITECTURE_PLAN" \
  "$XS_MAP" \
  "$GLOBAL_PROJECT_PLAN" \
  > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    step,
    timestamp,
    decision,
    all_ok,
    classification,
    missing_commands,
    safe_mtime,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": timestamp,
    "assessment": {
        "decision": decision,
        "all_ok": all_ok == "true",
        "classification": classification,
    },
    "diagnosis": {
        "previous_remote_rc": 127,
        "previous_reached_backup_and_patch": False,
        "previous_production_modified": False,
        "missing_commands": (
            []
            if missing_commands == "NONE"
            else missing_commands.split(",")
        ),
        "safe_pool_mtime_expression": safe_mtime,
    },
    "safety": {
        "production_modified": False,
        "vm101_modified": False,
        "backup_created": False,
        "commit_flag_changed": False,
        "watcher_invoked": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "plan": {
        "current_milestone": "M06",
        "milestone_changed": False,
        "next_step":
            "STEP_050M06D_ENABLE_COMMIT_WITH_PROVEN_OPENWRT_COMMANDS",
    },
    "artifacts": {
        "step_script": "step.sh",
        "vm101_script": "vm101.sh",
        "previous_stderr":
            "previous-vm101.stderr",
        "previous_stderr_json":
            "previous-stderr.json",
        "vm101_diagnostic":
            "vm101-diagnostic.txt",
        "diagnosis":
            "diagnosis.json",
    },
    "publish": {
        "trycf_report": report,
        "report_txt": report_txt,
        "facts_json": facts_json,
        "architecture_plan": architecture,
        "xs_map": xs_map,
        "global_project_plan": global_plan,
    },
}, ensure_ascii=False, indent=2))
PY

cat > "$REPORT_DIR/index.html" <<EOF
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>${STEP}</title>
</head>
<body style="font-family:system-ui;max-width:1050px;margin:40px auto;padding:0 20px;line-height:1.5">

<h1>${STEP}</h1>

<h2>Исполнявшиеся скрипты</h2>
<ul>
<li><a href="step.sh">step.sh</a></li>
<li><a href="vm101.sh">vm101.sh</a></li>
</ul>

<h2>Результаты</h2>
<ul>
<li><a href="report.txt">report.txt</a></li>
<li><a href="facts.json">facts.json</a></li>
<li><a href="diagnosis.json">diagnosis.json</a></li>
<li><a href="previous-vm101.stderr">previous-vm101.stderr</a></li>
<li><a href="previous-stderr.json">previous-stderr.json</a></li>
<li><a href="vm101-diagnostic.txt">vm101-diagnostic.txt</a></li>
<li><a href="vm101-diagnostic.stderr">vm101-diagnostic.stderr</a></li>
</ul>

<h2>Исходники M06C</h2>
<ul>
<li><a href="sources/step050m06c-report.txt">report.txt</a></li>
<li><a href="sources/step050m06c-facts.json">facts.json</a></li>
<li><a href="sources/step050m06c-step.sh">step.sh</a></li>
<li><a href="sources/step050m06c-vm101.sh">vm101.sh</a></li>
<li><a href="sources/step050m06c-vm101.txt">vm101.txt</a></li>
</ul>

<h2>Планы</h2>
<ul>
<li><a href="${ARCHITECTURE_PLAN}">Local architecture plan</a></li>
<li><a href="${XS_MAP}">XS Map</a></li>
<li><a href="${GLOBAL_PROJECT_PLAN}">Global project plan</a></li>
</ul>

</body>
</html>
EOF

find "$REPORT_DIR" \
  -type f \
  ! -name SHA256SUMS \
  -print0 |
  sort -z |
  xargs -0 sha256sum \
  > "$REPORT_DIR/SHA256SUMS"

stage "06/06" "Завершаю без изменений VM101"

trap - ERR

echo "decision=$DECISION" |
  tee -a "$PROGRESS_LOG"

echo "classification=$CLASSIFICATION" |
  tee -a "$PROGRESS_LOG"

echo "missing_commands=$MISSING_COMMANDS" |
  tee -a "$PROGRESS_LOG"

echo "safe_pool_mtime_expression=$SAFE_MTIME" |
  tee -a "$PROGRESS_LOG"

echo "production_modified=false" |
  tee -a "$PROGRESS_LOG"

print_links

[ "$ALL_OK" = true ]
