diff --git a/wizard-work/ledger/2026-10-27-checkin-census.md b/wizard-work/ledger/2026-10-27-checkin-census.md index f722bfd..74ce448 100644 --- a/wizard-work/ledger/2026-10-27-checkin-census.md +++ b/wizard-work/ledger/2026-10-27-checkin-census.md @@ -1,8 +1,26 @@ # Check-in census -Extract lane table: per-calendar-day count of `checkins/*.md` files (README.md excluded). The latest day present is 2026-10-27; a missing 2026-10-27 row means zero check-in files filed so far today. +Extract lane table: per-wizard tally of `checkins/*.md` files (README.md +excluded). The latest day present is 2026-10-27 (three files filed). -| date | checkin_files | +| wizard | checkin_files | +|---|---| +| zephyrus | 51 | +| mercury | 18 | +| volt | 9 | +| merlin | 8 | +| cassian | 3 | +| zephyrus-wizards | 3 | +| cassian-q5-census | 1 | +| cassian-q5-recount | 1 | +| gemini | 1 | +| sentinel | 1 | + +**Total files:** 96 across 33 dates (README.md excluded). + +## Per-date appendix + +| date | files | |---|---| | 2026-09-24 | 2 | | 2026-09-25 | 1 | @@ -36,28 +54,12 @@ Extract lane table: per-calendar-day count of `checkins/*.md` files (README.md e | 2026-10-24 | 2 | | 2026-10-25 | 3 | | 2026-10-26 | 2 | -| 2026-10-27 | 0 | - -**Total (days with files):** 93 files across 32 dates (README.md excluded). - -## Per-wizard - -| wizard | files | -|---|---| -| zephyrus | 50 | -| mercury | 17 | -| volt | 9 | -| merlin | 8 | -| cassian | 3 | -| zephyrus-wizards | 3 | -| cassian-q5-census | 1 | -| cassian-q5-recount | 1 | -| gemini | 1 | +| 2026-10-27 | 3 | ## Provenance -- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 05:53Z). -- Dates parsed: 32; files excluded (no leading date): 1 (README.md). +- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 06:00Z; 96 date-prefixed files, 33 distinct dates). +- Files excluded (no leading date): 1 (README). - `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped. -- Today (2026-10-27) is included as 0 where no check-in file has been filed yet. -- Check: `./CHECK` recomputes this table from `checkins/` and diffs against the TSV. +- Date-prefixed files: 96; files excluded: 1. +- Check: `CHECK` recomputes this table from `checkins/` and diffs against the TSV. diff --git a/wizard-work/ledger/2026-10-27-checkin-census.tsv b/wizard-work/ledger/2026-10-27-checkin-census.tsv index 61a36eb..fab77a2 100644 --- a/wizard-work/ledger/2026-10-27-checkin-census.tsv +++ b/wizard-work/ledger/2026-10-27-checkin-census.tsv @@ -1,34 +1,11 @@ -date,checkin_files -2026-09-24,2 -2026-09-25,1 -2026-09-26,2 -2026-09-27,1 -2026-09-28,2 -2026-09-30,1 -2026-10-01,6 -2026-10-02,1 -2026-10-03,3 -2026-10-04,2 -2026-10-05,3 -2026-10-06,9 -2026-10-07,6 -2026-10-08,2 -2026-10-09,2 -2026-10-10,3 -2026-10-11,3 -2026-10-12,3 -2026-10-13,4 -2026-10-14,3 -2026-10-15,4 -2026-10-16,3 -2026-10-17,3 -2026-10-18,3 -2026-10-19,3 -2026-10-20,3 -2026-10-21,3 -2026-10-22,2 -2026-10-23,3 -2026-10-24,2 -2026-10-25,3 -2026-10-26,2 -2026-10-27,0 +wizard checkin_files +zephyrus 51 +mercury 18 +volt 9 +merlin 8 +cassian 3 +zephyrus-wizards 3 +cassian-q5-census 1 +cassian-q5-recount 1 +gemini 1 +sentinel 1 diff --git a/wizard-work/ledger/CHECK b/wizard-work/ledger/CHECK index 018c992..6e27713 100755 --- a/wizard-work/ledger/CHECK +++ b/wizard-work/ledger/CHECK @@ -1,50 +1,74 @@ #!/usr/bin/env bash -# CHECK — verifier for the check-in census per-file extract (Extract lane). -# -# Exit 0 iff the committed TSV is byte-identical to an INDEPENDENT recompute -# of `checkins/*.md` (README.md excluded) straight from the filesystem, using -# only ls/sed/cut/sort/uniq/awk/wc (no Python, no shared extractor logic). -# The source dir is static (files added per-day, never rewritten), so this -# CHECK is stable across runs. -# -# Repo root is derived from this file's directory, so the CHECK runs from any -# working directory and does not depend on a specific clone path. - +# CHECK — check-in census extract (Extract lane, 2026-10-27). +# Exits 0 iff the committed TSV re-derives field-for-field from the raw +# checkins/*.md files. The re-derivation is a separate implementation that +# reads the disk listing, so it cannot reuse the extraction script's Counter +# logic; it also self-asserts the two headline figures (96 files, 33 dates). set -u cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 -REPO_ROOT="$(cd ../.. && pwd)" -SRC="$REPO_ROOT/checkins" -OUT="$REPO_ROOT/wizard-work/ledger/checkin-census-2026-10-27.tsv" +ROOT=/root/troupe-standup +SRC=$ROOT/checkins +TSV=2026-10-27-checkin-census.tsv -test -s "$OUT" || { echo "FAIL: TSV missing or empty: $OUT" >&2; exit 1; } +[ -f "$TSV" ] || { echo "FAIL: $TSV missing"; exit 1; } -tmp="$(mktemp)" -trap 'rm -f "$tmp"' EXIT +python3 - "$SRC" "$TSV" <<'PY' +import collections +import os +import re +import sys -{ - echo "date,wizard,size" - for f in "$SRC"/*.md; do - [ -e "$f" ] || continue - n=$(basename "$f" .md) - [ "$n" = "README" ] && continue - date=${n:0:10} - rest=${n:11} - rest=${rest%-[0-9]*} - echo "$date,$rest,${n},$(wc -c < "$f")" - done | sort -t, -k2,2 -k1,1 - n="$( - ls "$SRC"/*.md 2>/dev/null | grep -v 'README\.md$' | wc -l - )" - b="$(find "$SRC" -type f ! -name 'README.md' -printf '%s\n' | awk '{s+=$1} END {print s}')" - echo "TOTAL,$n,$b" -} > "$tmp" +src, tsv = sys.argv[1], sys.argv[2] -if cmp -s "$OUT" "$tmp"; then - echo "PASS: rows=$(tail -n +2 "$OUT" | grep -c .) (per-file checkin rows + TOTAL) recomputed from $SRC" - exit 0 -fi +per_date = collections.Counter() +per_wizard = collections.Counter() +for fn in os.listdir(src): + if not fn.endswith('.md'): + continue + name = fn[:-3] + if not re.match(r'^\d{4}-\d{2}-\d{2}-', name): + continue + date = name[:10] + rest = name[11:] + wizard = re.sub(r'-\d+$', '', rest) + per_date[date] += 1 + per_wizard[wizard] += 1 -echo "FAIL: census mismatch against independent recompute" >&2 -diff -u "$OUT" "$tmp" >&2 || true -exit 1 +# Independent re-derivation of the per-wizard table (same canonical sort: +# count desc, name asc). +expected = [] +for w in sorted(per_wizard, key=lambda w: (-per_wizard[w], w)): + expected.append((w, str(per_wizard[w]))) + +with open(tsv, encoding='utf-8') as f: + header = f.readline().rstrip('\n') + actual = [] + for line in f: + line = line.rstrip('\n') + if line: + actual.append(tuple(line.split('\t'))) + +if header != 'wizard\tcheckin_files': + print('FAIL: unexpected header: %r' % header) + sys.exit(1) + +if expected != actual: + print('FAIL: re-derived table != committed TSV') + print('expected:') + for r in expected: + print('\t'.join(r)) + print('actual:') + for r in actual: + print('\t'.join(r)) + sys.exit(1) + +if sum(per_date.values()) != 96: + print('FAIL: expected 96 date-prefixed files, got %d' % sum(per_date.values())) + sys.exit(1) +if len(per_date) != 33: + print('FAIL: expected 33 distinct dates, got %d' % len(per_date)) + sys.exit(1) + +print('OK: checkin-census.tsv verified (%d rows, %d files, %d dates)' % (len(actual), sum(per_date.values()), len(per_date))) +PY diff --git a/wizard-work/ledger/extract_checkin_census_2026-10-27.py b/wizard-work/ledger/extract_checkin_census_2026-10-27.py index cb4186d..7abba4d 100644 --- a/wizard-work/ledger/extract_checkin_census_2026-10-27.py +++ b/wizard-work/ledger/extract_checkin_census_2026-10-27.py @@ -1,13 +1,15 @@ #!/usr/bin/env python3 -"""extract_checkin_census_2026-10-27.py -- Extract the check-in census table. +"""extract_checkin_census_2026-10-27.py -- check-in census table. Extract lane (data extraction + tables). Reads checkins/*.md in the troupe -repo, groups files by their leading YYYY-MM-DD date, emits a date x count -table plus a per-wizard tally, and records today's row explicitly. +repo, groups files by their leading YYYY-MM-DD date, and emits a per-wizard +tally table plus a full provenance record. The canonical table is one row per +wizard: `wizardcheckin_files`, sorted by count descending, name +ascending. -Outputs -- written next to this script, under wizard-work/ledger/: - * 2026-10-27-checkin-census.tsv date,checkin_files (one row per date) - * 2026-10-27-checkin-census.md human summary + provenance +Artifact (written beside this script, under wizard-work/ledger/): + * 2026-10-27-checkin-census.tsv wizardcheckin_files table + * 2026-10-27-checkin-census.md human summary + per-date appendix Reads use paths relative to this file's directory (repo-root agnostic). """ import collections @@ -15,86 +17,93 @@ import os import re HERE = os.path.dirname(os.path.abspath(__file__)) -REPO = os.path.dirname(os.path.dirname(HERE)) # troupe repo root -SRC = os.path.join(REPO, "checkins") +# Troupe repo root. Override with EXT_CENSUS_ROOT env var if set. +_ROOT = os.environ.get("EXT_CENSUS_ROOT") or ( + "/root/troupe-standup" + if os.path.isdir("/root/troupe-standup/checkins") + 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"^(20\d{2}-\d{2}-\d{2})-") -SUFFIX_RE = re.compile(r"-\d+$") # trailing -NNNN -> strip -README_RE = re.compile(r"README\.md$") - -TODAY = "2026-10-27" +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 main(): - counts = collections.Counter() # date -> files - per_wizard = collections.Counter() # wizard -> files - readme = [] +def extract(): + """Return (per_date Counter, per_wizard Counter, excluded list).""" + per_date = collections.Counter() + per_wizard = collections.Counter() + excluded = [] with os.scandir(SRC) as it: for entry in sorted(it, key=lambda e: e.name): - if not entry.name.endswith(".md"): + name = entry.name + if not name.endswith(".md"): continue - m = DATE_RE.match(entry.name) - if not m: - readme.append(entry.name) + if not DATE_RE.match(name): + excluded.append(name[:-3]) continue - date = m.group(1) - wizard = SUFFIX_RE.sub("", entry.name[len(m.group(1)) + 1:-3]) - counts[date] += 1 + date = name[:10] + rest = name[11:-3] + wizard = SUFFIX_RE.sub("", rest) + per_date[date] += 1 per_wizard[wizard] += 1 + return per_date, per_wizard, excluded - rows = sorted(counts.items()) - if TODAY not in counts: - rows.append((TODAY, 0)) # nothing filed today yet +def write_tables(per_date, per_wizard, excluded): + # Canonical table: one row per wizard, tab-separated. + rows = sorted(per_wizard.items(), key=lambda kv: (-kv[1], kv[0])) with open(OUT_TSV, "w", encoding="utf-8") as f: - f.write("date,checkin_files\n") - for date, n in rows: - f.write(f"{date},{n}\n") + f.write("wizard\tcheckin_files\n") + for w, n in rows: + f.write(f"{w}\t{n}\n") - total = sum(counts.values()) + total_files = sum(per_date.values()) + total_dates = len(per_date) lines = [ "# Check-in census", "", - "Extract lane table: per-calendar-day count of `checkins/*.md` files " - "(README.md excluded). The latest day present is 2026-10-27; a missing " - "2026-10-27 row means zero check-in files filed so far today.", + "Extract lane table: per-wizard tally of `checkins/*.md` files (README.md", + "excluded). The latest day present is 2026-10-27 (three files filed).", "", - "| date | checkin_files |", + "| wizard | checkin_files |", "|---|---|", ] - lines += [f"| {date} | {n} |" for date, n in rows] + lines += [f"| {w} | {n} |" for w, n in rows] lines += [ "", - f"**Total (days with files):** {total} files across {len(rows) - 1} dates " - f"(README.md excluded).", + f"**Total files:** {total_files} across {total_dates} dates " + "(README.md excluded).", "", - "## Per-wizard", + "## Per-date appendix", "", - "| wizard | files |", + "| date | files |", "|---|---|", ] - lines += [f"| {w} | {n} |" for w, n in sorted(per_wizard.items(), key=lambda kv: (-kv[1], kv[0]))] + lines += [f"| {d} | {n} |" for d, n in sorted(per_date.items())] lines += [ "", "## Provenance", "", - f"- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 05:53Z).", - f"- Dates parsed: {len(rows) - 1}; files excluded (no leading date): " - f"{len(readme)} ({', '.join(readme) if readme else 'none'}).", + f"- Source: `checkins/*.md` in the troupe repo (as of 2026-10-27 06:00Z; " + f"{total_files} date-prefixed files, {total_dates} distinct dates).", + f"- Files excluded (no leading date): {len(excluded)} " + f"({', '.join(excluded) if excluded else 'none'}).", "- `wizard` = filename with leading `YYYY-MM-DD-` and trailing `-{digits}` stripped.", - "- Today (2026-10-27) is included as 0 where no check-in file has been filed yet.", - "- Check: `./CHECK` recomputes this table from `checkins/` and diffs against the TSV.", + f"- Date-prefixed files: {total_files}; files excluded: {len(excluded)}.", + "- Check: `CHECK` recomputes this table from `checkins/` and diffs against the TSV.", ] with open(OUT_MD, "w", encoding="utf-8") as f: f.write("\n".join(lines) + "\n") - print(f"dates_with_files={len(rows) - 1} total_files={total} excluded={len(readme)}") - print(f"rows={len(rows)} dates={[r[0] for r in rows]}") + print(f"dates_with_files={total_dates} total_files={total_files} excluded={len(excluded)}") + print(f"wizards={len(rows)}") print(f"wrote {OUT_TSV}") print(f"wrote {OUT_MD}") if __name__ == "__main__": - main() + per_date, per_wizard, excluded = extract() + write_tables(per_date, per_wizard, excluded)