Clasificar PRs
Objetivo
Convertir un backlog de PRs abiertos en un conjunto de fusiones enviadas en una sola sesión enfocada. Producir un plan rastreable y reanudable (<VERSIÓN>_PR_TRIAGE.md), luego ejecutarlo — rebasando cuando sea necesario, fusionando en lotes aislados seguros, aplicando seguimientos post-fusión y cerrando PRs reemplazados o parcialmente aplicables dando crédito a sus autores.
Esta habilidad se complementa con draft-release-notes y release-bump: clasifica primero, luego redacta las notas contra el nuevo main, y luego lanza la versión.
Cuándo usarlo
- Antes de un lanzamiento menor o mayor cuando se hayan acumulado 10+ PRs abiertos
- Cuando quieras desbloquear fusiones sin perder la narrativa de lo que está llegando
- Cuando sepas que no puedes revisar cada PR en profundidad personalmente, pero necesitas aterrizar el subconjunto crítico rápidamente
Prerrequisitos
- CLI
ghautenticada contra el repositorio - Un
worktreededicado para la revisión de PRs (evitar contaminarmaincon checkouts de ramas de colaboradores) - Claridad sobre la versión objetivo — el documento de clasificación se nombra según ella (ej.
0.4.0_PR_TRIAGE.md)
Flujo de trabajo
1. Configurar un worktree aislado para revisión de PRs
git worktree list # verificar si hay worktrees antiguos
git worktree prune
git worktree add ../voicebox-pr-review -b pr-review-<VERSIÓN> main
Mantén el worktree principal para trabajo de preparación de lanzamiento (borradores de changelog, seguimientos directos a main). Mantén el worktree de revisión para gh pr checkout — cada checkout mueve HEAD a una rama de colaborador, lo cual no quieres hacer en el worktree principal.
2. Recopilar metadatos de cada PR abierto
gh pr list --state open --limit 50 --json \
number,title,author,isDraft,mergeable,mergeStateStatus,files,additions,deletions,reviewDecision,statusCheckRollup,maintainerCanModify \
--jq '.[] | {num: .number, title, author: .author.login, mergeable, state: .mergeStateStatus, canModify: .maintainerCanModify, changes: "+\(.additions)/-\(.deletions)", files: [.files[].path]}'
Necesitas, para cada PR:
- Tamaño (
+adiciones/-eliminaciones) - Estado de fusionabilidad (
CLEAN,UNSTABLE,DIRTY= conflictos,UNKNOWN= GitHub aún calculando) - Si las ediciones del mantenedor están permitidas en la rama (necesario más tarde si rebasas por el autor)
- Rutas de archivos tocados (ayuda a detectar superposiciones entre PRs)
UNKNOWN es común justo después de un push a main — solo intenta la fusión y mira.
3. Clasificar en niveles
Ordena cada PR exactamente en un grupo:
Nivel 1 — Fusionar: pequeño, fusionable, corrige un error real, CI limpia, bajo costo de revisión. One-liners, relajaciones de dependencias, endurecimiento de seguridad dirigido. Estos son los fáciles.
Nivel 2 — Candidato, revisar: tamaño mediano (50-200 líneas), toca más superficie, parece sólido pero necesita una lectura más detallada. Nuevas características orientadas al usuario que encajan en la dirección del producto.
Reemplazado: la corrección o característica ya está cubierta por algo fusionado. Cierra con un comentario señalando el PR que lo reemplaza. Verifica cuidadosamente — «título similar» no es prueba; compara los diffs reales.
Diferir a la próxima versión: características grandes, conflictos sucios, PRs en borrador, cualquier cosa que toque el pipeline de lanzamiento de manera que introduzca riesgo. No fusiones estos en una sesión rápida — necesitan atención dedicada.
4. Escribir el documento de clasificación
Crea <VERSIÓN>_PR_TRIAGE.md en la raíz del worktree de revisión de PRs. Estructura:
# <Repositorio> <VERSIÓN> — Clasificación de PRs
Documento de trabajo para rastrear qué PRs abiertos llegan a <VERSIÓN>. Eliminar después del lanzamiento.
Última actualización: <FECHA>
## Progreso
**Nivel 1: 0 / N fusionados**
**Nivel 2: 0 / M manejados**
**Clasificación reemplazados: pendiente**
---
## Fusionar para <VERSIÓN> — correcciones de errores críticas
| PR | Estado | Tamaño | Qué corrige | Por qué imprescindible |
|---|---|---|---|---|
| [#123](url) | [ ] | +5/-0 | ... | ... |
## Candidato fuerte — necesita una revisión rápida
| PR | Estado | Tamaño | Resumen |
|---|---|---|---|
## Cerrar como reemplazado
| PR | Estado | Razón |
|---|---|---|
## Diferir a <PRÓXIMA_VERSIÓN>
- [#xxx](url) ... — razón
---
## Orden de ataque
1. Cerrar PRs reemplazados (comentarios de una línea)
2. Fusionar nivel 1 en lotes sin dependencias cruzadas — verificar que las rutas de archivos no se superpongan
3. Revisar nivel 2 individualmente
4. Volver a ejecutar `draft-release-notes` para recoger todo
5. Ejecutar `release-bump`
El encabezado Progreso es la parte más importante — es tu marcador y te permite reanudar limpiamente si la sesión se interrumpe.
5. Ejecutar el ciclo — por PR
Para cada PR en la lista de nivel 1 / nivel 2:
a. Checkout en el worktree de revisión:
cd ../voicebox-pr-review
git checkout pr-review-<VERSIÓN> # resetear a base neutral
gh pr checkout <N>
b. Leer el commit real, no main..HEAD:
git show HEAD # los cambios reales del PR
git show --stat HEAD # archivos tocados + recuento de líneas
NO revises mediante git diff main..HEAD si la rama del PR es más antigua que main. Ese diff incluye cada commit que llegó a main después de que el PR se bifurcó como líneas de - (eliminación). Un PR de 3 líneas puede parecer una reversión de 700 líneas. Esta es la forma más fácil de juzgar mal un PR.
c. Evaluar preocupaciones: corrección, alcance, interacción con trabajo ya fusionado, compatibilidad de versiones (ej. no se puede usar una API que requiera una versión de dependencia que aún no fijamos).
d. Rebasear si la rama está detrás de main:
git fetch origin main
git rebase origin/main
Esto es esencial antes de fusionar con squash. El squash de GitHub calcula diff(cabeza-del-PR, base-de-fusión) — en una rama desactualizada, ese diff incluye revertir cada commit intermedio. Rebasear mueve la base de fusión hacia adelante para que el squash sea limpio.
e. Si las ediciones del mantenedor están permitidas, empujar el rebase de vuelta al fork del colaborador:
git remote add <autor> https://github.com/<autor>/<repositorio>.git
git fetch <autor> <rama> # obtener su referencia primero
git push <autor> HEAD:<rama> --force-with-lease
Esto mantiene la UI de PR de GitHub sincronizada con el estado rebasado y hace que la fusión sea limpia desde el lado de GitHub.
f. Fusionar:
gh pr merge <N> --squash
g. Actualizar el documento de clasificación — marca la casilla a ✅ fusionado <sha> (usa el SHA corto de gh pr view <N> --json mergeCommit --jq '.mergeCommit.oid[0:7]'). Actualiza el encabezado de Progreso.
6. Fusionar correcciones pequeñas en lote
PRs con cambios de ≤5 líneas, CI limpia, rutas de archivos que no se superponen e intención obviamente correcta (ej. relajación de dependencia de una línea, adición de variable de entorno, corrección de ruta de importación) se pueden fusionar en un solo ciclo sin la ceremonia de revisión por PR:
for pr in 425 384 416 429; do
echo "=== Fusionando PR $pr ==="
gh pr merge $pr --squash
done
Verificar después que cada uno haya llegado limpiamente:
for pr in 425 384 416 429; do
gh pr view $pr --json state,mergeCommit --jq "{pr: $pr, state, sha: .mergeCommit.oid[0:7]}"
done
7. Seguimientos post-fusión
A veces vale la pena fusionar un PR a pesar de un problema menor conocido (ej. mapeo de tipos incompleto, limpieza de sentinelas obsoletas). No bloquees la fusión; aplica el seguimiento como una rama normal + PR justo después:
cd <worktree-principal>
git pull --ff-only origin main
git checkout -b fix/<nombre-corto>
# editar...
git commit -m "fix(<área>): <una línea>"
git push -u origin fix/<nombre-corto>
gh pr create --title "..." --body "Seguimiento de #<N>. ..."
Registra ambos SHAs en el documento de clasificación (✅ fusionado <sha-pr> + seguimiento <pr>).
Excepción directo-a-main: solo bajo una política explícita y limitada (ej. «carrera de velocidad de lanzamiento»). No lo hagas por defecto.
8. Reemplazado: cerrar con un comentario dando crédito
gh pr close <N> --comment "Cerrando — reemplazado por la fusionado #<M> que llegó <breve descripción>. ¡Gracias!"
Verifica los diffs primero — «título similar» no es suficiente. Si el PR está parcialmente reemplazado (el diagnóstico es correcto pero solo la mitad de los cambios siguen siendo necesarios), haz una aplicación parcial en su lugar.
9. Patrón de aplicación parcial
Cuando un PR tiene cambios valiosos y cuestionables combinados:
cd <worktree-principal>
git pull --ff-only origin main
# Seleccionar archivos específicos de la rama del PR
git checkout <sha-commit-pr> -- <archivo1> <archivo2>
# Revisar los cambios preparados, ajustar según sea necesario
git diff --cached
# Aplicar ediciones quirúrgicas a archivos que no quieres reemplazar en masa
# (ej. el archivo del PR es anterior a un commit reciente en main que necesitas conservar)
# Confirmar con un trailer que acredita al autor original
git commit -m "$(cat <<'EOF'
<asunto>
<cuerpo explicando qué se mantuvo y qué se descartó>
Co-Authored-By: <autor> <[email protected]>
EOF
)"
git push ... # rama + PR, a menos que sea bajo la excepción directo-a-main
Luego cierra el PR con un comentario explicando qué se aplicó y qué se descartó, haciendo referencia al SHA del commit.
10. Mantener el documento actualizado
Cada fusión, cada cierre, cada seguimiento → actualizar <VERSIÓN>_PR_TRIAGE.md. El documento es tu registro de sesión. Si te interrumpen y reanudas mañana, el documento es la única fuente de verdad para «¿dónde estoy?».
11. Cuando la clasificación está completa
- Cada PR en el documento tiene un estado terminal (✅ fusionado / ✅ cerrado / diferido)
- El encabezado de Progreso muestra N/N para cada nivel
- La siguiente habilidad a ejecutar es
draft-release-notes(para regenerar[Unreleased]contra el nuevo main), luegorelease-bump
Puedes eliminar el documento de clasificación después de que el lanzamiento se publique, o mantenerlo en el historial de versiones como registro.
Trampas
main..HEADen una rama desactualizada miente. Muestra todo lo que main ha ganado desde que la rama se bifurcó como eliminaciones. Siempre revisa mediantegit show HEADpara el commit real del PR.- Fusionar con squash una rama sin rebasear revierte trabajo intermedio. El squash calcula
diff(cabeza-PR, base-de-fusión). Rebasear mueve la base de fusión hacia adelante. mergeable=UNKNOWNes transitorio — GitHub está recalculando después de un push. Solo intenta la fusión.- El orden de las rutas importa (FastAPI y similares):
DELETE /history/faileddebe registrarse antes queDELETE /history/{id}, o la ruta parametrizada consumirá"failed"como un ID. -weak_frameworkde Apple anula-frameworkpara el mismo framework, independientemente del orden — úsalo mediantecargo:rustc-link-arg=-Wl,-weak_framework,Namecuando una dependencia enlace algo opcional de forma fuerte.- Los pisos de versión de dependencias limitan lo que puedes aplicar. Antes de aceptar un cambio de nombre de kwarg como
torch_dtype=→dtype=, verifica que la versión mínima lo soporte. A veces lo correcto es seleccionar la mitad del PR. - Los tipos de audio
cpal::Streamy similares!Sendno pueden cruzar puntos deawaitospawn_blocking. A veces una espera síncrona «no ideal pero correcta» es la mejor corrección disponible; señala pero no bloquees. - Las compilaciones nocturnas de PyTorch no son enviables para lanzamientos — no deterministas, pueden retroceder entre ejecuciones. Si un PR sugiere cambiar a nocturno para solucionar un problema de GPU, prefiere
TORCH_CUDA_ARCH_LIST=...+PTXo espera soporte estable en su lugar.
Referencia de comandos canónicos
# Metadatos de PRs en lote
gh pr list --state open --limit 50 --json number,title,author,mergeable,mergeStateStatus,additions,deletions,maintainerCanModify,files
# Vista detallada de un solo PR
gh pr view <N> --json body,author,headRefName,baseRefName,mergeable,maintainerCanModify,files,statusCheckRollup
# El commit real, no el diff rama-vs-main
git show HEAD
git show --stat HEAD
gh pr diff <N>
# Rebasear rama del colaborador en main actual
git fetch origin main && git rebase origin/main
# Empujar rebase de vuelta al fork del colaborador (requiere maintainerCanModify=true)
git remote add <autor> https://github.com/<autor>/<repositorio>.git
git fetch <autor> <rama>
git push <autor> HEAD:<rama> --force-with-lease
# Fusionar
gh pr merge <N> --squash
# Confirmar SHA de fusión para el documento de clasificación
gh pr view <N> --json state,mergeCommit --jq '{state, sha: .mergeCommit.oid[0:7]}'
# Cerrar reemplazado
gh pr close <N> --comment "Cerrando — reemplazado por la fusionado #<M>. ¡Gracias!"
Notas
- Nunca revises una rama desactualizada mediante
main..HEAD. Esta es la línea más importante de toda esta habilidad. - El documento de clasificación es el estado de la sesión. Pierde el documento, pierde la sesión. Actualízalo después de cada acción.
- Da crédito a los colaboradores incluso en aplicaciones parciales. Usa trailers
Co-Authored-By:y comentarios de cierre que enlacen al commit aplicado. - No dejes que lo perfecto sea enemigo de lo enviado. Una corrección que pasa de «roto» a «funciona con un problema menor conocido» es una mejora estricta. Señala el problema, crea un seguimiento, fusiona la corrección.