Skip to content

Keep admin seed alive across root-definition supersession (#110)#111

Merged
tkuhn merged 1 commit into
mainfrom
fix/issue-110-root-supersession-seed
May 26, 2026
Merged

Keep admin seed alive across root-definition supersession (#110)#111
tkuhn merged 1 commit into
mainfrom
fix/issue-110-root-supersession-seed

Conversation

@tkuhn
Copy link
Copy Markdown
Contributor

@tkuhn tkuhn commented May 26, 2026

Fixes #110.

Root cause

The admin-tier seed in AuthorityResolver.adminTierUpdate gated the npa:hasRootAdmin seed on the root definition's own invalidation (invalidationFilter("defNp")). SpacesExtractor emits hasRootAdmin only for the own-root definition, so updating a space publishes a continuation revision that re-roots to the original root via gen:hasRootDefinition (carrying no hasRootAdmin of its own) and npx:supersedes the prior root. Over a chain of edits, every hasRootAdmin-bearing definition ends up invalidated while the only live definition has no seed — the seed branch empties, the admin closure produces nothing, and (since attachments / maintainer / member / observer / sub-space all cascade from the admin set) the space loses all materialized role state. This is the clean all-or-nothing correlation in the issue.

knowledgepixels is the live instance: root RAOOz… (which carries hasRootAdmin) was superseded by RAvJcz… (a non-root continuation), so all three live query instances materialize 0 admin RIs for it and its space page shows no views.

Fix

Gate the seed on the space ref still being alive — i.e. some non-invalidated SpaceDefinition of the same ref exists — via the new spaceRefAliveFilter(), instead of on the seed definition's own invalidation. The seed is anchored to the immutable root NPID (the space-ref identity), so it survives supersession of the root nanopub by a live continuation. A fully-retracted ref (every definition invalidated) has no live definition, so the FILTER EXISTS fails and the seed correctly disappears.

Escalation-safe: the candidate admin RI's npa:pubkeyHash must still resolve to the seeded root admin, so a superseding party can only re-affirm the founding admin, not seed themselves.

Scope note on the issue's "33 broken"

The issue's reproduction query #2 counts a space if any definition — even an invalidated one — asserts gen:hasAdmin inline, so it sweeps in deleted spaces. Verified breakdown of the 33:

  • 1 real bug — knowledgepixels (superseded-but-alive). This PR fixes it, and protects every future space-definition update from the same regression.
  • ~30 genuinely retracted (single definition, npx:retracts, no live successor — e.g. beetles, knowledgepixels/news). Correctly empty; unaffected by this PR.
  • 2 live-but-bad-input — test/john-doe-test-group (signing key not trust-approved) and fair2adapt-cs4 (malformed admin IRI https://orcid.org/https://orcid.org/…). Not addressed (correct).

Verification

  • All 215 unit tests pass; added regression test adminTierUpdate_seedSurvivesRootSupersession_issue110.
  • Validated end-to-end on a freshly-synced local instance (load counter 2119, materializer caught up): with the identical supersession chain as live, knowledgepixels now materializes its 4 admin agents (each resolving to pubkey hashes), genuinely-retracted spaces stay empty, and no space shows admin RIs without a live definition. Net: 95 → 96 working spaces, exactly one space flipped, no regressions.

Out of scope (separate issue worth filing)

The materializer's invalidation filter honors any npx:invalidates/retracts/supersedes edge with no authorship/admin check. Base-nanopub same-key semantics aren't applied here (and arguably shouldn't be for spaces, where admin co-management is legitimate), but neither is an admin-scoped authority check — so a validly-signed nanopub from any agent can invalidate a space definition. This PR narrows rather than widens that exposure, but the gap is pre-existing and orthogonal.

🤖 Generated with Claude Code

…ssion (#110)

The admin-tier seed gated the `npa:hasRootAdmin` seed on the root
definition's own invalidation (`invalidationFilter("defNp")`). Since
`SpacesExtractor` emits `hasRootAdmin` only for the own-root definition,
updating a space publishes a continuation revision that re-roots to the
original root (no `hasRootAdmin` of its own) and `npx:supersedes` the
prior root. After any update every `hasRootAdmin`-bearing definition is
invalidated, the seed empties, and the whole admin closure — plus
everything cascading from it — produces nothing. So any space whose
definition had ever been updated lost all materialized role state.

Fix: gate the seed on the space ref still being alive (some
non-invalidated definition of the same ref) via the new
`spaceRefAliveFilter()`, instead of on the seed definition's own
invalidation. The seed is anchored to the immutable root NPID (the
space-ref identity), so it survives supersession of the root nanopub
by a live continuation; a fully-retracted ref (every definition
invalidated) still drops it. Escalation-safe: the candidate admin RI's
`npa:pubkeyHash` must still resolve to the seeded root admin.

Verified end-to-end on a freshly-synced instance: knowledgepixels (0
admin RIs on all three live instances) now materializes its admin set;
genuinely-retracted spaces stay correctly empty; no regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tkuhn tkuhn merged commit fc55a46 into main May 26, 2026
8 checks passed
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 1.14.3 🎉

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.

Spaces with inline gen:hasAdmin sometimes get no role instantiations materialized in /repo/spaces

1 participant