Repository navigation
Receipts under contract: parity-receipt-v1 shape + extract:parity-receipt + back-fill the 7 comparator-less receipts (ONT-4c3 re-scoped) #3577
Description
Operator ruling 2026-09-20, ruling-receipts-under-contract.md. Receipts become an entity type under contract; ONT-4c3 is re-scoped to parity receipts first. One PR, one ticket. Supersedes the split plan of filing #3576, back-filling, and unblocking 4c3 separately — they are one defect.
Receipts are the only artifact family in the tree with no shape, so nothing could have refused a receipt without a comparator. Fixing #3576 with a shell lint while ONT-4c3 stays blocked would build a second validator for exactly the thing
pvexists to validate.
Phase-0 [U]s, bound by measurement so nobody re-derives them
Σ parent — no STOP. contracts/ontology.yaml entity_types carries {name: json, extractor: json, implemented: true} with reader ontology/extract/json.rs. ParityReceipt subclasses json. The sigma-parent-absent STOP condition does not fire.
Back-fill denominator — 7, not 119. evidence/parity/**/*.json holds 119 files, but only 7 are parity records (a metrics[] array or a parity key); the other 112 are props-*, derived_expiries.json, thresholds.yaml and similar. Of the 7, 0 carry a comparator field → 7 RED before back-fill, 0 after. That is the PR-body number.
evidence/parity/l0-1/{lambda,gx10}/qwen2.5-coder-{1.5b,7b}-instruct-q4_k_m.json
evidence/parity/l0-1b/gx10/qwen2.5-coder-{1.5b,7b}-instruct-q4_k_m.json
evidence/parity/l0-1/lambda/qwen2.5-1.5b-instruct-q4_k_m.json
The extractor must handle two record shapes — found the hard way. The receipt written for #3574 was not matched by the naive predicate above: it nests the readings under raw.metrics and the verdict under result.parity, while the seven older records put metrics[] and parity at the top level. An extractor keyed to one shape will silently skip the other, and skipping is indistinguishable from passing. Either the extractor handles both, or the new receipt is reshaped to the old layout in this PR — decide it explicitly and say which.
Correction to ruling item 6 — receipt-lint is not an aprender script
The ruling says "receipt-lint is deleted, replaced by pv lint --gate shapes --path evidence/". There is no receipt-lint in this repository. The name belongs to ~/.claude/skills/paiml-implement/scripts/receipt-lint.sh, which lints quorum and dispatch receipts — a different artifact family with different fields. Deleting it would remove quorum-receipt validation that has nothing to do with parity.
The aprender-side counterparts are scripts/check_parity_receipt.sh and scripts/check_receipt_complete.sh. Item 6 should re-aim at check_parity_receipt.sh: fold what it asserts into the shape, list anything the implemented SHACL-Core subset cannot express under Σ not_expressible with a Rust fallback, and delete only what the shape replaces. One validator per artifact family — not one validator for all families.
What lands (unchanged from the ruling)
contracts/parity-receipt-v1.yamlwith its shape in the contract'sshape:block (the only syntax the gate path parses), exported tocontracts/shapes.ttl.- Constraints, using only
sh:in,sh:minCount,sh:datatype,sh:node(one level),sh:closed,resolves:— the implemented subset:
| property | constraint |
|---|---|
comparator.kind |
sh:in (llama_cpp transformers self), minCount 1 |
comparator.comparator_sha |
minCount 1 when kind ≠ self; maxCount 0 when kind = self (two shapes over one sh:node — the subset has no sh:or) |
comparator.reason |
minCount 1 when kind = self |
threshold_source |
resolves: a path that exists; the threshold value is not in the receipt |
model_sha256 |
sh:pattern ^[0-9a-f]{64}$ |
unmeasured |
minCount 1 — an empty list is a completeness claim and must be written ["none"] deliberately |
partially_receipted |
sh:datatype boolean, required |
host, backend, apr_version, generated_at |
required; backend ∈ (cpu cuda wgpu metal) |
sh:closed: true, ignoredProperties: [positions, passed, failed, min_cosine] |
PROV-O per the spec's evidence{} block: wasGeneratedBy {command, git_sha}, wasAttributedTo, generatedAtTime — bound, not just specified.
- Extractor
extract:parity-receiptoverevidence/parity/**/*.json→ focus nodes, classParityReceipt. - Positive controls, all RED→GREEN in the PR body: the ARB-APR-3: parity receipt for the decide lane's cell on the agent's host (Qwen3.5-9B-Q4_K_M @ lambda-labs) #3574 receipt passes · all 7 older receipts fail before back-fill, 0 after · plant (remove
comparatorfrom a fixture copy) → exactly 1 violation naming focus node and property · mutation (sh:inacceptsoracle) → RED ·pc_shapefired,shapes_n > 0,focus_nodes_n > 0. - Back-fill in the same PR — the 7 gain
comparator: {kind: self, comparator_sha: null, reason: "…"}andpartially_receipted: true. A relabel, not a re-measurement: it makes the history honest and the shape green. - Re-aimed as above.
- Touches no shared file: the shape is a new contract;
census.jsonis derived per decision 8; nolint-baseline.jsoncount edits. If either is touched, thetouches-shared-contractsguard labels it and it merges in a group of one behind slice M.
Fleet reality
pv 0.65.2 fleet-wide has no lint --gate and no extract, so this is a checkout-only gate until the pin lands (#3567). done_when includes "verdict rendered on ≥1 fleet host"; until then the receipt reports checkout-only: true. This row does not wait for that.
Comparator ruling the shape encodes
- (d) now — re-measure the dense basis against
llama.cpp @ llama_pin.toml build_commit(CPU). Two gates result: oracle (CPU vs llama.cpp) and arm-agreement (CUDA vs CPU). The 1.5B cell must stay RED against the oracle or the threshold discriminates nothing. A moved threshold lands with its command; the old one struck[X]. - (b) this week — Qwen 3.5 gets a pinned
transformersreference (version, model revision sha, dtype, device), out of gate,[X],comparator.kind: transformers. - (a) at the 0.70 cut — evaluate bumping the llama.cpp pin; pay the re-measurement at a cut, never mid-train.
- Until (b), Qwen 3.5 receipts are
kind: self, partially_receipted: true, and the decide lane is admissible on that stated basis, not an implied one.
STOP conditions
sigma-parent-absent (does not apply — bound to json) · subset-insufficient (list under not_expressible, Rust fallback, never a second linter) · shared-file-touched without the guard label · any threshold typed into the shape instead of resolved from thresholds.yaml.
Activity
noahgift commentedon Sep 20, 2026
Amendments (operator, 2026-09-20) — read these before starting
1. Count-based positive control — the vacuity hole inside the vacuity gate
An extractor that matches one record shape and silently skips the other is exactly the defect this shape exists to refuse, living inside the shape's own tooling. So skipping must stop being indistinguishable from passing:
evidence/parity/**— a file, not the extractor. Today that number is 8 (7 legacy records + the ARB-APR-3: parity receipt for the decide lane's cell on the agent's host (Qwen3.5-9B-Q4_K_M @ lambda-labs) #3574 receipt).Unknown{ExtractorMiss}, exit 2 — neverPass, never a fabricatedFail.Unknown{ExtractorMiss}. Point the extractor at a subdirectory → same.2. Two layouts now, one after this PR
The fork is real: the 7 legacy records put
metrics[]andparityat the top level; the #3574 receipt nestsraw.metricsandresult.parity. Do not teach the extractor both forever.parity-receipt-v1(legacy layout) andparity-receipt-v2(the ARB-APR-3: parity receipt for the decide lane's cell on the agent's host (Qwen3.5-9B-Q4_K_M @ lambda-labs) #3574 layout — raw readings and judgment separated, which is the right shape).kind: selfrelabel. It is already rewriting those files; migrating them costs nothing extra and ends the fork.ignoredPropertiesempty. AnignoredPropertieslist is a place for drift to hide; with one layout it is not needed.3. Σ parent —
jsonnow,ReceiptlaterParityReceiptsubclassesjsonfor this row. Note in the contract that it should move under a sharedReceiptclass once quorum and dispatch receipts join, so the three families inherit common shapes — R-19 materialisation makes that free. Do not createReceiptin this PR.4. The next entity type is already decided
Quorum receipts, same pattern, same session — that is the "receipts with reviewer model
unknown" row (301/301 on 09-10). The paiml-implement quorum-receipt linter stays until then; onlyscripts/check_parity_receipt.shis in scope here.done_when
(Fixed heading.
pmat work addderives neitherspec:noracceptance_criteria:, andpmat work editcannot set them on a planned ticket — paiml-mcp-agent-toolkit#1414. The ticketPMAT-3577is your authorization; this heading is your definition of done.)contracts/parity-receipt-v1.yamlandparity-receipt-v2.yamlwith their shapes in the contract'sshape:block, exported tocontracts/shapes.ttlextract:parity-receiptimplemented overevidence/parity/**/*.json, classParityReceipt(parentjson)Unknown{ExtractorMiss}exit 2comparator: {kind: self, comparator_sha: null, reason: …}andpartially_receipted: true, in one commitpv lint --gate shapes --path evidence/in the required checkcomparator) = exactly 1 violation naming focus node and property · mutation (sh:inacceptsoracle) → RED ·pc_shapefired,shapes_n > 0,focus_nodes_n > 0scripts/check_parity_receipt.shfolded into the shape, or its remainder listed underΣ not_expressiblewith a Rust fallback — never a second lintergated_rmsnormfixturecheckout-only: truein the receipt until thepvpin lands (The fleet-pinned pv is 0.65.2 and has no 'extract' subcommand and no '--gate' flag — every SHACL gate measured on the fleet binary is unrunnable, not merely unmeasured #3567); then a verdict rendered on ≥1 fleet hostignoredPropertiesempty after migrationSTOP:
subset-insufficient(list undernot_expressible, Rust fallback, never a second linter) ·shared-file-touchedwithout thetouches-shared-contractslabel · any threshold typed into the shape instead of resolved fromthresholds.yaml·Unknown{ExtractorMiss}at the end of the run.roadmap: the five tickets' fragments (check_roadmap_fragment_required)
noahgift commentedon Sep 20, 2026
Correction: the committed denominator is 7, not 8. Measured by the ontology lane on
origin/main: 113 files underevidence/parity/, 7 parity records, 0 with acomparatorfield. My "8" counted the #3574 receipt, which is in PR #3575 and still open — not on main. Counting my own unmerged work as landed; the same stale-reference error four times over today.Ruling: commit 7. #3575 carries the bump to 8 when it lands, and that is recorded as a landing obligation on it. If #3575 merges without the bump, this row's gate fires
Unknown{ExtractorMiss}and names it — which is the acceptance criterion working as specified, not a defect.Also confirmed:
model_sha256ispattern-only with nominCount, deliberately. Six of the seven legacy records have no recoverable model hash, and manufacturing one by hashing the files today would be, in the lane's words, "a claim about a different world wearing a witness's clothes" — a 2026-09-20 measurement attached to a 2026-09-06 receipt. Absent is legal; present must be 64-hex;partially_receipted: trueplus anunmeasuredentry carries the honesty. That is ONT-4c1's no measurement is not a match.noahgift commentedon Sep 20, 2026
Item 6, corrected a second time — my re-aim was itself mis-aimed, one level down
I corrected the ruling's "delete
receipt-lint" to "re-aim atscripts/check_parity_receipt.sh". That was wrong for the same reason the original was, and the ontology lane caught it before a line was written.There are two artifact families sharing the word "parity":
compute_classrulesscripts/lib/parity_receipt_cases/*.jsoncheck_parity_receipt.sh— requiresinstrument,protocol_ref,model,lanes[]; guards the #2696 defect (published apr takes the CPU path and reports 0.099×)evidence/parity/**Verified here on a real record —
evidence/parity/l0-1/gx10/qwen2.5-coder-1.5b-instruct-q4_k_m.jsoncarries exactly['failed','metrics','model','parity','passed','tokens']: not one of the throughput family's four required keys, and never has.check_parity_receipt.sh's callers arecheck_perf_claims_cite_receipts.shand the dogfood/host-receipt paths — throughput surfaces.So folding it into this shape would delete the #2696 validator and leave the throughput family with nothing — precisely the deletion the ruling refused for
receipt-lint, one level further down. Two corrections, same mistake, and I made the second one.Ruling
check_parity_receipt.shis out of scope and untouched. Stated in the contract and in the PR body, so the next reader does not "finish the job". Thedone_whenline — folded into the shape, or its remainder listed underΣ not_expressible— is satisfied by the honest answer: nothing to fold, because it governs a different family.And the ruling's premise gets stronger, not weaker. "Receipts are the only artifact family in the tree with no shape" turns out to be literally true of the logit records: they have had no validator of any kind. That is why seven of them carry no comparator and nobody noticed for months — there was nothing that could have noticed.
Throughput parity receipts become their own row, after quorum receipts, in the same entity-type sequence.
The class, named — third instance today
Same word, different family. A name collision that invites deleting a validator:
receipt-lint(quorum/dispatch),check_parity_receipt(throughput), and the logit records this row covers. Each time, the "obvious" consolidation would have removed coverage from a family nobody was looking at. The discriminator is the artifact's required keys, not its filename — one validator per family, and the family is defined by what the records actually contain.noahgift commentedon Sep 20, 2026
Applying the key-set discriminator to the "three families" ruling — it over-splits by one
The rule is right: two validators' key sets differ ⇒ different families. Applied to these two artifacts, measured:
The arb-apr record is not a different family — it is the same family with the legacy record nested verbatim inside it under
raw, plus provenance. Same measurement (CPU vs CUDA cosine per position), same producer (apr parity), same subject. The new keys are all about the measurement — comparator, threshold source, host, binary, what was not measured — not a different measurement.So the discriminator says two families, not three:
instrument,protocol_ref,model,lanes[]check_parity_receipt.sh— untouched, guards #2696model,metrics[]… at v1; + comparator/provenance at v2parity-receipt-v1+-v2, this rowTreating the arb-apr record as a third family would be the mirror of the mistake we just avoided — instead of one validator absorbing two families, two validators would split one family, and the seven legacy records would end up governed by a shape that does not know the eighth is the same artifact enriched. That is also why the back-fill migrates v1 → v2 in one commit and
ignoredPropertiescomes out empty: one family, one layout, after this PR.So this row covers the whole logit family — the 7 legacy records and the arb-apr one — and the ruling's "logit gets its own shape only if its keys are already known" is satisfied: they are known, they were measured, and they are a strict subset of v2's. No guessing, no follow-up row needed for it.
Throughput stays exactly where the ruling put it:
check_parity_receipt.sh, untouched, until a shape reproduces every one of its checks — proven by the #2696 fixture staying RED. That fixture is the acceptance, not a code review.Quorum and dispatch receipts remain the next entity types, in that order, as separate rows.
PMAT-3577: name it WrongCorpus, not ExtractorMiss — a prefix of its o…
PMAT-3577: the ONT-4c3 divergence is filed as paiml/infra#814, not le…
PMAT-3577: count parity-receipt in by_entity_type — ONT-001 v4.10's p…
PMAT-3577: regenerate contracts.nt with a pv built from THIS worktree…
#3600: the migration broke TWO readers of the records, including the …
#3600: the migration broke TWO readers of the records, including the …
4 remaining items
PMAT-3577: dispatch_runtime_commands back to main's complexity — #3604'…
noahgift commentedon Sep 21, 2026
ONT-4c3 is bound in the ONT-001 ledger: paiml/infra#897 (PMAT-894), merged as
d7317fca. The binding names #3706's merge52f43da71, not #3600's: at the #3669 squasha877fa056the row's probe was red on its gate conjunct (pc_extracthad noparity-receiptkey, becauseparity_receipt::positive_controlwas only called by a unit test), and #3706 is what wired it (plus thejsonandpv-contractcontrols, withpc_extracttied to Σ). The row's note names #3577, #3600 anda877fa056. R-23 from the pv at52f43da71: aprender Pass,by_entity_type["parity-receipt"]=7,pc_extract7/7 fired; rmedia Pass withparity-receipt: 0. That satisfies #3706's keep-open reason for this issue. I haven't checked this issue's other done_when items (--path evidence/in the required check; the fleet-host verdict once the pv pin lands), so I'm leaving closure to its owner.noahgift commentedon Sep 23, 2026
milestone → 0.71.0 per docs/roadmaps/milestone-triage-2026-09-23.md (#4024), operator approval 2026-09-23 (relayed by aprender-cf). Reason: model/backend correctness or the certified matrix: Don't Leave Behind (#3994)
noahgift commentedon Sep 23, 2026
triage 2026-09-23 (aprender-dd): kept in 0.70.0 and labelled ont-0.70. Operator, verbatim (relayed by the cop aprender-cf): "the ontology spec MUST be finished in .7". Classified as ontology/SHACL spec work: its title names ONT/ontology/SHACL/capability cells, or its body cites an ONT- row.
noahgift commentedon Sep 23, 2026
Correction to the triage comment above (aprender-dd): this issue was MOVED from 0.71.0 to 0.70.0, not "kept". A failed first attempt applied the milestone before its label step errored, so the second pass read it as already in 0.70.0. The move and the reason stand.
noahgift commentedon Sep 24, 2026
Closing on evidence (pvl-b, 2026-09-24). Landed via #3669 (release/0.69-batch), on origin/main and batch/0.70.0: contracts/parity-receipt-v2.yaml (shape + comparator), v1 is recorded as retired with no instances, extract:parity-receipt (crates/aprender-contracts/src/ontology/extract/parity_receipt.rs) refuses an unmigrated v1 record by name. Measured on batch/0.70.0 @5a70d9195:
scripts/parity_receipt_denominator.shgives PASS, 7 receipts == EXPECTED_RECEIPTS 7, and all 7 carrycomparator(0 RED after back-fill, as the issue's PR-body number requires). The PR never said Closes, so the issue stayed open.roadmap: push the five tickets minted today (they existed only in a w…
release(0.69): batch — 15 receipted PRs + fleet-pv andon fix, one CI …
PMAT-3685: apr parity REFUSES (exit 12) a model the CUDA capability g…