#!/usr/bin/env python3 """Independent verifier for the daily local-pipe triage artifact (2026-10-25). The artifact records the repository's pending-work index (files the worktree has that git does not track) at capture time, frozen into the stored index list. This verifier: 1. Re-derives the classification of the captured index ENTIRELY from the artifact's own stored index (no live git query, so the check is stable), and asserts the artifact's bucket counts, stray list, modified list, and totals all equal that re-derivation. 2. Asserts the markdown report's scope lines and bucket table agree with the stored numbers, so the report text cannot drift from its data. Exit 0 only when the artifact is internally correct; nonzero otherwise. """ import collections import json import os import sys REPO = "/root/troupe-standup" HERE = os.path.dirname(os.path.abspath(__file__)) JSON_PATH = os.path.join(HERE, "daily-local-pipe-triage-2026-10-25.json") MD_PATH = os.path.join(REPO, "wizard-work", "sol", "daily-local-pipe-triage-2026-10-25.md") def classify(path): b = path.split("/") base = b[-1] if ".hermes-tmp." in path or base.startswith(".triage_"): return "STRAY" if "beam" in b: return "STRAY" if base == "CHECK" and "\n" in path: return "STRAY" if base.startswith("README-"): return "DOC" if base.startswith("CHECK"): return "CHECK" if base.startswith("_probe") or base.startswith("probe_"): return "PROBE" if "forge" in b: return "FORGE" if "sphinx" in b: return "SPHINX" if "recoveries" in b: return "RECOVERY" if "ledger" in b: return "LEDGER" if "wizard-work/null/" in ("/" + path + "/"): return "NULL" if path.startswith("wizard-work/"): return "LORE" return "MISC" def main(): errors = [] # --- load stored artifact (contains the captured index) --- try: with open(JSON_PATH, encoding="utf-8") as fh: stored = json.load(fh) except Exception as exc: print("VERIFY FAIL: cannot read %s: %s" % (JSON_PATH, exc)) sys.exit(1) # --- re-derive classification from the captured index --- captured = stored.get("untracked_paths", []) buckets = collections.Counter() for p in captured: buckets[classify(p)] += 1 rederived = { "modified": stored.get("modified", []), "untracked": len(captured), "total_pending": len(captured), "buckets": {k: buckets[k] for k in sorted(buckets)}, "stray": sorted(p for p in captured if classify(p) == "STRAY"), } checks = [ ("modified", rederived["modified"], stored.get("modified")), ("untracked", rederived["untracked"], stored.get("untracked")), ("total_pending", rederived["total_pending"], stored.get("total_pending")), ("buckets", rederived["buckets"], stored.get("buckets")), ("stray", rederived["stray"], stored.get("stray")), ] for key, got, want in checks: if got != want: errors.append("%s: rederived=%r stored=%r" % (key, got, want)) # --- report scope lines must agree with stored numbers --- try: text = open(MD_PATH, encoding="utf-8").read() except OSError: text = "" for marker in ( "**modified**: %d" % len(stored.get("modified", [])), "**untracked**: %d" % stored.get("untracked"), "**total pending**: %d" % stored.get("total_pending"), ): if marker not in text: errors.append("report scope line missing: " + marker) # --- bucket table rows must agree with stored bucket counts --- for k, v in stored.get("buckets", {}).items(): if "| %s | %d |" % (k, v) not in text: errors.append("report bucket row missing/incorrect: %s | %d" % (k, v)) if errors: print("VERIFY FAIL") for e in errors: print(" -", e) sys.exit(1) print("VERIFY PASS") print(json.dumps({ "modified": len(stored.get("modified", [])), "untracked": stored.get("untracked"), "total_pending": stored.get("total_pending"), "buckets": stored.get("buckets"), "stray": len(stored.get("stray", [])), }, indent=2)) sys.exit(0) if __name__ == "__main__": main()