Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/rules/git-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ GitHub auto-closes issues on `Refs #N`, `Fixes #N`, `Closes #N`. Use safe syntax

- **L'alternative merge d'abord, quand elle existe** : `git merge origin/main`, `gh pr update-branch`, cherry-pick, revert, nouveaux commits. Le force-push est le dernier recours, jamais le réflexe de rebase par défaut.
- **`gh pr update-branch` remet le plancher DWELL à zéro** (#15859) : le plancher de merge (120 min, `scripts/ci/merge_dwell.py`) se mesure depuis le **dernier commit** de la branche, et `update-branch` en crée un (merge commit). Rafraîchir sa branche pour récupérer un fix de `main` repousse donc le merge de 120 min — et tout plancher déjà noté dans un rapport est périmé. Ce rouge n'est **pas un défaut de la PR** : le gate le nomme `DWELL -- ... leve au premier balayage suivant <ts>` et le balayage horaire (`pr-gate-stale-sweep.yml`) lève seul. Ne pas re-pusher pour « réparer » : chaque push re-arme le plancher depuis la nouvelle tête.
- **`update-branch` tue AUSSI le dossier de prévalidation, et c'est la moitié qu'on oublie** : il change la tête, donc le contrat exact-head (`[ADJOINT PREFLIGHT]`, `surfaces-sha256`, comptes `diff-files`/`additions`) est **périmé à la seconde**, et `check_adjoint_prevalidation.py` rend `head is stale` / `discussion surfaces changed`. La boucle se referme sur elle-même : `main` rouge → la lane doit `update-branch` (bon geste) → **le dossier périt** → l'adjoint ne peut plus attester `checks: latest-wins-green` sans mentir, donc il retient le dossier (à juste titre) → `exit 1` → pas de merge → la PR vieillit → il faut re-`update-branch`. **Correction du 2026-09-20 (#16962)** : cette boucle avait d'abord été décrite avec un second effet — « et le DWELL se ré-arme ». C'est **faux** pour un `update-branch` sans conflit (`last_authoritative_committed_at` saute les fusions prouvées content-free), et l'erreur rendait la boucle plus serrée qu'elle ne l'est. Le seul effet réel est la **péremption du dossier**, et il suffit à produire la boucle. Mesure du 2026-09-19 : **17 candidates sur 17** refusées par le gate pour ce seul motif, aucune pour un défaut de PR.

**L'ordre qui sort de la boucle — le dossier vient APRÈS la stabilisation de la branche, jamais avant :**

1. la lane `update-branch` si elle doit récupérer `main` ;
2. on laisse les checks se ré-agréger à la nouvelle tête, puis **on rejoue** le job si besoin — personne ne re-pousse. Le plancher DWELL, lui, n'a **pas** été ré-armé par un `update-branch` sans conflit : il continue de se mesurer depuis le dernier commit d'auteur. Il ne se ré-arme que si le rafraîchissement a exigé une **résolution manuelle de conflit** (arbre ≠ auto-merge des parents), cas où l'attente de 120 min redevient réelle ;
3. **alors** l'adjoint écrit le dossier, à la tête exacte ;
4. le coordinateur merge **immédiatement**, et **la branche est gelée entre 3 et 4**.

Le gel est la pièce qui manquait : un dossier a besoin d'une **branche silencieuse**, sinon le travail de prévalidation est détruit par le travail de réparation, indéfiniment.
- **`--force-with-lease` plutôt que `--force`** : il échoue si le remote a bougé depuis ta dernière lecture — précisément le cas « une autre lane a poussé sans que je le sache ». C'est le garde-fou qui rend le périmètre ci-dessus sûr.
- **Jamais de `reset --hard`** sur `main` ni sur une branche partagée.
- **Un secret déjà commité ne se répare PAS par réécriture d'historique** : branche propre + cherry-pick, et **rotation de la clé** (cf [secrets-hygiene.md](secrets-hygiene.md) règle 5).
Expand Down
Loading