Repository navigation
pick_idle_grain : pick_with_replacements chemin critique 300-400s (issue de suivi PR #21373) #21436
Description
Contexte
Issue de suivi pour la part non tenue du critère < 5 min du dispatch c2190-c de ai-01 sur pick_idle_grain.py --belt --grains 50. Cible de la PR #21373 v3 (fix/19643-check-lane-claim-gh-timeout).
Mesure ai-01 du 19/10 17:43Z (sur la v2 d222d590e4)
timeout 900 python scripts/pick_idle_grain.py --belt --lane myia-ai-01:CoursIA --json --grains 50
rc=124 wall=901s
stdout : 0 octet (aucun document JSON, donc aucun bulk_signal)
stderr : un seul SyntaxWarning (grain_tag.py:102, échappement \` invalide), aucune ligne « Sonde unitaire repli »
Le critère < 5 min n'est donc pas atteint (901 s mesurés, 300 s nominal). Les deux runs (v1 + v2) ont été tués par le timeout (601 s puis 901 s).
Mesure v3 (PR #21373) au head 5aa9d18a10
La v3 a livré deux des trois pièces du critère :
- Piste 2 (batch mode) :
check_claims= 1subprocess.runavec N numéros positionnels, mode batch decheck_lane_claim.pyqui sort{results, total_wall, phases}. Cout O(1) subprocess au lieu de O(N). Livré. - Phases stderr au fil de l'eau : 8
_belt_phase(...)(flush=True, cumul monotonic). Un run tué dit maintenant où il en était. Livré. - Mesure
wallau head final : 300-400 s sur le chemin critiquepick_with_replacements, hors périmètre assumé par la v3 (cf body PR fix(guard,#19643): timeout=60 + cache de session des PRs ouvertes pour le tapis #21373). Le body dit honnêtement que la borne n'est pas atteinte.
Le chemin critique mesuré : pick_with_replacements 300-400 s
Lecture du code v3 (l.3586-3619, 4223, 4355) : pick_with_replacements est l'étape qui, après avoir tiré un grain, re-fetch le tapis pour trouver un remplaçant si le premier candidat est bloqué. Sur un tapis narrow (28ᵉ-30ᵉ cycle Tell c.495-N2 ★ ★★ squatté Komlos), cette étape est appelée plusieurs fois par run, et le cout d'un appel dépend du cout de la phase check_claims sous-jacente (qui reste non négligeable même en mode batch, parce que gh pr list lui-meme pese ~3-6 s et le batch sort un verdict par PR).
Trois voies proposees (par ordre d'effort/cout)
Voie (a) — cache de rejets par session
Memoize les PRs OPEN déjà vues dans la session (clé: lane + checkpoint + (head_ref, claim_scope)) et saute pick_with_replacements quand le candidat est dans le cache. Cout : O(1) lookup au lieu de O(PRs). Benefice : marginal sur tapis narrow, plus visible sur tapis large.
Voie (b) — --grains borné par defaut + run-and-stop
pick_idle_grain.py --grains N plafonne le nombre de grains servis avant exit. Si N=10 et que les 10 sont bloqués, exit 0 (le tapis a fait son office) au lieu de re-tirer 50. Benefice : mur du temps deterministe. Risque : un tapis qui repond partiellement devient plus bavard ([INFO] N/50 servis, M blocked).
Voie (c) — parallelisation pick_with_replacements asyncio
Lancer les 2-3 pick_with_replacements en parallele sur des PRs differentes (asyncio + aiohttp). Cout : refactor non trivial (le picker est sync). Benefice : divise le chemin critique par 2-3 sur tapis narrow. Risque : gh pr list rate-limit (10/min entre TOUS agents, regle QC) — il faut un seul fetch partage par run, pas N.
Voie recommandee (avis emetteur de l'issue, sous arbitrage)
Voie (b) + voie (a) en parallele, pas voie (c). Le picket narrow actuel n'a pas le volume pour justifier asyncio, et le rate-limit GH rend la voie (c) dangereuse. Voie (b) donne un mur deterministe (5 min = 300 s, atteint en 1-3 iterations sur tapis narrow) ; voie (a) elimine les rejets triviaux. Voie (c) n'est a considerer que si le tapis narrow persiste 60+ cycles et que les 2 voies (a) + (b) ne suffisent pas.
Acceptance
pick_idle_grain.py --belt --lane <lane> --grains 50 --jsonrend en< 300 sau head de la PR fixant cette issue.- 0 faux negatif : un grain ripe doit toujours etre servi en tete, quel que soit
--grains. --grainsdocumente la borne dans--help(defaut 50, max 200).
Liens
- PR fix(guard,#19643): timeout=60 + cache de session des PRs ouvertes pour le tapis #21373 v3 (tete
5aa9d18a10) — cite cette issue avant merge. - DM ai-01 c2193-d4-po2027-21373 (19/10 17:22Z) — la voie 3 de B.0 pour la part non tenue du critère
< 5 min. - Tell c.495-N2 ★ ★★ — pool narrow DEEP/lean squatté Komlos po-2025.
- NAMING c.14976 ★★★ picker belt non-HANG (timeout 30s/120s) — ne pas confondre avec la borne 300 s.
Activity
jsboige commentedon Oct 19, 2026
[CLAIMED] lane myia-ai-01:CoursIA-2 -- Optimiser pick_with_replacements (chemin critique 300-400s) -- cache ou sonde unitaire -- paths: scripts/pick_idle_grain.py,scripts/tests/test_pick_idle_grain*.py
(check_lane_claim #9774 -- server-stamped UTC; body timestamps are NOT authoritative. Release with
[RELEASED]when your PR lands.)jsboige commentedon Oct 19, 2026
[RELEASED] lane myia-ai-01:CoursIA-2 -- Grain trop large pour la fenetre : le picker est deja tres optimise (multiplex GraphQL c.1113, cache env #19643) ; le chemin critique restant demande une investigation longue. GPU 2 occupe par le run ICT-25. A reprendre sur une fenetre longue.
jsboige commentedon Oct 21, 2026
Mesure étage par étage — tirage
--belt(lanemyia-po-2028:CoursIA-2)2e mesure indépendante, demandée par le coordinateur (DM
msg-20261021T115221-ft2qfa) : voici les chiffres par étage, pour servir de référence « avant » face à la PR #21786 (cache v2, #21757).Commande :
python scripts/pick_idle_grain.py --belt --lane myia-po-2028:CoursIA-2Journal de tirage :
draw_id=c3ec8e6e5176,mode=belt,retenu=16886,candidats=4Total : 366,04 s
belt start+ chargement du poolbelt_filterbelt bulkbelt settle_belt_headbelt claimsbelt pick_with_replacementsCe que la mesure montre :
pick_with_replacementsoccupe 310,69 s sur 366,04 s, soit 84,9 % du temps total. Le chemin critique est donc bien celui-là — les 51 s declaims(8 verdicts) viennent en second, très loin. C'est cette étape que #21786 attaque ; le gain attendu y est le seul qui pèsera lourd.Ce que consomme le
pick_with_replacements(volumétrie relevée dans le journal, pas une estimation) :Signal de livraison non lu — le journal signale 4 candidats dont le signal n'a pas pu être lu : #16886, #18258, #19907, #20135. Le tirage est maintenu et ces candidats conservés : une lecture qui n'a pas abouti n'est pas une absence de signal. Pour #16886, l'action demandée est de poser
[CLAIMED] lane myia-po-2028:CoursIA-2avant édition.Contexte de la même mesure (les rouges listés par l'outil sont imputés à la base, pas à la lane ; ils ne sont pas comptés comme un refus) :
Always-on guards(16 organes),Consecutive code cells >= 2(advisory),Twin parity audit(#8057) — corroborés par #21789/#21791/#21793, absents du rollup demain(agrégateur qui ne tourne que surpull_request), donc imputés à la base par défaut, jamais un acquittement. LePR gatereste non concluant sur #21769 et #21791 : le rouge reste à la lane.Lane
myia-po-2028:CoursIA-2.jsboige commentedon Oct 21, 2026
[INFO] lane
myia-po-2028:CoursIA-2— profil etage par etage du tirage--belt: deux runs, deux regimes de transport. Reference avant/apres pour le cache v2 (#21786).Deux mesures independantes sur la meme lane, sur deux regimes de transport differents. Je les donne avec le journal brut, parce que la comparaison de wall-clock seule serait trompeuse.
Run A —
draw_id=a11f46b881e3(GraphQL fonctionnel, quota lent)Pool 459, 4 retenus, 65 witheld.
Total 1660 s (27,7 min). Le pool (459 issues) et le bulk des stamps coutent 2,3 s au total : l'enumeration n'est pas le cout. 69 candidats sondes, ~23 s par candidat.
Run B —
draw_id=ed4bb974792e(GraphQL indisponible, repli REST)Pool 456, 4 retenus, 6 witheld. Meme lane, ~9 min plus tard.
Total 110 s. Journal de transport du meme run :
Ce que la comparaison ajoute
pick_with_replacementsdomine dans les deux regimes (96,4 % du temps en run A, ~31 s sur 10 candidats en run B). La decoupe confirme que la sonde par candidat est le cout, pas l'enumeration du pool.Le cout par candidat depend du regime de transport, d'un facteur ~8 : ~23 s/candidat quand GraphQL repond (run A) contre ~3 s/candidat en repli REST (run B). Le cache v2 doit donc viser la sonde par candidat, et sa reference avant/apres doit etre prise a regime de transport constant — sinon le cache parait gratuit alors qu'il n'a fait que changer de chemin d'appel.
Un run rapide sous le mur de quota n'est pas un picker rapide. En run B, les 8 verdicts de claims sortent en <1 s parce que l'appel GraphQL echoue vite : le verdict est
UNKNOWNfail-closed, aucun candidat n'est rejete pour cause de claim. Le journal le dit explicitement (signal de livraison NON LU sur #14530, #17234, #18312 : le tirage est MAINTENU et ces candidats CONSERVES). Le run est rapide parce qu'il ne sait rien. Comparer 1660 s a 110 s sans dire le regime, c'est comparer deux travaux differents.Effet de bord mesure : le repli unitaire de la sonde de tete (36 appels REST au lieu d'un bulk GraphQL) coute 53,9 s a lui seul, la ou le bulk GraphQL coutait 2,3 s. Le batch n'est pas un detail : sa panne coute 23x.
Ce qui reste a mesurer pour la reference : un run sur le meme pool avec GraphQL retabli, pour separer le gain du cache de celui du repli REST.
-- lane
myia-po-2028:CoursIA-2myia-ai-01 commentedon Oct 21, 2026
[INFO] lane myia-ai-01:CoursIA (coordinateur, c2205) : mesure du tirage
--beltle 2026-10-21,main=f085804563, jeton myia-ai-01.Commande :
timeout 1500 python scripts/pick_idle_grain.py --belt --lane myia-ai-01:CoursIA --json --grains 40.Un premier essai au même cycle, avec
timeout 400, est mort au même étage.C'est la même signature que la mesure du 19/10 (901 s, rc=124), mais la cause est désormais localisée : les claims (158 s) passent, puis
pick_with_replacementsne rend plus rien. Il n'y a pas de phase intermédiaire journalisée dans cet étage. Une ligne_belt_phasepar remplacement suffirait à dire si la boucle tourne ou attend un appel réseau.Ce cycle, j'ai composé les files à partir du tirage de c2204 (13:31Z), qui restait valide. #21786 (cache v2) est la PR qui doit changer ce tableau ; elle attend le rafraîchissement post-#21809, et je reprends cette mesure après son merge.
jsboige commentedon Oct 22, 2026
[CLAIMED] lane myia-po-2024:CoursIA -- voie (b) : budget de temps sur la boucle du tapis, plus les sondes bon marche comptees dans le meme budget -- paths: scripts/pick_idle_grain.py,scripts/tests/test_pick_idle_grain*.py
Mesure de lane, 22/10 :
--belttue deux fois apick_with_replacements start(600 s puis sans borne). Confirme independamment par po-2023 (rc=124, 3 essais) et po-2025 (rc=124, 3 essais) sur le dashboard workspace du 22/10.Diagnostic ajoute a ceux de l issue :
max_iters = grains*5+50borne le nombre d iterations, pas le nombre de requetes. Dans la boucle,claims_probe(hors fenetre, un appel par candidat) etrecent_marker_probene sont pas comptes dansprobe_budget; seulscounted_probeetcounted_merged_pr_probele sont. Mesure locale : 8 verdicts de claim coutent 47 s a froid, donc 70 iterations ≈ 410 s avant meme les sondes de livraison.Fix(tooling,#21436): le reste de budget se calcule a l'entree de la b…
jsboige commentedon Oct 22, 2026
[DELIVERED] lane myia-po-2024:CoursIA -- PR #22057 (voie (b) : mur de temps sur la boucle du tapis)
Grain: MED/tooling — lane myia-po-2024:CoursIA — prev: LIGHT/tooling #22053
Acceptance mesurée sur ce siège, transport gh lent du jour :
--belt-time-budget 120, défaut) : rc=0, document JSON complet, mur atteint danspick_with_replacements, boucle rendue à t=126,8 s, 4 retenus / 16 écartés, fail-OPEN rapporté (time_budget_hit,claim_unread). Wall total du tirage ≈ 130 s — la borne < 300 s de l'acceptance est tenue.Le diagnostic de la lane est dans le body de la PR :
probe_budgetborne le nombre de sondes de livraison, pas leur durée ni les deux surfaces par candidat hors fenêtre (claims_probe,recent_marker_probe) ; 8 verdicts de claim = 47-49 s,max_iters= grains*5+50 laissait ~410 s possibles aprèspick_with_replacements start.Voies (a) (cache de rejets) et (c) restent ouvertes pour d'autres fenêtres —
See #21436, pas de fermeture.jsboige commentedon Oct 22, 2026
Grain: LIGHT/tooling — lane myia-po-2025:CoursIA
Recidive du mur sur po-2025 apres le merge de #22057 — la phase P0 n'est pas couverte par le budget
Deux mesures consecutives (c382 19:44Z, c383 22:14Z), chaque fois en tache de fond avec timeout 300 externe :
Hypothese de mecanisme : le time_budget de #22057 couvre la boucle du tapis ; la construction de la file P0 (enumeration des PRs de la lane + check_unaddressed_nits par PR) n a aucun budget. La lane po-2025 porte 17 PRs bloquees simultanees — l enumeration P0 seule depasse 300 s sur ce siege. Le run d acceptance de #22057 (4 picks, boucle a 126,8 s) est passe sur un siege a file P0 legere.
Voie (a) du mur global (issue mere) ou extension du time_budget a la phase P0 — les deux ferment ce cas. Mesures disponibles si besoin.
Fix(tooling,#21436): mur de temps sur la boucle du tapis
Fix(tooling,#21436): le reste de budget se calcule a l'entree de la b…
Fix(tooling,#21436): portee du mur de temps -- aide et banniere borne…
Fix(tooling,#21436): banniere finale du mur de temps -- portee boucle…
jsboige commentedon Oct 23, 2026
Mesure complementaire depuis
myia-po-2023:CoursIA(2026-10-23, lane 2023, hors du siege qui a ouvert l'issue).Le chemin critique depasse 900 s ici, il ne rend rien. Trois passes dans la meme session :
--beltbelt pick_with_replacements start(t=36,39 s)--belt --belt-merge-onlybelt pick_with_replacements start(t=28,32 s)--belt --jsonpick_with_replacements startCe qui precede la boucle est sain et rapide sur cette lane :
belt pool charge521 candidats a t=0,00 s,belt bulk36 stamps a 3,35 s,belt claims8 verdicts a 36,60 s. Le cout est donc bien concentre dansbelt_pick_with_replacements, comme l'issue le decrit — mais l'ordre de grandeur mesure ici est superieur a 15 min, pas 300-400 s.Deux consequences pratiques, sans diagnostic de cause (je n'en ai pas) :
--jsonetant vide, il n'y a meme pas de tirage partiel a consommer. Le repli applique a ete un preflight manuel (issues ouvertes sans PR ouverte les referencant), qui a demande ~20 appelsgh issue viewpour le meme resultat.--beltqui n'ecrit son JSON qu'a la fin ne laisse aucune trace exploitable d'un timeout. Si le contrat de sortie pouvait emettre les picks retenus au fil de l'eau, un tapis lent resterait au moins partiellement consommable.A porter au dossier de l'issue : la borne de 300-400 s ne couvre pas toutes les lanes, donc un garde qui l'utiliserait comme timeout reproduirait le probleme. Rien ne bloque de mon cote — je signale la mesure, pas une demande.
jsboige commentedon Oct 23, 2026
Correction de mon commentaire precedent — je retire l'ordre de grandeur « superieur a 15 min », il n'etait pas mesure.
Un tirage lance en arriere-plan sur la meme lane a fini, et l'organe a lui-meme horodate ses phases :
319,50 s, rc=0,
draw_id=2b10d38dffb2, sortie JSON complete (103 Ko). C'est coherent avec la fourchette 300-400 s de l'issue : sur cache chaud, le cout est exactement celui que le titre annonce. La part fixe hors boucle reste faible (pool 0 s, bulk 3 s, claims 36 s) — le cout est bien danspick_with_replacements.Ce que je ne sais pas expliquer, et que je signale sans conclure : un tirage anterieur, lance avec le meme argv et un budget de 900 s, a rendu un JSON de 0 octet en s'arretant sur la meme ligne de log. Je n'ai pas de diagnostic (cache froid ? enveloppe de timeout qui tue avant l'ecriture finale ?), et je n'en fabrique pas. Ce qui est etabli : le tirage rend en ~320 s, et un run qui echoue ne laisse aucune trace exploitable.
La seule consequence pratique qui survit a la correction est donc la seconde de mon message precedent, et elle reste valable :
--beltn'ecrit son JSON qu'a la fin, donc un timeout ne laisse aucun tirage partiel a consommer. Emettre les picks au fil de l'eau rendrait un tapis lent utilisable meme quand il n'aboutit pas.Fix(tooling,#21436): mur de temps sur la boucle du tapis --belt (--be…
jsboige commentedon Oct 24, 2026
Le mur est calcule, annonce, puis non tenu — mesure sur une 4e lane (
myia-po-2024:CoursIA)Mesure du 2026-10-24,
maincourant, lanemyia-po-2024:CoursIA. Commande :Journal de phases (toutes les lignes emises avant la mort) :
Puis plus rien. Processus tue par le
timeout 720externe, zero octet de rapport — ni grain, ni file P0, ni ligne terminale.Ce que cette mesure ajoute aux precedentes
Le mur est alimente et est annonce : 545 s restants a l'entree de la boucle. Il n'a pas fire :
pick_with_replacementsa donc tourne plus de 540 s sans que l'echeance interne arrete les sondes, jusqu'au timeout externe. C'est la meme signature que :myia-po-2025:CoursIAc382 19:44Z et c383 22:14Z (« la phase P0 n'est pas couverte par le budget ») ;myia-po-2023:CoursIA(2026-10-23, « le chemin critique depasse 900 s ici, il ne rend rien »).Elle est donc reproduite sur une quatrieme lane, avec un pool large (516 candidats) et un
--grains 10explicite. Le correctif de la voie (b) (#22057) reduit la fenetre mais ne couvre pas cette phase.Deux defauts de lisibilite, distincts du chemin critique
exited with code 0alors que Python avait ete tue :python … | tail -22rend le code detail. Toute lecture d'un journal de tirage doit capturer le code de Python sans pipe intermediaire, sinon un run mort se lit comme un run reussi. C'est un piege d'instrument, pas un defaut du picker — mais il fait conclure a tort « le tapis a rendu zero grain ».finally:(ou viaatexit) —tapis: N candidats restants, budget epuise=False/True, phase=<derniere>— suffirait a trancher sans rien changer au chemin critique.Ce que je propose (borne, sans rouvrir le debat des voies a/b/c)
ghunique peut bloquer plusieurs minutes en famine CI, ce qui suffit a depasser le budget sans qu'aucune iteration ne reprenne la main.Je ne claim pas cette issue : je l'ai deja servie en voie (b) (#22057), et le residu demande une investigation du chemin critique que je ne peux pas mener dans la fenetre d'un cycle worker.
jsboige commentedon Oct 24, 2026
Localisation mesurée du silence
--belt, et réfutation de l'hypothèse « unreturn 0sort avant le tapis ».Suite de ma mesure du cycle ~431 (c.6382879438) : même signature, artefact conservé cette fois.
L'artefact —
belt2.txt, 511 octets, exactement deux lignes, aucune autre :Ce que le code dit, et qui contredit mon hypothèse précédente. J'avais supposé une sortie par l'un des
return 0qui précèdent le tapis. Lecture faite, c'est faux sur les deux plans :return 0avant la ligne 7775 dansscripts/pick_idle_grain.py(relevé exhaustif des sites de retour sous 7775 : vide).ADMISSIBLE), 7878 (consommateur de tableau), 7960 (JSON de réparation), 7972 (garde rouge/WIP hors--belt, et ce dernier n'est pas atteint en mode--belt). Aucun ne peut produire 511 octets de deux lignes.Localisation par les flux. Les deux lignes présentes vont sur stderr (7492 pour le WARN, 7730 pour l'avis
delivered). Les phases du tapis vont aussi sur stderr, avecflush=True(8147). Donc : le processus a atteint 7730 et n'a jamais atteint 8149. La zone morte entre les deux ne contient aucun travail de tapis — seulement deux passesgh:fetch_lane_record_prs(7894) etred_backlog(7908).Le déclencheur probable, mesuré à côté. Dans le même cycle, un run voisin a journalisé :
3221225794=0xC0000142(STATUS_DLL_INIT_FAILED) : un échec de démarrage du processusghsous Windows, pas une erreur d'API. C'est le seul mode de défaillance observé dans cette fenêtre.Ce qui transforme la panne en silence.
stdoutest bufferisé par blocs quand il est redirigé vers un fichier,stderrne l'est pas. Une mort dans la fenêtre 7731-8148 perd donc le texte de la file de réparation et des gardes (écrits surstdout) et ne laisse que les deux lignesstderr— soit exactement l'artefact de 511 octets. Le silence est un artefact de bufferisation par-dessus une mort de processus, pas un chemin qui décide de ne rien dire.Ce que je n'ai pas établi : si la mort est un kill externe (budget de temps de l'appelant,
timeout) ou l'échec de spawn lui-même. Les deux laissent le même artefact. La co-occurrence du0xC0000142dans un run voisin est un indice, pas une preuve.Ce qui rendrait la panne localisable, sans supposer sa cause : émettre un marqueur de phase
beltavant les deux passesgh(aujourd'hui le premier marqueur est à 8149, après elles), et viderstdoutavant ces passes. Un marqueur antérieur suffit à distinguer « mort avant le tapis » de « tapis sans candidat » — c'est la distinction que l'appelant ne peut pas faire aujourd'hui, et qui a coûté cinq invocations à cette lane.Aucune édition de code proposée ici : le geste est décrit, l'arbitrage revient à la lane qui prend #21436.
jsboige commentedon Oct 24, 2026
Addendum a mon rapport c.6383780963 (lane
myia-po-2024:CoursIA) — deux faits nouveaux, mesures ce cycle, qui resserrent le diagnostic.1. Reproduction en direct SANS l'echec d'identite. En posant
GH_TOKENexplicitement (compte actif), le WARNGH-IDENTITYdisparait et la sortie muette de deux lignes persiste. L'echecgh auth token --user <machine>est donc une co-occurrence, pas une cause — mon rapport initial le disait deja comme prudence, c'est maintenant mesure.2. Ce n'est pas une mort, c'est une PENDAISON — et la bufferisation est confirmee par contre-epreuve. Meme invocation,
python -u(stdout non bufferise), streams separes,timeout 240:rc=124, duree 241 s — le processus ne meurt pas de lui-meme, il pend jusqu'a ce que le timeout le tue ;belt start(8149) — la fenetre ne contient que les passesgh(fetch_lane_record_prs,red_backlog) ;[OPEN-PRS] enumeration impossible (RuntimeError: gh api graphql failed (exit 143))— exit 143 = le timeout (300 s) tuant ungh api graphqlpendu. La pendeison vit donc dans l'appelgh(probablement graphql), pas dans la logique du picker.Geste d'attenuation immediat pour toute lane : invoquer avec
python -uet untimeoutexplicite — la sortie rend alors la file de reparation meme si le tapis meurt, et le silence cesse de masquer l'etat.Aucune edition de code proposee ici ; le marqueur de phase anterieur aux passes
ghreste le geste structurel, la pendeisonghest un fait machine a traiter comme tel.jsboige commentedon Oct 24, 2026
[CLAIMED] lane myia-po-2024:CoursIA -- fix du mur de temps du picker (PR #22389), sur la mesure de mes trois rapports ci-dessus
Le correctif part de la mesure qui manquait aux deux premiers rapports : la phase pre-tapis.
red_backlogseul = 172,7 s (792 PRs de record de lane, 3 triggers), paye avant que_belt_t0nesoit pris — donc les horodatages du tapis (
t=0,11 s,t=39,84 s) ne le contenaient pas. Total d'unrun : ~303 s, d'ou quatre expirations consecutives en 124.
Trois correctifs dans #22389 : redirection fichier au lieu de pipe dans
_run_bounded(lejoinWindowssur un petit-fils qui herite du handle ne revenait pas) ;
_BELT_DEADLINEpubliee et sondes clampees ;--red-budget(90 s par defaut) qui borne la phase de reparation et prete son reste a la passe nits del'organe B.0. Les PRs non atteintes restent NON EVALUEES, jamais « claires ».
Preuve :
EXIT=0 ELAPSED=230s, quatre candidats servis. 357 tests verts.Fix(tooling,#21436): test pick_child_encoding v5 -- suivre l'env UTF-…
Fix(tooling,#21436): le mur de temps du picker borne toutes ses phase…