#!/bin/sh
# Deterministic CI gates for spt-claude-code — each a binary pass/fail, no model in the loop.
# Runnable by hand on any fleet host: this IS the manual fallback. [impl->REQ-CI-GATES]
# [impl->REQ-CI-MANUAL]
# Gates whose artifact doesn't exist yet SKIP with a logged note (no silent caps — a skip is
# always announced so "green" never hides un-run coverage).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
cd "$ROOT" || exit 2
rc=0
gate() { printf '\n=== GATE: %s ===\n' "$1"; }
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

gate "shell-syntax (sh -n)"
for f in $(find plugin tests ci -name '*.sh' 2>/dev/null | sort); do
  if sh -n "$f"; then printf 'ok  %s\n' "$f"; else fail "syntax: $f"; fi
done

gate "unit tests (tests/*.sh)"
for t in tests/*.sh; do
  [ -e "$t" ] || continue
  if sh "$t" >/dev/null 2>&1; then printf 'ok  %s\n' "$t"; else fail "unit: $t"; sh "$t" || true; fi
done

gate "digest-extractor (cargo build + test)"
if [ -f "$ROOT/ci/digest/build.sh" ]; then
  if sh "$ROOT/ci/digest/build.sh"; then :; else fail "digest-extractor"; fi
else
  echo "SKIP: no digest extractor build yet"
fi

gate "psyche-runner (cargo build + test)"
if [ -f "$ROOT/ci/psyche/build.sh" ]; then
  if sh "$ROOT/ci/psyche/build.sh"; then :; else fail "psyche-runner"; fi
else
  echo "SKIP: no psyche runner build yet"
fi

gate "idle-translate (cargo build + test)"
if [ -f "$ROOT/ci/idle-translate/build.sh" ]; then
  if sh "$ROOT/ci/idle-translate/build.sh"; then :; else fail "idle-translate"; fi
else
  echo "SKIP: no idle-translate build yet"
fi

# The READ-ONLY ints run in the gate. Both self-SKIP when spt or the built binary is absent, and
# both are explicitly "read-only, NO SPTC_ACCEPTANCE gate" — nothing spawned, no perch state touched.
# The mutating ints (bind, wake-survival, multi-subnet, poll, registration, live-relay, activate,
# release-acquire, psyche-download) stay opt-in behind SPTC_ACCEPTANCE=1 and are NOT run here.
#
# WHY (2026-08-04): translate-proof-int asserted a contract against the wrong input and sat RED for
# roughly three weeks after the v0.20.0 stub migration without anyone noticing — because the gate
# built the idle-translate crate and never RAN the int. A requirement whose int the gate never runs
# is not covered, however green the suite reads; an int nothing executes rots into decoration.
gate "read-only ints (translate-proof, digest-proof)"
for i in ci/idle-translate/translate-proof-int.sh ci/digest/digest-proof-int.sh; do
  if [ -f "$ROOT/$i" ]; then
    if sh "$ROOT/$i" >/dev/null 2>&1; then echo "ok  $i"; else fail "$i"; sh "$ROOT/$i" 2>&1 | grep -E '^(FAIL|SKIP)' || true; fi
  fi
done

gate "traceable-reqs check (requirement coverage)"
if command -v traceable-reqs >/dev/null 2>&1; then
  if traceable-reqs check >/dev/null 2>&1; then echo "ok  coverage green"; else fail "traceable-reqs check"; traceable-reqs check || true; fi
else
  echo "SKIP: traceable-reqs not on PATH (install per docs/TRACEABILITY.md)"
fi

gate "skeleton-validate (cplugs installability)"
if [ -x "$ROOT/ci/publish/validate-skeleton.sh" ] || [ -f "$ROOT/ci/publish/validate-skeleton.sh" ]; then
  if sh "$ROOT/ci/publish/validate-skeleton.sh" >/dev/null 2>&1; then echo "ok  skeleton installable"; else fail "skeleton-validate"; sh "$ROOT/ci/publish/validate-skeleton.sh" || true; fi
else
  echo "SKIP: no skeleton validator yet"
fi

gate "manifest-schema"
if [ -f "$ROOT/ci/manifest/check-manifest.sh" ]; then
  if sh "$ROOT/ci/manifest/check-manifest.sh"; then :; else fail "manifest-schema"; fi
else
  echo "SKIP: no adapter manifest yet — activates when the CC adapter manifest lands"
fi

gate "docs-drift"
if [ -f "$ROOT/ci/docs/check-docs.sh" ]; then
  if sh "$ROOT/ci/docs/check-docs.sh"; then :; else fail "docs-drift"; fi
else
  echo "SKIP: no docs check yet"
fi

printf '\n=== RESULT: %s ===\n' "$([ "$rc" -eq 0 ] && echo PASS || echo FAIL)"
exit "$rc"
