Skip to content

fix(inference): rdfs:range entailment for blank nodes (+ gx refresh to v2.2.1)#83

Merged
jdsika merged 3 commits into
mainfrom
fix/rdfs-range-blank-node-inference
Jul 8, 2026
Merged

fix(inference): rdfs:range entailment for blank nodes (+ gx refresh to v2.2.1)#83
jdsika merged 3 commits into
mainfrom
fix/rdfs-range-blank-node-inference

Conversation

@jdsika

@jdsika jdsika commented Jul 7, 2026

Copy link
Copy Markdown

Description

This PR contains two related changes (separable into two PRs if maintainers prefer):

1. fix(inference) — RDFS rdfs:range entailment for blank-node objects (primary change)

OMB's RDFS inference (src/tools/validators/shacl/inference.py) only entailed rdfs:range
types when the triple object was an IRI (FILTER(isIRI(?object))). This dropped blank nodes,
so nested/embedded resources expressed as blank nodes (the normal case for compact JSON-LD that
omits per-node @id) never received their range-entailed rdf:type. As a result, instance data
had to restate @type on every nested node to satisfy the generated SHACL sh:class /
closed target-class shapes.

Root cause & fix: per RDF 1.1 Semantics (rule rdfs3) and OWL 2 RL (prp-rng), the range
type applies to the object of a triple whenever that object is not a literal — IRIs and blank
nodes both qualify; only literals are excluded (a literal can never be an RDF subject). The fix
changes FILTER(isIRI(?object)) -> FILTER(!isLiteral(?object)) in both the _DOMAIN_RANGE_RULES
range rule and apply_rdfs_inference_transitive, with spec-citation comments.

Effect: instances can rely on inference for nested-node typing (e.g. only a top-level @type)
instead of restating @type on every embedded object.

2. chore(gx) — refresh Gaia-X artifacts to service-characteristics v2.2.1

Bumps the service-characteristics submodule to the current develop tip (f4be530, tagged
v2.2.1 on the canonical Gaia-X upstream) and tracks develop via .gitmodules. Regenerates
gx.owl.ttl / gx.shacl.ttl / gx.context.jsonld (+ VERSION / UPSTREAM_* / PROPERTIES.md ->
2.2.1) and refreshes docs/registry.json + docs/ontologies/*.

Note: OMB's submodule URL (gitlab.com/ascs-ev/service-characteristics) is a mirror that does
not carry the v2.2.x release tags, so git describe there resolves to v2.1.1-31; the recorded
version is taken from the canonical upstream tag on this commit.

Versioning & Compatibility

  • Patch — the inference change is additive (only adds inferred rdf:type triples for
    non-literal objects); no schema/type changes. The gx bump is a routine upstream artifact refresh
    (v2.2.1). Existing Self-Descriptions remain valid.

Type of change

  • Change (non-breaking change or fix on an existing type)

How Has This Been Tested?

  • Inference unit tests: added test_apply_rdfs_inference_range_types_blank_and_iri_not_literal
    (blank + IRI objects receive the range type; a literal object does not); existing inference tests pass.
  • Regenerated expected snapshots: 4 .expected violation reports (envited-x, hdmap,
    manifest, openlabel-v2) now include the newly-entailed types; all failing-tests still fail as expected.
  • Data-conformance: openlabel-v2 and gx valid instances still PASS; invalid fixtures still REJECTED.
  • gx artifacts: gx.owl.ttl parses (68,199 triples, owl:versionInfo "2.2.1"); LF endings verified.

Checklist

  • My code follows the modelling guidelines of this project
  • I have performed a self-review of my own changes
  • I have updated the version number in the ontology file (gx owl:versionInfo -> 2.2.1)
  • I have added tests that prove my fix is effective
  • New and existing unit tests pass locally with my changes

Reviewer notes / caveats

  • The gx artifacts were regenerated on Windows; re-running make generate gx on Linux/CI is
    recommended to confirm byte-identical output.
  • OMB's service-characteristics submodule points at the ASCS-eV mirror, which lacks the
    v2.2.x tags — consider syncing tags to the mirror (or repointing to the canonical Gaia-X remote)
    so git describe-derived versions are reproducible from a fresh clone.
  • The two changes are independent and can be split into separate PRs on request.

jdsika and others added 3 commits July 7, 2026 16:19
RDFS range inference filtered range entailment with FILTER(isIRI(?object)),
which silently dropped the type of every blank-node object. Per RDF 1.1
Semantics rule rdfs3 and OWL 2 RL rule prp-rng, rdfs:range entails the type of
ANY resource object; only literals are excluded, because RDF forbids literals in
subject position (rdf11-mt "generalized RDF triple" note).

Change the guard to FILTER(!isLiteral(?object)) in both range rules so anonymous
(blank-node) objects -- e.g. nested JSON-LD objects without an @id -- receive
their range type. Instances can then validate against closed class shapes
without repeating the type on every nested node.

- Add a unit test asserting range entailment types blank nodes and IRIs but not
  literals.
- Regenerate 4 negative-test snapshots whose violation reports now include the
  additional inferred type (envited-x, hdmap, manifest, openlabel-v2); all still
  fail as expected.

Refs: RDF 1.1 Semantics (rdfs3); OWL 2 RL/RDF Rules (prp-rng)
Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
Update the service-characteristics submodule to the current develop tip
(f4be530), tagged v2.2.1 on the canonical Gaia-X upstream, and track the
develop branch via .gitmodules. Regenerate the gx artifacts from
linkml/gaia-x.yaml.

- Regenerate gx.owl.ttl / gx.shacl.ttl / gx.context.jsonld (+ VERSION,
  UPSTREAM_COMMIT, UPSTREAM_REF, PROPERTIES.md); version -> 2.2.1.
- Refresh docs/registry.json and docs/ontologies properties/catalog pages.
- gx test data still conforms; the invalid fixture still fails as expected.

Note: OMB's submodule URL (gitlab.com/ascs-ev/service-characteristics) is a
mirror that does not carry the v2.2.x release tags, so 'git describe' there
resolves to v2.1.1-31. The recorded version is taken from the canonical
upstream tag on this commit (gitlab.com/gaia-x/technical-committee/
service-characteristics-working-group/service-characteristics @ v2.2.1).

Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
…n the transitive variant

The transitive helper duplicates the domain/range SPARQL rules verbatim;
the fixed range guard there carried no explanation, inviting exactly the
isIRI regression the canonical rule now documents. Point readers at the
spec-citation comment.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>

@jdsika jdsika left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — verified against the head branch (verdict: ✅ merge)

1. Inference fix (isIRI!isLiteral) — correct, and the spec citations check out

  • Semantics: RDF 1.1 Semantics rule rdfs3 and OWL 2 RL prp-rng entail the range type for any triple object that is a resource; only literals are excluded (a literal cannot be an RDF subject). Blank nodes are resources — the old FILTER(isIRI(?object)) was a genuine bug that silently dropped typing for every anonymous nested node, which is the default shape of compact JSON-LD without per-node @id.
  • Completeness: both occurrences are fixed (_DOMAIN_RANGE_RULES and the transitive variant); I grepped the head tree — there is no third isIRI(?object) guard anywhere in src/. The rdfs:domain rule correctly needs no filter (subjects are never literals).
  • Test quality: the new unit test pins the exact trichotomy that matters — blank node typed ✔, IRI typed ✔, literal not typed ✔. That is the regression test this fix needs.
  • Snapshot updates: I diffed all four .expected files — the changes are presentation-only (blank owl:Restriction nodes in the report text now additionally carry entailed types like rdfs:Class); every invalid fixture still fails, every valid instance still passes. No validation outcome changed, which matches CI (Regression Tests + SHACL Validation + Data-conformance all green).
  • One consequence worth knowing (not a defect): entailment now also types blank nodes inside the ontology graph (e.g. OWL restriction bnodes), since inference runs over the combined graph. Cosmetic in reports today; if report noise ever becomes a problem, scoping the rule to the data graph is the knob.

2. gx refresh to v2.2.1 — faithful, but the compatibility claim is optimistic

  • Pinned submodule SHA f4be530 matches UPSTREAM_COMMIT; VERSION/UPSTREAM_REF/owl:versionInfo/docs/registry are all consistent at 2.2.1. The mirror-lacks-tags caveat is honestly documented.
  • The Generate Artifacts CI job regenerates on Linux and fails on any drift — it is green, so the "regenerated on Windows" caveat is already answered: the committed artifacts are byte-reproducible.
  • ⚠️ This is more than a patch: upstream 2.1.8 → 2.2.1 removes gx:ServiceEntity (+ its shape) and the gx:spiffeId / gx:trustDomain / gx:operatedBy properties, and adds gx:ComplianceCredential (+ shape). "Existing Self-Descriptions remain valid" does not hold for any SD using the removed terms. Nothing in this repo's own domains references them (CI green), so this doesn't block the merge — but the changelog should say "minor upstream bump with removals" rather than "patch". Recommend noting the removed terms in the release notes when this ships.
  • .gitmodules now tracks branch = develop: fine for --remote convenience since the SHA pin governs reproducibility, but be aware submodule update --remote now moves with upstream's develop.

3. Improvement applied on the branch

Pushed 359810e: the transitive helper duplicates the range rule verbatim but carried no rationale on its !isLiteral guard — exactly the split-brain that let one site rot the last time. It now cross-references the canonical spec-citation comment. (Longer-term, extracting the shared domain/range rule strings would remove the duplication entirely — left out of this PR to keep it tight.)

Downstream note (ontology-based-nl-search)

The consumer pipeline was exercised against these artifacts as part of the pin: schema graph loads, term index builds, and the retrieval eval gates stay at recall 1.00 — details in the pin PR.

@jdsika
jdsika merged commit 0dc1a42 into main Jul 8, 2026
12 checks passed
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