guard(G-10b): check_pmat_pinned.sh — every analyser reference resolves through scripts/pmat_bin.sh, counted and shrink-only against a measured baseline (PMAT-1063) - #3021
Conversation
…cripts/ and .github/workflows/ resolves through scripts/pmat_bin.sh, counted and shrink-only against scripts/pmat_unpinned_baseline.txt (243, measured by the guard at this commit, never typed; a missing or INVALID baseline is ENV); 20-row case table incl. the ratchet's both polarities; wired case table then live; contract 1.1.0 PIN-OB-005/PIN-F-005 (PMAT-1063, #3013) Pmat-Ticket: PMAT-1063
…get_watch.sh for the CI RED leg (I3) — check_pmat_pinned.sh must report unpinned=244 > baseline 243. REVERTED in the next commit Pmat-Ticket: PMAT-1063
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
…D on the shrink-only step (unpinned=244 > 243); receipt PMAT-1063 complete (v4: the receipt says complete in the PR before auto-merge is armed) Pmat-Ticket: PMAT-1063
…sh as the union of both sides (T-5 R-4) Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… count (288), basis re-dated; not a raise, the ratchet did not exist on main Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…4 --check) guard-tree on the merge reported DRIFT between the spec's rendered block and docs/specifications/pp-066-dag.yaml; scripts/render_dag.py render pasted between the markers, --check green. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
check_roadmap_diff_additive.sh reported base id(s) missing at head: this branch forked before they landed on main, and a plain update takes one side of the file whole. Resolved as main's roadmap plus this branch's own new entries, each at its sorted slot among same-prefix peers. The other three roadmap guards pass on the result. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # .github/workflows/ci.yml
…ard-cargo fails on the header, before it ever reads the count
FAIL header scripts/pmat_unpinned_baseline.txt declares no instrument
3 of 19 baseline(s) name a pinned tool; MISMATCH
check_baseline_ratchets.sh requires every baseline to name the instrument that
produced it, because "a ratchet compares (tree, instrument), and a baseline that
never names its instrument cannot tell 'the count changed' from 'the analyser
changed'" (lib_baseline_ratchet.sh). This file is new in this PR and carries no
`# tool_version=` line.
The honest value is `none`, and the reason is worth stating rather than copying:
this count is produced by a grep over scripts/ and .github/workflows/, so it is a
property of the TREE. No pmat or bashrs release changes it. Pinning an instrument
here would assert a dependency that does not exist — the opposite failure to the
one the rule guards against.
The count parse is unaffected: baseline_count() reads
`sed -nE 's/^[[:space:]]*([0-9]+)...'`, which skips `#` lines, and the file still
parses as 288.
THIS FIXES THE HEADER, NOT THE COUNT. check_pmat_pinned.sh is separately red at
`unpinned=299 baseline=288`, because merging current main brought 11 more matches —
the same drift the baseline's own comment already records once ("main gained 36
unpinned references between the branch fork and the merge"). That is this PR's
design question, not a merge artifact, and it is left for its author; see the PR
comment for the measurement.
Pmat-Ticket: PMAT-1063
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Two separate reds here. I fixed one and am leaving the other, because it is this PR's design Fixed: the baseline declares no instrument
Added Not fixed: the count, and why it may not be fixable as specifiedMerging current main brought 11 more matches. The baseline's own comment already records this The measurement that matters: Over half of what this ratchet counts is prose: That third one is a comment about pinning instruments, counted as an unpinned reference. Two consequences, both worth deciding deliberately:
A regex that reads prose as code is the same shape this repo keeps finding elsewhere — I hit it I have not made that change: it alters what this PR is measuring, and that is yours to decide. 🤖 Generated with Claude Code |
…false RED costs the most
`check_no_pipe_into_grep_q.sh` — the guard this PR already fixes the regex of — flags
two sites in scripts/tests/check_roadmap_sorted_test.sh, and both are the worst
placement for this construct:
54: if [ "$rc" = "$want" ] && printf '%s' "$out" | grep -qE -- "$pat"; then
165: if [ "$self_rc" = 0 ] && printf '%s' "$self_out" | grep -qE '...'; then
Line 54 is a row() helper; line 165 asserts the guard's own self-test is green.
Under `pipefail` the quiet grep exits on first match, printf takes SIGPIPE and
returns 141, and the PIPELINE is 141 — so the row reports FAILED precisely when the
pattern MATCHED. In a row() helper that false-REDs the entire table, and in the
self-test-is-green row it reports a working guard as broken. Both directions teach
the reader to stop trusting the harness, which is worse than the harness not existing.
Not hypothetical: the same construct false-REDded `guard-cargo` twice in one afternoon
(runs 34759351727, 34765202728), both times printing the wanted string in their own
failure output, both times ejecting work from the merge queue.
Converted to the here-string the guard prescribes — a here-string has no producer to
kill, so nothing can take SIGPIPE.
harness: 9/9 checks, 0 failed
sites: 81 -> 79, baseline restamped (shrink-only)
bashrs: 0 errors
These two are PRE-EXISTING on main, not introduced here; main sits at exactly its
ceiling (83/83) with zero headroom, which is why they surfaced now — #3021's merge
pushed the count over and named them. Fixing them here rather than there because this
PR already owns the guard and its baseline, and a shrink-only baseline split across
two PRs strands the second.
Pmat-Ticket: PMAT-3232
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…th no consumer is refused, not counted (paiml#3280) * feat(ont): §11.2's five counters become measurable — and an anchor with no consumer is refused, not counted §11.2 gives five counters, a direction each, and records "Today" as 0/0/0/0/[U]. Those numbers lived in a spec table: nothing computed them, `git ls-files contracts/lint-baseline.json` was empty, and `grep -c '^ont-ratchet' Makefile` was 0. A ratchet whose counters are typed into prose cannot turn. Measured now, on the real tree: consumer_present false contracts_total 1837 (§11.0 said 1818 — main has grown since fa6e35f) entity_types_registered 0 extractors_implemented 0 contracts_anchored 0 contracts_shaped 0 unanchored_but_bindable 297 <- §11.2 had this as [U] THE GATE, and the reason this is not just a counter. $ printf 'entity: kernel\n' | cat - contracts/<any>.yaml > /tmp/a.yaml $ pv validate /tmp/a.yaml 0 error(s), 0 warning(s) Contract is valid. `pv validate` ACCEPTS `entity:` — by ignoring it. There is no `pv census`, no `pv extract`, no crates/aprender-contracts/src/ontology/, and nothing in either contracts crate reads the key (the one grep hit was `identity:` in a test message). So anchoring the corpus today would write 1837 rows that every gate calls valid and no code reads, while `ont.contracts_anchored` climbed to 1837. A counter measuring its own decoration. ONT-001 R-2 — "zero is a decline, never an accept" — applies to the instrument as much as to a verdict. `contracts_anchored` may not RISE while `consumer_present` is false; the guard refuses it and names ONT-1 as the fix. The probe is derived from `pv --help`, never declared, so it flips on its own the day census lands. Case table: 10 rows, and the deciding one is mutation-verified — replacing the consumer gate with `if false` turns the table RED. Three defects found writing it, each kept as a comment where it bit: * `set -euo pipefail` + a grep that matches NOTHING = pipeline exit 1 = silent death. Every counter here starts at zero, so the FIRST real measurement was the one that died: `--write` produced no file, no error, exit 1, and only `bash -x` showed it stopping one line after `anchored=0`. * a RETURN trap runs after bash destroys the function's locals, so cleanup died on an unbound variable AFTER the rows had passed. * `field()` stripped the wrong characters and compared "7" against "7}}". AND A DEFECT IN A NEIGHBOURING GUARD. check_no_pipe_into_grep_q.sh's HAZARD regex matched the SECOND bar of `||`, so `cmd || grep -q PAT file` — a logical OR with no producer to kill and no SIGPIPE to misread — was flagged as a pipe. Two pre-existing sites in this tree (dogfood.sh:1477 greps a FILE, qwen-story.sh:281 greps a HERE-STRING) plus every new one. The regex now requires a single bar and ships four rows in BOTH directions: `||` not flagged, here-string not flagged, a real pipe still flagged, and a real pipe LATER on a line that also contains `||` still flagged. Its shrink-only baseline restamped 83 -> 81. Wiring: no workflow edit. guard_universe() is `git ls-files 'scripts/check_*.sh'` — tracked-only — so the guard was invisible to the dispatcher until it was staged, which is the free-pass shape that has cost this repo four guards. Staged, `guard_tree.sh --dry-run` reports `run: scripts/check_ont_ratchet.sh` and check_guards_are_wired.sh exits 0. guard_tree --no-cargo: 55 checks (was 53), 2 failed — both the pmat 3.39.0 baselines this branch inherits from main, which paiml#3277 fixes. bashrs 7.4.1: 0 errors on both touched guards. ont-delta: resolves scripts/check_ont_ratchet.sh — §11.2 was a table of numbers nothing computed; it is now a measurement with a refusal (form 4). Pmat-Ticket: PMAT-3232 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ont): the R-5 rule, not just its number — `kernel OR binding` admits a case R-5 excludes ONT R-5, verbatim: "Only contracts that *should* be anchored (kernel-kind with a binding, and any contract naming a file) count against the `unanchored_but_bindable` ratchet." That is two disjuncts and the first is a CONJUNCTION: (kernel-kind AND a binding) OR (names a file) The first draft spelled it `kernel OR binding OR file`, which admits a kernel-kind contract that has no binding. Both spellings return 297 on today's corpus, because that set is currently EMPTY: as shipped (kernel OR binding OR file): 297 ONT R-5 ((kernel AND binding) OR file): 297 kernel-kind with NO binding: 0 So the NUMBER agreed while the RULE did not, and it would have diverged silently the first time such a contract was written — a guard that is right by accident of the corpus is a guard that goes wrong without anything changing in it. Also worth stating because the ratchet's direction depends on it: an unanchored contract is NOT a defect. R-5 makes `entity:` optional by design — "many contracts are laws, patterns or policies with nothing to anchor, and an absent entity is not inferred to be code" (ONT-001 v4.3 §0.0, v4.1's correction to v4). Only the bindable ones are the backlog this ↓ counter drains, which is why §11.2 made it the counter that goes DOWN rather than making `contracts_anchored` the one that must go up. Upstream is now readable fleet-wide: infra#583 landed v4.3 on infra main (972 lines), so this premise is fetchable from gx10, yoga and mini rather than living on one box. ont-delta: none — this corrects a rule already landed in this PR; the delta is the one already recorded against scripts/check_ont_ratchet.sh. Pmat-Ticket: PMAT-3232 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(roadmap): collapse the union's stray blank line back to base bytes (roadmap_trim.py) Same defect as PMAT-3231: my hand-rolled union rejoined entry blocks and left an extra blank line, re-serialising the entry above the insertion. PMAT-980 (#2874). Pmat-Ticket: PMAT-3232 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(tests): two pipe-into-quiet-grep rows in a test HARNESS, where a false RED costs the most `check_no_pipe_into_grep_q.sh` — the guard this PR already fixes the regex of — flags two sites in scripts/tests/check_roadmap_sorted_test.sh, and both are the worst placement for this construct: 54: if [ "$rc" = "$want" ] && printf '%s' "$out" | grep -qE -- "$pat"; then 165: if [ "$self_rc" = 0 ] && printf '%s' "$self_out" | grep -qE '...'; then Line 54 is a row() helper; line 165 asserts the guard's own self-test is green. Under `pipefail` the quiet grep exits on first match, printf takes SIGPIPE and returns 141, and the PIPELINE is 141 — so the row reports FAILED precisely when the pattern MATCHED. In a row() helper that false-REDs the entire table, and in the self-test-is-green row it reports a working guard as broken. Both directions teach the reader to stop trusting the harness, which is worse than the harness not existing. Not hypothetical: the same construct false-REDded `guard-cargo` twice in one afternoon (runs 34759351727, 34765202728), both times printing the wanted string in their own failure output, both times ejecting work from the merge queue. Converted to the here-string the guard prescribes — a here-string has no producer to kill, so nothing can take SIGPIPE. harness: 9/9 checks, 0 failed sites: 81 -> 79, baseline restamped (shrink-only) bashrs: 0 errors These two are PRE-EXISTING on main, not introduced here; main sits at exactly its ceiling (83/83) with zero headroom, which is why they surfaced now — paiml#3021's merge pushed the count over and named them. Fixing them here rather than there because this PR already owns the guard and its baseline, and a shrink-only baseline split across two PRs strands the second. Pmat-Ticket: PMAT-3232 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(tests): four more row() helpers that report FAILED precisely when they MATCHED Three test harnesses, four sites, same construct as the two fixed earlier in this PR and the one in paiml#3134: scripts/tests/baseline_tool_version_test.sh:62 row() helper scripts/tests/baseline_tool_version_test.sh:152 the mismatch assertion scripts/tests/check_tool_versions_test.sh:43 row() helper scripts/tests/ci_gpu_touched_test.sh:34 row() helper Under `pipefail` the quiet grep exits on first match, printf takes SIGPIPE and returns 141, and the PIPELINE is 141 — so the row reports FAILED exactly when the pattern MATCHED. In a row() helper that inverts the whole case table, and a case table is what every other guard in this repo is proven by. Chosen deliberately over the other five sites the guard still lists. These are test HARNESSES: a mistake here cannot change what production CI decides. `ci_test_tier.sh` (6 sites) and `ci_gpu_touched.sh` (5) decide the test tier and whether the GPU lane fires; those stay listed in the PR comment rather than swept at this hour. VERIFIED PER FILE, not in aggregate: baseline_tool_version_test.sh rows 2-6 ok (row 1 fails IDENTICALLY before and after — it is this box's pmat 3.40.1 against a 3.40.0 baseline, the fleet split in aprender#3291, not this change; compared by running origin/main's copy in the same worktree) check_tool_versions_test.sh 6/6 checks, 0 failed ci_gpu_touched_test.sh 20 checks, 0 failed sites 79 -> 75, baseline restamped (shrink-only) bashrs 7.4.1: 0 errors on all three One defect of my own on the way: the edit to line 152 left a stray `b_match=1 &&` and a `grep` with no input, which would have read stdin and hung. Reading the result rather than trusting the substitution is what caught it — the same habit this whole class of fix is about. ci_gpu_touched_test.sh:63 is left alone: the pipe is inside a single-quoted script the test GENERATES, so converting it changes the fixture's own text rather than the harness, and that deserves its own reasoning. Pmat-Ticket: PMAT-3232 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
PP-066 DAG row G-10b · ticket PMAT-1063 · Closes #3013 · refs #2999 · epic #2873. Receipt:
docs/audits/impl-PMAT-1063-receipt.md. Follows PR-A (#3011, merged b0a0a51).What lands.
scripts/check_pmat_pinned.sh— the operator assertion as a shrink-only guard:grep -rEn '(^|[^_/])pmat ' scripts/ .github/workflows/ | grep -v pmat_bin, counted againstscripts/pmat_unpinned_baseline.txt. The baseline is 243, measured by the guard itself at the commit it names (--updatewrites the command and the sha into the file; the "281" of the driver was the pre-PR-A count). A count above the baseline is RED naming every line; below it is an improvement to record; a missing orINVALIDbaseline is ENV (exit 2), never a pass. Kind-table entry (count) incheck_baseline_ratchets.sh; two CI steps inguard-runner-labels(case table, then live); contractapr-pinned-analyser-ratchet-v11.0.0 → 1.1.0 (PIN-OB-005 / PIN-F-005). The sweep to 0 is G-10c (#3014).Mutation evidence (I3) — on this branch, never in the queue.
c4f6b618amutant: one barepmat analyze satdcomment appended toscripts/ci_target_watch.shFAIL check_pmat_pinned: unpinned=244 baseline=243 — 1 new line(s) …naming the linePASS … unpinned=243 baseline=243Case table
bash scripts/check_pmat_pinned.sh --self-test→ 20/20: rows 1–11 the spellings (five match, six sanctioned do not), rows 12–15 the resolver (at-pin resolves; off-pin and absent refused; option-neutral), rows 16–20 the ratchet on a fixture tree (baseline 2 PASS · baseline 1 RED naming both lines · baseline 3 PASS + improvement · no baseline ENV 2 ·INVALIDENV 2).Acceptance (re-run by the orchestrator on the re-cut base b0a0a51 —
.pr/G-10b-verify.log): self-test 20/20 · liveunpinned=243 baseline=243·check_baseline_ratchets.shPASS ·check_guards_are_wired.shPASS ·pv validatevalid ·check_shell_lint_ratchet.shPASS ·check_no_claim_literals.shrc 0.Write set: the guard, the baseline, the kind-table line, two ci.yml steps, the contract, the receipt. No DAG/roadmap/README/spec edit.
no-close: #2873 is the epic this row belongs to and #2999 / #3014 are cited for context; this PR closes #3013 only, and an epic is not closed by one of its rows.