#!/usr/bin/env bash
# v0.68.0 GOLDEN r3 ATTEMPT 2 acceptance reader - deployah 2026-09-09.
#
# SUPERSEDES accept-r3.sh, which is preserved AS RUN with its defect and must NOT be repaired
# in place. THAT defect: it shelled to the `jq` BINARY, which is not installed on this box.
# jq's absence produced EMPTY output, the checks compared empty against expected, and every
# jq-backed criterion printed FAIL. It claimed "job count < 9" and "no Windows Docs drift STEP
# found" purely from an absent extractor; both claims were FALSE. A VOID reading printed as a
# RED, and a false red costs a gater a hunt.
#
# THREE THINGS THIS SCRIPT DOES DIFFERENTLY:
#   1. NO external jq. Uses gh's BUILT-IN --jq (embedded, always present with gh).
#   2. ASSERTS its dependencies up front and exits 3 (VOID) if one is missing.
#   3. VOID and FAIL are DIFFERENT WORDS ON SCREEN and different exit codes. An unreadable
#      criterion is never allowed to render as a failed one.
#
# Exit: 0 = all criteria MET · 1 = a criterion genuinely NOT MET · 3 = VOID (could not read).
set -uo pipefail

# Defaults are the REAL target. Overridable ONLY so the PERMIT arm can be exercised against a
# completed CONTROL run before the real one is terminal - a script proven only in the refuse
# direction is indistinguishable from one that refuses unconditionally.
RUN=${ACCEPT_RUN:-34310511612}
REPO=${ACCEPT_REPO:-BigscreenVR/spt-bs-core}
SHA=${ACCEPT_SHA:-f6110c2a12df0dd50b87dfb60a2ec4120b5cf98d}
ATTEMPT=${ACCEPT_ATTEMPT:-2}
if [ "$RUN" != "34310511612" ]; then
  echo "*** CONTROL RUN $RUN - NOT the r3 target. Any verdict below is a SCRIPT TEST, never an"
  echo "*** acceptance reading. Do not quote it as evidence about v0.68.0."
  echo
fi

void() { echo "VOID: $*" >&2; exit 3; }

# --- dependency assert (the whole point of this rewrite) -------------------
command -v gh >/dev/null 2>&1 || void "gh not on PATH"
gh --version >/dev/null 2>&1 || void "gh present but not runnable"
# gh's built-in jq is exercised, not assumed:
probe=$(gh api user --jq '.login' 2>/dev/null) || void "gh api failed (auth?)"
[ -n "$probe" ] || void "gh --jq returned EMPTY on a known-populated field: built-in jq not working"
echo "dep-assert OK: gh runnable, built-in --jq returns a value (login=$probe)"
echo

# --- run level ------------------------------------------------------------
run=$(gh run view "$RUN" --repo "$REPO" --json status,conclusion,attempt,headSha \
       --jq '"\(.status)|\(.conclusion)|\(.attempt)|\(.headSha)"') || void "run view failed"
[ -n "$run" ] || void "run view returned EMPTY"
st=$(cut -d'|' -f1 <<<"$run"); cc=$(cut -d'|' -f2 <<<"$run")
at=$(cut -d'|' -f3 <<<"$run"); hs=$(cut -d'|' -f4 <<<"$run")
echo "run $RUN: status=$st conclusion=$cc attempt=$at headSha=$hs"
[ "$hs" = "$SHA" ] || void "headSha MOVED off the ruled sha - stop and report"
[ "$at" -ge "$ATTEMPT" ] || void "run is at attempt $at, expected >= $ATTEMPT - reading the WRONG attempt"
[ "$st" = "completed" ] || void "run not terminal yet (status=$st) - do not read a verdict"
echo

# --- jobs, pinned to THIS attempt ----------------------------------------
# The default jobs endpoint mixes attempts. Read the attempt-scoped endpoint.
jobs_json=$(gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100") \
  || void "attempt-scoped jobs endpoint failed"
[ -n "$jobs_json" ] || void "jobs endpoint returned EMPTY"
njobs=$(gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100" --jq '.jobs | length')
[ -n "$njobs" ] || void "job count unreadable"
echo "jobs at attempt $at: $njobs"
gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100" \
  --jq '.jobs[] | "  \(.name)\t\(.status)/\(.conclusion)\tstarted=\(.started_at)"'
echo
echo "NOTE: carried jobs keep their ORIGINAL started_at. Dedupe executions on (run, box, started_at)"
echo "      before quoting any rate; a carried conclusion is the SAME observation, not a repeat."
echo

notgreen=$(gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100" \
  --jq '[.jobs[] | select(.conclusion != "success")] | length')
[ -n "$notgreen" ] || void "not-green count unreadable"

# --- step-level reads. A SKIP IS NOT A PASS; read the STEP, never the job. -
# DEFECT FOUND BY THE CONTROL RUN 2026-09-09, and fixed here rather than discovered at terminal:
# the first cut matched the job by the SUBSTRING "Windows", which matches BOTH
#   "test (self-hosted, Windows, hfenduleam)"  AND  "n1-gate (self-hosted, Windows, hfenduleam)".
# The extractor returned TWO lines (measured: "success" + "skipped"), and a two-line value
# compared against "success" can NEVER be equal - so a genuinely GREEN run would have printed a
# red. Same shape as the jq defect this script replaces: an instrument manufacturing a verdict.
# TWO FIXES: match the job name EXACTLY, and treat a multi-line answer as VOID, not as a value.
# A column that must hold ONE value is only read correctly when >1 is an ERROR, never a pick-first.
WIN_JOB='test (self-hosted, Windows, hfenduleam)'

# SECOND DEFECT, found by the same control run once the job match was exact: the WINDOWS test job
# carries BOTH OS VARIANTS of every floor/drift step, with the wrong-OS one SKIPPED --
#   35 skipped  DISK docs floor (Linux)          37 skipped  DISK docs floor (Windows)
#   36 skipped  Docs drift gate (...) - linux    38 skipped  Docs drift gate (...) - windows
#   43 success  DISK end floor (Windows)         44 skipped  DISK end floor (Linux)
# So a SUBSTRING step match can hand back the LINUX step's conclusion for a WINDOWS criterion.
# That is precisely the collapse criterion C2 exists to prevent ("a green Linux docs gate is NOT
# Windows evidence"), and it would have happened INSIDE the instrument, invisibly.
# $2 is therefore a jq BOOLEAN PREDICATE over the step name, anchored, never a bare substring.
# The drift gate's real name contains an EM DASH; the predicate is written as startswith/endswith
# so no em dash has to survive a shell round trip to make the match correct.
step_conclusion() { # $1 = EXACT job name, $2 = anchored jq predicate over .name
  gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100" \
    --jq ".jobs[] | select(.name == \"$1\") | .steps[]? | select(.name | $2) | .conclusion"
}

# EMPTY means the STEP WAS NOT FOUND; MULTI-LINE means the match was AMBIGUOUS. Both are VOID
# readings, NOT failures. This is the exact confusion that made accept-r3.sh manufacture a red.
report_step() { # $1 label, $2 value
  local n; n=$(printf '%s' "$2" | grep -c . || true)
  if [ -z "$2" ]; then
    echo "  $1: VOID (step not found in the job's step list - do NOT read as a fail)"
  elif [ "$n" -gt 1 ]; then
    echo "  $1: VOID (AMBIGUOUS - $n matches, expected exactly 1; do NOT read as a fail)"
    printf '%s\n' "$2" | sed 's/^/      candidate: /'
  else
    echo "  $1: $2"
  fi
}

# A value is USABLE only if it is exactly one line. Anything else must not reach a comparison.
one_line() { [ -n "$1" ] && [ "$(printf '%s' "$1" | grep -c .)" -eq 1 ]; }

# The exact-name match is itself asserted: if the Windows test job is not in this attempt's job
# list under that exact name, every step read below is VOID and the script must say so loudly.
win_job_count=$(gh api "repos/$REPO/actions/runs/$RUN/attempts/$at/jobs?per_page=100" \
  --jq "[.jobs[] | select(.name == \"$WIN_JOB\")] | length")
[ "$win_job_count" = "1" ] || void "expected EXACTLY 1 job named '$WIN_JOB' in attempt $at, found $win_job_count"

win_drift=$(step_conclusion "$WIN_JOB" '(startswith("Docs drift gate") and endswith("windows"))')
win_docs_floor=$(step_conclusion "$WIN_JOB" '(. == "DISK docs floor (Windows)")')
win_end_floor=$(step_conclusion "$WIN_JOB" '(. == "DISK end floor (Windows)")')

echo "SEVEN CRITERIA (attempt $at):"
echo " C1 FLOOR_DOCS (Windows)"; report_step "DISK docs floor (Windows)" "$win_docs_floor"
echo " C2 Windows docs-drift STEP  (skip != pass; per-OS because it diffs the WINDOWS --help)"
report_step "Docs drift gate -- windows" "$win_drift"
echo " C3 FLOOR_END (Windows)"; report_step "DISK end floor (Windows)" "$win_end_floor"
echo " C4 Summary invariant, AMENDED BY DOYLE 2026-09-09 05:40Z:"
echo "    each LEG carries exactly ONE Phase A and ONE Phase B Summary (4 total across two legs);"
echo "    >1 of EITHER PHASE ON ONE LEG is the doubled-run signature. Count per leg per phase from"
echo "    the downloaded log, not from a run-wide grep -c."
echo " C5 two_host_web_helper_role_a green on A   -- read from twohost-a"
echo " C6 two_host_web_role_b green on B          -- read from B'S OWN SERVED COUNT, never A's poll"
echo " C7 terminal + NINE jobs + every job green"
echo "    terminal=$([ "$st" = completed ] && echo YES || echo NO)  jobs=$njobs (expect 9)  not-green=$notgreen"
echo
echo "C4/C5/C6 need the job LOGS, which this script deliberately does not fetch:"
echo "  gh run view $RUN --repo $REPO --log > r3-a2-run-$RUN.log   (then count per leg per phase)"
echo "Reading a Summary count from a run-wide grep collapses the legs and is how '==2' got"
echo "mis-specified in the first place."
echo

if ! one_line "$win_drift" || ! one_line "$win_docs_floor" || ! one_line "$win_end_floor"; then
  echo "VOID: at least one Windows step read is EMPTY or AMBIGUOUS - those criteria are"
  echo "      UNREADABLE, not failed. Fix the reader before reporting anything to the gater."
  exit 3
fi
if [ "$njobs" -ge 9 ] && [ "$notgreen" -eq 0 ] \
   && [ "$win_drift" = "success" ] && [ "$win_docs_floor" = "success" ] && [ "$win_end_floor" = "success" ]; then
  echo "MACHINE-READABLE CRITERIA MET (C1 C2 C3 C7). C4/C5/C6 still need the log read above."
  exit 0
fi
echo "NOT MET on at least one machine-readable criterion - see the lines above."
echo "If any line above says VOID, that criterion was UNREADABLE, not failed. Do not report it as red."
exit 1
