Enquêter sur les défaillances CI
L'objectif : prendre un test qui échoue ou une exécution rouge et arriver à un verdict sur lequel un développeur peut agir — le sien / trunk-borne / flaky, et quand trunk-borne : le SHA coupable, son auteur, la PR, et si un correctif a déjà atterri. Tout ce qui suit est une dérivation sur des données qui existent déjà ; tu n'as jamais besoin de relancer CI pour répondre.
Deux vues warehouse en sont le substrat (toutes deux non-matérialisées — toujours à jour, interroge-les librement) :
engineering_analytics_ci_failures— une ligne par ligneFAILED <nodeid>pytest du log CI, pré-fingerprints (fingerprint= id de test + erreur normalisée chiffre/hex). Groupe parfingerprintpour obtenir première/dernière détection, nombre d'occurrences, et couverture de branche.engineering_analytics_ci_job_history— une ligne par tentative de job avecconclusionET attribution de commit :head_sha,commit_author_name,commit_message,commit_pr_number(la PR fusionnée qui a produit le commit, la seule attribution PR qu'une exécution master push possède). C'est là que vivent les verts ; les logs contiennent les défaillances uniquement, donc toute question « quand est-ce devenu rouge / vert de nouveau » doit venir d'ici, jamais des logs.
Des requêtes SQL prêtes à copier pour chaque étape se trouvent dans references/investigation-queries.md.
Commence large : qu'est-ce qui est cassé maintenant
Pour « à quelles défaillances CI dois-je m'intéresser maintenant » (avant d'avoir un test spécifique), l'outil MCP engineering-analytics-broken-tests effectue la classification de forme ci-dessous sur toutes les défaillances actives à la fois : il groupe les défaillances des 2 derniers jours par fingerprint et étiquète chaque cas breaking_master / blocking_merge_queue / novel_burst / potentially_resolved / flaky / pr_only, du plus urgent au moins urgent, plus breaking_master_jobs (les jobs de branche par défaut dont la dernière exécution est rouge). Utilise-le comme point d'entrée de triage, puis plonge dans le workflow par-défaillance ci-dessous pour atteindre le coupable. C'est l'équivalent automatisé du fingerprinting à la main ; les requêtes manuelles restent le moyen d'épingler une défaillance spécifique à une limite et un auteur.
blocking_merge_queue est la seule forme que le tableau manuel ci-dessous ne couvre pas, car elle ressemble à une défaillance mono-branche et ne l'est pas. La file d'attente de fusion exécute la suite complète sur une branche de portail (trunk-merge/pr-<n>/…) transportant master, la PR, et toutes les PR en file d'attente devant elle avec chevauchement des cibles impactées (en pratique la plupart de la file), donc une défaillance là est sur un commit qui a déjà passé la CI propre de la PR : un conflit avec ce qui a atterri ou s'est mis en file d'attente entre-temps, ou la propre pause d'une queue-mate, pas le bug de cette PR par défaut. Lis-le comme « cela a arrêté une fusion », énumère ce que la branche de portail a exécuté (requête 8), et fais un diff contre trunk plutôt que de lire la PR seule.
Les quatre formes de défaillance
Fingerprint la défaillance en premier (requête 1 dans les références), puis lis sa forme — la classification ressort de trois colonnes :
| Forme | Lecture | Étape suivante |
|---|---|---|
| 1 branche, n'importe quelle fenêtre | Problème propre à cette PR | Lis ses lignes d'erreur ; fini |
1 branche de portail trunk-merge/pr-<n>/… |
Queue-mate, ou atterri depuis | Requête 8, puis diff de la branche de portail contre trunk |
| Plusieurs branches, rafale dense, touche master | Pause trunk (master est/était rouge) | Requête limite → coupable (ci-dessous) |
| Plusieurs branches, sporadique sur jours/semaines | Flaky | Corrobore avec engineering-analytics-flaky-tests |
Pourquoi cross-branch signifie trunk : la CI PR exécute la PR fusionnée avec master, donc un mauvais commit master échoue dans chaque PR s'exécutant concurremment. Une défaillance apparaissant sur de nombreuses branches sans lien dans une fenêtre serrée est la signature d'une pause master-merge, pas du code de ces PRs. Dis explicitement à celui qui demande quand sa PR n'est pas en faute — c'est généralement la phrase la plus précieuse de la réponse.
Pause trunk → coupable
Exécute la requête limite (requête 2) : l'historique des jobs master-only pour le job qui échoue, ordonné par created_at. Le motif se lit directement :
... success success | failure failure ... failure | success ...
^ premier rouge = la ligne coupable ^ premier vert = la ligne du correctif
La ligne coupable porte tout : head_sha, commit_author_name, commit_message (qui nomme ce qui a changé), commit_pr_number. La ligne premier-vert identifie le correctif de la même façon. Vérification de confiance avant de nommer quelqu'un : le commit coupable touche-t-il plausiblement la zone défaillante (son message / diff PR vs le module du test qui échoue) ? Une limite atterrissant sur un commit sans lien signifie bruit de sharding ou de timing — élargis la fenêtre et vérifie le commit adjacent avant d'affirmer.
Ensuite, vérifie que la fenêtre de défaillance dans ci_failures correspond (first_seen juste après la fusion du coupable, last_seen peu après le correctif à mesure que la file de PR se vide). Une non-correspondance = tu regardes deux problèmes différents partageant un test.
Flaky → corrobore, ne devine pas
Lis les tentatives sœurs avant de dire « flaky ». Une seule exécution ne peut pas distinguer un flake d'une défaillance déterministe, et ci_job_history porte déjà la conclusion de chaque tentative pour la branche. Si chaque exécution sur une branche est rouge, il n'y a pas de flake à trouver, et proposer une relance gaspille une exécution : une nouvelle tentative ne change aucune des entrées.
La seule forme sporadique est suggestive, pas une preuve. L'outil MCP engineering-analytics-flaky-tests lit les spans CI par-test (signal rerun-pass — un test qui échoue puis passe lors d'une relance dans le même job) et c'est le signal plus fort où il a une couverture. Comptes uniquement, jamais de taux : les exécutions réussies sous le seuil de durée de l'émetteur ne sont pas enregistrées, donc il n'y a pas d'honnête dénominateur.
Réserves que tu dois porter dans chaque réponse
- Les logs ne contiennent que les défaillances. Aucune ligne de base verte n'existe dans
ci_failures; l'absence de fingerprint est une faible preuve (le job peut simplement ne pas avoir été exécuté). Les verts proviennent deci_job_historyuniquement. - Les fingerprints sont pytest uniquement (v1). Les défaillances Jest / playwright / cargo apparaissent dans les logs de défaillance bruts mais ne sont pas dans
ci_failures. Pour celles-ci, reviens aux logs de défaillance bruts via les outils MCPengineering-analytics-ci-failure-logs(scoped PR) /engineering-analytics-run-failure-logs(scoped exécution). - Un job qui échoue avant que ses tests ne s'exécutent est invisible à chaque surface au niveau du test. Pas de ligne
FAILEDsignifie pas de fingerprint, pas de span, rien pour l'outil flaky-tests à lire — la défaillance est réelle mais la réponse au niveau du test est le silence, pas « correct ». Les défaillances de setup, docker et runner ne sont visibles que comme conclusions de job (requête 7), qui est aussi la seule surface ici qui donne un taux honnête, puisqu'elle enregistre les verts. - Vérifie
runs-onavant de blâmer un cache. Les jobs sur les runnersdepot-*résolventactions/cachecontre Depot Cache, que l'API de cache GitHub Actions ne peut voir, donc un résultat vide là n'est pas une preuve d'éviction. Lis la ligneCache not found for input keys:du job pour les clés effectivement demandées. Les deux étapes restent vertes dans tous les cas :actions/cache/restoreréussit en cas de manque, et unactions/cache/saveéchoué n'est qu'un avertissement, donc un job producteur vert peut n'avoir stocké rien. - La fraîcheur diffère par source. Les logs arrivent en quasi-temps réel ; les tables warehouse jobs/runs arrivent via sync webhook et peuvent traîner. Pendant un incident actif, commence par
ci_failureset vérifie lemax(created_at)du warehouse avant de faire confiance à une limite (requête 5). Une limite calculée contre un warehouse stale nomme le mauvais commit. - La
conclusiond'une exécution peut être stale jusqu'à ce que le webhookworkflow_runle règle (SPEC §7) — traite une queue très récente « sans défaillance » avec suspicion. - Relances : les lignes
run_attempt > 1sont le même job relancé. Une défaillance qui s'efface en tentative 2 est un signal flake ; une qui échoue jusqu'à tentative 5+ est déterministe. - Reverts : un revert apparaît comme un nouveau premier-vert (ou premier-rouge) commit dont le
commit_pr_numberest la PR reverting — l'attribution suit le revert, pas l'original. - Limite chaque requête logs en temps. Le flux log de défaillance est large ; les scans non-limités frappent le plafond de lecture. 14 jours couvre presque chaque enquête.
- Apparie le jumeau warehouse aussi. Une requête
ci_job_historyfenêtrée surcreated_atseul force un scan jobs complet — l'horodatage parsé est une colonne calculée que le scan parquet ne peut pas élaguer. Ajoute un plancher de stringcreated_at_raw >= '<YYYY-MM-DD>'grossier (un jour sous la fenêtre) aux côtés de la limitecreated_atprécise pour que le scan saute ;created_atreste le filtre exact.
Choisir une surface
| Question | Utilise |
|---|---|
| « Qu'est-ce qui est cassé à travers CI maintenant ? » | L'outil MCP engineering-analytics-broken-tests (trié, classifié) |
| Pareil, mais depuis un terminal | hogli ci:insights — ces endpoints, scoped au repo du checkout |
| « Pourquoi la CI de MA PR a échoué ? » | L'outil MCP engineering-analytics-ci-failure-logs (scoped PR, groupé) |
| « Qui a cassé master / quand X a commencé ? » | Les deux vues, workflow ci-dessus |
| « X est-il flaky ? » | Forme de ci_failures + l'outil flaky-tests |
| « Cette défaillance setup/infra est-elle courante ? » | Conclusions de job, requête 7 (aucune ligne de test n'existe pour elle) |
| « Qu'est-ce qui échoue sur master maintenant ? » | Les lignes breaking_master + breaking_master_jobs du tool broken-tests |
| « CI est-il lent / coûteux / les PRs sont coincées ? » | La skill diagnosing-ci-and-merge-bottlenecks |
| « Sauvegarde ceci comme dashboard/insight » | La skill turning-engineering-analytics-into-insights |
Attentes de sortie
Mène avec le verdict et l'exonération/blame en langage clair (« pas ta PR — master était cassé entre 08:01 et 09:58 UTC par #68727 ; réparé par #68855 »), puis la preuve : les lignes limite, la fenêtre fingerprint, les comptes occurrence/branche. Nomme l'auteur factuellement (il a créé le commit coupable), jamais d'un ton accusateur — le message commit et le lien PR laissent le lecteur juger le changement, et la moitié du temps le « coupable » était un changement raisonnable avec une dépendance de test non-mockée.