Files
troupe-standup/wizard-work/ledger/check_live_roster.py
T

119 lines
3.9 KiB
Python

#!/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()