Une application signalait deux symptômes. Ils sont indépendants, et ils ont une
racine commune : se connecter ne rejouait qu'UN des registres durables du
portefeuille.
connectedUser lisait les AddLink de la branche User — ce qu'on vous a partagé —
et rien d'autre. Les capacités des documents que vous avez FAITS vivent sur la
branche Store, et un seul chemin les relisait : listMyEntityDocs. Une
application qui recharge et va droit à sa note ne tenait donc rien pour elle.
Constaté : capFor(note) vaut undefined juste après une connexion résolue, et
devient défini dès que listMyEntityDocs passe.
Le public survivait en lecture parce que fetchReadCap va rechercher la clé dans
le store ; il échouait quand même à l'écriture, qui ne fait pas cette démarche.
Deuxième défaut : myInboxes énumérait les inbox sans en remettre la clé au
détenteur. Se connecter demandait donc un document qu'on n'avait pas de quoi
lire — sur une inbox, que l'application n'a jamais nommée puisque rien ne le
lui permet.
Troisième défaut, et c'est lui qui rendait tout ça fatal : un drainage refusé
faisait échouer toute la connexion. Une inbox n'étant pas consommée par un
échec, elle refusait à chaque tentative suivante. D'où le « trois fois sur
trois », et d'où un verrouillage plutôt qu'un partage manquant.
C'est ma spécification qui l'a créé. En rendant les échecs visibles j'avais
écrasé une distinction : ne pas ATTEINDRE la file est une panne, et refuser la
session est juste ; ne pas pouvoir APPLIQUER un élément est une donnée, et ça ne
doit priver personne de sa session. Le drainage par inbox est désormais isolé —
signalé haut et fort, les autres files drainées, la session accordée — tandis
qu'énumérer les files et restaurer rejettent toujours.
Le parcours e2e ratait les deux : personne ne se reconnecte après avoir ouvert
son document aux messages. Il s'arrêtait une reconnexion trop tôt.
connectedUser() restaure les capacités reçues et draine les inbox. Aucun de ses
chemins ne pouvait échouer : un broker injoignable rendait exactement la même
promesse qu'un succès complet. L'application affichait alors des listes vides,
et rien nulle part ne disait que la restauration n'avait pas eu lieu.
L'énumération m'avait échappé sur deux points, l'agent les a établis.
resolveAccount attrapait tout et rendait null : une lecture qui ÉCHOUAIT
ressortait donc comme « ce compte n'existe pas ». L'échec était déguisé en
absence — c'est la racine du partage cassé trouvé ce matin, dont on n'avait
traité que le déclencheur. lookupAccount le remplace : le silence n'est plus
possible que sur une absence VÉRIFIÉE.
Et readLinks comme readInboxCapPairs avalaient leur propre erreur en rendant un
tableau vide, un étage sous le catch de connect. Une panne n'y parvenait même
pas. Elles relèvent désormais.
La règle est simple : tout échec remonte, seul « il n'y avait rien à faire »
se résout en silence. Ce qui reste silencieux — aucun détenteur, compte
réellement absent, identité changée en route — l'est parce que c'est la vérité.
Le piège consigné hier est fermé par là même : une exécution qui ne peut pas
répondre rejette, et ceux qui la rejoignent en héritent. Sa feuille est
supprimée, la question qu'elle laissait ouverte étant tranchée.
Trois fixtures de test utilisaient un ng vide — une forme qu'aucune plateforme
ne présente, dont le TypeError était mangé par le catch. Remplacées par un
broker au portefeuille vide. Aucune assertion modifiée.