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

STEP_ID="STEP_050M07R15D_VM130_WORKFLOW_HARDENING_R01"
STEP_LABEL="step050m07r15d_vm130_workflow_hardening_r01"
SOURCE_LABEL="vm130_router_ops_source_snapshot_r15d_workflow_hardening_r01"
POST_LABEL="project_source_post_vm130_workflow_hardening_r15d"
EXPECTED_OLD_PUBLISHER_SHA256="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"

BUNDLE_DIR="$(cd "$(dirname "$0")" && pwd -P)"
SELF="$(readlink -f "$0")"
BUNDLE_ZIP="${BUNDLE_ZIP:?BUNDLE_ZIP is required}"
EXPECTED_BUNDLE_SHA256="${EXPECTED_BUNDLE_SHA256:?EXPECTED_BUNDLE_SHA256 is required}"
EXPECTED_INSTALLER_SHA256="${EXPECTED_INSTALLER_SHA256:?EXPECTED_INSTALLER_SHA256 is required}"
source "$BUNDLE_DIR/plans/current-links.env"
LATEST_ROOT_URL="https://reports.secret-studio.ru/latest/"
OLD_ROOT_URL="https://reports.secret-studio.ru/old/"

TS="$(date -u +%Y%m%d-%H%M%S)"
BASE="${ROUTER_OPS_BASE:-/opt/router-ops}"
CADDYFILE="${ROUTER_CADDYFILE:-/etc/caddy/Caddyfile}"
EXPECTED_HOST="${ROUTER_EXPECTED_HOST:-router-ops}"
ACTUAL_HOST="${ROUTER_HOSTNAME_OVERRIDE:-$(hostname)}"
STEP_DIR="$BASE/steps/${TS}_${STEP_ID}"
BIN="$BASE/bin"
LIB="$BASE/lib"
PUBLISHER="$BIN/router-publish-report"
HTTP_VERIFY="$BIN/router-http-verify"
REINDEX="$BIN/router-public-reindex"
TOKEN_FILE="$BASE/public/.router-public-token"
mkdir -p "$STEP_DIR"; chmod 700 "$STEP_DIR"
STEP_FILE="$STEP_DIR/step-code.sh"; cp -a "$SELF" "$STEP_FILE"; chmod 700 "$STEP_FILE"
BUNDLE_COPY="$STEP_DIR/$(basename "$BUNDLE_ZIP")"; cp -a "$BUNDLE_ZIP" "$BUNDLE_COPY"

REPORT="$STEP_DIR/report.txt"; FACTS="$STEP_DIR/facts.json"; VERIFY="$STEP_DIR/verify.txt"; ROLLBACK="$STEP_DIR/rollback.sh"
MAIN_LOG="$STEP_DIR/main.log"; PROGRESS_LOG="$STEP_DIR/progress.log"; TRANSFER_VERIFY="$STEP_DIR/transfer-verify.txt"
SYSTEM_MODEL="$STEP_DIR/system-model.txt"; METHODS="$STEP_DIR/methods-limitations.txt"; ACCESS_ROUTES="$STEP_DIR/access-routes.txt"
PHASE_CURRENT="$STEP_DIR/phase-current.txt"; PHASE_EVENTS="$STEP_DIR/phase-events.tsv"
BACKUP_ROOT="$STEP_DIR/rollback-files"; BEFORE_MANIFEST="$STEP_DIR/before-managed.sha256"; AFTER_MANIFEST="$STEP_DIR/after-managed.sha256"
INSTALL_LOG="$STEP_DIR/install.log"; SELFTEST_LOG="$STEP_DIR/installed-selftests.log"; SOURCE_DIR="$STEP_DIR/vm130-source"
SOURCE_PUBLISH_LOG="$STEP_DIR/source-publish.log"; SOURCE_HTTP_LOG="$STEP_DIR/source-http.log"
POST_DIR="$STEP_DIR/project-source-post"; POST_FILE="$POST_DIR/WG_PAID_MGTS_VM130_WORKFLOW_HARDENING_R15D.md"
POST_FACTS="$POST_DIR/facts.json"; POST_VERIFY="$POST_DIR/verify.txt"; POST_PUBLISH_LOG="$STEP_DIR/post-publish.log"; POST_HTTP_LOG="$STEP_DIR/post-http.log"
SAFE_PUBLISH="$STEP_DIR/step-report-payload"; STEP_PUBLISH_LOG="$STEP_DIR/step-publish.log"; HTTP_LOG="$STEP_DIR/http-verify.log"
mkdir -p "$BACKUP_ROOT" "$SOURCE_DIR" "$POST_DIR" "$SAFE_PUBLISH"
chmod 700 "$BACKUP_ROOT" "$SOURCE_DIR" "$POST_DIR" "$SAFE_PUBLISH"

RESULT="STOP_NOT_STARTED"; STOP_REASON="none"; CURRENT_PHASE="initialization"; COPY_VERIFY="FAIL"; STEP_WAS_NOT_STARTED="true"
CORE_CHANGE_COMPLETE="false"; CORE_CHANGE_REPEATED="false"; TARGET_INSTALLER_RERUN="false"; MACHINE_GIT_COMPLETE="false"
ROUTER_MACHINE_CLOSE_RERUN="false"; LIVE_ROUTER_MACHINE_CLOSE_RUN="false"; VM101_RUNTIME_CHANGE="false"; RUNTIME_IMPACT="false"
INSTALLED_STARTED="false"; INSTALLED_SELFTEST_PASS="false"; SOURCE_PUBLICATION_COMPLETE="false"; POST_PUBLICATION_COMPLETE="false"
STEP_REPORT_PUBLICATION_COMPLETE="false"; HTTP_VERIFY_PASS="false"; CORE_ROLLED_BACK="false"; WORKFLOW_READY="false"
PUBLIC_OBJECT_KIND=""; PREVIOUS_MOVED_COUNT=""; COLLISION_DEDUPLICATED_COUNT=""; PUBLISHER_RC="unknown"
STEP_REPORT_URL=""; FACTS_URL=""; VERIFY_URL=""; ROLLBACK_URL=""; STEP_PUBLISHED_DIR=""
PROJECT_SOURCE_POST_URL=""; PROJECT_SOURCE_POST_FILE_URL=""; POST_PUBLISHED_DIR=""
SOURCE_PUBLISHED_DIR=""; SOURCE_ARCHIVE_SHA256=""; SOURCE_FILE_COUNT=""

exec 3>&1
exec >> "$MAIN_LOG" 2>&1
progress(){ printf '>>> [%s] %s\n' "$(date '+%F %T')" "$*" | tee -a "$PROGRESS_LOG" >&3; }

PAYLOAD_ROOT="$BUNDLE_DIR/payload"
CHANGED_PATHS=(
  "$BASE/bin/router-publish-report"
  "$BASE/bin/router-http-verify"
  "$BASE/bin/router-http-verify-selftest"
  "$BASE/bin/router-publication-workflow-selftest"
  "$BASE/bin/router-step-workflow-selftest"
  "$BASE/bin/router-machine-close"
  "$BASE/bin/router-machine-close-contract-selftest"
  "$BASE/bin/router-vm100-git-source-publish"
  "$BASE/bin/router-vm101-git-source-publish"
  "$BASE/bin/router-vm121-git-source-publish"
  "$BASE/lib/router-publication-contract.sh"
  "$BASE/lib/router-step-workflow.sh"
  "$BASE/lib/router-machine-close-contract.sh"
)

write_docs(){
  local step_sha=""; step_sha="$(sha256sum "$STEP_FILE" 2>/dev/null | awk '{print $1}' || true)"
  cat > "$SYSTEM_MODEL" <<MODEL
STEP_ID=$STEP_ID
HOST=$(hostname)
SCOPE=VM130 workflow and publication infrastructure only
CHANGED_PATH_COUNT=${#CHANGED_PATHS[@]}
VM100_RUNTIME_CHANGE=false
VM101_RUNTIME_CHANGE=false
VM121_RUNTIME_CHANGE=false
LIVE_ROUTER_MACHINE_CLOSE_RUN=false
R15B2_CORE_REPEATED=false
SERVICE_RESTARTS=none
TRAFFIC_INTERRUPTION=false
RUNTIME_IMPACT=false
MODEL
  cat > "$METHODS" <<METHODS_EOF
Methods: exact live SHA preflight; atomic per-file install; installed synthetic regression suite; source archive round-trip; hardened local publisher contract; independent HTTP retry/cache-busting verification; append-only phase events plus current phase state; no live machine-close execution.
Collision rule: only byte-identical latest/old duplicates may be deduplicated. Any mismatch is blocking and rollback-safe.
Continuation rule: first incomplete phase is authoritative; completed core/publication phases are not repeated.
Limitations: this STEP does not exercise a real VM100/VM101/VM121 source publication; those paths are covered by synthetic contract fixtures and will receive their first live use in a later changing STEP.
METHODS_EOF
  cat > "$ACCESS_ROUTES" <<ACCESS
No access routes, aliases, keys, IP addresses or jump hosts are changed.
ACCESS_MAP_URL=$ACCESS_MAP_URL
ACCESS
  python3 - "$FACTS" <<PY
import json,sys
p=sys.argv[1]
def b(value):
    return value == "true"
data={
 "step_id":"$STEP_ID","result":"$RESULT","stop_reason":"$STOP_REASON","current_phase":"$CURRENT_PHASE",
 "copy_verify":"$COPY_VERIFY","step_sha256":"$step_sha","expected_step_sha256":"$EXPECTED_INSTALLER_SHA256",
 "step_was_not_started":b("$STEP_WAS_NOT_STARTED"),"core_change_complete":b("$CORE_CHANGE_COMPLETE"),"core_change_repeated":b("$CORE_CHANGE_REPEATED"),
 "target_installer_rerun":b("$TARGET_INSTALLER_RERUN"),"machine_git_complete":b("$MACHINE_GIT_COMPLETE"),
 "router_machine_close_rerun":b("$ROUTER_MACHINE_CLOSE_RERUN"),"live_router_machine_close_run":b("$LIVE_ROUTER_MACHINE_CLOSE_RUN"),
 "vm101_runtime_change":b("$VM101_RUNTIME_CHANGE"),"installed_selftest_pass":b("$INSTALLED_SELFTEST_PASS"),
 "source_publication_complete":b("$SOURCE_PUBLICATION_COMPLETE"),"project_source_post_publication_complete":b("$POST_PUBLICATION_COMPLETE"),
 "step_report_publication_complete":b("$STEP_REPORT_PUBLICATION_COMPLETE"),"http_verify_pass":b("$HTTP_VERIFY_PASS"),
 "runtime_impact":b("$RUNTIME_IMPACT"),"core_rolled_back":b("$CORE_ROLLED_BACK"),
 "vm130_source_url":"$VM130_SOURCE_URL","project_source_post_url":"$PROJECT_SOURCE_POST_URL",
 "project_source_post_file_url":"$PROJECT_SOURCE_POST_FILE_URL","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",
 "source_archive_sha256":"$SOURCE_ARCHIVE_SHA256","source_file_count":"${SOURCE_FILE_COUNT:-}",
 "public_object_kind":"$PUBLIC_OBJECT_KIND","previous_moved_count":"${PREVIOUS_MOVED_COUNT:-}",
 "collision_deduplicated_count":"${COLLISION_DEDUPLICATED_COUNT:-}","publisher_rc":"$PUBLISHER_RC"
}
with open(p,'w',encoding='utf-8') as f: json.dump(data,f,ensure_ascii=False,indent=2,sort_keys=True); f.write('\n')
PY
  cat > "$VERIFY" <<VERIFY_EOF
RESULT=$RESULT
CURRENT_PHASE=$CURRENT_PHASE
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$step_sha
EXPECTED_STEP_SHA256=$EXPECTED_INSTALLER_SHA256
CORE_CHANGE_COMPLETE=$CORE_CHANGE_COMPLETE
INSTALLED_SELFTEST_PASS=$INSTALLED_SELFTEST_PASS
SOURCE_PUBLICATION_COMPLETE=$SOURCE_PUBLICATION_COMPLETE
PROJECT_SOURCE_POST_PUBLICATION_COMPLETE=$POST_PUBLICATION_COMPLETE
STEP_REPORT_PUBLICATION_COMPLETE=$STEP_REPORT_PUBLICATION_COMPLETE
HTTP_VERIFY_PASS=$HTTP_VERIFY_PASS
LIVE_ROUTER_MACHINE_CLOSE_RUN=false
VM101_RUNTIME_CHANGE=false
RUNTIME_IMPACT=false
VERIFY_EOF
  cat > "$REPORT" <<REPORT_EOF
RESULT=$RESULT
STEP_ID=$STEP_ID
STOP_REASON=$STOP_REASON
CURRENT_PHASE=$CURRENT_PHASE
CORE_CHANGE_COMPLETE=$CORE_CHANGE_COMPLETE
CORE_CHANGE_REPEATED=$CORE_CHANGE_REPEATED
TARGET_INSTALLER_RERUN=$TARGET_INSTALLER_RERUN
MACHINE_GIT_COMPLETE=$MACHINE_GIT_COMPLETE
ROUTER_MACHINE_CLOSE_RERUN=$ROUTER_MACHINE_CLOSE_RERUN
LIVE_ROUTER_MACHINE_CLOSE_RUN=false
VM101_RUNTIME_CHANGE=false
INSTALLED_SELFTEST_PASS=$INSTALLED_SELFTEST_PASS
SOURCE_PUBLICATION_COMPLETE=$SOURCE_PUBLICATION_COMPLETE
PROJECT_SOURCE_POST_PUBLICATION_COMPLETE=$POST_PUBLICATION_COMPLETE
STEP_REPORT_PUBLICATION_COMPLETE=$STEP_REPORT_PUBLICATION_COMPLETE
HTTP_VERIFY_PASS=$HTTP_VERIFY_PASS
RUNTIME_IMPACT=false
STEP_REPORT_URL=${STEP_REPORT_URL:-PENDING_NOT_PUBLISHED}
PROJECT_SOURCE_POST_URL=${PROJECT_SOURCE_POST_URL:-PENDING_NOT_PUBLISHED}
PROJECT_SOURCE_POST_FILE_URL=${PROJECT_SOURCE_POST_FILE_URL:-PENDING_NOT_PUBLISHED}
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:-PENDING_NOT_PUBLISHED}
VERIFY_URL=${VERIFY_URL:-PENDING_NOT_PUBLISHED}
ROLLBACK_URL=${ROLLBACK_URL:-PENDING_NOT_PUBLISHED}
PUBLIC_OBJECT_KIND=${PUBLIC_OBJECT_KIND:-step-report}
PREVIOUS_MOVED_COUNT=${PREVIOUS_MOVED_COUNT:-unknown}
COLLISION_DEDUPLICATED_COUNT=${COLLISION_DEDUPLICATED_COUNT:-unknown}
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$step_sha
REPORT_EOF
}

print_final(){
  write_docs
  {
    echo "RESULT=$RESULT"
    echo "STEP_REPORT_URL=${STEP_REPORT_URL:-PENDING_NOT_PUBLISHED}"
    echo "PROJECT_SOURCE_POST_URL=${PROJECT_SOURCE_POST_URL:-PENDING_NOT_PUBLISHED}"
    echo "PROJECT_SOURCE_POST_FILE_URL=${PROJECT_SOURCE_POST_FILE_URL:-PENDING_NOT_PUBLISHED}"
    echo "LOCAL_M07_PLAN_URL=$LOCAL_M07_PLAN_URL"
    echo "GLOBAL_PROJECT_PLAN_URL=$GLOBAL_PROJECT_PLAN_URL"
    echo "ACCESS_MAP_URL=$ACCESS_MAP_URL"
    echo "VM100_PUBLIC_URL=$VM100_PUBLIC_URL"
    echo "VM101_PUBLIC_URL=$VM101_PUBLIC_URL"
    echo "VM121_PUBLIC_URL=$VM121_PUBLIC_URL"
    echo "VM101_MODEL_URL=$VM101_MODEL_URL"
    echo "VM101_METHODS_URL=$VM101_METHODS_URL"
    echo "VM130_SOURCE_URL=$VM130_SOURCE_URL"
    echo "LATEST_ROOT_URL=$LATEST_ROOT_URL"
    echo "OLD_ROOT_URL=$OLD_ROOT_URL"
    echo "FACTS_URL=${FACTS_URL:-PENDING_NOT_PUBLISHED}"
    echo "VERIFY_URL=${VERIFY_URL:-PENDING_NOT_PUBLISHED}"
    echo "ROLLBACK_URL=${ROLLBACK_URL:-PENDING_NOT_PUBLISHED}"
    echo "PUBLIC_OBJECT_KIND=${PUBLIC_OBJECT_KIND:-step-report}"
    echo "PREVIOUS_MOVED_COUNT=${PREVIOUS_MOVED_COUNT:-unknown}"
    echo "COLLISION_DEDUPLICATED_COUNT=${COLLISION_DEDUPLICATED_COUNT:-unknown}"
    echo "COPY_VERIFY=$COPY_VERIFY"
    echo "STEP_SHA256=$(sha256sum "$STEP_FILE" | awk '{print $1}')"
    echo "HTTP_VERIFY_PASS=$HTTP_VERIFY_PASS"
    echo "RUNTIME_IMPACT=false"
  } >&3
}

restore_partial_install(){
  set +e
  while IFS=$'\t' read -r path existed mode; do
    [[ -n "$path" ]] || continue
    backup="$BACKUP_ROOT$path"
    if [[ "$existed" == true ]]; then
      mkdir -p "$(dirname "$path")"; cp -a "$backup" "${path}.rollback.$$"; mv -f "${path}.rollback.$$" "$path"
    else
      rm -f "$path"
    fi
  done < "$STEP_DIR/backup-index.tsv"
  CORE_ROLLED_BACK="true"
  set -e
}

publish_stop_best_effort(){
  set +e
  write_docs
  if [[ -x "$PUBLISHER" ]]; then
    rm -rf "$SAFE_PUBLISH"; mkdir -p "$SAFE_PUBLISH"
    for f in "$STEP_FILE" "$REPORT" "$FACTS" "$VERIFY" "$ROLLBACK" "$MAIN_LOG" "$PROGRESS_LOG" "$TRANSFER_VERIFY" "$SYSTEM_MODEL" "$METHODS" "$ACCESS_ROUTES" "$PHASE_CURRENT" "$PHASE_EVENTS" "$INSTALL_LOG" "$SELFTEST_LOG"; do [[ -f "$f" ]] && cp -a "$f" "$SAFE_PUBLISH/$(basename "$f")"; done
    "$PUBLISHER" "${STEP_LABEL}_stop" "$SAFE_PUBLISH"/* > "$STEP_PUBLISH_LOG" 2>&1
    if [[ $? -eq 0 ]]; then
      STEP_REPORT_URL="$(awk -F= '$1=="STEP_REPORT_URL"{print substr($0,index($0,"=")+1)}' "$STEP_PUBLISH_LOG"|tail -1)"
      STEP_PUBLISHED_DIR="$(awk -F= '$1=="PUBLISHED_DIR"{print substr($0,index($0,"=")+1)}' "$STEP_PUBLISH_LOG"|tail -1)"
      FACTS_URL="${STEP_REPORT_URL}facts.json"; VERIFY_URL="${STEP_REPORT_URL}verify.txt"; ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"
    fi
  fi
  set -e
}

stop_step(){
  local result="$1" reason="$2" rc="${3:-1}"
  trap - ERR
  RESULT="$result"; STOP_REASON="$reason"
  if [[ "$WORKFLOW_READY" == true ]]; then router_step_phase_fail "$CURRENT_PHASE" "$reason" || true; fi
  if [[ "$INSTALLED_STARTED" == true && "$CORE_CHANGE_COMPLETE" == false ]]; then restore_partial_install; fi
  publish_stop_best_effort
  print_final
  exit "$rc"
}

on_err(){ local rc=$? line="$1"; trap - ERR; if [[ "$WORKFLOW_READY" == true ]] && router_step_workflow_is_complete; then RESULT=PASS_R15D_VM130_WORKFLOW_HARDENED; STOP_REASON=late_error_after_complete_ignored; print_final; exit 0; fi; stop_step STOP_R15D_UNEXPECTED_ERROR "phase=$CURRENT_PHASE;line=$line;rc=$rc" "$rc"; }
trap 'on_err $LINENO' ERR

# Bundle-local workflow library is verified before it is trusted.
phase_begin(){ CURRENT_PHASE="$1"; router_step_phase_begin "$1"; progress "$1"; }
phase_complete(){ router_step_phase_complete "$1"; }

# Transfer verification without installed dependencies.
CURRENT_PHASE=transfer_verification; progress transfer_verification
ACTUAL_BUNDLE_SHA="$(sha256sum "$BUNDLE_COPY"|awk '{print $1}')"; ACTUAL_INSTALLER_SHA="$(sha256sum "$STEP_FILE"|awk '{print $1}')"
[[ "$ACTUAL_BUNDLE_SHA" == "$EXPECTED_BUNDLE_SHA256" && "$ACTUAL_INSTALLER_SHA" == "$EXPECTED_INSTALLER_SHA256" ]] || stop_step STOP_COPY_OR_TRANSFER_ERROR sha_mismatch
(cd "$BUNDLE_DIR" && sha256sum -c manifest.sha256) > "$STEP_DIR/manifest-check.log" 2>&1 || stop_step STOP_COPY_OR_TRANSFER_ERROR manifest_failed
bash -n "$STEP_FILE" || stop_step STOP_COPY_OR_TRANSFER_ERROR installer_syntax
for t in "$BUNDLE_DIR"/tests/test-*.sh; do bash "$t" >> "$STEP_DIR/bundle-fixtures.log" 2>&1 || stop_step STOP_COPY_OR_TRANSFER_ERROR "fixture:$(basename "$t")"; done
COPY_VERIFY=true; COPY_VERIFY="PASS"; STEP_WAS_NOT_STARTED=false
cat > "$TRANSFER_VERIFY" <<TRANSFER
EXPECTED_BUNDLE_SHA256=$EXPECTED_BUNDLE_SHA256
ACTUAL_BUNDLE_SHA256=$ACTUAL_BUNDLE_SHA
EXPECTED_INSTALLER_SHA256=$EXPECTED_INSTALLER_SHA256
ACTUAL_INSTALLER_SHA256=$ACTUAL_INSTALLER_SHA
MANIFEST_VERIFY=PASS
SYNTAX_VERIFY=PASS
FIXTURE_VERIFY=PASS
COPY_VERIFY=PASS
STEP_SHA256=$ACTUAL_INSTALLER_SHA
TRANSFER

source "$BUNDLE_DIR/payload/opt/router-ops/lib/router-step-workflow.sh"
router_step_workflow_init "$PHASE_CURRENT" "$PHASE_EVENTS" transfer_verification vm130_preflight backup core_install installed_selftests source_build source_publication source_http_verification project_source_post_build project_source_post_publication project_source_post_http_verification step_report_preparation step_report_publication external_http_verification final_artifact_sync
WORKFLOW_READY=true
router_step_phase_begin transfer_verification; router_step_phase_complete transfer_verification

phase_begin vm130_preflight
for cmd in bash awk sed grep find tar gzip base64 sha256sum cmp diff mktemp stat curl flock python3 tee sort xargs readlink dirname basename cp mv rm chmod mkdir date hostname install sync; do command -v "$cmd" >/dev/null 2>&1 || stop_step STOP_PREFLIGHT_REQUIRED_COMMAND_MISSING "missing_command=$cmd"; done
[[ "$ACTUAL_HOST" == "$EXPECTED_HOST" ]] || stop_step STOP_PREFLIGHT_WRONG_HOST "hostname=$ACTUAL_HOST expected=$EXPECTED_HOST"
[[ -x "$REINDEX" && -r "$TOKEN_FILE" ]] || stop_step STOP_PREFLIGHT_PUBLICATION_INFRA_MISSING reindex_or_token
[[ "$(sha256sum "$PUBLISHER"|awk '{print $1}')" == "$EXPECTED_OLD_PUBLISHER_SHA256" ]] || stop_step STOP_PREFLIGHT_CURRENT_PUBLISHER_SHA_MISMATCH unexpected_live_version
map_reference_path(){
  local rel="$1"
  case "$rel" in
    ./opt/router-ops/*) printf '%s/%s\n' "$BASE" "${rel#./opt/router-ops/}" ;;
    ./etc/caddy/Caddyfile) printf '%s\n' "$CADDYFILE" ;;
    *) printf '/%s\n' "${rel#./}" ;;
  esac
}
while read -r sha rel; do live="$(map_reference_path "$rel")"; [[ -f "$live" ]] || stop_step STOP_PREFLIGHT_CURRENT_SOURCE_MISSING "$live"; [[ "$(sha256sum "$live"|awk '{print $1}')" == "$sha" ]] || stop_step STOP_PREFLIGHT_CURRENT_SOURCE_SHA_MISMATCH "$live"; done < "$BUNDLE_DIR/reference/vm130-r15c-current-source-manifest.sha256"
phase_complete vm130_preflight

phase_begin backup
: > "$STEP_DIR/backup-index.tsv"; : > "$BEFORE_MANIFEST"
for path in "${CHANGED_PATHS[@]}"; do
  if [[ -e "$path" ]]; then
    mkdir -p "$BACKUP_ROOT$(dirname "$path")"; cp -a "$path" "$BACKUP_ROOT$path"; mode="$(stat -c '%a' "$path")"; printf '%s\ttrue\t%s\n' "$path" "$mode" >> "$STEP_DIR/backup-index.tsv"; printf '%s  %s\n' "$(sha256sum "$path"|awk '{print $1}')" "$path" >> "$BEFORE_MANIFEST"
  else
    printf '%s\tfalse\tmissing\n' "$path" >> "$STEP_DIR/backup-index.tsv"
  fi
done
cat > "$ROLLBACK" <<ROLLBACK_EOF
#!/usr/bin/env bash
set -Eeuo pipefail
BACKUP_ROOT='$BACKUP_ROOT'; INDEX='$STEP_DIR/backup-index.tsv'
while IFS=	 read -r path existed mode; do
  [[ -n "\$path" ]] || continue
  if [[ "\$existed" == true ]]; then mkdir -p "\$(dirname "\$path")"; cp -a "\$BACKUP_ROOT\$path" "\${path}.rollback.\$\$"; mv -f "\${path}.rollback.\$\$" "\$path"; else rm -f "\$path"; fi
done < "\$INDEX"
echo RESULT=PASS_R15D_CODE_ROLLBACK
ROLLBACK_EOF
chmod 700 "$ROLLBACK"
phase_complete backup

phase_begin core_install
INSTALLED_STARTED=true
: > "$INSTALL_LOG"
for path in "${CHANGED_PATHS[@]}"; do
  case "$path" in "$BASE"/*) canonical="/opt/router-ops/${path#"$BASE"/}" ;; *) canonical="$path" ;; esac
  src="$PAYLOAD_ROOT$canonical"; [[ -f "$src" ]] || stop_step STOP_R15D_PAYLOAD_MISSING "$src"
  mkdir -p "$(dirname "$path")"; mode=0644; [[ "$path" == "$BASE"/bin/* ]] && mode=0755
  install -m "$mode" "$src" "${path}.new.$STEP_ID"
  case "$(head -n1 "${path}.new.$STEP_ID" 2>/dev/null || true)" in '#!/usr/bin/env bash'|'#!/bin/bash') bash -n "${path}.new.$STEP_ID";; '#!/bin/sh') sh -n "${path}.new.$STEP_ID";; esac
  mv -f "${path}.new.$STEP_ID" "$path"
  printf 'INSTALLED=%s SHA256=%s\n' "$path" "$(sha256sum "$path"|awk '{print $1}')" >> "$INSTALL_LOG"
done
sync
phase_complete core_install

phase_begin installed_selftests
: > "$SELFTEST_LOG"
ROUTER_PUBLISHER_UNDER_TEST="$PUBLISHER" "$BIN/router-publication-workflow-selftest" >> "$SELFTEST_LOG" 2>&1
ROUTER_HTTP_VERIFY_UNDER_TEST="$HTTP_VERIFY" "$BIN/router-http-verify-selftest" >> "$SELFTEST_LOG" 2>&1
ROUTER_STEP_WORKFLOW_LIB_UNDER_TEST="$LIB/router-step-workflow.sh" "$BIN/router-step-workflow-selftest" >> "$SELFTEST_LOG" 2>&1
ROUTER_MACHINE_CLOSE_WRAPPER_UNDER_TEST="$BIN/router-machine-close" ROUTER_MACHINE_CLOSE_LIBRARY_UNDER_TEST="$LIB/router-machine-close-contract.sh" "$BIN/router-machine-close-contract-selftest" >> "$SELFTEST_LOG" 2>&1
for marker in PASS_ROUTER_PUBLICATION_WORKFLOW_SELFTEST PASS_ROUTER_HTTP_VERIFY_SELFTEST PASS_ROUTER_STEP_WORKFLOW_SELFTEST PASS_ROUTER_MACHINE_CLOSE_CONTRACT_SELFTEST; do grep -q "RESULT=$marker" "$SELFTEST_LOG" || stop_step STOP_R15D_INSTALLED_SELFTEST_FAILED "$marker"; done
: > "$AFTER_MANIFEST"
for path in "${CHANGED_PATHS[@]}"; do [[ -f "$path" ]]; printf '%s  %s\n' "$(sha256sum "$path"|awk '{print $1}')" "$path" >> "$AFTER_MANIFEST"; done
INSTALLED_SELFTEST_PASS=true; CORE_CHANGE_COMPLETE=true
phase_complete installed_selftests

phase_begin source_build
SOURCE_ROOT="$SOURCE_DIR/root"; mkdir -p "$SOURCE_ROOT"
mkdir -p "$SOURCE_ROOT/etc/caddy"; cp -a "$CADDYFILE" "$SOURCE_ROOT/etc/caddy/Caddyfile"
SOURCE_PATHS=("$BASE/bin/router-public-reindex" "${CHANGED_PATHS[@]}")
for live in "${SOURCE_PATHS[@]}"; do
  if [[ "$live" == "$BASE"/* ]]; then rel="/opt/router-ops/${live#"$BASE"/}"; else rel="$live"; fi
  dst="$SOURCE_ROOT$rel"; mkdir -p "$(dirname "$dst")"; cp -a "$live" "$dst"
done
SOURCE_MANIFEST="$SOURCE_DIR/vm130-source-manifest.sha256"; (cd "$SOURCE_ROOT" && find . -type f -print0 | sort -z | xargs -0 sha256sum) > "$SOURCE_MANIFEST"
SOURCE_FILE_COUNT="$(wc -l < "$SOURCE_MANIFEST"|tr -d ' ')"; SOURCE_ARCHIVE="$SOURCE_DIR/vm130-router-ops-r15d-workflow-hardening.tar.gz"; SOURCE_B64="$SOURCE_ARCHIVE.b64.txt"; SOURCE_SHA_FILE="$SOURCE_ARCHIVE.sha256"
tar -C "$SOURCE_ROOT" -czf "$SOURCE_ARCHIVE" .; SOURCE_ARCHIVE_SHA256="$(sha256sum "$SOURCE_ARCHIVE"|awk '{print $1}')"; printf '%s  %s\n' "$SOURCE_ARCHIVE_SHA256" "$(basename "$SOURCE_ARCHIVE")" > "$SOURCE_SHA_FILE"; base64 -w 76 "$SOURCE_ARCHIVE" > "$SOURCE_B64"
ROUND="$SOURCE_DIR/roundtrip"; mkdir -p "$ROUND/extract"; base64 -d "$SOURCE_B64" > "$ROUND/archive.tar.gz"; gzip -t "$ROUND/archive.tar.gz"; tar -xzf "$ROUND/archive.tar.gz" -C "$ROUND/extract"; (cd "$ROUND/extract" && sha256sum -c "$SOURCE_MANIFEST") > "$SOURCE_DIR/roundtrip.log" 2>&1
while IFS= read -r f; do case "$(head -n1 "$f" 2>/dev/null||true)" in '#!/usr/bin/env bash'|'#!/bin/bash') bash -n "$f";; '#!/bin/sh') sh -n "$f";; esac; done < <(find "$ROUND/extract" -type f|sort)
cat > "$SOURCE_DIR/report.txt" <<SOURCE_REPORT
RESULT=PASS_VM130_SOURCE_BUILT
STEP_ID=$STEP_ID
ARCHIVE=$(basename "$SOURCE_ARCHIVE")
ARCHIVE_SHA256=$SOURCE_ARCHIVE_SHA256
FILE_COUNT=$SOURCE_FILE_COUNT
ROUNDTRIP_VERIFY=PASS
SYNTAX_VERIFY=PASS
RUNTIME_IMPACT=false
SOURCE_REPORT
cat > "$SOURCE_DIR/facts.json" <<SOURCE_FACTS
{"result":"PASS_VM130_SOURCE_BUILT","step_id":"$STEP_ID","archive":"$(basename "$SOURCE_ARCHIVE")","archive_sha256":"$SOURCE_ARCHIVE_SHA256","file_count":$SOURCE_FILE_COUNT,"roundtrip_verify":true,"syntax_verify":true,"runtime_impact":false}
SOURCE_FACTS
cat > "$SOURCE_DIR/verify.txt" <<SOURCE_VERIFY
ARCHIVE_SHA256=$SOURCE_ARCHIVE_SHA256
GZIP_TEST=PASS
BASE64_DECODE=PASS
MANIFEST_CHECK=PASS
SYNTAX_CHECK=PASS
SOURCE_VERIFY
phase_complete source_build

source "$LIB/router-publication-contract.sh"
phase_begin source_publication
set +e
"$PUBLISHER" "$SOURCE_LABEL" "$SOURCE_DIR/report.txt" "$SOURCE_DIR/facts.json" "$SOURCE_DIR/verify.txt" "$SOURCE_ARCHIVE" "$SOURCE_B64" "$SOURCE_SHA_FILE" "$SOURCE_MANIFEST" > "$SOURCE_PUBLISH_LOG" 2>&1
SOURCE_PUBLISH_RC=$?
set -e
cat "$SOURCE_PUBLISH_LOG"
router_publication_contract_load "$SOURCE_PUBLISH_LOG" vm130-source || stop_step STOP_R15D_VM130_SOURCE_PUBLICATION_CONTRACT "publisher_rc=$SOURCE_PUBLISH_RC"
SOURCE_PUBLISHED_DIR="$RPC_PUBLISHED_DIR"; VM130_SOURCE_URL="$RPC_PUBLIC_URL"
[[ "$SOURCE_PUBLISH_RC" -eq 0 || "$RPC_PUBLICATION_COMMITTED" == true ]] || stop_step STOP_R15D_VM130_SOURCE_PUBLICATION_FAILED "rc=$SOURCE_PUBLISH_RC"
SOURCE_PUBLICATION_COMPLETE=true
cat > "$BASE/state/current-vm130-source.env.tmp.$$" <<STATE
VM130_SOURCE_UPDATED_AT_UTC=$TS
VM130_SOURCE_STEP_ID=$STEP_ID
VM130_SOURCE_PUBLIC_DIR=$SOURCE_PUBLISHED_DIR
VM130_SOURCE_URL=$VM130_SOURCE_URL
VM130_SOURCE_ARCHIVE_SHA256=$SOURCE_ARCHIVE_SHA256
VM130_SOURCE_FILE_COUNT=$SOURCE_FILE_COUNT
STATE
chmod 600 "$BASE/state/current-vm130-source.env.tmp.$$"; mv -f "$BASE/state/current-vm130-source.env.tmp.$$" "$BASE/state/current-vm130-source.env"
phase_complete source_publication

phase_begin source_http_verification
: > "$SOURCE_HTTP_LOG"
"$HTTP_VERIFY" --url "$VM130_SOURCE_URL" --output "$STEP_DIR/http-source-index.html" >> "$SOURCE_HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "${VM130_SOURCE_URL}$(basename "$SOURCE_ARCHIVE")" --output "$STEP_DIR/http-source.tar.gz" --sha256 "$SOURCE_ARCHIVE_SHA256" >> "$SOURCE_HTTP_LOG" 2>&1
phase_complete source_http_verification

phase_begin project_source_post_build
PUBLISHER_AFTER_SHA="$(sha256sum "$PUBLISHER"|awk '{print $1}')"; HTTP_AFTER_SHA="$(sha256sum "$HTTP_VERIFY"|awk '{print $1}')"; WORKFLOW_LIB_SHA="$(sha256sum "$LIB/router-step-workflow.sh"|awk '{print $1}')"; CONTRACT_LIB_SHA="$(sha256sum "$LIB/router-publication-contract.sh"|awk '{print $1}')"
cat > "$POST_FILE" <<POST
# WG Paid / MGTS — VM130 workflow hardening перед R16

Дата выполнения: $(date -u '+%Y-%m-%d %H:%M:%S UTC')  
STEP: \`$STEP_ID\`  
Итог: \`PASS_R15D_VM130_WORKFLOW_HARDENED\`

## Зачем понадобился отдельный hardening

R15B1 и R15B2 показали повторяющийся класс проблем: целевая функция уже работала, но STEP получал формальный STOP на transport, collector, Machine Git wrapper, state update, collision \`latest/old\` или поздней HTTP-проверке. Это создавало continuation, хотя runtime VM101 оставался корректным.

Цель R15D — отделить production core от инфраструктуры доказательств и сделать публикацию транзакционной, проверяемой и пригодной для безопасного salvage.

## Что изменено

### 1. Атомарный publisher

\`router-publish-report\` теперь:

- допускает только одну текущую папку каждого canonical kind;
- повторную публикацию идентичного payload переиспользует без создания дубля;
- при collision \`latest/old\` удаляет только побайтно идентичный дубль;
- при любом различии останавливается до commit;
- откатывает перемещения при reindex или другой ошибке;
- печатает строгий контракт: \`PUBLICATION_COMMITTED\`, \`LOCAL_POSTCHECK\`, \`LATEST_INDEX_CONTAINS_FOLDER\`, \`PUBLICATION_REUSED\`, \`COLLISION_DEDUPLICATED_COUNT\` и \`RESULT=PASS_ROUTER_PUBLISH_REPORT\`.

### 2. HTTP verifier

Добавлен \`router-http-verify\`:

- retry с ограничением количества попыток;
- cache-busting query на каждой попытке;
- \`curl --resolve\` для постоянного HTTPS через VM130;
- проверка HTTP code и, при необходимости, SHA256 тела;
- сохранение доказанного файла только после PASS.

### 3. Source publication contract

Backends VM100/VM101/VM121 больше не держат publisher в хрупком command substitution. Они сохраняют stdout/stderr и RC отдельно, проверяют локальный publisher contract, затем независимо проверяют HTTPS и только после этого атомарно обновляют current state.

### 4. router-machine-close

\`router-machine-close\` принимает PASS только при полном наборе доказательств: Git/clone, publisher commit, local postcheck, latest index, HTTP postcheck, permanent HTTPS, state update, commit/tree/count и canonical URL. Backend RC может быть salvaged только при полном authoritative evidence.

### 5. Phase journal

Добавлена общая library \`router-step-workflow.sh\`:

- append-only events;
- current status каждой фазы;
- вычисление первой незавершённой фазы;
- явные \`complete/skipped/failed\`;
- защита от замены уже доказанного PASS поздним общим ERR.

## Regression fixtures

Проверены сценарии:

- первая публикация;
- идемпотентный повтор того же payload;
- нормальный перенос current в old;
- идентичный collision в latest/old;
- collision с различающимся содержимым;
- reindex failure и rollback;
- первый HTTP ответ со stale body, второй корректный;
- окончательный SHA mismatch;
- backend PASS с RC 0;
- backend authoritative PASS с RC 1;
- backend STOP;
- timeout/signal;
- неполный source-state contract;
- continuation от первой незавершённой фазы;
- поздняя ошибка после workflow complete.

## Установленные контрольные суммы

- router-publish-report: \`$PUBLISHER_AFTER_SHA\`
- router-http-verify: \`$HTTP_AFTER_SHA\`
- router-step-workflow.sh: \`$WORKFLOW_LIB_SHA\`
- router-publication-contract.sh: \`$CONTRACT_LIB_SHA\`
- VM130 source archive: \`$SOURCE_ARCHIVE_SHA256\`
- VM130 source file count: \`$SOURCE_FILE_COUNT\`

## Runtime impact

VM100, VM101 и VM121 не изменялись. Live \`router-machine-close\` не запускался. VPN-интерфейсы, routes, services и staged generation R15B2 не затрагивались. \`RUNTIME_IMPACT=false\`.

## Canonical состояние

- R15B2 report: $R15B2_STEP_REPORT_URL
- VM101 source: $VM101_PUBLIC_URL
- VM101 model: $VM101_MODEL_URL
- VM101 methods: $VM101_METHODS_URL
- Local M07 plan: $LOCAL_M07_PLAN_URL
- Fresh VM130 source: $VM130_SOURCE_URL

## Следующий этап

После R15D можно проектировать R16: транзакционную all-or-nothing активацию staged generation. R16 должен использовать phase journal и новый publication contract с самого начала.

## Как использовать этот документ

Файл предназначен для добавления в Project Sources как новая контрольная точка проекта. Он заменяет разрозненный пересказ ошибок R15B1/R15B2 единым описанием действующего workflow contract.
POST
POST_SHA="$(sha256sum "$POST_FILE"|awk '{print $1}')"
cat > "$POST_FACTS" <<POST_FACTS
{"result":"PASS_R15D_PROJECT_SOURCE_POST_BUILT","step_id":"$STEP_ID","post_file":"$(basename "$POST_FILE")","post_sha256":"$POST_SHA","vm130_source_url":"$VM130_SOURCE_URL","runtime_impact":false}
POST_FACTS
cat > "$POST_VERIFY" <<POST_VERIFY
POST_SHA256=$POST_SHA
VM130_SOURCE_URL=$VM130_SOURCE_URL
INSTALLED_SELFTEST_PASS=true
RUNTIME_IMPACT=false
POST_VERIFY
phase_complete project_source_post_build

phase_begin project_source_post_publication
set +e
"$PUBLISHER" "$POST_LABEL" "$POST_FILE" "$POST_FACTS" "$POST_VERIFY" > "$POST_PUBLISH_LOG" 2>&1
POST_PUBLISH_RC=$?
set -e
cat "$POST_PUBLISH_LOG"
router_publication_contract_load "$POST_PUBLISH_LOG" project-source-post || stop_step STOP_R15D_PROJECT_SOURCE_POST_PUBLICATION_CONTRACT "publisher_rc=$POST_PUBLISH_RC"
POST_PUBLISHED_DIR="$RPC_PUBLISHED_DIR"; PROJECT_SOURCE_POST_URL="$RPC_PUBLIC_URL"; PROJECT_SOURCE_POST_FILE_URL="${PROJECT_SOURCE_POST_URL}$(basename "$POST_FILE")"
[[ "$POST_PUBLISH_RC" -eq 0 || "$RPC_PUBLICATION_COMMITTED" == true ]] || stop_step STOP_R15D_PROJECT_SOURCE_POST_PUBLICATION_FAILED "rc=$POST_PUBLISH_RC"
POST_PUBLICATION_COMPLETE=true
phase_complete project_source_post_publication

phase_begin project_source_post_http_verification
POST_SHA="$(sha256sum "$POST_FILE"|awk '{print $1}')"; : > "$POST_HTTP_LOG"
"$HTTP_VERIFY" --url "$PROJECT_SOURCE_POST_URL" --output "$STEP_DIR/http-post-index.html" >> "$POST_HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "$PROJECT_SOURCE_POST_FILE_URL" --output "$STEP_DIR/http-project-source-post.md" --sha256 "$POST_SHA" >> "$POST_HTTP_LOG" 2>&1
phase_complete project_source_post_http_verification

phase_begin step_report_preparation
RESULT=PASS_R15D_VM130_WORKFLOW_HARDENED; STOP_REASON=none; write_docs
rm -rf "$SAFE_PUBLISH"; mkdir -p "$SAFE_PUBLISH"
for f in "$STEP_FILE" "$REPORT" "$FACTS" "$VERIFY" "$ROLLBACK" "$MAIN_LOG" "$PROGRESS_LOG" "$TRANSFER_VERIFY" "$SYSTEM_MODEL" "$METHODS" "$ACCESS_ROUTES" "$PHASE_CURRENT" "$PHASE_EVENTS" "$BEFORE_MANIFEST" "$AFTER_MANIFEST" "$INSTALL_LOG" "$SELFTEST_LOG" "$SOURCE_PUBLISH_LOG" "$SOURCE_HTTP_LOG" "$POST_FILE" "$POST_FACTS" "$POST_VERIFY" "$POST_PUBLISH_LOG" "$POST_HTTP_LOG"; do [[ -f "$f" ]] && cp -a "$f" "$SAFE_PUBLISH/$(basename "$f")"; done
phase_complete step_report_preparation

phase_begin step_report_publication
set +e
"$PUBLISHER" "$STEP_LABEL" "$SAFE_PUBLISH"/* > "$STEP_PUBLISH_LOG" 2>&1
PUBLISHER_RC=$?
set -e
cat "$STEP_PUBLISH_LOG"
router_publication_contract_load "$STEP_PUBLISH_LOG" step-report || stop_step STOP_R15D_STEP_REPORT_PUBLICATION_CONTRACT "publisher_rc=$PUBLISHER_RC"
STEP_PUBLISHED_DIR="$RPC_PUBLISHED_DIR"; STEP_REPORT_URL="$RPC_PUBLIC_URL"; PUBLIC_OBJECT_KIND="$RPC_PUBLIC_OBJECT_KIND"; PREVIOUS_MOVED_COUNT="$RPC_PREVIOUS_MOVED_COUNT"; COLLISION_DEDUPLICATED_COUNT="$RPC_COLLISION_DEDUPLICATED_COUNT"
FACTS_URL="${STEP_REPORT_URL}facts.json"; VERIFY_URL="${STEP_REPORT_URL}verify.txt"; ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"
[[ "$PUBLISHER_RC" -eq 0 || "$RPC_PUBLICATION_COMMITTED" == true ]] || stop_step STOP_R15D_STEP_REPORT_PUBLICATION_FAILED "rc=$PUBLISHER_RC"
STEP_REPORT_PUBLICATION_COMPLETE=true
phase_complete step_report_publication

phase_begin external_http_verification
: > "$HTTP_LOG"
"$HTTP_VERIFY" --url "$STEP_REPORT_URL" --output "$STEP_DIR/http-step-index.html" >> "$HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "${STEP_REPORT_URL}step-code.sh" --output "$STEP_DIR/http-step-code.sh" --sha256 "$EXPECTED_INSTALLER_SHA256" >> "$HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "$PROJECT_SOURCE_POST_FILE_URL" --output "$STEP_DIR/http-post-final.md" --sha256 "$(sha256sum "$POST_FILE"|awk '{print $1}')" >> "$HTTP_LOG" 2>&1
HTTP_VERIFY_PASS=true
phase_complete external_http_verification

phase_begin final_artifact_sync
CURRENT_PHASE=complete; RESULT=PASS_R15D_VM130_WORKFLOW_HARDENED; STOP_REASON=none; write_docs
for f in "$REPORT" "$FACTS" "$VERIFY"; do cp -a "$f" "$STEP_PUBLISHED_DIR/$(basename "$f").tmp.$$"; chmod 644 "$STEP_PUBLISHED_DIR/$(basename "$f").tmp.$$"; mv -f "$STEP_PUBLISHED_DIR/$(basename "$f").tmp.$$" "$STEP_PUBLISHED_DIR/$(basename "$f")"; done
"$REINDEX" >/dev/null
"$HTTP_VERIFY" --url "$FACTS_URL" --output "$STEP_DIR/http-final-facts.json" --sha256 "$(sha256sum "$FACTS"|awk '{print $1}')" >> "$HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "$VERIFY_URL" --output "$STEP_DIR/http-final-verify.txt" --sha256 "$(sha256sum "$VERIFY"|awk '{print $1}')" >> "$HTTP_LOG" 2>&1
"$HTTP_VERIFY" --url "${STEP_REPORT_URL}report.txt" --output "$STEP_DIR/http-final-report.txt" --sha256 "$(sha256sum "$REPORT"|awk '{print $1}')" >> "$HTTP_LOG" 2>&1
phase_complete final_artifact_sync
router_step_workflow_mark_complete
trap - ERR
set +e
print_final
exit 0
