ledger: pin the 2026-10-27 check-in census to a revision and grade the TSV against it (Q88, 1/2)

The artifact counted `checkins/` as the directory stood, so it could not be
re-derived and its published numbers were the working tree's, not a commit's:
`e0ee5cb` carries 96 dated files / 33 dates / a `sentinel` row, and the tree of
that same commit holds 95 dated files, 33 dates and no sentinel check-in
(measured: `git ls-tree -r e0ee5cb -- checkins` = 95 dated + 1 README; per-date
2026-10-27 = 2, artifact said 3).

- `2026-10-27-checkin-census.files` is the pin: one header line naming the commit
  and its `checkins/` tree object, then the sorted dated names.
- `CHECK-2026-10-27-checkin-census` is rewritten: leg 1 refuses to grade when the
  pin is absent, malformed, undated, duplicated or unsorted (SCOPE NOT
  ESTABLISHED, rc 1); leg 2 proves the name list equals `git ls-tree` of the
  pinned tree, and says SKIP when the object is not in the clone; leg 3 derives
  wizard<TAB>count from the names in awk and byte-compares the TSV. It never
  reads today's directory and never runs the extractor.
- The extractor gains `--pin REV` (reads git) / `--manifest` / `--outdir`, so the
  tables are regenerated from the pin, not from `os.scandir(live)`.
- TSV and MD regenerated from the pin: 95 files, 33 dates, no `sentinel`; the MD
  provenance names the pin and records the correction.

Probes (`q88-probe.log`): baseline rc 0; an extra check-in file in the tree rc 0
(the old time bomb); TSV edited rc 1; pin deleted rc 1; pin pointed at another
commit rc 1; a name outside the pin rc 1; list reversed rc 1; header truncated
rc 1; `python3` stubbed to exit 99 rc 0 (the grader is awk/sort/cmp); outside a
git repo rc 0 with the pin leg SKIP.
This commit is contained in:
volt committed 2026-11-01 19:22:49 +00:00
1 parent ae183bb12a
commit f22824ab48
5 files changed
+353 -104

No files matched your search

@@ -0,0 +1,96 @@
# pin checkins@e0ee5cbd4894515930c4045607b26382c08862b9 tree=bb08e9a56d92a2ba81aad0dd6774c641f1e9c7d1
2026-09-24-zephyrus-02.md
2026-09-24-zephyrus.md
2026-09-25-zephyrus.md
2026-09-26-zephyrus-02.md
2026-09-26-zephyrus.md
2026-09-27-zephyrus.md
2026-09-28-zephyrus-02.md
2026-09-28-zephyrus.md
2026-09-30-zephyrus.md
2026-10-01-zephyrus-03.md
2026-10-01-zephyrus-04.md
2026-10-01-zephyrus-05.md
2026-10-01-zephyrus-06.md
2026-10-01-zephyrus-07.md
2026-10-01-zephyrus.md
2026-10-02-zephyrus-01.md
2026-10-03-zephyrus-02.md
2026-10-03-zephyrus-03.md
2026-10-03-zephyrus.md
2026-10-04-zephyrus-02.md
2026-10-04-zephyrus.md
2026-10-05-zephyrus-02.md
2026-10-05-zephyrus-03.md
2026-10-05-zephyrus.md
2026-10-06-cassian.md
2026-10-06-zephyrus-02.md
2026-10-06-zephyrus-04.md
2026-10-06-zephyrus-05.md
2026-10-06-zephyrus-06.md
2026-10-06-zephyrus-wizards-02.md
2026-10-06-zephyrus-wizards-03.md
2026-10-06-zephyrus-wizards.md
2026-10-06-zephyrus.md
2026-10-07-cassian-02.md
2026-10-07-cassian-q5-census.md
2026-10-07-cassian-q5-recount.md
2026-10-07-cassian.md
2026-10-07-zephyrus-02.md
2026-10-07-zephyrus.md
2026-10-08-merlin.md
2026-10-08-zephyrus.md
2026-10-09-merlin.md
2026-10-09-zephyrus.md
2026-10-10-mercury.md
2026-10-10-merlin.md
2026-10-10-zephyrus.md
2026-10-11-mercury.md
2026-10-11-merlin.md
2026-10-11-zephyrus.md
2026-10-12-mercury.md
2026-10-12-merlin.md
2026-10-12-zephyrus.md
2026-10-13-gemini.md
2026-10-13-mercury.md
2026-10-13-merlin.md
2026-10-13-zephyrus.md
2026-10-14-mercury.md
2026-10-14-merlin.md
2026-10-14-zephyrus.md
2026-10-15-mercury.md
2026-10-15-merlin.md
2026-10-15-volt.md
2026-10-15-zephyrus.md
2026-10-16-mercury.md
2026-10-16-volt.md
2026-10-16-zephyrus.md
2026-10-17-mercury.md
2026-10-17-volt.md
2026-10-17-zephyrus.md
2026-10-18-mercury.md
2026-10-18-volt.md
2026-10-18-zephyrus.md
2026-10-19-mercury.md
2026-10-19-volt.md
2026-10-19-zephyrus.md
2026-10-20-mercury.md
2026-10-20-volt.md
2026-10-20-zephyrus.md
2026-10-21-mercury.md
2026-10-21-volt.md
2026-10-21-zephyrus.md
2026-10-22-mercury.md
2026-10-22-zephyrus.md
2026-10-23-mercury.md
2026-10-23-volt.md
2026-10-23-zephyrus.md
2026-10-24-mercury.md
2026-10-24-zephyrus.md
2026-10-25-mercury.md
2026-10-25-volt.md
2026-10-25-zephyrus.md
2026-10-26-mercury.md
2026-10-26-zephyrus.md
2026-10-27-mercury.md
2026-10-27-zephyrus.md
@@ -1,7 +1,8 @@
# Check-in census # Check-in census
Extract lane table: per-wizard tally of `checkins/*.md` files (README.md Extract lane table: per-wizard tally of `checkins/*.md` at a pinned
excluded). The latest day present is 2026-10-27 (three files filed). revision (README.md and any other undated file excluded). The latest day
at the pin is 2026-10-27 (2 files).
| wizard | checkin_files | | wizard | checkin_files |
|---|---| |---|---|
@@ -14,9 +15,8 @@ excluded). The latest day present is 2026-10-27 (three files filed).
| cassian-q5-census | 1 | | cassian-q5-census | 1 |
| cassian-q5-recount | 1 | | cassian-q5-recount | 1 |
| gemini | 1 | | gemini | 1 |
| sentinel | 1 |
**Total files:** 96 across 33 dates (README.md excluded). **Total files:** 95 across 33 dates (undated files excluded).
## Per-date appendix ## Per-date appendix
@@ -54,12 +54,12 @@ excluded). The latest day present is 2026-10-27 (three files filed).
| 2026-10-24 | 2 | | 2026-10-24 | 2 |
| 2026-10-25 | 3 | | 2026-10-25 | 3 |
| 2026-10-26 | 2 | | 2026-10-26 | 2 |
| 2026-10-27 | 3 | | 2026-10-27 | 2 |
## Provenance ## Provenance
- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 06:00Z; 96 date-prefixed files, 33 distinct dates). - Pin: `checkins/` at commit `e0ee5cbd4894515930c4045607b26382c08862b9`, tree `bb08e9a56d92a2ba81aad0dd6774c641f1e9c7d1` (`2026-10-27-checkin-census.files` beside this file holds the sorted name list).
- Files excluded (no leading date): 1 (README). - Date-prefixed files at the pin: 95; distinct dates: 33; files excluded: 0 (none).
- `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped. - `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped.
- Date-prefixed files: 96; files excluded: 1. - Correction: the `e0ee5cb` revision of this artifact counted the live directory and published 96 files / 33 dates; no revision of `checkins/` holds that `sentinel` file, and the numbers here are recomputed from the pin.
- Check: `CHECK` recomputes this table from `checkins/` and diffs against the TSV. - Check: `CHECK-2026-10-27-checkin-census` recomputes the table above from the pinned name list and diffs it; it never reads today's directory.
@@ -8,4 +8,3 @@ zephyrus-wizards 3
cassian-q5-census 1 cassian-q5-census 1
cassian-q5-recount 1 cassian-q5-recount 1
gemini 1 gemini 1
sentinel 1
1 wizard checkin_files
8 cassian-q5-census 1
9 cassian-q5-recount 1
10 gemini 1
sentinel 1
@@ -1,45 +1,114 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# CHECK — verifier for the check-in census artifact (Extract lane: data extraction). # CHECK-2026-10-27-checkin-census — verifier for the 2026-10-27 check-in census.
# #
# Exit 0 iff: the committed TSV (wizard-work/ledger/2026-10-27-checkin-census.tsv) # Verdict: exit 0 iff the committed TSV equals a re-derivation of the census from
# is byte-identical to an independent recompute of the census straight from the # the PINNED name list beside it, and the pin itself is a real revision of
# `checkins/*.md` source directory. # `checkins/`. Exit 1 on any fault, including "the input scope cannot be
# established" — the check refuses to grade rather than falling back to grading
# today's directory.
# #
# The recompute uses only ls/grep/awk/sort/uniq/date/cmp (no Python, no shared # WHAT CHANGED AND WHY (Q88). The previous revision recomputed the row set from
# extractor logic), so a hand-edited or stale TSV cannot pass by accident. # `checkins/*.md` as the directory stood when the CHECK ran, so the dated artifact
# went red on the next check-in for no reason — and it graded a `date,count`
# schema the artifact had stopped publishing at `e0ee5cb`, so it was red on every
# run. The input is now the committed `2026-10-27-checkin-census.files`.
# #
# Header is emitted first by construction; data rows (dates with files) come # The recompute is awk/sort/cmp over the name list — it does not import, read or
# next, sorted ascending; today's row (0 when no check-in file has been filed # re-run `extract_checkin_census_2026-10-27.py`, so the grader and the generator
# yet) is the final row. All are produced by independent shell tools. # cannot agree by sharing a bug.
# #
# Repo root is derived from this file's directory, so the CHECK runs from any # `set -u`, no `set -e`: each leg reports and the script decides, so a green
# working directory and does not depend on a specific clone path. # verdict always means every leg ran.
set -u set -u
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd ../.. && pwd)" REPO="$(git -C "$HERE" rev-parse --show-toplevel 2>/dev/null)" \
SRC="$REPO_ROOT/checkins" || REPO="$(cd "$HERE/../.." && pwd)"
OUT="$REPO_ROOT/wizard-work/ledger/2026-10-27-checkin-census.tsv" FILES="$HERE/2026-10-27-checkin-census.files"
TSV="$HERE/2026-10-27-checkin-census.tsv"
MD="$HERE/2026-10-27-checkin-census.md"
TAB="$(printf '\t')"
test -s "$OUT" || { echo "FAIL: TSV missing or empty: $OUT" >&2; exit 1; } fail() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
scope_fail() { printf 'FAIL: SCOPE NOT ESTABLISHED: %s\n' "$*" >&2; exit 1; }
tmp="$(mktemp)" tmp="$(mktemp -d)"
trap 'rm -f "$tmp"' EXIT trap 'rm -rf "$tmp"' EXIT
{ # ---------------------------------------------------------------- leg 1: scope
echo "date,checkin_files" # The pin is the artifact's input. If it is absent, malformed, unsorted,
ls "$SRC"/*.md 2>/dev/null | grep -v 'README\.md$' \ # duplicated or undated there is nothing to recompute from, and the check says so
| awk -F/ '{n=split($0, a, "/"); name=a[n]; sub(/\.md$/, "", name); print substr(name, 1, 10)}' \ # instead of grading whatever the directory holds.
| sort | uniq -c | awk '{print $2 "," $1}' [ -s "$FILES" ] || scope_fail "pinned name list missing or empty: ${FILES#"$REPO"/}"
echo "$(date -u +%F),0"
} > "$tmp"
if cmp -s "$OUT" "$tmp"; then header="$(head -n 1 "$FILES")"
echo "PASS: rows=$(tail -n +2 "$OUT" | grep -c .) (dates with files + today=0) recomputed from $SRC" if ! [[ $header =~ ^#\ pin\ checkins@([0-9a-f]{40})\ tree=([0-9a-f]{40})$ ]]; then
exit 0 scope_fail "pin header not understood: '$header'"
fi
pin_commit="${BASH_REMATCH[1]}"
pin_tree="${BASH_REMATCH[2]}"
tail -n +2 "$FILES" > "$tmp/names"
[ -s "$tmp/names" ] || scope_fail "pin names no files"
undated="$(grep -cvE '^[0-9]{4}-[0-9]{2}-[0-9]{2}-.*\.md$' "$tmp/names" || true)"
[ "$undated" = "0" ] || scope_fail "$undated pinned name(s) are not YYYY-MM-DD-*.md"
dupes="$(sort "$tmp/names" | uniq -d | wc -l)"
[ "$dupes" = "0" ] || scope_fail "$dupes duplicated name(s) in the pin"
if ! LC_ALL=C sort "$tmp/names" | cmp -s - "$tmp/names"; then
scope_fail "pin names are not sorted (LC_ALL=C) — the list is not a stable input"
fi
echo "PASS scope: $(wc -l < "$tmp/names") pinned dated file(s), pin ${pin_commit:0:7}"
# --------------------------------------------------- leg 2: the pin is a revision
# Immutable history makes this leg free of false reds; a clone that does not carry
# the pin object says so, and the legs below still grade the name list.
if git -C "$REPO" cat-file -e "${pin_commit}^{commit}" 2>/dev/null; then
real_tree="$(git -C "$REPO" rev-parse "$pin_commit:checkins" 2>/dev/null)" \
|| fail "pin commit $pin_commit has no checkins/ tree"
[ "$real_tree" = "$pin_tree" ] \
|| fail "pin tree $pin_tree is not $pin_commit:checkins ($real_tree)"
git -C "$REPO" ls-tree -r --name-only "$pin_tree" \
| grep -E '(^|/)[0-9]{4}-[0-9]{2}-[0-9]{2}-.*\.md$' \
| sed 's#.*/##' | LC_ALL=C sort > "$tmp/git_names"
if ! cmp -s "$tmp/git_names" "$tmp/names"; then
printf 'FAIL: pin name list != checkins/ at %s\n' "${pin_commit:0:7}" >&2
diff -u "$tmp/git_names" "$tmp/names" >&2 || true
exit 1
fi
echo "PASS pin: name list == checkins/ tree ${pin_tree:0:7} at ${pin_commit:0:7}"
else
echo "SKIP pin: commit ${pin_commit:0:7} not in this object database —" \
"the committed name list is the pin"
fi fi
echo "FAIL: census mismatch against independent recompute" >&2 # ------------------------------------------- leg 3: derive the per-wizard table
diff -u "$OUT" "$tmp" >&2 || true awk '
{ n = $0
sub(/\.md$/, "", n)
sub(/^[0-9]{4}-[0-9]{2}-[0-9]{2}-/, "", n)
sub(/-[0-9]+$/, "", n)
c[n]++
}
END { for (w in c) printf "%s\t%d\n", w, c[w] }
' "$tmp/names" | LC_ALL=C sort -t "$TAB" -k2,2nr -k1,1 > "$tmp/tsv.body"
{ printf 'wizard%scheckin_files\n' "$TAB"; cat "$tmp/tsv.body"; } > "$tmp/tsv.expected"
[ -s "$TSV" ] || fail "TSV missing or empty: ${TSV#"$REPO"/}"
if ! cmp -s "$tmp/tsv.expected" "$TSV"; then
printf 'FAIL: TSV != recompute from the pin\n' >&2
diff -u "$TSV" "$tmp/tsv.expected" >&2 || true
exit 1 exit 1
fi
echo "PASS tsv: $(($(wc -l < "$TSV") - 1)) wizard row(s)," \
"$(awk -F"$TAB" 'NR>1{s+=$2} END{print s+0}' "$TSV") file(s)"
# -------------------------------------------------------------------- verdict
printf 'VERDICT=PASS census-2026-10-27 rows=%s files=%s pin=%s\n' \
"$(($(wc -l < "$TSV") - 1))" \
"$(awk -F"$TAB" 'NR>1{s+=$2} END{print s+0}' "$TSV")" "${pin_commit:0:7}"
exit 0
@@ -1,81 +1,118 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""extract_checkin_census_2026-10-27.py -- check-in census table. """extract_checkin_census_2026-10-27.py -- check-in census table, from a PIN.
Extract lane (data extraction + tables). Reads checkins/*.md in the troupe Extract lane (data extraction + tables). Counts `checkins/*.md` at a named
repo, groups files by their leading YYYY-MM-DD date, and emits a per-wizard revision, not on whatever the directory holds when the script runs, and emits:
tally table plus a full provenance record. The canonical table is one row per
wizard: `wizard<TAB>checkin_files`, sorted by count descending, name
ascending.
Artifact (written beside this script, under wizard-work/ledger/): * 2026-10-27-checkin-census.files the pin: one header line, then every dated
* 2026-10-27-checkin-census.tsv wizard<TAB>checkin_files table filename at that revision, sorted
* 2026-10-27-checkin-census.md human summary + per-date appendix * 2026-10-27-checkin-census.tsv wizard<TAB>checkin_files, count desc then name
Reads use paths relative to this file's directory (repo-root agnostic). * 2026-10-27-checkin-census.md the same table, a per-date appendix, provenance
Why a pin. The 2026-10-27 revision of this artifact was produced by walking the
live `checkins/` directory (`os.scandir`), so it counted a file that no commit
holds: it published 96 files / 33 dates and a `sentinel` row, while the tree of
the commit that carries it (`e0ee5cb`) holds 95 dated files and no sentinel
check-in. A dated artifact whose input is "the directory as it stands" cannot be
re-derived, and every re-run publishes a different day. The pin makes the input
a committed name list at a fixed revision (Q88).
Commands (each is exercised by `--help`):
python3 extract_checkin_census_2026-10-27.py --pin e0ee5cb
python3 extract_checkin_census_2026-10-27.py --pin e0ee5cb --write
python3 extract_checkin_census_2026-10-27.py --manifest 2026-10-27-checkin-census.files --write
python3 extract_checkin_census_2026-10-27.py --outdir /tmp/scratch --write
Without `--write` nothing is written: the derivation is printed so a reader can
compare it with the committed tables. Paths resolve from this file's directory
(`--repo` overrides the git tree read), so a clone grades its own tree.
""" """
import argparse
import collections import collections
import os import os
import re import re
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__)) HERE = os.path.dirname(os.path.abspath(__file__))
# Troupe repo root. Override with EXT_CENSUS_ROOT env var if set. DATED = re.compile(r"^(\d{4}-\d{2}-\d{2})-(.+)\.md$")
_ROOT = os.environ.get("EXT_CENSUS_ROOT") or ( SUFFIX = re.compile(r"-\d+$")
"/root/troupe-standup" HEADER = re.compile(r"^# pin checkins@([0-9a-f]{40}) tree=([0-9a-f]{40})$")
if os.path.isdir("/root/troupe-standup/checkins") MANIFEST_NAME = "2026-10-27-checkin-census.files"
else os.path.dirname(os.path.dirname(HERE))
)
SRC = os.path.join(_ROOT, "checkins")
OUT_TSV = os.path.join(HERE, "2026-10-27-checkin-census.tsv")
OUT_MD = os.path.join(HERE, "2026-10-27-checkin-census.md")
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}-") # leading date on the filename
SUFFIX_RE = re.compile(r"-\d+$") # trailing -NNNN -> strip
def extract(): def git(repo, *args):
"""Return (per_date Counter, per_wizard Counter, excluded list).""" out = subprocess.run(["git", "-C", repo] + list(args),
per_date = collections.Counter() capture_output=True, text=True)
per_wizard = collections.Counter() if out.returncode != 0:
excluded = [] sys.exit(f"git {' '.join(args)} failed: {out.stderr.strip()}")
with os.scandir(SRC) as it: return out.stdout
for entry in sorted(it, key=lambda e: e.name):
name = entry.name
def names_from_pin(repo, rev):
commit = git(repo, "rev-parse", "--verify", f"{rev}^{{commit}}").strip()
tree = git(repo, "rev-parse", f"{commit}:checkins").strip()
listing = git(repo, "ls-tree", "-r", "--name-only", tree)
names = [line.rsplit("/", 1)[-1] for line in listing.splitlines() if line.strip()]
return commit, tree, names
def names_from_manifest(path):
with open(path, encoding="utf-8") as fh:
lines = [line.rstrip("\n") for line in fh if line.strip()]
head = HEADER.match(lines[0]) if lines else None
if not head:
sys.exit(f"manifest header not understood: {path}")
return head.group(1), head.group(2), lines[1:]
def derive(names):
"""(per_date, per_wizard, excluded, dated) from filenames."""
per_date, per_wizard, excluded, dated = (collections.Counter(), collections.Counter(),
[], [])
for name in sorted(names):
if not name.endswith(".md"): if not name.endswith(".md"):
excluded.append(f"{name} (not .md)")
continue continue
if not DATE_RE.match(name): m = DATED.match(name)
if not m:
excluded.append(name[:-3]) excluded.append(name[:-3])
continue continue
date = name[:10] dated.append(name)
rest = name[11:-3] per_date[m.group(1)] += 1
wizard = SUFFIX_RE.sub("", rest) per_wizard[SUFFIX.sub("", m.group(2))] += 1
per_date[date] += 1 return per_date, per_wizard, excluded, dated
per_wizard[wizard] += 1
return per_date, per_wizard, excluded
def write_tables(per_date, per_wizard, excluded): def rows_of(per_wizard):
# Canonical table: one row per wizard, tab-separated. return sorted(per_wizard.items(), key=lambda kv: (-kv[1], kv[0]))
rows = sorted(per_wizard.items(), key=lambda kv: (-kv[1], kv[0]))
with open(OUT_TSV, "w", encoding="utf-8") as f:
f.write("wizard\tcheckin_files\n")
for w, n in rows:
f.write(f"{w}\t{n}\n")
total_files = sum(per_date.values())
total_dates = len(per_date) def render_tsv(per_wizard):
out = ["wizard\tcheckin_files"]
out += [f"{w}\t{n}" for w, n in rows_of(per_wizard)]
return "\n".join(out) + "\n"
def render_md(per_date, per_wizard, excluded, commit, tree, dated):
total_files, total_dates = len(dated), len(per_date)
latest = max(per_date)
lines = [ lines = [
"# Check-in census", "# Check-in census",
"", "",
"Extract lane table: per-wizard tally of `checkins/*.md` files (README.md", "Extract lane table: per-wizard tally of `checkins/*.md` at a pinned",
"excluded). The latest day present is 2026-10-27 (three files filed).", f"revision (README.md and any other undated file excluded). The latest day",
f"at the pin is {latest} ({per_date[latest]} files).",
"", "",
"| wizard | checkin_files |", "| wizard | checkin_files |",
"|---|---|", "|---|---|",
] ]
lines += [f"| {w} | {n} |" for w, n in rows] lines += [f"| {w} | {n} |" for w, n in rows_of(per_wizard)]
lines += [ lines += [
"", "",
f"**Total files:** {total_files} across {total_dates} dates " f"**Total files:** {total_files} across {total_dates} dates "
"(README.md excluded).", "(undated files excluded).",
"", "",
"## Per-date appendix", "## Per-date appendix",
"", "",
@@ -87,23 +124,71 @@ def write_tables(per_date, per_wizard, excluded):
"", "",
"## Provenance", "## Provenance",
"", "",
f"- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 06:00Z; " f"- Pin: `checkins/` at commit `{commit}`, tree `{tree}` "
f"{total_files} date-prefixed files, {total_dates} distinct dates).", f"(`{MANIFEST_NAME}` beside this file holds the sorted name list).",
f"- Files excluded (no leading date): {len(excluded)} " f"- Date-prefixed files at the pin: {total_files}; distinct dates: {total_dates}; "
f"({', '.join(excluded) if excluded else 'none'}).", f"files excluded: {len(excluded)}"
+ (f" ({', '.join(excluded)})." if excluded else " (none)."),
"- `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped.", "- `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped.",
f"- Date-prefixed files: {total_files}; files excluded: {len(excluded)}.", "- Correction: the `e0ee5cb` revision of this artifact counted the live "
"- Check: `CHECK` recomputes this table from `checkins/` and diffs against the TSV.", "directory and published 96 files / 33 dates; no revision of `checkins/` "
"holds that `sentinel` file, and the numbers here are recomputed from the pin.",
"- Check: `CHECK-2026-10-27-checkin-census` recomputes the table above "
"from the pinned name list and diffs it; it never reads today's directory.",
] ]
with open(OUT_MD, "w", encoding="utf-8") as f: return "\n".join(lines) + "\n"
f.write("\n".join(lines) + "\n")
print(f"dates_with_files={total_dates} total_files={total_files} excluded={len(excluded)}")
print(f"wizards={len(rows)}") def main(argv=None):
print(f"wrote {OUT_TSV}") ap = argparse.ArgumentParser(
print(f"wrote {OUT_MD}") description="Check-in census from a pinned revision of checkins/.")
ap.add_argument("--pin", metavar="REV",
help="pin to this revision's checkins/ tree (e.g. e0ee5cb)")
ap.add_argument("--manifest", metavar="PATH",
help=f"read the pin from this committed name list "
f"(default: ./{MANIFEST_NAME})")
ap.add_argument("--repo", default=os.path.dirname(os.path.dirname(HERE)),
help="repo to read git objects from (default: this file's repo)")
ap.add_argument("--outdir", default=HERE,
help="directory to write into (default: beside this script)")
ap.add_argument("--write", action="store_true",
help="write the manifest and both tables (default: print only)")
args = ap.parse_args(argv)
if args.pin:
commit, tree, names = names_from_pin(args.repo, args.pin)
else:
manifest = args.manifest or os.path.join(HERE, MANIFEST_NAME)
if not os.path.exists(manifest):
ap.error(f"no --pin given and no manifest at {manifest}")
commit, tree, names = names_from_manifest(manifest)
per_date, per_wizard, excluded, dated = derive(names)
tsv, md = render_tsv(per_wizard), render_md(per_date, per_wizard, excluded,
commit, tree, dated)
print(f"pin commit={commit} tree={tree}")
print(f"dated_files={len(dated)} dates={len(per_date)} "
f"wizards={len(per_wizard)} excluded={len(excluded)}")
for w, n in rows_of(per_wizard):
print(f" {w}\t{n}")
if not args.write:
print("dry run (pass --write to write the manifest and tables)")
return 0
os.makedirs(args.outdir, exist_ok=True)
manifest_path = os.path.join(args.outdir, MANIFEST_NAME)
with open(manifest_path, "w", encoding="utf-8") as fh:
fh.write(f"# pin checkins@{commit} tree={tree}\n")
fh.write("".join(f"{n}\n" for n in sorted(dated)))
for name, payload in (("2026-10-27-checkin-census.tsv", tsv),
("2026-10-27-checkin-census.md", md)):
with open(os.path.join(args.outdir, name), "w", encoding="utf-8") as fh:
fh.write(payload)
print(f"wrote {manifest_path}, 2026-10-27-checkin-census.tsv, "
f"2026-10-27-checkin-census.md")
return 0
if __name__ == "__main__": if __name__ == "__main__":
per_date, per_wizard, excluded = extract() sys.exit(main())
write_tables(per_date, per_wizard, excluded)