Skip to content

ONT-4c3: pc_extract never carries parity-receipt — the positive control is written, unit-tested, and never drawn by the gate #3704

Closed

Description

@noahgift

The defect

pv lint contracts/ --gate shapes --format json never reports pc_extract["parity-receipt"]. The extractor's positive control exists — ontology/extract/parity_receipt.rs::positive_control, whose doc comment says it is "drawn beside the corpus so 'the extractor still reads this layout' is measured rather than assumed" — but its only caller is parity_receipt_tests.rs:110. lint/shapes_gate.rs::extract_controls() is a hand-written array of four entries (gguf, apr-model, code, lean), and nothing ties it to the extractors the gate actually runs.

Measured 2026-09-21 with the pv built at a877fa056 (the #3669 squash that landed #3600), on aprender's own contracts/:

verdict=Pass  by_entity_type={apr-model:0, code:270, gguf:8, lean:410, parity-receipt:7, pv-contract:1763}
pc_extract={apr-model:fired, code:fired, gguf:fired, lean:fired}        ← no parity-receipt key

extract_controls() is byte-identical on #3600's head 242bdc3b5, on a877fa056, and on origin/main today — the batch did not drop it; it was never wired.

Why it matters

ONT-001 §5 ONT-4c3's probe (infra scripts/ont/done_when/ONT-4c3.sh) asserts .pc_extract["parity-receipt"]=="fired". So the row is RED on aprender main whatever the ledger says, and infra cannot bind it (#3577's ONT-4c3 ledger binding). More generally: a parity-receipt extractor that regressed to reading nothing would be reported by by_entity_type as a count, never refused by a control — the control that would refuse it does not run.

Five whys

  1. pc_extract has no parity-receipt key → extract_controls() does not call parity_receipt::positive_control.
  2. → the list is hand-written, and adding an extractor does not force adding its control.
  3. → no test compares the set of controls to the set of extractors (the same shape as pv: by_entity_type is a hand-written list parallel to Σ — derive it from ontology.yaml, or refuse when an implemented entity type is uncounted #3624 for by_entity_type, and bashrs#266's "two hand-maintained lists with nothing tying them together").

Fix (this ticket)

  • extract_controls() draws parity_receipt::positive_control every gate run, on a sample record, under the key parity-receipt.
  • A test that fails when an extractor the shapes gate runs has no pc_extract entry — so the next extractor cannot ship without its control.
  • A not-fired parity control makes the gate refuse (as the other four already do), mutation-proved.

Refs #3577 (ONT-4c3), #3624 (the by_entity_type sibling). ONT-001 v4.10.

Activity

  1. added a commit that references this issue on Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions