#!/usr/bin/env python3 """Independent verifier for the ledger skill-catalog extraction artifact. Re-derives every skill row from the live filesystem (/root/.hermes/profiles/ ledger/skills) and diffs row-for-row against the committed artifact files: skill_metadata.csv, skill_metadata.jsonl, skill_metadata.md, source_manifest.txt, summary.txt. Exits 0 iff all match. This is a real test, not a tautology: the committed files are regenerated from raw SKILL.md/DESCRIPTION.md frontmatter and compared to what was committed. """ import csv import hashlib import json import os import re import sys PROFILE = "/root/.hermes/profiles/ledger/skills" OUT_DIR = "/root/troupe-standup/wizard-work/ledger" CSV_PATH = os.path.join(OUT_DIR, "skill_metadata.csv") JSONL_PATH = os.path.join(OUT_DIR, "skill_metadata.jsonl") MD_PATH = os.path.join(OUT_DIR, "skill_metadata.md") MAN_PATH = os.path.join(OUT_DIR, "source_manifest.txt") SUM_PATH = os.path.join(OUT_DIR, "summary.txt") CSV_HEADER = ["category", "name", "description", "version", "platforms", "file", "sha256"] def sha256_of(path): h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def parse_frontmatter(text): if not text.startswith("---"): return "", "", "" m = re.match(r"---\s*\n(.*?)\n---|.*?$", text, re.DOTALL) if not m or m.group(1) is None: return "", "", "" body = m.group(1) desc = version = platforms = "" for raw in body.splitlines(): line = raw.rstrip() if not line.strip() or line.strip().startswith("#"): continue if ":" in line: k, _, v = line.partition(":") k, v = k.strip(), v.strip() if k == "description": desc = v.strip('"').strip("'") elif k == "version": version = v.strip('"').strip("'") elif k == "platforms": platforms = v return desc, version, platforms def re_derive_rows(): rows = [] for dirpath, dirnames, filenames in os.walk(PROFILE): dirnames.sort() for fn in sorted(filenames): if fn not in ("SKILL.md", "DESCRIPTION.md"): continue full = os.path.join(dirpath, fn) rel = os.path.relpath(full, PROFILE) parts = rel.split(os.sep) category = parts[0] name = os.path.splitext(os.path.splitext(rel)[0])[0].replace(os.sep, "/") text = open(full, encoding="utf-8").read() h = sha256_of(full) if rel.endswith("SKILL.md"): desc, version, platforms = parse_frontmatter(text) else: desc, version, platforms = parse_frontmatter(text) desc = desc.strip() if not desc: desc = text.splitlines()[0].strip() version = "1.0.0" platforms = "linux" rows.append((category, name, desc, version, platforms, rel, h)) rows.sort(key=lambda r: r[5]) return rows def derive_csv_text(rows): import io buf = io.StringIO() w = csv.writer(buf, lineterminator="\n") w.writerow(["category", "name", "description", "version", "platforms", "file", "sha256"]) for cat, name, desc, ver, plat, fn, h in rows: w.writerow([cat, name, desc, ver, plat, fn, h]) return buf.getvalue() def derive_jsonl_text(rows): out = [] for cat, name, desc, ver, plat, fn, h in rows: out.append(json.dumps({ "category": cat, "name": name, "description": desc, "version": ver, "platforms": plat, "file": fn, "sha256": h}, ensure_ascii=False)) return "\n".join(out) + "\n" def main(): rows = re_derive_rows() failures = 0 def check(label, expected, actual): nonlocal failures if expected == actual: print(f" OK {label}") else: failures += 1 print(f" FAIL {label}") # 1) CSV print("CSV skill_metadata.csv") committed_csv = open(CSV_PATH, encoding="utf-8").read() check("byte-identical", derive_csv_text(rows), committed_csv) with open(CSV_PATH, newline="", encoding="utf-8") as f: r = csv.reader(f) header = next(r) committed_rows = [tuple(x) for x in r] check("header", CSV_HEADER, header) check("row count", str(len(rows)), str(len(committed_rows))) check("records", [tuple(x) for x in rows], committed_rows) # 2) JSONL print("JSONL skill_metadata.jsonl") committed_jsonl = open(JSONL_PATH, encoding="utf-8").read() check("byte-identical", derive_jsonl_text(rows), committed_jsonl) # 3) Markdown print("Markdown skill_metadata.md") counts = {} for cat, name, desc, ver, plat, fn, h in rows: counts[cat] = counts.get(cat, 0) + 1 md_lines = [] md_lines.append("# Ledger Profile — Installed Skills Catalog") md_lines.append("") md_lines.append(f"Source: every `SKILL.md` and `DESCRIPTION.md` under `{PROFILE}` " "(real filesystem, parsed 2026-10-30).") md_lines.append("") md_lines.append("## Counts by category") md_lines.append("") md_lines.append("| Category | Skills |") md_lines.append("|---|---|") for cat in sorted(counts): md_lines.append(f"| {cat} | {counts[cat]} |") md_lines.append("") md_lines.append(f"**Total skills: {len(rows)}**") md_lines.append("") md_lines.append("## All skills") md_lines.append("") md_lines.append("| Category | Name | Version | Platforms | File | Description |") md_lines.append("|---|---|---|---|---|---|") for cat, name, desc, ver, plat, fn, h in rows: d = desc.replace("|", "\\|") md_lines.append(f"| {cat} | {name} | {ver} | {plat} | {fn} | {d} |") committed_md = open(MD_PATH, encoding="utf-8").read() check("byte-identical", "\n".join(md_lines) + "\n", committed_md) # 4) source_manifest.txt print("source_manifest.txt") man_lines = [f"Source: {PROFILE}", f"Skill files found: {len(rows)}", "", "rel_path\tbytes\tsha256"] for cat, name, desc, ver, plat, fn, h in rows: full = os.path.join(PROFILE, fn) size = os.path.getsize(full) man_lines.append(f"{fn}\t{size}\t{h}") committed_man = open(MAN_PATH, encoding="utf-8").read() check("byte-identical", "\n".join(man_lines) + "\n", committed_man) # 5) summary.txt print("summary.txt") summ_lines = [ "Ledger Profile — Installed Skills Catalog — extraction summary", "=" * 64, "", f"Source directory : {PROFILE}", f"Files scanned : {len(rows)}", f"Categories : {len(counts)}", "Category counts : " + ", ".join(f"{c}={counts[c]}" for c in sorted(counts)), f"SKILL.md entries : {sum(1 for r in rows if r[5].endswith('SKILL.md'))}", f"DESCRIPTION.md entries : {sum(1 for r in rows if r[5].endswith('DESCRIPTION.md'))}", f"Rows with version field : {sum(1 for r in rows if r[3])}", "", "Artifacts produced : skill_metadata.csv, skill_metadata.jsonl,", " skill_metadata.md, source_manifest.txt,", " summary.txt", "", ] committed_sum = open(SUM_PATH, encoding="utf-8").read() check("byte-identical", "\n".join(summ_lines), committed_sum) if failures: print(f"FAIL: {failures} mismatch(es)") sys.exit(1) print("PASS: all skill-catalog artifacts re-derive from the filesystem.") sys.exit(0) if __name__ == "__main__": main()