fix(client): dé-dupliquer les ensureAccount concurrents au cold-start (fork résolu)

Dernière couche du bug de reconnexion : au cold-start, `watchShape` public +
protected + l'effet owned-events appellent `ensureAccount(A)` quasi-simultanément
AVANT la sync du shim → chacun lit 0 → chacun provisionne un nouveau jeu de docs
(fork par-appelant) → la résolution déterministe canonique fait alors diverger
lecteur et écrivain sur le docProtected → `readScopeIndex` vide.

Fix : `ensureInFlight` (map de promesses) dé-duplique les provisions concurrentes
en UNE seule ; `discovery.readIndex` ouvre son repo au cold-start (`ensureRepoOpen`).
Avec la résolution canonique déjà committée, écrivain et lecteur convergent.
Mesuré (levier isSuccess) : la participation protected converge `isSuccess=true,
data=1` sur la page fraîche (plus « vide à 30s »).

gate : tsc 0 ; bun test 123 ; test:e2e 42/42.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-10 11:29:59 +02:00
parent bd48b16e31
commit 9103996dbe
4 changed files with 127 additions and 40 deletions
+7
View File
@@ -156,6 +156,13 @@ export async function post(targetInbox: Nuri, opts: PostOptions): Promise<void>
*/
export async function read(targetInbox: Nuri): Promise<Deposit[]> {
const sid = await sessionId();
// NOTE: cold-start repo opening is done by the COLD DIRECT readers that need it
// (e.g. `discovery.readIndex` → `ensureInboxRepoOpen`), NOT here — `inbox.watch`
// already holds the repo open via its own `subscribeDoc`, so opening a second
// bootstrap subscription from inside a watch's re-read would be redundant and can
// race the watch's own initial-`State` delivery. Keeping `read` a pure anchored
// read leaves both callers correct: the watch path stays event-driven, and the
// cold direct-read path opens the repo explicitly before calling `read`.
// NO explicit `GRAPH <…>` clause — read the anchored DEFAULT graph (see the
// note in `post`). The anchor (`targetInbox`) scopes the query to that repo's
// default graph, exactly where `post` writes.