beam: Tarjan SCC iterative, brute-force verified
This commit is contained in:
1 parent
9a9061f71c
commit
7659e22493
9 files changed
+574
-38
No files matched your search
+148
@@ -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
|
||||
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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))
|
||||
|
||||
@@ -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.
|
Reference in new issue
Block a user