TL;DR — Quand une session s'arrête au milieu (crash, budget épuisé, bascule d'agent), le travail
devait pouvoir rejoindre une branche distante sans passer la porte des tests. Un crochet
pre-push lançait check:quick et refusait la poussée — normal, puisque la session s'arrête
précisément avant la livraison. Le dépôt interdit --no-verify. La solution n'est pas un
contournement : c'est une voie nommée, gardée par un handoff status: interrompu, journalisée
par un préfixe wip:, et détectable par l'agent qui reprend.
Le piège, et pourquoi il fallait mesurer
Le réflexe est « il suffit de pousser quand même ». Mais deux faits mesurés changent tout :
- La chaîne d'intégration ne se déclenche que sur une pull request et sur la poussée vers la branche principale. Une poussée vers une branche ordinaire ne lance aucun test — le transport était déjà ouvert, gratuit.
- Le seul obstacle réel est un crochet local
pre-push, qui exécute la porte rapide. Et ce crochet porte une consigne écrite noir sur blanc : ne jamais le contourner avec--no-verify, parce qu'un échec local serait un échec d'intégration quelques minutes plus tard.
Donc : un --no-verify débloquerait la poussée, mais en supprimant la garde pour tout le monde
et pour toujours. Ce n'est pas ça qu'on veut.
La voie nommée
On a ajouté une exception explicite au crochet, conditionnée à deux choses à la fois :
- une variable posée uniquement par un nouveau geste,
npm run wip:push— jamais à la main ; - un handoff marqué
status: interrompudans le dossier des relais.
Si l'un des deux manque, la porte redevient normale. Le geste refuse même de continuer sans le
marqueur : sans handoff d'interruption, wip:push sort en erreur. C'est le point clé — la voie ne
s'ouvre que pour une session réellement interrompue, pas pour esquiver un test qui gêne.
Le reste est de la discipline :
- le commit porte le préfixe
wip:, donc l'historique dit ce qu'il est ; - la pull request reste en brouillon, donc le plan d'intégration choisit la vérification la plus légère, et jamais la batterie complète ;
- l'agent qui reprend lit le handoff — branche, dernier commit, ce qui reste, reproduction —, récupère la branche, et rejoue la porte avant de livrer.
Ce qu'on a vérifié, pas supposé
- Le détecteur de handoff interrompu : deux cas, 2/2 verts (il ignore les relais clos, repère
interrompu, retient le plus récent). - Sans handoff d'interruption, la poussée refuse (code de sortie 1).
- Avec le marqueur et la variable, le crochet affiche « porte sautée » et laisse passer.
- Le lint complet (plus de mille fichiers) : zéro après avoir condensé la nouvelle règle, qui faisait d'abord dépasser le plafond de cent vingt lignes d'un fichier toujours chargé.
Le principe réutilisable
Une porte locale n'a pas à choisir entre « tout bloquer » et « ne rien garder ». Elle peut avoir
trois issues : passer, échouer, ou sauvegarder pour reprise. La troisième est légitime dès
qu'elle est nommée (un geste dédié), gardée (un marqueur obligatoire), journalisée
(un préfixe visible) et détectable (le repreneur sait ce qu'il hérite). Un --no-verify silencieux
n'a aucune de ces quatre propriétés ; la voie interrompue les a toutes.
La leçon tient en une phrase : quand un outil bloque un cas légitime, on n'ouvre pas une brèche — on pose une porte à côté, avec son propre verrou.