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

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

STEP="STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101"
PASS_DECISION="PASS_STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101"
PLAN_ID="vm101-hmn-autonomous-egress-recovery"

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

PREVIOUS_REPORT="${PUBLIC_BASE}/20260711-174717_step050m06b_enable_emergency_commit_vm101/"
PREVIOUS_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"
STATE_ROOT="${ROOT}/state"
PLAN_STATE_DIR="${STATE_ROOT}/local-plans/${PLAN_ID}"
PUBROOT="${ROOT}/public/r/${TOKEN}"

M06A_DIR="${PUBROOT}/20260711-173308_step050m06a_compare_live_toolchain_to_frozen_map"
M06B_DIR="${PUBROOT}/20260711-174717_step050m06b_enable_emergency_commit_vm101"

STATUS_TOOL="${ROOT}/bin/vm101-local-plan-status"
REPUBLISH_TOOL="${ROOT}/bin/vm101-local-plan-republish"

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

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

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

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

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

VM101_CHANGED=false
VM101_ROLLBACK=""
PLAN_UPDATED=false
PRODUCTION_MODIFIED=false
ROLLBACK_RC="NOT_NEEDED"
PLAN_RESTORE_RC="NOT_NEEDED"

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"
}

remote_vm101() {
  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 \
      '$1'"
}

rollback_vm101() {
  if [ "$VM101_CHANGED" != true ]; then
    ROLLBACK_RC="NOT_NEEDED"
    return 0
  fi

  if [ -z "$VM101_ROLLBACK" ]; then
    ROLLBACK_RC="ROLLBACK_PATH_MISSING"
    PRODUCTION_MODIFIED=unknown
    return 1
  fi

  if remote_vm101 \
    "sh '$VM101_ROLLBACK'" \
    > "$REPORT_DIR/vm101-rollback.txt" \
    2> "$REPORT_DIR/vm101-rollback.stderr"
  then
    VM101_CHANGED=false
    PRODUCTION_MODIFIED=false
    ROLLBACK_RC=0
    return 0
  fi

  ROLLBACK_RC=$?
  PRODUCTION_MODIFIED=unknown
  return "$ROLLBACK_RC"
}

restore_plan_state() {
  if [ "$PLAN_UPDATED" != true ]; then
    PLAN_RESTORE_RC="NOT_NEEDED"
    return 0
  fi

  set +e

  "$STATUS_TOOL" \
    M07 pending \
    --note "Ожидает завершения M06 после rollback." \
    > "$REPORT_DIR/plan-restore-m07.txt" \
    2> "$REPORT_DIR/plan-restore-m07.stderr"

  rc_m07=$?

  "$STATUS_TOOL" \
    M06 in_progress \
    --note "Изменение VM101 откачено; M06 снова в работе." \
    > "$REPORT_DIR/plan-restore-m06.txt" \
    2> "$REPORT_DIR/plan-restore-m06.stderr"

  rc_m06=$?

  restore_output="$(
    "$REPUBLISH_TOOL" \
      "ROLLBACK_${STEP}" \
      "$TRYCF_REPORT" \
      2> "$REPORT_DIR/plan-restore-republish.stderr"
  )"

  rc_publish=$?
  set -e

  restored_plan="$(
    printf '%s\n' "$restore_output" |
      sed -n 's/^ARCHITECTURE_PLAN=//p' |
      tail -n1
  )"

  [ -z "$restored_plan" ] ||
    ARCHITECTURE_PLAN="$restored_plan"

  if [ "$rc_m07" -eq 0 ] &&
     [ "$rc_m06" -eq 0 ] &&
     [ "$rc_publish" -eq 0 ]
  then
    PLAN_UPDATED=false
    PLAN_RESTORE_RC=0
    return 0
  fi

  PLAN_RESTORE_RC="M07:${rc_m07},M06:${rc_m06},PUBLISH:${rc_publish}"
  return 1
}

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=M06_THRESHOLD_AWARE_VM101_ONLY
error_rc=${rc}
error_line=${line}

scope:
  blocking_host=VM101
  vm100_checked=false
  vm121_checked=false

safety:
  production_modified=${PRODUCTION_MODIFIED}
  rollback_rc=${ROLLBACK_RC}
  plan_restore_rc=${PLAN_RESTORE_RC}
  watcher_invoked=false
  real_refresh_ran=false
  rebalance_apply_ran=false
  direct_failopen_changed=false

plan:
  milestone=M06
  milestone_completed=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" \
    "$PRODUCTION_MODIFIED" \
    "$ROLLBACK_RC" \
    "$PLAN_RESTORE_RC" \
    "$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,
    production_modified,
    rollback_rc,
    plan_restore_rc,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

modified = {
    "true": True,
    "false": False,
}.get(production_modified, "unknown")

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),
    },
    "scope": {
        "blocking_hosts": ["VM101"],
        "vm100_checked": False,
        "vm121_checked": False,
    },
    "safety": {
        "production_modified": modified,
        "rollback_rc": rollback_rc,
        "plan_restore_rc": plan_restore_rc,
        "watcher_invoked": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "plan": {
        "milestone": "M06",
        "milestone_completed": 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
}

fail_step() {
  local reason="$1"
  local rc="${2:-1}"
  local line="${3:-$LINENO}"

  trap - ERR

  rollback_vm101 || true
  restore_plan_state || true

  write_stop "$reason" "$rc" "$line"
  print_links
  exit "$rc"
}

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

  fail_step "UNEXPECTED_ERROR" "$rc" "$line"
}

trap 'on_error "$LINENO"' ERR

stage "01/09" "Изучаю предыдущий STOP, frozen map и текущий план"

for required in \
  "$M06A_DIR/report.txt" \
  "$M06A_DIR/facts.json" \
  "$M06A_DIR/comparison.json" \
  "$M06A_DIR/vm101.sh" \
  "$M06B_DIR/report.txt" \
  "$M06B_DIR/facts.json" \
  "$M06B_DIR/assessment.json" \
  "$M06B_DIR/vm101.txt" \
  "$M06B_DIR/vm101-rollback.txt" \
  "$M06B_DIR/step.sh" \
  "$PLAN_STATE_DIR/canonical-plan.md" \
  "$PLAN_STATE_DIR/milestones.json" \
  "$PLAN_STATE_DIR/render_plan.py" \
  "$PLAN_STATE_DIR/set_status.py" \
  "$PLAN_STATE_DIR/republish.sh" \
  "$STATUS_TOOL" \
  "$REPUBLISH_TOOL" \
  "$STATE_ROOT/current-local-architecture-plan-url.txt"
do
  [ -s "$required" ] || {
    echo "MISSING_REQUIRED=$required"
    fail_step "REQUIRED_ARTIFACT_MISSING" 2 "$LINENO"
  }
done

grep -Fq \
  "toolchain_exact_match=true" \
  "$M06A_DIR/report.txt" ||
  fail_step "FROZEN_TOOLCHAIN_NOT_PROVEN" 3 "$LINENO"

grep -Fq \
  "decision=STOP_STEP_050M06B_ENABLE_EMERGENCY_COMMIT_VM101" \
  "$M06B_DIR/report.txt" ||
  fail_step "PREVIOUS_M06B_NOT_EXPECTED_STOP" 4 "$LINENO"

grep -Fq \
  "production_modified=false" \
  "$M06B_DIR/report.txt" ||
  fail_step "PREVIOUS_M06B_ROLLBACK_NOT_PROVEN" 5 "$LINENO"

grep -Fq \
  "rollback_rc=0" \
  "$M06B_DIR/report.txt" ||
  fail_step "PREVIOUS_M06B_ROLLBACK_RC_NOT_ZERO" 6 "$LINENO"

python3 - \
  "$M06B_DIR/assessment.json" <<'PY'
import json
import sys

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

assert data["all_ok"] is False

assert set(data["failed_checks"]) == {
    "runner_pre_safe",
    "hook_pre_safe",
    "runner_post_safe",
    "watchers_healthy",
}

runtime = data["runtime"]

assert runtime["commit_enabled_pre"] == "false"
assert runtime["commit_enabled_post"] == "true"
assert runtime["daily_counter_pre"] == "5"
assert runtime["daily_counter_post"] == "5"

assert data["checks"]["threshold_safe"] is True
assert data["checks"]["commit_transition"] is True
assert data["checks"]["hook_trace_dryrun"] is True
assert data["checks"]["scheduler_safe"] is True
assert data["checks"]["no_real_actions"] is True
assert data["checks"]["strict_all"] is True
assert data["checks"]["routes_all"] is True
assert data["checks"]["network_unchanged"] is True
assert data["checks"]["hook_unchanged"] is True
assert data["checks"]["runner_unchanged"] is True
assert data["checks"]["rollback_available"] is True
PY

CURRENT_PLAN="$(
  tr -d '\r\n' \
    < "$STATE_ROOT/current-local-architecture-plan-url.txt"
)"

[ "$CURRENT_PLAN" = "$PREVIOUS_ARCHITECTURE_PLAN" ] ||
  fail_step "CURRENT_PLAN_POINTER_MISMATCH" 7 "$LINENO"

python3 - \
  "$PLAN_STATE_DIR/milestones.json" <<'PY'
import json
import sys

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

items = {
    item["id"]: item
    for item in data["milestones"]
}

assert data["scope"] == ["VM101"]
assert data["current_milestone"] == "M06"
assert items["M06"]["status"] == "in_progress"
assert items["M07"]["status"] == "pending"
PY

cp -a \
  "$M06A_DIR/comparison.json" \
  "$REPORT_DIR/sources/step050m06a-comparison.json"

cp -a \
  "$M06B_DIR/assessment.json" \
  "$REPORT_DIR/sources/step050m06b-assessment.json"

cp -a \
  "$M06B_DIR/vm101.txt" \
  "$REPORT_DIR/sources/step050m06b-vm101.txt"

cp -a \
  "$M06B_DIR/step.sh" \
  "$REPORT_DIR/sources/step050m06b-step.sh"

stage "02/09" "Публикую threshold-aware VM101-скрипт"

cat > "$REPORT_DIR/vm101.sh" <<'VM101'
#!/bin/sh
set -eu
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"
HELPER="/usr/local/lib/router-egress-recovery-state.sh"

HOOK_INIT="/etc/init.d/router-egress-emergency-decision"
WATCHER_INIT="/etc/init.d/router-egress-health-repair"
CRON="/etc/crontabs/root"
POOL="/root/hmn/cache/ok-awg1-strict-foreign-latest.tsv"

PATCHED=false
ROLLBACK=""

trace() {
  printf '__TRACE__ stage=%s\n' "$1"
}

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

json_block() {
  echo "__JSON_BEGIN__ $1"
  printf '%s\n' "$2"
  echo "__JSON_END__ $1"
}

bool_cmd() {
  if "$@" >/dev/null 2>&1; then
    printf true
  else
    printf false
  fi
}

commit_value() {
  (
    unset EMERGENCY_COMMIT_ENABLED
    . "$CONF"

    case "${EMERGENCY_COMMIT_ENABLED:-0}" in
      1|true|TRUE|yes|YES|on|ON)
        printf true
        ;;
      *)
        printf false
        ;;
    esac
  )
}

repair_counter() {
  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_daily_repair_get
  )
}

strict_ping() {
  interface="$1"
  attempt=1

  while [ "$attempt" -le 3 ]; do
    if ping \
      -I "$interface" \
      -c 1 \
      -W 4 \
      1.1.1.1 \
      >/dev/null 2>&1
    then
      return 0
    fi

    attempt=$((attempt + 1))
    sleep 1
  done

  return 1
}

endpoint_value() {
  interface="$1"

  wg show "$interface" endpoints 2>/dev/null |
    awk '
      NF >= 2 {
        print $NF
        exit
      }
    '
}

rollback_on_error() {
  rc="$?"

  trap - EXIT

  if [ "$rc" -ne 0 ] &&
     [ "$PATCHED" = true ] &&
     [ -n "$ROLLBACK" ] &&
     [ -x "$ROLLBACK" ]
  then
    if sh "$ROLLBACK"; then
      echo "__AUTO_ROLLBACK__ restored=true"
    else
      echo "__AUTO_ROLLBACK__ restored=false"
    fi
  fi

  exit "$rc"
}

trap rollback_on_error EXIT

trace required_files

for path in \
  "$CONF" \
  "$RUNNER" \
  "$HOOK" \
  "$PLANNER" \
  "$HELPER" \
  "$HOOK_INIT" \
  "$WATCHER_INIT" \
  "$CRON" \
  "$POOL"
do
  [ -e "$path" ] || {
    echo "__ERROR__ missing=$path"
    exit 21
  }
done

trace scheduler_contract

fact hook_running "$(bool_cmd "$HOOK_INIT" running)"
fact hook_enabled "$(bool_cmd "$HOOK_INIT" enabled)"
fact watcher_running "$(bool_cmd "$WATCHER_INIT" running)"
fact watcher_enabled "$(bool_cmd "$WATCHER_INIT" enabled)"

SCHEDULER_LINES="$(
  grep -Ec \
    'router-egress-emergency-decision-hook\.sh' \
    "$CRON" ||
  true
)"

HOOK_COMMIT_REFS="$(
  grep -Ec -- \
    '--commit|EMERGENCY_COMMIT_ENABLED' \
    "$HOOK" ||
  true
)"

fact emergency_scheduler_lines "$SCHEDULER_LINES"
fact hook_commit_flag_refs "$HOOK_COMMIT_REFS"

trace pre_state

COMMIT_PRE="$(commit_value)"
COUNTER_PRE="$(repair_counter)"

RUNNER_PRE="$("$RUNNER" --dry-run)"
HOOK_PRE="$("$HOOK")"
PLANNER_PRE="$("$PLANNER")"

fact commit_enabled_pre "$COMMIT_PRE"
fact repair_counter_pre "$COUNTER_PRE"

json_block runner_pre "$RUNNER_PRE"
json_block hook_pre "$HOOK_PRE"
json_block planner_pre "$PLANNER_PRE"

CONFIG_HASH_PRE="$(
  sha256sum "$CONF" |
    sed 's/[[:space:]].*$//'
)"

RUNNER_HASH_PRE="$(
  sha256sum "$RUNNER" |
    sed 's/[[:space:]].*$//'
)"

HOOK_HASH_PRE="$(
  sha256sum "$HOOK" |
    sed 's/[[:space:]].*$//'
)"

NETWORK_HASH_PRE="$(
  uci export network |
    sha256sum |
    sed 's/[[:space:]].*$//'
)"

POOL_HASH_PRE="$(
  sha256sum "$POOL" |
    sed 's/[[:space:]].*$//'
)"

POOL_MTIME_PRE="$(
  stat -c '%Y' "$POOL"
)"

fact config_hash_pre "$CONFIG_HASH_PRE"
fact runner_hash_pre "$RUNNER_HASH_PRE"
fact hook_hash_pre "$HOOK_HASH_PRE"
fact network_hash_pre "$NETWORK_HASH_PRE"
fact pool_hash_pre "$POOL_HASH_PRE"
fact pool_mtime_pre "$POOL_MTIME_PRE"

STRICT_PRE_ALL=true

for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
  endpoint="$(endpoint_value "$interface")"

  [ -n "$endpoint" ] ||
    endpoint="UNRESOLVED"

  fact "endpoint_pre.${interface}" "$endpoint"

  if strict_ping "$interface"; then
    strict=true
  else
    strict=false
    STRICT_PRE_ALL=false
  fi

  fact "strict_pre.${interface}" "$strict"
done

fact strict_pre_all "$STRICT_PRE_ALL"

ROUTES_PRE_ALL=true

for table in 201 202 203 204 205; do
  if ip route show table "$table" 2>/dev/null |
    grep -q '^default '
  then
    route=true
  else
    route=false
    ROUTES_PRE_ALL=false
  fi

  fact "route_pre.${table}" "$route"
done

fact routes_pre_all "$ROUTES_PRE_ALL"

trace backup_and_patch

BACKUP="${CONF}.step050m06c.${$}.${RANDOM:-0}.bak"
TEMP="${CONF}.step050m06c.${$}.${RANDOM:-0}.tmp"
ROLLBACK="/root/rollback-step050m06c-$(date -u +%Y%m%d-%H%M%S).sh"

cp -a "$CONF" "$BACKUP"

cat > "$ROLLBACK" <<EOF
#!/bin/sh
set -eu
cp -a '$BACKUP' '$CONF'
echo 'STEP_050M06C config restored.'
EOF

chmod 700 "$ROLLBACK"

awk '
  BEGIN {
    replaced=0
  }

  /^[[:space:]]*EMERGENCY_COMMIT_ENABLED[[:space:]]*=/ {
    print "EMERGENCY_COMMIT_ENABLED=1"
    replaced++
    next
  }

  {
    print
  }

  END {
    if (replaced != 1) {
      exit 42
    }
  }
' "$CONF" > "$TEMP"

chmod "$(
  stat -c '%a' "$CONF"
)" "$TEMP"

chown "$(
  stat -c '%u:%g' "$CONF"
)" "$TEMP"

mv "$TEMP" "$CONF"

PATCHED=true

fact rollback "$ROLLBACK"
fact rollback_exists "$(bool_cmd test -x "$ROLLBACK")"

trace post_state

COMMIT_POST="$(commit_value)"
COUNTER_POST="$(repair_counter)"

RUNNER_POST="$("$RUNNER" --dry-run)"
HOOK_POST="$("$HOOK")"
PLANNER_POST="$("$PLANNER")"

fact commit_enabled_post "$COMMIT_POST"
fact repair_counter_post "$COUNTER_POST"

json_block runner_post "$RUNNER_POST"
json_block hook_post "$HOOK_POST"
json_block planner_post "$PLANNER_POST"

CONFIG_HASH_POST="$(
  sha256sum "$CONF" |
    sed 's/[[:space:]].*$//'
)"

RUNNER_HASH_POST="$(
  sha256sum "$RUNNER" |
    sed 's/[[:space:]].*$//'
)"

HOOK_HASH_POST="$(
  sha256sum "$HOOK" |
    sed 's/[[:space:]].*$//'
)"

NETWORK_HASH_POST="$(
  uci export network |
    sha256sum |
    sed 's/[[:space:]].*$//'
)"

POOL_HASH_POST="$(
  sha256sum "$POOL" |
    sed 's/[[:space:]].*$//'
)"

POOL_MTIME_POST="$(
  stat -c '%Y' "$POOL"
)"

fact config_hash_post "$CONFIG_HASH_POST"
fact runner_hash_post "$RUNNER_HASH_POST"
fact hook_hash_post "$HOOK_HASH_POST"
fact network_hash_post "$NETWORK_HASH_POST"
fact pool_hash_post "$POOL_HASH_POST"
fact pool_mtime_post "$POOL_MTIME_POST"

STRICT_POST_ALL=true

for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
  endpoint="$(endpoint_value "$interface")"

  [ -n "$endpoint" ] ||
    endpoint="UNRESOLVED"

  fact "endpoint_post.${interface}" "$endpoint"

  if strict_ping "$interface"; then
    strict=true
  else
    strict=false
    STRICT_POST_ALL=false
  fi

  fact "strict_post.${interface}" "$strict"
done

fact strict_post_all "$STRICT_POST_ALL"

ROUTES_POST_ALL=true

for table in 201 202 203 204 205; do
  if ip route show table "$table" 2>/dev/null |
    grep -q '^default '
  then
    route=true
  else
    route=false
    ROUTES_POST_ALL=false
  fi

  fact "route_post.${table}" "$route"
done

fact routes_post_all "$ROUTES_POST_ALL"

fact watcher_invoked false
fact commit_invoked false
fact refresh_ran false
fact rebalance_apply_ran false
fact direct_failopen_changed false

trace complete

trap - EXIT
exit 0
VM101

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

stage "03/09" "Повторно проверяю frozen toolchain перед изменением"

cp -a \
  "$M06A_DIR/vm101.sh" \
  "$REPORT_DIR/vm101-tool-hash-check.sh"

chmod 600 "$REPORT_DIR/vm101-tool-hash-check.sh"
sh -n "$REPORT_DIR/vm101-tool-hash-check.sh"

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-tool-hash-check.sh" \
  > "$REPORT_DIR/vm101-tool-hash-check.txt" \
  2> "$REPORT_DIR/vm101-tool-hash-check.stderr"
then
  HASH_RC=0
else
  HASH_RC=$?
fi

[ "$HASH_RC" -eq 0 ] ||
  fail_step "FROZEN_TOOLCHAIN_REMOTE_CHECK_FAILED" "$HASH_RC" "$LINENO"

grep -Fq \
  "__SUMMARY__ checked=10 errors=0" \
  "$REPORT_DIR/vm101-tool-hash-check.txt" ||
  fail_step "LIVE_TOOLCHAIN_DRIFT_DETECTED" 8 "$LINENO"

stage "04/09" "Выполняю threshold-aware изменение 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.txt" \
  2> "$REPORT_DIR/vm101.stderr"
then
  VM101_RC=0
else
  VM101_RC=$?
fi

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

VM101_ROLLBACK="$(
  grep '^__FACT__ rollback=' \
    "$REPORT_DIR/vm101.txt" |
    tail -n1 |
    cut -d= -f2- ||
    true
)"

if [ "$VM101_RC" -ne 0 ]; then
  if grep -Fq \
    '__AUTO_ROLLBACK__ restored=true' \
    "$REPORT_DIR/vm101.txt"
  then
    PRODUCTION_MODIFIED=false
    ROLLBACK_RC="REMOTE_AUTO_ROLLBACK_OK"
  elif grep -Fq \
    '__TRACE__ stage=post_state' \
    "$REPORT_DIR/vm101.txt"
  then
    VM101_CHANGED=true
    PRODUCTION_MODIFIED=true
  fi

  fail_step "VM101_REMOTE_FAILED" "$VM101_RC" "$LINENO"
fi

[ -n "$VM101_ROLLBACK" ] ||
  fail_step "VM101_ROLLBACK_PATH_MISSING" 9 "$LINENO"

VM101_CHANGED=true
PRODUCTION_MODIFIED=true

stage "05/09" "Оцениваю threshold-aware инварианты"

python3 - \
  "$REPORT_DIR/vm101.txt" \
  "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys
from pathlib import Path

source_path, output_path = sys.argv[1:]

facts = {}
blocks = {}
traces = []
errors = []

current_block = None
block_lines = []

for line in Path(source_path).read_text(
    encoding="utf-8",
    errors="replace",
).splitlines():
    if line.startswith("__TRACE__ "):
        traces.append(line)
        continue

    if line.startswith("__ERROR__ "):
        errors.append(line)
        continue

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

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

        continue

    if line.startswith("__JSON_BEGIN__ "):
        current_block = line[len("__JSON_BEGIN__ "):].strip()
        block_lines = []
        continue

    if line.startswith("__JSON_END__ "):
        name = line[len("__JSON_END__ "):].strip()

        if name == current_block:
            blocks[name] = json.loads(
                "\n".join(block_lines)
            )

        current_block = None
        block_lines = []
        continue

    if current_block is not None:
        block_lines.append(line)

runner_pre = blocks.get("runner_pre", {})
runner_post = blocks.get("runner_post", {})
hook_pre = blocks.get("hook_pre", {})
hook_post = blocks.get("hook_post", {})
planner_pre = blocks.get("planner_pre", {})
planner_post = blocks.get("planner_post", {})

counter_pre = int(facts.get("repair_counter_pre", "-1"))
counter_post = int(facts.get("repair_counter_post", "-1"))

threshold = runner_pre.get("daily_fail_threshold")
threshold_reached = runner_pre.get("threshold_reached")

currents_pre = {
    item.get("iface"): item.get("current")
    for item in planner_pre.get("plan", [])
}

currents_post = {
    item.get("iface"): item.get("current")
    for item in planner_post.get("plan", [])
}

endpoints_pre = {
    interface: facts.get(f"endpoint_pre.{interface}")
    for interface in ("vpn1", "vpn2", "vpn3", "vpn4", "vpn5")
}

endpoints_post = {
    interface: facts.get(f"endpoint_post.{interface}")
    for interface in ("vpn1", "vpn2", "vpn3", "vpn4", "vpn5")
}

strict_pre = {
    interface: facts.get(f"strict_pre.{interface}") == "true"
    for interface in ("vpn1", "vpn2", "vpn3", "vpn4", "vpn5")
}

strict_post = {
    interface: facts.get(f"strict_post.{interface}") == "true"
    for interface in ("vpn1", "vpn2", "vpn3", "vpn4", "vpn5")
}

routes_pre = {
    table: facts.get(f"route_pre.{table}") == "true"
    for table in ("201", "202", "203", "204", "205")
}

routes_post = {
    table: facts.get(f"route_post.{table}") == "true"
    for table in ("201", "202", "203", "204", "205")
}

checks = {
    "remote_complete":
        "__TRACE__ stage=complete" in traces
        and not errors,

    "services_running_enabled":
        facts.get("hook_running") == "true"
        and facts.get("hook_enabled") == "true"
        and facts.get("watcher_running") == "true"
        and facts.get("watcher_enabled") == "true",

    "scheduler_present":
        int(facts.get("emergency_scheduler_lines", "0")) >= 1,

    "hook_has_no_commit_path":
        facts.get("hook_commit_flag_refs") == "0",

    "commit_transition":
        facts.get("commit_enabled_pre") == "false"
        and facts.get("commit_enabled_post") == "true",

    "counter_valid":
        counter_pre >= 0
        and counter_pre == counter_post,

    "threshold_currently_reached":
        threshold_reached is True
        and isinstance(threshold, int)
        and counter_pre >= threshold,

    "runner_pre_threshold_safe":
        runner_pre.get("mode") == "dry-run"
        and runner_pre.get("decision")
            == "would_run_emergency_refresh"
        and runner_pre.get("commit_enabled") is False
        and runner_pre.get("direct_failopen_enabled") is False,

    "hook_pre_threshold_safe":
        hook_pre.get("mode") == "dry-run"
        and hook_pre.get("decision")
            == "would_run_emergency_refresh"
        and hook_pre.get("commit_enabled") is False
        and hook_pre.get("direct_failopen_enabled") is False,

    "runner_post_threshold_safe":
        runner_post.get("mode") == "dry-run"
        and runner_post.get("decision")
            == "would_run_emergency_refresh"
        and runner_post.get("commit_enabled") is True
        and runner_post.get("direct_failopen_enabled") is False,

    "hook_post_threshold_safe":
        hook_post.get("mode") == "dry-run"
        and hook_post.get("decision")
            == "would_run_emergency_refresh"
        and hook_post.get("commit_enabled") is True
        and hook_post.get("direct_failopen_enabled") is False,

    "emergency_state_unchanged":
        runner_pre.get("last_emergency_refresh_status")
            == runner_post.get("last_emergency_refresh_status")
        and runner_pre.get("last_emergency_refresh_epoch")
            == runner_post.get("last_emergency_refresh_epoch"),

    "planner_currents_preserved":
        len(currents_pre) == 5
        and currents_pre == currents_post,

    "planner_quarantine_preserved":
        planner_pre.get("quarantine_enabled") is True
        and planner_post.get("quarantine_enabled") is True,

    "endpoints_preserved":
        endpoints_pre == endpoints_post
        and all(endpoints_pre.values()),

    "strict_pre_all":
        all(strict_pre.values())
        and facts.get("strict_pre_all") == "true",

    "strict_post_all":
        all(strict_post.values())
        and facts.get("strict_post_all") == "true",

    "routes_pre_all":
        all(routes_pre.values())
        and facts.get("routes_pre_all") == "true",

    "routes_post_all":
        all(routes_post.values())
        and facts.get("routes_post_all") == "true",

    "network_unchanged":
        facts.get("network_hash_pre")
            == facts.get("network_hash_post"),

    "runner_unchanged":
        facts.get("runner_hash_pre")
            == facts.get("runner_hash_post"),

    "hook_unchanged":
        facts.get("hook_hash_pre")
            == facts.get("hook_hash_post"),

    "config_changed":
        facts.get("config_hash_pre")
            != facts.get("config_hash_post"),

    "pool_unchanged":
        facts.get("pool_hash_pre")
            == facts.get("pool_hash_post")
        and facts.get("pool_mtime_pre")
            == facts.get("pool_mtime_post"),

    "watcher_not_invoked":
        facts.get("watcher_invoked") == "false",

    "no_real_actions":
        facts.get("commit_invoked") == "false"
        and facts.get("refresh_ran") == "false"
        and facts.get("rebalance_apply_ran") == "false"
        and facts.get("direct_failopen_changed") == "false",

    "rollback_available":
        facts.get("rollback_exists") == "true"
        and bool(facts.get("rollback")),
}

all_ok = all(checks.values())

assessment = {
    "all_ok": all_ok,
    "decision": (
        "PASS_STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101"
        if all_ok
        else
        "STOP_STEP_050M06C_ENABLE_COMMIT_THRESHOLD_AWARE_VM101"
    ),
    "checks": checks,
    "failed_checks": [
        name
        for name, value in checks.items()
        if not value
    ],
    "scope": {
        "blocking_hosts": ["VM101"],
        "vm100_checked": False,
        "vm121_checked": False,
    },
    "runtime": {
        "repair_counter": counter_pre,
        "daily_fail_threshold": threshold,
        "threshold_reached": threshold_reached,
        "commit_enabled_before": False,
        "commit_enabled_after": True,
        "endpoints": endpoints_post,
        "strict": strict_post,
        "routes": routes_post,
        "rollback": facts.get("rollback"),
    },
    "safety": {
        "watcher_invoked": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "plan_transition": {
        "completed": "M06",
        "next": "M07",
    },
}

Path(output_path).write_text(
    json.dumps(
        assessment,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)
PY

ALL_OK="$(
  python3 - "$REPORT_DIR/assessment.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
)"

DECISION="$(
  python3 - "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

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

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

FAILED_CHECKS="$(
  python3 - "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

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

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

[ "$ALL_OK" = true ] ||
  fail_step "VALIDATION_FAILED_${FAILED_CHECKS}" 10 "$LINENO"

stage "06/09" "Проверяю persisted commit отдельным read-only скриптом"

cat > "$REPORT_DIR/vm101-postcheck.sh" <<'VM101POST'
#!/bin/sh
set -eu
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"
HELPER="/usr/local/lib/router-egress-recovery-state.sh"

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

json_block() {
  echo "__JSON_BEGIN__ $1"
  printf '%s\n' "$2"
  echo "__JSON_END__ $1"
}

COMMIT="$(
  (
    unset EMERGENCY_COMMIT_ENABLED
    . "$CONF"

    case "${EMERGENCY_COMMIT_ENABLED:-0}" in
      1|true|TRUE|yes|YES|on|ON)
        printf true
        ;;
      *)
        printf false
        ;;
    esac
  )
)"

COUNTER="$(
  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_daily_repair_get
  )
)"

RUNNER_JSON="$("$RUNNER" --dry-run)"
HOOK_JSON="$("$HOOK")"

fact commit_enabled "$COMMIT"
fact repair_counter "$COUNTER"

json_block runner "$RUNNER_JSON"
json_block hook "$HOOK_JSON"

fact watcher_invoked false
fact real_refresh_ran false
fact rebalance_apply_ran false
fact direct_failopen_changed false
VM101POST

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

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-postcheck.sh" \
  > "$REPORT_DIR/vm101-postcheck.txt" \
  2> "$REPORT_DIR/vm101-postcheck.stderr"
then
  POSTCHECK_RC=0
else
  POSTCHECK_RC=$?
fi

[ "$POSTCHECK_RC" -eq 0 ] ||
  fail_step "PERSISTED_POSTCHECK_REMOTE_FAILED" "$POSTCHECK_RC" "$LINENO"

python3 - \
  "$REPORT_DIR/vm101-postcheck.txt" <<'PY'
import json
import sys
from pathlib import Path

facts = {}
blocks = {}
current = None
lines = []

for line in Path(sys.argv[1]).read_text(
    encoding="utf-8",
    errors="replace",
).splitlines():
    if line.startswith("__FACT__ "):
        payload = line[len("__FACT__ "):]

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

    elif line.startswith("__JSON_BEGIN__ "):
        current = line[len("__JSON_BEGIN__ "):]
        lines = []

    elif line.startswith("__JSON_END__ "):
        name = line[len("__JSON_END__ "):]

        if name == current:
            blocks[name] = json.loads("\n".join(lines))

        current = None
        lines = []

    elif current is not None:
        lines.append(line)

runner = blocks["runner"]
hook = blocks["hook"]

assert facts["commit_enabled"] == "true"

assert runner["mode"] == "dry-run"
assert runner["decision"] == "would_run_emergency_refresh"
assert runner["threshold_reached"] is True
assert runner["commit_enabled"] is True
assert runner["direct_failopen_enabled"] is False

assert hook["mode"] == "dry-run"
assert hook["decision"] == "would_run_emergency_refresh"
assert hook["threshold_reached"] is True
assert hook["commit_enabled"] is True
assert hook["direct_failopen_enabled"] is False

assert facts["watcher_invoked"] == "false"
assert facts["real_refresh_ran"] == "false"
assert facts["rebalance_apply_ran"] == "false"
assert facts["direct_failopen_changed"] == "false"
PY

stage "07/09" "Отмечаю M06 выполненным и переиздаю план"

"$STATUS_TOOL" \
  M06 done \
  --evidence "$TRYCF_REPORT" \
  --note "Emergency commit включён на VM101 при уже достигнутом repair threshold. Автоматический hook доказан как dry-run-only; refresh, rebalance и Direct не запускались." \
  > "$REPORT_DIR/plan-status-m06.txt" \
  2> "$REPORT_DIR/plan-status-m06.stderr"

PLAN_UPDATED=true

REPUBLISH_OUTPUT="$(
  "$REPUBLISH_TOOL" \
    "$STEP" \
    "$TRYCF_REPORT"
)"

printf '%s\n' \
  "$REPUBLISH_OUTPUT" \
  > "$REPORT_DIR/plan-republish.txt"

NEW_ARCHITECTURE_PLAN="$(
  printf '%s\n' "$REPUBLISH_OUTPUT" |
    sed -n 's/^ARCHITECTURE_PLAN=//p' |
    tail -n1
)"

[ -n "$NEW_ARCHITECTURE_PLAN" ] ||
  fail_step "PLAN_REPUBLISH_URL_MISSING" 11 "$LINENO"

ARCHITECTURE_PLAN="$NEW_ARCHITECTURE_PLAN"

python3 - \
  "$PLAN_STATE_DIR/milestones.json" <<'PY'
import json
import sys

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

items = {
    item["id"]: item
    for item in data["milestones"]
}

assert items["M06"]["status"] == "done"
assert items["M07"]["status"] == "in_progress"
assert data["current_milestone"] == "M07"
PY

stage "08/09" "Публикую PASS report и facts"

REPAIR_COUNTER="$(
  python3 - "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

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

print(data["runtime"]["repair_counter"])
PY
)"

FAIL_THRESHOLD="$(
  python3 - "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

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

print(data["runtime"]["daily_fail_threshold"])
PY
)"

cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${PASS_DECISION}
all_ok=true
mode=M06_THRESHOLD_AWARE_VM101_ONLY

scope:
  blocking_host=VM101
  vm100_checked=false
  vm121_checked=false

implementation:
  frozen_toolchain_rechecked=true
  previous_stop_classified=STALE_BELOW_THRESHOLD_EXPECTATIONS
  repair_counter=${REPAIR_COUNTER}
  daily_fail_threshold=${FAIL_THRESHOLD}
  threshold_reached=true
  emergency_commit_enabled_before=false
  emergency_commit_enabled_after=true
  persisted_postcheck=true

scheduler_safety:
  automatic_hook_mode=dry-run
  hook_commit_path=false
  watcher_invoked=false

safety:
  production_modified=true
  endpoints_changed=false
  network_config_changed=false
  runner_changed=false
  hook_changed=false
  pool_changed=false
  repair_counter_changed=false
  real_refresh_ran=false
  rebalance_apply_ran=false
  direct_failopen_changed=false
  rollback_vm101=${VM101_ROLLBACK}

plan:
  milestone_completed=M06
  current_milestone=M07
  plan_republished=true

next_step:
  M07_READONLY_REVIEW_REAL_FULL_HMN_REFRESH_CONTRACT

artifacts:
  step_script=step.sh
  tool_hash_script=vm101-tool-hash-check.sh
  executed_vm101_script=vm101.sh
  postcheck_script=vm101-postcheck.sh
  assessment=assessment.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 - \
  "$REPORT_DIR/assessment.json" \
  "$STEP" \
  "$TS" \
  "$VM101_ROLLBACK" \
  "$PREVIOUS_REPORT" \
  "$TRYCF_REPORT" \
  "$REPORT_TXT" \
  "$FACTS_JSON" \
  "$ARCHITECTURE_PLAN" \
  "$XS_MAP" \
  "$GLOBAL_PROJECT_PLAN" \
  > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    assessment_path,
    step,
    timestamp,
    rollback,
    previous_report,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

with open(assessment_path, encoding="utf-8") as source:
    assessment = json.load(source)

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": timestamp,
    "assessment": assessment,
    "classification": {
        "previous_stop":
            "STALE_BELOW_THRESHOLD_EXPECTATIONS",
        "threshold_aware": True,
    },
    "scope": {
        "blocking_hosts": ["VM101"],
        "vm100_checked": False,
        "vm121_checked": False,
    },
    "implementation": {
        "emergency_commit_enabled_before": False,
        "emergency_commit_enabled_after": True,
        "persisted_postcheck": True,
        "rollback_vm101": rollback,
    },
    "scheduler_safety": {
        "automatic_hook_mode": "dry-run",
        "hook_commit_path": False,
        "watcher_invoked": False,
    },
    "safety": {
        "production_modified": True,
        "endpoints_changed": False,
        "network_config_changed": False,
        "runner_changed": False,
        "hook_changed": False,
        "pool_changed": False,
        "repair_counter_changed": False,
        "real_refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_changed": False,
    },
    "plan": {
        "milestone_completed": "M06",
        "current_milestone": "M07",
        "plan_republished": True,
    },
    "next_step":
        "M07_READONLY_REVIEW_REAL_FULL_HMN_REFRESH_CONTRACT",
    "source": {
        "step050m06b": previous_report,
    },
    "artifacts": {
        "step_script": "step.sh",
        "tool_hash_script":
            "vm101-tool-hash-check.sh",
        "executed_vm101_script":
            "vm101.sh",
        "postcheck_script":
            "vm101-postcheck.sh",
        "assessment":
            "assessment.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:1100px;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-tool-hash-check.sh">vm101-tool-hash-check.sh</a></li>
<li><a href="vm101.sh">vm101.sh</a></li>
<li><a href="vm101-postcheck.sh">vm101-postcheck.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="assessment.json">assessment.json</a></li>
<li><a href="vm101.txt">VM101 execution</a></li>
<li><a href="vm101-postcheck.txt">Persisted postcheck</a></li>
<li><a href="plan-status-m06.txt">Plan status</a></li>
<li><a href="plan-republish.txt">Plan republish</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"

cat > "$STATE_ROOT/current-project-links.env" <<EOF
UPDATED_AT_UTC=${TS}
SOURCE_STEP=${STEP}
CURRENT_REPORT=${TRYCF_REPORT}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

stage "09/09" "Завершаю M06"

VM101_CHANGED=false
PLAN_UPDATED=false

trap - ERR

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

echo "milestone_completed=M06" |
  tee -a "$PROGRESS_LOG"

echo "current_milestone=M07" |
  tee -a "$PROGRESS_LOG"

echo "repair_counter=$REPAIR_COUNTER" |
  tee -a "$PROGRESS_LOG"

echo "daily_fail_threshold=$FAIL_THRESHOLD" |
  tee -a "$PROGRESS_LOG"

echo "emergency_commit_enabled=true" |
  tee -a "$PROGRESS_LOG"

echo "automatic_hook_mode=dry-run" |
  tee -a "$PROGRESS_LOG"

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

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

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

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

print_links
