fix(client): durcir le cold-start du shim — ensureRepoOpen(anchor) avant lecture/écriture
Défensif : loadShim/resolveAccount/ensureAccount ouvrent l'anchor (private-store-root) avant de lire/écrire le compte shim, comme le fait déjà readScopeIndex. Robustesse same-session si l'anchor est là-mais-pas-encore-souscrit. NB (vérifié nextgraph-rs) : sur le login broker normal, le bootstrap charge le private-store dans self.repos AVANT de rendre la session à JS → un wallet FRAIS retourne 0 rows (pas RepoNotFound) et provisionne. Il n'existe AUCUN primitif JS pour ouvrir un repo *inconnu* : ce heal n'est pas un remède à un store non bootstrappé (limite NextGraph), juste une robustesse d'ouverture same-session. Tests : cold-start-anchor.test.ts (rouge-avant/vert-après unit) ; harness e2e repro-fresh-wallet (mint un wallet neuf par run — comble le trou "aucun test de démarrage à froid sur wallet vierge"). Fakes anti-fork/watch-shape honorent désormais la barrière first-State dont dépend le heal. e2e réel 42/42. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -571,6 +571,67 @@ const identity = new IdentityStore(
|
||||
return { priv, prot, pub };
|
||||
},
|
||||
|
||||
/**
|
||||
* COLD-START anchor probe. Runs the EXACT shim SELECT the registry issues,
|
||||
* anchored to `did:ng:${private_store_id}` (the shim anchor), as the very first
|
||||
* thing in a fresh session — BEFORE anything opens that repo. Reports whether the
|
||||
* raw anchored query threw `RepoNotFound` (the cold-start bug: the private-store
|
||||
* repo not yet in `self.repos`) or returned rows. Uses the low-level docs
|
||||
* primitive directly so nothing (open-repo, ensureAccount) opens the repo first.
|
||||
*/
|
||||
async shimAnchorProbe() {
|
||||
const s = session ?? (await sessionReady);
|
||||
const anchor = `did:ng:${s.private_store_id}`;
|
||||
const query =
|
||||
"SELECT ?acc WHERE { GRAPH <" +
|
||||
anchor +
|
||||
"> { ?acc a <urn:ng-eventually:shim:Account> } }";
|
||||
try {
|
||||
const res: any = await docs.sparqlQuery(s.session_id, query, undefined, anchor);
|
||||
const rows = Array.isArray(res) ? res.length : (res?.results?.bindings?.length ?? 0);
|
||||
return { threw: false, error: null, rows, anchor };
|
||||
} catch (e: any) {
|
||||
return { threw: true, error: String(e?.message ?? e), rows: -1, anchor };
|
||||
}
|
||||
},
|
||||
/**
|
||||
* COLD-START account provision. resetRegistryCache then ensureAccount(id) — the
|
||||
* real bootstrap the app runs on first login. On a fresh wallet, if the anchor
|
||||
* repo isn't open, resolveAccount's read AND ensureAccount's provision write both
|
||||
* hit RepoNotFound; the account never persists. Returns the 3 scope docs (all
|
||||
* truthy iff provisioning succeeded) so the runner can gate on real persistence.
|
||||
*/
|
||||
async coldEnsureAccount(id: string) {
|
||||
storeRegistry.resetRegistryCache();
|
||||
try {
|
||||
const rec = await storeRegistry.ensureAccount(id);
|
||||
return {
|
||||
threw: false,
|
||||
error: null,
|
||||
docPublic: rec.docPublic,
|
||||
docProtected: rec.docProtected,
|
||||
docPrivate: rec.docPrivate,
|
||||
};
|
||||
} catch (e: any) {
|
||||
return { threw: true, error: String(e?.message ?? e), docPublic: "", docProtected: "", docPrivate: "" };
|
||||
}
|
||||
},
|
||||
/**
|
||||
* VERIFY the provisioned account actually PERSISTED to the shim: resetRegistryCache
|
||||
* then re-resolve the SAME id via a fresh anchored read. Returns whether the read
|
||||
* threw + the resolved docs. After the fix, on a fresh wallet this returns the SAME
|
||||
* docs coldEnsureAccount minted (real persistence, no RepoNotFound).
|
||||
*/
|
||||
async verifyShimPersisted(id: string) {
|
||||
storeRegistry.resetRegistryCache();
|
||||
try {
|
||||
const rec = await storeRegistry.ensureAccount(id);
|
||||
return { threw: false, error: null, docPublic: rec.docPublic, docProtected: rec.docProtected, docPrivate: rec.docPrivate };
|
||||
} catch (e: any) {
|
||||
return { threw: true, error: String(e?.message ?? e), docPublic: "", docProtected: "", docPrivate: "" };
|
||||
}
|
||||
},
|
||||
|
||||
// ── watchShape (reactive useQuery-shaped read) ───────────────────────────
|
||||
// A minimal SHEX-ish ShapeType pinning rdf:type to an e2e class IRI. watchShape
|
||||
// reads only `.shape`/`.schema[...].predicates[rdf:type].dataTypes[].literals`
|
||||
|
||||
Reference in New Issue
Block a user