CommunityPesquisa e Análise de Dadosgithub.com

chrislacey89/closeout

Etapa final do pipeline principal após /pre-merge, para quando um PR revisado está pronto para ser mesclado e você deseja retornar o workspace a uma base limpa. Use para mesclar, mudar da worktree antes de removê-la, podar o branch mesclado, puxar a base e verificar um estado final limpo.

O que é closeout?

O skill `/closeout` é a etapa final do pipeline de desenvolvimento, projetado para ser executado após `/pre-merge`, quando um Pull Request (PR) foi revisado e está pronto para ser integrado. Ele automatiza o processo de mesclagem do PR, remoção segura da worktree associada, poda do branch local, atualização da branch base e verificação de que o workspace está em um estado limpo. A principal motivação para sua existência é resolver um problema estrutural: worktrees são criadas por `/execute`, mas não havia um mecanismo para removê-las, resultando em um acúmulo sem controle. Além disso, a sequência manual de mesclagem e limpeza era propensa a erros, como remover a worktree enquanto o shell ainda estava dentro dela, causando falsos erros de lint. O skill verifica pré-condições de segurança, como se o PR é mergeável, se as verificações necessárias passaram e se a revisão foi aprovada, e também checa se a revisão cobre o diff atual, comparando um carimbo (stamp) deixado por `/pre-merge` com o HEAD atual. Ele pode ser configurado para ceder o gerenciamento da worktree a ambientes host (como Conductor ou Codespaces) através de `worktree.provisioning` em `.claude/settings.json`. Suporta plataformas como GitHub CLI e sistemas de state-backend (sidecar ou GitHub). É uma ferramenta HITL (Human-in-the-Loop) e não bloqueante, sempre exigindo confirmação antes de mesclar, pois a mesclagem é uma ação externa e difícil de reverter.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/chrislacey89/skills/tree/HEAD/closeout

Installed? Explore more Pesquisa e Análise de Dados skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Perguntar na sua IA favorita

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

Documentação

O que closeout faz?

Pegue um PR revisado e retorne o workspace a uma base limpa — mescle, mude o shell da worktree antes de removê-la, pode o branch mesclado, puxe a base e verifique o estado final. Esta é a etapa final do pipeline: o movimento mais repetido ("Terminei, mescle e me traga de volta a uma base limpa") sai da cabeça do usuário e vai para um skill.

Por Que Isso Existe

/execute Passo 0 cria uma worktree para cada fatia; até este skill existir, nada removia uma. Worktrees são um estoque com entrada e sem saída, então o estoque cresce sem limites (Meadows, Thinking in Systems — a saída estrutural para um estoque com entrada sem saída é adicionar o loop de feedback ausente, não se esforçar mais). A sequência de mesclagem → desmontagem → pull vivia apenas na memória do usuário, e "a memória prospectiva falha silenciosamente" (Norman, The Design of Everyday Things — mova o conhecimento da cabeça para o mundo). A ordenação é genuinamente propensa a erros: remover uma worktree enquanto o cwd do shell ainda está dentro dela deixa o shell em um diretório destruído, e um hook Stop dependente de node_modules disparando dali emite uma parede de falsos erros MODULE_NOT_FOUND que parecem uma regressão real. A ordem de cd-para-a-base-antes-de-remover no Passo 4 é a correção estrutural exatamente para esse deslize.

Este skill orquestra ferramentas existentes (gh pr merge, wt remove / git worktree remove, commit-commands:clean_gone). Ele não introduz estado no sistema de arquivos. Ele verifica o resultado — árvore limpa, em uma base nova, PR mesclado — não apenas que a limpeza foi executada (Meadows, "buscando o objetivo errado": um closeout medido por "foi executado" otimiza o proxy e produz cerimônia frágil).

Posição de Invocação

Este é um skill primário do pipeline que possui a etapa final de mesclagem → limpeza do caminho de entrega padrão:

… → /pre-merge → /compound (dentro do PR, quando existe uma lição) → /pre-merge (reexecutado sobre o commit composto; re-carimba) → [mesclagem + desmontagem da worktree — ESTE SKILL] → [fechamento de issue — Passo 9 prosa]

Use /closeout quando:

  • /pre-merge criou um PR e a revisão está concluída (ou a mudança é trivial o suficiente para não precisar de uma)
  • o PR está aprovado e mergeável, e você deseja que ele seja mesclado e o workspace retornado a uma base limpa
  • o usuário pede para "fechar," "mesclar e limpar," "desmontar a worktree," ou "voltar a uma base limpa"

Não use /closeout quando:

  • o PR ainda não foi criado ou ainda está em revisão — execute /pre-merge primeiro
  • você deseja capturar uma lição durável — isso é /compound, que por padrão já viajou no PR antes de /closeout mesclar (pós-mesclagem /compound é o fallback); /closeout não captura lições
  • você deseja fechar o PRD e as issues da fatia ou reconciliar o frontmatter do artefato de pesquisa — isso é a prosa de limpeza no SYSTEM-OVERVIEW.md Passo 9; /closeout realiza a metade de higiene do git e adia a metade de fechamento de issue e substituição de artefato de pesquisa para o Passo 9, em vez de duplicá-la. O único fechamento que ele faz é o item de trabalho da fatia mesclada no backend sidecar (Passo 3), porque nenhuma linha Closes #N faz isso na mesclagem
  • a mudança não está em um branch de feature (você está no branch base sem nada para mesclar) — não há nada para fechar

Ele é HITL-confirmado e não bloqueante: ele operacionaliza uma etapa final já opcional. Ele nunca auto-mescla — a mesclagem é voltada para o exterior e difícil de reverter, então sempre confirma primeiro.

Processo

Execute isso como uma lista de verificação do tipo faça-confirme (Gawande): realize cada passo e verifique seu resultado antes do próximo. Os passos destrutivos (4–6) são autoprotegidos — confirme as pré-condições de segurança no Passo 2 antes de executá-los.

0. Isolamento provisionado pelo host? Ceder a desmontagem primeiro

Antes da sequência de mesclagem/desmontagem, determine se o isolamento deste repositório é de propriedade do host (Conductor, Codespaces, devcontainers). Quando for, /closeout ainda realiza a mesclagem nativa do GitHub, mas não desmonta a worktree nem poda o branch local — o host possui essa metade do ciclo de vida, e lutar contra isso é a resistência política de Meadows (dois atores empurrando o mesmo estoque). Ceda o estoque da worktree ao host.

A detecção de saída é mais estrita do que a verificação de entrada do /execute e não deve usar a heurística genérica "toplevel ≠ árvore de trabalho primária" sozinha: uma worktree criada pelo pipeline também satisfaz esse teste, então o sinal genérico não pode distinguir "host é dono da desmontagem" de "o pipeline fez isso e deve desmontá-lo." Resolva a partir de .claude/settings.json worktree.provisioning (padrão "auto" quando ausente), e honre uma substituição explícita neste final exatamente como /execute faz na entrada:

  • host — ceder a desmontagem incondicionalmente.
  • pipeline — o pipeline é dono da desmontagem. Execute os Passos 4–6 completos abaixo mesmo se uma variável de ambiente do host estiver presente — uma configuração explícita pipeline significa que /execute provisionou a worktree por conta própria, então /closeout deve desmontá-la (o comportamento anterior ao host). Uma variável de ambiente do host não substitui uma escolha explícita pipeline.
  • auto (padrão) — ceder a desmontagem apenas se uma variável de ambiente do host descrever esta árvore: o bloco abaixo imprime host_owned=yes. Nunca ceder no sinal genérico "toplevel ≠ primária" sozinho, e nunca na mera presença de CONDUCTOR_WORKSPACE_PATH — um shell iniciado sob o Conductor o herda e pode carregar um que nomeia o workspace de um repositório diferente, e ceder com isso deixa uma worktree criada pelo pipeline sem ninguém para desmontá-la.
# host-signal: does a host env var describe THIS tree, not just this shell?
host_owned=no
top=$(cd "$(git rev-parse --show-toplevel)" && pwd -P)
if [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ]; then
  ws=$(cd "$CONDUCTOR_WORKSPACE_PATH" 2>/dev/null && pwd -P) || ws=
  if [ -n "$ws" ]; then
    case "$top/" in "$ws"/*) host_owned=yes ;; esac
  fi
fi
if [ -n "${CODESPACES:-}" ] || [ -n "${REMOTE_CONTAINERS:-}" ]; then host_owned=yes; fi
echo "host_owned=$host_owned"

Se a desmontagem não for cedida (incluindo worktree.provisioning: "auto" com host_owned=no), o pipeline é dono dela — execute os Passos 4–6 completos abaixo como escrito.

Quando o isolamento é de propriedade do host, /closeout executa uma sequência reduzida:

  • Passo 3 (mesclagem) ainda é executado por completo — gh pr merge --delete-branch. Conductor e hosts similares criam o PR, mas deixam a mesclagem para uma ação humana; o pipeline realizar a mesclagem nativa do GitHub é o valor que ele adiciona, e o host reflete o resultado. A delegação é limitada à desmontagem do sistema de arquivos, nunca à mesclagem.
  • Pular a reancoragem do Passo 4, o wt remove / git worktree remove / ExitWorktree { remove } do Passo 5 e a poda do branch local do Passo 6 — o host possui o ciclo de vida da worktree e do branch (Conductor os recupera quando o workspace é arquivado). Nunca use --force contra uma árvore gerenciada pelo host.
  • Lista de verificação do Passo 8: "Worktree removida" e "Branch podado" tornam-se "N/A — gerenciado pelo host." As verificações de mesclagem, árvore limpa, PR mesclado e branch remoto ainda se aplicam. O último é fácil de colocar no mesmo N/A e não é: o que o host possui é a worktree do sistema de arquivos e o branch local, enquanto o branch remoto é deletado pelo gh pr merge --delete-branch que este skill ainda executa por completo no marcador acima. Ceder a metade que o host possui não cede a metade que ele não possui.

1. Confirmar intenção e coletar estado

Nunca auto-mesclar. Declare o que você está prestes a fazer e obtenha uma autorização explícita.

Colete os fatos primeiro (estes são somente leitura):

git branch --show-current                                   # o branch de feature
git rev-parse --show-toplevel                               # a árvore de trabalho atual (pode ser uma worktree)
git worktree list --porcelain | awk '/^worktree /{print $(2); exit}'   # o checkout BASE (árvore de trabalho principal)
gh pr view --json number,title,url,state,mergeable,mergeStateStatus,reviewDecision 2>/dev/null

Determine o branch base que o PR tem como alvo — o padrão declarado do repositório, não uma suposição:

SC="<diretório deste skill>/references/sidecar.sh"
bash "$SC" base-ref --name   # o nome do branch: prod, nunca origin/prod

Este local deseja o nome do branch base, não uma ref — ele alimenta git switch <branch-base> no Passo 4, e git switch rejeita uma ref de rastreamento remoto diretamente (fatal: a branch is expected, got remote branch 'origin/prod') — ele quer o branch base local que você está prestes a puxar. Então ele pede base-ref --name, e deliberadamente não a forma de ref que /pre-merge, /help, /walk-commits, /visual-recap, /compound, e /execute usam; eles precisam de uma ref apenas porque usam a base como um ponto final de intervalo, o que este skill nunca faz. Não "corrija" isso para corresponder a eles. Se base-ref --name sair com código diferente de zero, ele não encontrou nenhuma base: repasse seu stderr e pare, em vez de adivinhar um.

Informe ao usuário, em um bloco curto: qual PR será mesclado, qual worktree (se houver) e branch serão desmontados e para qual branch base você retornará e puxará. Aguarde confirmação. Se não houver nenhum PR neste branch, pare e encaminhe para /pre-merge.

2. Verificar pré-condições de segurança antes de qualquer passo destrutivo

A desmontagem é destrutiva; verifique se é segura primeiro (autoproteção). Todas as seguintes devem ser verdadeiras:

  • O branch de feature está totalmente enviado — git status não mostra commits não enviados, e git log @{u}.. está vazio.
  • Um PR existe para este branch e é mergeável (mergeable é MERGEABLE; mergeStateStatus não é BLOCKED/DIRTY).
  • As verificações necessárias estão verdes e a revisão está aprovada (reviewDecision é APPROVED, ou o repositório não exige revisão e o usuário aceitou explicitamente).

Se qualquer pré-condição falhar, pare e relate qual — não mescle ou remova nada. Uma pré-condição falha é um sinal para retornar à revisão, não para forçar passagem. Quando a que falhou é a revisão — reviewDecision é CHANGES_REQUESTED — o caminho de volta é /respond-to-review: ele verifica cada comentário contra o código, o humano decide o que corrigir e o que contestar, e as correções passam por /fix-findings e uma reexecução de /pre-merge antes de /closeout ser executado novamente.

Atualidade da revisão — a aprovação ainda cobre este diff? (não bloqueante)

As três caixas de seleção acima perguntam se uma revisão ocorreu. Nenhuma delas pergunta se ela cobriu o diff prestes a ser mesclado. No fluxo linear pretendido, essas são a mesma pergunta, que é exatamente por que a lacuna é invisível; em uma sessão HITL real, o escopo se acumula em um branch após sua passagem de revisão, e nada conecta o diff que /pre-merge leu ao diff que /closeout mescla. Isso é uma entrada sem loop de feedback (Meadows — a correção estrutural é adicionar o loop, não se esforçar), e silenciosamente degrada "o PR foi revisado" para "uma versão inicial do PR foi revisada" no momento em que a degradação mais importa. O Continuous Delivery afirma a regra claramente: o artefato que você revisou deve ser o artefato que você entrega.

Leia o carimbo que /pre-merge Fase 4 deixou e compare com o HEAD atual. Ele reside onde o backend stamp disser, por references/state-backend.md: no corpo do PR, ou no sidecar.

git fetch --quiet
SC="<diretório deste skill>/references/sidecar.sh"
case "$(bash "$SC" backend stamp)" in
  sidecar)
    REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)")
    HEAD_SHA=$(git rev-parse '@{u}' 2>/dev/null || git rev-parse HEAD) ;;
  github)
    REVIEWED_SHA=$(gh pr view <número> --json body -q .body \
      | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1)
    HEAD_SHA=$(gh pr view <número> --json headRefOid -q .headRefOid) ;;
  *) echo "closeout abortado: sidecar.sh backend stamp falhou; não adivinhando o caminho do github" >&2; exit 1 ;;
esac

No backend sidecar, o HEAD é o upstream do branch, que é o que a forja mesclará: a primeira pré-condição acima já exige que ele corresponda ao HEAD local. Ele recai para HEAD quando o branch não tem upstream. stamp read imprime o mesmo OID completo de 40 caracteres que o marcador no corpo do PR carrega, ou nada, então tudo abaixo é executado inalterado em qualquer backend.

Exatamente 40 caracteres hexadecimais, comparados por igualdade de string. Ambas as metades dessa frase são estruturais, porque este marcador é um contrato que abrange dois skills. /pre-merge Fase 4 emite o OID completo de 40 caracteres, e headRefOid é sempre de 40 caracteres, então \{40\} é a única largura que pode jamais ser igual — aceitar um mais curto deixaria um marcador malformado (um corpo editado manualmente, um placeholder de template substituído na metade errada) ser analisado com sucesso e então diferir de $HEAD_SHA para sempre. [[ "$REVIEWED_SHA" == "$HEAD_SHA" ]] é a comparação: igualdade exata de string em dois OIDs completos, nunca um teste de prefixo. Um marcador que a regex rejeita produz um $REVIEWED_SHA vazio e segue o ramo "nenhum carimbo encontrado" abaixo, que degrada graciosamente — muito melhor do que o ramo "SHAs diferem" gritando lobo em um PR cujo HEAD nunca se moveu. No repositório Skill Kit, scripts/test-review-currency-marker.sh fixa esta ida e volta contra ambos os skills para que o formato do escritor e este analisador não possam se distanciar silenciosamente.

Três resultados, e apenas um deles interrompe:

  • Os SHAs coincidem — OIDs completos iguais por string, então o diff revisado é o candidato a mesclagem. Passe silenciosamente. Não narre isso: este é o caso comum, e anunciar um não-evento a cada execução é o que transforma uma comporta em papel de parede.
  • Nenhum carimbo encontrado ($REVIEWED_SHA vazio) — um PR escrito à mão, uma contribuição externa, um PR que antecede o carimbo, ou um marcador cuja forma a regex acima rejeitou. Anote em uma linha ("nenhum carimbo /pre-merge neste PR — atualidade da revisão desconhecida") e continue. Ausência de um carimbo não é evidência de um diff não revisado, e parar em cada PR sem carimbo é precisamente a fadiga de alarme que faz uma comporta ser derrotada reflexivamente (Meadows, resistência política).
  • Os SHAs diferem — dois OIDs bem formados de 40 caracteres que não são iguais por string, significando que commits chegaram após a revisão. Não pare,

...

[truncado para tradução]

Individual skills in this repo

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

Habilidades Relacionadas