Les deux contrats et la doctrine portent des centaines de citations `file:line` vers `src/`. Le rangement par destin les périmait en bloc — 208 reprises (45 chemins `src/x.ts`, 163 mentions nues). Sans ça la réorganisation pourrissait l'instrument même qui tient la discipline qu'elle sert. Et un défaut relevé par le contrat interne, vérifié : `ng-proxy` fabriquait un membre `login`. `@ng-org/web` n'expose aucune méthode de ce nom — zéro occurrence dans les déclarations installées comme dans `sdk/js/lib-wasm/src/lib.rs` — mais le proxy répondait une fonction au lieu d'`undefined`, laquelle plantait à l'appel. C'était le seul endroit où ce wrapper ajoutait à la surface du SDK, contre son propre en-tête. 157 tests unitaires, typecheck src/test/e2e vert.
7.3 KiB
Migration guide — when real NextGraph matures
The whole point of this library: the consumer application already writes SDK-shaped code, so when NextGraph ships cross-wallet reads, capabilities and inboxes, only this lib changes. The consumer application's code does not change. This is the checklist.
Guiding invariant
Every emulated piece has a 1:1 image in the real infra. Migration = swap the
emulation for the real primitive, remove the scaffold. If a piece of the emulation
has no clear target image, that is a drift signal (see
simulation.md).
Checklist
1. Emulated ReadCaps → real capabilities
The shape is already the target's (P1a): a ReadCap is the document's key, a
each identity holds a set of caps, and there is no read-ACL anywhere. So
this step swaps the emulated key for the real one, not the model:
emulated-verifier/caps.ts's per-identity record becomes the verifier's own local user storage — it was always the cache, not the register. The two durable registers we emulate (readCapon the store's Store branch,linkon its User branch) become the realAddRepo/AddLinkcommits. Remove the emulation; the wallet and the branches already hold them.nuri.ts's stand-in cap value — the constantOK— becomes the realr:{base64url(serde_bare(ObjectRef))}. It is one function (mintCap), because every path now READS a stored cap instead of recomputing one.hasReadCap/targetOfstay meaningful: ther:discriminant is upstream grammar, not ours.shareCap(cap, toInbox)becomes the native sealed delivery (whatever the SDK ends up naming it — see the note below andContactDetails.read_cap), andinbox.read's inline absorption becomes the recipient's own verifier applying queued messages. The consumer's call does not change.publishRepoLinkbecomesRepoLinkV0.- The read filter (
emulated-verifier/read-filter.ts) and the possession gate inread-model.readUnionare then dead code — the broker only delivers documents whose cap the wallet holds. Remove them. - The write guard (
surface/ng-proxy.tssparql_updateoverride) is a separate axis and is decorative today (every internal writer bypasses the proxy); it belongs to the P1b batch, not here.
The access unit is already the document (@graph), matching the native per-repo cap
model, so this is a key-material step, not a reshape.
2. Place documents in real native stores
Today docCreate(..., undefined) writes every document into the shared wallet's
private store, and the public|protected|private scope is a logical label
in the shim (see the two-axes section in simulation.md).
doc_createcannot target a non-private native store today — verified:StoreRepois not JS-constructible from the SDK, so there is no way to pass a public/protected store as the create destination (docCreate's trailingstorearg is leftundefined→ private store). The private store works only because it opens withoutRepoNotFound.- When the SDK lets you construct/target a native store, the migration adds a
getNativeStore(scope)-style resolver returning the real store to pass as thedocCreatedestination, so the logical scope label becomes a real store placement. (No such helper exists yet — it is blocked on the SDK gap above.) - At that point
shared-wallet/account-registry.tsmaps(account, scope)to the user's real store NURI instead of a document in the shared wallet; the per-scope index document (the store-container emulation) is replaced by the store itself. The surface facing the consumer application (createEntityDoc,listMyEntityDocs, resolvers) is designed to survive that swap unchanged.
3. Drop the resolver / shim
The sharedWalletShim (account → 3 scope-document NURIs, held in a subscribable
doc-shim reached via a write-once pointer in the store-root — see
nextgraph-current-state.md § The pointer → doc-shim
indirection) has no target equivalent — the target has no central directory. Remove
it entirely: shared-wallet/account-registry.ts, configureStoreRegistry, the pointer + doc-shim
resolution, and the pointerGuard dep. Cross-wallet reads replace the fan-out;
per-user wallets replace the shared one.
4. Real inbox → drop the in-lib read emulation
Replace the emulated inbox.ts deposit (docs.sparqlUpdate into a shared-wallet
document) with the native sealed deposit, once one is exposed to JS. Its name and shape are NOT known: no inbox method exists in @ng-org/web, the verifier has no InboxPost arm, and inbox_post_link is OUR proposed name (fork-inbox-fallback.md), not an announced API. On the read side the
recipient's own verifier unseals each queued sealed message and applies it inline
when it processes its inbox — there is no separate curator to build; the in-lib read
emulation simply goes away. (There is no global index to replace the cross-account fan-out: both were removed on 2026-07-30 — you cannot discover in NextGraph, you follow links.)
5. Retire the identity store → real per-user login
Remove shared-wallet/accounts.ts (the IdentityStore that persists the identity id in
localStorage) and the app-level "Connexion" screen. The technical broker gate
becomes the real per-user login
(see decisions/shared-wallet-login-flow.md).
The flow shape ("broker redirect → app") does not change.
6. Drop the isolation scaffold — already gone
isolation.ts (the old application-visibility filter) was deleted from the library;
nothing remains to remove at migration. Kept as a numbered step so the following
numbers stay stable across references.
7. Remove the build alias — the client becomes the real SDK
The consumer application imports @ng-org/web / @ng-org/orm resolved to this lib
via a build alias during the polyfill period. Removing the alias makes those imports
resolve to the real SDK — the ng/useShape/inbox surface is SDK-identical, so
no consumer code changes. The one non-SDK call — configure(...) /
@ng-eventually/client/polyfill — is deleted. The lib itself disappears.
The one break already taken: declareConnections
P1a broke the consumer once, deliberately and early, so that migration would not.
The old surface was an ACL held in memory, which forced the consumer to re-declare
every grant on every session (declareConnections). That call disappears: with
delivered caps the grant moves to the moment a connection is accepted — one
shareCap(capFor(doc), theirInbox) per document shared — and it persists, because
the delivery lives in the recipient's inbox rather than in a map that empties at
reload. There is no analogue of protectedDocsOf + the re-derivation loop.
This is a consumer re-architecture, not an API swap, and it is the price of being coded against a model that will exist. Nothing else about the migration below touches consumer code.
What does not change
The consumer application's code. Shapes, screens, the acts of sharing access, entity→scope mapping, the relationship graph — all injected, all untouched. Migration is entirely inside this library plus removing the alias + the bootstrap call. That asymmetry — a mature SDK face outward, all compensation inward — is the library's reason to exist.