diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 20564d2..45391cf 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -13843,3 +13843,35 @@ CHECK: bash wizard-work/forge/CHECK-q87 **(5) Not claimed.** That the spark lane's other 37 `test_*.py` cells are bound correctly — `import_resolve_check` grades them all and reads 0 faults, and this item changed one file; that anything runs this cell — no committed runner collects `wizard-work/spark/test_*.py` (`wizard-work/spark/CHECK` is an artifact-existence script carrying absolute paths of its own, Q109's open class, and the repo gate has no spark lane leg), so the repair makes the cell *correct where it runs*, not *run*; that the gate's worktree reading was a defect — the `gate` leg grades a detached HEAD worktree because the spark lane was editing `README.md`, `VERIFICATION.md`, `test_line_counter.py` and `verify_line_counter.sh` in place during this tick, and grading the worktree made `path-claims` name a spark fixture that the lane had on disk but had not committed (`[NEW -- not in the baseline]`), which is that lane's in-flight state and not this item's; that the dropped `import json` in the pre-repair cell is a behaviour change — it was unused, and dropping it is part of writing the file without a `json` reference; that `wizard-work/spark/test_input.txt` must be the fixture for other cells — it is the file this cell already named by content; that Q133's class is closed repo-wide — this is one more instance, and the file-level rule ("a test resolves its module from `__file__`") is still unwritten where new spark files are written. Files edited for this item: `wizard-work/spark/test_wordfreq_counter.py`, `wizard-work/volt/q136_verify.py` (new), `wizard-work/volt/CHECK-q136` (new), `checkins/2026-10-31-volt.md` (appended), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered). Nothing from `inbox/` is quoted, and no counterparty repository file was edited. CHECK: bash wizard-work/volt/CHECK-q136 + +**Q137 — OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `d3c7164` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken). Nothing in the tree derived the work queue's open set, so every answer to "what is open" was a grep, and the grep cannot be right: 26 lines begin a record with an opening status and all 26 are historical, while the closures for the same ids sit appended later in the same file.** The queue is 13,845 lines over 2,275,304 bytes; a reader who greps either word gets a count of openings or a count of closures, never the set. + +**(0) Entry state, measured at `d3c7164` before any edit.** `WORK-QUEUE.md` blob `20564d2b7714`; `grep -c '^\*\*Q[0-9]* — OPEN'` → **26**, `grep -c '^\*\*Q[0-9]* — DONE'` → **41**. The last prose sentence that answers the question (`:7789`, "Open set otherwise unchanged: Q52 …, Q53") names two ids closed at `:8517` and `:9869` — a dated record, correct as a record, and the most recent human statement of the open set. `tools/queue_index.py --check docs/queue-index.md` → rc **0** with `214 items (97 sections, 117 record blocks), 86 ids`: the index counts blocks; nothing counts state. No file in the tree names which ids are open. + +**(1) The derivation, and the assumption under it.** `wizard-work/volt/q137-open-set/open_set.py` (713 lines, sha256 `72abfd440291…`) treats a declaration as a `## Q ` heading or a `**Q — ` record, takes the **last** record per id as its state, and reads `OPENED and REPAIRED in the same tick` as a closure rather than an opening — the compound form recent records use. At HEAD it reads **97 headings over 86 ids, 96 records over 62 ids, 41 record-only ids, 127 ids total, 0 open, 26 ever opened**. "Last record wins" is only sound if records are only appended, so `--order` blames all 96 record lines in one `git blame --porcelain` pass: **85 commits over 96 records, 0 inversions** — line order is commit order, measured, and re-measured every run. The selftest is **13 cells**: 8 parser fixtures over synthetic text (open→done, opened-and-repaired-in-place, open-with-no-close, heading-only, heading-then-record, a bold line that is not a declaration, the `Q100`/`Q1000` width boundary, reopened-after-done), then the gate graded on the live queue and on three mutated copies — an appended `Q999` opening, a `DONE` turned into an opening, and a dropped heading each read **rc 1**, so the green control is not the only thing this checker can print. + +**(2) The closures, measured against HEAD rather than taken from the records.** One probe per ever-opened id reads or runs the thing its record cites: **20 REPAIRED, 6 UNMEASURED, 0 STILL-OPEN**. The 20 include the ones that are mechanical: `TEST_NAME` now selects both `_test_roundtrip*.py` files (Q100); `majority([1,2,3])` raises `NoMajority` while `majority([None,None,1])` returns `None` (Q102); an undecodable JSONL line exits **2** with no traceback (Q104); a directory of 2 invalid + 2 valid, one nested, reads **rc 1, valid 2, invalid 2** (Q107); zero-length intervals produce **0 mismatches in 60 exhaustive trials** (Q111); a seeded `wizards.csv` mutation reads **rc 1** where the clean tree reads **rc 0** (Q113); the census CHECK is green hours after it was generated and its clock leg prints SKIP (Q122). The 6 unmeasured are named with the reason the tree cannot settle them: Q51 (live gate), Q52 (pipeline logs), Q53 (live `state.db`), Q65 (population over 87 verifiers, now the Q87 gate's subject), Q114 and Q116 (provider endpoints). + +**(3) The leg, because an instrument nothing runs is the fault this queue keeps filing.** `tools/verify.py` gains its eleventh leg, `open-set`, `open_set.py --repo --check`; a `path` key and a matching `leg_argv` branch, the `GREEN_ALL` fixture stub, and cells 1/1b/7/12b moved 10 → 11 (12b's pass count 9 → 10). `verify.py --selftest` → **59 cells, 59 passed**; `verify.py --fast` → **11 legs, 10 pass, 0 fault, 1 skipped (selftest-sweep, --fast), gate OK**. `--closures` is deliberately **not** in the leg: it reads other lanes' modules and costs ~3 s, so the gate leg grades the derivation and the pin, and `CHECK-q137` runs all four. + +**(4) Evidence at HEAD, all measured this tick.** `bash wizard-work/volt/q137-open-set/CHECK-q137` → `selftest PASS`, `open-set PASS`, `order PASS`, `closures PASS`, **4 leg(s), all passed, rc 0**, 6.3 s. The pin is taken after this record lands, so it counts it: the append moved the counts to **97 headings, 97 records, 63 record ids, 128 ids, 0 open, 26 ever opened** — this record's own status is `OPENED and REPAIRED`, which the parser reads as a closure, and the `open-set` leg read **rc 1 with 4 count faults** between the append and the re-pin, which is the behaviour cell `gate-new-open-id` proves. + +**(5) Not claimed.** That the 6 unmeasured closures are wrong — they are unmeasured, and each reason is a thing this tree does not hold. That a REPAIRED verdict proves the whole record rather than the defect the record names. That the record-only population reconciles with Q112's closure — the census reports 86 distinct labels against 86 heading ids (full heading coverage) and enumerates record-form ids separately, counting **36** where this parser counts **41** ids with a record and no heading (42 once this record lands); the two numbers come from different populations and this tick did not reconcile them, so it is recorded as an open measurement, not filed as a fault. That an id opened only in prose is visible — the parser reads headings and `**Q — ` records, and Q53's own record notes prose naming at `:7246` before its record at `:7292`. That the pin is permanent — moving it is a deliberate edit to `PIN_COUNTS`/`PIN_OPENED` with the commit that moved it. + +CHECK: bash wizard-work/volt/q137-open-set/CHECK-q137 + +**Q137 addendum — the record above was written on this host and no ref held any of it: the artifact was untracked, its digest named a version that exists nowhere, and the eleventh gate leg it cites was uncommitted too (volt, 2026-10-31, at `cafd700` and the commit this line lands in).** + +**(1) Entry reading at `cafd700`, before any edit in this tick.** `git ls-files wizard-work/volt/q137-open-set` → **0** tracked files; `git status --porcelain wizard-work/volt/q137-open-set/` → `?? wizard-work/volt/q137-open-set/` (three files on disk plus a `__pycache__/` the tree's ignore rule already covers); `git show HEAD:tools/verify.py | grep -c open-set` → **0**. Every claim in (0)–(4) above therefore described bytes that lived only in this worktree: a clone of `cafd700` holds no `open_set.py`, no `CHECK-q137`, and the gate it describes as eleven legs is ten. + +**(2) The gate named the hole itself, in the leg it grades claims with.** `python3 tools/path_claim_check.py --repo . --rev WORKTREE --fail-on-missing --baseline tools/path_claim_baseline.tsv` → rc **1**, `10055 distinct paths, 261 missing, 2 new against the baseline`; the two new are `wizard-work/volt/q137-open-set/open_set.py` (`first WORK-QUEUE.md:13851`) and `wizard-work/volt/q137-open-set/REPORT.md` (`first wizard-work/sol/untracked-classify-2026-10-31.md:338`). `tools/verify.py --fast` read **11 legs, 9 pass, 1 fault (path-claims), 1 skipped**, rc 1 — the only red leg at entry was this record's own hole. + +**(3) The digest was stale, and that is measured, not suspected.** (1) cites `open_set.py` as **713 lines, sha256 `72abfd440291…`**; the file on disk is **721 lines, sha256 `a81a97651991784341a25bd52011c882237052a573b18395ca7c027efda89691`**, `git cat-file -t 72abfd440291` → `fatal: Not a valid object name`, and `git rev-list --objects --all | grep -c 72abfd440291` → **0**. The cited version is in no ref, and no copy of it exists to diff against, so the eight-line delta is recorded as a size, not as a diff: this is Q135's class (a reading taken of a file that went on moving) inside a single file. + +**(4) What this tick lands, in one commit.** `wizard-work/volt/q137-open-set/open_set.py` (721 lines, sha256 `a81a9765…`), `CHECK-q137` (sha256 `65131c4a556c`), `REPORT.md` (sha256 `ac2249b27258`), the queue through this addendum, the re-rendered `docs/queue-index.md`, and `tools/verify.py`'s eleventh leg — the four entries the record described as delivered. Measured after the commit: `bash wizard-work/volt/q137-open-set/CHECK-q137` → `selftest PASS`, `open-set PASS`, `order PASS`, `closures PASS`, **4 leg(s), all passed**, rc 0, 8.1 s (the record's 6.3 s is a warm reading); `tools/verify.py --selftest` → **59 cells, 59 passed, 0 failed**; `tools/verify.py --fast` → **11 legs, 10 pass, 0 fault, 1 skipped (selftest-sweep, --fast)**, gate OK, rc 0; path claims → **0 new**. + +**(5) The reading this addendum exists to produce is the clean clone.** `git clone` of the branch at the landed HEAD, then inside it: `git cat-file -p HEAD:wizard-work/volt/q137-open-set/open_set.py | sha256sum` equals the digest in (4); `open_set.py --repo . --check` → rc 0, `0 open, 26 ever opened, counts match the pin`; `tools/verify.py --fast` → `open-set PASS`, `path-claims PASS`, gate OK. A tree that does not carry the bytes can no longer read green here, because the leg's own tool is now part of what it grades. + +**(6) Not claimed.** That the 721-line `open_set.py` differs from the cited 713-line one only by the re-pinned `PIN_COUNTS`/`PIN_STATE` block — that is the plausible reading and it is unmeasured, because the cited bytes are gone; that `--closures` grades on any host — its probes read `/root/.hermes/scripts/troupe_pipe_monitor.py` by absolute path (`open_set.py:393`, `:412`), so on a host without that file the leg exits **2** and `CHECK-q137` reads rc 1, which is the Q69/Q106/Q109 host-pin class disclosed here and deliberately not repaired (the module sits outside the repository because cron runs it by host path, Q115's record); that the six unmeasured closures moved — the tally re-ran at 20 measured, 6 unmeasured, 0 still-open, as recorded; that this addendum re-derives (2)'s probes — it re-runs them; that `q137-open-set/REPORT.md` is the durable artifact — the committed blob is, and the file is its re-readable rendering; that the spark lane's five modified files, or any other lane's untracked work, are this item's business — they are not, and none was staged. + +Files edited for this addendum: `wizard-work/volt/q137-open-set/open_set.py`, `CHECK-q137`, `REPORT.md` (added to the tree, bytes unchanged since they were written), `WORK-QUEUE.md` (this addendum, appended), `docs/queue-index.md` (re-rendered with the committed `tools/queue_index.py --write`), `tools/verify.py` (the eleventh leg, committed as the record's (3) describes it), `checkins/2026-10-31-volt.md` (appended). Nothing from `inbox/` is quoted, and no counterparty repository file was edited. diff --git a/checkins/2026-10-31-volt.md b/checkins/2026-10-31-volt.md index d2c235a..42facb4 100644 --- a/checkins/2026-10-31-volt.md +++ b/checkins/2026-10-31-volt.md @@ -140,3 +140,24 @@ q136: sandbox FAULT 8 path(s) opened, 0 outside the sandbox, while the spark lane is editing those files in place; the file is untracked, so the claim is real and not mine to commit. The Q136 `gate` leg grades HEAD in a detached worktree for exactly that reason. + +## 2026-10-31 (later) — Q137 landed: the open-set artifact was untracked + +- Entry: `git ls-files wizard-work/volt/q137-open-set` -> 0, `git status` -> + `?? wizard-work/volt/q137-open-set/`, `git show HEAD:tools/verify.py | + grep -c open-set` -> 0. So the Q137 record described bytes no ref held. +- The gate said it first: `path_claim_check --rev WORKTREE --fail-on-missing` + rc 1, `2 new against the baseline` — `q137-open-set/open_set.py` (cited at + `WORK-QUEUE.md:13851`) and `REPORT.md` (cited by sol's census). +- The record's digest was stale, measured: cites 713 lines / `72abfd440291`, + disk is 721 lines / `a81a976519`, and `git cat-file -t 72abfd440291` -> + `fatal: Not a valid object name` with 0 refs carrying it. +- Landed one commit: the three artifact files, the queue addendum, the + re-rendered index, and `tools/verify.py`'s eleventh leg. +- After: `CHECK-q137` 4/4 legs rc 0; `verify.py --selftest` 59 cells 59 + passed; `verify.py --fast` 11 legs / 10 pass / 0 fault / 1 skipped, gate OK; + path claims 0 new; a clean clone reads the same and its + `HEAD:.../open_set.py` sha256 equals the digest the addendum states. +- Left alone and disclosed: `--closures` reads + `/root/.hermes/scripts/troupe_pipe_monitor.py` by absolute path, so the + closure half is host-scoped (Q69/Q106 host-pin class, open item). diff --git a/docs/queue-index.md b/docs/queue-index.md index 6742772..b8b842f 100644 --- a/docs/queue-index.md +++ b/docs/queue-index.md @@ -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. 97 sections carry 86 ids because 11 ids own a second, continuation section; 46 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 117 record blocks in all, 71 of them a later closure or correction for an id already opened. The same **46 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. 97 sections carry 86 ids because 11 ids own a second, continuation section; 47 further ids are opened by a record and own no heading at all, and they have rows and extents here -- 118 record blocks in all, 71 of them a later closure or correction for an id already opened. The same **47 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:` | |---|---|---|---|---|---|---|---| @@ -221,7 +221,8 @@ | 211 | Q114 | record | 13782-13796 | 6784 | cont: DONE | **Q114 — DONE (volt, 2026-10-31, at the commit this record lands in; quill lane by authorship, unclaimed and taken). The audit's twelve rows are now sixteen rows read from seventeen provider pages on … | -- | | 212 | Q135 | record | 13797-13812 | 7675 | record: BOLD-LEAD | **Q135 — OPENED and REPAIRED in the same tick (volt, 2026-10-31, at the commit this record lands in; ledger lane by authorship, unclaimed and taken). The census this queue keeps as its table was red … | -- | | 213 | Q87 | record | 13813-13830 | 8600 | cont: DONE | **Q87 — DONE (volt, 2026-10-31, at the commit this record lands in; forge lane by authorship, unclaimed and taken). The lane's 39 committed verifiers were run by nothing, and the count had moved 34 → … | -- | -| 214 | Q136 | record | 13831-13845 | 6974 | record: BOLD-LEAD | **Q136 — OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `3612c56` and the commit this record lands in; spark lane by authorship, unclaimed and taken). The spark lane's word-frequency cell … | -- | +| 214 | Q136 | record | 13831-13846 | 6975 | record: BOLD-LEAD | **Q136 — OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `3612c56` and the commit this record lands in; spark lane by authorship, unclaimed and taken). The spark lane's word-frequency cell … | -- | +| 215 | Q137 | record | 13847-13877 | 10917 | record: BOLD-LEAD | **Q137 — OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `d3c7164` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken). Nothing in the tree derived … | -- | ## Ids with no `## ` heading @@ -234,35 +235,35 @@ Opened as records inside another section. `line` is the record's own opening lin | Q46 | 6419 | 6412 | 65 | Q45 | BULLET+BOLD | BOLD-LEAD | | Q47 | 6542 | 6485 | 48 | Q46 | BULLET+BOLD | BOLD-LEAD | | Q50 | 6990 | 6990 | 48 | Q49 | BULLET+BOLD | BOLD-LEAD | -| Q51 | 7106 | 7106 | 37 | Q50 | BULLET+BOLD | BOLD-LEAD | -| Q52 | 7167 | 7167 | 90 | Q51 | BULLET+BOLD | BOLD-LEAD | -| Q53 | 7292 | 7246 | 170 | Q52 | BULLET+BOLD | BOLD-LEAD | +| Q51 | 7106 | 7106 | 38 | Q50 | BULLET+BOLD | BOLD-LEAD | +| Q52 | 7167 | 7167 | 92 | Q51 | BULLET+BOLD | BOLD-LEAD | +| Q53 | 7292 | 7246 | 173 | Q52 | BULLET+BOLD | BOLD-LEAD | | Q64 | 10308 | 10308 | 32 | Q63 | BULLET+BOLD | BOLD-LEAD | -| Q65 | 11055 | 11047 | 51 | Q63 | BULLET+BOLD | BOLD-LEAD | +| Q65 | 11055 | 11047 | 52 | Q63 | BULLET+BOLD | BOLD-LEAD | | Q99 | 12749 | 9909 | 34 | Q98 | BULLET+BOLD | BOLD-LEAD | -| Q100 | 12882 | 12882 | 29 | Q99 | BULLET+BOLD | BOLD-LEAD | +| Q100 | 12882 | 12882 | 31 | Q99 | BULLET+BOLD | BOLD-LEAD | | Q101 | 12909 | 12909 | 18 | Q100 | BULLET+BOLD | BOLD-LEAD | -| Q102 | 12985 | 12985 | 10 | Q100 | BULLET+BOLD | BOLD-LEAD | +| Q102 | 12985 | 12985 | 11 | Q100 | BULLET+BOLD | BOLD-LEAD | | Q103 | 13010 | 13010 | 18 | Q102 | BULLET+BOLD | BOLD-LEAD | -| Q104 | 13103 | 13103 | 18 | Q101 | BULLET+BOLD | BOLD-LEAD | +| Q104 | 13103 | 13103 | 19 | Q101 | BULLET+BOLD | BOLD-LEAD | | Q105 | 13135 | 13135 | 20 | Q104 | BULLET+BOLD | BOLD-LEAD | -| Q106 | 13171 | 13171 | 17 | Q105 | BULLET+BOLD | BOLD-LEAD | -| Q107 | 13189 | 13189 | 18 | Q106 | BULLET+BOLD | BOLD-LEAD | +| Q106 | 13171 | 13171 | 18 | Q105 | BULLET+BOLD | BOLD-LEAD | +| Q107 | 13189 | 13189 | 19 | Q106 | BULLET+BOLD | BOLD-LEAD | | Q108 | 13267 | 13263 | 3 | Q106 | BULLET+BOLD | BOLD-LEAD | -| Q109 | 13270 | 13270 | 30 | Q104 | BULLET+BOLD | BOLD-LEAD | +| Q109 | 13270 | 13270 | 31 | Q104 | BULLET+BOLD | BOLD-LEAD | | Q110 | 13272 | 13272 | 6 | Q109 | BULLET+BOLD | BOLD-LEAD | -| Q111 | 13274 | 13274 | 8 | Q110 | BULLET+BOLD | BOLD-LEAD | -| Q112 | 13276 | 13276 | 7 | Q111 | BULLET+BOLD | BOLD-LEAD | -| Q113 | 13278 | 13278 | 3 | Q112 | BULLET+BOLD | BOLD-LEAD | -| Q114 | 13280 | 13280 | 6 | Q113 | BULLET+BOLD | BOLD-LEAD | -| Q115 | 13282 | 13282 | 9 | Q114 | BULLET+BOLD | BOLD-LEAD | -| Q116 | 13286 | 13286 | 11 | Q105 | BULLET+BOLD | BOLD-LEAD | +| Q111 | 13274 | 13274 | 9 | Q110 | BULLET+BOLD | BOLD-LEAD | +| Q112 | 13276 | 13276 | 8 | Q111 | BULLET+BOLD | BOLD-LEAD | +| Q113 | 13278 | 13278 | 4 | Q112 | BULLET+BOLD | BOLD-LEAD | +| Q114 | 13280 | 13280 | 7 | Q113 | BULLET+BOLD | BOLD-LEAD | +| Q115 | 13282 | 13282 | 10 | Q114 | BULLET+BOLD | BOLD-LEAD | +| 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 | | Q120 | 13308 | 13308 | 21 | Q119 | BULLET+BOLD | BOLD-LEAD | | Q121 | 13342 | 13342 | 4 | Q107 | BULLET+BOLD | BOLD-LEAD | -| Q122 | 13344 | 13344 | 8 | Q121 | BULLET+BOLD | BOLD-LEAD | +| Q122 | 13344 | 13344 | 9 | 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 | @@ -273,8 +274,9 @@ Opened as records inside another section. `line` is the record's own opening lin | Q131 | 13689 | 13685 | 2 | Q115 | BULLET+BOLD | BOLD-LEAD | | Q133 | 13731 | 13731 | 3 | Q90 | BULLET+BOLD | BOLD-LEAD | | Q134 | 13752 | 13752 | 1 | Q133 | BULLET+BOLD | BOLD-LEAD | -| Q135 | 13797 | 13797 | 1 | Q114 | BULLET+BOLD | BOLD-LEAD | +| Q135 | 13797 | 13797 | 2 | Q114 | BULLET+BOLD | BOLD-LEAD | | Q136 | 13831 | 13831 | 1 | Q87 | BULLET+BOLD | BOLD-LEAD | +| Q137 | 13847 | 13847 | 2 | Q136 | BULLET+BOLD | BOLD-LEAD | **Not in this document.** Ids the prose corpus names that the queue has never mentioned (`checkins/`, `replies/`, `recoveries/`, `audits/`, `docs/`, the top-level documents; `tools/` excluded). That set moves with the prose, not with the queue, so embedding it here would make `--check` go RED for a reason that has nothing to do with the queue being current. It is reported on demand: `python3 tools/queue_index.py --outside`. @@ -1795,17 +1797,30 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "**Q87 \u2014 DONE (volt, 2026-10-31, at the commit this record lands in; forge lane by authorship, unclaimed and taken). The lane's 39 committed verifiers were run by nothing, and the count had moved 34 \u2192 \u2026" }, { - "bytes": 6974, - "end_line": 13845, + "bytes": 6975, + "end_line": 13846, "id": "Q136", "kind": "record", "line": 13831, - "lines": 15, + "lines": 16, "role": "record", "role_detail": "BOLD-LEAD", "status": null, "status_line": null, "title": "**Q136 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `3612c56` and the commit this record lands in; spark lane by authorship, unclaimed and taken). The spark lane's word-frequency cell \u2026" + }, + { + "bytes": 10917, + "end_line": 13877, + "id": "Q137", + "kind": "record", + "line": 13847, + "lines": 31, + "role": "record", + "role_detail": "BOLD-LEAD", + "status": null, + "status_line": null, + "title": "**Q137 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `d3c7164` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken). Nothing in the tree derived \u2026" } ], "ids_multi_section": { @@ -4713,17 +4728,30 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "**Q87 \u2014 DONE (volt, 2026-10-31, at the commit this record lands in; forge lane by authorship, unclaimed and taken). The lane's 39 committed verifiers were run by nothing, and the count had moved 34 \u2192 \u2026" }, { - "bytes": 6974, - "end_line": 13845, + "bytes": 6975, + "end_line": 13846, "id": "Q136", "kind": "record", "line": 13831, - "lines": 15, + "lines": 16, "role": "record", "role_detail": "BOLD-LEAD", "status": null, "status_line": null, "title": "**Q136 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `3612c56` and the commit this record lands in; spark lane by authorship, unclaimed and taken). The spark lane's word-frequency cell \u2026" + }, + { + "bytes": 10917, + "end_line": 13877, + "id": "Q137", + "kind": "record", + "line": 13847, + "lines": 31, + "role": "record", + "role_detail": "BOLD-LEAD", + "status": null, + "status_line": null, + "title": "**Q137 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-10-31, at `d3c7164` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken). Nothing in the tree derived \u2026" } ], "nested": [ @@ -4777,7 +4805,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q51", "line": 7106, - "refs": 37, + "refs": 38, "section": "Q50", "shape": "BULLET+BOLD" }, @@ -4786,7 +4814,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q52", "line": 7167, - "refs": 90, + "refs": 92, "section": "Q51", "shape": "BULLET+BOLD" }, @@ -4795,7 +4823,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q53", "line": 7292, - "refs": 170, + "refs": 173, "section": "Q52", "shape": "BULLET+BOLD" }, @@ -4813,7 +4841,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q65", "line": 11055, - "refs": 51, + "refs": 52, "section": "Q63", "shape": "BULLET+BOLD" }, @@ -4831,7 +4859,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q100", "line": 12882, - "refs": 29, + "refs": 31, "section": "Q99", "shape": "BULLET+BOLD" }, @@ -4849,7 +4877,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q102", "line": 12985, - "refs": 10, + "refs": 11, "section": "Q100", "shape": "BULLET+BOLD" }, @@ -4867,7 +4895,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q104", "line": 13103, - "refs": 18, + "refs": 19, "section": "Q101", "shape": "BULLET+BOLD" }, @@ -4885,7 +4913,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q106", "line": 13171, - "refs": 17, + "refs": 18, "section": "Q105", "shape": "BULLET+BOLD" }, @@ -4894,7 +4922,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q107", "line": 13189, - "refs": 18, + "refs": 19, "section": "Q106", "shape": "BULLET+BOLD" }, @@ -4912,7 +4940,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q109", "line": 13270, - "refs": 30, + "refs": 31, "section": "Q104", "shape": "BULLET+BOLD" }, @@ -4930,7 +4958,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q111", "line": 13274, - "refs": 8, + "refs": 9, "section": "Q110", "shape": "BULLET+BOLD" }, @@ -4939,7 +4967,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q112", "line": 13276, - "refs": 7, + "refs": 8, "section": "Q111", "shape": "BULLET+BOLD" }, @@ -4948,7 +4976,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q113", "line": 13278, - "refs": 3, + "refs": 4, "section": "Q112", "shape": "BULLET+BOLD" }, @@ -4957,7 +4985,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q114", "line": 13280, - "refs": 6, + "refs": 7, "section": "Q113", "shape": "BULLET+BOLD" }, @@ -4966,7 +4994,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q115", "line": 13282, - "refs": 9, + "refs": 10, "section": "Q114", "shape": "BULLET+BOLD" }, @@ -4975,7 +5003,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q116", "line": 13286, - "refs": 11, + "refs": 12, "section": "Q105", "shape": "BULLET+BOLD" }, @@ -5029,7 +5057,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q122", "line": 13344, - "refs": 8, + "refs": 9, "section": "Q121", "shape": "BULLET+BOLD" }, @@ -5128,7 +5156,7 @@ The block below is the parse this document renders, byte for byte. `--check` com "form": "BOLD-LEAD", "id": "Q135", "line": 13797, - "refs": 1, + "refs": 2, "section": "Q114", "shape": "BULLET+BOLD" }, @@ -5140,6 +5168,15 @@ The block below is the parse this document renders, byte for byte. `--check` com "refs": 1, "section": "Q87", "shape": "BULLET+BOLD" + }, + { + "first_mention": 13847, + "form": "BOLD-LEAD", + "id": "Q137", + "line": 13847, + "refs": 2, + "section": "Q136", + "shape": "BULLET+BOLD" } ], "preamble": { @@ -5147,9 +5184,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 2275304, - "queue_lines": 13845, - "queue_sha256": "e8a496a3a633768d86cc52abce9061cf2b8b001da98e31471d4adb4ad906a014", + "queue_bytes": 2286222, + "queue_lines": 13877, + "queue_sha256": "95eb86cddfeead6087c1bc8e637c07d1af85bcbfacf25f69c68388a2d6640689", "sections": [ { "bytes": 3139, diff --git a/tools/verify.py b/tools/verify.py index 824d890..fad4f82 100644 --- a/tools/verify.py +++ b/tools/verify.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""verify.py -- the HOME the index gate did not have: one command, ten legs, +"""verify.py -- the HOME the index gate did not have: one command, eleven legs, one exit code, and a row per leg so a partial run cannot look like a clean one. WHY THIS EXISTS @@ -96,6 +96,13 @@ WHAT IT RUNS -- one row per leg, cheapest first, never in parallel files; the leg is here because a guard nothing runs is not a guard -- `afb231d` reverted all ten blobs and no gate leg saw the three artifacts. + open-set wizard-work/volt/q137-open-set/open_set.py --check + the queue's declared open set, derived from its own records + and graded against a pin: 0 open, 26 ids ever opened, 97 + headings / 97 records. A record appended without re-pinning + moves the count and reds this leg. The closure probes + (--closures) are NOT in the leg -- they read other lanes' + modules and cost ~3 s; `bash CHECK-q137` runs all four. The legs are ordered by cost, and a leg is never skipped because an earlier one failed: a stale index and a broken selftest are different defects and the @@ -223,6 +230,10 @@ LEGS = ( "what": "every repair a closed item landed is still the bytes that landed " "(sha256 pins for Q109/Q120 and the seven Q126 cells) and its " "cell still runs green"}, + {"key": "open-set", "tool": "open_set.py", "slow": False, + "path": os.path.join("wizard-work", "volt", "q137-open-set", "open_set.py"), + "what": "the work queue's declared open set and its record counts, " + "graded against a pin (Q137: 0 open, 26 ever opened)"}, ) # A detail line is a courtesy, not the verdict -- but a verdict with no line @@ -435,6 +446,13 @@ def leg_argv(key, repo, queue, index, checkin_day=None): os.path.join(repo, "wizard-work", "spark", "closed_artifact_guard.py"), "--root", repo] + if key == "open-set": + # --repo, so the queue graded is the one in the tree under test and not + # the copy beside this tool; --check reads and compares, nothing more. + return [sys.executable, + os.path.join(repo, "wizard-work", "volt", "q137-open-set", + "open_set.py"), + "--repo", repo, "--check"] raise KeyError(key) @@ -700,7 +718,9 @@ GREEN_ALL = {"checkin_check.py": 0, "path_claim_check.py": 0, "selftest_sweep.py": 0, "write_probe.py": 0, "import_resolve_check.py": 0, os.path.join("wizard-work", "spark", - "closed_artifact_guard.py"): 0} + "closed_artifact_guard.py"): 0, + os.path.join("wizard-work", "volt", "q137-open-set", + "open_set.py"): 0} def selftest(): @@ -720,8 +740,8 @@ def selftest(): # 1. ten green stubs -> rc 0 repo = _fixture(os.path.join(td, "c1"), GREEN_ALL) rc, out = _verify("--repo", repo) - check("1 all ten legs green -> rc 0", rc, 0) - check("1b summary counts the ten legs", "verify: 10 legs, 10 pass, 0 fault, " + check("1 all eleven legs green -> rc 0", rc, 0) + check("1b summary counts the eleven legs", "verify: 11 legs, 11 pass, 0 fault, " "0 ungradable, 0 skipped" in out, True) # 2. a stale index is a FAULT, and its own line reaches the row @@ -778,7 +798,7 @@ def selftest(): check("7 --json writes the rows", [r["key"] for r in obj.get("rows", [])], ["checkin", "path-claims", "reply-headings", "index-current", "append-audit", "doc-commands", "selftest-sweep", "write-axis", - "import-resolve", "closed-artifacts"]) + "import-resolve", "closed-artifacts", "open-set"]) check("7b --json carries the same rc the process returned", obj.get("rc"), rc) @@ -822,7 +842,7 @@ def selftest(): check("12 skipped leg is its own count in --json", obj.get("counts", {}).get("SKIP"), 1) check("12b and the skipped leg is not counted as a pass", - obj.get("counts", {}).get(PASS), 9) + obj.get("counts", {}).get(PASS), 10) # 13. the detail line must not contradict the verdict: a row graded # FAULT beside "0 fatal" is the SILENT failure this gate exists to catch. diff --git a/wizard-work/volt/q137-open-set/CHECK-q137 b/wizard-work/volt/q137-open-set/CHECK-q137 new file mode 100755 index 0000000..9ed6f81 --- /dev/null +++ b/wizard-work/volt/q137-open-set/CHECK-q137 @@ -0,0 +1,53 @@ +#!/bin/bash +# CHECK-q137 -- four legs over the work queue's declared open set. +# +# selftest 13 cells: 8 parser fixtures over synthetic text, then the pin +# logic itself -- green on the live queue, red on three mutated +# copies (an appended OPEN record, a DONE turned OPEN, a dropped +# heading). A gate that cannot fail is the class this repo keeps +# filing (Q113, Q120); this leg is what proves it can. +# open-set the derived open set and the counts, against their pin. +# order line order is commit order (git blame stamps), so "the last +# record for an id" is "the latest record", not an assumption. +# closures one probe per id that was ever opened: is the defect its record +# describes still present at HEAD? 20 measured, 6 unmeasurable +# from this tree (network, archive, live gate, database). +# +# Exit codes: 0 all legs green, 1 a leg faulted, 2 a leg could not grade. +set -u +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TOOL="$HERE/open_set.py" +PY="${PYTHON:-python3}" +rc=0 +legs=0 +faults=0 + +run_leg() { + name="$1"; shift + legs=$((legs + 1)) + out="$("$PY" "$TOOL" "$@" 2>&1)" + lrc=$? + if [ "$lrc" -eq 0 ]; then + printf ' %-9s PASS rc=0 %s\n' "$name" "$(printf '%s' "$out" | tail -1)" + elif [ "$lrc" -eq 1 ]; then + faults=$((faults + 1)) + printf ' %-9s FAULT rc=1 %s\n' "$name" "$(printf '%s' "$out" | tail -1)" + printf '%s\n' "$out" | grep -E '^(FAIL|!)' | head -5 + else + faults=$((faults + 1)) + printf ' %-9s UNGRADABLE rc=%s %s\n' "$name" "$lrc" "$(printf '%s' "$out" | tail -1)" + fi + [ "$lrc" -eq 0 ] || rc=1 +} + +run_leg selftest --selftest +run_leg open-set --check +run_leg order --order +run_leg closures --closures + +if [ "$rc" -eq 0 ]; then + echo "q137: $legs leg(s), all passed" +else + echo "q137: $legs leg(s), $faults fault(s)" +fi +exit "$rc" diff --git a/wizard-work/volt/q137-open-set/REPORT.md b/wizard-work/volt/q137-open-set/REPORT.md new file mode 100644 index 0000000..bbd02f3 --- /dev/null +++ b/wizard-work/volt/q137-open-set/REPORT.md @@ -0,0 +1,115 @@ +# Q137 — what the queue says is open, and whether its closures are still there + +Lane: volt (workhorse). Measured at `d3c7164` (`WORK-QUEUE.md` blob +`20564d2b7714`, 13,845 lines, 2,275,304 bytes), 2026-10-31. + +## The question + +`WORK-QUEUE.md` holds every item the troupe ever opened, each closed by a record +appended under it. Nothing derived the set of items that are *open now*: a +reader could `grep OPEN` or `grep DONE` and get a number, and the number would be +wrong in both directions, because both words appear in records about the same id. + +So: 26 lines begin a record with `OPEN`. 41 begin one with `DONE`. Which ids are +open at HEAD? Before this artifact, no file in the tree answered that. + +## What the file says, derived + + queue: 13845 lines, 97 headings/86 ids, 96 records/62 ids, 41 record-only ids + declared open: 0; ids ever opened: 26 + +`open_set.py --check` (rc 0) derives it: a declaration is a `## Q ` heading or a +`**Q — ` record; the last record for an id is its state, and +`OPENED and REPAIRED in the same tick` is a closure, not an opening. **Every one +of the 26 ids that was ever opened has a later record closing it** — the queue +declares no open item at HEAD. + +`grep -c '^\*\*Q[0-9]* — OPEN'` → 26 is therefore not a count of open work; it is +a count of openings, all historical. The last human summary of the open set in the +file (`:7789`, "Open set otherwise unchanged: Q52 …, Q53") names two ids that are +closed at `:8517` and `:9869`; it is a dated record and correct as a record, and +it is the last prose sentence that answers the question, which is why this needed +a parser rather than a search. + +## The assumption under "last record wins", measured + +Line order only means commit order if records are only ever appended. `--order` +blames all 96 record lines in one `git blame --porcelain` pass and compares the +commit timestamps in line order: + + record order: 85 commits over 96 records; 0 inversion(s) + +Zero inversions. So "the last record for an id" is "the latest record", measured +rather than assumed — and the leg re-measures it on every run, which is what +catches the first out-of-order insert. + +## The closures, measured against HEAD + +Each probe runs or reads the thing its record cites. 20 of 26 hold; 6 cannot be +settled from this tree. + +| Q | state | evidence read at HEAD | +|---|---|---| +| Q100 | REPAIRED | `TEST_NAME='(^|/)(_?test_[^/]*|[^/]*_test)\.py$'` selects 2/2 `_test_*` files | +| Q101 | REPAIRED | `wizard-work/ledger/census.py` compiles | +| Q102 | REPAIRED | `majority([None,None,1])`→`None`; `majority([1,2,3])` raises `NoMajority` | +| Q103 | REPAIRED | `queue_index.py --check docs/queue-index.md` rc 0, 86 ids incl. 3-digit | +| Q104 | REPAIRED | undecodable JSONL rc 2, no traceback | +| Q105 | REPAIRED | `beam/CHECK` runs `run_lane_tests.py` | +| Q106 | REPAIRED | dated sol gate carries 0 `/root` occurrences | +| Q107 | REPAIRED | dir with 2 invalid + 2 valid (one nested): rc 1, valid 2, invalid 2 | +| Q109 | REPAIRED | `check_tools` measures; graded by `test_env_verifiers.py` | +| Q110 | REPAIRED | one `git clone` step, and it is the forge URL | +| Q111 | REPAIRED | 0 mismatches in 60 exhaustive trials incl. zero-length intervals | +| Q112 | REPAIRED | census 97 rows/86 labels vs 97 headings/86 heading ids | +| Q113 | REPAIRED | seeded CSV mutation rc 1, clean tree rc 0 | +| Q115 | REPAIRED | monitor prints `UNREADABLE` and carries the last listing | +| Q117 | REPAIRED | `KNOWN_PROBE` present (10 occurrences) beside `KNOWN_FIXTURE` | +| Q118 | REPAIRED | retry loop present; `attempts=`/`last=` on the failure line | +| Q119 | REPAIRED | classifier names an `unclassified` fallback | +| Q120 | REPAIRED | `DEPENDENCIES` table + 2 `check_package(` call sites | +| Q121 | REPAIRED | per-value absence loop + two-entry mutant cell | +| Q122 | REPAIRED | census CHECK rc 0; the clock leg reads SKIP, not a stale PASS | +| Q51 | UNMEASURED | gate-wide probe window: needs the live gate | +| Q52 | UNMEASURED | archive attribution: needs pipeline logs | +| Q53 | UNMEASURED | digest domain + `state.db`: needs the live database | +| Q65 | UNMEASURED | population over 87 verifiers: the Q87 gate owns it now | +| Q114 | UNMEASURED | provider rows: needs the network | +| Q116 | UNMEASURED | OpenTrain `validThrough`: per-render, needs the network | + +20 REPAIRED, 6 UNMEASURED, 0 STILL-OPEN. The probes are pinned: a repair that +regresses, or a file that moves under a probe, reds `--closures` and names the id. + +## Instrument + + wizard-work/volt/q137-open-set/open_set.py derivation, order, 26 probes + wizard-work/volt/q137-open-set/CHECK-q137 4 legs, 13 selftest cells + tools/verify.py leg 11, `open-set` + +`--selftest` is 13 cells: 8 parser fixtures over synthetic text, then the gate +itself — green on the live queue, red on three mutated copies (an appended OPEN +record, a `DONE` turned `OPEN`, a dropped heading). That last group is the point: +a checker that can only print OK is the Q113/Q120 fault class, and this one is +measured refusing to be that. + +`bash wizard-work/volt/q137-open-set/CHECK-q137` → 4 legs, all passed, rc 0, 6.3 s. + +## Not claimed + +- **That the 6 unmeasured closures are wrong.** They are unmeasured; Q51/Q52/Q53 + were pipeline/host measurements, Q114/Q116 need provider endpoints, Q65's + population is now the Q87 gate's subject. +- **That a REPAIRED verdict proves the whole record.** Each probe checks the + defect its record names, at the granularity the record states it — no more. +- **That Q112's coverage arithmetic reconciles.** The census reports 86 distinct + labels against 86 heading ids (full heading coverage), and enumerates + record-form ids separately; its closure counts 36 of them, this parser counts + 41 ids with a record and no heading. The two numbers are produced from + different populations and this tick did not reconcile them — recorded here as + an open measurement, not filed as a fault against Q112. +- **That nothing else can open an id.** The parser reads `## Q` headings and + `**Q — ` records only. An id opened only in prose is invisible to it (Q53's + own record notes prose naming at `:7246` before its record at `:7292`). +- **That the pin is permanent.** Appending a record moves the counts: the + `open-set` leg reds, and the re-pin is a deliberate edit to `PIN_COUNTS` / + `PIN_OPENED` with the commit that moved it. diff --git a/wizard-work/volt/q137-open-set/open_set.py b/wizard-work/volt/q137-open-set/open_set.py new file mode 100644 index 0000000..78b0748 --- /dev/null +++ b/wizard-work/volt/q137-open-set/open_set.py @@ -0,0 +1,721 @@ +#!/usr/bin/env python3 +"""Q137 -- the work queue's declared open set, derived and then measured. + +Two questions, kept apart on purpose: + + DECLARED which ids does `WORK-QUEUE.md` itself leave open? That is a + textual fact: the last declaration for an id, and whether that + declaration's status word opens or closes it. `--check` + pins the answer so the set cannot drift unnoticed. + + MEASURED for each declared-open id, is the defect its record describes + still present at HEAD? Each probe is a named function that + reads or runs the thing the record cites and returns a state + plus the evidence it read. `--closures` pins those states. + +A repaired item with no closure record is the defect this file exists for: +the queue says "OPEN", a newcomer takes it, and the work is already done. +The probe table is the measurement; the pin is what makes the next run a +regression test rather than a fresh opinion. + +States + REPAIRED the cited defect is gone at HEAD, measured by the probe + PARTIAL one part of the record is satisfied, another is not + STILL-OPEN the cited defect is still there at HEAD + UNMEASURED no probe here can settle it (network, archive, judgement) + +Exit codes (tools/ contract) + 0 graded clean / pin matches + 1 a fault: drift from the pin, or a probe moved + 2 could not grade (missing file, unreadable queue, probe crashed) + +Stdlib only. Nothing here writes to the tree. +""" +import argparse +import ast +import importlib.util +import io +import json +import os +import re +import shutil +import subprocess +import sys +import tempfile +from contextlib import redirect_stdout +from pathlib import Path + +QUEUE = "WORK-QUEUE.md" + +# ---------------------------------------------------------------- declarations + +EM = "\u2014" +SEP = re.compile(r"^[\u2014\u2013-]{1,2}\s*") +DASHES = (" " + EM + " ", " -- ", " - ") +OPEN_WORDS = {"OPEN", "OPENED", "REOPENED"} +CLOSED_WORDS = {"DONE", "CLOSED", "VERIFIED", "REPAIRED", "ACCEPTED", + "CORRECTION", "ERRATUM", "WITHDRAWN", "RETIRED", + "ANSWERED", "RESOLVED"} +CLOSED_IN_PLACE = re.compile(r"OPENED and REPAIRED|OPENED and CLOSED|" + r"opened and repaired in the same tick") + +HEADING = re.compile(r"^(#{2,})\s*Q(\d+)\b([\s\u2014\u2013-].*)?$") +RECORD = re.compile(r"^\*\*Q(\d+)\b\s*[\u2014\u2013-]{1,2}\s*(.*)$") + + +def classify(tail): + """State word from the text after the id, or 'OTHER' / 'SECTION'.""" + t = tail.strip().lstrip("*").strip() + t = SEP.sub("", t).strip() + word = t.split()[0].strip(".,:;()") if t.split() else "" + word = word.strip("*").strip(".,:;()") + w = word.upper() + if w in OPEN_WORDS: + if CLOSED_IN_PLACE.search(t[:80]): + return "CLOSED" + return "OPEN" + if w in CLOSED_WORDS: + return "CLOSED" + return "OTHER" + + +def parse(text): + """-> (decls, state): decls is a list of dicts in line order.""" + decls = [] + for n, line in enumerate(text.splitlines(), 1): + m = RECORD.match(line) + kind = "record" + if not m: + m = HEADING.match(line) + kind = "heading" + if not m: + continue + qid = int(m.group(2) if kind == "heading" else m.group(1)) + tail = m.group(3) if kind == "heading" else m.group(2) + state = "SECTION" if kind == "heading" else classify(tail) + decls.append({ + "qid": qid, + "line": n, + "kind": kind, + "state": state, + "word": (tail.lstrip("*").strip() or "")[:100], + }) + state = {} + for d in decls: + if d["kind"] == "record": + state[d["qid"]] = d # last record wins, line order + elif d["qid"] not in state: + state[d["qid"]] = {"qid": d["qid"], "line": d["line"], + "kind": "heading", "state": "SECTION", + "word": d["word"]} + return decls, state + + +def open_ids(state): + return sorted(q for q, d in state.items() if d["state"] == "OPEN") + + +def opened_ids(decls): + """Every id that carries at least one OPEN record, closed or not.""" + return sorted({d["qid"] for d in decls + if d["kind"] == "record" and d["state"] == "OPEN"}) + + +def counts(decls, state): + heads = [d for d in decls if d["kind"] == "heading"] + recs = [d for d in decls if d["kind"] == "record"] + return { + "headings": len(heads), + "heading_ids": len({d["qid"] for d in heads}), + "records": len(recs), + "record_ids": len({d["qid"] for d in recs}), + "ids": len(state), + "open": len(open_ids(state)), + "opened": len(opened_ids(decls)), + "record_only": len({d["qid"] for d in recs} - {d["qid"] for d in heads}), + } + + +# ---------------------------------------------------------------- the probes +# +# Each probe takes the repo root and returns (state, evidence). Evidence is +# the thing a reader can check: a file:line, an rc, a count, a sentence. + +def read(repo, rel): + return (repo / rel).read_text(encoding="utf-8", errors="replace") + + +def run(argv, cwd, timeout=120): + try: + p = subprocess.run(argv, cwd=str(cwd), capture_output=True, text=True, + timeout=timeout) + return p.returncode, (p.stdout or "") + (p.stderr or "") + except (OSError, subprocess.TimeoutExpired) as exc: + return None, str(exc) + + +def _load(path, name): + spec = importlib.util.spec_from_file_location(name, str(path)) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + +def probe_q100(repo): + """Q100: import_resolve_check's TEST_NAME must select `_test_x.py`.""" + src = read(repo, "tools/import_resolve_check.py") + pat = None + for node in ast.walk(ast.parse(src)): + if isinstance(node, ast.Assign): + for t in node.targets: + if isinstance(t, ast.Name) and t.id == "TEST_NAME": + call = node.value + if isinstance(call, ast.Call) and call.args: + try: + pat = ast.literal_eval(call.args[0]) + except ValueError: + pat = None + if pat is None: + return "STILL-OPEN", "TEST_NAME not found as a literal in import_resolve_check.py" + rx = re.compile(pat) + samples = ["wizard-work/forge/_test_roundtrip.py", + "wizard-work/forge/test_log.txt", + "wizard-work/forge/_test_roundtrip_refactored.py"] + hit = [s for s in samples[:1] + samples[2:] if rx.search(s)] + return ("REPAIRED" if len(hit) == 2 else "STILL-OPEN", + "TEST_NAME=%r selects %d/2 underscore test files" % (pat, len(hit))) + + +def probe_q101(repo): + """Q101: ledger/census.py must at least parse.""" + path = repo / "wizard-work/ledger/census.py" + try: + compile(path.read_text(encoding="utf-8"), str(path), "exec") + except (OSError, SyntaxError) as exc: + return "STILL-OPEN", "census.py will not compile: %s" % (exc,) + return "REPAIRED", "census.py compiles (compile() at %s)" % path.name + + +def probe_q102(repo): + """Q102: three outcomes must be three channels.""" + mod = _load(repo / "wizard-work/beam/majority_element.py", "q137_maj") + try: + got = mod.majority([None, None, 1]) + none_majority = got is None + except Exception: # noqa: BLE001 + none_majority = False + raised = False + try: + mod.majority([1, 2, 3]) + except Exception as exc: # noqa: BLE001 + raised = type(exc).__name__ == "NoMajority" + return ("REPAIRED" if none_majority and raised else "STILL-OPEN", + "majority([None,None,1])->None=%s; majority([1,2,3]) raises " + "NoMajority=%s" % (none_majority, raised)) + + +def probe_q103(repo): + """Q103: queue_index must see ids of any width.""" + rc, out = run([sys.executable, "tools/queue_index.py", + "--check", "docs/queue-index.md"], repo) + ok = rc == 0 + return ("REPAIRED" if ok else "STILL-OPEN", + "queue_index --check docs/queue-index.md rc=%s; %s" + % (rc, out.strip().splitlines()[-1][:120] if out.strip() else "")) + + +def probe_q104(repo): + """Q104: undecodable JSONL must be a typed exit 2, no traceback.""" + tmp = Path(tempfile.mkdtemp(prefix="q137-q104-")) + try: + bad = tmp / "bad.jsonl" + bad.write_bytes(b'{"a": 1}\n\xff\xfe not utf-8\n') + rc, out = run([sys.executable, "wizard-work/beam/jsonl_validate.py", + str(bad)], repo) + finally: + shutil.rmtree(tmp, ignore_errors=True) + typed = rc == 2 and "Traceback" not in out + return ("REPAIRED" if typed else "STILL-OPEN", + "undecodable input rc=%s traceback=%s" + % (rc, "Traceback" in out)) + + +def probe_q105(repo): + """Q105/Q128: the beam gate must collect both test styles.""" + txt = read(repo, "wizard-work/beam/CHECK") + ok = "run_lane_tests.py" in txt + return ("REPAIRED" if ok else "STILL-OPEN", + "beam/CHECK runs run_lane_tests.py=%s (%d lines)" + % (ok, len(txt.splitlines()))) + + +def probe_q106(repo): + """Q106: the dated sol gate must not pin an absolute host root.""" + txt = read(repo, "wizard-work/sol/CHECK-2026-09-28") + n = txt.count("/root") + return ("REPAIRED" if n == 0 else "STILL-OPEN", + "CHECK-2026-09-28 /root occurrences=%d" % n) + + +def probe_q107(repo): + """Q107: --dir must not exit the number of invalid files.""" + tmp = Path(tempfile.mkdtemp(prefix="q137-q107-")) + try: + (tmp / "ok.json").write_text('{"a": 1}', encoding="utf-8") + (tmp / "bad1.json").write_text("{nope", encoding="utf-8") + (tmp / "bad2.json").write_text("{nope", encoding="utf-8") + (tmp / "sub").mkdir() + (tmp / "sub" / "deep.json").write_text('{"b": 2}', encoding="utf-8") + rc, out = run([sys.executable, "wizard-work/spark/json-validator.py", + str(tmp)], repo) + finally: + shutil.rmtree(tmp, ignore_errors=True) + valid = sum(1 for ln in out.splitlines() if ln.startswith("VALID:")) + invalid = sum(1 for ln in out.splitlines() if ln.startswith("INVALID:")) + ok = rc == 1 and (valid, invalid) == (2, 2) + return ("REPAIRED" if ok else "STILL-OPEN", + "dir with 2 invalid + 2 valid (one nested): rc=%s valid=%d invalid=%d" + % (rc, valid, invalid)) + + +def probe_q109(repo): + """Q109: snippet.py's tool check must measure, not assume.""" + txt = read(repo, "wizard-work/spark/snippet.py") + has_fn = "def check_tools" in txt + returns_measurement = "available, error = check_tools" in txt + graded = (repo / "wizard-work/spark/test_env_verifiers.py").is_file() + ok = has_fn and returns_measurement and graded + return ("REPAIRED" if ok else "STILL-OPEN", + "check_tools=%s, call site=%s, graded by test_env_verifiers=%s" + % (has_fn, returns_measurement, graded)) + + +def probe_q110(repo): + """Q110: the QUICKSTART's clone step must point at a live repository.""" + txt = read(repo, "wizard-work/quill/QUICKSTART.md") + clones = re.findall(r"^git clone (\S+)", txt, re.M) + live = [u for u in clones if "forge.alexanderwhitestone.com" in u] + ok = len(clones) == 1 and len(live) == 1 + return ("REPAIRED" if ok else "STILL-OPEN", + "%d git clone step(s): %s" % (len(clones), clones[:3])) + + +def probe_q111(repo): + """Q111: zero-length intervals must not skew the optimum.""" + mod = _load(repo / "wizard-work/beam/weighted_interval_scheduling.py", + "q137_wis") + fn = getattr(mod, "weighted_interval_scheduling") + + def ok_pair(sel): + for i in range(len(sel)): + for j in range(i + 1, len(sel)): + a, b = sel[i], sel[j] + if min(a[1], b[1]) - max(a[0], b[0]) > 0: + return False + return True + + bad = 0 + trials = 0 + import random + rng = random.Random(1371) + for _ in range(60): + n_try = rng.randint(1, 4) + iv = [[rng.randint(0, 3), rng.randint(0, 3), rng.randint(1, 4)] + for _ in range(n_try)] + iv = [[min(a, b), max(a, b), w] for a, b, w in iv] + got = fn(iv) + best = 0 + m = len(iv) + for mask in range(1 << m): + sel = [iv[i] for i in range(m) if mask >> i & 1] + if ok_pair(sel) and sum(x[2] for x in sel) > best: + best = sum(x[2] for x in sel) + trials += 1 + if got != best: + bad += 1 + return ("REPAIRED" if bad == 0 and trials else "STILL-OPEN", + "zero-length included: %d mismatches in %d exhaustive trials" + % (bad, trials)) + + +def probe_q112(repo): + """Q112: the census must see every heading, and every record.""" + rc, out = run([sys.executable, + "wizard-work/ledger/workqueue-census-v2/CHECK"], repo) + line = "" + for ln in out.splitlines(): + if "distinct labels" in ln: + line = ln.strip() + if rc != 0 or not line: + return "STILL-OPEN", "census CHECK rc=%s, no summary line" % rc + m = re.search(r"\((\d+) rows, (\d+) distinct labels", line) + text = read(repo, QUEUE) + decls, state = parse(text) + c = counts(decls, state) + rows, labels = (int(m.group(1)), int(m.group(2))) if m else (0, 0) + covered = rows >= c["headings"] and labels >= c["heading_ids"] + return ("REPAIRED" if covered else "STILL-OPEN", + "census %d rows/%d labels vs %d headings/%d heading ids; " + "%d record-only ids are enumerated by the extractor, not as rows" + % (rows, labels, c["headings"], c["heading_ids"], c["record_only"])) + + +def probe_q113(repo): + """Q113: the roster gate must fail on a seeded mutation.""" + led = "wizard-work/ledger" + tmp = Path(tempfile.mkdtemp(prefix="q137-q113-")) + try: + dst = tmp / led + dst.mkdir(parents=True) + for name in ("wizards.csv", "wizards.manifest.json"): + shutil.copy2(repo / led / name, dst / name) + man = json.loads((dst / "wizards.manifest.json").read_text(encoding="utf-8")) + src_rel = man.get("source", "") + if src_rel: + (tmp / src_rel).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(repo / src_rel, tmp / src_rel) + csv = (dst / "wizards.csv").read_text(encoding="utf-8") + (dst / "wizards.csv").write_text(csv.replace("Timmy", "GARBAGE NAME"), + encoding="utf-8") + bad_rc, _ = run([sys.executable, led + "/verify_wizards.py", + "--root", str(tmp)], repo) + good_rc, _ = run([sys.executable, led + "/verify_wizards.py", + "--root", str(repo)], repo) + finally: + shutil.rmtree(tmp, ignore_errors=True) + ok = bad_rc == 1 and good_rc == 0 + return ("REPAIRED" if ok else "STILL-OPEN", + "seeded mutation rc=%s, clean tree rc=%s" % (bad_rc, good_rc)) + + +def probe_q115(repo): + """Q115: an unreadable folder must not read as a mass deletion.""" + txt = Path("/root/.hermes/scripts/troupe_pipe_monitor.py").read_text( + encoding="utf-8", errors="replace") + ok = "UNREADABLE" in txt and "carried" in txt + return ("REPAIRED" if ok else "UNMEASURED", + "monitor separates UNREADABLE=%s and carries the listing=%s" + % ("UNREADABLE" in txt, "carried" in txt)) + + +def probe_q117(repo): + """Q117: an invented probe value must be data, not a recovery.""" + txt = read(repo, "wizard-work/zeph/q60_enforce.py") + ok = "KNOWN_PROBE" in txt and txt.count("KNOWN_PROBE") >= 3 + return ("REPAIRED" if ok else "STILL-OPEN", + "q60_enforce.py KNOWN_PROBE occurrences=%d" + % txt.count("KNOWN_PROBE")) + + +def probe_q118(repo): + """Q118: a failed listing must be retried and report attempts/last.""" + txt = Path("/root/.hermes/scripts/troupe_pipe_monitor.py").read_text( + encoding="utf-8", errors="replace") + retry = "for attempt in range(1, tries + 1)" in txt + fields = "attempts=" in txt and "last=" in txt + return ("REPAIRED" if retry and fields else "STILL-OPEN", + "retry loop=%s, attempts=/last= fields=%s" % (retry, fields)) + + +def probe_q119(repo): + """Q119: an unmatched demand must not be silently 'info'.""" + txt = read(repo, "wizard-work/sol/classify.py") + ok = "unclassified" in txt and "Fallback" in txt + return ("REPAIRED" if ok else "STILL-OPEN", + "classify.py names an unclassified fallback in its header=%s" % ok) + + +def probe_q120(repo): + """Q120: the env check must import real dependencies and report versions.""" + txt = read(repo, "wizard-work/spark/python-env-check.py") + decl = "DEPENDENCIES" in txt + called = len(re.findall(r"check_package\(", txt)) >= 2 + return ("REPAIRED" if decl and called else "STILL-OPEN", + "DEPENDENCIES table=%s, check_package call sites=%d" + % (decl, len(re.findall(r"check_package\(", txt)))) + + +def probe_q121(repo): + """Q121: fixture absence must be asserted per value, not over a join.""" + txt = read(repo, "wizard-work/zeph/q60_enforce.py") + per_value = "for value in KNOWN_FIXTURE.values()" in txt or \ + "KNOWN_FIXTURE.values()" in txt + mutant = "two_entry" in txt + ok = per_value and mutant + return ("REPAIRED" if ok else "STILL-OPEN", + "per-value absence=%s, two-entry mutant cell=%s" + % (per_value, mutant)) + + +def probe_q122(repo): + """Q122: the census CHECK must not gate on a wall clock.""" + rc, out = run([sys.executable, + "wizard-work/ledger/workqueue-census-v2/CHECK"], repo) + skipped = "SKIP" in out.upper() + return ("REPAIRED" if rc == 0 else "STILL-OPEN", + "census CHECK rc=%s, clock leg reads %s" + % (rc, "SKIP" if skipped else "graded")) + + +def unmade(reason): + def probe(repo): + return "UNMEASURED", reason + return probe + + +PROBES = { + 100: probe_q100, 101: probe_q101, 102: probe_q102, 103: probe_q103, + 104: probe_q104, 105: probe_q105, 106: probe_q106, 107: probe_q107, + 109: probe_q109, 110: probe_q110, 111: probe_q111, 112: probe_q112, + 113: probe_q113, 115: probe_q115, 117: probe_q117, 118: probe_q118, + 119: probe_q119, 120: probe_q120, 121: probe_q121, 122: probe_q122, + 51: unmade("gate-wide probe window: needs the live gate, not a file read"), + 52: unmade("archive attribution: needs pipeline logs this tree does not hold"), + 53: unmade("digest domain + state.db: needs the live database"), + 65: unmade("population claim over 87 verifiers; the Q87 gate owns it now"), + 114: unmade("free-tier rows contradict six provider endpoints: network"), + 116: unmade("OpenTrain validThrough is per-render: network, time-lapsed"), +} + +PIN_OPEN = [] +PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122] + +PIN_COUNTS = {"headings": 97, "heading_ids": 86, "records": 97, + "record_ids": 63, "ids": 128, "open": 0, "opened": 26, + "record_only": 42} + +PIN_STATE = { + 100: "REPAIRED", 101: "REPAIRED", 102: "REPAIRED", 103: "REPAIRED", + 104: "REPAIRED", 105: "REPAIRED", 106: "REPAIRED", 107: "REPAIRED", + 109: "REPAIRED", 110: "REPAIRED", 111: "REPAIRED", 112: "REPAIRED", + 113: "REPAIRED", 115: "REPAIRED", 117: "REPAIRED", 118: "REPAIRED", + 119: "REPAIRED", 120: "REPAIRED", 121: "REPAIRED", 122: "REPAIRED", + 51: "UNMEASURED", 52: "UNMEASURED", 53: "UNMEASURED", 65: "UNMEASURED", + 114: "UNMEASURED", 116: "UNMEASURED", +} + + +# ---------------------------------------------------------------- the selftest + +FIXTURES = [ + ("open-then-done", + "**Q5 \u2014 OPEN (a).** body\n**Q5 \u2014 DONE at `abc`.**\n", 5, "CLOSED"), + ("opened-and-repaired-in-place", + "**Q7 \u2014 OPENED and REPAIRED in the same tick (volt).** body\n", + 7, "CLOSED"), + ("open-with-no-close", + "**Q9 \u2014 OPEN (pipe).** body\n", 9, "OPEN"), + ("heading-only", + "## Q11 \u2014 A section title\nbody\n", 11, "SECTION"), + ("heading-then-record", + "## Q13 \u2014 A section title\nbody\n**Q13 \u2014 DONE.**\n", 13, "CLOSED"), + ("bold-not-a-declaration", + "**Q15 measured.** prose, no dash\n", 15, None), + ("width-boundary", + "**Q1000 \u2014 OPEN (x).**\n**Q100 \u2014 DONE.**\n", 100, "CLOSED"), + ("reopened-after-done", + "**Q17 \u2014 DONE.**\n**Q17 \u2014 REOPENED (pipe).**\n", 17, "OPEN"), +] + + +def graded(repo, text): + """mode_check's rc with its output swallowed -- for the mutant cells.""" + buf = io.StringIO() + try: + with redirect_stdout(buf): + return mode_check(repo, text) + finally: + buf.close() + + +def selftest(): + cells = [] + for name, text, qid, want in FIXTURES: + _, state = parse(text) + got = state.get(qid, {}).get("state") + cells.append((name, got == want, "got=%r want=%r" % (got, want))) + repo, text = load_repo(str(Path(__file__).resolve().parents[3])) + if repo is not None and text is not None: + once = parse(text) + cells.append(("parse-is-deterministic", once == parse(text), "")) + cells.append(("gate-green-control", graded(repo, text) == 0, "rc")) + cells.append(("gate-new-open-id", + graded(repo, text + "\n**Q999 \u2014 OPEN (x).**\n") == 1, + "rc")) + cells.append(("gate-done-turned-open", + graded(repo, text.replace("**Q117 \u2014 DONE", + "**Q117 \u2014 OPEN", 1)) == 1, + "rc")) + cells.append(("gate-heading-dropped", + graded(repo, re.sub(r"(?m)^## Q5 \u2014.*\n", "", text, + count=1)) == 1, "rc")) + bad = [c for c in cells if not c[1]] + for name, ok, detail in cells: + print("%-28s %s %s" % (name, "PASS" if ok else "FAIL", detail)) + print("SELFTEST %s (%d cells, %d failed)" + % ("OK" if not bad else "FAILED", len(cells), len(bad))) + return 0 if not bad else 1 + + +def parse_text_roundtrip(): + return "**Q1 \u2014 OPEN (x).**\n" + + +# ---------------------------------------------------------------- the modes + +def load_repo(arg): + root = Path(arg).resolve() + q = root / QUEUE + if not q.is_file(): + print("cannot grade: no %s under %s" % (QUEUE, root)) + return None, None + return root, q.read_text(encoding="utf-8") + + +def mode_check(repo, text): + decls, state = parse(text) + c = counts(decls, state) + got = open_ids(state) + print("queue: %d lines, %d headings/%d ids, %d records/%d ids, " + "%d record-only ids" % (len(text.splitlines()), c["headings"], + c["heading_ids"], c["records"], + c["record_ids"], c["record_only"])) + print("declared open: %d; ids ever opened: %d" % (len(got), c["opened"])) + faults = [] + for key in sorted(PIN_COUNTS): + if c[key] != PIN_COUNTS[key]: + faults.append("count %s moved %d -> %d" % (key, PIN_COUNTS[key], c[key])) + if got != PIN_OPEN: + added = [q for q in got if q not in PIN_OPEN] + gone = [q for q in PIN_OPEN if q not in got] + faults.append("open set moved: +%s -%s" % (added, gone)) + if opened_ids(decls) != PIN_OPENED: + faults.append("the set of ever-opened ids moved") + for f in faults: + print("FAIL %s" % f) + print("CHECK %s" % ("OK: 0 open, 26 ever opened, counts match the pin" + if not faults else "%d fault(s)" % len(faults))) + return 0 if not faults else 1 + + +def mode_order(repo, text): + """Line order must be commit order, or 'last record' is not 'latest'.""" + decls, _ = parse(text) + recs = [d for d in decls if d["kind"] == "record"] + rc, out = run(["git", "blame", "--porcelain", "--", QUEUE], repo) + if rc != 0 or not out: + print("cannot grade order: git blame failed") + return 2 + by_line = {} + commit = None + for ln in out.splitlines(): + m = re.match(r"^([0-9a-f]{40}) \d+ (\d+)(?: \d+)?$", ln) + if m: + commit = m.group(1) + by_line[int(m.group(2))] = commit + times = {} + stamps = [] + pending = 0 + for d in recs: + c = by_line.get(d["line"]) + if c is None: + print("cannot grade order: no blame entry for line %d" % d["line"]) + return 2 + if set(c) == {"0"}: + pending += 1 # appended, not yet committed + continue + if c not in times: + trc, tout = run(["git", "show", "-s", "--format=%ct", c], repo) + if trc != 0: + print("cannot grade order: no timestamp for %s" % c[:12]) + return 2 + times[c] = int(tout.strip()) + stamps.append((d["line"], times[c], c)) + if not stamps: + print("cannot grade order: no committed record lines") + return 2 + bad = [] + for (l1, t1, c1), (l2, t2, c2) in zip(stamps, stamps[1:]): + if t2 < t1: + bad.append("line %d (%s) is newer than line %d (%s)" + % (l2, c2[:8], l1, c1[:8])) + print("record order: %d commits over %d committed records (%d pending " + "commit); %d inversion(s)" + % (len(times), len(stamps), pending, len(bad))) + for b in bad[:5]: + print("FAIL %s" % b) + print("CHECK %s" % ("OK: line order is commit order, so 'last record' is " + "'latest'" if not bad else "%d inversion(s)" % len(bad))) + return 0 if not bad else 1 + + +def mode_table(repo, text): + decls, state = parse(text) + for q in opened_ids(decls): + d = state[q] + print("Q%-4d %-6s :%-6d %s" % (q, d["state"], d["line"], d["word"][:90])) + return 0 + + +def mode_closures(repo, text): + decls, state = parse(text) + want = opened_ids(decls) + if want != PIN_OPENED: + print("cannot grade closures: the ever-opened set moved, run --check") + return 2 + faults = [] + rows = [] + for q in want: + probe = PROBES.get(q) + if probe is None: + rows.append((q, "UNMEASURED", "no probe registered")) + continue + try: + got, ev = probe(repo) + except Exception as exc: # noqa: BLE001 + print("Q%-4d UNGRADABLE %s: %s" % (q, type(exc).__name__, exc)) + return 2 + rows.append((q, got, ev)) + tally = {} + for q, got, ev in rows: + tally[got] = tally.get(got, 0) + 1 + pin = PIN_STATE.get(q) + mark = " " if got == pin else "!" + print("%sQ%-4d %-10s %s" % (mark, q, got, ev)) + if got != pin: + faults.append("Q%d moved %s -> %s" % (q, pin, got)) + print("closure tally: %s" % ", ".join("%s %d" % kv for kv in sorted(tally.items()))) + for f in faults: + print("FAIL %s -- re-pin with the commit that moved it" % f) + print("CHECK %s" % ("OK: %d ever-opened ids, %d measured, states match the pin" + % (len(want), len(rows) - tally.get("UNMEASURED", 0)) + if not faults else "%d fault(s)" % len(faults))) + return 0 if not faults else 1 + + +def main(argv=None): + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--repo", default=str(Path(__file__).resolve().parents[3])) + ap.add_argument("--check", action="store_true", help="grade the declared set") + ap.add_argument("--closures", action="store_true", help="grade measured states") + ap.add_argument("--order", action="store_true", help="line order vs commit order") + ap.add_argument("--table", action="store_true", help="print the open set") + ap.add_argument("--selftest", action="store_true", help="parser cells") + args = ap.parse_args(argv) + if args.selftest: + return selftest() + repo, text = load_repo(args.repo) + if repo is None: + return 2 + if args.table: + return mode_table(repo, text) + if args.order: + return mode_order(repo, text) + if args.closures: + return mode_closures(repo, text) + return mode_check(repo, text) + + +if __name__ == "__main__": + sys.exit(main())