Green Gate
Boucle autonome de qualité pour les packages Dart et Flutter. Exécute quatre portes — analyze, format, test, coverage — lit la sortie réelle des outils, édite le code et les tests pour corriger les défaillances, et boucle jusqu'à une itération finale qui prouve que les quatre passent simultanément avec les nombres observés. Agit de manière autonome sur les défaillances objectives ; remonte uniquement sur les blocages, l'ambiguïté véritable ou les défaillances d'infrastructure.
Cette skill orchestre les outils et édite les fichiers. Elle déférence le comment de l'écriture des tests à la skill testing — elle ne duplique jamais les mocks, la structure ou les conseils de couverture.
Standards Fondamentaux
Appliquez ceux-ci à TOUT travail green-gate :
- Utilisez les outils MCP, jamais l'interface Bash, pour les portes — analyze via
mcp__dart__analyze_files, format viamcp__dart__dart_format, test et coverage viamcp__very-good-cli__test. Le chemin de test Bash (very_good test,flutter test,dart test) est bloqué par le hookblock-cli-workarounds.sh;dart analyzevia Bash est redondant avec l'outil MCP. Bash est réservé au parsing decoverage/lcov.infouniquement. - Ne mettez jamais en cache le vert — réévaluez chaque porte à chaque round. Corriger les défaillances d'analyze ou de test et écrire de nouveaux fichiers de test décale à la fois le formatage et le dénominateur de couverture, donc une porte précédemment verte peut régresser.
- Quittez uniquement sur les nombres observés — la boucle se termine uniquement après une itération finale unique dans laquelle analyze est propre, format rapporte zéro changements, tous les tests passent, et
min_coverageest satisfait, tous observés dans le même round. Déclarer le succès de mémoire est interdit ; confirmez le succès uniquement avec les nombres réels observés dans ce round final. - Passez
coverage: true,min_coverage, etcheck_ignore: trueensemble — omettrecoverage: trueproduit silencieusement aucunlcov.info(mime une mauvaise configuration) ; omettrecheck_ignore: truerend le remède// coverage:ignoreun no-op. - Déférez l'écriture de tests à la skill
testing— quand une correction nécessite d'authorer des tests, suivezskills/testing/SKILL.mdpour la structure, les mocks et le nommage. - Corrigez les causes racines, pas les symptômes — ne faiblirez jamais une porte pour la faire passer (ne supprimez pas les assertions défaillantes, ne baissez pas la cible pour éviter le travail, ou
// coverage:ignorele code accessible). Remontez plutôt les décisions produit/API véritables au lieu de deviner. - Agissez de manière autonome sur les défaillances objectives — les erreurs d'analyseur, les tests rouges et les lacunes de couverture sont corrigés sans re-prompt. Remontez uniquement selon la matrice.
La Boucle
Pour chaque racine de package (voir Recursive / Monorepo), exécutez cet algorithme :
- Découvrez — résolvez la racine du package (l'argument
directoryou la racine du workspace) ; confirmez qu'unpubspec.yamlexiste. Initialisez l'état de la boucle (compteur d'itération0, empreintes vides, ensemble de fichiers modifiés vide). - Analyze — exécutez
mcp__dart__analyze_filesavecapplyFixes: true. Si des erreurs persistent, c'est la porte active. Corrigez, enregistrez l'empreinte, allez à l'étape 7. - Format — exécutez
mcp__dart__dart_formatsur la racine du package. Il reformate le package entier sur place. S'il rapporte des fichiers modifiés, la porte est maintenant verte pour le round suivant. - Test — uniquement si analyze est vert ce round. Exécutez
mcp__very-good-cli__testavec les paramètres de couverture de Test Gate. Si les tests échouent, corrigez, enregistrez l'empreinte, allez à l'étape 7. - Coverage — uniquement si les tests passent ce round. Le résultat MCP
min_coverageest autoritaire pour le pass/fail. Parsezcoverage/lcov.infopour le pourcentage affiché et les cibles de correction par fichier (consultatif). Si inférieur à la cible, authorizez des tests pour les fichiers sous-couverts classés (via la skilltesting), allez à l'étape 7. - Exit — si les quatre portes sont vertes dans cette même itération, confirmez le succès avec les nombres observés et arrêtez. C'est le seul chemin exit-vert.
- Re-vérifiez — incrémentez le compteur d'itération, recalculez l'empreinte de défaillance, vérifiez les déclencheurs de remontée (pas de progrès, oscillation, cap). Si un déclencheur se déclenche, remontez ; sinon, bouclez à l'étape 2 et réévaluez chaque porte.
Chemin no-op d'un seul passage — invoqué sur un package déjà vert, l'itération 1 trouve analyze propre, format rapportant zéro changements, tous les tests passant, et la couverture à ou au-dessus de la cible. La boucle confirme vert et quitte sans éditer un seul fichier. Un package vert coûte exactement une itération de vérification.
État de la Boucle
État porté sur les itérations — sans lui, « pas de progrès » est indécidable :
| État | Objectif |
|---|---|
| Compteur d'itération | Imposez le cap (défaut 5, par package) |
| Empreinte de défaillance par porte (round antérieur) | Détectez pas-de-progrès et oscillation |
| Fichiers modifiés ce round | Distinguez un round no-op d'un round no-progrès ; remplissez les rapports de remontée |
Clés d'empreinte par porte :
| Porte | Empreinte |
|---|---|
| Analyze | Ensemble trié de diagnosticCode @ file:line |
| Format | Ensemble de fichiers que dart format changerait (vide = vert) |
| Test | Ensemble d'ID / noms de test défaillants |
| Coverage | Pourcentage observé + ensemble trié de fichiers SF sous-couverts |
Définitions :
- Pas de progrès — l'empreinte de défaillance actuelle est identique à celle du round antérieur, ou son compte de défaillance n'a pas diminué. Remontez.
- Oscillation — les mêmes deux portes échangent vert/rouge sur deux rounds consécutifs (p.ex. une correction de format re-casse analyze, dont la correction re-casse format). Remontez.
Précédence de Porte
Ordre fixe : analyze → format → test → coverage. Deux règles le gouvernent :
- Une porte en aval n'est pas évaluée jusqu'à ce que la porte en amont soit verte ce round. Un analyseur rouge peut faire échouer les tests à la compilation ; la couverture est sans sens quand les tests ne passent pas — ne parsez jamais
lcov.infoaprès un échec de compilation. Format s'exécute après analyze pour que son reformatage ne brassouille pas le code queapplyFixesd'analyze est sur le point de réécrire. - Chaque porte est réévaluée à chaque round ; le vert n'est jamais mis en cache. Exit nécessite les quatre vertes dans la même itération finale.
Porte Analyze
- Exécutez
mcp__dart__analyze_filesavecapplyFixes: truepour que les correctifs rapides soient appliqués avant que les diagnostics ne reviennent.rootsprend[{ root: "file:///abs/path" }]— un URIfile:de la racine du package. Il n'existe pas de flag récursif ; la skill énumère elle-même les racines du package et transmet chacune. - La porte est verte quand zéro erreurs persistent. Traitez les diagnostics de gravité erreur comme bloquants ; adressez les avertissements et infos aussi quand ils sont dans la portée de la correction, mais ne laissez pas une info pré-existante sans rapport bloquer la porte.
- Interplay du hook — le hook PostToolUse
analyze.shse déclenche à chaque Edit/Write et quitte 2 (bloquant) quand une correction introduit une nouvelle erreur d'analyze. Traitez ce rejet comme retour de porte analyze-gate dans le même round, pas un mode de défaillance séparé : l'édition n'a pas atterri, donc révisez-la.
Porte Format
- Exécutez
mcp__dart__dart_formatsur la racine du package à chaque round. Il exécutedart format .sur le package entier sur place, donc la porte est basée sur l'observation (elle capture les édits manuels et la dérive pré-existante, pas seulement les fichiers que la boucle a édités) et auto-réparante (un appel laisse le package vert ; un second appel rapporte zéro changements). - Pourquoi format est une vraie porte, pas juste le hook — le hook PostToolUse
format.shne se déclenche que sur les fichiers que la boucle édite via Edit/Write. Un fichier édité manuellement ou pré-existant non-formaté que la boucle ne touche jamais passerait autrement green-gate et échouerait alors CI. Le format de package entier ferme cet écart.
Porte Test
- Exécutez
mcp__very-good-cli__testaveccoverage: true,min_coverage: <target>,check_ignore: true, et le globexclude_coverage(ci-dessous). - Dart vs Flutter — omettez
dartpour laisser l'outil auto-détecter (Flutter s'exécute quand un projet Flutter est détecté). Passezdart: trueuniquement pour un package Dart pur que l'outil classerait mal autrement. directory— passez-le quand le package n'est pas à la racine du workspace (packages sous-monorepo) ; omettez-le uniquement quandpubspec.yamlest à la racine.timeout_seconds— définissez toujours un cap (p.ex.120). Les tests Flutter pendent indéfiniment quandpumpAndSettle()s'exécute sans timeout ; le cap tue la run au lieu de bloquer la boucle. Un kill timeout est une défaillance d'outil, pas une défaillance de test — remontez-la selon la matrice.- La porte est verte quand chaque test passe. Un test défaillant est corrigé de manière autonome sauf s'il encode une décision produit véritable (remontez selon la matrice).
Porte Coverage
Le résultat MCP min_coverage est autoritaire pour le pass/fail. Le parse de coverage/lcov.info est consultatif — il fournit le pourcentage affiché et les cibles de correction par fichier.
- La cible défaut est
100(standard maison de VGV), remplaçable par invocation (p.ex.80) pour les packages hérités ou non-template. Les fichiers générés sortent le dénominateur viaexclude_coverageetcheck_ignore: truehonore// coverage:ignore, donc 100% signifie 100% du code testable, écrit à la main. exclude_coveragepar défaut —**/*.{g,freezed,gen}.dartplus les répertoires générés et l10n. Une seule chaîne de glob (expansion accolade ; revenez à**/*.g.dartsi une CLI build ne l'honore pas).- Passez
coverage: true,min_coverage, etcheck_ignore: trueensemble — voir Standards Fondamentaux. - Quand
coverage/lcov.infomanque ou est en dessous de la cible, suivez l'arbre de décision et les règles de parsing dansreferences/coverage.md— il couvre les trois causes d'absence, la gestion de lcov stale, les champs d'enregistrement lcov (SF/LF/LH/DA), la règle advisory-mirrors-the-gate, et la limitation Dart-only decheck_ignore.
Correction
- Corrigez uniquement les éléments défaillants — adressez les diagnostics, tests ou fichiers sous-couverts remontés ce round. Ne refactorisez pas le code sans rapport (YAGNI).
- Corrections de couverture = authorizez des tests — pour chaque fichier
SFclassé sous-couvert, écrivez des tests suivantskills/testing/SKILL.md. Priorisez les fichiers par compte de lignes non-couverts (LF - LH). - Bornez les fichiers par round — corrigez un batch cohérent, puis re-vérifiez. Exécuter les portes après chaque batch est ce qui rend « pas de progrès » détectable et prévient la correction d'une porte tout en cassant silencieusement une autre.
- Ne faiblirez jamais une porte — pas d'assertions supprimées, pas de cible baissée pour éviter le travail, pas de
// coverage:ignoresur le code accessible.
Escalade
Arrêtez et remontez à l'utilisateur quand :
| Déclencheur | Détail |
|---|---|
| Pas de progrès entre rounds | Empreinte de défaillance identique ou non-décroissante |
| Oscillation | Les mêmes deux portes échangent vert/rouge sur deux rounds |
| Cap atteint, portes toujours rouges | Terminal — rapportez les défaillances restantes par porte avec leurs empreintes |
| Test rouge vrai-bug | Une défaillance qui ressemble à une décision produit véritable plutôt qu'une erreur de codage |
| Correction ambiguë | Résolutions multiples valides (p.ex. changer l'API vs supprimer le lint) — préférez la cause racine ; remontez quand c'est une décision produit/API |
| Lacune de couverture code non-accessible | Suggérez // coverage:ignore (nécessite check_ignore: true, Dart-only) plutôt que de chasser 100% |
| Hygiène du dénominateur | Un fichier généré non-matché par le glob d'exclusion — élargissez exclude_coverage, pas // coverage:ignore |
| Défaillance d'outil / hook | Timeout de test MCP (timeout_seconds kill), crash d'analyseur, déni de hook manquant CLI (remontez avec le conseil d'installation dart pub global activate very_good_cli), ou un rejet analyze.sh répété qui bloque toujours une édition nécessaire après révision — un seul rejet est du retour de porte analyze-gate dans-boucle (voir Porte Analyze), pas une escalade |
Quand remontant, rapportez la porte active, son empreinte actuelle, les fichiers modifiés, le compte d'itération, et la décision spécifique que l'utilisateur doit prendre.
Recursive / Monorepo
- Tous les packages doivent passer — continuez en cas d'échec (corrigez chaque package défaillant), puis confirmez le résultat de chaque package. La porte rouge d'un package n'avorte pas les autres.
- Budget d'itération par package — le cap de 5 est par package, pas global, donc un monorepo de 12 packages n'épuise pas un budget global sur le package un.
- Découverte de racine de package partagée — marchez pour les fichiers
pubspec.yaml. L'ensemblerootsdeanalyze_filesdoit correspondre à l'ensemble de packages quemcp__very-good-cli__test --recursive(recursive: true) couvre. - Le chemin lcov est
<package>/coverage/lcov.info— résolu par racine découverte. - Un seul
min_coverages'applique à tous les packages — limitation documentée : le schéma d'outil n'a aucune override de couverture par package. Déclarez la cible partagée quand confirmant la couverture.
Ressources Supplémentaires
references/coverage.md— détail de porte coverage de green-gate (cible défaut, globs d'exclusion, champs lcov, arbre de décision,check_ignore, lcov stale).skills/testing/SKILL.md— comment écrire des tests unitaires Dart, de widget Flutter, et golden (structure, mockingmocktail, nommage).skills/testing/references/coverage.md— motifs de test pilotés par couverture (copyWith, branches, chemins d'erreur) pour fermer les lacunes par fichier.hooks/scripts/block-cli-workarounds.sh— pourquoi le chemin de test Bash est bloqué et les portes utilisent les outils MCP.