Le symptome
Le message DWELL de la gate annonce une heure de levee que le balayage ne peut pas honorer. Sur #15840 :
[pr-gate] DWELL -- tete du 2026-09-14T00:08:14Z, 81 min -- plancher 120 min, reste 39 min,
leve au premier balayage suivant 2026-09-14T02:08:14Z.
Le balayage horaire (pr-gate-stale-sweep.yml, cron '7 * * * *') re-agrege cette jambe
des que le plancher est ecoule ; aucun geste manuel n'est requis.
2026-09-14T02:08:14Z n'est pas un instant de balayage. Le balayage tourne a :07. Le passage de 02:07:00Z arrive 74 secondes trop tot — le plancher n'est pas encore ecoule, la jambe n'est pas re-agregee. Le premier balayage qui leve reellement est 03:07:00Z, une heure plus tard que l'heure publiee.
La mesure
|
|
| tete de la PR |
2026-09-14T00:08:14Z |
| plancher 120 min ecoule a |
2026-09-14T02:08:14Z — la valeur publiee par le message |
balayage :07 precedent |
02:07:00Z → 74 s trop tot, ne leve pas |
balayage :07 suivant |
03:07:00Z → leve |
Le cron: '7 * * * *' est declare en .github/workflows/pr-gate-stale-sweep.yml L101.
Le module se contredit lui-meme
Le defaut n'est pas une ambiguite de specification : la bonne semantique est deja ecrite dans le docstring de tete de scripts/ci/merge_dwell.py, quelques dizaines de lignes au-dessus du code fautif —
« Consequence a assumer et a dire : le plancher est un PLANCHER, pas une horloge. Une PR devient mergeable au premier balayage horaire suivant l'ecoulement des 2 h — donc entre 2 h 00 et 3 h 00 apres son dernier commit, pas a 2 h 00 pile. »
et le code publie malgre tout l'instant du plancher sous l'etiquette « premier balayage suivant » :
lift = (committed_at + timedelta(minutes=dwell_min)).strftime("%Y-%m-%dT%H:%M:%SZ")
...
"leve au premier balayage suivant {}. "
L'intention de #15693 etait bonne (donner une heure absolue plutot que « reste 39 min », pour ne pas inciter a un re-push qui remet le plancher a zero). C'est l'arrondi au balayage qui manque.
Ce que ca coute
Une lane — ou le coordinateur — lit l'heure annoncee, revient a cette heure-la, et trouve la PR toujours rouge. Je me suis fait prendre cette nuit : mon propre document de passation portait « #15840 : merge des 02:08:14Z » sur la foi de ce message. Le cout n'est pas le retard d'une heure, c'est qu'un organe dont le role est de dire « aucun geste manuel n'est requis » devient une source de doute : l'heure passe, le rouge reste, et le reflexe suivant est le geste manuel que le message disait inutile (poser merge-dwell-waived, qui est reserve a l'urgence « main rouge »).
Le correctif
Arrondir lift au premier instant :07 strictement posterieur au plancher, au lieu de publier le plancher :
floor = committed_at + timedelta(minutes=dwell_min)
lift_dt = floor.replace(minute=SWEEP_MINUTE, second=0, microsecond=0)
if lift_dt <= floor:
lift_dt += timedelta(hours=1)
avec SWEEP_MINUTE = 7 nomme comme constante et rattache au cron (un commentaire qui pointe la ligne du workflow : si le cron bouge, la constante doit bouger).
Le test suit le correctif, il ne se desserre pas. scripts/tests/test_merge_dwell.py epingle la valeur actuelle dans un cas nomme (120 min -> leve au premier balayage suivant 13:55). C'est un cliquet legitime : il rend la semantique visible. Il se deplace avec le changement, dans le meme commit — pas remplace par une assertion lache.
Portee — ce que je n'ai pas etabli
- Le sweep porte un second etage de maturite (
DWELL_FLOOR_S, tier proxy sur l'age du verdict et non sur la date de la tete, avec MAX_IMMATURE=4). Un verdict jeune sur une tete agee « lande en tier 1 et attend une heure » d'apres son propre commentaire. Je n'ai pas mesure si cet etage repousse encore la levee au-dela du :07 calcule ici. Le correctif ci-dessus rend le message exact vis-a-vis du cron ; il ne pretend pas modeliser le tiering.
- Je n'ai pas verifie le comportement au changement d'heure (le module travaille en UTC, donc a priori sans objet — non teste).
Scope
ci / workflow. Petit, borne, entierement dans scripts/ci/merge_dwell.py + son test. Grain lanable — je ne le claime pas : la lane qui le prend n'a pas besoin de refaire la mesure, elle est ci-dessus.
See #15853
Le symptome
Le message
DWELLde la gate annonce une heure de levee que le balayage ne peut pas honorer. Sur #15840 :2026-09-14T02:08:14Zn'est pas un instant de balayage. Le balayage tourne a:07. Le passage de 02:07:00Z arrive 74 secondes trop tot — le plancher n'est pas encore ecoule, la jambe n'est pas re-agregee. Le premier balayage qui leve reellement est 03:07:00Z, une heure plus tard que l'heure publiee.La mesure
2026-09-14T00:08:14Z2026-09-14T02:08:14Z— la valeur publiee par le message:07precedent02:07:00Z→ 74 s trop tot, ne leve pas:07suivant03:07:00Z→ leveLe
cron: '7 * * * *'est declare en.github/workflows/pr-gate-stale-sweep.ymlL101.Le module se contredit lui-meme
Le defaut n'est pas une ambiguite de specification : la bonne semantique est deja ecrite dans le docstring de tete de
scripts/ci/merge_dwell.py, quelques dizaines de lignes au-dessus du code fautif —et le code publie malgre tout l'instant du plancher sous l'etiquette « premier balayage suivant » :
L'intention de #15693 etait bonne (donner une heure absolue plutot que « reste 39 min », pour ne pas inciter a un re-push qui remet le plancher a zero). C'est l'arrondi au balayage qui manque.
Ce que ca coute
Une lane — ou le coordinateur — lit l'heure annoncee, revient a cette heure-la, et trouve la PR toujours rouge. Je me suis fait prendre cette nuit : mon propre document de passation portait « #15840 : merge des 02:08:14Z » sur la foi de ce message. Le cout n'est pas le retard d'une heure, c'est qu'un organe dont le role est de dire « aucun geste manuel n'est requis » devient une source de doute : l'heure passe, le rouge reste, et le reflexe suivant est le geste manuel que le message disait inutile (poser
merge-dwell-waived, qui est reserve a l'urgence « main rouge »).Le correctif
Arrondir
liftau premier instant:07strictement posterieur au plancher, au lieu de publier le plancher :avec
SWEEP_MINUTE = 7nomme comme constante et rattache au cron (un commentaire qui pointe la ligne du workflow : si le cron bouge, la constante doit bouger).Le test suit le correctif, il ne se desserre pas.
scripts/tests/test_merge_dwell.pyepingle la valeur actuelle dans un cas nomme (120 min -> leve au premier balayage suivant 13:55). C'est un cliquet legitime : il rend la semantique visible. Il se deplace avec le changement, dans le meme commit — pas remplace par une assertion lache.Portee — ce que je n'ai pas etabli
DWELL_FLOOR_S, tier proxy sur l'age du verdict et non sur la date de la tete, avecMAX_IMMATURE=4). Un verdict jeune sur une tete agee « lande en tier 1 et attend une heure » d'apres son propre commentaire. Je n'ai pas mesure si cet etage repousse encore la levee au-dela du:07calcule ici. Le correctif ci-dessus rend le message exact vis-a-vis du cron ; il ne pretend pas modeliser le tiering.Scope
ci/workflow. Petit, borne, entierement dansscripts/ci/merge_dwell.py+ son test. Grain lanable — je ne le claime pas : la lane qui le prend n'a pas besoin de refaire la mesure, elle est ci-dessus.See #15853