fix: un échec de lecture ne fabrique plus d'état durable et faux

userInbox avalait deux échecs, et chacun laissait des dégâts sur le disque.

À la lecture : la requête « quelle inbox ce compte possède-t-il » levait, on
journalisait, et on créait une inbox de plus. Deux associations pour un même
couple utilisateur/portée — vérifié en conditions, sdoc5 et sdoc6 coexistants.

À l'écriture : l'INSERT levait, on retournait quand même la référence en la
mettant en cache. Le propriétaire tenait sdoc5 pendant qu'un déposant résolvait
sdoc6. Il lit une boîte où nul n'écrit, ils écrivent dans une boîte que nul ne
lit.

Et un troisième que je n'avais pas vu : recordInbox avalait son propre INSERT
puis marquait son index en mémoire — l'inbox était associée mais refusée à la
session suivante, donc tous les dépôts rebondissaient, définitivement.

Le balayage demandé a trouvé la famille entière : onze sites de cette forme, un
catch qui journalise puis une exécution qui continue comme si la chose cherchée
était absente. Les huit autres corrigés vont d'une seconde racine de registre
créée sur budget épuisé, à un document public qui ne sert plus jamais sa clé.

La règle appliquée partout : seule une absence VÉRIFIÉE autorise à créer, et une
référence n'est remise à personne avant que son association ait atterri.

Les sites laissés échouent en fermeture — un refus, une liste vide — sans rien
écrire. Ils sont listés, pas oubliés.
This commit is contained in:
Sylvain Duchesne
2026-08-13 13:21:54 +02:00
parent 0f1e493e93
commit e32b6d04fc
6 changed files with 796 additions and 11 deletions
@@ -197,3 +197,15 @@ test("a failed key write is reported too, and names that half", async () => {
await expect(storeRegistry.createEntityDoc("protected")).rejects.toThrow(/key is not recorded/i);
});
// The third write a PUBLIC creation makes, and the one that was still silent: exposing the
// cap on the document is what lets anyone else read it (`public-store.exposeReadCap`, the
// emulated `expose_outer`), it happens once at creation and nothing ever redoes it. A
// swallowed failure therefore left a document sitting in a public store serving nothing —
// unreadable by everyone but its creator, permanently, with a reference handed back as
// though it had worked.
test("a public document whose cap could not be exposed is reported, not returned", async () => {
inject(/shim:exposedReadCap/);
setCurrentUser("alice");
await expect(storeRegistry.createEntityDoc("public")).rejects.toThrow(/broker refused/i);
});