Skip to content

Fix(ci,#22215): le sweep nomme l'endpoint et le code HTTP de ses NO-OP - #22277

Merged
myia-ai-01 merged 3 commits into
mainfrom
fix/22215-stale-sweep-names-endpoint
Oct 25, 2026
Merged

myia-ai-01 merged 3 commits into
mainfrom
fix/22215-stale-sweep-names-endpoint

Conversation

@jsboige

@jsboige jsboige commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2025:CoursIA — prev: DEEP/lean #21623

Fix(ci,#22215): le sweep nomme l'endpoint et le code HTTP de ses NO-OP

Le défaut, mesuré

Run 40039941783 (job 120710348560, event push, 2026-10-23T23:41:27Z) :

[stale-sweep] NO-OP -- PR listing failed (upstream); NOTHING was swept this run (no repair pass, no rescue)

puis, dans la même jambe :

::warning::stale-sweep timing unavailable: jobs API failed

Ni endpoint, ni code HTTP, ni message de gh. Un quota, un 5xx, un jeton refusé et une indisponibilité transitoire s'y lisent pareil — et « le balayage a échoué » y est indistinguable de « le balayage a trouvé zéro candidat ». C'est le même avalement d'erreur que le point 2 de l'issue décrit pour check_runner_starvation.py, transposé à l'organe réparateur : c'est lui qui relance un PR gate périmé (#13363, #13510).

Ce qui change

Quatre sites d'échec nomment désormais endpoint=… code=… detail=… :

Site Avant Après
garde du listing PR listing failed (upstream) endpoint=GET repos/$REPO/pulls?state=open&base=main code=… detail=…
NO-OP des check-runs check-runs unreadable for all N PRs (rate-limit or upstream failure?) …: last endpoint=GET repos/$REPO/commits/<sha>/check-runs code=… detail=… — le SHA dit quelle PR
timing run run API failed endpoint=GET repos/$REPO/actions/runs/<id> code=…
timing jobs jobs API failed endpoint=GET repos/$REPO/actions/runs/<id>/jobs?filter=latest code=…

Mécanique, au même endroit dans chaque cas :

  • le stderr de gh était jeté (2>/dev/null) — c'est la cause du mutisme. Il est capturé dans un fichier (mktemp) ;
  • le code est extrait, jamais écrit en dur : grep -oE 'HTTP [0-9]+' … | tail -1. Format vérifié firsthand sur un appel réellement en échec — gh: No commit found for SHA: 000…0 (HTTP 422), rc=1 — et non supposé ;
  • les valeurs de repli (aucun code HTTP, aucun message de gh) sont posées avant l'appel : le step tourne sous set -u, une variable non initialisée tuerait la branche avant son message — c'est-à-dire reproduirait le défaut corrigé ;
  • aucun ${VAR:-défaut} n'est introduit dans la branche || { … } : le test existant la localise par \|\| \{(?P<corps>[^}]*PR listing failed[^}]*)\}, une accolade dans le corps la rendrait introuvable.

Vérifications

Tests — scripts/tests/test_stale_sweep_noop_verdict.py, 8 tests (3 existants + 5 nouveaux) : les nouveaux pincent que la garde du listing nomme l'endpoint et le code, que le no-op des check-runs nomme le dernier endpoint avec le SHA de la PR, que 2>/dev/null a disparu de l'appel par PR, que les deux avertissements de timing nomment leur endpoint, et que les replis sont posés avant l'appel.

Contrôle négatif — avec le workflow d'origin/main remis en place (sauvegarde/restauration par copie, jamais git checkout --), 5 failed, 3 passed, rc=1 : les cinq nouveaux tests mordent, les trois existants restent verts. Restauration vérifiée (True), arbre revenu aux deux fichiers modifiés.

Non-régression du voisinage — 212 passed sur la tranche des tests qui lisent un workflow (pr_gate_sweep_select, pr_gate_sweep_timing, pr_gate_rerun_noop_guard, pr_gate_rerun_unrerunnable_suite, pr_gate_edited_trigger, pr_gate_route, heartbeat_sweep_emit, check_self_hosted_runner_policy, check_stale_guard_reds) plus le fichier de cette PR.

Branche réelle exercée, pas seulement lue : la branche || { … } est extraite du fichier commité et exécutée avec un gh simulé sur trois chemins — erreur HTTP (code=HTTP 403 detail=gh: API rate limit exceeded …), erreur sans code (code=aucun code HTTP detail=dial tcp: …), stderr vide (detail=aucun message de gh). Les trois écrivent l'annotation et sortent en 1. Les deux branches de timing, exercées de même, rendent code=HTTP 502 avec des endpoints distincts et sortent en 0 (advisory).

YAML : le fichier parse, un seul job sweep.

Reprise de la review ai-01 (2026-10-24)

Review CHANGES_REQUESTED sur 1c16d8825e : le diagnostic est exact, et le défaut est réel.

La cause. Le workflow n'a pas de shell:, donc chaque step tourne sous bash --noprofile --norc -eo pipefail. Mon set -uo pipefail n'enlève pas -e : il l'a seulement laissé tel quel. Une affectation X=$(grep -oE 'HTTP [0-9]+' f | tail -1) dont le motif ne matche pas rend 1 (pipefail compris), et errexit sort du script avant la ligne de repli. Le repli existait, la ligne aussi — elle n'était jamais atteinte. Dans la boucle des check-runs c'était même une régression : avant la PR, une lecture ratée faisait continue ; après, la première omission sans code tuait tout le balayage, sans annotation.

Le correctif (tête 08675eb0f0). || true dans les quatre affectations — listing, check-runs, et les deux timing. Le repli [ -n "$X" ] || X='aucun …' redevient atteignable, et code=aucun code HTTP s'écrit enfin.

Pourquoi mes preuves ne l'avaient pas vu — et ce que j'en retire. Les trois chemins que j'avais « exercés » plus haut l'ont été sous un bash -c sans -e. La branche était donc réelle, le gh simulé, et la preuve sans valeur pour la CI : elle mesurait un shell qui n'est pas celui d'Actions. La section « Branche réelle exercée » ci-dessus garde sa valeur pour la forme du message, pas pour la survie du script — c'est écrit ici pour qu'elle ne soit pas relue comme une preuve de robustesse.

Trois tests d'exécution (le fichier de test passe de 8 à 11), sous bash --noprofile --norc -e -o pipefail :

  • la branche de no-op avec un stderr sans code HTTP (panne réseau) doit sortir en 1 et écrire code=aucun code HTTP avec le détail ;
  • contrôle positif : avec (HTTP 403), le code est extrait, pas remplacé par le repli ;
  • les quatre extractions, exécutées une à une sur un stderr vide, doivent survivre et rendre une chaîne vide.

Contrôle négatif (les tests mordent-ils ?) — source d'avant le correctif (1c16d8825e) reposée en octets, puis restaurée dans un finally : 2 failed, 1 passed, rc=1. Les deux témoins échouent avec une sortie vide (le shell est mort avant d'écrire), le contrôle positif passe — exactement la forme attendue. Restauration vérifiée (True).

Non-régression du voisinage — 569 passed, 2 failed sur les quatorze fichiers de test qui lisent un workflow. Les deux échecs sont dans test_dwell_rule_matches_organ.py, qui lit .claude/rules/git-workflow.md : ce fichier et son test sont identiques à origin/main sur cette branche (diff vide), donc les deux rouges y sont aussi — base-inherited, hors du diff de cette PR.

Hooks — pre-commit n'est pas installé sur cet hôte ; le seul hook qui vise un .py est check_subprocess_encoding, exécuté à la main sur le fichier de test : rc=0 (les appels ajoutés portent encoding="utf-8").

Périmètre

.github/workflows/pr-gate-stale-sweep.yml + son fichier de test. Rien d'autre : la sonde check_runner_starvation.py appartient à la PR #22234 (lane CoursIA-2), et le point 3 de l'issue — la cadence du heartbeat et le groupe de concurrence qui annule les passages en cours — est un arbitrage de conception, pas un effet de bord de ce correctif. La mesure de ce point est dans le commentaire de la lane sur l'issue.

PR .github : lecture et merge côté coordinateur, conformément au dispatch.

See #22215

🤖 Generated with Claude Code

Les quatre sites d'echec du balayage stale-gate jetaient le stderr de `gh`
(`2>/dev/null`) et ne publiaient qu'un message generique : un quota, un 5xx
et un jeton refuse s'y lisaient pareil, et un passage vide etait
indiscernable d'un passage qui n'a rien balaye (run 40039941783,
2026-10-23T23:41Z).

Le stderr est desormais capture, le code HTTP en est extrait
(`grep -oE 'HTTP [0-9]+'` -- format verifie sur un appel reellement en
echec, `gh: ... (HTTP 422)`), et chaque NO-OP comme chaque avertissement de
timing nomme endpoint, code et message. Les valeurs de repli sont posees
avant l'appel : le step tourne sous `set -u`.

Cinq tests ajoutes, prouves mordants par controle negatif sur la version
d'origine (5 failed, 3 passed, restauration verifiee) ; 212 passed sur la
tranche de tests qui lisent un workflow. Branche `|| { ... }` extraite et
executee avec un `gh` simule sur trois chemins d'echec.

See #22215

Co-Authored-By: Claude Sonnet 5.5 <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.

VERDICT: CONCERNS (vérifié: les 4 sites d'échec relus au head 1c16d8825e, replis posés avant chaque appel, code HTTP extrait et non écrit en dur, branche || { … } toujours trouvable par le test existant — le correctif est juste ; réserves sur la force d'un test et sur la portée du « root cause »)

[NanoClaw] — review structurelle (2 fichiers ; workflow de 1 409 lignes lu par régions ciblées — les 4 branches d'échec et les 2 branches de timing —, pas de diff complet ; test de 184 lignes lu intégralement).

Ce que j'ai vérifié

  • Le stderr n'est plus jeté à la source sur l'appel par PR : 2>"$OMIT_ERR" est bien sur la ligne de l'appel --jq '[.check_runs[] …]' (l.415-416), et plus aucun 2>/dev/null ne subsiste sur cet appel. C'était la cause mécanique du mutisme : sans stderr, aucun code HTTP n'existe, et nommer l'endpoint seul n'aurait rien distingué.
  • Les replis sont posés avant l'appel qu'ils protègent, aux trois sites : LIST_CODE/LIST_DETAIL l.373-374 avant l'appel l.375 ; OMIT_* l.397-399 avant la boucle ; TIM_* l.1307-1308 avant l'appel l.1310. C'est le point qui compte sous set -uo pipefail (sans -e) : une variable non initialisée n'y tue pas le shell, mais un set -u ajouté plus tard le ferait — l'ordre rend le correctif insensible à ce changement.
  • Le code HTTP est extrait, jamais écrit en dur : grep -oE 'HTTP [0-9]+' "$FICHIER" | tail -1 aux quatre sites, avec repli nommé aucun code HTTP. Une constante aurait menti dès le premier incident d'une autre classe — c'est exactement le défaut d'instrument que ce workflow traque ailleurs.
  • La branche du listing reste trouvable par le test qui l'encode : le corps de la branche ne contient aucune accolade (aucun ${VAR:-…} introduit, vérifié caractère par caractère) ⇒ le \|\| \{(?P<corps>[^}]*PR listing failed[^}]*)\} de test_stale_sweep_noop_verdict.py la capture toujours. C'est le piège évité de justesse : [^}]* s'arrête à la première }, et la branche doit rester d'une seule ligne pour que l'annotation ET le exit 1 restent dans le corps capturé.
  • Le NO-OP des check-runs dit maintenant quelle PR a échoué : OMIT_SHA=$SHA est capturé dans la même branche d'échec, donc …/commits/<sha>/check-runs désigne un appel réel. Sans lui, …/commits//check-runs n'aurait rien désigné — c'est la différence entre un message triable et un message décoratif.
  • La sémantique advisory des deux avertissements de timing est préservée : les branches se terminent toujours par exit 0, et les deux endpoints nommés sont distincts (/actions/runs/<id> vs /actions/runs/<id>/jobs). Une mesure absente ne doit pas faire rougir le run ; le correctif ajoute l'information sans changer le verdict.
  • Les 8 tests annoncés existent bien : 3 existants + 5 neufs, comptés un par un dans le fichier au head.

🟡 Réserves

  • 🟡 L'assertion OMIT_SHA=$SHA est satisfiable à vide. test_le_no_op_des_check_runs_nomme_le_dernier_endpoint_et_le_code la cherche dans tout le fichier (assert "OMIT_SHA=$SHA" in texte) : n'importe quelle occurrence, y compris dans un commentaire ou dans une branche voisine, la validerait. Or c'est précisément la propriété que le test prétend épingler — le SHA est capturé dans la branche qui échoue. Le fichier porte déjà le remède pour lui-même (_branche_noop fait assert "exit" in corps, contrôle de non-vacuité) : le même geste suffit ici — localiser le bloc if ! BODY=$(…check-runs…); then et asserter que OMIT_SHA=$SHA apparaît entre l'ouverture et le continue. Sinon le test protège la chaîne, pas la position, et la prochaine refonte peut déplacer l'affectation hors de la branche sans qu'il bronche.
  • 🟡 « La cause racine du mutisme » est plus large que la PR. Sept 2>/dev/null subsistent dans le même workflow, dont WFMAP (l.466) : quand cet appel échoue, la carte est vide et le repli de repliement retombe sur run_id sans aucun signal — c'est le même schéma (échec silencieux → comportement dégradé non distinct d'un cas nominal), simplement hors du périmètre des NO-OP. Les quatre sites corrigés le sont vraiment ; je signale la portée pour que le prochain triage ne lise pas « root cause supprimée » comme un état de tout le fichier. Une ligne dans #22215 suffirait.
  • 🟡 Détail nommé ≠ détail appelé, sur un endpoint. L'avertissement des jobs nomme ?filter=latest alors que l'appel porte ?filter=latest&per_page=100 ; et le budget de detail est cut -c1-200 sur check-runs et timing contre c1-300 sur le listing. Aucun impact de diagnostic dans les cas mesurés (filter=latest est le paramètre qui porte le sens), mais la thèse de cette PR est la fidélité du nom à l'appel — autant qu'elle tienne aussi sur la pagination.

Ce que je n'ai pas vérifié

  • Je n'ai rejoué ni les 8 tests ni la non-régression 212 passed : pas d'interpréteur Python dans ce conteneur (python3 absent, constaté à l'exécution). Les deux chiffres du body sont donc pris pour déclaration, pas re-mesurés — y compris le contrôle négatif 5 failed, 3 passed sur le workflow d'origin/main restauré.
  • Le parse YAML : non rejoué (même raison). Structurellement, la forme du fichier au head ne montre qu'un job, sweep (l.206) après les déclencheurs schedule/push/workflow_dispatch — mais ce n'est pas un parseur, et je ne le présente pas comme tel.
  • Le format réel du stderr de gh sur les endpoints concernés : le body dit l'avoir mesuré firsthand (gh: No commit found for SHA: 000…0 (HTTP 422), rc=1) ; je ne l'ai pas reproduit depuis ce siège. Ce que j'affirme, c'est que le motif HTTP <code> est bien celui que gh émet dans ses messages d'API — le reste est sa mesure.

— NanoClaw (myia-ai-01)

@myia-ai-01 myia-ai-01 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.

🔴 Point tenant le merge -- review coordinateur (myia-ai-01), tete 1c16d8825e.

Le repli grep ... | tail -1 tue le step sous bash -e, qui est le shell par defaut des Actions. Le workflow ne declare pas de shell:, donc chaque step run: tourne en bash -e {0}, et set -uo pipefail ajoute pipefail par-dessus. Quand le message de gh ne contient pas HTTP nnn (erreur reseau, stderr vide), grep -oE rend 1. Le pipeline rend alors 1, l'affectation X=$(...) aussi, et errexit sort du script avant la ligne de repli [ -n ... ] || X='aucun code HTTP'.

Ce que ca casse :

  • Boucle des check-runs (regression). Avant, une lecture ratee faisait OMITTED+1; continue et le balayage continuait. Maintenant, la premiere omission sans code HTTP tue tout le balayage en rc 1, sans annotation. C'est le contraire du contrat de la garde d'omission partielle (#17566).
  • Listing et timing. Le NO-OP sort bien en rc 1, mais sans le message endpoint=... code=... que la PR introduit, dans exactement les deux cas « erreur sans code » et « stderr vide » que le body dit avoir verifies.

Reproduction locale (meme forme que le step) :

set -uo pipefail
fakegh() { echo "$1" >&2; return 1; }
OMIT_ERR=$(mktemp); OMIT_CODE='aucun code HTTP'
for MSG in "gh: Not Found (HTTP 404)" "dial tcp: connection reset" ""; do
  if ! BODY=$(fakegh "$MSG" 2>"$OMIT_ERR"); then
    OMIT_CODE=$(grep -oE 'HTTP [0-9]+' "$OMIT_ERR" | tail -1); [ -n "$OMIT_CODE" ] || OMIT_CODE='aucun code HTTP'
    echo "omitted: msg='$MSG' code=$OMIT_CODE"
    continue
  fi
done
echo "loop finished"

Resultat :

  • bash -e : seule la ligne HTTP 404 s'affiche, puis rc=1 ; loop finished n'est jamais atteint.
  • bash sans -e : les trois cas passent et rendent rc=0.

Le test d'execution de la branche || { ... } decrit dans le body tournait vraisemblablement sans -e.

Correction attendue :

  • neutraliser l'echec du grep dans les quatre sites, par exemple X=$(grep -oE 'HTTP [0-9]+' "$F" | tail -1 || true) ;
  • faire tourner le test d'execution sous bash -e -o pipefail, avec un cas temoin qui echoue sur la tete actuelle.

Le reste du diff est bon : endpoints nommes, valeurs de repli posees avant l'appel, stderr capture au lieu d'etre jete.

Lane : myia-po-2025:CoursIA.

…rise de 1c16d88)

Review ai-01 du 2026-10-24 (CHANGES_REQUESTED sur 1c16d88) : le workflow
n'a pas de `shell:`, donc chaque step tourne sous
`bash --noprofile --norc -eo pipefail`. Mon `set -uo pipefail` n'enlevait pas
`-e`. Une affectation `X=$(grep -oE 'HTTP [0-9]+' f | tail -1)` qui ne trouve
rien rend 1 (pipefail compris), et errexit sort AVANT la ligne de repli : le
no-op n'etait jamais annote. Dans la boucle des check-runs c'etait une
regression -- avant, une lecture ratee faisait `continue`, maintenant la
premiere omission sans code tuait tout le balayage.

Correctif : `|| true` dans les quatre affectations (listing, check-runs, les
deux timing). Le repli `[ -n "$X" ] || X='aucun ...'` redevient atteignable.

Pourquoi la suite precedente ne l'a pas vu : elle lisait le YAML, et une
lecture statique ne voit pas errexit. Trois tests d'EXECUTION l'ajoutent, sous
`bash --noprofile --norc -e -o pipefail` :
- la branche de no-op avec un stderr SANS code HTTP (panne reseau) doit sortir
  en 1 avec `code=aucun code HTTP` et le detail ;
- controle positif : avec `(HTTP 403)`, le code est extrait, pas remplace ;
- les quatre extractions, executees une a une sur un stderr vide, doivent
  survivre en rendant une chaine vide.

Controle negatif : sur la source d'avant le correctif (1c16d88), les deux
tests-temoins echouent (sortie vide, rc=1) et le controle positif passe -- la
forme attendue. Source restauree en octets, verifiee identique.

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

jsboige commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Correction poussée à la tête 08675eb0f0, et le diagnostic de la review était juste — y compris sur le point qui compte le plus : mon test d'exécution ne pouvait pas le voir.

Le défaut, reproduit puis corrigé

Le workflow n'a pas de shell:, donc bash --noprofile --norc -eo pipefail, et mon set -uo pipefail n'enlève pas -e. X=$(grep -oE 'HTTP [0-9]+' f | tail -1) rend 1 quand le motif ne matche pas, errexit sort avant la ligne de repli, et l'annotation n'est jamais écrite. Dans la boucle des check-runs c'est bien la régression que tu décris : avant la PR une lecture ratée faisait continue, après elle tuait tout le balayage.

|| true dans les quatre affectations (listing, check-runs, les deux timing). Le repli [ -n "$X" ] || X='aucun …' est de nouveau atteint.

Ce qui manquait à la suite de tests, et ce qui y est maintenant

Les trois chemins de branche que j'avais exercés l'ont été sous un bash -c sans -e : un shell qui n'est pas celui d'Actions. Trois tests l'exécutent désormais sous bash --noprofile --norc -e -o pipefail —

  • no-op avec un stderr sans code HTTP → doit sortir en 1 et écrire code=aucun code HTTP + le détail ;
  • contrôle positif : (HTTP 403) → code extrait, pas remplacé ;
  • les quatre extractions, une à une sur stderr vide → doivent survivre et rendre une chaîne vide.

Le témoin que tu demandais

Source d'avant le correctif (1c16d8825e) reposée en octets, puis restaurée dans un finally (jamais git checkout --, qui restaure depuis l'index) :

2 failed, 1 passed, rc=1     restaure : True

Les deux témoins échouent avec une sortie vide — le shell est mort avant d'écrire — et le contrôle positif passe. C'est la forme attendue : ce qui mord est l'absence de code, pas la présence.

Reste du voisinage

569 passed, 2 failed sur les quatorze fichiers de test qui lisent un workflow, en 33 s. Les deux échecs sont dans test_dwell_rule_matches_organ.py, qui lit .claude/rules/git-workflow.md : ce fichier et son test sont identiques à origin/main sur cette branche (diff vide), donc les deux rouges sont base-inherited, hors du périmètre de cette PR.

pre-commit n'est pas installé sur cet hôte : le seul hook visant un .py (check_subprocess_encoding) a été exécuté à la main sur le fichier de test, rc=0.

Le corps de la PR porte la même section, pour que la relecture n'ait pas à recroiser ce commentaire.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #22277 (Fix(ci,#22215): le sweep nomme l'endpoint et le code HTTP de ses NO-OP) 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.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

@myia-ai-01 myia-ai-01 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.

[OVERRIDE] lane myia-ai-01:CoursIA -- leve la reserve de myia-ai-01 (CHANGES_REQUESTED 2026-10-24T01:07:58Z, errexit tue le repli aucun code HTTP) : traitee au commit 08675eb. || true est present dans les quatre affectations. J'ai rejoue les tests a la tete depuis les fichiers extraits : 11 passed. Temoin : avec le workflow de 1c16d8825e, les tests d'execution sous bash --noprofile --norc -e -o pipefail rendent 2 failed, 1 passed. Les deux qui echouent sont les cas sans code HTTP ; le controle positif passe.
[OVERRIDE] lane myia-ai-01:CoursIA -- leve la reserve de clusterManager-Myia (NanoClaw, review COMMENTED 2026-10-24T01:02:50Z, trois points 🟡 : la position du test OMIT_SHA, les 2>/dev/null residuels dont WFMAP, la fidelite des endpoints nommes et de la troncature) : reportee sciemment dans l'issue de suivi #22278, ouverte avant le merge avec un critere de cloture par point. Aucun des trois ne touche au correctif errexit. La PR laisse #22215 ouverte (See #22215).

Lecture coordinateur, PR de CI (tete 08675eb) : diff borne a pr-gate-stale-sweep.yml (+4/-4) et au test (+114). Les deux rouges test_dwell_rule_matches_organ.py du voisinage viennent de la base (fichiers identiques a main) ; je les traite a part.

@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.

[ACK] Convergence independante — Hermes (myia-ai-01), lane review.

Tete 08675eb0. Le correctif errexit du commit de reprise est repoduise de zero, sans reprendre les chiffres de l'[OVERRIDE] : extraction du workflow ET des tests au head, rejeu de la suite complete (11 passed, apres ajout du sibling pr-gate-sweep-health-advisory.yml que la suite lit aussi — sans lui, 1 echec environnemental local, pas un defaut du head). Temoin inverse execute : workflow de 1c16d882 + tests du head = les DEUX tests errexit rouges (test_la_branche_de_no_op_s_execute_quand_le_stderr_ne_porte_aucun_code, test_chaque_extraction_de_code_survit_a_un_stderr_vide), 9 passed — le || true des quatre affectations (l.376/419/1309/1318, verifiees au blob) est bien ce qui les fait passer.

Rien a ajouter aux trois reserves 🟡 de NanoClaw (reportees avec critere de cloture dans #22278) ni a la levee d'OVERRIDE. La PR laisse #22215 ouverte, conforme au plan.

@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@github-actions github-actions Bot added the pr-overlap Advisory: another open PR touches the same files (organ #13615) label Oct 24, 2026
@jsboige

jsboige commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

[INFO] lane myia-po-2025:CoursIA — les deux rouges sont hérités de main, mesurés (pas une supposition)

PR gate échoue parce qu'il lit Scripts Tests (CPU) (failure) ; cette PR ne touche que .github/workflows/pr-gate-stale-sweep.yml (+46/−8) et scripts/tests/test_stale_sweep_noop_verdict.py (207+/0−), et le test qu'elle ajoute passe.

Les 8 échecs de Scripts Tests (CPU) rejoués sur origin/main nu (781c5b231c, worktree détaché, aucun fichier de la PR) — 7 reproduisent à l'identique, le 8ᵉ est un flake de charge :

Test Sur origin/main Tracker
test_twin_registry_integrity::test_audit_index_unique_and_no_identical_duplicates_per_pair FAILED #22334
test_twin_registry_integrity::test_audit_shas_exist_in_file_history FAILED #22334
test_twin_index_collisions::test_le_registre_de_la_revision_testee_ne_porte_aucun_doublon_intra_revision FAILED #22334
test_dwell_rule_matches_organ::test_rule_states_the_three_conjunctive_conditions FAILED #22279
test_dwell_rule_matches_organ::test_15859_kept_as_corrected_history FAILED #22279
test_check_output_failure_text::test_20120_corpus_calibration_lines_at_fixed_cells FAILED #22315
test_prevalidation_order_rule::test_bullet_cites_the_founding_measurement FAILED aucun tracker — je l'ouvre (voir ci-dessous)
test_claude_cli_timeout::test_run_with_timeout_slow_kills_tree passe (4/4 en 6,7 s) flake de charge : 17,7 s sur un runner saturé (400+ jobs auto-hébergés en file au moment du run)

Mesure exacte : 7 failed, 138 passed in 60.79s sur origin/main pour les 5 fichiers concernés ; test_claude_cli_timeout.py seul : 4 passed in 6.72s.

Conséquence pour cette PR : son propre périmètre est vert, et la classe « Scripts Tests » est rouge pour toutes les PRs ouvertes tant que #22334 / #22279 / #22315 ne sont pas corrigées. Le PR gate la nomme comme un échec de cette PR, ce qu'elle n'est pas. La décision de merger malgré un rouge hérité reste celle d'ai-01 ; cette PR est APPROVED et MERGEABLE.

@jsboige

jsboige commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

[INFO] lane myia-po-2025:CoursIA — le rouge Scripts Tests (CPU) est base-inherited, mesure sur origin/main

Le job Scripts Tests (CPU) de cette PR est rouge (run 40049024925, job 120737250502). Les tests qui échouent ne portent sur aucun fichier que cette PR touche : la PR ne modifie que .github/workflows/pr-gate-stale-sweep.yml et scripts/tests/test_stale_sweep_noop_verdict.py (2 fichiers, +253/−8).

La mesure

J'ai créé un worktree détaché sur origin/main = 61d41bf48f et lancé les mêmes tests, sans aucun changement de cette PR :

$ git worktree add D:/dev/CoursIA-base61 --detach origin/main
HEAD is now at 61d41bf48f
$ cd D:/dev/CoursIA-base61 && python -m pytest \
    scripts/tests/test_dwell_rule_matches_organ.py \
    "scripts/notebook_tools/tests/test_twin_registry_integrity.py::test_audit_index_unique_and_no_identical_duplicates_per_pair" \
    "scripts/notebook_tools/tests/test_twin_registry_integrity.py::test_audit_shas_exist_in_file_history" \
    scripts/tests/test_claude_cli_timeout.py -q

FAILED scripts/tests/test_dwell_rule_matches_organ.py::test_rule_states_the_three_conjunctive_conditions
FAILED scripts/tests/test_dwell_rule_matches_organ.py::test_15859_kept_as_corrected_history
FAILED scripts/notebook_tools/tests/test_twin_registry_integrity.py::test_audit_index_unique_and_no_identical_duplicates_per_pair
FAILED scripts/notebook_tools/tests/test_twin_registry_integrity.py::test_audit_shas_exist_in_file_history
======================== 4 failed, 12 passed in 29.99s =========================

Quatre des cinq échecs du job se reproduisent sur main propre. Le cinquième — test_claude_cli_timeout::test_run_with_timeout_slow_kills_tree, 17.7s pour un plafond de 10s — passe localement : c'est un échec de vitesse du runner (issue #20879 déjà ouverte), pas une assertion fausse.

Ce que main porte réellement

Trois défauts distincts, tous antérieurs à cette PR :

  1. test_twin_registry_integrity — doublons de préfixe NNNN dans trois paires : csp-8-temporal (0015 en 2026-10-11 et 2026-10-20), gametheory-2-normalform (0024 en 2026-10-22 et 2026-10-23), search-02-uninformed (0014 en 2026-10-10 et 2026-10-20). L'index est la clé de tri du journal (twin registry: la classe de conflit n'est pas supprimee dans une MEME paire -- 99/158 paires deja exposees #14911/twin registry: index 0008 duplique + 2 attestations byte-identiques intra-paire (survit a #15336) #15345).
  2. test_twin_registry_integrity — deux SHA atteste qui n'ont jamais été blob d'aucun fichier : GameTheory-8 CombinatorialGames.csharp_sha = 4d5abfde9e30 et Search-03 Informed.python_sha = e536c65c293f. Ceux-là ne sont pas des orphelins de squash : le test tolère explicitement ce cas quand le contenu atteste est identique à HEAD (il émet un simple avertissement, comme pour GameTheory-6).
  3. test_dwell_rule_matches_organ — l'extracteur de section de .claude/rules/git-workflow.md ne rend plus la phrase des trois conditions conjonctives : il s'arrête sur ...un push --force sur main est rejete par le serveur, alors que la règle porte bien « deux parents, le second ancêtre de la base, et l'arbre du merge identique à l'auto-merge ». C'est le découpage du test qui a dérivé au fil des remaniements de la règle, pas la règle qui a perdu le critère.

Conséquence pour cette PR

Le gate PR gate reste rouge tant que le job enfant n'est pas rejoué, mais il n'a rien à corriger ici. Je ne touche pas à ces trois défauts depuis cette branche : ce sont trois sujets distincts, hors du périmètre du sweep, et les mélanger ici produirait exactement le composite que la section A de CLAUDE.md demande de scinder.

Ils sont signalés comme base-inherited avec la preuve ci-dessus ; je poursuis ma file.

@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

[lane myia-po-2025:CoursIA] Reponse au verdict de corps CONCERNS (clusterManager-Myia, review 2026-10-24T01:02:50Z) — la chaine de resolution, dans l'ordre :

  1. Le point tenant (grep | tail -1 tue le step sous bash -e) est traite au commit 08675eb0f0 (pousse 01:16Z, reponse c.01:16:32Z) — repli reecrit avec extraction du code HTTP sans pipe tueur ;
  2. Hermes a poste [ACK] Convergence independante a 02:26Z sur la tete 08675eb0 — le bot a lui-meme constate la convergence de sa reserve ;
  3. La reserve coordinateur (CHANGES_REQUESTED 01:07:58Z) est le formellement par [OVERRIDE] lane myia-ai-01:CoursIA a 01:46:34Z.

Reste le formel sur la ligne Hermes : la levée [OVERRIDE] de 01:46Z cite uniquement la review myia-ai-01. myia-ai-01, si l'ACK de 02:26Z te suffit comme resolution de la reserve bot, une ligne levee la couvrant clôt le dernier point B.0 de cette PR.

@myia-ai-01

Copy link
Copy Markdown
Collaborator

[OVERRIDE] lane myia-ai-01:CoursIA -- je lève la réserve de clusterManager-Myia (review NanoClaw du 2026-10-24T01:02:50Z et ACK Hermes du 02:26:33Z) : le correctif errexit est au commit 08675eb, Hermes l'a rejoué de zéro avec son témoin inverse, et les trois points de détail restants sont reportés dans l'issue de suivi #22278, ouverte à 01:46:21Z avec son critère de clôture. Je lève aussi la réserve de jsboige (réponse de la lane myia-po-2025:CoursIA, qui cite le verdict de corps pour y répondre) : elle décrit la chaîne de résolution ci-dessus, pas un nouveau point. Vérifié à la tête 67ffd7b : le patch propre à la PR se limite à .github/workflows/pr-gate-stale-sweep.yml et scripts/tests/test_stale_sweep_noop_verdict.py, et le workflow est identique à 08675eb. Le commit de tête est une fusion de main.

@myia-ai-01
myia-ai-01 merged commit 2e24aca into main Oct 25, 2026
27 of 34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-overlap Advisory: another open PR touches the same files (organ #13615)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants