feat(gen-shacl): add presence-implies-value rule pattern (stacked on #11)#19
Conversation
Adversarial audit findings (PIV converter, audited at stack tip incl. the hardening PR)Two substantive findings, both empirically demonstrated with pyshacl end-to-end probes and cross-checked against LinkML's reference rule semantics ( A1 — real bug: greedy dispatch drops extra pre/postcondition operators → false positives. A2 — edge case: boolean-guard shadows PIV for Verified clean (attacked, held up): absent-target semantics (violation via Test gaps (cosmetic): These findings equally apply to the consolidated branch of #18. Suggest addressing A1/A2 as a follow-up commit on this stack before upstreaming. |
Generalise the boolean-guard SHACL-SPARQL pattern to enum-valued targets. A rule whose precondition is `value_presence: PRESENT` on a value slot and whose postcondition is `equals_string` / `equals_string_in` on a target slot now emits an `sh:sparql` constraint requiring the target slot to be present and hold one of the allowed values. Each allowed value resolves to its enum `meaning` IRI, with a string-literal fallback. Motivating case (aiSim environment): - "if texture_sky_color is set, sky_model must be TextureSky" - "if overcast_sky_illuminance is set, sky_model must be OvercastSky or MeasuredOvercastSky" The existing boolean-guard (`equals_string: "true"`) and exclusive-value patterns are unchanged; boolean guard keeps priority over the new branch. Adds focused unit tests (enum IRI vs. literal fallback, single value vs. set membership, message emission, SPARQL syntax) plus pyshacl end-to-end validation for the new pattern. (cherry picked from commit ea5cf57)
2e56a36 to
8999ad6
Compare
Summary
Adds the presence-implies-value rule pattern to the SHACL generator's
rules → SHACL-SPARQL converter: a LinkML
rules:block whose precondition isvalue_presence: PRESENTon a guard slot and whose postcondition isequals_string/equals_string_inon a target slot is translated into ash:SPARQLConstraintthat flags focus nodes where the guard is present but thetarget does not hold one of the allowed values. Enum permissible values resolve
to their
meaningIRIs; values withoutmeaningfall back to string literals.$thisis pre-bound per W3C SHACL §5.3.1.Example (from the modeled ENVITED-X use case): if
sun_altitudeis present,daytimemust be one ofday/twilight— cross-parameter consistency theplain per-slot SHACL constraints cannot express.
Stack position
This PR is stacked on #11 (
feat/shaclgen-rules-sparql, green CI), whichprovides the rules → SPARQL framework (
_add_rules,_rule_to_sparql, theboolean-guard and exclusive-value patterns). The base branch of this PR is
feat/shaclgen-rules-sparql, so only the presence-implies-value delta shows inthe diff:
Fork PRs here are review + CI artifacts (fork
mainmirrors upstream); theintegration branch is
feat/envited-x-pipeline, and upstream submissions tolinkml/linkmlfollow the same sequence.Relationship to #17 (supersedes)
#17 aimed to deliver this feature as a self-contained branch off
mainbybundling a copy of the #11 framework. The assembled bundle has four defects
(found while auditing; #17's CI masked them because
quality-checksfailedfirst and the test job never ran):
if self.emit_rules: self._add_rules(g, class_uri_with_suffix, c)in theclass loop was lost during assembly —
_add_rulesis defined but neverinvoked, so no
sh:sparqlconstraint is ever emitted. 16 of thebranch's own tests fail locally.
PresenceEnumis used inshaclgen.pybut never imported (ruff F821 ×2)._RULES_SCHEMA_YAML/EX_RULESfixtures thatwere not carried over (F821 ×3).
ruff format --checkfails on the test file.Instead of patching the bundle, this PR takes the known-good incremental
commit from the consolidated branch (
ea5cf575onfeat/shacl-rule-converters, whose full-stack CI is green on #18) and stacksit on #11. The commit content is unchanged (cherry-pick, original authorship
preserved). Recommend closing #17 in favour of this PR.
How was this tested?
tests/linkml/test_generators/test_shaclgen.pyon this branch: 84 passed(framework suite from feat(gen-shacl): generate sh:sparql constraints from LinkML rules #11 + 20 presence-implies-value tests: structural
triple assertions,
prepareQuerysyntax validation, and pyshacl end-to-endwith
advanced=True— conforming instances pass, crafted violations fail).ruff checkandruff format --checkclean on both changed files(
shaclgen.py,test_shaclgen.py).ea5cf575of feat(gen-shacl): translate LinkML rules to SHACL-SPARQL constraints #18, where the full linkmlCI matrix (ubuntu/windows × 3.10/3.13, slow tests, notebook tests) is green.
Areas of uncertainty
equals_string: "true"on non-boolean slots). Both are fixed with regression tests in the stacked fix(gen-shacl): enforce operator exactness, nested-slot parity, numeric bounds #22 — fold them in when upstreaming.Checklist
AI Assistance
If you used AI tools while preparing this PR, you are still the author and responsible for understanding, verifying, and defending your submission. Please engage with reviewers personally rather than through your agent during feedback and revisions. See our AI Covenant for details.