Files
troupe-standup/wizard-work/ledger/verify_reply_volume_census.py
T

127 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Independent verifier for the replies volume census artifacts.
Recomputes ground truth from the LIVE <repo>/replies/ directory using shell
pipelines (grep/sed/cut/sort/uniq via subprocess), then asserts the committed
TSV artifacts and the markdown summary match that ground truth.
The extractor parses with a compiled regex over pathlib; this verifier parses
with shell text tools -- two independent code paths over the same evidence
directory, so agreement is a real cross-check.
Exit status:
0 artifact matches ground truth
1 mismatch (or artifact missing / unreadable)
Used as the CHECK line: python3 wizard-work/ledger/verify_reply_volume_census.py
"""
import csv
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
REPLIES = REPO_ROOT / "replies"
DATE_TSV = REPO_ROOT / "wizard-work" / "ledger" / "reply-volume-census.tsv"
RECIP_TSV = REPO_ROOT / "wizard-work" / "ledger" / "reply-volume-by-recipient.tsv"
MD = REPO_ROOT / "wizard-work" / "ledger" / "reply-volume-census.md"
DATE_PAT = r"^[0-9]{4}-[0-9]{2}-[0-9]{2}-to-"
FIND_BASENAMES = "find replies -maxdepth 1 -type f -name '*.md' -printf '%f\\n'"
SEP = "-------------------------------------------------------------------------------"
def sh(cmd):
"""Run a shell command and return stripped stdout."""
return subprocess.run(
cmd, shell=True, check=True, capture_output=True, text=True
).stdout
def ground_truth():
"""Independent ground truth derived from the live replies/ directory."""
date_cmd = (FIND_BASENAMES + " | grep -E '" + DATE_PAT + "' | "
"sed -E 's/-to-.*//' | sort | uniq -c")
date_files = sh(date_cmd).strip().splitlines()
date_counts = {}
for line in date_files:
cnt, date = line.split(None, 1)
date_counts[date] = int(cnt)
# Recipients: strip the date prefix, drop ".md", then drop a trailing "-NN".
recip_cmd = (FIND_BASENAMES + " | grep -E '" + DATE_PAT + "' | "
"sed -E 's/^" + DATE_PAT + "//' | sed -E 's/\\.md$//' | "
"sed -E 's/-[0-9]+$//' | sort | uniq -c")
recip_files = sh(recip_cmd).strip().splitlines()
recip_counts = {}
for line in recip_files:
cnt, recip = line.split(None, 1)
recip_counts[recip] = int(cnt)
return date_counts, recip_counts, sum(date_counts.values())
def parse_tsv(path):
rows = {}
with path.open(newline="", encoding="utf-8") as fh:
reader = csv.reader(fh, delimiter="\t")
next(reader, None) # skip header row
for row in reader:
if len(row) == 2:
rows[row[0]] = int(row[1])
return rows
def main():
date_gt, recip_gt, total_gt = ground_truth()
problems = []
for label, tsv_path, gt in (("date", DATE_TSV, date_gt),
("recipient", RECIP_TSV, recip_gt)):
if not tsv_path.is_file():
problems.append("missing " + str(tsv_path))
continue
rows = parse_tsv(tsv_path)
rows_data = {k: v for k, v in rows.items() if k != "TOTAL"}
if rows_data != gt:
problems.append(
"%s: data rows differ; got %d keys, ground truth %d keys"
% (label, len(rows_data), len(gt))
)
if rows.get("TOTAL", -1) != sum(gt.values()):
problems.append(
"%s: TOTAL row %r != sum of rows %d"
% (label, rows.get("TOTAL"), sum(gt.values()))
)
# Markdown summary must name the correct headline numbers.
if MD.is_file():
text = MD.read_text(encoding="utf-8")
for token in ("430", "34", "17", "README.md"):
if token not in text:
problems.append("markdown missing assertion: %s" % token)
else:
problems.append("missing " + str(MD))
if problems:
print("CHECK FAIL")
for p in problems:
print(" -", p)
sys.exit(1)
print(
"CHECK PASS: %d replies, %d dates, %d recipients "
"(md_files=%d, canonical=%d)"
% (total_gt, len(date_gt), len(recip_gt), md_total(), total_gt)
)
sys.exit(0)
def md_total():
return len(sh("ls -1 replies/*.md").splitlines())
if __name__ == "__main__":
main()