#!/usr/bin/env python3 """check_live_roster.py — independent re-derivation + artifact cross-check. Exit 0 (CHECK: PASS) only if every row of the artifact TSV matches live evidence recomputed from: (a) WIZARDS-TABLE.md roster table, (b) checkins/ filenames, (c) git log. Any mismatch -> CHECK: FAIL, exit 1. Usage: python3 check_live_roster.py /path/to/live-wizard-roster.tsv """ import csv import os import subprocess import sys REPO = "/root/troupe-standup" SRC = os.path.join(REPO, "WIZARDS-TABLE.md") CHECKINS = os.path.join(REPO, "checkins") def load_roster(): text = open(SRC, encoding="utf-8").read() rows, in_table = [], False for ln in text.splitlines(): if ln.startswith("| Wizard | Model"): in_table = True continue if in_table: if ln.startswith("**Two faults**"): break s = ln.strip() if s.startswith("|") and not s.startswith("|---"): rows.append(s) out = [] for r in rows: cells = [c.strip() for c in r.split("|") if c.strip()] if len(cells) >= 3 and cells[0]: out.append({"Wizard": cells[0], "Model": cells[1], "Lane": cells[2]}) return out def recompute_checkin(z): key = {"Timmy": "timmy", "Zephyrus Sparkborn": "zephyrus", "Cassian Assayborn": "cassian", "Merlin": "merlin", "Mercury": "mercury", "Gemini": "gemini", "Argus": "argus", "Volt": "volt", "Sentinel": "sentinel", "Beam": "beam"}[z] best = None for fn in os.listdir(CHECKINS): if not fn.endswith(".md"): continue base = fn[:-3] if len(base) > 10 and base[10:11] == "-" and base[11:].startswith(key): d = base[:10] if best is None or d > best: best = d return best if best else "— (no check-in)" def recompute_commit(z): # map roster name -> a git author name we can match (case-insensitive prefix) git = subprocess.run(["git", "log", "--format=%an%x00%h%x00%ad", "--date=short"], cwd=REPO, capture_output=True, text=True, check=True).stdout best = {} for ln in git.splitlines(): a, h, d = ln.split("\x00") if a not in best: # git log is newest-first: first occurrence = newest best[a] = (h, d) for name in z, z.lower(), z.title(): for an in list(best): if an.lower() == name.lower() or an.lower().startswith(name.lower()): h, d = best[an] return h + " " + d # fall back to exact roster-name author if z in best: h, d = best[z] return h + " " + d return "— —" def main(): art = sys.argv[1] if len(sys.argv) > 1 else \ os.path.join(REPO, "wizard-work/ledger/live-wizard-roster.tsv") if not os.path.exists(art): print("CHECK: FAIL - artifact missing: %s" % art) sys.exit(1) roster = load_roster() got = list(csv.DictReader(open(art, newline="", encoding="utf-8"), delimiter="\t")) errs = [] names = [r["Wizard"] for r in got] if len(names) != len(roster): errs.append("row count %d != roster %d" % (len(names), len(roster))) for r in roster: if r["Wizard"] not in names: errs.append("missing wizard %r" % r["Wizard"]) for r in got: z = r["Wizard"] want_ci = recompute_checkin(z) if r["Latest check-in"] != want_ci: errs.append("%s: check-in %r != live %r" % (z, r["Latest check-in"], want_ci)) want_nc = recompute_commit(z) if r["Newest commit"] != want_nc: errs.append("%s: commit %r != live %r" % (z, r["Newest commit"], want_nc)) if errs: print("CHECK: FAIL") for e in errs: print(" -", e) sys.exit(1) print("CHECK: PASS") print(" wizards: %d (roster=%d)" % (len(got), len(roster))) sys.exit(0) if __name__ == "__main__": main()