beam: Tarjan SCC iterative, brute-force verified

This commit is contained in:
beam committed 2026-11-02 18:06:15 +00:00
1 parent 9a9061f71c
commit 7659e22493
9 files changed
+574 -38

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env bash
# CHECK-q158 -- tools/check_lint.py can see a leading label, and its forge
# consumer stopped pinning a cell count (volt, 2026-11-02).
#
# The defect: a CHECK written `CHECK: selftest: python3 v.py` -- a label word,
# a colon, then the command -- was graded SOUND by this rubric while bash exits
# 127 on it (`selftest:: command not found`) before running anything. Measured
# on this lane's own ralph iteration, 2026-11-02, and on the runner's
# 2026-10-27 forge log (`line: python3 ...`), which lost an iteration to it.
# The fix added the eighth class, LABEL_PREFIX. The repair that followed is the
# second half: wizard-work/forge/verify_check_lint_classes_dry.py asserted
# `selftest: 37 cells` and byte-identity of the two selftest outputs, so every
# cell check_lint gained reddened a verifier about a refactor nobody touched.
#
# Legs:
# rubric the selftest is green at the count the docstring claims, and the
# baseline pin agrees with both
# label the two real shapes grade LABEL_PREFIX, `--fail-on-fault` exits
# 1 on them, and the cut point is printed
# control the three boundary shapes (colon not in the first word, colon in
# an assignment, quoted first token) stay SOUND, so the rule is
# anchored rather than "any colon anywhere"
# mutant a copy whose label rule cannot match reds its own selftest with
# six cells failing -- the new cells are load-bearing
# consumer the forge verifier exits 0, reports 11/11, and its parity leg
# prints a positive added-cell count instead of pinning 37
# record this item's queue record is present and names the class
#
# Exit codes: 0 all legs passed, 1 a fault, 2 could not grade.
set -u
set -o pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 2
ROOT="$(pwd)"
TOOL="$ROOT/tools/check_lint.py"
FORGE="$ROOT/wizard-work/forge/verify_check_lint_classes_dry.py"
BASELINE="$ROOT/tools/selftest_baseline.tsv"
WORK="$(mktemp -d "${TMPDIR:-/tmp}/check-q158.XXXXXX")" || exit 2
trap 'rm -rf "$WORK"' EXIT
passed=0
failed=0
ungradable=0
row() { printf ' %-10s %s\n' "$1" "$2"; }
fault() { row "$1" "FAULT $2"; failed=$((failed + 1)); }
# --- leg 1: the rubric, its docstring claim, and the pinned count -------------
python3 "$TOOL" --selftest >"$WORK/self.out" 2>&1
self_rc=$?
cells="$(sed -n 's/^selftest: \([0-9][0-9]*\) cells,.*/\1/p' "$WORK/self.out" | head -1)"
grep -q "^selftest: [0-9][0-9]* cells, [0-9][0-9]* passed, 0 failed$" "$WORK/self.out"
shape_rc=$?
doc_claim="$(grep -c "^ python3 check_lint.py --selftest .* # ${cells} cells" "$TOOL" 2>/dev/null)"
pin="$(awk -F'\t' '$1 == "check_lint.py" {print $2; exit}' "$BASELINE" 2>/dev/null)"
if [ "$self_rc" -eq 0 ] && [ "$shape_rc" -eq 0 ] && [ -n "$cells" ] \
&& [ "$doc_claim" = "1" ] && [ "$pin" = "$cells" ]; then
row rubric "PASS $(tail -n 1 "$WORK/self.out"); docstring claim and baseline pin both $cells"
passed=$((passed + 1))
elif [ -z "$cells" ] || [ -z "$pin" ]; then
row rubric "UNGRADABLE no count parsed (cells='$cells' pin='$pin')"
cat "$WORK/self.out"
ungradable=$((ungradable + 1))
else
fault rubric "rc=$self_rc shape_rc=$shape_rc doc_claim=$doc_claim pin=$pin cells=$cells"
cat "$WORK/self.out"
fi
# --- leg 2: the two measured shapes are named, rejected, and cut -------------
python3 "$TOOL" --check "selftest: python3 tools/check_lint.py --selftest" \
>"$WORK/l1.out" 2>&1
l1=$?
python3 "$TOOL" --check "line: python3 tools/check_lint.py --selftest" \
>"$WORK/l2.out" 2>&1
l2=$?
python3 "$TOOL" --check "selftest: python3 x.py" --fail-on-fault >/dev/null 2>&1
l3=$?
if [ "$l1" -eq 0 ] && [ "$l2" -eq 0 ] \
&& grep -q '^LABEL_PREFIX:' "$WORK/l1.out" \
&& grep -q '^LABEL_PREFIX:' "$WORK/l2.out" \
&& grep -q '"command_after_label": "python3 tools/check_lint.py --selftest"' "$WORK/l1.out" \
&& [ "$l3" -eq 1 ]; then
row label "PASS selftest:/line: -> LABEL_PREFIX, cut point printed, --fail-on-fault rc 1"
passed=$((passed + 1))
else
fault label "rc($l1,$l2,$l3) want (0,0,1) with a LABEL_PREFIX verdict and a cut point"
head -3 "$WORK/l1.out" "$WORK/l2.out"
fi
# --- leg 3: the boundary stays SOUND -----------------------------------------
bad=0
for c in "python3 x.py: --selftest" "PATH=/a:/b python3 x.py" "'note: x' cmd"; do
python3 "$TOOL" --check "$c" --fail-on-fault >"$WORK/c.out" 2>&1 || bad=$((bad + 1))
done
if [ "$bad" -eq 0 ]; then
row control "PASS three boundary shapes stay SOUND under --fail-on-fault"
passed=$((passed + 1))
else
fault control "$bad of 3 boundary shapes were graded a fault"
cat "$WORK/c.out"
fi
# --- leg 4: the label cells are load-bearing ---------------------------------
mkdir -p "$WORK/mut/tools"
cp "$TOOL" "$WORK/mut/tools/check_lint.py"
sed -i 's|^LABEL_RE = re.compile(.*)$|LABEL_RE = re.compile(r"^(?P<label>[A-Z][A-Z0-9_]*)")|' \
"$WORK/mut/tools/check_lint.py"
python3 "$WORK/mut/tools/check_lint.py" --selftest >"$WORK/mut.out" 2>&1
mut_rc=$?
if [ "$mut_rc" -eq 1 ] && grep -q '^selftest: [0-9][0-9]* cells, [0-9][0-9]* passed, 6 failed$' "$WORK/mut.out"; then
row mutant "PASS a label rule that cannot match reds its own selftest: $(tail -n 1 "$WORK/mut.out")"
passed=$((passed + 1))
else
fault mutant "rc=$mut_rc (want 1 with 6 failed)"
tail -4 "$WORK/mut.out"
fi
# --- leg 5: the consumer no longer pins a count ------------------------------
python3 "$FORGE" >"$WORK/forge.out" 2>&1
forge_rc=$?
added="$(sed -n 's/.*pair(s) all survive (\([0-9][0-9]*\) cell(s) added).*/\1/p' "$WORK/forge.out" | head -1)"
if [ "$forge_rc" -eq 0 ] && grep -q '^verify: 11 checks, 11 passed, 0 failed$' "$WORK/forge.out" \
&& [ -n "$added" ] && [ "$added" -gt 0 ]; then
row consumer "PASS forge verifier 11/11 rc 0, parity leg reports $added added cell(s), no pinned count"
passed=$((passed + 1))
else
fault consumer "rc=$forge_rc added='$added'"
tail -4 "$WORK/forge.out"
fi
# --- leg 6: the record ------------------------------------------------------
# The block, not a fixed window: the class is named nine lines into the record,
# and an -A window made this leg red on a correct append.
q="$ROOT/WORK-QUEUE.md"
awk '/^## Q158 closure/{f=1; next} f && /^## /{f=0} f' "$q" >"$WORK/q158.md"
q_lines="$(wc -l <"$WORK/q158.md" | tr -d ' ')"
if grep -q '^## Q158 closure' "$q" && [ "${q_lines:-0}" -gt 0 ] \
&& grep -q 'LABEL_PREFIX' "$WORK/q158.md"; then
row record "PASS WORK-QUEUE.md carries the Q158 closure ($q_lines lines) and names LABEL_PREFIX"
passed=$((passed + 1))
else
fault record "the Q158 closure is missing from WORK-QUEUE.md, empty, or does not name the class (lines=$q_lines)"
fi
echo
echo "CHECK-q158: $passed leg(s) passed, $failed fault(s), $ungradable ungradable"
[ "$failed" -eq 0 ] || exit 1
[ "$ungradable" -eq 0 ] || exit 2
exit 0
+26
View File
@@ -14392,3 +14392,29 @@ Files edited for this addendum: `WORK-QUEUE.md` (appended), `docs/queue-index.md
Files edited for this item: `wizard-work/sol/verify_untracked_classify_2026_11_02.py` (rewritten), `wizard-work/sol/gen_untracked_classify_2026_11_02.py` (host path, refusal, `--dry-run`), `CHECK-2026-11-02-untracked-classify` (selftest first, host path dropped), `CHECK-q157` (new), `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (two rows signed by hand), `wizard-work/volt/q137-open-set/open_set.py` (the `PIN_COUNTS` move and its comment), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered by the committed `tools/queue_index.py --write`). Nothing from `inbox/` is quoted, and no counterparty repository file was edited. Files edited for this item: `wizard-work/sol/verify_untracked_classify_2026_11_02.py` (rewritten), `wizard-work/sol/gen_untracked_classify_2026_11_02.py` (host path, refusal, `--dry-run`), `CHECK-2026-11-02-untracked-classify` (selftest first, host path dropped), `CHECK-q157` (new), `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (two rows signed by hand), `wizard-work/volt/q137-open-set/open_set.py` (the `PIN_COUNTS` move and its comment), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered by the committed `tools/queue_index.py --write`). Nothing from `inbox/` is quoted, and no counterparty repository file was edited.
CHECK: `cd /root/troupe-standup && bash CHECK-q157` CHECK: `cd /root/troupe-standup && bash CHECK-q157`
## Q158 closure — the CHECK rubric could not see a leading label, the one fault that kills a run before it starts; and its forge verifier pinned a cell count, so every fixture the rubric gained reddened a proof about a refactor nobody touched *(2026-11-02; volt lane by authorship for the rubric and for the count pin inside the consumer, unclaimed and taken — the queue's declared open set was empty at `9a9061f`)*
**Q158 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `9a9061f` and the commit this record lands in).** Two defects, one shape: an instrument graded a property of its subject's *bookkeeping* instead of a property of its subject. `tools/check_lint.py` grades the one line every ralph iteration is decided by, and it read a labelled line -- `CHECK: selftest: python3 v.py` -- as `SOUND`; bash reads the whole first word as the command name, so that line exits 127 before a single test runs. Beside it, `wizard-work/forge/verify_check_lint_classes_dry.py` asserted the literal string `selftest: 37 cells, 37 passed, 0 failed`, so when the rubric grew to 42 cells the verifier went red on a tree where the refactor it grades was untouched -- 9 of its 11 legs passing, including both legs that actually pin the tally.
**(1) The class, measured before any edit.**
- `python3 tools/check_lint.py --check "selftest: python3 tools/check_lint.py --selftest"` → `SOUND: '...'`, **rc 0**, no faults, at `9a9061f`.
- The same line as bash sees it, via a two-line probe script run with `bash probe_label.sh` → `probe_label.sh: line 2: selftest:: command not found`, **rc 127**.
- Prevalence, so the size is stated rather than implied: a scan of all **4,952 tracked files** at `9a9061f` found **0** CHECK lines whose command opens with a label. A scan of the runner's own **3,118** iteration logs under `/root/.hermes/wizard-outputs/ralph` found **1**: `forge-2026-10-27-030720-iter2.txt:511` carries `line: python3 ...` as the graded CHECK, and that run reached GREEN only at iteration 3 on a CHECK rewritten without the label (`forge-2026-10-27-0307.log:10`). On 2026-11-02 the same shape cost one of **this lane's** own iterations (`bash: line 1: selftest:: command not found`), which is where the item came from. The class is rare in the archive and expensive when it appears: it is not a line that tests the wrong thing, it is a line that cannot run.
**(2) The rubric.** An eighth class, `LABEL_PREFIX`, ranked after `CATCH_ALL`: the command's first word ends in `:`. The rule is anchored to the first word and does not require a space after the colon (`ok:true` is the same defect), and three boundaries are pinned as `SOUND` cells rather than assumed -- a colon that is not in the first word (`python3 x.py: --selftest`), a colon inside an assignment (`PATH=/a:/b python3 x.py`) and a quoted first token (`'note: x' cmd`). The verdict prints the cut point the way `PROSE_TAIL` does: `detail {"label": "selftest", "command_after_label": "python3 tools/check_lint.py --selftest"}`. Selftest **42 → 51 cells** (`selftest: 51 cells, 51 passed, 0 failed`, rc 0); the docstring's own claim moved with it (`SEVEN CLASSES` → `EIGHT`, `42 cells` → `51 cells`), and `tools/selftest_baseline.tsv`'s row was re-taken **42 → 51**. `LABEL_PREFIX` is deliberately in neither `--ralph-dir` cross-check arm: a labelled line exits 127, so a runner cannot have called it GREEN, and a class that cannot appear in a GREEN would only dilute the two buckets that can.
**(3) The consumer, red before this item and repaired here.** At `9a9061f`, `python3 wizard-work/forge/verify_check_lint_classes_dry.py` → **rc 1**, `verify: 11 checks, 9 passed, 2 failed`, `VERDICT: MISMATCH -- behavior NOT preserved`, on `FAIL [2] selftest: refactored 37/37 rc 0` and `FAIL [3] selftest: stdout BYTE-IDENTICAL`. Both legs pinned a count, and neither is the refactor's subject -- legs [5] and [6], which pin the tally itself, passed throughout. Repaired, each leg re-measured:
- **[2]** reads the tool's own summary line and requires `0 failed` at **≥ 37** cells; the number is printed, never pinned.
- **[3]** replaces byte-identity of two selftest outputs with **(fixture, verdict) pair survival**: every pair the pinned blob (`4f76eaa^`, blob `e4bf8f7d`) graded must still grade the same way in the tree. The cell **index is dropped** on measurement, not on taste: the 2026-11-01 parenthetical fix inserted cells **mid-list**, so a prefix test reds a correct tree -- measured, the first attempt at this leg did exactly that, naming index 29 (`python3 x.py --json /tmp/o.json`, pinned) against the tree's cell 29 (`bash CHECK-quill-draft-compliance (the artifact gate).`).
- **[5]** now compares report() output **from the first `RUN ` block**, because report() prints the rubric prelude: adding one rule line moved the comparison by 63 bytes (**2707 vs 2770**) while the tally under test was byte-identical.
- After the repair: **11/11 rc 0**, `VERDICT: ALL GREEN -- report() behavior preserved`, parity leg reading `37 (fixture, verdict) pair(s) all survive (14 cell(s) added)`.
- **Sensitivity**, because a green leg is evidence only if it can fail: a clone with the em-dash marker deleted from `PROSE_MARKERS` → **rc 1**, four legs failing, [3] naming the lost pair `'CHECK: python3 x.py\n\n CHECK: line — one shell command that verifies your work and exits 0'`/`PROSE_TAIL` (`mutant_after.txt`). The same probe that green-lights a clean clone green-lights a mutant only if it is deaf.
**(4) Guards and pins.** `CHECK-q158` (new, 6 legs, 1.0 s): rubric + docstring claim + baseline pin agree on one count; the two measured shapes grade `LABEL_PREFIX` with the cut point printed and `--fail-on-fault` rc 1; three boundary shapes stay `SOUND`; a copy whose label rule cannot match reds its own selftest with **6 cells failing**; the forge verifier reads **11/11 rc 0** with a positive added-cell count; and this record is present and names the class, so deleting the record reds the guard. All six pass at the repaired tree. The artifact, the scans, the pre/post verifier transcripts and the mutant run are under `/root/.hermes/wizard-outputs/volt/q158-label-prefix/`.
**Not claimed:** that the class is frequent -- it is 1 in 3,118 runner logs and 0 in 4,952 tracked files, and the item is worth its cost because of what the line *is*, not how often it appears. That every existing CHECK line in the archive is now graded correctly: the rubric is textual and never runs the command, which is unchanged. That the volt lane gate is green: `bash wizard-work/volt/CHECK` reads **rc 1** both before and after this item, on a single pre-existing fault that this item did not touch -- `UNCLASSED wizard-work/ledger/verify_github_repo_census.py` (a committed `verify_*.py` in neither ledger), which is still unowned. That the q65 runner ledger's row for the forge verifier is current: it reads `FAIL 1` at its stated rev `c1497ef`, which is what that file returned **there**; the next sweep re-takes it, and no re-take is claimed here.
Files edited for this item: `tools/check_lint.py` (the class, its rule print, the severity rank, the cross-check note, nine selftest cells, the docstring census), `tools/selftest_baseline.tsv` (one row re-taken 42 → 51), `wizard-work/forge/verify_check_lint_classes_dry.py` (legs [1]–[3] and [5], `--repo`, the docstring's WHY paragraph), `CHECK-q158` (new), `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (one row added by hand for the new entry point), `wizard-work/volt/q137-open-set/open_set.py` (the `PIN_COUNTS` move and its comment), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered by the committed `tools/queue_index.py --write`). Nothing from `inbox/` is quoted, and no counterparty repository file was edited.
CHECK: `bash /root/troupe-standup/CHECK-q158`
+67 -12
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 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. 105 sections carry 92 ids because 13 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 -- 140 record blocks in all, 80 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. 106 sections carry 93 ids because 13 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 -- 141 record blocks in all, 81 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:` | | # | id | kind | lines | bytes | role | title | first `Status:` |
|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
@@ -252,7 +252,9 @@
| 242 | Q156 | heading | 14317-14318 | 234 | opening | closure — the Q85 landing appended to the queue without re-rendering its index or re-taking the open-set pin, so three gate legs were red at HEAD *(volt, 2026-11-02; queue-wide, no lane by … | -- | | 242 | Q156 | heading | 14317-14318 | 234 | opening | closure — the Q85 landing appended to the queue without re-rendering its index or re-taking the open-set pin, so three gate legs were red at HEAD *(volt, 2026-11-02; queue-wide, no lane by … | -- |
| 243 | Q156 | record | 14319-14355 | 9515 | cont: OPENED | **Q156 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `8e08516` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken).** Three legs of the repo … | -- | | 243 | Q156 | record | 14319-14355 | 9515 | cont: OPENED | **Q156 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `8e08516` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken).** Three legs of the repo … | -- |
| 244 | Q157 | heading | 14356-14357 | 184 | opening | closure — the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)* | -- | | 244 | Q157 | heading | 14356-14357 | 184 | opening | closure — the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)* | -- |
| 245 | Q157 | record | 14358-14394 | 10454 | cont: OPENED | **Q157 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` … | -- | | 245 | Q157 | record | 14358-14395 | 10455 | cont: OPENED | **Q157 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` … | -- |
| 246 | Q158 | heading | 14396-14397 | 421 | opening | closure — the CHECK rubric could not see a leading label, the one fault that kills a run before it starts; and its forge verifier pinned a cell count, so every fixture the rubric gained reddened a … | -- |
| 247 | Q158 | record | 14398-14420 | 7443 | cont: OPENED | **Q158 — OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `9a9061f` and the commit this record lands in).** Two defects, one shape: an instrument graded a property of its subject's … | -- |
## Ids with no `## ` heading ## Ids with no `## ` heading
@@ -2139,17 +2141,30 @@ The block below is the parse this document renders, byte for byte. `--check` com
"title": "**Q156 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `8e08516` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken).** Three legs of the repo \u2026" "title": "**Q156 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `8e08516` and the commit this record lands in; queue-wide, no lane by authorship, unclaimed and taken).** Three legs of the repo \u2026"
}, },
{ {
"bytes": 10454, "bytes": 10455,
"end_line": 14394, "end_line": 14395,
"id": "Q157", "id": "Q157",
"kind": "record", "kind": "record",
"line": 14358, "line": 14358,
"lines": 37, "lines": 38,
"role": "continuation", "role": "continuation",
"role_detail": "OPENED", "role_detail": "OPENED",
"status": null, "status": null,
"status_line": null, "status_line": null,
"title": "**Q157 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` \u2026" "title": "**Q157 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` \u2026"
},
{
"bytes": 7443,
"end_line": 14420,
"id": "Q158",
"kind": "record",
"line": 14398,
"lines": 23,
"role": "continuation",
"role_detail": "OPENED",
"status": null,
"status_line": null,
"title": "**Q158 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `9a9061f` and the commit this record lands in).** Two defects, one shape: an instrument graded a property of its subject's \u2026"
} }
], ],
"ids_multi_section": { "ids_multi_section": {
@@ -2298,7 +2313,8 @@ The block below is the parse this document renders, byte for byte. `--check` com
"Q153", "Q153",
"Q155", "Q155",
"Q156", "Q156",
"Q157" "Q157",
"Q158"
], ],
"items": [ "items": [
{ {
@@ -5474,17 +5490,43 @@ The block below is the parse this document renders, byte for byte. `--check` com
"title": "closure \u2014 the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)*" "title": "closure \u2014 the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)*"
}, },
{ {
"bytes": 10454, "bytes": 10455,
"end_line": 14394, "end_line": 14395,
"id": "Q157", "id": "Q157",
"kind": "record", "kind": "record",
"line": 14358, "line": 14358,
"lines": 37, "lines": 38,
"role": "continuation", "role": "continuation",
"role_detail": "OPENED", "role_detail": "OPENED",
"status": null, "status": null,
"status_line": null, "status_line": null,
"title": "**Q157 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` \u2026" "title": "**Q157 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `602e982` and the commit this record lands in; sol lane by authorship, unclaimed and taken).** `CHECK-2026-11-02-untracked-classify` \u2026"
},
{
"bytes": 421,
"end_line": 14397,
"id": "Q158",
"kind": "heading",
"line": 14396,
"lines": 2,
"role": "opening",
"role_detail": "",
"status": null,
"status_line": null,
"title": "closure \u2014 the CHECK rubric could not see a leading label, the one fault that kills a run before it starts; and its forge verifier pinned a cell count, so every fixture the rubric gained reddened a \u2026"
},
{
"bytes": 7443,
"end_line": 14420,
"id": "Q158",
"kind": "record",
"line": 14398,
"lines": 23,
"role": "continuation",
"role_detail": "OPENED",
"status": null,
"status_line": null,
"title": "**Q158 \u2014 OPENED and REPAIRED in the same tick (volt, 2026-11-02, at `9a9061f` and the commit this record lands in).** Two defects, one shape: an instrument graded a property of its subject's \u2026"
} }
], ],
"nested": [ "nested": [
@@ -6034,9 +6076,9 @@ The block below is the parse this document renders, byte for byte. `--check` com
"end_line": 8, "end_line": 8,
"line": 1 "line": 1
}, },
"queue_bytes": 2449068, "queue_bytes": 2456933,
"queue_lines": 14394, "queue_lines": 14420,
"queue_sha256": "8dec39ca9c5a998e24ffaf7d5672b1dfa875e6c046bb3787515eb697bd96d959", "queue_sha256": "d9559792a698243d73a4a3ec95859db6206037bd5498d41e73f978a2a9626c06",
"sections": [ "sections": [
{ {
"bytes": 3139, "bytes": 3139,
@@ -7402,6 +7444,19 @@ The block below is the parse this document renders, byte for byte. `--check` com
"status": null, "status": null,
"status_line": null, "status_line": null,
"title": "closure \u2014 the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)*" "title": "closure \u2014 the untracked-file census graded the live tree, so it was red within minutes of the tick that named it *(2026-11-02; sol lane by authorship, unclaimed and taken)*"
},
{
"bytes": 421,
"end_line": 14397,
"id": "Q158",
"kind": "heading",
"line": 14396,
"lines": 2,
"role": "opening",
"role_detail": "",
"status": null,
"status_line": null,
"title": "closure \u2014 the CHECK rubric could not see a leading label, the one fault that kills a run before it starts; and its forge verifier pinned a cell count, so every fixture the rubric gained reddened a \u2026"
} }
] ]
} }
+59 -3
View File
@@ -26,7 +26,7 @@ WHY THIS EXISTS
This tool grades the line so a fault is visible before it costs an iteration. This tool grades the line so a fault is visible before it costs an iteration.
WHAT IT MEASURES -- SEVEN CLASSES, EACH WITH ITS RULE PRINTED WHAT IT MEASURES -- EIGHT CLASSES, EACH WITH ITS RULE PRINTED
MISSING no line matches `^\\s*CHECK:`; the runner calls this RED at once. MISSING no line matches `^\\s*CHECK:`; the runner calls this RED at once.
EMPTY a CHECK line with nothing after the colon. EMPTY a CHECK line with nothing after the colon.
TAUTOLOGY the whole command is `true`, `:`, `exit 0`, `echo <literal>`, TAUTOLOGY the whole command is `true`, `:`, `exit 0`, `echo <literal>`,
@@ -35,6 +35,12 @@ WHAT IT MEASURES -- SEVEN CLASSES, EACH WITH ITS RULE PRINTED
true, `:`, `exit 0`, `echo`, `printf`, or a terminal statement true, `:`, `exit 0`, `echo`, `printf`, or a terminal statement
`; true` / `; :` / `; exit 0` / `; echo ...`. Every path returns `; true` / `; :` / `; exit 0` / `; echo ...`. Every path returns
0, so the runner's GREEN carries no information. 0, so the runner's GREEN carries no information.
LABEL_PREFIX the command opens with a label, `word:` -- the line was written
`CHECK: selftest: bash CHECK-x` or `CHECK: line: bash CHECK-x`.
bash takes the whole first word as the command name, so the line
dies with `word:: command not found` (rc 127) before anything is
tested. Added 2026-11-02 after measuring the class on a live run:
the same line shape RED'd one of this lane's own iterations.
TRAILING_CR a literal carriage return survives at end of line; it reaches TRAILING_CR a literal carriage return survives at end of line; it reaches
bash as part of the last word. bash as part of the last word.
PROSE_TAIL prose follows the command: an em dash, ` -- `, a trailing PROSE_TAIL prose follows the command: an em dash, ` -- `, a trailing
@@ -61,7 +67,7 @@ WHAT IT DOES NOT DO
USAGE USAGE
python3 check_lint.py --check "$CHECK" python3 check_lint.py --check "$CHECK"
python3 check_lint.py --ralph-dir /root/.hermes/wizard-outputs/ralph python3 check_lint.py --ralph-dir /root/.hermes/wizard-outputs/ralph
python3 check_lint.py --selftest # 42 cells, one per class and edge python3 check_lint.py --selftest # 51 cells, one per class and edge
""" """
from __future__ import annotations from __future__ import annotations
@@ -112,6 +118,22 @@ CATCH_ALL_RE = re.compile(
r"|(?:(?<!&)(?<!\|)[;&]\s*(?:true|:|exit\s+0|echo\b|printf\b)(?:\s|$))" r"|(?:(?<!&)(?<!\|)[;&]\s*(?:true|:|exit\s+0|echo\b|printf\b)(?:\s|$))"
) )
# A leading label: `selftest: python3 v.py`, `line: python3 v.py`, `Step-2: ...`.
# bash takes the WHOLE first word as the command name, so a label makes the line
# exit 127 with `word:: command not found` before a single test runs. Measured
# 2026-11-02 rather than reasoned: a ralph iteration died on
# `bash: line 1: selftest:: command not found` while this rubric read the line
# SOUND, and the runner's own 2026-10-27 forge log carries the sibling shape
# (`line: python3 ...`) one iteration before that CHECK was rewritten without it.
#
# The rule is anchored to the FIRST word and needs no space after the colon
# (`ok:true` is the same defect), and it deliberately does not match:
# `PATH=/a:/b python3 x.py` -- the `=` breaks the word class before the colon
# `python3 x.py: --selftest` -- the colon is not in the first word
# `'note: x' cmd` -- a quoted first token is not a bare word
# The last two are pinned as SOUND cells so the boundary is measured, not assumed.
LABEL_RE = re.compile(r"^(?P<label>[A-Za-z0-9][A-Za-z0-9_.+-]*):")
PROSE_MARKERS = [ PROSE_MARKERS = [
(re.compile(r"\s+[\u2014\u2013]\s+"), "em dash"), (re.compile(r"\s+[\u2014\u2013]\s+"), "em dash"),
(re.compile(r"\s--\s+"), "spaced --"), (re.compile(r"\s--\s+"), "spaced --"),
@@ -128,7 +150,12 @@ PROSE_MARKERS = [
# zero-exit constructs outrank the mechanical ones because they are the faults # zero-exit constructs outrank the mechanical ones because they are the faults
# that make a GREEN meaningless, while TRAILING_CR/PROSE_TAIL only make one # that make a GREEN meaningless, while TRAILING_CR/PROSE_TAIL only make one
# noisy (they break the run, loudly, and cost an iteration instead of lying). # noisy (they break the run, loudly, and cost an iteration instead of lying).
SEVERITY = ["EMPTY", "TAUTOLOGY", "CATCH_ALL", "TRAILING_CR", "PROSE_TAIL"] # LABEL_PREFIX sits with the zero-exit group in rank, not in kind: it cannot
# return 0 either, but it fails the other way round -- the run REDs for a reason
# that has nothing to do with what was tested, so it is the first thing a reader
# should be told when a line carries both a label and a prose tail.
SEVERITY = ["EMPTY", "TAUTOLOGY", "CATCH_ALL", "LABEL_PREFIX", "TRAILING_CR",
"PROSE_TAIL"]
def prose_tail(core: str): def prose_tail(core: str):
@@ -167,6 +194,14 @@ def classify(cmd: str | None):
faults.append("CATCH_ALL") faults.append("CATCH_ALL")
detail["catch_all_match"] = m_catch.group(0) detail["catch_all_match"] = m_catch.group(0)
m_label = LABEL_RE.match(core.lstrip())
if m_label:
faults.append("LABEL_PREFIX")
detail["label"] = m_label.group("label")
# The cut point, printed the way PROSE_TAIL prints its own: what the
# line would say with the label removed, which is what bash needs.
detail["command_after_label"] = core.lstrip()[m_label.end():].strip()
pt = prose_tail(core) pt = prose_tail(core)
if pt: if pt:
faults.append("PROSE_TAIL") faults.append("PROSE_TAIL")
@@ -302,6 +337,10 @@ def grade_run(run: dict) -> dict:
cross = "GREEN_BY_CONSTRUCTION" cross = "GREEN_BY_CONSTRUCTION"
elif final == "GREEN" and primary in ("PROSE_TAIL", "TRAILING_CR"): elif final == "GREEN" and primary in ("PROSE_TAIL", "TRAILING_CR"):
cross = "GREEN_DESPITE_FORMATTING" cross = "GREEN_DESPITE_FORMATTING"
# LABEL_PREFIX is deliberately in neither arm: a labelled line exits 127 at
# the shell, so a runner cannot have reported it GREEN. If one ever did, the
# run is reported with cross="none" and the class printed, rather than being
# folded into a bucket whose meaning it would dilute.
per_iteration = [] per_iteration = []
for n in sorted(iters): for n in sorted(iters):
@@ -393,6 +432,22 @@ FIXTURES = [
("CHECK: python3 x.py\n\nA CHECK: line is required.\n", "SOUND"), ("CHECK: python3 x.py\n\nA CHECK: line is required.\n", "SOUND"),
("CHECK: python3 x.py\n\n CHECK: line \u2014 one shell command that verifies " ("CHECK: python3 x.py\n\n CHECK: line \u2014 one shell command that verifies "
"your work and exits 0\n", "PROSE_TAIL"), "your work and exits 0\n", "PROSE_TAIL"),
# LABEL_PREFIX cells (2026-11-02, volt). The first two are the measured
# shapes: cell 1 is the line this lane's own ralph iteration died on
# (`bash: line 1: selftest:: command not found`), cell 2 is the one the
# runner's 2026-10-27 forge log carries, one iteration before that CHECK was
# rewritten without the label. The four controls pin the boundary of the
# rule: a colon that is not in the FIRST word, a colon inside an assignment,
# and a quoted first token are all SOUND.
("CHECK: selftest: python3 tools/check_lint.py --selftest\n", "LABEL_PREFIX"),
("CHECK: line: python3 tools/check_lint.py --selftest\n", "LABEL_PREFIX"),
("CHECK: Verdict: bash CHECK-x\n", "LABEL_PREFIX"), # capitalised
("CHECK: step-2: python3 x.py\n", "LABEL_PREFIX"), # hyphenated
("CHECK: 2026-11-02: python3 x.py\n", "LABEL_PREFIX"), # date label
("CHECK: ok:true\n", "LABEL_PREFIX"), # no space after
("CHECK: python3 x.py: --selftest\n", "SOUND"), # colon, word 2
("CHECK: PATH=/a:/b python3 x.py\n", "SOUND"), # assignment
("CHECK: 'note: x' cmd\n", "SOUND"), # quoted token
] ]
EXTRACT_FIXTURES = [ EXTRACT_FIXTURES = [
@@ -435,6 +490,7 @@ def print_rules():
print(" EMPTY a CHECK line with nothing after the colon") print(" EMPTY a CHECK line with nothing after the colon")
print(" TAUTOLOGY whole command is true/: /exit 0/echo/printf/sleep") print(" TAUTOLOGY whole command is true/: /exit 0/echo/printf/sleep")
print(" CATCH_ALL a || arm or terminal ; statement that exits 0 regardless") print(" CATCH_ALL a || arm or terminal ; statement that exits 0 regardless")
print(" LABEL_PREFIX a leading `word:` label -- bash exits 127 on it")
print(" TRAILING_CR a literal carriage return survives at end of line") print(" TRAILING_CR a literal carriage return survives at end of line")
print(" PROSE_TAIL prose follows the command (CANDIDATE, cut point printed)") print(" PROSE_TAIL prose follows the command (CANDIDATE, cut point printed)")
print(" SOUND none of the above -- not a promise about its meaning") print(" SOUND none of the above -- not a promise about its meaning")
+1 -1
View File
@@ -12,7 +12,7 @@
append_only_audit.py 18 cells append_only_audit.py 18 cells
append_only_owners.py 15 cells append_only_owners.py 15 cells
census_row_delta.py 37 cells census_row_delta.py 37 cells
check_lint.py 42 cells check_lint.py 51 cells
checkin_check.py 36 cells checkin_check.py 36 cells
citation_hash_class.py 19 fixtures citation_hash_class.py 19 fixtures
citation_resolve_check.py 14 fixtures citation_resolve_check.py 14 fixtures
1 # selftest_baseline.tsv -- the cell count each tool's selftest prints, pinned.
12 append_only_audit.py
13 append_only_owners.py
14 census_row_delta.py
15 check_lint.py
16 checkin_check.py
17 citation_hash_class.py
18 citation_resolve_check.py
+116
View File
@@ -0,0 +1,116 @@
"""Tarjan's strongly connected components (SCC) algorithm.
Iterative DFS to avoid recursion limits; verified against a brute-force
reachability SCC on 2000 random directed graphs.
"""
import random
import sys
def tarjan_scc(n, edges):
adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
index_counter = [0]
stack = []
on_stack = [False] * n
indices = [-1] * n
lowlink = [0] * n
result = []
for start in range(n):
if indices[start] != -1:
continue
work = [(start, 0)]
indices[start] = lowlink[start] = index_counter[0]
index_counter[0] += 1
stack.append(start)
on_stack[start] = True
while work:
u, pi = work[-1]
if pi < len(adj[u]):
work[-1] = (u, pi + 1)
v = adj[u][pi]
if indices[v] == -1:
indices[v] = lowlink[v] = index_counter[0]
index_counter[0] += 1
stack.append(v)
on_stack[v] = True
work.append((v, 0))
elif on_stack[v]:
if indices[v] < lowlink[u]:
lowlink[u] = indices[v]
else:
if lowlink[u] == indices[u]:
comp = []
while True:
w = stack.pop()
on_stack[w] = False
comp.append(w)
if w == u:
break
result.append(comp)
work.pop()
if work:
parent = work[-1][0]
if lowlink[u] < lowlink[parent]:
lowlink[parent] = lowlink[u]
return result
def brute_scc(n, edges):
adj = [[False] * n for _ in range(n)]
for u, v in edges:
adj[u][v] = True
for k in range(n):
for i in range(n):
if adj[i][k]:
row_i = adj[i]
row_k = adj[k]
for j in range(n):
if row_k[j]:
row_i[j] = True
seen = [False] * n
comps = []
for i in range(n):
if seen[i]:
continue
comp = [j for j in range(n) if not seen[j] and adj[i][j] and adj[j][i]]
if i not in comp:
comp.append(i)
for j in comp:
seen[j] = True
comps.append(comp)
return comps
def normalize(comps):
return sorted(sorted(c) for c in comps)
def self_test():
random.seed(42)
for t in range(2000):
n = random.randint(1, 12)
m = random.randint(0, n * n)
edges = [(random.randrange(n), random.randrange(n)) for _ in range(m)]
a = normalize(tarjan_scc(n, edges))
b = normalize(brute_scc(n, edges))
if a != b:
print("MISMATCH", n, edges)
print("tarjan", a)
print("brute", b)
return False
assert normalize(tarjan_scc(0, [])) == []
assert normalize(tarjan_scc(1, [])) == [[0]]
assert normalize(tarjan_scc(3, [(0, 1), (1, 2), (2, 0)])) == [[0, 1, 2]]
assert normalize(tarjan_scc(3, [(0, 1), (1, 2)])) == [[0], [1], [2]]
print("ALL TESTS PASSED (2000 random + edge cases)")
return True
if __name__ == "__main__":
ok = self_test()
sys.exit(0 if ok else 1)
@@ -12,7 +12,7 @@ tally DRY refactor in tools/check_lint.py (forge lane: Refactor).
The tally feeds ONLY summary["classes"], which is consumed by (a) the SUMMARY The tally feeds ONLY summary["classes"], which is consumed by (a) the SUMMARY
print() -- `for k,v in summary.items(): print(f" {k}: {v}")` -- whose repr of print() -- `for k,v in summary.items(): print(f" {k}: {v}")` -- whose repr of
`classes` is the output surface, and (b) the --json payload. The selftest `classes` is the output surface, and (b) the --json payload. The selftest
(37 cells) exercises classify()/extract_check() only, NOT the tally, so selftest exercises classify()/extract_check() only, NOT the tally, so fixture-level
parity is a guard but not the proof; legs [5] and [6] pin the tally itself. parity is a guard but not the proof; legs [5] and [6] pin the tally itself.
Behavior-preservation rests on: Counter reads a missing key as 0, so Behavior-preservation rests on: Counter reads a missing key as 0, so
@@ -20,10 +20,30 @@ Behavior-preservation rests on: Counter reads a missing key as 0, so
as the hand-rolled loop; and re-assigning the EXISTING "classes" key keeps its as the hand-rolled loop; and re-assigning the EXISTING "classes" key keeps its
position in `summary` (CPython 3.7+), so the SUMMARY line order is unchanged. position in `summary` (CPython 3.7+), so the SUMMARY line order is unchanged.
WHY [2]/[3] DO NOT PIN A CELL COUNT (measured 2026-11-02, volt)
Until 2026-11-02 legs [2] and [3] asserted the literal line `selftest: 37
cells, 37 passed, 0 failed` and byte-identity of the two selftest outputs.
That made this verifier a vote on check_lint.py's FIXTURE LIST, which is not
the refactor's subject: check_lint.py grew to 42 cells, and this file read
`11 checks, 9 passed, 2 failed`, rc 1, `VERDICT: MISMATCH` -- on a tree where
the refactor it grades was untouched and the tally was provably identical
(legs [5]/[6] passed throughout). A guard that reddens when its subject
gains a test is measuring the wrong thing.
What replaces it is still a real guard: every (fixture, verdict) pair the
pinned build graded must still be graded the same way somewhere in the
working tree (`--selftest --verbose`: class-or-`extract`, fixture text). The
cell INDEX is dropped from the comparison, because the 2026-11-01 parenthetical
fix inserted cells MID-LIST -- the pinned build's cell 29 is the tree's
cell 34 -- so a prefix test reds on a correct tree. Additions are tolerated
and counted; a reclassified fixture, a deleted fixture or a rewritten fixture
is not, because that is a change of behaviour on an input the pinned build
already graded.
LEGS (exit 0 iff all pass, else 1): LEGS (exit 0 iff all pass, else 1):
[1] selftest: original (pinned 4f76eaa^) 37/37 rc 0 [1] selftest: original (pinned 4f76eaa^) -- summary parses, 0 failed, 37 cells
[2] selftest: refactored (working tree) 37/37 rc 0 [2] selftest: refactored (working tree) -- summary parses, 0 failed, >= 37
[3] selftest: stdout BYTE-IDENTICAL (original == refactored) [3] fixture-level parity: the pinned build's (fixture, verdict) pairs all
survive in the tree; the count is printed, never pinned
[4] code-level: original HAS the tally line & no Counter import/expr; [4] code-level: original HAS the tally line & no Counter import/expr;
refactored HAS Counter import + Counter expr and NO tally line refactored HAS Counter import + Counter expr and NO tally line
[5] report() stdout BYTE-IDENTICAL on a synthetic fixture (quiet=False), [5] report() stdout BYTE-IDENTICAL on a synthetic fixture (quiet=False),
@@ -31,19 +51,29 @@ LEGS (exit 0 iff all pass, else 1):
[6] report() --json payload BYTE-IDENTICAL on the same fixture [6] report() --json payload BYTE-IDENTICAL on the same fixture
(generated_utc excluded: it is a wall-clock stamp, by design) (generated_utc excluded: it is a wall-clock stamp, by design)
[7] non-vacuity: the fixture tallied >1 distinct class with nonzero counts [7] non-vacuity: the fixture tallied >1 distinct class with nonzero counts
--repo DIR grades a copy of the tree (used to prove [3] can fail: a mutated
copy must red it); it defaults to /root/troupe-standup.
""" """
import io import io
import json import json
import os import os
import re
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import argparse
import importlib.util import importlib.util
from collections import Counter from collections import Counter
REPO = "/root/troupe-standup" REPO = "/root/troupe-standup"
TOOL = "tools/check_lint.py" TOOL = "tools/check_lint.py"
SUMMARY = "selftest: 37 cells, 37 passed, 0 failed" # The pinned blob's own cell count (a fact of those bytes, not of the refactor);
# the working tree is required to be a SUPERSET of it, never equal to it.
ORIG_CELLS = 37
SUMMARY_RE = re.compile(r"^selftest: (\d+) cells, (\d+) passed, (\d+) failed$",
re.MULTILINE)
CELL_RE = re.compile(r"^ cell (\d+): (.*)$")
TALLY = 'summary["classes"][g["class"]] = summary["classes"].get(g["class"], 0) + 1' TALLY = 'summary["classes"][g["class"]] = summary["classes"].get(g["class"], 0) + 1'
IMP = "from collections import Counter" IMP = "from collections import Counter"
EXPR = 'dict(Counter(g["class"] for g in graded))' EXPR = 'dict(Counter(g["class"] for g in graded))'
@@ -98,17 +128,74 @@ def _write_tmp(src):
return path return path
def run_selftest(src): def run_selftest(src, verbose=False):
"""Run <src> --selftest as a subprocess; return (rc, stdout).""" """Run <src> --selftest as a subprocess; return (rc, stdout)."""
path = _write_tmp(src) path = _write_tmp(src)
try: try:
p = subprocess.run([sys.executable, path, "--selftest"], argv = [sys.executable, path, "--selftest"]
capture_output=True, text=True, timeout=120) if verbose:
argv.append("--verbose")
p = subprocess.run(argv, capture_output=True, text=True, timeout=120)
return p.returncode, p.stdout return p.returncode, p.stdout
finally: finally:
os.unlink(path) os.unlink(path)
def summary_counts(out):
"""-> (cells, passed, failed) from the tool's own summary line, or None.
The count is READ, never pinned: pinning it made this verifier a vote on
check_lint.py's fixture list rather than on the refactor (see docstring).
"""
m = SUMMARY_RE.search(out)
if not m:
return None
return tuple(int(g) for g in m.groups())
def cell_table(out):
"""-> [(index, class-or-'extract', fixture repr)] from `--selftest --verbose`.
The two cell kinds print as ` cell N: <CLASS:11s> <repr>` and
` cell N: extract <repr>`, so the first whitespace-delimited word after
the colon is the class (or the literal `extract`) and the rest is the
fixture's repr -- which is what makes a per-index comparison meaningful.
"""
rows = []
for line in out.splitlines():
m = CELL_RE.match(line)
if not m:
continue
idx = int(m.group(1))
parts = m.group(2).split(None, 1)
kind = parts[0] if parts else ""
body = parts[1] if len(parts) > 1 else ""
rows.append((idx, kind, body))
return rows
def pair_delta(orig_rows, ref_rows):
"""-> (missing_pairs, extra_pairs) between the pinned and working cell tables.
A row is (index, kind, body) where `kind` is the class the fixture graded to
(or the literal `extract`) and `body` is the fixture's own repr. The index is
DROPPED before comparing: on 2026-11-01 the parenthetical-prose fix inserted
cells MID-LIST (the pinned build's cell 29 `python3 x.py --json /tmp/o.json`
is the tree's cell 34), so position is not an invariant of this fixture list
and a prefix test reds on a correct tree.
What must hold is narrower and stronger: every (fixture, verdict) pair the
pinned build graded is still graded the same way somewhere in the tree.
Additions are allowed and reported; a reclassification, a lost fixture or a
rewritten fixture shows up in `missing_pairs`.
"""
want = Counter((body, kind) for _idx, kind, body in orig_rows)
have = Counter((body, kind) for _idx, kind, body in ref_rows)
missing = sorted((want - have).elements())
extra = sorted((have - want).elements())
return missing, extra
def load_module(src, name): def load_module(src, name):
"""Exec <src> as module <name> (__name__ != "__main__" so no run() side effects).""" """Exec <src> as module <name> (__name__ != "__main__" so no run() side effects)."""
path = _write_tmp(src) path = _write_tmp(src)
@@ -161,7 +248,16 @@ def build_fixture():
def report_stdout(mod, runs): def report_stdout(mod, runs):
"""All of report()'s stdout (quiet=False) -- faults print to stdout too.""" """report()'s stdout (quiet=False) from the first RUN block onward.
The rules PRELUDE is deliberately excluded. report() prints the rubric
(`print_rules()`) before the table, so a full-stdout comparison made this
leg a vote on the rubric's TEXT: adding the LABEL_PREFIX class on 2026-11-02
grew the prelude by one line and reddened [5] while the tally under test was
byte-identical (measured: len 2707 vs 2770 = the new rule line). The leg's
subject is the graded rows and the `classes: {...}` repr; a new class name in
the help text is a description of the rubric, not a change of behaviour.
"""
buf = io.StringIO() buf = io.StringIO()
old = sys.stdout old = sys.stdout
sys.stdout = buf sys.stdout = buf
@@ -169,7 +265,9 @@ def report_stdout(mod, runs):
rc = mod.report(list(runs), as_json=None, quiet=False, unlogged=2) rc = mod.report(list(runs), as_json=None, quiet=False, unlogged=2)
finally: finally:
sys.stdout = old sys.stdout = old
return rc, buf.getvalue() text = buf.getvalue()
cut = text.find("\nRUN ")
return rc, (text[cut + 1:] if cut >= 0 else text)
def report_json(mod, runs): def report_json(mod, runs):
@@ -200,7 +298,19 @@ def has_stmt(text, needle):
return False return False
def main(): def main(argv=None):
global REPO
ap = argparse.ArgumentParser(
description="Equivalence proof for the report() tally refactor in "
"tools/check_lint.py. Read-only.")
ap.add_argument("--repo", default=REPO,
help="tree to grade (default: the host checkout); a copy "
"with a mutated check_lint.py must red leg [3]")
a = ap.parse_args(argv)
REPO = os.path.abspath(a.repo)
if not os.path.isfile(os.path.join(REPO, TOOL)):
print("VERDICT: MISMATCH -- no such tool: %s" % os.path.join(REPO, TOOL))
return 1
cells, failed = [], [] cells, failed = [], []
def check(label, ok, detail=""): def check(label, ok, detail=""):
@@ -217,15 +327,29 @@ def main():
# ---- [1-3] selftest parity ------------------------------------------------ # ---- [1-3] selftest parity ------------------------------------------------
rc_o, out_o = run_selftest(orig) rc_o, out_o = run_selftest(orig)
rc_r, out_r = run_selftest(ref) rc_r, out_r = run_selftest(ref)
check("[1] selftest: original (pinned 4f76eaa^) 37/37 rc 0", cnt_o, cnt_r = summary_counts(out_o), summary_counts(out_r)
rc_o == 0 and SUMMARY in out_o, check("[1] selftest: original (pinned 4f76eaa^) -- 0 failed, %d cells"
"rc=%d" % rc_o) % ORIG_CELLS,
check("[2] selftest: refactored 37/37 rc 0", rc_o == 0 and cnt_o is not None and cnt_o[0] == ORIG_CELLS
rc_r == 0 and SUMMARY in out_r, and cnt_o[2] == 0,
"rc=%d" % rc_r) "rc=%d summary=%r" % (rc_o, cnt_o))
check("[3] selftest: stdout BYTE-IDENTICAL", check("[2] selftest: refactored (working tree) -- 0 failed, >= %d cells"
out_o == out_r, % ORIG_CELLS,
"len_o=%d len_r=%d" % (len(out_o), len(out_r))) rc_r == 0 and cnt_r is not None and cnt_r[1] == cnt_r[0]
and cnt_r[2] == 0 and cnt_r[0] >= ORIG_CELLS,
"rc=%d summary=%r" % (rc_r, cnt_r))
_, vo = run_selftest(orig, verbose=True)
_, vr = run_selftest(ref, verbose=True)
rows_o, rows_r = cell_table(vo), cell_table(vr)
missing, extra = pair_delta(rows_o, rows_r)
check("[3] fixture-level parity: the pinned build's %d (fixture, verdict) "
"pair(s) all survive (%d cell(s) added)"
% (len(rows_o), len(extra)),
not missing and len(rows_o) >= ORIG_CELLS and len(rows_r) >= len(rows_o),
"lost/reclassified: %r" % (missing[:3],))
# [5] compares from the first RUN block: the rubric prelude is excluded, see
# report_stdout().
# ---- [4] code-level: smell existed, was replaced (non-faculty) ----------- # ---- [4] code-level: smell existed, was replaced (non-faculty) -----------
check("[4a] code-level: original HAS tally line", has_stmt(orig, TALLY)) check("[4a] code-level: original HAS tally line", has_stmt(orig, TALLY))
+8 -2
View File
@@ -556,8 +556,14 @@ PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109,
# record_only stays 55 and open / opened stay 0 / 26 (same-tick closure, no # record_only stays 55 and open / opened stay 0 / 26 (same-tick closure, no
# probe; CHECK-q157 is its guard). Measured by --check after the append, which # probe; CHECK-q157 is its guard). Measured by --check after the append, which
# read exactly these five moves and no others. # read exactly these five moves and no others.
PIN_COUNTS = {"headings": 105, "heading_ids": 92, "records": 119, # Q158 (2026-11-02): one heading (## Q158 closure) and one record (**Q158 --
"record_ids": 85, "ids": 147, "open": 0, "opened": 26, # OPENED and REPAIRED ...) for a new id, so headings 105 -> 106, heading_ids
# 92 -> 93, records 119 -> 120, record_ids 85 -> 86, ids 147 -> 148;
# record_only stays 55 and open / opened stay 0 / 26 (same-tick closure, no
# probe; CHECK-q158 is its guard). Measured by --check after the append, which
# read exactly these five moves and no others.
PIN_COUNTS = {"headings": 106, "heading_ids": 93, "records": 120,
"record_ids": 86, "ids": 148, "open": 0, "opened": 26,
"record_only": 55} "record_only": 55}
PIN_STATE = { PIN_STATE = {
@@ -163,3 +163,8 @@ CHECK-q155 0 q155: 3 leg(s), all passed
# --check` at the Q157 worktree; every other row above is unchanged. # --check` at the Q157 worktree; every other row above is unchanged.
CHECK-2026-11-02-untracked-classify 0 CHECK OK: 306 pinned paths; manifest==snapshot in order CHECK-2026-11-02-untracked-classify 0 CHECK OK: 306 pinned paths; manifest==snapshot in order
CHECK-q157 0 q157: 6 leg(s), all passed CHECK-q157 0 q157: 6 leg(s), all passed
# rows added by hand (Q158, 2026-11-02): CHECK-q158 is a NEW entry point and the
# guard for the LABEL_PREFIX class and for the forge verifier's count pin. Six legs,
# rc 0 measured at this worktree with `bash CHECK-q158` (3.2 s). Every row above
# is unchanged and none was re-taken by this item.
CHECK-q158 0 q158: 6 leg(s), all passed
Can't render this file because it contains an unexpected character in line 93 and column 87.