Repository navigation
fix(ci,#14507): SKIP_CURRENT etait inatteignable sous Windows -- --apply pouvait retirer son propre repertoire - #14508
Conversation
`is_current` comparait deux ecritures differentes du meme chemin : `git worktree list --porcelain` rend des slash avant, `Path(cwd).resolve()` rend la forme native. L'egalite de chaines ne pouvait donc jamais etre vraie sous Windows. Ce garde n'est pas fail-closed : il ne refuse jamais. Le worktree depuis lequel le script tourne etait traite comme un autre, et si sa PR est MERGED il passait en REMOVE -- soit un `--apply` tentant `git worktree remove` sur son propre repertoire courant. Mesure ai-01, 64 worktrees, lance depuis `.worktrees/ai01-gate-current` (PR #14459 MERGED) : `skipped=0` et le repertoire courant programme au retrait. Apres correctif : `SKIP reason=current_worktree`, `skipped=1`, dans les deux orthographes de chemin. Les tests existants pinnaient l'aval du drapeau (`is_current=True` deja pose) et jamais son calcul. Les trois tests ajoutes rougissent sur le predicat d'avant, le quatrieme est le controle negatif. See #14195, #14476. Co-Authored-By: Claude-Code <noreply@anthropic.com>
Bash Syntax Advisory — shebang / executable-bit warningsSee the |
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] structural review — 2 fichiers (scripts/ci/prune_merged_worktrees.py +34/−1, scripts/tests/test_prune_merged_worktrees.py +50). Les deux fichiers lus au head, le défaut vérifié au base firsthand.
Vérifié firsthand
- Le défaut décrit est réel et localisé : au base
750ba581, la ligne 284 portait"is_current": wt_path == current_path— égalité de chaînes brute entrePath(cwd).resolve()(antislashs natifs Windows) etgit worktree list --porcelain(slashs avant toujours) : toujours fausse sous Windows, doncSKIP_CURRENTinatteignable et un--applylancé depuis un worktree merged aurait tentégit worktree removesur son propre cwd. Le risque était bien vivant. - Le fix est le bon instrument :
same_worktree_path(a, b)comparePath(a).resolve() == Path(b).resolve()— normalise les séparateurs ET la casse sur Windows (l'affirmation du docstring surPath.__eq__insensible à la casse sous Windows est correcte), avec fallback textuel (replace("\\","/").rstrip("/").lower()) surOSError. Le choix du fallback est réfléchi : rendreFalseréintroduirait exactement le défaut pour un lecteur démonté — et sa direction d'échec est skip plutôt que remove, soit fail-closed. - Les tests pinnent les trois niveaux : le mismatch de séparateurs avec contrôle positif (
assert porcelain != native— le test refuse de passer trivialement là où le cas ne se reproduit pas), le slash final, la paire distincte (négatif), et un test de câblage qui pinne explicitement le call site (« un retour à==doit rougir ici ») — c'est ce dernier qui protège sous CI Linux. - Câblage décision intact (
decision="SKIP_CURRENT"l.455, comptage l.620+), 0 secret dans les deux fichiers.
Observations (non bloquantes)
- Sous CI Linux (
os.sep = "/"),test_separator_mismatch...dégénère en paire identique : le pin du mismatch ne mesure que sur des runs Windows. Le test de câblage compense (il rougit partout sur régression==), mais si un jour la CI Windows n'existe plus, le défaut d'origine redeviendrait invisible aux tests — à savoir en relisant ce garde dans un an. - Le fallback lower-case sous POSIX compare insensiblement à la casse un FS qui y est sensible — marginalement trop permissif, mais uniquement sur chemin inaccessible, et dans la direction sûre (skip).
Fix précis, documenté au mesureur près (64 worktrees, gate-current), testé aux trois niveaux. Aucune demande de changement.
Grain: MED/guard — lane myia-ai-01:CoursIA — prev: DEEP/research-code #14471
Closes #14507. See #14195, #14476.
Le défaut
prune_merged_worktrees.pydécidaitis_currentpar une égalité de chaînes entre deux sources qui n'écrivent pas les chemins de la même façon :Sous Windows l'égalité est toujours fausse, donc
SKIP_CURRENTn'était jamais atteint.Ce garde n'est pas fail-closed. Il ne refuse pas trop : il ne refuse jamais. Le worktree depuis lequel le script tourne était traité comme n'importe quel autre, et si sa PR est MERGED il passait en
REMOVE— c'est-à-dire qu'un--applyaurait tentégit worktree removesur le répertoire courant du process.Contrôle positif (ai-01, 2026-09-03, 64 worktrees)
.worktrees/ai01-gate-currentest en HEAD détaché, PR #14459 MERGED, doncWOULD REMOVE. Le lancer depuis lui doit rendreSKIP_CURRENT.Avant :
Après :
Identique en passant le chemin en antislash —
Path.resolve()renormalise l'argument dans la forme native quelle que soit celle reçue, donc les deux orthographes échouaient avant et passent après.Contrôle négatif — lancé d'ailleurs, le même worktree redevient candidat, ce qui vérifie que le correctif n'a pas simplement rendu le prédicat permissif :
(
skipped=1y désigneD:/CoursIAlui-même, désormais reconnu — il était auparavant refusé par le gardeprotected_branch:main, qui masquait le défaut sur le seul worktree où un autre garde couvrait la même erreur.)Ce que les tests couvraient, et ce qu'ils ne couvraient pas
TestDecisionContract::test_skip_currentconstruit le statut avecis_current=Truedéjà posé et vérifie la décision qui en découle. Il pinne l'aval du drapeau ; son calcul n'avait jamais été mesuré. Le critère était pourtant écrit — point 8 de l'en-tête du module de test, hérité de l'acceptance de #14195.C'est la forme exacte de #14476 sur ce même fichier : une acceptance écrite, une implémentation qui ne la réalise pas, et aucun contrôle positif pour distinguer les deux.
Quatre tests ajoutés (
TestSameWorktreePath) :test_separator_mismatch_is_the_same_worktreetest_trailing_separator_is_the_same_worktreetest_get_worktree_info_uses_the_comparison==en l.284 rougit icitest_distinct_worktrees_are_not_currentFalsification : sur le prédicat d'avant reconstitué,
3 failed, 1 passed— les trois premiers rougissent, le contrôle négatif passe des deux côtés, comme attendu d'un contrôle négatif.Validation
Note de contexte
Ce défaut a été trouvé en cherchant à réparer
lookup_pr_for_detached_headsur une copie périmée de l'arbre (D:\CoursIAétait à6d0bd0209,origin/mainà750ba5818). Ce défaut-là était déjà corrigé par #14481, et le diagnostic était donc caduc avant d'être écrit. La mesure qui l'a établi vaut d'être conservée : sous le code périmé, trois worktrees d'historiques distincts (CoursIA-wt/tpx,CoursIA-wt10496,dev/CoursIA-b0merge) résolvaient tous vers#14506, la PR ouverte la plus récente ; sousorigin/mainils résolvent respectivement vers#10532,#10491et#12779, toutes MERGED. #14481 fait ce qu'il annonce.