Repository navigation
fix(nb-tools,#16213): reprise EAGAIN partagee pour les deux derniers gardes - #16217
Conversation
…gardes Les deux gardes sans reprise sur la pression de fork -- check_slot_reservation et check_source_output_ratchet -- sont cables sur une primitive partagee, `fork_retry.run_with_fork_retry`, sur le modele de `naming_canon.py` (#15503) plutot qu'en quatrieme et cinquieme copie de la meme boucle. Chaque garde CONSERVE sa politique d'epuisement : fail-closed pour check_slot_reservation (un instrument muet ne vote pas), fail-open pour check_source_output_ratchet (politique preexistante, arbitrage #16164). Deux tests l'epinglent garde par garde -- c'est exactement ce qu'un refactor distrait harmoniserait. Le filtre reste etroit : seuls EAGAIN et EWOULDBLOCK sont retentes, controle positif inclus (ENOENT / EACCES / ENOMEM / EINVAL remontent au premier appel). See #16213, #16111, #16164. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels |
Path-collision (organ #13359/#13615)Cette PR #16217 (
|
myia-ai-01
left a comment
There was a problem hiding this comment.
[ai-01 — validation finale exact-head f10bacb29ee38690af523240f8fef58fd0d058d7]
Lecture personnelle B.0 complète : body, trois commentaires, zéro thread inline, zéro review antérieure et diff intégral de 460 lignes. La primitive ne retente que EAGAIN/EWOULDBLOCK, transmet les kwargs sans altération et remonte la dernière OSError à l'épuisement. Les intégrations préservent délibérément les politiques distinctes : slot reservation fail-closed, source-output ratchet fail-open.
Validation indépendante sur ce head dans un worktree détaché : 70 passed en 15,12 s (test_fork_retry.py, test_check_slot_reservation.py, test_check_source_output_ratchet.py). Les checks latest-wins requis sont verts, dont Always-on, Scripts Tests et PR gate. G-VAR-3 recalculé sur la séquence mergée : guard_pass=true, exemption mécanique #14357 (MED, fichiers disjoints du prédécesseur réel #15748).
La collision faible avec #15813 est comprise : cette autre PR modifie le même helper pour distinguer les suppressions réelles via l'API GitHub, sans contredire le wrapper de reprise ; #15813 est déjà DIRTY et devra intégrer ce head/main après fusion plutôt que perdre la résilience EAGAIN.
Verdict ai-01 : READY pour fusion squash après re-grounding instant-T. GitHub refuse l'état APPROVED car le compte propriétaire de la PR et le compte reviewer sont identiques ; cette review COMMENTED consigne donc la validation sans contourner cette protection.
…rk_retry #16217 a livre la primitive partagee (run_with_fork_retry, modele naming_canon) et laisse check_exec_ratchet sur sa copie inline de #16157 -- cet arbitrage etait explicitement reserve a #16164. La boucle locale (_EAGAIN_ERRNOS / _EAGAIN_ATTEMPTS / _EAGAIN_BACKOFF / _est_eagain) est supprimee : git() appelle run_with_fork_retry et traduit l'OSError remontee en InstrumentUnavailable. Le filtre etroit (EAGAIN/EWOULDBLOCK seuls retentes, toute autre OSError remonte au premier appel) vit desormais dans la primitive et reste couvert par test_fork_retry.py ; les deux tests de retry du ratchet qui la dupliquaient sont retires, la couverture propre au garde (traduction en InstrumentUnavailable, absence de retry sur OSError non transitoire) est conservee. Tests : 57 passed (ratchet + fork_retry + exec_sequence). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…gardes (#16217) Les deux gardes sans reprise sur la pression de fork -- check_slot_reservation et check_source_output_ratchet -- sont cables sur une primitive partagee, `fork_retry.run_with_fork_retry`, sur le modele de `naming_canon.py` (#15503) plutot qu'en quatrieme et cinquieme copie de la meme boucle. Chaque garde CONSERVE sa politique d'epuisement : fail-closed pour check_slot_reservation (un instrument muet ne vote pas), fail-open pour check_source_output_ratchet (politique preexistante, arbitrage #16164). Deux tests l'epinglent garde par garde -- c'est exactement ce qu'un refactor distrait harmoniserait. Le filtre reste etroit : seuls EAGAIN et EWOULDBLOCK sont retentes, controle positif inclus (ENOENT / EACCES / ENOMEM / EINVAL remontent au premier appel). See #16213, #16111, #16164. Co-authored-by: jsboige <jsboige@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…onible » à l'épuisement EAGAIN, fin du faux vert (#16255) * fix(nb-tools,#16164): ratchet — exit 2 instrument indisponible, plus de faux vert git() leve InstrumentUnavailable quand le spawn echoue (EAGAIN a l'epuisement des 3 tentatives, ou OSError non transitoire) ; returncode != 0 reste None (git a repondu). Le CLI sort en 2 avec un message nommant l'echec de spawn, distinct de 1 (regression) et de 0 (mesure faite) — plus jamais "changed notebooks : 0" sur un arbre non lu. Chemin masque repare : verdict_at_base() lisait ABSENT (donc "ajoute par la PR") sur un spawn failure de git show ; il monte desormais. Decision arbitree (#16164 critere 4) : fail-closed sur instrument indisponible, convergence avec check_kernel_suffix_canon.py. Appelants recenses : seul notebook-exec-sequence-ratchet.yml:88 (run nu -> exit 2 rougit l'etape, voulu), aucun importateur Python hors tests. Tests : 2 pins #16157 revises (arbitrage reserve a cette issue) + spawn failure != ABSENT + CLI exit 2 in-process. Ratchet 21 passed ; suite notebook_tools 5772 passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * refactor(nb-tools,#16164): ratchet — migrer la reprise EAGAIN vers fork_retry #16217 a livre la primitive partagee (run_with_fork_retry, modele naming_canon) et laisse check_exec_ratchet sur sa copie inline de #16157 -- cet arbitrage etait explicitement reserve a #16164. La boucle locale (_EAGAIN_ERRNOS / _EAGAIN_ATTEMPTS / _EAGAIN_BACKOFF / _est_eagain) est supprimee : git() appelle run_with_fork_retry et traduit l'OSError remontee en InstrumentUnavailable. Le filtre etroit (EAGAIN/EWOULDBLOCK seuls retentes, toute autre OSError remonte au premier appel) vit desormais dans la primitive et reste couvert par test_fork_retry.py ; les deux tests de retry du ratchet qui la dupliquaient sont retires, la couverture propre au garde (traduction en InstrumentUnavailable, absence de retry sur OSError non transitoire) est conservee. Tests : 57 passed (ratchet + fork_retry + exec_sequence). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>
Grain: MED/guard — lane myia-ai-01:CoursIA — prev: LIGHT/tooling #16036
Perimetre : 2 fichiers modifies, 2 fichiers ajoutes. Aucun notebook, aucune regle,
catalogue byte-identique a
main.Ce que livre cette PR
Les deux derniers gardes sans reprise sur la pression de fork sont cables sur une primitive
partagee,
scripts/notebook_tools/fork_retry.py:check_slot_reservation.py_gitOSErrorremontecheck_source_output_ratchet.pygitNone, comme avantTrois gardes avaient deja recu cette reprise, ecrite trois fois :
check_twin_parity(#16125),check_kernel_suffix_canonetcheck_exec_ratchet(#16157). Ecrire une quatrieme et unecinquieme copie aurait ete le geste facile. Le depot a deja tranche cette classe de probleme
avec
naming_canon.py(#15503) -- plusieurs organes redefinissant la meme primitive, extraiteen lecture unique -- et c'est ce modele qui est suivi.
La cause, datee
f149f2fe93, mergee surmainle 2026-09-13T23:08:17Z : passage deScripts Tests (CPU)a
pytest-xdist -n 4 --dist loadscope(#15833, sur #14598). Quatre workers executant desgardes qui forkent
gitsaturent la table de processus du runner ; le spawn suivant est refuseavec
BlockingIOError: [Errno 11]. Les echecs commencent le lendemain a 02:34.-n 4n'est pas en cause et reste : decision mesuree (92,5 % d'annulations sur les runnerspo-2024 contre 4 % ailleurs ; 1,90x de gain mesure sur #15762). Les deux gestes sont
complementaires -- on ne retire pas le parallelisme, on rend les gardes capables de le
supporter. Le payoff est mesure : sur
mainaec79792f60,Scripts Tests (CPU)rendsuccess.Ce que la mutualisation ne doit PAS faire, et qui est teste
Extraire une primitive partagee par cinq gardes fait courir un risque precis : uniformiser en
passant ce qui etait deliberement different. Deux choses l'etaient.
La forme de l'appel.
run_with_fork_retrytransmet seskwargsVERBATIM -- aucuneposition sur le mode texte, l'encodage,
cwd,envoucheck. Un garde binaire et un gardeutf-8 passent par la meme porte sans qu'aucun n'impose sa forme a l'autre
(
test_kwargs_transmis_verbatim).La politique d'epuisement. Fail-closed d'un cote, fail-open de l'autre : deux tests
l'epinglent garde par garde. Le fail-open du ratchet est preexistant et deliberement
conserve -- son arbitrage appartient a #16164, pas a cette PR, qui ne doit pas le trancher au
passage.
Mesures
Controle de discriminance. Un correctif de reprise est, vu de l'exterieur, indiscernable
d'un avaleur d'erreurs. En retirant la reprise des deux gardes (retour a
subprocess.runnu),la suite rend
rc=1et fait tomber exactement les deux tests d'integration :Ils mesurent donc les gardes, pas eux-memes.
Controle positif du filtre.
ENOENT,EACCES,ENOMEMetEINVALremontent au premierappel, sans reprise ni attente.
ENOMEMest le voisin dangereux : lui aussi vient de lacharge, et lui n'est pas transitoire.
Hors perimetre, volontairement
une fois le module en service.
-n 4 --dist loadscope.runner que personne n'a faite).
See #16213, #16111, #16164.
🤖 Generated with Claude Code