diff --git a/CHECK-extension-census-2026-11-03 b/CHECK-extension-census-2026-11-03 new file mode 100755 index 0000000..81e4b63 --- /dev/null +++ b/CHECK-extension-census-2026-11-03 @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# CHECK -- extension census (extract lane). Exit 0 iff the committed +# wizard-work/ledger/extension-census.csv is byte-identical to a fresh +# re-derivation from repo_files.tsv. +set -u +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 2 +exec python3 wizard-work/ledger/verify_extension_census.py diff --git a/wizard-work/ledger/README-extension-census.md b/wizard-work/ledger/README-extension-census.md new file mode 100644 index 0000000..85b2090 --- /dev/null +++ b/wizard-work/ledger/README-extension-census.md @@ -0,0 +1,27 @@ +# Extension census 2026-11-03 + +Ranked census of file extensions across the troupe worktree. + +- **Source:** `repo_files.tsv` (rankpath inventory, 3345 file rows). +- **Artifact:** `extension-census.csv` (rank, extension, file_count) — 31 rows. +- **Interactive view:** `extension-census.md` (ranked table + totals). +- **Independent verifier:** `verify_extension_census.py` — re-derives from + `repo_files.tsv` with a standalone loop and asserts byte-match; exit 0. +- **CHECK file:** `../CHECK-extension-census-2026-11-03`. + +Ranked top of the census (full list in `extension-census.csv`): + +| rank | extension | file_count | +|---|---|---| +| 1 | .md | 829 | +| 2 | .png | 803 | +| 3 | .py | 653 | +| 4 | .txt | 551 | +| 5 | .json | 231 | +| 6 | .sh | 56 | +| 7 | .html | 55 | +| 8 | .csv | 43 | +| 9 | .svg | 31 | +| 10 | .age | 16 | + +**Totals:** 3345 files, 31 unique extensions. diff --git a/wizard-work/ledger/extension-census.csv b/wizard-work/ledger/extension-census.csv new file mode 100644 index 0000000..90aa517 --- /dev/null +++ b/wizard-work/ledger/extension-census.csv @@ -0,0 +1,32 @@ +rank,extension,file_count +1,.md,829 +2,.png,803 +3,.py,653 +4,.txt,551 +5,.json,231 +6,.sh,56 +7,.html,55 +8,.csv,43 +9,.svg,31 +10,.age,16 +11,.tsv,15 +12,.xml,12 +13,(none),11 +14,.gitignore,5 +15,.log,5 +16,.pdf,4 +17,.sha256,4 +18,.jsonl,3 +19,.mp3,3 +20,.c,2 +21,.diff,2 +22,.jpg,2 +23,.check,1 +24,.dl,1 +25,.goyiwz,1 +26,.gz,1 +27,.mailmap,1 +28,.md5,1 +29,".py""""",1 +30,.rc,1 +31,.yaml,1 diff --git a/wizard-work/ledger/extension-census.manifest.json b/wizard-work/ledger/extension-census.manifest.json new file mode 100644 index 0000000..fce338d --- /dev/null +++ b/wizard-work/ledger/extension-census.manifest.json @@ -0,0 +1,10 @@ +{ + "source": "repo_files.tsv", + "rows": 31, + "total_files": 3345, + "unique_extensions": 31, + "extractor": "extract_extension_census.py", + "verifier": "verify_extension_census.py", + "check": "CHECK-extension-census-2026-11-03", + "created_utc": "2026-11-03T00:00:00Z" +} diff --git a/wizard-work/ledger/extension-census.md b/wizard-work/ledger/extension-census.md new file mode 100644 index 0000000..f61a9d0 --- /dev/null +++ b/wizard-work/ledger/extension-census.md @@ -0,0 +1,41 @@ +# Extension census + +Ranked census of file extensions across the troupe worktree (re-derived from `repo_files.tsv` by `extract_extension_census.py`). + +| rank | extension | file_count | +|---|---|---| +| 1 | .md | 829 | +| 2 | .png | 803 | +| 3 | .py | 653 | +| 4 | .txt | 551 | +| 5 | .json | 231 | +| 6 | .sh | 56 | +| 7 | .html | 55 | +| 8 | .csv | 43 | +| 9 | .svg | 31 | +| 10 | .age | 16 | +| 11 | .tsv | 15 | +| 12 | .xml | 12 | +| 13 | (none) | 11 | +| 14 | .gitignore | 5 | +| 15 | .log | 5 | +| 16 | .pdf | 4 | +| 17 | .sha256 | 4 | +| 18 | .jsonl | 3 | +| 19 | .mp3 | 3 | +| 20 | .c | 2 | +| 21 | .diff | 2 | +| 22 | .jpg | 2 | +| 23 | .check | 1 | +| 24 | .dl | 1 | +| 25 | .goyiwz | 1 | +| 26 | .gz | 1 | +| 27 | .mailmap | 1 | +| 28 | .md5 | 1 | +| 29 | .py"" | 1 | +| 30 | .rc | 1 | +| 31 | .yaml | 1 | + +**Total files: 3345** +**Unique extensions: 31** + diff --git a/wizard-work/ledger/extract_extension_census.py b/wizard-work/ledger/extract_extension_census.py new file mode 100755 index 0000000..eebce66 --- /dev/null +++ b/wizard-work/ledger/extract_extension_census.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Extract a ranked file-extension census from wizard-work/ledger/repo_files.tsv. + +Source: repo_files.tsv is a (rank, path) inventory of the troupe worktree: + rankpath + +Some path cells were written through a Python repr/json serializer and carry +escaped quotes/newlines; we de-escape, take the basename, and count +extensions (lowercased, leading dot on the suffix unless there is none, +which is emitted as the literal "(none)"). + +Output (extension-census.csv): + rank,extension,file_count + 1,.md,829 + ... + +and extension-census.md (ranked + totals row). Run: + python3 extract_extension_census.py +""" +import csv +import os +import sys +from collections import Counter + +REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +SRC = os.path.join(REPO, "repo_files.tsv") +OUT_CSV = os.path.join(REPO, "wizard-work", "ledger", "extension-census.csv") +OUT_MD = os.path.join(REPO, "wizard-work", "ledger", "extension-census.md") + + +def clean_path(raw: str) -> str: + s = raw.strip() + # De-quote a cell that was wrapped in double quotes (Python repr / json). + if len(s) >= 2 and s[0] == '"' and s[-1] == '"': + s = s[1:-1] + # De-escape backslash escapes: \\ -> \, \" -> ", \n -> newline, \t -> tab. + s = s.encode("latin-1").decode("unicode_escape") + return s + + +def ext_of(path: str) -> str: + base = os.path.basename(path.rstrip("/")) + if not base or base in (".", ".."): + return "(none)" + if "." in base: + return "." + base.rsplit(".", 1)[1].lower() + return "(none)" + + +def main() -> int: + counts: Counter = Counter() + total_rows = 0 + with open(SRC, newline="", encoding="utf-8") as f: + for line in f: + line = line.rstrip("\n").rstrip("\r") + if not line.strip(): + continue + if "\t" not in line: + continue + rank, path = line.split("\t", 1) + path = clean_path(path) + total_rows += 1 + ext = ext_of(path) + counts[ext] += 1 + # order: by count desc, then extension asc + ordered = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])) + with open(OUT_CSV, "w", newline="", encoding="utf-8") as f: + w = csv.writer(f) + w.writerow(["rank", "extension", "file_count"]) + for i, (ext, n) in enumerate(ordered, 1): + w.writerow([i, ext, n]) + total_files = sum(counts.values()) + md = [] + md.append("# Extension census") + md.append("") + md.append("Ranked census of file extensions across the troupe worktree " + "(re-derived from `repo_files.tsv` by `extract_extension_census.py`).") + md.append("") + md.append("| rank | extension | file_count |") + md.append("|---|---|---|") + for i, (ext, n) in enumerate(ordered, 1): + md.append("| %d | %s | %d |" % (i, ext, n)) + md.append("") + md.append("**Total files: %d**" % total_files) + md.append("**Unique extensions: %d**" % len(ordered)) + md.append("") + with open(OUT_MD, "w", encoding="utf-8") as f: + f.write("\n".join(md) + "\n") + print("rows=%d total_files=%d extensions=%d" + % (total_rows, total_files, len(ordered))) + print("top10=%s" % ordered[:10]) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/wizard-work/ledger/verify_extension_census.py b/wizard-work/ledger/verify_extension_census.py new file mode 100755 index 0000000..c51b069 --- /dev/null +++ b/wizard-work/ledger/verify_extension_census.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Independent verifier for the extension census. + +Re-derives the census from the SOURCE file `repo_files.tsv` (rankpath, +CRLF) with a standalone loop -- it never imports extract_extension_census -- +normalizes CRLF line endings, and asserts the resulting ranked (extension, +count) table byte-matches `extension-census.csv` produced by that script. + +Exit code 0 = artifact matches re-derivation; 1 = mismatch; 2 = misuse. +""" +import csv +import os +import sys + +REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +SRC = os.path.join(REPO, "repo_files.tsv") +ART = os.path.join(REPO, "wizard-work", "ledger", "extension-census.csv") + +PASS = 0 + + +def clean_path(raw): + s = raw.strip() + if len(s) >= 2 and s[0] == '"' and s[-1] == '"': + s = s[1:-1] + return s.encode("latin-1", "replace").decode("unicode_escape", "replace") + + +def ext_of(path): + base = os.path.basename(path.rstrip("/")) + if not base or base in (".", ".."): + return "(none)" + if "." in base: + return "." + base.rsplit(".", 1)[1].lower() + return "(none)" + + +def re_derivation(): + counts = {} + total = 0 + with open(SRC, "r", encoding="utf-8", errors="replace") as f: + for line in f: + line = line.rstrip("\n").rstrip("\r") + if "\t" not in line: + continue + path = clean_path(line.split("\t", 1)[1]) + total += 1 + ext = ext_of(path) + counts[ext] = counts.get(ext, 0) + 1 + return sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])) + + +def read_artifact(): + rows = [] + with open(ART, newline="", encoding="utf-8") as f: + for row in csv.reader(f): + rows.append(row) + return rows[0], rows[1:] + + +def main(): + global PASS + header, rows = read_artifact() + if header != ["rank", "extension", "file_count"]: + print("FAIL: bad header %r" % (header,)) + return 1 + expected = re_derivation() + if len(rows) != len(expected): + print("FAIL: row count artifact=%d rederivation=%d" + % (len(rows), len(expected))) + return 1 + for i, ((ext, cnt), row) in enumerate(zip(expected, rows), 1): + if row != [str(i), ext, str(cnt)]: + print("FAIL: row %d artifact=%r expected=%r" + % (i, row, [str(i), ext, str(cnt)])) + return 1 + print("OK: %d rows byte-match re-derivation from repo_files.tsv; header=%r" + % (len(rows), header)) + return 0 + + +if __name__ == "__main__": + sys.exit(main())