doctrine(bdd-testing): remplacer le caveat poll par la règle « ne jamais poller »

L'ancien caveat_poll-broker-reads érigeait à tort le POLLING en pratique de test.
Remarque utilisateur : le polling est un anti-pattern dans le contexte NextGraph
(par abonnement). Remplacé par rule_no-broker-polling : attendre le push réactif /
la barrière du 1er State ; ne JAMAIS re-interroger le broker en boucle. Fallback
pragmatique admis : un intervalle court qui OBSERVE l'état réactif déjà mis à jour
(pas une re-lecture broker) — au plus près de l'utilisateur qui attend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-09 13:20:29 +02:00
parent 517045c257
commit 4c80ada3de
2 changed files with 52 additions and 36 deletions
@@ -1,36 +0,0 @@
---
type: caveat
summary: Les lectures @data/@multibrowser contre le broker réel convergent avec un lag de sync (~s) — asserter en POLLING borné, jamais en lecture unique ; une lecture one-shot course la fenêtre de sync/réactivité et flake. Vaut pour les lectures à froid (reconnexion) ET la propagation réactive (compteur cross-navigateur).
last_checked: 2026-07-08
---
# Asserter les lectures broker en polling, pas en one-shot
Contre le **broker réel** (@data, @multibrowser), une donnée écrite n'est pas
lisible **instantanément** par un lecteur : il y a un **lag de sync** (le CRDT du
doc doit se propager/s'ouvrir avant qu'une lecture ancrée le rende). Mesuré : une
donnée fraîche se lit typiquement en **~1 s**, parfois après la 1ʳᵉ tentative.
**Piège** : une assertion qui lit **une seule fois**, tout de suite, **course
cette fenêtre** et échoue de façon **flaky** — alors que le comportement applicatif
est correct (l'UI réelle est **réactive** : `doc_subscribe` fait converger l'écran
en quelques secondes). Un test qui lit one-shot mesure le lag, pas le bug.
**Règle** : asserter en **POLLING borné** (re-lire en boucle jusqu'à ~10-15 s max)
la condition attendue. C'est ce que fait déjà la matérialisation côté steps ; toute
NOUVELLE assertion de lecture doit suivre le même pattern.
Deux familles concernées, toutes deux vérifiées :
- **Lecture à froid / reconnexion** — une session verifier fraîche (page fraîche,
nouveau login) relit ses propres docs après ouverture de repo ; la 1ʳᵉ lecture
peut rendre 0, la suivante les données. Voir `event/reconnexion-meme-identite`
(les 3 `Then` de la page fraîche pollent) et `event/steps/data/reconnexion.steps.ts`.
- **Propagation réactive cross-navigateur** — un compteur écrit par le propriétaire
se propage au `doc_subscribe` d'un autre navigateur ; asserter « passe à {int}
sans reload » en attendant la convergence. Voir le scénario réactif de
`event/e2e-multibrowser.feature` (POV A **et** B).
Ce caveat porte sur **comment écrire les assertions** contre le broker — pas sur
les internes NextGraph (lag/sync/ouverture de repo), qui vivent dans le repo
`@ng-eventually/client`. Voir aussi [[caveat_wallet-bloat-hang]] (autre source de
flakiness @data : le profil gonflé qui fait hanger les requêtes ancrées).
@@ -0,0 +1,52 @@
---
type: rule
summary: Ne JAMAIS poller le broker (re-lire en boucle « c'est là ? »). NextGraph est par abonnement — la donnée arrive par PUSH, et le 1er `State` d'un `doc_subscribe` est la barrière de sync déterministe (après lui : présence garantie / absence définitive). Tests ET app attendent le push / l'état réactif settlé, jamais une boucle de re-lecture broker.
last_checked: 2026-07-09
---
# Ne jamais poller le broker — attendre l'abonnement
NextGraph est **par abonnement (réactif)**. Une lecture n'est PAS « interroge en
boucle jusqu'à ce que ça apparaisse » ; c'est « abonne-toi, réagis au push ». Le
**1er `State`** d'un `doc_subscribe` marque la fin de la synchronisation initiale
(barrière synchrone) : après lui, la **présence** d'une donnée est **garantie** et
l'**absence** est **définitive**. Contrat vérifié empiriquement côté SDK
(`@ng-eventually/client`, test e2e « CONTRAT 3 »).
## L'anti-pattern à bannir
```
for (i = 0; i < N; i++) { if (await authParticipationCount(...) === X) break; sleep(500); }
```
Toute boucle qui **re-interroge le broker** (`authParticipationCount`,
`listMyEntityDocs`, `sparql_query` répétés) pour « attendre » une donnée est
proscrite : elle masque le vrai mécanisme, fragilise le test (timeout deviné), et
contredit frontalement le modèle NextGraph. C'est la remarque qui a fait supprimer
l'ancien caveat qui, à tort, érigeait le polling en pratique.
## Ce qu'il faut faire
Attendre le **push réactif**. En pratique (app ET test) : l'état réactif
(`AD().*` alimenté par `subscribeDoc` dans le contexte de données) se met à jour
**au push**. On attend que CET état reflète l'attendu — on **observe l'état réactif
settlé**, on ne ré-émet PAS de lecture broker. Le mécanisme de données est
l'abonnement ; l'attente ne fait qu'**observer le résultat réactif**.
- App : l'écran est déjà réactif (`subscribeDoc` → re-render au push) — pas de poll
applicatif, pas de spinner piloté par timeout deviné (si un état d'attente est
voulu, il vient de la barrière d'abonnement native, pas d'un signal ajouté).
- Test : **un helper qui attend le push/la barrière de façon fiable est bienvenu**
(fiabilise sans fragiliser). Ce qui est banni, c'est la **boucle de re-lecture**,
pas l'attente d'un signal.
- **Fallback pragmatique** : si attendre strictement le push/signal s'avère fragile
d'une manière ou d'une autre, un **intervalle court** (`setInterval` / re-check
rapproché) qui **observe l'état réactif DÉJÀ mis à jour** (l'état local alimenté
par l'abonnement — PAS une re-lecture broker) est acceptable : c'est au plus près
de ce que vit l'utilisateur, qui **attend** simplement que l'écran (réactif) se
mette à jour. La ligne rouge est invariante : **ne jamais re-interroger le broker
en boucle** ; observer l'état réactif settlé, oui.
Voir aussi [[caveat_wallet-bloat-hang]] (autre source de flakiness @data,
orthogonale). Le mécanisme non-polling côté lib (`open-repo` : subscribe + attendre
le 1er State + lire) vit dans le repo `@ng-eventually/client`, pas ici.