GitHub Triage
Trie les issues et pull requests ouvertes d'un dépôt GitHub. Optionnellement, nettoie d'abord les PR prêtes (fusionne les PR bot qui réussissent et les PR approuvées par les mainteneurs, examine celles jamais relues), puis ferme les issues déjà résolues (avec un commentaire citant la PR ou le commit qui les a résolues), s'assure que les issues et les PR en attente qui les corrigent se référencent mutuellement, et attribue une priorité locale uniquement et une estimation de taille de modification pour chaque issue qui est toujours en suspens.
Quand l'utiliser
- Lorsque l'utilisateur exécute
/github-triagepour nettoyer ou examiner les issues et pull requests ouvertes d'un dépôt. - Lorsqu'un backlog d'issues a dérivé : du travail résolu laissé ouvert, des correctifs atterris sans fermer leurs issues, ou des PR en cours qui n'ont jamais lié leur issue.
- Lorsque des PR prêtes se sont accumulées (mises à jour de dépendances réussies, PR approuvées-et-vertes) ou que des PR sont en attente de revue.
Quand NE PAS l'utiliser
- Ne pas invoquer automatiquement. Cette compétence effectue des écritures irréversibles sur GitHub (fusion de PR, fermeture d'issues, publication de commentaires) et ne s'exécute que sur invocation explicite.
- Ne pas utiliser pour appliquer des labels de priorité/effort sur GitHub. La priorité et la taille sont présentées localement uniquement et ne sont jamais publiées (voir Règles de sécurité).
- Ne pas utiliser comme substitut à la décision finale de fusion d'un humain — chaque fusion est proposée pour approbation explicite, jamais effectuée de manière autonome.
Principes fondamentaux
- Les écritures sont verrouillées. Calculez d'abord le triage complet, présentez chaque écriture proposée — y compris les fusions — pour examen, et n'exécutez rien tant que l'utilisateur n'a pas approuvé.
- Preuve avant de fermer. Ne fermez jamais une issue sans une raison concrète et citée (une PR fusionnée ou un commit sur la branche par défaut). Lorsque la preuve est faible ou ambiguë, laissez l'issue ouverte et signalez-la pour examen.
- La priorité et la taille restent locales. Ce sont une aide à la planification interne pour l'utilisateur, jamais écrites sur GitHub.
Flux de travail
Phase 0 : Sélectionner le dépôt cible
gh auth status # confirme que gh est authentifié
git rev-parse --is-inside-work-tree # le répertoire courant est-il un dépôt git ?
git remote -v # énumère les dépôts distants
Déterminez l'ensemble des dépôts distincts hébergés sur GitHub parmi les dépôts distants. Un dépôt distant est hébergé sur GitHub lorsque son URL hôte est github.com, sous l'une de ces formes :
https://github.com/PROPRIÉTAIRE/DÉPÔT(.git)[email protected]:PROPRIÉTAIRE/DÉPÔT(.git)ssh://[email protected]/PROPRIÉTAIRE/DÉPÔT(.git)
Normalisez chaque URL en PROPRIÉTAIRE/DÉPÔT et dédupliquez (une configuration fork peut avoir origin et upstream pointant vers différents dépôts GitHub ; plusieurs dépôts distants pointant vers le même PROPRIÉTAIRE/DÉPÔT comptent une fois).
Règle de sélection :
| Situation | Action |
|---|---|
| Exactement un dépôt GitHub distinct | Utilisez-le comme valeur par défaut — ne demandez pas. |
| Pas un dépôt git, ou zéro dépôt distant GitHub | Demandez à l'utilisateur le PROPRIÉTAIRE/DÉPÔT à trier. |
| Plus d'un dépôt GitHub distinct | Utilisez AskUserQuestion pour laisser l'utilisateur choisir quel PROPRIÉTAIRE/DÉPÔT. |
Les instances GitHub Enterprise ne peuvent pas être détectées automatiquement de manière fiable. Si l'utilisateur travaille sur une instance GHE, demandez
PROPRIÉTAIRE/DÉPÔTet faites-lui définirGH_HOST/ utiliser l'hôte configuré degh. Confirmez le dépôt résolu à l'utilisateur avant de continuer.
Stockez le résultat comme REPO="PROPRIÉTAIRE/DÉPÔT" et passez -R "$REPO" à chaque appel gh. Validez-le avec ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ avant utilisation, afin qu'une URL distante malformée ou hostile ne parvienne jamais à une commande.
Phase 1 : Rassembler les issues et le contexte
# Issues ouvertes (gh issue list exclut les PR par défaut)
gh issue list -R "$REPO" --state open --limit 1000 \
--json number,title,body,labels,assignees,comments,reactionGroups,createdAt,updatedAt,url
# PR ouvertes — candidates pour "correctif en attente" (phase issues) et triage PR (Phase 2)
gh pr list -R "$REPO" --state open --limit 1000 \
--json number,title,body,author,isDraft,reviewDecision,latestReviews,\
mergeable,mergeStateStatus,statusCheckRollup,labels,createdAt,headRefName,url,closingIssuesReferences
# PR récemment fusionnées — candidates pour "déjà résolu"
gh pr list -R "$REPO" --state merged --limit 300 \
--json number,title,body,mergedAt,url,closingIssuesReferences
closingIssuesReferences liste les issues qu'une PR est liée à fermer — renseigné par l'un des mots-clés de fermeture de GitHub (close/closes/closed, fix/fixes/fixed, resolve/resolves/resolved) dans le corps de la PR, ou par un lien manuel dans l'interface. C'est le signal le plus fort disponible. Pour les issues qu'il ne couvre pas, recherchez également les messages de commit sur la branche par défaut. Résolvez la branche par défaut de manière autoritaire à partir du dépôt sélectionné (pas depuis le origin/HEAD local, qui peut être non défini ou pointer vers le mauvais dépôt distant) :
default_branch=$(gh repo view "$REPO" --json defaultBranchRef --jq .defaultBranchRef.name)
# Ancrez le numéro d'issue pour que #12 ne corresponde pas à #120, #123, …
git log --oneline "origin/$default_branch" \
| grep -iE "(close|fix|resolve)[sd]? +#<N>([^0-9]|$)"
Phase 2 : Trier les pull requests ouvertes (optionnel)
Traitez les PR avant les issues : fusionner les PR prêtes ici signifie que la vérification "déjà résolu" dans la phase des issues voit le travail que ces fusions viennent d'atterrir.
S'il n'y a pas de PR ouvertes, sautez cette phase. Sinon, résumez les PR ouvertes et demandez à l'utilisateur s'il souhaite traiter les PR maintenant (AskUserQuestion : traiter les PR / passer aux issues). S'il passe, allez directement à la classification des issues.
Classifiez chaque PR ouverte à partir de son état de revue, CI et fusion. Les formes de champ ci-dessous sont ce que gh pr ... --json renvoie réellement — fiez-vous à elles, pas à l'intuition :
- Prête à fusionner =
mergeable == "MERGEABLE"etmergeStateStatus == "CLEAN"et CI ne bloque pas (ci-dessous).CLEANest le verdict côté serveur de GitHub "pas de conflits, pas en retard, pas de brouillon, vérifications requises vertes" — tout autremergeStateStatus(BEHIND,UNSTABLE,BLOCKED,DIRTY,DRAFT, …) n'est pas prêt. Traitezmergeable == "UNKNOWN"(GitHub recalcule la fusionnabilité de manière paresseuse, surtout juste après une autre fusion) comme pas prêt — ré-interrogez brièvement ou sautez ; ne fusionnez jamais sur cela. - CI ne bloque pas — scannez
statusCheckRollup(indexé sur__typename) et rejetez la PR uniquement sur un problème réel : un échec matériel (unCheckRundontconclusionestFAILURE/CANCELLED/TIMED_OUT/ACTION_REQUIRED/STARTUP_FAILURE/STALE, ou unStatusContextdontstateestFAILURE/ERROR), ou quoi que ce soit encore en cours (unCheckRunavecstatusdeQUEUED/IN_PROGRESS/WAITING/PENDING, ou unStatusContextavecstatedePENDING/EXPECTED— attendez, ne fusionnez pas encore).SUCCESS,NEUTRAL, etSKIPPEDsont corrects et ne doivent pas bloquer (par exemple, une exécution CodeQL qui signaleNEUTRALsur une mise à jour de dépendance — une PR Dependabot verte estCLEANavec une vérification CodeQLNEUTRAL). Un rollup vide signifie pas de CI — un état distinct, jamais traité comme prêt.mergeStateStatus == "CLEAN"reflète déjà le statut des vérifications requises ; utilisez le rollup pour détecter les vérifications non requises en échec/en attente. - Bot/automatisé =
author.is_bot == true. Faites correspondre la liste blanche d'auto-merge avecauthor.loginaprès normalisation du rendu degh— supprimez un préfixeapp/et un suffixe[bot](gh renvoie Dependabot commeapp/dependabotdans certaines versions etdependabot[bot]dans d'autres ; normalisez les deux endependabot). Par défaut :dependabot,renovate, plus ceux nommés par l'utilisateur. Une PR réussie provenant d'un bot non autorisé est signalée, jamais proposée pour fusion. - Approuvé par le mainteneur =
latestReviewsa une entrée avecstate == "APPROVED"dontauthorAssociationestOWNER/MEMBER/COLLABORATORet dontauthor.loginn'est pas l'auteur de la PR. N'utilisez pasreviewDecision == "APPROVED"seul : il est piloté par la protection de branche et estnullsur les dépôts sans règle de revue requise, donc il fait à la fois trop confiance (ne peut pas confirmer l'accès en écriture) et manque de véritables approbations. - Jamais relue =
latestReviewsn'a aucune entréeAPPROVED/CHANGES_REQUESTEDde quiconque autre que l'auteur de la PR (un commentaire de bot "revue désactivée" sur un fork n'est pas une revue).
| Catégorie | Condition | Action proposée |
|---|---|---|
| PR bot fusionnable | bot autorisé + prêt + pas brouillon | Proposer une fusion incrémentielle, dans l'ordre |
| Approuvée et prête | approuvée par mainteneur + prêt + pas brouillon | Demander à fusionner |
| Jamais relue | non-bot + seul l'auteur a relu (ou aucune revue) + pas brouillon | Proposer de lancer un sous-agent de revue |
| Nécessite du travail | brouillon, échec CI matériel, CI en attente, conflits, en retard, modifications demandées, ou une PR bot qui n'est pas prête | Signaler seulement — aucune action proposée |
("prêt" inclut déjà CI-ne-bloque-pas. Les sous-agents de revue sont pour les PR rédigées par des humains — une mise à jour de dépendance bot qui n'est pas prête est signalée comme Nécessite du travail, pas revue.)
Présentez les PR catégorisées et proposez les actions applicables via AskUserQuestion.
Fusion incrémentielle, dans l'ordre (PR bot et PR approuvées-prêtes) : confirmez d'abord l'ensemble de fusions et la méthode de fusion (fusionner est irréversible), puis fusionnez une à la fois, les plus anciennes en premier. Choisissez une méthode que le dépôt autorise et échouez fermé si ce n'est pas le cas :
gh repo view "$REPO" --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed # dépôt positionnel, pas -R
Pour chaque PR dans l'ordre, re-vérifiez immédiatement avant de fusionner (l'état dérive après chaque fusion — une PR atterrie peut laisser la suivante BEHIND, en conflit, ou en recalcul), fusionnez de manière synchrone, puis confirmez qu'elle a atterri avant d'avancer :
gh pr view <N> -R "$REPO" --json isDraft,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup
# ne continuez que si toujours prêt : pas brouillon, mergeable == MERGEABLE, mergeStateStatus == CLEAN, CI ne bloque pas
gh pr merge <N> -R "$REPO" --<méthode> # jamais --auto, jamais --admin
gh pr view <N> -R "$REPO" --json state # attendez "MERGED" avant de passer à la PR suivante
Arrêtez-vous et signalez si une PR n'est plus prête (y compris mergeable == "UNKNOWN"), si la fusion n'a pas atterri, ou si une vérification requise n'est pas verte — ne forcez jamais, --admin, --auto, ou ne sautez pas une vérification. Pour les PR bot, mettez en évidence la dépendance et le saut de version (par exemple, les sauts majeurs) dans la barrière pour que l'utilisateur puisse décider avec contexte.
Sous-agents de revue (PR jamais relues) : lorsque l'utilisateur opte, lancez un Agent par PR — tous dans un seul message d'assistant pour qu'ils s'exécutent en parallèle. Chaque sous-agent examine le diff d'une PR et renvoie une revue structurée ; écrivez chaque revue dans github-pr-<numéro>-review.md dans le répertoire de travail (en écrasant tout fichier antérieur pour cette PR). Les revues sont en lecture seule et jamais publiées sur GitHub. Voir references/reviewing-prs.md pour l'invite du sous-agent, la grille de revue et le format de fichier.
Après toute fusion, re-importez la liste des PR fusionnées (la requête --state merged de la Phase 1) afin que la phase des issues puisse détecter les issues que ces fusions ont résolues.
Phase 3 : Classifier chaque issue ouverte
Classez chaque issue ouverte dans exactement un seau.
Seau A — Déjà résolu (le travail a atterri, l'issue est restée ouverte). Nécessite une preuve concrète ; privilégiez la corroboration à un seul signal faible :
- Une PR fusionnée liste l'issue dans
closingIssuesReferences, ou son titre/corps référence#Navec un mot-clé de fermeture. (Le plus fort.) - Un commit sur la branche par défaut référence
#Navec un mot-clé de fermeture. - Le comportement demandé par l'issue existe de manière démontrable dans le code actuel — vérifiez en lisant le code pertinent avec Grep/Read, ne présumez pas. Une implémentation différente ou partielle ne résout pas l'issue.
→ Écriture proposée : fermez l'issue avec un commentaire qui cite la PR/commit de résolution.
gh issue close <N> -R "$REPO" \
-c "Résolu par #<PR> (<raison courte>). Fermeture car la modification est maintenant sur $default_branch."
Seau B — Une PR en attente la résoudrait (une PR ouverte adresse l'issue).
Détectez dans les deux sens : closingIssuesReferences de la PR ouverte inclut l'issue ; le corps/commentaires de l'issue lient la PR ; ou une PR ouverte corrige clairement la même chose que l'issue décrit. Le but est que l'issue et la PR se référencent mutuellement — remplissez uniquement les lacunes réelles et privilégiez les écritures non destructives :
- Aucune référence dans les deux sens → publiez un commentaire indicatif (non destructif) du côté qui en manque. Une mention de
#<PR>/#<N>crée une référence croisée que GitHub reflète dans le fil de l'autre.gh issue comment <N> -R "$REPO" -b "Un correctif est en cours dans #<PR>." - Déjà lié dans au moins un sens → enregistrez "déjà lié — aucune action".
Ne modifiez pas le corps de la PR sauf si l'utilisateur souhaite explicitement que la PR ferme automatiquement l'issue lors de la fusion. Un commentaire établit une référence mais ne déclenche pas la fermeture automatique — seul un mot-clé de fermeture dans le corps de la PR ou un message de commit le fait. Lorsque l'utilisateur opte, n'écrasez jamais la description : re-importez le corps immédiatement avant l'édition et passez-le via stdin, afin que le texte de PR non fiable ne transite jamais par une chaîne interpolée dans le shell :
body=$(gh pr view <PR> -R "$REPO" --json body --jq .body)
printf '%s\n\nCloses #%s\n' "$body" "<N>" | gh pr edit <PR> -R "$REPO" --body-file -
Ne dupliquez pas les liens qui existent déjà.
Seau C — En suspens (aucune résolution, aucune PR en attente). Attribuez, localement uniquement, une priorité et une estimation de taille de modification (section suivante).
Phase 4 : Noter les issues en suspens (LOCAL UNIQUEMENT)
Priorité — Critique / Haute / Moyenne / Basse. Pesez :
- Impact / gravité — les bugs de sécurité, perte de données, crash, ou exactitude sont supérieurs aux améliorations ; la documentation/l'esthétique sont les plus bas. Les labels existants (
security,bug,crash,regression) sont des signaux forts. - Portée — combien d'utilisateurs/flux de travail sont affectés.
- Signal — réactions (👍), rapports en double, âge avec activité continue.
- Urgence — bloque une version, a une échéance, ou a une régression active.
Taille de modification — le proxy d'effort, exprimé comme un seau de t-shirt size/* à partir du nombre estimé total de lignes modifiées (ajouts + suppressions, en ignorant les fichiers générés/vendus), en utilisant le seuil Kubernetes/Prow :
| Taille | Seuil |
|---|---|
size/XS | ≤ 10 lignes |
size/S | ≤ 30 lignes |
size/M | ≤ 100 lignes |
size/L | ≤ 500 lignes |
size/XL | > 500 lignes |
Estimez via inspection de code avec Grep/Read, pas gh. Si l'estimation est difficile, utilisez la taille d'un correctif existant (PR ou commit) qui résout un problème similaire. Documentez le raisonnement pour la priorité et la taille dans la réponse de l'outil.
Règles de sécurité
- Toutes les écritures sont verrouillées. Pas d'appels GitHub
PATCH/POST/DELETE/PUTavant que l'utilisateur n'ait explicitement confirmé l'action complète. Les appels GET (lecture) sont autorisés sans approbation. - Ne jamais forcer la fusion — pas de
--admin, pas de--auto, pas de contournement des vérifications de protection de branche. Si GitHub refuse une fusion, rapportez-le, ne le contournez pas. - Aucune métadonnée publiée. Ne créez, modifiez, ou supprimez jamais de labels, milestones ou jalons. Priorité et taille sont présentées textuellement dans la sortie de l'outil ; ne les approchez même pas via l'API.
- Évitez la persistance. Ne stockez pas les résultats en dehors du fil de discussion — n'écrivez pas dans un fichier, un magasin de clés-valeurs, ou un cache persistant (sauf si l'utilisateur demande explicitement une sortie dans un fichier pour la revue du sous-agent).
- Désinfectez les entrées. Validez
$REPOavec^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$. N'interpolez jamais le numéro, le titre ou le corps d'une issue/PR dans une chaîne de commande shell — passez les valeurs inconnues via stdin ou les options JSON degh. - N'utilisez jamais
--auto. GitHub Actions peut fusionner immédiatement après quegh pr merge --autoactive le bouton ; contrairement à une fusion synchrone, un merge auto ne peut pas vérifier que la PR est encore prête au moment où GitHub la fusionne. Utilisez uniquement la fusion synchrone (--squash,--rebase, ou--mergesans--auto).
Public cible
Développeurs et responsables de maintenance qui trient régulièrement les issues et PR d'un dépôt, en particulier après une période d'inactivité ou avant une version. Utile pour les dépôts avec un nombre modéré d'issues (jusqu'à 1 000) et de PR (jusqu'à 1 000) — les dépôts monolithiques ou anciens peuvent nécessiter plusieurs passages.