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.
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 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:` |
|---|---|---|---|---|---|---|---|
@@ -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 … | -- |
| 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)* | -- |
| 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
@@ -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"
},
{
"bytes": 10454,
"end_line": 14394,
"bytes": 10455,
"end_line": 14395,
"id": "Q157",
"kind": "record",
"line": 14358,
"lines": 37,
"lines": 38,
"role": "continuation",
"role_detail": "OPENED",
"status": 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"
},
{
"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": {
@@ -2298,7 +2313,8 @@ The block below is the parse this document renders, byte for byte. `--check` com
"Q153",
"Q155",
"Q156",
"Q157"
"Q157",
"Q158"
],
"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)*"
},
{
"bytes": 10454,
"end_line": 14394,
"bytes": 10455,
"end_line": 14395,
"id": "Q157",
"kind": "record",
"line": 14358,
"lines": 37,
"lines": 38,
"role": "continuation",
"role_detail": "OPENED",
"status": 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"
},
{
"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": [
@@ -6034,9 +6076,9 @@ The block below is the parse this document renders, byte for byte. `--check` com
"end_line": 8,
"line": 1
},
"queue_bytes": 2449068,
"queue_lines": 14394,
"queue_sha256": "8dec39ca9c5a998e24ffaf7d5672b1dfa875e6c046bb3787515eb697bd96d959",
"queue_bytes": 2456933,
"queue_lines": 14420,
"queue_sha256": "d9559792a698243d73a4a3ec95859db6206037bd5498d41e73f978a2a9626c06",
"sections": [
{
"bytes": 3139,
@@ -7402,6 +7444,19 @@ The block below is the parse this document renders, byte for byte. `--check` com
"status": 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)*"
},
{
"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.
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.
EMPTY a CHECK line with nothing after the colon.
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 ...`. Every path returns
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
bash as part of the last word.
PROSE_TAIL prose follows the command: an em dash, ` -- `, a trailing
@@ -61,7 +67,7 @@ WHAT IT DOES NOT DO
USAGE
python3 check_lint.py --check "$CHECK"
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
@@ -112,6 +118,22 @@ CATCH_ALL_RE = re.compile(
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 = [
(re.compile(r"\s+[\u2014\u2013]\s+"), "em dash"),
(re.compile(r"\s--\s+"), "spaced --"),
@@ -128,7 +150,12 @@ PROSE_MARKERS = [
# 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
# 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):
@@ -167,6 +194,14 @@ def classify(cmd: str | None):
faults.append("CATCH_ALL")
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)
if pt:
faults.append("PROSE_TAIL")
@@ -302,6 +337,10 @@ def grade_run(run: dict) -> dict:
cross = "GREEN_BY_CONSTRUCTION"
elif final == "GREEN" and primary in ("PROSE_TAIL", "TRAILING_CR"):
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 = []
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\n CHECK: line \u2014 one shell command that verifies "
"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 = [
@@ -435,6 +490,7 @@ def print_rules():
print(" EMPTY a CHECK line with nothing after the colon")
print(" TAUTOLOGY whole command is true/: /exit 0/echo/printf/sleep")
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(" PROSE_TAIL prose follows the command (CANDIDATE, cut point printed)")
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_owners.py 15 cells
census_row_delta.py 37 cells
check_lint.py 42 cells
check_lint.py 51 cells
checkin_check.py 36 cells
citation_hash_class.py 19 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
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
(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.
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
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):
[1] selftest: original (pinned 4f76eaa^) 37/37 rc 0
[2] selftest: refactored (working tree) 37/37 rc 0
[3] selftest: stdout BYTE-IDENTICAL (original == refactored)
[1] selftest: original (pinned 4f76eaa^) -- summary parses, 0 failed, 37 cells
[2] selftest: refactored (working tree) -- summary parses, 0 failed, >= 37
[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;
refactored HAS Counter import + Counter expr and NO tally line
[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
(generated_utc excluded: it is a wall-clock stamp, by design)
[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 json
import os
import re
import subprocess
import sys
import tempfile
import argparse
import importlib.util
from collections import Counter
REPO = "/root/troupe-standup"
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'
IMP = "from collections import Counter"
EXPR = 'dict(Counter(g["class"] for g in graded))'
@@ -98,17 +128,74 @@ def _write_tmp(src):
return path
def run_selftest(src):
def run_selftest(src, verbose=False):
"""Run <src> --selftest as a subprocess; return (rc, stdout)."""
path = _write_tmp(src)
try:
p = subprocess.run([sys.executable, path, "--selftest"],
capture_output=True, text=True, timeout=120)
argv = [sys.executable, path, "--selftest"]
if verbose:
argv.append("--verbose")
p = subprocess.run(argv, capture_output=True, text=True, timeout=120)
return p.returncode, p.stdout
finally:
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):
"""Exec <src> as module <name> (__name__ != "__main__" so no run() side effects)."""
path = _write_tmp(src)
@@ -161,7 +248,16 @@ def build_fixture():
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()
old = sys.stdout
sys.stdout = buf
@@ -169,7 +265,9 @@ def report_stdout(mod, runs):
rc = mod.report(list(runs), as_json=None, quiet=False, unlogged=2)
finally:
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):
@@ -200,7 +298,19 @@ def has_stmt(text, needle):
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 = [], []
def check(label, ok, detail=""):
@@ -217,15 +327,29 @@ def main():
# ---- [1-3] selftest parity ------------------------------------------------
rc_o, out_o = run_selftest(orig)
rc_r, out_r = run_selftest(ref)
check("[1] selftest: original (pinned 4f76eaa^) 37/37 rc 0",
rc_o == 0 and SUMMARY in out_o,
"rc=%d" % rc_o)
check("[2] selftest: refactored 37/37 rc 0",
rc_r == 0 and SUMMARY in out_r,
"rc=%d" % rc_r)
check("[3] selftest: stdout BYTE-IDENTICAL",
out_o == out_r,
"len_o=%d len_r=%d" % (len(out_o), len(out_r)))
cnt_o, cnt_r = summary_counts(out_o), summary_counts(out_r)
check("[1] selftest: original (pinned 4f76eaa^) -- 0 failed, %d cells"
% ORIG_CELLS,
rc_o == 0 and cnt_o is not None and cnt_o[0] == ORIG_CELLS
and cnt_o[2] == 0,
"rc=%d summary=%r" % (rc_o, cnt_o))
check("[2] selftest: refactored (working tree) -- 0 failed, >= %d cells"
% ORIG_CELLS,
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) -----------
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
# probe; CHECK-q157 is its guard). Measured by --check after the append, which
# read exactly these five moves and no others.
PIN_COUNTS = {"headings": 105, "heading_ids": 92, "records": 119,
"record_ids": 85, "ids": 147, "open": 0, "opened": 26,
# Q158 (2026-11-02): one heading (## Q158 closure) and one record (**Q158 --
# 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}
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-2026-11-02-untracked-classify 0 CHECK OK: 306 pinned paths; manifest==snapshot in order
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.