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

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_PUBLISHER_SHA="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"

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

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

TARGET_BASE="/root/router-steps/$STEP_ID"
TARGET_B64="$TARGET_BASE/target-step.tar.gz.b64.txt"
TARGET_GZ="$TARGET_BASE/target-step.tar.gz"
TARGET_EXTRACTED="$TARGET_BASE/extracted"
TARGET_RUNTIME_DIR="$TARGET_EXTRACTED/target-step"
TARGET_STEP_FILE="$TARGET_RUNTIME_DIR/apply.sh"
TARGET_GZ_SHA="536d6e4579cb571584b6cd84cec8dda41bb414efd0b8e14e8e1c5c4e13655f0a"

RESULT="STOP_UNHANDLED_R15B1_ERROR"
CORE_CHANGE_COMPLETE=false
COPY_VERIFY=PASS
RUNTIME_IMPACT=false
STEP_WAS_NOT_STARTED=true
PUBLISHER_APPROVED=false
FINALIZED=false
PHASE=initialization
ERROR_DETAIL=""
ERROR_RC=""
ERROR_LINE=""

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

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_shell() {
    local command="$1"
    ssh pve-mgts         "ssh -o BatchMode=yes -o ConnectTimeout=10 -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 '$command'"
}

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 safe_step_sha="unknown"
    [[ -f "$STEP_DIR/step-code.sh" ]] && safe_step_sha="$(sha256sum "$STEP_DIR/step-code.sh" | awk '{print $1}')"

    cat >"$STEP_DIR/report.txt" <<EOF
STEP_ID=$STEP_ID
RESULT=$RESULT
PHASE=$PHASE
ERROR_DETAIL=$ERROR_DETAIL
ERROR_RC=$ERROR_RC
ERROR_LINE=$ERROR_LINE
CORE_CHANGE_COMPLETE=$CORE_CHANGE_COMPLETE
STEP_WAS_NOT_STARTED=$STEP_WAS_NOT_STARTED
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$safe_step_sha
RUNTIME_IMPACT=$RUNTIME_IMPACT
EOF

    cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
PHASE=$PHASE
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$safe_step_sha
CORE_CHANGE_COMPLETE=$CORE_CHANGE_COMPLETE
STEP_WAS_NOT_STARTED=$STEP_WAS_NOT_STARTED
RUNTIME_IMPACT=$RUNTIME_IMPACT
EOF

    python3 - "$STEP_DIR/facts.json" <<PY
import json, sys
with open(sys.argv[1], "w") as f:
    json.dump({
        "step_id": "$STEP_ID",
        "result": "$RESULT",
        "phase": "$PHASE",
        "error_detail": "$ERROR_DETAIL",
        "error_rc": "$ERROR_RC",
        "error_line": "$ERROR_LINE",
        "core_change_complete": "$CORE_CHANGE_COMPLETE" == "true",
        "step_was_not_started": "$STEP_WAS_NOT_STARTED" == "true",
        "copy_verify": "$COPY_VERIFY",
        "runtime_impact": "$RUNTIME_IMPACT" == "true"
    }, f, indent=2, sort_keys=True)
    f.write("\\n")
PY

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

publish_stop_if_possible() {
    [[ "$PUBLISHER_APPROVED" == true ]] || return 0
    publish_files \
        "step050m07r15b1_generation_schema_validator_r02_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/main.log" \
        "$STEP_DIR/transfer-verify.txt" \
        "$STEP_DIR/previous-r01-classification.txt" 2>/dev/null || 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
}

finalize_stop() {
    local rc="${1:-1}"
    set +e
    trap - ERR
    [[ "$FINALIZED" == true ]] && exit "$rc"
    FINALIZED=true
    write_stop_artifacts
    publish_stop_if_possible
    finish
    exit "$rc"
}

fail() {
    RESULT="$1"
    ERROR_DETAIL="${2:-explicit_failure}"
    ERROR_RC="${3:-1}"
    ERROR_LINE="${BASH_LINENO[0]:-unknown}"
    finalize_stop 1
}

on_unexpected_error() {
    local rc=$?
    RESULT="STOP_R15B1_UNHANDLED_ERROR"
    ERROR_RC="$rc"
    ERROR_LINE="${BASH_LINENO[0]:-unknown}"
    ERROR_DETAIL="phase=$PHASE"
    finalize_stop "$rc"
}

trap on_unexpected_error ERR

PHASE=bundle_verification
progress "verify exact bundle, target gzip/Base64 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/previous-r01-classification.txt" "$STEP_DIR/previous-r01-classification.txt"

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
EXPECTED_TARGET_GZIP_SHA256=$TARGET_GZ_SHA
EOF

[[ "$ACTUAL_BUNDLE_SHA" == "$EXPECTED_BUNDLE_SHA256" ]] || {
    COPY_VERIFY=FAIL
    fail STOP_COPY_OR_TRANSFER_ERROR
}
[[ "$ACTUAL_INSTALLER_SHA" == "$EXPECTED_INSTALLER_SHA256" ]] || {
    COPY_VERIFY=FAIL
    fail STOP_COPY_OR_TRANSFER_ERROR
}
(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) >>"$STEP_DIR/transfer-verify.txt" 2>&1 || {
    COPY_VERIFY=FAIL
    fail STOP_COPY_OR_TRANSFER_ERROR
}

base64 -d "$BUNDLE_ROOT/target-step.tar.gz.b64.txt" >"$STEP_DIR/target-step.decoded.tar.gz"
cmp "$BUNDLE_ROOT/target-step.tar.gz" "$STEP_DIR/target-step.decoded.tar.gz"
[[ "$(sha256sum "$BUNDLE_ROOT/target-step.tar.gz" | awk '{print $1}')" == "$TARGET_GZ_SHA" ]]
gzip -t "$BUNDLE_ROOT/target-step.tar.gz"
bash -n "$STEP_DIR/step-code.sh"

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

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

PHASE=vm101_preflight
progress "preflight exact VM101 clean-foundation source and runtime"
http_fetch     "https://reports.secret-studio.ru/latest/20260716-133906_vm101_git_source_c737f6b40fbf/facts.json"     "$STEP_DIR/before-source-facts.json"
python3 - "$STEP_DIR/before-source-facts.json" <<'PY'
import json, sys
f = json.load(open(sys.argv[1]))
assert f["commit"] == "c737f6b40fbf882950e343b44c98ab26dfd01e9f"
assert f["tree"] == "f79773d8235c88bfcb1d69a4baecf857504adb95"
assert f["file_count"] == 29
PY

CLONE="$STEP_DIR/before-github-clone"
git clone --quiet "$REPOSITORY" "$CLONE" >"$STEP_DIR/git-clone-before.log" 2>&1
git -C "$CLONE" checkout --quiet --detach "$EXPECTED_BEFORE_COMMIT"
[[ "$(git -C "$CLONE" rev-parse HEAD)" == "$EXPECTED_BEFORE_COMMIT" ]]
[[ "$(git -C "$CLONE" rev-parse 'HEAD^{tree}')" == "$EXPECTED_BEFORE_TREE" ]]
[[ "$(git -C "$CLONE" ls-tree -r --name-only HEAD | wc -l | tr -d ' ')" == "$EXPECTED_BEFORE_COUNT" ]]

vm101_stdin >"$STEP_DIR/vm101-before.txt" <<'VM101'
set -eu
echo "COMMIT=$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse HEAD)"
echo "TREE=$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse 'HEAD^{tree}')"
echo STATUS_BEGIN
git --git-dir=/root/.vm101-source.git --work-tree=/ status --short
echo STATUS_END
command -v base64
command -v sha256sum
command -v gzip
command -v tar
for required_command in cp chmod mkdir mv sed awk grep sha256sum base64 gzip tar; do
    command_path="$(command -v "$required_command" 2>/dev/null || true)"
    [ -n "$command_path" ] || {
        echo "RESULT=STOP_REQUIRED_COMMAND_MISSING"
        echo "COMMAND=$required_command"
        exit 41
    }
    echo "COMMAND_${required_command}=$command_path"
done
df -Pk /
echo 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 ENDPOINTS_END
VM101

[[ "$(value COMMIT "$STEP_DIR/vm101-before.txt")" == "$EXPECTED_BEFORE_COMMIT" ]]
[[ "$(value TREE "$STEP_DIR/vm101-before.txt")" == "$EXPECTED_BEFORE_TREE" ]]
awk '/STATUS_BEGIN/{f=1;next}/STATUS_END/{f=0}f && NF{bad=1} END{exit bad?1:0}'     "$STEP_DIR/vm101-before.txt" || fail STOP_VM101_WORKTREE_NOT_CLEAN

PHASE=target_transfer
progress "transfer target Base64, gzip and extracted STEP separately; verify before run"
vm101_shell "umask 077; rm -rf '$TARGET_BASE'; mkdir -p '$TARGET_BASE'"
cat "$BUNDLE_ROOT/target-step.tar.gz.b64.txt" |
    ssh pve-mgts         "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 'cat > "$TARGET_B64"'"

vm101_stdin >"$STEP_DIR/target-transfer-verify.txt" <<VM101
set -eu
TARGET_BASE='$TARGET_BASE'
TARGET_B64='$TARGET_B64'
TARGET_GZ='$TARGET_GZ'
TARGET_EXTRACTED='$TARGET_EXTRACTED'
EXPECTED_SHA='$TARGET_GZ_SHA'

base64 -d "\$TARGET_B64" >"\$TARGET_GZ"
ACTUAL_SHA="\$(sha256sum "\$TARGET_GZ" | awk '{print \$1}')"
[ "\$ACTUAL_SHA" = "\$EXPECTED_SHA" ]
gzip -t "\$TARGET_GZ"
mkdir -p "\$TARGET_EXTRACTED"
tar -xzf "\$TARGET_GZ" -C "\$TARGET_EXTRACTED"
cd "\$TARGET_EXTRACTED/target-step"
sha256sum -c target-manifest.sha256
sh -n apply.sh
sh -n rootfs/usr/local/lib/router-egress-generation.sh
sh -n rootfs/usr/local/sbin/router-egress-generation-validate
chmod 700 apply.sh
echo "COPY_VERIFY=PASS"
echo "TARGET_GZIP_SHA256=\$ACTUAL_SHA"
echo "TARGET_STEP_FILE=\$TARGET_EXTRACTED/target-step/apply.sh"
VM101

grep -q '^COPY_VERIFY=PASS$' "$STEP_DIR/target-transfer-verify.txt" || {
    COPY_VERIFY=FAIL
    fail STOP_COPY_OR_TRANSFER_ERROR
}

PHASE=target_execution
progress "run verified target STEP without service restart or active-slot change"
STEP_WAS_NOT_STARTED=false
set +e
vm101_stdin >"$STEP_DIR/target-run.txt" <<VM101
set +e
STEP_FILE='$TARGET_STEP_FILE'
RUN_LOG='$TARGET_BASE/run.log'
"\$STEP_FILE" >"\$RUN_LOG" 2>&1
rc=\$?
cat "\$RUN_LOG"
exit \$rc
VM101
target_rc=$?
set -e

[[ "$target_rc" -eq 0 ]] || fail STOP_TARGET_R15B1_FAILED
grep -q '^RESULT=PASS_R15B1_GENERATION_SCHEMA_VALIDATOR_INSTALLED$' "$STEP_DIR/target-run.txt" ||
    fail STOP_TARGET_R15B1_RESULT_MISSING

CORE_CHANGE_COMPLETE=true

PHASE=target_postcheck
progress "collect target artifacts and independently confirm unchanged endpoints"
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 run.log'"     >"$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"

awk '/ENDPOINTS_BEGIN/{f=1;next}/ENDPOINTS_END/{f=0}f' "$STEP_DIR/vm101-before.txt"     >"$STEP_DIR/endpoints-before.tsv"
cp "$STEP_DIR/target-artifacts/state/endpoints-after.tsv" "$STEP_DIR/endpoints-after.tsv"
cmp "$STEP_DIR/endpoints-before.tsv" "$STEP_DIR/endpoints-after.tsv"

PHASE=machine_git_publication
progress "publish changed VM101 source through Machine Git v2"
MACHINE_LOG="$STEP_DIR/router-machine-close.log"
set +e
router-machine-close vm101     "$STEP_ID: add staged-generation schema and strict read-only 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")"
VM101_PUBLIC_URL="$(url_from_log VM101_PUBLIC_URL "$MACHINE_LOG")"
VM130_STATUS="$(value VM130_STATUS "$MACHINE_LOG")"

machine_proof=true
[[ -n "$NEW_COMMIT" ]] || machine_proof=false
[[ -n "$NEW_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
    fail STOP_MACHINE_CLOSE_FAILED
fi
[[ "$machine_proof" == true ]] || fail STOP_MACHINE_CLOSE_PROOF_FAILED

PHASE=github_exact_clone
progress "exact HTTPS clone verification of new commit/tree/file count"
AFTER_CLONE="$STEP_DIR/after-github-clone"
git clone --quiet "$REPOSITORY" "$AFTER_CLONE" >"$STEP_DIR/git-clone-after.log" 2>&1
git -C "$AFTER_CLONE" checkout --quiet --detach "$NEW_COMMIT"
[[ "$(git -C "$AFTER_CLONE" rev-parse HEAD)" == "$NEW_COMMIT" ]]
[[ "$(git -C "$AFTER_CLONE" rev-parse 'HEAD^{tree}')" == "$NEW_TREE" ]]
[[ "$(git -C "$AFTER_CLONE" ls-tree -r --name-only HEAD | wc -l | tr -d ' ')" == "$EXPECTED_AFTER_COUNT" ]]
for required in     etc/router-egress-generation.conf     usr/local/lib/router-egress-generation.sh     usr/local/sbin/router-egress-generation-validate
do
    git -C "$AFTER_CLONE" cat-file -e "HEAD:$required"
done

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 files: $NEW_COUNT
- active production slots: vpn1..vpn5, unchanged
- active generation: absent
- staged generation directories: empty
- generation schema: router-egress-generation-v1
- validator selftest: PASS
- activation capability: disabled
- Direct failopen: unchanged and disabled
EOF

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

R15B1 installs a read-only staged-generation contract.

A valid generation contains:

- metadata.kv;
- candidates.tsv with exact egress1..egress5 / vpn1..vpn5 / tables 201..205;
- source-pool.tsv;
- active-endpoints.tsv;
- quarantine-snapshot.tsv;
- copied config files for every slot;
- a ten-entry SHA256 manifest.

Validation rejects duplicates, active endpoint reuse, quarantine reuse, stale tests,
malformed endpoints, incomplete five-slot mappings, config hash mismatch and manifest
tampering. It cannot activate a generation or alter active VPN slots.
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/endpoints-before.tsv"     "$STEP_DIR/endpoints-after.tsv"     "$MACHINE_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/rollback.sh"
VM101_METHODS_URL="$(url_from_log VM101_METHODS_URL "$STEP_DIR/publish-methods.log")"

PHASE=local_plan_publication
progress "publish updated canonical local M07 plan while retaining full specification"
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/"

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 report, facts, verify 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 "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",
  "before_commit": "$EXPECTED_BEFORE_COMMIT",
  "after_commit": "$NEW_COMMIT",
  "after_tree": "$NEW_TREE",
  "before_file_count": $EXPECTED_BEFORE_COUNT,
  "after_file_count": $EXPECTED_AFTER_COUNT,
  "runtime_impact": false,
  "active_slots_changed": false,
  "active_generation_created": false,
  "validator_selftest": "PASS",
  "machine_git": "PASS",
  "local_m07_plan_url": "$LOCAL_M07_PLAN_URL",
  "full_plan_retained": true,
  "previous_r01_result": "STOP_PREFLIGHT_REQUIRED_COMMAND_MISSING",
  "previous_r01_step_was_not_started": true
}
EOF

cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
COPY_VERIFY=PASS
STEP_SHA256=$(step_sha)
TARGET_GZIP_SHA256=$TARGET_GZ_SHA
TARGET_DECODE_COMPARE=PASS
TARGET_GZIP_TEST=PASS
TARGET_MANIFEST=PASS
TARGET_SYNTAX=PASS
VALIDATOR_SELFTEST=PASS
ENDPOINTS_UNCHANGED=PASS
ROUTES_201_205=PASS
WATCHER_UNCHANGED=PASS
ACTIVE_GENERATION_CREATED=false
ACTIVE_SLOTS_CHANGED=false
BEFORE_COMMIT=$EXPECTED_BEFORE_COMMIT
AFTER_COMMIT=$NEW_COMMIT
AFTER_TREE=$NEW_TREE
AFTER_FILE_COUNT=$NEW_COUNT
GITHUB_EXACT_CLONE=PASS
MACHINE_GIT=PASS
FULL_CANONICAL_PLAN_RETAINED=PASS
PREVIOUS_R01_RESULT=STOP_PREFLIGHT_REQUIRED_COMMAND_MISSING
PREVIOUS_R01_STEP_WAS_NOT_STARTED=true
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_STATE_DIR=/var/lib/router-egress-recovery/generations
GENERATION_ACTIVE_LINK_PRESENT=false
GENERATION_STAGING_EMPTY=true
ACTIVE_SLOTS=vpn1 vpn2 vpn3 vpn4 vpn5
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=Install schema and validator only; no builder and no activation path.
VALIDATION=Positive plus duplicate, quarantine, active reuse, missing row, stale test, config mismatch, malformed endpoint and manifest tamper fixtures.
LIMITATION=IPv4 endpoint:port schema only, matching current HideMyName candidate format.
RUNTIME_IMPACT=false
SERVICE_RESTARTS=none
INTERFACES_CHANGED=none
TRAFFIC_INTERRUPTION=none
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=Install the immutable five-slot staged-generation schema and strict validator without building or activating a generation.
BEFORE_COMMIT=$EXPECTED_BEFORE_COMMIT
AFTER_COMMIT=$NEW_COMMIT
AFTER_TREE=$NEW_TREE
BEFORE_FILE_COUNT=$EXPECTED_BEFORE_COUNT
AFTER_FILE_COUNT=$NEW_COUNT
EXPECTED=Exactly five unique fresh tested candidates, fixed slot mapping, no active/quarantined endpoint reuse, immutable SHA256 evidence.
ACTUAL=Schema and validator installed; all positive and destructive fixtures passed.
ACTIVE_SLOTS_CHANGED=false
ACTIVE_GENERATION_CREATED=false
SERVICE_RESTARTS=none
TRAFFIC_INTERRUPTION=none
RUNTIME_IMPACT=false
ROLLBACK=$TARGET_RUNTIME_DIR/rollback.sh plus Machine Git republish
PREVIOUS_R01_RESULT=STOP_PREFLIGHT_REQUIRED_COMMAND_MISSING
PREVIOUS_R01_STEP_WAS_NOT_STARTED=true
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 STEP report"
publish_files     "step050m07r15b1_generation_schema_validator"     "$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/previous-r01-classification.txt"     "$STEP_DIR/target-transfer-verify.txt"     "$STEP_DIR/target-run.txt"     "$STEP_DIR/target-artifacts.tar.gz"     "$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/endpoints-before.tsv"     "$STEP_DIR/endpoints-after.tsv"     "$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")"
FACTS_URL="${STEP_REPORT_URL}facts.json"
VERIFY_URL="${STEP_REPORT_URL}verify.txt"
ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"

PHASE=external_http_verification
progress "external permanent HTTPS verification"
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-vm101-source-index.html"
http_fetch "$VM101_MODEL_URL" "$STEP_DIR/http-vm101-model-index.html"
http_fetch "$VM101_METHODS_URL" "$STEP_DIR/http-vm101-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 ]] || fail STOP_LATEST_CANONICAL_DUPLICATE
done

FINALIZED=true
trap - ERR
finish
