Tests de mutation — fichier unique
Enveloppe pnpm --filter=<pkg> mutate <file> et analyse summary.json dans une structure compacte et structurée adaptée aux itérations suivantes « renforcer les mutants survivants ».
Quand utiliser
- L'utilisateur invoque explicitement :
/mutation-test <path>, « teste la mutation de ce fichier », « vérifie l'efficacité de mes tests sur X » - L'utilisateur vient de modifier un fichier de test et veut savoir si ses assertions sont pertinentes
- Boucle de suivi après un verdict
red— transmettre la sortie structurée à une itération de « correction »
Ne pas utiliser cette skill pour :
- Les exécutions de mutation sur l'ensemble du package ou du repo — fichier unique seulement
- Les questions sur le pourcentage de couverture (utiliser le workflow de couverture existant)
- Les fichiers en dehors de
packages/workflow/— Stryker n'est câblé que là pour l'instant
Entrées
Un argument obligatoire : le fichier source à muter, soit comme chemin relatif au repo, soit comme chemin relatif au package. Exemples qui signifient tous la même chose :
packages/workflow/src/cron.tssrc/cron.ts(suppose packages/workflow)workflow/src/cron.ts(suppose packages/)
En cas d'ambiguïté, demander à l'utilisateur une seule fois quel package ; ne pas deviner.
Étapes
-
Résoudre le package + le chemin source relatif. Aujourd'hui seul
n8n-workflow(packages/workflow) a Stryker câblé. Si l'utilisateur passe un fichier en dehors, le signaler et arrêter — ne pas fabriquer de sortie. -
Exécuter Stryker avec sortie réduite :
pnpm --filter=n8n-workflow mutate <package-relative-src> 2>&1 | tail -40tail -40écarte le spam de la barre de progression de Stryker ; les chiffres pertinents + la liste des survivants atterrissent toujours dans les ~30 dernières lignes. Codes de sortie :0= succès,1= sous le seuil (toujours valide, summary.json existe),2= erreur d'usage,3= échec de Stryker (pas de summary.json). -
Si le code de sortie est 3, afficher la sortie réduite à l'utilisateur, suggérer de vérifier que les dépendances du workspace sont construites (
pnpm build), et arrêter. Ne pas fabriquer de rapport. -
Lire
packages/workflow/reports/mutation/summary.json— jamaisraw.json. raw.json fait 600 KB+ et n'est pas nécessaire pour la boucle de renforcement. summary.json contient déjà chaque mutant survivant avec sa localisation, son remplacement, le nom du mutateur et les noms des tests qui ont couvert la ligne. -
Plafonner covering_tests à 3 par survivant. Si un mutant a été couvert par plus de 3 tests, conserver les 3 premiers et ajouter
+N morecomme compte. Les noms au-delà de 3 consomment des tokens sans ajouter de signal actionnable — la boucle de renforcement a besoin de savoir quel test étendre, pas tous. -
Calculer
minimum_kills_neededpour atteindre le seuil :killed_now = summary.overall.counts.killed + summary.overall.counts.timeout valid_total = killed_now + summary.overall.counts.survived + summary.overall.counts.noCoverage needed = ceil((threshold/100) * valid_total) - killed_nowCela indique à la boucle suivante le nombre minimum de survivants à éliminer pour basculer de
redàgreen. Plafonner au nombre de survivants. -
Afficher la structure compacte décrite ci-dessous. Garder la prose à une ligne de titre ; le reste est le bloc JSON.
Forme de sortie
Une ligne de titre, puis un bloc JSON fencé. Rien d'autre — pas de préambule, pas de commentaire par survivant, pas de tri des risques (c'est le travail de la boucle suivante).
[red|green] <score>% (seuil <T>%) — <N> survivants ; il faut en éliminer ≥<K> pour passer au vert.
```json
{
"verdict": "red",
"target": "packages/workflow/src/augment-object.ts",
"package": "n8n-workflow",
"score": 76.74,
"threshold": 80,
"delta_to_threshold": 3.26,
"minimum_kills_needed": 5,
"counts": {
"killed": 99,
"survived": 28,
"no_coverage": 2,
"timeout": 0
},
"survivors": [
{
"id": "77",
"mutator": "ConditionalExpression",
"location": "src/augment-object.ts:95:6",
"original": "value === null",
"replacement": "false",
"covering_tests": [
"augmentObject should handle null values",
"augmentObject should handle nested nulls"
],
"covering_tests_overflow": 0
}
]
}
```
Trier le tableau survivors par location (numéro de ligne croissant, puis colonne) pour que la boucle de renforcement traite les survivants de haut en bas du fichier.
Contraintes
- Pas de raw.json — ne jamais le lire ou le présenter. summary.json est la seule entrée.
- Pas de rapport HTML — ne pas
openraw.html ou coller de liens vers lui. Si l'utilisateur veut explorer visuellement, il le demandera. - Pas de tri automatique — ne pas catégoriser les survivants en « vrai bug » ou « assurance refactoring ». C'est une étape d'analyse distincte qui doit se faire sur demande, pas par défaut. Cela maintient le coût en tokens prévisible.
- Pas de « je vais régénérer vos tests maintenant » — cette skill rapporte l'écart. Utiliser
n8n:strengthen-testspour des modifications d'assertions.
Suites courantes (ne pas faire sauf si demandé)
- L'utilisateur dit « corrige ceux-ci » → lancer une boucle de renforcement en utilisant la sortie JSON comme entrée. Lire la source de covering_tests, proposer des changements par mutant, relancer la skill pour vérifier.
- L'utilisateur dit « explique le survivant #N » → récupérer ce mutant de summary.json, afficher ses ~5 lignes environnantes du fichier source, aucune analyse au-delà de ce que contient summary.json.
- L'utilisateur demande « quel est le seuil ? » → 80% provisoirement ; voir
scripts/mutation-health/README.mdpour la justification. - L'utilisateur dit « exécute-le sur les fichiers modifiés » → pas encore câblé. Suggérer
git diffpour trouver les candidats, puis invoquer cette skill par fichier.
Connexes
scripts/mutation-health/README.md— l'histoire plus large de l'observabilité soutenue par BQpackages/workflow/stryker.config.mjs— la configuration Stryker que cette skill pilote