Files
troupe-standup/wizard-work/ledger/verify_replies_index_2026-11-02.py

85 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Ledger — independent re-derivation check for the replies index (2026-11-02).
Exits 0 only if every row in the catalog TSV is well-formed AND the
per-recipient/per-day counts in the summary MD match the catalog.
Does not touch the git tree.
"""
import os
import re
import sys
from collections import Counter
CATALOG = "/root/troupe-standup/wizard-work/ledger/replies-catalog-2026-11-02.tsv"
SUMMARY = "/root/troupe-standup/wizard-work/ledger/replies-catalog-2026-11-02.md"
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
FILENAME_RE = re.compile(
r"^\d{4}-\d{2}-\d{2}-to-[A-Za-z0-9_-]+(?:-(\d+|interview|preview))?\.md$"
)
def main():
problems = []
with open(CATALOG, encoding="utf-8") as f:
lines = [ln for ln in f.read().split("\n") if ln.strip() != ""]
header = lines[0]
if header != "date\trecipient\tnumber\tcategory\tfile":
problems.append("bad header: %r" % header)
data = lines[1:]
if len(data) != 430:
problems.append("expected 430 data rows, got %d" % len(data))
seen_files = set()
recip_counts = Counter()
date_counts = Counter()
cat_counts = Counter()
for i, ln in enumerate(data, start=2):
parts = ln.split("\t")
if len(parts) != 5:
problems.append("line %d: %d columns" % (i, len(parts)))
continue
date, recip, number, category, file = parts
if not DATE_RE.match(date):
problems.append("line %d: bad date %r" % (i, date))
if not FILENAME_RE.match(file):
problems.append("line %d: bad file %r" % (i, file))
if not recip:
problems.append("line %d: empty recipient" % i)
if file in seen_files:
problems.append("duplicate file %r" % file)
seen_files.add(file)
recip_counts[recip] += 1
date_counts[date] += 1
cat_counts[category] += 1
with open(SUMMARY, encoding="utf-8") as f:
smd = f.read()
if "total" not in smd:
problems.append("summary missing a total row")
for recip, n in recip_counts.items():
if not re.search(r"\|\s*%s\s*\|\s*%d\s*\|" % (re.escape(recip), n), smd):
problems.append("summary missing/mismatched per-recipient row for %r" % recip)
if sum(recip_counts.values()) != 430:
problems.append("recipient counts sum != 430")
if sum(date_counts.values()) != 430:
problems.append("date counts sum != 430")
if dict(cat_counts) != {"reply": 430}:
problems.append("unexpected category mix: %r" % dict(cat_counts))
if problems:
print("FAIL (%d problems)" % len(problems))
for p in problems[:25]:
print(" -", p)
return 1
print("OK: %d rows, all well-formed, summary consistent" % len(data))
return 0
if __name__ == "__main__":
sys.exit(main())