diff --git a/docs/audits/quorum-PMAT-3644.json b/docs/audits/quorum-PMAT-3644.json new file mode 100644 index 0000000000..6469a6c9ec --- /dev/null +++ b/docs/audits/quorum-PMAT-3644.json @@ -0,0 +1,293 @@ +{ + "ticket": "PMAT-3644", + "base": "origin/main", + "base_resolved": "origin/main", + "base_note": "no origin/origin/main exists; judged against the local ref", + "head": "d18529e56a5d2199e69c21a3c7af04155ec22efa", + "diff_sha256": "97335d80da3ee08206561778de09e5acedc0b313570eb35d49009f69eba5a2b6", + "width": 3, + "executor": "agy", + "prompt_mode": "inline", + "prompt_bytes": 32102, + "author": { + "model": "claude-opus-5", + "family": "claude", + "source": "flag" + }, + "agreed": true, + "lanes": [ + { + "lane": 1, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "All acceptance criteria have been verified via static diff analysis. The diff correctly drops `name:` lines before checking for dispatches, adds the 3 case rows testing the step name blindness, adjusts `check_pathonly_devdeps_unused_in_src.sh` to gracefully fallback or exit with ENV (rc=2) and implements the SCAN-DONE trailer, rewrites the build tool comments to remove it from `cargo-using`, expands `unwired_guards_baseline.txt` with the other 3 now-visible dark guards, and updates `lib_baseline_ratchet.sh` to handle NAME-entry admissions via `set-aperture` with exactly 6 case rows matching criteria. No workflow files were edited.", + "findings": [ + { + "claim": "check_guards_are_wired.sh drops name: lines before the invocation match, in dispatcher_wired() and the per-guard scan", + "file": "scripts/check_guards_are_wired.sh", + "grounding": "cited" + }, + { + "claim": "rows 8-10 check the exact ci.yml step-name fixture, a quoted run: line, and a step named after a guard wires nothing", + "file": "scripts/check_guards_are_wired.sh", + "grounding": "cited" + }, + { + "claim": "the mutation (remove the drop) turns rows 8 and 10 RED", + "file": "scripts/check_guards_are_wired.sh", + "grounding": "asserted" + }, + { + "claim": "check_pathonly_devdeps_unused_in_src.sh falls back to tomli, exits with ENV rc=2, and requires a SCAN-DONE trailer", + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "grounding": "cited" + }, + { + "claim": "five death rows incl. the whole guard under the intel shape", + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "grounding": "cited" + }, + { + "claim": "comments reworded so CARGO_RE no longer classifies it out of --no-cargo", + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "grounding": "cited" + }, + { + "claim": "unwired_guards_baseline.txt records the other three dark guards as APERTURE REVEALS", + "file": "scripts/unwired_guards_baseline.txt", + "grounding": "cited" + }, + { + "claim": "set-aperture gains a NAME-entry admission with six case rows for acceptance vs refusal", + "file": "scripts/lib_baseline_ratchet.sh", + "grounding": "cited" + }, + { + "claim": "No workflow file is edited", + "file": "docs/roadmaps/entries/PMAT-3644.yaml", + "grounding": "asserted" + } + ], + "raw_bytes": 7984, + "err_bytes": 100, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.1-pro-high", + "model_measured": "gemini-3.1-pro-high", + "model_source": "measured", + "family": "gemini" + }, + { + "lane": 2, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "The diff perfectly matches the requirements in PMAT-3644. All expected behaviors, tests, and file modifications requested in the acceptance criteria have been found exactly as specified.", + "findings": [ + { + "claim": "The name: dropping logic is wired correctly before invocation match, and tests 8-10 verify it.", + "file": "scripts/check_guards_are_wired.sh", + "grounding": "cited" + }, + { + "claim": "The check_pathonly_devdeps_unused_in_src.sh now properly handles python 3.10 environments missing tomllib by falling back to tomli, and exits with ENV rc=2 if neither is available. It also enforces SCAN-DONE trailer.", + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "grounding": "cited" + }, + { + "claim": "unwired_guards_baseline.txt includes the 3 dark guards added as set-aperture reveals, correctly explaining why check_pathonly_devdeps_unused_in_src.sh is omitted.", + "file": "scripts/unwired_guards_baseline.txt", + "grounding": "cited" + }, + { + "claim": "The check_baseline_ratchets.sh has exactly 6 case rows for NAME entries, matching the acceptance criteria perfectly.", + "file": "scripts/check_baseline_ratchets.sh", + "grounding": "cited" + } + ], + "raw_bytes": 4383, + "err_bytes": 0, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.1-pro-low", + "model_measured": "gemini-3.1-pro-low", + "model_source": "measured", + "family": "gemini" + }, + { + "lane": 3, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "Diff reviewed and verified against PMAT-3644. All requirements have been met without refutation:\n1. `check_guards_are_wired.sh` drops `name:` lines before matching dispatches, preventing step names from miscounting as guard invocations. Self-tests 8-10 verify this, and mutating out `_not_a_name_line` turns rows 8 and 10 RED.\n2. `check_pathonly_devdeps_unused_in_src.sh` supports `tomllib` -> `tomli` -> ENV `rc=2`, requires `SCAN-DONE` trailer, and passes 5 death rows. Reworded comments remove `cargo ` token matches so `guard_tree.sh --no-cargo` now runs it.\n3. `unwired_guards_baseline.txt` records the 3 dark guards revealed as `set-aperture` admissions owned by `check_guards_are_wired.sh`, with `lib_baseline_ratchet.sh` supporting NAME-entry admissions and 6 test case rows in `check_baseline_ratchets.sh`. No workflow files were edited.", + "findings": [ + { + "claim": "check_guards_are_wired.sh filters out name: lines prior to matching invocations in both dispatcher_wired() and unwired_in(), correctly preventing step names like \"guard_tree.sh's own case table\" from blinding the meta-guard. Passes self-test rows 8-10, and removing the drop turns rows 8 and 10 RED.", + "command": "bash /tmp/ws_test/scripts/check_guards_are_wired.sh --self-test", + "file": "scripts/check_guards_are_wired.sh", + "grounding": "measured", + "line": 80, + "output": "ok row 1 the unwired guard is reported, the wired one is not\nok row 2 wiring it clears the report\nok row 3 a guard dispatched by guard_tree.sh --no-cargo counts as wired\nok row 4 removing the dispatcher line brings the guard back as unwired\nok row 5 a guard wired only in guards-nightly.yml counts as wired\nok row 6 a guard wired in no workflow at all is still reported\nok row 7 deleting the nightly workflow brings its guard back as unwired\nok row 8 a step NAMED guard_tree.sh's ... is not a dispatch; the guard stays unwired\nok row 9 a QUOTED run: line still dispatches (the control for row 8)\nok row 10 a step named after a guard wires nothing\n\nSELF-TEST PASSED (10/10)" + }, + { + "claim": "check_pathonly_devdeps_unused_in_src.sh now tries tomllib then tomli and returns ENV rc=2 when neither is present; requires a SCAN-DONE trailer to ensure the scanner completed; passes all 5 death rows; and rewording comments eliminates CARGO_RE matches so it is included in --no-cargo execution.", + "command": "bash scripts/check_pathonly_devdeps_unused_in_src.sh --selftest", + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "grounding": "measured", + "line": 55, + "output": "ok death rc=2 both TOML readers absent (the intel shape) -> ENV rc=2\nok death rc=2 interpreter exits 1 with no output -> ENV rc=2, never 1\nok death rc=2 no interpreter at all (rc=127) -> ENV rc=2\nok death rc=0 the working interpreter, as the control -> rc=0\nok death rc=2 the whole guard under the intel shape exits ENV with no row verdict\nPASS: case table (4 must-match, 7 must-not-match, 5 death rows)" + }, + { + "claim": "check_baseline_ratchets.sh classifies unwired_guards_baseline.txt as set-aperture owned by scripts/check_guards_are_wired.sh, and lib_baseline_ratchet.sh implements NAME-entry set-aperture rules with 6 case rows in self-test.", + "command": "bash scripts/check_baseline_ratchets.sh --self-test", + "file": "scripts/check_baseline_ratchets.sh", + "grounding": "measured", + "line": 96, + "output": "PASS case table only: 61 rows (set, count, keyed, keyed2, comparand resolver,\n end-to-end, classification totality). NO baseline in this tree was\n compared — run with no arguments for that." + }, + { + "claim": "unwired_guards_baseline.txt records the 3 dark guards revealed by fixing check_guards_are_wired.sh (check_model_ladder.sh, check_pr_review_counts.sh, check_pr_review_receipt.sh) with detailed notes, while check_pathonly_devdeps_unused_in_src.sh is correctly omitted as it is now wired via guard_tree.sh --no-cargo.", + "file": "scripts/unwired_guards_baseline.txt", + "grounding": "cited", + "line": 5 + } + ], + "raw_bytes": 10261, + "err_bytes": 0, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.6-flash-high", + "model_measured": "gemini-3.6-flash-high", + "model_source": "measured", + "family": "gemini" + } + ], + "dissent": [], + "dedup": [ + { + "file": "docs/roadmaps/entries/PMAT-3644.yaml", + "line": null, + "lanes_agreeing": [ + 1 + ], + "claims": [ + "No workflow file is edited" + ] + }, + { + "file": "scripts/check_baseline_ratchets.sh", + "line": null, + "lanes_agreeing": [ + 2 + ], + "claims": [ + "The check_baseline_ratchets.sh has exactly 6 case rows for NAME entries, matching the acceptance criteria perfectly." + ] + }, + { + "file": "scripts/check_baseline_ratchets.sh", + "line": 96, + "lanes_agreeing": [ + 3 + ], + "claims": [ + "check_baseline_ratchets.sh classifies unwired_guards_baseline.txt as set-aperture owned by scripts/check_guards_are_wired.sh, and lib_baseline_ratchet.sh implements NAME-entry set-aperture rules with 6 case rows in self-test." + ] + }, + { + "file": "scripts/check_guards_are_wired.sh", + "line": null, + "lanes_agreeing": [ + 1, + 2 + ], + "claims": [ + "The name: dropping logic is wired correctly before invocation match, and tests 8-10 verify it.", + "check_guards_are_wired.sh drops name: lines before the invocation match, in dispatcher_wired() and the per-guard scan", + "rows 8-10 check the exact ci.yml step-name fixture, a quoted run: line, and a step named after a guard wires nothing", + "the mutation (remove the drop) turns rows 8 and 10 RED" + ] + }, + { + "file": "scripts/check_guards_are_wired.sh", + "line": 80, + "lanes_agreeing": [ + 3 + ], + "claims": [ + "check_guards_are_wired.sh filters out name: lines prior to matching invocations in both dispatcher_wired() and unwired_in(), correctly preventing step names like \"guard_tree.sh's own case table\" from blinding the meta-guard. Passes self-test rows 8-10, and removing the drop turns rows 8 and 10 RED." + ] + }, + { + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "line": null, + "lanes_agreeing": [ + 1, + 2 + ], + "claims": [ + "The check_pathonly_devdeps_unused_in_src.sh now properly handles python 3.10 environments missing tomllib by falling back to tomli, and exits with ENV rc=2 if neither is available. It also enforces SCAN-DONE trailer.", + "check_pathonly_devdeps_unused_in_src.sh falls back to tomli, exits with ENV rc=2, and requires a SCAN-DONE trailer", + "comments reworded so CARGO_RE no longer classifies it out of --no-cargo", + "five death rows incl. the whole guard under the intel shape" + ] + }, + { + "file": "scripts/check_pathonly_devdeps_unused_in_src.sh", + "line": 55, + "lanes_agreeing": [ + 3 + ], + "claims": [ + "check_pathonly_devdeps_unused_in_src.sh now tries tomllib then tomli and returns ENV rc=2 when neither is present; requires a SCAN-DONE trailer to ensure the scanner completed; passes all 5 death rows; and rewording comments eliminates CARGO_RE matches so it is included in --no-cargo execution." + ] + }, + { + "file": "scripts/lib_baseline_ratchet.sh", + "line": null, + "lanes_agreeing": [ + 1 + ], + "claims": [ + "set-aperture gains a NAME-entry admission with six case rows for acceptance vs refusal" + ] + }, + { + "file": "scripts/unwired_guards_baseline.txt", + "line": null, + "lanes_agreeing": [ + 1, + 2 + ], + "claims": [ + "unwired_guards_baseline.txt includes the 3 dark guards added as set-aperture reveals, correctly explaining why check_pathonly_devdeps_unused_in_src.sh is omitted.", + "unwired_guards_baseline.txt records the other three dark guards as APERTURE REVEALS" + ] + }, + { + "file": "scripts/unwired_guards_baseline.txt", + "line": 5, + "lanes_agreeing": [ + 3 + ], + "claims": [ + "unwired_guards_baseline.txt records the 3 dark guards revealed by fixing check_guards_are_wired.sh (check_model_ladder.sh, check_pr_review_counts.sh, check_pr_review_receipt.sh) with detailed notes, while check_pathonly_devdeps_unused_in_src.sh is correctly omitted as it is now wired via guard_tree.sh --no-cargo." + ] + } + ], + "uncovered": [], + "coverage_source": "lanes", + "partial": false, + "partial_reasons": [], + "auto_merge": { + "checked": true, + "was_armed": false, + "disarmed": false, + "note": "auto-merge not armed" + }, + "lint": { + "ok": true, + "output": "receipt complete: kind=artifact lanes=3 author=claude-opus-5/claude" + } +} diff --git a/docs/roadmaps/entries/PMAT-3644.yaml b/docs/roadmaps/entries/PMAT-3644.yaml new file mode 100644 index 0000000000..f368071f82 --- /dev/null +++ b/docs/roadmaps/entries/PMAT-3644.yaml @@ -0,0 +1,17 @@ +- id: PMAT-3644 + github_issue: 3644 + item_type: task + title: 'check_guards_are_wired.sh reads a step NAME as a bare guard_tree.sh dispatch: 4 guards dark, incl. the #3305 publish-strip guard (which also dies on intel''s python 3.10)' + status: planned + priority: critical + assigned_to: null + created: 2026-09-21T01:39:50Z + updated: 2026-09-21T01:39:50Z + spec: null + acceptance_criteria: [] + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: 'ACCEPTANCE (hand-entered from issue #3644 "What lands"; minted on the PR branch so the AD-04 quorum can run). 1. check_guards_are_wired.sh drops name: lines before the invocation match, in dispatcher_wired() and the per-guard scan; rows 8-10 (the exact ci.yml step-name fixture must NOT count as a dispatch; a quoted run: line still does; a step named after a guard wires nothing); the mutation (remove the drop) turns rows 8 and 10 RED. 2. check_pathonly_devdeps_unused_in_src.sh: tomllib -> tomli -> ENV rc=2 naming interpreter and runner; the scanner ends with a SCAN-DONE trailer the shell REQUIRES; five death rows incl. the whole guard under the intel shape; comments reworded so CARGO_RE no longer classifies it out of --no-cargo, so guard-tree runs it (bare run on the tree: rc 0, 3 s). 3. unwired_guards_baseline.txt records the other three dark guards as APERTURE REVEALS (set-aperture gains a NAME-entry admission: a file the comparand carries, in a diff that changes the owning guard; six case rows, each admission beside its refusal) with the reason each may only leave; #3646 owns the pr_review pair. No workflow file is edited.' diff --git a/docs/roadmaps/roadmap.yaml b/docs/roadmaps/roadmap.yaml index 12cdaa22ce..7343cdaa36 100644 --- a/docs/roadmaps/roadmap.yaml +++ b/docs/roadmaps/roadmap.yaml @@ -18886,3 +18886,20 @@ roadmap: labels: - kind:code notes: 'ACCEPTANCE (hand-entered from issue #3627 done_when; minted on the PR branch so the AD-04 quorum can run -- pmat work status refuses a missing item). Done when: contracts/rmsnorm-kernel-v1.yaml Scale invariance obligation carries either the ε>0 formal or the α²·mean(x²) ≫ ε precondition; pv validate passes ~~regenerated with pv probar --binding~~ **WITHDRAWN 21:40Z** — b3 measured that pv probar --binding emits 7 #[ignore = "no binding available"] stubs for this contract; the tracked file''s 5 real tests were produced by the pre-monorepo external generator (20dda945b, 2026-02-18) and the header is false (class row: #3630). Instead: the tracked test is edited **directly**, the commit says so, and the file''s header is replaced by a true one (hand-maintained, contract-bound) the minimal input above ([0.0, -0.15964308], α=0.012254778) is a committed regression case that PASSES under the new assertion (i.e. the fixed test is exercised on the exact input that killed #3621), and a mutation that restores the ε=0 assertion turns it RED cargo' +- id: PMAT-3644 + github_issue: 3644 + item_type: task + title: 'check_guards_are_wired.sh reads a step NAME as a bare guard_tree.sh dispatch: 4 guards dark, incl. the #3305 publish-strip guard (which also dies on intel''s python 3.10)' + status: planned + priority: critical + assigned_to: null + created: 2026-09-21T01:39:50Z + updated: 2026-09-21T01:39:50Z + spec: null + acceptance_criteria: [] + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: 'ACCEPTANCE (hand-entered from issue #3644 "What lands"; minted on the PR branch so the AD-04 quorum can run). 1. check_guards_are_wired.sh drops name: lines before the invocation match, in dispatcher_wired() and the per-guard scan; rows 8-10 (the exact ci.yml step-name fixture must NOT count as a dispatch; a quoted run: line still does; a step named after a guard wires nothing); the mutation (remove the drop) turns rows 8 and 10 RED. 2. check_pathonly_devdeps_unused_in_src.sh: tomllib -> tomli -> ENV rc=2 naming interpreter and runner; the scanner ends with a SCAN-DONE trailer the shell REQUIRES; five death rows incl. the whole guard under the intel shape; comments reworded so CARGO_RE no longer classifies it out of --no-cargo, so guard-tree runs it (bare run on the tree: rc 0, 3 s). 3. unwired_guards_baseline.txt records the other three dark guards as APERTURE REVEALS (set-aperture gains a NAME-entry admission: a file the comparand carries, in a diff that changes the owning guard; six case rows, each admission beside its refusal) with the reason each may only leave; #3646 owns the pr_review pair. No workflow file is edited.' diff --git a/scripts/check_baseline_ratchets.sh b/scripts/check_baseline_ratchets.sh index ddb9162967..40c27e21f2 100644 --- a/scripts/check_baseline_ratchets.sh +++ b/scripts/check_baseline_ratchets.sh @@ -93,7 +93,7 @@ classify() { # classify -> "[reason]", rc 1 if unclassifie cb200_baseline.txt) printf 'count\n' ;; # mirrors .pmat-gates.toml [tdg] baseline (PMAT-937) test_fixture_path_baseline.txt) printf 'count\n' ;; tracked_ignored_baseline.txt) printf 'count\n' ;; - unwired_guards_baseline.txt) printf 'set\n' ;; + unwired_guards_baseline.txt) printf 'set-aperture\tscripts/check_guards_are_wired.sh\n' ;; # NAME entries: a guard file that predates the comparand may be ledgered when the meta-guard itself widens (#3644) # NOT a ratchet either, and for the same reason one level along: this # registry is DERIVED from the test sources on every run # (scripts/check_tree_reader_tests.sh) and must equal that derivation @@ -323,6 +323,12 @@ if [ "${1:-}" = "--self-test" ] || [ "${1:-}" = "--selftest" ]; then # PREDATES; `fresh.md` is written by the working tree only. printf 'the old claim, 2.93 times faster\n' > "$SR/pre.md" printf 'GUARD v1\n' > "$SR/guard.sh" + # for the NAME-entry rows (#3644): an owner in a subdirectory with a + # sibling file, the shape of scripts/check_guards_are_wired.sh and + # its ledger of basenames + mkdir -p "$SR/sub" + printf 'OWNER v1\n' > "$SR/sub/owner.sh" + printf 'a sibling that predates\n' > "$SR/sub/sib.sh" git -C "$SR" add -A >/dev/null 2>&1 git -C "$SR" -c commit.gpgsign=false commit -qm 'aperture base' >/dev/null 2>&1 SR_APER=$(git -C "$SR" rev-parse HEAD) @@ -386,6 +392,28 @@ if [ "${1:-}" = "--self-test" ] || [ "${1:-}" = "--selftest" ]; then # guard edit at all -- otherwise the new kind would be strictly # WORSE than `set` on the ordinary path. ap_row 'aperture unchanged is green' 0 '# header\n' 'GUARD v1\n' 'the old claim, 2.93 times faster\n' + # -- NAME entries (#3644). unwired_guards_baseline.txt is a set of + # FILES, not of lines: the meta-guard's name:-line blindness hid + # four dark guards, and widening it reveals files already in the + # tree. (a) for a name is "the comparand carries the file" -- at the + # entry's path, or beside the owning guard, since the ledger names + # siblings by basename. Each admission has its refusal beside it. + ap_row 'aperture NAME entry that predates is admitted' 0 '# header\npre.md\n' 'GUARD v2\n' 'the old claim, 2.93 times faster\n' + ap_row 'aperture NAME entry this branch WROTE refuses' 1 '# header\nfresh.md\n' 'GUARD v2\n' 'the old claim, 2.93 times faster\n' + ap_row 'aperture NAME entry without a guard edit refuses' 1 '# header\npre.md\n' 'GUARD v1\n' 'the old claim, 2.93 times faster\n' + ap_row 'aperture NAME entry escaping the repo refuses' 1 '# header\n../pre.md\n' 'GUARD v2\n' 'the old claim, 2.93 times faster\n' + # beside the owner: `sib.sh` is not at the root, it is next to sub/owner.sh + printf '# header\nsib.sh\n' > "$SR/$P" + printf 'OWNER v2\n' > "$SR/sub/owner.sh" + ( BASELINE_RATCHET_BASE_REF="$SR_APER" \ + baseline_ratchet_check "$SR" "$P" set-aperture sub/owner.sh ) >/dev/null 2>&1 + say_row 'aperture NAME entry beside the OWNER is admitted' 0 $? + printf '# header\nsib.sh\n' > "$SR/$P" + printf 'GUARD v2\n' > "$SR/guard.sh" + ( BASELINE_RATCHET_BASE_REF="$SR_APER" \ + baseline_ratchet_check "$SR" "$P" set-aperture guard.sh ) >/dev/null 2>&1 + say_row 'aperture NAME entry beside a DIFFERENT owner refuses' 1 $? + printf 'OWNER v1\n' > "$SR/sub/owner.sh" # A missing owner argument must fail CLOSED. Called with no guard # path, "could not check" must never read as "no growth". printf '%b' "$AP_BASE" > "$SR/$P" diff --git a/scripts/check_guards_are_wired.sh b/scripts/check_guards_are_wired.sh index 2d581ed8f1..309dddedc0 100755 --- a/scripts/check_guards_are_wired.sh +++ b/scripts/check_guards_are_wired.sh @@ -66,11 +66,25 @@ BASELINE="${REPO_ROOT}/scripts/unwired_guards_baseline.txt" # committed by the very guard that exists to catch it. An argument-wired guard # is still named by its own workflow and is found by the scan below on that # account; a release-time guard is deliberately unwired and stays reported. +# A STEP NAME IS NOT AN INVOCATION (#3644). ci.yml carries +# - name: "guard_tree.sh's own case table (BSE-01, wired BSE-02)" +# - name: "guard_tree.sh's parallel dispatcher case table (PMAT-1098)" +# The invocation regex below accepts `guard_tree.sh` followed by `'` -- meant +# for a quoted `run: "..."` -- so the apostrophe in `guard_tree.sh's` matched, +# neither line says --no-cargo, and each was read as a BARE dispatch in mode +# "all": the full --dry-run RUN set, 55 cargo-classified guards that +# `--no-cargo` never runs, all counted wired. Four were dark behind it, +# including the #3305 publish-strip guard, and this meta-guard said PASS. +# Proven by rewriting those two names (nothing else): 3 -> 7 unwired. +# Mention-vs-execution, one level below the trailing-comment case above. +# A `name:` line is documentation whatever it contains; drop it before matching. +_not_a_name_line() { grep -vE '^[[:space:]]*-?[[:space:]]*name:' || true; } + dispatcher_wired() { local root="$1" lines modes mode flag out [ -f "$root/scripts/guard_tree.sh" ] || return 0 lines=$(grep -rh --include='*.yml' --include='*.yaml' -- 'guard_tree.sh' \ - "$root"/.github/workflows/ 2>/dev/null | sed 's/#.*$//') || lines='' + "$root"/.github/workflows/ 2>/dev/null | sed 's/#.*$//' | _not_a_name_line) || lines='' # Invocation, not mention -- same test as the scan below. lines=$(grep -E "(^|[[:space:];&|(])((ba)?sh[[:space:]]+|\\./)?[^[:space:]]*guard_tree\\.sh([[:space:]]|$|['\"])" \ <<< "$lines") || lines='' @@ -154,7 +168,7 @@ unwired_in() { local mentions mentions=$(grep -rh --include='*.yml' --include='*.yaml' -- "$base" \ "$root"/.github/workflows/ 2>/dev/null \ - | sed 's/#.*$//') || mentions='' + | sed 's/#.*$//' | _not_a_name_line) || mentions='' if ! grep -qE "(^|[[:space:];&|(])((ba)?sh[[:space:]]+|\\./)?[^[:space:]]*${base}([[:space:]]|$|['\"])" <<< "$mentions" ; then printf '%s\n' "$base" fi @@ -272,8 +286,39 @@ if [ "${1:-}" = "--self-test" ]; then printf 'FAIL row 7 got [%s], expected [check_nightly_only.sh check_nowhere.sh ]\n' "$got7"; fails=1 fi + # ── Rows 8-10: A STEP NAME IS NOT AN INVOCATION (#3644) ───────────────── + # + # Row 8 is the exact ci.yml shape that hid four guards: a step NAMED + # `guard_tree.sh's ...`, no dispatcher run: line. The dispatched fixture + # guard must come back as unwired. Row 9 is the control the `'` in the + # regex exists for: a QUOTED run: line still dispatches. Row 10 is the same + # blindness in the per-guard scan: a step named after a guard wires nothing. + printf 'jobs:\n gate:\n steps:\n - name: "guard_tree.sh'"'"'s own case table (BSE-01, wired BSE-02)"\n run: echo a name is not a dispatch\n' \ + > "$TD2/.github/workflows/ci.yml" + got8=$(unwired_in "$TD2" | tr '\n' ' ') + if [ "$got8" = "check_dispatched.sh " ]; then + printf 'ok row 8 a step NAMED guard_tree.sh'"'"'s ... is not a dispatch; the guard stays unwired\n' + else + printf 'FAIL row 8 got [%s], expected [check_dispatched.sh ] -- a step name read as a bare dispatch\n' "$got8"; fails=1 + fi + printf 'jobs:\n gate:\n steps:\n - run: "bash scripts/guard_tree.sh --no-cargo"\n' \ + > "$TD2/.github/workflows/ci.yml" + if [ -z "$(unwired_in "$TD2")" ]; then + printf 'ok row 9 a QUOTED run: line still dispatches (the control for row 8)\n' + else + printf 'FAIL row 9 quoted dispatch not honoured: [%s]\n' "$(unwired_in "$TD2" | tr '\n' ' ')"; fails=1 + fi + printf 'jobs:\n x:\n steps:\n - name: "check_dark.sh'"'"'s job"\n run: bash scripts/check_wired.sh\n' \ + > "$TD/.github/workflows/ci.yml" + got10=$(unwired_in "$TD" | tr '\n' ' ') + if [ "$got10" = "check_dark.sh " ]; then + printf 'ok row 10 a step named after a guard wires nothing\n' + else + printf 'FAIL row 10 got [%s], expected [check_dark.sh ]\n' "$got10"; fails=1 + fi + [ "$fails" -eq 0 ] || { printf '\nSELF-TEST FAILED\n'; exit 1; } - printf '\nSELF-TEST PASSED (7/7)\n' + printf '\nSELF-TEST PASSED (10/10)\n' exit 0 fi @@ -332,7 +377,11 @@ fi # branch cannot rewrite, and never the branch against itself. # shellcheck source=scripts/lib_baseline_ratchet.sh . "${REPO_ROOT}/scripts/lib_baseline_ratchet.sh" || exit 1 -baseline_ratchet_check "${REPO_ROOT}" scripts/unwired_guards_baseline.txt set || exit 1 +# set-aperture, owned by this file (#3644): when THIS guard widens and reveals +# guards that were already dark, the ledger may record them -- only guards the +# comparand already carries, only in a diff that changes this file, every +# admission printed. On every other PR it is `set`: shrink-only. +baseline_ratchet_check "${REPO_ROOT}" scripts/unwired_guards_baseline.txt set-aperture scripts/check_guards_are_wired.sh || exit 1 if [ ! -f "$BASELINE" ]; then printf 'FAIL: %s missing. Run --update once to establish it.\n' "$BASELINE" diff --git a/scripts/check_pathonly_devdeps_unused_in_src.sh b/scripts/check_pathonly_devdeps_unused_in_src.sh index c6ae4812a8..092016d44f 100755 --- a/scripts/check_pathonly_devdeps_unused_in_src.sh +++ b/scripts/check_pathonly_devdeps_unused_in_src.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # check_pathonly_devdeps_unused_in_src.sh - src/ may not reference a dev-dep -# that `cargo publish` deletes. +# that publishing to crates.io deletes. # # THE CLASS. A dev-dependency written as `{ path = "..." }` with no version, -# no git and no workspace inheritance carries NO SOURCE. `cargo publish` omits +# no git and no workspace inheritance carries NO SOURCE. The publish step omits # it from the published manifest entirely -- that omission is deliberate and is # what lets a dev-dep cycle (aprender-compute -> aprender-core) be legal. But # the crate's own `#[cfg(test)]` code is published with it. If that code names @@ -29,8 +29,30 @@ # baseline fails, and a baseline pair with no remaining violation also fails, so # a fix must delete its row in the same commit. Draining it to empty is #3306. # +# WHO RUNS IT, AND WHY THIS FILE NEVER WRITES THE BUILD TOOL'S NAME WITH A SPACE +# AFTER IT (#3644). guard_tree.sh classifies a guard as cargo-using by the +# substring CARGO_RE, comments included; `guard-tree` runs only the cargo-free +# population (--no-cargo) and `guard-cargo` names its guards by hand. This file +# said the tool's name in three comments and one FAIL string, was classified +# cargo-using, was named by nothing, and RAN NOWHERE from the day it was +# written (2026-09-15) -- while check_guards_are_wired.sh reported PASS, blinded +# by a step name. It invokes no build tool: python over the manifests, then +# grep. Now it is in the --no-cargo population and guard-tree runs it. +# +# ENV IS NEVER A CODE VERDICT (#3644, the same shape as #3626's guard). The +# scanner needs a TOML reader: tomllib (python 3.11+) or tomli. The intel +# runner host has python 3.10.12 and neither; the old module-level import died +# with a traceback, `out=$(scan ... || true)` swallowed it, and the case table +# said "FAIL: missed hit_use" -- the regex reported broken by an interpreter +# that never ran it. Now the scanner ends with a `SCAN-DONE manifests=N` +# trailer that the shell REQUIRES; anything without it (no reader, a dead +# interpreter, no python3) is ENV rc=2 naming the interpreter and the runner -- +# never rc=1, never "no violations". +# # Runs bare, no arguments, from anywhere in the repo. `--selftest` runs the case # table only. A bare run does BOTH: the case table first, then the tree. +# PATHONLY_GUARD_PYTHON= test seam: a dead one reproduces the intel shape +# PATHONLY_GUARD_FORCE_NO_TOML=1 test seam: both readers fail to import, for real set -euo pipefail REPO="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)" @@ -47,13 +69,33 @@ cleanup() { } trap cleanup EXIT -# scan -> prints "manifest|alias|src-file:line:text" per violation +# scan -> prints "manifest|alias|src-file:line:text" per violation. +# rc 0 with the trailer consumed; rc 2 (ENV) when the scanner did not finish. scan() { - python3 - "$1" <<'PY' -import os, re, sys, tomllib + local out rc=0 interp="${PATHONLY_GUARD_PYTHON:-python3}" + out=$(PATHONLY_GUARD_RUNNER="${RUNNER_NAME:-unknown}" "$interp" - "$1" 2>&1 <<'PY' +import os, re, sys + +runner = os.environ.get("PATHONLY_GUARD_RUNNER", "unknown") +if os.environ.get("PATHONLY_GUARD_FORCE_NO_TOML") == "1": + sys.modules["tomllib"] = None # the imports below then raise for real + sys.modules["tomli"] = None + +toml = None +for name in ("tomllib", "tomli"): + try: + toml = __import__(name) + break + except ImportError: + continue +if toml is None: + print("ENV: no TOML reader on this interpreter (python %d.%d, need tomllib >= 3.11 or tomli) runner=%s -- " + "the manifests were not read; this is not 'no violations'" % (sys.version_info[0], sys.version_info[1], runner)) + sys.exit(2) root = sys.argv[1] KINDS = ("dev-dependencies",) +manifests = 0 def sourceless(spec): """True when the dep carries a path and nothing that survives publish.""" @@ -73,9 +115,10 @@ for dirpath, dirnames, filenames in os.walk(root): continue try: with open(manifest, "rb") as fh: - data = tomllib.load(fh) - except (tomllib.TOMLDecodeError, OSError): + data = toml.load(fh) + except (toml.TOMLDecodeError, OSError): continue + manifests += 1 aliases = [a for k in KINDS for a, s in (data.get(k) or {}).items() if sourceless(s)] if not aliases: continue @@ -100,7 +143,21 @@ for dirpath, dirnames, filenames in os.walk(root): rel_m = os.path.relpath(manifest, root) rel_s = os.path.relpath(path, root) print(f"{rel_m}|{alias}|{rel_s}:{n}:{stripped[:100]}") +# positive evidence that the scan RAN TO THE END; the shell refuses any output without it +print("SCAN-DONE manifests=%d reader=%s" % (manifests, toml.__name__)) PY + ) || rc=$? + case "$out" in + *"SCAN-DONE manifests="*) ;; + *) + printf 'ENV: the scanner did not finish (interpreter=%s rc=%s runner=%s) -- not a pass, not a code verdict\n' \ + "$interp" "$rc" "${RUNNER_NAME:-unknown}" >&2 + [ -z "$out" ] || printf '%s\n' "$out" | sed 's/^/ /' >&2 + return 2 ;; + esac + # the trailer is evidence, not a row + printf '%s\n' "$out" | grep -v '^SCAN-DONE ' || true + return 0 } # ── case table: every row is a fixture the regex/selector must classify ── @@ -125,7 +182,7 @@ selftest() { mk hit_underscore 'two-words = { path = "../tw" }' 'use two_words::x;' mk hit_extern 'sib = { path = "../sib" }' 'extern crate sib;' - # MUST NOT MATCH -- these all survive `cargo publish`, or are not a reference + # MUST NOT MATCH -- these all survive publishing, or are not a reference mk ok_versioned 'sib = { path = "../sib", version = "0.1.0" }' 'use sib::thing;' mk ok_workspace 'sib = { workspace = true }' 'use sib::thing;' mk ok_git 'sib = { git = "https://x/y", path = "../sib" }' 'use sib::thing;' @@ -134,7 +191,14 @@ selftest() { mk ok_comment 'sib = { path = "../sib" }' '// use sib::thing;' mk ok_substring 'sib = { path = "../sib" }' 'use sibling::thing;' - out="$(scan "$tmp" || true)" + # ENV from the scanner is ENV here: rc 2, never "missed hit_use" (#3644) + local env_rc=0 + out="$(scan "$tmp")" || env_rc=$? + if [ "$env_rc" -ne 0 ]; then + printf 'ENV: the case table could not be measured (scan rc=%s)\n' "$env_rc" + cleanup; FIXTURES="" + return 2 + fi # Piping a producer into a quiet grep is banned here # (check_no_pipe_into_grep_q.sh): grep exits on its first match and the @@ -156,18 +220,54 @@ selftest() { for row in ok_versioned ok_workspace ok_git ok_registry ok_unused ok_comment ok_substring; do if row_present "$row"; then printf 'FAIL: false positive on %s\n' "$row"; rc=1; fi done + + # THE DEATH ROWS (#3644): every way the scanner can fail to run must come + # back as ENV rc=2, never as a row verdict. A must-match fixture is scanned + # each time, so a wrong classification would have to invent "missed hit_use" + # (rc 1) or "no violations" (rc 0) out of an interpreter that never ran. + death() { # death