sign-in — comment un utilisateur passe de rien à une identité qui agit. La barrière, l'identifiant qui franchit une frontière de partition par l'URL, et surtout : la redirection vers le broker appartient à @ng-org/web, vérifié dans son bundle. Le polyfill ne la réimplémente pas ; ce qui lui revient est la seule chose qu'init() ne peut pas faire, écrire l'identifiant dans l'URL avant qu'il ne la lise. La feuille centrale dit pourquoi régler l'identité et se connecter sont deux actes séparés : l'un ne demande aucune session, l'autre en exige une, et les confondre bloque dans un sens et casse le partage en silence dans l'autre. Le piège encore vivant — une connexion abandonnée qui reste joignable — est consigné comme tel, non corrigé. e2e-harness — ce que chaque suite juge, et deux choses qu'un agent doit savoir avant de diagnostiquer : le raccourci qui pré-injectait l'identifiant dans l'URL a tenu deux défauts invisibles pendant des mois (un lien de téléchargement vers un 404 que rien ne servait, et une barrière inatteignable pour tout nouvel arrivant) ; et un tuyau devtools qui lâche tue une exécution sans que Playwright n'émette d'événement, ce qui ressemble à un défaut produit et n'en est pas. Le vocabulaire gagne settle, barrier, journey et batch. J'avais aussi introduit « hand-over » pour la redirection : retiré, c'est le mot de NextGraph qui l'emporte.
2.8 KiB
type, summary
| type | summary |
|---|---|
| knowledge | Deciding which identity acts needs no session; connecting does — conflating them deadlocks one way and silently breaks sharing the other |
Settling is not connecting
Two acts of different nature hide behind "sign in":
settle— decide which identity is acting, from the URL, from storage, or by asking at thebarrier, then persist it. Pure DOM and storage. No session required.- connect — restore what was shared with that identity and drain its inbox. Requires a live session.
They must stay apart, and the reason is not tidiness.
Why they cannot be one call
init() hands the page over synchronously when the page is top-level, so nothing placed after it runs. The barrier must therefore appear before init().
But an identity cannot connect before init() either: connecting reaches the application's session thunk, which only init()'s own callback resolves. Await the whole of sign-in before init() and it deadlocks; call it after and the barrier never appears.
The way out is that only half of it has that dependency. Settling is awaited by this package's init() wrapper before it delegates; connecting stays in the published call, awaited where a session exists.
The ordering invariant is carried by the composition, not by a documented call order — which is why an application must call this package's init, not the one it injected. A note telling callers to order two calls correctly is not a mechanism.
The failure this cost
Settling once reached the session, because recording who is acting also fired the connection. In the reference application init() is called from inside the executor that builds the session promise, so the thunk could not answer by construction. It threw, the account lookup answered null, and the connection run abandoned without restoring or draining — having already registered itself as in flight. The published call then joined that dead run and resolved having done nothing.
Symptom: a document shared with someone did not open for them. No error, just unreadable content.
Nothing had changed in the connection logic. What changed was when the identity was recorded. Before the split, nothing recorded an identity during module evaluation: a session existed, the run was healthy, and joining it was harmless.
Hence the shape of the fix — recording who acts and starting to connect are separate operations, and the session-free half only records. To validate: the session thunk must never be called while settling.
The lesson worth keeping
A "session-free" half that calls something which fires a session lookup is not session-free. When splitting on a dependency, check what the side effects of each remaining call reach, not only what the call itself does.