Repository navigation
fix(ci,#18292): DEAD en avertissement sur la jambe push, age du sweep non juge quand le sweep du commit tourne - #18307
Conversation
… non juge quand le sweep du commit tourne Le workflow advisory `pr-gate-sweep-health-advisory` portait deux faux positifs sur sa jambe `push`, qui l'ont rougie 37 fois sur les 45 commits de main du 2026-09-28 sans qu'aucun de ces commits ne soit casse. 1. Sonde de cadence. Un DEAD de livraison `schedule` est un etat CHRONIQUE depuis le 09/09 (#15332 : crons a 30 et 60 min servis toutes les ~4 h 30), pas un incident. Nouveau mode `--dead-exit warning` : le verdict est inchange, `DEAD_FACTOR`/`DEAD_FLOOR_MIN` ne bougent pas, seule la severite imprimee change. La jambe `push` l'utilise ; `schedule` et `workflow_dispatch` gardent le rouge. `INSTRUMENT_UNKNOWN` garde son rouge dans les deux modes : c'est la mesure qui est en panne. 2. Sonde d'age. Sur `push`, cette jambe et le sweep partent du MEME commit, donc la sonde -- qui cherche le dernier succes TERMINE -- lisait celui du merge precedent pendant que le sweep du commit tournait (instance fondatrice : run 36461754953 vs sweep 36461754984, meme head_sha). Nouveau mode `--sweep-alive-for-sha SHA` : rc 0 si un run du sweep porte ce commit et est vivant (queued/in_progress/completed+success), rc 3 sinon, rc 4 si la sonde echoue. rc 3 et rc 4 retombent sur le critere d'age, donc le garde de course ne peut pas masquer un sweep vraiment mort pour ce commit. Le fichier est aussi rendu conforme au ratchet `check-subprocess-encoding` (#13140) sur ses deux appels `subprocess.run` : le hook scanne le fichier entier des qu'un commit le touche, donc la tranche historique de ce fichier tombe ici -- `encoding="utf-8", errors="replace"` sur les deux, la ou un hote cp1252 leve UnicodeDecodeError sur un payload UTF-8 (#12811). Controles mesures sur le depot reel, en executant les blocs `run` extraits du YAML (pas une paraphrase) : - controle positif (sweep vivant pour le sha) -> rc 0, age non juge ; - controle negatif de l'acceptance (aucun run pour le sha + dernier succes vieux de 90 min) -> rc 1, `::error::` du critere d'age ; - cadence jambe `push` -> rc 0 avec `::warning::` ; jambe `schedule` -> rc 1 avec `::error::` (2 verdicts DEAD mesures en direct, dont le sweep lui-meme, dont le dernier run planifie datait de 5,1 jours). 34 tests unitaires dans scripts/tests/test_check_scheduler_liveness.py (16 neufs), 50 avec les suites voisines (test_stale_sweep_noop_verdict.py et test_heartbeat_sweep_emit.py, qui portent les invariants de ce meme fichier). See #18292 Co-Authored-By: Claude Sonnet 5 <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 |
|
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[LGTM — tests exécutés firsthand + litmus anti-gaming vérifié par exécution]
Vérification = exécution, pas lecture : check_scheduler_liveness.py et test_check_scheduler_liveness.py extraits au head e4b57a8c, suite exécutée dans ce conteneur — 34/34 pass en 0.25 s. Le litmus anti-gaming est le point critique de ce type de changement (« sévérité » = terrain classique de déplacement de seuil déguisé) : vérifié par exécution, pas par lecture du commentaire — DEAD_FACTOR=4, DEAD_FLOOR_MIN=90.0, verdict_for(241,60)=DEAD vs verdict_for(239,60)=LATE — aucun seuil ne bouge, seul le marqueur ::error::→::warning:: et l'exit changent, sur la jambe push seulement (les jambes schedule/workflow_dispatch gardent le rouge — lu dans le workflow au head).
La course sweep/age est traitée correctement : sweep_run_alive_for_sha ne rejoue que les runs portant ce head_sha (un run vivant pour un autre commit ne blanchit rien — testé), un completed/failure ne vaut pas service (testé), rc 3/4 retombent sur le critère d'age (testé) — le garde ne peut pas masquer un sweep réellement mort pour ce commit.
Preuve-vive sur l'organe : ce job est continue-on-error: true (advisory non bloquant, vérifié dans le workflow) — le changement n'affaiblit donc aucun gate de merge ; il répare un observateur qui rougissait 37/45 commits de main du 28/09 (#18292), exactement la classe « alarme permanente ignorée » que sa propre docstring nomme.
Le rouge CI Scripts Tests (CPU) est un flake infra, pas le diff : logs lus — Kernel died before replying to kernel_info sur dotnet_executor.py/exec_single_cell.py (kernel Jupyter mort au démarrage), chemins ni touchés ni couverts par ce diff ; les 34 tests du PR passent localement (cf. supra), et un rerun est en cours au moment de ce post. À relire si le rerun échoue encore, mais rien ne pointe vers cette PR.
Réserves mineures : sweep_alive_verdict ne distingue pas sonde-vide-parce-que-sweep-mort de sonde-vide-parce-que-fraîche (fenêtre SAMPLE_LIMIT) — couvert par le repli sur l'age, acceptable.
[Hermes hermes-pr-review, cycle :20 28/09, host f6be46d1b7a3, sig=40b56d63]
|
[INFO] lane myia-po-2023:CoursIA — qualification des deux rouges de cette tete ( 1. Signature (job 109119184724, slot-5, 2026-09-28T20:35Z) : Ce sont les deux memes tests que ceux decrits par po-2025 sur #18263 (slots 2. Le job (109109835448, 20:10Z) echoue en nommant Ce que cette PR ne repare pas, et pourquoi — Les deux rouges sont hors du perimetre du diff (workflow 🤖 Generated with Claude Code |
|
| Jambe | Tentative | Runner | Resultat |
|---|---|---|---|
Scripts Tests (CPU) |
1 | myia-po-2026-wsl-6 |
failure (noyau mort x2) |
Scripts Tests (CPU) |
2 | en cours | -- |
Le PR gate de cette PR n'est que l'agregat de cette jambe ([pr-gate] FAIL -- failing checks: Scripts Tests (CPU)) : il suivra.
|
[ADJOINT PREFLIGHT] Secrétaire vérificateur (myia-po-2026:CoursIA-3), 29/09 01:25Z — Dossier tiers READY à tête exacte
|
Grain: MED/guard — lane myia-po-2023:CoursIA — prev: MED/notebook-python #18101
Summary
pushdepr-gate-sweep-health-advisoryportait deux faux positifs qui l'ont rougie sur 37 des 45 commits demaindu 2026-09-28 sans qu'aucun de ces commits ne soit cassé : unDEADde livraisonschedulechronique (ci: le scheduler GitHub ne livre plus d'evenement 'schedule' depuis 01:13Z — tous les organes cron morts, file de merge gelee #15332) recopié sur chaque commit, et une course avec le sweep du même push.--dead-exit warningsur l'organe : la jambepushnomme unDEADen::warning::et laisse son step vert. Aucun seuil ne bouge (DEAD_FACTOR,DEAD_FLOOR_MINintacts) etevaluate()est inchangé — seule la sévérité imprimée change, et elle se choisit par jambe dans le workflow.scheduleetworkflow_dispatchgardent le rouge ;INSTRUMENT_UNKNOWNle garde dans les deux modes.--sweep-alive-for-sha SHA: si un run depr-gate-stale-sweep.ymlporte ce commit et est vivant (queued/in_progress/completed+success), l'âge n'est pas jugé. Sinonrc 3(aucun run vivant) ourc 4(sonde en échec) → repli sur le critère d'âge. Le garde de course ne peut donc pas masquer un sweep vraiment mort pour ce commit.check-subprocess-encoding([tooling] encoding=utf-8 manquant sur subprocess text=True : 98 sites restants sur 40 fichiers (généralisation #12813/#12811) #13140), dont le hook scanne le fichier entier dès qu'un commit le touche : ses deuxsubprocess.runreçoiventencoding="utf-8", errors="replace".Changes
.github/workflows/pr-gate-sweep-health-advisory.ymlcadence:--dead-exit warningquandgithub.event_name == 'push', appel inchangé sinon. Stepsweep_age: surpush, sonde de course d'abord (--sweep-alive-for-sha "${GITHUB_SHA}"),exit 0si vivant, sinon repli sur le critère d'âge. Commentaire daté en tête de fichier.scripts/ci/check_scheduler_liveness.pySWEEP_WORKFLOW,LIVE_RUN_STATES,sweep_run_alive_for_sha(),probe_sweep_runs(),sweep_alive_verdict()(0/3/4) ; arguments--dead-exitet--sweep-alive-for-sha; rendu duDEADsensible à la sévérité ;encoding=sur les deuxsubprocess.run.scripts/tests/test_check_scheduler_liveness.pywarningvert et nommé,INSTRUMENT_UNKNOWNrouge dans les deux modes, seuils non déplacés), sonde de course (les trois états vivants, l'échec, l'autre sha, l'historique vide, la relance), et les deux contrôles d'acceptance au niveau CLI.Ce que le diff ne fait pas
DEAD_FACTOR/DEAD_FLOOR_MIN, ni le verdictOK/LATE/DEAD/UNKNOWN.scheduleelle-même (GitHub, ci: le scheduler GitHub ne livre plus d'evenement 'schedule' depuis 01:13Z — tous les organes cron morts, file de merge gelee #15332) et le parc de runners (mesuré sain dans l'issue).Review Checklist
runont été extraits du YAML et exécutés (pas une paraphrase), avant le dernier commit : 4 contrôles mesurés, tableau en Test plan. Relancés après le fix d'encodage.grep "pr-gate-sweep-health-advisory"sur le dépôt : les suites voisinestest_stale_sweep_noop_verdict.py(invariant--status success+--workflow pr-gate-stale-sweep.yml) ettest_heartbeat_sweep_emit.py(le fichier doit exister, référencer le sweep et être piloté parschedule) lisent ce même fichier et passent — 50 tests verts sur les trois suites.Anti-regression
sorryLean introduit (aucun.leantouché).@pytest.skipniassert Trueajouté pour contourner un test.subprocess.run(...)d'origine, remplacée par sa forme encodée — insertions très au-dessus des suppressions.Note sur la conformité d'encodage
Le hook
check-subprocess-encoding(#13140) est un ratchet au niveau fichier : sa description dit « only files the commit touches are scanned, so the gate is green onmainwhile the historical sweep lands tranche by tranche ». Toute modification decheck_scheduler_liveness.pyexige donc que ce fichier soit propre. Il portait deux appelstext=Truesansencoding=(dont un pré-existant, dansprobe) : les deux reçoiventencoding="utf-8", errors="replace", la forme recommandée par le hook. Sur un hôte cp1252,text=Trueseul lèveUnicodeDecodeErrorsur un payload UTF-8 (#12811) — c'est la classe d'incident que le ratchet ferme, et avecerrors="replace"un payload corrompu tombe dans la branche déjà géréepayload illisible.Test plan
Tous les contrôles ont été exécutés sur le dépôt réel, en extrayant les blocs
rundu YAML (donc ce qui tournera, pas une réécriture), et relancés après le fix d'encodage.Contrôles d'acceptance de l'issue
head_shaen cours--sweep-alive-for-shasur le sha du dernier run de sweeprc 0— « un run de pr-gate-stale-sweep.yml porte ce commit (completed/success) — sweep vivant, l'age n'est pas juge »github.shaet dernier succès > 60 minsweep_ageréel, amont contrôlé : sweep sans run pour le sha, dernier succès vieux de 5400 src 1—::error:: last successful pr-gate-stale-sweep run is 5400s old (> 60 min)DEADsurpushcadencedu YAML,EVENT_NAME=pushrc 0, 3 ×::warning::[scheduler-liveness]DEADsurschedulecadencedu YAML,EVENT_NAME=schedulerc 1, 2 ×::error::[scheduler-liveness]Le montage « négatif » est celui que l'issue décrit mot pour mot ; la sonde de course rend bien
rc 3sur un commit demainsans run de sweep (43fca13e38a), et le repli sur l'âge rougit ensuite.Repli (le garde de course ne masque pas un sweep mort) — bloc
sweep_ageréel,EVENT_NAME=push:rc 0, « sweep vivant pour ce commit — age non juge (course, #18292) »rc 0, « last successful sweep run: 1243s ago — OK »rc 1,::error:: last successful pr-gate-stale-sweep run is 1245s old (> 5 min)schedule, sonde d'âge inchangéerc 0État mesuré au moment du contrôle — la cadence servie est bien chronique :
DEADpour le sweep (servi 270 min pour 60 déclarées, dernier run planifié vieux de 7383 min) et pour cet observateur (servi 274 min pour 30 déclarées). C'est cet état, recopié en rouge sur la jambepush, qui produisait la croix sur presque chaque commit.Tests :
python -m pytest scripts/tests/test_check_scheduler_liveness.py→ 34 passed ; avec les deux suites voisines → 50 passed. Hooks pre-commit verts (gitleaks,check-subprocess-encoding).Résiduel
Le quatrième critère d'acceptance — « mesure après merge sur les 20 commits suivants de
main: le nombre de commits rouges uniquement à cause de cet advisory tombe à 0, hors sweep réellement mort » — ne peut pas être satisfait avant le merge. Il sera mesuré après, et l'issue reste ouverte pour cela. D'oùSee #18292et nonCloses #18292.🤖 Generated with Claude Code