diff --git a/crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs b/crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs index 9a4568e351..e6a055ef77 100644 --- a/crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs +++ b/crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs @@ -180,6 +180,27 @@ fn the_entity_type_is_counted_in_by_entity_type_not_merely_registered() { ); } +#[test] +fn the_parity_extractor_control_is_drawn_by_the_gate_every_run() { + // PMAT-3704. ONT-001 v4.10's probe also asks `.pc_extract["parity-receipt"] == "fired"`, at the TOP level + // of the report, exactly as read here. The control existed from #3600 on — `parity_receipt::positive_control` + // — but only a unit test called it, so the gate never reported it and an extractor that stopped reading the + // v2 layout would have been counted, never refused. R-3: one planted defect per registered extractor, every + // run. + let out = Command::new(pv_bin()) + .args(["lint", "contracts", "--gate", "shapes", "--format", "json"]) + .current_dir(repo_root()) + .output() + .expect("failed to spawn pv"); + let v: serde_json::Value = + serde_json::from_str(&String::from_utf8_lossy(&out.stdout)).expect("json report"); + assert_eq!( + v["pc_extract"]["parity-receipt"], "fired", + "pc_extract carries no fired `parity-receipt` control: {}", + v["pc_extract"] + ); +} + #[test] fn every_fixture_carries_the_real_contract_byte_for_byte() { // A fixture copy that drifts from `contracts/parity-receipt-v2.yaml` would let the real shape be mutated diff --git a/crates/aprender-contracts/src/lint/shapes_gate.rs b/crates/aprender-contracts/src/lint/shapes_gate.rs index feab19f457..5739d10db9 100644 --- a/crates/aprender-contracts/src/lint/shapes_gate.rs +++ b/crates/aprender-contracts/src/lint/shapes_gate.rs @@ -22,15 +22,20 @@ //! proves nothing about the gate. //! //! **The positive controls.** `pc_shape`: a bare focus node of each shape's target class, drawn every run, must -//! violate at least one armed shape. `pc_extract.gguf`: a corrupt magic is refused. `pc_extract["apr-model"]`: -//! a header whose tensor count disagrees with its index is refused. All three every run, in memory. +//! violate at least one armed shape. `pc_extract` — one planted defect per extractor Σ marks implemented (R-3): +//! `pv-contract`, a contract stripped of `metadata` carries no `ont:kind`; `json`, a nested key the vocabulary does +//! not map is refused naming it; `gguf`, a corrupt magic is refused; `apr-model`, a header whose tensor count +//! disagrees with its index is refused; `code` and `lean`, as their modules state; `parity-receipt`, a record +//! stripped of `comparator` loses its comparator edge. All of them every run, in memory. use std::collections::{BTreeMap, BTreeSet}; use std::path::Path; use std::time::Instant; use crate::ontology::arming::ArmedShapes; -use crate::ontology::extract::{self, apr_model, code, gguf, lean, pv_contract, ExtractFailure}; +use crate::ontology::extract::{ + self, apr_model, code, gguf, json, lean, parity_receipt, pv_contract, ExtractFailure, +}; use crate::ontology::rdf::{iri, Graph, Term, RDF_TYPE}; use crate::ontology::receipts; use crate::ontology::shapes::{self, NodeShape, Report, Severity, ShapeError}; @@ -378,14 +383,22 @@ fn decline( }) } -/// The extractor positive controls (R-3), run in memory every gate run. +/// The extractor positive controls (R-3: one planted defect per registered extractor), run in memory every gate +/// run, keyed by the Σ entity type each extractor reads. The key set must equal Σ's implemented entity types — +/// [`tests::every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires`] holds the two together. fn extract_controls() -> BTreeMap { let apr_sample = apr_model::minimal_container(2); [ + ("pv-contract", pv_contract::positive_control()), + ("json", json::positive_control()), ("gguf", gguf::positive_control()), ("apr-model", apr_model::positive_control(&apr_sample)), ("code", code::positive_control()), ("lean", lean::positive_control()), + ( + "parity-receipt", + parity_receipt::positive_control(&parity_receipt::control_sample()), + ), ] .into_iter() .map(|(k, fired)| { @@ -798,4 +811,37 @@ mod tests { ShapesOutcome::PositiveControlFailed { .. } )); } + + #[test] + fn every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires() { + // PMAT-3704 — R-3: `pc_extract`, one planted defect per registered extractor, every run. The control set + // was a hand-written array beside Σ with nothing tying the two together, so `parity-receipt` (#3600) and + // `json` (#3516) shipped as implemented entity types the gate never controlled — the same shape as #3624 + // for `by_entity_type`, and as bashrs#266's two lists. Σ is this build's registry (its `extractors[]` + // name this crate's readers), so the two sets must be EQUAL: an implemented type without a control fails, + // and so does a control for a type Σ does not implement. + let sigma_path = + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../contracts/ontology.yaml"); + let text = std::fs::read_to_string(&sigma_path).expect("contracts/ontology.yaml"); + let sigma = crate::ontology::sigma::Sigma::from_yaml(&text).expect("Σ parses"); + let implemented: BTreeSet = sigma + .entity_types + .iter() + .filter(|e| e.implemented) + .map(|e| e.name.clone()) + .collect(); + assert!( + !implemented.is_empty(), + "Σ implements nothing — the comparison would be vacuous" + ); + let controls = extract_controls(); + let controlled: BTreeSet = controls.keys().cloned().collect(); + assert_eq!( + controlled, implemented, + "pc_extract keys (left) must equal Σ's implemented entity types (right)" + ); + for (k, v) in &controls { + assert_eq!(v, "fired", "pc_extract.{k}"); + } + } } diff --git a/crates/aprender-contracts/src/ontology/extract/json.rs b/crates/aprender-contracts/src/ontology/extract/json.rs index 89a2b53e41..e32d092f15 100644 --- a/crates/aprender-contracts/src/ontology/extract/json.rs +++ b/crates/aprender-contracts/src/ontology/extract/json.rs @@ -160,19 +160,64 @@ pub fn extract_into( path: path.clone(), why: e.to_string(), })?; + extract_text(g, stem, &path, &text, &vocab) +} + +/// Everything after the read: the document's text as nodes. Shared by [`extract_into`] and [`positive_control`], +/// so the control exercises the code the gate runs rather than a copy of it. +fn extract_text( + g: &mut Graph, + stem: &str, + path: &str, + text: &str, + vocab: &Vocabulary, +) -> Result, ExtractError> { if path.ends_with(".jsonl") { - return extract_jsonl(g, stem, &path, &text, &vocab); + return extract_jsonl(g, stem, path, text, vocab); } let value: serde_json::Value = - serde_json::from_str(&text).map_err(|e| ExtractError::NotJson { + serde_json::from_str(text).map_err(|e| ExtractError::NotJson { contract: stem.to_string(), - path: path.clone(), + path: path.to_string(), why: e.to_string(), })?; - node(g, stem, stem, &vocab.root_class, true, &value, &vocab)?; + node(g, stem, stem, &vocab.root_class, true, &value, vocab)?; Ok(Vec::new()) } +/// The positive control (R-3, PMAT-3704), in memory every gate run: a document whose nested key the vocabulary +/// maps must extract with that child typed by its class, and the planted copy whose nested key the vocabulary +/// does NOT map must be refused naming the key — the "no inference" rule this module states, measured. +#[must_use] +pub fn positive_control() -> bool { + let vocab = Vocabulary { + prefix: "pc".into(), + root_class: "pc:Root".into(), + nested: vec![("child".into(), "pc:Child".into())], + }; + let mut g = Graph::new(); + let mapped = extract_text( + &mut g, + "__pc_extract__", + "pc.json", + r#"{"a":1,"child":{"b":true}}"#, + &vocab, + ) + .is_ok() + && g.objects(&iri("pc", "__pc_extract__.child"), RDF_TYPE) + .iter() + .any(|t| t.as_iri() == Some(expand("pc:Child").as_str())); + let planted = extract_text( + &mut Graph::new(), + "__pc_extract__", + "pc.json", + r#"{"a":1,"orphan":{"b":true}}"#, + &vocab, + ); + let refused = matches!(planted, Err(ExtractError::Unmapped { ref key, .. }) if key == "orphan"); + mapped && refused +} + fn extract_jsonl( g: &mut Graph, stem: &str, @@ -516,4 +561,10 @@ mod tests { let none: serde_yaml::Value = serde_yaml::from_str("name: x").unwrap(); assert!(!applies(&none)); } + + #[test] + fn the_positive_control_fires() { + // PMAT-3704: drawn by the shapes gate every run as pc_extract.json. + assert!(positive_control()); + } } diff --git a/crates/aprender-contracts/src/ontology/extract/parity_receipt.rs b/crates/aprender-contracts/src/ontology/extract/parity_receipt.rs index 1a9c0968a0..df7e6db595 100644 --- a/crates/aprender-contracts/src/ontology/extract/parity_receipt.rs +++ b/crates/aprender-contracts/src/ontology/extract/parity_receipt.rs @@ -268,6 +268,33 @@ fn emit(g: &mut Graph, root: &Path, rel: &str, v: &serde_json::Value, stats: &mu } } +/// The body of [`control_sample`], less its `schema` (which is [`SCHEMA`], inserted so the two cannot drift). +const CONTROL_SAMPLE: &str = r#"{ + "cell": {"model": "pc", "file": "./pc.gguf", "quant": "Q4_K_M"}, + "host": "pc-host", + "backend": "cuda", + "apr_version": "0.0.0", + "generated_at": "1970-01-01", + "comparator": {"kind": "self", "reason": "positive control"}, + "partially_receipted": true, + "threshold_source": "evidence/parity/thresholds.yaml", + "unmeasured": ["ORACLE ARM: not measured."], + "result": {"positions": 1, "parity": true}, + "raw": {"model": "./pc.gguf", "metrics": []} +}"#; + +/// The record the gate hands [`positive_control`] every run (PMAT-3704): a minimal [`SCHEMA`] record in the +/// layout the seven back-filled receipts carry, self-compared, so its comparator edge is the one thing the +/// plant removes. Fails closed: a sample that did not parse is `Null`, on which the control cannot fire. +#[must_use] +pub fn control_sample() -> serde_json::Value { + let mut v: serde_json::Value = serde_json::from_str(CONTROL_SAMPLE).unwrap_or_default(); + if let Some(o) = v.as_object_mut() { + o.insert("schema".into(), SCHEMA.into()); + } + v +} + /// The positive control (R-3): a copy of a real record with `comparator` removed must lose its comparator /// edge, every run. Drawn beside the corpus so "the extractor still reads this layout" is measured rather /// than assumed. diff --git a/crates/aprender-contracts/src/ontology/extract/parity_receipt_tests.rs b/crates/aprender-contracts/src/ontology/extract/parity_receipt_tests.rs index 4ea4a66f20..12e5155f61 100644 --- a/crates/aprender-contracts/src/ontology/extract/parity_receipt_tests.rs +++ b/crates/aprender-contracts/src/ontology/extract/parity_receipt_tests.rs @@ -110,6 +110,21 @@ fn the_plant_removes_the_comparator_edge_every_run() { assert!(positive_control(&record("self", serde_json::json!({})))); } +#[test] +fn the_gate_sample_is_a_record_the_control_fires_on() { + // PMAT-3704: the gate draws the control on control_sample(), not on a test helper's record. + assert!(positive_control(&control_sample())); +} + +#[test] +fn a_sample_without_a_comparator_cannot_fire_the_control() { + // Discrimination: the control is a claim about the comparator edge, so a sample that has none to lose must + // not fire — else "fired" would say nothing about the extractor. + let mut bare = control_sample(); + bare.as_object_mut().expect("object").remove("comparator"); + assert!(!positive_control(&bare)); +} + #[test] fn an_unmigrated_legacy_record_is_refused_by_name_and_never_skipped() { // The exact layout the seven records carried before #3577: no schema, metrics[] at the top level. diff --git a/crates/aprender-contracts/src/ontology/extract/pv_contract.rs b/crates/aprender-contracts/src/ontology/extract/pv_contract.rs index 5ff199bf68..37c8a5bc8f 100644 --- a/crates/aprender-contracts/src/ontology/extract/pv_contract.rs +++ b/crates/aprender-contracts/src/ontology/extract/pv_contract.rs @@ -106,6 +106,37 @@ pub fn extract_one(g: &mut Graph, stem: &str, file: &str, doc: &serde_yaml::Valu } } +/// The positive control (R-3, PMAT-3704), in memory every gate run: a contract carrying `metadata.kind` and a +/// `depends_on` relation must come out with `ont:kind` and the typed edge, and the planted copy with `metadata` +/// removed must carry NO `ont:kind` — "nothing is inferred", measured rather than stated. +#[must_use] +pub fn positive_control() -> bool { + const STEM: &str = "__pc_extract__"; + let Ok(doc) = serde_yaml::from_str::( + "metadata: {kind: kernel}\nrelations: {depends_on: [contracts/__pc_target__-v1.yaml]}\n", + ) else { + return false; + }; + let s = iri("contract", STEM); + let mut g = Graph::new(); + extract_one(&mut g, STEM, "contracts/__pc_extract__.yaml", &doc); + let kind = g + .objects(&s, &ont("kind")) + .iter() + .any(|t| t.as_literal().is_some_and(|l| l.0 == "kernel")); + let edge = g + .objects(&s, &ont("depends_on")) + .iter() + .any(|t| t.as_iri() == Some(iri("contract", "__pc_target__-v1").as_str())); + let mut planted = doc; + if let Some(m) = planted.as_mapping_mut() { + m.remove("metadata"); + } + let mut g2 = Graph::new(); + extract_one(&mut g2, STEM, "contracts/__pc_extract__.yaml", &planted); + kind && edge && g2.objects(&s, &ont("kind")).is_empty() +} + /// §4.2 `entity: {type, ref, properties}` — the entity's OWN properties, as `:` on the contract node. /// /// A contract may carry the properties of the thing it is a contract FOR, and for a pre-registration the @@ -292,4 +323,10 @@ mod tests { assert_eq!(a, b); assert!(a.contains("/contract/a> "), "{a}"); } + + #[test] + fn the_positive_control_fires() { + // PMAT-3704: drawn by the shapes gate every run as pc_extract["pv-contract"]. + assert!(positive_control()); + } } diff --git a/docs/audits/impl-PMAT-3704-receipt.md b/docs/audits/impl-PMAT-3704-receipt.md new file mode 100644 index 0000000000..9acec8f9ef --- /dev/null +++ b/docs/audits/impl-PMAT-3704-receipt.md @@ -0,0 +1,95 @@ +# PMAT-3704 — receipt + +**Ticket:** PMAT-3704 (issue #3704) — ONT-4c3: pc_extract draws the parity-receipt positive control every gate run, and every extractor the gate runs has a control. **Kind:** code (`kind:code`). +**Branch:** `PMAT-3704-ont-4c3-pc-extract` from `origin/main` at `225b2a9ab` (release 0.69.0, #3698), in the worktree `~/.cache/paiml-implement/wt/aprender-ont4c3`; `~/src/aprender` untouched. +**Spec:** `paiml/infra docs/specifications/paiml-ontology.md` v4.10 — §2 R-3 (*"`pc_extract` (one planted defect per registered extractor, every run)"*) and §5 ONT-4c3, whose probe asks `.pc_extract["parity-receipt"]=="fired"`. +**discover.json:** `repo_root` the worktree, `default_branch=main`, `required_check=ci / gate,workspace-test`, `gate_cmd=make gate`, `quorum_tool=agy`, `code_search=pmat query`, `gate_cmd_fallback=false`. + +orch_model: opus-5 [V] orch_class: opus orch_decision: admit orch_basis: file + +Phase 0: `kind-gate.sh PMAT-3704 docs/roadmaps/roadmap.yaml --base origin/main` → `kind=code ticket=PMAT-3704 files=0`; `model-gate.sh` → `model=opus-5 class=opus decision=admit basis=file`; `config-lint.sh` → `slots=3 gh_calls_per_min=30 bank=3`; `target-guard.sh` PASS. + +## How this ticket was found + +The session was launched to implement ONT-001. R-24's next row was ONT-4c3, and its implementation (#3600) had landed in the 0.69 batch (#3669, squash `a877fa056`), so the next unit looked like the infra ledger carrier. The carrier's own first check refused it: with `pv` built at `a877fa056`, the gate on `contracts/` reports `verdict=Pass`, `by_entity_type["parity-receipt"]=7` and `pc_extract={apr-model, code, gguf, lean}` — no `parity-receipt` key. `extract_controls()` is byte-identical on #3600's head `242bdc3b5`, on `a877fa056` and on `225b2a9ab`: the batch did not drop the control, it was never wired. `parity_receipt::positive_control` had one caller, `parity_receipt_tests.rs`. + +## Five whys + +1. The probe is RED → `pc_extract` has no `parity-receipt` key. +2. → `extract_controls()` does not call `parity_receipt::positive_control`. +3. → the control list is a hand-written array beside Σ, and adding an extractor does not force adding its control. Measured: Σ implements **seven** entity types (`pv-contract`, `json`, `code`, `lean`, `apr-model`, `gguf`, `parity-receipt`); the array had **four**. `json` (#3516) shipped without one too. +4. → no test compared the two sets — the shape of #3624 (`by_entity_type`, one map over) and of bashrs#266 (two lists, nothing tying them). +5. → so the fix is the tie, not the missing entry: a test that makes the key set EQUAL Σ's implemented entity types, and the three controls it then demands. + +## Plan and routing + +| phase | what | route | A_i | +|---|---|---|---| +| ph1 | RED: the Σ-tie lib test + the CLI mirror of the probe | `route=self` (direct) | both tests red on main: 4 keys vs 7; `pc_extract` has no `parity-receipt` | +| ph2 | GREEN: three controls, wired | `route=agy-goal w=1.00 basis=absent` → **refused at spawn**, fell back to direct | both tests green unedited; gate `Pass`, 7/7 fired | +| ph3 | mutation | `route=self` | four mutants, each RED (below) | +| ph4 | gate, receipt, diff quorum, PR | `route=agy-quorum w=1.00 basis=absent` (review); push/PR `route=self` | `make gate` rc 0 on the committed tree; quorum 3/3 | + +Trigger: Q1 (`|M|=4`: `lint/shapes_gate.rs`, `ontology/extract/{parity_receipt,json,pv_contract}.rs`). + +## Dispatch ledger + +- **ph2.goal** (`paiml-agy-delegate`, goal lane, writes=true) and **ph1.grill** (`paiml-agy-delegate`, grillme quorum width 3) — both **refused at spawn** by the `SubagentStart` gate: `kind-gate refused PMAT-3704 (exit 2) — … not filed in /home/noah/src/infra/docs/roadmaps/roadmap.yaml`. The hook resolves the roadmap from the SESSION cwd (`~/src/infra`, where ONT-001 is launched), not from the ticket's repository. Not retried. Filed as **paiml/paiml-implement#326** with the repro. +- Consequence: ph2 was implemented direct, and the ph1 plan grill is **NotRun** — the diff quorum in ph4 is the review of record. +- slots used: 0 running; attempted=2 denied=2 running_peak=0 slots=3. +- `pmat hooks install --strict --force` was NOT run: aprender's hooks directory is the common git dir shared by every aprender worktree and session. Every commit on this branch carries `Pmat-Ticket: PMAT-3704` by hand. + +## What landed + +- **`lint/shapes_gate.rs`** — `extract_controls()` carries seven controls keyed by entity type, and the module doc names what each plants. New test `every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires`: reads `contracts/ontology.yaml` through `Sigma::from_yaml`, asserts the implemented set is non-empty (a vacuous comparison is refused), that the key set EQUALS it, and that every value is `fired`. +- **`ontology/extract/parity_receipt.rs`** — `control_sample()`: a minimal `apr-parity-receipt/v2` record from a literal plus `SCHEMA` (inserted, so the two cannot drift). It fails closed: a literal that did not parse is `Null`, and the control cannot fire on `Null`. Built without `serde_json::json!`, whose expansion calls `to_value(..).unwrap()` and trips the crate's `disallowed_methods` (14 clippy errors on the first draft). +- **`ontology/extract/json.rs`** — `extract_text()`: the post-read body of `extract_into`, factored so the control runs the gate's code rather than a copy. `positive_control()`, in memory: a mapped nested key extracts typed by its class, and an unmapped one is refused as `Unmapped { key: "orphan" }`. +- **`ontology/extract/pv_contract.rs`** — `positive_control()`: `metadata.kind` and a `depends_on` relation come out as `ont:kind` and the typed edge, and the copy without `metadata` carries no `ont:kind`. +- **`crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs`** — `the_parity_extractor_control_is_drawn_by_the_gate_every_run`: the probe's question, at the top level of the report, on the repo's own `contracts/`. +- Unit tests: `json::the_positive_control_fires`, `pv_contract::the_positive_control_fires`, `parity_receipt::the_gate_sample_is_a_record_the_control_fires_on`, and `parity_receipt::a_sample_without_a_comparator_cannot_fire_the_control` (discrimination: a sample with no comparator edge to lose must not fire). + +## Verification (claimed = what the tree said when written; rerun = the orchestrator's own run) + +| cmd | claimed | rerun | note | +|---|---|---|---| +| `cargo test -p aprender-contracts --lib lint::shapes_gate::tests::every_implemented…` on `d542db3cd` (RED) | 101 | 101 | `left: {apr-model, code, gguf, lean}` · `right: {apr-model, code, gguf, json, lean, parity-receipt, pv-contract}` | +| `cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts the_parity_extractor_control…` on `d542db3cd` (RED) | 101 | 101 | `pc_extract carries no fired parity-receipt control` | +| the same two on `9f6b2e02c` (GREEN) | 0 | 0 | unedited | +| `cargo test -p aprender-contracts --lib ontology::extract` | 0 | 0 | 49 passed | +| `cargo test -p aprender-contracts --lib lint::shapes_gate` | 0 | 0 | 15 passed | +| `cargo test -p aprender-contracts-cli` over every `tests/ont*.rs` (11 files) | 0 | 0 | 8+11+8+11+12+10+7+6+6+7+9 passed | +| `cargo clippy -p aprender-contracts --all-targets -- -D warnings` · same for `-cli` | 0 | 0 | after the `json!` rewrite above | +| `cargo fmt --all -- --check` | 0 | 0 | no diff | +| `pmat analyze complexity` on the new functions | — | — | `extract_controls` 2/1 · `pv_contract::positive_control` 4/3 · `parity_receipt::positive_control` 3/2 · `control_sample` 2/1 · `json::extract_text` 2/3 · `json::positive_control` 3/2 (cyclomatic/cognitive) | +| `bash scripts/check_ont_ratchet.sh` | 0 | 0 | PASS | +| `pv lint contracts/ --gate shapes --format json` (pv from this tree) | 0 | 0 | `verdict=Pass`, `pc_extract` 7/7 `fired`, `pc_shape=fired`, `by_entity_type["parity-receipt"]=7` | +| the ONT-4c3 probe's gate conjunct: `jq -e '.verdict=="Pass" and .by_entity_type["parity-receipt"]==7 and .pc_extract["parity-receipt"]=="fired"'` | 0 | 0 | was 1 at `a877fa056` and at `225b2a9ab` | + +## Mutations (each applied with a one-match assertion, then `git checkout`) + +| mutant | Σ-tie test | gate on `contracts/` | +|---|---|---| +| M1 — `parity_receipt::emit` drops the comparator edge | 101 | exit 2, `positive control pc_extract.parity-receipt did not fire` · `decline: PositiveControlFailed` | +| M2 — `json::nested_class` infers a class for an unmapped key | 101 | exit 2, `positive control pc_extract.json did not fire` | +| M3 — `pv_contract::extract_one` defaults `metadata.kind` to `kernel` (the inference ONT-6b made visible) | 101 | exit 2, `positive control pc_extract.pv-contract did not fire` | +| M4 — the `json` entry removed from `extract_controls()` | 101 on the assertion (`left` lacks `json`), not a compile error | — | + +Each gate decline names the control that was broken, and no other. After the four, the tree is clean and the rebuilt `pv` reports `Pass` with 7/7. + +## ont-delta + +`none — adds pc_extract positive controls for three already-implemented extractors (pv-contract, json, parity-receipt); no entity type, shape, relation or verdict reason is added or changed.` + +## Estimates + +K̂ = 64 (`estimate.sh aprender 4`, basis `docs/audits/impl-estimates.jsonl:L49-L52`), K = 80 (`1.25 K̂`, ONT-001 §6). + +## Gaps + +- **ph1 plan grill: NotRun** — refused at spawn (paiml-implement#326). The ph4 diff quorum is the review of record. +- The ONT-4c3 ledger binding is paiml/infra's row, written after this merges, from a `pv` built at this PR's squash sha (R-23). +- #3624 (`by_entity_type` is a hand-written list parallel to Σ) is the same class, one map over, and is NOT closed here: `by_entity_type` also has no `json` key today. + +## Verdict + +`PARTIAL(open)` until merged green on `ci / gate` + `workspace-test`. diff --git a/docs/audits/quorum-PMAT-3704.json b/docs/audits/quorum-PMAT-3704.json new file mode 100644 index 0000000000..2827354b71 --- /dev/null +++ b/docs/audits/quorum-PMAT-3704.json @@ -0,0 +1,178 @@ +{ + "ticket": "PMAT-3704", + "base": "main", + "base_resolved": "origin/main", + "base_note": "local main differs from origin/main by 141 commit(s); judged against origin/main", + "head": "18108846f222382d69a6242a77066add2c688e6b", + "diff_sha256": "f95dae019c7867dc49c0152657c9bd5c7e50183af01c26193bf1ac231d50cd2b", + "width": 3, + "executor": "agy", + "prompt_mode": "inline", + "prompt_bytes": 42991, + "author": { + "model": "claude-opus-5", + "family": "claude", + "source": "flag" + }, + "agreed": true, + "lanes": [ + { + "lane": 1, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "I reviewed the diff for PMAT-3704. It accurately adds the `parity-receipt` control to `extract_controls()` along with `json` and `pv-contract`, effectively making the test `every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires` pass and verify that the 7 entity types align. It also adds proper tests that check the correct structure mapping for `pv_contract` and verifies that an unmapped nested key is refused for `json`. The receipt shows all validations succeeded. No refutations found.", + "findings": [], + "raw_bytes": 3264, + "err_bytes": 100, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.1-pro-high", + "model_measured": "gemini-3.1-pro-high", + "model_source": "measured", + "family": "gemini", + "role": "independent", + "fallback": { + "judged_by": "gemini-3.1-pro-high", + "exhausted": false, + "attempts": [ + { + "model": "gemini-3.1-pro-high", + "family": "gemini", + "outcome": "answered" + } + ], + "skipped": [] + } + }, + { + "lane": 2, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "The diff for PMAT-3704 correctly implements the missing extractor positive controls for `pv-contract`, `json`, and `parity-receipt`. It adds the `pc_extract` control integration to `extract_controls()` and properly asserts that the set of controls matches the exact implemented entity types in `contracts/ontology.yaml` (`pv-contract`, `json`, `gguf`, `apr-model`, `code`, `lean`, `parity-receipt`), ensuring no extractor is left without a control. The new tests `every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires` and `the_parity_extractor_control_is_drawn_by_the_gate_every_run` strictly enforce this. Also, it adds proper failing samples, such as an unmapped nested key `orphan` for `json`, ensuring that failures are caught accurately as described by the tickets. There are no side-effects or regressions detected that would contradict the acceptance criteria.", + "findings": [], + "raw_bytes": 3038, + "err_bytes": 100, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.1-pro-low", + "model_measured": "gemini-3.1-pro-low", + "model_source": "measured", + "family": "gemini", + "role": "independent", + "fallback": { + "judged_by": "gemini-3.1-pro-low", + "exhausted": false, + "attempts": [ + { + "model": "gemini-3.1-pro-low", + "family": "gemini", + "outcome": "answered" + } + ], + "skipped": [] + } + }, + { + "lane": 3, + "status": "SUCCESS", + "verdict": "PASS", + "summary": "Lane 3 Review: PASS. The diff directly addresses PMAT-3704 without extraneous or forbidden changes. \n\nKey observations:\n1. `crates/aprender-contracts/src/lint/shapes_gate.rs` now wires positive controls for `pv-contract`, `json`, and `parity-receipt` into `extract_controls()`.\n2. Added the unit test `every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires` which asserts that the `pc_extract` control key set equals Σ's implemented entity types (7/7) and every control reports \"fired\".\n3. Added `the_parity_extractor_control_is_drawn_by_the_gate_every_run` in CLI test suite asserting `pc_extract[\"parity-receipt\"] == \"fired\"`.\n4. Extractor positive control implementations in `json.rs`, `parity_receipt.rs`, and `pv_contract.rs` accurately verify mapped extraction and refusal on planted defects.\n5. No gates are weakened and all claims in the receipt match the provided diff.", + "findings": [], + "raw_bytes": 3106, + "err_bytes": 0, + "envelope_status": "SUCCESS", + "verdict_source": "structured_output", + "grounding_check": "parity", + "model": "gemini-3.6-flash-low", + "model_measured": "gemini-3.6-flash-low", + "model_source": "measured", + "family": "gemini", + "role": "independent", + "fallback": { + "judged_by": "gemini-3.6-flash-low", + "exhausted": false, + "attempts": [ + { + "model": "gemini-3.6-flash-low", + "family": "gemini", + "outcome": "answered" + } + ], + "skipped": [] + } + } + ], + "dissent": [], + "dedup": [], + "uncovered": [], + "coverage_source": "lanes", + "partial": false, + "partial_reasons": [], + "fallback": { + "same_family_width": 1, + "chain": [ + { + "model": "gemini-3.1-pro-high", + "family": "gemini", + "disposition": "configured" + }, + { + "model": "gemini-3.1-pro-low", + "family": "gemini", + "disposition": "configured" + }, + { + "model": "gemini-3.6-flash-low", + "family": "gemini", + "disposition": "configured" + }, + { + "model": "gpt-oss-120b-medium", + "family": "openai", + "disposition": "fallback" + }, + { + "model": "qwen3.5", + "family": "qwen", + "disposition": "not-run", + "why": "no quorum.local_lane in the config — the aprender lane has no model to load" + }, + { + "model": "claude-opus-4-6-thinking", + "family": "claude", + "disposition": "width", + "why": "same family as the author: at most 1 lane, recorded role width, counted toward no floor (R-15a)" + }, + { + "model": "claude-sonnet-4-6", + "family": "claude", + "disposition": "width", + "why": "same family as the author: at most 1 lane, recorded role width, counted toward no floor (R-15a)" + } + ], + "precheck": [ + { + "family": "gemini", + "model": "gemini-3.1-pro-high", + "probe": 1, + "outcome": "live" + } + ], + "prah": { + "source": "install-receipt", + "path": "/home/noah/.claude/skills/paiml-implement/bin/prah" + } + }, + "auto_merge": { + "checked": true, + "was_armed": false, + "disarmed": false, + "note": "auto-merge not armed" + }, + "lint": { + "ok": true, + "output": "receipt complete: kind=artifact lanes=3 author=claude-opus-5/claude" + } +} diff --git a/docs/roadmaps/entries/PMAT-3704.yaml b/docs/roadmaps/entries/PMAT-3704.yaml new file mode 100644 index 0000000000..78156ab89f --- /dev/null +++ b/docs/roadmaps/entries/PMAT-3704.yaml @@ -0,0 +1,21 @@ +- id: PMAT-3704 + github_issue: 3704 + item_type: task + title: 'ONT-4c3: pc_extract draws the parity-receipt positive control every gate run, and every extractor the gate runs has a control' + status: planned + priority: high + assigned_to: null + created: 2026-09-21T14:16:17Z + updated: 2026-09-21T14:16:17Z + spec: null + acceptance_criteria: + - "cargo test -p aprender-contracts --lib lint::shapes_gate::tests::every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires passes: the pc_extract key set EQUALS contracts/ontology.yaml's implemented entity types (7) and every control is fired — red on main (4 vs 7)" + - "cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts the_parity_extractor_control_is_drawn_by_the_gate_every_run passes: top-level pc_extract[\"parity-receipt\"] == \"fired\" on aprender's contracts/, the ONT-001 v4.10 ONT-4c3 probe's question" + - "pv lint contracts/ --gate shapes --format json: verdict Pass, pc_extract 7/7 fired, by_entity_type[\"parity-receipt\"] == 7" + - "Mutation: each of the three new controls goes not-fired when its extractor regresses the way it watches (parity drops the comparator edge; json infers a class for an unmapped nested key; pv-contract infers metadata.kind) and the gate declines exit 2 naming pc_extract.; dropping a control key turns the \u03a3-tie test red" + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: "Refs #3577 (ONT-4c3; its ledger binding is infra's row, bound after this merges) and #3624 (the by_entity_type sibling, not closed here). R-3 of ONT-001: pc_extract is one planted defect per registered extractor, every run." diff --git a/docs/roadmaps/roadmap.yaml b/docs/roadmaps/roadmap.yaml index 38f3913d9c..3103603d5b 100644 --- a/docs/roadmaps/roadmap.yaml +++ b/docs/roadmaps/roadmap.yaml @@ -19946,3 +19946,24 @@ roadmap: labels: - kind:code notes: 'ANDON 2, found by aprender-62 as first responder on #3689: workspace-test-shard 1/3 (job 106309701273, runner yoga-build3) failed ont4c3_parity_receipts::the_committed_tree_agrees_with_its_own_denominator with "scripts/parity_receipt_denominator.sh: line 61: python3: command not found" for every receipt. classify() then printed nothing, the loop read that as "other", and the script said "the tree holds 0", exit 1. That is a missing interpreter scored as a count, the class of the git refusal in #3682. The cop ruled (~11:40Z) to fold it into #3689''s push under four conditions, which are done_when 1-5 above. SCOPE: scripts/parity_receipt_denominator.sh, crates/aprender-contracts-cli/tests/ont4c3_parity_receipts.rs, and this fragment. HOW IT IS MET. PY_BIN (seam PARITY_PYTHON, default python3). count_and_check checks `command -v "$PY_BIN"` after listing the tree and before any file: absent -> UNMEASURED line to stderr, return 3, which verify and --print propagate. Each file''s class must now be exactly record|legacy|other; anything else, including nothing, is "ENV the classifier () gave no answer for ", return 2 (the old `*) :` fall-through was the defect). --self-test on a runner with no interpreter prints an UNMEASURED line and exits 0: the classification rows cannot run there. The Rust test accepts exit 3 only when stderr carries both "UNMEASURED runner=" and "reason=no-interpreter interpreter="; any other non-success still fails. MEASURED at this commit: `--self-test` rc 0 with 7 rows, the 5 existing plus "no interpreter is UNMEASURED exit 3 naming it, never a count of 0" and "an interpreter that answers nothing is ENV exit 2, never a count of 0"; a bare run with python3 gives rc 0, "PASS 7 receipt(s) under evidence/parity/**, and evidence/parity/EXPECTED_RECEIPTS says 7." (the T-2 requirement, measured); PARITY_PYTHON=/nonexistent gives rc 3 "UNMEASURED runner=nopy-probe reason=no-interpreter interpreter=/nonexistent/python3 …"; PARITY_PYTHON=/bin/true gives rc 2 "ENV the classifier (/bin/true) gave no answer for evidence/parity/derived_expiries.json". THE YOGA SHAPE, a PATH made of every /usr/bin and /bin executable except python*: the PRE-FIX script (25d2ee264) prints "python3: command not found" 118 times, then "FAIL evidence/parity/EXPECTED_RECEIPTS says 7; the tree holds 0.", rc 1, which is #3689''s failure reproduced; the fix prints "UNMEASURED runner=yoga-shape reason=no-interpreter interpreter=python3 …", rc 3. MUTANT, the silent-classifier arm restored to the old `*) :` fall-through: self-test rc 1, "FAIL a silent interpreter gave exit 1: FAIL evidence/parity/EXPECTED_RECEIPTS says 2; the tree holds 0." THE RUST TEST: `cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts` gives 9 passed with python3; PARITY_PYTHON=/nonexistent/python3 gives 1 passed, printing the UNMEASURED line; PARITY_PYTHON=/bin/true gives FAILED, "exit Some(2)" plus the ENV line, so a crash is still RED and only the named UNMEASURED line buys exit 3. `cargo fmt --all -- --check` rc 0.' +- id: PMAT-3704 + github_issue: 3704 + item_type: task + title: 'ONT-4c3: pc_extract draws the parity-receipt positive control every gate run, and every extractor the gate runs has a control' + status: planned + priority: high + assigned_to: null + created: 2026-09-21T14:16:17Z + updated: 2026-09-21T14:16:17Z + spec: null + acceptance_criteria: + - "cargo test -p aprender-contracts --lib lint::shapes_gate::tests::every_implemented_entity_type_in_sigma_has_an_extract_control_and_it_fires passes: the pc_extract key set EQUALS contracts/ontology.yaml's implemented entity types (7) and every control is fired — red on main (4 vs 7)" + - "cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts the_parity_extractor_control_is_drawn_by_the_gate_every_run passes: top-level pc_extract[\"parity-receipt\"] == \"fired\" on aprender's contracts/, the ONT-001 v4.10 ONT-4c3 probe's question" + - "pv lint contracts/ --gate shapes --format json: verdict Pass, pc_extract 7/7 fired, by_entity_type[\"parity-receipt\"] == 7" + - "Mutation: each of the three new controls goes not-fired when its extractor regresses the way it watches (parity drops the comparator edge; json infers a class for an unmapped nested key; pv-contract infers metadata.kind) and the gate declines exit 2 naming pc_extract.; dropping a control key turns the \u03a3-tie test red" + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: "Refs #3577 (ONT-4c3; its ledger binding is infra's row, bound after this merges) and #3624 (the by_entity_type sibling, not closed here). R-3 of ONT-001: pc_extract is one planted defect per registered extractor, every run."