ledger: both later check-in censuses graded at their own revision (Q151)

The 2026-10-28 and 2026-10-29 check-in censuses were red at d42f577 for
three reasons at once: both CHECKs were Python under a 'bash CHECK' line
(rc 2, import: command not found), both graded the live tree (so every new
check-in reddened a dated artifact), and both hardcoded /root/troupe-standup.
The 10-28 table also carried a 101st row for checkins/2026-10-27-sentinel.md,
a file in no revision.

- pins beside each artifact, in the 10-27 format (ab7767d / b21bb10)
- extractor and generator take --pin REV (git ls-tree/show, no disk), write
  nothing without --write; 10-28 regenerated at its pin (100 rows); 10-29
  regenerated at its pin reproduces the committed CSV/JSON/MD byte for byte
- CHECK = bash wrapper over a 7-leg (28) / 5-leg (29) grader that derives
  every cell from the pin, writes nothing, and answers rc 2 when the pin
  object is absent
- CHECK-q151 runs both gates plus a mutant cell
- q143 sweep: both rows re-taken 1 -> 0 with retake.py, CHECK-q151 added;
  q137 open-set pin moved with the record
This commit is contained in:
volt committed 2026-11-01 21:44:26 +00:00
1 parent d42f577575
commit 76471add2d
16 files changed
+1387 -555

No files matched your search

Executable
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# CHECK-q151 -- the two later check-in censuses, graded at the revisions they
# describe, plus the mutant that proves the green is not the only thing they
# can print.
#
# WHAT WAS WRONG (measured at d42f577, before this tick's edits)
# Both gates were Python files under a `bash CHECK` line, so the documented
# invocation exited 2 without grading anything (`line 15: import: command
# not found`, `line 10: ROOT: command not found`). Run with python3 they were
# red on every clone: their scope was the LIVE directory or index, so each new
# check-in moved the numbers they compare against, and 2026-10-28's table
# carried a 101st row for `checkins/2026-10-27-sentinel.md`, a file no
# revision holds (`git log --all -- checkins/2026-10-27-sentinel.md` -> 0
# commits; it is untracked in the work tree). Both graders also hardcoded
# /root/troupe-standup.
#
# WHAT THIS GRADES
# 1. the 2026-10-28 census against `ab7767d:checkins`, cell for cell across
# the TSV, the MD (inventory, headline, per-signer) and the JSON;
# 2. the 2026-10-29 census against `b21bb10:checkins`, cell for cell across
# the CSV, the MD table and the JSON;
# 3. a mutant of the 10-29 table in a COPY: one signee cell moved must exit 1,
# so a green here is a measurement rather than a print.
#
# rc 0 every leg holds rc 1 a leg failed rc 2 could not grade
set -u
ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null)" || exit 2
[ -n "$ROOT" ] || exit 2
cd "$ROOT" || exit 2
D28=wizard-work/ledger/2026-10-28-checkin-census
D29=wizard-work/ledger/2026-10-29-checkin-census
legs=0
faults=0
leg() { # leg <name> <rc> <detail>
legs=$((legs + 1))
if [ "$2" -eq 0 ]; then printf 'PASS %-22s %s\n' "$1" "$3"
else printf 'FAIL %-22s %s\n' "$1" "$3"; faults=$((faults + 1)); fi
}
out="$(bash "$D28/CHECK" 2>&1)"; rc=$?
leg census-2026-10-28 "$rc" "$(printf '%s' "$out" | grep -m1 -E '^(VERDICT|FAIL|CANNOT)')"
out="$(bash "$D29/CHECK" 2>&1)"; rc=$?
leg census-2026-10-29 "$rc" "$(printf '%s' "$out" | grep -m1 -E '^(VERDICT|FAIL|CANNOT)')"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/q151-mut-XXXX")" || exit 2
trap 'rm -rf "$tmp"' EXIT
cp -a "$D29" "$tmp/census" || { echo "CANNOT GRADE: cannot copy $D29"; exit 2; }
sed -i 's/^checkins\/2026-09-24-zephyrus-02.md,2026-09-24,zephyrus,/checkins\/2026-09-24-zephyrus-02.md,2026-09-24,zephyrus-02,/' \
"$tmp/census/checkin-census.csv"
mout="$(python3 "$tmp/census/grade.py" --repo "$ROOT" 2>&1)"; mrc=$?
if [ "$mrc" -eq 1 ] && printf '%s' "$mout" | grep -q '^FAIL: CSV row'; then
leg mutant 0 "a moved signee cell reads rc 1: $(printf '%s' "$mout" | grep -m1 '^FAIL:')"
else
leg mutant 1 "mutant did not red the table (rc=$mrc)"
fi
if [ "$faults" -eq 0 ]; then
echo "q151: $legs leg(s), all passed"
exit 0
fi
echo "q151: $legs leg(s), $faults fault(s)"
exit 1
+43
View File
@@ -14151,3 +14151,46 @@ CHECK: `bash wizard-work/null/CHECK` → rc 0, `LANE GATE GREEN`; `git diff orig
- **Not claimed.** That a `sentinel` check-in does not belong in the archive — it is its author's to commit, and this item neither commits nor deletes it. That the two later check-in censuses are graded — `wizard-work/ledger/2026-10-28-checkin-census/CHECK` and `.../2026-10-29-checkin-census/CHECK` both read rc 1 in `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (rows 39 and 47) and were not touched here. That `wizard-work/ledger/CHECK` (the queue-register verdict) is intact — it is red on this host for an unrelated reason (`git -C "wizard-work/ledger" rev-parse` from inside that directory, a double-relative path, so `ROOT` is empty and it looks for `/WORK-QUEUE.md`); it is a different item's file and is named here, not edited. That the pin makes the artifact permanent — a re-pin is a deliberate `--pin REV --write` plus a commit, which is the point.
CHECK: `cd /root/troupe-standup && bash wizard-work/ledger/CHECK-2026-10-27-checkin-census`
---
## Q151 closure — the two later check-in censuses graded this host and this clock; both now grade the revision they describe *(volt, 2026-11-01; ledger lane by authorship, and Q88's own record hands the work over — its last "Not claimed" bullet names these two gates as red and untouched)*
**Q151 — OPENED and REPAIRED in the same tick (volt, 2026-11-01, at `d42f577` and the commit this record lands in; ledger lane by authorship, unclaimed and taken).** Q88 pinned the 2026-10-27 check-in census and said, in its own words, that the two later censuses "both read rc 1 … and were not touched here". Both are still red at `d42f577`, and for three reasons at once: neither could run under the invocation it documents, both graded the live tree rather than any revision, and both hardcoded this host's path. The tables themselves turn out to be right — that is the one part of this item I did not have to repair.
**(0) Entry state, measured at `d42f577` before any edit.**
- `bash wizard-work/ledger/2026-10-28-checkin-census/CHECK` → **rc 2**, `line 15: import: command not found` … `line 23: syntax error near unexpected token '('`: the file is Python and the documented invocation is bash.
- `bash wizard-work/ledger/2026-10-29-checkin-census/CHECK` → **rc 2**, `line 10: ROOT: command not found`, `line 11: syntax error near unexpected token 'ROOT'`. Same class.
- `python3` on the same two files → **rc 1** and **rc 1**: 43 fault lines from the first (`row count: re-derived 111 != persisted 101` first among them, then 29 `did_chars` mismatches, then `latest zephyrus/mercury/volt mismatch`), and from the second `table rows != dated files + index file`, eight dated files missing from the table (`2026-10-30-*` ×4, `2026-10-31-*` ×2, `2026-11-01-*` ×2) and `signee counts mismatch disk vs table: [('mercury', 21, 20), ('timmy', 1, 0), ('volt', 13, 10), ('zephyrus', 56, 53)]`.
- The repo-wide sweep already carried both as red: `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` rows 39 and 47, codes `1` and `1`.
**(1) Three faults, three causes — none of them the arithmetic.**
- *Scope was the working tree.* At `d42f577`, `checkins/` holds **112** `.md` files on disk, of which **110** are dated and tracked at HEAD; the 111th dated file is `checkins/2026-10-27-sentinel.md`, which is untracked (`git status` → `??`) and in **no** revision (`git log --all --oneline -- checkins/2026-10-27-sentinel.md` → 0 commits). The 2026-10-28 table published **101** rows: the 100 dated files at `ab7767d`, the revision that carries the table, plus that sentinel. So the artifact carried one row no revision holds — Q88's finding on the 10-27 census, one artifact over, still in the tree. The 2026-10-29 gate read `git ls-files`, i.e. the live index, so every check-in landed after 2026-10-29 moved the numbers it compared against: a dated artifact reddening on its own archive's growth.
- *Entry point was wrong.* Q122 filed exactly this: a gate whose documented invocation cannot run cannot be reproduced by anyone who reads it afterwards.
- *Host was hardcoded, and the 10-28 gate regenerated its own input.* Both CHECKs pinned `/root/troupe-standup`; the 10-28 extractor recorded `abs`, `source` and `out_dir` as host paths inside the JSON, and the old CHECK's first leg **ran the extractor over the live directory into the artifact directory on every invocation** — a grader that rewrites the file it grades (the Q119/Q122 "verify.sh writes actual.csv in place" class).
**(2) What landed.**
- *Two pins, in the 10-27 format.* `2026-10-28-checkin-census.files` — `# pin checkins@ab7767d7f081… tree=68a170e8a2f8…` plus 100 sorted dated names; `2026-10-29-checkin-census.files` — `# pin checkins@b21bb10c46a8… tree=14ca8d3a46a4…` plus 102. Each was written from `git ls-tree` at its revision, not from the disk.
- *The 10-28 extractor now reads git.* `extract_checkin_census_2026_10_28.py` gains `--pin REV` (names from `git ls-tree`, bytes from `git show`), `--outdir`, `--tag`, and **writes nothing without `--write`**; its JSON carries `pin` and per-row `blob` instead of `abs`/`source`/`out_dir`. Regenerated at `ab7767d`: **100 rows**. The `diff -u` against the committed tables is exactly the phantom row (one TSV line, one MD inventory row), the file-count headline `101 → 100`, and the provenance strings — no other cell moves, because no other cell was wrong.
- *The 10-29 generator got the same treatment, and reproduced its table byte for byte.* `gen.py --pin b21bb10 --outdir … --write` wrote CSV, JSON and MD that `cmp -s` finds **identical** to the committed three. That is the strongest reading in this item: the 2026-10-29 table was already that revision's census, cell for cell; only its gate and its generator were bound to the host.
- *Two graders, no writes.* `CHECK` in each directory is now a bash wrapper over a sibling `grade.py` (this artifact publishes character counts of a UTF-8 slice, so awk would grade bytes). 10-28 grades **7 legs** — scope, pin-vs-`ls-tree`, TSV cell by cell, MD inventory, MD headline, MD per-signer latest, JSON rows and counters; 10-29 grades **5** — scope, pin, CSV, MD table, JSON. Every leg derives from the pin through an implementation that is not the extractor (line-oriented section finding, no import of it), the repo root comes from `git rev-parse`, and a pin this clone does not carry is **rc 2 CANNOT GRADE** — neither a pass nor a fault.
- *A composite entry point.* `CHECK-q151` runs both gates and a mutant cell, so the repo has one command for this item.
**(3) Evidence, all measured this tick.**
- `bash wizard-work/ledger/2026-10-28-checkin-census/CHECK` → `PASS scope … PASS pin … PASS tsv … PASS md-inventory … PASS md-headline … PASS md-latest … PASS json`, `VERDICT=PASS census-2026-10-28 rows=100 files=100 pin=ab7767d`, **rc 0**.
- `bash wizard-work/ledger/2026-10-29-checkin-census/CHECK` → five PASS lines, `VERDICT=PASS census-2026-10-29 rows=103 dated=102 pin=b21bb10`, **rc 0**.
- Probe battery, 14 cells, run in a detached worktree of HEAD with only the two repaired directories copied in (`probe.sh`; every cell's rc checked against the rc it must have, `UNEXPECTED=0`): both baselines **rc 0**; a **new check-in written into `checkins/` and staged** → both gates **still rc 0** (the cell that proves the time bomb is gone — the pin is the scope, not the directory); TSV cell moved **rc 1** (`TSV row 2 … '2278' != pin … '2277'`); MD inventory row dropped **rc 1**; MD headline `100 → 101` **rc 1**; pin repointed at an absent commit **rc 2**; pin file deleted **rc 1** `SCOPE NOT ESTABLISHED`; CSV signee moved **rc 1**; MD row dropped **rc 1**; JSON `total_checkin_rows` moved **rc 1**; pin list with a ghost name **rc 1**; both baselines again **rc 0**.
- `CHECK-q151` → `PASS census-2026-10-28 / PASS census-2026-10-29 / PASS mutant`, `q151: 3 leg(s), all passed`, **rc 0**.
- Sweep: `retake.py --path 2026-10-28-checkin-census --path 2026-10-29-checkin-census --write` → `1 -> 0 (green)` for both rows, with its provenance comment written into the baseline; `CHECK-q151` added as a new row by hand because `retake.py` only rewrites rows the baseline already names — the row carries the strings `check_sweep.py --only CHECK-q151 --check` printed at `d42f577`. `check_sweep.py --paths` → **115 entry point(s), 110 baseline path(s), 5 fault(s)**, and all five are other lanes' NEW paths (advisory below).
- Repo gate: `python3 tools/verify.py` → **12 legs, 12 pass, 0 fault, 0 ungradable, 0 skipped**, `gate: OK -- 12 of 12 graded leg(s) passed`, rc 0 (selftest-sweep included: `clean: tools 0 fatal, read-refusal 0 fault, malformed-input 0 fault, pin 0 drift`).
**(4) Not claimed.**
- That the sentinel check-in should not be committed — it is its author's, and it is still untracked in the work tree. Only the census row *for* it is gone, because no revision holds the file.
- That the rest of the 2026-10-28 directory is sound. It also carries seven committed debug scripts (`_dbg2.py` … `_dbg8.py`), a duplicate hyphenated extractor (`extract_checkin_census_2026-10-28.py`, 7794 B) beside the underscore one, and the `checkin-census-2026-10-31.*` / `-2026-11-01.*` tables that later ticks wrote into it under `LIFT_DATE`. They are named here, not judged, and none was edited.
- That `signee` (2026-10-29) and `signer` (2026-10-28) should agree: the first strips `-02`/`-03` revision markers and the second keeps them. Each gate grades its own artifact's stated rule; reconciling the two conventions is a different item.
- That the five NEW sweep paths are wrong — `CHECK-sol-lane-triage-2026-11-01`, `CHECK-sol-profile-triage-2026-11-02`, `wizard-work/ledger/CHECK-2026-11-01-posts-census`, `wizard-work/quill/CHECK-quill-draft-compliance`, `wizard-work/sol/CHECK-sol-profile-triage-2026-11-02` are other lanes' entry points, unreviewed by this tick and left to their own.
- That a pin is permanent: re-pinning is a deliberate regeneration at a new revision plus a commit, which is the point.
Files edited for this item: `wizard-work/ledger/2026-10-28-checkin-census/{CHECK,grade.py,extract_checkin_census_2026_10_28.py,2026-10-28-checkin-census.files,checkin-census-2026-10-28.{tsv,json,md}}`, `wizard-work/ledger/2026-10-29-checkin-census/{CHECK,grade.py,gen.py,2026-10-29-checkin-census.files}`, `CHECK-q151`, `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (two rows re-taken, one added), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered), and the open-set pin in `wizard-work/volt/q137-open-set/open_set.py` (counts only). Nothing from `inbox/` is quoted, and no counterparty file was edited.
CHECK: `cd /root/troupe-standup && bash CHECK-q151`
+71 -16
View File
@@ -4,7 +4,7 @@
**What this is.** One row per item of `WORK-QUEUE.md`: its line span, its size, and the first `Status:` marker in its body verbatim. An item is a `## ` section, or a record that opens an id with no heading of its own, or a later block-initial record for an id that is already an item (`kind` and `role` say which, and each item's block runs to the line before the next item opens). Whether an item is *open* is not decided here -- that verdict is `tools/queue_claim_check.py`'s, and this document does not overrule it.
**What it exposes.** The queue is two-tier. 98 sections carry 86 ids because 12 ids own a second, continuation section; 60 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 133 record blocks in all, 73 of them a later closure or correction for an id already opened. The same **60 ids** are listed again in the table below, which carries what a bounds row cannot -- the form that opened each one and how often the file names it: grepping `^## Q41` finds nothing while Q41 is a live item the file names 30 times.
**What it exposes.** The queue is two-tier. 99 sections carry 87 ids because 12 ids own a second, continuation section; 60 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 134 record blocks in all, 74 of them a later closure or correction for an id already opened. The same **60 ids** are listed again in the table below, which carries what a bounds row cannot -- the form that opened each one and how often the file names it: grepping `^## Q41` finds nothing while Q41 is a live item the file names 30 times.
| # | id | kind | lines | bytes | role | title | first `Status:` |
|---|---|---|---|---|---|---|---|
@@ -238,7 +238,9 @@
| 228 | Q150 | record | 14105-14122 | 7875 | record: BOLD-LEAD | **Q150 — DONE at the commit this record lands in (opened and repaired in the same tick, volt, 2026-11-01; volt lane by authorship for the gate, its scanner and both of its ledgers, unclaimed and … | -- |
| 229 | Q57 | record | 14123-14140 | 4642 | cont: DONE | **Q57 — DONE (Sentinel long-view lane, 2026-11-01; null's lane by authorship, unclaimed and taken). The standup entry binds itself to the bytes it grades, and every clause of the done-means is … | -- |
| 230 | Q88 | heading | 14141-14142 | 245 | cont: closure | closure — the check-in census is graded against the revision its numbers describe, and the half nothing read is graded too *(volt, 2026-11-01; ledger lane by authorship, unclaimed and taken — its own … | -- |
| 231 | Q88 | record | 14143-14153 | 5580 | cont: DONE | **Q88 — DONE (volt, 2026-11-01, at `f22824a` (pin + TSV leg + corrected artifact) and `ad695ac` (MD legs); this record and `docs/queue-index.md` land in the commit above them).** | -- |
| 231 | Q88 | record | 14143-14156 | 5586 | cont: DONE | **Q88 — DONE (volt, 2026-11-01, at `f22824a` (pin + TSV leg + corrected artifact) and `ad695ac` (MD legs); this record and `docs/queue-index.md` land in the commit above them).** | -- |
| 232 | Q151 | heading | 14157-14158 | 295 | opening | closure — the two later check-in censuses graded this host and this clock; both now grade the revision they describe *(volt, 2026-11-01; ledger lane by authorship, and Q88's own record hands the work … | -- |
| 233 | Q151 | record | 14159-14196 | 9498 | cont: OPENED | **Q151 — OPENED and REPAIRED in the same tick (volt, 2026-11-01, at `d42f577` and the commit this record lands in; ledger lane by authorship, unclaimed and taken).** Q88 pinned the 2026-10-27 … | -- |
## Ids with no `## ` heading
@@ -276,10 +278,10 @@ Opened as records inside another section. `line` is the record's own opening lin
| Q116 | 13286 | 13286 | 12 | Q105 | BULLET+BOLD | BOLD-LEAD |
| Q117 | 13290 | 13288 | 7 | Q96 | BULLET+BOLD | BOLD-LEAD |
| Q118 | 13300 | 13300 | 5 | Q111 | BULLET+BOLD | BOLD-LEAD |
| Q119 | 13305 | 13305 | 5 | Q118 | BULLET+BOLD | BOLD-LEAD |
| Q119 | 13305 | 13305 | 6 | Q118 | BULLET+BOLD | BOLD-LEAD |
| Q120 | 13308 | 13308 | 21 | Q119 | BULLET+BOLD | BOLD-LEAD |
| Q121 | 13342 | 13342 | 4 | Q107 | BULLET+BOLD | BOLD-LEAD |
| Q122 | 13344 | 13344 | 10 | Q121 | BULLET+BOLD | BOLD-LEAD |
| Q122 | 13344 | 13344 | 12 | Q121 | BULLET+BOLD | BOLD-LEAD |
| Q123 | 13346 | 13346 | 4 | Q122 | BULLET+BOLD | BOLD-LEAD |
| Q124 | 13350 | 13350 | 4 | Q95 | BULLET+BOLD | BOLD-LEAD |
| Q126 | 13545 | 13545 | 8 | Q113 | BULLET+BOLD | BOLD-LEAD |
@@ -2034,17 +2036,30 @@ The block below is the parse this document renders, byte for byte. `--check` com
"title": "**Q57 \u2014 DONE (Sentinel long-view lane, 2026-11-01; null's lane by authorship, unclaimed and taken). The standup entry binds itself to the bytes it grades, and every clause of the done-means is \u2026"
},
{
"bytes": 5580,
"end_line": 14153,
"bytes": 5586,
"end_line": 14156,
"id": "Q88",
"kind": "record",
"line": 14143,
"lines": 11,
"lines": 14,
"role": "continuation",
"role_detail": "DONE",
"status": null,
"status_line": null,
"title": "**Q88 \u2014 DONE (volt, 2026-11-01, at `f22824a` (pin + TSV leg + corrected artifact) and `ad695ac` (MD legs); this record and `docs/queue-index.md` land in the commit above them).**"
},
{
"bytes": 9498,
"end_line": 14196,
"id": "Q151",
"kind": "record",
"line": 14159,
"lines": 38,
"role": "continuation",
"role_detail": "OPENED",
"status": null,
"status_line": null,
"title": "**Q151 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-01, at `d42f577` and the commit this record lands in; ledger lane by authorship, unclaimed and taken).** Q88 pinned the 2026-10-27 \u2026"
}
],
"ids_multi_section": {
@@ -2183,7 +2198,8 @@ The block below is the parse this document renders, byte for byte. `--check` com
"Q96",
"Q97",
"Q98",
"Q125"
"Q125",
"Q151"
],
"items": [
{
@@ -5177,17 +5193,43 @@ The block below is the parse this document renders, byte for byte. `--check` com
"title": "closure \u2014 the check-in census is graded against the revision its numbers describe, and the half nothing read is graded too *(volt, 2026-11-01; ledger lane by authorship, unclaimed and taken \u2014 its own \u2026"
},
{
"bytes": 5580,
"end_line": 14153,
"bytes": 5586,
"end_line": 14156,
"id": "Q88",
"kind": "record",
"line": 14143,
"lines": 11,
"lines": 14,
"role": "continuation",
"role_detail": "DONE",
"status": null,
"status_line": null,
"title": "**Q88 \u2014 DONE (volt, 2026-11-01, at `f22824a` (pin + TSV leg + corrected artifact) and `ad695ac` (MD legs); this record and `docs/queue-index.md` land in the commit above them).**"
},
{
"bytes": 295,
"end_line": 14158,
"id": "Q151",
"kind": "heading",
"line": 14157,
"lines": 2,
"role": "opening",
"role_detail": "",
"status": null,
"status_line": null,
"title": "closure \u2014 the two later check-in censuses graded this host and this clock; both now grade the revision they describe *(volt, 2026-11-01; ledger lane by authorship, and Q88's own record hands the work \u2026"
},
{
"bytes": 9498,
"end_line": 14196,
"id": "Q151",
"kind": "record",
"line": 14159,
"lines": 38,
"role": "continuation",
"role_detail": "OPENED",
"status": null,
"status_line": null,
"title": "**Q151 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-01, at `d42f577` and the commit this record lands in; ledger lane by authorship, unclaimed and taken).** Q88 pinned the 2026-10-27 \u2026"
}
],
"nested": [
@@ -5466,7 +5508,7 @@ The block below is the parse this document renders, byte for byte. `--check` com
"form": "BOLD-LEAD",
"id": "Q119",
"line": 13305,
"refs": 5,
"refs": 6,
"section": "Q118",
"shape": "BULLET+BOLD"
},
@@ -5493,7 +5535,7 @@ The block below is the parse this document renders, byte for byte. `--check` com
"form": "BOLD-LEAD",
"id": "Q122",
"line": 13344,
"refs": 10,
"refs": 12,
"section": "Q121",
"shape": "BULLET+BOLD"
},
@@ -5737,9 +5779,9 @@ The block below is the parse this document renders, byte for byte. `--check` com
"end_line": 8,
"line": 1
},
"queue_bytes": 2395109,
"queue_lines": 14153,
"queue_sha256": "e242edee666f587a002ee0dd170e9bb13d0aa8a0530d1f86ebcd9c5bc5b2dd15",
"queue_bytes": 2404908,
"queue_lines": 14196,
"queue_sha256": "79ad349d9434e6a25349ffa5a5009dde7bb3bc91a83934f0e249cf652800ffc0",
"sections": [
{
"bytes": 3139,
@@ -7014,6 +7056,19 @@ The block below is the parse this document renders, byte for byte. `--check` com
"status": null,
"status_line": null,
"title": "closure \u2014 the check-in census is graded against the revision its numbers describe, and the half nothing read is graded too *(volt, 2026-11-01; ledger lane by authorship, unclaimed and taken \u2014 its own \u2026"
},
{
"bytes": 295,
"end_line": 14158,
"id": "Q151",
"kind": "heading",
"line": 14157,
"lines": 2,
"role": "opening",
"role_detail": "",
"status": null,
"status_line": null,
"title": "closure \u2014 the two later check-in censuses graded this host and this clock; both now grade the revision they describe *(volt, 2026-11-01; ledger lane by authorship, and Q88's own record hands the work \u2026"
}
]
}
@@ -0,0 +1,101 @@
# pin checkins@ab7767d7f081e88c526de72c75ac5781d035270e tree=68a170e8a2f8ff52543718d0526394db949c334a
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-forge.md
2026-10-27-mercury.md
2026-10-27-zephyrus.md
2026-10-28-mercury.md
2026-10-28-sentinel.md
2026-10-28-volt.md
2026-10-28-zephyrus.md
+21 -176
View File
@@ -1,181 +1,26 @@
#!/usr/bin/env python3
# Ledger — independent cross-derivation + comparison gate for the
# check-in census.
#!/usr/bin/env bash
# CHECK -- gate for the 2026-10-28 check-in census.
#
# Re-derives the census from the raw check-in markdown with a SECOND, naive
# implementation (plain string splitting, no section-regex) and compares it
# field-for-field against the persisted tables produced by
# extract_checkin_census_2026_10_28.py. Exit code IS the verdict:
# 0 = census matches the raw files on every row and headline invariant
# 1 = mismatch found
# `bash CHECK` runs the grader, and so does `./CHECK`. The grade itself lives in
# grade.py because this artifact publishes character counts of a UTF-8 slice and
# awk measures bytes; this wrapper exists so the documented invocation is the one
# that actually runs. The previous revision was a Python file under a
# `bash CHECK` line, so it exited 2 on `import: command not found` without
# grading anything, and its own leg ran the extractor over the live directory and
# rewrote the committed tables in place.
#
# A re-run of `python3 wizard-work/ledger/2026-10-28-checkin-census/CHECK`
# re-derives everything from the current tree, so the check stays green only
# while the census honestly reflects `checkins/`.
import csv
import datetime
import json
import re
import subprocess
import sys
from pathlib import Path
# Exit codes: 0 every leg passed; 1 a fault; 2 cannot grade (no python3 or git).
set -u
BASE = Path("/root/troupe-standup")
CHECKS = BASE / "checkins"
OUT = BASE / "wizard-work/ledger/2026-10-28-checkin-census"
EXTRACTOR = OUT / "extract_checkin_census_2026_10_28.py"
TSV = OUT / "checkin-census-2026-10-28.tsv"
JSON = OUT / "checkin-census-2026-10-28.json"
MD = OUT / "checkin-census-2026-10-28.md"
EXCLUDE = {"README.md"}
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if ! command -v python3 >/dev/null 2>&1; then
printf 'CANNOT GRADE: python3 not on PATH\n' >&2
exit 2
fi
if ! command -v git >/dev/null 2>&1; then
printf 'CANNOT GRADE: git not on PATH\n' >&2
exit 2
fi
def naive_parse(path):
"""Second, independent implementation to cross-derive the census.
Uses plain string-finding on the raw text (no section regex, no line
splitting). Bounds the `## Did` section the same way the extractor does
(up to the next `## ` heading) and reports did_items / did_chars on that
exact slice, so both implementations must agree on every row.
"""
import re as _re
name = path.name
body = name[:-3]
m = _re.match(r"^(\d{4}-\d{2}-\d{2})-(.+)$", body)
if not m:
return None
date_str, signer = m.group(1), m.group(2)
try:
datetime.date.fromisoformat(date_str)
except ValueError:
return None
text = path.read_text(encoding="utf-8", errors="replace")
t = text.replace("\r\n", "\n").replace("\r", "\n")
def section(name):
# A heading may sit at file start (no preceding newline).
if t.startswith(name):
idx = 0
else:
idx = t.find("\n" + name)
if idx == -1:
return ""
idx += 1 # "\n" at idx; "##" one char later
nxt = t.find("\n## ", idx)
return t[idx:nxt if nxt != -1 else len(t)]
has_done = t.find("\n## Did") != -1 or t.startswith("## Did")
has_blocked = t.find("\n## Blocked") != -1 or t.startswith("## Blocked")
has_next = t.find("\n## Next") != -1 or t.startswith("## Next")
has_ask = t.find("\n## Ask") != -1 or t.startswith("## Ask")
did = section("## Did")
did_items = 0
for line in did.split("\n"):
s2 = line.strip()
if s2.startswith("### "):
rest = s2[len("### "):]
if len(rest) > 1 and rest[0].isdigit() and rest[1] == ".":
did_items += 1
did_chars = len(did)
return {"date": date_str, "signer": signer, "file": name,
"has_done": has_done, "has_blocked": has_blocked,
"has_next": has_next, "has_ask": has_ask,
"did_items": did_items, "did_chars": did_chars}
def rederive():
rows = []
for path in sorted(CHECKS.iterdir()):
if path.name in EXCLUDE or not path.is_file():
continue
r = naive_parse(path)
if r is not None:
rows.append(r)
return rows
def load_tsv():
with open(TSV, encoding="utf-8", newline="") as f:
return list(csv.DictReader(f, delimiter="\t"))
def load_json():
return json.loads(JSON.read_text(encoding="utf-8"))
def same(a, b):
return a == b
def main():
errors = []
# 1) The extractor must run clean.
proc = subprocess.run([sys.executable, str(EXTRACTOR), str(CHECKS), str(OUT)],
capture_output=True, text=True)
if proc.returncode != 0:
print("CHECK FAIL: extractor exited {!r}\nstd{}out{}end{}".format(
proc.returncode, "out:", proc.stdout, "end:", proc.stderr))
return 1
# 2) Independent re-derivation vs persisted TSV.
expect = rederive()
actual = load_tsv()
if len(expect) != len(actual):
errors.append("row count: re-derived {} != persisted {}".format(len(expect), len(actual)))
for i in range(max(len(expect), len(actual))):
e = expect[i] if i < len(expect) else None
a = actual[i] if i < len(actual) else None
if e is None or a is None:
errors.append("row {} side mismatch".format(i))
continue
if e["date"] != a["date"]:
errors.append("row {} date {} != {}".format(i, e["date"], a["date"]))
if e["signer"] != a["signer"]:
errors.append("row {} signer {} != {}".format(i, e["signer"], a["signer"]))
if e["has_done"] != (a["has_done"] == "True"):
errors.append("row {} has_done mismatch".format(i))
if e["did_items"] != int(a["did_items"]):
errors.append("row {} did_items {} != {}".format(i, e["did_items"], a["did_items"]))
if e["did_chars"] != int(a["did_chars"]):
errors.append("row {} did_chars mismatch".format(i))
# 3) Headline invariants from the JSON.
d = load_json()
h = d.get("headline", {})
if h.get("files") != len(expect):
errors.append("headline.files {} != re-derived {}".format(h.get("files"), len(expect)))
if h.get("skipped_non_dated") != 1:
errors.append("headline.skipped != 1")
signers = {r["signer"] for r in expect}
if h.get("signers") != len(signers):
errors.append("headline.signers {} != re-derived {}".format(h.get("signers"), len(signers)))
# Re-derived latest per signer must match JSON's latest_per_signer.
latest = {}
for r in expect:
cur = latest.get(r["signer"])
if cur is None or r["date"] > cur["date"]:
latest[r["signer"]] = {"signer": r["signer"], "date": r["date"], "file": r["file"]}
j_latest = {x["signer"]: x for x in h.get("latest_per_signer", [])}
if set(latest) != set(j_latest):
errors.append("latest signers mismatch: {} != {}".format(set(latest), set(j_latest)))
for s, rec in latest.items():
if s not in j_latest:
continue
if j_latest[s]["latest_date"] != rec["date"] or j_latest[s]["file"] != rec["file"]:
errors.append("latest {} mismatch: json {} vs re-derived {}".format(
s, j_latest[s], rec))
if errors:
print("CHECK FAIL:")
for e in errors:
print(" -", e)
return 1
print("CHECK PASS: {} dated check-ins, {} signers, 1 non-dated skipped, "
"all rows + headline re-derive clean".format(len(expect), len(signers)))
return 0
if __name__ == "__main__":
sys.exit(main())
exec python3 "$HERE/grade.py" "$@"
@@ -1,13 +1,13 @@
{
"source": "/root/troupe-standup/checkins",
"out_dir": "/root/troupe-standup/wizard-work/ledger/2026-10-28-checkin-census",
"source": "git ls-tree ab7767d7f081e88c526de72c75ac5781d035270e -- checkins/",
"pin": "ab7767d7f081e88c526de72c75ac5781d035270e",
"generated": "2026-10-28",
"rows": [
{
"date": "2026-09-24",
"signer": "zephyrus-02",
"file": "2026-09-24-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-09-24-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-24-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -19,7 +19,7 @@
"date": "2026-09-24",
"signer": "zephyrus",
"file": "2026-09-24-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-24-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-24-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -31,7 +31,7 @@
"date": "2026-09-25",
"signer": "zephyrus",
"file": "2026-09-25-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-25-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-25-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": true,
@@ -43,7 +43,7 @@
"date": "2026-09-26",
"signer": "zephyrus-02",
"file": "2026-09-26-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-09-26-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-26-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -55,7 +55,7 @@
"date": "2026-09-26",
"signer": "zephyrus",
"file": "2026-09-26-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-26-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-26-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -67,7 +67,7 @@
"date": "2026-09-27",
"signer": "zephyrus",
"file": "2026-09-27-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-27-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-27-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -79,7 +79,7 @@
"date": "2026-09-28",
"signer": "zephyrus-02",
"file": "2026-09-28-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-09-28-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-28-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -91,7 +91,7 @@
"date": "2026-09-28",
"signer": "zephyrus",
"file": "2026-09-28-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-28-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-28-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -103,7 +103,7 @@
"date": "2026-09-30",
"signer": "zephyrus",
"file": "2026-09-30-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-09-30-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-09-30-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -115,7 +115,7 @@
"date": "2026-10-01",
"signer": "zephyrus-03",
"file": "2026-10-01-zephyrus-03.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus-03.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus-03.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -127,7 +127,7 @@
"date": "2026-10-01",
"signer": "zephyrus-04",
"file": "2026-10-01-zephyrus-04.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus-04.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus-04.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -139,7 +139,7 @@
"date": "2026-10-01",
"signer": "zephyrus-05",
"file": "2026-10-01-zephyrus-05.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus-05.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus-05.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -151,7 +151,7 @@
"date": "2026-10-01",
"signer": "zephyrus-06",
"file": "2026-10-01-zephyrus-06.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus-06.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus-06.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -163,7 +163,7 @@
"date": "2026-10-01",
"signer": "zephyrus-07",
"file": "2026-10-01-zephyrus-07.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus-07.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus-07.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -175,7 +175,7 @@
"date": "2026-10-01",
"signer": "zephyrus",
"file": "2026-10-01-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-01-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-01-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -187,7 +187,7 @@
"date": "2026-10-02",
"signer": "zephyrus-01",
"file": "2026-10-02-zephyrus-01.md",
"abs": "/root/troupe-standup/checkins/2026-10-02-zephyrus-01.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-02-zephyrus-01.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -199,7 +199,7 @@
"date": "2026-10-03",
"signer": "zephyrus-02",
"file": "2026-10-03-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-03-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-03-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -211,7 +211,7 @@
"date": "2026-10-03",
"signer": "zephyrus-03",
"file": "2026-10-03-zephyrus-03.md",
"abs": "/root/troupe-standup/checkins/2026-10-03-zephyrus-03.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-03-zephyrus-03.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -223,7 +223,7 @@
"date": "2026-10-03",
"signer": "zephyrus",
"file": "2026-10-03-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-03-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-03-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -235,7 +235,7 @@
"date": "2026-10-04",
"signer": "zephyrus-02",
"file": "2026-10-04-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-04-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-04-zephyrus-02.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -247,7 +247,7 @@
"date": "2026-10-04",
"signer": "zephyrus",
"file": "2026-10-04-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-04-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-04-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -259,7 +259,7 @@
"date": "2026-10-05",
"signer": "zephyrus-02",
"file": "2026-10-05-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-05-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-05-zephyrus-02.md",
"has_done": false,
"has_blocked": true,
"has_next": false,
@@ -271,7 +271,7 @@
"date": "2026-10-05",
"signer": "zephyrus-03",
"file": "2026-10-05-zephyrus-03.md",
"abs": "/root/troupe-standup/checkins/2026-10-05-zephyrus-03.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-05-zephyrus-03.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -283,7 +283,7 @@
"date": "2026-10-05",
"signer": "zephyrus",
"file": "2026-10-05-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-05-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-05-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -295,7 +295,7 @@
"date": "2026-10-06",
"signer": "cassian",
"file": "2026-10-06-cassian.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-cassian.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-cassian.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -307,7 +307,7 @@
"date": "2026-10-06",
"signer": "zephyrus-02",
"file": "2026-10-06-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -319,7 +319,7 @@
"date": "2026-10-06",
"signer": "zephyrus-04",
"file": "2026-10-06-zephyrus-04.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-04.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-04.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -331,7 +331,7 @@
"date": "2026-10-06",
"signer": "zephyrus-05",
"file": "2026-10-06-zephyrus-05.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-05.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-05.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -343,7 +343,7 @@
"date": "2026-10-06",
"signer": "zephyrus-06",
"file": "2026-10-06-zephyrus-06.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-06.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-06.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -355,7 +355,7 @@
"date": "2026-10-06",
"signer": "zephyrus-wizards-02",
"file": "2026-10-06-zephyrus-wizards-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-wizards-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-wizards-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -367,7 +367,7 @@
"date": "2026-10-06",
"signer": "zephyrus-wizards-03",
"file": "2026-10-06-zephyrus-wizards-03.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-wizards-03.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-wizards-03.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -379,7 +379,7 @@
"date": "2026-10-06",
"signer": "zephyrus-wizards",
"file": "2026-10-06-zephyrus-wizards.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus-wizards.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus-wizards.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -391,7 +391,7 @@
"date": "2026-10-06",
"signer": "zephyrus",
"file": "2026-10-06-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-06-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-06-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -403,7 +403,7 @@
"date": "2026-10-07",
"signer": "cassian-02",
"file": "2026-10-07-cassian-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-cassian-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-cassian-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -415,7 +415,7 @@
"date": "2026-10-07",
"signer": "cassian-q5-census",
"file": "2026-10-07-cassian-q5-census.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-cassian-q5-census.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-cassian-q5-census.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -427,7 +427,7 @@
"date": "2026-10-07",
"signer": "cassian-q5-recount",
"file": "2026-10-07-cassian-q5-recount.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-cassian-q5-recount.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-cassian-q5-recount.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -439,7 +439,7 @@
"date": "2026-10-07",
"signer": "cassian",
"file": "2026-10-07-cassian.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-cassian.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-cassian.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -451,7 +451,7 @@
"date": "2026-10-07",
"signer": "zephyrus-02",
"file": "2026-10-07-zephyrus-02.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-zephyrus-02.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-zephyrus-02.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -463,7 +463,7 @@
"date": "2026-10-07",
"signer": "zephyrus",
"file": "2026-10-07-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-07-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-07-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -475,7 +475,7 @@
"date": "2026-10-08",
"signer": "merlin",
"file": "2026-10-08-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-08-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-08-merlin.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -487,7 +487,7 @@
"date": "2026-10-08",
"signer": "zephyrus",
"file": "2026-10-08-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-08-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-08-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -499,7 +499,7 @@
"date": "2026-10-09",
"signer": "merlin",
"file": "2026-10-09-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-09-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-09-merlin.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -511,7 +511,7 @@
"date": "2026-10-09",
"signer": "zephyrus",
"file": "2026-10-09-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-09-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-09-zephyrus.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -523,7 +523,7 @@
"date": "2026-10-10",
"signer": "mercury",
"file": "2026-10-10-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-10-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-10-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -535,7 +535,7 @@
"date": "2026-10-10",
"signer": "merlin",
"file": "2026-10-10-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-10-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-10-merlin.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -547,7 +547,7 @@
"date": "2026-10-10",
"signer": "zephyrus",
"file": "2026-10-10-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-10-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-10-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": true,
@@ -559,7 +559,7 @@
"date": "2026-10-11",
"signer": "mercury",
"file": "2026-10-11-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-11-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-11-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -571,7 +571,7 @@
"date": "2026-10-11",
"signer": "merlin",
"file": "2026-10-11-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-11-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-11-merlin.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -583,7 +583,7 @@
"date": "2026-10-11",
"signer": "zephyrus",
"file": "2026-10-11-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-11-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-11-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": true,
@@ -595,7 +595,7 @@
"date": "2026-10-12",
"signer": "mercury",
"file": "2026-10-12-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-12-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-12-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -607,7 +607,7 @@
"date": "2026-10-12",
"signer": "merlin",
"file": "2026-10-12-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-12-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-12-merlin.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -619,7 +619,7 @@
"date": "2026-10-12",
"signer": "zephyrus",
"file": "2026-10-12-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-12-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-12-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -631,7 +631,7 @@
"date": "2026-10-13",
"signer": "gemini",
"file": "2026-10-13-gemini.md",
"abs": "/root/troupe-standup/checkins/2026-10-13-gemini.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-13-gemini.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -643,7 +643,7 @@
"date": "2026-10-13",
"signer": "mercury",
"file": "2026-10-13-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-13-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-13-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -655,7 +655,7 @@
"date": "2026-10-13",
"signer": "merlin",
"file": "2026-10-13-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-13-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-13-merlin.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -667,7 +667,7 @@
"date": "2026-10-13",
"signer": "zephyrus",
"file": "2026-10-13-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-13-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-13-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -679,7 +679,7 @@
"date": "2026-10-14",
"signer": "mercury",
"file": "2026-10-14-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-14-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-14-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -691,7 +691,7 @@
"date": "2026-10-14",
"signer": "merlin",
"file": "2026-10-14-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-14-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-14-merlin.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -703,7 +703,7 @@
"date": "2026-10-14",
"signer": "zephyrus",
"file": "2026-10-14-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-14-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-14-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -715,7 +715,7 @@
"date": "2026-10-15",
"signer": "mercury",
"file": "2026-10-15-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-15-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-15-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -727,7 +727,7 @@
"date": "2026-10-15",
"signer": "merlin",
"file": "2026-10-15-merlin.md",
"abs": "/root/troupe-standup/checkins/2026-10-15-merlin.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-15-merlin.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -739,7 +739,7 @@
"date": "2026-10-15",
"signer": "volt",
"file": "2026-10-15-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-15-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-15-volt.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -751,7 +751,7 @@
"date": "2026-10-15",
"signer": "zephyrus",
"file": "2026-10-15-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-15-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-15-zephyrus.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -763,7 +763,7 @@
"date": "2026-10-16",
"signer": "mercury",
"file": "2026-10-16-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-16-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-16-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -775,7 +775,7 @@
"date": "2026-10-16",
"signer": "volt",
"file": "2026-10-16-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-16-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-16-volt.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -787,7 +787,7 @@
"date": "2026-10-16",
"signer": "zephyrus",
"file": "2026-10-16-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-16-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-16-zephyrus.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -799,7 +799,7 @@
"date": "2026-10-17",
"signer": "mercury",
"file": "2026-10-17-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-17-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-17-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -811,7 +811,7 @@
"date": "2026-10-17",
"signer": "volt",
"file": "2026-10-17-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-17-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-17-volt.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -823,7 +823,7 @@
"date": "2026-10-17",
"signer": "zephyrus",
"file": "2026-10-17-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-17-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-17-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -835,7 +835,7 @@
"date": "2026-10-18",
"signer": "mercury",
"file": "2026-10-18-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-18-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-18-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -847,7 +847,7 @@
"date": "2026-10-18",
"signer": "volt",
"file": "2026-10-18-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-18-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-18-volt.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -859,7 +859,7 @@
"date": "2026-10-18",
"signer": "zephyrus",
"file": "2026-10-18-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-18-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-18-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -871,7 +871,7 @@
"date": "2026-10-19",
"signer": "mercury",
"file": "2026-10-19-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-19-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-19-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -883,7 +883,7 @@
"date": "2026-10-19",
"signer": "volt",
"file": "2026-10-19-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-19-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-19-volt.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -895,7 +895,7 @@
"date": "2026-10-19",
"signer": "zephyrus",
"file": "2026-10-19-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-19-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-19-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -907,7 +907,7 @@
"date": "2026-10-20",
"signer": "mercury",
"file": "2026-10-20-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-20-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-20-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -919,7 +919,7 @@
"date": "2026-10-20",
"signer": "volt",
"file": "2026-10-20-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-20-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-20-volt.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -931,7 +931,7 @@
"date": "2026-10-20",
"signer": "zephyrus",
"file": "2026-10-20-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-20-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-20-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -943,7 +943,7 @@
"date": "2026-10-21",
"signer": "mercury",
"file": "2026-10-21-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-21-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-21-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -955,7 +955,7 @@
"date": "2026-10-21",
"signer": "volt",
"file": "2026-10-21-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-21-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-21-volt.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -967,7 +967,7 @@
"date": "2026-10-21",
"signer": "zephyrus",
"file": "2026-10-21-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-21-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-21-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -979,7 +979,7 @@
"date": "2026-10-22",
"signer": "mercury",
"file": "2026-10-22-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-22-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-22-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -991,7 +991,7 @@
"date": "2026-10-22",
"signer": "zephyrus",
"file": "2026-10-22-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-22-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-22-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1003,7 +1003,7 @@
"date": "2026-10-23",
"signer": "mercury",
"file": "2026-10-23-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-23-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-23-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1015,7 +1015,7 @@
"date": "2026-10-23",
"signer": "volt",
"file": "2026-10-23-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-23-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-23-volt.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1027,7 +1027,7 @@
"date": "2026-10-23",
"signer": "zephyrus",
"file": "2026-10-23-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-23-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-23-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1039,7 +1039,7 @@
"date": "2026-10-24",
"signer": "mercury",
"file": "2026-10-24-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-24-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-24-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1051,7 +1051,7 @@
"date": "2026-10-24",
"signer": "zephyrus",
"file": "2026-10-24-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-24-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-24-zephyrus.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1063,7 +1063,7 @@
"date": "2026-10-25",
"signer": "mercury",
"file": "2026-10-25-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-25-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-25-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1075,7 +1075,7 @@
"date": "2026-10-25",
"signer": "volt",
"file": "2026-10-25-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-25-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-25-volt.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1087,7 +1087,7 @@
"date": "2026-10-25",
"signer": "zephyrus",
"file": "2026-10-25-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-25-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-25-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -1099,7 +1099,7 @@
"date": "2026-10-26",
"signer": "mercury",
"file": "2026-10-26-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-26-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-26-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1111,7 +1111,7 @@
"date": "2026-10-26",
"signer": "zephyrus",
"file": "2026-10-26-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-26-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-26-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -1123,7 +1123,7 @@
"date": "2026-10-27",
"signer": "forge",
"file": "2026-10-27-forge.md",
"abs": "/root/troupe-standup/checkins/2026-10-27-forge.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-27-forge.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1135,19 +1135,7 @@
"date": "2026-10-27",
"signer": "mercury",
"file": "2026-10-27-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-27-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
"has_ask": false,
"did_items": 0,
"did_chars": 0
},
{
"date": "2026-10-27",
"signer": "sentinel",
"file": "2026-10-27-sentinel.md",
"abs": "/root/troupe-standup/checkins/2026-10-27-sentinel.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-27-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1159,7 +1147,7 @@
"date": "2026-10-27",
"signer": "zephyrus",
"file": "2026-10-27-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-27-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-27-zephyrus.md",
"has_done": true,
"has_blocked": false,
"has_next": false,
@@ -1171,7 +1159,7 @@
"date": "2026-10-28",
"signer": "mercury",
"file": "2026-10-28-mercury.md",
"abs": "/root/troupe-standup/checkins/2026-10-28-mercury.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-28-mercury.md",
"has_done": false,
"has_blocked": false,
"has_next": false,
@@ -1183,7 +1171,7 @@
"date": "2026-10-28",
"signer": "sentinel",
"file": "2026-10-28-sentinel.md",
"abs": "/root/troupe-standup/checkins/2026-10-28-sentinel.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-28-sentinel.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -1195,7 +1183,7 @@
"date": "2026-10-28",
"signer": "volt",
"file": "2026-10-28-volt.md",
"abs": "/root/troupe-standup/checkins/2026-10-28-volt.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-28-volt.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -1207,7 +1195,7 @@
"date": "2026-10-28",
"signer": "zephyrus",
"file": "2026-10-28-zephyrus.md",
"abs": "/root/troupe-standup/checkins/2026-10-28-zephyrus.md",
"blob": "ab7767d7f081e88c526de72c75ac5781d035270e:checkins/2026-10-28-zephyrus.md",
"has_done": true,
"has_blocked": true,
"has_next": true,
@@ -1220,7 +1208,7 @@
"README.md"
],
"headline": {
"files": 101,
"files": 100,
"skipped_non_dated": 1,
"signers": 21,
"total_did_items": 11,
@@ -1,12 +1,12 @@
# Check-in census - 2026-10-28
*source*: every `YYYY-MM-DD-<signer>.md` file under `checkins/` (non-dated files like README.md skipped). Per-row section flags and did-item counts; `latest_per_signer` = newest dated check-in per signer.
*source*: every `YYYY-MM-DD-<signer>.md` file under `checkins/` at revision `ab7767d7f081e88c526de72c75ac5781d035270e` (non-dated files like README.md skipped). Per-row section flags and did-item counts; `latest_per_signer` = newest dated check-in per signer.
## Headline
| measure | value |
| --- | --- |
| dated check-in files | 101 |
| dated check-in files | 100 |
| non-dated files skipped | 1 |
| signers | 21 |
| total did items | 11 |
@@ -137,7 +137,6 @@
| 2026-10-26 | zephyrus | `2026-10-26-zephyrus.md` | 1 | 0 | 0 | 0 | 0 | 6050 |
| 2026-10-27 | forge | `2026-10-27-forge.md` | 0 | 0 | 0 | 0 | 0 | 0 |
| 2026-10-27 | mercury | `2026-10-27-mercury.md` | 0 | 0 | 0 | 0 | 0 | 0 |
| 2026-10-27 | sentinel | `2026-10-27-sentinel.md` | 0 | 0 | 0 | 0 | 0 | 0 |
| 2026-10-27 | zephyrus | `2026-10-27-zephyrus.md` | 1 | 0 | 0 | 0 | 5 | 9085 |
| 2026-10-28 | mercury | `2026-10-28-mercury.md` | 0 | 0 | 0 | 0 | 0 | 0 |
| 2026-10-28 | sentinel | `2026-10-28-sentinel.md` | 1 | 1 | 1 | 1 | 0 | 1968 |
@@ -94,7 +94,6 @@ date signer file has_done has_blocked has_next has_ask did_items did_chars
2026-10-26 zephyrus 2026-10-26-zephyrus.md True False False False 0 6050
2026-10-27 forge 2026-10-27-forge.md False False False False 0 0
2026-10-27 mercury 2026-10-27-mercury.md False False False False 0 0
2026-10-27 sentinel 2026-10-27-sentinel.md False False False False 0 0
2026-10-27 zephyrus 2026-10-27-zephyrus.md True False False False 5 9085
2026-10-28 mercury 2026-10-28-mercury.md False False False False 0 0
2026-10-28 sentinel 2026-10-28-sentinel.md True True True True 0 1968
1 date signer file has_done has_blocked has_next has_ask did_items did_chars
94 2026-10-26 zephyrus 2026-10-26-zephyrus.md True False False False 0 6050
95 2026-10-27 forge 2026-10-27-forge.md False False False False 0 0
96 2026-10-27 mercury 2026-10-27-mercury.md False False False False 0 0
2026-10-27 sentinel 2026-10-27-sentinel.md False False False False 0 0
97 2026-10-27 zephyrus 2026-10-27-zephyrus.md True False False False 5 9085
98 2026-10-28 mercury 2026-10-28-mercury.md False False False False 0 0
99 2026-10-28 sentinel 2026-10-28-sentinel.md True True True True 0 1968
@@ -1,39 +1,69 @@
#!/usr/bin/env python3
"""Extract the daily check-in census from `checkins/` into tables.
"""Extract the 2026-10-28 daily check-in census from a PINNED revision.
Reads every dated check-in markdown file under `checkins/` (filename
`YYYY-MM-DD-<signer>.md`), detects which of the four ledger sections
(`## Did`, `## Blocked`, `## Next`, `## Ask`) each file carries, and counts
the numbered `### N.` items under `## Did`. Emits:
WHY THIS REVISION EXISTS (Q151). The first version walked `checkins/` on disk
with `os.scandir` and recorded `abs` / `source` / `out_dir` as absolute
`/root/troupe-standup` paths, so it published the working tree rather than any
commit: the committed tables carried 101 rows including
`2026-10-27-sentinel.md`, a file no revision of this repository holds, while the
revision that carries the tables (`ab7767d`) holds 100 dated check-ins. The
scope is now a git revision; nothing is read from the disk, and no host path
appears in the output.
checkin-census-<YYYY-MM-DD>.tsv - machine table (date, signer, file, flags, counts)
checkin-census-<YYYY-MM-DD>.md - human markdown table + per-signer summary
checkin-census-<YYYY-MM-DD>.json - structured rows + headline
Reads `<rev>:checkins/<name>` for every dated `YYYY-MM-DD-<signer>.md` file at
`<rev>`, detects which of the four ledger sections (`## Did`, `## Blocked`,
`## Next`, `## Ask`) each file carries, counts the numbered `### N.` items inside
`## Did`, and emits the three tables the artifact has always published.
Exit status is 0 only when every dated check-in parsed, every date is valid,
and the rows reconcile with the file inventory (no gaps, no dupes). Any
failure is a nonzero exit.
Writes nothing unless `--write` is given: a generator that rewrites a committed
artifact as a side effect of being graded is how a green gate regenerates its
own input in place.
Exit status: 0 derived (and, with --write, published); 1 the pin is unreadable
or the rows fail an invariant; 2 bad arguments.
Usage:
extract_checkin_census_2026_10_28.py [checkins_dir] [out_dir]
extract_checkin_census_2026_10_28.py --pin ab7767d # dry run
extract_checkin_census_2026_10_28.py --pin ab7767d --write # publish
extract_checkin_census_2026_10_28.py --pin ab7767d --outdir DIR --tag TAG --write
"""
import argparse
import csv
import json
import os
import re
import subprocess
import sys
from datetime import datetime
from pathlib import Path
DEFAULT_CHECKINS = Path("/root/troupe-standup/checkins")
DEFAULT_OUT = Path("/root/troupe-standup/wizard-work/ledger") / "2026-10-28-checkin-census"
HERE = Path(__file__).resolve().parent
SECTION_RE = re.compile(r"^## (Did|Blocked|Next|Ask)\s*$", re.MULTILINE)
ITEM_RE = re.compile(r"^\s*### \d+\.\s+\S", re.M)
FILENAME_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-(.+)$")
def repo_root():
"""The git toplevel that owns this file, else the tree three levels up."""
try:
out = subprocess.run(["git", "-C", str(HERE), "rev-parse", "--show-toplevel"],
capture_output=True, text=True, timeout=60)
if out.returncode == 0 and out.stdout.strip():
return Path(out.stdout.strip())
except (OSError, subprocess.SubprocessError):
pass
return HERE.parent.parent.parent
def git(repo, *args, binary=False):
out = subprocess.run(["git", "-C", str(repo)] + list(args),
capture_output=True, text=not binary, timeout=120)
if out.returncode != 0:
err = out.stderr if not binary else (out.stderr or b"").decode("utf-8", "replace")
raise RuntimeError("git %s failed: %s" % (" ".join(args), (err or "").strip()))
return out.stdout
def parse_name(name):
"""Return (date, signer) from a check-in filename; None on any failure."""
if not name.endswith(".md"):
@@ -50,18 +80,28 @@ def parse_name(name):
return date_str, signer
def pin_names(repo, rev):
"""(sorted dated basenames, sorted non-dated basenames) under checkins/ at rev."""
out = git(repo, "ls-tree", "-r", "--name-only", rev, "--", "checkins/")
dated, skipped = [], []
for path in out.splitlines():
if not path.startswith("checkins/") or not path.endswith(".md"):
continue
base = path[len("checkins/"):]
if "/" in base:
continue
(dated if parse_name(base) else skipped).append(base)
return sorted(dated), sorted(skipped)
def section_bounds(text):
"""Return ordered list of (section_name, start, end) offsets."""
bounds = []
for m in SECTION_RE.finditer(text):
bounds.append((m.group(1), m.start(), m.end()))
return bounds
return [(m.group(1), m.start(), m.end()) for m in SECTION_RE.finditer(text)]
def section_text(text, name):
"""Text of one section (up to the next section, or EOF)."""
bounds = section_bounds(text)
for i, (n, start, end) in enumerate(bounds):
def section_text(text, name, bounds):
"""Text of one section (up to the next recognised section, or EOF)."""
for i, (n, start, _end) in enumerate(bounds):
if n != name:
continue
nxt = bounds[i + 1][1] if i + 1 < len(bounds) else len(text)
@@ -71,37 +111,35 @@ def section_text(text, name):
def analyze(text):
"""Return presence flags + did-item count + did chars for a file."""
present = {name: False for name in ("Did", "Blocked", "Next", "Ask")}
for n, _start, _end in section_bounds(text):
present[n] = True
did = section_text(text, "Did")
bounds = section_bounds(text)
present = {n for n, _s, _e in bounds}
did = section_text(text, "Did", bounds)
return {
"has_done": present["Did"],
"has_blocked": present["Blocked"],
"has_next": present["Next"],
"has_ask": present["Ask"],
"has_done": "Did" in present,
"has_blocked": "Blocked" in present,
"has_next": "Next" in present,
"has_ask": "Ask" in present,
"did_items": len(ITEM_RE.findall(did)),
"did_chars": len(did),
}
def parse_checkins(checkins_dir):
def blob(repo, rev, path):
return git(repo, "show", "%s:%s" % (rev, path), binary=True).decode("utf-8", "replace")
def derive(repo, rev):
"""Rows + skipped list, every byte read through git at <rev>."""
dated, skipped = pin_names(repo, rev)
rows = []
skipped = []
for path in sorted(checkins_dir.iterdir()):
if not path.is_file():
continue
parsed = parse_name(path.name)
if parsed is None:
skipped.append(path.name)
continue
date_str, signer = parsed
text = path.read_text(encoding="utf-8", errors="replace")
for name in dated:
date_str, signer = parse_name(name)
text = blob(repo, rev, "checkins/" + name)
rows.append({
"date": date_str,
"signer": signer,
"file": path.name,
"abs": str(path),
"file": name,
"blob": "%s:checkins/%s" % (rev, name),
**analyze(text),
})
return rows, skipped
@@ -110,110 +148,125 @@ def parse_checkins(checkins_dir):
def latest_per_signer(rows):
out = {}
for r in rows:
key = r["signer"]
cur = out.get(key)
cur = out.get(r["signer"])
if cur is None or r["date"] > cur["latest_date"]:
cur = {"signer": key, "latest_date": r["date"], "file": r["file"]}
out[key] = cur
out[r["signer"]] = {"signer": r["signer"], "latest_date": r["date"],
"file": r["file"]}
return sorted(out.values(), key=lambda x: x["signer"])
def write_outputs(out_dir, rows, skipped):
def write_outputs(out_dir, tag, rev, rows, skipped):
out_dir.mkdir(parents=True, exist_ok=True)
tag = os.environ.get("LIFT_DATE", datetime.now().strftime("%Y-%m-%d"))
csv_path = out_dir / ("checkin-census-" + tag + ".tsv")
tsv_path = out_dir / ("checkin-census-" + tag + ".tsv")
json_path = out_dir / ("checkin-census-" + tag + ".json")
md_path = out_dir / ("checkin-census-" + tag + ".md")
fieldnames = ["date", "signer", "file", "has_done", "has_blocked", "has_next", "has_ask", "did_items", "did_chars"]
with open(csv_path, "w", encoding="utf-8", newline="") as f:
fieldnames = ["date", "signer", "file", "has_done", "has_blocked", "has_next",
"has_ask", "did_items", "did_chars"]
with open(tsv_path, "w", encoding="utf-8", newline="") as f:
w = csv.DictWriter(f, fieldnames=fieldnames, delimiter="\t")
w.writeheader()
for r in rows:
w.writerow({k: r.get(k, "") for k in fieldnames})
json.dump({
"source": str(DEFAULT_CHECKINS),
"out_dir": str(out_dir),
"source": "git ls-tree %s -- checkins/" % rev,
"pin": rev,
"generated": tag,
"rows": rows,
"skipped": skipped,
"headline": {
"files": len(rows),
"skipped_non_dated": len(skipped),
"signers": len(set(r["signer"] for r in rows)),
"signers": len({r["signer"] for r in rows}),
"total_did_items": sum(r["did_items"] for r in rows),
"total_did_chars": sum(r["did_chars"] for r in rows),
"latest_per_signer": latest_per_signer(rows),
},
}, open(json_path, "w", encoding="utf-8"), indent=2)
lines = []
w = lines.append
w("# Check-in census - " + tag)
w("")
w("*source*: every `YYYY-MM-DD-<signer>.md` file under `checkins/` "
"(non-dated files like README.md skipped). Per-row section flags and "
"did-item counts; `latest_per_signer` = newest dated check-in per signer.")
w("")
w("## Headline")
w("")
w("| measure | value |")
w("| --- | --- |")
w("| dated check-in files | {} |".format(len(rows)))
w("| non-dated files skipped | {} |".format(len(skipped)))
w("| signers | {} |".format(len(set(r['signer'] for r in rows))))
w("| total did items | {} |".format(sum(r['did_items'] for r in rows)))
w("| total did chars | {} |".format(sum(r['did_chars'] for r in rows)))
w("")
w("## Per-signer latest check-in")
w("")
w("| signer | latest check-in | file |")
w("| --- | --- | --- |")
w = []
w.append("# Check-in census - " + tag)
w.append("")
w.append("*source*: every `YYYY-MM-DD-<signer>.md` file under `checkins/` at "
"revision `%s` (non-dated files like README.md skipped). Per-row section "
"flags and did-item counts; `latest_per_signer` = newest dated check-in "
"per signer." % rev)
w.append("")
w.append("## Headline")
w.append("")
w.append("| measure | value |")
w.append("| --- | --- |")
w.append("| dated check-in files | {} |".format(len(rows)))
w.append("| non-dated files skipped | {} |".format(len(skipped)))
w.append("| signers | {} |".format(len({r["signer"] for r in rows})))
w.append("| total did items | {} |".format(sum(r["did_items"] for r in rows)))
w.append("| total did chars | {} |".format(sum(r["did_chars"] for r in rows)))
w.append("")
w.append("## Per-signer latest check-in")
w.append("")
w.append("| signer | latest check-in | file |")
w.append("| --- | --- | --- |")
for s in latest_per_signer(rows):
w("| {} | {} | `{}` |".format(s["signer"], s["latest_date"], s["file"]))
w("")
w("## Full inventory")
w("")
w("| date | signer | file | Did | Blocked | Next | Ask | did items | did chars |")
w("| --- | --- | --- | --- | --- | --- | --- | --- | --- |")
w.append("| {} | {} | `{}` |".format(s["signer"], s["latest_date"], s["file"]))
w.append("")
w.append("## Full inventory")
w.append("")
w.append("| date | signer | file | Did | Blocked | Next | Ask | did items | did chars |")
w.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- |")
for r in sorted(rows, key=lambda x: (x["date"], x["signer"])):
w("| {} | {} | `{}` | {} | {} | {} | {} | {} | {} |".format(
w.append("| {} | {} | `{}` | {} | {} | {} | {} | {} | {} |".format(
r["date"], r["signer"], r["file"],
"1" if r["has_done"] else "0",
"1" if r["has_blocked"] else "0",
"1" if r["has_next"] else "0",
"1" if r["has_ask"] else "0",
r["did_items"], r["did_chars"]))
w("")
md_path.write_text("\n".join(lines), encoding="utf-8")
return csv_path, json_path, md_path
w.append("")
md_path.write_text("\n".join(w), encoding="utf-8")
return tsv_path, json_path, md_path
def main():
checkins = Path(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_CHECKINS
out_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_OUT
rows, skipped = parse_checkins(checkins)
def main(argv=None):
ap = argparse.ArgumentParser(
description="derive the check-in census from a pinned git revision")
ap.add_argument("--pin", required=True, help="revision whose checkins/ is the scope")
ap.add_argument("--outdir", default=None,
help="directory to write into (default: this artifact's directory)")
ap.add_argument("--tag", default="2026-10-28", help="artifact date tag")
ap.add_argument("--write", action="store_true",
help="publish; without it the run only derives and reports")
args = ap.parse_args(argv)
# Invariants the extractor stands behind.
assert rows, "no dated check-ins found"
assert len({r["file"] for r in rows}) == len(rows), "duplicate file"
for r in rows:
datetime.strptime(r["date"], "%Y-%m-%d")
csv_path, json_path, md_path = write_outputs(out_dir, rows, skipped)
repo = repo_root()
try:
rev = git(repo, "rev-parse", "--verify", args.pin + "^{commit}").strip()
rows, skipped = derive(repo, rev)
except RuntimeError as exc:
print("VALIDATION_FAILED: %s" % exc, file=sys.stderr)
return 1
except (OSError, subprocess.SubprocessError, AttributeError, ValueError) as exc:
print("CANNOT GRADE: %s: %s" % (type(exc).__name__, exc), file=sys.stderr)
return 2
print("files={} skipped={} signers={} rows={}".format(
len(rows), len(skipped), len(set(r['signer'] for r in rows)), len(rows)))
print("wrote: csv={} json={} md={}".format(csv_path, json_path, md_path))
if not rows:
print("VALIDATION_FAILED: no dated check-ins at %s" % rev[:12], file=sys.stderr)
return 1
if len({r["file"] for r in rows}) != len(rows):
print("VALIDATION_FAILED: duplicate file", file=sys.stderr)
return 1
print("pin={} files={} skipped={} signers={} did_items={} did_chars={}".format(
rev[:12], len(rows), len(skipped), len({r["signer"] for r in rows}),
sum(r["did_items"] for r in rows), sum(r["did_chars"] for r in rows)))
if not args.write:
print("dry run: nothing written (pass --write to publish)")
return 0
out = Path(args.outdir) if args.outdir else HERE
for p in write_outputs(out, args.tag, rev, rows, skipped):
print("wrote:", p)
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except AssertionError as exc:
print("VALIDATION_FAILED: {}".format(exc), file=sys.stderr)
sys.exit(1)
except Exception as exc: # noqa: BLE001
print("EXTRACTION_FAILED: {}".format(exc), file=sys.stderr)
sys.exit(1)
sys.exit(main())
+327
View File
@@ -0,0 +1,327 @@
#!/usr/bin/env python3
"""Grade the 2026-10-28 check-in census against its PINNED revision.
WHAT IT GRADES -- seven legs, each re-deriving from `git show <pin>:checkins/...`
and comparing against the committed tables. Nothing is read from the working
tree, so a check-in that lands tomorrow cannot red this artifact, and nothing is
written, so a run cannot regenerate its own input.
1 scope the pin file beside this script: header, sortedness, uniqueness, names
2 pin the name list equals `checkins/` at the pinned commit (SKIP if the
object is not in this database -- the committed list is then the pin)
3 tsv every cell of the committed TSV against the re-derivation
4 md-inv the MD's full-inventory rows
5 md-head the MD's headline measures
6 md-late the MD's per-signer latest block
7 json the JSON's rows, headline counters and pin field
The re-derivation is deliberately a second implementation -- line-oriented
section finding, not the committed extractor's section regex -- and it never
imports that extractor, so the two cannot agree by sharing a bug.
EXIT CODES: 0 every leg passed; 1 a fault (including "the pin is not a usable
input", which is a refusal to grade rather than a fallback to today's disk);
2 cannot grade at all (no git, no python3 in the wrapper).
Usage: python3 grade.py [--repo DIR] (or `bash CHECK`, which runs this file)
"""
import argparse
import csv
import json
import re
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
PIN_FILE = HERE / "2026-10-28-checkin-census.files"
TAG = "2026-10-28"
TSV = HERE / ("checkin-census-" + TAG + ".tsv")
MD = HERE / ("checkin-census-" + TAG + ".md")
JSON = HERE / ("checkin-census-" + TAG + ".json")
SECTIONS = ("## Did", "## Blocked", "## Next", "## Ask")
NAME_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-(.+)\.md$")
ITEM_RE = re.compile(r"^\s*### \d+\.\s+\S")
class Fault(Exception):
"""A graded leg failed; the message is the verdict line."""
class CannotGrade(Exception):
"""The instrument itself cannot run."""
def git(repo, *args):
try:
out = subprocess.run(["git", "-C", str(repo)] + list(args),
capture_output=True, timeout=180)
except (OSError, subprocess.SubprocessError) as exc:
raise CannotGrade("git %s: %s" % (args[0], exc))
if out.returncode != 0:
raise Fault("git %s failed: %s" % (" ".join(args),
out.stderr.decode("utf-8", "replace").strip()))
return out.stdout
def find_repo():
for cand in (HERE, HERE.parent):
r = subprocess.run(["git", "-C", str(cand), "rev-parse", "--show-toplevel"],
capture_output=True, text=True)
if r.returncode == 0 and r.stdout.strip():
return Path(r.stdout.strip())
return HERE.parent.parent.parent
# --------------------------------------------------------------- the pin
def require_pin(repo, sha):
"""A revision this clone does not carry cannot grade a census of it."""
try:
git(repo, "cat-file", "-e", sha + "^{commit}")
except Fault:
raise CannotGrade("pin %s is not in this object database: a census of that "
"revision cannot be graded without it" % sha[:12])
def read_pin():
if not PIN_FILE.is_file() or PIN_FILE.stat().st_size == 0:
raise Fault("SCOPE NOT ESTABLISHED: pinned name list missing or empty: %s"
% PIN_FILE.name)
lines = PIN_FILE.read_text(encoding="utf-8").splitlines()
m = re.match(r"^# pin checkins@([0-9a-f]{40}) tree=([0-9a-f]{40})$", lines[0])
if not m:
raise Fault("SCOPE NOT ESTABLISHED: pin header not understood: %r" % lines[0])
names = lines[1:]
if not names:
raise Fault("SCOPE NOT ESTABLISHED: pin names no files")
bad = [n for n in names if not NAME_RE.match(n)]
if bad:
raise Fault("SCOPE NOT ESTABLISHED: %d pinned name(s) are not YYYY-MM-DD-*.md "
"(first: %r)" % (len(bad), bad[0]))
if len(set(names)) != len(names):
raise Fault("SCOPE NOT ESTABLISHED: duplicated name(s) in the pin")
if sorted(names) != names:
raise Fault("SCOPE NOT ESTABLISHED: pin names are not sorted (LC_ALL=C)")
return m.group(1), m.group(2), names
def leg_pin(repo, sha, tree, names):
real = git(repo, "rev-parse", ("%s:checkins" % sha)).decode().strip()
if real != tree:
raise Fault("pin tree %s is not %s:checkins (%s)" % (tree[:12], sha[:7], real[:12]))
out = git(repo, "ls-tree", "-r", "--name-only", sha, "--", "checkins/").decode("utf-8")
at_pin = sorted(p[len("checkins/"):] for p in out.splitlines()
if p.startswith("checkins/") and NAME_RE.match(p[len("checkins/"):]))
if at_pin != names:
only_pin = [n for n in at_pin if n not in set(names)]
only_list = [n for n in names if n not in set(at_pin)]
raise Fault("pin name list != checkins/ at %s (+%d at pin, +%d in pin): %s %s"
% (sha[:7], len(only_pin), len(only_list), only_pin[:3], only_list[:3]))
return "PASS pin: %d name(s) == checkins/ tree %s at %s" % (len(names), tree[:12], sha[:7])
# ------------------------------------------------------- the re-derivation
def blob(repo, sha, name):
return git(repo, "show", "%s:checkins/%s" % (sha, name)).decode("utf-8", "replace")
def analyse(text):
"""Line-oriented: flags, numbered items under ## Did, and the slice length."""
lines = text.split("\n")
flags = {s: False for s in SECTIONS}
start = None
for i, line in enumerate(lines):
bare = line.rstrip()
if bare in flags:
flags[bare] = True
if bare == "## Did" and start is None:
start = i
items, chars = 0, 0
if start is not None:
end = len(lines)
for j in range(start + 1, len(lines)):
if lines[j].rstrip() in flags:
end = j
break
body = lines[start:end]
items = len([l for l in body[1:] if ITEM_RE.match(l)])
chars = len("\n".join(body)) + (1 if end < len(lines) else 0)
return {
"has_done": flags["## Did"], "has_blocked": flags["## Blocked"],
"has_next": flags["## Next"], "has_ask": flags["## Ask"],
"did_items": items, "did_chars": chars,
}
def derive(repo, sha, names):
rows = []
for name in names:
m = NAME_RE.match(name)
rows.append(dict(date=m.group(1), signer=m.group(2), file=name,
**analyse(blob(repo, sha, name))))
return rows
def skipped_at_pin(repo, sha):
out = git(repo, "ls-tree", "-r", "--name-only", sha + ":checkins").decode("utf-8")
return [p for p in out.splitlines()
if p.endswith(".md") and not NAME_RE.match(p)]
# ------------------------------------------------------------- the tables
def leg_tsv(rows):
with open(TSV, encoding="utf-8", newline="") as fh:
reader = csv.reader(fh, delimiter="\t")
header = next(reader)
got = [[c.strip() for c in r] for r in reader]
want_header = ["date", "signer", "file", "has_done", "has_blocked", "has_next",
"has_ask", "did_items", "did_chars"]
if header != want_header:
raise Fault("TSV header %r != %r" % (header, want_header))
want = [[r["date"], r["signer"], r["file"], str(r["has_done"]), str(r["has_blocked"]),
str(r["has_next"]), str(r["has_ask"]), str(r["did_items"]), str(r["did_chars"])]
for r in rows]
if len(got) != len(want):
raise Fault("TSV has %d row(s); the pin derives %d" % (len(got), len(want)))
for i, (g, w) in enumerate(zip(got, want)):
if g != w:
raise Fault("TSV row %d: %r != pin %r" % (i, g, w))
return "PASS tsv: %d row(s) match the pin cell for cell" % len(got)
def md_block(text, header_cells):
"""Rows of the table whose header is exactly `header_cells`, as cell lists."""
lines = text.splitlines()
out = []
inside = False
for line in lines:
if not line.startswith("|"):
if inside:
break
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if not inside:
if cells == header_cells:
inside = True
continue
if set("".join(cells)) <= set("-: "):
continue
out.append(cells)
return out
def leg_md_inventory(text, rows):
want = sorted([[r["date"], r["signer"], r["file"],
"1" if r["has_done"] else "0", "1" if r["has_blocked"] else "0",
"1" if r["has_next"] else "0", "1" if r["has_ask"] else "0",
str(r["did_items"]), str(r["did_chars"])]
for r in rows], key=lambda x: (x[0], x[1]))
got = [[c.strip("`") for c in r] for r in md_block(
text, ["date", "signer", "file", "Did", "Blocked", "Next", "Ask",
"did items", "did chars"])]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("MD inventory: %d row(s) vs pin %d; first difference at row %d "
"(md %r, pin %r)" % (len(got), len(want), bad,
got[bad] if bad < len(got) else None,
want[bad] if bad < len(want) else None))
return "PASS md-inventory: %d row(s) match the pin" % len(got)
def leg_md_headline(text, rows, skipped):
want = {
"dated check-in files": str(len(rows)),
"non-dated files skipped": str(len(skipped)),
"signers": str(len({r["signer"] for r in rows})),
"total did items": str(sum(r["did_items"] for r in rows)),
"total did chars": str(sum(r["did_chars"] for r in rows)),
}
got = {c[0]: c[1] for c in md_block(text, ["measure", "value"])}
misses = [(k, got.get(k), v) for k, v in want.items() if got.get(k) != v]
if misses:
raise Fault("MD headline disagrees with the pin: %s"
% "; ".join("%s md=%r pin=%r" % m for m in misses))
return "PASS md-headline: %d measure(s) match the pin" % len(want)
def leg_md_latest(text, rows):
latest = {}
for r in rows:
cur = latest.get(r["signer"])
if cur is None or r["date"] > cur["latest_date"]:
latest[r["signer"]] = {"signer": r["signer"], "latest_date": r["date"],
"file": r["file"]}
want = [[latest[s]["signer"], latest[s]["latest_date"], latest[s]["file"]]
for s in sorted(latest)]
got = [[c[0], c[1], c[2].strip("`")] for c in md_block(
text, ["signer", "latest check-in", "file"])]
if got != want:
raise Fault("MD per-signer latest block != pin (%d md row(s), %d derived)"
% (len(got), len(want)))
return "PASS md-latest: %d signer row(s) match the pin" % len(got)
def leg_json(rows, sha, skipped):
doc = json.loads(JSON.read_text(encoding="utf-8"))
if doc.get("pin") != sha:
raise Fault("JSON pin %r != %r" % (doc.get("pin"), sha))
got = [{k: r.get(k) for k in ("date", "signer", "file", "has_done", "has_blocked",
"has_next", "has_ask", "did_items", "did_chars")}
for r in doc.get("rows", [])]
want = [{k: r[k] for k in ("date", "signer", "file", "has_done", "has_blocked",
"has_next", "has_ask", "did_items", "did_chars")}
for r in rows]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("JSON rows: %d vs pin %d; first difference at row %d"
% (len(got), len(want), bad))
h = doc.get("headline", {})
want_h = {"files": len(rows), "signers": len({r["signer"] for r in rows}),
"skipped_non_dated": len(skipped),
"total_did_items": sum(r["did_items"] for r in rows),
"total_did_chars": sum(r["did_chars"] for r in rows)}
misses = [(k, h.get(k), v) for k, v in want_h.items() if h.get(k) != v]
if misses:
raise Fault("JSON headline disagrees with the pin: %s"
% "; ".join("%s json=%r pin=%r" % m for m in misses))
return "PASS json: %d row(s) and %d headline counter(s) match the pin" % (
len(got), len(want_h))
def main(argv=None):
ap = argparse.ArgumentParser(description="grade the 2026-10-28 check-in census")
ap.add_argument("--repo", default=None, help="repository to grade (default: this tree's)")
args = ap.parse_args(argv)
repo = Path(args.repo) if args.repo else find_repo()
try:
sha, tree, names = read_pin()
require_pin(repo, sha)
print("PASS scope: %d pinned dated file(s), pin %s" % (len(names), sha[:7]))
lines = [leg_pin(repo, sha, tree, names)]
rows = derive(repo, sha, names)
skipped = skipped_at_pin(repo, sha)
text = MD.read_text(encoding="utf-8")
lines.append(leg_tsv(rows))
lines.append(leg_md_inventory(text, rows))
lines.append(leg_md_headline(text, rows, skipped))
lines.append(leg_md_latest(text, rows))
lines.append(leg_json(rows, sha, skipped))
except Fault as exc:
print("FAIL: %s" % exc)
return 1
except CannotGrade as exc:
print("CANNOT GRADE: %s" % exc)
return 2
for line in lines:
print(line)
print("VERDICT=PASS census-%s rows=%d files=%d pin=%s"
% (TAG, len(rows), len(rows), sha[:7]))
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,103 @@
# pin checkins@b21bb10c46a805cd8ffcbdc25c5f8870ec6a710d tree=14ca8d3a46a49bf514a616cfb1f3aef775b447b5
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-forge.md
2026-10-27-mercury.md
2026-10-27-zephyrus.md
2026-10-28-mercury.md
2026-10-28-sentinel.md
2026-10-28-volt.md
2026-10-28-zephyrus.md
2026-10-29-mercury.md
2026-10-29-zephyrus.md
+23 -66
View File
@@ -1,69 +1,26 @@
#!/usr/bin/env python3
# Evidence-law gate for the check-in census. Exits 0 only if the table is
# derivable from the real files + real git objects. Nothing may be phantom.
import csv
import os
import re
import subprocess
import sys
#!/usr/bin/env bash
# CHECK -- gate for the 2026-10-29 check-in signee census.
#
# `bash CHECK` runs the grader, and so does `./CHECK`. The previous revision was
# a Python file under a `bash CHECK` line, so it exited 2 on `import: command not
# defined` without grading anything; and its legs read `git ls-files`, which is
# the live index, so every check-in that landed after 2026-10-29 reddened a
# dated artifact for no reason. The scope is now the pinned revision beside this
# file, and the grade is in grade.py so the documented invocation is the one that
# runs.
#
# Exit codes: 0 every leg passed; 1 a fault; 2 cannot grade (no python3 or git).
set -u
ROOT = "/root/troupe-standup"
os.chdir(ROOT)
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
def git(*a):
return subprocess.run(
["git", "-C", ROOT] + list(a), check=True, capture_output=True, text=True
).stdout
if ! command -v python3 >/dev/null 2>&1; then
printf 'CANNOT GRADE: python3 not on PATH\n' >&2
exit 2
fi
if ! command -v git >/dev/null 2>&1; then
printf 'CANNOT GRADE: git not on PATH\n' >&2
exit 2
fi
# Expected table, derived FROM GIT (not from the CSV).
files = sorted(f for f in git("ls-files", "checkins/").strip().split("\n") if f.endswith(".md"))
dated = [f for f in files if re.match(r"^checkins/\d{4}-\d{2}-\d{2}-", f) and not f.endswith("README.md")]
indexed = [f for f in files if f.endswith("README.md")]
print("tracked md files on git: %d | dated checkin files: %d | index file: %d" % (len(files), len(dated), len(indexed)))
rows = list(csv.DictReader(open("wizard-work/ledger/2026-10-29-checkin-census/checkin-census.csv")))
print("table rows present: %d" % len(rows))
fails = []
# 1) Row count must equal source files (index row = README.md).
if len(rows) != len(dated) + len(indexed):
fails.append("table rows != dated files + index file")
# 2) No dated file may be missing from the table, and nothing un-tracked may appear.
disk_files = set(r["file"] for r in rows)
ghost = sorted(f for f in dated if f not in disk_files)
if ghost:
fails.append("dated files missing from table: %s" % ghost)
notracked = sorted(f for f in disk_files if f not in set(files))
if notracked:
fails.append("table rows not tracked by git: %s" % notracked)
# 3) Every data row carries a real committer (a git object must exist for the file).
empty_commits = sorted(r["file"] for r in rows if not r["committer"] or r["committer"] == "(no committer)")
if empty_commits:
fails.append("rows with empty/none committer: %s" % empty_commits)
# 4) Per-signee disk counts must reconcile with the table (no lost files).
disk_counts = {}
for f in dated:
m = re.match(r"^checkins/(\d{4}-\d{2}-\d{2}-(.+?))(?:-\d+)?\.md$", f)
if m:
s = re.sub(r"-\d+$", "", m.group(2))
disk_counts[s] = disk_counts.get(s, 0) + 1
table_counts = {}
for r in rows:
if r["file"] == "checkins/README.md":
continue
table_counts[r["signee"]] = table_counts.get(r["signee"], 0) + 1
mismatch = [(s, disk_counts.get(s, 0), table_counts.get(s, 0)) for s in sorted(set(disk_counts) | set(table_counts)) if disk_counts.get(s, 0) != table_counts.get(s, 0)]
if mismatch:
fails.append("signee counts mismatch disk vs table: %s" % mismatch)
if fails:
print("CHECK: FAILED")
for x in fails:
print(" -", x)
sys.exit(1)
print("CHECK: PASSED")
print("VERDICT: 0 (correct)")
exec python3 "$HERE/grade.py" "$@"
@@ -1,77 +1,93 @@
#!/usr/bin/env python3
"""Generator: check-in signee census.
"""Generator: check-in signee census, derived from a PINNED revision.
Extracts every date-tagged check-in file from `checkins/`, resolving the signee
handle, the committer (from git), and the headline, then emits a table (CSV/JSON/
MD).
Extracts every date-tagged check-in file at `<pin>:checkins/`, resolving the
signee handle, the committer, and the headline, then emits a table (CSV/JSON/MD).
Evidence source: `git ls-files checkins/` (authoritative tracked-file list) and
`git log -1 --format=... -- <file>` (committer of the last commit touching it).
Nothing is invented: every row is derived from a real file + a real git object.
Evidence source: `git ls-tree <pin> -- checkins/` (the authoritative file list at
that revision) and `git log -1 <pin> --format=... -- <file>` (the committer of
the last commit touching the file at that revision). Nothing is invented: every
row is derived from a real blob and a real git object, and no host path is read
or written -- the first version hardcoded `/root/troupe-standup` for both ROOT
and OUT and walked `git ls-files`, which is the working tree, not a commit.
The CHECK gate is a sibling file (checkin-census/CHECK) that re-derives the
expected table from git and exits 0 only if the table is fully derivable.
The scope is the revision, so this generator is reproducible: run at the pin
that carries the committed tables (`b21bb10`), it rewrites them byte for byte.
It writes nothing without `--write`.
Usage:
gen.py --pin b21bb10 # dry run: row counts only
gen.py --pin b21bb10 --write # publish into this artifact's directory
gen.py --pin b21bb10 --outdir DIR --write
Exit: 0 derived/published, 1 the pin cannot be read, 2 bad arguments.
"""
import argparse
import csv
import json
import os
import re
import subprocess
import sys
from pathlib import Path
ROOT = "/root/troupe-standup"
INS = os.path.join(ROOT, "checkins")
OUT = os.path.join(ROOT, "wizard-work", "ledger", "2026-10-29-checkin-census")
HERE = Path(__file__).resolve().parent
DATE = re.compile(r"^checkins/(\d{4}-\d{2}-\d{2})-(.+)$")
INDEX_FILE = "checkins/README.md"
TAG = "2026-10-29"
def run_git(args):
return subprocess.run(
["git", "-C", ROOT] + args, check=True, capture_output=True, text=True
).stdout
def repo_root():
try:
out = subprocess.run(["git", "-C", str(HERE), "rev-parse", "--show-toplevel"],
capture_output=True, text=True, timeout=60)
if out.returncode == 0 and out.stdout.strip():
return Path(out.stdout.strip())
except (OSError, subprocess.SubprocessError):
pass
return HERE.parent.parent.parent
def tracked_checkin_files():
out = run_git(["ls-files", "checkins/"]).strip().split("\n")
return sorted(f for f in out if f.endswith(".md"))
def run_git(repo, args, binary=False):
out = subprocess.run(["git", "-C", str(repo)] + args,
capture_output=True, text=not binary, timeout=120)
if out.returncode != 0:
err = out.stderr if not binary else (out.stderr or b"").decode("utf-8", "replace")
raise RuntimeError("git %s failed: %s" % (" ".join(args), (err or "").strip()))
return out.stdout
def pinned_checkin_files(repo, rev):
out = run_git(repo, ["ls-tree", "-r", "--name-only", rev, "--", "checkins/"])
return sorted(f for f in out.splitlines() if f.endswith(".md"))
def signee_from_filename(name):
m = DATE.match(name)
token = m.group(2)
token = token[:-3] if token.endswith(".md") else token
token = re.sub(r"-\d+$", "", token) # drop -02/-03 revision markers
return token
return re.sub(r"-\d+$", "", token) # drop -02/-03 revision markers
def checkin_headline(path):
try:
with open(path, encoding="utf-8", errors="replace") as fh:
for line in fh:
line = line.rstrip("\n")
if line.startswith("# "):
return line[2:].strip()
except OSError:
pass
def headline(repo, rev, path):
text = run_git(repo, ["show", "%s:%s" % (rev, path)], binary=True)
for line in text.decode("utf-8", "replace").split("\n"):
line = line.rstrip("\r")
if line.startswith("# "):
return line[2:].strip()
return ""
def committer(path):
out = run_git(["log", "-1", "--format=%an <%ae>", "--", path]).strip()
def committer(repo, rev, path):
out = run_git(repo, ["log", "-1", "--format=%an <%ae>", rev, "--", path]).strip()
return out or "(no committer)"
def main():
os.makedirs(OUT, exist_ok=True)
def derive(repo, rev):
rows = []
for f in tracked_checkin_files():
for f in pinned_checkin_files(repo, rev):
if f == INDEX_FILE:
rows.append({
"file": INDEX_FILE,
"date": "2026-10-29",
"date": TAG,
"signee": "troupe-pipe (index)",
"headline": "Check-ins index",
"committer": "(index file; no committer)",
@@ -79,30 +95,29 @@ def main():
})
continue
m = DATE.match(f)
date = m.group(1)
signee = signee_from_filename(f)
headline = checkin_headline(os.path.join(ROOT, f))
rows.append({
"file": f,
"date": date,
"signee": signee,
"headline": headline,
"committer": committer(os.path.join(ROOT, f)),
"date": m.group(1),
"signee": signee_from_filename(f),
"headline": headline(repo, rev, f),
"committer": committer(repo, rev, f),
"kind": "checkin",
})
rows.sort(key=lambda r: (r["date"], r["file"]))
return rows
# ---- CSV ----
csv_path = os.path.join(OUT, "checkin-census.csv")
def write_outputs(out_dir, rows):
out_dir.mkdir(parents=True, exist_ok=True)
csv_path = out_dir / "checkin-census.csv"
with open(csv_path, "w", newline="", encoding="utf-8") as fh:
w = csv.DictWriter(fh, fieldnames=["file", "date", "signee", "headline", "committer", "kind"])
w = csv.DictWriter(fh, fieldnames=["file", "date", "signee", "headline",
"committer", "kind"])
w.writeheader()
for r in rows:
w.writerow(r)
# ---- JSON ----
json_path = os.path.join(OUT, "checkin-census.json")
json_path = out_dir / "checkin-census.json"
with open(json_path, "w", encoding="utf-8") as fh:
json.dump({
"source": "git ls-files checkins/ (authoritative tracked list)",
@@ -110,23 +125,50 @@ def main():
"rows": rows,
}, fh, indent=2, ensure_ascii=False)
# ---- MD table ----
md_path = os.path.join(OUT, "checkin-census.md")
md_path = out_dir / "checkin-census.md"
with open(md_path, "w", encoding="utf-8") as fh:
fh.write("# Check-in signee census (2026-10-29)\n\n")
fh.write("Source: `git ls-files checkins/`. Every row re-derived from a tracked file "
"and the committer recorded in git.\n\n")
fh.write("# Check-in signee census (%s)\n\n" % TAG)
fh.write("Source: `git ls-files checkins/`. Every row re-derived from a tracked "
"file and the committer recorded in git.\n\n")
fh.write("| file | date | signee | headline | committer | kind |\n")
fh.write("|---|---|---|---|---|---|\n")
for r in rows:
fh.write(f"| {r['file']} | {r['date']} | {r['signee']} | {r['headline']} | {r['committer']} | {r['kind']} |\n")
fh.write(f"| {r['file']} | {r['date']} | {r['signee']} | {r['headline']} | "
f"{r['committer']} | {r['kind']} |\n")
return csv_path, json_path, md_path
print("wrote outputs:")
print(" ", csv_path)
print(" ", json_path)
print(" ", md_path)
print("rows:", len(rows), "dated checkin:", len([r for r in rows if r["kind"] == "checkin"]))
def main(argv=None):
ap = argparse.ArgumentParser(description="derive the check-in signee census "
"from a pinned git revision")
ap.add_argument("--pin", required=True, help="revision whose checkins/ is the scope")
ap.add_argument("--outdir", default=None,
help="directory to write into (default: this artifact's directory)")
ap.add_argument("--write", action="store_true",
help="publish; without it the run only derives and reports")
args = ap.parse_args(argv)
repo = repo_root()
try:
rev = run_git(repo, ["rev-parse", "--verify", args.pin + "^{commit}"]).strip()
rows = derive(repo, rev)
except RuntimeError as exc:
print("VALIDATION_FAILED: %s" % exc, file=sys.stderr)
return 1
except (OSError, subprocess.SubprocessError, ValueError, AttributeError) as exc:
print("CANNOT GRADE: %s: %s" % (type(exc).__name__, exc), file=sys.stderr)
return 2
dated = len([r for r in rows if r["kind"] == "checkin"])
print("pin=%s rows=%d dated=%d index=%d" % (rev[:12], len(rows), dated, len(rows) - dated))
if not args.write:
print("dry run: nothing written (pass --write to publish)")
return 0
out = Path(args.outdir) if args.outdir else HERE
for p in write_outputs(out, rows):
print("wrote:", p)
return 0
if __name__ == "__main__":
main()
sys.exit(main())
+237
View File
@@ -0,0 +1,237 @@
#!/usr/bin/env python3
"""Grade the 2026-10-29 check-in signee census against its PINNED revision.
WHAT IT GRADES -- five legs, every one re-derived from `git ls-tree` / `git show`
/ `git log` at the pinned commit, compared against the committed tables. Nothing
is read from the working tree (so tomorrow's check-in cannot red this artifact)
and nothing is written (the previous revision's legs ran against `git ls-files`,
which is the live index, and its row set grew with the archive).
1 scope the pin file beside this script: header, sortedness, uniqueness, names
2 pin the dated name list equals `checkins/` at the pinned commit (SKIP if
the object is not in this database -- the committed list is then the pin)
3 csv every cell of the committed CSV against the re-derivation, plus the
`checkins/README.md` index row and the header order
4 md the MD's table block, row for row, against the same derivation
5 json the JSON's rows and `total_checkin_rows`
EXIT CODES: 0 every leg passed; 1 a fault (including "the pin is not a usable
input"); 2 cannot grade at all (no git).
Usage: python3 grade.py [--repo DIR] (or `bash CHECK`, which runs this file)
"""
import argparse
import csv
import json
import re
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
PIN_FILE = HERE / "2026-10-29-checkin-census.files"
TAG = "2026-10-29"
CSV_PATH = HERE / "checkin-census.csv"
MD_PATH = HERE / "checkin-census.md"
JSON_PATH = HERE / "checkin-census.json"
INDEX_FILE = "checkins/README.md"
FIELDS = ["file", "date", "signee", "headline", "committer", "kind"]
NAME_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-(.+)\.md$")
PIN_RE = re.compile(r"^# pin checkins@([0-9a-f]{40}) tree=([0-9a-f]{40})$")
class Fault(Exception):
"""A graded leg failed; the message is the verdict line."""
class CannotGrade(Exception):
"""The instrument itself cannot run."""
def git(repo, *args):
try:
out = subprocess.run(["git", "-C", str(repo)] + list(args),
capture_output=True, timeout=180)
except (OSError, subprocess.SubprocessError) as exc:
raise CannotGrade("git %s: %s" % (args[0], exc))
if out.returncode != 0:
raise Fault("git %s failed: %s" % (" ".join(args),
out.stderr.decode("utf-8", "replace").strip()))
return out.stdout
def find_repo():
for cand in (HERE, HERE.parent):
r = subprocess.run(["git", "-C", str(cand), "rev-parse", "--show-toplevel"],
capture_output=True, text=True)
if r.returncode == 0 and r.stdout.strip():
return Path(r.stdout.strip())
return HERE.parent.parent.parent
def require_pin(repo, sha):
"""A revision this clone does not carry cannot grade a census of it."""
try:
git(repo, "cat-file", "-e", sha + "^{commit}")
except Fault:
raise CannotGrade("pin %s is not in this object database: a census of that "
"revision cannot be graded without it" % sha[:12])
def read_pin():
if not PIN_FILE.is_file() or PIN_FILE.stat().st_size == 0:
raise Fault("SCOPE NOT ESTABLISHED: pinned name list missing or empty: %s"
% PIN_FILE.name)
lines = PIN_FILE.read_text(encoding="utf-8").splitlines()
m = PIN_RE.match(lines[0])
if not m:
raise Fault("SCOPE NOT ESTABLISHED: pin header not understood: %r" % lines[0])
names = lines[1:]
if not names:
raise Fault("SCOPE NOT ESTABLISHED: pin names no files")
bad = [n for n in names if not NAME_RE.match(n)]
if bad:
raise Fault("SCOPE NOT ESTABLISHED: %d pinned name(s) are not YYYY-MM-DD-*.md "
"(first: %r)" % (len(bad), bad[0]))
if len(set(names)) != len(names):
raise Fault("SCOPE NOT ESTABLISHED: duplicated name(s) in the pin")
if sorted(names) != names:
raise Fault("SCOPE NOT ESTABLISHED: pin names are not sorted (LC_ALL=C)")
return m.group(1), m.group(2), names
def leg_pin(repo, sha, tree, names):
real = git(repo, "rev-parse", ("%s:checkins" % sha)).decode().strip()
if real != tree:
raise Fault("pin tree %s is not %s:checkins (%s)" % (tree[:12], sha[:7], real[:12]))
out = git(repo, "ls-tree", "-r", "--name-only", sha, "--", "checkins/").decode("utf-8")
at_pin = sorted(p[len("checkins/"):] for p in out.splitlines()
if p.startswith("checkins/") and NAME_RE.match(p[len("checkins/"):]))
if at_pin != names:
only_pin = [n for n in at_pin if n not in set(names)]
only_list = [n for n in names if n not in set(at_pin)]
raise Fault("pin name list != checkins/ at %s (+%d at pin, +%d in pin): %s %s"
% (sha[:7], len(only_pin), len(only_list), only_pin[:3], only_list[:3]))
return "PASS pin: %d dated name(s) == checkins/ tree %s at %s" % (
len(names), tree[:12], sha[:7])
def derive(repo, sha, names):
"""Rows in the artifact's order: (date, file), plus the README index row."""
rows = []
for name in names:
path = "checkins/" + name
m = NAME_RE.match(name)
signee = re.sub(r"-\d+$", "", m.group(2))
text = git(repo, "show", "%s:%s" % (sha, path)).decode("utf-8", "replace")
head = ""
for line in text.split("\n"):
if line.startswith("# "):
head = line[2:].strip()
break
who = git(repo, "log", "-1", "--format=%an <%ae>", sha, "--", path).decode().strip()
rows.append({"file": path, "date": m.group(1), "signee": signee,
"headline": head, "committer": who or "(no committer)",
"kind": "checkin"})
tracked = git(repo, "ls-tree", "-r", "--name-only", sha, "--", "checkins/").decode()
if INDEX_FILE in tracked.splitlines():
rows.append({"file": INDEX_FILE, "date": TAG, "signee": "troupe-pipe (index)",
"headline": "Check-ins index",
"committer": "(index file; no committer)", "kind": "index"})
rows.sort(key=lambda r: (r["date"], r["file"]))
return rows
def leg_csv(rows):
with open(CSV_PATH, encoding="utf-8", newline="") as fh:
reader = csv.reader(fh)
header = next(reader)
got = list(reader)
if header != FIELDS:
raise Fault("CSV header %r != %r" % (header, FIELDS))
want = [[r[f] for f in FIELDS] for r in rows]
if len(got) != len(want):
raise Fault("CSV has %d row(s); the pin derives %d" % (len(got), len(want)))
for i, (g, w) in enumerate(zip(got, want)):
if g != w:
raise Fault("CSV row %d: %r != pin %r" % (i, g, w))
return "PASS csv: %d row(s) match the pin cell for cell" % len(got)
def md_rows(text):
out, inside = [], False
for line in text.splitlines():
if not line.startswith("|"):
if inside:
break
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if not inside:
inside = cells == FIELDS
continue
if set("".join(cells)) <= set("-: "):
continue
out.append(cells)
return out
def leg_md(rows):
got = md_rows(MD_PATH.read_text(encoding="utf-8"))
want = [[r[f] for f in FIELDS] for r in rows]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("MD table: %d row(s) vs pin %d; first difference at row %d "
"(md %r, pin %r)" % (len(got), len(want), bad,
got[bad] if bad < len(got) else None,
want[bad] if bad < len(want) else None))
return "PASS md: %d row(s) match the pin" % len(got)
def leg_json(rows):
doc = json.loads(JSON_PATH.read_text(encoding="utf-8"))
got = [{f: r.get(f) for f in FIELDS} for r in doc.get("rows", [])]
want = [{f: r[f] for f in FIELDS} for r in rows]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("JSON rows: %d vs pin %d; first difference at row %d" % (
len(got), len(want), bad))
n_dated = len([r for r in rows if r["kind"] == "checkin"])
if doc.get("total_checkin_rows") != n_dated:
raise Fault("JSON total_checkin_rows %r != %d derived" % (
doc.get("total_checkin_rows"), n_dated))
return "PASS json: %d row(s), total_checkin_rows=%d, match the pin" % (len(got), n_dated)
def main(argv=None):
ap = argparse.ArgumentParser(description="grade the 2026-10-29 check-in census")
ap.add_argument("--repo", default=None, help="repository to grade (default: this tree's)")
args = ap.parse_args(argv)
repo = Path(args.repo) if args.repo else find_repo()
try:
sha, tree, names = read_pin()
require_pin(repo, sha)
print("PASS scope: %d pinned dated file(s), pin %s" % (len(names), sha[:7]))
lines = [leg_pin(repo, sha, tree, names)]
rows = derive(repo, sha, names)
lines.append(leg_csv(rows))
lines.append(leg_md(rows))
lines.append(leg_json(rows))
except Fault as exc:
print("FAIL: %s" % exc)
return 1
except CannotGrade as exc:
print("CANNOT GRADE: %s" % exc)
return 2
for line in lines:
print(line)
print("VERDICT=PASS census-%s rows=%d dated=%d pin=%s"
% (TAG, len(rows), len(rows) - 1, sha[:7]))
return 0
if __name__ == "__main__":
sys.exit(main())
+10 -2
View File
@@ -513,8 +513,16 @@ PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109,
# this append, so record_ids 77 -> 78 with record_only unchanged at 55.
# open, opened, ids and heading_ids are all unchanged (0 / 26 / 141 / 86): the
# append closes an id, it does not open one.
PIN_COUNTS = {"headings": 98, "heading_ids": 86, "records": 112,
"record_ids": 78, "ids": 141, "open": 0, "opened": 26,
# Q151 (2026-11-01): the closure record appended at the foot of WORK-QUEUE.md
# adds one heading (## Q151 closure) and one record (**Q151 — OPENED and
# REPAIRED in the same tick ...). Q151 is a new id with no earlier heading, so
# everything that counts a distinct id moves together: headings 98 -> 99,
# heading_ids 86 -> 87, records 112 -> 113, record_ids 78 -> 79, ids 141 -> 142;
# record_only stays 55 (the new id has a heading, so it is not record-only) and
# open / opened stay 0 / 26 -- a same-tick "OPENED and REPAIRED" is a closure
# (the Q138 rule), so no probe is registered for it and CHECK-q151 is its guard.
PIN_COUNTS = {"headings": 99, "heading_ids": 87, "records": 113,
"record_ids": 79, "ids": 142, "open": 0, "opened": 26,
"record_only": 55}
PIN_STATE = {
@@ -18,6 +18,9 @@
# CHECK-q148 1 -> 0 (green)
# row-level re-take (q148 via retake.py, 2026-11-01): 1 row(s) measured, --timeout 120 --jobs 4, at HEAD 9e476c5; 1 code(s) moved.
# wizard-work/beam/CHECK 1 -> 0 (green)
# row-level re-take (q148 via retake.py, 2026-11-01): 2 row(s) measured, --timeout 120 --jobs 4, at HEAD d42f577; 2 code(s) moved.
# wizard-work/ledger/2026-10-28-checkin-census/CHECK 1 -> 0 (green)
# wizard-work/ledger/2026-10-29-checkin-census/CHECK 1 -> 0 (green)
CHECK 0 verify_selftest_sweep_q93_refactor: ALL GREEN (0 failed, all checks)
CHECK-2026-11-01-ledger-dir-census 0 VERDICT: rc 0 -- the committed CSV is the derivation at the pinned rev
CHECK-ledger-dir-census 0 VERDICT: rc 0 -- the committed CSV is the derivation at the pinned rev
@@ -36,7 +39,7 @@ wizard-work/forge/CHECK-q87 1 CHECK-q87: a leg failed -- see the FAIL line above
wizard-work/ledger/2026-10-25-git-history-live-b1edd09/CHECK 0 ALL CHECKS PASSED: git-history re-derives byte-identically from git at pinned head b1edd09
wizard-work/ledger/2026-10-25-head-20-commits/CHECK 0 CHECK PASS | re-derived 20 commits from live HEAD 039246958fcf802aba9224aa5c24fad6bf181dc5 (git log -n 20 039246958fcf802aba9224aa5c24fad6bf181dc5); commits.tsv
wizard-work/ledger/2026-10-28-arxiv-csai-recent/CHECK 0 PASS re-derived from raw_api.xml; table has 20 rows
wizard-work/ledger/2026-10-28-checkin-census/CHECK 1 - latest volt mismatch: json {'signer': 'volt', 'latest_date': '2026-10-28', 'file': '2026-10-28-volt.md'} vs re-derived {'signer': 'volt', 'date': '2026-11-01'
wizard-work/ledger/2026-10-28-checkin-census/CHECK 0 VERDICT=PASS census-2026-10-28 rows=100 files=100 pin=ab7767d
wizard-work/ledger/2026-10-28-commits-by-author/CHECK 0 ALL CHECKS PASSED: census re-derives byte-identically from git at pinned head 0f0d02b4b1fb1f4a63a13d6732f4c5b0f970b1a4
wizard-work/ledger/2026-10-28-directory-census/CHECK 0 ALL CHECKS PASSED: 148 rows, 148 distinct dirs, 3343 total_files, 1 malformed
wizard-work/ledger/2026-10-28-git-tree-census/CHECK 1 FAIL: census mismatch against independent recompute of committed tree
@@ -44,7 +47,7 @@ wizard-work/ledger/2026-10-28-recovery-registry/CHECK 1 FAIL fresh extraction ma
wizard-work/ledger/2026-10-28-wizardwork-census/CHECK 1 FAIL: census mismatch against independent recompute of committed tree
wizard-work/ledger/2026-10-28-workqueue-census/CHECK 1 FAIL manifest: 4 of 6 leg(s) reconciled, 2 fault(s) (54 record-form-only id(s), 140 id(s) opened in any form)
wizard-work/ledger/2026-10-29-albums-census/CHECK 0 CHECK: OK
wizard-work/ledger/2026-10-29-checkin-census/CHECK 1 - signee counts mismatch disk vs table: [('mercury', 21, 20), ('timmy', 1, 0), ('volt', 13, 10), ('zephyrus', 55, 53)]
wizard-work/ledger/2026-10-29-checkin-census/CHECK 0 VERDICT=PASS census-2026-10-29 rows=103 dated=102 pin=b21bb10
wizard-work/ledger/2026-10-29-commit-author-census/CHECK 1 FAIL: TSV author=Timmy/557 got=Timmy/556
wizard-work/ledger/2026-10-29-jsonplaceholder-census/CHECK 0 RESULT: ALL CHECKS PASSED
wizard-work/ledger/2026-10-29-workqueue-census/CHECK 0 PASS: 88 work-queue sections reconciled to census (CLOSED 13, DONE 27, OPEN 31, UNCLAIMED 11, OTHER 6)
@@ -130,3 +133,10 @@ CHECK-q148 0 q148: 5 of 5 leg(s) passed
CHECK-q149 0 q149: 6 of 6 leg(s) passed
# rows signed (q148 via retake.py, 2026-11-01): 1 entry point(s) measured, --timeout 300 --jobs 4, at HEAD fe23317
CHECK-q150 0 q150: 6 leg(s), all passed
# row added by hand (Q151, 2026-11-01): CHECK-q151 is a NEW entry point, and
# retake.py only rewrites rows the baseline already names, so the row was
# inserted here with the strings `check_sweep.py --only CHECK-q151 --check`
# printed at HEAD d42f577 (rc 0, "q151: 3 leg(s), all passed"). No other row in
# this file was touched: the five NEW paths `--paths` reports at that rev belong
# to other lanes and are left for their own tick.
CHECK-q151 0 q151: 3 leg(s), all passed
Can't render this file because it contains an unexpected character in line 93 and column 87.