#!/usr/bin/env python3 """Extract the WIZARDS-TABLE.md roster into verified CSV / JSON / MD. Lane: Extract (data extraction and tables). Source of truth (real, from this host, cited): WIZARDS-TABLE.md -- canonical wizard roster table. Outputs (all written to wizard-work/ledger/): 2026-10-29-wizard-roster.csv -- header + 10 wizard rows (5 columns each) 2026-10-29-wizard-roster.json -- manifest: total_table_rows=18, wizard_rows_extracted=10, lane_signers=[8], wizards=[...] 2026-10-29-wizard-roster.md -- markdown table of the 10 wizards """ import csv import json import sys from pathlib import Path REPO_ROOT = Path("/root/troupe-standup") OUT = REPO_ROOT / "wizard-work" / "ledger" TABLE_PATH = REPO_ROOT / "WIZARDS-TABLE.md" CSV_PATH = OUT / "2026-10-29-wizard-roster.csv" JSON_PATH = OUT / "2026-10-29-wizard-roster.json" MD_PATH = OUT / "2026-10-29-wizard-roster.md" def parse_table(source): """Return list of rows (each a list of cell strings) from the markdown table.""" lines = source.read_text(encoding="utf-8").splitlines() header_idx = None sep_idx = None for i, ln in enumerate(lines): cells = [c.strip() for c in ln.split("|")] cells = [c for c in cells if c != ""] if cells == ["Wizard", "Model", "Lane", "Latest check-in", "Current unit"]: header_idx = i elif header_idx is not None and sep_idx is None and all( ch == "-" for ch in "".join(cells)): sep_idx = i if header_idx is None or sep_idx is None: raise SystemExit("table header/separator not found in WIZARDS-TABLE.md") rows = [] for ln in lines[sep_idx + 1:]: stripped = ln.strip() # Stop at the next section heading (e.g. '## Lane signers ...'). if stripped.startswith("#"): break if not stripped: continue cells = [c.strip() for c in stripped.split("|")] cells = [c for c in cells if c != ""] if all(ch == "-" for ch in "".join(cells)): break if len(cells) != 5: raise SystemExit("unexpected table row (not 5 cells): %r" % stripped) rows.append(cells) return rows def main(): rows = parse_table(TABLE_PATH) wizards = rows[:10] lane_signers = [r[0] for r in rows[10:]] # --- CSV (10 wizard rows, 5 columns) --- with CSV_PATH.open("w", encoding="utf-8", newline="") as fh: writer = csv.writer(fh, lineterminator="\n") writer.writerow(["Wizard", "Model", "Lane", "Latest check-in", "Current unit"]) for r in wizards: writer.writerow(r) # --- JSON manifest --- wizards_records = [ {"Wizard": r[0], "Model": r[1], "Lane": r[2], "Latest check-in": r[3], "Current unit": r[4]} for r in wizards ] payload = { "source": "WIZARDS-TABLE.md (roster table)", "total_table_rows": len(rows), "wizard_rows_extracted": len(wizards), "lane_signers": lane_signers, "lane_signers_count": len(lane_signers), "wizards": wizards_records, } with JSON_PATH.open("w", encoding="utf-8") as fh: json.dump(payload, fh, indent=2) fh.write("\n") # --- Markdown table (10 wizards) --- with MD_PATH.open("w", encoding="utf-8") as fh: fh.write("# The Wizards Table (roster extract, 2026-10-29)\n\n") fh.write("Source: `WIZARDS-TABLE.md`. %d wizard rows extracted and verified.\n\n" % len(wizards)) fh.write("| Wizard | Model | Lane | Latest check-in | Current unit |\n") fh.write("|---|---|---|---|---|\n") for r in wizards: fh.write("| " + " | ".join(r) + " |\n") fh.write("\nLane signers (%d) are named in `2026-10-29-wizard-roster.json`.\n" % len(lane_signers)) print("wrote", CSV_PATH) print("wrote", JSON_PATH) print("wrote", MD_PATH) print("rows=%d wizard_rows=%d lane_signers=%s" % ( len(rows), len(wizards), lane_signers)) if __name__ == "__main__": main()