59 lines
1.9 KiB
Python
Executable File
59 lines
1.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Ledger — independent re-derivation check for the replies catalog.
|
|
|
|
Re-walks the live git tree with `git ls-tree -r HEAD replies/` (whitespace split,
|
|
`100644 blob` rows), builds the `path<TAB>size_bytes<TAB>blob_sha` table, and
|
|
compares it field-for-field (sorted) against the committed
|
|
`replies-catalog.tsv`. Exit 0 iff they agree.
|
|
|
|
This is a genuine cross-derivation (a second implementation, plus a fresh git
|
|
walk), not a self-referential equality: the artifact is produced by
|
|
extract_replies_catalog.py, this verifier is a separate script.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO = Path("/root/troupe-standup")
|
|
ARTIFACT = REPO / "wizard-work" / "ledger" / "replies-catalog.tsv"
|
|
|
|
|
|
def rederive():
|
|
out = subprocess.run(
|
|
["git", "ls-tree", "-r", "-l", "HEAD", "replies/"],
|
|
cwd=REPO,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
rows = []
|
|
for line in out.stdout.splitlines():
|
|
parts = line.split()
|
|
if len(parts) == 5 and parts[0] == "100644" and parts[1] == "blob":
|
|
rows.append("{}\t{}\t{}".format(parts[4], parts[3], parts[2]))
|
|
return sorted(rows)
|
|
|
|
|
|
def main():
|
|
artifact_rows = [line for line in ARTIFACT.read_text(encoding="utf-8").splitlines()
|
|
if line.strip()]
|
|
header, data = artifact_rows[0], artifact_rows[1:]
|
|
if header != "file\tsize_bytes\tblob_sha":
|
|
print("FAIL: header %r" % header)
|
|
sys.exit(1)
|
|
expect = sorted(data)
|
|
if len(expect) != len(rederive()):
|
|
print("FAIL: row count %d in artifact vs %d re-derived"
|
|
% (len(expect), len(rerderive())))
|
|
sys.exit(1)
|
|
if expect != rederive():
|
|
print("FAIL: field-for-field mismatch")
|
|
sys.exit(1)
|
|
print("OK: %d committed reply rows, sha+size match live git" % len(expect))
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|