refactor(client): retirer anti-fork — gap non exhibé, resolveAccount simple

Preuve e2e (wallet frais, broker RÉEL rapide : 1er State 1-2 ms) : le private
store est synchronisé au login, la lecture du shim réussit à froid — le « fork sur
lag » que anti-fork compensait n'est PAS exhibé. Par le principe du polyfill
(compenser un gap RÉEL, jamais du poids mort), et par la règle no-polling :
- la version retry = polling (bannie) ;
- la version barrière `ensureRepoOpen(privateStore)` = CASSÉE (un store n'émet pas
  de `State`, la barrière timeout systématiquement → CONTRAT 2 e2e échouait) ;
- le gap = non exhibé.
→ `ensureAccount` fait un `resolveAccount(id)` SIMPLE (une lecture, provision si 0).
`resolveAccountReliably`, `_forceOpenedSyncState` retirés ; `provisionRetry` gardé
optionnel @deprecated (ignoré) pour ne pas casser les 8 tests qui le passent.
`ensureRepoOpen`/`getSyncState` inchangés (chemin de lecture open-repo).

gate : tsc 0 ; bun test 116 ; test:e2e 39 passed, CONTRAT 2 VERT (« same account,
no second provisioning »). Le ~10s du re-resolve public est du scaling anchorless,
pas de la lenteur broker (broker mesuré à 1-2 ms).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-09 13:47:07 +02:00
parent 45dbd9a33a
commit 38b152136b
4 changed files with 25 additions and 153 deletions
+7 -20
View File
@@ -25,17 +25,9 @@ export interface StoreRegistryDeps {
/** Normalize an identity id for shim keying. Default: trim (identity-ish). */
normalizeId?: (id: string) => string;
/**
* ANTI-FORK budget for account resolution (polyfill-era). Before provisioning
* a "missing" account, the registry re-reads its shim record a bounded number
* of times with backoff, so a record merely lagging by broker sync (fresh
* session over a persistent wallet) is FOUND and REUSED instead of triggering
* a second set of scope docs (the account fork). See the note in
* store-registry.ts. Only if every attempt still reads 0 do we provision.
*
* Default (production): a real budget (~8 attempts / ≲8.5s). Set `attempts: 1`
* to disable the retry entirely — appropriate for a SYNCHRONOUS in-memory
* store (the unit fake `ng`) where a 0-row read is authoritative and the
* backoff would only add dead time; there is no sync lag to wait out there.
* @deprecated Removed. The anti-fork retry/barrier mechanism has been dropped
* (the private store is synchronised at login; the barrier was both unnecessary
* and broken for STORE NURIs). This field is accepted but silently ignored.
*/
provisionRetry?: { attempts?: number; baseMs?: number; maxStepMs?: number };
}
@@ -64,7 +56,9 @@ export interface EventuallyConfig {
let cfg: EventuallyConfig | null = null;
let currentUser: PrincipalId | null = null;
let registryDeps: Required<StoreRegistryDeps> | null = null;
/** Required fields of StoreRegistryDeps after defaults are applied (excludes deprecated provisionRetry). */
type ResolvedRegistryDeps = Required<Pick<StoreRegistryDeps, "getSession" | "normalizeId">>;
let registryDeps: ResolvedRegistryDeps | null = null;
/** The emulated ReadCap/WriteCap registry. Empty until the app declares caps;
* while it has no read policy the read filter passes through (no regression). */
let caps = new CapRegistry();
@@ -98,18 +92,11 @@ export function configureStoreRegistry(deps: StoreRegistryDeps): void {
registryDeps = {
getSession: deps.getSession,
normalizeId: deps.normalizeId ?? ((id: string) => id.trim()),
// Production default: a real anti-fork budget. Consumers over a synchronous
// store pass `{ attempts: 1 }` to opt out (see StoreRegistryDeps).
provisionRetry: {
attempts: deps.provisionRetry?.attempts ?? 8,
baseMs: deps.provisionRetry?.baseMs ?? 300,
maxStepMs: deps.provisionRetry?.maxStepMs ?? 1500,
},
};
}
/** @internal — used by the storeRegistry to reach its injected dependencies. */
export function getStoreRegistryDeps(): Required<StoreRegistryDeps> {
export function getStoreRegistryDeps(): ResolvedRegistryDeps {
if (!registryDeps) {
throw new Error("[ng-eventually] configureStoreRegistry() must be called before use");
}