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

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

STEP="STEP_050M07R04A_REPAIR_CANONICAL_CURRENT_AND_STATE"
PASS_DECISION="PASS_${STEP}"

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

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

CANONICAL_STATE="${STATE_ROOT}/current-vm101-canonical.env"
CANONICAL_ROOT="${PUBROOT}/vm101-canonical"
PUBLIC_CURRENT="${CANONICAL_ROOT}/current"

ARCHITECTURE_PLAN="${PUBLIC_BASE}/20260711-181158_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/"
VM101_REFERENCE="${PUBLIC_BASE}/vm101-reference/current/"
VM101_CANONICAL="${PUBLIC_BASE}/vm101-canonical/current/"

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

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

CURRENT_STAGE="initialization"
LAST_SUCCESS="step_saved"

EXPECTED_ID="UNRESOLVED"
EXPECTED_WORKSPACE="UNRESOLVED"
EXPECTED_SNAPSHOT="UNRESOLVED"
EXPECTED_SNAPSHOT_URL="UNRESOLVED"

CURRENT_ID_BEFORE="UNRESOLVED"
CURRENT_ID_AFTER="UNRESOLVED"

SNAPSHOT_CREATED=false
CURRENT_REPUBLISHED=false
STATE_REPAIRED=false
VALIDATOR_INSTALLED=false
VALIDATION_PASSED=false

VM101_CONTACTED=false
VM101_MODIFIED=false

mkdir -p "$REPORT_DIR"

cp -a "$0" "$REPORT_DIR/step.sh"
chmod 600 "$REPORT_DIR/step.sh"

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

stage() {
  CURRENT_STAGE="$1"

  echo
  echo ">>> [$1] $2" | tee -a "$PROGRESS_LOG"

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

mark_success() {
  LAST_SUCCESS="$1"
  echo "last_success=$LAST_SUCCESS" >> "$PROGRESS_LOG"
}

env_value() {
  local key="$1"

  sed -n "s/^${key}=//p" "$CANONICAL_STATE" |
    tail -n1
}

json_value() {
  local path="$1"
  local expression="$2"

  python3 - "$path" "$expression" <<'PY'
import json
import sys

path, expression = sys.argv[1:]

data = json.load(open(path, encoding="utf-8"))

value = data

for part in expression.split("."):
    value = value[part]

if isinstance(value, bool):
    print("true" if value else "false")
else:
    print(value)
PY
}

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"
  echo "VM101_REFERENCE=$VM101_REFERENCE"
  echo "VM101_CANONICAL=$VM101_CANONICAL"
}

create_index() {
  cat > "$REPORT_DIR/index.html" <<EOF
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>${STEP}</title>
</head>
<body style="font-family:system-ui;max-width:1100px;margin:40px auto;padding:0 20px">
<h1>${STEP}</h1>

<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="validation.json">validation.json</a></li>
<li><a href="current-before.json">current-before.json</a></li>
<li><a href="current-after.json">current-after.json</a></li>
<li><a href="step.sh">step.sh</a></li>
</ul>

<h2>Постоянные ссылки</h2>
<ul>
<li><a href="${VM101_REFERENCE}">VM101 Reference</a></li>
<li><a href="${VM101_CANONICAL}">VM101 Canonical</a></li>
</ul>
</body>
</html>
EOF
}

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}
step_execution=STOP
operation_result=CANONICAL_PUBLICATION_NOT_REPAIRED
production_health=UNCHANGED
milestone_status=M07_IN_PROGRESS
all_ok=false

failure:
  rc=${rc}
  line=${line}
  stage=${CURRENT_STAGE}
  last_success=${LAST_SUCCESS}

canonical:
  expected_id=${EXPECTED_ID}
  expected_workspace=${EXPECTED_WORKSPACE}
  expected_snapshot=${EXPECTED_SNAPSHOT}
  current_id_before=${CURRENT_ID_BEFORE}
  current_id_after=${CURRENT_ID_AFTER}

actions:
  snapshot_created=${SNAPSHOT_CREATED}
  current_republished=${CURRENT_REPUBLISHED}
  state_repaired=${STATE_REPAIRED}
  validator_installed=${VALIDATOR_INSTALLED}
  validation_passed=${VALIDATION_PASSED}

safety:
  local_only=true
  vm101_contacted=false
  vm101_modified=false
  network_changed=false
  services_changed=false
  state_changed=false
  refresh_ran=false
  rebalance_ran=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}
VM101_REFERENCE=${VM101_REFERENCE}
VM101_CANONICAL=${VM101_CANONICAL}
EOF

  python3 - \
    "$STEP" \
    "$reason" \
    "$rc" \
    "$line" \
    "$CURRENT_STAGE" \
    "$LAST_SUCCESS" \
    "$EXPECTED_ID" \
    "$EXPECTED_WORKSPACE" \
    "$EXPECTED_SNAPSHOT" \
    "$CURRENT_ID_BEFORE" \
    "$CURRENT_ID_AFTER" \
    "$SNAPSHOT_CREATED" \
    "$CURRENT_REPUBLISHED" \
    "$STATE_REPAIRED" \
    "$VALIDATOR_INSTALLED" \
    "$VALIDATION_PASSED" \
    "$TRYCF_REPORT" \
    "$REPORT_TXT" \
    "$FACTS_JSON" \
    "$ARCHITECTURE_PLAN" \
    "$XS_MAP" \
    "$GLOBAL_PROJECT_PLAN" \
    "$VM101_REFERENCE" \
    "$VM101_CANONICAL" \
    > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    step,
    reason,
    rc,
    line,
    stage,
    last_success,
    expected_id,
    expected_workspace,
    expected_snapshot,
    current_before,
    current_after,
    snapshot_created,
    current_republished,
    state_repaired,
    validator_installed,
    validation_passed,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
    vm101_reference,
    vm101_canonical,
) = sys.argv[1:]

to_bool = lambda value: value == "true"

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "assessment": {
        "decision": f"STOP_{step}_{reason}",
        "step_execution": "STOP",
        "operation_result":
            "CANONICAL_PUBLICATION_NOT_REPAIRED",
        "production_health": "UNCHANGED",
        "milestone_status": "M07_IN_PROGRESS",
        "all_ok": False,
        "failure": {
            "reason": reason,
            "rc": int(rc),
            "line": int(line),
            "stage": stage,
            "last_success": last_success,
        },
    },
    "canonical": {
        "expected_id": expected_id,
        "expected_workspace": expected_workspace,
        "expected_snapshot": expected_snapshot,
        "current_id_before": current_before,
        "current_id_after": current_after,
    },
    "actions": {
        "snapshot_created":
            to_bool(snapshot_created),
        "current_republished":
            to_bool(current_republished),
        "state_repaired":
            to_bool(state_repaired),
        "validator_installed":
            to_bool(validator_installed),
        "validation_passed":
            to_bool(validation_passed),
    },
    "safety": {
        "local_only": True,
        "vm101_contacted": False,
        "vm101_modified": False,
        "network_changed": False,
        "services_changed": False,
        "state_changed": False,
        "refresh_ran": False,
        "rebalance_ran": 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,
        "vm101_reference": vm101_reference,
        "vm101_canonical": vm101_canonical,
    },
}, ensure_ascii=False, indent=2))
PY

  create_index

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

  print_links
  exit "$rc"
}

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

  trap - ERR
  write_stop "$reason" "$rc" "$line"
}

trap 'rc=$?; fatal "UNEXPECTED_ERROR" "$rc" "$LINENO"' ERR

stage "01/07" "Читаю canonical state без source и без переменных-коллизий"

[ -s "$CANONICAL_STATE" ] ||
  fatal "CANONICAL_STATE_MISSING" 2 "$LINENO"

EXPECTED_ID="$(
  env_value VM101_CANONICAL_ID
)"

EXPECTED_WORKSPACE="$(
  env_value VM101_CANONICAL_PRIVATE_WORKSPACE
)"

[ -n "$EXPECTED_ID" ] ||
  fatal "EXPECTED_ID_EMPTY" 3 "$LINENO"

[ -n "$EXPECTED_WORKSPACE" ] ||
  fatal "EXPECTED_WORKSPACE_EMPTY" 4 "$LINENO"

EXPECTED_SNAPSHOT="${CANONICAL_ROOT}/snapshots/${EXPECTED_ID}"
EXPECTED_SNAPSHOT_URL="${PUBLIC_BASE}/vm101-canonical/snapshots/${EXPECTED_ID}/"

echo "expected_id=$EXPECTED_ID" |
  tee -a "$PROGRESS_LOG"

echo "expected_workspace=$EXPECTED_WORKSPACE" |
  tee -a "$PROGRESS_LOG"

echo "expected_snapshot=$EXPECTED_SNAPSHOT" |
  tee -a "$PROGRESS_LOG"

mark_success "canonical_state_read_safely"

stage "02/07" "Проверяю private edit-map workspace"

for required in \
  "$EXPECTED_WORKSPACE/contracts/edit-map-private.json" \
  "$EXPECTED_WORKSPACE/contracts/edit-map-public.json" \
  "$EXPECTED_WORKSPACE/contracts/replacement-blueprints.json" \
  "$EXPECTED_WORKSPACE/contracts/source-lock-validation.json" \
  "$EXPECTED_WORKSPACE/public-summary/canonical-manifest.json" \
  "$EXPECTED_WORKSPACE/public-summary/contracts/edit-map.json" \
  "$EXPECTED_WORKSPACE/public-summary/contracts/replacement-blueprints.json" \
  "$EXPECTED_WORKSPACE/public-summary/index.html" \
  "$EXPECTED_WORKSPACE/public-summary/SHA256SUMS" \
  "$EXPECTED_WORKSPACE/SHA256SUMS"
do
  [ -s "$required" ] || {
    echo "MISSING_PRIVATE_COMPONENT=$required"
    fatal "PRIVATE_EDITMAP_WORKSPACE_INCOMPLETE" 5 "$LINENO"
  }
done

(
  cd "$EXPECTED_WORKSPACE"
  sha256sum -c SHA256SUMS
) > "$REPORT_DIR/private-workspace-sha256.txt" 2>&1 ||
  fatal "PRIVATE_WORKSPACE_SHA256_FAILED" 6 "$LINENO"

(
  cd "$EXPECTED_WORKSPACE/public-summary"
  sha256sum -c SHA256SUMS
) > "$REPORT_DIR/public-summary-sha256.txt" 2>&1 ||
  fatal "PUBLIC_SUMMARY_SHA256_FAILED" 7 "$LINENO"

WORKSPACE_ID="$(
  json_value \
    "$EXPECTED_WORKSPACE/contracts/edit-map-private.json" \
    canonical_id
)"

PUBLIC_STAGE_ID="$(
  json_value \
    "$EXPECTED_WORKSPACE/public-summary/canonical-manifest.json" \
    canonical_id
)"

[ "$WORKSPACE_ID" = "$EXPECTED_ID" ] || {
  echo "WORKSPACE_ID_MISMATCH=$WORKSPACE_ID"
  fatal "PRIVATE_WORKSPACE_ID_MISMATCH" 8 "$LINENO"
}

[ "$PUBLIC_STAGE_ID" = "$EXPECTED_ID" ] || {
  echo "PUBLIC_STAGE_ID_MISMATCH=$PUBLIC_STAGE_ID"
  fatal "PUBLIC_STAGE_ID_MISMATCH" 9 "$LINENO"
}

EDIT_READY="$(
  json_value \
    "$EXPECTED_WORKSPACE/contracts/edit-map-private.json" \
    edit_ready_target_count
)"

TARGET_COUNT="$(
  json_value \
    "$EXPECTED_WORKSPACE/contracts/edit-map-private.json" \
    target_count
)"

[ "$EDIT_READY" = "$TARGET_COUNT" ] ||
  fatal "EDIT_MAP_NOT_READY" 10 "$LINENO"

mark_success "private_editmap_workspace_verified"

stage "03/07" "Фиксирую фактический current до ремонта"

if [ -s "$PUBLIC_CURRENT/canonical-manifest.json" ]; then
  cp -a \
    "$PUBLIC_CURRENT/canonical-manifest.json" \
    "$REPORT_DIR/current-before.json"

  CURRENT_ID_BEFORE="$(
    json_value \
      "$PUBLIC_CURRENT/canonical-manifest.json" \
      canonical_id
  )"
else
  echo '{}' > "$REPORT_DIR/current-before.json"
  CURRENT_ID_BEFORE="MISSING"
fi

echo "current_id_before=$CURRENT_ID_BEFORE" |
  tee -a "$PROGRESS_LOG"

mark_success "current_before_captured"

stage "04/07" "Проверяю или создаю immutable edit-map snapshot"

if [ -e "$EXPECTED_SNAPSHOT" ]; then
  [ -s "$EXPECTED_SNAPSHOT/canonical-manifest.json" ] ||
    fatal "EXISTING_SNAPSHOT_INCOMPLETE" 11 "$LINENO"

  EXISTING_ID="$(
    json_value \
      "$EXPECTED_SNAPSHOT/canonical-manifest.json" \
      canonical_id
  )"

  [ "$EXISTING_ID" = "$EXPECTED_ID" ] || {
    echo "EXISTING_SNAPSHOT_ID=$EXISTING_ID"
    fatal "EXISTING_SNAPSHOT_ID_MISMATCH" 12 "$LINENO"
  }

  (
    cd "$EXPECTED_SNAPSHOT"
    sha256sum -c SHA256SUMS
  ) > "$REPORT_DIR/existing-snapshot-sha256.txt" 2>&1 ||
    fatal "EXISTING_SNAPSHOT_SHA256_FAILED" 13 "$LINENO"
else
  SNAPSHOT_TMP="${EXPECTED_SNAPSHOT}.new.$$"

  rm -rf "$SNAPSHOT_TMP"

  mkdir -p "$(dirname "$EXPECTED_SNAPSHOT")"

  cp -a \
    "$EXPECTED_WORKSPACE/public-summary" \
    "$SNAPSHOT_TMP"

  chmod -R a+rX "$SNAPSHOT_TMP"

  (
    cd "$SNAPSHOT_TMP"
    sha256sum -c SHA256SUMS
  ) > "$REPORT_DIR/new-snapshot-sha256.txt" 2>&1 ||
    fatal "NEW_SNAPSHOT_SHA256_FAILED" 14 "$LINENO"

  mv "$SNAPSHOT_TMP" "$EXPECTED_SNAPSHOT"
  SNAPSHOT_CREATED=true
fi

mark_success "immutable_snapshot_verified"

stage "05/07" "Атомарно обновляю canonical current"

CURRENT_NEW="${CANONICAL_ROOT}/current.new.$$"
CURRENT_OLD="${CANONICAL_ROOT}/current.old.$$"

rm -rf "$CURRENT_NEW" "$CURRENT_OLD"

cp -a "$EXPECTED_SNAPSHOT" "$CURRENT_NEW"

(
  cd "$CURRENT_NEW"
  sha256sum -c SHA256SUMS
) > "$REPORT_DIR/current-new-sha256.txt" 2>&1 ||
  fatal "CURRENT_NEW_SHA256_FAILED" 15 "$LINENO"

NEW_ID="$(
  json_value \
    "$CURRENT_NEW/canonical-manifest.json" \
    canonical_id
)"

[ "$NEW_ID" = "$EXPECTED_ID" ] ||
  fatal "CURRENT_NEW_ID_MISMATCH" 16 "$LINENO"

if [ -e "$PUBLIC_CURRENT" ]; then
  mv "$PUBLIC_CURRENT" "$CURRENT_OLD"
fi

mv "$CURRENT_NEW" "$PUBLIC_CURRENT"

rm -rf "$CURRENT_OLD"

CURRENT_REPUBLISHED=true

CURRENT_ID_AFTER="$(
  json_value \
    "$PUBLIC_CURRENT/canonical-manifest.json" \
    canonical_id
)"

[ "$CURRENT_ID_AFTER" = "$EXPECTED_ID" ] ||
  fatal "CURRENT_AFTER_ID_MISMATCH" 17 "$LINENO"

cp -a \
  "$PUBLIC_CURRENT/canonical-manifest.json" \
  "$REPORT_DIR/current-after.json"

mark_success "canonical_current_republished"

stage "06/07" "Исправляю state, history и устанавливаю валидатор"

REFERENCE_ID="$(
  json_value \
    "$PUBLIC_CURRENT/canonical-manifest.json" \
    reference_id
)"

SOURCE_REFERENCE_ID="$(
  json_value \
    "$PUBLIC_CURRENT/canonical-manifest.json" \
    source_reference_id
)"

SOURCE_CANONICAL_ID="$(
  json_value \
    "$PUBLIC_CURRENT/canonical-manifest.json" \
    source_canonical_id
)"

cat > "$CANONICAL_STATE" <<EOF
VM101_CANONICAL_ID=${EXPECTED_ID}
VM101_CANONICAL=${VM101_CANONICAL}
VM101_CANONICAL_SNAPSHOT=${EXPECTED_SNAPSHOT_URL}
VM101_CANONICAL_PRIVATE_WORKSPACE=${EXPECTED_WORKSPACE}
VM101_CANONICAL_REFERENCE_ID=${REFERENCE_ID}
VM101_CANONICAL_SOURCE_REFERENCE_ID=${SOURCE_REFERENCE_ID}
VM101_CANONICAL_SOURCE_CANONICAL_ID=${SOURCE_CANONICAL_ID}
VM101_CANONICAL_GENERATED_AT_UTC=${BUILD_TS}
VM101_CANONICAL_STATUS=EDIT_MAP_LOCKED_REPLACEMENTS_NOT_YET_WRITTEN
EOF

chmod 600 "$CANONICAL_STATE"
STATE_REPAIRED=true

python3 - \
  "$CANONICAL_ROOT" \
  "$EXPECTED_ID" \
  "$SOURCE_CANONICAL_ID" \
  "$EXPECTED_SNAPSHOT_URL" \
  "$VM101_CANONICAL" \
  "$BUILD_TS" <<'PY'
import html
import json
import sys
from pathlib import Path

(
    root_arg,
    canonical_id,
    source_canonical_id,
    snapshot_url,
    current_url,
    generated_at,
) = sys.argv[1:]

root = Path(root_arg)
history_path = root / "history.json"

if history_path.exists():
    try:
        history = json.loads(
            history_path.read_text(
                encoding="utf-8"
            )
        )
    except Exception:
        history = {
            "schema": "vm101-canonical-history-v1",
            "snapshots": [],
        }
else:
    history = {
        "schema": "vm101-canonical-history-v1",
        "snapshots": [],
    }

history["current"] = canonical_id

snapshots = history.setdefault(
    "snapshots",
    [],
)

existing = {
    item.get("canonical_id")
    for item in snapshots
}

if canonical_id not in existing:
    snapshots.append({
        "canonical_id": canonical_id,
        "source_canonical_id":
            source_canonical_id,
        "generated_at_compact_utc":
            generated_at,
        "snapshot_url":
            snapshot_url,
        "status":
            "EDIT_MAP_LOCKED_REPLACEMENTS_NOT_YET_WRITTEN",
    })

history["snapshots"] = sorted(
    snapshots,
    key=lambda item: item["canonical_id"],
    reverse=True,
)

history_path.write_text(
    json.dumps(
        history,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)

items = []

for item in history["snapshots"]:
    items.append(
        '<li><a href="'
        + html.escape(item["snapshot_url"])
        + '">'
        + html.escape(item["canonical_id"])
        + "</a> — "
        + html.escape(item.get("status", "UNKNOWN"))
        + "</li>"
    )

index = f"""<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>VM101 Canonical</title>
</head>
<body style="font-family:system-ui;max-width:1100px;margin:40px auto;padding:0 20px">
<h1>VM101 Canonical</h1>

<p><a href="{html.escape(current_url)}"><strong>Current canonical</strong></a></p>

<h2>Immutable snapshots</h2>
<ul>
{''.join(items)}
</ul>

<p><a href="history.json">history.json</a></p>
</body>
</html>
"""

(root / "index.html").write_text(
    index,
    encoding="utf-8",
)
PY

cat > "$ROOT/bin/vm101-canonical-validate" <<'VALIDATOR'
#!/usr/bin/env bash
set -Eeuo pipefail

STATE="/opt/router-ops/state/current-vm101-canonical.env"
PUBLIC_ROOT="/opt/router-ops/public/r/e94a0859747d7b96f29c7fdafc2d0351ba603bb0a7e9e5a4/vm101-canonical"
CURRENT="${PUBLIC_ROOT}/current"

fail() {
  echo "VALIDATION_PASS=false"
  echo "reason=$1"
  exit 1
}

[ -s "$STATE" ] ||
  fail "state_missing"

value() {
  sed -n "s/^${1}=//p" "$STATE" |
    tail -n1
}

EXPECTED_ID="$(value VM101_CANONICAL_ID)"
WORKSPACE="$(value VM101_CANONICAL_PRIVATE_WORKSPACE)"
SNAPSHOT_URL="$(value VM101_CANONICAL_SNAPSHOT)"

[ -n "$EXPECTED_ID" ] ||
  fail "expected_id_empty"

[ -n "$WORKSPACE" ] ||
  fail "workspace_empty"

for required in \
  "$CURRENT/canonical-manifest.json" \
  "$CURRENT/contracts/edit-map.json" \
  "$CURRENT/contracts/replacement-blueprints.json" \
  "$CURRENT/SHA256SUMS" \
  "$WORKSPACE/contracts/edit-map-private.json" \
  "$WORKSPACE/contracts/replacement-blueprints.json" \
  "$WORKSPACE/SHA256SUMS"
do
  [ -s "$required" ] ||
    fail "required_missing:$required"
done

CURRENT_ID="$(
  python3 - "$CURRENT/canonical-manifest.json" <<'PY'
import json
import sys

data = json.load(open(sys.argv[1], encoding="utf-8"))
print(data["canonical_id"])
PY
)"

PRIVATE_ID="$(
  python3 - "$WORKSPACE/contracts/edit-map-private.json" <<'PY'
import json
import sys

data = json.load(open(sys.argv[1], encoding="utf-8"))
print(data["canonical_id"])
PY
)"

[ "$CURRENT_ID" = "$EXPECTED_ID" ] ||
  fail "current_id_mismatch:$CURRENT_ID:$EXPECTED_ID"

[ "$PRIVATE_ID" = "$EXPECTED_ID" ] ||
  fail "private_id_mismatch:$PRIVATE_ID:$EXPECTED_ID"

(
  cd "$CURRENT"
  sha256sum -c SHA256SUMS
) >/dev/null ||
  fail "current_sha256_failed"

(
  cd "$WORKSPACE"
  sha256sum -c SHA256SUMS
) >/dev/null ||
  fail "private_sha256_failed"

TARGETS="$(
  python3 - "$CURRENT/contracts/edit-map.json" <<'PY'
import json
import sys

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

print(
    f"{data['edit_ready_target_count']}/"
    f"{data['target_count']}"
)
PY
)"

[ "$TARGETS" = "9/9" ] ||
  fail "edit_ready_targets:$TARGETS"

echo "VALIDATION_PASS=true"
echo "canonical_id=$EXPECTED_ID"
echo "current_id=$CURRENT_ID"
echo "private_id=$PRIVATE_ID"
echo "edit_ready_targets=$TARGETS"
echo "snapshot_url=$SNAPSHOT_URL"
echo "workspace=$WORKSPACE"
VALIDATOR

chmod 755 "$ROOT/bin/vm101-canonical-validate"
VALIDATOR_INSTALLED=true

if "$ROOT/bin/vm101-canonical-validate" \
  > "$REPORT_DIR/validation.txt"
then
  VALIDATION_PASSED=true
else
  fatal "CANONICAL_VALIDATOR_FAILED" 18 "$LINENO"
fi

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

facts = {}

for line in Path(sys.argv[1]).read_text(
    encoding="utf-8"
).splitlines():
    if "=" in line:
        key, value = line.split("=", 1)
        facts[key] = value

print(json.dumps({
    "schema":
        "vm101-canonical-publication-validation-v1",
    "passed":
        facts.get("VALIDATION_PASS") == "true",
    "canonical_id":
        facts.get("canonical_id"),
    "current_id":
        facts.get("current_id"),
    "private_id":
        facts.get("private_id"),
    "edit_ready_targets":
        facts.get("edit_ready_targets"),
    "snapshot_url":
        facts.get("snapshot_url"),
    "workspace":
        facts.get("workspace"),
}, ensure_ascii=False, indent=2))
PY

mark_success "state_history_and_validator_repaired"

stage "07/07" "Формирую PASS"

CURRENT_MANIFEST_SHA="$(
  sha256sum "$PUBLIC_CURRENT/canonical-manifest.json" |
    cut -d' ' -f1
)"

EDIT_MAP_SHA="$(
  sha256sum "$PUBLIC_CURRENT/contracts/edit-map.json" |
    cut -d' ' -f1
)"

BLUEPRINT_SHA="$(
  sha256sum "$PUBLIC_CURRENT/contracts/replacement-blueprints.json" |
    cut -d' ' -f1
)"

python3 - \
  "$STEP" \
  "$EXPECTED_ID" \
  "$SOURCE_CANONICAL_ID" \
  "$EXPECTED_WORKSPACE" \
  "$EXPECTED_SNAPSHOT" \
  "$EXPECTED_SNAPSHOT_URL" \
  "$CURRENT_ID_BEFORE" \
  "$CURRENT_ID_AFTER" \
  "$SNAPSHOT_CREATED" \
  "$CURRENT_REPUBLISHED" \
  "$STATE_REPAIRED" \
  "$VALIDATOR_INSTALLED" \
  "$VALIDATION_PASSED" \
  "$CURRENT_MANIFEST_SHA" \
  "$EDIT_MAP_SHA" \
  "$BLUEPRINT_SHA" \
  "$VM101_CANONICAL" \
  > "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

(
    step,
    canonical_id,
    source_canonical_id,
    workspace,
    snapshot_path,
    snapshot_url,
    current_before,
    current_after,
    snapshot_created,
    current_republished,
    state_repaired,
    validator_installed,
    validation_passed,
    manifest_sha,
    edit_map_sha,
    blueprint_sha,
    current_url,
) = sys.argv[1:]

to_bool = lambda value: value == "true"

print(json.dumps({
    "schema":
        "vm101-canonical-publication-repair-v1",
    "decision": f"PASS_{step}",
    "step_execution": "PASS",
    "operation_result":
        "CANONICAL_CURRENT_STATE_AND_VALIDATOR_REPAIRED",
    "production_health": "UNCHANGED",
    "milestone_status": "M07_IN_PROGRESS",
    "all_ok": True,
    "canonical": {
        "canonical_id": canonical_id,
        "source_canonical_id":
            source_canonical_id,
        "private_workspace": workspace,
        "snapshot_path": snapshot_path,
        "snapshot_url": snapshot_url,
        "current_url": current_url,
        "current_id_before": current_before,
        "current_id_after": current_after,
    },
    "actions": {
        "snapshot_created":
            to_bool(snapshot_created),
        "current_republished":
            to_bool(current_republished),
        "state_repaired":
            to_bool(state_repaired),
        "validator_installed":
            to_bool(validator_installed),
        "validation_passed":
            to_bool(validation_passed),
    },
    "integrity": {
        "manifest_sha256": manifest_sha,
        "edit_map_sha256": edit_map_sha,
        "replacement_blueprint_sha256":
            blueprint_sha,
        "edit_ready_targets": "9/9",
    },
    "safety": {
        "local_only": True,
        "vm101_contacted": False,
        "vm101_modified": False,
        "network_changed": False,
        "services_changed": False,
        "state_changed": False,
        "refresh_ran": False,
        "rebalance_ran": False,
    },
    "next_step":
        "GENERATE_COMPLETE_CANONICAL_REPLACEMENT_FILES",
}, ensure_ascii=False, indent=2))
PY

cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${PASS_DECISION}
step_execution=PASS
operation_result=CANONICAL_CURRENT_STATE_AND_VALIDATOR_REPAIRED
production_health=UNCHANGED
milestone_status=M07_IN_PROGRESS
all_ok=true

canonical:
  canonical_id=${EXPECTED_ID}
  source_canonical_id=${SOURCE_CANONICAL_ID}
  private_workspace=${EXPECTED_WORKSPACE}
  immutable_snapshot=${EXPECTED_SNAPSHOT}
  immutable_snapshot_url=${EXPECTED_SNAPSHOT_URL}
  public_current=${VM101_CANONICAL}
  current_id_before=${CURRENT_ID_BEFORE}
  current_id_after=${CURRENT_ID_AFTER}

actions:
  snapshot_created=${SNAPSHOT_CREATED}
  current_republished=${CURRENT_REPUBLISHED}
  state_repaired=${STATE_REPAIRED}
  validator_installed=${VALIDATOR_INSTALLED}
  validation_passed=${VALIDATION_PASSED}

integrity:
  edit_ready_targets=9/9
  current_manifest_sha256=${CURRENT_MANIFEST_SHA}
  edit_map_sha256=${EDIT_MAP_SHA}
  replacement_blueprint_sha256=${BLUEPRINT_SHA}

safety:
  local_only=true
  vm101_contacted=false
  vm101_modified=false
  network_changed=false
  services_changed=false
  state_changed=false
  refresh_ran=false
  rebalance_ran=false

plan:
  current_milestone=M07
  milestone_completed=false

next_step:
  GENERATE_COMPLETE_CANONICAL_REPLACEMENT_FILES

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}
VM101_REFERENCE=${VM101_REFERENCE}
VM101_CANONICAL=${VM101_CANONICAL}
EOF

python3 - \
  "$REPORT_DIR/assessment.json" \
  "$STEP" \
  "$BUILD_TS" \
  "$TRYCF_REPORT" \
  "$REPORT_TXT" \
  "$FACTS_JSON" \
  "$ARCHITECTURE_PLAN" \
  "$XS_MAP" \
  "$GLOBAL_PROJECT_PLAN" \
  "$VM101_REFERENCE" \
  "$VM101_CANONICAL" \
  > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    assessment_path,
    step,
    generated_at,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
    vm101_reference,
    vm101_canonical,
) = sys.argv[1:]

assessment = json.load(
    open(assessment_path, encoding="utf-8")
)

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": generated_at,
    "assessment": assessment,
    "safety": assessment["safety"],
    "plan": {
        "current_milestone": "M07",
        "milestone_completed": False,
    },
    "next_step":
        "GENERATE_COMPLETE_CANONICAL_REPLACEMENT_FILES",
    "publish": {
        "trycf_report": report,
        "report_txt": report_txt,
        "facts_json": facts_json,
        "architecture_plan": architecture,
        "xs_map": xs_map,
        "global_project_plan": global_plan,
        "vm101_reference": vm101_reference,
        "vm101_canonical": vm101_canonical,
    },
}, ensure_ascii=False, indent=2))
PY

create_index

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

trap - ERR

echo "decision=$PASS_DECISION"
echo "step_execution=PASS"
echo "operation_result=CANONICAL_CURRENT_STATE_AND_VALIDATOR_REPAIRED"
echo "production_health=UNCHANGED"
echo "canonical_id=$EXPECTED_ID"
echo "current_id_before=$CURRENT_ID_BEFORE"
echo "current_id_after=$CURRENT_ID_AFTER"
echo "snapshot_created=$SNAPSHOT_CREATED"
echo "current_republished=$CURRENT_REPUBLISHED"
echo "state_repaired=$STATE_REPAIRED"
echo "validator_installed=$VALIDATOR_INSTALLED"
echo "validation_passed=$VALIDATION_PASSED"
echo "edit_ready_targets=9/9"
echo "vm101_contacted=false"
echo "vm101_modified=false"

print_links
