feat(ndo): NDO membership — join, list, and is-member on the per-NDO cell - #129
Conversation
Status: where this stands, and what is still missingVerifiedSweettest — 4/4 pass, run in
Not verified
Missing / deliberately out of scope
Merge orderStacked on #128 ( The one thing a reviewer should push back on if they disagreeThe claim that membership is not an access grant. Everything here follows from it: no read gate, no approval flow, |
Review verdict: APPROVE (after rebase onto dev, post-#128)Reviewed the marginal diff (16 files on top of the #128 tip). Backend — clean.
Tests — round-trip, double-join rejection (no duplicate), unknown-identity rejection, two-agent convergence with consistency waits, plus a browser e2e (both agents join and see each other). UI — Suggestion (non-blocking): Merge mechanics: this branch is 1 ahead / 3 behind the #128 tip (the #128 self-review round added commits), hence the DIRTY state. After #128 merges: retarget base to |
6a2eec2 to
b89cb22
Compare
…cell
Replaces the UI stub with a real DHT-backed membership surface in
zome_resource, so agents can declare participation in an NDO and
enumerate who else is taking part.
Membership is deliberately not an access grant. Under the per-NDO-cell
model (ADR-010 model A) an agent must already hold the cloned NDO cell
to read the NDO at all, so cell possession is what confers access.
NdoMembership only makes participation listable; nothing may treat its
absence as a read denial.
Backend (zome_resource):
- NdoMembership integrity entry { ndo_identity_hash, role }. Author and
join time come from the action header, matching GroupMembership.
- join_ndo: idempotent (AlreadyMember), verifies the identity exists in
this cell, creates NdoToMembers + MemberToNdos links.
- get_ndo_members: reads each link's author, never a per-link get. A
per-link get silently drops peers whose record has not gossiped to
this shard — the bug already fixed once in zome_group.
- is_ndo_member: participation check only, never a read gate.
- Validation: role, when present, non-empty and <= 100 chars. Identity
existence stays in the coordinator so creation does not depend on
gossip timing.
UI (ndo.service.ts, NdoView, MemberList):
- joinNdo / getNdoMembers resolve the NDO clone cell from its anchor
and call the zome; an unresolved identity is a hard NdoNotFoundError
rather than a silent degrade.
- joinNdo guards on is_ndo_member so re-joining is a benign no-op.
- Member names resolve via zome_person with a truncated-pubkey fallback
for agents with no Person entry yet (REQ-UI-ID-03).
- NdoNotImplementedError removed.
Tests:
- Sweettest ndo_membership: round trip, double-join rejection, unknown
identity rejection, two-agent convergence.
- Playwright multi-agent: both agents join and see each other.
Docs: resource_zome.md, specifications.md, ui_architecture.md updated
from "planned" to implemented; .rules and DOCUMENTATION_INDEX.md record
that await_consistency_20_s takes the array by value.
350960f to
71cf7f0
Compare
Resolves both conflicts by keeping #129's membership additions and the round 1 cell-routing fix together, since neither replaces the other: - NdoView.svelte: the `membersStubMessage` placeholder is gone (membership is real now, so `membersError` carries the failure), and `ndoCellId` stays, because every Layer 1 and Layer 2 tab still needs the NDO's own clone cell. - ndo.service.ts: `resolveCellIdForNdo` keeps its interface entry and its implementation alongside the real `joinNdo` / `getNdoMembers`, which now call `join_ndo`, `is_ndo_member`, and `get_ndo_members` on the ndo cell.
What this does
Replaces the NDO membership stub (
NdoNotImplementedError) with a real DHT-backed surface inzome_resource, so an agent can declare participation in an NDO and enumerate who else is taking part.Stacked on #128 (
fix/120-clone-cell-dna-properties). Review/merge that first; this PR targets it as base.The design decision worth reading
Membership is not an access grant. Under the per-NDO-cell model (ADR-010 model A) an agent must already hold the cloned NDO cell to read the NDO at all, so cell possession is what confers access.
NdoMembershiponly makes participation listable. Nothing may treat its absence as a read denial, and the module docs, integrity comments, and tests all say so explicitly so a future reader does not turn it into a permission check.Backend —
zome_resourceNew integrity entry:
The joining agent and join time are not in the entry — they come from the action header, matching the
GroupMembershipconvention in the group DNA.join_ndo(JoinNdoInput)AlreadyMember. Verifies the identity exists in this cell. CreatesNdoToMembers+MemberToNdos.get_ndo_members(ActionHash)Vec<AgentPubKey>, read from each link's authoris_ndo_member((AgentPubKey, ActionHash))Why members come from link authors, not a per-link
get: a per-linkgetcan fail when a peer's membership record has not yet gossiped to this shard, silently dropping that member so only the local agent appears. That exact bug was found and fixed inzome_group(#126); the comment inget_ndo_memberssays not to reintroduce it, and the two-agent Sweettest is the assertion that would catch a regression.Validation split follows the group DNA convention: integrity checks only what is local (role non-empty, <= 100 chars); identity existence is checked in the coordinator so membership creation never depends on gossip timing.
UI
joinNdo/getNdoMembersresolve the NDO clone cell from its anchor, then call the zome. Unlike reads, membership has no legacy shared-cell fallback: an unresolved identity is a hardNdoNotFoundErrorrather than a silent degrade.joinNdoguards onis_ndo_memberso re-joining is a benign no-op (mirrors the group's self-healing membership, REQ-UI-GRP-04).zome_personon the provisioned cell, with a truncated-pubkey fallback for agents who have noPersonentry yet (normal — REQ-UI-ID-03 defers Person creation).NdoNotImplementedErrordeleted; the amber "not yet implemented" banners become real error states.Tests
dnas/nondominium/tests/src/nondominium/ndo_membership/— round trip (creating an NDO does not implicitly join it), double-join rejection with no duplicate, unknown-identity rejection, and two-agent convergence where each agent must see both members.multi-agent.spec.ts— both agents join and see each other in the member list, polling through gossip.Docs
resource_zome.md,specifications.md,ui_architecture.mdmove from "planned / stub" to implemented, with the not-an-access-grant rule and the link-author rule written down..rules+DOCUMENTATION_INDEX.mdrecord thatawait_consistency_20_stakes the array by value ([&a, &b], not&[&a, &b]) — the bound isIntoIterator<Item = &SweetCell>, so a slice reference yields&&SweetCelland fails to compile.Closes #133