fix(spaces): resolve custom role-predicate direction+tier from the role declaration#124
Merged
Merged
Conversation
…le declaration A user-defined role predicate (e.g. gen:hasMaintainer, declared via a gen:SpaceMemberRole nanopub's gen:hasInverseProperty) was dropped at extraction: SpacesExtractor only emitted a RoleInstantiation for gen:hasAdmin and the backcompat predicates, whose direction it knows. So an explicitly gen:RoleInstantiation-typed "<space> hasMaintainer <agent>" binding never reached npa:spacesGraph, and the maintainer tier never materialized — even though the role declaration already carried everything needed to resolve it. Defer direction inference from extraction-time to materialization-time, where the role-declaration registry is available (the deferred FIXME in extractRoleInstantiation): - Extractor: for an explicitly gen:RoleInstantiation-typed nanopub whose predicate isn't classifiable, emit a neutral binding (npa:rolePredicate + raw npa:bindingSubject/npa:bindingObject, predicate-discriminated subject) instead of dropping it. Known-predicate path unchanged. Gated on the explicit type so we don't mint inert rows for incidental IRI-valued triples. - Materializer: nonAdminTierUpdate's direction-pairing UNION gains two branches that join npa:rolePredicate against the tier-pinned RoleDeclaration attached to the space; the matched gen:hasInverseProperty/gen:hasRegularProperty fixes both the direction and which raw endpoint is the space vs. the agent. The INSERT is unchanged, so the materialized row is identical to a pre-classified grant (carries npa:inverseProperty/npa:regularProperty) — read side needs no special case. - New SpacesVocab terms: npa:rolePredicate, npa:bindingSubject, npa:bindingObject. Tests: extractor emits/gates the neutral binding; end-to-end materializer resolves both INVERSE and REGULAR custom predicates into the correct ref. Design doc updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 1.17.1 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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.
Problem
A user→space role assignment using a custom role predicate (e.g.
gen:hasMaintainer, declared via agen:SpaceMemberRolenanopub'sgen:hasInverseProperty) was silently dropped at extraction.SpacesExtractor.extractRoleInstantiationonly emitted agen:RoleInstantiationfor predicates whose direction it knows —gen:hasAdminplus theBackcompatRolePredicatesset.gen:hasMaintainer/gen:hasMemberare in neither, so an explicitlygen:RoleInstantiation-typed<space> hasMaintainer <agent>binding never reachednpa:spacesGraph, the maintainer tier never materialized, and Maintainer-tier users never showed up in a space's about tab — even though the role declaration already carried everything needed to resolve the binding.This is the deferred FIXME in
extractRoleInstantiation: "the materializer in PR 2 should refine direction for the typed-but-unknown-predicate case."Fix
Defer direction inference from extraction-time (per-nanopub, can't see the declaration) to materialization-time (where the role-declaration registry is available):
gen:RoleInstantiation-typed nanopub whose predicate isn't classifiable, emit a neutral binding (npa:rolePredicate+ rawnpa:bindingSubject/npa:bindingObject, predicate-discriminated subject) instead of dropping it. The known-predicate path is unchanged. Gated on the explicit type so incidental IRI-valued triples don't mint inert rows.nonAdminTierUpdate's direction-pairing UNION gains two branches that joinnpa:rolePredicateagainst the tier-pinnednpa:RoleDeclarationattached to the space; the matchedgen:hasInverseProperty/gen:hasRegularPropertyfixes both the direction and which raw endpoint is the space vs. agent. The INSERT is untouched, so the materialized state row is identical to a pre-classified grant (carriesnpa:inverseProperty/npa:regularProperty) — the read side needs no special case. Applies uniformly to maintainer/member/observer.SpacesVocabterms:npa:rolePredicate,npa:bindingSubject,npa:bindingObject.No hardcoding of
hasMaintainer; any future user-defined role predicate now works via its declaration. The hardcodeddirectionForset remains a fallback for predicates without a declaration.Tests
SpacesExtractorTest: emits the neutral binding for an explicitly-typed custom predicate; gates it off when the type is absent.AuthorityResolverTierIsolationTest: end-to-end resolution of both INVERSE and REGULAR custom predicates into the correct ref, carrying the resolved direction property.AuthorityResolverTest:nonAdminTierUpdateSPARQL resolves neutral bindings.Live verification
Verified against
localhost:9393after a fresh full re-sync: the published bindingRAuAs_L_…(<biodiversity> hasMaintainer <orcid>) now extracts as a neutral RI, materializes into the current state graph with the resolvednpa:inverseProperty gen:hasMaintainer, and the about-tab read join returns the Maintainer-tier users (3 maintainers across multiple bindings). Admin tier unaffected.Design doc updated.
🤖 Generated with Claude Code