356 lines
14 KiB
Bash
Executable File
356 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# CHECK — verifier for the live wizard roster (2026-10-29) extract
|
|
# (Extract lane: data extraction and tables.)
|
|
#
|
|
# Exits 0 iff the committed TSV (`live-wizard-roster-2026-10-29.tsv`) is a
|
|
# faithful replay of the three evidence sources it names: the WIZARDS-TABLE.md
|
|
# roster rows, the latest `checkins/` filename per wizard, and the newest
|
|
# commit per author in git log. No `|| true` / `; exit 0` fallback: the last
|
|
# command's exit code is the verdict. The artifact is re-derived with string
|
|
# and file operations and does not import extract_live_wizard_roster.py.
|
|
#
|
|
# THREE REPAIRS (Q146), each measured before and after, in this one file:
|
|
#
|
|
# 1. THE DETAIL FORMATTER CRASHED ON THE FAILURE IT EXISTS TO REPORT.
|
|
# `"first=%r" % (mism[0] if mism else None)` fed a 3-tuple to a one-slot
|
|
# format, so the first mismatching row raised `TypeError: not all
|
|
# arguments converted during string formatting`. The old file printed
|
|
# four PASS lines and then a traceback: it could never print FAIL, could
|
|
# never print a summary, and its rc 1 was the crash's, not a verdict.
|
|
# A mismatch is now named cell by cell -- row, column, both values.
|
|
#
|
|
# 2. THE SUBJECT WAS THE CLOCK, NOT THE ARTIFACT. Column 5 was re-derived
|
|
# from `git log` of whatever worktree was on disk, against a snapshot
|
|
# dated 2026-10-29, so the check was green only until the next commit by
|
|
# any wizard and red on every clone after that. The TSV is a dated
|
|
# snapshot and is now replayed at the rev that added it (REV_PIN; that
|
|
# rev is unique -- the file has exactly one touch in its history). The
|
|
# live drift is printed as INFO and never decides the verdict: an
|
|
# artifact graded against HEAD is red by construction, which is Q122's
|
|
# fault class, not a verification.
|
|
#
|
|
# 3. THE TREE WAS PINNED BY HOST PATH. `repo = Path("/root/troupe-standup")`
|
|
# graded whatever tree sat at that absolute path, on any host. The repo
|
|
# root is now derived from this script's own location, and a repo without
|
|
# the pinned rev reads rc 2 (cannot grade) rather than a false fault.
|
|
#
|
|
# The evidence is named, not assumed: the TSV's git blob SHA at REV_PIN is
|
|
# pinned, so a re-written artifact is a different artifact even at equal bytes
|
|
# in the worktree.
|
|
#
|
|
# Usage:
|
|
# bash CHECK-live-wizard-roster-2026-10-29 # grade the committed TSV
|
|
# bash CHECK-live-wizard-roster-2026-10-29 --selftest # grade the grader
|
|
#
|
|
# Exit codes: 0 all checks passed, 1 a check failed, 2 the tree could not be read.
|
|
|
|
set -u
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
ART="$HERE/live-wizard-roster-2026-10-29.tsv"
|
|
REV_PIN="e6ce7836e98592d8b5662b72de36896f2c5a0702"
|
|
BLOB_PIN="bb182b3a22e4c97d9eb30024ec41b91638e2d58c"
|
|
|
|
if [ ! -f "$ART" ]; then
|
|
printf 'CANNOT GRADE: missing %s\n' "$ART"
|
|
exit 2
|
|
fi
|
|
|
|
exec "${PYTHON:-python3}" - "$ROOT" "$ART" "$REV_PIN" "$BLOB_PIN" "$@" <<'PY'
|
|
import csv
|
|
import hashlib
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
REPO = Path(sys.argv[1])
|
|
ART = Path(sys.argv[2])
|
|
REV = sys.argv[3]
|
|
BLOB_PIN = sys.argv[4]
|
|
ARGS = sys.argv[5:]
|
|
|
|
CANONICAL = [
|
|
"Timmy", "Zephyrus Sparkborn", "Cassian Assayborn", "Merlin", "Mercury",
|
|
"Gemini", "Argus", "Volt", "Sentinel", "Beam",
|
|
]
|
|
HEADER = ["Wizard", "Model", "Lane", "Latest check-in", "Newest commit"]
|
|
COVERED = {"Zephyrus Sparkborn", "Cassian Assayborn", "Merlin", "Mercury",
|
|
"Gemini", "Volt", "Sentinel"}
|
|
NO_CHECKIN = "\u2014 (no check-in)"
|
|
NO_COMMIT = "\u2014 (no commit)"
|
|
|
|
|
|
class CannotGrade(Exception):
|
|
"""The tree does not hold what the replay needs: rc 2, not a fault."""
|
|
|
|
|
|
def git(*args, cwd=None):
|
|
try:
|
|
r = subprocess.run(["git", *args], cwd=str(cwd or REPO),
|
|
capture_output=True, text=True, timeout=120)
|
|
except (OSError, subprocess.SubprocessError) as exc:
|
|
raise CannotGrade("git %s: %s" % (" ".join(args), exc))
|
|
if r.returncode != 0:
|
|
first = (r.stderr.strip().splitlines() or ["(no stderr)"])[0]
|
|
raise CannotGrade("git %s exited %d: %s"
|
|
% (" ".join(args), r.returncode, first))
|
|
return r.stdout
|
|
|
|
|
|
def derive(repo, rev):
|
|
"""-> (roster{name: (model, lane)}, checkins{name: date}, best{name: (date, sha)}).
|
|
|
|
Every source is read AT `rev`, never from the worktree: the artifact is a
|
|
dated snapshot, and only a rev-pinned replay can be re-derived forever.
|
|
"""
|
|
table = git("show", rev + ":WIZARDS-TABLE.md", cwd=repo).splitlines()
|
|
roster = {}
|
|
for line in table:
|
|
if not line.startswith("|"):
|
|
continue
|
|
cells = [c.strip() for c in line.split("|") if c.strip()]
|
|
if len(cells) == 5 and cells[0] != "Wizard":
|
|
roster[cells[0]] = (cells[1], cells[2]) # model, lane
|
|
|
|
slug2w = {"zephyrus": "Zephyrus Sparkborn", "merlin": "Merlin",
|
|
"mercury": "Mercury", "gemini": "Gemini", "volt": "Volt",
|
|
"sentinel": "Sentinel"}
|
|
checkins = {}
|
|
for path in git("ls-tree", "--name-only", rev, "checkins/",
|
|
cwd=repo).splitlines():
|
|
name = path.rsplit("/", 1)[-1]
|
|
if not name.endswith(".md"):
|
|
continue
|
|
parts = name[:-3].split("-", 3)
|
|
if len(parts) < 4:
|
|
continue
|
|
date, slug = "-".join(parts[:3]), parts[3]
|
|
if slug.startswith("cassian"):
|
|
w = "Cassian Assayborn"
|
|
elif slug in slug2w:
|
|
w = slug2w[slug]
|
|
else:
|
|
continue
|
|
if w not in checkins or date > checkins[w]:
|
|
checkins[w] = date
|
|
|
|
alias = {w.lower(): w for w in roster}
|
|
alias["zephyrus-sparkborn"] = "Zephyrus Sparkborn"
|
|
best = {}
|
|
for line in git("log", rev, "--format=%an%x00%H%x00%ad", "--date=short",
|
|
cwd=repo).splitlines():
|
|
if "\x00" not in line:
|
|
continue
|
|
author, sha, date = line.split("\x00", 2)
|
|
w = alias.get(author.lower())
|
|
if w is None or w not in roster:
|
|
continue
|
|
if w not in best or date > best[w][0]:
|
|
best[w] = (date, sha)
|
|
return roster, checkins, best
|
|
|
|
|
|
def expected_rows(roster, checkins, best, rev):
|
|
rows = []
|
|
for w in CANONICAL:
|
|
if w not in roster:
|
|
raise CannotGrade("roster at %s has no row for %s" % (rev[:12], w))
|
|
model, lane = roster[w]
|
|
rows.append([w, model, lane,
|
|
checkins.get(w, NO_CHECKIN),
|
|
(best[w][1] + " " + best[w][0]) if w in best else NO_COMMIT])
|
|
return rows
|
|
|
|
|
|
def read_rows(path):
|
|
try:
|
|
with open(path, encoding="utf-8", newline="") as fh:
|
|
rows = list(csv.reader(fh, delimiter="\t"))
|
|
except OSError as exc:
|
|
raise CannotGrade("cannot read %s: %s" % (path, exc))
|
|
if not rows:
|
|
raise CannotGrade("%s is empty" % path)
|
|
return rows[0], rows[1:]
|
|
|
|
|
|
def blob_sha(repo, rev, relpath):
|
|
try:
|
|
return git("rev-parse", "%s:%s" % (rev, relpath), cwd=repo).strip() or None
|
|
except CannotGrade:
|
|
return None
|
|
|
|
|
|
def grade(repo, art, rev, blob_pin, live=False):
|
|
"""-> (fails, lines). Raises CannotGrade when the tree is short."""
|
|
fails, out = [], []
|
|
|
|
def check(name, cond, detail=""):
|
|
out.append(("PASS " if cond else "FAIL ") + name
|
|
+ ((" " + detail) if detail else ""))
|
|
if not cond:
|
|
fails.append(name)
|
|
|
|
roster, checkins, best = derive(repo, rev)
|
|
check("roster: all 10 canonical wizards present at the pinned rev",
|
|
set(roster) >= set(CANONICAL),
|
|
"rows=%d extra=%s" % (len(roster), sorted(set(roster) - set(CANONICAL))))
|
|
check("checkins: covered wizards at the pinned rev",
|
|
set(checkins) == COVERED, repr(sorted(checkins)))
|
|
|
|
header, committed = read_rows(art)
|
|
check("tsv header", header == HEADER, repr(header))
|
|
check("row count", len(committed) == len(CANONICAL),
|
|
"committed=%d canonical=%d" % (len(committed), len(CANONICAL)))
|
|
|
|
try:
|
|
relpath = str(Path(art).resolve().relative_to(Path(repo).resolve()))
|
|
except ValueError:
|
|
relpath = None
|
|
if relpath is None:
|
|
out.append("SKIP artifact blob %s is outside the graded tree; "
|
|
"no blob to name" % art)
|
|
else:
|
|
got = blob_sha(repo, rev, relpath)
|
|
check("artifact blob is the pinned evidence", got == blob_pin,
|
|
"blob=%s pinned=%s" % (got, blob_pin))
|
|
|
|
expected = expected_rows(roster, checkins, best, rev)
|
|
mism = [(k, c, g) for k, (c, g) in enumerate(zip(committed, expected))
|
|
if c != g]
|
|
detail = ""
|
|
if mism:
|
|
k, c, g = mism[0]
|
|
cells = ["col%d %s: committed=%r re-derived=%r"
|
|
% (i, HEADER[i] if i < len(HEADER) else "?", a, b)
|
|
for i, (a, b) in enumerate(zip(c, g)) if a != b]
|
|
detail = "rows=%d first-row=%d %s" % (len(mism), k, " | ".join(cells))
|
|
check("rows identical to independent re-derive at the pinned rev",
|
|
not mism and len(committed) == len(expected), detail)
|
|
|
|
if live:
|
|
try:
|
|
lr, lc, lb = derive(repo, "HEAD")
|
|
moved = sum(1 for c, g in zip(committed,
|
|
expected_rows(lr, lc, lb, "HEAD"))
|
|
if c[4] != g[4])
|
|
out.append("INFO live-drift %d of %d row(s) name a newer commit at "
|
|
"HEAD than the snapshot records (informational, does not "
|
|
"decide the verdict)" % (moved, len(CANONICAL)))
|
|
except CannotGrade as exc:
|
|
out.append("INFO live-drift unavailable: %s" % exc)
|
|
return fails, out
|
|
|
|
|
|
def run_grade(repo, art, rev, blob_pin, live):
|
|
try:
|
|
fails, out = grade(repo, art, rev, blob_pin, live=live)
|
|
except CannotGrade as exc:
|
|
print("CANNOT GRADE: %s" % exc)
|
|
return 2
|
|
for line in out:
|
|
print(line)
|
|
if fails:
|
|
print("\n%d CHECK(S) FAILED" % len(fails))
|
|
return 1
|
|
print("\nALL CHECKS PASSED -- 10 wizard rows replayed from WIZARDS-TABLE.md "
|
|
"+ checkins/ + git log at %s" % rev[:12])
|
|
return 0
|
|
|
|
|
|
def grade_text(repo, art, rev, blob_pin):
|
|
"""-> (rc, text) without printing, for the selftest."""
|
|
try:
|
|
fails, out = grade(repo, art, rev, blob_pin)
|
|
except CannotGrade as exc:
|
|
return 2, "CANNOT GRADE: %s" % exc
|
|
text = "\n".join(out)
|
|
if fails:
|
|
return 1, text + "\n%d CHECK(S) FAILED" % len(fails)
|
|
return 0, text + "\nALL CHECKS PASSED"
|
|
|
|
|
|
def write_rows(path, rows):
|
|
with open(path, "w", encoding="utf-8", newline="") as fh:
|
|
csv.writer(fh, delimiter="\t", lineterminator="\n").writerows(rows)
|
|
return Path(path)
|
|
|
|
|
|
def selftest():
|
|
"""Nine cells: one green control, seven mutants, and the formatter."""
|
|
cases = []
|
|
|
|
def cell(name, art, rev, want_rc, want=None, blob_pin=None):
|
|
rc, text = grade_text(REPO, art, rev, blob_pin)
|
|
ok = rc == want_rc
|
|
why = "rc=%d want=%d" % (rc, want_rc)
|
|
if ok and want is not None and want not in text:
|
|
ok, why = False, "rc matched, output lacks %r" % want
|
|
if ok and "Traceback" in text:
|
|
ok, why = False, "output carries a traceback"
|
|
cases.append((name, ok, why if not ok else "rc=%d" % rc))
|
|
|
|
tmp = Path(tempfile.mkdtemp(prefix="roster-selftest-"))
|
|
try:
|
|
with open(ART, encoding="utf-8", newline="") as fh:
|
|
base_rows = list(csv.reader(fh, delimiter="\t"))
|
|
control = write_rows(tmp / "control.tsv", base_rows)
|
|
cell("green-control", control, REV, 0, "ALL CHECKS PASSED")
|
|
|
|
def mutant(name, row, col, value):
|
|
rows = [list(r) for r in base_rows]
|
|
rows[row + 1][col] = value
|
|
return write_rows(tmp / (name + ".tsv"), rows)
|
|
|
|
cell("mutant-model", mutant("model", 0, 1, "GARBAGE MODEL"), REV, 1,
|
|
"col1 Model")
|
|
cell("mutant-lane", mutant("lane", 7, 2, "GARBAGE LANE"), REV, 1,
|
|
"col2 Lane")
|
|
cell("mutant-commit", mutant("commit", 3, 4, "deadbeef 2020-01-01"),
|
|
REV, 1, "col4 Newest commit")
|
|
cell("mutant-dropped-row",
|
|
write_rows(tmp / "dropped.tsv", base_rows[:-1]), REV, 1,
|
|
"FAIL row count")
|
|
cell("live-head-is-not-the-snapshot", control, "HEAD", 1,
|
|
"FAIL rows identical")
|
|
cell("absent-rev-is-cannot-grade", control, "0" * 40, 2, "CANNOT GRADE")
|
|
|
|
# The in-repo artifact under a moved pin: the blob leg must catch it.
|
|
cell("real-tree-blob-pin", ART, REV, 1, "artifact blob is the pinned evidence",
|
|
blob_pin="0" * 40)
|
|
# ... and the same artifact under the real pin is the green-control
|
|
# above only because it is a copy without a blob; this one carries it.
|
|
cell("real-tree-blob-passes", ART, REV, 0, "PASS artifact blob",
|
|
blob_pin=BLOB_PIN)
|
|
|
|
# The formatter: the old line raised on the first mismatch and printed
|
|
# nothing. Both values must appear, in a FAIL line, with no traceback.
|
|
rows = [list(r) for r in base_rows]
|
|
rows[1][1] = "X"
|
|
p = write_rows(tmp / "fmt.tsv", rows)
|
|
rc, text = grade_text(REPO, p, REV, None)
|
|
ok = (rc == 1 and "col1 Model" in text and "committed='X'" in text
|
|
and "re-derived='Hermes / DeepSeek'" in text
|
|
and "Traceback" not in text)
|
|
cases.append(("formatter-names-the-cell", ok, "rc=%d" % rc))
|
|
finally:
|
|
shutil.rmtree(tmp, ignore_errors=True)
|
|
|
|
bad = [c for c in cases if not c[1]]
|
|
for name, ok, detail in cases:
|
|
print(("PASS " if ok else "FAIL ") + name + " " + detail)
|
|
print("\nselftest: %d cell(s), %d passed, %d failed"
|
|
% (len(cases), len(cases) - len(bad), len(bad)))
|
|
return 0 if not bad else 1
|
|
|
|
|
|
if "--selftest" in ARGS:
|
|
sys.exit(selftest())
|
|
if ARGS:
|
|
print("usage: %s [--selftest]" % sys.argv[0])
|
|
sys.exit(2)
|
|
sys.exit(run_grade(REPO, ART, REV, BLOB_PIN, live=True))
|
|
PY
|