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

STEP_ID="STEP_050M07R15B1_GENERATION_SCHEMA_VALIDATOR_R02S_PUBLICATION_SALVAGE_R02_REBUILT"
BASE_STEP_ID="STEP_050M07R15B1_GENERATION_SCHEMA_VALIDATOR_R02_REBUILT"
EXPECTED_BEFORE_COMMIT="c737f6b40fbf882950e343b44c98ab26dfd01e9f"
EXPECTED_BEFORE_TREE="f79773d8235c88bfcb1d69a4baecf857504adb95"
EXPECTED_BEFORE_COUNT="29"
EXPECTED_AFTER_COUNT="32"
EXPECTED_CONF_SHA="d9013d65e3b2538c0cfddc98d0585d71616bd847b89e68ef776ac9ac3780c254"
EXPECTED_LIB_SHA="cfdb352742aa83f35303b87cfc367991892f82f71fc34e841a8aafaa3e84440d"
EXPECTED_VALIDATOR_SHA="69c8215bb6dc54f8730afe6b6e7f8bb6e8a28a753faa741d371292b8b8d5dddf"
EXPECTED_R02_INSTALLER_SHA="975826884e765440c3f648049d6c692d6381f23c84bbccca352c5a1b90ae0707"
EXPECTED_PUBLISHER_SHA="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"

PUBLISHER="/opt/router-ops/bin/router-publish-report"
REPOSITORY="https://github.com/DChuvelev/wg-paid-vm101.git"
PERMANENT_ROOT="https://reports.secret-studio.ru"
RESOLVE="reports.secret-studio.ru:443:10.71.100.84"

TARGET_BASE="/root/router-steps/$BASE_STEP_ID"
TARGET_RUNTIME_DIR="$TARGET_BASE/extracted/target-step"
TARGET_RUN_LOG="$TARGET_BASE/run.log"

BUNDLE_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
BUNDLE_ZIP="${BUNDLE_ZIP:?BUNDLE_ZIP required}"
EXPECTED_BUNDLE_SHA256="${EXPECTED_BUNDLE_SHA256:?EXPECTED_BUNDLE_SHA256 required}"
EXPECTED_INSTALLER_SHA256="${EXPECTED_INSTALLER_SHA256:?EXPECTED_INSTALLER_SHA256 required}"

GLOBAL_PROJECT_PLAN_URL="https://reports.secret-studio.ru/latest/20260711-123348_global_project_plan_wg_paid/"
ACCESS_MAP_URL="https://reports.secret-studio.ru/latest/20260715-210236_access_map_canonical_current/"
VM100_PUBLIC_URL="https://reports.secret-studio.ru/latest/20260715-190653_vm100_git_source_1958200ccc1b/"
VM121_PUBLIC_URL="https://reports.secret-studio.ru/latest/20260713-182700_vm121_git_source_93e0e5a31f09/"
VM130_SOURCE_URL="https://reports.secret-studio.ru/latest/20260716-064558_vm130_router_ops_source_snapshot_r14b2/"
LATEST_ROOT_URL="https://reports.secret-studio.ru/latest/"
OLD_ROOT_URL="https://reports.secret-studio.ru/old/"

STAMP="$(date -u +%Y%m%d-%H%M%S)"
STEP_DIR="/opt/router-ops/steps/${STAMP}_${STEP_ID}"
mkdir -p "$STEP_DIR"
LOG="$STEP_DIR/main.log"
exec > >(tee -a "$LOG") 2>&1

RESULT="STOP_UNHANDLED_R15B1_SALVAGE_ERROR"
PHASE=initialization
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
STEP_WAS_NOT_STARTED=false
COPY_VERIFY=PASS
RUNTIME_IMPACT=false
PUBLISHER_APPROVED=false
FINALIZED=false

STEP_REPORT_URL=""
LOCAL_M07_PLAN_URL=""
VM101_PUBLIC_URL=""
VM101_MODEL_URL=""
VM101_METHODS_URL=""
FACTS_URL=""
VERIFY_URL=""
ROLLBACK_URL=""
PUBLIC_OBJECT_KIND=""
PREVIOUS_MOVED_COUNT=""
NEW_COMMIT=""
NEW_TREE=""
NEW_COUNT=""

progress() {
    printf '>>> [%s] %s\n' "$(date -u +%H:%M:%S)" "$*"
}

value() {
    local key="$1" file="$2"
    awk -F= -v k="$key" '$1==k {v=substr($0,index($0,"=")+1)} END{print v}' "$file"
}

url_from_log() {
    local key="$1" file="$2" url rel dir
    url="$(value "$key" "$file")"
    [[ -n "$url" ]] || url="$(value PUBLIC_URL "$file")"
    if [[ -z "$url" ]]; then
        rel="$(value PUBLISHED_RELATIVE_PATH "$file")"
        [[ -n "$rel" ]] && url="${PERMANENT_ROOT}/${rel}/"
    fi
    if [[ -z "$url" ]]; then
        dir="$(value PUBLISHED_DIR "$file")"
        [[ -n "$dir" ]] && url="${PERMANENT_ROOT}/latest/$(basename "$dir")/"
    fi
    printf '%s\n' "$url"
}

publish_files() {
    local label="$1" log="$2"
    shift 2
    "$PUBLISHER" "$label" "$@" >"$log" 2>&1
}

http_fetch() {
    curl --fail --silent --show-error --location         --resolve "$RESOLVE"         --connect-timeout 5 --max-time 30         "$1" -o "$2"
}

vm101_stdin() {
    ssh pve-mgts         "ssh -o BatchMode=yes -o ConnectTimeout=10 -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 'sh -s'"
}

step_sha() {
    sha256sum "$STEP_DIR/step-code.sh" | awk '{print $1}'
}

finish() {
    printf '%s\n'         "RESULT=$RESULT"         "STEP_REPORT_URL=$STEP_REPORT_URL"         "LOCAL_M07_PLAN_URL=$LOCAL_M07_PLAN_URL"         "GLOBAL_PROJECT_PLAN_URL=$GLOBAL_PROJECT_PLAN_URL"         "ACCESS_MAP_URL=$ACCESS_MAP_URL"         "VM100_PUBLIC_URL=$VM100_PUBLIC_URL"         "VM101_PUBLIC_URL=$VM101_PUBLIC_URL"         "VM121_PUBLIC_URL=$VM121_PUBLIC_URL"         "VM101_MODEL_URL=$VM101_MODEL_URL"         "VM101_METHODS_URL=$VM101_METHODS_URL"         "VM130_SOURCE_URL=$VM130_SOURCE_URL"         "LATEST_ROOT_URL=$LATEST_ROOT_URL"         "OLD_ROOT_URL=$OLD_ROOT_URL"         "FACTS_URL=$FACTS_URL"         "VERIFY_URL=$VERIFY_URL"         "ROLLBACK_URL=$ROLLBACK_URL"         "PUBLIC_OBJECT_KIND=$PUBLIC_OBJECT_KIND"         "PREVIOUS_MOVED_COUNT=$PREVIOUS_MOVED_COUNT"         "COPY_VERIFY=$COPY_VERIFY"         "STEP_SHA256=$(step_sha)"         "RUNTIME_IMPACT=$RUNTIME_IMPACT"
}

write_stop_artifacts() {
    local rc="$1" line="$2"
    cat >"$STEP_DIR/report.txt" <<EOF
STEP_ID=$STEP_ID
RESULT=$RESULT
PHASE=$PHASE
ERROR_RC=$rc
ERROR_LINE=$line
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
BASE_STEP_RESULT=STOP_R15B1_UNHANDLED_ERROR
BASE_CORE_RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED
PREVIOUS_SALVAGE_RESULT=STOP_SALVAGE_REMOTE_HEREDOC_LOCAL_EXPANSION
RUNTIME_IMPACT=false
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$(step_sha)
EOF

    cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
PHASE=$PHASE
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$(step_sha)
RUNTIME_IMPACT=false
EOF

    python3 - "$STEP_DIR/facts.json" "$rc" "$line" <<PY
import json, sys
json.dump({
  "step_id": "$STEP_ID",
  "result": "$RESULT",
  "phase": "$PHASE",
  "error_rc": sys.argv[2],
  "error_line": sys.argv[3],
  "core_change_complete": True,
  "core_change_repeated": False,
  "runtime_impact": False
}, open(sys.argv[1], "w"), indent=2)
open(sys.argv[1], "a").write("\n")
PY

    cat >"$STEP_DIR/rollback.sh" <<EOF
#!/usr/bin/env bash
set -Eeuo pipefail
echo "RESULT=STOP_MANUAL_APPROVAL_REQUIRED"
echo "TARGET_ROLLBACK=$TARGET_RUNTIME_DIR/rollback.sh"
echo "CORE_CHANGE_COMPLETE=true"
exit 1
EOF
    chmod 700 "$STEP_DIR/rollback.sh"
}

publish_stop_if_possible() {
    [[ "$PUBLISHER_APPROVED" == true ]] || return 0
    publish_files         "step050m07r15b1_generation_schema_validator_r02s_stop"         "$STEP_DIR/publish-stop.log"         "$STEP_DIR/step-code.sh"         "$STEP_DIR/report.txt"         "$STEP_DIR/facts.json"         "$STEP_DIR/verify.txt"         "$STEP_DIR/rollback.sh"         "$STEP_DIR/r02-classification.txt"         "$STEP_DIR/transfer-verify.txt"         "$STEP_DIR/main.log" || return 0

    STEP_REPORT_URL="$(url_from_log STEP_REPORT_URL "$STEP_DIR/publish-stop.log" || true)"
    PUBLIC_OBJECT_KIND="$(value PUBLIC_OBJECT_KIND "$STEP_DIR/publish-stop.log")"
    PREVIOUS_MOVED_COUNT="$(value PREVIOUS_MOVED_COUNT "$STEP_DIR/publish-stop.log")"
    if [[ -n "$STEP_REPORT_URL" ]]; then
        FACTS_URL="${STEP_REPORT_URL}facts.json"
        VERIFY_URL="${STEP_REPORT_URL}verify.txt"
        ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"
    fi
}

on_error() {
    local rc=$? line="${BASH_LINENO[0]:-unknown}"
    set +e
    trap - ERR
    [[ "$FINALIZED" == true ]] && exit "$rc"
    FINALIZED=true
    RESULT="STOP_R15B1_SALVAGE_ERROR"
    write_stop_artifacts "$rc" "$line"
    publish_stop_if_possible
    finish
    exit "$rc"
}
trap on_error ERR

PHASE=bundle_verification
progress "verify exact salvage bundle and approved publisher"
cp "$0" "$STEP_DIR/step-code.sh"
cp "$BUNDLE_ZIP" "$STEP_DIR/"
cp "$BUNDLE_ROOT/manifest.sha256" "$STEP_DIR/bundle-manifest.sha256"
cp "$BUNDLE_ROOT/r02-classification.txt" "$STEP_DIR/"
cp "$BUNDLE_ROOT/r02s-r01-classification.txt" "$STEP_DIR/"
cp "$BUNDLE_ROOT/release-info.json" "$STEP_DIR/"

ACTUAL_BUNDLE_SHA="$(sha256sum "$BUNDLE_ZIP" | awk '{print $1}')"
ACTUAL_INSTALLER_SHA="$(sha256sum "$BUNDLE_ROOT/install.sh" | awk '{print $1}')"

cat >"$STEP_DIR/transfer-verify.txt" <<EOF
EXPECTED_BUNDLE_SHA256=$EXPECTED_BUNDLE_SHA256
ACTUAL_BUNDLE_SHA256=$ACTUAL_BUNDLE_SHA
EXPECTED_INSTALLER_SHA256=$EXPECTED_INSTALLER_SHA256
ACTUAL_INSTALLER_SHA256=$ACTUAL_INSTALLER_SHA
EOF

[[ "$ACTUAL_BUNDLE_SHA" == "$EXPECTED_BUNDLE_SHA256" ]]
[[ "$ACTUAL_INSTALLER_SHA" == "$EXPECTED_INSTALLER_SHA256" ]]
(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) >>"$STEP_DIR/transfer-verify.txt" 2>&1
bash -n "$STEP_DIR/step-code.sh"

[[ "$(sha256sum "$PUBLISHER" | awk '{print $1}')" == "$EXPECTED_PUBLISHER_SHA" ]]
PUBLISHER_APPROVED=true

echo "COPY_VERIFY=PASS" >>"$STEP_DIR/transfer-verify.txt"
echo "STEP_SHA256=$(step_sha)" >>"$STEP_DIR/transfer-verify.txt"

PHASE=core_result_verification
progress "verify completed R15B1 core without rerunning target installer"

R02_STEP_DIR="$(
    find /opt/router-ops/steps -maxdepth 1 -type d         -name '*STEP_050M07R15B1_GENERATION_SCHEMA_VALIDATOR_R02_REBUILT' |
    sort | tail -n1
)"
[[ -n "$R02_STEP_DIR" ]]
[[ "$(sha256sum "$R02_STEP_DIR/step-code.sh" | awk '{print $1}')" == "$EXPECTED_R02_INSTALLER_SHA" ]]
grep -q '^RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED$' "$R02_STEP_DIR/target-run.txt"

ssh pve-mgts     "ssh -o BatchMode=yes -o ConnectTimeout=10 -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2     'TARGET_BASE="'$TARGET_BASE'" TARGET_RUNTIME_DIR="'$TARGET_RUNTIME_DIR'" TARGET_RUN_LOG="'$TARGET_RUN_LOG'" sh -s'"     >"$STEP_DIR/vm101-core-state.txt" <<'VM101'
set -eu

CONF=/etc/router-egress-generation.conf
LIB=/usr/local/lib/router-egress-generation.sh
VALIDATOR=/usr/local/sbin/router-egress-generation-validate

echo "CONF_SHA=\$(sha256sum "\$CONF" | awk '{print \$1}')"
echo "LIB_SHA=\$(sha256sum "\$LIB" | awk '{print \$1}')"
echo "VALIDATOR_SHA=\$(sha256sum "\$VALIDATOR" | awk '{print \$1}')"

test -f "\$TARGET_RUNTIME_DIR/target-facts.json"
test -f "\$TARGET_RUNTIME_DIR/target-verify.txt"
test -f "\$TARGET_RUNTIME_DIR/rollback.sh"
test -f "\$TARGET_RUNTIME_DIR/state/validator-selftest.txt"
test -f "\$TARGET_RUNTIME_DIR/state/endpoints-before.tsv"
test -f "\$TARGET_RUNTIME_DIR/state/endpoints-after.tsv"
test -f "\$TARGET_RUN_LOG"

grep -q '^RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED$' "\$TARGET_RUN_LOG"
grep -q '^RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED$' "\$TARGET_RUNTIME_DIR/target-verify.txt"
grep -q '^RESULT=PASS_ROUTER_EGRESS_GENERATION_VALIDATOR_SELFTEST$' "\$TARGET_RUNTIME_DIR/state/validator-selftest.txt"

sh -n "\$LIB"
sh -n "\$VALIDATOR"
"\$VALIDATOR" --selftest >"\$TARGET_RUNTIME_DIR/state/validator-selftest-salvage.txt"
grep -q '^RESULT=PASS_ROUTER_EGRESS_GENERATION_VALIDATOR_SELFTEST$' "\$TARGET_RUNTIME_DIR/state/validator-selftest-salvage.txt"

test ! -e /var/lib/router-egress-recovery/generations/active
test -z "\$(find /var/lib/router-egress-recovery/generations/staging -mindepth 1 -maxdepth 1 2>/dev/null)"

echo LIVE_ENDPOINTS_BEGIN
for iface in vpn1 vpn2 vpn3 vpn4 vpn5; do
    endpoint="\$(amneziawg show "\$iface" endpoints 2>/dev/null | awk 'NR==1 {print \$2}')"
    printf '%s\t%s\n' "\$iface" "\$endpoint"
done
echo LIVE_ENDPOINTS_END

echo TRACKED_DIFF_BEGIN
git --git-dir=/root/.vm101-source.git --work-tree=/ diff --name-only
echo TRACKED_DIFF_END

. /etc/router-machine-git-source.conf
printf '%s\n' $MANAGED_PATHS | sed '/^$/d' | sort -u >"$TARGET_RUNTIME_DIR/state/managed-paths-live.txt"
echo "MANAGED_PATH_COUNT=\$(wc -l <"$TARGET_RUNTIME_DIR/state/managed-paths-live.txt" | tr -d ' ')"

echo "HEAD_COMMIT=\$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse HEAD)"
echo "HEAD_TREE=\$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse 'HEAD^{tree}')"
VM101

[[ "$(value CONF_SHA "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_CONF_SHA" ]]
[[ "$(value LIB_SHA "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_LIB_SHA" ]]
[[ "$(value VALIDATOR_SHA "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_VALIDATOR_SHA" ]]
[[ "$(value HEAD_COMMIT "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_BEFORE_COMMIT" ]]
[[ "$(value HEAD_TREE "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_BEFORE_TREE" ]]

ssh pve-mgts     "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2     'tar -czf - -C "$TARGET_RUNTIME_DIR" state target-facts.json target-verify.txt rollback.sh'"     >"$STEP_DIR/target-artifacts.tar.gz"

mkdir -p "$STEP_DIR/target-artifacts"
tar -xzf "$STEP_DIR/target-artifacts.tar.gz" -C "$STEP_DIR/target-artifacts"

ssh pve-mgts     "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2     'cat "$TARGET_RUN_LOG"'"     >"$STEP_DIR/target-run.log"

python3 - "$STEP_DIR/target-artifacts/target-facts.json" <<'PY'
import json, sys
f = json.load(open(sys.argv[1]))
assert f["result"] == "PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED"
assert f["runtime_impact"] is False
assert f["active_slots_changed"] is False
assert f["active_generation_created"] is False
assert f["managed_path_count"] == 32
assert f["validator_selftest"] == "PASS"
PY

cmp     "$STEP_DIR/target-artifacts/state/endpoints-before.tsv"     "$STEP_DIR/target-artifacts/state/endpoints-after.tsv"

awk '/LIVE_ENDPOINTS_BEGIN/{f=1;next}/LIVE_ENDPOINTS_END/{f=0}f'     "$STEP_DIR/vm101-core-state.txt" >"$STEP_DIR/live-endpoints.tsv"
cmp "$STEP_DIR/target-artifacts/state/endpoints-after.tsv" "$STEP_DIR/live-endpoints.tsv"

awk '/TRACKED_DIFF_BEGIN/{f=1;next}/TRACKED_DIFF_END/{f=0}f'     "$STEP_DIR/vm101-core-state.txt" |
    sed '/^$/d' | sort >"$STEP_DIR/tracked-diff.txt"

printf '%s\n' 'etc/router-machine-git-source.conf' >"$STEP_DIR/expected-tracked-diff.txt"
cmp "$STEP_DIR/expected-tracked-diff.txt" "$STEP_DIR/tracked-diff.txt"
[[ "$(value MANAGED_PATH_COUNT "$STEP_DIR/vm101-core-state.txt")" == "$EXPECTED_AFTER_COUNT" ]]

for required in     etc/router-egress-generation.conf     etc/router-machine-git-source.conf     usr/local/lib/router-egress-generation.sh     usr/local/sbin/router-egress-generation-validate
do
    grep -Fxq "$required" "$STEP_DIR/target-artifacts/state/managed-paths-live.txt"
done

PHASE=machine_git_publication
progress "publish exact changed VM101 source through Machine Git v2"

MACHINE_LOG="$STEP_DIR/router-machine-close.log"
set +e
router-machine-close vm101     "$STEP_ID: salvage completed R15B1 generation schema and validator"     >"$MACHINE_LOG" 2>&1
machine_rc=$?
set -e

NEW_COMMIT="$(awk -F= '/^(VM130_COMMIT|GIT_COMMIT|COMMIT)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$MACHINE_LOG")"
NEW_TREE="$(awk -F= '/^(VM130_TREE|GIT_TREE|TREE)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$MACHINE_LOG")"
NEW_COUNT="$(awk -F= '/^(VM130_FILE_COUNT|GIT_FILE_COUNT|FILE_COUNT)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$MACHINE_LOG")"
VM130_STATUS="$(value VM130_STATUS "$MACHINE_LOG")"
VM101_PUBLIC_URL="$(url_from_log VM101_PUBLIC_URL "$MACHINE_LOG")"

machine_proof=true
[[ -n "$NEW_COMMIT" && "$NEW_COMMIT" != "$EXPECTED_BEFORE_COMMIT" ]] || machine_proof=false
[[ -n "$NEW_TREE" && "$NEW_TREE" != "$EXPECTED_BEFORE_TREE" ]] || machine_proof=false
[[ "$NEW_COUNT" == "$EXPECTED_AFTER_COUNT" ]] || machine_proof=false
[[ "$VM130_STATUS" == CLEAN ]] || machine_proof=false
[[ "$VM101_PUBLIC_URL" == "${PERMANENT_ROOT}/latest/"*/ ]] || machine_proof=false
grep -q '^PUBLISHED_DIR=' "$MACHINE_LOG" || machine_proof=false

if [[ "$machine_rc" -ne 0 && "$machine_proof" != true ]]; then
    false
fi
[[ "$machine_proof" == true ]]

PHASE=github_exact_clone
progress "verify exact GitHub commit, tree, file count and managed paths"

CLONE="$STEP_DIR/github-exact-clone"
git clone --quiet "$REPOSITORY" "$CLONE" >"$STEP_DIR/git-clone.log" 2>&1
git -C "$CLONE" checkout --quiet --detach "$NEW_COMMIT"

[[ "$(git -C "$CLONE" rev-parse HEAD)" == "$NEW_COMMIT" ]]
[[ "$(git -C "$CLONE" rev-parse 'HEAD^{tree}')" == "$NEW_TREE" ]]
[[ "$(git -C "$CLONE" ls-tree -r --name-only HEAD | wc -l | tr -d ' ')" == "$EXPECTED_AFTER_COUNT" ]]

for path in     etc/router-egress-generation.conf     etc/router-machine-git-source.conf     usr/local/lib/router-egress-generation.sh     usr/local/sbin/router-egress-generation-validate
do
    git -C "$CLONE" cat-file -e "HEAD:$path"
done

[[ "$(git -C "$CLONE" show HEAD:etc/router-egress-generation.conf | sha256sum | awk '{print $1}')" == "$EXPECTED_CONF_SHA" ]]
[[ "$(git -C "$CLONE" show HEAD:usr/local/lib/router-egress-generation.sh | sha256sum | awk '{print $1}')" == "$EXPECTED_LIB_SHA" ]]
[[ "$(git -C "$CLONE" show HEAD:usr/local/sbin/router-egress-generation-validate | sha256sum | awk '{print $1}')" == "$EXPECTED_VALIDATOR_SHA" ]]

PHASE=model_methods_publication
progress "publish fresh VM101 model and methods"

cat >"$STEP_DIR/vm101-model.md" <<EOF
# VM101 model after R15B1

- Machine Git commit: $NEW_COMMIT
- tree: $NEW_TREE
- managed file count: $NEW_COUNT
- generation schema: router-egress-generation-v1
- validator: /usr/local/sbin/router-egress-generation-validate
- validator selftests: PASS
- active generation: absent
- staging directory: empty
- active vpn1..vpn5 endpoints: unchanged
- activation capability: disabled
- service restarts: none
- runtime impact: false
EOF

cat >"$STEP_DIR/vm101-methods.md" <<'EOF'
# VM101 generation validation methods after R15B1

The validator requires exactly five candidates mapped to:

- egress1 / vpn1 / table 201
- egress2 / vpn2 / table 202
- egress3 / vpn3 / table 203
- egress4 / vpn4 / table 204
- egress5 / vpn5 / table 205

It rejects duplicate endpoints and ranks, active endpoint reuse, quarantine reuse,
stale or future tests, malformed endpoints, incomplete mappings, config hash or
endpoint mismatch and manifest tampering.

R15B1 has no generation builder and no activation path. Its selftests use temporary
fixtures and cannot alter vpn1..vpn5.
EOF

publish_files     "vm101_model_generation_schema_validator_r15b1"     "$STEP_DIR/publish-model.log"     "$STEP_DIR/vm101-model.md"     "$STEP_DIR/target-artifacts/target-facts.json"     "$STEP_DIR/target-artifacts/target-verify.txt"     "$STEP_DIR/live-endpoints.tsv"     "$STEP_DIR/router-machine-close.log"

VM101_MODEL_URL="$(url_from_log VM101_MODEL_URL "$STEP_DIR/publish-model.log")"

publish_files     "vm101_methods_generation_schema_validator_r15b1"     "$STEP_DIR/publish-methods.log"     "$STEP_DIR/vm101-methods.md"     "$STEP_DIR/target-artifacts/state/validator-selftest.txt"     "$STEP_DIR/target-artifacts/state/validator-selftest-salvage.txt"     "$STEP_DIR/target-artifacts/rollback.sh"

VM101_METHODS_URL="$(url_from_log VM101_METHODS_URL "$STEP_DIR/publish-methods.log")"

PHASE=local_plan_publication
progress "publish updated local M07 plan with complete canonical architecture retained"

cp "$BUNDLE_ROOT/canonical-m07-plan.md" "$STEP_DIR/"
cp "$BUNDLE_ROOT/canonical-plan.md" "$STEP_DIR/"
cp "$BUNDLE_ROOT/local-plan-contract.md" "$STEP_DIR/"
cp "$BUNDLE_ROOT/next-implementation-sequence.md" "$STEP_DIR/"

[[ "$(sha256sum "$STEP_DIR/canonical-plan.md" | awk '{print $1}')" == "d5127b5b617bc043c9a631f0853e750f8173166c619d02f51249a67d95976075" ]]

publish_files     "local_architecture_plan_vm101_autonomous_hmn_recovery_r15b1"     "$STEP_DIR/publish-plan.log"     "$STEP_DIR/canonical-m07-plan.md"     "$STEP_DIR/canonical-plan.md"     "$STEP_DIR/local-plan-contract.md"     "$STEP_DIR/next-implementation-sequence.md"

LOCAL_M07_PLAN_URL="$(url_from_log LOCAL_M07_PLAN_URL "$STEP_DIR/publish-plan.log")"

PHASE=report_preparation
progress "prepare formal report, facts, verification and rollback"

cat >"$STEP_DIR/rollback.sh" <<EOF
#!/usr/bin/env bash
set -Eeuo pipefail

[[ "\${APPROVE_ROLLBACK:-NO}" == YES ]] || {
    echo "RESULT=STOP_MANUAL_APPROVAL_REQUIRED"
    exit 1
}

TARGET_ROLLBACK="$TARGET_RUNTIME_DIR/rollback.sh"

ssh pve-mgts     "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2     'APPROVE_ROLLBACK=YES "\$TARGET_ROLLBACK"'"

router-machine-close vm101     "$STEP_ID rollback: remove R15B1 generation schema and validator"

echo "RESULT=PASS_R15B1_ROLLED_BACK"
echo "SERVICE_RESTARTS=none"
echo "RUNTIME_IMPACT=false"
EOF
chmod 700 "$STEP_DIR/rollback.sh"

RESULT="PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_ACTIVE"

cat >"$STEP_DIR/facts.json" <<EOF
{
  "step_id": "$STEP_ID",
  "result": "$RESULT",
  "base_step_result": "STOP_R15B1_UNHANDLED_ERROR",
  "base_core_result": "PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED",
  "core_change_complete": true,
  "core_change_repeated": false,
  "stop_cause": "run.log artifact path mismatch",
  "previous_salvage_result": "STOP_SALVAGE_REMOTE_HEREDOC_LOCAL_EXPANSION",
  "before_commit": "$EXPECTED_BEFORE_COMMIT",
  "after_commit": "$NEW_COMMIT",
  "after_tree": "$NEW_TREE",
  "before_file_count": $EXPECTED_BEFORE_COUNT,
  "after_file_count": $NEW_COUNT,
  "active_slots_changed": false,
  "active_generation_created": false,
  "validator_selftest": "PASS",
  "machine_git": "PASS",
  "github_exact_clone": "PASS",
  "full_canonical_plan_retained": true,
  "runtime_impact": false
}
EOF

cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
COPY_VERIFY=PASS
STEP_SHA256=$(step_sha)
BASE_STEP_RESULT=STOP_R15B1_UNHANDLED_ERROR
BASE_CORE_RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
CONF_SHA256=$EXPECTED_CONF_SHA
LIBRARY_SHA256=$EXPECTED_LIB_SHA
VALIDATOR_SHA256=$EXPECTED_VALIDATOR_SHA
TARGET_FACTS=PASS
TARGET_VERIFY=PASS
TARGET_RUN_LOG=PASS
VALIDATOR_SELFTEST_ORIGINAL=PASS
VALIDATOR_SELFTEST_SALVAGE=PASS
ENDPOINTS_BEFORE_AFTER_LIVE=PASS
ACTIVE_GENERATION_CREATED=false
STAGING_EMPTY=true
MACHINE_GIT=PASS
GITHUB_EXACT_CLONE=PASS
AFTER_COMMIT=$NEW_COMMIT
AFTER_TREE=$NEW_TREE
AFTER_FILE_COUNT=$NEW_COUNT
FULL_CANONICAL_PLAN_RETAINED=PASS
SERVICE_RESTARTS=none
RUNTIME_IMPACT=false
EOF

cat >"$STEP_DIR/system-model.txt" <<EOF
GENERATION_SCHEMA=router-egress-generation-v1
GENERATION_VALIDATOR=/usr/local/sbin/router-egress-generation-validate
GENERATION_ACTIVE_LINK_PRESENT=false
GENERATION_STAGING_EMPTY=true
ACTIVE_SLOTS_CHANGED=false
ACTIVATION_ALLOWED=false
VM101_COMMIT=$NEW_COMMIT
VM101_TREE=$NEW_TREE
VM101_FILE_COUNT=$NEW_COUNT
EOF

cat >"$STEP_DIR/methods-limitations.txt" <<'EOF'
METHOD=Publication salvage after completed target installation.
CORE_CHANGE_REPEATED=false
TARGET_INSTALLER_RERUN=false
SERVICE_RESTARTS=none
INTERFACES_CHANGED=none
ROUTES_CHANGED=none
TRAFFIC_INTERRUPTION=none
LIMITATION=R15B1 validates staged generations but does not build or activate them.
RUNTIME_IMPACT=false
EOF

cat >"$STEP_DIR/access-routes.txt" <<'EOF'
ACCESS_MAP_CHANGED=false
VM101_ROUTE=ops@router-ops -> pve-mgts -> root@10.71.100.2
VM101_DIRECT_FROM_ROUTER_OPS=unsupported
EOF

cat >"$STEP_DIR/report.txt" <<EOF
STEP_ID=$STEP_ID
RESULT=$RESULT
PURPOSE=Salvage publication for completed R15B1 without repeating target installation.
BASE_STEP_RESULT=STOP_R15B1_UNHANDLED_ERROR
BASE_CORE_RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED
STOP_CAUSE=run.log artifact path mismatch
PREVIOUS_SALVAGE_RESULT=STOP_SALVAGE_REMOTE_HEREDOC_LOCAL_EXPANSION
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
BEFORE_COMMIT=$EXPECTED_BEFORE_COMMIT
AFTER_COMMIT=$NEW_COMMIT
AFTER_TREE=$NEW_TREE
BEFORE_FILE_COUNT=$EXPECTED_BEFORE_COUNT
AFTER_FILE_COUNT=$NEW_COUNT
ACTIVE_SLOTS_CHANGED=false
ACTIVE_GENERATION_CREATED=false
VALIDATOR_SELFTEST=PASS
MACHINE_GIT=PASS
GITHUB_EXACT_CLONE=PASS
SERVICE_RESTARTS=none
TRAFFIC_INTERRUPTION=none
RUNTIME_IMPACT=false
ROLLBACK=$TARGET_RUNTIME_DIR/rollback.sh plus Machine Git republish
LOCAL_M07_PLAN_URL=$LOCAL_M07_PLAN_URL
GLOBAL_PROJECT_PLAN_URL=$GLOBAL_PROJECT_PLAN_URL
ACCESS_MAP_URL=$ACCESS_MAP_URL
VM100_PUBLIC_URL=$VM100_PUBLIC_URL
VM101_PUBLIC_URL=$VM101_PUBLIC_URL
VM121_PUBLIC_URL=$VM121_PUBLIC_URL
VM101_MODEL_URL=$VM101_MODEL_URL
VM101_METHODS_URL=$VM101_METHODS_URL
VM130_SOURCE_URL=$VM130_SOURCE_URL
COPY_VERIFY=PASS
STEP_SHA256=$(step_sha)
EOF

PHASE=step_report_publication
progress "publish formal R15B1 salvage report"

publish_files     "step050m07r15b1_generation_schema_validator_r02s_publication_salvage"     "$STEP_DIR/publish-step.log"     "$STEP_DIR/step-code.sh"     "$STEP_DIR/report.txt"     "$STEP_DIR/facts.json"     "$STEP_DIR/verify.txt"     "$STEP_DIR/rollback.sh"     "$STEP_DIR/system-model.txt"     "$STEP_DIR/methods-limitations.txt"     "$STEP_DIR/access-routes.txt"     "$STEP_DIR/transfer-verify.txt"     "$STEP_DIR/r02-classification.txt"     "$STEP_DIR/release-info.json"     "$STEP_DIR/vm101-core-state.txt"     "$STEP_DIR/target-artifacts.tar.gz"     "$STEP_DIR/target-run.log"     "$STEP_DIR/live-endpoints.tsv"     "$STEP_DIR/tracked-diff.txt"     "$STEP_DIR/target-artifacts/state/managed-paths-live.txt"     "$STEP_DIR/router-machine-close.log"     "$STEP_DIR/publish-model.log"     "$STEP_DIR/publish-methods.log"     "$STEP_DIR/publish-plan.log"     "$STEP_DIR/canonical-m07-plan.md"     "$STEP_DIR/canonical-plan.md"     "$STEP_DIR/local-plan-contract.md"     "$STEP_DIR/next-implementation-sequence.md"     "$STEP_DIR/main.log"

STEP_REPORT_URL="$(url_from_log STEP_REPORT_URL "$STEP_DIR/publish-step.log")"
PUBLIC_OBJECT_KIND="$(value PUBLIC_OBJECT_KIND "$STEP_DIR/publish-step.log")"
PREVIOUS_MOVED_COUNT="$(value PREVIOUS_MOVED_COUNT "$STEP_DIR/publish-step.log")"
STEP_PUBLISHED_DIR="$(value PUBLISHED_DIR "$STEP_DIR/publish-step.log")"

FACTS_URL="${STEP_REPORT_URL}facts.json"
VERIFY_URL="${STEP_REPORT_URL}verify.txt"
ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"

cat >>"$STEP_DIR/report.txt" <<EOF
STEP_REPORT_URL=$STEP_REPORT_URL
FACTS_URL=$FACTS_URL
VERIFY_URL=$VERIFY_URL
ROLLBACK_URL=$ROLLBACK_URL
LATEST_ROOT_URL=$LATEST_ROOT_URL
OLD_ROOT_URL=$OLD_ROOT_URL
PUBLIC_OBJECT_KIND=$PUBLIC_OBJECT_KIND
PREVIOUS_MOVED_COUNT=$PREVIOUS_MOVED_COUNT
EOF

cp "$STEP_DIR/report.txt" "$STEP_PUBLISHED_DIR/report.txt"
(
    cd "$STEP_PUBLISHED_DIR"
    find . -type f ! -name manifest.sha256 -print0 |
        sort -z |
        xargs -0 sha256sum >manifest.sha256
)

PHASE=external_http_verification
progress "verify source, model, methods, plans and STEP artifacts over permanent HTTPS"

http_fetch "$FACTS_URL" "$STEP_DIR/http-facts.json"
http_fetch "$VERIFY_URL" "$STEP_DIR/http-verify.txt"
http_fetch "$ROLLBACK_URL" "$STEP_DIR/http-rollback.sh"
http_fetch "$VM101_PUBLIC_URL" "$STEP_DIR/http-source-index.html"
http_fetch "$VM101_MODEL_URL" "$STEP_DIR/http-model-index.html"
http_fetch "$VM101_METHODS_URL" "$STEP_DIR/http-methods-index.html"
http_fetch "${LOCAL_M07_PLAN_URL}canonical-m07-plan.md" "$STEP_DIR/http-canonical-m07-plan.md"
http_fetch "${LOCAL_M07_PLAN_URL}canonical-plan.md" "$STEP_DIR/http-canonical-plan.md"

cmp "$STEP_DIR/facts.json" "$STEP_DIR/http-facts.json"
cmp "$STEP_DIR/verify.txt" "$STEP_DIR/http-verify.txt"
cmp "$STEP_DIR/rollback.sh" "$STEP_DIR/http-rollback.sh"
cmp "$STEP_DIR/canonical-m07-plan.md" "$STEP_DIR/http-canonical-m07-plan.md"
cmp "$STEP_DIR/canonical-plan.md" "$STEP_DIR/http-canonical-plan.md"

TOKEN="$(cat /opt/router-ops/public/.router-public-token)"
LATEST_DIR="/opt/router-ops/public/r/$TOKEN/latest"
for pattern in     '*_vm101_git_source_*'     '*_vm101_model_*'     '*_vm101_methods_*'     '*_local_architecture_plan_vm101_autonomous_hmn_recovery_*'
do
    count="$(find "$LATEST_DIR" -mindepth 1 -maxdepth 1 -type d -name "$pattern" | wc -l | tr -d ' ')"
    [[ "$count" == 1 ]]
done

FINALIZED=true
trap - ERR
finish
