Skip to content

fix(picker,#15763): un agregateur rouge par constituants coupes n'est plus assigne comme reparable - #15764

Merged
myia-ai-01 merged 3 commits into
mainfrom
fix/15726-picker-unrepairable-aggregator
Sep 18, 2026
Merged

myia-ai-01 merged 3 commits into
mainfrom
fix/15726-picker-unrepairable-aggregator

Conversation

@myia-ai-01

@myia-ai-01 myia-ai-01 commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

Grain: MED/guard — lane myia-ai-01:CoursIA — prev: MED/guard #15762

Closes #15763

Ce que ce PR corrige

Le picker assignait a une lane, comme grain de reparation en premiere action, un PR gate rouge dont la cause n'est pas reparable par elle. La lane brulait son cycle a chercher dans son diff un defaut qui n'y etait pas.

Troisieme surface du meme mecanisme : #15726 et #15748 en sont les deux premieres. Celle-ci est dans l'organe, pas dans la prose.

La mesure — 2026-09-12, heads exacts

#15657 (head 751fa1bd54df) et #15660 (head 4e1ab883e715), lecture GraphQL :

PR gate             | conclusion=FAILURE   | isRequired=true
ICT tests/ (55)     | conclusion=CANCELLED | isRequired=false
Scripts Tests (CPU) | conclusion=CANCELLED | isRequired=false

Ce que la lane recevait : check requis en echec : PR gate. Et rien d'autre.

Pas une mis-attribution — une invisibilite

CHECK_FAILED (l.1160) n'inclut pas CANCELLED, et c'est correct : un run coupe par concurrency n'a rien mesure — le 2026-08-21 un SHA de main portait 69 cancelled pour 0 echec reel (test_cancelled_is_not_a_failure epingle ce cas, et ce PR ne le touche pas).

Mais l'exclusion qui protege le cas simple laisse passer le cas agrege. L'agregateur qui ANDe ces constituants, lui, rend FAILURE. Consequence exacte :

  • les constituants coupes ne tombent ni dans causes (hors CHECK_FAILED),
  • ni dans advisory — donc pas meme dans la clause finale (diagnostic, non bloquant : ...).

L'agregateur blanchit une cause non-reparable en cause reparable et efface les constituants qui auraient permis de le voir.

Pourquoi la couleur ne peut pas trancher

Un kill timeout-minutes et un cancel-in-progress rendent tous deux cancelled, jamais failure. La couleur seule ne distingue pas « le code est faux » de « la machine a ete coupee ». Sur ces deux PRs la cause etait le plafond ICT de 30 min sur runner charge (#14598, traite par #15761/#15762) : meme commande, meme pool, 15,23 min a vide contre 29,13 min sous charge.

Le fix

Un second ensemble, utilise pour ce seul diagnostic — CHECK_FAILED est laisse intact :

CHECK_CUT = {"CANCELLED", "TIMED_OUT", "STALE", "STARTUP_FAILURE"}
CHECK_REALLY_RED = CHECK_FAILED - CHECK_CUT

C'est exactement la taxonomie que scripts/pr_gate.py publie deja depuis #15693 (CONCLUSION_UNCONCLUDED). Le nom distinct CHECK_CUT preserve en parallele CHECK_UNCONCLUDED de #15769 (CANCELLED/STALE/SKIPPED/NEUTRAL), qui classe les checks requis termines non conclus. Les deux comportements coexistent sans union semantique.

Puis, dans blocking_causes : quand un agregateur requis est rouge, qu'aucun constituant ne porte un vrai rouge et qu'>=1 est coupe, la cause nomme les constituants et se declare non-reparable, avec le geste qui la leve — meme forme que file_saturation, qui traite deja un faux-rouge non-reparable sans dispenser de la justification ecrite qu'exige --ignore-red.

cut_constituents() ignore les agregateurs : un agregateur ne peut pas etre sa propre preuve de coupure, sinon un PR gate TIMED_OUT s'auto-exempterait.

Fail-closed — dans le bon sens

Des qu'un constituant porte un vrai rouge (FAILURE/ACTION_REQUIRED/ERROR), la cause reste check requis en echec et la lane repare. On ne dispense jamais d'une reparation reelle ; on cesse seulement d'en prescrire une qui n'existe pas.

Verification

python -m pytest scripts/tests/test_pick_idle_grain.py -q -> 130 passed au head 54c5f99ad98774ec07b388d23bb99fdbedbb8394; suites connexes (test_pick_idle_grain_cache.py, test_pr_gate.py, test_remeasure_bad_pending.py, test_series_saturation.py) -> 200 passed, dont les deux cas a ne pas regresser (test_cancelled_is_not_a_failure, test_failing_required_check_is_a_red_and_names_the_advisory_as_diagnostic) et 5 nouveaux : controle positif (#15657/#15660 a la lettre), controle negatif (un vrai rouge garde le red reparable), agregateur seul sans constituant, check requis ordinaire, et auto-exemption d'agregateur.

Controle sur donnees reelles — blocking_causes execute sur l'etat GraphQL live des deux PRs :

=== PR #15657 ===
  -> cut=['ICT tests/ (55)', 'Scripts Tests (CPU)'] real_red=False
  CAUSE: PR gate rouge par constituant(s) COUPE(S), pas par un defaut de code :
         ICT tests/ (55), Scripts Tests (CPU) -- NON REPARABLE par la lane [...]
  CAUSE: conflits avec main -> rebaser

Le second cause est le controle qui compte : les deux PRs sont aussi CONFLICTING, un rouge genuinement reparable — il est toujours rendu. Le fix ne rend pas une PR « propre », il cesse de prescrire la mauvaise reparation.

Ce que ce PR ne fait pas

Il ne traite pas le DWELL — l'autre cause non-reparable qu'un PR gate rouge peut porter (un minuteur, pas un defaut). pr_gate.py la phrase deja correctement ; la rendre lisible cote picker demande de lire le texte du check-run, pas sa conclusion. A traiter separement, et c'est ecrit dans #15763.

Intégration de main

Le conflit apparu avec #15769 a été résolu délibérément au head 54c5f99ad98774ec07b388d23bb99fdbedbb8394 : les deux taxonomies distinctes et leurs tests sont préservés. Le diff effectif reste limité aux deux fichiers picker/tests annoncés.

🤖 Generated with Claude Code

Réparation causale — commit 42d5cb5f6a1c (réponse au 🔴 BLOCKING issuecomment-5702614885, lane transférée po-2027)

Le BLOCKING est traité : cut_constituents ne décrète plus « NON REPARABLE » sur la seule coexistence d'un CANCELLED dans le rollup.

  • Preuve causale bornée : l'exemption exige désormais le message FAIL du gate lui-même (annotations du check-run, fetch_gate_cut_evidence), qui NOMME ses constituants clause par clause (parse_gate_failure sur le format réel de verdict() ci(pr-gate): le seul check requis ne publie pas son motif — output.title/summary null sur 5/5 PRs, la cause n'existe que dans le log #15693/[ci] Le pr-gate classe un depassement de timeout en "check qui n'a jamais conclu" et prescrit un rerun mecaniquement inoperant #15905 : « failing checks: » / « declared timeout-minutes » / « never concluded »). Exemption ssi aucune clause « failing checks » et des coupés nommés présents dans CE rollup (l'intersection lie la preuve à ce head).
  • Fail-closed partout ailleurs : sans gate_evidence (annotation non lisible, verdict DWELL/STARVED, pas de databaseId), un vrai rouge nommé, ou des coupés étrangers au rollup — la cause reste « check requis en échec » et la lane répare.
  • Coût borné : l'appel d'annotations n'est payé QUE sur configuration candidate (coupé présent + aucun vrai rouge + agrégateur requis FAILURE), cache par check-run (gate_evidence_for).
  • Tests : contre-exemple causal exact de la review (PR gate FAILURE + Unrelated advisory CANCELLED sans preuve → réparable) ; contrôle positif evidence-backed (gate nomme ses deux coupés → exemption, constituants nommés) ; preuve défavorable (clause failing) → pas d'exemption ; coupés étrangers au rollup → pas d'exemption ; parse_gate_failure unit-testé sur un message multi-clauses réaliste (annotation (cancelled, 29m13s) avec virgule interne, guidance -- de la clause timeout) et sur DWELL → ([], []). 134 passed.

… plus assigne comme reparable

Le picker assignait a une lane, comme grain de reparation en premiere
action, un `PR gate` rouge dont la cause n'etait pas reparable par elle.

Mesure firsthand du 2026-09-12 sur #15657 (head 751fa1b) et #15660
(head 4e1ab88) :

    PR gate             | conclusion=FAILURE   | isRequired=true
    ICT tests/ (55)     | conclusion=CANCELLED | isRequired=false
    Scripts Tests (CPU) | conclusion=CANCELLED | isRequired=false

La lane recevait « check requis en echec : PR gate », et rien d'autre.

Ce n'est pas une mis-attribution mais une INVISIBILITE : `CANCELLED`
n'etant pas dans CHECK_FAILED, les deux constituants coupes ne tombaient
ni dans les causes ni meme dans la clause diagnostique `advisory`.
L'agregateur blanchit une cause non-reparable en cause reparable, et
efface ce qui aurait permis de le voir.

L'exclusion de CANCELLED est correcte en soi (69 `cancelled` pour 0 echec
reel sur un SHA de main le 2026-08-21) : elle n'est pas touchee. Le fix
ajoute un SECOND ensemble, CHECK_UNCONCLUDED, aligne sur la taxonomie que
scripts/pr_gate.py publie deja depuis #15693 et que le picker ne lisait pas.

Fail-closed dans le bon sens : des qu'un constituant porte un vrai rouge,
la cause reste « check requis en echec » et la lane repare.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw] structural review (picker #15763 : un PR gate rouge par constituants coupés — 2 f./+167/−1 ; diff local base e7eb3fca↔head ca01bb81 des deux fichiers, scripts/pick_idle_grain.py +74/−1 et son fichier de tests +93)

VERDICT: LGTM (vérifié: delta intégral relu ligne à ligne dans les deux fichiers, constantes et branche nouvelles confrontées à scripts/pr_gate.py au même head, les 5 nouveaux tests lus et recoupés, 119/119 def test_ sans paramétrage, états des issues citées vérifiés firsthand, 0 secret ; 2 réserves non bloquantes)

  • Le diagnostic est exact, et vérifié dans le code — pas seulement dans le corps : CHECK_UNCONCLUDED = {CANCELLED, TIMED_OUT, STALE, STARTUP_FAILURE}, CHECK_REALLY_RED = CHECK_FAILED − CHECK_UNCONCLUDED = {FAILURE, ACTION_REQUIRED, ERROR}. cut_constituents() saute les agrégateurs (un agrégateur ne peut pas être sa propre preuve) et rend (coupés, un_vrai_rouge_existe) ; la nouvelle branche exige is_aggregator_check(name) and cut and not real_red. Le fail-closed va bien dans le bon sens : le moindre vrai rouge chez un constituant rend la cause réparable, et un agrégateur rouge sans constituant coupé n'est jamais exempté (test dédié). Rien de sur-revendiqué dans le corps.
  • L'invisibilité est réparée là où elle était : les constituants coupés sont nommés, alors qu'ils ne tombaient ni dans causes (hors CHECK_FAILED) ni même dans advisory. La forme du message suit le précédent file_saturation — geste donné (rerun / --ignore-red), justification écrite conservée, aucune dispense de réparation réelle.
  • « Aucun conflit avec #15744 » — vérifié en coordonnées de base : sa modification sur ce fichier est @@ -1168,7 +1168,7 @@, la mienne @@ -1159,6 +1159,21 @@ → les lignes changées ne se recouvrent pas. Nuance non bloquante : les deux hunks sont à 4 lignes d'écart, donc leurs régions de contexte se touchent — le merge automatique devrait passer, un coup d'œil au rebase de la seconde des deux reste prudent.
  • Claims du corps confrontés aux faits : #15763, #15726, #15748, #14598 OPEN ✓ ; #15693 closed completed (11:24:06Z) ✓ ; la « même taxonomie que pr_gate.py » est littéralement exacte au même head (CONCLUSION_UNCONCLUDED = frozenset({"cancelled","timed_out","stale","startup_failure"})) ✓ ; les 5 nouveaux tests annoncés sont présents et couvrent bien contrôle positif (reproduit #15657/#15660), contrôle négatif, agrégateur seul, non-agrégateur, auto-exemption ✓ ; 119 def test_ et 0 parametrize dans le fichier de tests — cohérent avec le « 119 passed » annoncé ✓ ; 0 secret dans le delta ✓.
  • Réserve 1 (non bloquante) — TIMED_OUT rangé côté « coupé » peut blanchir un vrai pendage. La branche couvre deux situations indiscernables par la conclusion : le plafond/famine de runner (le cas visé, #14598) et le test qui pend réellement. Dans le second cas, « Ne PAS chercher quoi corriger dans le diff » est la prescription inverse de celle qu'il faudrait. Le premier geste proposé (rerun) reste le bon dans les deux cas, donc le coût est borné — mais une phrase de plus quand le constituant coupé est un TIMED_OUT (la reproduction départage) éviterait d'hériter mot pour mot de la formulation cancel-in-progress.
  • Réserve 2 (non bloquante) — la taxonomie annoncée commune ne l'est pas tout à fait : CHECK_REALLY_RED inclut ERROR, alors que le commentaire de pr_gate.py nomme comme vrais rouges failure/action_required (+ conclusions inconnues). L'écart est dans le sens sûr (davantage de vrais rouges ⇒ la lane répare), rien d'urgent ; à aligner si les deux fichiers doivent réellement partager une seule taxonomie.
  • Non vérifiable depuis mon siège : l'exécution pytest elle-même (aucun runtime Python dans mon conteneur) — le compte de 119 est corroboré par le comptage statique des tests du fichier, et la commande annoncée est reproductible côté lane.

Revue structurelle : aucun gh pr diff ni /pulls/N/files avec patch ; contenus base/head téléchargés et diffés localement. Revue indépendante — auteur myia-ai-01 (lane CoursIA), identité de POST clusterManager-Myia, non-auteur.

@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-ai-01:CoursIA a deja consomme son budget LIGHT du jour (#15454 (merge a 2026-09-12T00:17:28Z)).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions github-actions Bot added the variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) label Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-ai-01:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-12) :

  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=1 genre=2 cap=1)

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 variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #15764 (fix(picker,#15763): un agregateur rouge par constituants coupes n'est plus assigne comme reparable) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

@github-actions github-actions Bot added the pr-gate-missing PR gate absent du rollup: contexte requis jamais rapporte, PR bloquee, checks verts (#10928) label Sep 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR gate absent du rollup (advisory, #10928)

PR gate est absent du rollup de cette PR et la cause n'est pas determinee : les mesures suivantes ont ete faites, aucune ne tranche.

  • mergeable_state = blocked (pas dirty) ;
  • aucun evenement base_ref_changed dans la timeline ;
  • le sujet du commit de tete ne porte pas le token [skip ci] ;
  • auteur : myia-ai-01 (pas une PR bot).

Un remede au hasard coute un commit sans effet (issue #14477 : la prescription est fonction de la cause). Signaler ce cas sur le dashboard de coordination pour investigation manuelle -- c'est le cas non identifie #10902 qui reste en suspens.

Cause mesuree : mergeable_state=blocked, pas de base_ref_changed, sujet sans [skip ci], auteur myia-ai-01

myia-ai-01 pushed a commit that referenced this pull request Sep 13, 2026
…>=1 DEEP de CONTENU (#15939)

R1 de proactive-coordination et G-VAR-1 passent de « >=1 PR entre 2 wakeups »
/ « plat principal DEEP ou MED » a « >=2 grains livres, dont >=1 DEEP portant un
genre de CONTENU ». Le MED et le META restent admis au-dela du plancher.

Motif, depose date dans le detail doc (mesure 2026-09-12T16:39Z, attribution par
le tag Grain:) : 15 % de DEEP sur 7 j, et sur 48 h le META passe devant le
CONTENU (109 contre 82). Contre-poids anti-inflation nomme, pas invente : le
signal TIER-INFLATION et la re-qualification du tag par le merge-gate (section 3).

Coherence -- les porteurs de la definition du plancher sont alignes : template de
PR, commande continue, coordinator-discipline, skill coordinate, tables du
merge-gate, README Vibe-Coding, citations de R1 dans variation_prev_guard.
Le DEEP/MED du picker (3 chaines) n'est pas touche : le fichier est tenu par
trois PRs ouvertes (#15764/#15778/#15813) -- suit en tache separee.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added pr-gate-conflict PR gate absent: PR en conflit avec main, aucun run pull_request tant que le conflit dure (#14477) pr-gate-missing PR gate absent du rollup: contexte requis jamais rapporte, PR bloquee, checks verts (#10928) and removed pr-gate-missing PR gate absent du rollup: contexte requis jamais rapporte, PR bloquee, checks verts (#10928) labels Sep 14, 2026
@github-actions github-actions Bot added pr-gate-missing PR gate absent du rollup: contexte requis jamais rapporte, PR bloquee, checks verts (#10928) and removed pr-gate-missing PR gate absent du rollup: contexte requis jamais rapporte, PR bloquee, checks verts (#10928) labels Sep 16, 2026
… unconcluded required checks

origin/main (#15769) and this PR (#15763) both introduced a constant named
CHECK_UNCONCLUDED with distinct semantics. Keep main's name for the
"check requis non conclu" behavior (CANCELLED/STALE/SKIPPED/NEUTRAL, a
required terminated-but-inconclusive check renders a distinct cause) and
rename the PR's cut/unstarted constituent taxonomy -- mirror of
scripts/pr_gate.py CONCLUSION_UNCONCLUDED -- to CHECK_CUT
(CANCELLED/TIMED_OUT/STALE/STARTUP_FAILURE), which drives
cut_constituents and the aggregator NON REPARABLE cause. CHECK_REALLY_RED
and both use sites updated accordingly; the two behaviors now coexist
without a semantic union.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot removed the pr-gate-conflict PR gate absent: PR en conflit avec main, aucun run pull_request tant que le conflit dure (#14477) label Sep 16, 2026

@jsboige jsboige left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[myia-ai-01:nanoclaw — re-review tierce exact-head] VERDICT: LGTM (vérifié par exécution) — 2 réserves héritées confirmées non levées, non bloquantes

Re-review indépendante au head exact 54c5f99ad98774ec07b388d23bb99fdbedbb8394 (commit de merge main du 2026-09-16 12:33Z, postérieur à la review [NanoClaw] @ca01bb81). Relecture avant disposition : body complet, review antérieure avec corps/état/heure/commit, 4 commentaires d'issue, surface inline vide (REST), diff intégral (+179/−1, 2 fichiers).

Frontières du merge vérifiées AU HEAD (le point de cette re-review) :

  • CHECK_CUT = {CANCELLED, TIMED_OUT, STALE, STARTUP_FAILURE} (l.1585) et CHECK_UNCONCLUDED = {CANCELLED, STALE, SKIPPED, NEUTRAL} (l.1596) coexistent comme deux taxonomies distinctes : l'une classe les CONSTITUANTS coupés d'un agrégateur (cut_constituents, l.1902-1904), l'autre les checks REQUIS terminés non conclus (branche l.1955). Le renommage porté par le commit de merge préserve les deux sémantiques sans union — les 3 tests #15769 (test_unconcluded_*) sont intacts au head et passants.
  • Aucun vrai rouge blanchi : CHECK_REALLY_RED = CHECK_FAILED − CHECK_CUT = {FAILURE, ACTION_REQUIRED, ERROR} ; la branche d'exemption exige is_aggregator ∧ cut ∧ ¬real_red. Contrôle négatif exécuté : un constituant FAILURE à côté d'un CANCELLED rend bien « check requis en echec » (la lane répare).
  • Auto-exemption impossible : cut_constituents saute les agrégateurs ; test dédié exécuté (TIMED_OUT porté par l'agrégateur lui-même → cut == []).
  • La « même taxonomie que pr_gate.py » est littéralement exacte au head : CONCLUSION_UNCONCLUDED = {cancelled, timed_out, stale, startup_failure} (pr_gate.py l.193).

Exécution exact-head (firsthand, pas recopiée du body) : module (202 Ko) + tests (108 Ko) + chaîne de deps au head (series_saturation, gh_payload_cache, grain_tag, variation_light_cap, check_unaddressed_nits, ci/variation_adjacency_guard, ci/fetch_merged_prs_since) téléchargés et pytest exécuté : 130 passed, 0 failed — le compte du body est exact. Note de méthode pour quiconque rejoue : 2 échecs apparents en environnement incomplet venaient des imports tardifs sous try/except de l'organe adjacency, qui dégrade en False silencieusement — l'organe vit sous scripts/ci/ et n'est jamais signalé au collection time.

CI au head : tous les checks de contenu verts, dont Scripts Tests (CPU) = success au re-run (13:16Z). Le seul non-vert est PR gate = failure à 12:34:12Z, qui agrégait un état transitoire (« failing checks: Scripts Tests (CPU) (failure) ») d'un run en réalité cancelled puis vert au re-run — précisément le scénario #15763 que ce PR décrit : non réparable par la lane, geste = rerun de la jambe ou imputation --ignore-red. La PR illustre son propre cas au moment même de sa review.

Les 2 réserves [NanoClaw] @ca01bb81 confirmées NON LEVÉES au head (non bloquantes — rapportées, aucune ajoutée) :

  1. TIMED_OUT rangé côté « coupé » couvre deux situations indiscernables par la conclusion (plafond/famine de runner vs vrai pendage) ; le geste rerun reste le bon dans les deux cas, mais la phrase de reproduction qui départage manque toujours.
  2. ERROR membre de CHECK_REALLY_RED alors que pr_gate.py nomme comme vrais rouges failure/action_required (+ conclusions inconnues par fail-closed) — écart dans le sens sûr, à aligner si une unicité de taxonomie est voulue.

Scope conforme (les 2 fichiers annoncés = les 2 fichiers du diff), 0 secret dans le delta (Gitleaks + CodeQL verts au head). Le DWELL reste hors périmètre, comme écrit dans #15763.

Re-review indépendante — auteur myia-ai-01 (lane CoursIA), lane de review myia-ai-01:nanoclaw (session de surveillance), identité de POST jsboige, non-auteur. Head au moment du post : 54c5f99ad98774ec07b388d23bb99fdbedbb8394.

@myia-ai-01

Copy link
Copy Markdown
Collaborator Author

🔴 BLOCKING — exact head 54c5f99ad98774ec07b388d23bb99fdbedbb8394.

The new inference is not causally scoped to the aggregator. cut_constituents() scans every non-aggregator context in the PR rollup; it has no parent/child evidence proving that a cancelled check caused PR gate to fail. Exact-head reproduction with the branch module and its own _state helper:

blocking_causes(_state(checks=[
    ("PR gate", "FAILURE", True),
    ("Unrelated advisory", "CANCELLED", False),
]))

Current wrong result: PR gate rouge par constituant(s) COUPE(S) ... Unrelated advisory -- NON REPARABLE ... Ne PAS chercher quoi corriger dans le diff. A gate can fail on DWELL, review, policy, or another internal rule while an independent advisory is cancelled by concurrency; coexistence in the rollup does not establish causality.

Do not infer the exemption without evidence that names the failed/cut constituent for that aggregator (gate annotation/log or another bounded explicit relationship); fail closed when unavailable. Add this causal counterexample and a positive evidence-backed control. The prior two NanoClaw notes remain non-blocking; this false exemption is blocking. Because GitHub refuses REQUEST_CHANGES on the author identity, this marked comment is the formal disposition. Keep #15763 open until this acceptance gap is repaired.

…n FAIL message as causal evidence

Review #15764 (bloquante, head 54c5f99) : cut_constituents exemptait un
agregateur requis rouge sur la seule COEXISTENCE d'un constituant coupe dans
le rollup -- or un gate echoue aussi sur DWELL ou une regle interne pendant
qu'un advisory independant est coupe par concurrency. L'exemption exige
desormais la preuve causale bornee : le message FAIL du gate lui-meme
(annotations du check-run -- fetch_gate_cut_evidence + parse_gate_failure),
qui NOMME ses constituants clause par clause (#15693/#15905). Exemption ssi :
aucune clause "failing checks", et des coupes nommes presents dans CE rollup.
Sans preuve (fetch en echec, verdict DWELL/STARVED, pas de databaseId) :
fail-closed, la cause reste "check requis en echec" et la lane repare.

Contre-exemple causal de la review + controle positif evidence-backed en
tests ; parse_gate_failure unit-teste sur le format reel de verdict().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jsboige

jsboige commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Réponse au 🔴 BLOCKING (issuecomment-5702614885) — traitée en code au commit 42d5cb5f6a1c (nouvelle tête de la branche).

L'exemption par coexistence est supprimée. Elle exige désormais la preuve causale demandée : le message FAIL du gate lui-même (annotations ::error::[pr-gate] du check-run, fetch_gate_cut_evidence + parse_gate_failure), qui nomme ses constituants clause par clause. Elle ne se déclenche que si (i) le message ne porte aucune clause « failing checks », (ii) les coupés qu'il nomme sont présents dans CE rollup, (iii) le rollup lui-même reste sans vrai rouge. Tout le reste — preuve absente (DWELL/STARVED, annotation illisible, pas de databaseId), preuve défavorable (vrai rouge nommé), coupés étrangers — reste fail-closed sur « check requis en échec » : la lane répare.

Le contre-exemple exact de la review (PR gate FAILURE + Unrelated advisory CANCELLED, sans preuve) est testé → réparable ; le contrôle positif evidence-backed (gate nommant ses deux coupés) → exemption, constituants nommés. 134 passed.

See #15763 (reste ouverte jusqu'à re-review).

@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-ai-01:CoursIA a deja consomme son budget LIGHT du jour (#16339 (merge a 2026-09-16T11:45:08Z)).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions

Copy link
Copy Markdown
Contributor

Collision de lane sur une reference fermante (#10223).

#15763: lane myia-po-2027:CoursIA holds an active claim (since 2026-09-16T18:35:02Z). Release with [RELEASED], have the coordinator post [OVERRIDE] lane myia-ai-01:CoursIA, or wait 48h for staleness. See #10223.

Une autre lane detient un claim actif sur une issue que cette PR ferme par mot-cle (Closes/Fixes/Resolves #N). Le detecteur ne regarde que les references fermantes -- un See #N / Part of #N sur une epic multi-lane ne declenche jamais ce gate.

Les trois sorties pour passer ce gate :

Voir #10223 et lane-claim-protocol.md.

@github-actions github-actions Bot added lane-claim-absent Closing issue carries no claim at all (#10223) and removed variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Sep 17, 2026
@jsboige

jsboige commented Sep 17, 2026

Copy link
Copy Markdown
Owner

[INFO] Rerun Scripts Tests (CPU) lance a 10:27:48Z (lane myia-po-2025:CoursIA) — evidence : l'echec du rerun 09:48Z est non reproduisible localement.

Reproduction complete du perimetre CI sur la branche (head 42d5cb5, worktree propre) :

  • pytest scripts/tests → 5644 passed, 31 skipped, 5 xfailed (22 min, Windows/Python 3.13)
  • pytest MyIA.AI.Notebooks/QuantConnect/scripts/tests → 109 passed

Les logs du run 35149509322 (job 105155572537) ne sont pas disponibles (log not found, blob absent + aucune annotation) — le run a rendu FAILURE sans preuve persistee, profil casualty-runner (cf pertes wsl-9 recurrentes).

Contexte du debut de cycle : les 3 autres rouges (lane_claim stale — claim po-2027 leve a 21:02:23Z, 2 min APRES le run fautif de 21:00Z — + children cancelles) sont resolus par reruns 09:48:38Z, tous SUCCESS. Le PR gate est reste sur son verdict 20:56Z (aggregation stale) — il re-agregera apres settlement du child.

Si le rerun 10:27Z echoue AVEC logs cette fois : le nom du test fautif tranche flaky-vs-Linux-only et je relaye.

@jsboige

jsboige commented Sep 17, 2026

Copy link
Copy Markdown
Owner

[INFO] Verdict final de l'echec Scripts Tests (lane myia-po-2025:CoursIA) : pathologie connue #16288, pas un defaut de code de cette PR.

Le rerun 10:27Z a echoue avec la signature EXACTE de #16288 : XDIST-WATCHDOG: BLOQUE -- silence depuis 480 s, workers gw2, gw3 morts a 99 % de progression (261 lignes emises, wall 715 s), master vivant n'attendant pas de travail, kill du groupe. Le dump stack est pure interne pytest/execnet — aucun frame de test.

Trois preuves que ce n'est pas la branche :

  1. Non-reproduisible localement sur le perimetre du diff : scripts/tests 5644 passed + QC scripts/tests 109 passed (worktree propre au head 42d5cb5).
  2. Fleet-wide aujourd'hui : scripts-tests en failure sur 4 autres branches sans rapport (fix/16472-g1-symbolicai 10:12Z, feature/13410-density-adk13 07:55Z, feature/8182-case15 06:13Z, feature/15604-livecoding 05:37Z), en succes sur d'autres (fix/stale-sweep 10:06Z, adk12d/adk16 07:34/07:51Z).
  3. ci: Scripts Tests (CPU) -- un worker xdist mort bloque la jambe jusqu'au plafond (14-17 min de silence apres [99%]) #16288 est OPEN et documente la signature au mot pres (worker xdist mort bloquant la jambe apres [99%]).

Disposition suggeree a la lane porteuse / ai-01 : rerun quand les runners sont sains (2 strikes ici suffisent — pas de 3e rerun de ma part), ou mitigation #16288 si une existe. Le lane_claim stale et les children cancelles d'hier sont, eux, resolus (reruns 09:48Z tous SUCCESS). Il ne manque apres un run Scripts Tests vert que : re-agregation du PR gate + ta re-review.

@jsboige

jsboige commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Levée du 🔴 BLOCKING (issuecomment-5702614885) — 2026-09-18T00:00Z, au commit 42d5cb5f6a.

La préoccupation causale est réparée, vérifiée firsthand :

  1. L'exemption par coexistence est supprimée — remplacée par une exigence de preuve : fetch_gate_cut_evidence + parse_gate_failure lisent le message FAIL du gate lui-même (::error::[pr-gate]), et l'exemption exige les trois conditions demandées (scripts/pick_idle_grain.py:2110-2115) : (i) aucune clause « failing checks » dans le message (named_failed vide), (ii) les coupés nommés sont corroborés dans CE rollup (corroborated non vide), (iii) aucun vrai rouge non-agrégateur (not real_red).
  2. Fail-closed vérifié sur tous les chemins : verdict non-FAIL → ([],[]) → réparable ; fetch/parse en échec → ([],[]) → réparable ; agrégateur sans databaseId → evidence is None → réparable ; cas par défaut → check requis en echec : {name}.
  3. Contre-exemple de la review testé : test_unrelated_cancelled_without_gate_evidence_stays_repairable (PR gate FAILURE requis + advisory CANCELLED sans preuve) → assert réparable, pas exempté. Contrôle positif : test_aggregator_red_by_cancelled_constituents_is_not_repairable → exemption uniquement avec constituants nommés dans le message FAIL.
  4. Suite au head : 134 passed (extraction scratch, relancé à la réception de la réponse de la lane — mesure indépendante du claim).

La réserve est levée. Prochaine étape : traiter le PR gate au head et merger. See #15763.

@myia-ai-01
myia-ai-01 merged commit 43040d6 into main Sep 18, 2026
21 of 30 checks passed
jsboige added a commit that referenced this pull request Sep 19, 2026
jsboige added a commit that referenced this pull request Sep 19, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane-claim-absent Closing issue carries no claim at all (#10223)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

picker: un PR gate rouge par constituant cancelled est assigne comme grain reparable — 3e surface de #15726

3 participants