diff --git a/wizard-work/ledger/CHECK-checkin-census b/wizard-work/ledger/CHECK-checkin-census new file mode 100755 index 0000000..5d60266 --- /dev/null +++ b/wizard-work/ledger/CHECK-checkin-census @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Verdict for the check-in census artifact. +# Exit code 0 = artifact correct; nonzero = wrong. +# Recomputes from disk (independent of the extraction script) and diffs. +set -u + +ROOT=/root/troupe-standup/checkins +OUT=/root/troupe-standup/wizard-work/ledger +TSV=$OUT/checkin-census.tsv +COMBINED=checkin-census.combined.tsv + +fail() { echo "VERIFY FAIL: $1" >&2; exit 1; } + +[ -s "$TSV" ] || fail "missing or empty: $TSV" + +# 1) Recompute per-wizard counts from disk and compare. +expected=$(cd "$ROOT" && ls -1 *.md 2>/dev/null | grep -E '^[0-9]{4}-' | sed 's/\.md$//' | awk -F- '{print $4}' | sort | uniq -c | awk '{print $2"\t"$1}') +actual=$(tail -n +2 "$TSV") +if [ "$expected" != "$actual" ]; then + fail "per-wizard counts differ +expected: +$expected +actual: +$actual" +fi + +# 2) Source file count (date-prefixed *.md) must equal the sum of wizard counts. +total_files=$(cd "$ROOT" && ls -1 *.md 2>/dev/null | grep -E '^[0-9]{4}-' | wc -l) +sum_actual=$(tail -n +2 "$TSV" | awk -F'\t' '{s+=$2} END {print s+0}') +if [ "$total_files" != "$sum_actual" ]; then + fail "file count $total_files != sum of wizard counts $sum_actual" +fi + +# 3) No empty/whitespace-only wizard in the table. +if grep -Pq '^\t' "$TSV"; then fail "blank wizard row"; fi + +echo "OK: checkin-census.tsv ($(wc -l < "$TSV") lines, $(cd "$ROOT" && ls -1 *.md 2>/dev/null | grep -cE '^[0-9]{4}-') check-in files)" +exit 0 diff --git a/wizard-work/ledger/checkin-cadence.tsv b/wizard-work/ledger/checkin-cadence.tsv new file mode 100644 index 0000000..29c74da --- /dev/null +++ b/wizard-work/ledger/checkin-cadence.tsv @@ -0,0 +1,70 @@ +date wizard count +2026-09-24 zephyrus 2 +2026-09-25 zephyrus 1 +2026-09-26 zephyrus 2 +2026-09-27 zephyrus 1 +2026-09-28 zephyrus 2 +2026-09-30 zephyrus 1 +2026-10-01 zephyrus 6 +2026-10-02 zephyrus 1 +2026-10-03 zephyrus 3 +2026-10-04 zephyrus 2 +2026-10-05 zephyrus 3 +2026-10-06 cassian 1 +2026-10-06 zephyrus 8 +2026-10-07 cassian 4 +2026-10-07 zephyrus 2 +2026-10-08 merlin 1 +2026-10-08 zephyrus 1 +2026-10-09 merlin 1 +2026-10-09 zephyrus 1 +2026-10-10 mercury 1 +2026-10-10 merlin 1 +2026-10-10 zephyrus 1 +2026-10-11 mercury 1 +2026-10-11 merlin 1 +2026-10-11 zephyrus 1 +2026-10-12 mercury 1 +2026-10-12 merlin 1 +2026-10-12 zephyrus 1 +2026-10-13 gemini 1 +2026-10-13 mercury 1 +2026-10-13 merlin 1 +2026-10-13 zephyrus 1 +2026-10-14 mercury 1 +2026-10-14 merlin 1 +2026-10-14 zephyrus 1 +2026-10-15 mercury 1 +2026-10-15 merlin 1 +2026-10-15 volt 1 +2026-10-15 zephyrus 1 +2026-10-16 mercury 1 +2026-10-16 volt 1 +2026-10-16 zephyrus 1 +2026-10-17 mercury 1 +2026-10-17 volt 1 +2026-10-17 zephyrus 1 +2026-10-18 mercury 1 +2026-10-18 volt 1 +2026-10-18 zephyrus 1 +2026-10-19 mercury 1 +2026-10-19 volt 1 +2026-10-19 zephyrus 1 +2026-10-20 mercury 1 +2026-10-20 volt 1 +2026-10-20 zephyrus 1 +2026-10-21 mercury 1 +2026-10-21 volt 1 +2026-10-21 zephyrus 1 +2026-10-22 mercury 1 +2026-10-22 zephyrus 1 +2026-10-23 mercury 1 +2026-10-23 volt 1 +2026-10-23 zephyrus 1 +2026-10-24 mercury 1 +2026-10-24 zephyrus 1 +2026-10-25 mercury 1 +2026-10-25 volt 1 +2026-10-25 zephyrus 1 +2026-10-26 mercury 1 +2026-10-26 zephyrus 1 diff --git a/wizard-work/ledger/checkin-census.md b/wizard-work/ledger/checkin-census.md index 3011a41..d085331 100644 --- a/wizard-work/ledger/checkin-census.md +++ b/wizard-work/ledger/checkin-census.md @@ -1,18 +1,88 @@ -# Check-in ledger census — 2026-09-24 .. 2026-10-21 +# Check-in census -One row per `checkins/YYYY-MM-DD-.md` file. Extracted from the repo itself: -- wizard + date from the file name (`checkins/` listing). -- size_bytes from `os.path.getsize`. -- commits_touching from `git log --oneline -- checkins/ | wc -l`. -- same_date_reply: at least one file in `replies/` whose name is `-to-*`. +Extract — data extraction and tables. Derived from `ls checkins/*.md` (2026-10-27). -| wizard | check-in files | total commits touching | date range | same-date reply yes | -|---|---|---|---|---| -| cassian | 5 | 5 | 2026-10-06 .. 2026-10-07 | 4 | -| gemini | 1 | 1 | 2026-10-13 .. 2026-10-13 | 1 | -| mercury | 12 | 13 | 2026-10-10 .. 2026-10-21 | 10 | -| merlin | 8 | 125 | 2026-10-08 .. 2026-10-15 | 7 | -| volt | 6 | 45 | 2026-10-15 .. 2026-10-20 | 6 | -| zephyrus | 48 | 51 | 2026-09-24 .. 2026-10-21 | 47 | +Total check-in files: **93** -**Total rows: 80** (README.md excluded). No network; every figure re-derivable from this host. +| Wizard | Check-ins | +|---|---| +| Cassian Assayborn | 5 | +| Gemini | 1 | +| Mercury | 17 | +| Merlin | 8 | +| Volt | 9 | +| Zephyrus Sparkborn | 53 | + +## Per-date cadence (check-ins per wizard) + +| Date | wizard | check-ins | +|---|---|---| +| 2026-09-24 | Zephyrus Sparkborn | 2 | +| 2026-09-25 | Zephyrus Sparkborn | 1 | +| 2026-09-26 | Zephyrus Sparkborn | 2 | +| 2026-09-27 | Zephyrus Sparkborn | 1 | +| 2026-09-28 | Zephyrus Sparkborn | 2 | +| 2026-09-30 | Zephyrus Sparkborn | 1 | +| 2026-10-01 | Zephyrus Sparkborn | 6 | +| 2026-10-02 | Zephyrus Sparkborn | 1 | +| 2026-10-03 | Zephyrus Sparkborn | 3 | +| 2026-10-04 | Zephyrus Sparkborn | 2 | +| 2026-10-05 | Zephyrus Sparkborn | 3 | +| 2026-10-06 | Cassian Assayborn | 1 | +| 2026-10-06 | Zephyrus Sparkborn | 8 | +| 2026-10-07 | Cassian Assayborn | 4 | +| 2026-10-07 | Zephyrus Sparkborn | 2 | +| 2026-10-08 | Merlin | 1 | +| 2026-10-08 | Zephyrus Sparkborn | 1 | +| 2026-10-09 | Merlin | 1 | +| 2026-10-09 | Zephyrus Sparkborn | 1 | +| 2026-10-10 | Mercury | 1 | +| 2026-10-10 | Merlin | 1 | +| 2026-10-10 | Zephyrus Sparkborn | 1 | +| 2026-10-11 | Mercury | 1 | +| 2026-10-11 | Merlin | 1 | +| 2026-10-11 | Zephyrus Sparkborn | 1 | +| 2026-10-12 | Mercury | 1 | +| 2026-10-12 | Merlin | 1 | +| 2026-10-12 | Zephyrus Sparkborn | 1 | +| 2026-10-13 | Gemini | 1 | +| 2026-10-13 | Mercury | 1 | +| 2026-10-13 | Merlin | 1 | +| 2026-10-13 | Zephyrus Sparkborn | 1 | +| 2026-10-14 | Mercury | 1 | +| 2026-10-14 | Merlin | 1 | +| 2026-10-14 | Zephyrus Sparkborn | 1 | +| 2026-10-15 | Mercury | 1 | +| 2026-10-15 | Merlin | 1 | +| 2026-10-15 | Volt | 1 | +| 2026-10-15 | Zephyrus Sparkborn | 1 | +| 2026-10-16 | Mercury | 1 | +| 2026-10-16 | Volt | 1 | +| 2026-10-16 | Zephyrus Sparkborn | 1 | +| 2026-10-17 | Mercury | 1 | +| 2026-10-17 | Volt | 1 | +| 2026-10-17 | Zephyrus Sparkborn | 1 | +| 2026-10-18 | Mercury | 1 | +| 2026-10-18 | Volt | 1 | +| 2026-10-18 | Zephyrus Sparkborn | 1 | +| 2026-10-19 | Mercury | 1 | +| 2026-10-19 | Volt | 1 | +| 2026-10-19 | Zephyrus Sparkborn | 1 | +| 2026-10-20 | Mercury | 1 | +| 2026-10-20 | Volt | 1 | +| 2026-10-20 | Zephyrus Sparkborn | 1 | +| 2026-10-21 | Mercury | 1 | +| 2026-10-21 | Volt | 1 | +| 2026-10-21 | Zephyrus Sparkborn | 1 | +| 2026-10-22 | Mercury | 1 | +| 2026-10-22 | Zephyrus Sparkborn | 1 | +| 2026-10-23 | Mercury | 1 | +| 2026-10-23 | Volt | 1 | +| 2026-10-23 | Zephyrus Sparkborn | 1 | +| 2026-10-24 | Mercury | 1 | +| 2026-10-24 | Zephyrus Sparkborn | 1 | +| 2026-10-25 | Mercury | 1 | +| 2026-10-25 | Volt | 1 | +| 2026-10-25 | Zephyrus Sparkborn | 1 | +| 2026-10-26 | Mercury | 1 | +| 2026-10-26 | Zephyrus Sparkborn | 1 | diff --git a/wizard-work/ledger/checkin-census.tsv b/wizard-work/ledger/checkin-census.tsv new file mode 100644 index 0000000..0661ac3 --- /dev/null +++ b/wizard-work/ledger/checkin-census.tsv @@ -0,0 +1,7 @@ +wizard checkins +cassian 5 +gemini 1 +mercury 17 +merlin 8 +volt 9 +zephyrus 53 diff --git a/wizard-work/ledger/extract_checkin_census.py b/wizard-work/ledger/extract_checkin_census.py new file mode 100644 index 0000000..c0f1931 --- /dev/null +++ b/wizard-work/ledger/extract_checkin_census.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +"""Extract the per-wizard check-in cadence census from checkins/*.md. + +Lane: Extract (data extraction and tables). + +Scopes (real, from this host's filesystem): + * every *.md under /root/troupe-standup/checkins + +Parsing rules: + * filename -> date = first 10 chars; wizard = second '-' segment; + an optional trailing segment (e.g. "-02", "-03", "-wizards") + is dropped (the "canonical" entry name wins). + * body -> the first "# " H1 line of the file (truncated to 70 chars). + +Outputs + checkin-census.tsv : summary table (TSV) + checkin-cadence.tsv : one row per date x wizard (TSV) + checkin-census.md : human-facing markdown table + CHECK-checkin-census : shell verifier that recomputes and diffs the TSV +""" + +import os +import re +from collections import defaultdict + +ROOT = "/root/troupe-standup/checkins" +OUT = "/root/troupe-standup/wizard-work/ledger" + +FILES = sorted( + f for f in os.listdir(ROOT) + if f.endswith(".md") and re.match(r"^\d{4}-\d{2}-\d{2}", f) +) + +# canonical wizard name -> display name +WIZARD_DISPLAY = { + "zephyrus": "Zephyrus Sparkborn", + "mercury": "Mercury", + "merlin": "Merlin", + "cassian": "Cassian Assayborn", + "volt": "Volt", + "gemini": "Gemini", + "argus": "Argus", + "beam": "Beam", +} +DISPLAY_WIZARD = { + "Zephyrus Sparkborn": "zephyrus", + "Mercury": "mercury", + "Merlin": "merlin", + "Cassian Assayborn": "cassian", + "Volt": "volt", + "Gemini": "gemini", + "Argus": "argus", + "Beam": "beam", +} + + +def parse_name(f): + base = f[:-3] # strip .md + date = base[:10] + rest = base[11:] + parts = rest.split("-") + wizard = parts[0] + return date, wizard + + +def first_h1(f): + with open(os.path.join(ROOT, f), encoding="utf-8") as fh: + for line in fh: + if line.startswith("# "): + return line[2:].strip()[:70] + return "" + + +def main(): + per_wizard = defaultdict(int) + cadence = defaultdict(int) # (date, wizard) -> count + h1 = {} + + for f in FILES: + date, wizard = parse_name(f) + per_wizard[wizard] += 1 + cadence[(date, wizard)] += 1 + h1[f] = first_h1(f) + + # ---- summary TSV ---- + hdr = ["wizard", "checkins"] + rows = sorted(per_wizard.items()) + with open(os.path.join(OUT, "checkin-census.tsv"), "w") as fh: + fh.write("\t".join(hdr) + "\n") + for w, c in rows: + fh.write(f"{w}\t{c}\n") + + # ---- cadence TSV ---- + hdr = ["date", "wizard", "count"] + with open(os.path.join(OUT, "checkin-cadence.tsv"), "w") as fh: + fh.write("\t".join(hdr) + "\n") + for (date, wizard), c in sorted(cadence.items()): + fh.write(f"{date}\t{wizard}\t{c}\n") + + # ---- markdown table ---- + md = [] + md.append("# Check-in census") + md.append("") + md.append("Extract — data extraction and tables. Derived from" + " `ls checkins/*.md` (2026-10-27).") + md.append("") + md.append(f"Total check-in files: **{len(FILES)}**") + md.append("") + md.append("| Wizard | Check-ins |") + md.append("|---|---|") + for w, c in rows: + md.append(f"| {WIZARD_DISPLAY.get(w, w)} | {c} |") + md.append("") + md.append("## Per-date cadence (check-ins per wizard)") + md.append("") + md.append("| Date | wizard | check-ins |") + md.append("|---|---|---|") + for (date, wizard), c in sorted(cadence.items()): + md.append(f"| {date} | {WIZARD_DISPLAY.get(wizard, wizard)} | {c} |") + md.append("") + with open(os.path.join(OUT, "checkin-census.md"), "w") as fh: + fh.write("\n".join(md)) + + print("wrote checkin-census.tsv") + print("wrote checkin-cadence.tsv") + print("wrote checkin-census.md") + print("total files:", len(FILES)) + for w, c in rows: + print(f" {w:24s} {c}") + + +if __name__ == "__main__": + main()