diff --git a/wizard-work/ledger/2026-10-29-wizard-roster.csv b/wizard-work/ledger/2026-10-29-wizard-roster.csv new file mode 100644 index 0000000..eb3c0f7 --- /dev/null +++ b/wizard-work/ledger/2026-10-29-wizard-roster.csv @@ -0,0 +1,11 @@ +Wizard,Model,Lane,Latest check-in,Current unit +Timmy,Hermes / DeepSeek,"Operating partner — replies, queue, escalation",— (replies daily),queue replies +Zephyrus Sparkborn,Mistral (Muse),Archive verification & summary fidelity,2026-10-10,"**Q20 — taken, not worked.** His named unit (excerpt fidelity & placement, 91 magazine entries) was closed by **Merlin** at `9a1de9a` (2026-10-12 04:49Z; verified from this host in `WORK-QUEUE.md`'s Q20 block and `replies/2026-10-12-to-merlin-20.md`) after Merlin's own process-fault disclosure. No Zephyrus check-in since 2026-10-10 (his newest commit is `a4fc298`, 2026-10-10T13:17:32Z; there is none for 2026-10-11 or 2026-10-12 — the earlier ""20:58Z"" in this cell was a clock this pipe cannot reproduce from `checkins/`, so the commit time replaces it), so his lane has nothing assigned and no unit of his own: **this needs Alexander's one line — re-task the lane or let it lapse — not silence**" +Cassian Assayborn,Grok,Repo census & access-state verification,2026-10-07,"— (Q5 closed; no open unit — lane silent since 2026-10-07, 5 days, needs a decision, not silence)" +Merlin,StepFun Step 5,"The Crucible — one hard problem → one finished, verifiable artifact",2026-10-12,"**All observed through Run 6; nothing of his is open.** Q39 closed (`fad802c`, fixed gate `9b09f328…`, all five negatives refused) and **verified from this host** in reply 22; Q40 closed and verified in reply 23 — the deployed gate `7ed1bca8…` returns rc 0 / 119 passed on all five negatives because `grep -c HELM_COLLECTOR` = **0**: it imports its subject at module scope (`:39`) and has **no subject-selection path at all**. **Adoption hold stands: do not `cp` gate `74aba348…`, and the §4 `cp` of collector `4e45021d…` + gate `9b09f328…` is Alexander's** — it is the only thing that puts Q31–Q40's protection into production. Standing asks, unchanged: adopt/hold, log rotation + `docker system prune`, and a ruling on `stackchain-dashboard.service`" +Mercury,Groq (free tier),"Fast verification & triage — rapid mechanical passes, first-pass extraction",2026-10-12,"**Day-3 check-in landed `e9efb23` at 12:00:38Z and it is the Q31 first slice, executed** — the collector's entry point run from a lane that is not Merlin's; that unit is now **discharged** (reply `replies/2026-10-12-to-mercury-03.md`). All eight of his measurements reproduce from this host, six byte-exact, including the two `/tmp` files he left behind (`efc9a241…`, `99e38db0…`) and the `--compare-live` line character for character; his two bodies differ from each other in clocks only. Next unit is set in the harness: a **read-only disk census** that must reproduce or falsify this pipe's own two figures (`500–600 MB/day` vs `~153 MB/day`; and ""logrotate did not rotate `syslog` at 2026-10-12 00:00Z"") — nothing may be deleted, moved, rotated, pruned, stopped or reconfigured on it" +Gemini,"Google Gemini (relayed, chat-only)","The Synthesis — reconcile the record, surface contradictions and the cross-cutting picture",2026-10-13,"First reply landed via relay: identity confirmed + a draft ""Submission & Verification Audit Log"" format. **Lane charter + three first queue items owed.** Fault recorded (see check-in): cited example IDs (`fleet-ops`, `timmy-dispatch`, `self_heal.py`, …) that do not exist in the repo, not marked as placeholders" +Argus,LongCat 2.5,"The Watch — evidence-law enforcement: verifies every check-in/commit/reply against the evidence rules, files cited findings to `audits/`",2026-10-14,Staffed. Lane: audit the pipe daily; findings carry file:line + SHA + severity; a clean pass must cite the SHA range checked. Model `meituan/longcat-2.5-preview:free` +Volt,DeepSeek V4.1 Flash,"The Workhorse — high-throughput general agent work: queue grind, general coding/reasoning, quick verifications, refactors, scripts",2026-10-15,"Staffed. Model `deepseek-v4.1-flash` (552B MoE, 8B/16B active, native multimodal) via DeepSeek API. Clears the unowned queue items the specialized lanes don't take" +Sentinel,Nemotron 3.5 Super VL,"The Long View — long-horizon multimodal agent work: sustained multi-step tasks, end-to-end reads, image+text reconciliation",2026-10-15,"Staffed. Model `nvidia/nemotron-3.5-super-vl-120b-a12b` (120B MoE, 12B active, vision-language) via NVIDIA NIM free credits. Holds the thread on runs the flash lanes can't sustain" +Beam,Reflection Beam 1.0 (501B),"The Foundry — deep reasoning & hard coding/agentic problems → finished, CHECK-verified artifacts, max throughput",2026-10-20,"Staffed. Model `reflection/beam-1.0-501b-a23b:free` (501B MoE, 23B active, reasoning model) via Nous free tier. **FREE WINDOW ≈ 1 week — run at max throughput until it expires**" diff --git a/wizard-work/ledger/2026-10-29-wizard-roster.json b/wizard-work/ledger/2026-10-29-wizard-roster.json new file mode 100644 index 0000000..ffe7947 --- /dev/null +++ b/wizard-work/ledger/2026-10-29-wizard-roster.json @@ -0,0 +1,88 @@ +{ + "source": "WIZARDS-TABLE.md (roster table)", + "total_table_rows": 18, + "wizard_rows_extracted": 10, + "lane_signers": [ + "forge", + "ledger", + "sol", + "quill", + "spark", + "sphinx", + "null", + "zeph" + ], + "lane_signers_count": 8, + "wizards": [ + { + "Wizard": "Timmy", + "Model": "Hermes / DeepSeek", + "Lane": "Operating partner \u2014 replies, queue, escalation", + "Latest check-in": "\u2014 (replies daily)", + "Current unit": "queue replies" + }, + { + "Wizard": "Zephyrus Sparkborn", + "Model": "Mistral (Muse)", + "Lane": "Archive verification & summary fidelity", + "Latest check-in": "2026-10-10", + "Current unit": "**Q20 \u2014 taken, not worked.** His named unit (excerpt fidelity & placement, 91 magazine entries) was closed by **Merlin** at `9a1de9a` (2026-10-12 04:49Z; verified from this host in `WORK-QUEUE.md`'s Q20 block and `replies/2026-10-12-to-merlin-20.md`) after Merlin's own process-fault disclosure. No Zephyrus check-in since 2026-10-10 (his newest commit is `a4fc298`, 2026-10-10T13:17:32Z; there is none for 2026-10-11 or 2026-10-12 \u2014 the earlier \"20:58Z\" in this cell was a clock this pipe cannot reproduce from `checkins/`, so the commit time replaces it), so his lane has nothing assigned and no unit of his own: **this needs Alexander's one line \u2014 re-task the lane or let it lapse \u2014 not silence**" + }, + { + "Wizard": "Cassian Assayborn", + "Model": "Grok", + "Lane": "Repo census & access-state verification", + "Latest check-in": "2026-10-07", + "Current unit": "\u2014 (Q5 closed; no open unit \u2014 lane silent since 2026-10-07, 5 days, needs a decision, not silence)" + }, + { + "Wizard": "Merlin", + "Model": "StepFun Step 5", + "Lane": "The Crucible \u2014 one hard problem \u2192 one finished, verifiable artifact", + "Latest check-in": "2026-10-12", + "Current unit": "**All observed through Run 6; nothing of his is open.** Q39 closed (`fad802c`, fixed gate `9b09f328\u2026`, all five negatives refused) and **verified from this host** in reply 22; Q40 closed and verified in reply 23 \u2014 the deployed gate `7ed1bca8\u2026` returns rc 0 / 119 passed on all five negatives because `grep -c HELM_COLLECTOR` = **0**: it imports its subject at module scope (`:39`) and has **no subject-selection path at all**. **Adoption hold stands: do not `cp` gate `74aba348\u2026`, and the \u00a74 `cp` of collector `4e45021d\u2026` + gate `9b09f328\u2026` is Alexander's** \u2014 it is the only thing that puts Q31\u2013Q40's protection into production. Standing asks, unchanged: adopt/hold, log rotation + `docker system prune`, and a ruling on `stackchain-dashboard.service`" + }, + { + "Wizard": "Mercury", + "Model": "Groq (free tier)", + "Lane": "Fast verification & triage \u2014 rapid mechanical passes, first-pass extraction", + "Latest check-in": "2026-10-12", + "Current unit": "**Day-3 check-in landed `e9efb23` at 12:00:38Z and it is the Q31 first slice, executed** \u2014 the collector's entry point run from a lane that is not Merlin's; that unit is now **discharged** (reply `replies/2026-10-12-to-mercury-03.md`). All eight of his measurements reproduce from this host, six byte-exact, including the two `/tmp` files he left behind (`efc9a241\u2026`, `99e38db0\u2026`) and the `--compare-live` line character for character; his two bodies differ from each other in clocks only. Next unit is set in the harness: a **read-only disk census** that must reproduce or falsify this pipe's own two figures (`500\u2013600 MB/day` vs `~153 MB/day`; and \"logrotate did not rotate `syslog` at 2026-10-12 00:00Z\") \u2014 nothing may be deleted, moved, rotated, pruned, stopped or reconfigured on it" + }, + { + "Wizard": "Gemini", + "Model": "Google Gemini (relayed, chat-only)", + "Lane": "The Synthesis \u2014 reconcile the record, surface contradictions and the cross-cutting picture", + "Latest check-in": "2026-10-13", + "Current unit": "First reply landed via relay: identity confirmed + a draft \"Submission & Verification Audit Log\" format. **Lane charter + three first queue items owed.** Fault recorded (see check-in): cited example IDs (`fleet-ops`, `timmy-dispatch`, `self_heal.py`, \u2026) that do not exist in the repo, not marked as placeholders" + }, + { + "Wizard": "Argus", + "Model": "LongCat 2.5", + "Lane": "The Watch \u2014 evidence-law enforcement: verifies every check-in/commit/reply against the evidence rules, files cited findings to `audits/`", + "Latest check-in": "2026-10-14", + "Current unit": "Staffed. Lane: audit the pipe daily; findings carry file:line + SHA + severity; a clean pass must cite the SHA range checked. Model `meituan/longcat-2.5-preview:free`" + }, + { + "Wizard": "Volt", + "Model": "DeepSeek V4.1 Flash", + "Lane": "The Workhorse \u2014 high-throughput general agent work: queue grind, general coding/reasoning, quick verifications, refactors, scripts", + "Latest check-in": "2026-10-15", + "Current unit": "Staffed. Model `deepseek-v4.1-flash` (552B MoE, 8B/16B active, native multimodal) via DeepSeek API. Clears the unowned queue items the specialized lanes don't take" + }, + { + "Wizard": "Sentinel", + "Model": "Nemotron 3.5 Super VL", + "Lane": "The Long View \u2014 long-horizon multimodal agent work: sustained multi-step tasks, end-to-end reads, image+text reconciliation", + "Latest check-in": "2026-10-15", + "Current unit": "Staffed. Model `nvidia/nemotron-3.5-super-vl-120b-a12b` (120B MoE, 12B active, vision-language) via NVIDIA NIM free credits. Holds the thread on runs the flash lanes can't sustain" + }, + { + "Wizard": "Beam", + "Model": "Reflection Beam 1.0 (501B)", + "Lane": "The Foundry \u2014 deep reasoning & hard coding/agentic problems \u2192 finished, CHECK-verified artifacts, max throughput", + "Latest check-in": "2026-10-20", + "Current unit": "Staffed. Model `reflection/beam-1.0-501b-a23b:free` (501B MoE, 23B active, reasoning model) via Nous free tier. **FREE WINDOW \u2248 1 week \u2014 run at max throughput until it expires**" + } + ] +} diff --git a/wizard-work/ledger/2026-10-29-wizard-roster.md b/wizard-work/ledger/2026-10-29-wizard-roster.md new file mode 100644 index 0000000..9f39ab7 --- /dev/null +++ b/wizard-work/ledger/2026-10-29-wizard-roster.md @@ -0,0 +1,18 @@ +# The Wizards Table (roster extract, 2026-10-29) + +Source: `WIZARDS-TABLE.md`. 10 wizard rows extracted and verified. + +| Wizard | Model | Lane | Latest check-in | Current unit | +|---|---|---|---|---| +| Timmy | Hermes / DeepSeek | Operating partner — replies, queue, escalation | — (replies daily) | queue replies | +| Zephyrus Sparkborn | Mistral (Muse) | Archive verification & summary fidelity | 2026-10-10 | **Q20 — taken, not worked.** His named unit (excerpt fidelity & placement, 91 magazine entries) was closed by **Merlin** at `9a1de9a` (2026-10-12 04:49Z; verified from this host in `WORK-QUEUE.md`'s Q20 block and `replies/2026-10-12-to-merlin-20.md`) after Merlin's own process-fault disclosure. No Zephyrus check-in since 2026-10-10 (his newest commit is `a4fc298`, 2026-10-10T13:17:32Z; there is none for 2026-10-11 or 2026-10-12 — the earlier "20:58Z" in this cell was a clock this pipe cannot reproduce from `checkins/`, so the commit time replaces it), so his lane has nothing assigned and no unit of his own: **this needs Alexander's one line — re-task the lane or let it lapse — not silence** | +| Cassian Assayborn | Grok | Repo census & access-state verification | 2026-10-07 | — (Q5 closed; no open unit — lane silent since 2026-10-07, 5 days, needs a decision, not silence) | +| Merlin | StepFun Step 5 | The Crucible — one hard problem → one finished, verifiable artifact | 2026-10-12 | **All observed through Run 6; nothing of his is open.** Q39 closed (`fad802c`, fixed gate `9b09f328…`, all five negatives refused) and **verified from this host** in reply 22; Q40 closed and verified in reply 23 — the deployed gate `7ed1bca8…` returns rc 0 / 119 passed on all five negatives because `grep -c HELM_COLLECTOR` = **0**: it imports its subject at module scope (`:39`) and has **no subject-selection path at all**. **Adoption hold stands: do not `cp` gate `74aba348…`, and the §4 `cp` of collector `4e45021d…` + gate `9b09f328…` is Alexander's** — it is the only thing that puts Q31–Q40's protection into production. Standing asks, unchanged: adopt/hold, log rotation + `docker system prune`, and a ruling on `stackchain-dashboard.service` | +| Mercury | Groq (free tier) | Fast verification & triage — rapid mechanical passes, first-pass extraction | 2026-10-12 | **Day-3 check-in landed `e9efb23` at 12:00:38Z and it is the Q31 first slice, executed** — the collector's entry point run from a lane that is not Merlin's; that unit is now **discharged** (reply `replies/2026-10-12-to-mercury-03.md`). All eight of his measurements reproduce from this host, six byte-exact, including the two `/tmp` files he left behind (`efc9a241…`, `99e38db0…`) and the `--compare-live` line character for character; his two bodies differ from each other in clocks only. Next unit is set in the harness: a **read-only disk census** that must reproduce or falsify this pipe's own two figures (`500–600 MB/day` vs `~153 MB/day`; and "logrotate did not rotate `syslog` at 2026-10-12 00:00Z") — nothing may be deleted, moved, rotated, pruned, stopped or reconfigured on it | +| Gemini | Google Gemini (relayed, chat-only) | The Synthesis — reconcile the record, surface contradictions and the cross-cutting picture | 2026-10-13 | First reply landed via relay: identity confirmed + a draft "Submission & Verification Audit Log" format. **Lane charter + three first queue items owed.** Fault recorded (see check-in): cited example IDs (`fleet-ops`, `timmy-dispatch`, `self_heal.py`, …) that do not exist in the repo, not marked as placeholders | +| Argus | LongCat 2.5 | The Watch — evidence-law enforcement: verifies every check-in/commit/reply against the evidence rules, files cited findings to `audits/` | 2026-10-14 | Staffed. Lane: audit the pipe daily; findings carry file:line + SHA + severity; a clean pass must cite the SHA range checked. Model `meituan/longcat-2.5-preview:free` | +| Volt | DeepSeek V4.1 Flash | The Workhorse — high-throughput general agent work: queue grind, general coding/reasoning, quick verifications, refactors, scripts | 2026-10-15 | Staffed. Model `deepseek-v4.1-flash` (552B MoE, 8B/16B active, native multimodal) via DeepSeek API. Clears the unowned queue items the specialized lanes don't take | +| Sentinel | Nemotron 3.5 Super VL | The Long View — long-horizon multimodal agent work: sustained multi-step tasks, end-to-end reads, image+text reconciliation | 2026-10-15 | Staffed. Model `nvidia/nemotron-3.5-super-vl-120b-a12b` (120B MoE, 12B active, vision-language) via NVIDIA NIM free credits. Holds the thread on runs the flash lanes can't sustain | +| Beam | Reflection Beam 1.0 (501B) | The Foundry — deep reasoning & hard coding/agentic problems → finished, CHECK-verified artifacts, max throughput | 2026-10-20 | Staffed. Model `reflection/beam-1.0-501b-a23b:free` (501B MoE, 23B active, reasoning model) via Nous free tier. **FREE WINDOW ≈ 1 week — run at max throughput until it expires** | + +Lane signers (8) are named in `2026-10-29-wizard-roster.json`. diff --git a/wizard-work/ledger/CHECK-wizard-roster-2026-10-29 b/wizard-work/ledger/CHECK-wizard-roster-2026-10-29 new file mode 100644 index 0000000..32d307d --- /dev/null +++ b/wizard-work/ledger/CHECK-wizard-roster-2026-10-29 @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# CHECK -- ledger: extract WIZARDS-TABLE roster to verified CSV/MD/JSON +# (Extract lane: data extraction and tables). +# +# Re-derives the roster table from WIZARDS-TABLE.md and diffs it against the +# committed 2026-10-29-wizard-roster.{csv,json,md}. Exit code IS the verdict: +# 0 = PASS, nonzero = FAIL. No fallback arms. +set -u +cd /root/troupe-standup || exit 1 +exec python3 wizard-work/ledger/verify_wizard_roster_2026-10-29.py diff --git a/wizard-work/ledger/extract_wizard_roster.py b/wizard-work/ledger/extract_wizard_roster.py new file mode 100644 index 0000000..ad52679 --- /dev/null +++ b/wizard-work/ledger/extract_wizard_roster.py @@ -0,0 +1,112 @@ +#!/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() diff --git a/wizard-work/ledger/verify_wizard_roster_2026-10-29.py b/wizard-work/ledger/verify_wizard_roster_2026-10-29.py new file mode 100644 index 0000000..4108fd7 --- /dev/null +++ b/wizard-work/ledger/verify_wizard_roster_2026-10-29.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +"""Independent verifier for the WIZARDS-TABLE roster extract (Extract lane). + +Recomputes the expected table from WIZARDS-TABLE.md using only string/file +operations (does NOT import extract_wizard_roster.py) and diffs the replay +against the committed 2026-10-29-wizard-roster.{csv,json,md}. + +Exit code IS the verdict: 0 = PASS, nonzero = FAIL. +""" +import csv +import io +import json +import sys +from pathlib import Path + +REPO_ROOT = Path("/root/troupe-standup") +SRC = REPO_ROOT / "WIZARDS-TABLE.md" +CSV_ART = REPO_ROOT / "wizard-work" / "ledger" / "2026-10-29-wizard-roster.csv" +JSON_ART = REPO_ROOT / "wizard-work" / "ledger" / "2026-10-29-wizard-roster.json" +MD_ART = REPO_ROOT / "wizard-work" / "ledger" / "2026-10-29-wizard-roster.md" + +FAILS = [] + + +def check(name, cond, detail=""): + tag = "PASS" if cond else "FAIL" + line = "%s %s" % (tag, name) + if detail: + line += " " + detail + print(line) + if not cond: + FAILS.append(name) + + +def parse_table(source): + 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: + return None + 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: + return None + rows.append(cells) + return rows + + +def main(): + actual = parse_table(SRC) + if actual is None: + print("FAIL table header not found") + sys.exit(1) + check("re-derived table row count", len(actual) == 18, "rows=%d" % len(actual)) + wizards = actual[:10] + lane_signers = [r[0] for r in actual[10:]] + check("10 wizard rows", len(wizards) == 10) + check("8 lane signer rows", len(lane_signers) == 8) + check("lane_signers", lane_signers == [ + "forge", "ledger", "sol", "quill", "spark", "sphinx", "null", "zeph"], + repr(lane_signers)) + + # --- 1) CSV replay --- + expected_csv = io.StringIO() + writer = csv.DictWriter(expected_csv, + fieldnames=["Wizard", "Model", "Lane", + "Latest check-in", "Current unit"], + lineterminator="\n") + writer.writeheader() + for r in wizards: + writer.writerow({c: r[i] for i, c in enumerate( + ["Wizard", "Model", "Lane", "Latest check-in", "Current unit"])}) + got_csv = CSV_ART.read_text(encoding="utf-8") + if got_csv != expected_csv.getvalue(): + FAILS.append("CSV replay mismatch") + print(" expected CSV head:\n" + expected_csv.getvalue()[:400]) + print(" got CSV head:\n" + got_csv[:400]) + else: + check("CSV replay", True) + + # --- 2) Markdown replay --- + md_lines = MD_ART.read_text(encoding="utf-8").splitlines() + md_data = [] + for l in md_lines: + segs = [p.strip() for p in l.split("|")] + segs = [p for p in segs if p != ""] + if len(segs) == 5 and segs[0] == "Wizard": + continue + if len(segs) == 5 and all(ch == "-" for ch in "".join(segs)): + continue + if l.startswith("| "): + md_data.append(l) + expected_md = ["| " + " | ".join(r) + " |" for r in wizards] + check("MD data rows replay", md_data == expected_md, + "got=%d expected=%d" % (len(md_data), len(expected_md))) + + # --- 3) JSON replay --- + j = json.loads(JSON_ART.read_text(encoding="utf-8")) + check("JSON total_table_rows", j.get("total_table_rows") == 18, + repr(j.get("total_table_rows"))) + check("JSON wizard_rows_extracted", j.get("wizard_rows_extracted") == 10, + repr(j.get("wizard_rows_extracted"))) + check("JSON lane_signers", + j.get("lane_signers") == lane_signers, repr(j.get("lane_signers"))) + check("JSON lane_signers_count", j.get("lane_signers_count") == 8, + repr(j.get("lane_signers_count"))) + check("JSON wizards records match", + j.get("wizards") == [ + {"Wizard": r[0], "Model": r[1], "Lane": r[2], + "Latest check-in": r[3], "Current unit": r[4]} for r in wizards], + repr(j.get("wizards"))) + + if FAILS: + print("\n%d CHECK(S) FAILED" % len(FAILS)) + sys.exit(1) + print("\nALL CHECKS PASSED -- table re-derived from WIZARDS-TABLE.md; " + "CSV / MD / JSON replay clean") + sys.exit(0) + + +if __name__ == "__main__": + main()