merge-briefing

Procédure de demande d'approbation pré-fusion à la demande — sonde les trois surfaces, évalue chaque constatation selon son décor, reporte les lacunes `Unverified` de l'ancre, rédige un briefing en six parties et se termine par une question `AskUserQuestion`.

Qu'est-ce que merge-briefing ?

Cette compétence (skill) pour agent IA (compatible Claude Code, Cursor, Codex et autres) fournit une procédure rigoureuse de demande d'approbation de fusion de pull request (PR) sur GitHub. Elle repose sur un principe central : un merge est un état partagé irréversible, donc chaque PR doit obtenir une réponse explicite de l'utilisateur. La compétence définit six étapes : (1) sonder trois surfaces distinctes (CI, timeline de conversation + revues, fils de commentaires inline) — une seule commande ne suffit pas ; (2) évaluer chaque constatation sur son décor (bloquant/non-bloquant) plutôt que sur son étiquette ; (3) reporter les lacunes `Unverified` de l'ancre de vérification ; (4) composer un briefing structuré en six parties (changement, vérifié, non vérifié, risque/portée, éléments ouverts, demande explicite) ; (5) formuler la question d'approbation via `AskUserChat` ; (6) exécuter le merge ou documenter la raison du refus. Elle gère les cas complexes : dépassement des limites de 100 éléments des API GitHub (nécessitant des appels paginés), état de fusion (`mergeable`, `mergeStateStatus`), approbations de revue par commit, et cohabitation de constatations de bots (CodeRabbit, Copilot). Le briefing est toujours composé à partir des sondages de ce tour, jamais d'informations obsolètes. Un cas particulier de merge groupé (plusieurs PRs, une réponse) est traité, mais toute demande d'approbation est limitée à un seul PR sauf exception explicite.

Compatible avec✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/devseunggwan/praxis/tree/HEAD/skills/merge-briefing

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait merge-briefing ?

Vue d'ensemble

Un merge est un état partagé irréversible, donc il est conditionné à une réponse explicite de l'utilisateur pour chaque PR. La barrière est peu coûteuse à satisfaire formellement et facile à satisfaire faussement : la version honnête nécessite une sonde assez large pour confirmer que "rien n'est ouvert", et la surface où les constatations bloquantes résident réellement — les fils de commentaires inline — est celle qu'un appel gh pr view --json comments,reviews pourtant naturel omet silencieusement.

Cette compétence est le foyer à la demande de cette procédure. La règle Pre-Merge Reporting (ETHOS.md → Rules praxis carries) maintient la barrière ; la liste en six parties de l'étape 4 ci-dessous est la forme du rapport ; les étapes ci-dessous décrivent comment passer de "la PR semble terminée" à une question à laquelle l'utilisateur peut répondre en un mot.

Principe fondamental : le briefing est une affirmation sur l'état de la PR, donc chaque ligne est sondée au moment de la composition — pas rappelée depuis l'ouverture de la PR.

Quand l'utiliser

  • Sur le point de demander à l'utilisateur d'approuver un merge.
  • Un merge a été demandé et vous n'avez pas encore présenté de briefing lors de ce tour.
  • Nouvelle demande après qu'une demande précédente est restée sans réponse, ou après que CI/révision a changé.
  • Une PR compagnon, de suivi, ou régénérée est la suivante dans un cluster que l'utilisateur a déjà approuvé une fois.

La Loi de Fer

  • Une PR, un briefing, une réponse. Approuver la PR X n'approuve que X. La seule exception est le merge groupé : un tour avec une section de briefing séparée par PR, une seule question qui les liste, et une réponse qui approuve l'ensemble listé.
  • Composez le briefing à partir des résultats des sondages de ce tour. Un statut CLEAN au moment de l'entrée n'est pas permanent ; une ancre obsolète fait découvrir à l'utilisateur un conflit que vous auriez dû détecter.
  • Ne mergez jamais sur un signal de progression. "계속", "ok", "progress" font avancer le travail ; ils ne constituent pas un consentement pour une mutation d'état partagé.

Procédure

Étape 1 : Sonder les trois surfaces

Les constatations résident sur trois surfaces et aucune commande unique ne les couvre. Exécutez les trois avant d'écrire un mot du briefing.

# 1 — CI
gh pr checks <N> --repo <owner>/<repo>

# 2 — timeline de conversation + corps de revues + fusionnabilité + HEAD poussé
gh pr view <N> --repo <owner>/<repo> \
  --json mergeable,mergeStateStatus,isDraft,headRefOid,reviews,comments
# 3 — fils de commentaires inline (la surface que #2 N'inclut PAS)
gh api graphql --paginate -f query='
query($owner:String!, $repo:String!, $pr:Int!, $endCursor:String) {
  repository(owner:$owner, name:$repo) {
    pullRequest(number:$pr) {
      reviewThreads(first:100, after:$endCursor) {
        pageInfo { hasNextPage endCursor }
        nodes {
          id isResolved isOutdated path line
          comments(first:1) { nodes { author { login } body } }
        }
      }
    }
  }
}' -F owner=<owner> -F repo=<repo> -F pr=<N> \
  --jq '.data.repository.pullRequest.reviewThreads.nodes[]
        | select(.isResolved | not)
        | {id, path, line, author: .comments.nodes[0].author.login,
           body: .comments.nodes[0].body}'

body est dans la projection car l'étiquette se trouve dans le texte du fil lui-même, et l'étiquette est ce sur quoi l'étape 2 se base. --paginate avec $endCursor n'est pas une décoration — une PR sur laquelle deux bots ont travaillé dépasse 100 fils, et une seule page ignore silencieusement le reste, ce qui est alors interprété comme "aucun fil ouvert".

La surface #2 tronque également à 100, et silencieusement. gh pr view --json comments,reviews émet comments(first: 100) / reviews(first: 100) sans variable de curseur, donc sur une PR dépassant l'un ou l'autre compte, la fin est simplement absente. Lorsque l'un d'eux atteint 100 lignes, recollectez cette surface via le point de terminaison REST paginé avant d'évaluer :

github api repos/<owner>/<repo>/pulls/<N>/reviews --paginate -q '.[] | {user: .user.login, state, body}'
github api repos/<owner>/<repo>/issues/<N>/comments --paginate -q '.[] | {user: .user.login, body}'

Un résultat vide n'est pas encore une réponse. Il signifie à la fois "aucun fil ouvert" et "la requête est erronée" jusqu'à ce que vous ayez vu cette même requête afficher des lignes sur une PR connue pour en avoir. Exécutez-la une fois contre une telle PR ; c'est le contrôle positif sur lequel repose l'affirmation négative.

Dans un fork clone, --repo doit nommer le fork sur chaque sonde : un appel gh pr nu résout par rapport au dépôt parent (voir worktree-merge-cleanup → Liste de vérification pré-appel).

Si CI est en attente ou en échec, arrêtez-vous ici — ne demandez pas encore. Un briefing dont les lignes de vérification sont encore en mouvement demande à l'utilisateur d'approuver une supposition.

Lisez quel commit chaque approbation humaine couvre avant d'écrire "approuvé" : reviewDecision: APPROVED survit aux poussées ultérieures. Groupez les revues par réviseur et prenez la revue la plus récente APPROVED de chacun (une approbation antérieure est remplacée par une ré-approbation ; un CHANGES_REQUESTED ou DISMISSED ultérieur du même réviseur signifie aucune approbation actuelle). Comparez le commit_id de cette revue avec headRefOid et listez les commits non-merge après celui-ci (git log --no-merges <commit_id>..<head>) dans la partie 5 comme "non revu par " (voir la ligne des Modes d'Échec pour la commande).

L'état de fusion est une liste blanche, pas une vérification "pas manifestement cassé". Demandez uniquement lorsque mergeable est MERGEABLE et mergeStateStatus est CLEAN ou HAS_HOOKS — ces deux valeurs sont les seules qui signifient fusionnable avec un statut de commit réussi. Toute autre valeur arrête la demande et se nomme elle-même dans le briefing :

mergeStateStatusSignificationDemander ?
CLEANFusionnable et statut de commit réussiOui
HAS_HOOKSFusionnable, réussi, hooks de pré-réception présentsOui
UNSTABLEFusionnable avec un statut de commit non réussiNon — c'est le CI en échec ci-dessus
BLOCKEDMerge bloqué (règle de protection, revue requise)Non
BEHINDLa référence de tête est obsolèteNon — rebasez d'abord
DIRTYLe commit de merge ne peut pas être créé proprementNon — conflit
UNKNOWNL'état ne peut pas être déterminé actuellementNon — re-sondez

Le statut de brouillon n'est pas dans cette énumération — c'est le champ séparé isDraft, donc une PR en brouillon peut signaler CLEAN. Ajoutez isDraft à la liste --json et traitez un brouillon comme non prêt quel que soit son état de fusion.

CLEAN ne dit rien sur les commits de base qui sont arrivés depuis la création de la branche. Comptez-les (git fetch origin puis git rev-list --count HEAD..origin/<base>) et listez les fichiers qu'ils ont touchés et que la PR touche également (git diff --name-only HEAD...origin/<base>). Pour chaque fichier partagé, cherchez dans ce diff de base les symboles que cette PR modifie et exécutez git merge-tree --write-tree HEAD origin/<base> — zéro symbole trouvé et code de sortie 0 est la preuve "aucun chevauchement sémantique", citée dans la partie 5. Lorsque le déclencheur de rebase pré-merge se produit (3+ commits de base, un cycle de revue terminé), ramenez la base avec gh pr update-branch <N> (un commit de merge, jamais une réécriture), ce qui redémarre CI et la revue du bot : attendez les deux sur le nouveau HEAD, ré-analysez l'ancre avec le HEAD qui a bougé, puis ré-exécutez les étapes 1 à 3 sur le nouveau HEAD et reconstruisez le briefing avant de demander — les constatations, l'état de fusion et la couverture d'approbation collectées plus tôt décrivent l'ancienne tête.

Étape 2 : Évaluer chaque constatation sur les trois surfaces

Un nombre de fils ouverts n'est pas un nombre de bloqueurs, et la surface des fils n'est pas l'ensemble du corpus. Évaluez chaque constatation collectée à l'étape 1 — fil inline, commentaire de conversation, et corps de revue — par le décor, pas l'étiquette :

Première ligne de la constatationBloque le merge ?
issue (bloquant): / suggestion (bloquant):Oui — le merge attend
issue (non-bloquant): / suggestion (non-bloquant):Non
nitpick: / note:Non — non résolu par conception
Pas d'étiquette du toutÉvaluez vous-même — lisez le corps

suggestion n'est pas un échelon inférieur à issue : les deux portent exactement un (bloquant) / (non-bloquant), donc le décor est le jugement entier et l'étiquette dit seulement de quel type de constatation il s'agit. Lire l'étiquette à la place est ainsi qu'un bloqueur déclaré est laissé passer comme "juste une suggestion".

Un corps de revue n'est pas un fil et ne peut pas être résolu, ce qui est exactement pourquoi il reste non lu : rien ne semble ouvert. CodeRabbit dépose ses constatations exploitables dans des sections repliables du corps de revue plutôt que comme des commentaires de ligne, donc cherchez dans les corps collectés à l'étape 1 les mentions Actionable, Nitpick, outside diff, 🧹 et évaluez ce que vous trouvez. Un commentaire de conversation soulevant un défaut est évalué de la même manière. Les constatations bloquantes sur l'une ou l'autre surface arrêtent la demande tout comme une constatation inline — elles n'ont simplement aucun fil à laisser ouvert, donc elles sont portées dans les éléments ouverts du briefing à la place.

Réconciliez le compte CodeRabbit avant que l'évaluation ne soit terminée : comptez ses constatations par les marqueurs `LINE`: ** dans les corps et faites correspondre le total que l'en-tête de la revue indique (par exemple Major comments: N). Répondez à l'ensemble en un commentaire de conversation groupé Corrigé — <sha> (n) / Non corrigé (n) / Faux positif (n), dont les comptes additionnent ce total.

Le cas non étiqueté est le cas courant, pas l'exception : les bots écrivent dans leur propre vocabulaire. CodeRabbit commence par sa propre ligne de sévérité (_🎯 Functional Correctness_ | _🟠 Major_ | …), Copilot et BugBot sans rien. Traitez la sévérité d'un bot comme une entrée pour votre évaluation, jamais comme l'évaluation — et jamais comme une raison pour ignorer la constatation parce que "c'est juste un bot".

Un fil que vous avez évalué comme bloquant a une seule sortie légitime avant le merge : il est corrigé et fermé, ou il est ré-évalué — en modifiant le commentaire pour (non-bloquant) et en postant l'avis Révisé —. Laisser une constatation bloquante dans un suivi sans la ré-évaluer verrouille la branche alors que chaque étiquette se lit comme non-bloquante.

Effacer le REQUEST_CHANGES au niveau de la revue est une étape ultérieure et séparée. Cet état appartient à la revue, qui peut contenir plusieurs constatations, donc il est effacé seulement une fois que chaque constatation bloquante qu'elle contient est corrigée-et-fermée ou ré-évaluée — jamais sur la force d'une seule. La ré-évaluation de la dernière est ce qui la déverrouille ; jusque-là, la revue bloque correctement encore.

Étape 3 : Reporter les lacunes Unverified de l'ancre

Ouvrez l'ancre de vérification de la PR et lisez sa liste Unverified. Une lacune écrite comme "머지 후 실전송으로 확인" meurt avec la PR à moins que quelque chose ne lui survive.

  • Signalez exactement les lacunes qui nommaient une action suivante — une lacune sans action suivante indiquée n'est pas reportée.
  • Demandez, dans le cadre du briefing, s'il faut ouvrir un issue de suivi.
  • Écrivez la réponse dans la révision finale de l'ancre comme Reporté : #N ou Reporté : aucun — <raison> avant le merge.
  • L'écriture de cette révision finale nécessite gh — une session dont la seule surface GitHub est le serveur MCP ne peut pas mettre à jour le corps d'un commentaire lorsque la liste d'outils actifs de ce serveur n'expose aucune mise à jour de commentaire update (confirmez avant de supposer). Dans ce cas, placez la ligne Reporté : dans le commit de merge ou le corps de la PR, et jamais dans un deuxième commentaire d'ancre. Voir anchor-comment-gate/spec.md → Procedure for a gh-less session.
  • Ne créez jamais automatiquement le suivi : un nouvel issue a encore besoin de sa propre revue d'approche d'implémentation d'abord.
  • Lorsque le suivi est enregistré comme un commentaire à la place (Reporté : <URL du commentaire>), placez-le sur un issue qui reste ouvert après le merge — l'issue Ref: de la PR (par exemple un issue hub/parapluie fermé plus tard par une étape de release), jamais l'issue Closes #N, que le merge lui-même ferme et que personne ne rouvre. La même règle s'applique aux notes hors de portée écrites pendant que la PR est ouverte.
  • Lorsque l'issue de report est celui qu'une étape de release post-merge ferme (un issue hub fermé par /release), la release enterre le report exactement comme le ferait une cible Closes. Nommez ce conflit dans le rapport de merge (releasez maintenant et perdez les éléments ouverts, ou releasez après qu'ils soient vérifiés) plutôt que de le résoudre silencieusement d'une manière ou d'une autre. Vérifiez également la cible Closes #N après le merge : GitHub ne la ferme que lorsque la PR est mergée dans la branche par défaut du dépôt, donc une PR vers une base non par défaut (prod alors que la valeur par défaut est dev) la laisse ouverte pour l'étape de release.

Étape 4 : Composer le briefing en six parties

Six parties, dans cet ordre (cette liste est la forme canonique ; elle a été initialement dérivée du modèle de workflow PR de l'auteur) :

  1. Ce qui a changé — ouvrez par une phrase simple de ce que fait la PR et ce qui change pour l'utilisateur si elle est mergée (et si elle ne l'est pas), puis le périmètre (fichiers, changements logiques), pas le titre de l'issue à nouveau. Une partie 1 composée de chiffres et de termes internes est suivie d'un "que fait-elle ?" au lieu d'une réponse, ce qui réinitialise la fenêtre d'approbation et coûte une réintégration complète.
  2. Ce qui a été vérifié — résultat réel, avec la commande citée
  3. Ce qui n'a PAS été vérifié — ignoré, différé, bloqué, en attente de CI
  4. Risque / rayon d'impact — qui casse si c'est faux
  5. Éléments ouverts — chaque constatation que l'étape 2 a évaluée et laissée ouverte, sur les trois surfaces ; les lacunes de l'étape 3
  6. Demande explicite — Approuver le merge ? sur sa propre ligne, dans le texte, avec l'appel AskUserQuestion de l'étape 5 juste après (la ligne reste même si l'outil demande, car c'est l'élément sur lequel la barrière note)

La partie 3 est celle sous pression, et c'est la raison pour laquelle le briefing existe.

Lorsqu'un élément non vérifié est précisément l'étape que le merge déclenche (un déploiement post-merge, une construction d'actif que CI n'a jamais exécutée, une construction multi-plateforme que vous avez exécutée sur une seule plateforme), c'est un risque, pas une note de bas de page : mettez-le dans la partie 4 avec ce qui casse si cette étape échoue et qui d'autre cela bloque (déploiements ultérieurs de la même pile). Pour une PR dont la valeur est de la tenue de registres (verrous de version, catalogues, documentation), la partie 4 dit également en une ligne ce qui se passe si elle n'est pas mergée — l'utilisateur demande cela avant d'approuver une PR à faible enjeu qui déclenche néanmoins un déploiement.

Anti-patrons de la partie 3 : "완료했습니다, 머지할까요?" sans preuve ; une vérification ignorée reléguée à un aparté en fin de document ; signaler au moment du merge ce qui aurait dû être présenté au moment où la PR était prête.

Fermez les éléments de la partie 3 en observant, pas en lisant. Lorsque l'utilisateur vous demande de vérifier les éléments non vérifiés, allez élément par élément et exécutez la sonde la moins coûteuse qui règle la question. Le résultat in situ — une vérification de déploiement qui a depuis réussi, un son de page qui a chargé — est la preuve ; relire le plan de test qui a écrit l'élément est la lecture.

Étape 5 : Demander via AskUserQuestion

L'appel d'outil suit immédiatement la partie 6 ; il est une question unique sur l'état observable du système tout entier, pas un ensemble de questions de portée variable. AskUserChat et AskUserQuestion sont l'un et l'autre acceptables tant que la question est : "Approuver le merge de PR #N ?" — sans incident, scénario, contexte ou prochaine étape.

Quand la réponse est non, demandez des instructions de suivi avant de supposer une instruction spécifique. Si l'utilisateur a répondu plus tôt et que la PR n'a pas changé entre-temps, AskUserQuestion se bloque et AskUserChat reprend la conversation.

Étape 6 : Charger l'étape suivante

L'utilisateur a ditAction
"Oui" / "Approuve"Mergez (gh pr merge <N> --merge --subject ""). Note : une stratégie de merge doit être définie sauf si le dépôt est configuré pour en autoriser une seule ; utilisez --squash ou --rebase si la configuration du dépôt l'exige. Le sujet vide force GitHub à accepter le message de merge par défaut ou le message de titre + body standard, selon la configuration. N'utilisez jamais --delete-branch (c'est l'affaire du script de nettoyage).
"Non"Annulez. Écrivez l'appel de merge dans le journal : "Annonce PR #N — utilisateur a refusé. Raison donnée : <citation de la réponse>." Aucune nouvelle demande ce tour.
Conditionnel ("si CI passe", "une fois que la revue est terminée")Rédigez une observation qui vérifie la condition et déposez-la comme commentaire de suivi, puis annulez ce tour — la condition n'est pas maintenant.

Le champ --subject vide est intentionnel : un message de merge vide médiatise moins le merge ; l'utilisateur peut toujours le modifier dans l'interface utilisateur. Supprimez tout message modèle que GitHub pourrait insérer. Voir commit-message-style/spec.md → merge commits.

Merge groupé : plusieurs PRs, une réponse

Un cluster approuvé une fois contient des PRs qui partagent une frontière logique (un problème nécessitant plusieurs PRs de correctifs), un déclencheur de déploiement, ou une fenêtre de release. La procédure standard ne change pas :

  1. Exécutez les étapes 1 à 4 sur chaque PR du cluster. Si l'une d'elles échoue à l'étape 1, n'allez pas plus loin — aucune PR ne sera mergée ce tour.
  2. Composez une partie 5 qui contient des sous-sections de briefing séparées, une par PR, dans l'ordre : N° de PR, partie 1, partie 2, partie 3, partie 5 (pas de partie 4 séparée — une seule partie 4 pour le cluster entier).
  3. Composez une partie 6 qui nomme toutes les PR que la réponse approuvera : "Approuver le merge de PR #N₁, #N₂, #N₃ ?".
  4. L'utilisateur répond à la question du cluster ; si oui, mergez chaque PR individuellement. Si l'une des PR échoue au merge (conflit né, hook de pré-réception rejeté), annulez les PRs restantes et informez-en.

Échecs de la partie 3

Une revue qui approuve alors que la partie 3 signale des éléments non vérifiés est une revue sans ancre. Le processus est le même dans les deux sens : l'ancre est ce qui rend le briefing une observation plutôt qu'une relecture de plan. Lorsque vous recevez un briefing de revue dont la partie 3 est vide parce que la PR a été mergée rapidement ou parce que l'auteur du brief n'avait pas accès, déposez vos propres sondes de la partie 3 comme pièces jointes. Une ancre vide n'est pas une ancre.

Modes d'échec

Problèmes courants qui ne sont pas des erreurs de protocole mais qui font que le merge échoue à produire un résultat correct :

ProblèmeSymptômeCauseSolution
Constatation de bot invisibleCodeRabbit / Copilot trouve un défaut, mais le fil n'apparaît pas dans reviewThreadsLe défaut est dans le corps de la revue (une section repliable), pas comme commentaire de ligneUtilisez l'étape 2 — cherchez dans les corps de revue Actionable, 🧹
Réponse d'approbation obsolèteLe merge réussit, puis CI échouereviewDecision: APPROVED survit aux nouveaux commits ; une revue plus ancienne montrait toujours APPROVEDVérifiez toujours commit_id de la revue la plus récente par rapport à headRefOid
Liste de commentaires tronquéeUn commentaire au milieu de la conversation n'est pas dans le briefinggh pr view --json comments est limité à 100 — le commentaire était le 101eRe-collectez via l'API REST paginée avant l'évaluation
La PR n'est pas fusionnable mais le briefing dit le contraireL'utilisateur approuve, puis gh pr merge échoue avec une erreur en amontL'état mergeable a changé entre la sonde et le mergeLe merge ne peut pas échouer ici — c'est un échec de sonde. Ré-exécutez l'étape 1 avant d'agir sur la réponse.
Ancre non écriteLe briefing utilise les commits de la revue d'hierLa conversation a continué en privé ou via un autre canalL'étape 1 échoue sur la partie 2 déjà — expliquez pourquoi et ne proposez pas de merge

Commandes de diagnostic

Commandes utiles pour résoudre les problèmes d'approbation qui se chevauchent :

# Vérifier le commit_id d'une revue approuvée la plus récente par réviseur
git log --no-merges <commit_id>..<head>
# Lister les commits de base non vus par la PR (pour l'évaluation de chevauchement sémantique)
git fetch origin
git rev-list --count HEAD..origin/<base>
# Vérifier le chevauchement de fichiers entre la PR et les nouveaux commits de base
git diff --name-only HEAD...origin/<base>

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills associés