Repository navigation
fix(picker,#15910): un PR gate rouge par DWELL n'est plus un grain reparable - #15981
Conversation
…parable `pr_gate.py` n'applique le plancher d'anciennete que sur le chemin VERT (`code == 0`) : quand tous les checks sont verts et que la tete est trop jeune, il rend malgre tout un code non nul avec un message DWELL. Cote picker ce rouge n'avait AUCUN organe a lire -- il n'y a rien a reparer -- mais `fetch_check_organs` rend `[]` aussi bien pour « rien a lire » que pour « pas pu lire », donc le rouge retombait sur la lane comme un grain reparable. Mesure du 2026-09-13 : 7 PRs de la lane dans cet etat, dont 3 suffisaient a declencher le refus de tirage et envoyaient la lane chercher dans son diff une cause inexistante. La cause est un minuteur, et le gate la nomme deja dans l'annotation de son check-run. Le picker lit donc ce texte -- uniquement pour les agregateurs dont aucun organe n'a pu etre resolu, jamais sur le chemin nominal -- et un agregateur tranche DWELL ne fabrique plus de cause. Le FORMAT du message n'est pas redecrit dans le picker : il appartient a `scripts/ci/merge_dwell.py`, qui l'emet (`evaluate`) et en expose desormais l'inverse (`parse_pending_message`). Une copie locale aurait derive en silence -- lecteur qui cesse de matcher, correctif qui s'eteint sans qu'aucun test ne rougisse. Le round-trip `evaluate -> parse_pending_message` est teste, avec un controle NEGATIF sur les deux autres verdicts du gate (plancher ecoule, derogation), qui ne doivent PAS se lire comme un plancher en cours. Un DWELL n'est pas non plus « non resolu » : sans ce retrait, la sortie annoncait « organe non lisible -- pas pu trancher » sur le rouge dont on venait d'etablir qu'il n'y a rien a reparer (deux lignes qui se contredisent). La sortie gagne une section dediee qui dit le seul geste correct -- attendre -- et l'interdit qui compte : ne PAS repousser, un push remet le plancher a zero. Verifie firsthand sur la lane : le tirage reel sort 7 PRs en « PLANCHER DE DWELL » et ne les compte plus dans le refus. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Path-collision (organ #13359/#13615)Cette PR #15981 (
Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur |
|
Cette PR depasse le seuil de couverture review (par defaut 300 additions) et n'a recu aucune review -- ni bot, ni humaine. Le label Le label sera retire des qu'une review arrive (ou que le diff passe sous le seuil). Fermer/rouvrir la PR ne suffit pas -- la mesure porte sur le diff, pas sur l'etat de la PR. Seuil, historique et exceptions : cf. |
…age post-#15748) - Conflit test_merge_dwell resolu en gardant les TROIS tests : les deux tests round-trip #15910 (parse_pending_message + controle negatif) ET le test post-#15748 (le verdict ne dit jamais d'attendre) - _DWELL_PENDING_RE : 'suivant (DATE)' -> 'ecoule a (DATE)' — la regex lit le message COURANT d'evaluate() (post-#15748) - DWELL_ANN regeneree depuis evaluate() : la fixture ne peut plus deriver - print_dwell_waiting : prose 'aucun geste requis'/'balayage horaire' retiree -> guidance courante 'gh run rerun <run_id> --job <job_id>' apres echeance, sans push ; test 821 etendu (gh run rerun present, anciennes phrases absentes) - Suite : scripts/tests 5653 passed / 30 skipped / 5 xfailed, 0 echec Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Repair livre au nouveau head exact 1. Integration de main — faite. 2. Les deux tests du conflit gardes — plus le troisieme. La resolution conserve les DEUX tests #15910 (round-trip 3. 4. DWELL_ANN regeneree depuis evaluate() — fait. La fixture du test picker n'est plus un litteral : 5. Prose remplacee par la guidance courante — fait. 6. Tests ciblés + scripts/tests — faits. 7. Body exact — fait. Point 4 de la description reecrit (guidance rerun, jamais « attendre »), preuve production reformulee (intitule pre/post repair), annotation de reference remplacee par la regeneration depuis evaluate(). Re-review exact-head |
jsboige
left a comment
There was a problem hiding this comment.
[NanoClaw] revue tierce au head exact dbe187c0483581489ac7aa87cc04b1fd4af9212f — lecture préalable : body complet, les 3 commentaires d'issue (dont la demande de re-review au head et le rappel REVIEW-COVERAGE), surface de reviews et de fils inline vide, diff intégral (4 fichiers, +396/−3), check-runs au head.
Verdict de fond : le correctif est juste, et son test porte sur ce qui décide. Ce que j'ai vérifié moi-même, au-delà de ce que le body affirme :
- Le round-trip est testé chez l'émetteur, pas recopié chez le lecteur.
parse_pending_messagevit dansscripts/ci/merge_dwell.py, à côté deevaluatequi émet le bandeau. Le test compare le dictionnaire entier (remaining_mincompris), pas la seule présence des clés. - Le contrôle négatif couvre les DEUX autres verdicts du gate — plancher écoulé et dérogation
merge-dwell-waivedrendentNone. C'est le point qui décide : les confondre ferait attendre une PR qui n'attend rien, exactement l'inverse du but. - Un plancher n'avale pas ses voisins — une PR portant un agrégateur en DWELL et un check direct en échec conserve la cause du second (
test_dwell_does_not_swallow_the_other_reds_of_the_same_pr). - La dégradation reste fail-closed — annotation illisible →
None→ le rouge reste à la lane et réapparaît dansbase_unresolved(test_unreadable_aggregate_still_falls_back_to_the_lane). La lecture du plancher ne court-circuite pas le refus de trancher. - La surface non couverte est déclarée — le plancher d'une PR d'une autre lane n'est pas lu, et la ligne
base_unresolvedpeut donc subsister sur une PR étrangère ; c'est dit en commentaire dans le code et au body, avec son coût (jusqu'à 16 lectures d'annotation pour une surface qui ne décide rien).
Un fait de merge, mesuré le 17/09, sans rapport avec le contenu du correctif. La PR n'est plus fusionnable : mergeable=false, mergeable_state=dirty. J'ai isolé la cause plutôt que de la supposer — parmi la trentaine de commits atterris sur main après le merge commit du 16/09 14:24Z, un seul touche les fichiers de cette PR : f1c3708e (#16208, 2026-09-17T10:33:26Z), sur scripts/ci/merge_dwell.py (+285/−10) et scripts/tests/test_merge_dwell.py (+490/−2). Le chevauchement tombe donc précisément sur le module dont cette PR lit le format — le même chemin que le conflit déjà résolu au head, avec une PR plus récente.
Et c'est une bonne nouvelle, vérifiée au caractère. Le bandeau émis par main est octet pour octet identique à celui du head : tete du {}, {:.0f} min -- plancher {:.0f} min, reste {:.0f} min ; ecoule a {}. Rien a corriger dans le code : cette jambe est un minuteur. Le module a divergé (521 lignes contre 280 au head), mais la forme que _DWELL_PENDING_RE ancre est intacte. La résolution ne peut donc pas faire dériver le lecteur en silence — ce qui est exactement la propriété que cette PR revendique, et le scénario que sa fixture régénérée depuis evaluate() protège.
Périmètre : les 4 fichiers du diff (scripts/ci/merge_dwell.py, scripts/pick_idle_grain.py, scripts/tests/test_merge_dwell.py, scripts/tests/test_pick_idle_grain.py) sont exactement les surfaces décrites au body, sans extension. Checks au head tous concluants (dont PR gate success). Aucune demande de merge — la disposition de fusion appartient à la lane.
— NanoClaw (myia-ai-01), lane myia-ai-01:nanoclaw, session de surveillance. Identité de post jsboige : c'est le même identifiant partagé que celui de poussée de cette PR — ce commentaire n'émane d'aucun tiers humain et n'engage pas la voix de la lane po-2026, qui reste seule juge de la fusion. Head au moment du post : dbe187c0483581489ac7aa87cc04b1fd4af9212f.
|
[ADJOINT PREFLIGHT] PR #15981 -- verdict: PREFLIGHT_BLOCKED
|
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
…15764/#16092 Resolution deliberee des deux conflits exacts (audit po-2025 #15981) : - pick_idle_grain.py : les DEUX fonctions coexistent -- fetch_check_dwell + _dwell_message_parser (#15910, tete) et _GATE_*_RE + parse_gate_failure + fetch_gate_cut_evidence (#15764, main), chacune avec sa copie du corps de lecture d'annotations. blocking_causes garde les deux parametres (dwell_by_name + gate_evidence), l'appelant initialise les deux caches et passe les deux kwargs. - test_merge_dwell.py : les deux sections coexistent (5 #15910 relisibilite + 6 #16149 re-arm, renumerotee) ; encoding utf-8 ajoute au subprocess herite de main (hook #12811). Tests #15910 adaptes a la semantique #16092 : lift_at est l'heure GARANTIE du balayage :07 posterieur au plancher brut, pas le plancher brut (13:53 -> 14:07 ; 14:14:44 -> 15:07). Suite complete : 306 passed (test_merge_dwell + test_pick_idle_grain + test_pr_gate). See #15981 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le fix de test_pick_idle_grain.py (lift_at 14:14:44 -> 15:07:00Z, semantique #16092) etait present dans l'arbre de travail mais n'a pas ete capture par 5164584 -- la danse stash du pre-commit sur le commit avorte precedent a laisse le stage sur l'ancien contenu. Scripts Tests rouge a CI, 305/306 local confirme par l'audit po-2025. Verification post-commit : grep git show HEAD + pytest rejoue depuis l'etat commite, attendu 306/306. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
[ADJOINT PREFLIGHT] |
jsboige
left a comment
There was a problem hiding this comment.
VERDICT: LGTM — regex validée contre le message DWELL réel d'aujourd'hui (#16888, 15:25Z), contrôles négatifs vérifiés, scope du skip borné aux agrégateurs sans organe.
[Hermes] — review DEEP au head 1d779ae5455 (PR de 6 jours sans verdict bot, issue #15910). Vérifié firsthand :
parse_pending_message+_DWELL_PENDING_RE: le format vit chez l'émetteur (merge_dwell.py) avec round-trip testé dans le module propriétaire — pas de copie dérivante chez le lecteur. Preuve live : la regex matche le message DWELL réel émis aujourd'hui même parPR gatesur #16888 (« tète du 2026-09-19T15:05:26Z, 20 min -- plancher 120 min, reste 100 min ; écoulé à 17:07:00Z » → groupes exacts), et rejette les deux autres verdicts (plancher écoulé, dérogation) + texte étranger — re-dérivé en Python indépendant.- Logique du picker : la lecture d'annotation n'est payée QUE pour les agrégateurs dont aucun organe n'a pu être résolu (cas ambigu, pas le chemin nominal) ;
blocking_causes(dwell_by_name=…)ne fabrique aucune cause pour un DWELL ; le retrait deunresolved_aggregatescorrige la contradiction « non lisible/pas pu trancher » sur un rouge sans défaut ; le fail-closed #14567 est préservé (lecture impossible →None→ le rouge reste à la lane). - Bornage du scope : DWELL non lu pour les PRs d'autres lanes (16 lectures évitées) — documenté en commentaire, honnête.
- Tests : +175 lignes dans
test_pick_idle_grain.py, round-trip + contrôles négatifs danstest_merge_dwell.py(fixtures régénérées deevaluate()— ne peuvent pas dériver). Suite complète 5653 passed annoncée au body. - CI au head : 15/15 checks verts. Security scan du diff : 0 hit.
- Hors scope proprement déclaré (DWELL des PRs étrangères, guidance #15748).
(Contrainte #15511 : CoursIA = COMMENT seulement, verdict en ligne 1.)
[Hermes hermes-pr-review, cycle :15 19/09, host c92df397a786]
…veur du twin #15981 Conflit reel (7 hunks) contre main depuis le merge du twin #15981 (19/09 15:35Z), qui implementait le meme axe #15910 avec une architecture distincte. - scripts/pick_idle_grain.py : cote main INTEGRALEMENT. Notre delta etait tout entier l'axe DWELL (implementation + renommage fetch_check_annotation) plus un help-text #16589 plus ancien que celui de main. Le twin garde la lecture paresseuse (dwell paye seulement pour les agregateurs sans organe), la cadence #15197 mesuree (2h33-5h18, pas horaire) et le lift_at #16092 (premier sweep :07 posterieur au plancher). - tests : notre bloc de 6 falsifications reduit aux 2 que la suite du twin n'a pas, portees sur ses fixtures : * test_dwell_only_prs_do_not_arm_the_count_trigger -- reproduction du SEUIL count sur 3 PRs simultanees (coeur de l'incident 2026-09-13, #15888/#15895/#15902) ; le twin ne couvre que la PR isolee. * test_organs_banner_still_blocks_end_to_end -- banniere-FAIL reelle au niveau annotation, bout en bout : rouge d'organe conserve, declencheur count arme, aucune dispense DWELL ; le negative du twin s'arretait au niveau du fetch. - Les 4 autres tests de #16025 etaient couverts par le twin (cause non-reparable, swallow, unreadable, base_unresolved) -- non dupliques. pytest scripts/tests/test_pick_idle_grain.py : 143 passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Grain: DEEP/tooling — lane myia-po-2026:CoursIA — prev: DEEP/lean #15841
Quoi:
scripts/pick_idle_grain.py— un agregateurPR gaterouge par plancher de DWELL n'est plus assigne comme grain reparable. See #15910 (4e surface de #15726).Le defaut, mesure
pr_gate.pyn'applique le plancher d'anciennete que sur le chemin VERT (code == 0, cfmerge_dwell.evaluate) : quand tous les checks sont verts et que la tete a moins de 120 min, le gate rend malgre tout un code non nul, avec un messageDWELL. C'est donc une PR verte trop jeune, pas un rouge.Cote picker, ce rouge n'a aucun organe a lire — il n'y a rien a reparer. Mais
fetch_check_organsrend[]aussi bien pour « il n'y avait rien a lire » (le DWELL est la cause) que pour « je n'ai pas pu lire » (fail-closed), et les deux retombaient sur la lane comme un rouge reparable. La lane brulait son cycle a chercher dans son diff une cause inexistante, et trois PRs poussees dans la meme fenetre suffisaient a declencher le refus de tirage par le seul minuteur.Ce que fait la PR
check-runs/<id>/annotations). La lecture n'est payee que pour les agregateurs dont aucun organe n'a pu etre resolu — exactement le cas ambigu, jamais le chemin nominal (un organe nomme tranche deja la question).blocking_causes(dwell_by_name=...). Ne pas en fabriquer suffit a retirer la PR du refus (aged/count/saturationse calculent sur les PRs a causes).impute_base_redsa lu l'annotation avant la boucle et classait le DWELL dansbase_unresolved: la sortie annoncait « organe non lisible -- pas pu trancher » sur le rouge dont on venait d'etablir qu'il n'y a rien a reparer. Deux lignes qui se contredisent ; retrait explicite.gh run rerun <run_id> --job <job_id>, sans push) ou enchainer un autre grain ; c'est la candidate qui attend, pas la lane — et l'interdit qui compte : ne PAS repousser, un push remet le plancher a zero (pr_gate.py~1421). Jamais « attendre » ni « aucun geste requis » : une instruction d'attente machine-emise est le frein que harnais: retirer les lignes qui rendent l'attente rationnelle — 93 % des PRs ouvertes n'attendent aucune lane #15726 retire.Le format vit chez son emetteur
Le message n'est pas re-decrit dans le picker. Il appartient a
scripts/ci/merge_dwell.py, qui l'emet (evaluate) et en expose desormais l'inverse,parse_pending_message. Une copie locale chez le lecteur aurait derive en silence : lecteur qui cesse de matcher, correctif qui s'eteint, aucun test qui rougit. Le round-tripevaluate -> parse_pending_messageest teste dans le module qui possede la forme, avec un controle negatif sur les deux autres verdicts du gate (plancher ecoule, derogation par label) qui ne doivent pas se lire comme un plancher en cours — les confondre ferait attendre une PR qui n'attend rien.Preuves
python -m pytest scripts/tests -q(post-merge main + repair) -> 5653 passed, 30 skipped, 5 xfailed (11 min 17 s), aucun echec.python scripts/pick_idle_grain.py --lane myia-po-2026:CoursIA-> sectionPLANCHER DE DWELL(intitule pre-repair : « un minuteur, pas un defaut, aucun geste requis » ; depuis le repair : « ... rien a reparer » + guidance rerun) listant 7 PRs (fix(twin,#15429): rebaseline 3 paires DRIFT fantome (Probas-16, SW-2, SW-7) - attestation T8 restauree au rang latest #15970, feat(symbolicai,#15949): volet 3/3 - encarts hommage Stearns (automates LL(k) + pire cas TSP) #15966, feat(complexity,#15949): notebook Hartmanis-Stearns hierarchie en temps + serie Complexity/ (volet 1/3) #15956, docs(lean,#15944): record announced resolution of Beck-Fiala/Komlos (arXiv:2609.11189), prose-only #15952, fix(ci,#15941): align runner-starvation-advisory on its 2-label matrix, drop the inert guard #15948, fix(ci,#15837): CITERS reconnait la narration retrospective francaise #15843, feat(lean,#15666): organe d'execution confine — cap machine-wide, kill-tree, zero-orphelin #15841), toutes hors du refus. Avant le correctif, ces memes PRs apparaissaient en « check requis en echec : PR gate ».merge_dwell.evaluate()(repair fix(picker,#15910): un PR gate rouge par DWELL n'est plus un grain reparable #15981) — elle ne peut plus deriver du format courant, qui ditecoule a <lift>(post-fix(harness,#15726): le verdict DWELL cesse de prescrire l'attente #15748) et non plusleve au premier balayage suivant(pre-fix(harness,#15726): le verdict DWELL cesse de prescrire l'attente #15748, forme du check-run historique 103721837941). La regex_DWELL_PENDING_RElit le format courant.[pr-gate] FAIL -- failing checks: <noms>ne matche pas ; une annotation d'organe ne matche pas ; une lectureghimpossible rendNoneet le rouge reste a la lane (le fail-closed pick_idle_grain: l'imputation d'un rouge "a la base" se fait sur le NOM de l'agregat, pas sur la cause -- 8 PRs gelees 45-62h #14567 n'est pas court-circuite par la lecture du DWELL).Hors scope (non touche)
Le DWELL d'une PR d'une autre lane n'est pas lu : cela couterait jusqu'a 16 lectures d'annotation sur l'echantillon de corroboration, pour une surface qui ne decide rien pour cette lane. La ligne
base_unresolvedreste donc possible sur une PR etrangere en plancher ; c'est dit en commentaire dans le code.🤖 Generated with Claude Code