Files
troupe-standup/wizard-work/sol/CHECK
T

115 lines
3.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# CHECK — verifier for the inbox-triage artifact (Triage lane).
#
# Exit 0 iff the committed TSV is structurally complete and matches an
# INDEPENDENT recompute of the pending ids straight from the seed JSON, using
# only the standard library for the recompute and awk/sed for the set logic.
# This CHECK is stable across runs (seed is a build seed, static).
set -u
TSV="${1:-/root/.hermes/wizard-outputs/sol/triage-inbox-2026-09-26.tsv}"
MD="${2:-/root/.hermes/wizard-outputs/sol/triage-inbox-2026-09-26.md}"
SEED="${3:-/root/troupe-standup/inbox/seed-2026-09-26.json}"
fail() { echo "FAIL: $*" >&2; exit 1; }
test -s "$TSV" || fail "TSV missing or empty: $TSV"
test -s "$MD" || fail "MD missing or empty: $MD"
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
python3 - "$SEED" "$TSV" "$tmp" <<'PY'
import json, sys
seed_path, tsv_path, out_path = sys.argv[1:4]
allowed_disp = {"escalate", "route", "hold", "delegate"}
allowed_owner = {"alexander", "volt"}
with open(seed_path) as f:
d = json.load(f)
expected_ids = sorted(str(x["id"]) for x in d.get("pending", []))
with open(tsv_path) as f:
raw = f.read().rstrip("\n")
lines = raw.split("\n")
header, rows = lines[0], lines[1:]
exp_header = ["id","class","disposition","owner","escalated_by","summary","proposed_action","evidence"]
if header.split("\t") != exp_header:
print("FAIL: header mismatch", file=sys.stderr)
print("\t".join(header.split("\t")), file=sys.stderr)
sys.exit(1)
data = [ln.split("\t") for ln in rows]
if len(data) != len(expected_ids):
print(f"FAIL: row count {len(data)} != pending count {len(expected_ids)}", file=sys.stderr)
sys.exit(1)
ids = [r[0] for r in data]
if sorted(ids) != expected_ids:
print("FAIL: ids do not match recomputed pending set", file=sys.stderr)
print("missing:", sorted(set(expected_ids) - set(ids)), file=sys.stderr)
print("extra:", sorted(set(ids) - set(expected_ids)), file=sys.stderr)
sys.exit(1)
if len(set(ids)) != len(ids):
print("FAIL: duplicate ids", file=sys.stderr)
sys.exit(1)
for i, r in enumerate(data, start=1):
if len(r) != 8:
print(f"FAIL: id {r[0]} has {len(r)} columns (want 8)", file=sys.stderr)
sys.exit(1)
if r[2] not in allowed_disp:
print(f"FAIL: id {r[0]} bad disposition {r[2]!r}", file=sys.stderr)
sys.exit(1)
if r[3] not in allowed_owner:
print(f"FAIL: id {r[0]} bad owner {r[3]!r}", file=sys.stderr)
sys.exit(1)
if not r[7]:
print(f"FAIL: id {r[0]} empty evidence", file=sys.stderr)
sys.exit(1)
# Confirm the counted dispositions match the summary counts in the MD.
from collections import Counter
cnt = Counter(r[2] for r in data)
checks = [f"escalate={cnt.get('escalate',0)}", f"route={cnt.get('route',0)}",
f"hold={cnt.get('hold',0)}", f"delegate={cnt.get('delegate',0)}"]
print("OK: rows=%d pending=%d dispositions=%s ids_match=%s" % (
len(data), len(expected_ids),
",".join(sorted(checks)), "yes"))
sys.exit(0)
PY
rc=$?
if [ $rc -ne 0 ]; then
echo "FAIL: recompute from seed" >&2
exit 1
fi
echo "$rc" > "$tmp"
# Cross-check the counts quoted in the markdown summary ($tmp holds the earlier
# CHECK recompute result, not the md counts).
python3 - "$MD" <<'PY'
import re, sys
md_path = sys.argv[1]
with open(md_path) as f:
md = f.read()
m = re.search(r"Triage complete: \*\*(\d+)/12", md)
if not m:
print("FAIL: no 'Triage complete: **N/12**' line", file=sys.stderr)
sys.exit(1)
n = int(m.group(1))
if n != 12:
print(f"FAIL: MD reports {n}/12 routed, expected 12", file=sys.stderr)
sys.exit(1)
print("OK: md reports 12/12 pending routed")
PY
rc2=$?
if [ $rc2 -ne 0 ]; then
echo "FAIL: md cross-check" >&2
exit 1
fi
echo "PASS: triage artifact complete and verified"
exit 0