Renforcer les tests — éliminer les survivants les plus importants
L'autre moitié de la boucle locale de mutation testing. n8n:mutation-test rapporte les mutations qui ont échappé aux tests ; cette skill sélectionne celles qui comptent et rédige des changements d'assertion minimaux pour les éliminer.
Quand l'utiliser
- L'utilisateur vient de lancer
/n8n:mutation-test <file>et le verdict estred - L'utilisateur dit : « renforcer les tests », « éliminer les survivants », « réparer le red », « itérer sur les tests pour X »
- En cours de boucle : l'étape de vérification de cette skill appelle
n8n:mutation-testà nouveau, la boucle se ferme ici
Ne pas utiliser cette skill :
- Avant que du mutation testing ait été exécuté sur le fichier cible (pas de
summary.jsonà trier) - Pour un verdict
green— il n'y a rien à renforcer ; si l'utilisateur insiste, repousser et demander quel fichier a vraiment besoin de travail - Pour éliminer en masse chaque survivant — explicitement limité à 5 par invocation. Réinvoquer pour plus.
Entrées
- Par défaut : lire
packages/workflow/reports/mutation/summary.json(sortie de la dernière exécution den8n:mutation-test). - Substitution :
--summary <path>pour pointer vers un autre fichier de résumé.
Étapes
1. Lire le résumé
packages/workflow/reports/mutation/summary.json. Déjà compact (~50 KB). Extraire :
files[0].file— le fichier source testéfiles[0].score— score de mutation courantfiles[0].survivors[]— chaque mutant survivant (et sans couverture) avec localisation, remplacement, noms des tests couvrants
Si summary.json est manquant, arrêter. Dire à l'utilisateur de lancer n8n:mutation-test en premier.
2. Lire la source testée, sobrement
Lire le fichier source référencé dans summary.json. Lire une fois, le fichier entier (les fichiers source n8n-workflow typiques font 50-500 lignes ; le coût est borné). C'est le seul fichier lu ; ne pas charger les fichiers de test encore.
3. Trier les survivants
Catégoriser chaque survivant dans l'un de trois buckets. Utiliser le rubrique ci-dessous — ne pas l'appliquer mécaniquement, mais s'y appuyer.
HAUTE importance — « vecteur de régression réel » : mutant qui protège un comportement que les vrais utilisateurs atteignent réellement par la surface API publique.
- Vérifications de type contre les formes que les données utilisateur prennent routinement :
null,undefined,Date,Buffer,Uint8Array,Array, objets simples avec clés arbitraires - Branches conditionnelles qui gardent un parcours critique (p. ex. « si cela retourne tôt, le mauvais parcours exécute »)
- Chemins de code qui gèrent le flux contrôlé par l'utilisateur : expressions, comportement Code-node, manipulation d'items binaires, sémantique deep-clone
- Mutants sur clauses de garde qui préviennent des crash (
if (value == null) return ...)
IMPORTANCE MODÉRÉE — « invariant observable par l'utilisateur » : réel mais impact utilisateur moins fréquent.
- Pièges proxy qui changent la sémantique
Object.keys/in/hasOwnPropertyaprès mutation - Séquences set-puis-delete / re-set-après-delete (motifs d'assignation Code-node)
- Traiter l'assignation
undefinedcomme suppression (obj.foo = undefined→ clé supprimée) - Cas limites qui ne se déclenchent que sous des motifs d'itération spécifiques
BASSE importance — « assurance refactoring » ou bruit : ignorer ceux-ci. Documenter le saut dans la sortie mais ne pas rédiger de tests.
- Vérifications de propriété constructeur (
arr.constructor === Array) sauf si le code production en dépend - Invariants d'idempotence déclenchés uniquement par d'autre code de bibliothèque (Lodash, spread natif)
- Mutants équivalents — mutations qui produisent du code sémantiquement identique (p. ex. inverser un conditionnel inutilisé)
- Mutants sur helpers internes que les utilisateurs ne peuvent pas atteindre via aucune API publique
Si le résumé énumère des survivants noCoverage, les traiter comme leur propre bucket : « la suite de test n'exécute même pas ces lignes ». Les trier par la même rubrique, mais flaguer séparément car ils ont besoin d'un cas de test nouveau plutôt qu'une extension d'assertion.
4. Choisir l'ensemble de travail : jusqu'à 5 mutants
Ordre : tous les HIGH d'abord, puis MODERATE s'il reste du budget, jamais LOW. Plafond dur à 5 au total. Si HIGH seul dépasse 5, choisir les 5 plus distincts (ne pas choisir 5 mutants sur la même ligne — ils partagent probablement un fix ; choisir des représentants dans le fichier).
Si moins de 3 candidats HIGH+MODERATE existent, faire ce qui est là. Ne pas remplir avec LOW juste pour atteindre un nombre.
Rédiger l'ensemble de travail à l'utilisateur avant d'éditer :
Picked N survivors to address (M skipped as refactor-insurance / low-leverage):
1. [HIGH] location — original → replacement
plan: assert <X> in <covering test name>
2. [HIGH] ...
3. [MODERATE] ...
...
C'est la chance pour l'utilisateur de rediriger. Ne pas rédiger de code encore.
5. Lire les tests couvrants pour les survivants choisis
Pour chaque survivant choisi, le résumé énumère les noms de test qui ont couvert la ligne. Trouver ces tests dans le fichier de test (habituellement packages/<pkg>/test/<source-basename>.test.ts, mais vérifier) et lire juste les blocs test('...') pertinents — pas le fichier entier. Utiliser Grep + Read avec décalages de ligne pour garder le coût en tokens bas.
Objectif : comprendre ce que l'assertion existante asserte pour que la nouvelle assertion soit additive, pas contradictoire.
6. Rédiger les changements
Contraintes :
- Préférer étendre un test couvrant existant plutôt qu'en ajouter un nouveau. Moins de churn de fichier, révision plus facile.
- Matcher le style existant — même bibliothèque d'assertion, mêmes idiomes matcher (
.toBevs.toEqualvs.toStrictEqual). - Additions minimales — une ou deux assertions par mutant, pas un it-block nouveau par mutant.
- Pas de fabrication — seulement asserte ce que le code source fait réellement. Si vous ne pouvez pas le dire depuis la source, arrêter et demander à l'utilisateur.
- Pour les survivants
noCoverage, ajouter un nouveau cas de test nommé d'après le comportement épinglé. Placer à côté des tests connexes.
Utiliser Edit avec correspondances de chaîne exacte. Ne jamais réécrire des fichiers de test entiers.
7. Vérifier
Réinvoquer n8n:mutation-test sur le même fichier source. Rapporter :
Before: red 76.74% (28 survivors)
After: green 82.34% (22 survivors)
Killed: 6 of 5 targeted (1 bonus — fix for #77 also killed #78)
Still surviving: 22 — re-invoke /n8n:strengthen-tests for another batch.
Si le score a monté mais le seuil n'est toujours pas atteint : l'itération fonctionne, recommander un autre passage. Si le score a baissé ou est resté le même : au moins un test nouveau n'asserte pas ce qu'on pense. Exposer le diff à l'utilisateur et arrêter — ne pas auto-annuler. Si un test échoue maintenant (ne survit pas — échoue réellement) : nous avons asserété quelque chose que le code ne fait pas. Annuler cette assertion spécifique, laisser le reste, rapporter laquelle était fausse.
Forme de sortie
Chaque invocation produit :
- Plan de travail (avant les édits) — les survivants choisis et le plan pour chacun
- Diffs (pendant les édits) — appels outil Edit, visibles dans la transcription
- Vérifier (après) — relancer + comparaison avant/après
Garder la prose minimale entre sections. Le plan et les étapes de vérification sont les sorties structurées ; tout le reste est mécanique.
Contraintes
- 5 mutants max par invocation. Réinvoquer pour plus. Prévient les sessions qui s'emballent sur fichiers à 30 survivants.
- Ne jamais fabriquer d'assertions. Si la source ne fait clairement pas X, ne pas le prétendre.
- Pas de fichiers de test nouveaux sauf absolument nécessaire. Étendre le fichier de test couvrant existant.
- Pas d'annulation des tests d'autres personnes. Éditer seulement les tests du package muté.
- Ne pas relancer mutation-test plus qu'une fois par invocation. C'est l'étape de vérification. Ne pas boucler dans une seule invocation ; laisser l'utilisateur réinvoquer.
- Pas de commits. Les édits atterrissent dans l'arbre de travail ; l'utilisateur révise et commite.
Suites courantes
- L'utilisateur dit « go again » → réinvoquer cette skill. Le summary.json reflète maintenant l'état post-édition.
- L'utilisateur dit « pourquoi #N a été classé en LOW ? » → expliquer l'application de la rubrique pour ce mutant spécifique, pas de retri des autres.
- L'utilisateur dit « tue #N spécifiquement » → substituer la triage pour ce mutant, le traiter comme choisi.
- L'utilisateur dit « saute l'étape de vérification » → non ; l'étape de vérification est le contrat que les édits ont réellement fait bouger le score.
Connexes
n8n:mutation-test— le côté lecture de cette bouclescripts/mutation-health/README.md— l'histoire d'observabilité sauvegardée par BQ dans laquelle cela s'insère