Skip to content

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

Closed

Description

@noahgift

The defect class

crates/aprender-contracts/src/lint/shapes_gate.rs:261 builds by_entity_type from an array literal of (name, count) pairs. contracts/ontology.yaml:134-142 declares the entity types with implemented: true|false. Nothing ties the two lists together.

Measured on PMAT-3577-receipts-under-contract before b88246cf2 (aprender-04's measurement, on the branch):

Σ:              {name: parity-receipt, extractor: parity_receipt, implemented: true}
by_shape:       parity-receipt-complete = 7
by_entity_type: {"apr-model": 0, "code": 270, "gguf": 8, "lean": 410, "pv-contract": 1736}   ← no parity-receipt key

The extractor ran, the shape counted seven focus nodes, Σ said implemented: true — and the report the probe reads had no key. b88246cf2 added the pair by hand. That fixes the instance; the class is still open: the next implemented: true entity type (readme, llm-context, csv — ONT-4c) will land in Σ and in an extractor and be absent from by_entity_type until someone remembers the array literal, and every ONT-001 probe of the form jq -e '.by_entity_type["<name>"] == N' reads ABSENT for it.

An absent key is not zero. The probe fails closed (jq -e on null == 7 is false), so the failure mode is a false RED with a misleading cause — the row looks unbound when the count is right and the report is incomplete. It is the same shape as #3610 one map over, and the same root-cause shape as bashrs#266: two hand-maintained lists, no mechanism tying them together, a test suite that asserted each list in isolation.

Fix (class, not instance)

Either, in this order of preference:

  1. Derive, don't enumerate. run_shapes_gate reads Σ's entity_types (it already loads contracts/ontology.yaml for the shapes) and builds by_entity_type by mapping each implemented: true name to its extractor's count through a match that is exhaustive — an implemented name with no counting arm is a compile error or a gate ERROR, never a missing key. implemented: false types are emitted as 0 or omitted by rule, and the rule is stated in the output.
  2. Failing that, a self-check in the gate: after building the map, every implemented: true name in Σ must be a key; a miss fails the gate with entity type <name> is registered in Σ as implemented and is not counted in by_entity_type — the report names the fact, so the probe's consumer never has to infer it from null.

Plus the test that closes it independently of which is chosen: read contracts/ontology.yaml, collect implemented: true names, run the gate over contracts/, assert every name is a key. Prove it discriminates by temporarily flipping one type's implemented to true without a counting arm and watching it go red.

Why pv and not per row

ONT-001 v4.10 has rows binding to by_entity_type[<name>] for gguf, apr-model, code, lean, parity-receipt, and will for readme/llm-context/csv (ONT-4c). Each is a separate instance of the same unchecked assumption — "registered in Σ" ⇒ "counted in the report". One self-check in the gate closes all of them; per-row tests close one each and the next row re-opens it.

Refs paiml/infra#831 (the probe), #3577 / #3600 (b88246cf2, the instance fix), #3610.

Activity

  1. modified the milestones: 0.70.0, 0.71.0 on Sep 20, 2026
  2. noahgift commented on Sep 23, 2026

    @noahgift
    MemberAuthor

    milestone → 0.71.0 per docs/roadmaps/milestone-triage-2026-09-23.md (#4024), operator approval 2026-09-23 (relayed by aprender-cf). Reason: debt ratchet pillars B/C (#3997: pv at the deepest level, ontology merge), slice 2

  3. modified the milestones: 0.71.0, 0.70.0 on Sep 23, 2026
  4. added
    ont-0.70Ontology spec work that must finish in 0.70 (operator 2026-09-23)
    on Sep 23, 2026
  5. noahgift commented on Sep 23, 2026

    @noahgift
    MemberAuthor

    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.

  6. noahgift commented on Sep 23, 2026

    @noahgift
    MemberAuthor

    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.

  7. modified the milestones: 0.70.0, 0.71.0 on Oct 4, 2026
  8. noahgift commented on Oct 12, 2026

    @noahgift
    MemberAuthor

    evidence for close: #4655 @ 989cb01 (on main), checked by reading main: crates/aprender-contracts/src/lint/shapes_gate.rs builds by_entity_type from sigma_implemented(contract_dir) (the Σ list in contracts/ontology.yaml), not a hand-written array, and refuses with "entity type {name} is registered in Σ as implemented and is not counted in by_entity_type" (fn by_entity_type, ~L700); the regression test near L1663 pins the json / release-evidence case and guards against going vacuous.

  9. noahgift commented on Oct 12, 2026

    @noahgift
    MemberAuthor

    Cop batch close (FLOW-004 drain, ≤43/d): fix verified on main — merged #4655 ; ancestor of origin/main: 989cb01 . Evidence: #3624 (comment)

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

    P1High priorityont-0.70Ontology spec work that must finish in 0.70 (operator 2026-09-23)owner:aprender-a7owner sessionsize:MK̂ 3-4 h

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions