fix(inference): rdfs:range entailment for blank nodes (+ gx refresh to v2.2.1)#83
Merged
Merged
Conversation
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
commented
Jul 8, 2026
jdsika
left a comment
Author
There was a problem hiding this comment.
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
rdfs3and OWL 2 RLprp-rngentail 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 oldFILTER(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_RULESand the transitive variant); I grepped the head tree — there is no thirdisIRI(?object)guard anywhere insrc/. Therdfs:domainrule 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
.expectedfiles — the changes are presentation-only (blankowl:Restrictionnodes in the report text now additionally carry entailed types likerdfs: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
f4be530matchesUPSTREAM_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 removesgx:ServiceEntity(+ its shape) and thegx:spiffeId/gx:trustDomain/gx:operatedByproperties, and addsgx: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..gitmodulesnow tracksbranch = develop: fine for--remoteconvenience since the SHA pin governs reproducibility, but be awaresubmodule update --remotenow 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR contains two related changes (separable into two PRs if maintainers prefer):
1.
fix(inference)— RDFSrdfs:rangeentailment for blank-node objects (primary change)OMB's RDFS inference (
src/tools/validators/shacl/inference.py) only entailedrdfs:rangetypes 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-entailedrdf:type. As a result, instance datahad to restate
@typeon every nested node to satisfy the generated SHACLsh:class/closed target-class shapes.
Root cause & fix: per RDF 1.1 Semantics (rule
rdfs3) and OWL 2 RL (prp-rng), the rangetype 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_RULESrange 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
@typeon every embedded object.2.
chore(gx)— refresh Gaia-X artifacts to service-characteristics v2.2.1Bumps the
service-characteristicssubmodule to the currentdeveloptip (f4be530, taggedv2.2.1 on the canonical Gaia-X upstream) and tracks
developvia.gitmodules. Regeneratesgx.owl.ttl/gx.shacl.ttl/gx.context.jsonld(+ VERSION / UPSTREAM_* / PROPERTIES.md ->2.2.1) and refreshes
docs/registry.json+docs/ontologies/*.Versioning & Compatibility
rdf:typetriples fornon-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
How Has This Been Tested?
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.
.expectedviolation reports (envited-x,hdmap,manifest,openlabel-v2) now include the newly-entailed types; all failing-tests still fail as expected.openlabel-v2andgxvalid instances still PASS; invalid fixtures still REJECTED.gx.owl.ttlparses (68,199 triples,owl:versionInfo "2.2.1"); LF endings verified.Checklist
owl:versionInfo-> 2.2.1)Reviewer notes / caveats
make generate gxon Linux/CI isrecommended to confirm byte-identical output.
service-characteristicssubmodule points at the ASCS-eV mirror, which lacks thev2.2.xtags — 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.