Skip to content

feat(parity): L0-1a — every supported model computes the same function on GPU as on CPU or the GPU refuses it (manifest, C14, REG-15 admission, effective-config parity) - #3026

Merged
noahgift merged 34 commits into
mainfrom
agent/L0-1
Sep 8, 2026

Conversation

@noahgift

@noahgift noahgift commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Ticket

#2971 (alfredodeza's report; P0; DAG row L0-1a, ticket PMAT-1065, epic #2873). #3017 was closed as its duplicate.

Claim

Claim 2 of 0.66, the bounded half: every model in evidence/models/supported.yaml (derived, never typed) is measured over ≥ 64 positions against a threshold that carries its basis, and a model that fails is refused by the GPU with its reason printed — never silently run on the CPU under a forced request. The op that diverges on Qwen2.5-1.5B and its fix are L0-1b.

RED test

  • The RED-first record, before any kernel edit: c977cad58 — apr parity on lambda (RTX 4090, the 0.65.2 cuda install c642576eecb62daa), 78 positions: 1.5B min cosine 0.9508 at position 0 (token 785), 7B 0.9986; n=5 repeats are bit-for-bit identical (d2a566642).
  • The sentinel gate: d56f65430 — sentinel_1p5b_on_lambda_is_red_under_the_horizon_rule / sentinel_7b_on_lambda_is_green_under_the_horizon_rule (lib tests, ride workspace-test).
  • The must-RED twin: tests/fixtures/parity/defective/one-position-at-0.5.json (C14 case-table row 3).

Acceptance (.pr/L0-1/accept.sh, orchestrator's run, .pr/L0-1/accept.log)

derive_model_manifest.sh --self-test        rc=0   (6/6)
derive_model_manifest.sh --check            rc=0   (18 models)
check_model_parity.sh --self-test           rc=0   (6/6)
--judge 7B record                           rc=0   PASS min 0.9986
--judge 1.5B record                         rc=1   as required (RED)
--judge one-position-at-0.5 twin            rc=1   as required (RED)
cargo test -p apr-cli --test reg15_admission        rc=0 (7 passed)
cargo test -p apr-cli --lib sentinel_tests          rc=0 (3 passed)
cargo test -p aprender-serve --lib parity_report    rc=0
pv validate contracts/apr-gpu-cpu-parity-v1.yaml    rc=0 (valid)
--manifest: the fleet-verify leg on lambda/gx10 (not this host)

cargo check -p aprender-serve -p apr-cli --features cuda on lambda (CUDA 12.8): clean at 35c330c.

Mutation (I3)

round commit expected RED run
1 a5db15200 — a hand-typed manifest entry + min_cosine: 0.90 guard-runner-labels: "The manifest equals its derivation" FAILS; workspace-test: sentinel_1p5b_on_lambda_is_red FAILS run id after CI
2 (next) min_positions: 1 guard-runner-labels: C14 case-table row 4 FAILS run id after CI
GREEN the reverts 6/6 · 6/6 · 3 sentinels run id after CI

Contract

contracts/apr-gpu-cpu-parity-v1.yaml — kind: pattern; PAR-OB-001..003 ↔ PAR-F-001..003. pv validate (via scripts/pv_bin.sh): 0 error(s), 0 warning(s) — Contract is valid.

Quorum

review-only row: one agy lane on this diff — verdict recorded in .pr/L0-1/quorum.md and the receipt before arming. The L0-1 root-cause quorum (three lanes, one family — a recorded gap) refuted the fused-FFN hypothesis on default config; L0-1b answers the rest by measurement.

Receipt

docs/audits/impl-PMAT-1065-receipt.md (v6 DONE-IF ledger: (i) admission level ✓, (ii) ✓, (iii) blocked on R-0a, (iv) one pair measured, (v) ✓, (vi) pending G-11b, (vii) ✓).

Writes

scripts/derive_model_manifest.sh, scripts/check_model_parity.sh, scripts/dogfood.sh (C14 rows), evidence/models/supported.yaml, evidence/parity/{thresholds.yaml,l0-1/**}, evidence/dogfood/0.65.2/{lambda,gx10}.json (validity relabel), tests/fixtures/parity/defective/**, crates/apr-cli/src/commands/{parity_admission.rs,chat_generate_session_02.rs,comparison.rs,mod.rs}, crates/apr-cli/src/error.rs, crates/apr-cli/tests/reg15_admission.rs, crates/aprender-serve/src/gguf/cuda/{mod.rs,mod_parity_gate.rs}, crates/aprender-serve/src/api/effective_config.rs, contracts/apr-gpu-cpu-parity-v1.yaml, .github/workflows/ci.yml (three guard-runner-labels steps), .pr/L0-1/accept.sh, the receipt. No DAG, roadmap, README or spec edit.

@noahgift noahgift added pp-066 PP-066 (0.66) DAG row inst:A PP-066 instance claim (I14): inst:A L0-1a PP-066 DAG row L0-1a (#2971) labels Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026 •

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3026 head=d4e1f416ceba81f020004ff345315806c173fc06 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

… binary c642576eecb62daa) — apr parity --json over 78 positions: qwen2.5-coder-1.5b min cosine 0.9508 at position 0 (|Δlogit|max 11.97), qwen2.5-coder-7b min 0.9986; under the horizon rule (min over >= 64 positions >= 0.98 [U]) 1.5B RED / 7B GREEN before any kernel edit (#3017)

Pmat-Ticket: PMAT-1065
…est.sh -> evidence/models/supported.yaml, 18 models, every entry cites file:line; --check refuses a hand-typed entry; 6-row case table); C14 scripts/check_model_parity.sh --manifest (apr parity over >= 64 positions per manifest model, min cosine vs evidence/parity/thresholds.yaml — 0.98 [U] until item 5 measures it; UNMEASURED reported, README-cited unmeasured RED; SKIP_PARITY_GATE is an override that prints and refuses; 6-row case table over the lambda records: 1.5B RED, 7B GREEN, must-RED twin, < 64 positions refused) (#3017, PMAT-1065)

Pmat-Ticket: PMAT-1065
…tree already does (cited), the five-whys hypothesis for the N-lane quorum; threshold basis = PARITY_GATE_COSINE_MIN (mod.rs:803), itself [U]

Pmat-Ticket: PMAT-1065
…S (record corrected); the fused Q8_1 FFN is off by default (gpu_profile.rs:238), so the five-whys step 3 is refuted on default config and the 1.5B/7B asymmetry on the unfused path is the open question; the surviving zero-risk fix is REG-15's selected: line; lanes were one family (gap)

Pmat-Ticket: PMAT-1065
…s its duplicate under driver v5.1) — receipt, record, scripts and manifest cite it

Pmat-Ticket: PMAT-1065
…izon gate, the derived manifest, REG-15 admission); ci.yml guard-runner-labels runs the manifest case table + --check and C14's case table; every 0.65.2 (1.5B, cuda) dogfood receipt relabelled INVALID-CORRECTNESS citing #2971 (PMAT-1065)

Pmat-Ticket: PMAT-1065
… a forced backend never downgrades — CliError::ParityFailed with its own code read at test time; unforced prints selected: cpu (reason: parity FAILED …); PASS prints selected: cuda …), override_line() for SKIP_PARITY_GATE, parse_gate_error over the load-time gate's message; 7 hermetic tests (worker-written, orchestrator-verified) (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…oad failure: a parity-gate failure prints selected: cpu (reason: parity FAILED cosine=… threshold=…) when unforced (apr chat today) and refuses when forced (R-0b's --backend); apr compare prints the override line only when the user set SKIP_PARITY_GATE (diff-benchmark's silent set_var waits for its own decomposition — the file is over the complexity gate) (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…pare override), the worker ledger, the cuda type-check on lambda, and the gaps (diff-benchmark decomposition, effective-config block, apr devices --model, dogfood C14, threshold measurement, gx10)

Pmat-Ticket: PMAT-1065
…lsifier (FAILs the run if it ever passes), then check_model_parity.sh --manifest over the built binary: PASS when measured, FAIL on a measured failure or an override, REPORT with the reason on a host that holds no manifest model (the pre-publish proof is make fleet-verify ROW=release on lambda and gx10) (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…e lambda 1.5B record must be RED and the 7B record GREEN under evidence/parity/thresholds.yaml (read by regex, no default), both over >= 64 positions; the full manifest runs only in dogfood, the release and R-8 (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…ambda) sits at min cosine 0.9986 in all five runs and the known-bad pair (1.5B@lambda) at 0.9508 in all five (both deterministic, stdev 0.0); 0.98 lies between; [U] until the gx10 pair (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…ositions,threshold,basis} — the load-time gate returns the cosine it measured, the CUDA model carries a ParityGateRecord (PASS | skipped under the SKIP_PARITY_GATE override | not-run) set by one admission helper, the report is never absent (not-run on cpu residency or a non-cuda build); shape test (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…cs under --features cuda)

Pmat-Ticket: PMAT-1065
…st targets (GREEN on a GPU-less host; the live manifest is the fleet-verify leg); receipt carries the v6 DONE-IF ledger

Pmat-Ticket: PMAT-1065
…itations moved); accept.sh GREEN on this host (10/10 legs)

Pmat-Ticket: PMAT-1065
…rive_model_manifest.sh --check must go RED in guard-runner-labels) and min_cosine 0.90 (the 1.5B sentinel must PASS and sentinel_1p5b_on_lambda_is_red must FAIL in workspace-test). REVERTED after the RED run ids are captured (I3)

Pmat-Ticket: PMAT-1065
… row 7); UNMEASURED is a per-host REPORT and a host that measured nothing is not a pass (the fleet-level rule is R-5's); aliases of one file are measured once (longest name first); the case table judges under its own threshold fixture and writes the tracked twin only when absent; PAR-F-003's test is the admission level (R-0b owns backend_refusal_case_table); the manifest's admission rule is stated (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…one() so the silent SKIP_PARITY_GATE set_var becomes the printed override (the file was over the complexity gate) (#2971, PMAT-1065)

Pmat-Ticket: PMAT-1065
…to R-0b, two documented) in the receipt

Pmat-Ticket: PMAT-1065
…ED line (SC2075); the round-1 guard job stopped at the bashrs step before reaching the manifest mutation

Pmat-Ticket: PMAT-1065
…ade STALE (diff_benchmark_report.rs::run, error.rs::resolve_model_path, gguf/cuda/mod.rs::preload_and_verify are now under both thresholds) — the shrink-only ratchet demands the delete

Pmat-Ticket: PMAT-1065
…t alias only resolves without --features cuda (cuda build of apr-cli was RED: E0107)

Pmat-Ticket: PMAT-1065
… new I8 sentinel test (thresholds_demand_at_least_sixty_four_positions) must turn workspace-test RED; round-1 mutants (typed manifest entry, min_cosine 0.90) reverted — their RED is run 34049865821 (guard: manifest derivation step; workspace-test: sentinel_1p5b test)

Pmat-Ticket: PMAT-1065
…sis I8); the I8 threshold test stays. Round-2 RED: run 34052968770 (workspace-test: thresholds_demand_at_least_sixty_four_positions, 3/3 tries); round-1 RED: run 34049865821 (guard: manifest derivation step; workspace-test: sentinel_1p5b)

Pmat-Ticket: PMAT-1065
noahgift and others added 2 commits September 7, 2026 14:12
…e type is already must_use (clippy double_must_use under deny(clippy::all))

Pmat-Ticket: PMAT-1065
…s into guard-tree

BSE-001 M2 split the old guard-runner-labels job into guard-tree (cargo-free,
dispatched by scripts/guard_tree.sh) and guard-cargo. The four L0-1a steps
(derive_model_manifest.sh --self-test/--check, check_model_parity.sh --self-test)
are cargo-free by `grep -cE '(^|[^a-z_-])cargo '` == 0 on both scripts, so they
re-anchor into guard-tree after check_row_pr_write_set.sh, the same neighbour they
had before the split.

Also drops unfused_test.json — a 0-byte file left at the repo root by the round-2
mutation run (193d626) and committed by accident.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
…hold [U] lifted, the divergence is model-selected not device-selected

Card items (iv) and (vi). `make fleet-verify` does not exist (BSE's G-11b), so the
measurement was taken rather than waited on.

gx10-a5b5, aarch64, NVIDIA GB10 (sm_121), driver 590.48.01, apr 0.65.2 (c04eda8)
sha256 21d182d69505159c. Mechanism engaged, not intended: every run's stderr carries
`[GH-480] Patched N backward branch(es) for sm_121 JIT workaround`, a path lambda's
sm_89 build does not have.

  model                                positions<0.98  min cosine  max|dlogit|@0  verdict
  qwen2.5-coder-1.5b-instruct-q4_k_m         1           0.950611     12.0087       RED
  qwen2.5-coder-7b-instruct-q4_k_m           0           0.998465      0.8032       GREEN

n=5 per cell, stdev 0.0 in all four cells across both hosts.

THE FINDING. Two GPU generations, two ISAs, two host architectures, one running a JIT
workaround the other does not have — and the known-bad pair lands within 2.2e-4 of the
same cosine (lambda 0.950827, gx10 0.950611) while the known-good pair on the same
silicon is 0.9985+. The divergence is selected by the MODEL (hidden 1536/heads 12/kv 2
/GQA 6 vs 3584/28/4/7), not by the device. Every "an sm_89 kernel is wrong" or "the
sm_121 JIT patch corrupts a branch" hypothesis is refuted before L0-1b dispatches a
lane. It is asserted, not merely written down: the_two_hosts_agree_on_each_model_to_
within_a_thousandth goes RED if that stops holding.

The driver's 0.9418 / 5.38 were guessed to be gx10's numbers by all three earlier lanes.
They are not: gx10 measures 0.950611 / 12.0087. The source report's provenance stays [U];
the defect it names is confirmed on both required hosts.

Item (iv) CLOSED, the [U] on evidence/parity/thresholds.yaml lifted: 0.98 now separates
two measured known-good pairs from two measured known-bad pairs with 0.0185 of margin
under the lower good floor, asserted at >= 0.01 on each side, because a gate that passes
with no headroom is a finding (0.63.0 hansei) and a near-tie once inverted a whole
diagnosis (#2359).

Falsifiers added: check_model_parity.sh --self-test 7 -> 9 rows (both polarities on BOTH
hosts); sentinel_tests 4 -> 8 (1.5B RED / 7B GREEN per host, cross-host agreement,
threshold margin on both sides).

Refs #2971, PMAT-1065

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
noahgift and others added 2 commits September 8, 2026 14:18
…ies is one file

check_hardcoded_paths.sh refused +25 shipped machine-specific paths — 24 of them
`"model": "/home/noah/models/…"` inside the apr parity --json output, one a hardcoded
cargo in accept.sh. That guard has no allowlist by design, and its own header sets the
precedent for evidence: redacting a host path "would delete the evidence and fabricate a
cleaner history".

Neither redaction nor an exemption was needed, because the path was never part of the
measurement.

  * Every canonical record RE-TAKEN with the model named relatively (`cd ~/models &&
    apr parity ./<model>.gguf …`), same pinned binaries (lambda c642576eecb62daa, gx10
    21d182d69505159c), same prompt. The metrics array and every other key are
    BYTE-IDENTICAL to the absolute-path run; only `model` differs — compared field by
    field, not asserted.
  * n5/ drops its ten JSONs per host. All five runs of each model were byte-identical to
    each other AND to the canonical record — stdev 0 understates it, the whole file was
    the same file — so five copies carried nothing their sha256 does not. DETERMINISM.md
    records the hashes and the distinct-count; runs.log keeps the ten exit codes.
  * accept.sh: CARGO="${CARGO:-$HOME/.cargo/bin/cargo}" — still never a bare `cargo`
    (a shell function of that name overrides CARGO_TARGET_DIR), no longer a machine path.

Also `make readme-sync`: the CONTRACT_COUNT block stated 1815 and the merge tree carries
1816 (main's contracts/patterns/ratchet-verdict-d2-v1.yaml plus this row's
apr-gpu-cpu-parity-v1.yaml). That block is GENERATED, so it is an equality, not a ratchet.

check_hardcoded_paths.sh --full: delta +0. sentinel_tests 8/8. accept.sh 12/12.

Refs #2971, PMAT-1065

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
…achine path

check_hardcoded_paths.sh refused +25 shipped paths; the resolution was neither a
redaction nor an exemption — the records were re-taken with the model named relatively
(byte-identical metrics, same pinned binaries) and the n=5 series collapsed to its
sha256, because all five runs were byte-identical to the canonical record. delta +0.

Refs #2971, PMAT-1065

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
@noahgift

noahgift commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Card ledger, 2026-09-08 — the row is complete except one item that is another row's command

item state
(i) forced --gpu + parity FAIL ⇒ refusal code from error.rs, never selected: cpu ✓ forced_backend_over_a_failed_parity_gate_refuses_with_the_code_from_error_rs (7/7 in reg15_admission)
(ii) /v1/effective-config carries parity: {status, cosine, positions, threshold, basis} ✓ parity_report_carries_the_five_keys_when_no_gate_ran
(iii) apr devices --model <gguf> serviceable column blocked on R-0a (apr devices, #3004, inst:B) — A does not implement B's command. The DATA that column renders is this row's and has landed: the manifest, the per-host records, and check_model_parity.sh --judge
(iv) threshold basis from n ≥ 5 per known-good pair ✓ CLOSED, [U] lifted — see below
(v) diff-benchmark decomposition recorded ✓
(vi) make fleet-verify ROW=L0-1 on lambda and gx10 the target does not exist (BSE's G-11b) and was not waited on: both hosts measured, records attached
(vii) PR-time gate = two sentinels ✓ now two per host — 1.5B RED / 7B GREEN on lambda AND gx10, plus cross-host agreement and threshold-margin rows (8 tests)
(viii) every 0.65.2 (1.5B, cuda) receipt relabelled INVALID-CORRECTNESS ✓

The second host, and what it settles

gx10-a5b5, aarch64, NVIDIA GB10 (sm_121), driver 590.48.01; apr 0.65.2 (c04eda87) built from the tip of main, sha256 21d182d69505159c. Mechanism proved, not intended: every run's stderr carries [GH-480] Patched N backward branch(es) for sm_121 JIT workaround, a path lambda's sm_89 build does not have.

model lambda / RTX 4090 / sm_89 / x86_64 gx10 / GB10 / sm_121 / aarch64 Δ
1.5B min cosine 0.950827 0.950611 2.2e-4
7B min cosine 0.998607 0.998465 1.4e-4

n=5 per cell, stdev 0.0 in all four. The divergence is selected by the MODEL, not by the device — which refutes every "an sm_89 kernel is wrong" / "the sm_121 JIT corrupts a branch" hypothesis before L0-1b dispatches a lane, and is asserted by the_two_hosts_agree_on_each_model_to_within_a_thousandth rather than merely written down. (L0-1b has since named the op — a crushed Q8_K activation block, with the CPU reference the inaccurate side — and the gx10 twin of the fix reads 1.5B 0.999583 / 7B 0.999782, first_divergence none on both.)

Item (iv) is closed by the same measurement: 0.98 now separates two known-good pairs from two known-bad pairs with 0.0185 of margin under the lower good floor, asserted at ≥ 0.01 on each side because a gate that passes with no headroom is a finding.

Two guards, answered rather than routed around

check_hardcoded_paths.sh refused +25 shipped machine paths (24 of them "model": "/home/noah/models/…" inside the apr parity --json output). That guard has no allowlist by design, and its own header says redacting a host path "would delete the evidence and fabricate a cleaner history" — so neither a redaction nor an exemption was taken. The records were re-taken with the model named relatively (cd ~/models && apr parity ./<model>.gguf …) on the same pinned binaries: the metrics array and every other key are byte-identical, only model differs, compared field by field. And n5/ collapsed from ten JSONs per host to DETERMINISM.md, because all five runs were byte-identical to each other and to the canonical record. --full → delta +0, every number unchanged, sentinels 8/8.

make readme-sync: the generated CONTRACT_COUNT block stated 1815 against a merge tree carrying 1816 — a generated number is an equality, not a ratchet.

Receipt: docs/audits/impl-PMAT-1065-receipt.md.

…refusal proof never ran on main

Found by asking of this row what R-0b's regenerated tree-reader ledger asked of its
sibling: which of this PR's test targets does a workflow actually execute?

  $ grep -rc reg15_admission .github/workflows/   ->  no match

crates/apr-cli/tests/reg15_admission.rs is card item (i)'s falsifier — seven hermetic
tests, among them forced_backend_over_a_failed_parity_gate_refuses_with_the_code_from_
error_rs, which is the whole claim of REG-15: a forced backend never silently downgrades.
BSE-17's quick tier runs it on a PR that touches apr-cli, but the full tier and every push
to main run `--lib` plus ONE explicit list of --test targets, and it was not on that list.
Merged as it stood, nothing on main would ever have executed it again — the exact trap
recorded for the beats line (a new test target is theater until it is added there).

It joins that line beside cli_commands. Note the scale of the standing problem while
passing: apr-cli carries 64 integration targets and the line names 7 of them (#2341).

Refs #2971, PMAT-1065

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
@noahgift
noahgift added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 74e3b25 Sep 8, 2026
15 of 17 checks passed
@noahgift
noahgift deleted the agent/L0-1 branch September 8, 2026 18:44
noahgift added a commit that referenced this pull request Sep 8, 2026
…on of both sides

#3026 merged, so main now carries the three L0-1a manifest steps and this branch carries
the release_criteria self-test step. Both are additive steps in the same guard-tree job and
neither replaces the other; the resolution is the union, verified by counting each step
rather than by eye.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
noahgift added a commit that referenced this pull request Sep 8, 2026
…66's single claim, measured

L0-1a landed (#3026), so scripts/check_model_parity.sh is on main and C14 stopped being ENV.
Run verbatim as the criterion defines it, on lambda, with a cuda apr built from agent/L0-1b
(sha256 776cbbdb4306b5d8 — the tree carrying L0-1b's fix):

    PASS qwen2.5-coder-1.5b-instruct: 78 positions, min cosine 0.9998 at position 36
    PASS qwen2.5-coder-0.5b-instruct: 78 positions, min cosine 0.9996 at position 0
    PASS qwen2.5-coder-7b-instruct:   78 positions, min cosine 0.9996 at position 22
    C14: measured=3 rc=0

The first row is the model #2971 is about. It read 0.950827 on this host before the fix and
reads 0.9998 after, against a threshold whose basis is two measured known-good pairs across
two GPU architectures.

Fourteen models are UNMEASURED because this host does not hold them, and the script REPORTS
rather than fails — no single host holds every model the README names, and the fleet-level
rule belongs to the release.

From the outside, `apr chat --gpu` on the 1.5B now prints
`[GGUF CUDA: NVIDIA GeForce RTX 4090 …]` and answers correctly, with no `falling back to
CPU` anywhere in the output.

ONE GAP, NAMED RATHER THAN GLOSSED: the driver's Resolved criterion asks for
`selected: cuda … parity: PASS` on the success path, and that line does not appear —
REG-15's admission line is printed by `on_cuda_load_error`, so it is emitted only when
something goes wrong. Reporting the selection only on failure is weaker than the criterion
asks. That belongs to claim (1), which D-14 moved to 0.67 with R-0b; 0.66 makes claim (2),
and claim (2) is what the table above measures.

Refs #2971, #2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
noahgift added a commit that referenced this pull request Sep 8, 2026
… folded into the one with 11 falsifiers

Epic #3058 §B3: do not create contracts/apr-gpu-cpu-parity-v1.yaml; extend
contracts/apr-cpu-vs-gpu-output-parity-v1.yaml, which already exists with 11 falsification
tests. L0-1a shipped the duplicate anyway (#3026, merged), so this removes it.

Verified before acting, not taken on faith: the existing contract carries 11 FALSIFY ids and
58 KB of history; mine carried 3 obligations and 3 falsifiers in 10 KB.

THE TWO ARE THE SAME ARGUMENT SPLIT IN HALF, which is the real cost B3 names. The existing
contract already records the #1864 five-whys whose root cause is "the gate's domain was too
narrow (single-step instead of multi-step)" — and L0-1a's >= 64-position horizon rule is the
answer to exactly that. Half the reasoning sat in each file.

Ported as FALSIFY-CPU-GPU-012/013/014, each with the mutation that turns it RED:
  012  the manifest is derived, never typed        derive_model_manifest.sh --self-test  (6/6)
  013  the domain is >= 64 positions, both         check_model_parity.sh --self-test     (9/9)
       polarities on BOTH required GPU hosts
  014  a forced backend never downgrades           cargo test --test reg15_admission     (7/7)
All three re-run here, green. `pv validate`: 0 errors, 14 falsifiers.

THE STALE ANCHOR, AND A SHARPER VERSION OF B3's POINT. B3 is right that the contract cites
`mod.rs:268-279` for the SKIP_PARITY_GATE bypass and that :268-279 is something else (a doc
comment about qtype resolution). But B3's proposed replacement, `:333`/`:349`, had ALREADY
DRIFTED by the time I read it — L0-1a and L0-1b moved the bypass to :390/:406 on this branch.
The fix for a drifting line anchor cannot be a different line anchor, so both live citations
now anchor on the SYMBOL (`grep for the literal SKIP_PARITY_GATE`) and say why. The 1.1.0
changelog entry keeps its line numbers: it is history, and history is allowed to be stale.

References repointed in the DAG (4), the spec table, parity_admission.rs, reg15_admission.rs
and .pr/L0-1/accept.sh. DAG invariants PASS; §5.0 re-rendered byte-identical.

Refs #3058, #2971, #2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH
noahgift added a commit that referenced this pull request Sep 9, 2026
#3026 (L0-1a) landed after v0.1 was written and shipped four things this spec called
new: the derived model manifest, C14 (check_model_parity.sh), the parity contract
apr-gpu-cpu-parity-v1.yaml, and a PR-time sentinel pair in workspace-test. And
binary-release.yml already builds and attaches tagged-release assets. The spec now
starts from those instead of proposing them.

What changed:

  - Section 3.1 lists what already ships and from where. There are FOUR target sets to
    reconcile, not two: nightly's 5, binary-release's 4 (with musl), C13's 5, and this
    section's 4. musl is an open question -- tagged releases already ship it for pv, and
    including it makes the matrix 8 artifacts, not 4. binary-release.yml's own header
    invites other CLIs to opt in, which is a smaller change than a new workflow, and it
    lets hosted runners build while house hardware only verifies. That also takes most
    of RD-4's contention problem away, since gx10 stops being a builder.
  - Section 5.2: the manifest exists and is a CITATION INDEX (name, family, size,
    cited_by), not the sha256/hidden/heads pin this section assumed. Same filename, two
    different artifacts. Extend the deriver or use a different file; do not hand-write a
    second, which is what its --check exists to stop.
  - Section 7: the sentinel pair already runs at PR time against recorded lambda
    evidence. The real gap is LIVE kernel execution, which is a narrower ask than "no
    parity check at PR time". RD-3 restated on that.
  - Section 8: apr-gpu-cpu-parity-v1.yaml exists; this document uses it rather than
    creating it. Two parity contracts now cover CPU/GPU parity and need an owner -- a
    falsifier added to one does not constrain the other.
  - M3's threshold is no longer [U]: 0.98, measured n=5 on both hosts against a
    known-good and a known-bad pair, stdev 0.
  - Counts corrected everywhere: 4 of 21 present, 17 absent, re-tested with find against
    origin/main ebc9e9d.

The review's GT-1 is amended in the same commit, and it overtakes finding F5: the
draft's contract name was right and the file exists. F5 becomes "two contracts now cover
one invariant" rather than "do not mint it".

No RD is decided.

Pmat-Ticket: PMAT-1092
noahgift added a commit that referenced this pull request Sep 9, 2026
…claimed

dc99835 said "The review's GT-1 is amended in the same commit". It was not. The edit
targeted PMAT-1092-review.md; GT-1 lives in ground-truth.md, so the assert fired, the
python exited 1, and git commit ran anyway. set -euo pipefail was on the first line and
did not stop it.

That is the third time in this branch a commit message has described an edit that did
not happen -- e7244c3, 6e14065, and now dc99835 -- and every one was caught the
same way: by checking the file afterwards rather than trusting the script exited. The
lesson is not "remember set -e". It is that in this environment the shell's exit
handling cannot be relied on, so the check has to be explicit and the commit has to be
gated on it. This commit was.

The content: GT-1 now carries the 2026-09-09 re-measurement against origin/main
ebc9e9d -- 4 present, 17 absent -- names the three that #3026 shipped, and records
that this overtakes finding F5. The original c04eda8 measurement is preserved below
it, because the review's reasoning was built on that number.

Pmat-Ticket: PMAT-1092
noahgift added a commit to guyernest/aprender that referenced this pull request Sep 13, 2026
…ms ratchet, executable criteria), DAG rows/edges/amendments, decision receipts, roadmap mints, rescope quorum record (paiml#3025)

* docs(PP-066): PHASE 0 reconcile status 2026-09-06 — main is vacuously green (ratchet SKIPPED under pmat 3.31.0 at 04:35Z, ARMED and RED under 3.37.0 from 07:53Z, cited from job logs); only agent/G-10 unpushed (PR-A); C0 uncredited; D-2..D-11 recorded on paiml#2873

Pmat-Ticket: PMAT-966

* docs(PP-066): orchestrator write-back 2026-09-06 — DAG rows G-11 (paiml#3012, PMAT-1062), G-10b (paiml#3013, PMAT-1063), G-10c (paiml#3014, PMAT-1064), U-1 (paiml#3015, pmat#1200, expiry = C0-3 - 6 d), S-0 (paiml#3016, D-3 speed lane on perf-solo); amendments: P-1.1/P-1.2 -> 0.67 (D-11), C0-3 blocked by U-1 (R4), G-10 expiry 10-03 -> 09-12 (PR-A armed); §5.0 re-rendered (96 rows); roadmap mints PMAT-1061..1064 by hand (pmat#1169)

Pmat-Ticket: PMAT-966

* docs(PP-066): decisions D-2/D-3/D-5/D-8/D-9/D-10/D-11 recorded — seven decision receipts (status: complete, each citing its paiml#2873 comment), DEC rows complete, roadmap tickets PMAT-1018..1023 + PMAT-985 completed with proof, §5.0 re-rendered

Pmat-Ticket: PMAT-966

* docs(PP-066): roadmap — PMAT-1018..1023 and PMAT-985 (decisions D-2/D-3/D-8/D-9/D-10/D-11/D-5) completed with proof (the paiml#2873 comments and their decision receipts)

Pmat-Ticket: PMAT-966

* docs(PP-066): ordering commit (driver v4) — rows L0-1 (paiml#3017, P0), G-11b (paiml#3018), R-8 (paiml#3019); R-0a/R-0b split folded from paiml#3003 (R-0b paiml#3002 PMAT-1060, design-quorum record carried over); edges L0-1/G-11/G-11b <- G-10, every open 0.66 row <- G-11, C0-3 <- U-1, R-0b <- R-0+L0-1, R-2 <- R-0b+D-9, B-G1/R-5 <- R-2, R-6/R-8 <- R-5, R-7 <- R-6, T-0 <- L0-1, TAG-0.66.0 <- every 0.66 row; amendments for the four slack violations the edges exposed (G-10 -> 09-06, R-3 -> 09-19, R-2 -> 10-02, B-G1 -> 10-09); §5.0 re-rendered (100 rows)

Pmat-Ticket: PMAT-966

* docs(PP-066): ordering commit fix-up — I-18 expiry (anchor I-15 + 7 d = 10-03) -> 2026-10-09 for the R-2 -> I-18 edge; check_dag_invariants.sh exit 0 at 100 rows

Pmat-Ticket: PMAT-966

* docs(PP-066): G-10 (PMAT-1059) complete — paiml#3011 merged b0a0a51; receipt marker flipped, roadmap completed with proof, estimates row (turns 15, delegate tokens 64665, wall 2700 s, basis first-run [U])

Pmat-Ticket: PMAT-1059

* docs(PP-066): DAG row G-10 complete (paiml#3011 merged b0a0a51); §5.0 re-rendered

Pmat-Ticket: PMAT-1059

* docs(PP-066): tickets minted by hand for the rows created this session — L0-1 PMAT-1065 (paiml#3017, P0), G-11b PMAT-1066 (paiml#3018), R-8 PMAT-1067 (paiml#3019), S-0 PMAT-1068 (paiml#3016), U-1 PMAT-1069 (paiml#3015); DAG pmat_ids written; §5.0 re-rendered

Pmat-Ticket: PMAT-966

* docs(PP-066): SPEC-2.0 rescope in the DAG (driver v5) — 18-row 0.66 scope; 39 rows -> 0.67 each with cut_by and the claim it protected; L0-1 split into L0-1a (bounded) + L0-1b (unbounded); SPEC-2.0 row; C0-2 (pin); B-G1 folded into R-7; v5 edges; TAG-0.66.0 <- every kept row; invariants exit 0 at 102 rows; §5.0 re-rendered

Pmat-Ticket: PMAT-966

* docs+scripts(SPEC-2.0): spec v2.0 §4.1 — three claims, the 18-row scope and the 39 cut rows with the claim each protected (generated from the DAG), the claims ratchet, the executable criteria table; scripts/release_criteria.sh (C0 C4 C5 C6 C7 C8 C9 C11 C13 C14 one exit-coded command each, C0 first through the analyser pin, never vacuous, 6-row case table; C1 C2 C3 C10 C12 -> 0.67); scripts/run_clean_room.sh (C8 via ../infra beside the main checkout, ENV exit 2 otherwise)

Pmat-Ticket: PMAT-966

* docs(SPEC-2.0): rescope quorum record (scope-holds-with-changes; the scope-fails premise on C3 refuted by the spec: R-0b ships the resolution) — nine claim removals assigned to R-7, C5 -> 0.67 (Q3 unanimous), the ratchet-universe sentence corrected, L0-1a/L0-1b cite paiml#2971 (paiml#3017 closed as duplicate), SPEC-2.0 = paiml#3023; release_criteria.sh credits nine

Pmat-Ticket: PMAT-966

* docs(SPEC-2.0): the rescope record cites the hits by file:line without repeating the literals (the claims ratchet covers docs/audits too — it went RED on its own record)

Pmat-Ticket: PMAT-966

* docs(PP-066): session docs commit 2026-09-06 — tickets minted/completed from the DAG and the receipts, README counts exact, status doc, kaizen

Pmat-Ticket: PMAT-966

* docs(SPEC-2.0): the rescope record cites the spec line its verdict rests on (R-0b ships the resolution) and names C5's residue row (T-0h, 0.67) — driver v5.2 DONE-IF

Pmat-Ticket: PMAT-966

* docs(PP-066): renumber R-0b's ticket PMAT-1060 -> PMAT-1073 (PMAT-1060 is minted by paiml#3027, BSE-001 H4); mint PMAT-1072 for the paiml#3028 main-red hotfix (PR paiml#3030)

Pmat-Ticket: PMAT-1071

* docs(PP-066): driver kaizen — hotfix DONE-IF re-lists PRs; instrument change vs data defect; pre-commit complexity expansion; arms before instrument; checkout restores the index

Pmat-Ticket: PMAT-1071

* docs(PP-066): driver kaizen — three-family quorum mechanics; measure a criterion's distribution before designing a fallback; a PATH tool is not a pin

Pmat-Ticket: PMAT-1071

* ci(PP-066): release_criteria.sh --self-test runs in guard-runner-labels — the criteria script is a decision surface and check_guards_are_wired.sh refused it unwired

Pmat-Ticket: PMAT-1071

* docs(PP-066): session 2026-09-07 — status doc, kaizen (hook expansion, env arm first, update-branch pull), PMAT-1072 completed (proof:PR#3030)

Pmat-Ticket: PMAT-1071

* docs(PP-066): drop two duplicate top-level roadmap entries (title-id twins of [19] and [66]) the 2026-09-06 re-serialisation appended — main's duplicate-id guard refuses them

Pmat-Ticket: PMAT-1071

* docs(PP-066): decision rows D-5/D-2/D-3 cite their receipts as proof; the issue-comment URL stays as prose (the completion guard dereferences paths and merged PRs only)

Pmat-Ticket: PMAT-1071

* docs(PP-066): every decision row cites its receipt path as proof; issue-comment links stay as prose (the completion guard dereferences paths and merged PRs only)

Pmat-Ticket: PMAT-1071

* docs(PP-066): kaizen — never bare git stash (shared stack); the complexity hook charges a file's whole include! expansion, declare modules from a clean included file

Pmat-Ticket: PMAT-1071

* docs(PP-066): R-5 delta v6.1 — build != test; x86_64-cuda on lambda only in release-assets.yml at refs/tags/v*; base-owned promotion; derived fail-closed publish cascade; KEY row (paiml#3045, PMAT-1079); spec v2.1

Spec v2.1 (Appendix A 2.1) and DAG (103 rows) record the delta of 2026-09-08:

- BUILD != TEST. Assets build under the clean-room recipe (scripts/run_clean_room.sh, the
  forjar-pinned toolchain, fingerprint recorded); GPU verification is the fleet-verify receipt
  taken against the asset, never a build log.
- The x86_64-cuda asset (= apr-x86_64-unknown-linux-gnu, cli,cuda per D-10) builds on lambda only
  inside release-assets.yml at a v* tag: runner group gpu-release restricted to that workflow at
  refs/tags/v*, a JIT ephemeral runner for one job, lambda's queue slot claimed (host_queues.lambda
  4; S-1 -> 5, S-2 -> 6). Every other target: the CI clean-room runner.
- Promotion is a base-owned workflow_dispatch job over four receipts carrying the asset sha256 with
  C14 PASS and parity != skipped. No workflow runs cargo publish; scripts/publish_cascade.sh derives
  its set from cargo metadata in topological order and refuses branch/dirty/prerelease/token.
- New row KEY (paiml#3045, PMAT-1079, owner noahgift, expiry 2026-10-02 [U]): until
  keys/apr-release-minisign.pub is on main the manifest job and the C13 gate refuse and R-5 stays
  blocked_by KEY; DONE-IF present -> R-6 pins its fingerprint. R-5/R-6/TAG-0.66.0 <- KEY.
  The gpu-release group stays a blocked_by: RUNNER-GROUP note on R-5, as the delta directs.
- R-5 <- G-11b: make fleet-verify is G-11b's deliverable and is absent today [V].

Two corrections this session's verification pass found by measuring rather than reading:
- The delta's embedded-SASS premise is false for this binary. apr generates PTX text in Rust
  (crates/aprender-gpu/src/ptx/) and the driver JIT-compiles it, cubins landing in
  ~/.cache/trueno/ptx (driver/ptx_cache.rs:3), so nothing SASS-shaped ships. The A now requires
  cuobjdump --list-elf to list NO ELF and reads the emitted PTX's .target on both hosts.
- PMAT-1078 was not free: agent/R-5's contracts/apr-publish-cascade-v1.yaml:4 stamps it. KEY took
  PMAT-1079. An id is taken the moment a branch writes it, minted or not (pmat#1169 class).

Also recorded: the three divergences of agent/R-5 (authored at 19c015f before this delta) that
the row PR closes - an aarch64-cuda asset on gx10, .github/release-assets.pub, hosted builders;
its promotion gate and cascade already match [V]. G-11's typed status flipped to complete from
docs/audits/impl-PMAT-1062-receipt.md (paiml#3020 merged 3792afa) and the §5.0 block re-rendered -
D7 and render_dag.py --check were RED on this branch before this commit.

Guards on this tree: render_dag.py --check PASS (byte-identical, 103 rows) - check_dag_invariants.sh
PASS - check_receipt_complete.sh --dag PASS - check_roadmap_diff_additive.sh PASS (lifecycle only;
acceptance_criteria is not a lifecycle field) - check_roadmap_ids_unique.sh PASS (822) -
check_no_claim_literals.sh PASS - check_perf_claims_cite_receipts.sh PASS -
check_row_pr_write_set.sh ok (orchestrator branch).

Pmat-Ticket: PMAT-993
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1cW8TnmNe3hgjragNjUxr

* docs(PP-066): collapse the PMAT-1074/PMAT-1077 roadmap re-serialisation back to base bytes

The 2026-09-08 merge re-ordered two planned entries; check_roadmap_diff_additive.sh
(PMAT-980) reads that as a re-serialisation and refused, which took `gate` down
through guard-tree. scripts/roadmap_trim.py restores the base ordering; no field
changed in either entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): the roadmap keeps its SORTED order; PMAT-1077's block bytes differ only by a stray blank line

Two guards disagreed on the same file and the first fix satisfied the wrong one.
check_roadmap_diff_additive.sh (G-6) called PMAT-1077 re-serialised, so 5e2bd40
ran roadmap_trim.py, which restores base ORDER — and base order puts PMAT-1074/1077
before PMAT-1069..1073, which check_roadmap_sorted.sh (BSE-09a) then refused:
"PMAT-1069 at line 16527 sorts before PMAT-1077 earlier in the file".

The actual cause is neither ordering. `classify_pair` compares the entry BLOCK text,
and a block runs to the next `- id:`; PMAT-1079 had been appended with a blank line
before it, so PMAT-1077's block carried a trailing empty line that main's copy (where
PMAT-1077 is last in the file) does not have. One blank line, no field changed.

Dropping that blank line makes PMAT-1077's block byte-identical to main's AND keeps
the sorted insertion 1069 → 1079. Both guards pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* feat(G-11b): track scripts/pp066_state.sh and scripts/session_docs_commit.sh — the STATE and SESSION-END tools

Both have been in use for several sessions as working-tree files and were never
committed. That is the "free pass while untracked" trap running the other way:
check_shell_lint_ratchet.sh's universe is `find scripts -maxdepth 1 -name '*.sh'`
(the working tree, not `git ls-files`), so an untracked script already counts
against the ratchet while no reviewer can see it. session_docs_commit.sh was
contributing one such error line — bashrs 7.0.1 reads the `do` of a `for` nested
inside a single-line `if ...; then ...; fi` as the `if`'s body opener (SC2136) —
so the kaizen block is expanded onto its own lines. 177 scripts, 8 error lines,
baseline 8, PASS (ratcheted).

Refs PMAT-1066, paiml#3018

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): paiml#3022 and paiml#3024 join the 0.66 scope as rows F-1 and F-2 (operator ruling 2026-09-08)

F-1 (PMAT-1080, paiml#3022, P0): `apr chat` silently loaded its built-in Demo model for a
sharded SafeTensors index — exit 0, zero tokens, the real model's path printed in the
banner. `Path::extension()` returns the last dot-segment, so on
`model.safetensors.index.json` (the exact filename `apr pull` writes and recommends) it
is Some("json") and matched no arm. Shipped in PR paiml#3050 as a structural fix, not an
added arm: `resolve_chat_format` is one decision — suffix before extension, then magic
bytes, then a refusal from error.rs — and Demo is not an outcome for a path that exists.

F-2 (PMAT-1081, paiml#3024 ask 3, owner bse): the live matrix runs nightly against real
models, and the two axes still uncovered — an `apr serve` column and a sharded-GGUF row
(merge_gguf_shards, for which no fixture builder exists) — are named rather than dropped.

The rows earn their place in a rescoped 18-row release by claim (1): apr reports
truthfully and never silently substitutes what the user named. paiml#3022 is the strongest
instance of that failure in the tree — the substitution reported SUCCESS — and paiml#3024 is
why it survived: qwen-story-daily was green the same night and structurally could not
have caught it (`grep -c "apr chat"` is 0, `grep -c index.json` is 0).

DAG 103 -> 105 rows, 0.66 lane 32 -> 34; invariants PASS (violations=0); the spec's §5.0
block re-rendered byte-identical; roadmap 824 -> 826, sorted, unique, additive.

Refs paiml#3022, paiml#3024, paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): session 2026-09-08 — tickets minted, PMAT-1062 completed, status doc, six kaizen lines

Minted by hand (pmat#1169, `pmat work add` collides): PMAT-1082 for L0-1b (paiml#2971 — the
root cause is named: a crushed Q8_K activation block routes that matmul to the
f32-activation kernel, and the CPU reference was the inaccurate side, not the GPU) and
PMAT-1083 for SPEC-2.0 (paiml#3023). PMAT-1062 (G-11) flipped to completed — its receipt says
complete and PR paiml#3038 is merged.

Kaizen, all six from defects met today:
  * two guards disagreed about roadmap.yaml and the first fix satisfied the wrong one; the
    cause was neither ordering but a blank line inside the previous entry's compared BLOCK
  * `. scripts/apr_bin.sh` does not honour CARGO_TARGET_DIR (it reads cargo metadata's
    target_directory); APR_BIN is the documented pin
  * evidence need not carry a machine path — invoke the tool relatively and compare the
    two outputs field by field before claiming they are the same measurement
  * N identical evidence files are one file plus a sha256
  * a gate's RED leg is cheap: build the merge base in a second worktree
  * a second host can refute a hypothesis class before a lane is dispatched

DAG 105 rows, invariants PASS; §5.0 re-rendered byte-identical; roadmap 828 entries,
sorted, unique, additive; README counts exact.

Refs paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): drop docs/specifications/DOCUMENT_TEXT_MAP.md — session scratch, committed by an over-broad `git add -A docs/`

It is a flat concatenation of the spec at v1.5, produced by an earlier session as a
reading aid and left untracked. Committing it would put a STALE second copy of
PP-066-release-spec.md (v1.5 against the tree's v2.1) under docs/specifications/, where
the claims ratchet and every doc guard would then have two sources for the same text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): session 2026-09-08 part 2 — five kaizen lines, status doc refreshed

All five from defects met in this segment: two of my own PRs shipped a falsifier no
workflow executed; a new test-selection tier surfaced a test that had been RED on clean
main and unrun; an anchored grep meant to fix a pass-grep was itself a false green; the
release had two candidate signing-key paths; and a textual guard was answered with an
allowlist entry and a reason rather than by rewording the file to dodge its regex.

Refs paiml#2873, paiml#3051

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): the kaizen file tripped its own lesson, plus two more lines

Kaizen line 5 quoted a throughput literal while explaining how a textual guard flags a
must-match fixture — and docs/ is exactly the surface the claims ratchet reads, so the
line about textual guards was refused by one. De-literalised; the fact is unchanged.

Two new lines: the claims ratchet's aperture is the `///` vs `//` boundary and a refactor
can cross it (R-0b promoted rationale comments into rustdoc, turning four numbers main
already carried into published speed claims; re-baselining a MOVED line is refused by
design because the ratchet is set-based); and: run the shared guards across every branch
in one pass — three guards over six worktrees took minutes and found four failures CI had
not reported yet, each of which would otherwise have cost a serial ~1h cycle.

Refs paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): D-13 — 0.66 ships checksummed, UNSIGNED assets; KEY moves to 0.67

Operator, 2026-09-08: "skip it and deprioritize". Signing was the only step on the release
path that needed a human, and it was gating a release whose three claims do not include
provenance.

  * KEY (PMAT-1079, paiml#3045) -> lane 0.67, first row of the provenance track
  * R-5 (PMAT-993) and R-6 (PMAT-994) drop KEY from blockers — both unblocked
  * C13's command drops "+ minisign signature"
  * 0.66 lane 34 -> 33 rows; invariants PASS; §5.0 re-rendered byte-identical

WHAT THIS COSTS, STATED RATHER THAN ABSORBED. Claim (3) keeps its INTEGRITY reading and
loses its AUTHENTICITY one. A sha256 still proves the asset matches the manifest the
release job produced, and the four host receipts still prove that sha256 is what was
tested. It does not prove WHO produced it: the manifest and the assets live in the same
GitHub release, so whoever can replace an asset can replace its checksum.

So the release must say so, in the three places a user could otherwise infer otherwise:
the notes' install section, R-6's installer output at install time, and the vocabulary —
no artefact, script, contract or note may call a 0.66 asset "signed" or "verified". Those
four obligations are recorded on the decision (paiml#2873) and are what makes the smaller claim
honest rather than merely smaller.

Refs paiml#2873, paiml#3045

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): D-14 — 0.66.0 is the CUDA parity fix and nothing else (26 -> 5 open rows)

Operator, 2026-09-08: "lets reduce scope as goal is mainly to fix CUDA issue".

0.66.0 now makes ONE of the mission's three claims — (2): every model in the manifest
computes the same function on GPU as on CPU, or the GPU refuses it. Claims (1) and (3)
move to 0.67 IN FULL, and the notes will say so rather than leave it inferable.

KEPT (12 rows, 7 already complete, 5 open): L0-1b (the fix — a crushed Q8_K activation
block routes that matmul to the f32 kernel; 1.5B 0.9508 -> 0.999761 on lambda and
0.950611 -> 0.999583 on gx10, first_divergence none on both), L0-1a (what makes it
checkable and the failure honest: derived manifest, C14, the >=64-position rule, the
measured threshold, REG-15's refusal instead of a silent downgrade), F-1 (the same class
one layer up, already measured and armed), SPEC-2.0, TAG-0.66.0.

CUT to 0.67, each row now carrying `cut_by` and the `claim_protected` it was defending:
R-0/R-0b/R-2 (registry, apr devices, dogfood-reads-registry — claim 1), R-3 (training
banner), R-5/R-6/R-7/R-8 (assets, installer, README-first, nightly install — claim 3),
C0-1/C0-2/C0-4 (credit gates: they gate CREDIT, not correctness — I9), G-10b/G-11b
(tooling), F-2 (the nightly format matrix).

TAG-0.66.0's blockers reduce from 19 to the 6 kept rows. NO release assets and NO
installer in 0.66 — `cargo install aprender` is the only supported install, which with
D-13 (checksummed, not signed) means claim (3) is not made at all rather than made
weakly. scripts/publish_cascade.sh is cherry-picked from agent/R-5; the rest stays behind.

DAG invariants PASS (105 rows, 0 violations); §5.0 re-rendered byte-identical.

Refs paiml#2873, paiml#2971, paiml#3022

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* fix(PP-066): every PP-066 ticket's kind: label is DERIVED from its DAG row — paiml-implement was refused on 80% of the epic

`paiml-implement` refuses at Phase 0 (`kind-gate.sh`, AUTO-IMPL-SKILL-001 T-1) unless the
ticket's roadmap entry carries `kind:<code|triage|docs|measurement>`. Attempting to run it
on R-5/PMAT-993 returned exit 2, and it was not one ticket:

    PP-066 tickets in docs/roadmaps/roadmap.yaml : 105
    carrying a kind: label                       :  21
    NOT carrying one                             :  84

The label is now DERIVED, not typed, because the DAG already says it: SPEC-/DEC-/TAG- rows
are docs; a row with a command-shaped acceptance or a contract is code. 103 entries written,
derived=103 missing=0 wrong=0, and `check_kind_labels_derived.sh` refuses drift in either
direction (a missing label AND a label that disagrees with its row).

TWO rows are UNDECIDABLE and are REPORTED rather than guessed — refusing to guess is not
refusing the tree, so they do not fail the guard:
  G-2 (PMAT-985)  "one line in spec §0 with decided_by and date"   — correctly prose, a docs row
  R-8 (PMAT-1067) "the workflow is green once on all four hosts"   — a PROSE acceptance on a
                  code row, which is the "a prose test: never runs" defect in another costume

MY OWN FIRST DRAFT SHIPPED THE DEFECT THIS GUARD EXISTS TO PREVENT, and it is worth the
record: it read the roadmap by line regex in both directions. `--update` then CORRUPTED the
file — 284 entries carry the inline `labels: []`, which has no `  - ` block to scan, so the
insert landed after a flow sequence and the YAML stopped loading — and `--check` reported
`missing=0 wrong=0` on the wreckage, because a regex reader cannot see a parse error. A
writer that can break the file its own checker reads is exactly the class this guard is for.
Fixed both ends: the verdict now PARSES the roadmap, the writer handles the inline form, and
it refuses to write a rewrite that does not parse or that loses an entry. The case-table
fixture carries both label forms so the corruption cannot come back.

Case table 7/7, both polarities. No ci.yml edit needed: guard_tree.sh's universe is
`git ls-files 'scripts/check_*.sh'`, and the guard advertises `--self-test` in its usage, so
the dispatcher gives it both rows. check_guards_are_wired PASS, roadmap sorted/unique/additive
PASS (828 entries, 0 non-label fields changed).

Refs paiml#2873, PMAT-1093

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* docs(PP-066): mint PMAT-1093 and PMAT-1094 by hand

PMAT-1093 was cited by the kind-label commit and never existed — a Refs pointing at
nothing, which is the small version of the defect pmat#1240 describes. Minted retroactively
as `completed` with its acceptance command and `proof:` path.

PMAT-1094 (paiml#3055): the `*-lint --json` outcome surface emits three shapes for one field,
one of them a Rust `Debug` string in a JSON API. Found sweeping paiml#3051; the tests in paiml#3053
accept all three deliberately and document the table, so this ticket's falsifier is the
DELETION of `assert_outcome_ok`'s two string arms.

Both by hand: `pmat work add` mints colliding ids (pmat#1169), and now pmat#1240 — two
agents minting in parallel branches land on the same id and the merge deletes one, with
`work validate` passing because uniqueness is preserved by the loss.

roadmap 828 -> 830, sorted, unique; kind labels derived=103 missing=0 wrong=0.

Refs paiml#2873, paiml#3055

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* fix(TAG-0.66.0): the release's first step can return a verdict, and its credited set is derived

`scripts/release_criteria.sh --all` is step 1 of the release sequence. It could not produce
a verdict: measured 2026-09-08, C0, C4, C7 and C8 each hit a 120 s timeout and --all ran
past ten minutes with nothing printed. Three defects, one of them mine to have noticed
sooner.

1. C0 RE-RAN FOR EVERY CRITERION. `run_one` gated each criterion on `bash "$0" C0` (I9's
   credited-first rule), and C0 shells out to `pmat comply check` AND a `gh api` call. Nine
   criteria therefore paid that cost nine times. It is now evaluated ONCE per process and
   only while C0 is itself in the credited set. C7 alone runs in 57 s; through the old gate
   it timed out at 120 s.

2. THE CREDITED SET WAS STALE AFTER D-14 and is now DERIVED, not chosen: a criterion is
   credited iff at least one row the spec's §4 table names as its owner is still lane 0.66.

       C7  SPEC-2.0  KEEP   the claims ratchet — load-bearing for D-13/D-14's vocabulary
       C8  SPEC-2.0  KEEP   clean-room before publish; cargo publish rests on it
       C9  C0-7      KEEP   every credited row has a complete receipt
       C14 L0-1a     KEEP   GPU = CPU per manifest model, or the GPU refuses it — 0.66's ONE claim
       C0  C0-1/2/4  MOVE   all 0.67; keeping it made --all both unpassable and unrunnable
       C4  R-6,R-2   MOVE   four host receipts THROUGH the R-6 installer, which 0.66 does not ship
       C6  G-10a…    MOVE   two owners are not rows at all; its script does not exist (ENV 2)
       C11 R-0a/0b   MOVE   the backend registry is 0.67
       C13 KEY,R-5,R-6 MOVE no assets and no installer in 0.66 (D-13, D-14)

3. THE SUCCESS BANNER CARRIED A SECOND, HAND-TYPED COPY of the list, so editing the set
   would have left it asserting the old one. It prints $CREDITED now.

Two new self-test rows, both mutation-proven: hand-editing CREDITED to add C13 turns the
derivation row RED (6/7), restoring it returns 7/7; and the banner row greps the source for
a literal `ALL CREDITED (C…` and refuses one.

CORRECTING MY OWN EARLIER READING: not all of the ten minutes was a defect. C8 is
`make -C machines/clean-room clean-room-p1`, a real multi-minute docker build, and it is
SUPPOSED to be slow — it is the hard gate before publish. Worth naming separately: the
release sequence runs `release_criteria.sh --all` and then `run_clean_room.sh`, so the
clean room currently builds twice.

Current state on this branch: C7 CREDITED (57 s), C9 CREDITED (0 s), C14 ENV 2 —
`scripts/check_model_parity.sh` lives on agent/L0-1, which is in the merge queue — and C8
is the long build. C14's ENV is the correct answer, never a pass.

Refs paiml#2873, PMAT-1083

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* evidence(TAG-0.66.0): C14 runs live and PASSES over the manifest — 0.66's single claim, measured

L0-1a landed (paiml#3026), so scripts/check_model_parity.sh is on main and C14 stopped being ENV.
Run verbatim as the criterion defines it, on lambda, with a cuda apr built from agent/L0-1b
(sha256 776cbbdb4306b5d8 — the tree carrying L0-1b's fix):

    PASS qwen2.5-coder-1.5b-instruct: 78 positions, min cosine 0.9998 at position 36
    PASS qwen2.5-coder-0.5b-instruct: 78 positions, min cosine 0.9996 at position 0
    PASS qwen2.5-coder-7b-instruct:   78 positions, min cosine 0.9996 at position 22
    C14: measured=3 rc=0

The first row is the model paiml#2971 is about. It read 0.950827 on this host before the fix and
reads 0.9998 after, against a threshold whose basis is two measured known-good pairs across
two GPU architectures.

Fourteen models are UNMEASURED because this host does not hold them, and the script REPORTS
rather than fails — no single host holds every model the README names, and the fleet-level
rule belongs to the release.

From the outside, `apr chat --gpu` on the 1.5B now prints
`[GGUF CUDA: NVIDIA GeForce RTX 4090 …]` and answers correctly, with no `falling back to
CPU` anywhere in the output.

ONE GAP, NAMED RATHER THAN GLOSSED: the driver's Resolved criterion asks for
`selected: cuda … parity: PASS` on the success path, and that line does not appear —
REG-15's admission line is printed by `on_cuda_load_error`, so it is emitted only when
something goes wrong. Reporting the selection only on failure is weaker than the criterion
asks. That belongs to claim (1), which D-14 moved to 0.67 with R-0b; 0.66 makes claim (2),
and claim (2) is what the table above measures.

Refs paiml#2971, paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* fix(B3): one parity contract, not two — L0-1a's apr-gpu-cpu-parity-v1 folded into the one with 11 falsifiers

Epic paiml#3058 §B3: do not create contracts/apr-gpu-cpu-parity-v1.yaml; extend
contracts/apr-cpu-vs-gpu-output-parity-v1.yaml, which already exists with 11 falsification
tests. L0-1a shipped the duplicate anyway (paiml#3026, merged), so this removes it.

Verified before acting, not taken on faith: the existing contract carries 11 FALSIFY ids and
58 KB of history; mine carried 3 obligations and 3 falsifiers in 10 KB.

THE TWO ARE THE SAME ARGUMENT SPLIT IN HALF, which is the real cost B3 names. The existing
contract already records the paiml#1864 five-whys whose root cause is "the gate's domain was too
narrow (single-step instead of multi-step)" — and L0-1a's >= 64-position horizon rule is the
answer to exactly that. Half the reasoning sat in each file.

Ported as FALSIFY-CPU-GPU-012/013/014, each with the mutation that turns it RED:
  012  the manifest is derived, never typed        derive_model_manifest.sh --self-test  (6/6)
  013  the domain is >= 64 positions, both         check_model_parity.sh --self-test     (9/9)
       polarities on BOTH required GPU hosts
  014  a forced backend never downgrades           cargo test --test reg15_admission     (7/7)
All three re-run here, green. `pv validate`: 0 errors, 14 falsifiers.

THE STALE ANCHOR, AND A SHARPER VERSION OF B3's POINT. B3 is right that the contract cites
`mod.rs:268-279` for the SKIP_PARITY_GATE bypass and that :268-279 is something else (a doc
comment about qtype resolution). But B3's proposed replacement, `:333`/`:349`, had ALREADY
DRIFTED by the time I read it — L0-1a and L0-1b moved the bypass to :390/:406 on this branch.
The fix for a drifting line anchor cannot be a different line anchor, so both live citations
now anchor on the SYMBOL (`grep for the literal SKIP_PARITY_GATE`) and say why. The 1.1.0
changelog entry keeps its line numbers: it is history, and history is allowed to be stale.

References repointed in the DAG (4), the spec table, parity_admission.rs, reg15_admission.rs
and .pr/L0-1/accept.sh. DAG invariants PASS; §5.0 re-rendered byte-identical.

Refs paiml#3058, paiml#2971, paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* evidence(F1): the model dogfood run BY HAND on both GPU hosts — four cells, and one result stronger than the gate asks for

Epic paiml#3058 §F1: run it by hand on lambda-labs and gx10, needing none of the missing
automation. Four cells for the 7B — {lambda-labs, gx10} x {cpu, gpu} — as §5.5 receipts under
docs/audits/release/v0.66.0-pre/models/.

  M3  parity over >= 64 positions   GREEN on both GPU cells: 78 positions, 283 op rows,
                                    first_divergence None; lm_head 0.99958 (lambda) and
                                    0.99978 (gx10); worst op ffn_out@L23 0.994065 and
                                    attention@L22 0.996844
  M4  determinism                   GREEN, all four cells
  M6  7B service smoke              GREEN, all four cells — loads, non-empty output, zero OOM

THE RESULT WORTH MORE THAN ANY SINGLE CELL: the greedy stream sha256 is 6f38f13e5debd285 in
ALL FOUR cells. Same model bytes, two architectures (x86_64 sm_89 and aarch64 sm_121) and
both backends produce a byte-identical token stream. M4 only asks for determinism WITHIN a
cell; this is determinism across the fleet.

MY FIRST M4 RUN REPORTED NO, AND MY INSTRUMENT WAS WRONG. It compared whole stdout, whose
last line is `Completed in 12.63s` — a clock, not a nondeterminism. The generated text was
byte-identical both times. The comparator now strips that line, which is the same rule this
repo already enforces on required gates: no wall-clock assertion inside a correctness check.

EXCLUSIONS ARE NAMED, NEVER SKIPPED, because §5.5 says a missing cell is NO-GO:
  M1 artifact identity   0.66 ships no assets and no manifest (D-13, D-14) — nothing to compare
  M2 registry readback   `apr devices` is R-0a, 0.67
  M5 refusal semantics   PARTIAL — the FX fixtures are R-0a; the admission level is covered by
                         reg15_admission 7/7, now FALSIFY-CPU-GPU-014 in the parity contract
  M7 transport parity    NOT RUN — needs a port and a client; named, not dropped
  M8 performance         NOT RECORDED DELIBERATELY — the claims ratchet refuses a throughput
                         literal on a documented surface and 0.66 makes no speed claim

VERDICT WITHHELD, on purpose. Each receipt says "not GO and not NO-GO": this is a dry run on
a pre-tag directory, and a receipt calling itself GO with M1 and M2 structurally excluded
would be the theater §1.1 forbids.

Refs paiml#3058, paiml#2971, paiml#2873

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RouwmUL7vFfJyCx9qLEoH

* roadmap: PMAT-1062's `completed` cites its proof (BSE-01)

check_roadmap_completion_is_cited.sh went RED on this branch: PMAT-1062 said
`status: completed` with `github_issue: null` and `notes: null`, so nothing
could dereference the claim.

Checked before citing rather than after, because the guard's own precedent
(PERF-004) is an entry marked completed whose PR was closed unmerged. All three
things the title names are on origin/main:

  guard job runs all, reports all   guard_tree.sh + the guard-tree job, paiml#3037
                                    (git log -S'  guard-tree:' names ff122fa)
  aprender timeout-minutes          paiml#3037 added timeout-minutes 90/90/22
  CARGO_TARGET_DIR per container    all six container steps carry
                                    -e CARGO_TARGET_DIR=/workspace/target

So `completed` is honest and the fix is the citation, not the status.

The edit is applied through a YAML load/verify rather than a regex rewrite --
my own kind-label guard corrupted this file once by treating it as text, and
reported missing=0 wrong=0 on the wreckage. Entry count asserted unchanged
(830) and the citation asserted present after the write.

Verified: check_roadmap_completion_is_cited.sh PASS; guard_tree.sh --no-cargo
41 checks, 0 failed.

Refs paiml#3025

Pmat-Ticket: PMAT-1083

* docs: README CONTRACT_COUNT states 1816 — this branch removes the duplicate parity contract

guard-cargo's "README claims must match measurement" was RED: the README said
1817 (main's count) while this branch's tree carries 1816. The one-file delta is
contracts/apr-gpu-cpu-parity-v1.yaml, deleted here per epic paiml#3058 §B3 — the
instruction was to extend the contract that already had 11 falsifiers rather
than mint a second one over the same property, and the duplicate had already
merged on L0-1a.

Re-derived with `make readme-sync`, which reads `find contracts/ -name "*.yaml"`
and rewrote both CONTRACT_COUNT blocks. Not hand-edited.

Refs paiml#3025

Pmat-Ticket: PMAT-1083

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
noahgift added a commit to guyernest/aprender that referenced this pull request Sep 13, 2026
* roadmap: mint PMAT-1096 — the 0.66.0 release cut (milestone 0.66.0: paiml#3022 F-1, paiml#2971 L0-1, paiml#3063 T0)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* release: 0.66.0 — the honest-GPU model release (PMAT-1096)

Bump every workspace (root + crates/facades upstream pins + facades lock) with scripts/bump-version.sh 0.66.0; CHANGELOG gains [0.66.0] and the missing [0.65.1]/[0.65.2] sections. Scope is the operator-curated 0.66.0 milestone: paiml#3022 (F-1, paiml#3050), paiml#2971 (L0-1a/b, paiml#3026 paiml#3032), paiml#3063 (T0).

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* docs(audits): impl receipt for PMAT-1096 — the 0.66.0 cut, partial until the tag and the cascade; paiml#2971 close quorum 2/3 not agreed

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* evidence(parity): paiml#2971's exact file — base Qwen2.5-1.5B-Instruct q4_k_m measures 78 positions, min cosine 0.9978 on lambda with apr 0.66.0 (6119892)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* docs(audits): PMAT-1096 receipt — paiml#2971 closed on a 3/3 round-2 quorum after the reporter's exact file measured PASS; round-1 artifact kept

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* release gates 0.66.0: the pre-publish dogfood's five red rows fixed at the root — eight bashrs SEC/DET findings from paiml#3032/BSE scripts, six shifted claim literals deleted (shrink-only ratchet), fresh PP-26 witness on lambda (c=1/4/8/16 PASS), C14 builds its own cuda apr on a GPU host, delegate agent-memory gitignored

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* docs(audits): PMAT-1096 receipt — phase 3b: the early pre-publish dogfood NO-GO → GO at b891ab7

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

* evidence+docs(PMAT-1096): the parity record's model path is ~/models (shipped-path ratchet +0); receipt phase 3d — the four merged-tree reds and their root causes

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
noahgift added a commit to guyernest/aprender that referenced this pull request Sep 15, 2026
…fleet — 19 of 21 named artifacts do not exist, and the CUDA gate cannot fail (PMAT-1092) (paiml#3056)

* review(release-process): grill the v0.1 draft against the measured fleet — 11 findings, S0 ledger executed, RD-9 closed

The draft's normative sequence rests on artifacts that do not exist: of the 21
scripts/workflows/contracts it names, 1 exists, 1 is correctly marked new, and 19
are absent while written in the present tense. Verdict do-not-implement-as-written.

One agy /teamwork-preview lane (agy 1.1.27, writes=false, schema-valid, exit 0)
returned 10 findings; every one was re-executed here. Two were overturned:

  - the lane blamed the cuobjdump gate on a missing binary; cuobjdump is present on
    all four hosts. The real defect is that the probe excludes nothing — aprender
    emits PTX from pure Rust, so no apr binary ever contains device code, and
    "cuobjdump lists no ELF section" is equally true of the -cpu artifact and of
    /bin/true. Read literally it also exits 255 on a correct artifact.
  - the 7B is provisioned on both dogfood hosts; the gap is yoga, which section 7.1
    assigns a PR-time 7B cell and which has no models directory.

Originated here: the duplicate parity contract (apr-cpu-vs-gpu-output-parity-v1.yaml
already exists with 11 falsifiers), the PTX floor derived from the wrong population,
the merge-queue budget distribution (3-98 min, not "about 34"), and the unstated
move of every build off GitHub-hosted runners.

Appendix A executed on the physical hosts, closing RD-9 and every [U] in section 2.
yoga measures x86_64 / 22 cores / RTX 4060 Laptop / 8188 MiB / driver 595.91.07 /
sm_89, and its proprietary driver is now up — it executes kernels, reversing an
earlier probe the same day. Section 7 nonetheless reopens paiml/infra#359, which was
decided Option A (gx10-only) with a named cost the draft does not cite.

No RD is decided here; all nine are escalated with recommendations.

Pmat-Ticket: PMAT-1092

* review(release-process): the AD-04 quorum returned 3x FAIL on this review and was right on five counts

Applied every objection rather than arguing one.

1. cuobjdump on intel. This review claimed it present on all four hosts and
   overturned the first teamwork lane on that basis. The row came from running
   `command -v cuobjdump` on the local workstation (hostname noah-Lambda-Vector)
   and labelling it `intel`. `ssh intel 'command -v cuobjdump || echo ABSENT'`
   returns ABSENT. That is the "never label a run by intent" failure in this
   repo's own verification discipline, committed while writing a document about
   verification discipline. The overturn is withdrawn; the first lane was right.
   F2 now carries three independent reasons the probe cannot serve as the
   acceptance: it excludes nothing, it exits 255 on a correct artifact, and it is
   not installed on the host that gates the release. Vacuity remains the deeper
   defect and stands alone; absence belongs to the lane. The clean-room job also
   runs inside a container, so the image's contents are the operative question and
   are recorded [U], owner Noah.

2. F10's merge-queue basis mixed cancelled runs into a completion-time claim. A
   cancelled run's duration is a truncation. Re-derived over the 28 ci.yml runs
   with conclusion=success: min 38, median 65, p90 117, max 129. The three quorum
   lanes then disagreed with each other about the right population -- lane 1
   measured workspace-test on --branch main at 3/20/43/92 and called the original
   defensible. Different workflow, different ref, different filter; none refutes
   another. F10 now reports all three populations and marks the budget [U] rather
   than picking a winner, because the unspecified population IS the defect: a
   budget with no population cannot be exceeded, so it cannot fail.

3. GT-1 said "20 of 21 non-new" absent, which is arithmetically impossible. 21
   named, 1 new, 1 present, 19 of 20 non-new absent. The review body was always
   right; the evidence pack's summary line was not.

4. RD-2/6/8 carried "Accept" and "No objection" -- dispositions on decisions owned
   by the team, in a document claiming to decide none. Rephrased as findings or as
   explicit non-findings.

5. The draft moved out of docs/specifications/. All three lanes objected that a
   document headed "DRAFT v0.1 -- Not yet normative" landing in the normative specs
   directory will be read, and RAG-indexed, as a spec. It now sits beside the review
   at docs/audits/release-process-review/.

6. GT-7 and GT-8 carried [V] marks without printing their commands. Added.

Upheld: the 7B is provisioned on both dogfood hosts, so that overturn stands; the
gap is yoga, which section 7.1 assigns a 7B cell and which has no models directory.
All three lanes independently confirmed F2's core and F5's duplicate contract.

Lane hygiene, from the delegate receipt: the three lanes ran in disposable copy-trees
at /mnt/nvme-raid0/agent-wt/PMAT-1092-quorum, never in repo_root, which was
byte-identical before and after (diff md5 8d091c762e9dda0712614607e2a0f713). Lane 3's
file:line anchors are confabulated; its prose is sound. Its line numbers were not used.

Pmat-Ticket: PMAT-1092

* review(release-process): quorum round 2 — 3/3 FAIL on one defect, and they were right

All three lanes returned FAIL and all three named the same thing: the RD table still
carried dispositions after round 1 claimed to have removed them.

  - RD-3 read "Advisory->required is sound" -- an endorsement of a decision owned by
    the team -- and still cited the "3-98 min distribution" as the basis, the exact
    figure F10 had just disavowed two sections earlier. Round 1 rewrote F10 and did
    not propagate the retraction into the table that quotes it.
  - RD-4 read "Unchanged", which decides RD-4 by declining to change it.
  - The Method section asserted the dispositions were gone while two remained.

Both rows are now findings against the criterion as written, deciding nothing:
RD-3 carries (a) "0 false reds in 14 days" is satisfied by a box powered off for 14
days unless a reachability term is added, and (b) the budget names no population, so
it cannot be exceeded and cannot fail -- while leaving required-in-0.66 vs 0.66.1 to
the team. RD-4 records that check_host_slot.sh does not exist, so the slot discipline
is unenforced however the team decides the rest. The Method item now says which rows
were fixed in which round rather than claiming a clean sweep.

What the lanes verified rather than objected to, each by running the command:
F2's vacuity and the 255 exit (all three), F10's re-derived population (lanes 1 and 3),
GT-1's corrected arithmetic, the draft move as a content-preserving 100% rename, and
spot-checks of the previously unchallenged F5 (pv validate clean, 11 falsification
tests counted), F9 and F11.

Lanes ran in disposable copy-trees at /mnt/nvme-raid0/agent-wt/PMAT-1092-quorum2;
repo_root was not their working directory.

Separately filed paiml/paiml-implement#51: the delegate's documented out_dir is keyed
by phase number alone, and this run's ph4 directory already held three PASS verdicts
from a different session two days earlier on ticket PMAT-246. Reducing the documented
path would have armed auto-merge on a diff no lane had read.

Pmat-Ticket: PMAT-1092

* review(release-process): quorum round 3 — RD-9 was the seventh disposition, and two more of its shape

Round 3 ran on a clean ticket-scoped out_dir (agy/PMAT-1092-round3), not the polluted
ph4 directory, and returned 3/3 FAIL naming one row: RD-9 declared itself "Closed by
Appendix A above", with a matching "This closes RD-9" in the appendix.

They are right, and the distinction is the point of the whole document. Appendix A
takes the measurement RD-9 asks for. Recording RD-9 as closed is a different act, and
it is Noah's. A measurement being taken is not a decision being made. RD-9 now says
exactly that, and the appendix says it supplies what RD-9 asks for rather than closing
it.

Three rounds have each found exactly one more disposition after the previous round
claimed a clean sweep, so this time I swept the document myself instead of waiting for
round 4 to find the eighth. Two more of the same shape were in the findings, not the
table:

  - F3: "RD-3's promotion criterion becomes ..." -> "whatever RD-3 decides, its
    criterion needs a reachability term", which is a finding about the criterion
    rather than an answer to the question.
  - F7: "RD-7 becomes: the floor is ..." -> "state the basis as ...; what the declared
    minimum should be is RD-7's to settle".
  - F11: "RD-1 is resolved in the same PR that amends C13" -> "whatever RD-1 resolves
    to lands in the same PR", which is sequencing, not disposition.

The Method section no longer claims a clean sweep. It records seven rows over three
passes and says the count is a fact about the passes, not a guarantee about the table.

What round 3 verified rather than objected to, each lane running the command and the
delegate reproducing all three byte-for-byte: grep -c cuda .github/workflows/nightly.yml
-> 0; ssh yoga 'ls ~/models' -> No such file or directory; pv validate
contracts/apr-cpu-vs-gpu-output-parity-v1.yaml -> Contract is valid. The round-2 defect
is confirmed fixed: the retracted 3-98 figure now occurs only inside F10's retraction.

Pmat-Ticket: PMAT-1092

* review(release-process): land the three softenings the previous commit message claimed

Correcting the record. e7244c3ab's message described three prescriptive phrasings in
the findings being softened -- F3's "RD-3's promotion criterion becomes", F7's "RD-7
becomes:", and F11's "RD-1 is resolved in the same PR". The script that was supposed to
apply them asserted on the first substitution and exited, so none of the three landed.
Only the RD-9 and Method fixes were in that commit. The message overstated it.

This commit is the three edits themselves:

  - F3: "RD-3's promotion criterion becomes X" -> "whatever RD-3 decides about
    advisory-vs-required, its criterion needs a reachability term", which is a finding
    about the criterion rather than an answer to the question RD-3 asks.
  - F7: "RD-7 becomes: the floor is X" -> "state the basis as X ...; what that declared
    minimum should be is RD-7's to settle".
  - F11: "RD-1 is resolved in the same PR that amends C13" -> "whatever RD-1 resolves
    to lands in the same PR", which is sequencing rather than disposition.

The failure mode is worth naming because it is the one this review keeps finding in the
document under review: a claim written from intent rather than from what the mechanism
actually did. An assert that fires and a commit that proceeds anyway is the same shape
as reading $? through a pipe. The script exited 1; git commit ran in the same shell and
did not care.

Pmat-Ticket: PMAT-1092

* docs(review): link the work breakdown (paiml/aprender#3058) from the review header

The review says what is wrong; the epic says in what order to fix it and which
rows are blocked on a decision only Noah or the team can make. Anyone arriving at
the review should find the actionable form without hunting for it.

Pmat-Ticket: PMAT-1092

* docs(audits): impl receipt PMAT-1092 — verdict PARTIAL(escalate), 0/3 PASS on the quorum, two tickets filed

Records what the three quorum rounds overturned in this review rather than in the
draft, including the mislabelled cuobjdump probe and the commit message that claimed
edits its script never applied. Auto-merge is not armed and must not be until a round
returns 3 PASS.

Pmat-Ticket: PMAT-1092

* review(release-process): quorum round 4 — F8 decided RD-5, and the [U] rows still said "close"

3/3 FAIL, unanimous on the eighth of the pattern: F8's "Smallest fix" laid out the two
readings of the section 9 / section 5.5 contradiction and then picked one -- "section 9's
instrument already implies the former; make it explicit." RD-5 says "resolve F8 first",
so F8 deciding it is RD-5 decided by the back door. F8 now states that the document has
to say which it means, offers the non-maintainers scoping as evidence bearing on the
question rather than as the answer, and leaves the choice to RD-5.

Lane 3 alone raised a second of the same shape at line 446: "section 2's [U] rows now
close as: yoga = ...". Unseconded, and right -- it is the RD-9 defect verbatim, one
section further down, missed when RD-9 was fixed in round 3. A measurement being taken
is not a row being closed. It now reads "What section 2's [U] rows now measure as:".

Verified here rather than taken from the lanes: both anchors resolve to the quoted text
(line 315 and line 446; lane 3's :317 is a two-line drift onto the same sentence), and
the F5 spot-check the delegate explicitly declined to vouch for -- pv validate
contracts/apr-cpu-vs-gpu-output-parity-v1.yaml exits 0 with "Contract is valid", and the
contract carries 11 falsification_tests.

Four rounds, eight rows, and each round found exactly one more after the previous claimed
the sweep was done. That is the finding about the review's own method, and it is now in
the Method section rather than in another claim of completeness.

Pmat-Ticket: PMAT-1092

* review(release-process): the Method section now records round 4, which the last commit message said it already did

6e14065c0's message ended "it is now in the Method section rather than in another claim
of completeness". It was not. The commit fixed F8 and the [U]-rows line and left the
Method section saying "seven rows, three passes".

That is the second time in this branch a commit message described an edit that had not
been made -- the first was e7244c3ab, disclosed in f246fdafb. Both are the same defect
the review keeps finding in the document under review: a claim written from intent
rather than from what the mechanism did. Writing it down twice is worse than writing it
down once, so it is now the point of the paragraph rather than a footnote to it.

The Method section reads ten rows over four rounds, names what round 4 found, and says
plainly that a fifth round may find an eleventh -- and that this would be the method
working. The header row and the Method preamble now say four rounds, all 3 x FAIL, and
that the quorum never passed. No claim of completeness anywhere.

Pmat-Ticket: PMAT-1092

* review(release-process): round 5 — 2/3 FAIL, all four objections were my own arithmetic, and the fix had a fifth

First round that was not unanimous: lane 1 PASS, lanes 2 and 3 FAIL, and both FAIL lanes
explicitly agree F8 is genuinely fixed rather than merely softened -- which was the thing
round 4 blocked on and the thing most at risk of over-correction.

Every surviving objection was against the review's account of itself, not its findings:

  - "Ten rows" against nine enumerated. Both FAIL lanes; lane-reduce split them into
    singletons only because lane 3 cited a byte-identical scratch mirror one line off.
  - "each finding exactly one more", contradicted two sentences later by rounds that
    found two each.
  - The verdict table adopting section 9 outright while F8 says section 9 and section 5.5
    cannot both hold and RD-5 is open. Unseconded, and correct: calling it admissible is
    a disposition.
  - S0-Y4/Y5 "blocked behind F3's decision". F3 is a finding; RD-3 is the decision.
    Unseconded wording nit, also correct.

The Method paragraph is now a table anyone can add up: 4 + 2 + 1 + 3 + 2 + 3 = 15 over six
passes, five quorum rounds and one sweep of my own. Writing it exposed a fifth error of the
same kind -- the first draft of that sentence said "over five passes" while the table it
sits under has six rows. Counted before committing rather than after.

Verified here rather than taken from the lanes: both anchors resolve to the quoted text,
and pv validate contracts/apr-cpu-vs-gpu-output-parity-v1.yaml exits 0 with "Contract is
valid" carrying 11 falsification_tests -- the spot-check the delegate explicitly declined
to vouch for.

Pmat-Ticket: PMAT-1092

* review(release-process): round 6 — five more of the same class, including one no lane caught

3/3 FAIL, and all three lanes independently verified that round 5's fix landed: two
summed the Method table's count column to 15 and mapped six passes onto six commits,
and two confirmed the section 9 split creates no new disposition.

Applied:

  - The intro prose still declared section 9 "admissible today" without the jidoka
    carve-out the header and the verdict table had just gained. Fixing a claim in two
    places out of three is how this class survives.
  - Section 4 was "admissible once 3.1 resolves" while RD-6 governs exactly the claim
    section 4's table makes -- whether arm-gpu-cuda may ship with one verification
    host. Now blocked on RD-6 as well.
  - RD-1's "whatever is chosen, amend PP-066 C13 in the same PR" is an imperative about
    sequencing. Restated as the finding underneath it: the two documents carry different
    numbers and will disagree until both are edited; how to sequence that is the team's.
  - S0-N1's "default features" was true for 4 of 5 targets. nightly.yml:78 (unix) builds
    default features; :82 is Windows-only, guarded by if: runner.os == 'Windows', and
    adds --no-default-features --features inference. Verified here, not taken from the
    lane -- the lane called it a falsification, the delegate correctly narrowed it, and
    the narrowing is what landed. The "zero occurrences of cuda" claim is unaffected.
  - The paragraph's own trailing sentence still read "a fifth round may well find an
    eleventh", four lines under "Fifteen, over six passes". No lane flagged it; the
    delegate read it directly and passed it up.

The Method table now carries a seventh row and reads twenty over seven passes; the sum
was checked programmatically before committing, as it was last round. The closing
sentence no longer predicts a specific round -- it says every round so far has raised new
instances of one class rather than re-raising a fixed objection, and that no sentence
here should be read as "the sweep is complete", because that sentence has been wrong
five times.

Pmat-Ticket: PMAT-1092

* review(release-process): round 7 — the first machine-grounded defect inside a finding, plus five more

Round 7 added a BLOCKING/REGISTER classification so the gate had a stated threshold --
a gate whose threshold is unstated cannot meaningfully pass or fail, which is one of
this review's own findings turned on itself. It did not lower the bar: still 3/3 FAIL.
What actually changed the outcome was a different instruction -- audit findings F1-F11
as a set, and treat a false finding as the bucket that matters most. That produced the
round's real catch.

BLOCKING, both verified here against the machine rather than taken from a lane:

  - F11's body said nightly.yml builds with "default features" for all five targets,
    flatly contradicting the S0-N1 ledger row that round 6 had just made precise. :78
    (unix, four targets) is default features; :82, guarded if: runner.os == 'Windows',
    adds --no-default-features --features inference. The review disagreed with itself
    one round after fixing half of it.
  - F8 recorded "labels bug,P0,pp-066" under a [V] mark. gh issue view 2971 --json
    labels returns four: bug, P0, pp-066, inst:A. A [V] that no longer reproduces is
    not a [V]. Corrected in the review and in GT-5.

Four more of the established class:

  - F11's Smallest fix still carried "lands in the same PR" -- round 6 removed that
    imperative from the RD-1 row and left the identical construction one section up.
  - Section 12 was absent from the verdict table while being discussed in F1 and in the
    Upheld section. Now blocked, on F1.
  - S0-M1 had not been extended to yoga, which F9's own Smallest fix asks for and for
    which the measurement was already taken: 7807 MiB free against 4.36 GiB of weights,
    but ssh yoga 'ls ~/models' returns no such directory. Capacity yes, provisioning no.
  - The round-6 sentence read "Round 6 duly found a sixteenth ... a seventeenth" when
    round 6 found five. That sentence has now been wrong in three consecutive rounds,
    each time about the round before it, which is why it no longer predicts anything.

Method table: eight rows, 4+2+1+3+2+3+5+6 = 26 over eight passes. Summed
programmatically before commit for the third round running.

Pmat-Ticket: PMAT-1092

* review(release-process): round 8 — the first audit of the findings as a SET, and three of my measurements did not reproduce

2 FAIL / 1 PASS, and the round that finally earned the loop. Rounds 1-7 hunted the
review's prose. Round 8 was told to audit findings F1-F11 against the machine and to
treat a false finding as the bucket that matters most. That is what found these.

Three non-reproductions, all mine, all verified here and not taken from a lane:

  - Appendix A and GT-2 said "nvidia-smi -L names the AD107M".
    ssh yoga 'nvidia-smi -L' prints "GPU 0: NVIDIA GeForce RTX 4060 Laptop GPU".
    AD107M comes from lspci -nn. I lifted the part number out of a note that had it
    from lspci and attached it to the wrong tool -- a claim about which command
    produced a string, in a review whose second-largest finding is that you must
    never label a run by intent. Both files corrected with the real output.
  - F5 placed the SKIP_PARITY_GATE bypass at mod.rs:268-279. grep says :333 and :349;
    :268-279 is the HGEMM_PREFILL warm-up block. I copied the range out of
    contracts/apr-cpu-vs-gpu-output-parity-v1.yaml without opening the file. The
    contract is still wrong at YAML lines 18 and 179, which is now its own note in
    F5: a line-keyed citation inside a contract drifts silently, the same class as the
    file:line baseline drift this repo already tracks.
  - F10's figures move with the rolling 200-run window. Re-derived today: n=24 min=38
    median=65 p90=119 max=129, against the original n=28 min=38 median=65 p90=117
    max=129; lanes re-deriving minutes apart got min=39 median=67. Min and median are
    stable to +/-2, n and p90 are not. The sample is now dated, the drift is stated,
    and the surviving claim is the one that does not depend on the window: the median
    is in the sixties and the max above two hours, an order of magnitude from "34".

Three more of the established class: a duplicated S0-G2 id (now S0-G2a / S0-G2b), and
F3 and F4 carrying [V] -- "verified by a command in this document" -- with no command
printed. Both now print one. RD-6's "must be stated beside it" restated as the fact the
decision has in front of it.

Method table: nine rows, 4+2+1+3+2+3+5+6+6 = 32 over nine passes, summed
programmatically before commit for the fourth round running.

Everything else the lanes checked reproduced: F3's infra#359 quotation and the
cuda-nightly selector, F6's skill scope, F8's four labels and section 9's instrument
wording, and the S0 host figures on all three hosts over ssh.

Pmat-Ticket: PMAT-1092

* review(release-process): round 9 — GT-1 held in full, GT-6 did not

3/3 FAIL. Round 9 audited the two evidence tables that nine rounds of spot-checks had
never covered as wholes.

GT-1 HELD, and that matters more than the failures. All three lanes independently
re-tested every one of the 21 paths, ran find over the tree for each absent one, and
got 1 present, 1 correctly marked new, 19 of 20 non-new absent, with nothing hiding
under another path. The review's headline claim -- section 6 is normative while 19 of
the artifacts it depends on do not exist -- is now machine-verified end to end rather
than by sample.

GT-6 did not hold. Two quotation defects against docs/specifications/PP-066-release-spec.md:

  - GT-6 said "C13 says 5 targets, and pins them to the 5 nightly.yml targets". C13
    (:159) says five targets and a signed manifest; it never names nightly.yml. The row
    that pins the five to that workflow is R-5 (:218). Two rows merged into one
    attribution.
  - Both files quoted R-5 as "built on the nightly.yml runners". The spec says "build
    on". A one-letter misquote inside quotation marks is still a misquote, and it is the
    second round running where a citation was repeated rather than opened.

Lane 3 additionally claimed the round-8 nvidia-smi/lspci and F5 anchor fixes had not
landed. They had: grep returns zero occurrences of the old text, the lspci attribution
sits at review :455 and ground-truth :54, and all three lane trees were verified at
b645c4701. Not acted on, recorded because a lane being wrong is also a result.

An aside on method that belongs in the log rather than the document: the first attempt
at this commit was blocked by the subagent hook before its heredoc ran, so the fix
script never existed and nothing was applied -- and the arithmetic check printed
"passes=9 sum=32, residual 'built on': 1" instead of the expected 10/34/0, which is how
it was caught rather than committed with a message describing edits that had not
happened. That failure mode has now been caught twice in this branch by checking
mechanically after the fact instead of trusting the script exited.

Method table: ten rows, 4+2+1+3+2+3+5+6+6+2 = 34 over ten passes.

Pmat-Ticket: PMAT-1092

* review(release-process): round 10 — the epic was stale, and the epic is what the release is run from

3/3 FAIL. Round 10 audited three surfaces nobody had looked at: GT-3/GT-5, the impl
receipt, and the epic.

The costliest was the epic, paiml/aprender#3058. It is the artifact 0.66 will actually
be worked from, it was written from the review at round 7, and three rounds of
corrections had not reached it: it still placed the SKIP_PARITY_GATE bypass at
mod.rs:268-279 (real: :333/:349), still quoted R-5 as "built on the nightly.yml
runners" (verbatim: "build on"), still carried the "amend C13 in the same PR"
imperative the sweep had softened, said "the eight section 8 guards" over a list of
six, and omitted the Windows --no-default-features distinction. Fixed in a separate
edit to the issue itself; a derived artifact that drifts from its source is the same
defect class as a contract citing a line number that moved.

This receipt was also stale, and had never been reviewed. It said "0/3 PASS" over three
rounds with round 4 stopped. Ten rounds ran; rounds 5 and 8 each returned one PASS. The
dispatch ledger now carries all ten with what each found, and the Gaps section says
plainly that the quorum has never returned 3 PASS -- which was always the operative
fact, and was true even while the tally around it was wrong.

Two evidence defects: GT-3 dropped the gx10 label from gx10-blackwell's list (the org
API returns self-hosted,Linux,ARM64,gpu,gx10,cuda,blackwell,gb10), and GT-5 silently
truncated all four issue titles under a heading presenting them as gh output -- #2971
stopped at "cosine 0.94" where the API continues ", CPU works fine". Both now verbatim.

And the Method preamble said the review had been through the quorum "eight times" four
words from an enumeration of nine rounds. The table now also states its own scope: it
counts rows of one class, not every objection a round raised, which is why a round's
commit message can cite a different number.

Method table: eleven rows, 4+2+1+3+2+3+5+6+6+2+6 = 40 over eleven passes.

Pmat-Ticket: PMAT-1092

* review(release-process): round 11 — nothing false left, and the one recurring class removed at its root

3/3 FAIL, but the failure surface finally collapsed to a single class. No lane could
find anything false about the world, the machine, the draft, or any measurement. Six
independent re-audits -- GT-2's host figures on yoga and lambda-labs, GT-4's infra#359
evidence -- all reproduced, and all six of round 10's fixes held on all three lanes.

What remained was one class in three instances, and all three were the same sentence
pattern: a round count restated in prose, which goes stale the moment another round
runs. receipt:23 said "AD-04 quorum x3 ... width 3 x3"; receipt:26 said "four
adversarial rounds"; the epic's Provenance said "three AD-04 quorum rounds" four lines
above its own "Ten quorum rounds ran in total".

The delegate's read was the right one and is why this is fixed rather than incremented:
a twelfth round that only corrected the numbers would make them wrong again. So the
counts are gone from all three sites. The receipt's phase row now reads "3-n, one phase
per round, see the dispatch ledger below", and says explicitly that it names no count
because every count restated in prose here went stale. The epic cites #3056's Method
table instead of repeating it. The ledger and the table remain the single places a
round count is written, and both grow by construction.

Two things the round found that are worth keeping as facts rather than defects: the
lanes noted lambda-labs' free disk has drifted 376G -> 279G since the ledger was taken,
and infra#359 gained a comment today. Neither changes a claim -- the ledger is dated and
GT-4 quotes the issue's text, not its comment count -- and both were correctly filed
REGISTER.

Method table: twelve rows, 4+2+1+3+2+3+5+6+6+2+6+3 = 43 over twelve passes.

Pmat-Ticket: PMAT-1092

* fix(roadmap): PMAT-1092's acceptance criterion said 12 paths, 11 absent — the audited count is 21 and 19

Found by the pr-review skill's section 3.E cross-vendor consultation (agy 1.1.27,
gemini-3.1-pro-high), which is the arm that exists to have someone who did not write
the diff read it. Grounding: measured.

The criterion was written at the start of this ticket from a partial first sweep --
before the workflows, the four contracts, install.sh, the minisign public key and the
model manifest were counted. GT-1's full audit, re-run independently by three quorum
lanes in round 9 with find over the whole tree, is 21 paths named: 1 present, 1
correctly marked new, 19 of the 20 non-new absent.

The consequence the lane named is the right one and is why this is a defect rather than
a typo: the roadmap entry is the source of truth for what "done" means, so a criterion
reading "11 of 12" is satisfied by fixing eleven paths and silently drops the other
eight.

Pmat-Ticket: PMAT-1092

* review(pr-review): the section 3.E receipt for #3056, unsigned — CI holds the key

PR-REVIEW-SKILL-002 v2. Boundary per section 2: BASE 74e3b2540 (merge-base with
origin/main, recomputed after gh pr update-branch), reviewed head 30bacd1a6, 5 files
+1320, docs and roadmap only.

Consultations, encoded per section 3.0 so that "could not consult" and "nothing found"
are never the same receipt:

  pmat        consulted, transport=cli. Index built in THIS worktree at the reviewed
              head (88,623 functions / 10,344 files / 47.4s), index_is_ancestor computed
              not asserted. complexity_delta [] because 0 of 5 changed paths are code;
              satd_introduced [] because 0 added lines match TODO|FIXME|HACK|XXX; tdg
              A/A/A/A on the four markdown files and C- on roadmap.yaml, pre-existing on
              a 16,600-line file. The MCP note is recorded: pmat --mode mcp exits 0 on
              EOF, which proves the binary launches and not that a session is
              serviceable, so transport reads cli and says why.
  cuda        not-triggered, 0 cuda-path files.
  crux        not-triggered, no user-facing surface changed; no comparative claim (the
              one competitive number the review touches it refuses to adopt and marks
              [U]).
  mutation    not-triggered, 0 guards and no executable code in the diff.
  antigravity consulted. agy 1.1.27, gemini-3.1-pro-high, exit 0, 136.3s, schema-valid
              against the repo's own agy-review-v1 schema, in a disposable git-archive
              tree with the diff and prompt passed as files.

Verdict FINDINGS, on the arm's one finding, which eleven AD-04 quorum rounds had all
missed: PMAT-1092's roadmap acceptance criterion still read "12 ... 11 of 12 ABSENT"
against a headline of 21 and 19. divergence agy_only=1. Fixed in cae443ae4, before this
receipt, and re-verified by the primary reading the criterion and GT-1 directly rather
than re-running the lane's grep.

Recorded rather than smoothed over: cross-vendor identity is NOT verifiable from this
receipt -- model_id is what was requested on argv and nothing in agy's output carries
the answering model. lane-reduce.sh cannot represent an agy-review-v1 lane, so its
reduction came back NO-VERDICT with an empty dissent, and the finding survives only in
the raw lane output; an empty reduction there does not mean the lane found nothing. The
lane produced nothing for two of five judgement items at num_turns=1, and that gap is in
the receipt because an unrecorded gap reads as coverage.

The receipt is UNSIGNED on purpose. scripts/pr_review_sign_receipt.sh refuses on a
reviewer's box -- PR_REVIEW_SIGNING_KEY_B64 is a repository secret and the signature is
what proves the receipt came from CI. ci.yml:2315 signs any unsigned receipt under
evidence/pr-review/<pr>/ and commits the signature back to this branch. Signing it here
with a local key would manufacture exactly the provenance the design exists to prove.

check_pr_review_receipt.sh runs its four positive controls first -- schema-invalid,
self-review, findings-digest, cost-missing all fired -- and then rejected this receipt
on [B1] "receipt is unsigned", and on nothing else.

Pmat-Ticket: PMAT-1092

* chore(pr-review): sign this PR's receipt (PR-REVIEW-SKILL-002 v2 §4.3 CI signer)

* review(pr-review): the receipt now ACCEPTs — six defects the guard found that a signature would have hidden

Validated per section 4.4 by signing a COPY with a throwaway keypair and running
check_pr_review_receipt.sh against it, which is the only way to test everything except
CI provenance. That found six real defects in my own receipt, in order:

  1. cuda marked not-triggered. The guard's own --match-message predicate exits 0 on
     this commit range: sm_89 appears in the messages recording yoga's and lambda-labs'
     compute capability. Section 3.B says do not evaluate the trigger by eye, ask the
     guard, and I had evaluated it by eye over changed paths. The arm is now consulted,
     with three cited PTX-ISA authorities and one MANDATORY no-authority-found row.
  2. duplication_coverage.sibling_branches said "lexical" with 0 of 852 scanned. A
     sweep that attempted nothing passes vacuously. Now "none", with the denominator
     kept so the gap stays visible.
  3. A SARIF run named antigravity/agy, outside the attributable set. Now antigravity.
  4. Findings with no failure_scenario. A finding that cannot name the failure it
     permits is a comment.
  5. cited findings with no excerpt or digest. cited is verified, not labelled.
  6. B4: I had recorded the quoted "N x competitor" ratio in comparative_claims[] with
     a null comparator. That array is for claims the PR MAKES, and the guard demands
     all five comparator fields for anything in it. The line is a verbatim GitHub issue
     TITLE in the GT-5 evidence table; B4's diff surface (book/**.md, root *.md,
     literals in shipped .rs) excludes docs/**.md by design. crux is now not-triggered
     with the predicate match recorded in its trigger_reason, and the ratio is not
     restated in the receipt because a ratio in a finding message with an empty
     comparative_claims is itself B4.

The cuda arm earned its place. Three citations landed: PTX ISA 11.1.1 (".version ...
must be compiled with tools that support an equal or greater version number") and
Table 62's ISA-version-to-driver mapping, which is published authority FOR F7 -- a PTX
floor can be derived from a declared minimum instead of from whichever drivers sit in
one fleet; 11.1.2's .target list, which confirms sm_89 and sm_121 are real targets and
the S0 ledger's column is not a transcription error; and driver-API JIT samples
supporting the draft's dlopen-and-JIT model. The fourth row is the one that matters
most: two queries for cuobjdump's exit status on a binary with no device code returned
NOTHING, so F2's 255 rests on the measurement printed in the review and NOT on NVIDIA
documentation. That row is mandatory precisely so "the docs said nothing" and "I did
not ask" cannot be the same artifact.

Recorded as a deviation rather than omitted: section 3.B says dispatch the
cuda-docs-reviewer agent, because an arm run by the actor that wrote the diff is not an
independent check. That agent is a repo-scope definition absent from this session's
registry, so the arm ran inline by the primary against the same MCP server. Weaker than
the skill asks for, and the receipt says so.

Still unsigned on purpose. PR_REVIEW_SIGNING_KEY_B64 is a repository secret and
ci.yml:2315 signs it and commits the signature back. Against a throwaway key the guard
now prints ACCEPT, with all four positive controls -- schema-invalid, self-review,
findings-digest, cost-missing -- firing first.

Pmat-Ticket: PMAT-1092

* review(pr-review): drop the CI signature — it signs the receipt from before the six guard fixes

The CI signer ran on df3221ed9 and committed a signature back (8a09f1842). That
signature is over the receipt as it stood BEFORE the six defects the throwaway-key
validation found. Measured, both directions:

  minisign -V against the corrected receipt   -> Signature verification failed
  minisign -V against the receipt CI signed   -> Signature and comment signature verified

So the signature is real and correct; it is simply attesting to a document that no
longer exists. Leaving it in place would be worse than having none: Arm 4 verifies the
signature against the receipt it finds, so a stale .minisig beside a corrected receipt
is a receipt that fails verification, and a reader who did not run minisign would see a
signed receipt and assume otherwise.

ci.yml:2331 skips any directory that already has a .minisig -- "already signed" -- so
the signer will not replace it on its own. Deleting it is what asks for a re-sign, and
it is the only thing in this commit.

Pmat-Ticket: PMAT-1092

* chore(pr-review): sign this PR's receipt (PR-REVIEW-SKILL-002 v2 §4.3 CI signer)

* spec(release-process): v0.2 — facts and mechanisms corrected, all nine decisions still open

Updates the draft against the PMAT-1092 review, one agy /teamwork-preview grill, eleven
AD-04 quorum rounds and a CI-signed pr-review receipt. The rule this revision holds to
is the one the review spent eleven rounds learning: fix what is a defect of fact or
mechanism, and decide nothing that section 11 reserves to the team.

The two that would have shipped a gate that cannot fail:

  - Section 3.4's acceptance was "cuobjdump lists no ELF section". Withdrawn.
    aprender generates PTX from Rust with no LLVM and no nvcc, so no apr binary ever
    contains device code and that probe returns the same answer for the -cuda artifact,
    the -cpu artifact and /bin/true. cuobjdump --list-elf on a real apr prints "does not
    contain device code" and exits 255, so a guard written literally from v0.1 is also a
    false-RED generator, and cuobjdump is absent on intel, the host that gates the
    release. Two CUDA-docs queries for its exit status in that case returned no
    authority, so even the measured behaviour is one host's rather than a documented
    contract. Replaced with a unit assertion on PtxModule's emitted header and a
    registered mutation that turns it RED.
  - Section 8 minted contracts/apr-gpu-cpu-parity-v1.yaml for an invariant that already
    has contracts/apr-cpu-vs-gpu-output-parity-v1.yaml -- pv validate clean, 11
    falsification tests, 10 obligations. A second contract splits the ratchet. The
    publish-cascade contract likewise extends apr-cli-publish-v1.yaml. Two of the four
    were never new. The existing parity contract's own SKIP_PARITY_GATE citation points
    at mod.rs:268-279 where grep says :333 and :349, and fixing that is now in scope.

Sections 6 and 8 gain a status column and section 6 is retitled "target sequence": of
21 paths this document depends on, 1 exists, 1 is correctly marked new, and 19 of the
remaining 20 are absent, re-tested with find over the whole tree by three lanes.
Section 12 names the blocker v0.1 omitted -- section 5.3 requires install.sh, which is
PP-066 R-6 / PMAT-994, open, due 2026-10-16, so section 6 step 8 cannot run in 0.66.0
as scheduled.

Section 7 now cites paiml/infra#359 and quotes the cost on which Option B was declined,
and says plainly that a required PR-time check is stronger than the nightly lane that
was refused. Section 7.2's constraints are restated as prerequisites with a guard owed,
because measured today all three runner groups report restricted_to_workflows: false on
a PUBLIC repo. Section 7.1 records that yoga has no models directory at all, and that
the budget's defect is a missing population rather than a wrong number: three
disagreeing samples are on record and none refutes another.

Section 2's four [U] cells are measured, which makes the fleet sm_89 x2 and sm_121 x1 --
the mirror of RD-6's single-host gap, since x86-gpu-cuda's two verification hosts share
a compute capability. The skill moves to repo scope, which is #2361. Section 9 states
its contradiction with 5.5/RD-5 where the target is rather than leaving it to be found.

RD-9 is the one to read carefully: the measurement it asks for is taken and folded into
section 2, and the row says so -- and also says that recording RD-9 closed is Noah's
act, because a measurement being taken is not a decision being made. Every other RD
keeps its question and gains the finding that bears on it.

Filename drops the version so it stops going stale on every revision; the version lives
in the header, and a changelog table at the foot says what moved and what deliberately
did not.

Pmat-Ticket: PMAT-1092

* spec(release-process): v0.3 — match what the tree already does

#3026 (L0-1a) landed after v0.1 was written and shipped four things this spec called
new: the derived model manifest, C14 (check_model_parity.sh), the parity contract
apr-gpu-cpu-parity-v1.yaml, and a PR-time sentinel pair in workspace-test. And
binary-release.yml already builds and attaches tagged-release assets. The spec now
starts from those instead of proposing them.

What changed:

  - Section 3.1 lists what already ships and from where. There are FOUR target sets to
    reconcile, not two: nightly's 5, binary-release's 4 (with musl), C13's 5, and this
    section's 4. musl is an open question -- tagged releases already ship it for pv, and
    including it makes the matrix 8 artifacts, not 4. binary-release.yml's own header
    invites other CLIs to opt in, which is a smaller change than a new workflow, and it
    lets hosted runners build while house hardware only verifies. That also takes most
    of RD-4's contention problem away, since gx10 stops being a builder.
  - Section 5.2: the manifest exists and is a CITATION INDEX (name, family, size,
    cited_by), not the sha256/hidden/heads pin this section assumed. Same filename, two
    different artifacts. Extend the deriver or use a different file; do not hand-write a
    second, which is what its --check exists to stop.
  - Section 7: the sentinel pair already runs at PR time against recorded lambda
    evidence. The real gap is LIVE kernel execution, which is a narrower ask than "no
    parity check at PR time". RD-3 restated on that.
  - Section 8: apr-gpu-cpu-parity-v1.yaml exists; this document uses it rather than
    creating it. Two parity contracts now cover CPU/GPU parity and need an owner -- a
    falsifier added to one does not constrain the other.
  - M3's threshold is no longer [U]: 0.98, measured n=5 on both hosts against a
    known-good and a known-bad pair, stdev 0.
  - Counts corrected everywhere: 4 of 21 present, 17 absent, re-tested with find against
    origin/main ebc9e9d81.

The review's GT-1 is amended in the same commit, and it overtakes finding F5: the
draft's contract name was right and the file exists. F5 becomes "two contracts now cover
one invariant" rather than "do not mint it".

No RD is decided.

Pmat-Ticket: PMAT-1092

* docs(review): land the GT-1 re-measurement the last commit's message claimed

dc9983593 said "The review's GT-1 is amended in the same commit". It was not. The edit
targeted PMAT-1092-review.md; GT-1 lives in ground-truth.md, so the assert fired, the
python exited 1, and git commit ran anyway. set -euo pipefail was on the first line and
did not stop it.

That is the third time in this branch a commit message has described an edit that did
not happen -- e7244c3ab, 6e14065c0, and now dc9983593 -- and every one was caught the
same way: by checking the file afterwards rather than trusting the script exited. The
lesson is not "remember set -e". It is that in this environment the shell's exit
handling cannot be relied on, so the check has to be explicit and the commit has to be
gated on it. This commit was.

The content: GT-1 now carries the 2026-09-09 re-measurement against origin/main
ebc9e9d81 -- 4 present, 17 absent -- names the three that #3026 shipped, and records
that this overtakes finding F5. The original c04eda87d measurement is preserved below
it, because the review's reasoning was built on that number.

Pmat-Ticket: PMAT-1092

* plan(release-process): v0.4 plan, and the teamwork review that broke its main argument

Sequence: agy /grillme on the v0.3 spec -> a plan -> agy /teamwork on the plan. All
three ran; both lanes returned do-not-implement-as-written, and both were largely right.

The grill called the spec over-engineered for a project shipping via cargo install:
delete the physical dogfood, delete the yoga runner, close the open decisions, add
rollback, cut the ratio. The plan accepted four of five and rejected "abandon physical
dogfooding", arguing that recorded-evidence sentinels test the TREE while #2696 (the
published binary silently ignoring --gpu) was a defect in the TARBALL.

The teamwork lane broke that argument, correctly. A CI job can download the release
asset, install it and run it -- so "test the artifact, not the tree" does not imply
"test it by hand on owned hardware". Those are two claims and the plan ran them
together. The real residue is only what needs a GPU: CI has none, so the --gpu-refused
and install-and-run checks go to hosted CI against the downloaded asset, and the two
classes that genuinely need silicon (#2696's live fallback, #2971's divergence) go to
the cuda-nightly lane that already runs on gx10 under infra#359 Option A. The plan
deleted section 7 without saying where live-GPU release checks land; accepting Option A
implies it, but implying is not stating.

Three more accepted: gx10 stays, because dropping the only aarch64 host while shipping
an aarch64 artifact is a regression and closes RD-6 by omission rather than by decision;
check_release_receipts.sh survives, because deleting the enforcement while keeping the
receipt is the pendulum swinging; and rollback needs a fire drill -- an automated yank
exercised on a dummy release -- which is the right analogue of this project's registered
failing mutation, since a rollback nobody has executed is a rollback that does not work.

Net: the same reduction in machinery, reached by automation instead of amputation.

Not resolved: the lane's Q6 (what the grill and the plan both miss) came back uncovered.

Pmat-Ticket: PMAT-1092

* spec(release-process): v0.4 — 706 lines to 429, by automating what v0.3 wanted done by hand

Cut from the plan the /teamwork lane revised. The grill said the spec was
over-engineered for a project shipping via cargo install; the plan rebutted one of its
five findings; the teamwork lane broke the rebuttal. v0.4 is what survives.

The idea the whole document now rests on, and nothing else: verify the artifact a user
installs, not the tree that produced it. v0.3 read that as "run the models by hand on
owned hardware". It does not follow -- CI can download the asset, install it and run it.
Only two checks need silicon CI does not have, and a lane for them already exists.

  section 4 is now a table of what needs a GPU and what does not. Install-and-run,
  the registry readback, artifact identity and FX-16/17/18 go to hosted CI against the
  downloaded asset. The live --gpu fallback (#2696) and CPU/GPU parity (#2971, C14) go
  to cuda-nightly on gx10, which is what infra#359 Option A left in place. No step is
  manual except signing and publishing, which hold keys.

  section 7 of v0.3 -- a PR-time GPU runner on a laptop-class box on a public repo -- is
  deleted, and is now refusal 9.8. RD-3 governed it and is RETIRED rather than dropped:
  the row is struck through, says it was not answered, and records that a future proposal
  must still answer infra#359's intermittency cost.

  gx10 is kept. The first plan cut it to one host, which would have closed RD-6 by
  omission; dropping the only aarch64 host while shipping an aarch64 artifact is a
  regression, not a decision. RD-6 stays open.

  Rollback is new and is the hole the grill was most right about: v0.3 had ten refusals
  and no answer to "the release is out and it is bad". Detection criteria, a five-step
  path, and the part that makes it real -- a fire drill on a schedule that cuts a
  throwaway prerelease and executes the un-promote end to end. A rollback nobody has run
  is a rollback that does not work, and doctrine 1 does not exempt it. It also measures
  the number nobody currently knows: detection to users no longer being served the bad
  artifact, [U] until the first drill.

  Host slots deleted -- with the work on CI and an existing lane there is no fleet to
  schedule. check_release_receipts.sh kept, because deleting the enforcement while
  keeping the receipt is the pendulum swinging. RD-8 recommended closed by argument
  (cross-machine bit-reproducibility needs a toolchain this project is not building;
  same-machine determinism is what M1 actually checks), team to confirm.

Absent artifacts 17 -> 12. Decisions 9 -> 8, one of which is recommended closed.

Checked before committing: no malformed table rows, no dangling section references
(four were wrong after renumbering, including the rollback guard pointing at a section
that no longer existed), and RD-3 accounted for rather than vanished.

Pmat-Ticket: PMAT-1092

* spec(release-process): every open decision now has a recommended answer, an argument and a falsifier

Four revisions and thirteen review rounds moved none of the eight open decisions,
because every reviewer correctly said "not mine to decide". Careful had become the
failure mode. So this round asked an agy /grillme lane for POSITIONS, not objections.

It answered all eight. Assessed here, not pasted:

  ACCEPTED. RD-2 close (default -cuda, print the reason, --backend overrides; FX-16
  covers the present-but-broken driver). RD-8 close permanently out of scope, with the
  cost named -- a compromised toolchain on intel stays undetectable precisely because
  the binary cannot be reproduced elsewhere. RD-9 close. RD-6 ship with the single-host
  limit stated, and the lane's supporting point is the best thing said about that row in
  four revisions: the x86 pipeline is ALSO single-capability, since yoga and lambda-labs
  are both sm_89, so demanding a cross-check for aarch64 while accepting one for x86 is
  inconsistent.

  ACCEPTED WITH THE CITATION CORRECTED. RD-4: a GitHub Actions concurrency group shared
  by the nightly lane and any perf job. Three lines, no new machinery. The lane argued it
  from check_host_slot.sh and "a host holds one release role at a time" -- neither string
  exists in v0.4, because the slot scheduler was deleted. Right mechanism, invented
  citation.

  ACCEPTED, AND IT IS THE HARD ROAD. RD-5: block the tag, delete the UNSERVICEABLE
  hatch. M3 exists because of #2971, so an escape hatch on that one gate makes it theater
  by doctrine 1, and it contradicts the jidoka target outright. The lane's own falsifier
  is the question Noah has to answer first: if #2971 is architecturally unfixable,
  blocking halts every release indefinitely and the hatch is the only honest option.

  PARTLY REJECTED. RD-1: drop darwin and windows (no verification host, and refusal 1
  forbids publishing what nothing can run -- decisive, needs no measurement) and build
  without musl. But its fourth answer, a new release-assets.yml on self-hosted runners,
  is stale: it reasons from v0.3, and names yoga, which this process no longer uses.

  REJECTED, AND THE QUESTION GOT SHARPER. RD-7: the lane derived the floor from the
  OLDEST architecture -- sm_89, PTX ISA 7.8, driver 525.60.13 -- while its own RD-6
  answer ships an sm_121 artifact that no PTX 7.8 floor can express. Its Table 62 row was
  recalled, not cited. The inconsistency exposes the real question, which nobody had
  asked: is the floor GLOBAL or PER-ARTIFACT? Decide that and one lookup gives the
  number.

Blocking set corrected too. The lane said RD-1, RD-4, RD-5, RD-7 block and RD-6 does
not. RD-4 is now three lines of YAML and RD-6 decides whether an artifact ships at all,
so the blocking set is RD-1, RD-5, RD-6, RD-7.

Nothing is decided. The owner column is unchanged. What changed is that eight open
questions are now eight decisions ready to be taken, each with the argument for it and
the thing that would make it wrong.

Pmat-Ticket: PMAT-1092

* spec(release-process): v0.5 — four decisions taken, and RD-7 was already implemented

Operator decisions, 2026-09-09.

RD-1: four artifacts. x86_64 and aarch64, linux-gnu, times cpu and cuda. Darwin and
windows dropped -- no verification host, and refusal 9.1 forbids publishing what nothing
can run. musl dropped: it doubles the matrix and interacts badly with the dlopen of
libcuda.so.1, though binary-release.yml keeps shipping it for pv. apr-cli opts into that
workflow rather than getting a new one. PP-066 C13 says five targets and must be amended
to four in the implementing PR -- that is now written in two places so it cannot be
missed.

RD-5: the tag blocks on #2971 and the UNSERVICEABLE hatch is deleted. This resolves the
contradiction the document has carried since v0.1, where section 8's jidoka target said
zero user-found divergences while section 4.4 permitted shipping the known one. The
falsifier is recorded: if #2971 turns out to be architecturally unfixable, blocking halts
every release, and that has to come back as an explicit dated exception rather than a
standing hatch.

RD-6: arm-gpu-cuda ships with its single-host limit in the release notes. The argument
that settled it is that the x86 pipeline is ALSO single-capability -- yoga and
lambda-labs are both sm_89 -- so requiring a cross-check for aarch64 alone was
inconsistent. Jetson Orin sm_87 is the named risk and the 0.67 fleet ask.

RD-7 was asked as "whatever is idiomatic for Hugging Face". That turns out to be what
crates/aprender-gpu already does, which I had not checked before proposing alternatives.
The HF/PyTorch wheel convention is one global floor at sm_70 with forward compatibility
by driver JIT. The code declares exactly that: MIN_PTX_VERSION (7,0), validate_target
rejects sm_ below 70, and as_module() carries the comment "Uses sm_70 (Volta) as minimum
baseline for broad compatibility". So the floor is GLOBAL, not per-artifact -- which
answers the question the previous round said nobody had asked -- and the .version is
DERIVED per module rather than declared: ptx_version_for_target() emits 8.8 for sm_100+
and 8.0 below (trueno#188), matching the cited PTX ISA release note that 8.8 adds
sm_121.

The grillme lane's proposed answer to RD-7 was wrong twice over: it derived the floor
from the oldest architecture, which cannot express the sm_121 artifact its own RD-6
answer ships, and its Table 62 row was recalled rather than cited. The decision instead
ratifies the implementation and writes down the consequence nobody had stated: .version
8.0 requires a driver supporting PTX ISA 8.0, so the effective driver floor is around
r525, not sm_70's own r384. check_ptx_version.sh asserts that mapping.

RD-2, RD-4, RD-8, RD-9 remain recommended and unconfirmed; none blocks 0.66.

Pmat-Ticket: PMAT-1092

* spec(release-process): v0.6 — all eight decisions taken; two needed no work at all

RD-2, RD-4, RD-8 and RD-9 confirmed. Nothing in the document is waiting on a decision
now.

RD-2 is the only one of the four with work attached, and it is not separate work: the
installer defaults to -cuda with the reason printed and --backend overriding, which is
what install.sh (R-6 / PMAT-994) must implement when it is written.

RD-4 is ALREADY IMPLEMENTED and I proposed building it anyway. All three gx10 consumers
carry a job-level concurrency group perf-gx10 with cancel-in-progress false --
cuda-nightly.yml:82, qwen-story-daily.yml:60, silicon-nightly.yml:86 -- the group is
repo-wide which is what makes it work across workflows (PP-19 section 5.4), and
scripts/check_perf_concurrency_groups.sh enforces it: 36 jobs scanned, 5 perf-sensitive,
0 findings, PASS. I missed it twice with greps anchored to ^concurrency: against an
indented job-level block, and reported "none" both times before finally running the
guard.

RD-8 and RD-9 close with nothing to build.

The pattern worth naming, because it is now twice in one session: RD-7's answer was
already in crates/aprender-gpu and RD-4's was already in the workflows, and in both cases
this document proposed a change before anyone checked the tree. Both were caught by
reading the code rather than by review -- eleven quorum rounds, a grill and a teamwork
lane all discussed RD-4 and RD-7 without anyone opening the files. A spec that proposes
what already exists costs the implementer exactly the time this process exists to save,
and that is now written into section 10 rather than left as a lesson nobody recorded.

Residual risk on RD-4, since the guard cannot see it: a perf run started by hand over SSH
never enters the concurrency group.

Pmat-Ticket: PMAT-1092

* fix(guard): gpu and cuda stopped discriminating when yoga became permanent

yoga is rack-mounted and permanent now, so the fleet has TWO GPU runners, and both
carry `gpu` and `cuda`:

  gx10-blackwell  [self-hosted,Linux,ARM64,gpu,gx10,cuda,blackwell,gb10]
  yoga-gpu        [self-hosted,Linux,X64,gpu,cuda,yoga,ada]

check_runner_labels.sh listed both words as "discriminating". That was true when gx10
was the only GPU box; it is false now. Measured before the fix: a job pinning
`[self-hosted, Linux, gpu, cuda]` PASSED the guard and can land on either machine. That
is #2269 again with a new pair of boxes -- a selector that looks pinned, lands anywhere.

DISCRIM drops `gpu` and `cuda` and gains the host labels that actually name one box:
clean-room|perf-solo|gx10|yoga|rtx4090|ada|blackwell|gb10|apple-silicon|m4. perf-solo is
added because intel-clean-room-16 carries it INSTEAD of clean-room, so a job pinning it
would have been failed for naming a real single host.

Every selector in the tree today still passes; only the ambiguous shapes newly fail. The
failure message now says why: "[self-hosted, gpu, cuda] is NOT pinned: gx10 and yoga both
carry those. Name the host."

Ships a case table, per the house rule that a guard regex ships one -- twelve rows, both
polarities, `--self-test`. Two things it caught while being written, both mine:

  - The helpers were `grep ... || {...}` / `grep ... && {...}`. Under set -e a must_fail
    whose grep does not match returns non-zero from the && chain and aborts the function
    mid-table, so the table reported NOTHING and rc=1 looked like a real failure.
    Rewritten with if.
  - The first mutation harness put the script one directory too high, so `cd
    "$(dirname "$0")/.."` scanned the wrong tree and the mutant PASSED vacuously. The
    end-to-end table now runs against a real .github/workflows file: seven selectors,
    four expected-pass, three expected-fail, all correct.

Also `grep -rn` -> `grep -rHn`: with exactly one matching workflow file grep omits the
filename, so IFS=: read the line number into $file and the annotation pointed at nothing.
Harmless in this repo today and wrong the moment a fixture has one file -- which is
exactly how it surfaced.

bashrs: 0 errors.

Pmat-Ticket: PMAT-1092

* fix(guard): runs-on behind an expression was invisible — nothing stopped a matrix entry routing to a GPU box

Answering "confirm normal jobs never reach gx10 or yoga". Today they do not: every
self-hosted selector in the tree pins clean-room (intel pool) or gx10+blackwell, and
NO job targets yoga at all. Fourteen workflow files, zero .yaml, so the guard's universe
is complete on that side.

But "never" was not enforced, because PASS 1 greps for `self-hosted` on the runs-on line
and `runs-on: ${{ matrix.runner }}` carries no labels there. nightly.yml:63 is exactly
that shape. The job was neither checked nor reported -- it was skipped silently, which is
the shape this repo keeps getting caught by: a guard whose universe excludes the case that
would fail it. Today every value in that matrix is GitHub-hosted, so the tree is safe,
but that is a fact about the current file and not a guarantee. Adding one self-hosted
entry to it would route a build to a GPU box with nothing going red.

PASS 2 resolves the referenced matrix key and checks every value it can take:

  - a GitHub-hosted value is fine and skipped
  - a self-hosted value with a discriminating label passes (clean-room, or a deliberate
    yoga pin for the CUDA unit tests)
  - a self-hosted value without one FAILS, naming the value
  - an expression that is not matrix.X FAILS -- unresolvable is not proven safe
  - a matrix key with no resolvable values FAILS

Fail-closed on both unknown branches, which is the point: the previous behaviour was to
say nothing at all.

Verified end to end against a real workflow file, five cases, both polarities:
bare [self-hosted, Linux, ARM64] in nightly's matrix -> RED with the value named;
clean-room -> green; a deliberate yoga pin -> green; ${{ vars.SOME_RUNNER }} -> RED;
matrix.nosuchkey -> RED. Real tree unchanged at green, PASS 1's 12-row self-test still
green, bashrs 0 errors.

What this still does NOT enforce, stated rather than implied: the labels themselves are
assigned at the runner, not here, and `self-hosted`/`Linux`/`X64` are auto-assigned and
unremovable -- so the guarantee is "no workflow in this repo selects a GPU box without
naming it", not "GitHub cannot schedule one there". The org runner groups still report
restricted_to_workflows: false, which is the remaining GitHub-side gap and is infra work,
not a guard.

Pmat-Ticket: PMAT-1092

* fix(ci): cuda-nightly names gx10 — `gpu` stopped naming one box when yoga was registered

infra#494. The opt-in label a GPU selector must carry was `gpu`, enforced by
machines/clean-room/gx10-opt-in-guard.sh in paiml/infra. yoga is now a permanent
runner declaring `runner_labels: "gpu,yoga,cuda,ada"` against gx10's
`gpu,gx10,cuda,blackwell,gb10`, so `gpu` is on both boxes and no longer names one.

This selector was `[self-hosted, gpu, Linux, ARM64, cuda, blackwell]`. Only ARM64
kept it off yoga -- an architecture label doing the work of a host label, which is
exactly what `cuda` did before `gpu` and failed the same way against lambda-4090.
Measured with the infra guard under its new host-label rule: aprender had exactly
one finding, this line. With `gx10` added, zero.

Ordering matters and is why this is a separate commit: the infra guard sweeps the
org, so if that change merges first this repo goes red. This lands first.

aprender's own scripts/check_runner_labels.sh still passes; it enforces the same
idea one level down and already dropped `gpu`/`cuda` from its discriminating set
in an earlier commit on this branch.

Pmat-Ticket: PMAT-1092

* spec(release-process): v0.7 — support 0.67's CUDA-Rust direction, and record the toolkit flip it caused

0.67 (#3061, #3062) is NVIDIA CUDA Rust integration for GPU quality stabilization.
Three of its items touch this document and two already happened, so this is a
factual update as much as an alignment.

gx10's toolkit MOVED on 2026-09-09, one day after the S0 ledger was taken. The
operator ordered cuda-toolkit-13-3 to unblock T3 (cutile-rs on sm_121). Re-measured
on the box: nvcc and ptxas are 13.3, /usr/local/cuda resolves to /usr/local/cuda-13.3,
13.0 still on disk, and the DRIVER IS UNCHANGED at 590.48.01 -- no kernel-driver
package was installed. /usr/local/cuda is a Debian alternatives link rather than a
plain symlink, so registering 13.3 at priority 133 against 13.0's 130 flipped the
default and took the CI PATH with it; the 0.67 spec records that its own first report
of a side-by-side install was wrong. GT-2 carries the correction and the one-command
rollback.

Every GPU cell in section 4.1 runs on that lane, so the release now verifies under
13.3. That makes 0.67's section 9.5 post-upgrade regression gate -- aprender-gpu
--features cuda green on gx10 under 13.3 -- a prerequisite for trusting a release
receipt from that host, not a separate concern.

T0 un-darks aprender-gpu in workspace-test, and the honest number matters: 444 of
2,620 tests, the CPU-reachable 17%. About 2,176 sit behind --features cuda and need
hardware. So T0 does not make GPU code covered; it…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

inst:A PP-066 instance claim (I14): inst:A L0-1a PP-066 DAG row L0-1a (#2971) pp-066 PP-066 (0.66) DAG row

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant