Pré-Merge
Crie um PR no GitHub vinculando de volta ao PRD e às issues de slice, depois revise o diff completo contra os princípios arquiteturais do projeto. Produz recomendações consultivas — não bloqueia merge, não corrige código automaticamente nem cria issues.
Posição de Invocação
Esta é uma skill primária de pipeline usada após a implementação ter sido verificada e antes de mergiar na main, ou ao revisar o PR de outra pessoa.
Use /pre-merge quando o branch estiver pronto para criação de PR, revisão arquitetural e reconciliação final do plano com o código. Use /pre-merge --pr <número> ao revisar um PR que você não escreveu.
Não use como substituto para verificação de implementação, triagem de QA ou planejamento de refatoração. Assume que o trabalho já está construído e pronto para revisão.
Modos
/pre-merge executa em um de três modos. Todos os três reutilizam as dimensões de revisão arquitetural definidas em review-checklist.md; eles diferem no que consomem e no que produzem.
- Modo Autor (padrão) — invocado em seu próprio branch sem argumento
--pr. A skill cria o PR (Fase 2) e imprime descobertas no terminal como recomendações (Fase 4), então retorna o controle — ela não faz commits de correção próprios e não abre uma segunda passagem. Este é o modo auto-invocado pelo/executePasso 6 no caminho HITL. O que lê é decidido pelo estado do branch, não por uma flag: uma primeira revisão lê todo o slice, que é o branch inteiro, a menos que o work item do slice registre onde começou; uma reexecução contra um PR que já tem um carimbo de moeda de revisão, cuja head mudou desde então, toma o delta pós-carimbo — os commits após o SHA carimbado — como assunto da revisão, e não relê o que a revisão anterior cobriu. A Fase 1 passo 4 toma essa decisão e a imprime. O delta é o assunto, não o limite do que um revisor pode ler:review-checklist.mddetermina o que um revisor lê, e isso inclui a árvore ao redor do delta. - Modo Revisor — invocado como
/pre-merge --pr <número>contra um PR que você não escreveu. A skill pula a criação do PR (o PR já existe) e produz texto de comentário rascunho (Fase 4) para você revisar e postar, estruturado conformereferences/comment-craft.md(gate 5P, Triple-R, Sinais de Comentário, Troca MMG). - Modo Loop — invocado como
/pre-merge --loop, ou entrado automaticamente quando/executepassa o bastão em uma execução AFK (longe do teclado). Executa as Fases 1–4 como o modo autor, exceto pela decisão de escopo da Fase 1 passo 4: o modo loop sempre lê o branch inteiro e nunca pega o delta pós-carimbo, então continua para a Fase 5: registra cada descoberta em um registro durável — no corpo do PR, ou no work item, onde references/state-backend.md § Onde os registros da revisão vivem diz — anexa as evidências que suportam ou refutam cada uma, e retorna o controle ao operador. As descobertas param de ser saída de terminal neste modo; elas se tornam linhas duráveis com responsáveis. O modo loop não corrige nada e não faz commits — o operador decide o que acontece com cada descoberta (veja Fase 5 § Por que o loop não corrige).
A condição de saída do modo loop é que cada descoberta tenha um responsável — nunca que a revisão tenha voltado limpa. "Zero descobertas" é um sinal de terminação proibido, consistente com a proteção de descobertas mínimas existente da Fase 4, que foi escrita porque uma revisão quase vazia em um diff não trivial significa que a revisão parou cedo, não que o código está impecável. Um loop que termina em revisões limpas produzirá confiavelmente revisões limpas e nada mais (Meadows, Procurando o Objetivo Errado; Leveson: nunca recompense baixas contagens de incidentes — recompense a notificação em si).
Se você está executando em um branch diferente do branch de trabalho do usuário e --pr não foi fornecido, pergunte uma vez se o usuário quer dizer o modo revisor contra um número de PR específico, em vez de adivinhar — a detecção automática economiza um toque de tecla, mas classificar o modo incorretamente produz comentários de rascunho que seriam recomendações locais ou vice-versa.
Quando Usar
- Modo Autor: após o QA passar e antes de mergear um branch de feature na main; após Ralph terminar a execução AFK e você verificar o comportamento; ou para qualquer branch que você queira revisado antes do merge, mesmo sem uma execução completa de pipeline.
- Modo Revisor: quando um colega de equipe ou contribuidor externo abrir um PR e você quiser aplicar a revisão arquitetural completa em
review-checklist.mdao diff deles e produzir texto de comentário construtivo. - Modo Loop: quando as descobertas precisam sobreviver à sessão que as produziu — o handoff AFK do
/execute, ou qualquer branch que você revisará em várias sentadas e não quiser re-derivar cada vez. Use quando o custo que você está pagando é perder o rastro das descobertas, não fazer as correções.
Fluxo de Execução
Fase 1: Coletar Contexto
Modo Autor:
-
Pergunte pelo número da issue do PRD. Aceite "nenhum" se esta mudança não passou pelo pipeline completo. No modo loop entrado a partir de um handoff
/executeAFK, não pergunte — pegue o número da issue do handoff (/executePasso 6 o passa) e trate sua ausência como "nenhum". Não há ninguém para responder, então uma pergunta incondicional aqui travaria a execução no primeiro passo. No backend sidecar, a resposta é um ID de work itemW-<n>(passo 2). "Nenhum" lá deixa a Fase 2 sem nada para registrar o PR, e as Notas de Revisão e o registro do modo loop sem lar: references/state-backend.md § Onde os registros da revisão vivem diz o que acontece com eles. -
Se um PRD foi fornecido:
gh issue view <número> gh issue list --search "in:body #<número-prd>" --state all --json number,title,state,body --limit 100Analise os mapas de fronteira (seções Produz/Consome) do corpo de cada issue de slice.
No backend sidecar (
bash "$SC" backend slicesrespondesidecar, conforme references/state-backend.md; o bloco abaixo pergunta, e para em uma recusa em vez de recorrer aogh issue), o PRD e suas slices são work items, e o número perguntado no passo 1 é um IDW-<n>: o slice que este branch implementa, que/executepassa, ou seu PRD. Nenhum comandogh issueé executado; a linhagem e os mapas de fronteira vêm dos work items:SC="<diretório desta skill>/references/sidecar.sh" W="W-<n>" case "$(bash "$SC" backend slices)" in sidecar) ;; github) echo "backend é github: leia a linhagem com o bloco de issue acima"; exit 0 ;; *) echo "linhagem abortada: sidecar.sh backend slices falhou; não adivinhando o caminho github" >&2; exit 1 ;; esac J="$(bash "$SC" work view "$W" --json)" || { echo "linhagem abortada: não é possível ler $W" >&2; exit 1; } case "$(jq -r '.kind // empty' <<<"$J")" in slice) PRD="$(jq -r '.parent // empty' <<<"$J")" ;; prd) PRD="$W" ;; *) echo "linhagem abortada: $W não tem kind" >&2; exit 1 ;; esac SLICES="$W" if [ -n "$PRD" ]; then bash "$SC" work view "$PRD" --json | jq -r '"# \(.id) (prd, \(.state))\n\(.body)"' SLICES="$(bash "$SC" work list --kind slice --parent "$PRD" --state all)" \ || { echo "linhagem abortada: não é possível listar as slices de $PRD" >&2; exit 1; } fi while IFS= read -r S; do [ -n "$S" ] || continue bash "$SC" work view "$S" --json | jq -r '"# \(.id) (slice, \(.state))\n\(.body)"' done <<<"$SLICES"Analise os mapas de fronteira desses corpos exatamente como dos corpos de issue. Mantenha
$W: a Fase 2 registra o PR nele. -
Detecte o branch base e avalie o diff:
SC="<diretório desta skill>/references/sidecar.sh" # $BASE_REF é o endpoint do intervalo; $BASE_BRANCH é o nome que `--base` recebe. BASE_REF="$(bash "$SC" base-ref)" || { echo "base-ref falhou: não adivinhando uma base" >&2; exit 1; } BASE_BRANCH="$(bash "$SC" base-ref --name)" || { echo "base-ref --name falhou: não adivinhando uma base" >&2; exit 1; } git diff "$BASE_REF...HEAD" --stat git log --oneline "$BASE_REF..HEAD"base-refimprime obase_refdeclarado do sidecar quando há um, e caso contrário o padrãoorigin/HEAD. Em uma saída diferente de zero, não imprime nenhuma ref: pare e repasse seu stderr em vez de adivinhar uma base. Para uma slice de PR empilhado, nenhuma variável definida aqui alcança um bloco posterior: coloque o nome do branch do slice irmão (o upstream para o qual o PR vai mirar) no placeholderBASE_BRANCH=de cada bloco que o consome (o bloco da Fase 2 para seu backend e o bloco de carimbo da Fase 4). Se não houver diff da base, informe ao usuário que não há nada para revisar e pare.Residual:
$BASE_REFé tão recente quanto o últimogit fetch. Quandoorigin/<base>não resolve,base-refmede contra o branch local e avisa no stderr; se as contagens parecerem erradas,git fetche execute novamente. -
Decida o escopo — o slice inteiro, ou o delta pós-carimbo. Esta decisão é apenas do modo autor. O modo loop a pula e define
SCOPE_FROM="$BASE_REF"incondicionalmente, lendo o branch inteiro em cada invocação, conforme o bullet do modo loop da Fase 3. Uma primeira revisão lê o slice inteiro:sidecar.sh rangedá seu extremo esquerdo, que é o início registrado do slice quando o work item nomeado no passo 2 ainda tem um neste branch, e$BASE_REFcaso contrário, então no backendgithubum slice é o branch inteiro. Uma reexecução contra um PR que esta skill já carimbou, cuja head se moveu desde então — uma rodada/fix-findings, ou uma correção manual após uma exclusão deliberada de bloqueio — pega o delta pós-carimbo como seu assunto. O carimbo vive ondebackend stampdiz, conforme references/state-backend.md: o corpo do PR, ou o sidecar. Leia o corpo do PR com a mesma expressão que o Passo 2 do/closeoutusa;scripts/test-review-currency-marker.shfixa os dois idênticos em bytes, escripts/test-delta-review-scope.shexecuta este bloco contra uma fixture para cada estado de branch que nomeia, em ambos os backends:SC="<diretório desta skill>/references/sidecar.sh" case "$(bash "$SC" backend stamp)" in sidecar) REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)") ;; github) REVIEWED_SHA=$(gh pr list --head "$(git branch --show-current)" --json body -q '.[0].body // ""' \ | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1) ;; *) echo "escopo abortado: sidecar.sh backend stamp falhou; não adivinhando o caminho github" >&2; exit 1 ;; esac W="<o W-<n> nomeado no passo 2, ou vazio no backend github>" BASE_REF="$(bash "$SC" base-ref)" || { echo "escopo abortado: sidecar.sh base-ref falhou" >&2; exit 1; } RANGE="$(bash "$SC" range ${W:+"$W"})" || { echo "escopo abortado: sidecar.sh range falhou" >&2; exit 1; } SLICE_FROM="${RANGE%%..*}" SCOPE_FROM="$SLICE_FROM" if [ -n "$REVIEWED_SHA" ] \ && git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \ && [ "$REVIEWED_SHA" != "$(git rev-parse HEAD)" ] \ && [ -z "$(git log --merges --format=%H "$REVIEWED_SHA..HEAD")" ]; then SCOPE_FROM="$REVIEWED_SHA" elif [ -n "$REVIEWED_SHA" ] \ && ! git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \ && [ "$(git rev-parse -q --verify "$REVIEWED_SHA^{tree}")" = "$(git rev-parse 'HEAD^{tree}')" ]; then SCOPE_FROM="HEAD" fi if [ "$SCOPE_FROM" = HEAD ]; then echo "escopo da revisão: nenhum, reescrita apenas de histórico — HEAD carrega a árvore revisada em $REVIEWED_SHA" elif [ "$SCOPE_FROM" = "$BASE_REF" ]; then echo "escopo da revisão: branch inteiro, de $BASE_REF" elif [ "$SCOPE_FROM" = "$SLICE_FROM" ]; then echo "escopo da revisão: slice inteiro, de $SLICE_FROM (início registrado de $W)" else echo "escopo da revisão: delta pós-carimbo, de $SCOPE_FROM" fi git diff "$SCOPE_FROM...HEAD" --stat git log --oneline "$SCOPE_FROM..HEAD"Quatro estados caem para o slice inteiro, cada um intencionalmente: sem carimbo (uma primeira revisão, ou um PR escrito à mão); um carimbo que não é ancestral de
HEADe cuja árvoreHEADnão carrega mais (o branch foi reescrito e o conteúdo mudou, ou o commit revisado sumiu —/closeoutchama isso de sinal de divergência mais forte, e não é uma revisão menor); um carimbo igual aHEAD(nada novo para escopo); e um commit de merge após o carimbo (o diff pós-carimbo carregaria as próprias mudanças do branch base, que ninguém neste branch escreveu). O slice inteiro é o branch inteiro a menos querangetenha encontrado um início registrado, e quando encontra um que uma dobra ou rebase reescreveu, ele avisa no stderr e responde$BASE_REF, nunca reancorando o início. A decisão é impressa para que a leitura do terminal e o PR digam a mesma coisa sobre o que foi lido.Uma reescrita que manteve a árvore revisada não é uma divergência. Quando o commit carimbado não é ancestral de
HEADmas ainda existe localmente e carrega a mesma árvore queHEAD, o histórico mudou e o conteúdo não: a dobra que a entrega da Fase 4 oferece (git rebase -i --autosquash) produz exatamente isso. O bloco imprimeescopo da revisão: nenhume defineSCOPE_FROM="HEAD". Pule a Fase 3 e tudo na Fase 4 exceto o carimbo, que recarimba emHEAD, e o menu de entrega. O carimbo certifica o conteúdo revisado, e um hash de árvore é aquele conteúdo exatamente. Se o commit carimbado não estiver disponível localmente, a árvore não pode ser comparada, e a reescrita pega a fallback de branch inteiro acima.Em uma reexecução de delta, o delta é o assunto e a árvore ainda é legível. Instrua cada revisor da Fase 3 com as descobertas que os commits pós-carimbo afirmam fechar — a leitura anterior nesta sessão, o registro se o modo loop escreveu um (onde references/state-backend.md § Onde os registros da revisão vivem diz), ou as próprias mensagens de commit — com os vereditos de quebra
/fix-findingsquando houver algum, e com a saídabash "$SC" profile, ipsis litteris, que o contrato da Fase 3 entrega a todo revisor. A primeira pergunta por commit é se a correção fecha sua descoberta em todo local que a descoberta nomeou, que é a dimensão Completeza da Correção; a segunda é...