diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/CHECK b/wizard-work/ledger/2026-11-04-daily-commit-census/CHECK new file mode 100755 index 0000000..3ebf355 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/CHECK @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# CHECK -- daily commit census (Extract lane). Exit 0 iff the committed +# daily-commit-census.csv is a byte-identical re-derivation from git at the rev +# the committed extraction_manifest.json pins. Independent of the extractor -- +# pure bash + git + coreutils (sort/uniq/awk), no Python. +set -u + +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 +SCRIPT_DIR="$(pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +OUT="$REPO_ROOT/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.csv" +MANIFEST="$REPO_ROOT/wizard-work/ledger/2026-11-04-daily-commit-census/extraction_manifest.json" + +test -s "$OUT" || { echo "FAIL: CSV missing or empty: $OUT" >&2; exit 1; } +test -s "$MANIFEST" || { echo "FAIL: manifest missing: $MANIFEST" >&2; exit 1; } + +HEAD_FULL="$(sed -n 's/.*"head_full": "\([^"]*\)".*/\1/p' "$MANIFEST")" +test -n "$HEAD_FULL" || { echo "FAIL: no head_full in manifest" >&2; exit 1; } + +tmp="$(mktemp)" +trap 'rm -f "$tmp"' EXIT + +{ + echo "date,commits" + git -C "$REPO_ROOT" log --date=short --pretty=format:%ad "$HEAD_FULL" | sort | uniq -c | awk '{print $2 "," $1}' | sort + sum="$(git -C "$REPO_ROOT" log --date=short --pretty=format:%ad "$HEAD_FULL" | sort | uniq -c | awk '{s += $1} END {print s}')" + echo "TOTAL,$sum" +} > "$tmp" + +if cmp -s "$OUT" "$tmp"; then + echo "PASS: daily-commit-census.csv re-derives from git at pinned head $HEAD_FULL" + exit 0 +fi + +echo "FAIL: daily-commit-census.csv differs from re-derivation at pinned head $HEAD_FULL" >&2 +diff -u "$OUT" "$tmp" >&2 || true +exit 1 diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/README.md b/wizard-work/ledger/2026-11-04-daily-commit-census/README.md new file mode 100644 index 0000000..1a78ee4 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/README.md @@ -0,0 +1,36 @@ +# Ledger: daily commit census (2026-11-04) + +Lane: **Extract — data extraction and tables**. + +A machine-readable census of every commit on the troupe repository's `main` +branch, grouped by ISO calendar date. + +## Source and method + +- Source: `git log --date=short --pretty=format:%ad` on `/root/troupe-standup` + (branch `main`). +- Each commit contributes its ISO date `%ad`. Commits are then grouped by date + and counted; rows sorted ascending by date. +- Output gate: `daily-commit-census.csv` (41 day rows + 1 TOTAL row) is re-derived + from git at the pinned rev and diffed byte-identically by `verify_daily_commit_census.py` + and the standalone `CHECK` script. + +## Metrics (measured, not asserted) + +- Day rows: 41 +- Commit total: 2612 +- Pinned head: `4a4723c69b90799a833e6efba936972fc5659d58` (`git rev-parse HEAD` + at capture, 2026-11-04) +- Earliest date: 2026-09-24 +- Latest date: 2026-11-04 +- Busiest day: 2026-10-23 (171 commits) +- Quietest day (non-zero): 2026-11-04 (2 commits) + +## Files + +- `daily-commit-census.csv` — `date,commits` (one row per date, plus a `TOTAL` row) +- `extraction_manifest.json` — provenance: repo, pinned head, date range, counts +- `extract_daily_commit_census.py` — extractor (regenerates the CSV) +- `verify_daily_commit_census.py` — self-check gate (re-derives from pinned head) +- `CHECK` — independent bash re-derivation gate (no Python) +- `daily-commit-census.md` — top summary table for humans diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.csv b/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.csv new file mode 100644 index 0000000..58ff419 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.csv @@ -0,0 +1,43 @@ +date,commits +2026-09-24,31 +2026-09-25,4 +2026-09-26,24 +2026-09-27,5 +2026-09-28,11 +2026-09-30,6 +2026-10-01,17 +2026-10-02,3 +2026-10-03,21 +2026-10-04,13 +2026-10-05,14 +2026-10-06,40 +2026-10-07,25 +2026-10-08,21 +2026-10-09,8 +2026-10-10,44 +2026-10-11,63 +2026-10-12,106 +2026-10-13,90 +2026-10-14,90 +2026-10-15,46 +2026-10-16,26 +2026-10-17,41 +2026-10-18,42 +2026-10-19,50 +2026-10-20,92 +2026-10-21,123 +2026-10-22,115 +2026-10-23,171 +2026-10-24,167 +2026-10-25,153 +2026-10-26,141 +2026-10-27,147 +2026-10-28,154 +2026-10-29,84 +2026-10-30,107 +2026-10-31,98 +2026-11-01,80 +2026-11-02,80 +2026-11-03,57 +2026-11-04,2 +TOTAL,2612 diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.md b/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.md new file mode 100644 index 0000000..e65ec68 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/daily-commit-census.md @@ -0,0 +1,46 @@ +# Daily commit census — troupe repo `main` (2026-11-04) + +| date | commits | +|---|---| +| 2026-09-24 | 31 | +| 2026-09-25 | 4 | +| 2026-09-26 | 24 | +| 2026-09-27 | 5 | +| 2026-09-28 | 11 | +| 2026-09-30 | 6 | +| 2026-10-01 | 17 | +| 2026-10-02 | 3 | +| 2026-10-03 | 21 | +| 2026-10-04 | 13 | +| 2026-10-05 | 14 | +| 2026-10-06 | 40 | +| 2026-10-07 | 25 | +| 2026-10-08 | 21 | +| 2026-10-09 | 8 | +| 2026-10-10 | 44 | +| 2026-10-11 | 63 | +| 2026-10-12 | 106 | +| 2026-10-13 | 90 | +| 2026-10-14 | 90 | +| 2026-10-15 | 46 | +| 2026-10-16 | 26 | +| 2026-10-17 | 41 | +| 2026-10-18 | 42 | +| 2026-10-19 | 50 | +| 2026-10-20 | 92 | +| 2026-10-21 | 123 | +| 2026-10-22 | 115 | +| 2026-10-23 | 171 | +| 2026-10-24 | 167 | +| 2026-10-25 | 153 | +| 2026-10-26 | 141 | +| 2026-10-27 | 147 | +| 2026-10-28 | 154 | +| 2026-10-29 | 84 | +| 2026-10-30 | 107 | +| 2026-10-31 | 98 | +| 2026-11-01 | 80 | +| 2026-11-02 | 80 | +| 2026-11-03 | 57 | +| 2026-11-04 | 2 | +| **TOTAL** | **2612** | diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/extract_daily_commit_census.py b/wizard-work/ledger/2026-11-04-daily-commit-census/extract_daily_commit_census.py new file mode 100644 index 0000000..3b86a4a --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/extract_daily_commit_census.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Extract the daily commit census of the troupe repository. + +Lane: Extract - data extraction and tables. + +Source: live `git log --date=short --pretty=format:%ad` on the `main` branch of +/root/troupe-standup. Commits are grouped by ISO calendar date and counted. +Rows are sorted by date ascending. The pinned head (full + short) is recorded in +extraction_manifest.json so the extraction is reproducible at a fixed rev. + +Output (written to ): + daily-commit-census.csv - header, one row per date + a TOTAL row (41+1 lines) + extraction_manifest.json - provenance: repo, head, date range, row count, sums + +Usage: python3 extract_daily_commit_census.py +""" + +import json +import os +import subprocess +import sys +from collections import Counter + +REPO = "/root/troupe-standup" +DATE_FMT = "%Y-%m-%d" + + +def git(*args): + return subprocess.run( + ["git", "-C", REPO] + list(args), + check=True, + capture_output=True, + text=True, + ).stdout + + +def build_rows(): + dates = git("log", "--date=short", "--pretty=format:%ad").splitlines() + counts = Counter(d for d in dates if d) + return [{"date": d, "commits": c} for d, c in sorted(counts.items())] + + +def render_csv(rows): + lines = ["date,commits"] + for r in rows: + lines.append("{date},{commits}".format(**r)) + total = sum(r["commits"] for r in rows) + lines.append("TOTAL,{total}".format(total=total)) + return "\n".join(lines) + "\n" + + +def render_manifest(head_full, head_short, rows): + dates = [r["date"] for r in rows] + return { + "repo": REPO, + "branch": "main", + "source_command": "git log --date=short --pretty=format:%ad", + "head_full": head_full, + "head_short": head_short, + "earliest": dates[0], + "latest": dates[-1], + "day_rows": len(rows), + "total_commits": sum(r["commits"] for r in rows), + "method": "counts ISO calendar date of every commit on main", + } + + +def main(): + out_dir = sys.argv[1] if len(sys.argv) > 1 else "." + os.makedirs(out_dir, exist_ok=True) + + head_full = git("rev-parse", "HEAD").strip() + head_short = git("rev-parse", "--short", "HEAD").strip() + rows = build_rows() + + csv_path = os.path.join(out_dir, "daily-commit-census.csv") + with open(csv_path, "w", encoding="utf-8") as f: + f.write(render_csv(rows)) + + manifest = render_manifest(head_full, head_short, rows) + with open(os.path.join(out_dir, "extraction_manifest.json"), "w", encoding="utf-8") as f: + json.dump(manifest, f, indent=2) + f.write("\n") + + print("rows:", manifest["day_rows"], "total:", manifest["total_commits"], + "head:", head_short, "earliest:", manifest["earliest"], "latest:", manifest["latest"]) + print("wrote:", csv_path) + + +if __name__ == "__main__": + main() diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/extraction_manifest.json b/wizard-work/ledger/2026-11-04-daily-commit-census/extraction_manifest.json new file mode 100644 index 0000000..f70bf23 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/extraction_manifest.json @@ -0,0 +1,12 @@ +{ + "repo": "/root/troupe-standup", + "branch": "main", + "source_command": "git log --date=short --pretty=format:%ad", + "head_full": "4a4723c69b90799a833e6efba936972fc5659d58", + "head_short": "4a4723c", + "earliest": "2026-09-24", + "latest": "2026-11-04", + "day_rows": 41, + "total_commits": 2612, + "method": "counts ISO calendar date of every commit on main" +} diff --git a/wizard-work/ledger/2026-11-04-daily-commit-census/verify_daily_commit_census.py b/wizard-work/ledger/2026-11-04-daily-commit-census/verify_daily_commit_census.py new file mode 100644 index 0000000..d19b3b6 --- /dev/null +++ b/wizard-work/ledger/2026-11-04-daily-commit-census/verify_daily_commit_census.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""CHECK gate for the daily-commit-census extraction. + +Re-derives the daily commit census from git at the PINNED head recorded in the +committed extraction_manifest.json, re-renders the CSV with the identical +format the extractor uses, and diffs it against the committed file. Exits 0 only +when every check matches exactly. This is a re-derivation, not an assertion of a +stored number. + +Usage: python3 verify_daily_commit_census.py +""" + +import difflib +import json +import os +import subprocess +import sys + +REPO = "/root/troupe-standup" +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +from extract_daily_commit_census import render_csv, build_rows # noqa: E402 + +CSV_NAME = "daily-commit-census.csv" + + +def git(*args): + return subprocess.run( + ["git", "-C", REPO] + list(args), check=True, capture_output=True, text=True + ).stdout + + +def read_bytes(name): + with open(name, "rb") as f: + return f.read() + + +def main(): + artdir = sys.argv[1] if len(sys.argv) > 1 else "." + os.chdir(artdir) + + with open("extraction_manifest.json", encoding="utf-8") as f: + manifest = json.load(f) + head = manifest.get("head_full") + if not head: + print("FAIL: manifest has no head_full") + sys.exit(1) + + # Re-derive from the pinned head. + raw = git("log", "--date=short", "--pretty=format:%ad", head).splitlines() + from collections import Counter + + counts = Counter(d for d in raw if d) + rows = [{"date": d, "commits": c} for d, c in sorted(counts.items())] + expected = render_csv(rows) + + ok = True + + with open(CSV_NAME, encoding="utf-8") as f: + committed = f.read() + + if committed == expected: + print("PASS: daily-commit-census.csv re-derives byte-identically from git " + "at pinned head %s" % head) + else: + ok = False + print("FAIL: daily-commit-census.csv diverges from re-derivation at " + "pinned head %s" % head) + exp = expected.splitlines() + obs = committed.splitlines() + for line in list(difflib.unified_diff(exp, obs, "expected@", "committed@"))[:12]: + print(" " + line) + + # Independent integrity: the TOTAL row must equal the sum of the day rows. + day_sum = sum(int(r["commits"]) for r in rows) + total_lines = [ln for ln in committed.splitlines() if ln.startswith("TOTAL,")] + if total_lines == ["TOTAL,%d" % day_sum]: + print("PASS: TOTAL row %r == sum of day rows %d" % (total_lines[0], day_sum)) + else: + ok = False + print("FAIL: TOTAL row %r != sum of day rows %d" % (total_lines, day_sum)) + + if ok: + print("ALL CHECKS PASSED") + sys.exit(0) + sys.exit(1) + + +if __name__ == "__main__": + main()