Exécution du script check
Cette compétence est la source de vérité unique pour exécuter le script check du package.json dans les flux de travail lt-dev. Tout relecteur, rebaseur ou orchestrateur qui doit garantir l'exécutabilité du projet doit suivre cette procédure textuellement — dupliquer les règles entre les agents entraîne une dérive.
Objectif : Une exécution
checkvraiment verte (code de sortie 0) est une condition préalable non négociable pour que toute revue ou tout rebasage soit considéré comme complet. Le seul résidu acceptable est une vulnérabilité de dépendance amont pour laquelle l'échelle d'escalade complète a été épuisée.
Quand utiliser cette compétence
| Appelant | Phase | Déclencheur | Mode |
|---|---|---|---|
/lt-dev:review | Phase 1.5 | Avant de lancer tout relecteur spécialisé | Consultatif |
lt-dev:code-reviewer | Phase 1.5 | Avant la revue en un seul passage (ignorer si l'orchestrateur l'a déjà exécuté) | Consultatif |
lt-dev:test-reviewer | (briefing d'entrée) | Honore la sémantique de saut définie ici | Consultatif |
/lt-dev:take-ticket | ÉTAPE 8 | Après que tous les piliers de test sont verts | Bloquant |
/lt-dev:git:ship | ÉTAPE 1, ÉTAPE 4b | Prévol, et après la revérification post-rebasage | Bloquant |
/lt-dev:ticket-cycle | via take-ticket + git:ship | Cycle complet | Bloquant |
lt-dev:branch-rebaser | Phase 6.5 | Après lint/format, avant les tests | Bloquant |
Modes d'appel
La procédure est identique dans les deux modes. Seul ce qui se passe avec une erreur non résolue diffère, et cette différence est tout l'intérêt de la distinction :
- Consultatif — le travail de l'appelant est de signaler. Un
checkrouge est signalé comme un résultat critique et le flux continue, car l'utilisateur souhaite toujours le reste de la revue. - Bloquant — le travail de l'appelant est de livrer. Un
checkrouge arrête le flux. Rien n'est poussé, aucune MR/PR n'est créée, rien n'est fusionné.
Chaque chemin qui mène à une MR/PR ou à une fusion s'exécute en mode Bloquant. Un check rouge atteignant la branche de base coûte cher à toute l'équipe : l'IC devient rouge pour tout le monde, le prochain take-ticket bifurque à partir d'un code cassé, et la personne qui finit par le corriger n'a aucune idée du changement qui l'a causé. C'est pourquoi l'échelle de l'étape 3 n'a pas de limite d'itération et pourquoi la liste des résidus de l'étape 5 est aussi étroite.
Procédure
Étape 1 — Découvrir les scripts check
Utilisez le script d'assistance dédié (situé dans le plugin lt-dev) :
bash "${CLAUDE_PLUGIN_ROOT}/scripts/discover-check-scripts.sh"
La sortie est au format TSV, un projet par ligne :
<chemin package.json>\t<script check>\t<includes_tests:no|unit+api|yes>\t<gestionnaire de paquets>
Valeurs includes_tests — les appelants décident quoi faire avec chacune, la découverte elle-même ne prescrit aucun comportement :
| Valeur | Signification | Couverture Playwright |
|---|---|---|
no | Aucun exécuteur de test détecté dans la chaîne de check. | Non couvert. |
unit+api | La chaîne check délègue au wrapper lt check.mjs, qui exécute les étapes de test Unité (app) + API (api) et saute délibérément Playwright (Playwright s'exécute via lt dev test / CI — voir scripts/check.mjs). | Non couvert — Playwright doit toujours être exécuté séparément si nécessaire. |
yes | Correspondance directe avec test, vitest, jest ou playwright dans la chaîne de check (transitivement, un niveau de résolution composite). La portée est celle définie par le script. | Inconnu — les appelants doivent vérifier localement si la couverture Playwright est requise. |
Le script d'assistance :
- Utilise
git ls-files "package.json" "**/package.json"pour respecter.gitignoreet ignorernode_modules - Analyse le JSON via
jq→node -e→ chaîne de repligrep+sed(fonctionne même sansjqinstallé) - Résout les scripts composites à un seul niveau (
"check": "pnpm run ci"→ rechercheci) - Détecte le gestionnaire de paquets par projet via le fichier de verrouillage (
pnpm-lock.yaml→pnpm, etc. ; par défautpnpm)
Si le script d'assistance ne renvoie rien → aucun projet ne définit check → ignorer le reste de cette compétence.
Étape 1a — Aucune suggestion de correctif d'oxlint
oxlint --fix-suggestions applique les correctifs qu'oxlint marque lui-même comme potentiellement modificateurs de comportement : sa suggestion no-console supprime l'appel. --fix-dangerously va plus loin. Les modèles actuels ne passent ni l'un ni l'autre, mais les projets issus de modèles plus anciens le font toujours, dans le wrapper de check, lint:fix, lint-staged ou le propre scripts/check.mjs d'un projet. lt fullstack update les supprime à la racine de l'espace de travail et dans l'application (fichiers suivis et propres uniquement) ; le projet API conserve ses propres fichiers, donc une occurrence y est supprimée manuellement ou en prenant le fichier actuel de nest-server-starter. Observé dans nest-server (2026-09, claude-code PR #10) : un hook de commit a transformé un console.log en un corps de fonction vide et a tout de même signalé "réussi". Avant l'étape 2, recherchez une utilisation de l'un ou l'autre indicateur ; une mention dans un commentaire ne compte pas :
git grep -n -E "(^|[[:space:]\"'])\/-\/-(suggestions|dangerously)" -- '*package.json' '*.lintstagedrc*' '*lint-staged.config.*' '*scripts/check.mjs' \
| grep -v -E '^[^:]+:[0-9]+:[[:space:]]*(\/\/|\/?\*|#)'
Le second filtre supprime les lignes de commentaire : le wrapper actuel de nest-server-starter explique dans un commentaire pourquoi il ne passe que --fix simple.
En cas d'occurrence, le check s'exécute sans correction automatique :
- Exécutez le wrapper lt comme sa porte en lecture seule :
<pm> run check --no-fix(npm :npm run check -- --no-fix). Chaque wrapper qui passe encore l'un des indicateurs connaît déjà--no-fix. Lorsque la propre chaîne d'un projet appellelint:fix, ou que son proprescripts/check.mjsest l'une des occurrences, exécutez les étapes de ce projet aveclintau lieu delint:fix. - Corrigez les résultats de lint à la main ou avec le simple
oxlint --fix; formatez avec le formateur du projet comme d'habitude. - Nommez chaque occurrence (
fichier:ligne) dans le rapport de l'étape 8 avec son remède :lt fullstack updatepour la racine et l'application, suppression manuelle de l'indicateur (ou le fichier actuel de nest-server-starter) pour le projet API.
Étape 2 — Exécuter check par projet
Pour chaque projet découvert, cd dans son répertoire et exécutez le script check avec le gestionnaire de paquets détecté :
cd "$(dirname <chemin package.json>)"
<gestionnaire de paquets> run check
Capturez stdout, stderr et le code de sortie. Suivez un compteur d'itérations commençant à 1.
Étape 3 — Boucle de correction automatique (itérer jusqu'à ce que ce soit vraiment vert)
Si le check d'un projet se termine avec un code non nul :
- Analysez toutes les erreurs de l'exécution en cours (vérification de type, lint, build, imports manquants, incompatibilités de type, symboles inutilisés, résultats d'audit, etc.)
- Corrigez chaque erreur à la cause racine via
Read+Edit. Les erreurs préexistantes sont également corrigées — qu'une erreur provienne de ce diff ou qu'elle soit déjà là ne fait aucune différence pour l'exécutabilité du projet, et uncheckrouge bloque la personne suivante exactement aussi fort dans les deux cas. Les limites de portée ne s'appliquent pas à l'exécutabilité. La seule question qui décide quoi que ce soit ici est "cela peut-il être corrigé ?" et tant que la réponse est oui, cela est corrigé. - Ré-exécutez
checkà partir de zéro. Ne faites jamais confiance à un état partiel. - Continuez d'itérer jusqu'à ce que l'une de ces conditions terminales soit atteinte :
- (a) VERT —
checkse termine avec 0 → terminé pour ce projet - (b) BLOQUÉ — une itération complète n'a produit aucune réduction nette du nombre d'erreurs
- (a) VERT —
- Aucune limite d'itération stricte. Tant que chaque itération réduit strictement le nombre d'erreurs, continuez. L'objectif est le vrai vert.
Une exception étroite, et elle concerne la collision plutôt que la portée. Le point 2 reste valable : une erreur préexistante est corrigée, car un check rouge bloque la personne suivante quel que soit son auteur. Ce que le point 2 ne couvre pas, c'est une erreur à l'intérieur d'un travail non validé sur lequel une autre session en direct est en train de travailler — remanier cela n'est pas une question de portée, c'est deux sessions modifiant un seul fichier, et le pair revient à une refactorisation réécrite ayant perdu plus de temps que le correctif n'en a économisé. Lorsque l'arbre est sale et que bash "${CLAUDE_PLUGIN_ROOT}/scripts/change-provenance.sh" attribue le chemin défaillant à un pair en direct : corrigez-le s'il est trivial et autonome (un import manquant, une violation de format, un console.log errant), et sinon signalez-le comme un bloqueur en nommant l'auteur, plus un SOLVED portant le diagnostic afin qu'il puisse corriger son propre travail en une étape. Personne en direct, ou rien d'attribuable : le point 2 s'applique inchangé.
BLOQUÉ est un signal pour changer d'approche, pas une permission de s'arrêter. Le compteur cesse de baisser lorsque le même correctif est réessayé, donc la prochaine itération doit attaquer l'erreur différemment : lisez le fichier défaillant en entier plutôt que de patcher autour de la ligne signalée, vérifiez si deux erreurs partagent une cause, regardez comment un module frère fonctionnant résout la même chose, exécutez l'étape défaillante seule pour voir la sortie que l'exécution agrégée a avalée, vérifiez s'il s'agit de l'un des dangers connus des étapes 6.5 à 6.8. Ce n'est que lorsqu'une approche vraiment différente a été essayée et que le compteur reste stable que la classification résiduelle de l'étape 5 s'applique — et en mode Bloquant, tout, à l'exception d'une vulnérabilité de dépendance à l'échelle épuisée, arrête toujours le flux.
Étape 4 — Résultats d'audit : échelle d'escalade obligatoire des correctifs
Lorsque pnpm audit / npm audit / yarn audit (invoqué par check) signale une vulnérabilité, épuisez cette échelle avant de classer le résultat comme Accepté. Ré-exécutez check après chaque étape.
| # | Étape | Commande (pnpm / npm / yarn) |
|---|---|---|
| 1 | Mise à jour vers la dernière version compatible | pnpm update <pkg> / npm update <pkg> / yarn upgrade <pkg> |
| 2 | Correction automatique | pnpm audit --fix / npm audit fix / yarn audit --fix |
| 3 | Forcer la mise à jour majeure semver | pnpm audit --fix --force / npm audit fix --force |
| 4 | Augmenter la dépendance directe à la version majeure suivante si l'avis répertorie le correctif là | pnpm add <pkg>@<major> / npm install <pkg>@<major> / yarn add <pkg>@<major> |
| 5 | Forcer une version de dépendance transitive | Bloc pnpm.overrides / resolutions (yarn) / overrides (npm) dans package.json, puis réinstaller |
| 6 | Augmenter le CONSOMMATEUR, pas le paquet vulnérable | la dépendance directe qui le tire, et le framework lt si le framework l'épingle (@lenne.tech/nest-server / @lenne.tech/nuxt-extensions) — un /lt-dev:maintenance:maintain complet est la forme approfondie |
| 7 | Patcher le consommateur — uniquement dans les conditions ci-dessous | pnpm patch <pkg> → patchedDependencies |
| 8 | Remplacer le paquet | Passez à une alternative maintenue si abandonné |
L'étape 6 est l'échelon qui est sauté, et c'est celui qui fonctionne le plus souvent. Les étapes 1 à 5 poussent toutes le paquet vulnérable vers l'avant. Lorsque cela échoue, la raison n'est généralement pas du tout le paquet vulnérable — c'est le consommateur qui le retient, en épinglant une plage vulnérable ou en utilisant une API que la version majeure patchée a supprimée. La question à se poser est donc "qui maintient cette version ici ?" et la réponse est une dépendance directe, ou un framework dont une épingle ne peut pas vaincre l'override. Un override est une épingle, et une épingle est strictement pire qu'un vrai bump de version : elle diverge l'arbre de ce que les tests en amont testent, et elle pourrit silencieusement. Prenez le bump là où il existe.
L'étape 7 (patcher) est étroite à dessein, et l'étroitesse est le point. Lorsque la version majeure patchée ne parvient qu'à charger — un export par défaut supprimé, un point d'entrée renommé — un patch d'une ligne semble fermer correctement l'avis plutôt que de le supprimer. Parfois, c'est le cas. Trois conditions doivent d'abord être remplies, et la troisième est celle qui échoue généralement :
- Le patch cible un chemin stable dans le paquet vulnérable et est épinglé à une version exacte. Ce qui casse un patch n'est pas
dist/en tant que tel — les paquets publiés livrentdist/et rien d'autre — c'est un chemin qui bouge entre les versions.dist/chunks/<hash>.mjsest renommé par la prochaine build et le patch échoue alors pour tout le monde dans l'équipe ;dist/index.mjsest le propre point d'entrée du paquet et reste. Un patch qui nomme un symbole minifié est toujours acceptable avec l'épingle de version, car l'épingle transforme le risque en un échec retentissant depnpm installau lieu d'un mauvais patch silencieux. Patchez la dépendance vulnérable, pas un consommateur qui l'importe par hasard — voir le cas mesuré ci-dessous, où la première évaluation a regardé le chunk du consommateur et a rejeté l'échelon sur cette base. - Pesez la clôture de production contre le coût de maintenance. Un patch est une obligation permanente à chaque futur bump, donc pour un chemin d'outillage de développement qui ne peut pas atteindre la production, il pourrait bien dépenser plus qu'il n'achète — dans un seul projet. Un MODÈLE inverse cette arithmétique : chaque projet généré hérite d'un patch, alors qu'un résidu devrait être rediscuté dans chacun d'eux. C'est un jugement, pas un veto ; les conditions 1 et 3 sont les dures.
- Charger n'est pas la même chose que fonctionner. Une version majeure qui a supprimé un export a généralement aussi changé le comportement, donc un import patché fait importer le module tout en laissant le consommateur s'exécuter contre une API sur laquelle il n'a jamais été testé. Si vous ne pouvez pas dire ce qui a changé d'autre dans cette version majeure et pourquoi cela n'a pas d'importance ici, le patch est une hypothèse déguisée en correctif.
Patchez le paquet VULNÉRABLE, pas le consommateur. C'est la distinction qui décide si l'échelon fonctionne, et il est facile de se tromper, car l'erreur pointe vers le consommateur. Mesuré, lt-crm 2026-10-06 : @nuxt/[email protected] fait import Git from 'simple-git', et l'augmentation de simple-git à la seule ligne corrigée (4.x, qui a supprimé cet export par défaut) a fait échouer nuxt prepare avec does not provide an export named 'default'. La première évaluation a regardé le propre dist/chunks/module-main.mjs de devtools, a trouvé l'import là, et a rejeté le patchage sur la condition 1 — un chunk compilé — ce qui était le bon verdict concernant le mauvais fichier. Le patch appartient un niveau plus bas, dans le propre dist/index.mjs de simple-git, où une seule ligne ajoutée réexporte comme default la fabrique même que le module exporte déjà comme simpleGit. Rien dans le code durci ne change ; seule la surface du module revient.
Ce faisant, les trois conditions sont remplies : c'est le paquet vulnérable plutôt qu'un artefact de consommateur, il est épinglé à une version exacte (patchedDependencies: { '[email protected]': … } plus la cible de l'override), et l'API que devtools appelle réellement — branch, revparse, checkout, fetch, mergeLog, status, diffSummary — n'a pas changé entre 3.x et 4.x (simple-git a supprimé l'export par défaut et le constructeur new SimpleGit mais a conservé toutes les méthodes). Le patch tient.
Étape 5 — Classifier les erreurs résiduelles (blocage uniquement)
Si l'itération est bloquée, en mode Bloquant :
- Classez chaque erreur non résolue dans l'une des catégories ci-dessous
- Si TOUTES les erreurs sont des résidus Acceptables, vous pouvez passer outre le statut rouge — mais jamais en automatique. Un humain vérifie que l'échelle de l'étape 4 a été correctement suivie.
- Si UNE erreur est Verte, Rouge ou un autre Bloqueur, le flux s'arrête. Période.
Catégories résiduelles
| Catégorie | Définition | Traitement en mode Bloquant |
|---|---|---|
| Vert | La vérification se termine correctement après les étapes (1a), 2, 3, 4. | Traité comme VERT à toutes fins utiles. Le workflow continue. |
| Rouge (imblocable) | Erreur réelle du projet : typecheck, build, lint, import manquant, etc. | Le workflow s'arrête. |
| Bloqueur (test) | L'échec vient uniquement des tests (unitaires, API, Playwright) — le reste (typecheck, build, lint) est vert. | Le workflow s'arrête. (Les tests ont leur propre compétence : /lt-dev:fix-test.) |
| Vulnérabilité de dépendance (échelle épuisée) | L'échelle de l'étape 4 a été épuisée et la vulnérabilité persiste. | Peut être outrepassée par un humain. Le rapport de résidu liste l'avis, les étapes tentées et le résultat. |
| Non reproductible | L'erreur du rapport a disparu lors de la réexécution. | Traité comme vert pour cette exécution. Signalé dans le rapport comme "instable — le checker local peut être en décalage". |
| Erreur d'outillage | Dysfonctionnement du runner de test, conflit de version Node, module manquant dans l'environnement. | Traité comme vert si le projet se construit et que les tests passent avec un outillage alternatif. Signalé comme "problème d'environnement — doit être corrigé avant le prochain cycle". |
En mode Consultatif, ignorez la classification : signalez chaque erreur non résolue comme un résultat Critique et continuez.
Étape 6 — Rapport de sortie
Quelle que soit la terminaison, émettez un rapport structuré avec :
- Projets testés
- Résultat VERT / ROUGE par projet (y compris le nombre d'itérations nécessaires)
- Résultats d'audit traités (échelle et résultat)
- Résidus (avec classification)
- Fichiers modifiés (si corrigés automatiquement)
Étape 6.5 — Pièges & Dangers
Les tsconfig parasites
Certains projets ont plus d'un tsconfig. L'éditeur en utilise un, la compilation en utilise un autre, et check en utilise un troisième — ou pas. Lorsque vous voyez une erreur de type qui n'a aucun sens à l'emplacement du fichier, le problème n'est généralement pas le type, c'est le fichier tsconfig.json que le check a chargé. Vérifiez avec tsc --showConfig ce que le script de check invoque réellement ; un extends manquant ou un exclude supplémentaire est la cause habituelle.
La version de Node ne correspond pas
Le projet exige >=18 et le shell en a 16. check plante à l'étape de build avec "class private fields are not supported". Vérifiez .node-version, .nvmrc, engines dans package.json ; si l'environnement ne correspond pas, signalez-le et n'essayez pas de le corriger — c'est un résidu d'environnement.
Complétez le fichier, pas le diff
Un typecheck échoue sur un fichier que le diff n'a pas touché. Cela arrive. Voir l'étape 3.2 ci-dessus : les limites de portée ne s'appliquent pas à l'exécutabilité. Lisez le fichier qui échoue, corrigez le type, puis vérifiez ce qu'il importe — parfois l'import est un nouveau fichier qui a supprimé l'export, parfois le fichier était déjà cassé. Les deux sont corrigés.
Échecs de l'étape 4&5
L'étape 4 ne garantit pas que l'étape 5 fonctionne : npm audit fix --force peut casser une dépendance qui n'avait pas de vulnérabilité et faire passer une construction verte à rouge. C'est intentionnel. Lorsque cela se produit, l'échelle continue à l'étape 6 (augmenter le consommateur) ou 7 (patcher).
Les tests Playwright sont délibérément exclus de check
Lorsque includes_tests est unit+api, le script check saute Playwright (voir scripts/check.mjs). Cela ne signifie pas que Playwright est ignoré — il s'exécute via lt dev test / CI. Si un appelant a besoin de la couverture Playwright, il doit l'exécuter séparément.
flux de bout en bout de scripts/discover-check-scripts.sh
(partie de la doc de mise en œuvre — dérive rarement)
find_package_json_files— git ls-files + filtre.github/resolve_composite_script— résolution à un niveau : sicheckest unrunde quelque chose d'autre, regardez-le une foisdetect_package_manager— vérifie le fichier de verrouillage (priorité : pnpm > npm > yarn > bun > autres)check_includes_tests— recherchetest,vitest,jest,playwrightdans la chaîne résolue ;unit+apipour le wrapper lt