Skip to content

feat(spaces): materialize uniform ref-valued hasGoverningSpaceRef edge (#130)#132

Merged
tkuhn merged 1 commit into
mainfrom
feat/issue-130-governing-space-ref
Jun 25, 2026
Merged

feat(spaces): materialize uniform ref-valued hasGoverningSpaceRef edge (#130)#132
tkuhn merged 1 commit into
mainfrom
feat/issue-130-governing-space-ref

Conversation

@tkuhn

@tkuhn tkuhn commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Implements #130.

What

Materializes a uniform ref-valued npa:hasGoverningSpaceRef edge in each per-ref state graph, so consumer authority gates do one mandatory hop (resource → governing space ref → RoleInstantiation) whether the subject is a space or a maintained resource — instead of re-deriving the hop, the bare-IRI match, and the broken declaration join in every published query.

  • Reflexive<spaceIRI> npa:hasGoverningSpaceRef <its own SpaceRef>, via a new self-healing governingSpaceRefReflexiveUpdate pass (no source nanopub, no load filter; dedup on the edge). Removes the zero-hop special case (isMaintainedBy? zero-length path) that breaks once the hop is ref-valued — a space has no self ref-edge today.
  • Maintained<resourceIRI> npa:hasGoverningSpaceRef <maintaining space's ref>, emitted inside maintainedResourceAdmitUpdate pointing at the ref whose admin authorized the declaration. Ref-valued ⇒ no bare-IRI join ⇒ cross-ref bleed gone.

Invalidation

The maintained arm rides the #125 finding-#5 machinery: it's emitted under the existing MaintainedResourceLink reification and orphan-swept by maintainedResourceConvenienceEdgeCleanup, extended with a guard (?o npa:spaceIri ?r) that keeps the reflexive self-edge — which is self-healing and has no nanopub to invalidate.

Consumer side

Queries collapse to the simplified gate (keyed on npa:hasRoleType, materialized since #125):

?resource npa:hasGoverningSpaceRef ?ref .         # ref variant pins ?ref = ?passedRef
?ri a gen:RoleInstantiation ; npa:forSpaceRef ?ref ; npa:hasRoleType ?rt ; npa:forAgent ?authAgent .
filter(?rt = gen:AdminRole || ?rt = gen:MaintainerRole)
?authAcct a npa:AccountState ; npa:agent ?authAgent ; npa:pubkey ?pubkey .

That's the repoint for nanodash#510 (view-displays + preset/role assignments).

Tests

  • New AuthorityResolverGoverningSpaceRefTest (behavioral): reflexive per-ref emission; ref-valued maintained edge isolated to the authorizing ref (no bleed to a rival ref claiming the same IRI); the simplified gate resolving a space and a maintained resource but not a rival ref; maintained governing edge swept on invalidation while the reflexive self-edge is kept.
  • String-contract tests in AuthorityResolverTest.
  • Full suite green (292 tests).

Deploy

Requires a full re-ingest to backfill hasGoverningSpaceRef onto existing rows (rides the same re-ingest as #125 / #131).

Rollout order

Materializer (this PR) → re-ingest → consumer supersede per affected query (nanodash#510).

🤖 Generated with Claude Code

#130)

Consumer authority gates (view-displays, preset/role assignments) all
re-derive resource→space(ref)→RoleInstantiation→tier in federated SPARQL.
Copy-pasted across queries, that policy produced the cross-ref bleed and
dead-maintainer-arm bugs in nanodash#510. Move it into the materializer once.

Emit a uniform ref-valued npa:hasGoverningSpaceRef edge in each per-ref
state graph:

- Reflexive: <spaceIRI> hasGoverningSpaceRef <its own SpaceRef>, via the new
  self-healing governingSpaceRefReflexiveUpdate pass (no source nanopub, no
  load filter; a SpaceRef disappearing is itself a rebuild event). This
  removes the zero-hop special case — consumers no longer need
  isMaintainedBy? with a bare-IRI zero-length path for "resource is a space".
- Maintained: <resourceIRI> hasGoverningSpaceRef <maintaining space's ref>,
  emitted inside maintainedResourceAdmitUpdate (so it is covered by the
  existing MaintainedResourceLink reification) pointing at the ref whose
  admin authorized the declaration. Ref-valued, so no bare-IRI join survives
  and cross-ref bleed is gone.

The maintained arm is orphan-swept on invalidation by
maintainedResourceConvenienceEdgeCleanup (issue #125 #5 machinery), extended
with a guard (?o spaceIri ?r) that keeps the reflexive self-edge — which is
self-healing and has no nanopub to invalidate.

Consumers can then collapse the gate to a single mandatory hop keyed on
npa:hasRoleType (materialized since #125): see proposed queries in #130 /
nanodash#510.

Wired into runAllTierLoops + the late-arrival sweep + counters/logging.
Tests: new AuthorityResolverGoverningSpaceRefTest (reflexive per-ref emission,
ref-valued maintained edge isolated to the authorizing ref, the simplified
gate resolving a space and a maintained resource but not a rival ref, and
sweep-on-invalidation keeping the reflexive edge) plus string-contract tests
in AuthorityResolverTest. Full suite green.

Requires a full re-ingest on deploy to backfill the edges onto existing rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.18.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant