#!/bin/sh
# check-escalation — every skill states when it stops and hands back.
#
# #5 decided this belongs in the body, not the frontmatter: no harness reads
# frontmatter beyond name/description, so a `failure_criteria:` key would be a
# guarantee nothing honours. The model reads the body.
#
# #37 then had to define which skills need one, and every obvious answer was
# bad: a hardcoded list rots, sniffing the text for "does it write anything" is
# unfalsifiable, and a frontmatter flag is the rejected option wearing a hat.
#
# So there is no acting set. EVERY skill answers the question. A read-only skill
# answers it in one line — "read-only; findings are the output, nothing is
# handed back" — which turns an absence into a claim someone can disagree with.
#
# bin/escalation-pending.txt is a ratchet for skills whose real rule has not
# been written yet (a stop condition nobody thought about is not worth
# enforcing). It is self-cleaning: a listed skill that HAS a section fails, so
# the list cannot outlive its reason.
#
# Usage: check-escalation [checkout-root]

set -u

ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
PENDING="$ROOT/bin/escalation-pending.txt"
fail=0

listed() {
  [ -f "$PENDING" ] || return 1
  sed 's/#.*//' "$PENDING" | grep -qx "[[:space:]]*$1[[:space:]]*"
}

for dir in "$ROOT"/skills/*/; do
  [ -f "$dir/SKILL.md" ] || continue
  name=$(basename "$dir")

  if grep -q '^## Escalation' "$dir/SKILL.md"; then
    if listed "$name"; then
      echo "ERROR: skills/$name has an Escalation section but is still in bin/escalation-pending.txt — remove it from the list" >&2
      fail=1
    fi
    continue
  fi

  listed "$name" && continue
  echo "ERROR: skills/$name has no '## Escalation' section — say when it stops and hands back, or say it never does" >&2
  fail=1
done

# A pending entry for a skill that no longer exists is a stale exemption, and a
# stale exemption is how a gate quietly stops covering something. Looped without
# a pipe on purpose: a `while read` on the right of a pipe runs in a subshell,
# so `fail=1` would be set in a process that exits before it is read.
if [ -f "$PENDING" ]; then
  entries=$(sed 's/#.*//' "$PENDING" | tr -d '[:blank:]' | grep -v '^$' || true)
  for entry in $entries; do
    [ -f "$ROOT/skills/$entry/SKILL.md" ] && continue
    echo "ERROR: bin/escalation-pending.txt lists '$entry', which has no skills/$entry/SKILL.md" >&2
    fail=1
  done
fi

# --- the ratchet must actually ratchet ---------------------------------------
# "This list only shrinks" was asserted in five places and enforced in none. A
# traceability probe falsified it in one run: add an unsectioned skill, append it
# to the list, gate returns 0. So compare against the list as committed at HEAD —
# a convention nothing checks is a convention, and this repo has spent a day on
# what those cost.
if [ -f "$PENDING" ]; then
  if ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
    echo "WARN: $ROOT is not a git checkout — cannot verify the pending list only shrank" >&2
  elif ! git -C "$ROOT" cat-file -e HEAD:bin/escalation-pending.txt 2>/dev/null; then
    : # first commit introducing the file — nothing to compare against yet
  else
    before=$(git -C "$ROOT" show HEAD:bin/escalation-pending.txt 2>/dev/null \
      | sed 's/#.*//' | tr -d '[:blank:]' | grep -v '^$' | sort -u || true)
    now=$(sed 's/#.*//' "$PENDING" | tr -d '[:blank:]' | grep -v '^$' | sort -u || true)
    added=$(printf '%s\n' "$now" | grep -vxF "$before" 2>/dev/null || true)
    for entry in $added; do
      [ -n "$entry" ] || continue
      echo "ERROR: '$entry' was added to bin/escalation-pending.txt — the list only shrinks. Write its '## Escalation' section instead of exempting it." >&2
      fail=1
    done
  fi
fi

exit $fail
