chrislacey89/cierre

Paso final de la tubería principal después de /pre-merge, cuando un PR revisado está listo para integrarse y deseas devolver el espacio de trabajo a una base limpia. Se usa para fusionar, cambiar del worktree antes de eliminarlo, podar la rama fusionada, extraer la base y verificar un estado final limpio. Se activa con 'close out', 'merge and clean up', 'get back to a clean main/base', 'tear down the worktree'. No es para capturar lecciones (eso es /compound), ni para cerrar el PRD y otros slices abiertos o la higiene del artifacto de investigación (eso es la prosa de limpieza en SYSTEM-OVERVIEW Paso 9).

¿Qué es cierre?

Este skill automatiza el proceso de cierre de un Pull Request (PR) revisado, devolviendo el espacio de trabajo a un estado limpio y estable. Es el paso final de una tubería de desarrollo, ejecutándose después de `/pre-merge` y antes de cualquier cierre de issues. Su propósito es eliminar la carga cognitiva del desarrollador al tener que recordar la secuencia correcta de pasos: fusionar el PR, salir del worktree, eliminar el worktree, podar la rama local, volver a la rama base y actualizarla. El skill integra herramientas de GitHub CLI y Git, asegurando que el entorno de trabajo quede listo para la siguiente tarea. También incluye una verificación crucial de

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

Installed? Explore more Investigación y análisis de datos skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace cierre?

Toma un PR revisado y devuelve el espacio de trabajo a una base limpia: fusiona, cambia el shell del worktree antes de eliminarlo, poda la rama fusionada, extrae la base y verifica el estado final. Este es el paso final de la tubería: el movimiento más repetido ("He terminado, fúndelo y devuélveme a una base limpia") se saca de la cabeza del usuario y se convierte en un skill.

Por Qué Existe

/execute Paso 0 crea un worktree para cada slice; hasta que existía este skill, nada eliminaba uno. Los worktrees son un stock con una entrada y sin salida, por lo que el stock aumenta sin límite (Meadows, Thinking in Systems — la solución estructural para un stock con entrada sin salida es agregar el bucle de retroalimentación faltante, no esforzarse más). La secuencia de fusión → desmontaje → extracción solo existía en la memoria del usuario, y "la memoria prospectiva falla en silencio" (Norman, The Design of Everyday Things — mueve el conocimiento de la cabeza al mundo). El orden es genuinamente propenso a errores: eliminar un worktree mientras el cwd del shell todavía está dentro de él deja el shell varado en un directorio eliminado, y un hook de Stop dependiente de node_modules que se activa desde allí emite un muro de falsos "errores de lint/tipo" MODULE_NOT_FOUND que parecen una regresión real. El orden de cd-a-base-antes-de-eliminar en el Paso 4 es la corrección estructural para ese error exacto.

Este skill orquesta herramientas existentes (gh pr merge, wt remove / git worktree remove, commit-commands:clean_gone). No introduce estado en el sistema de archivos. Verifica el resultado — árbol limpio, en una base actualizada, PR fusionado — no simplemente que la limpieza se ejecutó (el "buscar el objetivo equivocado" de Meadows: un cierre medido por "si se ejecutó" optimiza el proxy y produce ceremonia frágil).

Posición de Invocación

Este es un skill de tubería principal que posee la cola merge → cleanup de la ruta de entrega predeterminada:

… → /pre-merge → /compound (en el PR, cuando existe una lección) → /pre-merge (re-ejecutar sobre el commit compuesto; re-sella) → [fusión + desmontaje del worktree — ESTE SKILL] → [cierre de issues — Paso 9 en prosa]

Usa /closeout cuando:

  • /pre-merge ha creado un PR y la revisión está completa (o el cambio es lo suficientemente trivial como para no necesitar una)
  • el PR está aprobado y se puede fusionar, y deseas que se fusione y el espacio de trabajo vuelva a una base limpia
  • el usuario solicita "cerrar", "fusionar y limpiar", "desmontar el worktree" o "volver a una base principal/limpia"

No uses /closeout cuando:

  • el PR aún no se ha creado o todavía está en revisión — ejecuta /pre-merge primero
  • deseas capturar una lección duradera — eso es /compound, que por defecto ya ha viajado en el PR antes de que /closeout lo fusione (el /compound posterior a la fusión es el plan de respaldo); /closeout no captura lecciones
  • deseas cerrar el PRD y los issues del slice o reconciliar el frontmatter del artifacto de investigación — eso es la prosa de limpieza en SYSTEM-OVERVIEW.md Paso 9; /closeout realiza la mitad de higiene de git y difiere la mitad de cierre de issues y supersesión del artifacto de investigación al Paso 9 en lugar de duplicarla. El único cierre que realiza es el elemento de trabajo del slice fusionado en el backend sidecar (Paso 3), porque ninguna línea Closes #N lo hace en la fusión
  • el cambio no está en una rama de características en absoluto (estás en la rama base sin nada que fusionar) — no hay nada que cerrar

Está confirmado por HITL y no es bloqueante: operacionaliza una cola ya opcional. Nunca auto-fusiona — la fusión está orientada al exterior y es difícil de revertir, por lo que siempre confirma primero.

Proceso

Ejecuta esto como una lista de verificación de hacer-confirmar (Gawande): realiza cada paso, luego verifica su resultado antes del siguiente. Los pasos destructivos (4–6) están autoprotegidos: confirma las condiciones previas de seguridad en el Paso 2 antes de ejecutarlos.

0. ¿Aislamiento aprovisionado por el host? Ceder el desmontaje primero

Antes de la secuencia de fusión/desmontaje, determina si el aislamiento de este repositorio es propiedad del host (Conductor, Codespaces, devcontainers). Cuando lo es, /closeout todavía realiza la fusión nativa de GitHub pero no desmonta el worktree ni poda la rama local — el host posee esa mitad del ciclo de vida, y luchar contra ella es la resistencia a políticas de Meadows (dos actores empujando el mismo stock). Cede el stock del worktree al host.

La detección de salida es más estricta que la verificación de entrada de /execute y no debe usar la heurística genérica "toplevel ≠ árbol de trabajo primario" por sí sola: un worktree hecho por la tubería también satisface esa prueba, por lo que la señal genérica no puede distinguir "el host posee el desmontaje" de "la tubería hizo esto y debe desmontarlo". Resuelve desde .claude/settings.json worktree.provisioning (por defecto "auto" cuando está ausente), y honra una anulación explícita en este extremo exactamente como /execute lo hace en la entrada:

  • host — cede el desmontaje incondicionalmente.
  • pipeline — la tubería posee el desmontaje. Ejecuta los Pasos 4–6 completos a continuación incluso si una variable de entorno del host está presente — una configuración explícita de pipeline significa que /execute aprovisionó el worktree por sí mismo, por lo que /closeout debe desmontarlo (el comportamiento anterior al host). Una variable de entorno del host no anula una elección explícita de pipeline.
  • auto (por defecto) — cede el desmontaje solo si una variable de entorno del host describe este árbol: el bloque a continuación imprime host_owned=yes. Nunca ceder por la señal genérica "toplevel ≠ primaria" sola, y nunca por la mera presencia de CONDUCTOR_WORKSPACE_PATH — un shell iniciado bajo Conductor lo hereda y puede llevar uno que nombre el espacio de trabajo de un repositorio diferente, y ceder por eso deja un worktree hecho por la tubería sin que nadie lo desmonte.
# señal-del-host: ¿una variable de entorno del host describe ESTE árbol, no solo este 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"

Si el desmontaje no se cede (incluyendo worktree.provisioning: "auto" con host_owned=no), la tubería lo posee — ejecuta los Pasos 4–6 completos a continuación como están escritos.

Cuando el aislamiento es propiedad del host, /closeout ejecuta una secuencia reducida:

  • El Paso 3 (fusión) todavía se ejecuta en su totalidad — gh pr merge --delete-branch. Conductor y hosts similares crean el PR pero dejan la fusión a una acción humana; la tubería que realiza la fusión nativa de GitHub es el valor que agrega, y el host refleja el resultado. La deferencia se limita al desmontaje del sistema de archivos, nunca a la fusión.
  • Omitir el reanclaje del Paso 4, wt remove / git worktree remove / ExitWorktree { remove } del Paso 5 y la poda de la rama local del Paso 6 — el host posee el ciclo de vida del worktree y la rama (Conductor los reclama cuando el espacio de trabajo se archiva). Nunca usar --force contra un árbol administrado por el host.
  • Lista de verificación del Paso 8: "Worktree eliminado" y "Rama podada" se convierten en "N/A — administrado por el host." Las comprobaciones de fusión, árbol limpio, PR fusionado y rama remota aún se aplican. La última es fácil de archivar bajo el mismo N/A y no lo es: lo que el host posee es el worktree del sistema de archivos y la rama local, mientras que la rama remota se elimina con gh pr merge --delete-branch que este skill aún ejecuta en su totalidad una viñeta arriba. Ceder la mitad que el host posee no cede la mitad que no posee.

1. Confirmar intención y recopilar estado

Nunca auto-fusionar. Declara lo que estás a punto de hacer y obtén una autorización explícita.

Recopila los hechos primero (estos son de solo lectura):

git branch --show-current                                   # la rama de características
git rev-parse --show-toplevel                               # el árbol de trabajo actual (puede ser un worktree)
git worktree list --porcelain | awk '/^worktree /{print $(2); exit}'   # el checkout BASE (árbol de trabajo principal)
gh pr view --json number,title,url,state,mergeable,mergeStateStatus,reviewDecision 2>/dev/null

Determina la rama base a la que apunta el PR — la predeterminada declarada del repositorio, no una suposición:

SC="<directorio de este skill>/references/sidecar.sh"
bash "$SC" base-ref --name   # el nombre de la rama simple: prod, nunca origin/prod

Este sitio quiere el nombre de la rama base, no una ref — alimenta git switch <rama-base> en el Paso 4, y git switch rechaza de plano una ref de seguimiento remoto (fatal: se esperaba una rama, se obtuvo la rama remota 'origin/prod') — quiere la rama base local que estás a punto de extraer. Por lo tanto, solicita base-ref --name, y deliberadamente no la forma de ref que toman /pre-merge, /help, /walk-commits, /visual-recap, /compound y /execute; necesitan una ref solo porque usan la base como un punto final de rango, que este skill nunca hace. No "arregles" esto para que coincida con ellos. Si base-ref --name sale con un código distinto de cero, no ha encontrado ninguna base: transmite su stderr y detente en lugar de adivinar una.

Informa al usuario, en un bloque corto: qué PR se fusionará, qué worktree (si lo hay) y rama se desmontarán, y a qué rama base regresarás y extraerás. Espera confirmación. Si no hay ningún PR en esta rama, detente y redirige a /pre-merge.

2. Verificar condiciones previas de seguridad antes de cualquier paso destructivo

El desmontaje es destructivo; verifica que sea seguro primero (autoprotegido). Todos estos deben cumplirse:

  • La rama de características está completamente enviada — git status no muestra commits sin enviar, y git log @{u}.. está vacío.
  • Existe un PR para esta rama y es fusionable (mergeable es MERGEABLE; mergeStateStatus no es BLOCKED/DIRTY).
  • Las comprobaciones requeridas están en verde y la revisión está aprobada (reviewDecision es APPROVED, o el repositorio no requiere revisión y el usuario lo ha aceptado explícitamente).

Si alguna condición previa falla, detente e informa cuál — no fusiones ni elimines nada. Una condición previa fallida es una señal para regresar a la revisión, no para forzar el paso. Cuando la que falla es la revisión — reviewDecision es CHANGES_REQUESTED — el camino de regreso es /respond-to-review: verifica cada comentario contra el código, el humano decide qué arreglar y qué rechazar, y las correcciones pasan por /fix-findings y una re-ejecución de /pre-merge antes de que /closeout se ejecute nuevamente.

Actualidad de la revisión: ¿la aprobación aún cubre este diff? (no bloqueante)

Las tres casillas de verificación anteriores preguntan si ocurrió una revisión. Ninguna de ellas pregunta si cubrió el diff que se va a fusionar. En el flujo lineal previsto, esas son la misma pregunta, que es exactamente por qué la brecha es invisible; en una sesión HITL real, el alcance se acumula en una rama después de su paso de revisión, y nada conecta el diff que /pre-merge leyó con el diff que /closeout fusiona. Eso es una entrada sin bucle de retroalimentación (Meadows — la solución estructural es agregar el bucle, no exhortar), y degrada silenciosamente "el PR fue revisado" a "una versión temprana del PR fue revisada" en el momento en que la degradación más importa. Continuous Delivery establece la regla claramente: el artifacto que revisaste debe ser el artifacto que envías.

Lee el sello que /pre-merge Fase 4 dejó y compáralo con la cabeza actual. Vive donde backend stamp dice, según references/state-backend.md: en el cuerpo del PR, o en el sidecar.

git fetch --quiet
SC="<directorio de este 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 falló; no se adivinará la ruta de github" >&2; exit 1 ;;
esac

En el backend sidecar, la cabeza es el upstream de la rama, que es lo que la forja fusionará: la primera condición previa anterior ya requiere que coincida con la cabeza local. Cae a HEAD cuando la rama no tiene upstream. stamp read imprime el mismo OID completo de 40 caracteres que lleva el marcador del cuerpo del PR, o nada, por lo que todo a continuación se ejecuta sin cambios en cualquier backend.

Exactamente 40 caracteres hexadecimales, comparados por igualdad de cadenas. Ambas mitades de esa oración son estructurales, porque este marcador es un contrato que abarca dos skills. /pre-merge Fase 4 emite el OID completo de 40 caracteres, y headRefOid siempre tiene 40 caracteres, por lo que \{40\} es el único ancho que puede compararse igual — aceptar uno más corto permitiría que un marcador mal formado (un cuerpo editado a mano, un marcador de posición de plantilla sustituido en la mitad equivocada) se analice correctamente y luego difiera de $HEAD_SHA para siempre. [[ "$REVIEWED_SHA" == "$HEAD_SHA" ]] es la comparación: igualdad exacta de cadenas en dos OID completos, nunca una prueba de prefijo. Un marcador que la expresión regular rechaza produce un $REVIEWED_SHA vacío y toma la rama "no se encontró sello" a continuación, que se degrada con gracia — mucho mejor que la rama "los SHA difieren" que grita lobo en un PR cuya cabeza nunca se movió. En el repositorio Skill Kit, scripts/test-review-currency-marker.sh fija este viaje de ida y vuelta contra ambos skills para que el formato del escritor y este analizador no puedan separarse silenciosamente.

Tres resultados, y solo uno de ellos interrumpe:

  • Los SHA coinciden — OID completos iguales en cadena, por lo que el diff revisado es el candidato a fusión. Pasa silenciosamente. No lo narres: este es el caso común, y anunciar un no evento cada ejecución es lo que convierte una puerta en papel tapiz.
  • No se encontró sello ($REVIEWED_SHA vacío) — un PR creado a mano, una contribución externa, un PR anterior al sello o un marcador cuya forma la expresión regular anterior rechazó. Anótalo en una línea ("sin sello /pre-merge en este PR — actualidad de la revisión desconocida") y continúa. La ausencia de un sello no es evidencia de un diff no revisado, y detenerse en cada PR sin sello es precisamente la fatiga de alarma que hace que una puerta sea derrotada reflexivamente (Meadows, resistencia a políticas).
  • Los SHA difieren — dos OID de 40 caracteres bien formados que no son iguales en cadena, lo que significa que los commits llegaron después de la revisión. No te detengas...

[truncado para la traducción]

Individual skills in this repo

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

Skills relacionados