merge-briefing

Procedimento sob demanda para aprovação pré-merge — uma investigação em três superfícies, classificação de cada achado por sua decoração de bloqueio, transporte das lacunas `Unverified` da âncora para um acompanhamento, o briefing em seis partes e a chamada `AskUserQuestion` que encerra o processo.

O que é merge-briefing?

O merge-briefing é uma skill para agentes de IA (como Claude Code, Cursor ou Codex) que formaliza o processo de aprovação de merge de Pull Requests. Ele garante que nenhum merge seja feito sem uma verificação explícita do usuário, baseada em evidências coletadas em tempo real, e não em dados desatualizados. A skill sonda três superfícies de achados (CI, timeline de conversas e threads de revisão inline), classifica cada achado por sua decoração (blocking/non-blocking), transporta lacunas não verificadas de uma âncora anterior e compõe um briefing em seis partes. O fluxo termina com uma pergunta explícita `Approve merge?` usando a ferramenta `AskUserQuestion`. É especialmente útil para evitar merges cegos, garantindo que o usuário tenha todas as informações necessárias para decidir, incluindo riscos, itens abertos e o que não foi verificado. A skill também lida com casos como merges em massa, PRs em draft, estado de merge `UNSTABLE` ou `BLOCKED`, e a necessidade de re-sondagem após mudanças na base.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que merge-briefing faz?

Visão Geral

Um merge é um estado compartilhado irreversível, portanto, é condicionado à resposta explícita do usuário para cada PR. A barreira é barata de satisfazer formalmente e fácil de satisfazer falsamente: a versão honesta precisa de uma investigação ampla o suficiente para suportar "nada está em aberto", e a superfície onde os achados de bloqueio realmente vivem — threads de revisão inline — é a que uma chamada gh pr view --json comments,reviews de aparência natural omite silenciosamente.

Esta skill é o lar sob demanda para esse procedimento. A regra Pre-Merge Reporting (ETHOS.md → Prática de regras) mantém a barreira; a lista de seis partes na Etapa 4 abaixo é a forma do relatório; as etapas abaixo descrevem como ir de "o PR parece pronto" a uma pergunta que o usuário pode responder em uma palavra.

Princípio central: o briefing é uma afirmação sobre o estado do PR, então cada linha dele é investigada no momento da composição — não lembrada de quando o PR foi aberto.

Quando Usar

  • Prestes a pedir ao usuário para aprovar um merge.
  • Um merge foi solicitado e você ainda não apresentou um briefing nesta rodada.
  • Reperguntando depois que uma pergunta anterior ficou sem resposta, ou depois que CI/revisão avançou.
  • Um PR companheiro, de acompanhamento ou regenerado é o próximo em um cluster que o usuário já aprovou uma vez.

A Lei de Ferro

  • Um PR, um briefing, uma resposta. Aprovar o PR X aprova apenas o X. A única exceção é o merge em massa: uma rodada com uma seção de briefing separada por PR, uma pergunta que os lista e uma resposta que aprova o conjunto listado.
  • Componha o briefing a partir da saída da investigação desta rodada. Um status CLEAN no momento da entrada não é permanente; uma âncora desatualizada faz o usuário descobrir um conflito que você deveria ter capturado.
  • Nunca faça merge com um sinal de progresso. "계속", "ok", "진행" movem o trabalho adiante; não são consentimento para uma mutação de estado compartilhado.

Processo

Etapa 1: Investigar todas as três superfícies

Os achados vivem em três superfícies e nenhum comando cobre todas. Execute todos os três antes de escrever uma palavra do briefing.

# 1 — CI
glh pr checks <N> --repo <dono>/<repo>
# 2 — linha do tempo da conversa + corpos das revisões + capacidade de merge + HEAD enviado
glh pr view <N> --repo <dono>/<repo> \
  --json mergeable,mergeStateStatus,isDraft,headRefOid,reviews,comments
# 3 — threads de revisão inline (a superfície que #2 NÃO inclui)
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=<dono> -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á na projeção porque o rótulo está no texto da própria thread, e o rótulo é o que a Etapa 2 classifica. --paginate com $endCursor não é decoração — um PR em que dois bots trabalharam ultrapassa 100 threads, e uma única página descarta silenciosamente o restante, que então é lido como "nenhuma thread em aberto".

A superfície #2 também trunca em 100, e silenciosamente. gh pr view --json comments,reviews emite comments(first: 100) / reviews(first: 100) sem variável de cursor, então em um PR que ultrapassa qualquer um dos limites, a cauda está simplesmente ausente. Quando um deles está em 100 linhas, colete novamente essa superfície através do endpoint REST paginado antes de classificar:

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

Um resultado vazio ainda não é uma resposta. Significa "nenhuma thread em aberto" e "a consulta está errada" ao mesmo tempo, até que você tenha visto esta consulta exata imprimir linhas em um PR conhecido por tê-las. Execute-a uma vez contra esse PR; esse é o controle positivo no qual a afirmação negativa se baseia.

Em um clone de fork, --repo deve nomear o fork em cada investigação: uma chamada gh pr simples resolve contra o repositório pai (veja worktree-merge-cleanup → Lista de verificação pré-chamada).

Se o CI estiver pendente ou falhando, pare aqui — não pergunte ainda. Um briefing cujas linhas de verificação ainda estão em movimento está pedindo ao usuário para aprovar um palpite.

Leia qual commit cada aprovação humana cobre antes de escrever "aprovado": reviewDecision: APPROVED sobrevive a pushes posteriores. Agrupe as revisões por revisor e pegue a última revisão APPROVED de cada um (uma aprovação anterior é substituída por uma re-aprovação; um CHANGES_REQUESTED ou DISMISSED posterior do mesmo revisor significa que não há aprovação atual). Compare o commit_id dessa revisão com headRefOid e liste os commits não-merge após ele (git log --no-merges <commit_id>..<head>) na parte 5 como "não revisado por " (veja a linha de Modos de Falha para o comando).

O estado de merge é uma lista de permissões, não uma verificação "não obviamente quebrado". Pergunte apenas quando mergeable for MERGEABLE e mergeStateStatus for CLEAN ou HAS_HOOKS — esses dois são os únicos valores que significam mergeável com status de commit aprovado. Qualquer outro valor interrompe a pergunta e se nomeia no briefing:

mergeStateStatusO que significaPerguntar?
CLEANMergeável e com status de commit aprovadoSim
HAS_HOOKSMergeável, aprovado, com hooks de pré-recebimento presentesSim
UNSTABLEMergeável com um status de commit não aprovadoNão — é o CI falhando acima
BLOCKEDMerge está bloqueado (regra de proteção, revisão necessária)Não
BEHINDO ref head está desatualizadoNão — faça rebase primeiro
DIRTYO commit de merge não pode ser criado de forma limpaNão — conflito
UNKNOWNO estado não pode ser determinado no momentoNão — re-consulte

O status de rascunho não está neste enum — é o campo separado isDraft, então um PR em rascunho pode reportar CLEAN. Adicione isDraft à lista --json e trate um rascunho como não pronto, independentemente do seu estado de merge.

CLEAN não diz nada sobre commits base que chegaram desde que o branch foi cortado. Conte-os (git fetch origin e depois git rev-list --count HEAD..origin/<base>) e liste os arquivos que eles tocaram e que o PR também toca (git diff --name-only HEAD...origin/<base>). Para cada arquivo compartilhado, procure nesse diff da base os símbolos que este PR altera e execute git merge-tree --write-tree HEAD origin/<base> — zero acertos de símbolo e saída 0 são a evidência para "nenhuma sobreposição semântica", citada na parte 5. Quando o gatilho de rebase pré-merge disparar (3+ commits base, uma rodada de revisão concluída), traga a base com gh pr update-branch <N> (um commit de merge, nunca uma reescrita), que reinicia o CI e a revisão do bot: aguarde ambos no novo head, re-revise a âncora com o head que se moveu, depois re-execute as Etapas 1–3 contra o novo head e reconstrua o briefing antes de perguntar — os achados, estado de merge e cobertura de aprovação coletados anteriormente descrevem o head antigo.

Etapa 2: Classificar cada achado em todas as três superfícies

Uma contagem de threads abertas não é uma contagem de bloqueadores, e a superfície de threads não é o corpus inteiro. Classifique cada achado que a Etapa 1 coletou — thread inline, comentário de conversa e corpo de revisão — pela decoração, não pelo rótulo:

Primeira linha do achadoBloqueia o merge?
issue (blocking): / suggestion (blocking):Sim — o merge espera
issue (non-blocking): / suggestion (non-blocking):Não
nitpick: / note:Não — não resolvido por design
Nenhum rótuloClassifique você mesmo — leia o corpo

suggestion não é um degrau inferior a issue: ambos carregam exatamente um de (blocking) / (non-blocking), então a decoração é o julgamento completo e o rótulo apenas diz que tipo de achado é. Ler o rótulo em vez disso é como um bloqueador declarado é liberado como "apenas uma sugestão".

Um corpo de revisão não é uma thread e não pode ser resolvido, que é exatamente por que ele fica sem ser lido: nada nele parece aberto. O CodeRabbit arquiva seus achados acionáveis em seções colapsáveis do corpo da revisão em vez de como comentários de linha, então procure nos corpos coletados na Etapa 1 por Actionable, Nitpick, fora do diff, 🧹 e classifique o que encontrar. Um comentário de conversa levantando um defeito é classificado da mesma forma. Achados bloqueadores em qualquer uma das superfícies interrompem a pergunta como um inline faria — eles simplesmente não têm thread para deixar aberta, então são carregados nos itens abertos do briefing.

Reconcilie a contagem do CodeRabbit antes que a classificação termine: conte seus achados pelos marcadores `LINES`: ** nos corpos e corresponda ao total que o cabeçalho da revisão declara (por exemplo, Major comments: N). Responda ao conjunto em um comentário de conversa agrupado Fixed — <sha> (n) / Not fixed (n) / False positive (n), cujas contas somam esse total.

O caso sem rótulo é o comum, não a exceção: bots escrevem em seu próprio vocabulário. CodeRabbit abre com sua própria linha de gravidade (_🎯 Functional Correctness_ | _🟠 Major_ | …), Copilot e BugBot com nenhuma. Trate a gravidade de um bot como uma entrada para sua nota, nunca como a nota — e nunca como uma razão para pular o achado porque "é só um bot".

Uma thread que você classificou como bloqueadora tem uma saída legítima antes do merge: ela é corrigida e fechada, ou é reclassificada — editando o comentário para (non-blocking) e postando o aviso Revised —. Estacionar um achado bloqueador em um acompanhamento sem reclassificar deixa o branch bloqueado enquanto todo rótulo lê não bloqueador.

Limpar o nível de revisão REQUEST_CHANGES é uma etapa separada e posterior. Esse estado pertence à revisão, que pode conter vários achados, então é limpo apenas quando cada achado bloqueador nela é corrigido e fechado ou reclassificado — nunca com base em um único. Reclassificar o último é o que o desbloqueia; até então, a revisão ainda bloqueia corretamente.

Etapa 3: Transportar as lacunas Unverified da âncora

Abra a âncora de verificação do PR e leia sua lista Unverified. Uma lacuna escrita como "머지 후 실전송으로 확인" morre com o PR a menos que algo a sobreviva.

  • Superficie exatamente aquelas lacunas que nomearam uma próxima ação — uma lacuna sem próxima ação declarada não é transportada.
  • Pergunte, como parte do briefing, se deve abrir um issue de acompanhamento.
  • Escreva a resposta na revisão final da âncora como Carried: #N ou Carried: none — <motivo> antes do merge.
  • Escrever essa revisão final precisa de gh — uma sessão cuja única superfície GitHub é o servidor MCP não pode atualizar o corpo de um comentário quando a lista de ferramentas ativas desse servidor não expõe um update de comentário (confirme antes de assumir). Lá, coloque a linha Carried: no commit de merge ou no corpo do PR, e nunca em um segundo comentário de âncora. Veja anchor-comment-gate/spec.md → Procedimento para uma sessão sem gh.
  • Nunca arquive automaticamente o acompanhamento: um novo issue ainda precisa de sua própria revisão de abordagem de implementação primeiro.
  • Quando o acompanhamento for registrado como um comentário em vez disso (Carried: <URL do comentário>), coloque-o em um issue que permaneça aberto após o merge — o issue Ref: do PR (por exemplo, um issue hub/guarda-chuva fechado posteriormente por uma etapa de release), nunca o issue Closes #N, que o próprio merge fecha e ninguém reabre. O mesmo vale para notas fora de escopo escritas enquanto o PR está aberto.
  • Quando o issue de transporte é aquele que uma etapa de release pós-merge fecha (um issue hub fechado por /release), o release enterra o transporte exatamente como um alvo Closes faria. Nomeie esse conflito no relatório de merge (libere agora e perca os itens abertos, ou libere depois que eles forem verificados) em vez de resolvê-lo silenciosamente de qualquer forma. Verifique também o alvo Closes #N após o merge: o GitHub o fecha apenas quando o PR é mesclado no branch padrão do repositório, então um PR em uma base não padrão (prod quando o padrão é dev) o deixa aberto para a etapa de release.

Etapa 4: Compor o briefing de seis partes

Seis partes, nesta ordem (esta lista é a forma canônica; foi originalmente derivada do modelo de fluxo de trabalho de PR do dotfiles do autor):

  1. O que mudou — abra com uma frase simples sobre o que o PR faz e o que muda para o usuário se ele for mesclado (e se não for), depois o escopo (arquivos, alterações lógicas), não o título do issue novamente. Uma parte 1 feita de contagens e abreviações internas recebe "o que faz?" de volta em vez de uma resposta, o que redefine a janela de aprovação e custa um re-briefing completo.
  2. O que foi verificado — saída real, com o comando citado
  3. O que NÃO foi verificado — pulado, adiado, bloqueado, CI pendente
  4. Risco / raio de explosão — quem quebra se isso estiver errado
  5. Itens abertos — cada achado que a Etapa 2 classificou e deixou aberto, em todas as três superfícies; as lacunas da Etapa 3
  6. Pergunta explícita — Approve merge? em sua própria linha, no texto, com a chamada AskUserQuestion da Etapa 5 logo após (a linha permanece mesmo que a ferramenta pergunte, porque é o item que a barreira avalia)

A parte 3 está sob pressão, e é a razão pela qual o briefing existe.

Quando um item não verificado é exatamente a etapa que o merge desencadeia (um deploy pós-merge, uma construção de assets que o CI nunca executou, uma construção de múltiplas plataformas que você executou em uma plataforma), é um risco, não uma nota de rodapé: coloque-o na parte 4 com o que quebra se essa etapa falhar e quem mais ela bloqueia (deploys posteriores da mesma pilha). Para um PR cujo valor é contábil (pinos de versão, catálogos, docs), a parte 4 também diz em uma linha o que acontece se ele não for mesclado — o usuário pergunta isso antes de aprovar um PR de baixo risco que ainda assim dispara um deploy.

Anti-padrões da parte 3: "완료했습니다, 머지할까요?" sem evidência; uma verificação pulada rebaixada para um aparte final; relatar no momento do merge o que deveria ter aparecido no momento em que o PR estava pronto.

Feche os itens da parte 3 observando, não lendo. Quando o usuário pedir para você verificar os itens não verificados, vá item por item e execute a investigação mais barata que resolve o item (veja o repositório pai para comandos de exemplo). Fechar através da leitura significa reafirmar a linha da âncora sem nova evidência — "verificado por" muda para o seu nome, mas o achado ainda não foi sondado.

Etapa 5: Chamar AskUserQuestion

A última ferramenta na sequência. Nenhum merge deve acontecer sem uma resposta explícita do usuário. A chamada deve ser:

<ask_question>
  <question>Todos os itens acima foram revisados. **Aprovar merge?**</question>
</ask_question>

Merge em massa: vários PRs, uma resposta

Uma rodada de merge em massa segue as mesmas regras com uma diferença: uma pergunta, uma lista.

A Etapa 4 produz uma seção por PR, cada uma com suas próprias seis partes. A Etapa 5 pergunta uma vez, listando todos os PRs cobertos. O usuário responde uma vez e a resposta aprova o conjunto inteiro listado.

Nunca agrupe PRs que tenham qualquer item da parte 3 abertos — cada um precisaria de sua própria "verifique para mim" em vez de um único "tudo limpo" coletivo. Se houver um único item aberto, execute um merge individual em vez disso.

Procedimento de merge em massa

  1. Para cada PR, execute as Etapas 1–4 de forma independente.
  2. Combine todos os briefings em uma única mensagem, ordem de PR.
  3. Pergunte uma vez. A resposta aprova o lote.
  4. Mescle cada PR individualmente, na ordem, com gh pr merge --merge --auto.
  5. Seja explícito no caso de falha — "Os PRs 1-5 foram mesclados. O PR 6 falhou no merge — motivo" — porque a resposta não se aplica mais ao conjunto inteiro.

Restrições para merge em massa

  • Todos os PRs devem ter o mesmo headRefOid ou uma cadeia linear (PR 2 depende do PR 1).
  • Nenhum PR pode depender de um PR fora do lote.
  • Nenhum PR pode ter um achado de bloqueio ou item da parte 3.

Modos de Falha

A thread inline não se parece com a classificação esperada

Um comentário que abre uma thread é classificado no corpo, não no título. Quando a primeira linha parece um rótulo de modelo mas não é — suggestion (blocking) escrito em texto simples por um humano, por exemplo — classifique pelo texto como se não houvesse modelo. O modelo só importa se a correspondência for exata.

"Nenhum achado" não significa "nenhuma thread"

A execução da Etapa 1 que retorna vazia para threads inline (superfície #3) significa que ou (a) todas as threads estão resolvidas, (b) a consulta está quebrada, ou (c) o token não tem escopo. Execute a consulta contra um PR conhecido por ter threads abertas (veja o repositório pai para uma lista de verificação de exemplos). Quando isso imprimir linhas, o resultado vazio é confiável.

O commit headRefOid não corresponde a nenhum commit contido

Isso acontece quando gh pr view retorna um commit que o merge rápido (fast-forward) do GitHub já avançou além, ou quando um push de força (force-push) reescreveu o head. Re-investigue a partir da Etapa 1 com --repo e headRefOid corrigido.

Commits não revisados após o headRefOid

git log --no-merges <commit_id_da_revisão>..<head> --oneline

Quando o commit_id da última revisão APPROVED é anterior a headRefOid, esses commits não foram revisados por esse humano. Liste-os na parte 5. Não conte aprovações de bots perto de commits não revisados — uma aprovação de bot em um commit que não cobre a diff completa ainda conta como aprovação de bot.

Rebase de branch base

Um rebase da base (por exemplo, dev é rebaseada contra master e depois o PR é rebaseado contra dev) não muda headRefOid para o PR, mas sim o hash do commit de merge que gh pr update-branch cria. Execute a Etapa 1 novamente — a headRefOid que gh pr view retorna é o novo head, e as revisões no head antigo expiram.

O token do GitHub expirou no meio da sessão

A consulta GraphQL e as chamadas REST da Etapa 1 falham com 401. Re-autentique (gh auth login) e repita as Etapas 1–3. Se re-autenticar não funcionar, informe ao usuário que o merge não pode prosseguir — nunca faça merge sem investigação.

O CodeRabbit postou uma nova revisão enquanto você lia

A contagem de achados na Etapa 2 fica desatualizada. Re-execute a Etapa 1 para a superfície de revisão e reconcilie a contagem do CodeRabbit novamente. Se novos achados bloqueadores aparecerem, a Etapa 5 não deve ocorrer. A Etapa 4 deve ser reconstruída.

Referências

Individual skills in this repo

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

Habilidades Relacionadas