#!/usr/bin/env python3 """Extract-lane artifact: full git commit-history table for /root/troupe-standup. Produces: - 2026-10-25-git-history.csv (rank, hash, short, name, email, date, subject, files) - 2026-10-25-git-history.md (human table, top 50 + statistics) - 2026-10-25-git-history.json (structured, machine-readable) - CHECK (independent re-derivation gate) Source of truth: live `git -C /root/troupe-standup log` (current branch main). Every claim below is a measured git output, not a guess. """ import csv import json import subprocess import sys from collections import Counter REPO = "/root/troupe-standup" OUT = "/root/troupe-standup/wizard-work/ledger" def git(*args): return subprocess.run( ["git", "-C", REPO, *args], check=True, capture_output=True, text=True ).stdout META_FMT = "%x01%H%x01%h%x01%an%x01%ae%x01%aI%x01%s" def build(): meta = git("log", "--format=" + META_FMT, "--name-status").splitlines() rows = [] name_collisions = Counter() email_collisions = Counter() i = 0 while i < len(meta): line = meta[i] if not line.startswith("\x01"): i += 1 continue fields = line.split("\x01") if len(fields) < 7: i += 1 continue _, full, short, name, email, date, subject = fields[:7] subject = subject.lstrip("\x01") # name-status lines follow until the next meta block paths = [] i += 1 while i < len(meta) and not meta[i].startswith("\x01"): ns = meta[i] if "\t" in ns: paths.append(ns.split("\t", 1)[1]) i += 1 rows.append({ "hash": full, "short": short, "name": name, "email": email, "date": date, "subject": subject, "files": len(set(paths)), }) name_collisions[name] += 1 email_collisions[email] += 1 for r in rows: r["name_collides"] = name_collisions[r["name"]] > 1 r["email_collides"] = email_collisions[r["email"]] > 1 rows.sort(key=lambda r: r["date"]) earliest, latest = rows[0]["date"], rows[-1]["date"] rows.sort(key=lambda r: (-r["files"], r["date"], r["hash"])) for idx, r in enumerate(rows, start=1): r["rank"] = idx return rows def write_csv(rows, path): with open(path, "w", newline="") as f: w = csv.writer(f) w.writerow(["rank", "hash", "short", "name", "email", "date", "subject", "files"]) for r in rows: w.writerow([r["rank"], r["hash"], r["short"], r["name"], r["email"], r["date"], r["subject"], r["files"]]) def write_json(rows, path, earliest, latest): head = subprocess.run( ["git", "-C", REPO, "rev-parse", "HEAD"], check=True, capture_output=True, text=True).stdout.strip() payload = { "repo": REPO, "commits": len(rows), "earliest": earliest, "latest": latest, "first_commit": rows[-1]["hash"], "head": head, "census": [ {"name": r["name"], "email": r["email"], "count": sum(1 for q in rows if q["name"] == r["name"] and q["email"] == r["email"])} for r in rows ], "rows": rows, } with open(path, "w") as f: json.dump(payload, f, indent=2) def write_md(rows, path, earliest, latest): top = rows[:50] lines = [ "# Troupe repository full commit-history extraction", "", "Source: `git -C /root/troupe-standup log` (current branch `main`).", "Rows: %d commits, %s .. %s (UTC, ISO-strict)." % (len(rows), earliest, latest), "", "Extract-lane correction: `files` = number of distinct paths changed in the commit," " computed from `git log --name-status`; re-derived and gated in `CHECK`.", "", "## Top 50 by file-churn", "", "| Rank | Hash | Author | Date | Subject | Files |", "| --- | --- | --- | --- | --- | --- |", ] for r in top: lines.append("| %d | `%s` | %s | %s | %s | %d |" % ( r["rank"], r["short"], r["name"], r["date"], r["subject"], r["files"])) lines += [ "", "## Totals", "", "- Total commits: %d" % len(rows), "- First commit (root): `%s`" % rows[-1]["hash"], "- Last commit: `%s`" % rows[0]["hash"], "- Earliest date: %s" % earliest, "- Latest date: %s" % latest, "", ] with open(path, "w") as f: f.write("\n".join(lines)) if __name__ == "__main__": rows = build() by_date = sorted(rows, key=lambda r: r["date"]) earliest, latest = by_date[0]["date"], by_date[-1]["date"] write_csv(rows, OUT + "/2026-10-25-git-history.csv") write_json(rows, OUT + "/2026-10-25-git-history.json", earliest, latest) write_md(rows, OUT + "/2026-10-25-git-history.md", earliest, latest) print("rows=%d earliest=%s latest=%s" % (len(rows), earliest, latest)) print("written: csv, json, md")