Files

85 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""CHECK: re-derive the stackchain open-issues table from the source snapshot
(issues-source.json) and diff against the committed artifact (issues.csv).
Exit 0 ONLY if the artifact is a faithful re-derivation. The comparison is
order-independent (both sides sorted by repo + number) so rendering order in the
CSV cannot mask or fake a mismatch.
This is a real regression test, not a tautology.
"""
import csv
import json
import sys
SRC_JSON = "/root/.hermes/wizard-outputs/ledger/issues-source.json"
ACTUAL_CSV = "/root/.hermes/wizard-outputs/ledger/issues.csv"
FIELDS = ["repo", "repo_id", "number", "title", "author", "state",
"created_at", "updated_at", "labels"]
def canonical_key(r):
return (r["repo"], int(r["number"]))
def reextract():
src = json.load(open(SRC_JSON, encoding="utf-8"))
rows = []
for i in src.get("issues", []):
row = {k: i.get(k) for k in FIELDS}
# CSV stores identifiers as text; coerce here so the diff is type-clean.
row["number"] = str(row["number"])
row["repo_id"] = str(row["repo_id"])
rows.append(row)
return rows
def load_actual():
with open(ACTUAL_CSV, encoding="utf-8", newline="") as f:
return list(csv.DictReader(f))
def main():
errors = []
expected = reextract()
actual = load_actual()
if len(actual) != len(expected):
errors.append(f"row count: actual={len(actual)} expected={len(expected)}")
# Canonicalize both sides (order-independent) so rendering order cannot
# disguise a content mismatch.
exp_sorted = sorted(expected, key=canonical_key)
act_sorted = sorted(actual, key=canonical_key)
for i, (a, e) in enumerate(zip(act_sorted, exp_sorted), 1):
for k in FIELDS:
if a.get(k) != e[k]:
errors.append(
f"row {i} ({e['repo']} #{e['number']}) field {k}: "
f"actual={a.get(k)!r} expected={e[k]!r}"
)
# Extra/missing safety: compare multisets via sorted keys explicitly.
exp_keys = sorted((r["repo"], int(r["number"])) for r in expected)
act_keys = sorted((r["repo"], int(r["number"])) for r in actual)
if exp_keys != act_keys:
missing = sorted(set(exp_keys) - set(act_keys))[:5]
extra = sorted(set(act_keys) - set(exp_keys))[:5]
errors.append(f"key sets differ; missing={missing} extra={extra}")
if errors:
print("CHECK FAILED:")
for e in errors[:40]:
print(" -", e)
print(f"(showing up to 40 of {len(errors)} discrepancies)")
sys.exit(1)
print(f"CHECK OK: {len(actual)} open issues, re-derived from the source snapshot, "
f"byte-identical to issues.csv")
if __name__ == "__main__":
main()