Repository navigation
Description
Corrected 2026-10-15. This issue first said the rule is dropped silently. That doesn't hold on linkml#3990 (
c48bb2a47) or #14 (536d6f194): gen-shacl logsWARNING … Rule 2 of class 'Thing': skipped, because its postconditions use any_of.Warnings name a rule by index anddescription, nottitle, so a search for the title finds nothing. Dropping the siblingslot_conditionsfollows linkml#3989's contract that a rule is translated whole or skipped. On upstream main such rules are skipped at DEBUG level only; linkml#3989 raises that to a warning. #48 is unrelated (class expressions on subclasses).
What
gen-shacl skips every rule whose conditions use an expression-level any_of (likewise all_of, exactly_one_of, none_of). gen-json-schema enforces the same rule, so the two disagree on the same instances.
Reproduction (linkml#3990 head c48bb2a47)
id: https://example.org/m
name: m
prefixes:
ex: https://example.org/m/
linkml: https://w3id.org/linkml/
default_prefix: ex
default_range: string
imports: [linkml:types]
classes:
Thing:
tree_root: true
attributes:
kind: {range: string}
status: {range: string}
level: {range: integer}
rules:
- preconditions:
slot_conditions:
kind: {equals_string: b}
postconditions:
slot_conditions:
level: {minimum_value: 1}
any_of:
- slot_conditions:
status: {equals_string: ok}
- slot_conditions:
status: {equals_string: fine}gen-json-schema's then is {"anyOf": [{"properties": {"status": {"const": "ok"}}, "required": ["status"]}, {… "fine" …}], "properties": {"level": {"minimum": 1}}, "required": ["level"]}.
Instance (kind: b) |
JSON Schema | SHACL (pyshacl) |
|---|---|---|
level: 2, status: ok |
valid | conforms |
level: 2, status: bad |
invalid | conforms |
level: 0, status: ok |
invalid | conforms |
Proposed fix
- Translate the operators in rule conditions. A violation of P → (A ∧ (B ∨ C)) is P ∧ (¬A ∨ (¬B ∧ ¬C)); the branches reuse the per-slot patterns of
_postcondition_violations. - Read slots inside the operands as gen-json-schema has since Rules support in JSON Schema generator linkml/linkml#1100: preconditions require their slots, and postconditions do too unless the rule is
open_world. Specification: define what a slot condition means for an absent slot in class-level boolean expressions #49 keeps rules on this reading. - Name the rule
titlein skip warnings (_RuleSite.__str__).
Acceptance criteria
_json_schema_and_shacl_verdicts(test_shaclgen.py) agrees forany_ofin postconditions, with siblingslot_conditions, and underopen_world. The other three operators are translated the same way or stay skipped with a warning.- Skip warnings name the rule
titlewhen it has one.
Base: linkml#3990 (fork #20).
Activity
[-]gen-shacl rules: a postcondition block containing any_of is dropped whole and silently, taking its sibling slot_conditions with it[/-][+]gen-shacl rules: translate any_of (and the other boolean operators) in rule conditions, as gen-json-schema does[/+]