Three-graph CI gate
Three-graph CI gate
The three-graph gate enforces ODR-0004 §3a’s five-part class / shape / annotation separation contract across the committed corpus. The contract says: classes, shapes, and annotations live in different graphs; no graph contains material that belongs to another. The gate runs five SHACL-AF queries against the emitted directory and fails on any violation.
What the gate enforces
The five separation rules per ODR-0004 §3a:
- No
sh:NodeShapetriples in class graphs —opda-classes.ttl,opda-<module>.ttl× 6, andopda-vocabularies.ttlMUST NOT carry SHACL shape declarations. - No
owl:Classtriples in shape graphs —opda-shapes.ttlandopda-<module>-shapes.ttl× 6 MUST NOT carry class declarations. - No
owl:importstriples in shape graphs — shape graphs are pure constraint material; imports live in the TBox graphs. - No
dct:references <https://w3id.org/dpv/…>triples in class graphs — DPV co-annotation is a reference-not-import concern (per ADR-0012) confined to-annotations.ttlfiles. - Foundation meta-shapes only in
opda-shapes.ttl— Cat 3 NoIdentityOverride, Cat 5 MetaShapeOverShapeGraph, and the three-rule interface contract meta-shapes live in the foundation shape graph; per-module shape graphs carry per-module shapes only.
Command
opda-gen ci-three-graph --ontology-dir source/03-standards/ontology
Default --ontology-dir resolves to source/03-standards/ontology/ relative to the OPDA repo root (per the _default_ontology_dir() walker in tools/opda-gen/src/opda_gen/cli.py).
Expected output on success:
three-graph CI: PASS (all 5 checks)
Expected output on failure (per violation):
THREE-GRAPH VIOLATION: <description of which rule and where>
Exit code: 0 on PASS, 1 on any violation.
Workflow definition
The gate runs inside .github/workflows/ontology-byte-identity.yml as the Three-graph CI test step:
- name: Three-graph CI test
working-directory: tools/opda-gen
run: opda-gen ci-three-graph --ontology-dir ../../source/03-standards/ontology
It runs immediately after Byte-identity diff so any byte-identity failure short-circuits before the separation check (an emitter regression often surfaces as a byte-identity violation before it surfaces as a separation violation).
Implementation
The test body lives in tools/opda-gen/src/opda_gen/ci/three_graph_test.py. It loads each TTL via rdflib, runs five filter queries, and returns a list of violation strings (empty list = PASS).
The CLI wrapper at tools/opda-gen/src/opda_gen/cli.py prints each violation to stderr prefixed with THREE-GRAPH VIOLATION: and exits 1.
Failure mode
A typical violation looks like:
THREE-GRAPH VIOLATION: opda-property.ttl contains 3 sh:NodeShape triples (rule 1)
Investigation order:
- Identify the offending triples via
grep -n 'sh:NodeShape' source/03-standards/ontology/opda-property.ttl. - Determine whether the triples should move to
opda-property-shapes.ttl(correct destination per ODR-0004 §3a) or be removed entirely (if they’re spurious). - Fix the emitter source — most likely a constraint definition leaked from the shape emitter into the module emitter.
- Re-emit (
opda-gen emit), confirm byte-identity, re-runci-three-graph.
The three-graph gate is the ontology-engineering analogue of a “no import * from "private/internal/sql"” lint: it preserves the architectural boundary that makes the three derived consumer profiles (opda-validation, opda-ui, opda-inference) composable.
Source ODR + ADR
- ODR-0004 — PDTF ontology foundation §3a — five-part separation contract.
- ADR-0009 — Foundation TTL emission — initial three-graph gate activation.
- ADR-0012 — SHACL + DPV annotation emission — extends the separation contract to per-module shape + annotation graphs.
Comments