Skip to content

fix(gen-shacl): enforce contextual class range expressions - #30

Open
jdsika wants to merge 1 commit into
feat/shaclgen-class-boolean-expressionsfrom
feat/shaclgen-inlined-range-sh-node
Open

jdsika wants to merge 1 commit into
feat/shaclgen-class-boolean-expressionsfrom
feat/shaclgen-inlined-range-sh-node

Conversation

@jdsika

@jdsika jdsika commented Oct 2, 2026 •

Copy link
Copy Markdown

Problem and behavior

gen-shacl silently ignored a class-ranged slot's range_expression. A schema could require Document.license.category to equal license, yet generated SHACL accepted an unrelated category while generated JSON Schema rejected it.

The generator now adds an anonymous sh:node constraint for the expression and retains the ordinary range check. In class-URI naming mode a value must still satisfy sh:class Resource; matching its contents does not substitute for membership. The constraint applies only in the declared context. No new option is introduced.

Stacked on #29. This replaces the earlier proposed --inlined-as-node feature: the downstream requirement is contextual constraints, and the existing metamodel can express them without using JSON/YAML inlining to waive RDF class membership.

Scope and standards

  • Supports contextual expressions on named class-ranged slots, class alternatives in any_of, and nested slot conditions. Reuses the existing class-expression translation for boolean operators and value/presence/cardinality constraints.
  • Scalar-literal equals_expression conditions are translated without evaluating user code. This includes the metamodel's boolean grouping-slot constraint. Variables, function calls, containers and arithmetic remain explicitly unsupported.
  • Resolves nested conditions against the value class's induced slots. Imported prefixes are loaded before namespace resolution even when imported shapes are excluded.
  • Unsupported expressions on ordinary slots or alternatives raise an error. Inside class-level boolean expressions, the existing warning and whole-operator skip policy remains. Expressions on datatype/enum-ranged slots and previously unsupported slot-condition operators are not added. A linkml:Any class range can combine class, datatype, and enum alternatives.
  • LinkML range_expression and slot_conditions provide the schema constructs. SHACL node constraints apply the contextual shape; class constraints retain the separate membership requirement. This preserves the LinkML ClassRange mapping.
  • Existing naming behavior remains. No constants derived from consumer-specific classes, properties, categories, or vocabulary lists are added.

The upstream design rationale is discussion #2791, where a maintainer demonstrates a generic quantity range with a contextual unit constraint. Merged JSON Schema PR #2860 implements the same nested-constraint construct.

Downstream evidence

The origin is ontology-management-base#106. Its migration represented contextual link requirements as subclasses while existing RDF stated only the generic link type. The earlier flag validated those subclasses' shapes without requiring their membership. An OMB blank-node inference defect was separate and already fixed in e99ace5.

An isolated model revision uses ordinary link/manifest ranges plus anonymous contextual expressions, including closed categories, conditional artifact metadata requirements, and simulation-manifest member requirements. Consumer fixtures and expected verdicts are unchanged. This is a migration experiment, not a committed change to OMB; the caller must remove the old flag and revise the affected models before adopting this replacement.

Cross-generator review

  • JSON Schema: executable positive/negative parity tests cover contextual cases, repeated values, nested constraints, and conditional requirements. General equals_expression validation is not implemented there; the scalar-expression SHACL cases are not claimed as parity tests.
  • OWL: already emits nested restrictions for range_expression; this PR does not change OWL output. Existing limitations include the treatment of equals_string as a datatype pattern and incomplete cardinalities in anonymous expressions, so full validation equivalence is not claimed.
  • ShEx: currently omits range_expression. Python/Pydantic generation likewise has no general implementation of this construct. These are existing support gaps, not evidence that the SHACL mapping can be assumed equivalent across all artifacts.

Validation

Real generated artifacts are validated with pySHACL (including meta-SHACL) and JSON Schema; no mocks or weakened assertions. Tests cover class membership, local scope, references, inheritance, imported prefixes, naming/suffixes, union branches, nested/boolean/conditional expressions, unsupported-input failures, and CLI behavior.

  • Final generator/compliance regression: 11,228 passed, 1,960 skipped, 3 expected failures and 261 subtests passed. The mixed class/datatype range-expression compliance cases generate successfully without weakening their assertions or skip policy.
  • 63 new contract cases passed, including mixed class/datatype/enum alternatives, scalar expression constants and explicit rejection of unsupported expressions. Both unchanged slow metamodel project-generation tests passed locally after fixing the CI-discovered boolean constant case.
  • Repository pre-commit hooks passed. The final Sphinx build, including notebook execution, passed with warnings treated as errors in a clean environment. Both packages' wheel/sdist builds and final-commit package CI passed.
  • Consumer experiment: all four migrated domains were regenerated (SHACL, OWL and JSON-LD context) in isolation. Of 62 unchanged fixtures, 61 produced their expected verdicts individually. The remaining survey-result fixture fails identically on the unchanged baseline because its referenced service offering is absent from that input graph; including the referenced fixture makes both baseline and candidate pass. No fixtures or expected verdicts were changed. The four generated JSON-LD contexts and named OWL assertions remain unchanged; anonymous OWL restrictions intentionally change. This is an integration experiment using the already-consumed generator stack, not standalone proof for later PRs.
  • All 17 remote checks passed at final signed commit 3f14cf91bdc82111ecca5aaf22fbbab21a13497e, including the full regular Linux/Windows Python matrix, all four slow-test jobs, both notebook jobs, dependency/link checks and the package build. CI run.

The implementation and validation are ready for fork review. Adopting it in OMB still requires the model/caller migration described above; no upstream PR is opened by this change.

@jdsika jdsika self-assigned this Oct 2, 2026
@jdsika
jdsika force-pushed the feat/shaclgen-inlined-range-sh-node branch from aee8951 to 5db175a Compare October 2, 2026 15:06
@jdsika
jdsika changed the base branch from main to feat/shaclgen-class-boolean-expressions October 2, 2026 15:06
@jdsika
jdsika force-pushed the feat/shaclgen-inlined-range-sh-node branch from 5db175a to 111a453 Compare October 2, 2026 15:13
@jdsika
jdsika force-pushed the feat/shaclgen-inlined-range-sh-node branch from 111a453 to 74a2483 Compare October 5, 2026 07:15
@jdsika
jdsika marked this pull request as draft October 5, 2026 07:28
@jdsika jdsika changed the title feat(gen-shacl): validate inlined values by their range shape with --inlined-as-node fix(gen-shacl): enforce contextual class range expressions Oct 5, 2026
@jdsika
jdsika force-pushed the feat/shaclgen-inlined-range-sh-node branch from 74a2483 to 937b986 Compare October 5, 2026 09:13
Translate class-ranged range_expression constraints into anonymous node
shapes while retaining ordinary range checks. Resolve nested conditions
against the value class, including induced slots and imported prefixes.

Cover slot alternatives, nested and conditional expressions, class
membership, JSON Schema parity, CLI behavior, and unsupported inputs with
real validator tests. Document the supported scope and standard mappings.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
jdsika force-pushed the feat/shaclgen-inlined-range-sh-node branch from 937b986 to 3f14cf9 Compare October 5, 2026 09:52
@jdsika
jdsika marked this pull request as ready for review October 5, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant