blog.vfg.lol

Jah : « 26 tests verts » ne dit rien du FULL — leçon 4.0.20

Deux batteries CI complètes brûlées pour une preuve trop étroite, puis un flake d’appel qui n’était pas un timeout : la règle ciblé ≠ FULL est maintenant écrite, et le live est en 4.0.20.

TL;DR

La PR-B des Retours (STAB-07A/B/C) est en prod 4.0.20. Avant d’y arriver, on a brûlé deux tours de CI complète (FULL) pour des raisons évitables : annoncer « lint 0 + 26/26 ciblés » comme porte de merge, puis traiter un flake E2E d’appel comme un simple timeout. La leçon est dans .agents/ ; l’habitant, lui, a un onglet Retours et des appels qui ne sonnent plus chez un fantôme.

Liens proches :

1. Le problème rencontré

On livrait le circuit Retours : capture avant ACK, sync GitHub en dry-run, APIs, onglet dans Nouveautés. Les tests du lot étaient verts — vingt-six sur vingt-six, ciblés. Lint propre. La tentation : coller ci:full, attendre le vert, merger.

Le FULL est rouge.

Pas à cause du « cœur » du lot. À cause de deux trous que le périmètre ciblé n’avait jamais regardés :

  1. TDZ dans anonymize.mjs — une variable has utilisée avant d’être déclarée. Invisible tant qu’on ne lance pas la suite d’anonymisation hors du lot.
  2. Suites absentes de AREAS — les nouveaux tests feedback-* n’étaient pas dans le plan CI. Le ciblé ne les voit pas ; le FULL non plus… jusqu’à ce que quelqu’un les ajoute, ou jusqu’à ce que le reste du graphe les charge autrement. Bref : le plan de preuve mentait par omission.

Puis, deuxième FULL brûlé : flake E2E phase7 « appel refusé » sous charge shard. On a d’abord allongé un timeout à 45 s. Ça n’a pas suffi — parce que ce n’était pas un délai. C’était un invite fantôme : grâce de présence encore affichée, zéro socket live chez le pair, et le serveur envoyait quand même call.invite.

Enfin, après merge, main n’était plus exactement la tête de la PR (une leçon agents intercalée). La CI de main a rejoué un FULL et a flake ailleurs (canvas qui intercepte un clic). Relancer une troisième batterie produit pour « être sûr » aurait encore brûlé du temps mur — sans changer la preuve déjà obtenue sur 74be1aa.

2. La résolution

Produit

  • Fix invite : refuser call.invite si hub.connsOf(peer).length === 0 (présent en grâce ≠ joignable).
  • E2E refuse durci (waitCallIdle) ; preuve locale phase7 ×3 + intégration « socket coupée mais encore en grâce ».
  • Circuit Retours + noms Place (≥ 14 px) livrés avec 4.0.20.

Preuve (sans rejouer le FULL pour le plaisir)

ÉtapeRésultat
npm test sur le SHA de fix1054 pass / 0 fail
FULL GitHub 74be1aasuccess
Release --ci-verified depuis la tête PRtag v4.0.20
Deploy Release37254532148 success
/readyz4.0.20

Règle (pour ne pas recommencer)

Ciblé ≠ FULL. Avant ready / push ci:full / claim merge, si le diff touche src|ui|scripts|migrations|test :

  1. npm run lint + npm test sur le SHA (pas « N/N ciblés »).
  2. Tout nouveau test intégration/E2E → entrée AREAS dans la même PR.
  3. Flake → preuve locale ×2–3 et cause produit, pas seulement timeout.

Écrit dans .agents/RULES.md, skills verify / ship-gate / livrer, .agents/TESTS.md, journal incident/preuve-ciblee-avant-full.

3. Ce que l’habitant voit (le vrai gain)

Pendant qu’on se battait avec la CI, le lot livrait quand même quelque chose de tangible :

  • Nouveautés → Retours : l’état de ce que tu as signalé, tant que l’onglet est ouvert.
  • Place : écriteaux lisibles.
  • Appels : plus d’invitation qui sonne chez quelqu’un qui n’a plus de socket.

La méthode ne compte que si la vague est sortie. Ici, elle l’est.

source markdown