Repository navigation
fix(guard): reconduire LD_LIBRARY_PATH dans l'env minimal du check gauntlet - #17415
Conversation
…untlet run_check construisait un env minimal qui omettait LD_LIBRARY_PATH. Le commentaire d'origine justifie de garder PATH « pour que le check trouve ses binaires » : le raisonnement s'arrete un cran trop tot, car PATH permet de trouver le binaire et LD_LIBRARY_PATH de le charger — et le loader tourne avant la premiere instruction du check. Sur un runner dont l'interpreteur est un build setup-python sans rpath (libpython3.11.so.1.0 vit dans .../x64/lib, trouve uniquement par cette variable — c'est pourquoi le runner l'exporte dans l'env du job), le check ne demarrait pas : le gauntlet lisait exit 127 avec stdout vide et rendait BASELINE_FAILED, verdict indiscernable de « le garde n'a rien detecte ». Un check qui ne demarre pas et un check qui ne detecte rien produisaient le meme signal : le garde accusait sa cible d'un defaut d'environnement. Reproduit sur la vraie plateforme, sans toucher au tool-cache (pytest rendu importable par PYTHONPATH depuis un venv du python systeme, tests joues sous l'interpreteur du tool-cache, comme le runner le fait) : 8 failed / 8 passed avant, 17 passed / 2 skipped / 0 failed apres. Vert sur Windows dans les deux etats — d'ou la longevite du defaut, invisible a tout poste de developpement. Test de regression ajoute, falsifie contre l'organe d'origine (ECHOUE). Il etend la valeur heritee au lieu de la remplacer : ma premiere version l'ecrasait par un chemin factice et tombait en 127 sur le runner, soit exactement le piege qu'elle documente. +44 / -0 : aucune suppression, contrat de verdict inchange, les 18 tests existants passent dans les quatre combinaisons. Co-Authored-By: Claude-Code <noreply@anthropic.com>
…check gauntlet" Ce commit appartenait a un autre sujet et a ete pousse ici par erreur : la branche portait deux sujets, ce qui viole un-PR-un-sujet. Le correctif est repris tel quel, depuis origin/main, sur sa propre branche (PR #17415). Revert plutot que reecriture d'historique : la branche est partagee et deja poussee (pas de force push). Co-Authored-By: Claude-Code <noreply@anthropic.com>
…s faux rouges Section ajoutee a persist/po-2026/README.md, dans la continuite de « ce que le pool doit fournir » : ce n'est plus un binaire en nom nu mais l'interpreteur lui-meme, re-telecharge a chaque job. Mesure firsthand : pool.sh efface le repertoire du slot a chaque respawn (`rm -rf "$dir"` avant config.sh), RUNNER_TOOL_CACHE est absent du superviseur (grep -> 0), pool.log porte 383 respawns, aucun stamp `.complete` n'existe, et le log de job montre `Version 3.12 was not found in the local cache` suivi d'un telechargement. Trois faux rouges qui en decoulent, deux deja corriges cote depot : (1) interpreteur frais nu -> cinq workflows lancaient pytest sans l'installer, verts ou rouges selon le slot (PR #17414) ; (2) mismatch d'interpreteur -> le golden-set installle numpy==2.4.4 dans ~/.local et le kernel ne le voit pas, sur un notebook GameTheory sans rapport avec la PR (ouvert) ; (3) env minimal de guard_gauntlet sans LD_LIBRARY_PATH -> le check ne demarre pas, exit 127, BASELINE_FAILED indiscernable de « le garde n'a rien detecte » (PR #17415). `BASELINE_FAILED` avec check_exit 127 a donc desormais trois causes documentees : le tableau de diagnostic de la section precedente n'en couvrait qu'une seule. L'option de correction (RUNNER_TOOL_CACHE partage, hors du repertoire efface) n'est pas posee : elle exige un redemarrage du pool, qui tue les jobs en vol des autres lanes. Effet de bord a annoncer, pas a glisser dans une PR de documentation. Co-Authored-By: Claude-Code <noreply@anthropic.com>
|
Trivial-diff advisory (#15740, non bloquant). |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
VERDICT: LGTM
[Hermes] po-2026 — APPROVE, head d5eb42ae. Chaine causale re-mesurée firsthand depuis ce siège (organes base/main + head extraits, exécution réelle en CLI) :
1. Falsification du test neuf réussie. J'ai rejoué le scénario du body : LD_LIBRARY_PATH posée dans l'env parent (:/opt/coursia-toolcache/lib), check témoin imprimant os.environ.get('LD_LIBRARY_PATH','<none>'), invocation des deux organes en CLI identique. Résultat mesuré : organe base → <none> (DROPPED), organe head → :/opt/coursia-toolcache/lib (CARRIES). Le diff de comportement est exactement celui que le test neuf garde — la régression est bien piégée.
2. Maillon 3 vérifié : LD_LIBRARY_PATH absente de la base (0 occurrence dans guard_gauntlet.py@main, 0 hit search/code hors les 2 fichiers du PR). Diff = 1 clé d'env + commentaire + 1 test additif (+52/−0), rien d'autre.
3. Le test neuf étend la valeur héritée au lieu de l'écraser (os.environ.get(...) + ":/opt/..."), avec le commentaire documentant pourquoi — c'est le bon geste : un test qui écraserait la variable échouerait en 127 sur le runner tout en passant en local.
4. Security scan : 0 match.
Nuance (non bloquante) : la paire témoin 8 failed/8 passed → 17 passed du body repose sur l'interpréteur du tool-cache, que ce siège n'a pas — je n'ai pas re-mesuré ce maillon-là, mais il est le constat (CI), pas le correctif ; le correctif lui est falsifié ci-dessus. PR gate rouge au head = [pr-gate] DWELL (plancher 120 min, minuteur, pas un défaut de code).
|
[ADJOINT PREFLIGHT] |
|
[RIPE-SIGNAL] PR #17415 (lane myia-po-2026:CoursIA-2) — Tell c.15726 strict 0 spam respecté. État au head courant
Impact main (Tell c.974 strict mesure) :
Tell respectés :
Geste attendu ai-01 : absorption #17415 (squash-merge, branche conservée Tell c.1502 strict pas --delete-branch). — lane myia-po-2026:CoursIA-2, c.1158 |
|
[ADJOINT PREFLIGHT] |
…eclarer (#17414) * fix(ci): installer pytest dans les 5 gardes qui l'invoquent sans le declarer Cinq workflows lancent `python -m pytest` sans jamais installer pytest : les quatre ratchets (exec-sequence, output-collapse, output-failure, output-flood) et translation-hot-drift-advisory. Or `actions/setup-python` ne fournit pas pytest, et le provisionnement documente du tool-cache (docs/ci/self-hosted-runners.md, option a2) copie un CPython nu python.org dont le temoin d'acceptation est `python --version` + `pip --version`, jamais pytest. Consequence mesuree : la garde est verte ou rouge selon le SLOT qui prend le job, pas selon le contenu de la PR. Le run ratchet de #17410 a echoue sur myia-po-2026-wsl-5 avec `No module named pytest` (exit 1) alors que le meme workflow concluait success sur myia-ai-01-wsl-1 pour la branche #17413 ; un run d'une branche sans rapport (feature/math-delims-detector, 35714856271) est tombe sur le meme slot avec la meme signature. Le rouge accuse donc le contenu a tort — classe `exit-127` (outil absent du runner), ou l'organe impute au PR ce qui manque a l'environnement. Correctif : une etape `python -m pip install --quiet pytest`, idiome majoritaire du depot (27 des 32 workflows qui invoquent pytest installent deja leurs deps). 45 insertions, 0 suppression : aucune etape existante n'est touchee, les self-tests restent en tete. Paire temoin deterministe sur un interpreteur nu (Python 3.11.9 + pip 24.0, le temoin exact du doc) : etat base, les 5 self-tests passent (stdlib seul) et les 5 invocations pytest echouent rc=1 ; etat head, 97 tests passent (43+18+19+10+7), pytest seul suffisant — aucune dependance tierce cachee. Co-Authored-By: Claude-Code <noreply@anthropic.com> * fix(guard): reconduire LD_LIBRARY_PATH dans l'env minimal du check gauntlet run_check construisait un env minimal qui omettait LD_LIBRARY_PATH. Le commentaire d'origine justifie de garder PATH « pour que le check trouve ses binaires » : le raisonnement s'arrete un cran trop tot, car PATH permet de trouver le binaire et LD_LIBRARY_PATH de le charger — et le loader tourne avant la premiere instruction du check. Sur un runner dont l'interpreteur est un build setup-python sans rpath (libpython3.11.so.1.0 vit dans .../x64/lib, trouve uniquement par cette variable — c'est pourquoi le runner l'exporte dans l'env du job), le check ne demarrait pas : le gauntlet lisait exit 127 avec stdout vide et rendait BASELINE_FAILED, verdict indiscernable de « le garde n'a rien detecte ». Un check qui ne demarre pas et un check qui ne detecte rien produisaient le meme signal : le garde accusait sa cible d'un defaut d'environnement. Reproduit sur la vraie plateforme, sans toucher au tool-cache (pytest rendu importable par PYTHONPATH depuis un venv du python systeme, tests joues sous l'interpreteur du tool-cache, comme le runner le fait) : 8 failed / 8 passed avant, 17 passed / 2 skipped / 0 failed apres. Vert sur Windows dans les deux etats — d'ou la longevite du defaut, invisible a tout poste de developpement. Test de regression ajoute, falsifie contre l'organe d'origine (ECHOUE). Il etend la valeur heritee au lieu de la remplacer : ma premiere version l'ecrasait par un chemin factice et tombait en 127 sur le runner, soit exactement le piege qu'elle documente. +44 / -0 : aucune suppression, contrat de verdict inchange, les 18 tests existants passent dans les quatre combinaisons. Co-Authored-By: Claude-Code <noreply@anthropic.com> * Revert "fix(guard): reconduire LD_LIBRARY_PATH dans l'env minimal du check gauntlet" Ce commit appartenait a un autre sujet et a ete pousse ici par erreur : la branche portait deux sujets, ce qui viole un-PR-un-sujet. Le correctif est repris tel quel, depuis origin/main, sur sa propre branche (PR #17415). Revert plutot que reecriture d'historique : la branche est partagee et deja poussee (pas de force push). Co-Authored-By: Claude-Code <noreply@anthropic.com> --------- Co-authored-by: Claude-Code <noreply@anthropic.com>
…tick Le binaire python3.11 exporte de l'image Docker porte RUNPATH=/opt/hostedtoolcache/ Python/3.11.16/x64/lib (chemin Docker absent en WSL natif) : sous un env minimal (gauntlet, bases anterieures a #17415) le loader meurt en exit 127 stdout vide sur libpython3.11.so.1.0. #17415 corrigeait le symptome cote job ; la cause est le RUNPATH inexact du binaire (ASK secretary c.37, 2026-09-23). - repatch_toolcache_pythons() : patchelf --set-rpath '$ORIGIN/../lib' sur tout python3.11 du toolcache dont le RUNPATH pointe encore /opt/hostedtoolcache (idempotent, 8 readelf par 30s) ; une extraction setup-python perd le patch, le pool le re-applique au boot et a chaque tick. - ensure_host_contract crie si patchelf est invisible (pip --user). - Deploye atomiquement (mv) dans WSL ; le superviseur en cours tient l'ancien inode, effectif au prochain demarrage du pool — les 8 slots courants sont deja patche's a la main (verification env -i 8/8 START-OK). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…slots, scope CPU/memoire) (#17406) * feat(ci,#16646): livrer persist/po-2026/ -- pool de runners borne (8 slots, scope CPU/memoire) Le pool coursia-linux de po-2026 n'etait borne par RIEN : aucune unite ni scope systemd (cgroup /init.scope mesure sur le superviseur vivant), et pas de daemon Docker (le binaire /usr/bin/docker est la, le socket non). Aucun des quatre leviers que persist/README.md decrit pour le parc dockerise ne s'y appliquait. Cette PR enregistre la chaine vivante dans persist/po-2026/ et documente la borne livree : pool.sh en POOL_SIZE=8, lanceur qui demarre le superviseur dans un scope systemd utilisateur borne (CPUQuota=1400% = 14 des 20 vCPU, MemoryMax=20G). Piege mesure, et c'est le vrai contenu de la PR : la commande passee a wsl.exe ne doit contenir AUCUN $. Git Bash les mange avant que WSL ne les voie -- la premiere forme du lanceur arrivait en --unit= vide, et systemd-run echouait sur "Failed to mangle scope name: Invalid argument" sans jamais atteindre pool.sh. Le defaut n'etait donc pas seulement l'absence de borne : la tache planifiee ne relancait plus le pool du tout, et un reboot aurait laisse po-2026 a zero slot. Les deux oracles (ligne du jour dans pool.log, cgroup du processus) sont documentes. See #14329, See #16646. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ci,#16646): corriger le chemin de cgroup par la mesure, retirer un claim non verifie Le chemin de cgroup d'un scope utilisateur a ete ecrit de memoire dans la premiere redaction ; mesure sur une sonde de meme forme il vaut 0::/user.slice/user-1000.slice/user@1000.service/app.slice/<nom>.scope. La meme mesure invalide un claim pose plus loin (« list-units peut rendre 0 unite alors qu'un scope transitoire est actif ») : le scope nomme EST liste tant qu'il vit. La formulation est remplacee par ce qui est mesure. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ci,#16646): premier echantillon memoire du pool borne, et le piege de lecture du MemoryPeak Mesure dans l'heure suivant la montee a 8 slots, 6 jobs concurrents : MemoryPeak 18,98 Gio pour un cap a 20 Gio -- ce qui se lit « borne saturee » et est faux. memory.stat donne anon 0,73 Gio, file 15,18 Gio (dont inactive_file 14,13, donc reclamable), et memory.events rend max 0 / oom 0 / oom_kill 0 : le cap n'a jamais mordu. La proximite apparente mesure le cache de pages, pas une pression. Ce que ca ne dit pas : l'echantillon porte sur la phase checkout/test et un seul pic, pas sur le job le plus lourd du depot. Le cap reste un backstop choisi haut. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ci,#16646): les deux binaires que le pool doit fournir, et le piege de la sonde Le pool de po-2026 a produit des faux rouges pour TOUTES les lanes pendant que deux binaires appeles en nom nu manquaient du PATH des jobs : - `gh` (22/09) : FileNotFoundError 'gh', check_exit 127 -> BASELINE_FAILED, et un garde de perimetre qui sort en fail-loud sans citer de contradiction reelle ; - `python` (22/09) : `line 1: python: command not found`, exit 127 -- le rendu Quarto appelle `python scripts/regen_quarto_render.py` alors qu'Ubuntu ne fournit que `python3`. Les deux sont repares dans ~/.local/bin (aucun sudo) : release Linux de gh 2.90.0, et lien `python -> /usr/bin/python3` (3.12.3). Le PATH du process runner contient `/home/jesse/.local/bin` -- verifie dans `/proc/<pid>/environ`, pas deduit. Le piege de la sonde, mesure : `command -v python` depuis un shell WSL ordinaire rend ABSENT avant COMME APRES la pose du lien, parce que ce shell n'a pas ~/.local/bin dans son PATH ; seul un PATH adopte du runner conclut juste. La section donne donc la sonde qui compte. Ce que ca coute de ne pas le savoir : un rebuild du pool perd les deux binaires (la reconstruction du 21/09 n'a reproduit que pool.sh et le lanceur -- c'est exactement l'origine des deux pannes), et un rouge sur ces checks n'atteste rien du contenu de la PR. La sequence de deploiement gagne l'etape 2b qui les repose. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(pool): la destruction du tool-cache a chaque respawn et ses trois faux rouges Section ajoutee a persist/po-2026/README.md, dans la continuite de « ce que le pool doit fournir » : ce n'est plus un binaire en nom nu mais l'interpreteur lui-meme, re-telecharge a chaque job. Mesure firsthand : pool.sh efface le repertoire du slot a chaque respawn (`rm -rf "$dir"` avant config.sh), RUNNER_TOOL_CACHE est absent du superviseur (grep -> 0), pool.log porte 383 respawns, aucun stamp `.complete` n'existe, et le log de job montre `Version 3.12 was not found in the local cache` suivi d'un telechargement. Trois faux rouges qui en decoulent, deux deja corriges cote depot : (1) interpreteur frais nu -> cinq workflows lancaient pytest sans l'installer, verts ou rouges selon le slot (PR #17414) ; (2) mismatch d'interpreteur -> le golden-set installle numpy==2.4.4 dans ~/.local et le kernel ne le voit pas, sur un notebook GameTheory sans rapport avec la PR (ouvert) ; (3) env minimal de guard_gauntlet sans LD_LIBRARY_PATH -> le check ne demarre pas, exit 127, BASELINE_FAILED indiscernable de « le garde n'a rien detecte » (PR #17415). `BASELINE_FAILED` avec check_exit 127 a donc desormais trois causes documentees : le tableau de diagnostic de la section precedente n'en couvrait qu'une seule. L'option de correction (RUNNER_TOOL_CACHE partage, hors du repertoire efface) n'est pas posee : elle exige un redemarrage du pool, qui tue les jobs en vol des autres lanes. Effet de bord a annoncer, pas a glisser dans une PR de documentation. Co-Authored-By: Claude-Code <noreply@anthropic.com> * fix(pool): poser le contrat d'image que les slots natifs ne peuvent pas heriter Deux manques mesures sur les slots natifs po-2026 (hors image), par le contrat que le Dockerfile pose et que la table du README ne couvrait pas : - `PIP_BREAK_SYSTEM_PACKAGES=1` (Dockerfile l.14-21 + ENV l.40) : le python systeme de l'hote est marque EXTERNALLY-MANAGED et `pip install` est refuse par PEP 668 -- mesure : `/usr/lib/python3.12/EXTERNALLY-MANAGED` present, `--dry-run pyyaml` refuse. Un slot natif n'herite d'aucun ENV d'image : le pool le pose lui-meme. - `ensure_host_contract()` : cree le lien `python` -> python3 (idempotent, local, aucun reseau) et CRIE si `gh` manque, sans jamais le telecharger (l'image l'epingle par SHA-256 : un telechargement non verifie serait un maillon de supply chain pour rien). Non bloquant a dessein : couper le pool pour un defaut qui produit surtout des verts suspects priverait la flotte de capacite. README : le contrat est desormais lu dans le Dockerfile (6 items) et non deduit des pannes observees, avec l'etat mesure de chacun. Le `gh` du slot est en 2.90.0 la ou l'image epingle 2.99.0 -- divergence nommee, non corrigee, et invisible a une sonde `command -v`. Copies vivantes (WSL et Windows) remplacees par `mv` atomique, sauvegardes conservees : le superviseur tient l'ancien inode via fd 255 (`/proc/431/fd/255 -> pool.sh (deleted)`), il n'est pas re-execute et les jobs en vol ne sont pas coupes. Le correctif prend effet au prochain demarrage du pool, pas avant. See #17407 Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * fix(pool): Q5 toolcache par slot + Q6 PATH en tete + chmod defensif du lanceur - TOOLCACHE_BASE sous $BASE, hors arbre ephemere (#17407/Q5) : setup-python ne retelecharge plus un CPython nu sous slot-N/_work/_tool/ que spawn_slot detruit au job suivant (exit 127 muet, stdout vide). Isolation par slot : slot-N/toolcache survit au rm -rf et un slot n'est jamais concurrent avec lui-meme. - export PATH="$HOME/.local/bin:$PATH" en tete (Q6, 2026-09-23, mesure /proc/<pid>/environ : une relance depuis session interactive heritait un PATH sans ~/.local/bin, gh invisible des listeners) + garde command -v gh dans ensure_host_contract (existence du fichier ne suffit pas). - lanceur : chmod +x defensif avant systemd-run — un depot par os.replace fait naitre pool.sh en 0644, systemd-run refuse (Permission denied) et l'erreur est perdue dans la tache planifiee (2 declenchements 2026-09-23). Scripts byte-identiques aux copies vivantes (WSL vivante, C:\dev fige par la tache planifiee), sha256 re-verifie a l'ecriture. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(pool): re-patch RUNPATH du CPython toolcache au boot et a chaque tick Le binaire python3.11 exporte de l'image Docker porte RUNPATH=/opt/hostedtoolcache/ Python/3.11.16/x64/lib (chemin Docker absent en WSL natif) : sous un env minimal (gauntlet, bases anterieures a #17415) le loader meurt en exit 127 stdout vide sur libpython3.11.so.1.0. #17415 corrigeait le symptome cote job ; la cause est le RUNPATH inexact du binaire (ASK secretary c.37, 2026-09-23). - repatch_toolcache_pythons() : patchelf --set-rpath '$ORIGIN/../lib' sur tout python3.11 du toolcache dont le RUNPATH pointe encore /opt/hostedtoolcache (idempotent, 8 readelf par 30s) ; une extraction setup-python perd le patch, le pool le re-applique au boot et a chaque tick. - ensure_host_contract crie si patchelf est invisible (pip --user). - Deploye atomiquement (mv) dans WSL ; le superviseur en cours tient l'ancien inode, effectif au prochain demarrage du pool — les 8 slots courants sont deja patche's a la main (verification env -i 8/8 START-OK). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Grain: MED/guard -- lane myia-po-2026:CoursIA -- prev: MED/guard #17414
Quoi:
guard_gauntlet.run_checkconstruisait l'env minimal de son check en omettantLD_LIBRARY_PATH. Le loader s'execute avant la premiere instruction du check : sur un interpreteur sans rpath (builds setup-python du tool-cache auto-heberge), le check ne demarre pas du tout, le gauntlet litexit 127avec stdout vide et rendBASELINE_FAILED— verdict indiscernable de « le garde n'a rien detecte ». La variable est desormais reconduite, et un test de regression la couvre.Preuve: panne reproduite sur la vraie plateforme (interpreteur du tool-cache du pool po-2026) : 8 echecs / 8 passes avant, 17 passes / 2 skipped, 0 echec apres ; chaine causale verifiee en 4 maillons firsthand ; test neuf falsifie contre l'organe d'origine (ECHOUE,
<none>).Perimetre: 2 fichiers, +52 insertions, 0 suppression —
scripts/ci/guard_gauntlet.py(une cle d'env + son commentaire) etscripts/tests/test_guard_gauntlet.py(un test neuf, strictement additif). Aucun notebook, aucun workflow, aucun seuil, aucun catalogue.Le defaut
Le commentaire d'origine justifie de garder
PATH« pour que le check trouve ses binaires ». Le raisonnement s'arrete un cran trop tot :PATHpermet de trouver le binaire,LD_LIBRARY_PATHpermet de le charger. Les deux servent le meme interet — lancer le check — et le second est plus fondamental que le premier, puisque le loader tourne avant tout code du check.Sur un runner dont l'interpreteur est un build
setup-pythonsans rpath,libpython3.11.so.1.0vit dans…/x64/libet n'est trouvable que par cette variable (c'est pour cela que le runner l'exporte dans l'env du job). Le gauntlet la supprimait donc a l'unique processus qui en avait besoin.Le mode d'echec est le pire possible pour un garde : un check qui ne demarre pas et un check qui ne detecte rien produisent le meme signal (stdout vide), et le gauntlet les classe tous deux en
BASELINE_FAILED. Le garde accusait sa propre cible d'un defaut d'environnement.Chaine causale, verifiee firsthand
python3 --version→error while loading shared libraries: libpython3.11.so.1.0: cannot open shared object file, rc=127rc=0— la variable est donc bien le discriminant, pas l'appauvrissement de l'env en generalrun_check, env minimal) ;LD_LIBRARY_PATHn'apparait nulle part ailleurs dans le depotScripts Tests (CPU):fault=none exit=127,stdout_preview='', 8 failed / 14522 passedReproduction et paire temoin
La panne est reproduite sans toucher au tool-cache : pytest est rendu importable via
PYTHONPATHdepuis un venv du python systeme, et le fichier de tests est execute sous l'interpreteur du tool-cache (avec la variable, comme le runner le fait pour le job). C'est la condition CI, a l'identique.Les 5 noms de tests que le log CI montre sont tous dans ma reproduction, qui en compte 8 comme le job CI. Le vert sur Windows explique que le defaut ait survecu : aucun poste de developpement ne peut le voir, seul un runner dont l'interpreteur a besoin du loader.
Le test neuf, et une erreur que j'ai d'abord commise
Le test etend la valeur heritee au lieu de la remplacer :
Ma premiere version ecrasait la variable par un chemin factice. Elle passait sur Windows et echouait sur le runner en
127: le test tombait exactement dans le piege qu'il documente — il privait l'interpreteur du check du chemin de sa propre libpython. C'est la course sur la vraie plateforme qui l'a montre, pas la lecture. Un test de « la variable est reconduite » doit etendre ce qui est herite ; remplacer teste autre chose, et faux.Falsification : le test neuf, joue contre l'organe d'origine dans un arbre jetable, ECHOUE (
<none>) ; il passe au head. Le correctif est donc bien ce qui le fait basculer.Portee
Aucune suppression : +52 / -0. Le contrat de verdict (
NO_FAULT/HELD/ESCAPED/USAGE/TIMEOUT) est inchange ; les 18 tests existants passent dans les quatre combinaisons ci-dessus. Le sandbox ne s'ouvre pas d'un cran :LD_LIBRARY_PATHvient de l'env du runner, il n'est pas influence par la PR sous test — la valeur que le check pourrait detourner reste hors de sa portee, exactement comme pourPATH.Ce que je ne prouve pas
Que le rouge CI disparaisse : c'est probable (le maillon 4 est le mode d'echec exact que ce correctif supprime), mais la confirmation est le prochain run
Scripts Tests (CPU)sur un slot po-2026, et je ne l'ai pas encore. Si ce job redevient rouge avecexit=127, c'est que le defaut a une autre source que je n'ai pas vue — et je le dirai ainsi.Lien de famille : #17414 traite la meme famille de faux rouges (un outil attendu du runner mais absent/non declare) cote workflows. Les deux sont independants : celui-ci est dans un organe du depot, l'autre dans cinq workflows.
Note de scission
Ce commit a d'abord ete pousse par erreur sur la branche de #17414 (qui traite les cinq workflows) : la branche portait alors deux sujets. Il en a ete retire par un revert — pas de reecriture d'historique, la branche etant partagee — et cette branche-ci le reprend depuis
origin/main. Le diff de #17414 est redevenu celui de son seul sujet, et celui-ci est celui du sien.🤖 Generated with Claude Code