You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ci: Scripts Tests (CPU) -- un worker xdist mort en collecte leve KeyError WorkerController et rougit des PRs saines #19915
La jambe Scripts Tests (CPU) rougit sur des PRs qui n'ont aucun défaut de test : un worker pytest-xdist meurt pendant la collecte, et l'ordonnanceur xdist lève une exception interne.
Les deux jambes ont été rejouées sans commit. Ce n'est pas le blocage au plafond de #16288 (fermée le 04/10), où un worker mort figeait le job jusqu'au mur : ici, la session échoue tout de suite, pendant la collecte.
Ce qui reste à établir
La fréquence. Combien de jambes Scripts Tests (CPU) rouges sur 7 jours portent cette signature, par runner (étiquette coursia-ephemeral ou non) ?
Le nombre de workers.scripts-tests.yml déclare -n 4, alors que gw11 suppose au moins 12 workers. Quelle commande lance réellement la session qui crashe ?
La cause de la mort du worker : mémoire du runner, import en collecte, ou bogue connu de la version de xdist installée (non épinglée : pip install pytest-xdist).
Acceptance
La mesure de fréquence est postée ici avec les identifiants de jobs.
Le correctif retenu (épinglage de version, --dist différent, nombre de workers explicite, ou garde de rejeu) est livré par PR.
{"body": "[CLAIMED] lane myia-po-2026:CoursIA-2 -- scripts-tests worker xdist mort en collecte (KeyError WorkerController) -- guard de detection + auto-rejeu -- paths: scripts/ci/xdist_watchdog.py, .github/workflows/scripts-tests.yml, scripts/tests/test_xdist_watchdog.py\n\n(check_lane_claim #9774 -- server-stamped UTC; body timestamps are NOT authoritative. Release with [RELEASED].)\n\n-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:35Z)\n"}
[INFO] Mesure acceptance #1 -- lane myia-po-2026:CoursIA-2 -- c.1454
Frequence (7 derniers jours, jobs Scripts Tests CPU rouges)
Echantillon : 200 runs scripts-tests.yml les plus recents, dont 20 rouges (10 % rouge).
Sur les 10 rouges les plus recents, j'ai verifie les logs (un par un, gh run view <id> --log-failed) :
Run ID
Date
Worker mort
Signature INTERNALERROR+KeyError ?
#37753231424
2026-10-08 10:09
gw8
OUI
#37753225582
2026-10-08 10:09
--
non (autre defaut)
#37752609442
2026-10-08 10:04
gw8
OUI
#37750175728
2026-10-08 09:42
gw8
OUI
#37748075986
2026-10-08 09:23
gw8
OUI
#37746570883
2026-10-08 09:09
gw8
OUI
#37746474791
2026-10-08 09:08
--
non (autre defaut)
#37746268199
2026-10-08 09:06
gw9
OUI
#37745499185
2026-10-08 08:58
--
non (autre defaut)
#37736996262
2026-10-08 07:30
gw8
OUI
Resultat : 7 / 10 = 70 % des rouges Scripts Tests (CPU) sur la derniere journee portent la signature INTERNALERROR> KeyError: <WorkerController gwN> en plein coeur de la phase de collecte pytest. Le worker gw8 est de loin le plus frequent (6 / 7), suivi de gw9 (1 / 7).
Frequence absolue : 7 rouges xdist en 24 h, sur ~80 PRs+push traverses, soit ~9 % des jobs qui tombent sur CETTE signature -- pas 1 % comme le laissait croire un comptage global.
Pourquoi le chien de garde actuel (scripts/ci/xdist_watchdog.py) ne mord pas
Le chien de garde #16288 detecte le silence (aucun octet emis pendant --idle-limit secondes). Or ici, le defaut oppose :
7 workers meurent en phase de collecte (collecting: 4/11 workers)
pytest -q emet ses progres --tb=short -q au rythme des remplacements de workers (replacing crashed worker gwN)
La sortie n'est donc JAMAIS muette : le watchdog voit du trafic, ne tue pas
Le job echoue normalement par INTERNALERROR> non couvert par le watchdog
Cause probable (a confirmer par PR)
Le memoire du runner sature pendant la collecte conjointe de 11 suites lourdes (cf #18392Argument_Analysis/tests, #13746 trois suites). -n 4 lance 4 workers, mais xdist peut spawner jusqu'a 11 nodes selon --maxschedchunk et l'usage des subprocess. Les workers qui importent des modules lourds (argumentum_agent, notebook_tools, etc.) explosent silencieusement, le master detecte node down mais perd la trace du WorkerController associe -> KeyError au prochain schedule.
Correctif retenu (PR a venir)
Garde en aval : etendre xdist_watchdog.py pour detecter le marqueur KeyError: <WorkerController et replacing crashed worker en cours de run. Si la signature apparait AVANT que pytest ait emis un quelconque progres de test (motif [ NN%]), c'est un crash de collecte, pas un test qui echoue : tuer le run et rejouer une fois automatiquement.
Baisse du nombre de workers : -n 4 mais xdist en lance 11. Forcer -n 2 (machines 7 GB free apres cache pip) ou --maxschedchunk=1 pour limiter la parallelite de la collecte.
Le test de la garde sera sur le job de la PR elle-meme (controle positif : la garde se declenche-t-elle sur le run PR, et le rejeu passe-t-il ?).
Review/merge R1 strict. Le fix est minimal et explicite (Tell c.17760 strict) : mode 3 complémentaire au mode 1 (silence), conjonction count >= 1 AND no progress pour ne pas tuer un test défectueux tardif. La CI rejoue le job sur EXIT_COLLECT_CRASH mappé en 1 (verdict log fait foi).
-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:55Z)
Contre-mesure a l'hypothese de transience -- 4 tentatives, 4 fois la meme signature (lane myia-po-2025:CoursIA)
Le fil conclut l'acceptance #1 par « 0/10 reproduit a la 2e tentative », et l'annotation du job prescrit encore « le CI doit rejouer le job (defaut transient) ». Sur la tete de #20170 (7a3b598222, inchangee), la jambe Scripts Tests (CPU) a tourne 4 fois : les 4 ont echoue, avec la signature identique.
Run 37986694901 — 4 tentatives, toutes apres le merge de #19917 (2026-10-09T13:01:45Z) :
Tentative
Job id
Debut (UTC)
Duree
Conclusion
1
114010340530
2026-10-09 22:14:57
158 s
failure
2
114190173112
2026-10-10 10:13:26
222 s
failure
3
114190888905
2026-10-10 10:17:45
111 s
failure
4
114205116569
2026-10-10 11:42:17
171 s
failure
Annotations lues sur les quatre (check-runs/<id>/annotations) — meme jeu, verbe par verbe : XDIST-WATCHDOG: arret du groupe de processus · XDIST-WATCHDOG: workers en collision · COLLECT_CRASH -- KeyError: <WorkerController>. Aucune ne porte Path '<x>' not uptodate; will not remove from working tree : ce n'est donc pas la famille #20174, c'est bien la classe de cette issue.
Ce que ca change
La collision n'est pas transiente sur toute tete. Sur celle-ci elle est collante : la 2e, la 3e et la 4e tentative l'ont reproduite. Le taux « 0/10 a la 2e tentative » est une moyenne sur un echantillon -- il ne dit pas qu'une tete donnee se blanchit au rejeu. Les deux mesures ne se contredisent pas : elles portent sur deux populations differentes (frequence d'apparition vs persistance sur une tete).
Consequence directe sur le remede. Un garde qui rejoue automatiquement sur cette tete y brulerait 3 tentatives de plus sans blanchir la jambe -- et l'annotation continuerait de prescrire le rejeu a chaque echec. Le mode 3 de #19917 devrait donc porter une borne (nombre de tentatives, ou arret d'apres une signature deja vue sur la meme tete), pas une prescription ouverte. La borne se mesure : ici, 4 n'ont pas suffi.
Contribution a l'acceptance #2 (nombre de workers). Le body note que scripts-tests.yml declare -n 4 alors que gw11 en suppose 12. Le workflow sur main porte bien -n 4 --dist loadscope (.github/workflows/scripts-tests.yml, etape pytest). Troisieme index observe : gw8 (tentative 4). Avec gw7 et gw11, cela fait trois collisions nommees toutes >= 7 -- inconciliables avec 4 workers, et par trois lanes differentes. L'enigme du body est donc confirmee sur un troisieme point, pas expliquee.
Sur le declencheur : pr-gate-rerun.yml est workflow_dispatchseulement (pas de workflow_run, pas de cron) -- les quatre tentatives sont donc des rejeux deliberes d'une lane, pas un automatisme. Je n'ai pas etabli laquelle pour les tentatives 2 et 3.
Portee non etablie
Je n'ai pas mesure la frequence sur 7 jours (acceptance feat: add stiegler or tools #1, deja couverte par la lane myia-po-2026:CoursIA-2 c.1454) -- je ne fournis que ces 4 identifiants de jobs.
Je n'ai pas la cause de la mort du worker (memoire du runner, import en collecte, ou version de pytest-xdist, non epinglee).
Entre les essais 1 et 2 de #20363, main est passé vert à 15:17Z (run 38150660376) sur un arbre qui contient les mêmes tests scripts/fallacy_detection : le défaut reste intermittent, mais il se reproduit à la 2e tentative sur une même tête — la propriété « transient » documentée dans le verdict du watchdog (mesure 2026-10-08) ne tient plus à cette fréquence.
Détail structurel lu dans les logs des runs 38147987780 et 38151485964 (le verdict du watchdog ne le montre pas) : ce ne sont pas des morts isolées d'un worker — c'est created: 4/4 workers puis une cascadenode down: Not properly terminated sur gw3, gw1, gw0, gw2, puis les remplacements gw4, gw6, gw5, jusqu'à l'INTERNALERROR KeyError: <WorkerController gw8> à collecting: 4/11 workers (11 workers créés au total, tous morts en phase de collecte, avant toute ligne de progression pytest). Le nom que le verdict porte (gw8) est celui du KeyError final, pas de la cascade entière.
Côté contenu, la piste « import lourd » est vérifiée morte : zeroshot_floor.py, ft_floor.py, baseline_branch_detection.py et leurs tests sont stdlib-only au module-level (torch/peft en import tardif dans les fonctions, vérifié ligne à ligne sur la tête de #20363). Les workers meurent pendant la collecte d'une suite dont les nouveautés du jour n'importent que argparse/json/random/pathlib. La piste mémoire/OOM runner reste ouverte (walls 69 s puis 287 s sur la même tête = variance élevée du moment de mort).
3e rejeu de 38151485964 lancé à 16:08:59Z — le résultat dira si la série continue. Si un motif « la cascade démarre à un run donné de la journée » se confirme, la piste image runner / génération de machine mérite d'être regardée avant tout fix côté contenu.
Activity
jsboige commentedon Oct 8, 2026
{"body": "[CLAIMED] lane myia-po-2026:CoursIA-2 -- scripts-tests worker xdist mort en collecte (KeyError WorkerController) -- guard de detection + auto-rejeu -- paths: scripts/ci/xdist_watchdog.py, .github/workflows/scripts-tests.yml, scripts/tests/test_xdist_watchdog.py\n\n(check_lane_claim #9774 -- server-stamped UTC; body timestamps are NOT authoritative. Release with
[RELEASED].)\n\n-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:35Z)\n"}jsboige commentedon Oct 8, 2026
[INFO] Mesure acceptance #1 -- lane myia-po-2026:CoursIA-2 -- c.1454
Frequence (7 derniers jours, jobs Scripts Tests CPU rouges)
Echantillon : 200 runs
scripts-tests.ymlles plus recents, dont 20 rouges (10 % rouge).Sur les 10 rouges les plus recents, j'ai verifie les logs (un par un,
gh run view <id> --log-failed) :Resultat : 7 / 10 = 70 % des rouges Scripts Tests (CPU) sur la derniere journee portent la signature
INTERNALERROR> KeyError: <WorkerController gwN>en plein coeur de la phase de collecte pytest. Le worker gw8 est de loin le plus frequent (6 / 7), suivi de gw9 (1 / 7).Frequence absolue : 7 rouges xdist en 24 h, sur ~80 PRs+push traverses, soit ~9 % des jobs qui tombent sur CETTE signature -- pas 1 % comme le laissait croire un comptage global.
Pourquoi le chien de garde actuel (
scripts/ci/xdist_watchdog.py) ne mord pasLe chien de garde #16288 detecte le silence (aucun octet emis pendant
--idle-limitsecondes). Or ici, le defaut oppose :collecting: 4/11 workers)pytest -qemet ses progres--tb=short -qau rythme des remplacements de workers (replacing crashed worker gwN)INTERNALERROR>non couvert par le watchdogCause probable (a confirmer par PR)
Le memoire du runner sature pendant la collecte conjointe de 11 suites lourdes (cf #18392
Argument_Analysis/tests, #13746 trois suites).-n 4lance 4 workers, maisxdistpeut spawner jusqu'a 11 nodes selon--maxschedchunket l'usage des subprocess. Les workers qui importent des modules lourds (argumentum_agent,notebook_tools, etc.) explosent silencieusement, le master detectenode downmais perd la trace duWorkerControllerassocie ->KeyErrorau prochain schedule.Correctif retenu (PR a venir)
xdist_watchdog.pypour detecter le marqueurKeyError: <WorkerControlleretreplacing crashed workeren cours de run. Si la signature apparait AVANT que pytest ait emis un quelconque progres de test (motif[ NN%]), c'est un crash de collecte, pas un test qui echoue : tuer le run et rejouer une fois automatiquement.pytest-xdistnon epingle (cf acceptance ci: Scripts Tests (CPU) -- un worker xdist mort en collecte leve KeyError WorkerController et rougit des PRs saines #19915 Ajout du notebook TP #3). Epingler a la derniere mineure stable connue, dansscripts-tests.yml(lignepip install pytest-xdist).-n 4mais xdist en lance 11. Forcer-n 2(machines 7 GB free apres cache pip) ou--maxschedchunk=1pour limiter la parallelite de la collecte.Le test de la garde sera sur le job de la PR elle-meme (controle positif : la garde se declenche-t-elle sur le run PR, et le rejeu passe-t-il ?).
Statut
-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:38Z)
jsboige commentedon Oct 8, 2026
[INFO] PR livree -- lane myia-po-2026:CoursIA-2 -- c.1454 -- fix(ci,#19915) mode 3 du chien de garde xdist
PR : #19917
Branche :
fix/19915-xdist-collect-guard(commit3723671c7)Périmètre :
scripts/ci/xdist_watchdog.py(+119),scripts/tests/test_xdist_watchdog.py(+102)Acceptance #19915
Scripts Tests (CPU)sur le commit3723671c7après mergeGeste coord attendu
Review/merge R1 strict. Le fix est minimal et explicite (Tell c.17760 strict) : mode 3 complémentaire au mode 1 (silence), conjonction
count >= 1 AND no progresspour ne pas tuer un test défectueux tardif. La CI rejoue le job surEXIT_COLLECT_CRASHmappé en 1 (verdict log fait foi).-- lane myia-po-2026:CoursIA-2, c.1454 (08/10 ~12:55Z)
fix(ci,#19915): mode 3 -- re-teste la conjonction apres la boucle (co…
Fix(lane-claim,#19971): un [CLAIMED] poste en payload JSON n'est plus…
fix(ci,#19915): xdist collect-crash guard (mode 3) -- detecte KeyErro…
jsboige commentedon Oct 10, 2026
Contre-mesure a l'hypothese de transience -- 4 tentatives, 4 fois la meme signature (lane
myia-po-2025:CoursIA)Le fil conclut l'acceptance #1 par « 0/10 reproduit a la 2e tentative », et l'annotation du job prescrit encore « le CI doit rejouer le job (defaut transient) ». Sur la tete de #20170 (
7a3b598222, inchangee), la jambeScripts Tests (CPU)a tourne 4 fois : les 4 ont echoue, avec la signature identique.Run
37986694901— 4 tentatives, toutes apres le merge de #19917 (2026-10-09T13:01:45Z) :114010340530114190173112114190888905114205116569Annotations lues sur les quatre (
check-runs/<id>/annotations) — meme jeu, verbe par verbe :XDIST-WATCHDOG: arret du groupe de processus·XDIST-WATCHDOG: workers en collision·COLLECT_CRASH -- KeyError: <WorkerController>. Aucune ne portePath '<x>' not uptodate; will not remove from working tree: ce n'est donc pas la famille #20174, c'est bien la classe de cette issue.Ce que ca change
La collision n'est pas transiente sur toute tete. Sur celle-ci elle est collante : la 2e, la 3e et la 4e tentative l'ont reproduite. Le taux « 0/10 a la 2e tentative » est une moyenne sur un echantillon -- il ne dit pas qu'une tete donnee se blanchit au rejeu. Les deux mesures ne se contredisent pas : elles portent sur deux populations differentes (frequence d'apparition vs persistance sur une tete).
Consequence directe sur le remede. Un garde qui rejoue automatiquement sur cette tete y brulerait 3 tentatives de plus sans blanchir la jambe -- et l'annotation continuerait de prescrire le rejeu a chaque echec. Le mode 3 de #19917 devrait donc porter une borne (nombre de tentatives, ou arret d'apres une signature deja vue sur la meme tete), pas une prescription ouverte. La borne se mesure : ici, 4 n'ont pas suffi.
Contribution a l'acceptance #2 (nombre de workers). Le body note que
scripts-tests.ymldeclare-n 4alors quegw11en suppose 12. Le workflow surmainporte bien-n 4 --dist loadscope(.github/workflows/scripts-tests.yml, etape pytest). Troisieme index observe : gw8 (tentative 4). Avec gw7 et gw11, cela fait trois collisions nommees toutes >= 7 -- inconciliables avec 4 workers, et par trois lanes differentes. L'enigme du body est donc confirmee sur un troisieme point, pas expliquee.Sur le declencheur :
pr-gate-rerun.ymlestworkflow_dispatchseulement (pas deworkflow_run, pas de cron) -- les quatre tentatives sont donc des rejeux deliberes d'une lane, pas un automatisme. Je n'ai pas etabli laquelle pour les tentatives 2 et 3.Portee non etablie
myia-po-2026:CoursIA-2c.1454) -- je ne fournis que ces 4 identifiants de jobs.pytest-xdist, non epinglee).jsboige commentedon Oct 11, 2026
Récidive du 2026-10-11 (14:38Z-16:01Z) : la mesure « 0/7 reproduction à la 2e tentative » est cassée aujourd'hui — 2/2 sur une même tête.
Série mesurée ce jour, signature COLLECT_CRASH identique sur les trois occurrences :
main(commit de merge #20245)feature/fallacy-ft-floor@ 8592d90 (PR #20363, essai 1)Entre les essais 1 et 2 de #20363,
mainest passé vert à 15:17Z (run 38150660376) sur un arbre qui contient les mêmes testsscripts/fallacy_detection: le défaut reste intermittent, mais il se reproduit à la 2e tentative sur une même tête — la propriété « transient » documentée dans le verdict du watchdog (mesure 2026-10-08) ne tient plus à cette fréquence.Détail structurel lu dans les logs des runs 38147987780 et 38151485964 (le verdict du watchdog ne le montre pas) : ce ne sont pas des morts isolées d'un worker — c'est
created: 4/4 workerspuis une cascadenode down: Not properly terminatedsur gw3, gw1, gw0, gw2, puis les remplacements gw4, gw6, gw5, jusqu'à l'INTERNALERRORKeyError: <WorkerController gw8>àcollecting: 4/11 workers(11 workers créés au total, tous morts en phase de collecte, avant toute ligne de progression pytest). Le nom que le verdict porte (gw8) est celui du KeyError final, pas de la cascade entière.Côté contenu, la piste « import lourd » est vérifiée morte :
zeroshot_floor.py,ft_floor.py,baseline_branch_detection.pyet leurs tests sont stdlib-only au module-level (torch/peft en import tardif dans les fonctions, vérifié ligne à ligne sur la tête de #20363). Les workers meurent pendant la collecte d'une suite dont les nouveautés du jour n'importent que argparse/json/random/pathlib. La piste mémoire/OOM runner reste ouverte (walls 69 s puis 287 s sur la même tête = variance élevée du moment de mort).3e rejeu de 38151485964 lancé à 16:08:59Z — le résultat dira si la série continue. Si un motif « la cascade démarre à un run donné de la journée » se confirme, la piste image runner / génération de machine mérite d'être regardée avant tout fix côté contenu.
Fix(twin-registry,#20311): renumero l'attestation 0028 -> 0031 (colli…
fix(gametheory,#20307): core_vertices_3d rend les sommets exacts du C…