feat(validators): per-resource SHACL validation mode#80
Merged
Conversation
Add an opt-in `--per-resource` mode that validates each JSON-LD document in its own graph instead of merging all `--data-paths` inputs into one graph. Merging multiple linked documents that legitimately reuse IRIs — e.g. a Verifiable Credential whose subject DID is also described by a DID document, or a credential embedded across several files — collapses those distinct resources onto a single RDF node. Under closed shapes this produces spurious ClosedConstraint and maxCount violations, and it can silently mask missing required properties by borrowing them across files. Per-resource validation is the correct grain for VC/DID data (cf. SHACL's single-graph model and the W3C VCWG 2024-01-31 discussion of it). - `ShaclValidator.validate_each()`: load the union of required shapes and compute the ontology RDFS closure once, then validate each file's data graph in isolation. ~20x faster than calling `validate()` per file (which re-parses shapes and re-infers the ontology every time). - `ShaclValidator._abox_inference()`: fast targeted RDFS (rdfs2/3/7/9) over the small data graph using indexed TBox lookups against the pre-closed ontology, avoiding a full-graph re-inference per resource. - `_load_data(load_fixtures=...)`: per-resource mode skips fixture resolution so referenced DID documents are not folded onto credential subjects. - `validation_suite`: `--per-resource` flag; in this mode the former "fixture" files are validated as their own resources rather than only used for IRI resolution. All additions are gated behind `--per-resource` (default off); the merged default path is unchanged. Verified against the existing suite: 469 passed (the one pre-existing `test_backslash_traversal_rejected` failure is a Linux path-separator quirk, unrelated to this change). Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Author
|
Consumed by reachhaven/harbour-credentials#13, which pins the submodule to this branch and switches |
The linkml fork branch feat/envited-x-pipeline was rebased onto the synced upstream main and force-pushed, orphaning the previous pin 3d3a52a3. Point the submodule at the rebased head b2b3dba5d2 — the same ENVITED-X feature commits (deterministic/normalize-prefixes/closed/covering-axiom-warning, etc.) now cleanly on top of current upstream main. Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Regenerate openlabel-v2.shacl.ttl to match the rebased feat/envited-x-pipeline linkml fork, which includes: - deterministic blank node ordering via WL hashing - str(?flag) != "true" SPARQL comparison (xsd:boolean/string safe) Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
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.
feat(validators): per-resource SHACL validation mode
Why
make validate shacl(and the downstream consumers, e.g. harbour-credentialsand simpulse-id-credentials) currently merge every
--data-pathsinput into asingle RDF graph before validation. That is wrong for Verifiable Credentials and
DID documents, which legitimately reuse the same IRIs across files:
credentialSubject.idis a DID that is also the subject of aDID document (carrying
verificationMethod/service);When these collapse onto one node, closed shapes reject the "foreign"
properties (ClosedConstraint), shared blank-node values multiply (
maxCount),and — more dangerously — missing required properties get silently borrowed
from another file in the merge, hiding real defects.
This matches SHACL's documented single-graph limitation and the W3C VC WG's
2024-01-31 decision
to keep SHACL out of core VC validation for exactly this reason.
What
A new opt-in
--per-resourcemode that validates each document in its owngraph, while sharing the expensive, data-independent work:
ShaclValidator.validate_each()ShaclValidator._abox_inference()_load_data(load_fixtures=False)validation_suite --per-resourcePerformance
Naive per-file validation is ~58s/file (parse 19s + RDFS inference 29s + pyshacl
10s, all re-done per file). By loading shapes and pre-computing the ontology
closure once,
validate_each()brings this to ~3s/file (~20x), so a 17-filesweep runs in ~40s instead of ~16 min.
Backwards compatibility
Everything is gated behind
--per-resource(default off). The merged defaultpath is untouched.
test_backslash_traversal_rejected) is pre-existing andunrelated — a Linux path-separator quirk, reproducible with these changes
stashed; it lives in
test_http_artifact_resolver.py, which this PR does nottouch.
Negative test
Confirmed
validate_eachstill catches violations: a credential missing requiredevidence→ FAIL (minCount); an unknown extra property → FAIL (closed-shape).