Files

200 lines
7.5 KiB
Python

#!/usr/bin/env python3
"""Extract the live, evidence-verified wizard roster as a table.
Lane: Extract (data extraction & tables).
Outputs (both mirrored):
- wizard-work/ledger/live-wizard-roster.tsv
- wizard-work/ledger/live-wizard-roster.md
- wizard-work/ledger/live-wizard-roster.json
- wizard-work/ledger/extraction-verification.md
Sources of truth, one per column:
- name / model / lane : roster table in WIZARDS-TABLE.md (10 rows)
- latest check-in (date) : max YYYY-MM-DD in checkins/<date>-<key>*.md
- newest commit (hash date) : git log --format='%an %h %ad' --date=short
(— = no matching evidence found this run; unverified, not an absence claim)
"""
import csv
import json
import os
import subprocess
REPO = "/root/troupe-standup"
SRC = REPO + "/WIZARDS-TABLE.md"
OUT_DIR = "/root/.hermes/wizard-outputs/ledger"
WORK = REPO + "/wizard-work/ledger"
os.makedirs(OUT_DIR, exist_ok=True)
os.makedirs(WORK, exist_ok=True)
# ------------------------------------------------------------ roster table
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)
assert rows, "no roster table found"
zrows = []
for r in rows:
zrows.append([c.strip() for c in r.split("|") if c.strip()])
zname = [c[0] for c in zrows if len(c) >= 1 and c[0]]
zmodel = {c[0]: c[1] for c in zrows if len(c) >= 2}
zlane = {c[0]: c[2] for c in zrows if len(c) >= 3}
# filename-friendly key per roster name (used to match checkins/ files)
KEY = {
"Timmy": "timmy", "Zephyrus Sparkborn": "zephyrus", "Cassian Assayborn": "cassian",
"Merlin": "merlin", "Mercury": "mercury", "Gemini": "gemini", "Argus": "argus",
"Volt": "volt", "Sentinel": "sentinel", "Beam": "beam",
}
def latest_checkin(key):
"""max YYYY-MM-DD over checkins/<date>-<key>*.md for this wizard."""
best = None
for fn in os.listdir(REPO + "/checkins"):
if not fn.endswith(".md"):
continue
base = fn[:-3]
date = base[:10]
if len(date) == 10 and base[10:11] == "-" and base[11:].startswith(key):
if best is None or date > best:
best = date
return best if best else "— (no check-in)"
# --------------------------------------------------------------- git history
git_out = subprocess.run(
["git", "log", "--format=%an%x00%h%x00%ad", "--date=short"],
cwd=REPO, capture_output=True, text=True, check=True).stdout
commits = {}
for line in git_out.splitlines():
parts = line.split("\x00")
if len(parts) == 3:
an, h, d = parts
if an not in commits or h > commits[an][0]:
commits[an] = (h, d)
def newest_commit(z):
# per-author newest commit on HEAD
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)
# match roster name to an author loosely (case-insensitive prefix)
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
return "— —"
# --------------------------------------------------------------- assemble
header = ["Wizard", "Model", "Lane", "Latest check-in", "Newest commit"]
wizards = []
for z in zname:
wizards.append({
"Wizard": z, "Model": zmodel.get(z, ""), "Lane": zlane.get(z, ""),
"Latest check-in": latest_checkin(KEY[z]),
"Newest commit": newest_commit(z),
})
# ------------------------------------------------------------------- write
def write_tsv():
p = os.path.join(WORK, "live-wizard-roster.tsv")
with open(p, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=header, delimiter="\t", lineterminator="\n")
w.writeheader()
for x in wizards:
w.writerow({k: x[k] for k in header})
return p
def write_md():
p = os.path.join(WORK, "live-wizard-roster.md")
with open(p, "w", encoding="utf-8") as f:
f.write("# Live wizard roster (evidence-verified)\n\n")
f.write("Source: `WIZARDS-TABLE.md` roster table; live check-in evidence "
"from `checkins/`, newest commit from `git log`. Extracted 2026-10-23.\n\n")
f.write("| " + " | ".join(header) + " |\n")
f.write("|---" + "|".join(["---"] * len(header)) + "|\n")
for x in wizards:
f.write("| " + " | ".join(str(x[k]) for k in header) + " |\n")
return p
def write_json():
p = os.path.join(WORK, "live-wizard-roster.json")
with open(p, "w", encoding="utf-8") as f:
json.dump({"header": header, "rows": wizards}, f, indent=2)
return p
def write_ver():
p = os.path.join(WORK, "extraction-verification.md")
with open(p, "w", encoding="utf-8") as f:
f.write("# Extraction verification log\n\n")
f.write("Ran at 2026-10-23 from this host. Each row of `live-wizard-roster.md` "
"is grounded below.\n\n")
f.write("## Commands\n\n")
f.write("1. `git log --format='%an%x00%h%x00%ad' --date=short` (cwd "
"`/` + REPO) -> per-author newest commit.\n")
f.write("2. `ls checkins/` -> per-wizard latest check-in filename (date "
"embedded).\n")
f.write("3. Parse `WIZARDS-TABLE.md` roster table -> name / model / lane.\n\n")
f.write("## Per-row evidence\n\n")
for x in wizards:
f.write("### %s\n\n" % x["Wizard"])
f.write("- **Model / lane** — from `WIZARDS-TABLE.md` roster table: "
"`%s` / `%s`.\n" % (x["Model"], x["Lane"]))
f.write("- **Latest check-in** (checkins/): `%s`.\n" % x["Latest check-in"])
f.write("- **Newest commit** (git log): `%s`.\n" % x["Newest commit"])
f.write("- **Mapping**: roster name `%s` -> checkin key `%s`.\n" % (
x["Wizard"], KEY[x["Wizard"]]))
f.write("\n")
f.write("## Counts\n\n")
n_ci = sum(1 for x in wizards if x["Latest check-in"] != "— (no check-in)")
n_cc = sum(1 for x in wizards if newest_commit(x["Wizard"]) != "— —")
f.write("- Wizards: %d | with check-in: %d | with commit on HEAD: %d\n" % (len(wizards), n_ci, n_cc))
f.write("\n")
f.write("## Provenance map\n\n")
for x in wizards:
f.write("- `%s` | %s | %s | %s\n" % (
x["Wizard"], x["Model"], x["Latest check-in"],
newest_commit(x["Wizard"])))
f.write("\n")
return p
def mirror(p):
d = os.path.join(OUT_DIR, os.path.basename(p))
with open(p, "rb") as f:
data = f.read()
with open(d, "wb") as f:
f.write(data)
return d
paths = [write_tsv(), write_md(), write_json(), write_ver()]
for p in paths:
mirror(p)
print("wrote:", p)
print("summary:", json.dumps({"wizards": len(wizards),
"with_checkin": sum(1 for x in wizards if x["Latest check-in"] != "— (no check-in)"),
"with_commit": sum(1 for x in wizards if newest_commit(x["Wizard"]) != "— —"),
"rows": wizards}, indent=2, ensure_ascii=False))