Constat mesuré (2026-09-23)
pr-gate-stale-sweep.yml conclut success alors qu'il n'a lu qu'une partie de la file, ou rien du tout. Voici les 12 derniers runs terminés, avec la ligne [stale-sweep] open PRs inspected: de chaque log :
| Run |
Créé à |
Événement |
Conclusion |
PRs inspectées |
| 35847738338 |
10:15:26Z |
push |
success |
164 |
| 35847840968 |
10:16:29Z |
push |
success |
159 |
| 35849577845 |
10:34:30Z |
push |
success |
158 |
| 35850437301 |
10:43:34Z |
push |
success |
155 |
| 35850476878 |
10:44:02Z |
push |
success |
155 |
| 35854154661 |
11:22:29Z |
push |
success |
154 |
| 35854581414 |
11:26:53Z |
schedule |
success |
153 |
| 35854957076 |
11:30:45Z |
push |
success |
153 |
| 35861164152 |
12:32:55Z |
push |
success |
156 |
| 35864705790 |
13:05:34Z |
push |
success |
101 |
| 35866395602 |
13:20:35Z |
push |
success |
0 |
| 35867123056 |
13:26:52Z |
push |
success |
0 |
Les trois derniers runs coïncident avec l'épuisement du quota d'installation de l'App. Leurs logs portent tous gh: API rate limit exceeded for installation (étape gate-absent, pr_gate_missing.py l.403). Le run 35866395602 imprime ensuite nothing to do.
Cause (lue sur origin/main)
.github/workflows/pr-gate-stale-sweep.yml, boucle par PR (vers l.371) :
BODY=$(gh api "repos/$REPO/commits/$SHA/check-runs?per_page=100" \
--jq '[...]' 2>/dev/null) || continue
Quand l'appel échoue, la PR est omise sans trace. Si toute la boucle tombe sous la limite, /tmp/runs.jsonl reste vide. Le compteur affiche alors 0, le sélecteur n'a aucun candidat, et le run sort en success.
Le listing, quelques lignes plus haut, a déjà été durci contre ce même défaut. Le commentaire l.340-353 le dit : « la quiétude lavait la sonde au vert ». Il échoue désormais bruyamment (::error::[stale-sweep] NO-OP -- PR listing failed, exit 1). La boucle par PR n'a pas reçu ce durcissement, et le défaut s'est déplacé d'un niveau.
Conséquence observée
Un DWELL échu n'est levé que si le balayage relance le PR gate. Exemple : #17497, écoulé à 13:07:00Z, a encore son PR gate rouge à 13:30Z, alors que deux balayages ont conclu success entre-temps. Sur le dashboard de circulation, cela se lit « le balayage n'est plus servi », alors qu'il tourne bien : il rend un vert à vide.
Acceptance proposée
- La boucle compte les PRs listées (
/tmp/openprs.txt), inspectées et omises, et imprime les trois nombres.
- Omission totale (PRs listées > 0 et inspectées = 0) :
::error::[stale-sweep] NO-OP -- check-runs unreadable for all N listed PRs puis exit 1. C'est la même famille d'annotation que le garde du listing, donc triable par scripts/ci/classify_job_deaths.py.
- Omission partielle : au minimum un
::warning:: qui nomme le nombre de PRs omises. Le seuil au-delà duquel ce devient un ::error:: reste à trancher (proposition : plus de 10 %).
- Contrôle positif : forcer l'échec de l'appel par PR (par exemple un
REPO invalide sur l'appel check-runs seul) doit rendre un run failure qui porte l'annotation NO-OP. Le chemin nominal doit rester success.
Hors périmètre, observation séparée à vérifier
Le même appel lit commits/<sha>/check-runs sans filter=all. Or le filtre par défaut (latest) masque les check-runs d'une suite relancée. C'est à vérifier dans un sujet distinct avant de conclure qu'il y a un impact sur le sélecteur.
Mesure : myia-po-2025:CoursIA-2 (titulaire). Les logs se relisent avec gh run view <id> --log | grep "open PRs inspected".
Constat mesuré (2026-09-23)
pr-gate-stale-sweep.ymlconclutsuccessalors qu'il n'a lu qu'une partie de la file, ou rien du tout. Voici les 12 derniers runs terminés, avec la ligne[stale-sweep] open PRs inspected:de chaque log :Les trois derniers runs coïncident avec l'épuisement du quota d'installation de l'App. Leurs logs portent tous
gh: API rate limit exceeded for installation(étape gate-absent,pr_gate_missing.pyl.403). Le run 35866395602 imprime ensuitenothing to do.Cause (lue sur
origin/main).github/workflows/pr-gate-stale-sweep.yml, boucle par PR (vers l.371) :Quand l'appel échoue, la PR est omise sans trace. Si toute la boucle tombe sous la limite,
/tmp/runs.jsonlreste vide. Le compteur affiche alors 0, le sélecteur n'a aucun candidat, et le run sort ensuccess.Le listing, quelques lignes plus haut, a déjà été durci contre ce même défaut. Le commentaire l.340-353 le dit : « la quiétude lavait la sonde au vert ». Il échoue désormais bruyamment (
::error::[stale-sweep] NO-OP -- PR listing failed,exit 1). La boucle par PR n'a pas reçu ce durcissement, et le défaut s'est déplacé d'un niveau.Conséquence observée
Un
DWELLéchu n'est levé que si le balayage relance lePR gate. Exemple : #17497,écoulé à 13:07:00Z, a encore sonPR gaterouge à 13:30Z, alors que deux balayages ont conclusuccessentre-temps. Sur le dashboard de circulation, cela se lit « le balayage n'est plus servi », alors qu'il tourne bien : il rend un vert à vide.Acceptance proposée
/tmp/openprs.txt), inspectées et omises, et imprime les trois nombres.::error::[stale-sweep] NO-OP -- check-runs unreadable for all N listed PRspuisexit 1. C'est la même famille d'annotation que le garde du listing, donc triable parscripts/ci/classify_job_deaths.py.::warning::qui nomme le nombre de PRs omises. Le seuil au-delà duquel ce devient un::error::reste à trancher (proposition : plus de 10 %).REPOinvalide sur l'appel check-runs seul) doit rendre un runfailurequi porte l'annotation NO-OP. Le chemin nominal doit restersuccess.Hors périmètre, observation séparée à vérifier
Le même appel lit
commits/<sha>/check-runssansfilter=all. Or le filtre par défaut (latest) masque les check-runs d'une suite relancée. C'est à vérifier dans un sujet distinct avant de conclure qu'il y a un impact sur le sélecteur.Mesure :
myia-po-2025:CoursIA-2(titulaire). Les logs se relisent avecgh run view <id> --log | grep "open PRs inspected".