From bc3d270bd4902c91c054b6a6d9847fd889c4a5b5 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Sat, 4 Jul 2026 09:58:52 +0200 Subject: [PATCH] chore: scrub simulation vocabulary from app comments + settle doc-debt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce the boundary in code-comments and doctrine (adversarial-review cleanup): - App comments in the data plane no longer narrate the SDK's internals: "emulated curator"→"the inbox read", "fan-out"→"discovered", removed store-placement reasoning and "polyfill/shim/mono-store" wording (FestipodDataContext, registration, storeRegistry, ngSession, AccountContext, isolation, sharedWallet, AccessGateScreen). Executable logic unchanged. - Removed dangling references to the dissolved `nextgraph-platform` concept and `brief_2026-06-15_shared-wallet-shim` from app code. - knowledge_nextgraph-stack: dropped "mécanique d'émulation" from the boundary note. - Settled and deleted all concept _debt.md (confirmatory; target leaves clean). (Test-infra under workshop/ + generated features.ts still carry some simulation vocabulary — parked as a separate below-SDK decision.) Co-Authored-By: Claude Opus 4.8 (1M context) --- .project/concepts/app-architecture/_debt.md | 14 ----- .project/concepts/app-security/_debt.md | 14 ----- .project/concepts/bdd-testing/_debt.md | 14 ----- .project/concepts/data-layer/_debt.md | 9 ---- .../data-layer/knowledge_nextgraph-stack.md | 2 +- .project/concepts/functional-domain/_debt.md | 14 ----- src/modules/auth/screens/AccessGateScreen.tsx | 4 +- src/modules/auth/sharedWallet.ts | 6 +-- src/shared/context/AccountContext.tsx | 6 +-- src/shared/context/FestipodDataContext.tsx | 12 ++--- src/shared/data/registration.ts | 15 +++--- src/shared/utils/isolation.ts | 11 ++-- src/shared/utils/ngSession.ts | 2 +- src/shared/utils/storeRegistry.ts | 52 +++++++------------ 14 files changed, 42 insertions(+), 133 deletions(-) delete mode 100644 .project/concepts/app-architecture/_debt.md delete mode 100644 .project/concepts/app-security/_debt.md delete mode 100644 .project/concepts/bdd-testing/_debt.md delete mode 100644 .project/concepts/data-layer/_debt.md delete mode 100644 .project/concepts/functional-domain/_debt.md diff --git a/.project/concepts/app-architecture/_debt.md b/.project/concepts/app-architecture/_debt.md deleted file mode 100644 index 71e68be..0000000 --- a/.project/concepts/app-architecture/_debt.md +++ /dev/null @@ -1,14 +0,0 @@ -# Doc-debt — app-architecture - -> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. -> One block = one "big change": `why` + `files` + `verify` (leaves to review). - -## Block: T03.g — routage par scope (pas de changement d'archi) -- **why**: FestipodDataContext route les entités par scope via le SDK et NextGraphContext ne surface plus les store-ids. La structure (provider stack, invariant d'imports module→shared, app shell) est inchangée — touches incidentes, pas d'évolution architecturale. -- **files**: src/shared/context/NextGraphContext.tsx, src/shared/context/FestipodDataContext.tsx -- **verify (leaves à relire)**: aucune — knowledge_app-shell.md / knowledge_module-structure.md inchangés. Bloc à supprimer après relecture confirmatoire. - -## Block: T03.b — AccountContext déclare l'identité courante au SDK -- **why**: `AccountProvider` appelle désormais `setCurrentUser(normalizeUsername(username))` au login / au changement de compte (effet sur `username`) — appel d'IDENTITÉ SDK, pas une règle d'accès applicative. Structure (provider stack, invariant d'imports) inchangée : touche incidente sur le glue React. -- **files**: src/shared/context/AccountContext.tsx -- **verify (leaves à relire)**: aucune — knowledge_app-shell.md inchangé. Bloc à supprimer après relecture confirmatoire. diff --git a/.project/concepts/app-security/_debt.md b/.project/concepts/app-security/_debt.md deleted file mode 100644 index a192095..0000000 --- a/.project/concepts/app-security/_debt.md +++ /dev/null @@ -1,14 +0,0 @@ -# Doc-debt — app-security - -> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. -> One block = one "big change": `why` + `files` + `verify` (leaves to review). - -## Block: T03.g — NextGraphContext ne surface plus les store-ids -- **why**: `NextGraphContext` ne journalise/expose plus les trois store-ids dans le contexte app (routés uniquement au point d'injection SDK). Renforce la doctrine "isolation déléguée au SDK, l'app ne manipule pas de store physique" — ne l'invalide pas. -- **files**: src/shared/context/NextGraphContext.tsx -- **verify (leaves à relire)**: knowledge_trust-model.md (confirmer : confiance dans le SDK, aucune manipulation de store côté app). Aucun changement de contenu attendu — relecture confirmatoire. - -## Block: T03.b — isolation ACTIVE (identité courante + acte de partage des connexions) -- **why**: L'app déclare désormais au SDK (a) l'IDENTITÉ courante au login (`AccountContext` → `setCurrentUser`) et (b) son graphe de CONNEXIONS (`FestipodDataContext` → `declareConnections` sur les friendships) — les deux actes DOMAINE qui rendent le filtre du SDK discriminant (private→propriétaire, protected→propriétaire+connexions, public→tous). L'app ne porte toujours AUCUNE règle d'accès elle-même ; elle affiche ce que le SDK laisse passer. Renforce knowledge_trust-model — ne l'invalide pas (l'app fournit juste au SDK le « qui lit » + « qui est connecté à qui » qui manquaient pour que la délégation soit effective). Write-guard : best-effort (chemins d'écriture réels passent par le vrai `ng`, non gardés) — couverture documentée côté lib (docs/simulation.md), PAS dans Festipod. -- **files**: src/shared/context/AccountContext.tsx, src/shared/context/FestipodDataContext.tsx -- **verify (leaves à relire)**: knowledge_trust-model.md — confirmer « isolation déléguée au SDK, aucune logique d'autorisation côté écran/contexte ». Nuance à vérifier : le contexte fournit maintenant identité + connexions au SDK (ce n'est pas un filtre applicatif, c'est le câblage domaine→SDK). Relecture confirmatoire. diff --git a/.project/concepts/bdd-testing/_debt.md b/.project/concepts/bdd-testing/_debt.md deleted file mode 100644 index 26f9ac6..0000000 --- a/.project/concepts/bdd-testing/_debt.md +++ /dev/null @@ -1,14 +0,0 @@ -# Doc-debt — bdd-testing - -> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. -> One block = one "big change": `why` + `files` + `verify` (leaves to review). - -## Block: T03.b — scénario @data « isolation protégée par connexions » -- **why**: Nouveau scénario @data (workshop) prouvant l'isolation ACTIVE via le SDK contre le vrai broker : un compte non connecté ne lit pas l'entité PROTÉGÉE d'un autre, la lit après `declareConnections`, lit la PUBLIQUE toujours. Nouveaux hooks harness (`governProtected`, `connect`, `canReadPublicProbe`) réutilisant `` sur le vrai set ORM. Suivent le contrat @data (mutation/persistance broker) — pas de nouvelle couche, pas de vestige source-grep. -- **files**: src/shared/test-harness/harness-ng.tsx, src/modules/workshop/features/protected-connections.feature, src/modules/workshop/steps/data/protected-connections.steps.ts -- **verify (leaves à relire)**: aucune — rule_test-layer-contracts.md / knowledge_data-layer-broker.md inchangés (scénario conforme au contrat @data). Bloc à supprimer après relecture confirmatoire. - -## Block: T03.c — scénario @data « découverte publique via l'index global » -- **why**: Le scénario @data existant `decouverte-publique.feature` bascule du fan-out cross-comptes vers l'INDEX GLOBAL : un compte publie (submit → dépôt dans l'index) et un compte NON connecté découvre en LISANT l'index (materialize → read) puis s'abonne au doc référencé via un vrai `useShape({graphs})`. Aucune nouvelle couche ; contrat @data respecté (mutation/persistance broker réel). Hooks harness `publishPublicEventAs`/`discoverPublicEventsAs` réécrits pour passer par `submitEventToIndex`/`readDiscoveredEvents`. -- **files**: src/shared/test-harness/harness-ng.tsx, src/modules/event/features/decouverte-publique.feature, src/modules/event/steps/data/decouverte.steps.ts -- **verify (leaves à relire)**: aucune — rule_test-layer-contracts.md / knowledge_data-layer-broker.md inchangés (scénario conforme au contrat @data). Bloc à supprimer après relecture confirmatoire. diff --git a/.project/concepts/data-layer/_debt.md b/.project/concepts/data-layer/_debt.md deleted file mode 100644 index 7331827..0000000 --- a/.project/concepts/data-layer/_debt.md +++ /dev/null @@ -1,9 +0,0 @@ -# Doc-debt — data-layer - -> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. -> One block = one "big change": `why` + `files` + `verify` (leaves to review). - -## Block: T03.g — l'app route par SCOPE via la lib, zéro store-id -- **why**: Les fuites de store physique retirées de l'app applicative. Toute lecture/écriture d'entité passe par le SDK par scope (public/protected/private) ; l'app ne construit plus de `did:ng:${store_id}`. La lib expose `resolveScopeGraph(scope)` / `resolveInboxAnchor()` (placement interne). Le flag `FESTIPOD_MULTISTORE` et le chemin mono-store/multi-doc sont fusionnés en UN chemin par scope. Le point d'injection unique (`ngSession`/`storeRegistry.configureStoreRegistry`) passe la session (dont les store-ids) à la lib — wiring sanctionné. -- **files**: src/shared/utils/ngGraph.ts, src/shared/hooks/useShapeWithDefaults.ts, src/shared/context/FestipodDataContext.tsx, src/shared/utils/ngSession.ts, src/shared/utils/storeRegistry.ts, src/shared/data/registration.ts -- **verify (leaves à relire)**: _overview.md (le modèle "entité = document par scope" est déjà énoncé — vérifier qu'il ne reste aucune trace de mono-store/store physique dans l'app), knowledge_nextgraph-stack.md (frontière SDK : confirmer "l'app parle uniquement en scopes, jamais de store-id"), knowledge_context-internals.md (les scopes sont désormais résolus async via le SDK dans un effet — `scopeGraphs` state, gate `ready`). NB : le contenu doctrinal actuel décrit déjà la cible ; ces changements font *converger le code vers la doctrine*, ils ne l'invalident pas. Relecture confirmatoire (T03.e possède la passe doctrine). diff --git a/.project/concepts/data-layer/knowledge_nextgraph-stack.md b/.project/concepts/data-layer/knowledge_nextgraph-stack.md index 46f4930..f4699f7 100644 --- a/.project/concepts/data-layer/knowledge_nextgraph-stack.md +++ b/.project/concepts/data-layer/knowledge_nextgraph-stack.md @@ -15,7 +15,7 @@ Festipod persiste via **`@ng-eventually/client`** — le SDK NextGraph que l'app - L'app **ne dépend que de `@ng-eventually/client`** pour la donnée. - Le SDK est **initialisé/injecté une seule fois** via `ngSession.configure(...)` (`src/shared/utils/ngSession.ts`) — point d'injection unique. Le reste de l'app (data-plane, lifecycle, login, types) passe par la lib. -- **Ne jamais documenter dans ce repo l'état courant de NextGraph** (contraintes du SDK sous-jacent, contournements, internes broker/verifier, mécanique d'émulation) : cela vit dans le repo `@ng-eventually/client`. Ici on décrit seulement **comment Festipod utilise ce SDK**. +- **Ne jamais documenter dans ce repo l'état courant de NextGraph** (contraintes du SDK sous-jacent, contournements, internes broker/verifier) : cela vit dans le repo `@ng-eventually/client`. Ici on décrit seulement **comment Festipod utilise ce SDK**. ## ORM & shapes SHEX diff --git a/.project/concepts/functional-domain/_debt.md b/.project/concepts/functional-domain/_debt.md deleted file mode 100644 index 96103a4..0000000 --- a/.project/concepts/functional-domain/_debt.md +++ /dev/null @@ -1,14 +0,0 @@ -# Doc-debt — functional-domain - -> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file. -> One block = one "big change": `why` + `files` + `verify` (leaves to review). - -## Block: T03.b — l'isolation par périmètre est désormais ACTIVE (confirmatoire) -- **why**: Le modèle produit public/protected/private (knowledge_data-scopes-and-discovery) devient effectivement appliqué : protected = propriétaire + connexions, public = tous, private = propriétaire. Le fait DOMAINE (les connexions) est déclaré au SDK par l'app ; le contenu doctrinal du périmètre est inchangé (le code converge vers la doctrine, ne l'invalide pas). Le fichier .feature ne fait que valider ce modèle. -- **files**: src/modules/workshop/features/protected-connections.feature -- **verify (leaves à relire)**: knowledge_data-scopes-and-discovery.md — confirmer que le triptyque public/protected/private + "connexions bilatérales" reste exact (aucun changement attendu). Bloc à supprimer après relecture confirmatoire. - -## Block: T03.c — découverte via index global (fan-out cross-comptes résorbé) -- **why**: L'app passe du **fan-out cross-comptes** (lecture directe des docs publics de tous les comptes) à la lecture d'un **index global** possédé par le SDK : publier un événement public = soumettre sa référence à l'index ; découvrir = lire l'index. Le fait DOMAINE (intention « la découverte lit un index global d'événements ») est INCHANGÉ — le code converge vers la doctrine existante, ne l'invalide pas. Compte spécial / inbox / curateur portant l'index = simulation du SDK, invisibles à Festipod (frontière SDK) ; aucun store-id ni mécanique d'index dans le plan de données de l'app. -- **files**: src/shared/data/discovery.ts (nouveau), src/shared/context/FestipodDataContext.tsx, src/shared/test-harness/harness-ng.tsx, src/modules/event/features/decouverte-publique.feature, src/modules/event/steps/data/decouverte.steps.ts -- **verify (leaves à relire)**: knowledge_data-scopes-and-discovery.md — la section « Découverte des événements » dit déjà « un index global … le SDK lit cet index » : confirmer qu'aucun mot ne décrit encore un fan-out (aucun attendu). Bloc à supprimer après relecture confirmatoire. diff --git a/src/modules/auth/screens/AccessGateScreen.tsx b/src/modules/auth/screens/AccessGateScreen.tsx index cb9dc8f..1676fee 100644 --- a/src/modules/auth/screens/AccessGateScreen.tsx +++ b/src/modules/auth/screens/AccessGateScreen.tsx @@ -8,8 +8,8 @@ * to open the SHARED wallet. After return (inside the broker iframe) NG * auto-connects and the app shows the perceived login (ConnexionScreen). * - * ASSISTED IMPORT (see the broker-import constraint, concept nextgraph-platform - * + decision_2026-06-17). The hosted broker can't import a wallet inline during + * ASSISTED IMPORT (see decision_2026-06-17). The hosted broker can't import a + * wallet inline during * web-app auth: a first-time device has no wallet, so the broker redirect would * dead-end. We therefore HAND the user the shared wallet FILE (download) + the * shared password and guide a one-time import on nextgraph.eu ("Import a Wallet diff --git a/src/modules/auth/sharedWallet.ts b/src/modules/auth/sharedWallet.ts index 2fc8c84..70f60ae 100644 --- a/src/modules/auth/sharedWallet.ts +++ b/src/modules/auth/sharedWallet.ts @@ -1,10 +1,8 @@ /** * Shared wallet material for the staging stopgap. * - * STOPGAP (see brief_2026-06-15_shared-wallet-shim + the broker-import - * constraint, concept nextgraph-platform): the hosted broker can't auto-import - * a wallet, so Festipod HANDS the user the shared wallet and guides a one-time - * import on nextgraph.eu. + * STOPGAP: the hosted broker can't auto-import a wallet, so Festipod HANDS the + * user the shared wallet and guides a one-time import on nextgraph.eu. * * The correct primitive is the **wallet FILE** (.ngw), NOT a TextCode: a * TextCode is a transient device-to-device transfer (5 min, source device diff --git a/src/shared/context/AccountContext.tsx b/src/shared/context/AccountContext.tsx index 0ba2353..fb6a69a 100644 --- a/src/shared/context/AccountContext.tsx +++ b/src/shared/context/AccountContext.tsx @@ -1,9 +1,7 @@ /** - * AccountContext — the *simulated* application-level login. + * AccountContext — the application-level login. * - * STOPGAP — part of the shared-wallet shim (see - * .project/concepts/nextgraph-platform/brief_2026-06-15_shared-wallet-shim.md - * and decision_2026-06-15_shared-wallet-login-flow.md). + * STOPGAP (see decision_2026-06-15_shared-wallet-login-flow.md). * * The real NextGraph login (a redirect to the broker, opening the single * SHARED wallet) is perceived by the user as a *technical access barrier*, diff --git a/src/shared/context/FestipodDataContext.tsx b/src/shared/context/FestipodDataContext.tsx index ffadaea..6dee8db 100644 --- a/src/shared/context/FestipodDataContext.tsx +++ b/src/shared/context/FestipodDataContext.tsx @@ -52,7 +52,7 @@ interface FestipodDataContextValue { participations: FpParticipationData[]; meetingPoints: FpMeetingPointData[]; friendships: FpFriendshipData[]; - /** Host-facing notifications, surfaced from the inbox curator (T02.c). */ + /** Host-facing notifications, surfaced from the inbox read (T02.c). */ notifications: FpNotificationData[]; getEvent(id: string): FpEventData | undefined; @@ -324,7 +324,7 @@ function useNgData(): FestipodDataContextValue { const usersShape = useShapeWithDefaults(FpUserProfileShapeType, protectedScope, emptyUsers, mapUser, true); const participationsShape = useShapeWithDefaults(FpParticipationShapeType, protectedScope, emptyParticipations, mapParticipation, true); - // Cross-account public discovery: read the fan-out documents as events. + // Cross-account public discovery: read the discovered documents as events. const discoveryShape = useShapeWithDefaults(FpEventShapeType, discoveryScope, emptyEvents, mapEvent, true); // Union the current-scope events with the cross-account discovered ones, @@ -345,7 +345,7 @@ function useNgData(): FestipodDataContextValue { const [meetingPoints, setMeetingPoints] = useState([]); const [friendships, setFriendships] = useState([]); // Host-facing notifications, materialized from the current user's inboxes - // (the emulated curator, T02.b/c). Data-level surfacing of "new participants". + // (the inbox read, T02.b/c). Data-level surfacing of "new participants". const [notifications, setNotifications] = useState([]); const [selectedEventId, setSelectedEventId] = useState(''); @@ -396,7 +396,7 @@ function useNgData(): FestipodDataContextValue { const selectedUser = users.find(u => u.id === selectedUserId); // --- Notification materialization (T02.c) --------------------------------- - // Run the emulated inbox curator over the current user's hosted events and + // Run the inbox read over the current user's hosted events and // surface "new participant" deposits as host-facing FpNotifications. Keyed on // the events the user hosts/selects; polls once per (events, selectedEvent). // Data-level surfacing — the notification module reads `notifications`. @@ -410,7 +410,7 @@ function useNgData(): FestipodDataContextValue { (async () => { try { // The SDK resolves the inbox anchor for the current session; read it ONCE - // and let the curator filter deposits per hosted event. + // and let the inbox filter deposits per hosted event. const targetInbox = await hostInboxNuri(''); const all: FpNotificationData[] = []; for (const evId of hostedEventIds) { @@ -545,7 +545,7 @@ function useNgData(): FestipodDataContextValue { await insertNotification(protectedGraph, notif).catch(() => { /* data-level best-effort */ }); // Surface immediately in reactive state (materialization also refreshes it). // Use the stable per-deposit uid for the id (F5 dedup) so it matches the - // curator-materialized id and same-ms/anon deposits never collide. + // notification id from the inbox and same-ms/anon deposits never collide. setNotifications(prev => [...prev, { ...notif, id: `notif-${depositUid}` }]); } catch (err) { console.error('[FestipodData] joinEvent inbox/notify failed:', err); diff --git a/src/shared/data/registration.ts b/src/shared/data/registration.ts index e1da30d..62fb5d1 100644 --- a/src/shared/data/registration.ts +++ b/src/shared/data/registration.ts @@ -67,11 +67,8 @@ function mintDepositUid(): string { * Preference order: the explicit MeetingPoint `inbox` NURI (SHEX field, T02.a) * when known → else the SDK-resolved inbox anchor for the current session * (`resolveInboxAnchor()`). The app asks the SDK for the anchor by intent and - * holds NO store id: the SDK owns where deposits physically land (today: the - * shared wallet's private store — a real repo NURI, required because the broker - * rejects a `urn:` anchor; deposits are discriminated by their `eventId` - * payload, the curator filters per event). At migration the SDK returns the - * host's native inbox NURI and this call is unchanged. + * holds NO store id: the SDK owns where deposits land. Deposits are + * discriminated by their `eventId` payload, and the inbox filters per event. */ export async function hostInboxNuri(eventId: string, explicitInbox?: string): Promise { void eventId; // reserved: per-event inbox docs at migration @@ -124,7 +121,7 @@ export async function depositRegistration( /** * Materialize a host inbox's deposits into host-facing notifications (data-level - * surfacing). The emulated curator (`inbox.read`) returns the raw deposits; we + * surfacing). The inbox read (`inbox.read`) returns the raw deposits; we * map each registration deposit to an `FpNotificationData` for `recipientId`. */ export async function readRegistrationNotifications( @@ -136,8 +133,8 @@ export async function readRegistrationNotifications( for (const d of deposits) { const p = d.payload as Partial | null; if (!p || p.kind !== NOTIF_TYPE_NEW_PARTICIPANT || !p.eventId) continue; - // The polyfill inbox is shared (private store): keep only deposits for the - // event whose host is reading. `recipientEventId` doubles as the recipient. + // Keep only deposits for the event whose host is reading. + // `recipientEventId` doubles as the recipient. if (recipientEventId && p.eventId !== recipientEventId) continue; const built = buildNotification(recipientEventId, p.eventId, d.from ?? null, d.ts); // F5 dedup: prefer the stable per-deposit uid carried in the payload so @@ -319,7 +316,7 @@ export async function insertNotification( const subject = `urn:festipod:notif:${Date.now()}:${Math.random().toString(36).slice(2)}`; // recipient/ref are bare domain ids ("user-1", "event-1"), not absolute IRIs; // store them as string literals to keep the INSERT valid (the raw shape read - // is not the primary surfacing path — the inbox curator is). Every literal is + // is not the primary surfacing path — the inbox read is). Every literal is // escaped via the lib's escapeLiteral (guards \ " \n \r \t — SPARQL injection). const refTriple = notif.ref ? `\n <${P.ref}> "${escapeLiteral(notif.ref)}" ;` : ''; const payloadTriple = notif.payload diff --git a/src/shared/utils/isolation.ts b/src/shared/utils/isolation.ts index 5ca6359..d4fc9d4 100644 --- a/src/shared/utils/isolation.ts +++ b/src/shared/utils/isolation.ts @@ -1,17 +1,14 @@ /** - * isolation — app-level enforcement of the authorization matrix. + * isolation — app-side visibility filter for the authorization matrix. * - * STOPGAP (see brief_2026-06-15_shared-wallet-shim): one shared wallet means - * everything is physically readable. To make staging *behave* like the target - * infra, the app HONORS the matrix by filtering reads by owner + connections: + * STOPGAP: the app HONORS the matrix by filtering reads by owner + connections: * * - public (events, meeting points) → visible to everyone * - protected (participations, connections) → owner + connections * - private (settings) → owner only * - * This is NOT crypto-enforced — it's a deliberate, removable scaffold (the real - * crypto isolation arrives with per-user wallets). Applied in CONNECTED mode - * only; demo/@ui mode keeps full seed data. + * This is a deliberate, removable scaffold. Applied in CONNECTED mode only; + * demo/@ui mode keeps full seed data. * * Pure functions — no NextGraph, no React. Trivially testable. */ diff --git a/src/shared/utils/ngSession.ts b/src/shared/utils/ngSession.ts index 82f606f..fd38aa7 100644 --- a/src/shared/utils/ngSession.ts +++ b/src/shared/utils/ngSession.ts @@ -1,5 +1,5 @@ // Injection point — the ONLY app module that imports the real @ng-org SDK, to -// inject it into the ng-eventually polyfill. Every other Festipod module gets +// inject it into @ng-eventually/client. Every other Festipod module gets // its NextGraph surface from @ng-eventually/client. Removed at migration. import { ng as realNg, init as realInit } from "@ng-org/web"; import type { NG } from "@ng-eventually/client"; diff --git a/src/shared/utils/storeRegistry.ts b/src/shared/utils/storeRegistry.ts index 232357a..0efa59d 100644 --- a/src/shared/utils/storeRegistry.ts +++ b/src/shared/utils/storeRegistry.ts @@ -1,16 +1,9 @@ /** - * storeRegistry (Festipod glue) — the GENERIC mechanism now lives in the lib - * (`@ng-eventually/client` `storeRegistry`, ported in T01.b). This file keeps - * ONLY the Festipod domain mapping (entity kind → native scope) and injects the - * consumer wiring the lib needs (session + username normalization) via - * `configureStoreRegistry(...)`. - * - * The lib knows only the three native scopes (`public|protected|private`) and - * performs all NextGraph I/O through the real injected `ng` (never the public - * proxy → no DataCloneError). Everything the app previously implemented here - * (shim model, doc_create, SPARQL r/w, index/fan-out) is now the lib's job; the - * app re-exports the lib surface so existing callers stay unchanged. See - * decision_2026-06-17_eventually-library and brief_2026-06-15_shared-wallet-shim. + * storeRegistry (Festipod glue) — the lib owns placement; the app maps + * entity → scope. This file keeps ONLY the Festipod domain mapping (entity kind + * → scope) and injects the consumer wiring the lib needs (session + username + * normalization) via `configureStoreRegistry(...)`. The app re-exports the lib + * surface so existing callers stay unchanged. */ import { @@ -41,17 +34,16 @@ export function entityScope(kind: EntityKind): Scope { } } -// --- Consumer wiring injected into the lib's storeRegistry (polyfill-era) --- -// The lib is Festipod-agnostic: it reaches the shared-wallet session and the -// username normalization through these injected deps. Idempotent module-load -// side effect (the app imports storeRegistry before any registry call). +// --- Consumer wiring injected into the lib's storeRegistry --- +// The lib is Festipod-agnostic: it reaches the session and the username +// normalization through these injected deps. Idempotent module-load side effect +// (the app imports storeRegistry before any registry call). configureStoreRegistry({ getSession: async () => { const session = await sessionPromise; - // Sanctioned injection point: the session (incl. the three native store ids) - // is handed to the lib HERE and nowhere else. The lib owns physical placement - // and resolves scope → store internally; the rest of the app speaks only in - // logical scopes and never touches a store id / builds a `did:ng:${…}` NURI. + // Sanctioned injection point: the session is handed to the lib HERE and + // nowhere else. The lib owns placement and resolves scope internally; the + // rest of the app speaks only in logical scopes. return { sessionId: session.session_id, privateStoreId: session.private_store_id, @@ -80,23 +72,15 @@ export const { } = libStoreRegistry; /** - * Create a per-entity document AND declare its ReadCap/WriteCap policy — the - * app-side ACTIVATION of the emulated cap registry (dormant until an app - * declares a policy). The lib's `createEntityDoc` stays domain-agnostic; the - * DOMAIN mapping (scope → who may read) is Festipod's, so it lives here. - * - * In the target this is a native cap operation attached at store/repo creation; - * here it is `getCaps().open(doc, scope, owner)`: + * Create a per-entity document AND declare its ReadCap/WriteCap policy. The + * lib's `createEntityDoc` stays domain-agnostic; the DOMAIN mapping (scope → who + * may read) is Festipod's, so it lives here via `getCaps().open(doc, scope, owner)`: * - public → world-readable (`makePublic`) — events, meeting points * - protected → owner reads now; connections granted later (a separate grant) * - private → owner only - * The owner always holds the WRITE cap (so only the owner may `sparql_update` - * the doc once the guard is active). `owner` = the account username (the same - * principal key the shim uses and that the app sets via `setCurrentUser`). - * - * NOTE ON BASELINE: `createEntityDoc` is only reached in MULTISTORE mode; the - * default mono-store path never calls it and never sets a current user, so both - * the ReadCap filter and the write guard stay inert (passthrough) by default. + * The owner always holds the WRITE cap (so only the owner may update the doc once + * the guard is active). `owner` = the account username (the principal the app + * sets via `setCurrentUser`). */ export async function createEntityDoc(username: string, scope: Scope): Promise { const entityNuri = await libStoreRegistry.createEntityDoc(username, scope);