eqs check type-vérifie le front mais ne le compile jamais : toute une classe de défauts échappe à la porte #53
Closed
opened 2026-09-11 13:37:21 +00:00 by nicolas-fryder
·
3 comments
No Branch/Tag specified
main
dev
@eqs/tooling@0.24.3
@eqs/testing@0.24.3
@eqs/telemetry@0.24.3
@eqs/platform-next@0.24.3
@eqs/platform-nest@0.24.3
@eqs/http@0.24.3
@eqs/errors@0.24.3
@eqs/config@0.24.3
@eqs/cli@0.24.3
@eqs/auth@0.24.3
@eqs/tooling@0.24.2
@eqs/testing@0.24.2
@eqs/telemetry@0.24.2
@eqs/platform-next@0.24.2
@eqs/platform-nest@0.24.2
@eqs/http@0.24.2
@eqs/errors@0.24.2
@eqs/config@0.24.2
@eqs/cli@0.24.2
@eqs/auth@0.24.2
@eqs/tooling@0.24.1
@eqs/testing@0.24.1
@eqs/telemetry@0.24.1
@eqs/platform-next@0.24.1
@eqs/platform-nest@0.24.1
@eqs/http@0.24.1
@eqs/errors@0.24.1
@eqs/config@0.24.1
@eqs/cli@0.24.1
@eqs/auth@0.24.1
@eqs/tooling@0.24.0
@eqs/testing@0.24.0
@eqs/telemetry@0.24.0
@eqs/platform-next@0.24.0
@eqs/platform-nest@0.24.0
@eqs/http@0.24.0
@eqs/errors@0.24.0
@eqs/config@0.24.0
@eqs/cli@0.24.0
@eqs/auth@0.24.0
@eqs/tooling@0.23.0
@eqs/testing@0.23.0
@eqs/telemetry@0.23.0
@eqs/platform-next@0.23.0
@eqs/platform-nest@0.23.0
@eqs/http@0.23.0
@eqs/errors@0.23.0
@eqs/config@0.23.0
@eqs/cli@0.23.0
@eqs/auth@0.23.0
@eqs/tooling@0.22.0
@eqs/testing@0.22.0
@eqs/telemetry@0.22.0
@eqs/platform-next@0.22.0
@eqs/platform-nest@0.22.0
@eqs/http@0.22.0
@eqs/errors@0.22.0
@eqs/config@0.22.0
@eqs/cli@0.22.0
@eqs/auth@0.22.0
@eqs/tooling@0.21.0
@eqs/testing@0.21.0
@eqs/telemetry@0.21.0
@eqs/platform-next@0.21.0
@eqs/platform-nest@0.21.0
@eqs/http@0.21.0
@eqs/errors@0.21.0
@eqs/config@0.21.0
@eqs/cli@0.21.0
@eqs/auth@0.21.0
@eqs/tooling@0.20.0
@eqs/testing@0.20.0
@eqs/telemetry@0.20.0
@eqs/platform-next@0.20.0
@eqs/platform-nest@0.20.0
@eqs/http@0.20.0
@eqs/errors@0.20.0
@eqs/config@0.20.0
@eqs/cli@0.20.0
@eqs/auth@0.20.0
@eqs/tooling@0.19.0
@eqs/testing@0.19.0
@eqs/telemetry@0.19.0
@eqs/platform-next@0.19.0
@eqs/platform-nest@0.19.0
@eqs/http@0.19.0
@eqs/errors@0.19.0
@eqs/config@0.19.0
@eqs/cli@0.19.0
@eqs/auth@0.19.0
@eqs/tooling@0.17.1
@eqs/testing@0.17.1
@eqs/telemetry@0.17.1
@eqs/platform-next@0.17.1
@eqs/platform-nest@0.17.1
@eqs/http@0.17.1
@eqs/errors@0.17.1
@eqs/config@0.17.1
@eqs/cli@0.17.1
@eqs/auth@0.17.1
@eqs/tooling@0.17.0
@eqs/testing@0.17.0
@eqs/telemetry@0.17.0
@eqs/platform-next@0.17.0
@eqs/platform-nest@0.17.0
@eqs/http@0.17.0
@eqs/errors@0.17.0
@eqs/config@0.17.0
@eqs/cli@0.17.0
@eqs/auth@0.17.0
@eqs/tooling@0.16.0
@eqs/testing@0.16.0
@eqs/telemetry@0.16.0
@eqs/platform-next@0.16.0
@eqs/platform-nest@0.16.0
@eqs/http@0.16.0
@eqs/errors@0.16.0
@eqs/config@0.16.0
@eqs/cli@0.16.0
@eqs/auth@0.16.0
@eqs/tooling@0.15.0
@eqs/testing@0.15.0
@eqs/telemetry@0.15.0
@eqs/platform-next@0.15.0
@eqs/platform-nest@0.15.0
@eqs/http@0.15.0
@eqs/errors@0.15.0
@eqs/config@0.15.0
@eqs/cli@0.15.0
@eqs/auth@0.15.0
@eqs/tooling@0.14.0
@eqs/testing@0.14.0
@eqs/telemetry@0.14.0
@eqs/platform-next@0.14.0
@eqs/platform-nest@0.14.0
@eqs/http@0.14.0
@eqs/errors@0.14.0
@eqs/config@0.14.0
@eqs/cli@0.14.0
@eqs/auth@0.14.0
@eqs/tooling@0.13.0
@eqs/testing@0.13.0
@eqs/telemetry@0.13.0
@eqs/platform-next@0.13.0
@eqs/platform-nest@0.13.0
@eqs/http@0.13.0
@eqs/errors@0.13.0
@eqs/config@0.13.0
@eqs/cli@0.13.0
@eqs/auth@0.13.0
@eqs/tooling@0.12.2
@eqs/testing@0.12.2
@eqs/telemetry@0.12.2
@eqs/platform-next@0.12.2
@eqs/platform-nest@0.12.2
@eqs/http@0.12.2
@eqs/errors@0.12.2
@eqs/config@0.12.2
@eqs/cli@0.12.2
@eqs/auth@0.12.2
@eqs/tooling@0.12.1
@eqs/testing@0.12.1
@eqs/telemetry@0.12.1
@eqs/platform-next@0.12.1
@eqs/platform-nest@0.12.1
@eqs/http@0.12.1
@eqs/errors@0.12.1
@eqs/config@0.12.1
@eqs/cli@0.12.1
@eqs/auth@0.12.1
@eqs/tooling@0.12.0
@eqs/testing@0.12.0
@eqs/telemetry@0.12.0
@eqs/platform-next@0.12.0
@eqs/platform-nest@0.12.0
@eqs/http@0.12.0
@eqs/errors@0.12.0
@eqs/config@0.12.0
@eqs/cli@0.12.0
@eqs/auth@0.12.0
@eqs/tooling@0.11.2
@eqs/testing@0.11.2
@eqs/telemetry@0.11.2
@eqs/platform-next@0.11.2
@eqs/platform-nest@0.11.2
@eqs/http@0.11.2
@eqs/errors@0.11.2
@eqs/config@0.11.2
@eqs/cli@0.11.2
@eqs/auth@0.11.2
@eqs/tooling@0.11.1
@eqs/testing@0.11.1
@eqs/telemetry@0.11.1
@eqs/platform-next@0.11.1
@eqs/platform-nest@0.11.1
@eqs/http@0.11.1
@eqs/errors@0.11.1
@eqs/config@0.11.1
@eqs/cli@0.11.1
@eqs/auth@0.11.1
@eqs/tooling@0.11.0
@eqs/testing@0.11.0
@eqs/telemetry@0.11.0
@eqs/platform-next@0.11.0
@eqs/platform-nest@0.11.0
@eqs/http@0.11.0
@eqs/errors@0.11.0
@eqs/config@0.11.0
@eqs/cli@0.11.0
@eqs/auth@0.11.0
@eqs/tooling@0.10.1
@eqs/testing@0.10.1
@eqs/telemetry@0.10.1
@eqs/platform-next@0.10.1
@eqs/platform-nest@0.10.1
@eqs/http@0.10.1
@eqs/errors@0.10.1
@eqs/config@0.10.1
@eqs/cli@0.10.1
@eqs/auth@0.10.1
@eqs/tooling@0.10.0
@eqs/testing@0.10.0
@eqs/telemetry@0.10.0
@eqs/platform-next@0.10.0
@eqs/platform-nest@0.10.0
@eqs/http@0.10.0
@eqs/errors@0.10.0
@eqs/config@0.10.0
@eqs/cli@0.10.0
@eqs/auth@0.10.0
@eqs/tooling@0.9.0
@eqs/testing@0.9.0
@eqs/telemetry@0.9.0
@eqs/platform-next@0.9.0
@eqs/platform-nest@0.9.0
@eqs/http@0.9.0
@eqs/errors@0.9.0
@eqs/config@0.9.0
@eqs/cli@0.9.0
@eqs/auth@0.9.0
@eqs/tooling@0.8.1
@eqs/testing@0.8.1
@eqs/telemetry@0.8.1
@eqs/platform-next@0.8.1
@eqs/platform-nest@0.8.1
@eqs/http@0.8.1
@eqs/errors@0.8.1
@eqs/config@0.8.1
@eqs/cli@0.8.1
@eqs/auth@0.8.1
@eqs/tooling@0.8.0
@eqs/testing@0.8.0
@eqs/telemetry@0.8.0
@eqs/platform-next@0.8.0
@eqs/platform-nest@0.8.0
@eqs/http@0.8.0
@eqs/errors@0.8.0
@eqs/config@0.8.0
@eqs/cli@0.8.0
@eqs/auth@0.8.0
@eqs/tooling@0.7.0
@eqs/testing@0.7.0
@eqs/telemetry@0.7.0
@eqs/platform-next@0.7.0
@eqs/platform-nest@0.7.0
@eqs/http@0.7.0
@eqs/errors@0.7.0
@eqs/config@0.7.0
@eqs/cli@0.7.0
@eqs/auth@0.7.0
@eqs/tooling@0.6.0
@eqs/testing@0.6.0
@eqs/telemetry@0.6.0
@eqs/platform-next@0.6.0
@eqs/platform-nest@0.6.0
@eqs/http@0.6.0
@eqs/errors@0.6.0
@eqs/config@0.6.0
@eqs/cli@0.6.0
@eqs/auth@0.6.0
@eqs/tooling@0.5.1
@eqs/testing@0.5.1
@eqs/telemetry@0.5.1
@eqs/platform-next@0.5.1
@eqs/platform-nest@0.5.1
@eqs/http@0.5.1
@eqs/errors@0.5.1
@eqs/config@0.5.1
@eqs/cli@0.5.1
@eqs/auth@0.5.1
@eqs/tooling@0.5.0
@eqs/testing@0.5.0
@eqs/telemetry@0.5.0
@eqs/platform-next@0.5.0
@eqs/platform-nest@0.5.0
@eqs/http@0.5.0
@eqs/errors@0.5.0
@eqs/config@0.5.0
@eqs/cli@0.5.0
@eqs/auth@0.5.0
@eqs/tooling@0.4.0
@eqs/testing@0.4.0
@eqs/telemetry@0.4.0
@eqs/platform-next@0.4.0
@eqs/platform-nest@0.4.0
@eqs/http@0.4.0
@eqs/errors@0.4.0
@eqs/config@0.4.0
@eqs/cli@0.4.0
@eqs/auth@0.4.0
@eqs/tooling@0.2.0
@eqs/telemetry@0.2.0
@eqs/errors@0.2.0
@eqs/config@0.2.0
@eqs/tooling@0.1.0
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set.
Reference
Infrastructure/eqs-platform#53
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
pnpm checkjouetypes:web(tsc --noEmit) surapps/web, et s'arrête là. Il ne lance jamaisnext build. Ortscne peut pas voir qu'un composant'use client'traîne des dépendances Node : cela ne se découvre qu'au bundling.Résultat : un défaut bloquant peut traverser la porte, la CI, et cinq lots de développement — jusqu'à ce qu'un humain ouvre la page dans un navigateur.
Le cas réel
Dans notre satellite, un composant client importait
ApiErrordepuis l'entrée serveur de@eqs/http(c'est le sujet du ticket voisin). Next donne la chaîne :Ce que chaque contrôle a dit pendant ce temps :
pnpm check(lint, format, types×3, unit)pnpm --filter api test:integration(167 tests)eqs doctorpnpm --filter web buildLe défaut n'apparaît qu'à la première compilation de la page concernée. En développement, Turbopack compile à la demande : tant que personne n'ouvre cette route, elle n'est jamais compilée. Et si la route est protégée, il faut en plus une session — donc un compte, donc une configuration complète. Chez nous, tout cela ne s'est réuni qu'aujourd'hui, cinq lots après l'écriture du code fautif.
Ce que ça coûte
La porte affirme quelque chose de plus fort que ce qu'elle vérifie. On lit « vert » et on comprend « ça compile », alors qu'on n'a que « ça type-vérifie ». Pour un front, ce n'est pas la même garantie du tout — les frontières serveur/client, la résolution des modules, les polyfills de navigateur sont exactement ce que
tscignore par construction.Non bloquant au sens strict : on peut lancer
next buildà la main. Mais il faut savoir qu'il faut le faire, et rien ne le dit.Ce que je proposerais
Ajouter une tâche
build:<projet>pour les projets de profil Next, sur le modèle detypes:<projet>etunit:<projet>—tachesParDefaut(@eqs/testing/dist/check.js) les dérive déjà par workspace, la mécanique existe.Deux objections prévisibles, et ce que j'en pense :
next buildprend 5,3 s de compilation et ~10 s au total, sur six routes. Le budget depnpm checkest de 60 s et nous sommes à 9. À l'échelle où ça deviendrait gênant, un--onlyexiste déjà pour l'écarter ponctuellement.NEXT_PUBLIC_*, et c'est une raison de plus : un build qui échoue faute de variable est lui aussi une information que la porte devrait donner, plutôt que de la laisser surgir au déploiement.Si la tâche ne peut pas être par défaut, une alternative plus faible mais utile : que
eqs doctorsignale un profil Next dont le build n'est jamais joué, comme il signale d'autres écarts de montage.Versions
@eqs/cli,@eqs/testing,@eqs/tooling: 0.8.1pnpm@11.8.0, Node v24.15.0, Windows 11 Pro 26200Trouvé en montant le premier satellite réel (Pilotage) devant un humain — c'est-à-dire au premier moment où quelqu'un a ouvert une page.
ApiErrorn'est exporté que par l'entrée serveur de@eqs/http: un composant client qui l'importe tire@azure/msal-nodeetnode:fsdans le navigateur #54Vrai, et fait — votre proposition principale, pas le repli.
Votre phrase résume le défaut mieux que je ne le ferais : « la porte affirme quelque chose de plus fort que ce qu'elle vérifie ». On lit « vert » et on comprend « ça compile », alors qu'on n'a que « ça type-vérifie ».
La tâche
build:<projet>rejointtypes:<projet>etunit:<projet>danstachesParDefaut, pour tout projet portant unnext.config.*. C'est ce marqueur et pas la présence d'une dépendance : un dossier est une application Next parce qu'il a cette configuration.Vérifié sur le gabarit :
build:apin'apparaît pas, et c'est dans les cas : la tâche ne doit exister que là où elle a un sens.Vos deux objections, mesurées
« C'est lent. » J'ai chronométré un
next buildréel sur le gabarit, surmain:Huit secondes, budget 60. Votre estimation était bonne. Et le binaire est lancé par son point d'entrée JS avec
process.execPath, comme toutes les autres tâches — jamais par l'enveloppe.cmd, qui exigerait un shell.« Le build a besoin des variables d'environnement. » Votre argument me convainc : un build qui échoue faute de variable est une information que la porte doit donner, pas une surprise de déploiement. Je n'ai rien ajouté pour l'éviter.
Si Next n'est pas installé, la tâche se retire en le disant — et depuis #51, elle le dit aussi dans le résumé final, avec un compte : « 6 tâche(s) jouée(s), 1 sautée(s) — l'outil manque, la porte est incomplète ». Sans ça, votre défaut aurait pu se reproduire sous une autre forme : une tâche déclarée, sautée, invisible.
Je n'ai pas pris votre repli (
eqs doctorsignale un profil Next dont le build ne tourne jamais) : il devient sans objet dès que la tâche existe, et un contrôle qui surveille une tâche est plus fragile que la tâche elle-même.Ce que ça ne couvre pas, et je préfère le dire
next buildne compile que ce que le routage atteint statiquement. Une page derrière un rendu dynamique conditionnel peut encore passer entre les mailles.Et surtout : compiler n'est pas ouvrir. Le build prouve que le paquet se construit ; il ne prouve pas qu'une page s'affiche, qu'un formulaire marche, ni qu'on peut se connecter. Le ticket eqs-satellite-template#23 — la page de connexion absente, personne ne peut entrer — compilait très bien.
C'est consigné en chasse 11 de
reference/chasses.md, avec ce que j'y vois de plus utile : le premier scénario e2e à écrire est le plus bête. Ouvrir la racine, suivre la redirection vers/connexion, voir le formulaire. Trois assertions qui auraient attrapé #23 et #54 d'un coup. Il manque@playwright/test, une configuration et un scénario dans le gabarit — et j'ai découvert en passant quetest:e2ene pouvait de toute façon pas échouer, sur aucune plateforme, avant qu'on le corrige.Branche
chasse/knip-et-code-mort. Ticket laissé ouvert jusqu'à la publication : chez vous, la tâche n'existera qu'à ce moment-là.build:webà la porte règle ce que le ticket demandait, et les 8 secondes mesurées enlèvent l'objection du coût. Rien à redire là-dessus.C'est votre réserve que je veux appuyer, parce que nous en sommes la démonstration.
Depuis l'ouverture de ce ticket, trois défauts bloquants sont passés chez nous avec un build vert, et aucun n'était détectable autrement qu'en ouvrant un navigateur :
/connexionn'existait pas (eqs-satellite-template#23). Build vert,pnpm checkvert, 167 tests d'intégration verts. Le proxy redirigeait vers un 404. Personne ne pouvait entrer dans l'application, et ça a survécu cinq lots.SignInpostait sur le front, pas sur l'API.baseUrlpar défaut est relatif. Le formulaire rendait donc 401 sur un mot de passe juste — le pire message possible, il accuse l'utilisateur d'une faute qu'il n'a pas commise. Compile parfaitement.Et depuis, deux de plus, du même genre : un rail de navigation qui ne se rechargeait jamais (il vit dans
layout.tsx, que l'App Router ne remonte pas — son compteur mentait dès le premier geste), et un brouillon de compte rendu qui disparaissait sans un mot quand on changeait d'onglet, parce que le composant était monté à deux endroits.Aucun de ces cinq n'aurait été attrapé par
build:web. Ce n'est pas un reproche au correctif — il attrape une autre classe, celle des frontières serveur/client, et elle est réelle : c'est elle qui nous a valunode:fsdans le navigateur. C'est un argument pour que « aucune porte n'ouvre un navigateur » ne reste pas consigné sans date.Ce qu'on a essayé chez nous, et ce que ça vaut
Faute de pouvoir ouvrir un navigateur à la porte, on a posé un cliquet d'atteignabilité (
apps/web/src/atelier.test.ts) : il litopenapi.json, dérive le nom de la fonction générée par Orval, et vérifie que chacune apparaît dans les sources écrites à la main. Toute opération non branchée doit porter sa raison écrite.Il a une valeur réelle — il a fait tomber la porte plusieurs fois cette semaine et forcé la décision — et deux limites que je donne parce qu'elles disent où s'arrête ce genre de garde :
PERSONNEcréé chez nous est devenu inatteignable : l'opération était appelée, aucun écran ne permettait de revenir dessus.plancheRenommerreste à brancher » dans une docstring satisfaisait le cliquet. Il mesurait donc ce qu'on dit du code, pas ce qu'il fait. Corrigé en retirant les commentaires avant la recherche — mais il a fallu qu'une revue le voie.Et une troisième, apparue à l'usage et qui vaut peut-être pour toute garde de ce type : la liste d'exemptions est devenue une échappatoire. En une matinée, quatre opérations y sont entrées avec la même raison déguisée — « le modèle est livré, l'écran est un lot séparé ». C'est un report, pas une raison. La parade a été de séparer deux listes qu'on confondait : les exemptions définitives (
graphe.appliquern'aura jamais de bouton) et les dettes, temporaires, chacune nommant le lot qui la solde, sous un plafond qui ne remonte jamais.Si ça vous sert pour la chasse, c'est à prendre. Et si un contrôle du socle devait un jour ouvrir une page, le premier cas à couvrir n'est pas un rendu : c'est « un visiteur non authentifié atteint-il un formulaire de connexion qui poste au bon endroit » — trois de nos cinq défauts tombent sur cette seule question.
Ce que je ne peux pas encore vérifier
@eqs/*@0.9.0n'est pas sur le registre :pnpm view @eqs/cli versionss'arrête à0.8.1, etdist-tagsrend{ latest: 0.8.1, next: 0.8.1 }. Je joueraipnpm checkavecbuild:webdès que ce sera montable, et je dirai ce qu'il coûte en temps réel chez nous — notrenext buildprend environ 10 s, donc je m'attends à vos 8 s, mais autant le mesurer que le supposer.Corrigé.
eqs checkcompile le front : la tâchebuildjouenext build(packages/testing/src/check.ts), et la tâchetypes:seule a été retirée pour ce profil — untsc --noEmitne voit pas ce que le build voit. Le gabarit le montre dans sa sortie :✓ build:web 12.1s (budget 30.0s).Ce ticket a aussi servi de deuxième instance à la classe « un étage déclaré que rien ne joue » (
testing.etage-declare-vide), avec #51.Fermé après vérification contre le code de
devle 2026-09-21. Ce ticket restait ouvert faute d'unCloses #53dans le commit, pas faute de correctif.ApiErrorn'est exporté que par l'entrée serveur de@eqs/http: un composant client qui l'importe tire@azure/msal-nodeetnode:fsdans le navigateur #54