#!/usr/bin/env python3 # verify_ledger_dir_census.py # Real test: re-derive the live inventory of wizard-work/ledger from the # filesystem and compare byte-for-byte with the committed artifact. Exits 0 # iff identical. The artifact CSV is excluded from both sides so the check is # self-consistent across regeneration. import hashlib, os, csv, sys HERE = os.path.dirname(os.path.abspath(__file__)) # wizard-work/ledger REPO_ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) # troupe-standup root ARTIFACT = os.path.join(REPO_ROOT, "wizard-work", "ledger", "2026-10-30-ledger-dir-census.csv") SRC = HERE def re_derive(): rows = [] for dirpath, dirnames, filenames in os.walk(SRC): dirnames.sort() for fn in sorted(filenames): full = os.path.join(dirpath, fn) if os.path.abspath(full) == os.path.abspath(ARTIFACT): continue rel = os.path.relpath(full, REPO_ROOT) size = str(os.path.getsize(full)) h = hashlib.sha256() with open(full, "rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) rows.append((rel, size, h.hexdigest())) rows.sort(key=lambda r: r[0]) return rows def read_committed(path): with open(path, newline="", encoding="utf-8") as f: r = csv.reader(f) header = next(r) assert header == ["rel_path", "size_bytes", "sha256"], f"bad header {header}" return [tuple(x) for x in r] committed = read_committed(ARTIFACT) derived = re_derive() print(f"artifact: {ARTIFACT}") print(f"rows committed: {len(committed)} rows re-derived: {len(derived)}") if committed == derived: print("PASS: committed artifact matches live re-derivation.") sys.exit(0) print("FAIL: mismatch (committed vs re-derived).") n = min(len(committed), len(derived)) for i in range(n): if committed[i] != derived[i]: print(f" first diff at row {i}:") print(f" committed: {committed[i]}") print(f" derived: {derived[i]}") break else: if len(committed) < len(derived): print(f" derived has {len(derived)-len(committed)} extra rows") else: print(f" committed has {len(committed)-len(derived)} extra rows") sys.exit(1)