The gate is built to be embedded in front of an Edit/Write (or a pre-commit hook). You supply, at most, four things: your SHACL shapes, a SHACL-conformance backend, a phase-completion backend, and which mutation types need an owner. Every one has a degrade-open default, so a partial wiring still runs.
from owl_sop_enforcement_gate import OntologyEditGate
gate = OntologyEditGate(
mode="enforce",
shapes_graph_path="ontology/my_shapes.shacl.ttl", # your shapes; omit to skip SHACL
)With the optional shacl extra installed (pip install -e ".[shacl]"), the bundled reference_shacl_check runs a real pyshacl validation against those shapes. With it absent, the check returns "skipped" and the edit is allowed (logged) — your CI never breaks for lack of a validator.
If you have your own SHACL toolkit (or want to reuse a project-wide validator), inject it. The seam is (data_path, shapes_path) -> result-with-.status:
def my_shacl_check(data_path, shapes_path=None):
report = my_toolkit.validate(data_path, shapes_path)
if report.conforms:
return ShaclResult(status="ok")
return ShaclResult(status="violations",
violation_count=report.count,
detail=report.text)
gate = OntologyEditGate(mode="enforce",
shapes_graph_path="ontology/my_shapes.shacl.ttl",
shacl_check=my_shacl_check)Return status="skipped" (or "error") to degrade open for an edit your toolkit can't process — the gate will allow it and log that conformance was not run. A separate OWL/SHACL governance toolkit repo is the natural home for the real validator + reasoner; this gate only needs its yes/no answer.
The seam is (change_set_id, mutation_type) -> (all_complete, missing_phases):
def my_phase_check(change_set_id, mutation_type):
required = my_tracker.required_phases_for(mutation_type)
missing = [p for p in required if not my_tracker.is_complete(change_set_id, p)]
return (len(missing) == 0, missing)
gate = OntologyEditGate(mode="enforce", phase_check=my_phase_check)A dedicated phase / step tracker repo (mapping each mutation type to its prerequisite governance steps and tracking their completion per change-set) is the natural backend here. The reference impl reports everything complete, so the gate is a no-op on this axis until you wire a tracker in.
gate = OntologyEditGate(
mode="enforce",
owner_required_mutations={"new_class", "delete_class", "rename_property"},
)Those mutation types now require a responsible_owner in addition to the verbatim rationale (the rest require only the rationale). Customize the path→mutation mapping itself with mutation_detector= if your surfaces are richer than .ttl/.json.
def sink(kind, detail):
if kind == "edit_blocked":
notify_channel(detail) # your Slack/webhook/etc.
gate = OntologyEditGate(mode="enforce", event_sink=sink)Events emitted: edit_allowed, edit_blocked, edit_overridden, phase_check_error. If you omit event_sink, the gate makes a best-effort probe for an importable advisor module and is otherwise a silent no-op — it never hard-imports anything.
OSEG_GATE_MODE=enforce \
OSEG_SHAPES_GRAPH=ontology/my_shapes.shacl.ttl \
OSEG_OWNER_REQUIRED_MUTATIONS=new_class,delete_class \
owl-sop-gate '{"file_path": "ontology/core.ttl", "verbatim_response": "...", "responsible_owner": "..."}'Exit 0 = allowed (or advisory/override); exit 2 = blocked in enforce mode (the conventional pre-commit reject code). Env vars: OSEG_GATE_MODE, OSEG_SHAPES_GRAPH, OSEG_OWNER_REQUIRED_MUTATIONS, OSEG_OVERRIDE (+ OSEG_OVERRIDE_REASON), and OSEG_TOOL_INPUT (the JSON envelope, as an alternative to argv[1]).
- Ship with
mode="advisory"— every finding is logged, nothing is blocked. Watch your logs. - Flip to
mode="enforce"once the noise is gone. Now bad edits are rejected. - Keep
OSEG_OVERRIDE=1(with a reason) for the genuine exception — it is allowed and loudly logged, never silent.
- Don't make
evaluate()raise or exit — its purity is what makes the gate testable; all control flow belongs inenforce(). - Don't make a degraded seam (
"skipped"/ a brokenphase_check) block — BLOCK must require a real finding, or the gate becomes the thing that breaks minimal CI. - Don't drop the substance floor to a presence check —
"ok"passing is the failure mode the gate exists to prevent.