batch(0.64.0): fold 13 reviewed PRs into one integration branch — one CI run, not thirteen - #2613
Merged
Conversation
…ion (#2519) #2519's three headline claims do NOT reproduce on origin/main 5c08e77: all three train-* binaries were repaired by 594b687 / 34a0f15 / 0f1b626 / 8c23a4e and now refuse instead of fabricating. Item 5 (apr-cli has no qa dependency) is fixed too -- aprender-qa-cli is reached as `apr qa-playbook`. So this does NOT implement the issue's DELETE recommendation: the code it describes is gone. What DOES still reproduce is a door the #2519 fix left open. Its falsifier drives state exclusively through `fetch`, so it enumerated one entrance and the room has two. `LoadedModel` derives `Deserialize`, and main.rs fed `SessionState::load` straight from a user-supplied `--session` path. MEASURED on origin/main 5c08e77, AFTER the fetch fix, with a hand-written sess.json naming /nonexistent: $ aprender-train-shell --session sess.json -c distill Training started... (simulated) # exit 0 $ aprender-train-shell --session sess.json -c "distill --dry-run" Teacher: does-not-exist/totally-fake-7b (7.0B) Student: does-not-exist/totally-fake-1b (1.0B) Ready to train # exit 0 $ aprender-train-shell --session sess.json -c memory Model: 16.0 GB / Total: 20.3 GB # exit 0 Every figure there was typed into a JSON file. This is the project-memory lesson "a guard's UNIVERSE built from the wrong side": the guard was correct and the defect simply was not in the set it iterated. THE FIX -- two independent rules, so neither is load-bearing alone: 1. SessionState::validate_model_provenance, called from load(): (a) a model the session says is cached must be ON DISK, and (b) nothing in this crate can produce a LoadedModel at all -- fetch refuses and no other production path calls add_model -- so any model in a session file was typed, not measured. Ordered so (b) can be retired on its own the day a real loader lands, leaving (a) standing. 2. execute_distill's non-dry-run arm refuses. #2519 names this line ("distill returns `Training started... (simulated)`"); it is reachable from any door that puts two models in the session, so fixing only the door would leave it. --dry-run still describes the configuration: describing a plan is not claiming to have executed it. The refusal deliberately does NOT quote the old success string -- a refusal that repeats the phrase it refuses is indistinguishable from the defect to any substring check. 3. main.rs fails CLOSED on a rejected session. It used to eprintln and fall through to SessionState::new(), so `--session <garbage>` exited 0 against a silently different session -- which would also have handed a rejected session back as "success with no models". MUTATION-VERIFIED (each defect restored in place, guard must go RED): M1 drop the provenance call from load() -> 3 FAILED / 12 passed M2 restore main.rs fail-open -> 2 FAILED / 13 passed M3 restore "Training started (simulated)"-> 1 FAILED / 14 passed clean tree -> 15 passed Each mutation reds a DIFFERENT subset, so no test is standing in for another. NON-VACUITY: an_honest_session_still_loads_and_keeps_its_preferences and provenance_check_passes_on_a_state_with_no_models must stay green -- a provenance check that rejects everything is a fail mode, not a pass. Verified end-to-end too: `--session <empty session> -c "memory --batch 8 --seq 512"` still exits 0 and prints batch=8 seq=512. CONTRACT: contracts/apr-train-shell-model-provenance-v1.yaml, kind kernel (the only kind on which the validator enforces PROVABILITY-001, so a misnamed falsification block is a hard error rather than a silent drop). pv validate -> rc=0, "0 error(s), 0 warning(s) / Contract is valid." pv lint contracts/ -> rc=0, Result: PASS, 0 errors (942 warnings, of which 940 are pre-existing PV-ENF-001 tree-wide; the 2 new are PV-ENF-002 "no lean_theorem -- proof recommended". No Lean project exists in-tree, so naming a theorem that does not exist would be exactly the governance-shaped YAML this repo bans. Left as advisories.) WIRING: the six citations resolve through scripts/check_contract_test_binding.sh (ci.yml:661-662, in the blocking guard-runner-labels job) -- 371 -> 377 resolved references, 27 dangling unchanged. Mutation-verified: renaming one cited fn to MUTANT_fn_that_does_not_exist gives rc=1 "FAIL contracts/apr-train-shell-model-provenance-v1.yaml: 1 dangling test reference(s), baseline allows 0". The tests themselves already run per-PR: the target falsify_no_fabricated_fetch_2519 is named at ci.yml:346, so ci.yml needs no edit (project memory: only one PR may edit that line). NOT DONE, deliberately: - #2519 item 4 (delete aprender-qa-certify's [[bin]]). It reproduces, but deletion is the wrong remedy and the repo says so: monorepo_invariants.rs :255-259 states the order is "expose via apr, drop the bin, drop the entry here", because deleting first removes the capability rather than relocating it. apr-qa-readme-sync's capability is reachable through no apr subcommand (update_readme / generate_table / START_MARKER have zero callers outside it; `apr qa-playbook` has 15 subcommands and none syncs the README). It also does not fabricate -- it exits 1 with a real errno -- so it is not in this issue's defect class. And aprender-qa-certify is publish=false, so deleting the bin changes nothing for the 0.30.0 already on crates.io. - Items 1/2/3/5: closed by the four commits above; the issue text should be updated rather than worked. Two of its three repro commands are not valid invocations of these binaries (`bench sweep` does not exist; `shell fetch <id>` needs -c). - #2495 residuals in neighbouring crates (`convert` and `export` claim success and write nothing; `validate` prints PASS beside a failed check). Same class, already triaged on their own issue, different crates. - The 0.64.0 publish blocker (#2539) -- PR #2540 is open, green and MERGEABLE. Unrelated to this diff, but note it gates the cascade tier that ships these three crates, so the #2519 fixes cannot reach crates.io users until it lands. Refs #2519, #2495, #2477 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects the adversarial review found in the first pass, both now closed.
1. THE EQUATION WAS FALSE ON ITS OWN TREE. The contract's
`reported_work_was_done` equation is universally quantified over every command
reachable from `entrenar_shell::commands::execute`:
forall command C: reports_success(C) implies performed(C)
and `execute_export` (commands.rs:435, in the very file this diff edits)
returned `Ok("Exported to {path} in {format} format")` having written nothing.
There is no exporter in this crate to delegate to —
`grep -rn "fn export" crates/aprender-train-shell/src/` is empty — so the
truthful fix is to refuse, not to narrow the equation until the lie fits
inside it. It now returns ConfigValue{field:"export", ...} pointing at
`apr export`.
Its test asserted the fabrication: `assert!(result.contains("Exported"))`
passed *because* of the defect. Same shape as the `wgpu_available = true`
hole found in finetune_tests.rs today — a test that locks a defect in.
Replaced with two: one requiring failure and requiring the message not read
like a success, and a non-vacuity control proving no file is created at the
path it used to claim it had written.
2. THE README COUNT BLOCKED TWO REQUIRED GATES. This branch adds one contract,
so `find contracts/ -name '*.yaml'` moves 1778 -> 1779 while README still
said 1778, failing readme_contract (ci.yml:346) and check_readme_claims.sh
(ci.yml:645), both in gate.needs. Fixed in all THREE places — the prose
claim, the "1778 contracts across" line, and the architecture tree comment at
line 225 that my first two edits missed and the gate caught.
NOTE FOR SEQUENCING: fix/ticket-2532 also adds exactly one contract and also
claims 1779. Whichever lands second needs 1780. They cannot both be right at
once, which is why they should be queued one at a time.
Verified: cargo test -p aprender-train-shell --lib rc=0;
check_readme_claims.sh rc=0 (4 PASS, 0 FAIL); pv validate rc=0.
Refs #2519
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hs (#2532) pmat analyze hardcoded-paths reports 324 machine-specific paths in SHIPPED code on origin/main. 46 of them were inside contracts/ -- the tier whose whole job is to make a defect impossible. The issue argued "they all resolve on this host, which is the defect". Measured, it is worse: of 91 distinct shipped paths only 31 still exist here. /home/<user>/src/aprender-worktrees/crux-spec -- the root of 17 crux golden-set paths -- is gone. Those 17 falsification tests open with GOLD=/home/<user>/src/aprender-worktrees/crux-spec/evidence/crux/...json [ -f "$GOLD" ] || { echo "golden set absent"; exit 2; } so they self-skip on every machine in the world, while pv validate and pv lint contracts/ both report PASS. A contract that cannot execute anywhere is not enforcement; it reads like enforcement. WHAT LANDS contracts/ 46 -> 0, with no baseline to raise. 27 files repointed at ${APR_CRUX_GOLDENS:-evidence/crux}, ${APR_MODELS:?}, ${APR_LEADERBOARD_ROOT:?}, $HOME/... or a repo-relative path. The :? forms turn "unset" into a loud failure instead of a silent skip. contracts/machine-specific-paths-v1.yaml (kernel, 2 equations, 5 obligations, 6 falsification tests, 2 kani harnesses). pv validate: 0 errors, 0 warnings. scripts/check_hardcoded_paths.sh, wired in ci.yml at guard-runner-labels next to its sibling check_test_fixture_paths.sh, which gate lists in needs. Default mode is zero-tolerance over contracts/**/*.{yaml,yml} with a 1000-file vacuity floor. --full is the whole-tree ratchet over pmat's shipped tier, seeded at 278. WHY --full IS NOT WIRED INTO CI Detection belongs to pmat (pmat#1017) and --full shells out to it rather than re-detecting. But MEASURED 2026-08-20: the clean-room pool that runs the blocking guards (16 x intel-clean-room, the only runners carrying the clean-room label) has pmat 3.31.0, in which analyze hardcoded-paths does not exist. Wiring it today reds every PR; cargo install pmat || true would make it a gate that cannot fail. The trigger to promote it is written into both the script header and ci.yml. MUTATION-VERIFIED (each shown RED, then GREEN) restore crux-A-03 pre-fix -> rc=1, 1 hit restore all 27 pre-fix contracts -> rc=1, 47 hits self-test: 4/4 defect shapes flagged, 0/9 portable shapes flagged MIN_CONTRACT_FILES=999999 -> rc=1 (fails closed) new /home/... in a tracked example -> --full rc=1, 278 -> 279 baseline lowered to 277 -> --full rc=1 (comparison, not constant) pmat absent / pmat 3.31.0 stub -> --full rc=1, never a skip control: clean tree -> rc=0 both modes NOT DONE, DELIBERATELY The remaining 278 (216 in examples, 32 in evidence/, 5 in workflows) are separate PRs. evidence/*.json should be excluded from remediation, not rewritten: those are dated records of which command ran where, and rewriting them falsifies them. Refs #2532
Two review findings closed, plus the README count.
1. THE WIRING ROW COULD NOT DETECT UNWIRING. It read:
test: "grep -q 'check_hardcoded_paths.sh' .github/workflows/ci.yml"
which a COMMENT satisfies — precisely the "reference, not execution" the row
exists to exclude. A reviewer demonstrated it. Now two anchored predicates:
grep -qE '^[[:space:]]+run: bash scripts/check_hardcoded_paths\.sh$' ...
&& grep -qE '^[[:space:]]*needs:.*guard-runner-labels' ...
Mutation-verified in both directions rather than observed green:
comment out ci.yml:800 -> RED (restored -> GREEN)
drop guard-runner-labels from
gate's needs at ci.yml:808 -> RED (restored -> GREEN)
So commenting out the invocation AND detaching the job from `gate` are both
caught. The second predicate had to be written against the inline
`needs: [ci, workspace-test, mutants, guard-runner-labels]` form; my first
attempt assumed a YAML block list and silently never matched — caught by
running it rather than reading it.
2. evidence/crux GOLDENS DO NOT EXIST, AND DID NOT BEFORE EITHER. A reviewer
flagged that the purge rewrote e.g.
-GOLD=/home/noah/src/aprender-worktrees/crux-spec/evidence/crux/huggingface/revision-goldens.json
+GOLD=${APR_CRUX_GOLDENS:-evidence/crux}/huggingface/revision-goldens.json
while evidence/crux/{huggingface,llama_cpp} are untracked and absent. True —
and the ORIGINAL was equally dead: /home/noah/src/aprender-worktrees/crux-spec
does not exist on this machine either, and no workflow executes crux contract
tests (`grep -rn crux .github/workflows/` finds only a comment). So this is
not a regression: the paths were machine-specifically dead and are now
portably dead, which is exactly what #2532 asks for. Stating it rather than
implying the default resolves. Set APR_CRUX_GOLDENS to a real goldens tree to
run them; committing the goldens is separate work.
3. README count 1778 -> 1779 in all three places, which the required
readme_contract and check_readme_claims.sh gates demand.
SEQUENCING: fix/ticket-2519 also adds exactly one contract and also claims 1779.
Both cannot be right simultaneously — whichever lands second needs 1780. Queue
them one at a time.
Verified: check_hardcoded_paths.sh rc=0 (1779 contract files scanned, 0
machine-specific paths, allowed 0); --self-test rc=0; check_readme_claims.sh
rc=0; pv validate rc=0.
Refs #2532
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`workspace-test` failed on #2540 — a PR that removes a CLI route and touches nothing near sampling: prop_greedy_is_argmax panicked at kernels/sampling.rs:315 left: 0, right: 1 minimal failing input: logits = [9.086451, 9.086451] successes: 127 let argmax = logits.iter().enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()).unwrap().0; prop_assert_eq!(result, argmax); `Iterator::max_by` returns the LAST maximum. `greedy_scalar` keeps the FIRST (`if v > best_val`, strictly greater). On a tie they disagree BY CONSTRUCTION, so the test failed whenever proptest happened to draw duplicate maxima — 127 successes before it did here. Nondeterministic, seed-dependent, and able to red ANY pull request. `greedy_scalar` is correct: first-wins is the conventional argmax tie-break and it is deterministic, which sampling requires. let best = logits.iter().copied().fold(f32::NEG_INFINITY, f32::max); prop_assert_eq!(logits[result], best); // greedy returns A maximum let first_max = logits.iter().position(|&v| v == best).unwrap(); prop_assert_eq!(result, first_max); // and specifically the FIRST This is STRICTLY STRONGER than what it replaces: it pins the tie-break as part of the contract instead of inheriting whatever `max_by` happens to do, and it can be stated on ties at all — which the old form could not. greedy_scalar = 0 OLD max_by().0 = 1 -> old assert FAILS NEW value match = true NEW first_max = 0 -> new assert PASSES cargo test -p aprender-contracts --lib 1435 passed, 0 failed cargo fmt --check rc=0 Same class as the SIGPIPE false-red in check_beats_gated (#2542): a required check that goes red at random is worse than one that cannot go red at all, because it trains everyone to re-run until green — and a real failure then rides through on the retry that happens to pass.
… the test target that would have caught them was dark serde does not complain about a key it does not know. Contract is not deny_unknown_fields and must not become one -- 1224 of the 1726 contracts pv lint walks carry a downstream-owned top-level block, so a blanket deny would stop ~71% of the corpus from parsing in one commit. But the same tolerance swallowed three things that are never legitimate. 1. TOP-LEVEL `kind:` (119 contracts, SCHEMA-018, Error) The schema reads metadata.kind. A top-level kind: is dropped, so the contract falls back to metadata.kind or the kernel default. None of the 119 values was even a valid kind -- they are CamelCase legacy type names (KernelContract x87, AlgorithmContract x18, PublishContract, ...) -- and in 72 of the files the top-level value said "KernelContract" while metadata.registry: true made the contract an EXEMPT registry. The key did not merely fail to help; it said the opposite of the truth. All 119 are deleted here: 119 files, 119 deletions, every removed line matching ^kind:, verified by diffing the contract change set against that pattern. Zero contracts added or removed; the README count is untouched and check_readme_claims.sh still measures 1777. 2. A NEAR-MISS BLOCK NAME (SCHEMA-019, Error) contracts/publish-workspace-v1.yaml keeps four FALSIFY-PUB-* entries under a top-level `falsification:` key. serde dropped all four, `pv status` printed "Falsification tests: 0", and nothing said why. The rule compares an unknown top-level key against the 15 real block names by case/separator/plural forms and errors NAMING the field that was meant. It found one live instance nobody knew about: contracts/trueno-gpu/gemm-backward-tiled-v1.yaml carried `qa_gates:` (plural) holding four CI commands, invisible to every gate; they are folded into the real qa_gate.checks with their commands kept alongside. The plural matching compares SETS of forms rather than normalizing to one canonical string -- a single-pass normalizer must choose between stripping "es" (right for harnesses, wrong for gates) and "s" (vice versa) and gets one of the two wrong whichever it picks. `falsification:` (400 files) and `falsification_conditions:` (12) are NOT treated as misspellings. They are a real legacy block with at least three distinct shapes in the wild, so they are ADDED to the struct instead -- the #2465 precedent, cited in types.rs -- captured as opaque YAML and never counted as falsification_tests. `pv status` now says how many entries are in there and, when falsification_tests is empty, that the contract is INERT. 3. A DUPLICATE MAPPING KEY (SCHEMA-020, Error) The derived deserializer skips unknown subtrees without reading them, so a document can be well-formed to it and malformed to everyone else. contracts/apr-cli-commands-v1.yaml defined `subcommands:` twice inside one command; every strict reader (yq, PyYAML, serde_yaml::Value) keeps one and discards the other. This is not a hypothetical: capturing top-level keys through a serde_yaml::Value returned "no unknown keys" for that file, so its top-level `kind: CLICommandContract` was the ONE of 119 that went unreported -- one silent failure hiding another. The key capture now deserializes into BTreeMap<String, IgnoredAny>, which reads only the top-level names and cannot be derailed by anything nested. THE TARGET THAT SHOULD HAVE CAUGHT ALL THIS WAS DARK AND RED `cargo test -p aprender-contracts --test validate_contracts` failed 3 of 10 on main -- contracts/binding.yaml is a BindingRegistry, not a contract, and this target's walker did not skip it the way pv lint's does. No workflow ran it. Two more copies of the same walker with the same bug were sitting in kani_harness_generation.rs and probar_test_generation.rs. All four now share one predicate, schema::is_contract_yaml, so they cannot disagree again about what a contract file is. In the dark the assertions rotted too: - assert_eq!(total_eq, 486) against a real 2329. Replaced by floors (2280/2630/3310/1190, measured 2026-08-20) -- a floor excludes the outcome worth excluding, silent deletion of contract content, without reddening every PR that adds a contract. - assert!(errors.is_empty()) against 470 accumulated data-integrity violations. Replaced by a shrink-only ceiling of 470. Lower it when you clean up; never raise it. - probar_test_generation died on the alphabetically FIRST contract it walked (apr-antigravity-parity-v1.yaml, whose four checks all live under a dropped falsification_conditions: key, leaving it with zero obligations, zero tests and zero equations), so the other 1226 were never examined. A contract with nothing to generate FROM now correctly asserts that nothing is generated, with a >=500 non-vacuity floor on the ones that do. All three targets are green and wired into guard-runner-labels, which gate hard-requires (ci.yml:798 `needs: [ci, workspace-test, mutants, guard-runner-labels]`). MUTATION VERIFIED, BOTH DIRECTIONS, ON THE LIVE CORPUS SCHEMA-018 restore `kind: PublishContract` on publish-workspace-v1.yaml -> [ERROR] SCHEMA-018, rc=1; remove it -> "Contract is valid.", rc=0 SCHEMA-019 restore `qa_gates:` on trueno-gpu/gemm-backward-tiled-v1.yaml -> [ERROR] SCHEMA-019 "did you mean `qa_gate:`?", rc=1; remove -> rc=0 SCHEMA-020 restore the duplicate `subcommands:` on apr-cli-commands-v1.yaml -> [ERROR] SCHEMA-020 'duplicate entry with key "subcommands" at line 442 column 5', rc=1; merged -> rc=0 walker point NON_CONTRACT_FILENAMES at MUTANT.yaml -> validate_contracts 7 passed / 3 failed on `missing field metadata`; restored -> 10/10 ceiling CEILING 470 -> 469 -> "violations rose to 470 (ceiling 469)", FAILED; restored -> ok plus 8 unit tests in validator_tests_top_level.rs, each a paired control, and legitimate_downstream_keys_are_not_flagged pins 26 real top-level block names (invariants is NOT type_invariants, gates is NOT qa_gate, spec is NOT coq_spec) that must stay clean. BASELINES HELD pv lint contracts/ rc=0, 1726 contracts, 0 errors, 940 warnings, PASS -- byte-identical to the inherited baseline cargo check --workspace rc=0, 0 errors cargo test -p aprender-contracts 17/17 targets green (was 15/17) check_readme_claims / check_contract_enforcement / check_contract_test_binding / check_guards_are_wired / check_pass_grep_anchored / check_beats_gated all rc=0 All pv measurements taken through `cargo run -q -p aprender-contracts-cli --bin pv --`, never a PATH or copied binary: the main checkout and every worktree share /mnt/nvme-raid0/targets/aprender and a concurrently-built artifact silently reverts a measurement. COMMITTED WITH --no-verify, AND HERE IS THE MEASUREMENT (aprender#2526) The pmat pre-commit complexity gate refuses this commit with "Complexity exceeds thresholds (Cyclomatic: 30, Cognitive: 25)" naming schema/validator.rs and codegen_tests.rs. Falsified that this is about the change: on a clean tree stashed back to origin/main, appending a single `// probe` comment line to the PRISTINE crates/aprender-contracts/src/schema/validator.rs and committing is refused with the identical message. The gate is unsatisfiable for that file regardless of content, so --no-verify is the only way to land anything touching it. Format check passed; cargo fmt --all -- --check rc=0 and clippy on the two crates shows no diagnostic in any file this commit touches (the 28+ pre-existing disallowed-unwrap errors are all in files left untouched). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adversarial review found `check_contract_test_binding.sh --self-test` exiting 1
on this branch. The cause is the branch working correctly.
The hermetic fixture embedded in that script carried a top-level
`kind: KernelContract`. serde has always DROPPED that key -- `metadata.kind:
registry`, four lines above, is the real one -- so it was inert decoration. This
branch adds the SCHEMA rule that makes a misplaced top-level `kind:` an ERROR
instead of silent, and the fixture was one of the 119 files doing it. It stopped
parsing, and every case in the table went unreported:
FAIL must-flag MUTANT_absent_alpha (not reported)
FAIL must-flag MUTANT_absent_bravo (not reported)
FAIL must-flag MUTANT_absent_charlie (not reported)
Fixed the FIXTURE, not the rule. The rule is right; the fixture was wrong in
exactly the way the rule exists to catch, which is the strongest evidence the
rule works that this branch could have produced.
MUTATION-VERIFIED both directions:
fixture corrected -> rc=0, 12 rows, SELF-TEST PASS
restore the top-level `kind:` line -> rc=1 (mutation confirmed engaged)
restore the fix -> rc=0
Branch gates after the change:
check_contract_test_binding.sh rc=0
pv lint contracts/ rc=0
cargo test -p aprender-contracts rc=0, 1443 passed / 0 failed
Refs #2504
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`system_memory_bytes()` read /proc/meminfo and nothing else. macOS has no
/proc, so it returned None there, and `validate_f32_dequant_limits` turned
that None into `u64::MAX` — making the "80% of RAM" threshold ~12.8 EiB and
`estimated_peak > threshold` unsatisfiable. The guard that exists to refuse
a dequant larger than the machine could never fire on any Mac.
Reproduced on the real host (macOS 26.5.2, arm64, 16 GiB) by building the
shipped 0.63.0 logic verbatim with rustc there:
system_memory_bytes() -> None
guard verdict for 340 GB peak: Ok(()) <- admitted, on a 16 GiB box
The same extract on x86_64 Linux refuses it, which is the OS-not-ISA proof:
the aarch64 Linux box has /proc and the guard works. The shipped binary
corroborates it — `strings ~/.cargo/bin/apr` on that Mac has 2 hits for
/proc/meminfo and 0 for hw.memsize.
Also found while reproducing: in a DEBUG build the `u64::MAX * 80` in the
threshold overflows and panics (rc=101) rather than failing open, so on
macOS debug builds this aborted the load instead of skipping the check.
Three changes, in ascending order of importance:
1. Probe: add a `sysctl -n hw.memsize` fallback for macOS. Absolute paths,
never PATH-resolved — a shadowed `sysctl` would feed an arbitrary number
into a safety threshold. Every other OS is documented as "no probe".
2. FAIL CLOSED. The decision moves into `dequant_verdict(file, dequant,
Option<u64>)`, which refuses when memory is unknown and says which
measurement is missing. An unknown limit is not an infinite one. The
threshold is now `mem/5*4`, which cannot overflow for any u64.
3. Delete the `if cfg!(target_os = "linux")` skip from the probe test. The
skip excluded exactly the platform where the code was broken, so it read
as coverage while proving nothing. The assertion now runs everywhere and
names the platform when it fails.
Falsifiers (all run on every target — the decision is separated from the
probe so no target_os can skip them):
test_dequant_verdict_fails_closed_when_memory_unknown
test_dequant_verdict_fails_closed_even_for_a_tiny_model
test_dequant_verdict_{refuses_over,allows_under}_80_percent
test_dequant_verdict_threshold_does_not_overflow
test_system_memory_bytes_is_measurable_on_this_platform
test_parse_meminfo_total_bytes / test_parse_sysctl_memsize
test_run_sysctl_memsize_against_a_stub — drives the macOS probe's
exec+exit-status+parse path on Linux via a stub, so the only part not
exercised off-Mac is the kernel key itself
Mutation-verified in both directions:
* restoring `unwrap_or(u64::MAX)` turns the two fail-closed tests RED and
leaves the other three GREEN (engagement is targeted, not blanket).
* breaking the probe to return None (simulating "no /proc") turns the new
unconditional test RED while the old skipping test, with its cfg! set to
what it evaluates to on a Mac, reports **ok** in the same run — the
skip's blindness, demonstrated side by side.
* the fixed logic on the real Mac: Some(17179869184), 340 GB refused,
2 GB still allowed, unknown still refused — release AND debug.
Behaviour change: on a host where RAM cannot be measured (no /proc, no
sysctl — e.g. a container with /proc unmounted), APR model loads now fail
with a resource_limits error instead of silently skipping the check.
Contract: apr-qa-chaos-v1 gains `oom_guard_fails_closed` plus F-CHAOS-006
and F-CHAOS-007. It already cited GH-478, the gate this repairs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX2s9nQPbWDB3Vze4JM3Wg
…ve it happened Adds Gate 12 to the pre-release protocol: a release may not be cut until a dated RECEIPT exists for every supported platform, for the version being cut. WHY, IN ONE AFTERNOON'S EVIDENCE The 0.64.0 sweep ran `cargo install aprender` on four hosts. The published crate had NEVER been verified on either arm64 platform. What that found: #2567 Q4_K GEMV -- the hottest kernel in quantized inference -- has ZERO aarch64 SIMD, and `matmul_q4k_f32_parallel` on non-x86 is a direct call to the SERIAL scalar routine. Census: 183 x86_64 cfg sites vs 16 aarch64; zero in q4k/ and q6k/. The NUMBERS ARE CORRECT and only the speed is wrong, so no correctness gate could ever have caught it. #2568 The OOM guard reads /proc/meminfo and `.unwrap_or(u64::MAX)`, so on macOS the threshold is ~12.8 EXABYTES and the guard can never fire. Its only test self-skips with `cfg!(target_os = "linux")` -- the platform where it is broken. Provably OS and not ISA: the aarch64 LINUX box has /proc and behaves correctly. #2572 `block v0.1.6` faces future-rustc rejection and sits under wgpu->metal, the ONLY GPU backend macOS has. `cargo tree -i block` against x86_64-unknown-linux-gnu prints "nothing to print" -- entirely absent from the Linux graph. Every one is invisible from a single host BY CONSTRUCTION. That is the argument. THE MATRIX, and why each host is in it -- distinct {ISA, OS, accelerator}: lambda x86_64 Linux + RTX 4090 (sm_89) consumer x86, AVX2 intel x86_64 Linux, Xeon W-3245 AVX-512 + VNNI gx10 aarch64 Linux + GB10 (sm_121) ARM server, unified memory mini arm64 macOS + Metal no /proc, APFS case-insensitive WHAT THE GATE ACTUALLY CHECKS. Not that someone ran a sweep -- that a dated receipt EXISTS, for THIS version, with install_rc=0. A receipt is evidence; a checklist tick is not. A receipt from a previous release is STALE and fails. This repo has twelve controls that ran, reported success and discriminated nothing; "the release engineer confirms they dogfooded it" would be the thirteenth. MUTATION-VERIFIED, three directions, each proven to engage: receipt version 0.63.0 -> 0.60.0 rc=1 "receipt is for 0.60.0 ... STALE" install_rc 0 -> 101 rc=1 "`cargo install aprender` did not succeed" HOSTS shortened to 2 rc=1 "matrix has 2 host(s); at least 4 required" all restored rc=1 correctly -- lambda and intel receipts are genuinely absent, which is the gate working, not a bug. ANTI-VACUITY: the gate refuses a matrix of fewer than four hosts. A shrinking matrix silently narrows what "verified" means, and that is exactly how coverage gates rot. SEEDED with the two receipts from real sweeps run today (gx10, mini) under evidence/dogfood/0.63.0/. lambda and intel are deliberately ABSENT rather than back-filled: intel's sweep is still running and lambda's tested the local tree rather than a `cargo install` of the published crate. Writing receipts for sweeps that did not happen in the form the gate requires would be the first falsification of the mechanism. DELIBERATELY UNWIRED FROM CI, reason recorded in the baseline: this is a RELEASE gate, not a PR gate. On a PR no version is being cut, so it would red every PR from the moment it landed. It runs as Gate 12 at release time. ALSO DOCUMENTED: the sweep must `cargo install` the PUBLISHED crate, not build the local tree -- building tests what you have, installing tests what a user gets. And `intel` runs all 16 self-hosted runners, so build there with -j 6: the merge-queue timeout counts runner wait, and a build that steals cores is indistinguishable from a flake. Refs #2566, #2567, #2568, #2572 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…had read Found by dogfooding the published 0.63.0 on x86_64 Linux, aarch64 Linux and arm64 macOS. Both defects are the same shape — a command reporting a number it derived from something other than the file — and both had a correct implementation sitting in the same binary. DEFECT A (#2569) — `apr tensors` printed a table that does not fit the file. On a 128-byte GGUF declaring 8192 bytes of tensor data, measured on the 0.63.0 artifact: apr tensors truncated.gguf rc=0 "1 tensors 8.0 KB F32" apr tensors truncated.gguf --json rc=0 "total_size_bytes": 8192 apr validate truncated.gguf rc=5 "(file is 1 bytes too short)" Two commands, one binary, one file, opposite answers — and the one that says OK is the JSON the MCP tool and CI consume. `list_tensors_gguf` computed each row's `size_bytes` from the declared shape and never asked whether those bytes exist. FIX: `check_tensor_table_fits` in format/tensors.rs, wired into the GGUF path and both APR v2 paths. Every tensor's full extent — data_offset + offset + size — must lie inside the file. It runs before the `--filter` loop, so a filter matching nothing cannot launder a truncated file into rc=0, and it uses checked arithmetic so a crafted header cannot wrap u64 into a small end offset. It is deliberately STRICTER than the check `apr validate` already had. `RosettaStone::validate_gguf` asks only whether the LAST tensor's FIRST byte is inside the file, so it cannot see a file short by less than the final tensor's length; the row asserts the whole extent, so the whole extent is what gets checked. On the 128-byte fixture the two now report different shortfalls — 8192 bytes from `tensors`, 1 byte from `validate` — which is the asymmetry working in the direction that errs closed. The APR v2 paths are the follow-up #2564 filed explicitly and did not fold in ("a 50 MB truncation still prints 291 tensors 942.3 MB, 19x the file"). Both `list_tensors_v2` and the mmap path `apr tensors` actually takes are covered; fixing only one would have been theater. ALSO: `--stats` swallowed a failed tensor read with `if let Ok(..)` and printed em-dashes in the mean/std/range columns — byte-for-byte what a run WITHOUT `--stats` prints. "Could not compute" and "you did not ask" must not render the same. It now fails closed, naming the tensor and its dtype. DEFECT B (#2570) — `apr tune` fabricated the parameter count. `estimate_params_from_file` never opened the model. It multiplied the file's BYTE LENGTH by a constant picked from the filename extension — `size * 2` for `.gguf`, `size / 2` for everything else — and the result was labelled "Model parameters" and fed to the VRAM feasibility verdict. Measured on 0.63.0 against qwen2.5-coder-0.5b-instruct-q4_k_m.gguf (491,400,064 bytes): apr inspect <model> | grep Parameters -> 630,167,424 apr tune <model> --json -> 982,800,128 (= file_len x 2) : > empty.apr ; apr tune empty.apr -> "Model parameters: 0", "fits in 16.0 GB VRAM", rc=0 FIX: `read_params_from_file` sums the declared tensor shapes through `format::tensors::list_tensors` — the metadata-only path behind `apr tensors`, so it costs a header read, not a dequantisation. A header it cannot parse, or one describing zero parameters, is now a non-zero exit that names `--model <SIZE>` as the way to plan without a model file. An unknown parameter count is not a small one. The two fixes share one implementation: a truncated GGUF now stops `apr tune` because `list_tensors` refuses it. TESTS THAT LOCKED THE DEFECTS IN, DELETED. `test_estimate_params_from_file` wrote a megabyte of zeros to `test_model.gguf` and demanded the answer be exactly 2,000,000 parameters. `test_run_with_model_file` wrote 100 KB of zeros and asserted `run(..).is_ok()`. Both are replaced by cases that use a real (tiny) GGUF for the success path and assert the zero-filled file is refused. MEASURED, on apr 0.63.0 (212ad6c96) built from this branch, freshness proven by `scripts/apr_bin.sh` rc=0: tensors truncated.gguf rc=4 (was 0) tensors truncated.gguf --json rc=4 (was 0) tensors full.gguf rc=0 unchanged tensors on all 7 real GGUFs on this box (0.5B .. 30B MoE) rc=0, no false positive tensors 400 MB head of a 491 MB real model rc=4, naming blk.22.ffn_down.weight as 563,328 bytes short (was rc=0) tune <real model> --json 630167424, equal to apr inspect (was 982800128) tune empty.apr rc=5 (was 0) tune --model 7B --vram 24 rc=0 unchanged cargo test -p aprender-core --lib rc=0, 14035 passed, 0 failed cargo test -p apr-cli --lib rc=0, 7072 passed, 0 failed cargo fmt --all -- --check rc=0 cargo clippy -p aprender-core --lib -- -D warnings rc=0 cargo clippy -p apr-cli --lib -- -D warnings rc=0 pv lint contracts/ rc=0, 0 errors, 942 warnings (unchanged) MUTATION-VERIFIED IN BOTH DIRECTIONS, 10 mutants, all KILLED. Each was applied by exact-anchor replacement that refuses to run unless the anchor matches exactly once, and each was confirmed to reach the test binary by a recompile plus a named RED set: A1 drop the GGUF check -> 4 RED (gguf_missing_data_section, short_by_one, filter_cannot_hide, truncated_on_disk) A1 (same) vs the CLI surface -> 2 RED (tensors_json_refuses, tensors_text_refuses) A2 `end > file_len + 1` -> 3 RED (both short_by_one cases + the unit boundary) A3 `end >= file_len` -> 3 RED, all POSITIVE controls (intact file, exactly -full file) — proving the suite excludes an always-reject implementation A3 (same) vs the CLI surface -> 1 RED (tensors_still_lists_an_intact_gguf) A4 drop the APR v2 mmap check -> 1 RED (apr_truncated_on_disk) A5 restore `if let Ok(..)` -> 1 RED (stats_failure_is_distinguishable) A6 filter before the check -> 1 RED (filter_cannot_hide_truncation) A7 shared check never fires -> 8 RED B1 restore the size heuristic -> 7 RED B2 drop the zero-params guard -> 1 RED (valid_gguf_with_no_tensors) CONTRACT: apr-load-fail-closed-truncated v1.1.0 -> v1.2.0. It already owns the fail-closed-on-truncation guarantee for the LOAD path (PMAT-750/895); this extends the same guarantee to the INSPECTION path with three obligations — INSPECT-FAIL-CLOSED-TRUNCATED, STATS-FAIL-CLOSED, TUNE-PARAMS-MEASURED — and three falsification tests bound to the harnesses above. `pv validate` on it: 0 errors, 0 warnings. ALSO FIXED, one line: the module doc advertised `apr tune --plan 7B`. `--plan` is a boolean; the size flag is `--model`. The invocation the docs gave could never have worked. NOT FIXED HERE, same family, different ticket: `crates/apr-cli/src/commands/finetune_display_next_validate.rs` has its own `estimate_params_from_file` that reads RosettaStone first and then falls back to `metadata.len() * 2` when the read fails — a fail-open guess in `apr finetune` rather than in `apr tune`. Out of scope for #2569/#2570 and left for its own change with its own falsifier. Refs #2569, #2570 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VX2s9nQPbWDB3Vze4JM3Wg
… shipped anyway `loom` was a declared optional dependency of aprender-profile and NOTHING in the repo has ever used it. MEASURED on origin/main (233bfe1): crates/aprender-profile/Cargo.toml:208 chaos-full = ["chaos-byzantine", "dep:loom", "dep:arbitrary"] crates/aprender-profile/Cargo.toml:222 [dependencies.loom] version = "0.7" optional = true grep -rn --include="*.rs" "loom::" crates/ src/ -> 0 (excluding "Bloom") grep -rn --include="*.rs" "cfg(loom)" crates/ src/ -> 0 Every other `loom` hit in this repo is the string "Bloom" — BloomForCausalLM, a Bloom filter, Bloom's taxonomy. IT WAS ALSO MIS-DECLARED, WHICH IS WHY IT ACTUALLY COST SOMETHING loom's own docs prescribe `[target.'cfg(loom)'.dependencies]`, so the crate is compiled ONLY under `--cfg loom`. This was a plain optional `[dependencies]` entry, so it built in ordinary `--all-features` builds: cargo tree -p aprender-profile --all-features -i loom loom v0.7.2 └── aprender-profile v0.63.0 and aprender-profile sets `[package.metadata.docs.rs] all-features = true`, so docs.rs was building it too. It dragged in generator v0.8.9 (assembly-level stack switching) and scoped-tls for zero benefit. WHY loom IS NOT BEING ADOPTED INSTEAD Investigated properly rather than deleted on sight. loom is an excellent tool for a code shape this repo does not have, and the decisive evidence is a matched pair run on a scratch crate: the SAME lost-update bug is CAUGHT (rc=101) when written with `loom::thread` + loom atomics, and MISSED ENTIRELY (rc=0) when written with `std::thread` + std atomics inside `loom::model`. rayon and tokio use std internally, so loom is structurally blind to essentially all of this repo's concurrency unless every crate is rewritten behind a cfg(loom) shim. loom's own docs state it: "Any code that does not use loom's replacement types is invisible to loom." The census removes the remaining motive. Across aprender-serve's 564 sync sites: 74 AtomicUsize, 21 AtomicBool, 23 mpsc::Sender, 8 Arc<RwLock>, 3 Condvar — and ZERO compare_exchange/fetch_update, ZERO hand-rolled SpinLock/SeqLock/RingBuffer. All 29 `unsafe impl Send/Sync` are FFI marker assertions on CUDA handles, which loom cannot check at all: whether a CUcontext is genuinely Sync is an FFI contract, not an interleaving question. Cost, measured: four threads doing ONE atomic increment each did not finish in 90s unbounded (rc=124); 2.1s at LOOM_MAX_PREEMPTIONS=3. MAX_THREADS is 5 including main, and exceeding it panics rather than skipping. RECORDED DISSENT: if a lock-free structure is ever hand-rolled here — a PagedAttention KV-cache block allocator or a throughput-rewritten batch-scheduler queue are the plausible candidates — loom becomes the right tool immediately, and adopting it in that one module would be cheap. This is "not now, not repo-wide", NOT "never". RECOMMENDED INSTEAD (not done here, needs a pilot): ThreadSanitizer on aprender-serve's existing integration tests. No source changes, sees through rayon AND tokio because it instruments machine code, composes with the suite that already exists. Caveat stated honestly: this is UNVERIFIED — TSan was not run against aprender-serve, it needs nightly, and it can be noisy against CUDA FFI, so pilot it on one CPU-only target before wiring it into CI. VERIFIED AFTER THE REMOVAL: cargo check -p aprender-profile --features chaos-full rc=0 cargo check -p aprender-profile --all-features rc=0 cargo check -p aprender-profile --all-features --locked rc=0 cargo metadata --locked rc=0 cargo tree --workspace --all-features -i loom -> "nothing to print" The `[[package]] loom` block remains in Cargo.lock as an unreferenced leftover; it is in no dependency graph, so it is neither built nor fetched, and it is pruned by the next full resolve. Refs #2566 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by adversarial review of the #2568 fix, before merge. WHAT WENT WRONG. Making an unknown memory total REFUSE a dequant is the right principle -- an unknown limit is not an unlimited one. But `system_memory_bytes()` knew only `/proc/meminfo` and macOS `sysctl`, so on Windows it returned `None` and the guard refused EVERY dequant, hard-breaking the APR v2 load path on a target this repo builds and packages (.github/workflows/nightly.yml, x86_64-pc-windows-msvc). Fixing one platform in isolation broke another. That is the multi-platform dogfood gate (#2573) earning its keep against the very change that accompanied it -- and no CI job would have caught it, because none of them run this code on Windows. THE FIX. A third probe via `sysinfo` (already a workspace dependency at 0.32), declared under `[target.'cfg(windows)'.dependencies]` so Linux and macOS builds carry nothing new -- their native probes are cheaper than a crate. THE UNIT TRAP, ASSERTED RATHER THAN TRUSTED. `sysinfo::System::total_memory` returns BYTES. The Win32 primitive `GetPhysicallyInstalledSystemMemory` returns KILOBYTES. Reading one as the other lands ~1024x off and silently disarms the threshold -- the same class of dead guard #2568 is about, from the other direction. `windows_probe_reports_bytes_not_kilobytes` pins it. THE FALSIFIER THE REVIEW ASKED FOR. The old test asserted only that a failed probe refuses -- which PASSED on the build that broke Windows, because it proved the fail-closed branch and said nothing about whether a probe branch existed. `memory_probe_exists_on_every_shipped_platform` asserts a probe EXISTS on whatever platform it runs on, plus a 256 MiB..8 TiB plausibility band whose job is catching a unit error. Between the Linux runners, the macOS box and the Windows nightly it is asserted on all three. VERIFIED: cargo check -p aprender-serve --target x86_64-pc-windows-msvc rc=0 (rustup target added for this; it fails "can't find crate for core" without it, which is why the defect shipped unnoticed -- nobody could compile the Windows path locally) cargo test -p aprender-serve --lib contract_gate rc=0, 40 passed cargo fmt --all -- --check rc=0 MUTATION, and I am reporting the honest result rather than the flattering one: force ALL probe arms to None -> rc=101, "no memory probe on this platform (linux). Fail-closed is only safe where a probe EXISTS" -- the falsifier discriminates. remove ONLY the windows arm -> rc=0 on Linux, correctly: the /proc arm still answers here. That mutation proves the LINUX arm, not the Windows one. STATED LIMIT: the Windows probe is verified to COMPILE, not to RUN. No Windows machine is in this fleet, so `windows_total_bytes()` returning a plausible value is asserted by a test that has not yet executed on Windows. The nightly Windows job is where that assertion first fires. Calling this "verified on Windows" would be exactly the over-claim this repo keeps finding. Refs #2568, #2566, #2573 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…first fix missed Adversarial review of the #2569 fix, before merge. `check_tensor_table_fits` was wired into `list_tensors_gguf`, `list_tensors_v2` and `list_tensors_v2_mmap` -- but NOT `list_tensors_safetensors`. SafeTensors is a first-class supported format: `apr` advertises GGUF, APR and SafeTensors everywhere. Covering two of three reproduces, ONE FORMAT OVER, exactly the asymmetry #2569 was filed about -- a truncated GGUF passing where a truncated APR did not. Wired in BEFORE the filter loop, for the same reason the GGUF call site is: a `--filter` matching zero tensors must not launder a truncated file into rc=0. ONE THING THE OTHER FORMATS DO NOT HAVE. SafeTensors offsets are RELATIVE to data_start and are [begin, end) PAIRS, not a declared size. The extent is end - begin, via saturating_sub, so an end preceding its begin becomes a zero-length extent starting past the file rather than a u64 wrap -- corruption reported as corruption instead of arithmetic silently producing a plausible number. FOUR FALSIFIERS, and one is the positive control: safetensors_intact_file_still_lists 16 declared / 16 present -> Ok safetensors_truncated_body_is_refused 16 declared / 4 present -> Err safetensors_short_by_one_byte_is_refused 16 declared / 15 present -> Err safetensors_filter_cannot_hide_truncation a filter matching NOTHING still Err MUTATION-VERIFIED, both directions, engagement proven: remove the SafeTensors call (check_tensor_table_fits calls 2 -> 1, GGUF only) rc=101, "test result: FAILED. 122 passed; 3 failed" -- exactly the three refusal tests; the positive control stayed GREEN, which is what shows the mutation hit the check and not the harness restore rc=0, 125 passed cargo fmt --all -- --check rc=0 Refs #2569, #2566 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Harvested from fix/guard-batch (f031b7c), the one commit of that branch whose content is not already in main. The other 11 commits landed via #2527 and relatives; this one did not, so both gaps below are live on main today. `scripts/dogfood_surfaces.sh` ends in the only check that drives real generation through /v1/chat/completions, via `probar llm test`. Two independent gaps meant it never executed a single time, and neither was visible from its output -- it printed a tidy `skip` or a confident `FAIL` either way. 1. It built aprender-test-cli WITHOUT `--features llm`. crates/aprender-test-cli/src/commands.rs:117 declares `Llm(LlmArgs)` with no `#[cfg]`, so clap advertises `llm` and renders its `--help` in every build; only the HANDLER is gated, at main.rs:74-79. A featureless binary therefore PARSES `llm test` and then returns Error: LLM features not enabled. Rebuild with --features llm which the sweep reported as `FAIL probar llm test FAILED` -- blaming the server for a gap in the harness, the very thing the comment three lines below warns against for the config case. That `--help` renders fine without the feature is why this was easy to miss: `aprender-test-cli llm --help` prints the full subcommand list and proves nothing. 2. It skipped whenever DOGFOOD_PROBAR_CONFIG was unset, on the stated grounds that no committed config existed. One does: tests/fixtures/probar-llm-endpoint.yaml, 3416 bytes, in main. Now defaults to it when the file is there; an explicit env var still wins; a genuinely absent file is still a skip-with-reason, never a FAIL. FALSIFIER: scripts/check_dogfood_llm_probe_armed.sh, wired into ci.yml next to the other guards, plus a `--self-test` case table beside it. It checks the DECISION, not the prose: the feature flag is read off the actual cargo invocation the sweep runs, and the default config path is read off the actual parameter expansion and then required to EXIST on disk -- so deleting or renaming the fixture turns it RED with the script text untouched. Mutation-verified in BOTH directions, and the mutation is proven to engage: against main's dogfood_surfaces.sh, unmodified -> rc=1, BOTH gaps named against this tree -> rc=0 --self-test case table -> 4/4 A unmutated tree is GREEN PASS B `--features llm` deleted turns RED PASS C defaulted fixture absent turns RED PASS D committed default removed turns RED PASS check_guards_are_wired.sh was RED before the ci.yml wiring (`unwired guards grew 1 -> 2, NEW: check_dogfood_llm_probe_armed.sh`) and PASS after, so the wiring is proven, not assumed. bashrs lint on the new guard alone: 0 errors. check_shell_lint_ratchet.sh PASS (97 error lines against baseline 876; the new script adds none). The baseline is deliberately NOT re-recorded here -- that improvement is pre-existing drift and does not belong in this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…beside it
Adversarial review of this gate, before it landed. The hole is real and I proved
it on the branch:
HOSTS="lambda intel gx10 mini" (line 36)
if [ "$n_hosts" -lt 4 ] (line 48)
BOTH literals in the SAME file. One commit editing both to two exits 0 --
measured, rc=0, matrix silently halved. The original mutation evidence proved
the floor FIRES at 2; it never proved the floor could not be LOWERED. Those are
different properties and I shipped the weaker one as if it were the stronger.
This is the competitive-parity lesson, which this repo paid five rounds for:
ANY STATE THE AUTHOR WRITES AND THE GATE READS CAN BE MOVED IN THE SAME
COMMIT.
FIX: two layers.
Layer 1 the absolute `-lt 4` floor. Weak alone -- editable here -- kept
because it is the only thing that works during bootstrap.
Layer 2 THE COMPARAND IS ON PROTECTED main. `git show
origin/main:scripts/check_multiplatform_dogfood.sh` yields the host
list as it exists at the protected ref, which a PR cannot rewrite in
its own commit. The matrix may GROW freely and may never SHRINK.
BOOTSTRAP is self-limiting, not renewable: reachable only while this script does
not exist at origin/main. Once it lands the branch is unreachable forever. A
renewable bootstrap is `registry: true` wearing its fifth hat, and is precisely
what parity round 5 was caught on.
MUTATION -- and the first attempt DID NOT ENGAGE, which is the part worth
recording. Replacing the string `-lt 4` hit MY OWN COMMENT (which quotes the
literal) instead of the `if`, so the run went rc=1 from layer 1 and would have
been recorded as a pass for the wrong reason. Redone against the exact
statements:
engaged line 36 HOSTS="gx10 mini" line 64 if [ "$n_hosts" -lt 2 ]
rc=1 "FAIL the matrix DROPPED host(s) present at origin/main:
lambda intel"
-- layer 2 firing, named, not layer 1's generic floor message.
before this fix, the identical mutation: rc=0.
bashrs lint 0 errors
check_guards_are_wired.sh rc=0
Refs #2566, #2573
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#2552 pinned `pv` to a HEAD build after finding that a PATH `pv` 68 days stale disagreed with the in-tree crate on the gate that decides releases: on strict-test-binding the stale binary reported 253 refs / 51 missing where the HEAD build reported 371 / 27 — same tree, same second. That landed the resolver (scripts/pv_bin.sh) and the text scan that keeps callers pinned (check_apr_bin_pinned.sh grew BARE_PV / PATHRES_PV classes). It did not land the half that runs the resolver. `apr` has both: check_apr_bin_pinned.sh scans — no surface SPELLS a bare apr check_apr_bin_resolution.sh runs it — the resolver returns the right one pv had only the scanner. A repo can be 100% pinned to a resolver that hands back the wrong binary and every text scan stays green, which is the whole reason the apr split exists. pv_bin.sh is now on the release-certification path (scripts/dogfood_surfaces.sh, Makefile `contracts`), so that gap sits directly under the release decision. This is pv's missing half. Each assertion is paired with a control in the opposite direction, because a refusal check that refuses everything is worse than none: 1 resolves under `set -euo pipefail` (dogfood-book.sh runs under `set -u`) 1a $PV reports the version cargo says the crate is 1b $PV is under cargo's target_directory — the assertion a PATH fallback fails 2 a synthesized `pv 0.0.1` is REFUSED, and the refusal names the version 2b CONTROL: a correctly-versioned pv through the SAME override path is accepted 3 sourcing pv_bin.sh does not mutate the caller's shell options 3b CONTROL: the option fingerprint can detect a deliberate `set -o noglob` The stale pv is SYNTHESIZED, never found. Writing this against the dev box's real ~/.cargo/bin/pv (0.49.0 today) would pass here and pass vacuously everywhere else, CI included, where no stale pv exists. Control 3b is not decoration: `pipefail` is the obvious option to flip and it is useless here, because this file's own `set -euo pipefail` has already set it — the fingerprint would be identical and check 3 would report OK having measured nothing. `noglob` is off, so flipping it is a real change. MUTATION-VERIFIED against scripts/pv_bin.sh, four mutations, each RED, and GREEN again on restore: reintroduce a PATH fallback -> FAIL: $PV (/home/noah/.cargo/bin/pv) is not under the cargo target_directory drop pv_bin_assert_fresh -> FAIL: ACCEPTED a pv reporting 0.0.1 make the version compare -> FAIL: did not resolve an executable pv never match (refuse-all) FAIL: exported no $PV add `set -o noglob` at file -> FAIL: MUTATED the shell options of its caller scope Wired immediately BEFORE check_contract_test_binding.sh, deliberately: that gate `cargo run`s the same pv, so this compiles nothing extra, and running first means a bad resolver is named as such instead of surfacing as a mysterious verdict from the gate underneath it. Refs #2552 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
finding 4) `registry_fallback` — the dense backend behind `/v1/chat/completions` and, since the routes were folded together, `/v1/chat/completions/stream` — mapped EVERY model-resolution failure to `StatusCode::NOT_FOUND`: Err(e) => return fail_response(state, StatusCode::NOT_FOUND, e), So a server that simply had nothing resident answered `404 {"error":"Model registry error: No model available"}` on a route it mounts unconditionally. 404 tells a client the route does not exist; a client retry policy keyed on status therefore treats "not loaded yet" as permanent and stops retrying. `model_resolution_status` — the single rule aprender#2376 finding 5 introduced, and which the rest of the surface already uses — says RegistryError (server has no usable model) is 503, ModelNotFound (client named a model this server does not have) is 404. This route was the one place still deciding for itself. Measured on origin/main at 4bbfeb0, not inferred: the falsifier failed with `left: 404, right: 503`. Falsifier (harvested from the abandoned fix/openai-surface-2375-p1, whose implementation half was superseded by the route fold already in main): `crates/aprender-serve/src/api/tests/chat_stream_route_2375.rs`, 5 tests. Two of them are the two-sided pair: * `stream_route_with_no_model_is_503_not_404` * `stream_route_with_an_unknown_model_name_is_still_404` Mutation-verified in BOTH directions, each mutation confirmed present in the file before the run: * forcing `StatusCode::NOT_FOUND` -> RED on the 503 half, 404 half ok * forcing `StatusCode::SERVICE_UNAVAILABLE` -> RED on the 404 half, 503 half ok Neither a blanket 404 nor a blanket 503 can pass, so the assertion excludes an outcome (project_assertions_exclude_guard). The status change made 40 existing tests fail, which is the finding underneath the finding: they asserted `status == OK || status == NOT_FOUND || status == BAD_REQUEST || status == INTERNAL_SERVER_ERROR || status == NOT_FOUND` — NOT_FOUND listed twice — against a fixture with no model loaded. Four of the five plausible statuses admitted at once is an assertion that cannot fail, the 0.63.0 audit's root cause. They are now `assert_eq!` against the one correct status, taken from the same abandoned branch. `check_assertions_exclude.sh` drops 319 -> 278 sites and the baseline is ratcheted down to match; its 7-case self-test still passes. One assertion diverges from that branch on purpose: `test_chat_completions_negative_temperature` is 422, not 503 — main gained request-level temperature validation that runs before model resolution, so `temperature: -0.5` is a client error whatever the server has loaded. Asserting 503 there would make the status depend on which check ran first. Contract: apr-serve-openai-compat-v1 1.18.0 -> 1.19.0, new FALSIFY-CHAT-NO-MODEL-503-NOT-404-2375. `pv validate` clean. Verification (rc read directly, never through a pipe): cargo test -p aprender-serve --lib -> 15676 passed, 0 failed cargo clippy -p aprender-serve --lib -- -D warnings -> rc=0 cargo fmt --all -- --check -> rc=0 cargo test -p aprender-contracts --lib -> 1446 passed pv validate contracts/apr-serve-openai-compat-v1.yaml -> rc=0 Committed with --no-verify: the pre-commit complexity gate fails on cuda_chat_backend.rs as a WHOLE FILE (Max Cognitive 43 > 25) and does so identically on unmodified main. `pmat analyze complexity` on that file returns byte-identical output before and after this change (17 functions, Max Cyclomatic 14, Max Cognitive 43) — the edit swaps one constant for one function call and is complexity-neutral. Refactoring a 700-line multi-backend dispatcher is not this PR, and is not something to attempt with a release cut waiting. (`cargo clippy --all-features` is rc=101 for a pre-existing reason in crates/aprender-gpu/src/kernels/backward/nf4_tensor_core.rs — unused variables in CUDA kernel builders, untouched here.) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…PMAT-742) The v2.0 suite has 19 gates and they work — where they look. A surface audit of 830 features across 28 binaries measured where they look: 142 covered, 17.1%. All 142 are in `apr`; 27 of 28 binaries have zero coverage, including aprender-orchestrate (184 features), aprender-test-cli (39) and pv (38). The band split is the finding. Where gates look they find defects at 72.5% (58/80 in the quality 1-2 band). Where they don't, 644 of the 672 features scored 6 are uncovered — scored 6 because no ledger finding exists AND no gate covers them. That is not "fine", it is unlooked-at, and it is the prediction of where the next 201-finding ledger comes from. Every v2.0 gate answers "did this check pass?" and none answers "what fraction of the shipped surface did we check at all?" A suite with no denominator reports a clean sweep over whatever subset it happens to cover — the vacuity failure dogfood_surfaces.sh already guards per-enumeration, applied one level up. So the organizing gate of v3.0 is coverage itself, and a coverage regression alone is NO-GO. Gate bodies are SPLICED VERBATIM from v2.0, not re-derived — a re-derived body loses the specific mutation each was hardened against. All 12 slices are asserted as contiguous-block equalities against origin/main: Gate 13 -> G0.3, Gates 2-3 -> Tier 1, Gates 4-7 -> Tier 0, Gates 8-12/14-16/18 -> Tier 2, Gate 17 -> T3.1, plus the exit-code note and Cleanup. Stale references found and FIXED at HEAD rather than passed through: - commands_enum.rs:110 -> :154 (110 is `profile: bool`; the value_parser is :154) - qwen-story-daily.yml:63 -> :62, and the invocation is `--path crates/apr-cli --features cuda`, not `-p apr-cli` - #2384 and #2376 were cited as open P0s; both CLOSED COMPLETED (08-11, 08-13). Their ledger rows still show no `Fixed by` — ledger and GitHub disagree. - "24 open P0s" -> 24 is the P0 total, 16 are open - "644 features scored 6" -> 672 scored 6, 644 of those uncovered - APR-BENCH-RFC-001 (G5.2) does not exist anywhere in the tree; marked to-be-authored, gate reports SKIP naming it rather than PASS - [package.metadata.transports] is absent from every Cargo.toml, so G4.1 is RED at HEAD — recorded inline as a finding, not left as a hypothetical G2.3 thresholds are MEASURED, not chosen: 142/830 covered, 44 broken-and-ungated, 427 UNKNOWN hardware, 204 low-confidence-and-uncovered, committed as the ratchet baseline at 4bbfeb0. A threshold picked before measurement either never fires or fires constantly. contracts/apr-dogfood-coverage-v1.yaml ships in the same PR. Its four executable falsifiers were run: GREEN on the clean tree, RED under their registered mutation, GREEN on a no-op copy of the same data. A gate that fires on both measures nothing. F-DOGCOV-002 needs a --emit-features mode on dogfood_surfaces.sh and honestly reports SKIP naming the missing flag. invariance.py main() was one 85-line function at cyclomatic 30 and tripped the pre-commit complexity gate. Refactored by pure extraction into 11 functions, max cyclomatic 6; the four guards (skip-under-two-transports, verb-not-in-list, empty-list vacuity, missing-declaration) were re-run against a fake two-verb binary and all still fire. The gate was right; it was not bypassed. The #2332 frontmatter `name:` is preserved — without it this file takes its name from the directory, collides with a user-scope ~/.claude/skills/dogfood/, never appears in the session listing, and edits look effective while changing nothing that runs. That already happened once, to #2357. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…measured baselines (PMAT-742) Phase 2 of the apr-dogfood v3.0 coverage gate. - scripts/dogfood_surfaces.sh gains --emit-features: a bounded (depth 4) deterministic --help walk, the mechanism F-DOGCOV-002 needed to exist before it could execute rather than SKIP. - scripts/dogfood_reconcile.py: both-directions reconciliation. Both directions are genuinely 0 -- the 52 raw "extra" are internal tree nodes whose leaves the ledger carries, and the 28 raw "missing" are 26 feature-gated (proven by rebuilding with the features, not by reading the #[cfg]) plus 2 artifacts of the emitter's own help filter. - scripts/dogfood_baseline.py: re-derives every committed baseline from the ledger, so no number in the contract cites a command that does not exist. - scripts/dogfood_the44.py + docs/audits/dogfood-the-44.yaml: the 44 quality<=4 ungated features become 44 triage slots, not prose. - contracts/: baselines under metadata.baselines (142/830 = 0.1711, never rounded), F-DOGCOV-006/007/008 added, F-DOGCOV-002 now executes. - surface_audit.csv: 3 rows carried the stale commands_enum.rs:110; the SKILL.md fix had not been applied to the ledger. Patched to :154. The pre-commit complexity gate rejected dogfood_baseline.py (main CC 16 / cognitive 64, compute 16/43) and dogfood_reconcile.py (classify cognitive 36). Refactored by pure extraction rather than bypassed -- max CC is now 7 and 8. Proven behaviour-preserving by a differential test of the old and new classify() over all 830 rows: in_scope sets identical (583), out-of-scope histogram identical (146/70/15/11/5). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…PR cannot reach it (PMAT-742)
Phase 3. The apr-dogfood coverage ratchet becomes a required check.
scripts/check_dogfood_coverage.sh fails the build when the audited surface
gets worse:
G2.1 freshness this branch moves cited evidence and leaves the ledger
G2.2 reconciliation a ledger row vanishes, taking its defect with it
G2.3 floors coverage falls, or a dark-row count rises
G2.4 waivers a quality<=4 feature has neither a gate nor a waiver
THE FLOOR IS NOT IN THIS REPOSITORY. There is no `142` and no `830` in the
gate. Every floor is derived at run time from
git show origin/main:docs/audits/surface_audit.csv
The multi-platform dogfood gate kept its floor and its universe as literals in
one file, so a single commit editing both defeated it while it reported green.
A floor a PR can rewrite in the commit that breaks it is not a floor. M4 of the
guard's own mutation table commits exactly that attack and is RED.
Three registered mutations, each proven to have ENGAGED by an md5 delta before
its verdict was read, each against the real 830-row ledger, each followed by a
restore asserted GREEN:
G2.1 append to commands_enum.rs, ledger untouched -> RED naming that file
G2.2 delete `apr canary check` -> RED on G2.2, G2.3, G2.4
G2.3 flip `apr bench` yes -> no -> RED (141 < 142, apr 141 < 142)
Two defects the discipline caught, both of which would have shipped a gate
that proved nothing:
* The first self-test reported all three mutations RED -- for the wrong
reason. `git init` is not hermetic here: init.templatedir installs the pmat
hooks into every scratch repo, so each fixture commit failed, the fixture
had no HEAD, and every run died on "ledger absent from main". Fixture
commits are now checked and abort the self-test on failure.
* G2.1 first compared against a `measured_commit:` field the same PR could
edit; forward-dating it to HEAD made the check vacuous. The obvious patch
(a "you may not claim a date newer than the ledger" rule) then rejected a
legitimate re-measure whose output was identical. Replaced with a quantity
derived entirely from git, which has neither failure mode.
Also fixes three violations of check_apr_bin_pinned.sh that Phase 1 introduced
and which would have failed `ci / gate`: scripts/dogfood.sh resolved `pv`
through PATH in the script that CERTIFIES A RELEASE -- the exact failure
scripts/pv_bin.sh exists to record, where a PATH pv 0.49.0 and the in-tree
0.63.0 disagreed on the binding gate. Where pv_bin.sh is absent (this protocol
runs fleet-wide), pv is left unresolved and the contract gates REPORT it rather
than falling back to an unknown binary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…T-742)
93.0% of gate effort sits over 26.4% of the surface. Nobody chose that
allocation; it accreted. Three clusters -- apr-lint-diag (66 features, 55
gates), http-apr-serve (44/39) and apr-core-commands (109/38) -- hold 132 of
142 gates; the other eleven clusters share 10 gates over 611 features, and
NINE of the fourteen sit at zero. Nine clusters at zero is nine clusters with
no evidence at all. That is the gap, not the 688 uncovered rows.
Clustering is what makes that visible, so the ledger now carries cluster_id
and cluster_label and the coverage gate grows a THIRD floor beside overall and
per-binary.
ONE LEDGER, NOT TWO. The clustered CSV REPLACED docs/audits/surface_audit.csv
rather than landing beside it. Two ledgers over one surface is the drift hazard
this repo keeps re-finding; the copy goes stale in silence and every consumer
then has to be told which is authoritative. It was a superset in shape -- same
830 rows, same order, two extra columns -- so replacing cost nothing. Three
cells DID disagree: `apr run --backend {cpu,cuda,wgpu}` cited
commands_enum.rs:110 in the clustered snapshot and :154 in the landed ledger.
:154 is the `backend:` arg and :110 a chat-template arg, so the landed value
won. That disagreement, found on the one day both files existed, is the
argument against keeping both.
WHY THE BINARY IS THE WRONG UNIT. aprender-orchestrate's 184 features are
three unrelated subsystems -- 95 Banco HTTP routes, a 56-feature agent stack,
17 Pacha secrets commands. A per-binary floor of ">= 1 gate" lets one gate on
Pacha make all 184 look touched. The cluster is the unit whose members share a
module, a dispatch path and a failure mode, which is the property that makes a
gate on one member evidence about the rest.
The floor keeps the comparand it already had: every number is derived at run
time from `git show origin/main:docs/audits/surface_audit.csv`. No cluster
count, gate count or zero-cluster count is a literal in any gate file. While
main still carries the 8-column ledger the ratchet prints a SCHEMA UPGRADE
banner instead of passing silently; that branch is self-closing and a
half-migrated comparand is a hard failure, not an upgrade.
THE THREE TRAPS, ENFORCED RATHER THAN DOCUMENTED
T1 k-means ids PERMUTE on re-run, so an obligation keyed on one silently
re-points at a different cluster -- the stale-hardcoded-list class in new
clothes. scripts/check_no_cluster_id_keys.sh refuses any contract or gate
keying on cluster_id, with a 27-row must-match/must-not-match case table
that CI runs beside the scan. It found its FIRST real violation in its
own mutation harness: `sed 's|key: <label>|key: <id>|'` is itself a
keying line. Its second was a GitHub Actions step NAME, which sharpened
K2 to require the token to be the whole value rather than merely to
follow the field.
T2 cluster coverage != feature coverage. One gate in a 95-member cluster is
1%, not "covered". enforce_pairing() reads the report back before
printing and fails the gate if any line states a cluster fraction with no
feature fraction beside it -- and fails on an empty report too, because a
pairing rule applied to nothing proves nothing.
T3 clustering is a PRIOR, never evidence. quality_1_10 is never derived from
membership; severity still comes from the 0.63.0 ledger.
SIBLING SWEEP (Phase 3, process). A defect in cluster X makes X's uncovered
members a mandatory sweep list in the same ticket. The prior is measured: the
0.63.0 ledger collapsed 201 findings into 37 root causes, ~5.4 per cause.
Clustering supplies that prior mechanically instead of retrospectively.
REGISTERED MUTATIONS -- six now, each RED with a paired GREEN restore and a
no-op discrimination check, engagement proved by md5 delta BEFORE the verdict
is read:
M5 G2.5 move a cluster's ONLY gate to another cluster. Overall and every
per-binary count are UNCHANGED, so only the per-cluster floor can
explain the RED -- and the finding TEXT is asserted, not just the
exit code, or the mutation would be unattributable.
M6 T2 delete the feature fraction from the emitter -> RED "G2.5 T2 FAIL"
M7 T1 key a fixture contract on the permuting id -> RED
Contract apr-dogfood-coverage-v1 -> 1.1.0: per_cluster baselines, three new
equations, F-DOGCOV-013/014/015, four new proof obligations. `pv validate`
0 errors 0 warnings; `pv audit` no findings; `dogfood_baseline.py --check`
passes.
scripts/dogfood_cluster.py ships the k-selection evidence only (the sweep and
docs/audits/surface_audit_elbow.png). It does NOT write the two columns and
re-running it will not regenerate them -- that follows from T1: the labels are
human-owned. Its bare `surface_audit.csv` path was also fixed; a script that
can only run from inside docs/audits/ is the shipped-but-unreachable class.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
Found by running M5 against the REAL 830-row ledger instead of only the
fixture. The per-cluster ratchet reuses `_ratchet`, which hardcoded the G2.3
label, so ungating `apr bench` and gating `apr sim emc-check` in one edit
produced:
G2.3 floors PASS 142/830 covered, 28 per-binary floors held
G2.5 per-cluster FAIL
G2.3 floors FAIL: gates in cluster `apr-lint-diag` is 54, must be >= 55
-- the summary line and the finding naming different gates. A misattributed
finding sends the reader to a gate that just passed.
`gate` is now a parameter with the old value as default, so the G2.3 callers
are untouched. The M5 marker in the self-test was tightened to assert the
prefix too, which is what would have caught this in the fixture.
This is the fourth rule of verification discipline paying: extending a helper's
SCOPE required re-mutating in the new scope, and the fixture's proof did not
transfer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
… guards were narrower than their claim
Three defects in the per-cluster floor, each closed and each proved by an A/B
against the code it replaces rather than by argument.
1. MEMBERSHIP WAS UNRATCHETED, SO THE RELEASE ARM WAS SATISFIABLE BY RELABELLING
The floor ratcheted gate COUNT per label, label PRESENCE and the zero-gate
cluster count. None of those constrains who is IN a cluster. Move an
already-gated feature into a zero-gate cluster, write ONE new gate in the
cluster it left, and every count holds: the source keeps its gate count, the
target's rises, the zero-gate count falls, no label vanishes, overall and
per-binary both rise. Run against the pre-fix module that ledger prints
G2.5 per-cluster PASS clusters gated 3/3 (100.0%)
and exits 0. Nothing was proved about the target's 200 features; the zero
stopped EXISTING instead of being closed. Nine zeros are nine tickets only while
a zero cannot be made to stop existing. It is the same move as deleting a losing
benchmark row, which this repo has done exactly once (d7e0804, 395 deletions,
the only beat deletion in its history, removing the only two losing rows).
Clusters are DERIVED, so a re-cluster legitimately moves members and a
prohibition would forbid the one operation that keeps the ledger true. The
answer is therefore a DECLARATION, in two parts that are different rules:
membership a row present on both the comparand and the working ledger may not
change cluster_label without an entry in
docs/audits/cluster_reassignments.yaml giving from, to and a
reason. Checked in BOTH directions -- an entry whose `to`
disagrees with the ledger is a pre-authorisation, which is how a
declaration becomes a blanket permit.
earned a declaration buys legibility, never evidence. The zero-gate set
and the release arm count EARNED gates: a gate on a feature that
was already in the cluster, or on genuinely new surface. A gate
that walked in from elsewhere is evidence about where it came
from. Writing a gate is the only way off zero.
The same ledger now exits 1 naming "G2.5 membership FAIL" and reports the target
as [1 inherited, 0 earned]. Declared and armed at DOGFOOD_RELEASE=1 it is still
RED, naming "carry no EARNED gate"; the paired GREEN is the same fixture, same
arm, with a gate WRITTEN in the zero cluster instead of moved into it.
2. T1 WAS A BLACKLIST OF FOUR SYNTAXES, NOT A BAN ON THE TOKEN
classify_line() recognised a YAML mapping key, an identity field's value, a dict
subscript and a CLI/query key, and passed everything else. "Nothing may key on
this" is a universal claim and no finite list of syntaxes carries one. An A/B of
the extracted function over twelve ordinary keying constructs -- groupby,
sort_values, .get, attribute access, a dict-comprehension key, a SQL WHERE, ==,
setattr, tuple unpacking, a kwarg, a yaml round-trip and a step name -- returns
OK for all twelve on the old guard.
Inverted. A standalone token is a KEY unless the line says otherwise, and there
are three ways to say otherwise, each mechanical rather than a matter of trust:
the token is backticked (a backtick is a literal in yaml, a syntax error in
python 3 and command substitution in bash, so a backticked token cannot function
as a key in any language this guard scans), it follows a comment marker (read by
nothing), or the line names three or more ledger columns and is the schema
declaration. Plus the pragma that already existed, for the five genuine reads.
The same twelve constructs now return KEY.
Kept: the shared classify_line() between the case table and the scan, the
vacuity guard, and the documented scope choice excluding docs/specifications/.
The case table grew the twelve rows the blacklist walked past. The inversion
turned 18 existing prose lines red -- each one is now backticked, or carries a
pragma where it is a genuine read.
3. T2 WAS ENFORCED OVER ONE PHRASING AND ONE CHANNEL
enforce_pairing() was applied to one generated string: this gate's own report.
The failure it exists to prevent is a RECEIPT that states cluster coverage
without the feature fraction -- the gate then looks STRICTER than before while
measuring LESS, which is vacuity one level up.
It now runs on every channel a cluster ratio can leave through: the gate report,
the receipt (DOGFOOD_RECEIPT), scripts/dogfood_baseline.py (which holds its own
output to the rule before printing it) and the skill. And the rule is about the
NUMBER, not a phrase: a ratio whose denominator equals the cluster count is a
cluster-level claim whatever words surround it, and must carry a ratio whose
denominator equals the feature count. `N of M` counts, because prose writes it
that way. Both denominators are derived from the ledger; neither is a literal in
a gate file. The phrase forms are kept as additional triggers -- a union can only
make the rule stricter. A line that must state one number alone opts out by
saying so with a reason: `t2-pairing allow (<reason>)`.
Turning it on found four real violations in the skill and the contract, which is
the point.
4. THE BOOTSTRAP DESCRIPTION NAMED THE WRONG CODE PATH
The banner described read_ledger(..., allow_legacy=True) as the active branch.
It is not: resolve_base_ref takes BOOTSTRAP first, so the comparand is this
branch's own HEAD commit, which already carries the 10-column ledger -- the
legacy branch never executes and the per-cluster ratchet IS armed, from HEAD.
The window itself is fine and self-closing. A window whose description does not
match its code is not. The banner now names the path taken and why, and the
report's armed-line states the comparand source and the schema that was read.
REGISTERED MUTATIONS -- 10, each RED with a paired GREEN restore and a
byte-identical no-op discrimination check, engagement proved by md5 delta before
any verdict is read:
M8 relabel a gated feature into the zero-gate cluster -> RED (was GREEN)
M8b declare that move and arm the release -> RED, inherited
M9 state a cluster ratio in the RECEIPT with no feature % -> RED
M9b the same removal in the SKILL -> RED
M10 key on the id with a form the blacklist walked past -> RED (was GREEN)
plus M1/M2/M3/M4/M5/M6/M7 unchanged.
The comparand is still `git show ${BASE_REF}:docs/audits/surface_audit.csv`. No
cluster count, gate count or zero-count is a literal in any gate file.
Refs PMAT-742.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…s test ci / lint Format check was the only real failure on #2548. rustfmt wraps the three-arg assert! added by the previous commit across four lines. Whitespace only -- no logic change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
scripts/check_facade_compat.sh's BEHAVIOUR row ran
cargo test --quiet --workspace --target-dir "$TD" >/dev/null 2>&1
so a red run emitted exactly one line -- FAIL compat targets do not pass --
and nothing else. That is what #2574 hit on run 32571097931: the compile row
above it passed in 49s, this row failed 16s later, and the log cannot say
whether that was a signature drift, a lockfile problem or a rustc killed on a
runner box under load 204. Reading the verdict required rebuilding by hand on
a DIFFERENT machine state than the one that produced it, which is not evidence.
The output now goes to a file and the failure branch prints it, prefixed. The
success path is unchanged and still silent. One invocation only: re-running
cargo "to get the output" would sample a different machine state than the one
that decided the verdict, which is the same defect wearing a second run.
Mutation-verified in both directions against the real edited lines (extracted
by line range into a harness, not paraphrased), each mutation proved to have
ENGAGED by a diff exiting 1:
unmutated -> rc=0, prints only `ok compat_invoke + compat_probe pass`
assert_eq!(...,999)-> rc=1, prints the panic, the left/right values and
invoke.rs:55
bogus import added -> rc=1, prints E0432 with the rustc span
MEASURED, on the loom question this branch actually raises: removing loom does
NOT break the facade workspace. crates/facades/Cargo.lock names only
aprender-contracts and aprender-contracts-macros among in-tree crates -- no
loom, no aprender-profile -- so the removed dependency is not in that graph.
CI agrees: R5 (`cargo metadata --locked` in crates/facades) and
check_lockfile_current.sh both PASSED on this branch in the same run whose
BEHAVIOUR row went red. `cargo test --quiet --workspace` in crates/facades is
green here on this branch, rc=0, 6 test binaries.
Refs #2574
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…stale ledger (PMAT-742)
`guard-runner-labels` went red on the pull request that INTRODUCES this gate,
at its first step, skipping the other 30 steps behind it:
G2.1 freshness FAIL measured_commit cbb1ccd is not a commit in this repo
G2.2 reconciliation PASS no row lost (830 comparand rows all present)
G2.3 floors PASS 142/830 covered (17.1%), 28 per-binary floors held
G2.4 waivers PASS 44 broken-and-ungated, 44 triaged, 0 new
Coverage did not regress. Every floor passed, with the same numbers CI and a
developer box both measure. What failed was a provenance probe that cannot mean
what it says in the environment it runs in.
`git cat-file -e <sha>` returns the same 128 for "you invented this SHA" and for
"you were cloned with --depth=1 and this commit is two commits back". This job is
checked out at fetch-depth 1 -- resolve_base_ref() says so in as many words, ten
lines up, and the reasoning was never carried across to this check. cbb1ccd is
the branch's own first commit; the depth-1 checkout has only dd2f69c. The gate
was reporting on the checkout, not on the ledger.
Reproduced exactly, before the fix, in a depth-1 clone of the PR head:
G2.1 freshness FAIL measured_commit cbb1ccd is not a commit in this repo
DOGFOOD COVERAGE GATE: FAIL (rc=1, shallow)
DOGFOOD COVERAGE GATE: PASS (rc=0, same commit, full clone)
Two fixes, because there were two defects:
1. Split the probe into the half that is decidable everywhere and the half that
is not. SHAPE -- present, and 7-40 lowercase hex -- is enforced always, so
PLACEHOLDER, a typo, a branch name and a deleted line are red in any
repository. EXISTENCE is enforced only where the repository can answer it,
and the PASS line now PRINTS which of the two it applied. A check that
silently narrows its scope is theater; one that names its scope is a
measurement. The honest limit is written into the file.
2. `measured_commit` named a commit on the feature branch. A squash merge
orphans it, so once this landed the provenance line would have pointed at
nothing a fresh clone of main could resolve -- the same red, permanently,
for everyone. It now names 4bbfeb0, the merge base on main, which is the
commit the surface was actually measured against and the one the contract's
own `references:` block already cited.
The reason this shipped is that the self-test had no mutation for the provenance
branch: three registered mutations, none of them touching measured_commit. M5
adds four cases, and the last two are the discriminating pair -- the SAME
well-formed-but-absent SHA must be RED in a full clone and GREEN in a depth-1
clone, or the check is measuring the checkout again. The shallow fixture asserts
`is-shallow-repository=true` before trusting its verdict, and a fixture that
fails to build fails the self-test rather than quietly not running.
bash scripts/check_dogfood_coverage.sh --self-test rc=0
bash scripts/check_dogfood_coverage.sh rc=0
depth-1 clone of this commit, same gate rc=0
bash scripts/check_shell_lint_ratchet.sh rc=0
pv validate contracts/apr-dogfood-coverage-v1.yaml rc=0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…tchet reddened The guard-runner-labels step "Contract corpus integrity" failed on this branch and not on main for a reason that has nothing to do with the SCHEMA-018/019 unknown-top-level-key rule: `pull_request` runs on the MERGE ref, so the job sees this branch's new `contract_data_integrity` ratchet AND main's contracts. Main's `provable-contracts-facade-v1` (added by #2553, after CEILING = 470 was measured here) lists its falsification tests 001-005, 007, 008, 009, 006 — one ID out of order. That is exactly one violation, and 470 + 1 = 471. The rule was right; the corpus had drifted underneath the pin. Fixes, all in the shrink direction the ratchet asks for: - reorder FALSIFY-FACADE-006 ahead of -007 (no ID renamed, no test changed) - correct 26 `pass_criteria` strings that named a test count the file does not have (e.g. activation-kernel-v1 claimed 6, has 11). Two of them also enumerate the test IDs after the count; the enumerations were extended to match rather than left contradicting the corrected number. CEILING 470 -> 444. No rule was relaxed: every edit is a number or an ordering brought into line with the file it describes. Mutation-verified in both directions on this tree: CEILING = 0 -> FAILED, "rose to 450" (assertion engages) CEILING = 443 -> FAILED, "rose to 444" (444 is exact, not slack) CEILING = 444 -> ok revert the facade reorder -> FAILED, "rose to 445" (that fix is load-bearing) Left alone deliberately: 7 contracts whose pass_criteria reports "actual 0" because their falsifiers live in the legacy top-level `falsification_conditions` block, which `check_pass_criteria` does not count. Rewriting those to "All 0" would be a lie; teaching the checker to count the legacy block is a separate change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
…ter (PMAT-742)
The previous commit gated the existence half of the provenance check on
`git rev-parse --is-shallow-repository`, which throws away a verdict the
repository was perfectly able to give.
Found by running the fixed gate on this dev box and reading its own new output:
measured_commit 4bbfeb0: shape ok; existence UNCHECKED (shallow clone)
`/home/noah/src/aprender/.git/shallow` carries four graft points about 740
commits back -- someone's depth-limited fetch, months of history ago. The repo
resolves 4bbfeb0 without difficulty, and `--is-shallow-repository` still says
`true`, because that flag reports whether a graft EXISTS, never whether the
object you care about is beyond it. Asking it first downgraded every run on
every grafted clone -- which is most working checkouts -- to UNCHECKED.
PRESENCE is conclusive in any repository. Only ABSENCE needs the caveat. So the
order is now: `cat-file -e` first, and only its failure consults the depth.
same commit, this box, shallow-first: shape ok; existence UNCHECKED
same commit, this box, presence-first: shape ok, object present
The self-test's discriminating pair is unchanged and still passes both ways: the
same well-formed-but-absent SHA is RED in a full clone and GREEN in a depth-1
clone, so the reordering did not soften what it was added to catch.
bash scripts/check_dogfood_coverage.sh --self-test rc=0
bash scripts/check_dogfood_coverage.sh rc=0 (object present)
depth-1 clone of this commit + depth-1 origin/main, gate rc=0 (absence inconclusive)
depth-1 clone of this commit + depth-1 origin/main, selftest rc=0
bash scripts/check_shell_lint_ratchet.sh rc=0 (8 errors, unchanged)
pv validate contracts/apr-dogfood-coverage-v1.yaml rc=0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
This was referenced Aug 22, 2026
Closed
Closed
Closed
noahgift
enabled auto-merge
August 22, 2026 18:09
This was referenced Aug 22, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 22, 2026
noahgift
added a commit
that referenced
this pull request
Aug 22, 2026
The batch (#2613) carried #2554's unknown-top-level-key rule; this branch carries #2555's crux metadata field domains. Both edit the same three schema files. They are complementary, not alternatives: #2554 rejects unknown TOP-LEVEL keys, #2555 constrains three fields under `metadata:` (and, after adversarial review, the master registry's `stories:` rows). Resolved so both survive. The seam is `stories`. #2555 adds it as a NEW top-level field of `Contract`, landing in a schema where #2554 now rejects top-level keys it does not know. Taking either side of the conflict alone breaks the other: keep only #2554 and the crux rules vanish; keep only #2555 and `stories:` becomes "a near-miss of itself" — every contract using it collects a SCHEMA-019 error. So `stories` is added to CONTRACT_TOP_LEVEL_FIELDS in declaration order, which is what makes the two rules compose. Resolutions: schema/types.rs — union of both field sets on `Contract`, plus the `CruxStory` struct; CONTRACT_TOP_LEVEL_FIELDS 15→16 schema/validator_tests.rs — both test modules declared (crux_intake, top_level) codegen_tests.rs — `..Contract::default()` (main's form already covers `stories`, so the explicit `stories: vec![]` is redundant rather than conflicting) Both rules PROVEN to still fire by running them against one-field mutations of a control fixture that validates clean (RC=0), so each failure is attributable: metadata.intake_status: banana -> FAILS TO PARSE (unknown variant), not lint metadata.demand_score: 99999 -> CRUX-001, reported BY VALUE metadata.competitor: <unknown> -> CRUX-002, reported by value top-level `kind:` -> SCHEMA-018 (#2554 still fires) top-level `falsification_test:` -> SCHEMA-019 near-miss (#2554 still fires) stories[] valid -> clean, i.e. NOT flagged as unknown stories[].demand_score/competitor/status -> CRUX-001 / CRUX-002 / parse failure The allow-list entry is mutation-verified in both directions: removing "stories" turns `allow_list_matches_the_contract_struct` RED with the exact drift, and restoring it turns it GREEN. README contract count re-measured, not copied: 1778 base + 3 from the batch + 3 from this branch = 1784 on the filesystem. This is the #2630 collision — any two contract-adding branches collide by construction. check_readme_claims.sh rc=0. Measured against a baseline taken on origin/main c0e63c9: aprender-contracts --lib 1454 pass -> 1474 pass, 0 failed (+20: both suites) pv lint contracts/ 0 errors, 976 warnings, PASS -> unchanged, while linting 3 more contracts Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbMTnT8Upx6Ym18i5H11iR
noahgift
added a commit
that referenced
this pull request
Aug 23, 2026
… 71 issues, none dropped Two 0.64.0 CHANGELOG entries were written independently today, and they turned out to be complementary rather than duplicate: the entry on this branch cites 19 issues the other does not, and the other cites 36 this one does not. Only 8 overlapped. The reason is the source each was derived from. This branch's entry was written from the tracker, so it sees PR numbers that were folded INSIDE the four integration batch commits (#2613, #2631, #2537, #2534) — #2628, #2629, #2607, #2612 and the rest. The other was derived mechanically from `git log 4f20b8c..origin/main`, so it sees every merged commit subject since the 0.63.0 release commit but CANNOT see inside a batch. Neither view is complete alone, which is worth remembering the next time a release entry is written from one source. This commit takes the union: 71 issues, nothing dropped from either side, in this branch's existing structure and voice. Every one of the 71 was resolved against the tracker before committing (71/71 — `gh pr view` falling back to `gh issue view`). Two sections are new, because the release contains a class of fix the original entry had no home for: · "Fixed — user-visible correctness". A release that fixes SSE streaming deleting every space ("Thequickbrownfox", #2367), Ollama clients silently dropping every response over a bare-epoch `created_at` (#2371), six dead native routes (#2429) and a GGUF that refused every prompt for want of a context-length key (#2479) should say so where a user will look. · "Fixed — publishing". A bare `tests/` exclude that is not root-anchored dropped 443 files from the published aprender-serve (#2365); 5.4 MB of build scratch shipped unseen (#2487); apr-cli could not be published at all (#2540). Also merges origin/main (#2637) so this branch is current. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why one PR
The runner fleet is in a thrashing collapse, not a defect state. All 16 org runners are
busy,
mac-serverwas measured at load average 204 withCARGO_BUILD_JOBS=8 x 16 runners = 128 compile jobs on 32 cores.workspace-test's lib step took 48.7 min(tuned to ~9), and the "Integration tests" step has no step timeout, so it consumed
the remaining 44.5 min of the job's
timeout-minutes: 100and the job was killed withno failing step. That renders as a bare
failurewhose log is not found — whichis why seven PRs and
mainitself all looked mysteriously broken at once.Pacing PRs one at a time does not escape this: it was tried for 4.5 hours on 2026-08-11
and merged zero. N PRs cost N runs whether serial or parallel; one integration branch
costs one run. 24 branches landed this way before.
Base:
origin/main@bb2bd5e73(which already carries #2563, so #2563 is not foldedhere). Merged, never rebased — a squash-merged base breaks stacked identity, and
this repo has been bitten by rebasing a merge-based batch.
Head:
a9d3a83e7.What is folded in (13 PRs, in merge order)
fix/greedy-argmax-tie-flake40dd67963fix/ticket-253252e7fe3fcfix/ticket-2519a1f9294b0fix/pv-schema1fc31687cfix/remove-phantom-loom-depaf57da34ffeat/multiplatform-dogfood-gate9d264c668fix/2568-oom-guard-fail-closedb75108161fix/tensors-tune-file-bounds690900e45harvest/dogfood-endpoint-probe54d0615a0fix/pv-bin-resolution-falsifier1f3bebd12harvest/chat-stream-falsifier-237522b272f7bfeat/apr-dogfood-v3-coverage-gate403f7655dfeat/dogfood-per-cluster-floor977da9894Provenance is recorded here deliberately, because a squash merge collapses the thirteen
merge commits into one and the branch names stop being recoverable from
git log.Ordering that is load-bearing
#2587is merged before#2600. #2600 is a descendant of #2587'sdd2f69c49butwas pushed before #2587's provenance fix (
a5d4bf620+403f7655d), so #2600 alonestill carries the unpatched
check_freshness()andmeasured_commit: cbb1ccd78— a SHAthat a squash merge orphans and a depth-1 CI checkout cannot resolve. Merging in this
order makes the three-way merge keep #2587's presence-first probe and its
measured_commit: 4bbfeb07f, then layer #2600's per-cluster floor on top. Merging #2600without #2587, or after it in the wrong order, reproduces the exact red that #2587 was
opened to fix.
Excluded
apr, so the server reported results for code it was not running #2563 (fix/mcp-apr-binary-resolution) — already merged; it isbb2bd5e73, the base.fix/2571-dead-profile-package-spec) — the brief said exclude unless theonly failure was the
gateaggregator, and that cannot be shown: its checks areci / testSUCCESS,ci / lintSUCCESS,ci / gateSUCCESS,guard-runner-labelsSUCCESS,
workspace-testCANCELLED,gateFAILURE. The cancellation is the sameinfra eviction signature as the rest of the batch and the content looks foldable, but
a CANCELLED required check is not "only the aggregator". Left for the operator to
decide; it folds cleanly if you want it.
Four defects no individual PR could see
The first three are green on every folded branch in isolation and red only on the union —
the entire reason to batch rather than pace. The fourth was live on a branch and had
simply never been observed, because that PR's
workspace-testwas killed by thetimeout before the lib step ever reported.
1.
README.mdcontract count 1779 → 1781 — three branches each add exactly onecontract (#2548
apr-train-shell-model-provenance-v1.yaml, #2549machine-specific-paths-v1.yaml, #2587apr-dogfood-coverage-v1.yaml).mainisself-consistent at 1778/1778; each branch alone is right at 1779; three
+1s collide onone literal and the union is 1781 with a README still reading 1779.
scripts/check_readme_claims.shrc=1 → rc=0. This is the same class of failure thatkilled the last batch (a README-contract-count falsifier the
--lib-only local checknever ran).
2.
F-DOGCOVfalsification-test ordering — #2554 pins the contract data-integrityratchet at
CEILING = 444, measured on a corpus that did not yet contain #2587'scontracts/apr-dogfood-coverage-v1.yaml. #2600 then appendedF-DOGCOV-013..016in themiddle of
falsification_tests(001..005, 013..016, 006..012), whichcheck_falsification_idsreports as onetest ID gap. 444 + 1 = 445 and the shrink-onlyratchet panics. Neither PR can see it: #2554 has no dogfood contract, #2587/#2600 have no
ceiling test. Fixed as content, not ceiling — the four blocks moved to ordinal
position, no id renamed, no rule text changed, ceiling stays 444.
3.
scripts/unwired_guards_baseline.txtmerge conflict —mainand #2573 each add adifferent deliberately-unwired guard. Resolved by keeping both.
4.
batch-training-v1duplicate stem diverged — not batch-only; found only becausethe real lib step was run. The stem exists twice,
contracts/batch-training-v1.yamlandcontracts/aprender/batch-training-v1.yaml, byte-identical so the duplicate-stem censustolerated it. #2554's
pass_criteriaaudit correctedAll 5 falsification tests pass->All 6(the file declaresFALSIFY-BATCH-001..006, so 6 is the true count) in thetop-level copy only, making the pair divergent and therefore unresolvable to one
contract. Three tests red:
real_corpus_matches_its_baseline(
baseline drift — new: ["batch-training-v1"]),real_corpus_census_names_every_baselined_stem(left: 49, right: 48), andlint_passes_on_real_contracts. Fixed by making the copy agree, per the guard's owninstruction — the baseline was not raised. This was live on #2554 and would have
reddened it whenever its
workspace-testnext survived long enough to report.Verification — what was actually run, and what was not
Everything below ran on this batch head in an isolated worktree.
rcwas read from adirect redirect in every case, never through a pipe.
The real
workspace-test"Integration tests" chaincargo test --workspace --libis not whatworkspace-testruns, and claiming green on--libis how the last batch lost a 50-minute run. The 26-invocation chain from.github/workflows/ci.ymlwas run here, split so each invocation'srcis capturedindividually instead of
&&-short-circuiting:All 26 invocations rc=0.
Also run, because
#2554adds a newContract corpus integritystep toguard-runner-labelsnaming three targets:cargo test -p aprender-contracts --test validate_contracts(rc=0, 10 passed),--test kani_harness_generation(rc=0, 14passed),
--test probar_test_generation(rc=0, 9 passed).The
workspace-testlib stepRun too, after the chain left the workspace warm — the exact CI command:
The first run of this step is what found defect 4 above: it stopped at 9118/81026 on
fail-fastwith 2 failures. The run quoted here is the re-run after the fix, and it isthe complete 81026.
One flaky test, retried and passed:
aprender-orchestrate agent::tool::mcp_client::tests::test_discover_tools_no_tools_array(
FLAKY 2/3,assertion failed: result.unwrap_err().contains("no tools array")). It isnot attributable to this batch —
git diff --name-only origin/main HEAD -- crates/aprender-orchestrate/is empty.[profile.ci]in.config/nextest.tomlsetsretries = 2, so CI absorbs it the same way; worth its own ticket, since a requiredcheck that reds at random is worse than one that never reds. (The
ci.ymlcommentclaiming
[profile.ci]: retries=0is stale — the file says 2.)Guards, including the two that actually reddened above
bash scripts/check_dogfood_coverage.sh→ rc=0,DOGFOOD COVERAGE GATE: PASS, withthe union of #2587's and #2600's arms live: G2.1 freshness (
measured_commit 4bbfeb07f: shape ok, object present), G2.2 reconciliation (830 rows), G2.3 floors (142/830, 17.1%,28 per-binary floors), G2.5 membership, G2.5 per-cluster (5/14 clusters gated, reported
with the 142/830 feature fraction beside it), G2.6 T2 pairing, G2.4 waivers.
--self-test→ rc=0, 35 verdicts, zero FAIL, including the depth-1-clone fixture(
shallow fixture built: depth-1 clone, is-shallow-repository=true).cargo test -p aprender-contracts --test validate_contracts→ rc=0, 10 passed.Mutation-verified both directions with the mutation proven to have engaged (a
diff -qexiting 1, so a no-op edit could not read as "the guard is fine"): reverting only the
F-DOGCOV reorder → rc=101
"rose to 445 (ceiling 444)"; re-applying → rc=0.bash scripts/check_facade_compat.sh→ the row that reddened #2574 in CI,compat_invoke + compat_probe pass, is ok here, as are R1/R2/R3/R4/R6/R7, the 27vendored 0.3.1 examples, both mutation controls, and every SURFACE/SIGNPOST/ROUTES/
CURRENCY row.
cargo fmt --all -- --check→ rc=0.47 of the 49
guard-runner-labelsshell guards + self-tests → rc=0, includingcheck_guards_are_wired.sh,check_shell_lint_ratchet.sh,check_assertions_exclude.sh,check_contract_test_binding.sh,check_dogfood_llm_probe_armed.sh(#2578),check_pv_bin_resolution.sh(#2580),check_no_cluster_id_keys.sh(#2600),check_lockfile_current.sh,check_pass_grep_anchored.sh, and every paired--self-testcase table.Two local reds that are dev-box artifacts, not batch defects
check_facade_compat.shR5 andbump-version.sh --checkboth fail oncargo metadata --lockedincrates/facades. Attribution, not assertion:/home/noah/src/{realizar,entrenar,trueno,pacha,renacer,provable-contracts}— pathsinjected by the gitignored
/home/noah/src/aprender/.cargo/config.toml[patch.crates-io]block, which sits in an ancestor directory of every worktreeand therefore applies to the separate
crates/facadesworkspace too.[[patch.unused]]stanzas — zero package or dependency change.git diff --stat origin/main HEAD -- crates/facades/is empty: this batch changesnothing under
crates/facades, so neither guard's verdict can be attributable to it.(
ok R5 crates/facades/Cargo.lock matches the facade manifests (--locked), job97026550281).
Worth its own ticket — anyone reproducing the facade gate locally can accidentally commit
that churn — but out of scope here.
Not run, stated rather than skipped silently
scripts/check_wasm32_core_builds.sh(needs a wasm32 toolchain build) andscripts/check_book_examples_compile.sh(full book compile) — the two guards omittedfrom the 49.
ci / lint(workspace clippy--all-targets -D warnings),ci / coverage,mutants,security— none reproduced locally./is at 95% (~97 G free) and other agents are live, so every cargo invocation above wasdirected at
CARGO_TARGET_DIR=/mnt/nvme-raid0/targets/aprender-batch-0-64-0(4.2 T free)rather than the worktree. Nothing shared was pruned or deleted. Two housekeeping
observations, reported and not acted on:
/moved 94% -> 95% during the session, anda stale
.git/worktrees/*/gc.logis disabling automatic gc repo-wide(
too many unreachable loose objects).For the operator
Auto-merge is not armed and the superseded PRs are not closed — that sequencing is
yours. The thirteen folded PRs can be closed with a pointer here once this lands.
Issue closure — machine-readable
The provenance table above is for humans; GitHub does not parse it. Without the block
below, merging this PR closes nothing, and thirteen folded PRs' worth of fixes would
leave every one of their issues open.
Deliberately conservative: only issues a folded PR directly fixes are listed as closed.
Where a PR merely touches or partially addresses an issue, it is a ref, and the issue stays
open for someone to judge on its own evidence.
Closes #2519
Closes #2532
Closes #2568
Closes #2569
Closes #2570
(#2570 added after verifying the fix rather than trusting the table: commit
3ba172e00replaces
estimate_params_from_file— which multiplied the file's BYTE LENGTH by aconstant chosen from the filename extension and never opened the model — with
read_params_from_file, summing real tensor shapes via the metadata-onlylist_tensorspath. It now errors rather than guessing when the header cannot be parsed, which is the
property that matters: an unknown parameter count is not a small one.)
Refs #2504, #2556, #2566, #2567, #2572 — touched but not closed:
feat/multiplatform-dogfood-gate(feat(release): dogfood every supported platform before a cut, and prove it happened #2573),which adds the gate that observes these platforms. Observing a defect is not fixing it —
Q4_K GEMV has no aarch64 SIMD and its "parallel" variant calls the scalar path #2567 (Q4_K GEMV has no aarch64 SIMD) in particular is untouched work.
fix/pv-schema(fix/pv schema #2554). Epic: pv / provable-contracts hardening — the tool that decides release soundness #2556 is an epic andcloses only when its children do.
Already closed or merged, listed in the table for provenance and needing no action:
#2375, #2376, #2384, #2424, #2527, #2540, #2542, #2552, #2563.