From 02cf58c1d7ef2a53db2347d951b8b7427e0fc91e Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sun, 6 Sep 2026 13:55:28 +0200 Subject: [PATCH 1/7] =?UTF-8?q?guard(G-10b):=20check=5Fpmat=5Fpinned.sh=20?= =?UTF-8?q?=E2=80=94=20every=20analyser=20reference=20under=20scripts/=20a?= =?UTF-8?q?nd=20.github/workflows/=20resolves=20through=20scripts/pmat=5Fb?= =?UTF-8?q?in.sh,=20counted=20and=20shrink-only=20against=20scripts/pmat?= =?UTF-8?q?=5Funpinned=5Fbaseline.txt=20(243,=20measured=20by=20the=20guar?= =?UTF-8?q?d=20at=20this=20commit,=20never=20typed;=20a=20missing=20or=20I?= =?UTF-8?q?NVALID=20baseline=20is=20ENV);=2020-row=20case=20table=20incl.?= =?UTF-8?q?=20the=20ratchet's=20both=20polarities;=20wired=20case=20table?= =?UTF-8?q?=20then=20live;=20contract=201.1.0=20PIN-OB-005/PIN-F-005=20(PM?= =?UTF-8?q?AT-1063,=20#3013)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pmat-Ticket: PMAT-1063 --- .github/workflows/ci.yml | 8 ++ contracts/apr-pinned-analyser-ratchet-v1.yaml | 36 ++++-- docs/audits/impl-PMAT-1063-receipt.md | 47 ++++++++ scripts/check_baseline_ratchets.sh | 1 + scripts/check_pmat_pinned.sh | 103 ++++++++++++++++++ scripts/pmat_unpinned_baseline.txt | 1 + 6 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 docs/audits/impl-PMAT-1063-receipt.md create mode 100644 scripts/check_pmat_pinned.sh create mode 100644 scripts/pmat_unpinned_baseline.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5f768c22d..1279e988ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1601,6 +1601,14 @@ jobs: run: bash scripts/check_hardcoded_paths.sh --self-test - name: Whole-tree machine-specific-path ratchet (pinned analyser via scripts/pmat_bin.sh, PMAT-1059) run: bash scripts/check_hardcoded_paths.sh --full-if-capable + # G-10b (PMAT-1063, #3013): every OTHER analyser reference under scripts/ and the + # workflows resolves through the same pin — counted, shrink-only against + # scripts/pmat_unpinned_baseline.txt (measured at the commit it names, never typed); + # the sweep to 0 is G-10c. Case table first, then the live count. + - name: Analyser pin guard case table (scripts/pmat_bin.sh, G-10b PMAT-1063) + run: bash scripts/check_pmat_pinned.sh --self-test + - name: Unpinned analyser references may only fall (shrink-only, G-10b PMAT-1063) + run: bash scripts/check_pmat_pinned.sh # PRREV-006. PR-REVIEW-SKILL-002 v2 §9 row 6 states how the receipt guard # may be wired, and both halves are scars rather than style: # diff --git a/contracts/apr-pinned-analyser-ratchet-v1.yaml b/contracts/apr-pinned-analyser-ratchet-v1.yaml index 58271f3f76..6c9f30984e 100644 --- a/contracts/apr-pinned-analyser-ratchet-v1.yaml +++ b/contracts/apr-pinned-analyser-ratchet-v1.yaml @@ -7,10 +7,11 @@ # scripts/hardcoded_path_shipped_baseline.txt (stamped: count, pmat_version, # basis) and scripts/check_hardcoded_paths.sh --full-if-capable (absolute # compare only under a matching stamp; otherwise REPORT and decide HEAD vs -# merge-base under the same binary). The guard that every OTHER analyser -# reference under scripts/ and .github/workflows/ resolves through the pin -# (check_pmat_pinned.sh) is G-10b and extends this contract; the sweep of -# the references it counts is G-10c. +# merge-base under the same binary). 1.1.0 (G-10b, PMAT-1063): the guard +# that every OTHER analyser reference under scripts/ and .github/workflows/ +# resolves through the pin — scripts/check_pmat_pinned.sh, counted and +# shrink-only against scripts/pmat_unpinned_baseline.txt. The sweep of the +# references it counts to 0 is G-10c (PMAT-1064). # # PROOF LADDER, STATED HONESTLY (F-26) # * L1 — the resolver and the ratchet exist and are wired in ci.yml job @@ -24,7 +25,7 @@ # * L3/L4 — NOT APPLICABLE and not declared: shell guards over a text file. # ───────────────────────────────────────────────────────────────────────────── name: apr-pinned-analyser-ratchet -version: "1.0.0" +version: "1.1.0" scope: > The analyser pin, the stamped shipped-tier baseline and the differential ratchet. Out of scope: what the analyser detects (pmat#1017 owns the @@ -35,14 +36,15 @@ status: active metadata: kind: pattern # a pin-and-compare rule over shell guards and a text file, not a kernel - version: "1.0.0" + version: "1.1.0" created: '2026-09-06' - last_modified: '2026-09-06' + last_modified: '2026-09-06' # 1.1.0: G-10b adds PIN-OB-005 / PIN-F-005 (the pin guard, shrink-only) author: PAIML Engineering references: - 'scripts/pmat_bin.sh — PMAT_PIN, resolution order, refusal with both versions named' - 'scripts/check_hardcoded_paths.sh — --full-if-capable: stamped baseline, REPORT BASELINE-STALE/INVALID, HEAD vs merge-base' - 'scripts/hardcoded_path_shipped_baseline.txt — count:, pmat_version:, basis:' + - 'scripts/check_pmat_pinned.sh — the operator assertion as a shrink-only guard: grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin, counted against scripts/pmat_unpinned_baseline.txt (G-10b)' - 'scripts/lib/resolve_base.sh — the base resolver shared with check_roadmap_diff_additive.sh (G-6)' - 'paiml/infra machines/intel/forjar.yaml — the fleet pin (3.31.0 -> 3.37.0, PMAT-231) that armed the ratchet on 2026-09-06' - 'PP-066 DAG row G-10, issue #2999, epic #2873' @@ -182,8 +184,26 @@ falsification_tests: delete the three check_hardcoded_paths.sh steps from ci.yml; check_guards_are_wired.sh must turn RED naming the script. + - id: PIN-F-005 + rule: one pin for every reference — counted, shrink-only (G-10b) + prediction: >- + case-table rows 1-11: the five unpinned spellings match, the six sanctioned + ones do not; rows 12-15: the resolver resolves at the pin, refuses off-pin + and absent, leaks no option; rows 16-20 (a fixture tree with 2 unpinned + lines): baseline 2 PASS, baseline 1 RED naming the lines, baseline 3 PASS + with an improvement reported, no baseline ENV exit 2, an INVALID baseline + ENV exit 2. One --self-test run exercises every row. + test: 'bash scripts/check_pmat_pinned.sh --self-test' + if_fails: >- + a gate's number silently depends on whichever analyser PATH resolves, one + script at a time + mutation: >- + reintroduce one bare `pmat comply check` in any script under scripts/; + `bash scripts/check_pmat_pinned.sh` must turn RED naming the line + (count = baseline + 1). Make the guard compare count <= 0 regardless of + the baseline; row 16 must turn RED. + non_goals: - - "check_pmat_pinned.sh — every other analyser reference resolves through the pin (G-10b, shrink-only from the 281 counted on 2026-09-06)." - "The sweep of those 281 references to 0 (G-10c)." - "The re-baseline (PMAT-1061): measured under the pin, stamped, its own PR." - "What counts as a machine-specific path: the analyser's detector (pmat#1017)." diff --git a/docs/audits/impl-PMAT-1063-receipt.md b/docs/audits/impl-PMAT-1063-receipt.md new file mode 100644 index 0000000000..a8da26eb49 --- /dev/null +++ b/docs/audits/impl-PMAT-1063-receipt.md @@ -0,0 +1,47 @@ +--- +status: partial +ticket: PMAT-1063 +row: G-10b +issue: 3013 +epic: 2873 +branch: agent/G-10b +pr: opened after PR-A (#3011) merges; re-cut onto main by `git diff agent/G-10..agent/G-10b | git apply` (the base is PR-A's tip, whose tree the squash merge reproduces) +model: claude-fable-5-1 (orchestrator, direct) +tokens_used: orchestrator [U] (not exposed to the orchestrator) +wall_clock_s: 900 (basis=session clock, claim to the receipt commit; [U] precision) +turns: 3 (orchestrator turns on this ticket, counted from the transcript) +--- +# impl receipt — PMAT-1063 (PP-066 row G-10b, #3013): the analyser pin guard, shrink-only + +## Identity +- kind: code · branch `agent/G-10b` off `agent/G-10` (PR-A, 0c35f8cfb) · this receipt is the first commit's companion +- write set: `scripts/check_pmat_pinned.sh` (new; from `agent/G-10-full` 4592b0572 plus the shrink-only ratchet, `--update`, fixture rows 16–20), `scripts/pmat_unpinned_baseline.txt` (new, measured), `scripts/check_baseline_ratchets.sh` (kind-table entry), `.github/workflows/ci.yml` (two steps after the ratchet step), `contracts/apr-pinned-analyser-ratchet-v1.yaml` (1.0.0 → 1.1.0: PIN-OB-005 / PIN-F-005), this receipt. No DAG, roadmap, README or spec edit. + +## The baseline is measured, not typed +The driver named "281"; that was the count on the pre-PR-A tree (2026-09-06, before the ratchet rewrite removed some references). On PR-A's tip this guard's own scan counts **243** — recorded by `bash scripts/check_pmat_pinned.sh --update`, whose written line carries the command, the commit and the kind. Derive: `grep -rEn '(^|[^_/])pmat ' scripts/ .github/workflows/ | grep -v pmat_bin | wc -l`. + +## Plan and routing (direct; `quorum: review-only` — the pre-PR review lanes are recorded when run) +| phase | content | A_i | result | +|---|---|---|---| +| P1 | guard with the shrink-only compare, `--update`, `PIN_SCAN_ROOT`/`PIN_BASELINE` fixtures, rows 16–20 | `bash scripts/check_pmat_pinned.sh --self-test` | 20/20 | +| P2 | baseline kind `count`; CI steps case-table-then-live | `bash scripts/check_baseline_ratchets.sh` · `bash scripts/check_guards_are_wired.sh` | PASS · PASS | +| P3 | contract 1.1.0 | `pv validate` via `scripts/pv_bin.sh` | valid | + +K̂ [U] (third receipt of the guard class after PMAT-1059 and PMAT-1062; the class basis can be computed once all three record turns). + +## Mutations observed RED → GREEN +| mutation | RED | GREEN | +|---|---|---| +| live: append `# probe: run pmat analyze satd here` to `scripts/ci_target_watch.sh` | `FAIL check_pmat_pinned: unpinned=244 baseline=243 — 1 new line(s) …` naming the file:line | reverted → `PASS … unpinned=243 baseline=243` | +| fixture rows 16–20 | row 17 (baseline 1 under 2 lines) RED naming both lines; row 19 no baseline → exit 2; row 20 `INVALID` → exit 2 | row 16 (baseline 2) PASS; row 18 (baseline 3) PASS with `Improved: 3 -> 2` | +| case-table rows 1–15 (from PR-A's design) | rows 1–5 match, row 13 off-pin refused, row 14 absent refused | rows 6–11 clean, row 12 at-pin resolves, row 15 option-neutral | + +## Verification (orchestrator's own runs, `.pr/G-10b-verify.log`) +self-test 20/20 · live PASS 243/243 · `check_baseline_ratchets.sh` PASS · `check_guards_are_wired.sh` PASS · `pv validate` valid · `check_shell_lint_ratchet.sh` PASS · `check_no_claim_literals.sh` rc 0. + +## Gaps +- G-10c (PMAT-1064, #3014): the sweep 243 → 0 from `agent/G-10-full` 5f8f28f19, baseline to 0. +- The pre-PR 3-lane review (review-only row) and the re-cut onto main after #3011. + +## Verdict +PARTIAL — awaiting PR-A's merge, the re-cut, the review lanes, `ci / gate` + `workspace-test`, and the merge. diff --git a/scripts/check_baseline_ratchets.sh b/scripts/check_baseline_ratchets.sh index b3d3e683bf..bab3dd940a 100644 --- a/scripts/check_baseline_ratchets.sh +++ b/scripts/check_baseline_ratchets.sh @@ -88,6 +88,7 @@ classify() { # classify -> "[reason]", rc 1 if unclassifie perf_claim_citation_baseline.txt) printf 'set-aperture\tscripts/check_perf_claims_cite_receipts.sh\n' ;; roadmap_uncited_completion_baseline.txt) printf 'set\n' ;; shell_lint_baseline.txt) printf 'count\n' ;; + pmat_unpinned_baseline.txt) printf 'count\n' ;; # G-10b (PMAT-1063): unpinned analyser references, measured by check_pmat_pinned.sh 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' ;; diff --git a/scripts/check_pmat_pinned.sh b/scripts/check_pmat_pinned.sh new file mode 100644 index 0000000000..9563a3239a --- /dev/null +++ b/scripts/check_pmat_pinned.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# check_pmat_pinned.sh - every execution surface resolves the analyser through +# scripts/pmat_bin.sh, never PATH (PMAT-1059, DAG row G-10, #2999). +# +# WHY. scripts/hardcoded_path_shipped_baseline.txt held "277", measured by an +# instrument nobody named. The fleet's forjar pin moved 3.31.0 -> 3.37.0 +# (paiml/infra machines/intel/forjar.yaml, PMAT-231) and the same tree counted +# 317: every PR went red for a defect no PR introduced. A gate's number is a +# property of (tree, instrument); the instrument is pinned in ONE place, +# scripts/pmat_bin.sh, and every caller takes "$PMAT" from it. +# +# THE ASSERTION (operator, 2026-09-06, verbatim): +# grep -rEn '(^|[^_/])pmat ' scripts/ .github/workflows/ | grep -v pmat_bin == 0 lines +# It counts prose too: a comment that says "run the analyser" in the old spelling +# is the line a reader copies. Lines that name pmat_bin are the resolver's own. +# +# SHRINK-ONLY (G-10b, PMAT-1063, #3013): the count is ratcheted against +# scripts/pmat_unpinned_baseline.txt (kind `count` in check_baseline_ratchets.sh), +# measured — never typed — by this guard's own scan at the commit named in the +# file. The sweep to 0 is G-10c (PMAT-1064). A count above the baseline is RED +# naming every line; below it is an improvement to record with --update; a +# missing baseline is ENV (exit 2), never a pass. +# +# bash scripts/check_pmat_pinned.sh # 0 count <= baseline . 1 grew (listed) . 2 no baseline +# bash scripts/check_pmat_pinned.sh --update # record an improvement (count < baseline) +# bash scripts/check_pmat_pinned.sh --self-test # case table, the resolver's three polarities, the ratchet's both polarities +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PROG=check_pmat_pinned +RE='(^|[^_/])pmat ' # the only sanctioned spelling is "$PMAT", resolved by pmat_bin.sh +SCAN_ROOT="${PIN_SCAN_ROOT:-$ROOT}" # PIN_SCAN_ROOT: a fixture tree, for --self-test +BASELINE="${PIN_BASELINE:-$ROOT/scripts/pmat_unpinned_baseline.txt}" + +scan() { # scan ... -> offending lines (file:line:text) + ( cd "$SCAN_ROOT" && grep -rEn "$RE" "$@" 2>/dev/null | grep -v 'pmat_bin' ) || true +} +baseline_count() { # -> the number on the baseline's first non-comment line, or "" when the file is missing/unparseable + [ -f "$BASELINE" ] || return 0 + sed -nE 's/^[[:space:]]*([0-9]+)[[:space:]]*(#.*)?$/\1/p' "$BASELINE" | head -n1 +} + +if [ "${1:-}" = "--self-test" ]; then + TD=$(mktemp -d "${TMPDIR:-/tmp}/pmatpin.XXXXXX"); trap 'rm -rf "${TD:?}"' EXIT + n=0; red=0 + row() { local want=$1 label=$2 line=$3 got; n=$((n + 1)) + if printf '%s\n' "$line" | grep -Eq "$RE" && ! printf '%s\n' "$line" | grep -q 'pmat_bin'; then got=match; else got=clean; fi + if [ "$got" = "$want" ]; then printf 'ok row %-2s %-5s %s\n' "$n" "$got" "$label"; else printf 'FAIL row %-2s %s (wanted %s) %s\n' "$n" "$got" "$want" "$label"; red=1; fi; } + p='pmat' # assembled so this file does not carry the unpinned spelling itself + row match "bare invocation in command position" " $p comply check --failures-only" + row match "workflow run: line" " run: $p analyze complexity --format json" + row match "inside a substitution" "VER=\$($p --version | head -1)" + row match "a comment that teaches the old spelling" "# run $p analyze satd before pushing" + row match "after a pipe" "cat x | $p query init" + row clean "the sanctioned spelling" " \"\$PMAT\" comply check --failures-only" + row clean "the resolver named on the line" " . scripts/${p}_bin.sh || exit 1" + row clean "a path segment, not a command" " cargo run -p ${p}-agent-mail" + row clean "an identifier with an underscore" " ${p}_ver=\$(\"\$PMAT\" --version)" + row clean "install line with the crate name LAST" " cargo install --version \"\$PMAT_PIN\" --locked $p" + row clean "possessive prose" "# ${p}'s hardcoded-paths analysis is the detector" + # the resolver's three polarities (scripts/pmat_bin.sh) + fake() { printf '#!/usr/bin/env bash\ncase "$1" in --version) echo "%s %s"; exit 0;; esac\necho ok\n' "$p" "$1" > "$2"; chmod +x "$2"; } + pin=$(sed -nE 's/^PMAT_PIN="([0-9.]+)"$/\1/p' "$ROOT/scripts/${p}_bin.sh" | head -1) + fake "$pin" "$TD/at-pin"; fake "3.0.0" "$TD/off-pin" + n=$((n + 1)); if out=$(PMAT_BIN_OVERRIDE="$TD/at-pin" bash -c ". '$ROOT/scripts/${p}_bin.sh' && printf '%s' \"\$PMAT\"") && [ "$out" = "$TD/at-pin" ]; then printf 'ok row %-2s resolver: a binary at the pin (%s) resolves to it\n' "$n" "$pin"; else printf 'FAIL row %-2s resolver did not resolve the pinned binary: %s\n' "$n" "$out"; red=1; fi + n=$((n + 1)); if PMAT_BIN_OVERRIDE="$TD/off-pin" PMAT_BIN_NO_FALLBACK=1 bash -c ". '$ROOT/scripts/${p}_bin.sh'" >/dev/null 2>&1; then printf 'FAIL row %-2s resolver accepted a binary off the pin\n' "$n"; red=1; else printf 'ok row %-2s resolver refuses a binary off the pin (3.0.0 != %s)\n' "$n" "$pin"; fi + n=$((n + 1)); if PMAT_BIN_OVERRIDE="$TD/absent" PMAT_BIN_NO_FALLBACK=1 bash -c ". '$ROOT/scripts/${p}_bin.sh'" >/dev/null 2>&1; then printf 'FAIL row %-2s resolver passed with no binary at all\n' "$n"; red=1; else printf 'ok row %-2s resolver refuses when no binary exists (ENV, not PASS)\n' "$n"; fi + # the resolver sets no shell options in the caller (sourced-lib rule) + n=$((n + 1)); if [ "$(PMAT_BIN_OVERRIDE="$TD/at-pin" bash -c "set +e; . '$ROOT/scripts/${p}_bin.sh'; set -o | grep -E '^(errexit|nounset)' | grep -c on")" = 0 ]; then printf 'ok row %-2s resolver is option-neutral\n' "$n"; else printf 'FAIL row %-2s resolver leaked shell options into the caller\n' "$n"; red=1; fi + # the ratchet's both polarities on a fixture tree (PIN_SCAN_ROOT / PIN_BASELINE) + F="$TD/tree"; mkdir -p "$F/scripts" "$F/.github/workflows" + printf '#!/usr/bin/env bash\n%s analyze satd\n"$PMAT" query x\n' "$p" > "$F/scripts/a.sh" + printf 'jobs:\n x:\n steps:\n - run: %s comply check\n' "$p" > "$F/.github/workflows/w.yml" + ratchet_row() { local want=$1 label=$2 bl=$3 rc=0 out; n=$((n + 1)) + if [ "$bl" = MISSING ]; then rm -f "$TD/bl.txt"; else printf '%s\n' "$bl" > "$TD/bl.txt"; fi + out=$(PIN_SCAN_ROOT="$F" PIN_BASELINE="$TD/bl.txt" bash "${BASH_SOURCE[0]}" 2>&1) || rc=$? + if [ "$rc" = "$want" ]; then printf 'ok row %-2s rc=%s %s\n' "$n" "$rc" "$label"; else printf 'FAIL row %-2s rc=%s (wanted %s) %s\n' "$n" "$rc" "$want" "$label"; printf '%s\n' "$out" | tail -4 | sed 's|^| |'; red=1; fi; } + ratchet_row 0 "ratchet: 2 unpinned lines, baseline 2: PASS" "2 # fixture" + ratchet_row 1 "ratchet: 2 unpinned lines, baseline 1: RED naming the lines (the registered mutation)" "1" + ratchet_row 0 "ratchet: 2 unpinned lines, baseline 3: PASS and an improvement to record" "3" + ratchet_row 2 "ratchet: no baseline file: ENV (exit 2), never a pass" MISSING + ratchet_row 2 "ratchet: an unparseable baseline (INVALID): ENV, never a number" "INVALID" + printf '%s/%s rows\n' "$((n - red))" "$n"; [ "$red" = 0 ] || exit 1; exit 0 +fi + +hits=$(scan scripts/ .github/workflows/) +count=$(printf '%s' "$hits" | grep -c . || true) +if [ "${1:-}" = "--update" ]; then + printf '%s # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at %s; shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c\n' "$count" "$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)" > "$BASELINE" + printf 'baseline set to %s\n' "$count"; exit 0 +fi +bl=$(baseline_count) +if [ -z "$bl" ]; then + printf '%s: ENV - no parseable count in %s (a missing or INVALID baseline is not a number and never a pass)\n' "$PROG" "${BASELINE#"$ROOT"/}" >&2; exit 2 +fi +if [ "$count" -gt "$bl" ]; then + printf 'FAIL %s: unpinned=%s baseline=%s — %s new line(s) invoke or teach the unpinned analyser spelling (the resolver is scripts/pmat_bin.sh):\n' "$PROG" "$count" "$bl" "$((count - bl))" + printf '%s\n' "$hits" | head -60 | sed 's|^| |' + [ "$count" -le 60 ] || printf ' ... and %s more\n' "$((count - 60))" + exit 1 +fi +if [ "$count" -lt "$bl" ]; then printf 'Improved: %s -> %s. Run --update to record it.\n' "$bl" "$count"; fi +if [ "$count" -eq 0 ]; then printf 'PASS %s: unpinned=0 — every analyser reference under scripts/ and .github/workflows/ is resolved by scripts/pmat_bin.sh\n' "$PROG"; else printf 'PASS %s: unpinned=%s baseline=%s (shrink-only; the sweep to 0 is G-10c)\n' "$PROG" "$count" "$bl"; fi +exit 0 diff --git a/scripts/pmat_unpinned_baseline.txt b/scripts/pmat_unpinned_baseline.txt new file mode 100644 index 0000000000..ad4d12a647 --- /dev/null +++ b/scripts/pmat_unpinned_baseline.txt @@ -0,0 +1 @@ +243 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at 0c35f8cfb; shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c From c4f6b618ab051f0fa0cd08a65f970bcaa9aea691 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sun, 6 Sep 2026 16:13:33 +0200 Subject: [PATCH 2/7] =?UTF-8?q?mutant(G-10b):=20one=20bare=20analyser=20re?= =?UTF-8?q?ference=20appended=20to=20scripts/ci=5Ftarget=5Fwatch.sh=20for?= =?UTF-8?q?=20the=20CI=20RED=20leg=20(I3)=20=E2=80=94=20check=5Fpmat=5Fpin?= =?UTF-8?q?ned.sh=20must=20report=20unpinned=3D244=20>=20baseline=20243.?= =?UTF-8?q?=20REVERTED=20in=20the=20next=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pmat-Ticket: PMAT-1063 --- scripts/ci_target_watch.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ci_target_watch.sh b/scripts/ci_target_watch.sh index 0e830ca0ea..384a6d2a90 100755 --- a/scripts/ci_target_watch.sh +++ b/scripts/ci_target_watch.sh @@ -436,3 +436,5 @@ case "${1:-}" in ;; esac exit 0 + +# MUTANT (G-10b RED leg): a bare analyser spelling — run pmat analyze satd before pushing From b6d52fd61afcb61379c64903c1d21a070927fb0d Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sun, 6 Sep 2026 16:58:40 +0200 Subject: [PATCH 3/7] =?UTF-8?q?revert(G-10b):=20the=20mutant=20=E2=80=94?= =?UTF-8?q?=20run=2034038557827=20(job=20101501015300)=20was=20RED=20on=20?= =?UTF-8?q?the=20shrink-only=20step=20(unpinned=3D244=20>=20243);=20receip?= =?UTF-8?q?t=20PMAT-1063=20complete=20(v4:=20the=20receipt=20says=20comple?= =?UTF-8?q?te=20in=20the=20PR=20before=20auto-merge=20is=20armed)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pmat-Ticket: PMAT-1063 --- docs/audits/impl-PMAT-1063-receipt.md | 17 ++++++++++++----- scripts/ci_target_watch.sh | 2 -- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/audits/impl-PMAT-1063-receipt.md b/docs/audits/impl-PMAT-1063-receipt.md index a8da26eb49..9167e63d0f 100644 --- a/docs/audits/impl-PMAT-1063-receipt.md +++ b/docs/audits/impl-PMAT-1063-receipt.md @@ -1,11 +1,11 @@ --- -status: partial +status: complete ticket: PMAT-1063 row: G-10b issue: 3013 epic: 2873 branch: agent/G-10b -pr: opened after PR-A (#3011) merges; re-cut onto main by `git diff agent/G-10..agent/G-10b | git apply` (the base is PR-A's tip, whose tree the squash merge reproduces) +pr: "#3021 — re-cut onto main b0a0a51b2 by rebase --onto; RED leg run 34038557827 (job 101501015300: 'Unpinned analyser references may only fall' FAILED on the mutant c4f6b618a, unpinned=244 > 243); GREEN leg = the run of this revert commit" model: claude-fable-5-1 (orchestrator, direct) tokens_used: orchestrator [U] (not exposed to the orchestrator) wall_clock_s: 900 (basis=session clock, claim to the receipt commit; [U] precision) @@ -39,9 +39,16 @@ K̂ [U] (third receipt of the guard class after PMAT-1059 and PMAT-1062; the cla ## Verification (orchestrator's own runs, `.pr/G-10b-verify.log`) self-test 20/20 · live PASS 243/243 · `check_baseline_ratchets.sh` PASS · `check_guards_are_wired.sh` PASS · `pv validate` valid · `check_shell_lint_ratchet.sh` PASS · `check_no_claim_literals.sh` rc 0. +## Mutation evidence at CI (I3) +| leg | commit | run | job | result | +|---|---|---|---|---| +| RED | c4f6b618a (one bare `pmat analyze satd` comment in `scripts/ci_target_watch.sh`) | 34038557827 | 101501015300 | guard-runner-labels FAILED at "Unpinned analyser references may only fall (shrink-only, G-10b PMAT-1063)" | +| GREEN | this commit (the revert) | the run of this commit (cited in the PR body) | — | expected PASS `unpinned=243 baseline=243` | + ## Gaps -- G-10c (PMAT-1064, #3014): the sweep 243 → 0 from `agent/G-10-full` 5f8f28f19, baseline to 0. -- The pre-PR 3-lane review (review-only row) and the re-cut onto main after #3011. +- G-10c (PMAT-1064, #3014): the sweep 243 → 1 is on `agent/G-10c` (the last reference is `render_dag.py`'s rendered header, the orchestrator's); baseline to 0 after the header rename. +- The pre-PR review lanes were not run on this diff (review-only row; the case table and the CI mutation stand as the evidence) — recorded as a gap, not a pass. +- Auto-merge is armed only after G-11 (#3020) merges: one orchestrator-armed code PR until the write-set guard is in `ci / gate` (driver v4 WIP rule). ## Verdict -PARTIAL — awaiting PR-A's merge, the re-cut, the review lanes, `ci / gate` + `workspace-test`, and the merge. +DONE on the branch: every A_i re-run by the orchestrator, the mutation RED at CI and reverted; complete = this receipt ∧ the merge of #3021. diff --git a/scripts/ci_target_watch.sh b/scripts/ci_target_watch.sh index 384a6d2a90..0e830ca0ea 100755 --- a/scripts/ci_target_watch.sh +++ b/scripts/ci_target_watch.sh @@ -436,5 +436,3 @@ case "${1:-}" in ;; esac exit 0 - -# MUTANT (G-10b RED leg): a bare analyser spelling — run pmat analyze satd before pushing From 72aefaa55fb75f17423f634d024178a3427c0890 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Mon, 7 Sep 2026 09:49:38 +0200 Subject: [PATCH 4/7] =?UTF-8?q?chore(G-10b):=20re-stamp=20scripts/pmat=5Fu?= =?UTF-8?q?npinned=5Fbaseline.txt=20on=20the=20merged=20tree=20=E2=80=94?= =?UTF-8?q?=20main=20gained=20analyser=20mentions=20(#3020,=20#3030)=20aft?= =?UTF-8?q?er=20the=20first=20stamp;=20the=20baseline=20is=20introduced=20?= =?UTF-8?q?by=20this=20PR,=20so=20its=20initial=20count=20is=20the=20merge?= =?UTF-8?q?d=20tree's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pmat-Ticket: PMAT-1063 --- .pr/G-10b-old-tip | 1 + .pr/G-10b-pr-body.md | 14 ++++++++++++++ scripts/pmat_unpinned_baseline.txt | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .pr/G-10b-old-tip create mode 100644 .pr/G-10b-pr-body.md diff --git a/.pr/G-10b-old-tip b/.pr/G-10b-old-tip new file mode 100644 index 0000000000..57ce212d45 --- /dev/null +++ b/.pr/G-10b-old-tip @@ -0,0 +1 @@ +21b11c7e298b36e577aa71528be6ada36f1021a9 diff --git a/.pr/G-10b-pr-body.md b/.pr/G-10b-pr-body.md new file mode 100644 index 0000000000..315b28486b --- /dev/null +++ b/.pr/G-10b-pr-body.md @@ -0,0 +1,14 @@ +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 b0a0a51b2). + +**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 against `scripts/pmat_unpinned_baseline.txt`. The baseline is **243, measured by the guard itself at the commit it names** (`--update` writes 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 or `INVALID` baseline is ENV (exit 2), never a pass. Kind-table entry (`count`) in `check_baseline_ratchets.sh`; two CI steps in `guard-runner-labels` (case table, then live); contract `apr-pinned-analyser-ratchet-v1` 1.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.** +| leg | commit | what | run | +|---|---|---|---| +| RED | `c4f6b618a` mutant: one bare `pmat analyze satd` comment appended to `scripts/ci_target_watch.sh` | `FAIL check_pmat_pinned: unpinned=244 baseline=243 — 1 new line(s) …` naming the line | _run id filled in after CI reports_ | +| GREEN | the revert (next commit) | `PASS … unpinned=243 baseline=243` | _run id filled in_ | + +Case 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 · `INVALID` ENV 2). + +**Acceptance (re-run by the orchestrator on the re-cut base b0a0a51b2 — `.pr/G-10b-verify.log`):** self-test 20/20 · live `unpinned=243 baseline=243` · `check_baseline_ratchets.sh` PASS · `check_guards_are_wired.sh` PASS · `pv validate` valid · `check_shell_lint_ratchet.sh` PASS · `check_no_claim_literals.sh` rc 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. diff --git a/scripts/pmat_unpinned_baseline.txt b/scripts/pmat_unpinned_baseline.txt index ad4d12a647..7379885354 100644 --- a/scripts/pmat_unpinned_baseline.txt +++ b/scripts/pmat_unpinned_baseline.txt @@ -1 +1 @@ -243 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at 0c35f8cfb; shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c +252 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at a9fe67cc7; shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c From f367fd52477a2bd14e6d4b4a1b65a883312faa3a Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sun, 13 Sep 2026 11:54:42 +0200 Subject: [PATCH 5/7] merge fix: the shrink-only baseline is installed at the merged tree's 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 --- scripts/pmat_unpinned_baseline.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pmat_unpinned_baseline.txt b/scripts/pmat_unpinned_baseline.txt index da7effb574..d40ebbbbc8 100644 --- a/scripts/pmat_unpinned_baseline.txt +++ b/scripts/pmat_unpinned_baseline.txt @@ -1 +1 @@ -288 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at a9fe67cc7; shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c +288 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at d56a4ecd9 (install value on the merged tree; main gained 36 unpinned references between the branch fork and the merge); shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c From d0eb672c3bdecadaf501765642bb0baccb6c775a Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Sun, 13 Sep 2026 12:31:33 +0200 Subject: [PATCH 6/7] docs(G-10b): PP-066 spec DAG block re-rendered on the merged tree (G-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 --- docs/specifications/PP-066-release-spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specifications/PP-066-release-spec.md b/docs/specifications/PP-066-release-spec.md index 8b75b3cf51..a4034a4773 100644 --- a/docs/specifications/PP-066-release-spec.md +++ b/docs/specifications/PP-066-release-spec.md @@ -387,7 +387,7 @@ _Rendered from `docs/specifications/pp-066-dag.yaml` (epic #2873, 105 rows). Edi | G-4 | the obligation DAG as data with invariants in CI: scripts/check_dag_invariants.sh + scripts/lib/dag_invariants.py over docs/specifications/pp-066-dag.yaml (0 cycles, >=6 days slack blocker->blockee, per-host queue ordered by expiry, owner present, exactly one expiry form, rows past expiry listed); scripts/render_dag.py renders §5/§6 tables byte-identical; pmat rule owed upstream [U] | — | — | any | 2026-09-19 | spec-owner | review-only | #2902 | PMAT-987 | complete | | G-10 | check_hardcoded_paths.sh --full-if-capable arms itself at pmat >= 3.32.0 and the fleet runs 3.31.0: under pmat 3.37.0 pristine main reports 317 shipped findings against a baseline of 277, so the day the fleet upgrades every PR goes red (found 2026-09-05 running the guard-runner-labels steps locally; the guard must name the pmat version it armed under and the baseline must carry a measured, versioned basis) | — | — | any | 2026-09-06 | spec-owner | review-only | #2999 | PMAT-1059 | complete | | G-11 | shared-file write contention: a row PR never writes pp-066-dag.yaml, roadmap.yaml, the rendered spec block or a README count line (check_row_pr_write_set.sh); DAG status DERIVED from the receipt (dag_status.py; render_dag.py; dag_invariants.py D7); README counts a ratchet (lag allowed, overstatement RED, --exact for the orchestrator); one orchestrator docs commit per merge writes the shared files | G-10 | — | any | 2026-09-12 | spec-owner | review-only | #3012 | PMAT-1062 | complete | -| G-10b | analyser pin guard: scripts/check_pmat_pinned.sh — every analyser reference under scripts/ and .github/workflows/ resolves through scripts/pmat_bin.sh; scripts/pmat_unpinned_baseline.txt 281 shrink-only; wired case table then live; extends apr-pinned-analyser-ratchet-v1 (PIN-OB-005) | G-10, G-11 | — | any | 2026-09-19 | spec-owner | review-only | #3013 | PMAT-1063 | open | +| G-10b | analyser pin guard: scripts/check_pmat_pinned.sh — every analyser reference under scripts/ and .github/workflows/ resolves through scripts/pmat_bin.sh; scripts/pmat_unpinned_baseline.txt 281 shrink-only; wired case table then live; extends apr-pinned-analyser-ratchet-v1 (PIN-OB-005) | G-10, G-11 | — | any | 2026-09-19 | spec-owner | review-only | #3013 | PMAT-1063 | complete | | G-10c | analyser reference sweep: the 281 unpinned references under scripts/ and .github/workflows/ resolve through scripts/pmat_bin.sh (presence probes become ENV failures; dogfood/verifier_pin/ci.sh take the pin; workflow installs at the pin); baseline to 0 | G-10b, G-11 | — | any | 2026-09-26 | spec-owner | review-only | #3014 | PMAT-1064 | open | | G-11b | one-call state (scripts/pp066_state.sh: head row, complete receipts, PR states, main reds with ticket ids, U-1 poll), the session docs commit (scripts/session_docs_commit.sh), the .pr//accept.sh convention in pp-066-plan.md, make fleet-verify ROW= (forjar/make only) | G-10, G-11 | — | any | 2026-09-19 | spec-owner | review-only | #3018 | PMAT-1066 | open | | SPEC-2.0 | spec v2.0 (driver v5): the 18-row scope with each cut row naming the claim it protected; the claims ratchet (README, notes, CLI output carry no performance, training-parity or proof-count number; check_no_claim_literals.sh + check_perf_claims_cite_receipts.sh over notes and README in ci / gate, mutation: one number → RED); scripts/release_criteria.sh C0 C4 C5 C6 C7 C8 C9 C11 C13 C14 one exit-coded command each (C1 C2 C3 C10 C12 → 0.67 with their tracks); C8 via scripts/run_clean_room.sh requiring ../infra; C11 = 15 fixtures; refusal codes from error.rs; REG-15 in R-0; §4 credited-first one reading; rescope quorum (three adversarial agy lanes over README/notes/CLI claims) | G-11 | — | any | 2026-09-19 | spec-owner | rescope quorum: three adversarial agy lanes | #3023 | PMAT-1083 | open | From 3ca9c135cc34dd15abfe6f90f0361e4fb926cd3e Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Mon, 14 Sep 2026 22:53:16 +0200 Subject: [PATCH 7/7] =?UTF-8?q?fix(baseline):=20pmat=5Funpinned=5Fbaseline?= =?UTF-8?q?.txt=20declares=20no=20instrument=20=E2=80=94=20guard-cargo=20f?= =?UTF-8?q?ails=20on=20the=20header,=20before=20it=20ever=20reads=20the=20?= =?UTF-8?q?count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- scripts/pmat_unpinned_baseline.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/pmat_unpinned_baseline.txt b/scripts/pmat_unpinned_baseline.txt index d40ebbbbc8..03fe8105c2 100644 --- a/scripts/pmat_unpinned_baseline.txt +++ b/scripts/pmat_unpinned_baseline.txt @@ -1 +1,2 @@ +# tool_version=none (measured by grep over scripts/ and .github/workflows/; not a versioned analyser -- the count is a property of the TREE, and no pmat/bashrs release changes it, so pinning an instrument here would assert a dependency that does not exist) 288 # unpinned analyser references under scripts/ and .github/workflows/; basis: bash scripts/check_pmat_pinned.sh (grep -rEn "(^|[^_/])pmat " scripts/ .github/workflows/ | grep -v pmat_bin | wc -l) at d56a4ecd9 (install value on the merged tree; main gained 36 unpinned references between the branch fork and the merge); shrink-only (check_baseline_ratchets.sh kind count); G-10b PMAT-1063, sweep to 0 is G-10c