CommunityRedacción y edicióngithub.com

h8nc4y/isolated-worktree-pr-flow

Skill para Claude Code y Codex: crea PRs desde un worktree git temporal basado en la rama por defecto de origin cuando el checkout principal está sucio, desincronizado o es compartido con otros agentes - con protecciones de limpieza conscientes del modo de fusión.

¿Qué es isolated-worktree-pr-flow?

Este skill automatiza la creación de Pull Requests desde un worktree git temporal, aislado del checkout principal, cuando este último está sucio (WIP sin commit), detrás del remoto o compartido con otros agentes. Su utilidad principal es evitar colisiones entre múltiples sesiones de agente que trabajan en el mismo repositorio, un escenario común en entornos de desarrollo colaborativo con IA. Soporta Claude Code y Codex (y otros agentes compatibles con `git` y `gh`), con ejemplos para PowerShell y POSIX. El flujo incluye la creación del worktree, manejo de dependencias faltantes (como node_modules o virtualenvs), espera acotada de CI, y una fase de limpieza con comprobaciones de seguridad que verifican el estado de la fusión antes de eliminar ramas o worktrees, adaptándose a modos de merge (merge commit, squash, rebase) para evitar pérdida de trabajo.

Compatible con✓Claude Code✓Codex CLI~Cursor
npx skills add h8nc4y/isolated-worktree-pr-flow

Installed? Explore more Redacción y edición skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Preguntar en tu IA favorita

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

Documentación

Flujo de PR con Worktree Aislado

Procedimiento para enviar un cambio como pull request desde un worktree git temporal cortado de la rama por defecto de origin, cuando el checkout principal no debe tocarse — porque está sucio (WIP sin commit), detrás del remoto, o compartido con otros agentes. El flujo cubre la creación del worktree, brechas de dependencias en worktrees nuevos, espera acotada de CI y protecciones de limpieza conscientes del modo de fusión.

Por Qué Aislar con Worktree

Cuando múltiples sesiones de agente entran al mismo directorio de trabajo (por ejemplo, a través de lanzamiento de sesiones por lotes), el HEAD de git, la rama actual y los cambios sin commit son estado compartido — cada sesión mueve el piso bajo la otra. En un incidente medido, en menos de un minuto de que una sesión creara una rama con git switch -c, una segunda sesión en el mismo checkout se bifurcó de ella, hizo commit y fusionó a la rama por defecto.

Patrón de detección (confirma una colisión en minutos):

  1. Señales: aparecen archivos que no escribiste, o git status ya no coincide con la instantánea que tomaste cuando tu sesión comenzó.
  2. Escrutinio de marcas de tiempo: ls --time-style=full-iso (o el equivalente de tu plataforma). Varios archivos compartiendo el mismo milisegundo significa que una operación git (checkout / merge / stash) los escribió, no ediciones manuales o secuenciales de LLM.
  3. git reflog --date=iso aclara la línea de tiempo: quién hizo checkout, commit, o merge, y cuándo.
  4. Si tu entorno de agente proporciona una función de listado de sesiones, úsala para encontrar otras sesiones en ejecución con el mismo directorio de trabajo.

Reglas de cesión una vez que se confirma una colisión:

  • El lado que hizo commit primero permanece como escritor. El recién llegado deja de escribir: continuar haciendo commit o cambiando de rama mueve el HEAD de la otra sesión y rompe ambas.
  • El recién llegado agrega valor como verificador en su lugar: revisión adversaria del resultado del escritor (afirmaciones del doc vs código, re-ejecutar pruebas, comprobaciones previas a la publicación) y entregables solo en chat que no tocan archivos.
  • Limpia solo las referencias intermedias que tú mismo creaste (por ejemplo, una rama vacía); nunca toques referencias que la otra sesión haya usado.
  • Los registros de solo añadidura son seguros para que ambas sesiones escriban, bajo encabezados de sección separados.

Prevención: un repositorio = una sesión de escritura. Cuando múltiples agentes deben trabajar en el mismo repositorio, aísla cada uno en su propio worktree — que es lo que hace este skill. El hábito de detección más barato es re-comparar git status contra tu instantánea de inicio de sesión justo después de comenzar a trabajar.

Portabilidad

Este flujo fue endurecido en Windows (PowerShell más Git Bash), pero los comandos git y gh son multiplataforma. Donde los shells difieren, se dan ejemplos tanto para PowerShell como para POSIX. CI incluye trabajos de PowerShell 7 para las protecciones de limpieza sintéticas y el escáner de marcadores privados en Ubuntu 24.04 y macOS 15 nativo además de los hosts Windows. Los tres trabajos pasaron en PR #7 ejecución 30216166105. Las piezas específicas de Windows — uniones de directorio y sus semánticas de eliminación — tienen equivalentes de enlaces simbólicos POSIX anotados en línea.

Cuándo Usarlo

  • El checkout principal está sucio (WIP sin commit), detrás del remoto, o compartido con otros agentes, y cortar una rama directamente en él podría romper ese WIP.
  • Incluso para una pequeña corrección de higiene: si el checkout existente está detrás por muchos commits y lleva WIP sucio, un worktree aislado es la ruta más segura (probado en campo).
  • Se asume un flujo de trabajo de una rama por tarea (nunca hacer commit directamente a la rama por defecto). Este skill cubre solo la mecánica de dónde vive esa rama: un worktree temporal fuera del checkout sucio.

Procedimiento

  1. Verifica el estado, de forma no destructiva. Ejecuta cada comando git con -C <repo> y cada comando gh con --repo <owner>/<name>, para que nada dependa del directorio actual — los entornos de agente pueden restablecer el directorio de trabajo entre llamadas, y sin --repo, gh falla con "not a git repository" cuando se invoca fuera del repositorio.

    git -C <repo> status --short          # registra el WIP previo al trabajo (verificado sin cambios al final)
    git -C <repo> branch --show-current
    git -C <repo> fetch origin
    gh repo view <owner>/<name> --json defaultBranchRef -q .defaultBranchRef.name   # usa el resultado como <default> abajo
    

    Los mismos comandos funcionan sin cambios en shells POSIX.

  2. Crea un worktree temporal desde la rama por defecto de origin, sin tocar el checkout sucio. Sustituye <default> del paso 1 — no codifiques main; los repositorios cuyo predeterminado es master siguen siendo comunes.

    git -C <repo> worktree add -b fix/<task> ..\_worktrees\<task> origin/<default>
    

    POSIX:

    git -C <repo> worktree add -b fix/<task> ../_worktrees/<task> origin/<default>
    

    Coloca el worktree en algún lugar confirmado como escribible. Los directorios temporales del sistema a veces no son escribibles desde entornos de agente enjaulados (probado en campo). Prefiere un directorio _worktrees/ junto al repositorio o el directorio temporal designado por tu entorno, y antes de ejecutar algo pesado, escribe un archivo de prueba y bórralo para confirmar el acceso de escritura.

  3. Mantén el diff mínimo. Edita (o copia) solo los archivos que la tarea necesita, dentro del worktree. No arrastres el WIP no rastreado del checkout principal en su totalidad — el upstream u otros agentes pueden agregar archivos con los mismos nombres, y los archivos extra causan conflictos add/add en el momento de la fusión o contaminan el diff del PR. Si debes incluir una copia de un archivo de dependencia sin commit del checkout base, indica en el cuerpo del PR qué lado gana en la fusión (lección de campo: hacer esto explícitamente evitó una mala auto-resolución).

  4. Maneja las brechas de dependencias en worktrees nuevos. Un worktree nuevo no tiene node_modules ni virtualenv (probado en campo).

    • Node: ejecuta npm ci en el worktree (requiere un archivo de bloqueo). Para verificación ligera solamente, un enlace temporal al node_modules del checkout principal también funciona — pero elimina el enlace antes de la limpieza (condición de seguridad 5). Unión de PowerShell:

      New-Item -ItemType Junction -Path <worktree>\node_modules -Target <repo>\node_modules
      # La eliminación debe borrar solo el enlace, nunca el destino: usa rmdir.
      cmd /c rmdir "<worktree>\node_modules"
      

      Enlace simbólico POSIX:

      ln -s <repo>/node_modules <worktree>/node_modules
      # Elimina el enlace en sí con rm simple (sin -r, sin barra diagonal final).
      rm <worktree>/node_modules
      
    • Python: reutiliza el intérprete del entorno virtual del repositorio principal y apúntalo a las fuentes del worktree, ejecutando solo las pruebas objetivo. PowerShell:

      $env:PYTHONPATH = "<worktree>\src"
      & <repo>\.venv\Scripts\python.exe -m pytest <worktree>\tests\<target_test>.py
      

      POSIX:

      PYTHONPATH=<worktree>/src <repo>/.venv/bin/python -m pytest <worktree>/tests/<target_test>.py
      
  5. Cuando la verificación pase: haz commit, empuja y crea el PR. Escribe el mensaje de commit en un archivo y pásalo con git commit -F <msgfile> — los mensajes multilínea en línea son frágiles bajo el quoting del shell, especialmente en Windows.

    git -C <worktree> add <solo archivos objetivo>
    git -C <worktree> commit -F <msgfile>
    git -C <worktree> push -u origin fix/<task>
    gh pr create --repo <owner>/<name> --head fix/<task> ...
    
  6. Espera por la CI de protección de rama con sondeo acotado. gh pr checks --watch es una espera ilimitada — los arneses de agente y las capas de aprobación pueden rechazarlo o colgarse (probado en campo). Usa comprobaciones únicas con un límite de intentos:

    gh pr checks <pr-number> --repo <owner>/<name> --watch=false
    gh pr view <pr-number> --repo <owner>/<name> --json state,statusCheckRollup
    

    Bucle acotado, POSIX. Cuando el arnés bloquea sleep en primer plano (Claude Code lo hace), ejecuta el bucle como tarea en segundo plano, o salta el bucle e intercala comprobaciones únicas entre otras piezas de trabajo:

    for i in $(seq 1 10); do gh pr checks <pr-number> --repo <owner>/<name> --watch=false && break; sleep 30; done
    

    Bucle acotado, PowerShell:

    for ($i = 1; $i -le 10; $i++) {
      gh pr checks <pr-number> --repo <owner>/<name> --watch=false
      if ($LASTEXITCODE -eq 0) { break }
      Start-Sleep -Seconds 30
    }
    

    El límite e intervalo correctos dependen de la duración de CI del repositorio (no verificado en general — ajusta por repositorio).

  7. Fusiona — y decide primero el modo de fusión, porque determina la verificación de trabajo no fusionado previa a la limpieza (condición de seguridad 2):

    • --merge (commit de fusión): los commits de la rama se convierten en ancestros de la rama por defecto, así que verifica con merge-base --is-ancestor y elimina con branch -d (condición de seguridad 2a; probado en campo).
    • --squash / --rebase: los commits de la rama nunca se convierten en ancestros de la rama por defecto, así que --is-ancestor en la rama sale con 1 para siempre, incluso después de una fusión completamente exitosa. Usa la protección "MERGED + coincidencia headRefOid" en su lugar (condición de seguridad 2b).
    gh pr merge <pr-number> --repo <owner>/<name> --merge      # repos con política de squash: --squash, luego usa protección 2b
    gh pr view <pr-number> --repo <owner>/<name> --json state,mergedAt,mergeCommit
    

    Comportamientos conocidos alrededor de gh pr merge (probado en campo):

    • Si la rama local por defecto está verificada en otro worktree, gh pr merge --delete-branch ejecutado desde dentro del repositorio puede fallar durante su post-procesamiento local aunque la fusión remota haya tenido éxito. Ejecutar la fusión con --repo desde fuera del repositorio omite el post-procesamiento local por completo, evitando la clase de fallo de antemano.
    • Si de todos modos se reporta un fallo, verifica gh pr view --json state primero — el lado remoto a menudo ya está MERGED. Si es así, completa los pasos locales restantes (eliminación de rama, remoción de worktree) manualmente después de las condiciones de seguridad a continuación.
    • gh pr merge ejecutado desde dentro del repositorio puede hacer fast-forward del worktree actual a la rama por defecto; ver grandes actualizaciones de PRs anteriores en su salida no es una anomalía.

Condiciones de Seguridad

Antes de cualquier paso destructivo (eliminación de worktree, borrado de rama, prune), verifica todo lo siguiente en tiempo de ejecución — ejecuta la verificación ANTES de la eliminación, nunca después. Omitir una verificación previa a la eliminación y "verificar después" anula la protección: un error de trabajo no fusionado se manifestaría solo una vez que la rama ya no existe (lección de campo). En checkouts compartidos, el estado puede variar entre cuándo se aprobó un paso y cuándo se ejecuta; si ha pasado tiempo, vuelve a ejecutar las comprobaciones inmediatamente antes de ejecutar (probado en campo).

  1. gh pr view <pr-number> --repo <owner>/<name> --json state,mergedAt reporta MERGED.

  2. Verificación de trabajo no fusionado acorde al modo de fusión. No mezcles los modos: después de un squash, la protección 2a nunca pasa incluso para un PR fusionado correctamente; y branch -d después de un squash o se cuela por la lógica de fusionado a upstream (si origin/fix/... aún existe — lo cual no es garantía de ancestro) o es rechazado para siempre como "not fully merged" (si el upstream ya no está).

    • 2a — Modo --merge (commit de fusión): git -C <repo> merge-base --is-ancestor fix/<task> origin/<default> sale con 0 (verifica $LASTEXITCODE en PowerShell, echo $? en shells POSIX). Elimina la rama con -d, para que git mismo aún rechace si algo quedó sin fusionar (probado en campo). Advertencia: -d juzga "fusionado" contra el upstream de la rama si está configurado, de lo contrario contra HEAD — así que después de que la rama remota se elimina y se poda, y con el HEAD del checkout sentado en una rama anterior no relacionada, -d puede rechazar con not fully merged aunque el PR se haya fusionado correctamente (probado en campo). En ese caso, eliminar con -D es aceptable solo inmediatamente después de que la verificación --is-ancestor haya salido con 0 — la misma forma de protección-luego--D que en 2b; un -D sin la protección sigue prohibido.
    • 2b — Modo --squash / --rebase: obtén state, mergeCommit y headRefOid a través de gh pr view <pr-number> --repo <owner>/<name> --json state,mergeCommit,headRefOid. Nota que mergeCommit es un objeto JSON — el hash del commit es su campo oid (-q .mergeCommit.oid lo extrae); headRefOid es una cadena simple. Luego confirma ambos: (i) git -C <repo> merge-base --is-ancestor <mergeCommit-oid> origin/<default> sale con 0, y (ii) git -C <repo> rev-parse fix/<task> es igual a headRefOid. La verificación (ii) es el reemplazo del modo squash para "no quedan commits atrás": prueba que el head del PR fusionado es exactamente la punta de tu rama local. Solo cuando ambas se cumplen, elimina con un git branch -D explícito (esta protección sustituye a -d). La topología y las rutas de rechazo de la protección 2b están cubiertas por historiales Git sintéticos desechables en scripts/test-cleanup-guards.ps1. La prueba aísla la configuración Git del sistema/global, hooks, firmado y rebase.updateRefs; captura, limpia y restaura cada variable GIT_* ambiente para que las rutas de repositorio/seguimiento no puedan escapar del fixture; y restringe la limpieza recursiva a su hijo temporal directo generado con comparación de rutas consciente del SO y rechazo de reparse points. La ruta de squash en vivo de GitHub se midió el 2026-07-23 con PR #2: MERGED, mergeCommit aterrizado, puntas locales y remotas sin cambios coincidiendo con headRefOid, -D local protegido y eliminación remota explícita, todo pasó. La ruta de rebase en vivo de GitHub se midió el 2026-07-26 con PR #5: MERGED, un mergeCommit aterrizado reescrito, el head original fuera de la ascendencia de la rama por defecto, puntas locales y remotas sin cambios coincidiendo con headRefOid, -D local protegido, eliminación remota explícita y worktree propio limpio...

…

[truncado para traducción]

Skills relacionados