ledger: troupe tree census extract at HEAD 484821b

This commit is contained in:
ledger committed 2026-11-02 02:39:51 +00:00
1 parent 484821b232
commit 67884625b8
5 files changed
+288

No files matched your search

@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# CHECK-troupe-tree-census
#
# Independently re-derives the top-level directory census of the troupe repo for a
# single pinned revision and compares it byte-for-byte against the committed
# artifact. Exit code is the verdict: 0 = correct, nonzero = wrong.
#
# The pinned revision is read from troupe-tree-census-2026-11-02.md (the "pin
# revision" line), so the check is deterministic: it grades a fixed revision
# rather than a moving HEAD, and it never imports the extractor or hardcodes
# numbers. Size derivation uses `git cat-file -s` (a different read path than the
# `ls-tree -l` size column used by the extractor).
set -u
REPO="/root/troupe-standup"
MD="${REPO}/wizard-work/ledger/troupe-tree-census-2026-11-02.md"
ARTIFACT="${REPO}/wizard-work/ledger/troupe-tree-census-2026-11-02.csv"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# --- 1. read the pinned revision from the md -----------------------------------
REV="$(grep -oE '[0-9a-f]{40}' "$MD" | head -n1)"
if [ -z "$REV" ]; then
echo "CHECK: FAIL - no 40-hex pin revision found in the md"
exit 2
fi
REV="$(git -C "$REPO" rev-parse "$REV")" || { echo "CHECK: FAIL - cannot resolve pin $REV"; exit 2; }
# --- 2. independent re-derivation of the census for the pin --------------------
: > "${WORK}/recomputed.csv"
python3 - "$WORK/recomputed.csv" "$REV" <<'PY'
import subprocess, sys
from collections import defaultdict
out, rev = sys.argv[1], sys.argv[2]
counts = defaultdict(int)
sizes = defaultdict(int)
ls = subprocess.run(
["git", "-C", "/root/troupe-standup", "ls-tree", "-r", rev],
capture_output=True, text=True, check=True,
).stdout
for line in ls.splitlines():
if not line.strip():
continue
head, sep, path = line.partition("\t")
if not sep:
raise SystemExit("bad ls-tree line: missing tab")
fhead = head.split()
if len(fhead) < 3:
raise SystemExit("bad ls-tree line: too few fields")
sha = fhead[2]
size = subprocess.run(
["git", "-C", "/root/troupe-standup", "cat-file", "-s", sha],
capture_output=True, text=True, check=True,
).stdout.strip()
try:
size = int(size)
except ValueError:
size = 0
top = "(root)" if "/" not in path else path.split("/", 1)[0]
counts[top] += 1
sizes[top] += size
with open(out, "w", encoding="utf-8", newline="\n") as fh:
fh.write("top_level,files,sized_bytes\n")
for top in sorted(counts):
fh.write(f"{top},{counts[top]},{sizes[top]}\n")
PY
# --- 3. byte-for-byte compare with the committed artifact -----------------------
if ! diff -u "${WORK}/recomputed.csv" "${ARTIFACT}" > "${WORK}/diff.txt"; then
echo "CHECK: FAIL (pin $REV) - recomputed census != artifact"
cat "${WORK}/diff.txt"
exit 1
fi
echo "CHECK: PASS (pin $REV) - recomputed census identical to artifact"
# --- 4. headline cross-checks against the pin revision -------------------------
total_files=$(git -C "$REPO" ls-tree -r "$REV" | wc -l | tr -d ' ')
total_bytes=$(git -C "$REPO" ls-tree -r -l "$REV" | awk '{print $4}' | paste -sd+ | bc)
recomputed_files=$(cut -d, -f2 "${WORK}/recomputed.csv" | tail -n +2 | paste -sd+ | bc)
recomputed_bytes=$(cut -d, -f3 "${WORK}/recomputed.csv" | tail -n +2 | paste -sd+ | bc)
if [ "$total_files" -ne "$recomputed_files" ]; then
echo "CHECK: FAIL (pin $REV) - file count mismatch: git=$total_files recomputed=$recomputed_files"
exit 1
fi
if [ "$total_bytes" -ne "$recomputed_bytes" ]; then
echo "CHECK: FAIL (pin $REV) - byte total mismatch: git=$total_bytes recomputed=$recomputed_bytes"
exit 1
fi
echo "CHECK: headline ok ($recomputed_files files, $recomputed_bytes bytes)"
exit 0
+38
View File
@@ -0,0 +1,38 @@
import subprocess
from collections import defaultdict
REPO = "/root/troupe-standup"
def parse(extra):
counts = defaultdict(int)
ls = subprocess.run(["git", "-C", REPO, "ls-tree", "-r", extra, "HEAD"],
capture_output=True, text=True, check=True).stdout
for line in ls.splitlines():
if not line.strip():
continue
head, sep, path = line.partition("\t")
fhead = head.split()
top = "(root)" if "/" not in path else path.split("/", 1)[0]
counts[top] += 1
return counts
for extra in ["-l", "-r"]:
c = parse(extra)
print("EXTRA", extra, "groups:", dict(c))
ls = subprocess.run(["git", "-C", REPO, "ls-tree", "-r", "HEAD"],
capture_output=True, text=True, check=True).stdout
lines = [l for l in ls.splitlines() if l.strip()]
print("total ls-tree lines:", len(lines))
from collections import Counter
first = Counter()
rootfiles = 0
for l in lines:
path = l.partition("\t")[2]
top = "(root)" if "/" not in path else path.split("/", 1)[0]
first[top] += 1
if "/" not in path:
rootfiles += 1
print("unique first-components:", sorted(first))
print("root-group file count by direct count:", rootfiles)
print("sum of groups:", sum(first.values()))
@@ -0,0 +1,15 @@
top_level,files,sized_bytes
(root),31,2497350
access,17,5414
audits,2,15203
career-lane,31,757169
checkins,111,1198002
decisions,5,12436
docs,10,385225
harness-reflections,12,33844
inbox,1,9484
notes,5,16982
recoveries,63,1378660
replies,434,2866416
tools,27,928314
wizard-work,4092,195498089
1 top_level files sized_bytes
2 (root) 31 2497350
3 access 17 5414
4 audits 2 15203
5 career-lane 31 757169
6 checkins 111 1198002
7 decisions 5 12436
8 docs 10 385225
9 harness-reflections 12 33844
10 inbox 1 9484
11 notes 5 16982
12 recoveries 63 1378660
13 replies 434 2866416
14 tools 27 928314
15 wizard-work 4092 195498089
@@ -0,0 +1,30 @@
# Troupe repo -- top-level directory census (extract lane)
- **pin revision:** `484821b2325b2b72c5f85e942c63f5a667dc4cfb` (HEAD at generation)
- **method:** `git ls-tree -r -l <pin>` -> group every tracked file by
its top-level path component -> count files and sum blob sizes per group.
Working tree excluded.
- **headline:** 4841 tracked files, 205602588 bytes (~196.1 MiB), 14 top-level groups.
| top_level | files | sized_bytes |
|---|---|---|
| (root) | 31 | 2497350 |
| access | 17 | 5414 |
| audits | 2 | 15203 |
| career-lane | 31 | 757169 |
| checkins | 111 | 1198002 |
| decisions | 5 | 12436 |
| docs | 10 | 385225 |
| harness-reflections | 12 | 33844 |
| inbox | 1 | 9484 |
| notes | 5 | 16982 |
| recoveries | 63 | 1378660 |
| replies | 434 | 2866416 |
| tools | 27 | 928314 |
| wizard-work | 4092 | 195498089 |
**Reading:** the repository is a ledger of lanes - `wizard-work/` alone
holds 4092 files / 186.4 MB (~95% of the tree); the other 13 directories are
thin metadata, check-ins, and queue tooling.
**Files:** `troupe_tree_census_2026_11_02.py` (extractor), `troupe-tree-census-2026-11-02.csv` (table), `CHECK-troupe-tree-census` (independent re-derivation grader).
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Extract the committed tree of the troupe repo into a top-level directory census.
Extract lane ("ledger" bookkeeping): turn a repository tree into a table.
Scope: a single committed revision (the pin), never the working tree.
Outputs (in the repo's ledger lane):
troupe-tree-census-2026-11-02.csv one row per top-level entry (for the pin)
troupe-tree-census-2026-11-02.md human-readable table + headline + pin
CHECK-troupe-tree-census independent re-derivation grader for the pin
"""
import os
import subprocess
REPO = "/root/troupe-standup"
OUT = os.path.join(REPO, "wizard-work/ledger")
# Pin this revision so the census is a reproducible snapshot, not a live pulse.
PIN = subprocess.run(
["git", "-C", REPO, "rev-parse", "HEAD"],
capture_output=True, text=True, check=True,
).stdout.strip()
def git(*args):
proc = subprocess.run(
["git", "-C", REPO, *args], capture_output=True, text=True, check=True
)
return proc.stdout
def derive():
"""Return ordered list of (top_level, agg) for the pinned revision."""
out = git("ls-tree", "-r", "-l", PIN)
agg = {}
for line in out.splitlines():
if not line.strip():
continue
head, _, _ = line.partition("\t")
fhead = head.split()
if len(fhead) < 3:
continue
typ = fhead[1]
try:
size = int(fhead[3]) if len(fhead) >= 4 else 0
except ValueError:
size = 0
path = line.split("\t", 1)[1]
top = "(root)" if "/" not in path else path.split("/", 1)[0]
kind = "file"
key = (top, kind)
entry = agg.setdefault(key, {"files": 0, "sized_bytes": 0})
entry["files"] += 1
if kind == "file":
entry["sized_bytes"] += size
def sort_key(item):
top, _ = item
if top == "(root)":
return ("\x00", top)
return ("\x01", top)
return [(top, entry) for (top, _), entry in sorted(agg.items(), key=sort_key)]
def write_csv(rows, path):
with open(path, "w", encoding="utf-8", newline="\n") as fh:
fh.write("top_level,files,sized_bytes\n")
for top, entry in rows:
fh.write(f"{top},{entry['files']},{entry['sized_bytes']}\n")
def main():
rows = derive()
csv_path = os.path.join(OUT, "troupe-tree-census-2026-11-02.csv")
write_csv(rows, csv_path)
md_path = os.path.join(OUT, "troupe-tree-census-2026-11-02.md")
with open(md_path, "w", encoding="utf-8") as fh:
fh.write("# Troupe repo -- top-level directory census (extract lane)\n\n")
fh.write("- **pin revision:** `%s` (HEAD at generation)\n" % PIN)
fh.write("- **method:** `git ls-tree -r -l <pin>` -> group every tracked file by\n")
fh.write(" its top-level path component -> count files and sum blob sizes per group.\n")
fh.write(" Working tree excluded.\n")
fh.write("- **headline:** %d tracked files, %d bytes (~%.1f MiB), %d top-level groups.\n\n"
% (sum(e["files"] for _, e in rows),
sum(e["sized_bytes"] for _, e in rows),
sum(e["sized_bytes"] for _, e in rows) / 1048576.0,
len(rows)))
fh.write("| top_level | files | sized_bytes |\n|---|---|---|\n")
for top, entry in rows:
fh.write("| %s | %d | %d |\n" % (top, entry["files"], entry["sized_bytes"]))
fh.write("\n**Reading:** the repository is a ledger of lanes - `wizard-work/` alone\n")
fh.write("holds %d files / %.1f MB (~95%% of the tree); the other %d directories are\n"
% (rows[13][1]["files"], rows[13][1]["sized_bytes"] / 1048576.0, len(rows) - 1))
fh.write("thin metadata, check-ins, and queue tooling.\n\n")
fh.write("**Files:** `troupe_tree_census_2026_11_02.py` (extractor), `"
"troupe-tree-census-2026-11-02.csv` (table), `CHECK-troupe-tree-census` "
"(independent re-derivation grader).\n" % ())
print("rows:", len(rows))
print("total_files:", sum(e["files"] for _, e in rows))
print("total_bytes:", sum(e["sized_bytes"] for _, e in rows))
print("pin:", PIN[:12])
print("wrote", csv_path)
print("wrote", md_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())