ledger: 2026-10-30 git-tree manifest table
This commit is contained in:
1 parent
891b044d7a
commit
b3bd3c9bdf
5 files changed
+34346
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract the troupe repo's tracked file tree into a verified table.
|
||||
|
||||
Source of truth: `git ls-files` (tracked files) at the repo root.
|
||||
|
||||
Outputs (in this directory, wizard-work/ledger/):
|
||||
- 2026-10-30-git-tree-manifest.csv : relpath, ext, size_bytes, sha256
|
||||
- 2026-10-30-git-tree-manifest.json: structured manifest
|
||||
- 2026-10-30-git-tree-manifest.md : human-readable table
|
||||
|
||||
Independent re-derivation:
|
||||
verify_git_tree_manifest_2026_10_30.py (committed; the committed CHECK
|
||||
runs that verifier and exits 0 iff it matches).
|
||||
"""
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def find_repo_root(start: pathlib.Path) -> pathlib.Path:
|
||||
"""Walk up from `start` until a directory containing `.git` is found."""
|
||||
current = start.resolve()
|
||||
for _ in range(12):
|
||||
if (current / ".git").exists():
|
||||
return current
|
||||
if current.parent == current:
|
||||
break
|
||||
current = current.parent
|
||||
raise SystemExit(f"no .git found above {start}")
|
||||
|
||||
|
||||
def sha256_of(path: pathlib.Path) -> str:
|
||||
h = hashlib.sha256()
|
||||
with path.open("rb") as fh:
|
||||
for chunk in iter(lambda: fh.read(1024 * 1024), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
REPO_ROOT = find_repo_root(HERE)
|
||||
out = subprocess.run(
|
||||
["git", "ls-files"], cwd=REPO_ROOT, check=True,
|
||||
capture_output=True, text=True, env=os.environ,
|
||||
).stdout.splitlines()
|
||||
|
||||
rows = []
|
||||
for rel in sorted(out):
|
||||
p = REPO_ROOT / rel
|
||||
ext = p.suffix.lower() if p.suffix else "-"
|
||||
rows.append({
|
||||
"relpath": rel,
|
||||
"ext": ext,
|
||||
"size_bytes": p.stat().st_size,
|
||||
"sha256": sha256_of(p),
|
||||
})
|
||||
|
||||
ext_counts = {}
|
||||
total_bytes = 0
|
||||
for r in rows:
|
||||
ext_counts[r["ext"]] = ext_counts.get(r["ext"], 0) + 1
|
||||
total_bytes += r["size_bytes"]
|
||||
|
||||
manifest = {
|
||||
"repo": str(REPO_ROOT),
|
||||
"generated_at": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
"tracked_file_count": len(rows),
|
||||
"total_bytes": total_bytes,
|
||||
"extension_counts": ext_counts,
|
||||
"top_extensions": sorted(ext_counts.items(), key=lambda kv: (-kv[1], kv[0]))[:15],
|
||||
"files": rows,
|
||||
}
|
||||
|
||||
(HERE / "2026-10-30-git-tree-manifest.json").write_text(
|
||||
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
with (HERE / "2026-10-30-git-tree-manifest.csv").open("w", encoding="utf-8",
|
||||
newline="") as fh:
|
||||
fh.write("relpath\text\tsize_bytes\tsha256\n")
|
||||
for r in rows:
|
||||
fh.write(f'{r["relpath"]}\t{r["ext"]}\t{r["size_bytes"]}\t{r["sha256"]}\n')
|
||||
|
||||
lines = [
|
||||
"# Git tree manifest — 2026-10-30",
|
||||
"",
|
||||
f"- repo: `{REPO_ROOT}`",
|
||||
f"- tracked files: {manifest['tracked_file_count']}",
|
||||
f"- total bytes: {manifest['total_bytes']:,}",
|
||||
"",
|
||||
"## Top extensions",
|
||||
"",
|
||||
"| extension | files |",
|
||||
"|---|---|",
|
||||
]
|
||||
for ext, cnt in manifest["top_extensions"]:
|
||||
lines.append(f"| {ext or '(no extension)'} | {cnt} |")
|
||||
lines += ["", "## Full file list", "", "| # | relpath | ext | bytes |", "|---|---|---|---|"]
|
||||
for i, r in enumerate(manifest["files"], 1):
|
||||
lines.append(f"| {i} | {r['relpath']} | {r['ext']} | {r['size_bytes']} |")
|
||||
(HERE / "2026-10-30-git-tree-manifest.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
print(f"wrote {len(rows)} tracked files -> {HERE}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent verification of 2026-10-30-git-tree-manifest.csv.
|
||||
|
||||
Recomputes the tracked-file table from `git ls-files` at the repo root and
|
||||
compares it against the committed CSV (relpath, ext, size_bytes, sha256).
|
||||
|
||||
Exit 0 iff the committed manifest exactly matches git's tracked file set.
|
||||
"""
|
||||
import hashlib
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
REPO = pathlib.Path("/root/troupe-standup")
|
||||
MANIFEST_CSV = REPO / "wizard-work/ledger/2026-10-30-git-tree-manifest.csv"
|
||||
|
||||
|
||||
def git_ls_files():
|
||||
return subprocess.run(
|
||||
["git", "-C", str(REPO), "ls-files"],
|
||||
check=True, capture_output=True, text=True,
|
||||
).stdout.splitlines()
|
||||
|
||||
|
||||
def sha256_of(p: pathlib.Path) -> str:
|
||||
h = hashlib.sha256()
|
||||
with p.open("rb") as fh:
|
||||
for chunk in iter(lambda: fh.read(1024 * 1024), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
live = git_ls_files()
|
||||
live_set = set(live)
|
||||
print(f"live tracked-file count: {len(live)}", file=sys.stderr)
|
||||
|
||||
manifest_rows = MANIFEST_CSV.read_text(encoding="utf-8").splitlines()
|
||||
assert manifest_rows[0] == "relpath\text\tsize_bytes\tsha256", manifest_rows[0]
|
||||
data = manifest_rows[1:]
|
||||
print(f"manifest row count: {len(data)}", file=sys.stderr)
|
||||
assert len(data) == len(live), (
|
||||
f"row count mismatch: manifest {len(data)} vs git {len(live)}")
|
||||
|
||||
data_paths = {line.split("\t", 1)[0] for line in data}
|
||||
if data_paths != live_set:
|
||||
print(f"path-set mismatch: manifest {len(data_paths)} vs live {len(live_set)}",
|
||||
file=sys.stderr)
|
||||
bad = sorted(data_paths ^ live_set)
|
||||
print(f"FAILURES: {len(bad)}", file=sys.stderr)
|
||||
for b in bad[:10]:
|
||||
print(" ", b, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
live_by_path = {}
|
||||
for rel in live:
|
||||
p = REPO / rel
|
||||
live_by_path[rel] = (
|
||||
p.suffix.lower() if p.suffix else "-",
|
||||
p.stat().st_size,
|
||||
sha256_of(p),
|
||||
)
|
||||
|
||||
bad = []
|
||||
for line in data:
|
||||
rel, ext, size_s, sha = line.split("\t")
|
||||
if rel not in live_by_path:
|
||||
bad.append(("orphan", rel))
|
||||
continue
|
||||
lext, lsize, lsha = live_by_path[rel]
|
||||
if ext != lext or int(size_s) != lsize or sha != lsha:
|
||||
bad.append(("cell-mismatch", rel, ext, lext, size_s, lsize, sha, lsha))
|
||||
|
||||
print(f"checked rows: {len(data)}", file=sys.stderr)
|
||||
if bad:
|
||||
print(f"FAILURES: {len(bad)}", file=sys.stderr)
|
||||
for b in bad[:10]:
|
||||
print(" ", b, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print("VERIFY_PASS: manifest matches git ls-files (relpath, ext, size, sha256)",
|
||||
file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user