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

ROOT="$(git rev-parse --show-toplevel)"
OUT_DIR="${1:-$ROOT/tmp/platform-foundation-guards}"
MODE="${PLATFORM_FOUNDATION_GUARD_MODE:-report_only}"
ALLOWED_DEBT_FILE="${PLATFORM_FOUNDATION_ALLOWED_DEBT_FILE:-$ROOT/doc/architecture/platform-foundation/guard-allowed-debt.tsv}"
FINGERPRINT_BASELINE_FILE="${PLATFORM_FOUNDATION_FINGERPRINT_BASELINE_FILE:-$ROOT/doc/architecture/platform-foundation/guard-fingerprint-baseline.tsv}"
mkdir -p "$OUT_DIR"

JSON_OUT="$OUT_DIR/summary.json"
MD_OUT="$OUT_DIR/summary.md"
FINDINGS_TSV="$OUT_DIR/findings.tsv"
ENRICHED_TSV="$OUT_DIR/findings.enriched.tsv"
FINGERPRINTS_TSV="$OUT_DIR/findings.fingerprints.tsv"
: >"$FINDINGS_TSV"
: >"$ENRICHED_TSV"
: >"$FINGERPRINTS_TSV"

add_finding() {
  local guard_id="$1"
  local severity="$2"
  local owner="$3"
  local file="$4"
  local line="$5"
  local rule="$6"
  local reason="$7"
  printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$guard_id" "$severity" "$owner" "$file" "$line" "$rule" "$reason" >>"$FINDINGS_TSV"
}

json_escape() {
  sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g'
}

count_guard() {
  local guard_id="$1"
  awk -F '\t' -v guard="$guard_id" '$1 == guard { count++ } END { print count + 0 }' "$FINDINGS_TSV"
}

count_unapproved_high_critical() {
  awk -F '\t' '($2 == "high" || $2 == "critical") && $8 != "approved" { count++ } END { print count + 0 }' "$ENRICHED_TSV"
}

count_approved_debt() {
  awk -F '\t' '$8 == "approved" { count++ } END { print count + 0 }' "$ENRICHED_TSV"
}

cd "$ROOT"

SOURCE_COMMIT="$(git rev-parse HEAD)"
GENERATED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"

# Import-boundary: focus on the target package shape. Existing packages/services
# is legacy debt and is reported only when new target packages depend on it
# outside explicit migration facade files.
DOMAIN_IMPORT_RE='"[^"]*(github.com/gpuaas/platform|GPUasService)/packages/(platform|products|services)[^"]*"'
PRODUCT_OR_SERVICE_IMPORT_RE='"[^"]*(github.com/gpuaas/platform|GPUasService)/packages/(products|services)[^"]*"'
PRODUCT_IMPORT_RE='"[^"]*(github.com/gpuaas/platform|GPUasService)/packages/products/[^"]*"'
SERVICE_IMPORT_RE='"[^"]*(github.com/gpuaas/platform|GPUasService)/packages/services/[^"]*"'
if [ -d packages/shared ]; then
  while IFS=: read -r file line text; do
    case "$text" in
      *"github.com/gpuaas/platform/packages/platform"*|*"github.com/gpuaas/platform/packages/products"*|*"github.com/gpuaas/platform/packages/services"*|*"GPUasService/packages/platform"*|*"GPUasService/packages/products"*|*"GPUasService/packages/services"*)
        add_finding "import-boundary" "high" "governance" "$file" "$line" "shared-must-not-import-domain" "shared package imports platform/product/service code"
        ;;
    esac
  done < <(rg -n "$DOMAIN_IMPORT_RE" packages/shared -g '*.go' || true)
fi

if [ -d packages/platform ]; then
  while IFS=: read -r file line text; do
    case "$text" in
      *"github.com/gpuaas/platform/packages/products"*|*"GPUasService/packages/products"*)
        add_finding "import-boundary" "high" "governance" "$file" "$line" "platform-must-not-import-product" "platform package imports product package"
        ;;
      *"github.com/gpuaas/platform/packages/services"*|*"GPUasService/packages/services"*)
        case "$file" in
          *adapter_*.go) ;;
          *) add_finding "import-boundary" "medium" "governance" "$file" "$line" "platform-service-import-needs-adapter-exception" "platform package imports legacy service outside an adapter file" ;;
        esac
        ;;
    esac
  done < <(rg -n "$PRODUCT_OR_SERVICE_IMPORT_RE" packages/platform -g '*.go' || true)
fi

# Semantic-facade: platform packages may adapt legacy services in adapter files,
# but public platform contracts should not be only re-exported legacy aliases.
if [ -d packages/platform ]; then
  while IFS=: read -r file line text; do
    add_finding "semantic-facade" "high" "governance" "$file" "$line" "platform-service-alias" "platform facade exposes legacy service as public Service alias"
  done < <(rg -n 'type[[:space:]]+Service[[:space:]]*=[[:space:]]*legacy\.Service' packages/platform -g '*.go' || true)

  while IFS=: read -r file line text; do
    add_finding "semantic-facade" "medium" "governance" "$file" "$line" "platform-contract-alias" "platform facade exposes public contract as legacy type alias"
  done < <(rg -n 'type[[:space:]]+[A-Z][A-Za-z0-9_]*[[:space:]]*=[[:space:]]*legacy\.[A-Z][A-Za-z0-9_]*' packages/platform -g '*.go' || true)

  while IFS=: read -r file line text; do
    add_finding "semantic-facade" "medium" "governance" "$file" "$line" "platform-error-alias" "platform facade exposes legacy sentinel error as public platform error"
  done < <(rg -n 'Err[A-Za-z0-9_]+[[:space:]]*=[[:space:]]*legacy\.Err[A-Za-z0-9_]+' packages/platform -g '*.go' || true)
fi

if [ -d packages/products ]; then
  while IFS=: read -r file line text; do
    case "$file" in
      *adapter_*.go|*types.go) ;;
      *) add_finding "import-boundary" "medium" "governance" "$file" "$line" "product-service-import-needs-facade-exception" "product package imports legacy service outside adapter/types migration file" ;;
    esac
  done < <(rg -n "$SERVICE_IMPORT_RE" packages/products -g '*.go' || true)

  while IFS=: read -r file line text; do
    source_product="$(printf '%s\n' "$file" | awk -F/ '{print $3}')"
    imported_product="$(printf '%s\n' "$text" | sed -E 's#.*packages/products/([^/]+)/.*#\1#')"
    if [ -n "$source_product" ] && [ -n "$imported_product" ] && [ "$source_product" = "$imported_product" ]; then
      continue
    fi
    case "$file" in
      packages/products/*/integrations/*) continue ;;
    esac
    add_finding "import-boundary" "medium" "governance" "$file" "$line" "product-cross-import-needs-contract" "product package imports another product package; review for explicit integration contract"
  done < <(rg -n "$PRODUCT_IMPORT_RE" packages/products -g '*.go' || true)
fi

# Route-placement: current frozen route file is allowed debt, but new durable
# route work should move toward owner-visible route files.
if [ -f cmd/api/routes_v1_frozen.go ]; then
  add_finding "route-placement" "low" "architecture" "cmd/api/routes_v1_frozen.go" "1" "frozen-route-file-legacy-debt" "v1 frozen route file exists; new durable routes need owner-visible placement"
fi

for file in cmd/api/routes*.go; do
  [ -e "$file" ] || continue
  base="$(basename "$file")"
  case "$base" in
    routes_platform_*|routes_gpuaas_*|routes_appplatform_*|routes_access_*|routes_v3_*|routes_v1_frozen.go|routes.go|routes_test.go|*_test.go) ;;
    *)
      add_finding "route-placement" "low" "architecture" "$file" "1" "route-owner-not-visible" "route file does not expose owner in filename"
      ;;
  esac
done

# Schema-owner: existing unprefixed tables are expected legacy debt. New tables
# should be owner-visible or explicitly exempted.
if [ -f doc/architecture/db_schema_v1.sql ]; then
  while IFS=: read -r line text; do
    table_name="$(printf '%s\n' "$text" | sed -E 's/.*create table if not exists ([a-zA-Z0-9_]+).*/\1/I')"
    case "$table_name" in
      platform_*|gpuaas_*|appplatform_*|tokenfactory_*) ;;
      *)
        add_finding "schema-owner" "low" "backend" "doc/architecture/db_schema_v1.sql" "$line" "legacy-unprefixed-table" "legacy table '$table_name' relies on schema ownership map rather than owner-visible prefix"
        ;;
    esac
  done < <(rg -n '^create table if not exists ' doc/architecture/db_schema_v1.sql || true)
fi

# Schema access boundary: flag direct package-level DB references across the
# platform/product ownership line. cmd/api is intentionally excluded while it is
# still the BFF/wiring shell; route-level thinning is tracked separately.
PLATFORM_TABLE_RE='(platform_iam_[a-zA-Z0-9_]*|platform_billing_[a-zA-Z0-9_]*|platform_payment_[a-zA-Z0-9_]*|platform_audit_[a-zA-Z0-9_]*|platform_evidence_[a-zA-Z0-9_]*|platform_status_[a-zA-Z0-9_]*|platform_registry_[a-zA-Z0-9_]*|platform_artifact_[a-zA-Z0-9_]*|platform_secret_[a-zA-Z0-9_]*|platform_pki_[a-zA-Z0-9_]*|platform_policy_[a-zA-Z0-9_]*|platform_entitlement_[a-zA-Z0-9_]*|platform_api_idempotency_keys|platform_outbox_events|users|organizations|projects|tenant_memberships|project_memberships|user_project_defaults|service_accounts|service_account_credentials|access_credentials|access_credential_bindings|ledger_entries|refund_requests|stripe_events|payment_sessions|payment_disputes|billing_provider_customers|billing_accounts|billing_reconciliation_runs|billing_tax_profiles|invoice_headers|invoice_lines|financial_restrictions|budget_policies|budget_events|commit_contracts)'
PRODUCT_TABLE_RE='(gpuaas_[a-zA-Z0-9_]*|appplatform_[a-zA-Z0-9_]*|sku_catalog|node_family_versions|provider_capacity_inventory|provider_resources|os_images|maas_sites|maas_site_policies|maas_site_profiles|maas_power_credential_overrides|maas_roce_assignments|maas_discovery_candidate_reviews|nodes|node_image_cache|node_resource_slots|node_onboardings|node_onboarding_events|node_decommissions|node_decommission_events|node_maas_state|node_agent_lifecycles|node_tasks|allocations|allocation_ssh_public_keys|allocation_access_grants|allocation_resource_claims|allocation_runtime_bundles|app_catalog|app_versions|project_app_entitlements|app_instances|shared_app_runtimes|shared_app_runtime_attachments|shared_app_runtime_workers|shared_app_runtime_worker_operations|shared_runtime_operator_credentials|app_instance_proxy_routes|app_instance_members|app_instance_member_operations|app_artifacts|app_artifact_publish_intents)'
SQL_TABLE_ACCESS_PREFIX_RE='(?i)(from|join|into|update|delete[[:space:]]+from)[[:space:]]+"?'
FINANCE_WRITE_RE='(?i)(insert[[:space:]]+into|update|delete[[:space:]]+from)[^`;"]*(ledger_entries|refund_requests|stripe_events|payment_sessions|payment_disputes|billing_accounts|invoice_headers|invoice_lines|financial_restrictions)'

if [ -d packages/products ]; then
  while IFS=: read -r file line text; do
    case "$file" in
      packages/products/*/legacyimpl/*|packages/products/*/adapter_*.go)
        add_finding "schema-access" "medium" "backend" "$file" "$line" "product-direct-platform-table-access" "product package directly references platform-owned table; classify as approved migration debt or move behind platform facade"
        ;;
      *)
        add_finding "schema-access" "medium" "backend" "$file" "$line" "product-direct-platform-table-access" "product package directly references platform-owned table; use platform service/read model"
        ;;
    esac
  done < <(rg -n "$SQL_TABLE_ACCESS_PREFIX_RE$PLATFORM_TABLE_RE\\b" packages/products -g '*.go' -g '!*_test.go' || true)

  while IFS=: read -r file line text; do
    add_finding "schema-access" "high" "backend" "$file" "$line" "product-finance-table-write" "product package appears to write platform finance/ledger table; use platform billing/payments contract"
  done < <(rg -n "$FINANCE_WRITE_RE" packages/products -g '*.go' -g '!*_test.go' || true)
fi

if [ -d packages/platform ]; then
  while IFS=: read -r file line text; do
    case "$file" in
      packages/platform/*/legacyimpl/*|packages/platform/*/adapter_*.go)
        add_finding "schema-access" "medium" "backend" "$file" "$line" "platform-direct-product-table-access" "platform package directly references product-owned table; classify as approved migration debt or move behind product facade/read model"
        ;;
      *)
        add_finding "schema-access" "medium" "backend" "$file" "$line" "platform-direct-product-table-access" "platform package directly references product-owned table; use product service/read model"
        ;;
    esac
  done < <(rg -n "$SQL_TABLE_ACCESS_PREFIX_RE$PRODUCT_TABLE_RE\\b" packages/platform -g '*.go' -g '!*_test.go' || true)
fi

# Event-owner: subject literals are allowed in shared events and contracts.
event_subject_literal_regex='"(platform\.billing\.(low_balance_warning|auto_release_pending|balance_depleted|budget_threshold_crossed|usage\.metered)|platform\.payments\.(balance_credited|reconcile_failed)|platform\.artifact\.(verified|revoked)|gpuaas\.provisioning\.(requested|active|failed|releasing\.requested|releasing\.completed|release_failed|force_release_requested)|appplatform\.(catalog\.entitlement\.updated|runtime\.(instance\.(requested|running|failed|deleting|deleted|upgrade_requested|rollback_requested|decommission_requested|stop_requested|start_requested|restart_requested)|shared_runtime\.(requested|running|failed|deleting|deleted))|artifact\.(registered|promoted|deprecated|retired))|storage\.attachment\.(requested|detach_requested|mounted|failed|detached|detach_failed)|dlq\.)"'
while IFS=: read -r file line text; do
  case "$file" in
    packages/shared/events/*|doc/api/*|doc/architecture/Event_Taxonomy.md|doc/architecture/NATS_Stream_Config.md) ;;
    *)
      add_finding "event-owner" "low" "ops" "$file" "$line" "event-subject-literal" "event subject literal outside shared events/contracts; review before moving to blocking"
      ;;
  esac
done < <(rg -n "$event_subject_literal_regex" packages cmd -g '*.go' -g '!*_test.go' || true)

while IFS=: read -r file line text; do
  case "$file" in
    packages/shared/events/*|packages/shared/outbox/*|cmd/outbox-relay/*|*_test.go) ;;
    *)
      case "$text" in
        *".PublishMsg("*|*"nats."*".Publish("*|*"js.Publish("*)
          add_finding "event-owner" "medium" "ops" "$file" "$line" "raw-nats-publish-review" "NATS/JetStream publish call outside shared events/outbox relay; verify this is not an HTTP handler publish"
          ;;
      esac
      ;;
  esac
done < <(rg -n '\.(Publish|PublishMsg)\(' packages cmd -g '*.go' || true)

# Frontend-boundary: keep this conservative until the frontend migration creates
# the target shared/platform/product folder split.
if [ -d packages/web/src/shared ]; then
  while IFS=: read -r file line text; do
    add_finding "frontend-boundary" "medium" "frontend" "$file" "$line" "shared-ui-imports-domain" "shared frontend module imports platform/product module"
  done < <(rg -n 'from ["'\''].*(platform|products|gpuaas|appplatform)' packages/web/src/shared -g '*.ts' -g '*.tsx' || true)
fi

# Worker/binary: report the current topology so owners can classify any
# multi-domain binaries before extraction pressure starts.
if [ -d cmd ]; then
  while IFS= read -r dir; do
    binary_name="$(basename "$dir")"
    case "$binary_name" in
      api|outbox-relay|billing-worker|webhook-worker|notification-relay|provisioning-worker|terminal-gateway|node-agent|gpuaas-cli|app-runtime-worker|proxy-runtime-reconciler|provider-reconciler|rke2-self-managed-controller|slurm-reference-controller|node-log-gateway) ;;
      *)
        add_finding "worker-binary" "low" "ops" "$dir" "1" "unknown-binary-owner" "binary is not listed in the initial worker ownership allow-list"
        ;;
    esac
  done < <(find cmd -mindepth 1 -maxdepth 1 -type d | sort)
fi

if [ -f "$ALLOWED_DEBT_FILE" ]; then
  awk -F '\t' -v allowed_file="$ALLOWED_DEBT_FILE" '
    BEGIN {
      while ((getline line < allowed_file) > 0) {
        if (line == "" || line ~ /^#/) {
          continue
        }
        split(line, parts, "\t")
        debt_count++
        debt_guard[debt_count] = parts[1]
        debt_rule[debt_count] = parts[2]
        debt_pattern[debt_count] = parts[3]
        debt_owner[debt_count] = parts[4]
        debt_task[debt_count] = parts[6]
        debt_expiry[debt_count] = parts[7]
      }
      close(allowed_file)
    }
    {
      approved = "unapproved"
      owner = ""
      task = ""
      expiry = ""
      for (i = 1; i <= debt_count; i++) {
        if ($1 == debt_guard[i] && $6 == debt_rule[i] && $4 ~ debt_pattern[i]) {
          approved = "approved"
          owner = debt_owner[i]
          task = debt_task[i]
          expiry = debt_expiry[i]
          break
        }
      }
      printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7, approved, owner, task, expiry
    }
  ' "$FINDINGS_TSV" >"$ENRICHED_TSV"
else
  awk -F '\t' '{ printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\tunapproved\t\t\t\n", $1, $2, $3, $4, $5, $6, $7 }' "$FINDINGS_TSV" >"$ENRICHED_TSV"
fi

UNAPPROVED_HIGH_CRITICAL="$(count_unapproved_high_critical)"
APPROVED_DEBT="$(count_approved_debt)"

while IFS=$'\t' read -r guard severity owner file line rule reason approval debt_owner debt_task debt_expiry; do
  [ -n "$guard" ] || continue
  fingerprint_basis="${guard}|${rule}|${file}|${line}|${reason}"
  fingerprint="$(printf '%s' "$fingerprint_basis" | shasum -a 256 | awk '{print $1}')"
  baseline_status="unbaselined"
  if [ -f "$FINGERPRINT_BASELINE_FILE" ]; then
    if grep -q "^${fingerprint}[[:space:]]" "$FINGERPRINT_BASELINE_FILE"; then
      baseline_status="baseline"
    else
      baseline_status="new"
    fi
  fi
  printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
    "$fingerprint" "$baseline_status" "$guard" "$severity" "$owner" "$file" "$line" "$rule" "$reason" "$approval" "$debt_owner" "$debt_task" "$debt_expiry" >>"$FINGERPRINTS_TSV"
done <"$ENRICHED_TSV"

cat >"$MD_OUT" <<EOF
# Platform Foundation Boundary Guard Report

- mode: \`$MODE\`
- source commit: \`$SOURCE_COMMIT\`
- generated at: \`$GENERATED_AT\`
- allowed debt file: \`$(realpath --relative-to="$ROOT" "$ALLOWED_DEBT_FILE" 2>/dev/null || printf '%s' "$ALLOWED_DEBT_FILE")\`
- fingerprint baseline file: \`$(realpath --relative-to="$ROOT" "$FINGERPRINT_BASELINE_FILE" 2>/dev/null || printf '%s' "$FINGERPRINT_BASELINE_FILE")\`
- fingerprint artifact: \`$(realpath --relative-to="$ROOT" "$FINGERPRINTS_TSV" 2>/dev/null || printf '%s' "$FINGERPRINTS_TSV")\`
- approved allowed-debt findings: \`$APPROVED_DEBT\`
- unapproved high/critical findings: \`$UNAPPROVED_HIGH_CRITICAL\`

| Guard | Findings |
|---|---:|
| import-boundary | $(count_guard import-boundary) |
| semantic-facade | $(count_guard semantic-facade) |
| route-placement | $(count_guard route-placement) |
| schema-owner | $(count_guard schema-owner) |
| schema-access | $(count_guard schema-access) |
| event-owner | $(count_guard event-owner) |
| frontend-boundary | $(count_guard frontend-boundary) |
| worker-binary | $(count_guard worker-binary) |

Mode behavior:

- \`report_only\`: writes artifacts and exits 0.
- \`warning\`: writes artifacts, prints warning summary, and exits 0.
- \`blocking_new\`: fails on unapproved high/critical findings.
- \`blocking_all\`: currently aliases \`blocking_new\` until medium/low legacy
  debt thresholds are explicitly approved.

## Findings

EOF

if [ -s "$FINGERPRINTS_TSV" ]; then
  awk -F '\t' '{
    suffix = ""
    if ($10 == "approved") {
      suffix = sprintf(" (allowed debt: owner=%s task=%s expiry=%s)", $11, $12, $13)
    }
    printf "- [%s/%s/%s/%s] %s:%s %s - %s%s fingerprint=%s\n", $3, $4, $10, $2, $6, $7, $8, $9, suffix, substr($1, 1, 12)
  }' "$FINGERPRINTS_TSV" >>"$MD_OUT"
else
  printf 'No findings.\n' >>"$MD_OUT"
fi

{
  printf '{\n'
  printf '  "guard_id": "platform-foundation-boundary-guards",\n'
  printf '  "mode": "%s",\n' "$(printf '%s' "$MODE" | json_escape)"
  printf '  "source_commit": "%s",\n' "$SOURCE_COMMIT"
  printf '  "generated_at": "%s",\n' "$GENERATED_AT"
  printf '  "inputs": [\n'
  printf '    "doc/architecture/platform-foundation/ownership-maps",\n'
  printf '    "doc/architecture/platform-foundation/Platform_Foundation_Boundary_Guards_v1.md"\n'
  printf '  ],\n'
  printf '  "summary": {\n'
  printf '    "import-boundary": %s,\n' "$(count_guard import-boundary)"
  printf '    "semantic-facade": %s,\n' "$(count_guard semantic-facade)"
  printf '    "route-placement": %s,\n' "$(count_guard route-placement)"
  printf '    "schema-owner": %s,\n' "$(count_guard schema-owner)"
  printf '    "schema-access": %s,\n' "$(count_guard schema-access)"
  printf '    "event-owner": %s,\n' "$(count_guard event-owner)"
  printf '    "frontend-boundary": %s,\n' "$(count_guard frontend-boundary)"
  printf '    "worker-binary": %s\n' "$(count_guard worker-binary)"
  printf '  },\n'
  printf '  "approved_allowed_debt": %s,\n' "$APPROVED_DEBT"
  printf '  "unapproved_high_critical": %s,\n' "$UNAPPROVED_HIGH_CRITICAL"
  printf '  "findings": [\n'
  first=1
  while IFS=$'\t' read -r fingerprint baseline_status guard severity owner file line rule reason approval debt_owner debt_task debt_expiry; do
    [ -n "$guard" ] || continue
    if [ "$first" -eq 0 ]; then
      printf ',\n'
    fi
    first=0
    printf '    {"fingerprint":"%s","baseline_status":"%s","guard_id":"%s","severity":"%s","owner":"%s","file":"%s","line":"%s","rule":"%s","reason":"%s","approval":"%s","allowed_debt_owner":"%s","allowed_debt_task":"%s","allowed_debt_expiry":"%s"}' \
      "$(printf '%s' "$fingerprint" | json_escape)" \
      "$(printf '%s' "$baseline_status" | json_escape)" \
      "$(printf '%s' "$guard" | json_escape)" \
      "$(printf '%s' "$severity" | json_escape)" \
      "$(printf '%s' "$owner" | json_escape)" \
      "$(printf '%s' "$file" | json_escape)" \
      "$(printf '%s' "$line" | json_escape)" \
      "$(printf '%s' "$rule" | json_escape)" \
      "$(printf '%s' "$reason" | json_escape)" \
      "$(printf '%s' "$approval" | json_escape)" \
      "$(printf '%s' "$debt_owner" | json_escape)" \
      "$(printf '%s' "$debt_task" | json_escape)" \
      "$(printf '%s' "$debt_expiry" | json_escape)"
  done <"$FINGERPRINTS_TSV"
  printf '\n  ],\n'
  printf '  "allowed_debt_file": "%s",\n' "$(printf '%s' "$(realpath --relative-to="$ROOT" "$ALLOWED_DEBT_FILE" 2>/dev/null || printf '%s' "$ALLOWED_DEBT_FILE")" | json_escape)"
  printf '  "fingerprint_baseline_file": "%s",\n' "$(printf '%s' "$(realpath --relative-to="$ROOT" "$FINGERPRINT_BASELINE_FILE" 2>/dev/null || printf '%s' "$FINGERPRINT_BASELINE_FILE")" | json_escape)"
  printf '  "fingerprint_artifact": "%s",\n' "$(printf '%s' "$(realpath --relative-to="$ROOT" "$FINGERPRINTS_TSV" 2>/dev/null || printf '%s' "$FINGERPRINTS_TSV")" | json_escape)"
  printf '  "graduation_criteria": "classify current findings, refine false positives, run two stable reports, move to warning, then block only new unapproved violations"\n'
  printf '}\n'
} >"$JSON_OUT"

printf 'Wrote %s\n' "$JSON_OUT"
printf 'Wrote %s\n' "$MD_OUT"

case "$MODE" in
  report_only)
    printf 'Report-only mode: exiting 0\n'
    ;;
  warning)
    printf 'Warning mode: %s unapproved high/critical findings; exiting 0\n' "$UNAPPROVED_HIGH_CRITICAL"
    ;;
  blocking_new|blocking_all)
    if [ "$UNAPPROVED_HIGH_CRITICAL" -gt 0 ]; then
      printf 'ERROR: %s unapproved high/critical platform-foundation guard findings\n' "$UNAPPROVED_HIGH_CRITICAL" >&2
      exit 1
    fi
    printf '%s mode: no unapproved high/critical findings; exiting 0\n' "$MODE"
    ;;
  *)
    printf 'ERROR: unsupported PLATFORM_FOUNDATION_GUARD_MODE=%s\n' "$MODE" >&2
    exit 2
    ;;
esac
