Le polling est un anti-pattern NextGraph (par abonnement). La résolution de compte retentait ×8 la lecture du shim tant qu'elle rendait 0 (lag de sync) — c'est du polling. Remplacé par la BARRIÈRE d'abonnement, déjà le mécanisme de open-repo : - `resolveAccountReliably` : `await ensureRepoOpen(did🆖${privateStoreId})` (subscribe + attendre le 1er State — le shim vit dans le graphe du private store), PUIS lecture UNIQUE. Après la barrière, 0 ligne = compte réellement inexistant → provision 1×, lignes présentes = réutilisé (garantie NO-FORK préservée). Plus de boucle de re-lecture. - timed-out (barrière expirée) : throw explicite, NE provisionne PAS (un provision sur sync incomplète re-forkerait). Le « trop long » est un signal, pas un feu vert. - fake ng sans doc_subscribe : ensureRepoOpen no-op → lecture immédiate (unit intact). - `_forceOpenedSyncState` : helper test-only (underscore, non ré-exporté). anti-fork.test.ts réécrit (5 tests : no-fork, neuf→1 provision, idempotence, fake no-op, timed-out→throw) ; plus aucun test de comptage de retry. gate : tsc propre ; bun test 117. e2e À RE-VALIDER quand le broker répond (dégradé ce jour : crash Chromium post-connexion) — la barrière ensureRepoOpen est déjà validée e2e (CONTRAT 3 + reconnexion) en broker sain. provisionRetry devient un champ mort de StoreRegistryDeps (nettoyage ultérieur). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ng-eventually/client
Two entry points — the data-plane is SDK-identical, the polyfill bootstrap is separate:
| Import | Surface |
|---|---|
@ng-eventually/client |
The same signature as the SDK — ng, useShape, inbox (+ types). A drop-in for @ng-org/web / @ng-org/orm; as NextGraph matures it resolves to the real SDK (build alias removed) with no code change. |
@ng-eventually/client/polyfill |
The only non-SDK surface — configure, setCurrentUser, and capability helpers (getCaps, grantRead, canRead/canWrite). It falls away as NextGraph matures. |
// bootstrap (the only non-SDK call) — inject the real SDK
import { configure } from "@ng-eventually/client/polyfill";
configure({ ng: realNg, useShape: realUseShape, sharedWallet, currentUser });
// from here on, a pure SDK surface:
import { ng, useShape, inbox } from "@ng-eventually/client";
await ng.doc_create(/* … */);
const set = useShape(MyShape, scope); // filtered to what the identity may read
await inbox.post(targetInbox, ref); // deposit (anticipated SDK API)
Principle — the polyfill compensates, it never extends
The polyfill's ONLY reason to exist is to bridge a NextGraph implementation gap or a bug. Every non-SDK surface must map to a capability NextGraph will provide natively, and must fall away at that point. The polyfill MUST NOT add functionality of its own — no bespoke features, no observability/tooling, no convenience API that isn't strictly "NextGraph will do this natively later." The test for any proposed addition: does it compensate a real, exhibited NextGraph gap or bug? If not, it does not belong here — build it in the consumer application, not in the polyfill. Corollary: a compensation whose gap is not actually exhibited on the target broker is dead weight, not defensive code — it should be removed, not kept "just in case."
What the polyfill adds on top of the real SDK (each emulated for now, native as NextGraph matures):
- Shared-wallet identity (one wallet for everyone; the current identity id is relayed to the SDK).
- Capability enforcement — a read filter + write guard over emulated grants attached to documents; the app declares a document's read policy and issues directed read grants.
- Anticipated methods (inbox
post, capability ops) with their future-SDK shapes, emulated for now.
Generic: no application domain. The consumer application injects its shapes and performs the acts of granting access. The relationship concept ("who is connected to whom") is the consumer application's own — the client exposes only directed per-document read grants.