Commit Graph

8 Commits

Author SHA1 Message Date
Sylvain Duchesne f5a3adc385 fix: la barrière s'affiche à chaque chargement en page de tête, comme chez Festipod
Notre version décidait d'afficher la barrière sur la présence de l'identifiant.
Festipod décidait sur la session — jamais établie en page de tête, donc l'écran
s'affichait toujours, l'identifiant servant seulement à pré-remplir le champ.

La différence n'est pas ergonomique. L'identifiant est un état qu'on observe ;
le portefeuille, lui, vit dans le stockage d'une autre origine et nous est
illisible. Un écran conditionnel doit donc DEVINER cet état invisible — et
quand il devine « déjà installé » alors que le portefeuille a disparu du
navigateur, il cache les seuls contrôles qui répareraient la situation et
précipite la personne dans une impasse.

Impasse observée sur le site réel : sans portefeuille, la page du broker affiche
un texte statique, zéro bouton, un seul lien vers nextgraph.eu qui NE TRANSPORTE
AUCUN retour vers l'application. Le retour arrière du navigateur est la seule
issue — et il ne sert à rien si la barrière ne reprend pas la personne à
l'arrivée.

Le discriminant devient le cadre, pas l'identifiant : page de tête → toujours,
iframe → on s'efface. C'est le signal que @ng-org/web utilise lui-même et que
Festipod utilisait un étage plus bas.

On ne détecte rien et on ne demande rien. Les trois étapes s'affichent toujours ;
qui possède déjà son portefeuille ignore les deux premières. Aucune case « je
l'ai déjà » : savoir si l'on a importé un portefeuille dans ce navigateur est
une question trop technique pour être posée.

Garde-fou repris de Festipod, qu'on n'avait pas : après un aller-retour vers
l'onglet NextGraph et un retour arrière, la barrière restait figée sur un bouton
mort. Elle recharge désormais sur pageshow persisted — seul moyen de rejouer le
init() qui porte la redirection.

Vérifié sur navigateur : cliquer « Import a Wallet File » ouvre un sélecteur sur
place, sans navigation ni changement d'onglet, et notre onglet ne reçoit AUCUN
signal quand l'import réussit. Détecter le retour est donc impossible, pas
seulement fragile.
2026-08-12 14:59:10 +02:00
Sylvain Duchesne c5b4703687 test(e2e): le parcours d'un primo-arrivant, et un harnais qui échoue au lieu de se pendre
Aucun parcours n'avait jamais marché le chemin d'un nouvel arrivant : tous
pré-injectaient l'identifiant dans l'URL, ce qui fait résoudre l'identité sans
jamais afficher la barrière. La suite était verte par-dessus un lien de
téléchargement pointant sur un fichier que personne ne servait — et le serveur
de test répondait la page HTML de l'application pour tout chemin inconnu, donc
un fichier manquant ne POUVAIT pas échouer.

Le nouveau parcours part d'un profil vide : barrière, téléchargement réel,
import dans l'application portefeuille, saisie de l'identifiant, remise au
broker, retour dans l'iframe. Il vérifie neuf points, dont celui qui compte —
l'identifiant a survécu et l'identité rapportée est celle qui a été saisie.

Le harnais, lui, se pendait au lieu d'échouer. Cause observée : le tuyau
devtools de Chromium lâche et Playwright n'émet ni close ni disconnected, si
bien que la suite bloquait dans son propre nettoyage sans imprimer ni résumé ni
l'échec déjà en route. Toutes les attentes sont désormais bornées et nomment ce
qu'elles attendaient ; vérifié en cassant délibérément une attente, et observé
en conditions réelles — trois minutes et « gave up waiting for: alice to sign
in » là où j'ai tué trois exécutions d'une heure ce matin.

Deux exécutions simultanées ne se détruisent plus : verrou atomique sur le
profil, et récupération d'un navigateur laissé par une exécution tuée. Le
marqueur devient .user-consumed — il n'a jamais attesté d'une disponibilité,
seulement qu'un lot avait déjà pris l'utilisateur de ce profil. Au passage, la
destruction du profil dépendait du marqueur, écrit en FIN de lot : une
exécution tuée avant laissait un profil que la suivante réutilisait, et
héritait de sa casse. Elle dépend maintenant du profil.

La suite applicative reste non mesurée sur cette machine : un conteneur en
boucle de redémarrage recycle son interface réseau, et sept exécutions sur dix
échouent sur le transport. Trois sont passées 21/21.
2026-08-11 19:34:09 +02:00
Sylvain Duchesne 16e24f67f9 refactor: les commentaires disent cap-surface et cap-enforcement
Suite du balayage commencé dans la doc : 20 occurrences de P1a/P1b dans les
commentaires, les titres de tests et le README.

Les phrases ont été récrites, pas substituées : « the breach P1a opened »
devient « the breach that cap-surface opened », et « labelled P1b's » ne
survivait pas à un nom plus long. Un lecteur qui n'a jamais entendu ni l'un ni
l'autre doit comprendre la phrase.

L'avertissement de déploiement du README garde sa force et gagne un nom :
« "anonymous" or "private" until cap-enforcement lands per-document
encryption. »

Reste une occurrence dans e2e/polyfill-entry.ts, qui part avec le lot e2e.
2026-08-11 19:14:49 +02:00
Sylvain Duchesne 3be8da2178 fix: régler l'identité n'a plus le droit de réclamer une session
Le partage était cassé : Bob n'ouvrait pas le document qu'Alice venait de lui
partager, sans erreur, juste « (illisible) ». Régression introduite en scindant
ensureIdentity().

Chaîne observée aux sondes, pas déduite : settleIdentity() appelait
setCurrentUser, qui déclenche startConnect(), qui va chercher la session via le
thunk getSession de l'application. Or l'exemple appelle init() DEPUIS
L'EXÉCUTEUR qui construit sessionReady — le thunk ne peut donc pas répondre, par
construction. Il lève, resolveAccount rend null, l'exécution est abandonnée sans
restauration ni drainage, mais s'est déjà enregistrée « en vol ». Le
ensureIdentity() suivant rejoint cette exécution morte et se résout sans avoir
rien fait.

Avant la scission, rien n'appelait setCurrentUser pendant l'évaluation du
module : la session existait, l'exécution était saine, et la rejoindre était
sans danger. C'était bien une affaire de moment.

bootstrap.ts scinde le setter : adoptCurrentUser enregistre qui agit,
setCurrentUser reste « enregistrer + connecter » pour tous les autres appelants.
La moitié sans session ne réclame donc plus de session, et se connecter
redevient l'affaire du seul ensureIdentity(), attendu, là où une session existe.

Ce que ça bloque, tracé avant de livrer : une application qui appellerait init()
sans jamais appeler ensureIdentity() n'aurait plus de restauration en arrière-
plan. Aucun appelant de ce genre n'existe, et avant la scission init() était un
passthrough nu qui ne déclenchait rien — c'est une répartition rétablie, pas un
comportement retiré.

Reste connu, non corrigé : connectedUser mémorise toujours une exécution
abandonnée. Le piège est documenté sur setCurrentUser.
2026-08-11 19:03:58 +02:00
Sylvain Duchesne 3547de202c fix: régler l'identité ne demande pas de session, se connecter oui
init() de @ng-org/web redirige vers le broker en première instruction, dès qu'on
est en tête. L'application appelait donc init() au chargement du module, la page
partait, et ensureIdentity() ne s'exécutait jamais : la barrière n'apparaissait
pas, ?ng-id= restait absent de l'URL remise au broker, et un primo-arrivant se
retrouvait devant la page de connexion sans portefeuille et sans moyen d'en
obtenir un — sans la moindre erreur.

Appeler ensureIdentity() avant init() ne marchait pas non plus : il attend la
session, que seul le callback d'init() résout. Cycle vérifié empiriquement.

La cause n'était ni l'ordre ni la redirection, mais une confusion dans
ensureIdentity() entre deux actes de nature différente — régler qui est
l'utilisateur (barrière, URL, stockage : aucune session) et se connecter
(session requise). settleIdentity() porte le premier ; le wrapper init() du
polyfill l'attend avant de déléguer. L'invariant d'ordre est ainsi porté par la
composition, pas par une consigne d'ordre d'appel que personne ne lit.

Piège trouvé et épinglé en écrivant les tests : init() et ensureIdentity() dans
le même tick montaient deux barrières, l'utilisateur répondait à l'une et
l'autre ne se résolvait jamais. Le règlement en vol est désormais partagé.
2026-08-11 12:52:49 +02:00
Sylvain Duchesne fc3c129bd3 fix: l'identifiant est dans l'URL avant qu'init() ne l'emporte
@ng-org/web fait déjà la redirection vers le broker — même hôte, même forme,
même test de cadre (dist/ngweb.js). Le polyfill n'a donc rien à réimplémenter
là : ce qui lui revient, c'est la seule chose qu'init() ne peut pas faire, à
savoir mettre ?ng-id= dans window.location.href avant qu'il ne le lise.

rememberIdentity() s'exécute désormais sur les trois chemins — identité posée
par l'appelant, venue du stockage, ou saisie à la barrière. Le cas du stockage
était le silencieux : le paramètre restait absent, l'iframe lisait une identité
vide et provisionnait un second espace virtuel, sans erreur.

L'écriture dans le stockage et celle dans la barre d'adresse sont deux try
indépendants : un stockage qui refuse d'écrire ne doit pas emporter avec lui le
paramètre, qui est le seul à franchir la frontière de partition.

Le contrat retire l'obligation « être ouverte via la redirection du broker » :
elle n'a jamais été celle de l'application. Ni broker, ni iframe, ni redirection
n'y sont plus nommés.
2026-08-11 11:59:57 +02:00
Sylvain Duchesne 9c487b59f3 refactor: le résumé du harnais e2e dit polyfill, pas SDK 2026-08-10 17:19:39 +02:00
Sylvain Duchesne 737729c9ce refactor: le paquet s'appelle polyfill, « SDK » désigne celui de NextGraph
Le nom @ng-eventually/sdk entrait en collision avec le SDK de NextGraph, dont
ce paquet est justement un polyfill. Impossible d'écrire « le SDK » sans lever
l'ambiguïté à chaque phrase — et le contrat publié, lu par une application,
était le pire endroit pour laisser traîner ça.

packages/sdk → packages/polyfill, @ng-eventually/sdk → @ng-eventually/polyfill,
contract_sdk-surface → contract_polyfill-surface, e2e/sdk-entry.ts →
e2e/polyfill-entry.ts, docs/sdk-reference.md → docs/polyfill-reference.md.

Les occurrences de « SDK » qui désignent celui de NextGraph restent intactes,
y compris les chemins dans nextgraph-rs (sdk/js/orm, sdk/js/web). Le tri s'est
fait occurrence par occurrence, pas par substitution.

Le contrat énonce désormais son identité en une phrase : « This package is a
polyfill of NextGraph's SDK. »
2026-08-10 17:14:25 +02:00