CommunityRédaction et éditiongithub.com

h8nc4y/isolated-worktree-pr-flow

Compétence agent pour Claude Code et Codex : créer des PR depuis un worktree git temporaire basé sur la branche par défaut distante lorsque le checkout principal est sale, en retard ou partagé avec d'autres agents — avec des gardes de nettoyage adaptées au mode de fusion.

Qu'est-ce que isolated-worktree-pr-flow ?

Cette compétence pour agents IA (Claude Code, Codex) permet de soumettre une pull request depuis un worktree git temporaire créé à partir de la branche par défaut d'origine, sans toucher au checkout principal lorsqu'il est sale (travail en cours non commité), en retard par rapport au dépôt distant, ou partagé entre plusieurs sessions d'agents. Elle résout le problème de collisions d'état entre agents travaillant sur le même dépôt en isolant chaque session dans son propre worktree, évitant ainsi les conflits de HEAD, de branche et de modifications non commitées. La procédure couvre la création du worktree, la gestion des dépendances manquantes (node_modules, virtualenv), l'attente des vérifications CI avec un polling limité, et des gardes de sécurité avant toute opération destructive (suppression de branche, de worktree) qui vérifient que la PR est bien fusionnée selon le mode utilisé (merge, squash ou rebase). La compétence inclut des commandes pour PowerShell et POSIX, et a été testée sur Windows, Ubuntu et macOS.

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

Installed? Explore more Rédaction et édition skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Isolated Worktree PR Flow

Procédure pour soumettre une modification sous forme de pull request depuis un worktree git temporaire créé à partir de la branche par défaut d'origin, lorsque le checkout principal ne doit pas être touché — car il est sale (travail en cours non commité), en retard par rapport au dépôt distant, ou partagé avec d'autres agents. Le flux couvre la création du worktree, les lacunes de dépendances du nouveau worktree, l'interrogation CI avec limitation, et les gardes de nettoyage adaptées au mode de fusion.

Pourquoi l'isolation par Worktree

Lorsque plusieurs sessions d'agents entrent dans le même répertoire de travail (par exemple via un lancement de session par lots), HEAD de git, la branche courante et les modifications non commitées sont un état partagé — chaque session déplace le terrain sous l'autre. Dans un incident mesuré, en moins d'une minute après qu'une session ait créé une branche avec git switch -c, une deuxième session dans le même checkout a créé une branche à partir de celle-ci, a commité et fusionné sur la branche par défaut.

Schéma de détection (confirme une collision en quelques minutes) :

  1. Signes : des fichiers que vous n'avez pas écrits apparaissent, ou git status ne correspond plus à l'instantané que vous avez pris au début de votre session.
  2. Examen des horodatages : ls --time-style=full-iso (ou l'équivalent de votre plateforme). Plusieurs fichiers partageant la même milliseconde signifie qu'une opération git (checkout / merge / stash) les a écrits, pas des éditions manuelles ou séquentielles par LLM.
  3. git reflog --date=iso établit la chronologie : qui a fait un checkout, commité ou fusionné, et quand.
  4. Si votre environnement d'agent fournit une fonctionnalité de liste de sessions, utilisez-la pour trouver d'autres sessions en cours avec le même répertoire de travail.

Règles de cession une fois une collision confirmée :

  • Le côté qui a commité en premier reste le rédacteur. Le retardataire cesse d'écrire : continuer à commiter ou changer de branche déplace le HEAD de l'autre session et les brise toutes deux.
  • Le retardataire apporte plutôt de la valeur en tant que vérificateur : relecture contradictoire de la sortie du rédacteur (revendications du doc vs code, réexécution des tests, vérifications pré-publication) et livrables uniquement par chat qui ne touchent à aucun fichier.
  • Nettoyez uniquement les références intermédiaires que vous avez vous-même créées (par exemple une branche vide) ; ne touchez jamais aux références que l'autre session a utilisées.
  • Les journaux en mode ajout seulement sont sûrs pour les deux sessions en écriture, sous des en-têtes de section séparés.

Prévention : un dépôt = une session en écriture. Lorsque plusieurs agents doivent travailler sur le même dépôt, isolez chacun dans son propre worktree — c'est ce que fait cette compétence. L'habitude de détection la plus simple est de re-comparer git status avec votre instantané de début de session juste après avoir commencé le travail.

Portabilité

Ce flux a été durci sur Windows (PowerShell plus Git Bash), mais les commandes git et gh elles-mêmes sont multiplateformes. Là où les shells diffèrent, des exemples PowerShell et POSIX sont donnés. Le CI inclut des jobs PowerShell 7 pour les gardes de nettoyage synthétiques et le scanner de marqueurs privés sur Ubuntu 24.04 et macOS 15 natif en plus des hôtes Windows. Les trois jobs ont réussi dans PR #7 run 30216166105. Les éléments spécifiques à Windows — les jonctions de répertoire et leur sémantique de suppression — ont des équivalents de liens symboliques POSIX notés en ligne.

Quand l'utiliser

  • Le checkout principal est sale (travail en cours non commité), en retard par rapport au dépôt distant, ou partagé avec d'autres agents, et créer une branche directement dedans pourrait casser ce travail en cours.
  • Même pour une petite correction d'hygiène : si le checkout existant est en retard de nombreux commits et contient du travail en cours sale, un worktree isolé est la voie la plus sûre (testé sur le terrain).
  • Un flux de travail par branche par tâche est supposé (ne jamais commiter directement sur la branche par défaut). Cette compétence couvre uniquement la mécanique de l'endroit où cette branche réside : un worktree temporaire en dehors du checkout sale.

Procédure

  1. Vérifier l'état, de manière non destructive. Exécutez chaque commande git avec -C <repo> et chaque commande gh avec --repo <propriétaire>/<nom>, de sorte que rien ne dépende du répertoire courant — les environnements d'agents peuvent réinitialiser le répertoire de travail entre les appels, et sans --repo, gh échoue avec « not a git repository » lorsqu'il est invoqué en dehors du dépôt.

    git -C <repo> status --short          # enregistrer le WIP pré-travail (vérifié inchangé à la fin)
    git -C <repo> branch --show-current
    git -C <repo> fetch origin
    gh repo view <propriétaire>/<nom> --json defaultBranchRef -q .defaultBranchRef.name   # utiliser le résultat comme <default> ci-dessous
    

    Les mêmes commandes fonctionnent inchangées dans les shells POSIX.

  2. Créer un worktree temporaire à partir de la branche par défaut d'origin, sans toucher au checkout sale. Remplacez <default> de l'étape 1 — ne codez pas en dur main ; les dépôts dont la valeur par défaut est master sont encore courants.

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

    POSIX :

    git -C <repo> worktree add -b fix/<tâche> ../_worktrees/<tâche> origin/<default>
    

    Placez le worktree à un endroit dont l'écriture est confirmée. Les répertoires temporaires système ne sont parfois pas accessibles en écriture depuis des environnements d'agents sandboxés (testé sur le terrain). Préférez un répertoire _worktrees/ à côté du dépôt ou le répertoire temporaire désigné de votre environnement, et avant d'exécuter quoi que ce soit de lourd, écrivez un fichier de test et supprimez-le pour confirmer l'accès en écriture.

  3. Garder le diff minimal. Modifiez (ou copiez) uniquement les fichiers dont la tâche a besoin, à l'intérieur du worktree. Ne transportez pas en bloc le WIP non suivi du checkout principal — en amont ou d'autres agents peuvent ajouter des fichiers avec les mêmes noms, et les fichiers supplémentaires provoquent des conflits add/add au moment de la fusion ou polluent le diff de la PR. Si vous devez inclure une copie d'un fichier de dépendance non commité du checkout de base, indiquez dans le corps de la PR quel côté gagne lors de la fusion (leçon de terrain : le faire explicitement a évité une mauvaise auto-résolution).

  4. Gérer les lacunes de dépendances d'un nouveau worktree. Un nouveau worktree n'a pas de node_modules ni de virtualenv (testé sur le terrain).

    • Node : exécutez npm ci dans le worktree (nécessite un fichier de verrouillage). Pour une vérification légère uniquement, un lien temporaire vers node_modules du checkout principal fonctionne aussi — mais supprimez le lien avant le nettoyage (condition de sécurité 5). Jonction PowerShell :

      New-Item -ItemType Junction -Path <worktree>\node_modules -Target <repo>\node_modules
      # La suppression doit supprimer uniquement le lien, jamais la cible : utilisez rmdir.
      cmd /c rmdir "<worktree>\node_modules"
      

      Lien symbolique POSIX :

      ln -s <repo>/node_modules <worktree>/node_modules
      # Supprimez le lien lui-même avec rm simple (pas de -r, pas de barre oblique finale).
      rm <worktree>/node_modules
      
    • Python : réutilisez l'interpréteur du virtualenv du dépôt principal et pointez-le vers les sources du worktree, en exécutant uniquement les tests cibles. PowerShell :

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

      POSIX :

      PYTHONPATH=<worktree>/src <repo>/.venv/bin/python -m pytest <worktree>/tests/<test_cible>.py
      
  5. Lorsque la vérification réussit : commiter, pousser et créer la PR. Écrivez le message de commit dans un fichier et passez-le avec git commit -F <fichier_msg> — les messages multi-lignes en ligne sont fragiles sous les guillemets du shell, surtout sur Windows.

    git -C <worktree> add <fichiers cibles uniquement>
    git -C <worktree> commit -F <fichier_msg>
    git -C <worktree> push -u origin fix/<tâche>
    gh pr create --repo <propriétaire>/<nom> --head fix/<tâche> ...
    
  6. Attendre le CI de protection de branche avec interrogation limitée. gh pr checks --watch est une attente illimitée — les harnais d'agents et les couches d'approbation peuvent le rejeter ou se bloquer (testé sur le terrain). Utilisez des vérifications ponctuelles avec un nombre maximum de tentatives :

    gh pr checks <num-pr> --repo <propriétaire>/<nom> --watch=false
    gh pr view <num-pr> --repo <propriétaire>/<nom> --json state,statusCheckRollup
    

    Boucle limitée, POSIX. Là où le harnais bloque sleep au premier plan (Claude Code le fait), exécutez la boucle comme une tâche d'arrière-plan, ou sautez la boucle et entrelacez les vérifications ponctuelles avec d'autres morceaux de travail :

    for i in $(seq 1 10); do gh pr checks <num-pr> --repo <propriétaire>/<nom> --watch=false && break; sleep 30; done
    

    Boucle limitée, PowerShell :

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

    La limite et l'intervalle corrects dépendent de la durée du CI du dépôt (non vérifié en général — ajuster par dépôt).

  7. Fusionner — et décider du mode de fusion en premier, car il détermine la vérification pré-nettoyage du travail non fusionné (condition de sécurité 2) :

    • --merge (commit de fusion) : les commits de la branche deviennent des ancêtres de la branche par défaut, donc vérifiez avec merge-base --is-ancestor et supprimez avec branch -d (condition de sécurité 2a ; testé sur le terrain).
    • --squash / --rebase : les commits de la branche ne deviennent jamais des ancêtres de la branche par défaut, donc --is-ancestor sur la branche termine avec 1 pour toujours, même après une fusion entièrement réussie. Utilisez plutôt la garde « MERGED + correspondance headRefOid » (condition de sécurité 2b).
    gh pr merge <num-pr> --repo <propriétaire>/<nom> --merge      # dépôts avec politique squash : --squash, puis utiliser la garde 2b
    gh pr view <num-pr> --repo <propriétaire>/<nom> --json state,mergedAt,mergeCommit
    

    Comportements connus autour de gh pr merge (testé sur le terrain) :

    • Si la branche par défaut locale est extraite dans un autre worktree, gh pr merge --delete-branch exécuté depuis l'intérieur du dépôt peut échouer lors de son post-traitement local même si la fusion distante a réussi. Exécuter la fusion avec --repo depuis l'extérieur du dépôt saute entièrement le post-traitement local, évitant la classe d'échec en amont.
    • Si un échec est quand même signalé, vérifiez d'abord gh pr view --json state — le côté distant est souvent déjà MERGED. Si c'est le cas, terminez les étapes locales restantes (suppression de branche, retrait du worktree) manuellement après les conditions de sécurité ci-dessous.
    • gh pr merge exécuté depuis l'intérieur du dépôt peut avancer rapidement le worktree actuel vers la branche par défaut ; voir des mises à jour importantes de PR antérieures dans sa sortie n'est pas une anomalie.

Conditions de sécurité

Avant toute étape destructive (retrait du worktree, suppression de branche, prune), vérifiez tous les éléments suivants au moment de l'exécution — exécutez la vérification AVANT la suppression, jamais après. Sauter une vérification pré-suppression et « vérifier après » neutralise la garde : une erreur de travail non fusionné ne ferait surface qu'une fois la branche déjà partie (leçon de terrain). Dans les checkouts partagés, l'état peut dériver entre le moment où une étape a été approuvée et celui où elle s'exécute ; si du temps s'est écoulé, réexécutez les vérifications immédiatement avant l'exécution (testé sur le terrain).

  1. gh pr view <num-pr> --repo <propriétaire>/<nom> --json state,mergedAt signale MERGED.

  2. Vérification du travail non fusionné adaptée au mode de fusion. Ne mélangez pas les modes : après un squash, la garde 2a ne passe jamais même pour une PR correctement fusionnée ; et branch -d après un squash soit passe à travers sur la logique « fusionné vers upstream » (si origin/fix/... existe encore — ce qui n'est pas une garantie d'ancêtre), soit est refusé pour toujours comme « pas entièrement fusionné » (si l'upstream est parti).

    • 2a — Mode --merge (commit de fusion) : git -C <repo> merge-base --is-ancestor fix/<tâche> origin/<default> termine avec 0 (vérifiez $LASTEXITCODE dans PowerShell, echo $? dans les shells POSIX). Supprimez la branche avec -d, afin que git lui-même refuse encore si quoi que ce soit a été laissé non fusionné (testé sur le terrain). Mise en garde : -d juge « fusionné » par rapport à l'upstream de la branche s'il est défini, sinon par rapport à HEAD — donc après que la branche distante a été supprimée et nettoyée, et avec HEAD du checkout sur une branche plus ancienne et non liée, -d peut refuser avec « not fully merged » même si la PR a fusionné correctement (testé sur le terrain). Dans ce cas, supprimer avec -D n'est acceptable qu'immédiatement après que la vérification --is-ancestor a terminé avec 0 — la même forme garde-puis--D que 2b ; un -D sans la garde reste interdit.
    • 2b — Mode --squash / --rebase : récupérez state, mergeCommit, et headRefOid via gh pr view <num-pr> --repo <propriétaire>/<nom> --json state,mergeCommit,headRefOid. Notez que mergeCommit est un objet JSON — le hachage du commit est son champ oid (-q .mergeCommit.oid l'extrait) ; headRefOid est une chaîne simple. Confirmez ensuite les deux : (i) git -C <repo> merge-base --is-ancestor <oid-mergeCommit> origin/<default> termine avec 0, et (ii) git -C <repo> rev-parse fix/<tâche> égale headRefOid. La vérification (ii) est le remplacement en mode squash pour « aucun commit laissé derrière » : elle prouve que la tête de la PR fusionnée est exactement le sommet de votre branche locale. Seulement lorsque les deux tiennent, supprimez avec un git branch -D explicite (cette garde remplace -d). La topologie et les chemins de rejet de la garde 2b sont couverts par des historiques Git synthétiques jetables dans scripts/test-cleanup-guards.ps1. Le test isole la configuration Git système/globale, les hooks, la signature et rebase.updateRefs ; capture, efface et restaure chaque variable GIT_* ambiante afin que les chemins du dépôt/de trace ne puissent pas s'échapper du fixture ; et contraint le nettoyage récursif à son enfant temporaire direct généré avec une comparaison de chemin sensible à l'OS et un rejet des points de réanalyse. Le chemin squash GitHub en direct a été mesuré le 2026-07-23 avec PR #2 : MERGED, mergeCommit atterri, pointes locales et distantes inchangées correspondant à headRefOid, -D local gardé et suppression distante explicite ont tous réussi. Le chemin rebase GitHub en direct a été mesuré le 2026-07-26 avec PR #5 : MERGED, un mergeCommit atterri réécrit, la tête d'origine en dehors de l'ascendance de la branche par défaut, pointes locales et distantes inchangées correspondant à headRefOid, -D local gardé, suppression distante explicite et retrait du worktree propriétaire ont tous fonctionné.

Skills associés