From 16b4ec2b21da8ea4bbca4f520daadc44999878bc Mon Sep 17 00:00:00 2001 From: Soushi888 Date: Tue, 3 Nov 2026 20:31:45 -0500 Subject: [PATCH] feat(ui): Moss group context for groups and NDOs Inside Moss the Moss group is the group: the tool instance's provisioned group cell is the one Nondominium group, joined automatically with no invite code. The profile comes from profilesClient, the role from myAccountabilitiesPerGroup, and NDO clones are created through the WeaveClient. A layer choice in LobbyServiceResolved keeps the Lobby DNA, Group DNA and invite codes for the standalone build. NDO clones are not registered as public: Moss 0.16.0-rc.5 cannot register a clone that carries DNA properties (PUBLIC_NDO_CLONES, see #162). --- CHANGELOG.md | 1 + documentation/IMPLEMENTATION_STATUS.md | 2 +- .../specifications/moss-integration.md | 23 ++- .../specifications/ui_architecture.md | 2 +- ui/src/lib/components/group/GroupView.svelte | 20 ++- .../components/lobby/LobbyProfileBar.svelte | 20 ++- ui/src/lib/components/lobby/NdoBrowser.svelte | 7 +- ui/src/lib/components/shell/Sidebar.svelte | 37 +++- ui/src/lib/moss/group-cell.spec.ts | 55 ++++++ ui/src/lib/moss/group-cell.ts | 31 ++++ ui/src/lib/moss/ndo-clone.spec.ts | 59 +++++++ ui/src/lib/moss/ndo-clone.ts | 43 +++++ ui/src/lib/moss/profile.spec.ts | 73 ++++++++ ui/src/lib/moss/profile.ts | 64 +++++++ .../lib/services/holochain.service.svelte.ts | 22 ++- ui/src/lib/services/zomes/lobby.service.ts | 55 +++++- ui/src/lib/services/zomes/moss-lobby.spec.ts | 163 +++++++++++++++++ ui/src/lib/services/zomes/moss-lobby.ts | 167 ++++++++++++++++++ ui/src/lib/stores/moss.store.svelte.ts | 49 +++++ ui/src/routes/+layout.svelte | 6 + 20 files changed, 858 insertions(+), 41 deletions(-) create mode 100644 ui/src/lib/moss/group-cell.spec.ts create mode 100644 ui/src/lib/moss/group-cell.ts create mode 100644 ui/src/lib/moss/ndo-clone.spec.ts create mode 100644 ui/src/lib/moss/ndo-clone.ts create mode 100644 ui/src/lib/moss/profile.spec.ts create mode 100644 ui/src/lib/moss/profile.ts create mode 100644 ui/src/lib/services/zomes/moss-lobby.spec.ts create mode 100644 ui/src/lib/services/zomes/moss-lobby.ts create mode 100644 ui/src/lib/stores/moss.store.svelte.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index eb36f1dd..20ffb74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ NDO over a live DHT. with two agents and one group (`bun run tool-dev`), `@theweave/api` in the UI, and a connection mode that takes the conductor client from Moss. The standalone build is unchanged. See `documentation/specifications/moss-integration.md`. +- **Moss group context.** Inside Moss the Moss group is the group: the tool instance's `group` cell is the one Nondominium group, joined automatically with no invite code, the profile comes from Moss, the role from the Moss accountabilities, and NDO clones are created through the `WeaveClient`. Standalone keeps the Lobby, the Group DNA and invite links. - **Lobby DNA** as the entry point. Groups are announced here for discovery, and NDOs are browsed and filtered across every group the agent belongs to. - **Group DNA, one cloned cell per group.** Each group is its own DHT with its own diff --git a/documentation/IMPLEMENTATION_STATUS.md b/documentation/IMPLEMENTATION_STATUS.md index 969d2790..efd68510 100644 --- a/documentation/IMPLEMENTATION_STATUS.md +++ b/documentation/IMPLEMENTATION_STATUS.md @@ -296,7 +296,7 @@ There is no `get_all_groups` extern in a Group cell; each cell represents one is Nondominium runs as a Moss tool in `weave` dev mode. Standalone (Lobby, Group DNA, invite codes) is unchanged. - ✅ **Scaffold (#163).** `weave/weave.dev.config.ts` (two agents, one group, tool served from `localhost:8888`), `bun run tool-dev`, `@theweave/api` in `ui/`, host detection and the Moss connection mode in `ui/src/lib/moss/`, an empty `AppletServices`. The UI connects through the applet client Moss hands it. -- ❌ **Moss group context (#164).** Profiles from Moss, roles from `myAccountabilitiesPerGroup()`, the instance's `group` cell as the Nondominium group, NDO clones through the `WeaveClient`. Not yet implemented: inside Moss the Lobby and invite-code flows are not adapted. +- ✅ **Moss group context (#164).** Inside Moss the profile comes from `profilesClient`, the role from `myAccountabilitiesPerGroup()`, the tool instance's provisioned `group` cell is the one Nondominium group (the tool installer creates its profile, every other member joins it automatically, no invite code), and NDO clones are created through the `WeaveClient`. Standalone keeps the Lobby DNA, Group DNA and invite codes. Known gap: NDO clones are not registered as public to the Moss group, because Moss rc.5 cannot register a clone that carries DNA properties (see [moss-integration.md](specifications/moss-integration.md)). - ❌ **NDOs as assets (#165).** WAL, `getAssetInfo`, `search`, asset view. See [moss-integration.md](specifications/moss-integration.md). diff --git a/documentation/specifications/moss-integration.md b/documentation/specifications/moss-integration.md index fd9e816f..2883bead 100644 --- a/documentation/specifications/moss-integration.md +++ b/documentation/specifications/moss-integration.md @@ -1,6 +1,6 @@ # Moss Integration -**Status:** v0.1.0 pilot, in progress. The tool scaffold and dev config are implemented (#163). The Moss group context (#164) and NDOs as assets (#165) follow. Supersedes the "Moss Integration Contract" in [lobby-architecture.md](lobby-architecture.md) §9 where they differ. +**Status:** v0.1.0 pilot, in progress. The tool scaffold and dev config (#163) and the Moss group context (#164) are implemented. NDOs as assets (#165) follows. Supersedes the "Moss Integration Contract" in [lobby-architecture.md](lobby-architecture.md) §9 where they differ. Moss is the Weave host (lightningrodlabs/moss). In the v0.1.0 pilot Nondominium runs as a **tool** inside Moss, started with the `weave` CLI in dev mode. The standalone build (Lobby DNA, Group DNA, invite codes) keeps compiling and running unchanged; the pilot ships Moss only. @@ -55,7 +55,26 @@ hc-connect.ts connectHolochainClient() Moss mode is a connection mode (`HolochainConnectionMode` gains `'moss'`). The cross-group view is not implemented: a cross-group render throws. +## The Moss group context (#164) + +Inside Moss the standalone Lobby model is replaced behind one seam, `LobbyServiceResolved` in `lobby.service.ts`: it is `MossLobbyServiceLive` when the page is hosted by Moss and `LobbyServiceLive` otherwise. Stores, `NdoService` and components see the same `LobbyService` interface in both. + +| Concern | Standalone | Inside Moss | +|---|---|---| +| Groups | Lobby DNA announces clones of the Group DNA | One group: the tool instance's provisioned `group` cell (`MOSS_GROUP_ID = 'moss'`) | +| Joining | Invite code or link | Automatic: the tool installer (`toolInstaller`) creates the `GroupProfile`, named after the Moss group; every other member waits for it to gossip, then calls `join_group` | +| Profile | `LobbyUserProfile` form, localStorage | `profilesClient`, mapped to the same `LobbyUserProfile` shape | +| Role | none | `myAccountabilitiesPerGroup()`, strongest of Member, Steward, Progenitor, shown in the sidebar | +| NDO clone | `AppWebsocket.createCloneCell` | `WeaveClient.createCloneCell(req, PUBLIC_NDO_CLONES)` | +| Discovery of NDOs | `NdoAnchor` in the group cell | same | + +Create group, join group and invite links are hidden in Moss (`mossStore.isMoss`), and the service refuses them with `Groups are managed by Moss`. The Lobby DHT calls answer with nothing: every tool instance is its own network, so there is no shared lobby to announce to. + +Code: `ui/src/lib/moss/` (`group-cell.ts`, `profile.ts`, `ndo-clone.ts`), `ui/src/lib/services/zomes/moss-lobby.ts` (the service, with injected dependencies so it runs in a unit test against a mocked WeaveClient) and `ui/src/lib/stores/moss.store.svelte.ts`. + +**NDO clones are not public to the Moss group (`PUBLIC_NDO_CLONES = false`).** `createCloneCell(req, true)` registers the clone in the Moss group DNA so other members and always-online nodes can install it. Moss `0.16.0-rc.5` cannot register a clone that carries DNA properties: `join_cloned_cell` declares `properties: Option` and the renderer forwards the properties object untouched, so the call fails with a Deserialize error and leaves an orphan cell. NDO clones need their properties (ADR-013). Discovery does not depend on the registration, since peers re-derive the clone from the `NdoAnchor` in the group cell, but an always-online node (wdocker) will not host the NDO cells until this flips. Flip `PUBLIC_NDO_CLONES` once Moss accepts properties. + ## Open items -- `weaveClient.createCloneCell(req, true)` fails whenever `req.modifiers.properties` is set, which NDO clones need. See the #162 result and #164. +- `weaveClient.createCloneCell(req, true)` fails whenever `req.modifiers.properties` is set, which NDO clones need (see above and the #162 result). - `AppletServices` in `@theweave/api` `0.7.0-rc.3` has `getAssetInfo`, `search` and `creatables`. Opening an asset is not a method on it: Moss renders the tool with an `asset` view (`renderInfo.view.type === 'asset'`), and the tool calls `weaveClient.openAsset` to open other assets. diff --git a/documentation/specifications/ui_architecture.md b/documentation/specifications/ui_architecture.md index fc7ae813..f6279e42 100644 --- a/documentation/specifications/ui_architecture.md +++ b/documentation/specifications/ui_architecture.md @@ -102,7 +102,7 @@ On top of this hierarchy, the **next** UI iteration adds a cross-cutting **Persp Dependencies only point down: routes render components, components read stores, stores call services, services call the Holochain client. The domain layer sits beside the services and is imported by stores and components. The three rules that keep it that way are enforced by a script (see [§17](#17-architecture-invariants)). -**Moss host (`lib/moss/`).** When the UI runs inside Moss it is a tool, not a page that owns its conductor connection. `lib/moss/` holds that seam as plain TypeScript beside the service layer: `host.ts` (pure host detection), `connect.ts` (the `WeaveClient` connection, injectable for tests) and `applet-services.ts` (what the tool tells Moss about itself). Only `utils/hc-connect.ts` imports it, as one more connection mode (§15.4), so components, stores and services see the same `AppClient` whether the host is Moss or a standalone conductor. Details in [moss-integration.md](moss-integration.md). +**Moss host (`lib/moss/`).** When the UI runs inside Moss it is a tool, not a page that owns its conductor connection. `lib/moss/` holds that seam as plain TypeScript beside the service layer: `host.ts` (pure host detection), `connect.ts` (the `WeaveClient` connection, injectable for tests) and `applet-services.ts` (what the tool tells Moss about itself). Only `utils/hc-connect.ts` imports it, as one more connection mode (§15.4), so components, stores and services see the same `AppClient` whether the host is Moss or a standalone conductor. The group context has one more seam: `LobbyServiceResolved` selects `MossLobbyServiceLive` (one group, the tool instance's `group` cell) or the Lobby and Group DNA implementation, behind the same `LobbyService` interface, and `moss.store.svelte.ts` holds the Moss profile and role for components. Details in [moss-integration.md](moss-integration.md). --- diff --git a/ui/src/lib/components/group/GroupView.svelte b/ui/src/lib/components/group/GroupView.svelte index 150422f3..b2c8d15b 100644 --- a/ui/src/lib/components/group/GroupView.svelte +++ b/ui/src/lib/components/group/GroupView.svelte @@ -9,6 +9,7 @@ import { appContext } from '$lib/stores/app.context.svelte'; import { groupStore } from '$lib/stores/group.store.svelte'; import { lobbyStore } from '$lib/stores/lobby.store.svelte'; + import { mossStore } from '$lib/stores/moss.store.svelte'; import { devStorageKey } from '$lib/utils/hc-connect'; import type { ActiveFilters } from '$lib/stores/lobby.store.svelte'; import NdoBrowser from '$lib/components/lobby/NdoBrowser.svelte'; @@ -102,7 +103,8 @@ appContext.currentView = 'group'; appContext.selectedGroupId = groupId; void groupStore.loadGroupData(groupId); - if (!hasVisited(groupId)) { + // Inside Moss the profile and who sees it are the Moss group's business. + if (!mossStore.isMoss && !hasVisited(groupId)) { showProfileModal = true; } }); @@ -183,13 +185,15 @@

{groupId}

- + {#if !mossStore.isMoss} + + {/if}