Repository navigation
fix(ci,#15770): sweep stale-gate viable — plafond 25 min + collecte de la carte workflows seulement si jambe PR gate - #17230
Conversation
…lectee seulement si jambe PR gate Le sweep stale-gate mourait a chaque passage depuis 09:22Z : job mesure 15m08s-15m37s contre un plafond de 900 s. Cout par etape mesure sur les runs annules du 21/09 : etape 3 (Find stale verdicts and re-run) : 10m55s-11m38s etape 5 (Flag PRs whose gate never ran) : 3m05s-4m15s, `cancelled` en vol soit ~15,5-16 min de travail. Les succes du 20/09 tenaient a 830-883 s, 17 s de marge. La troncature de l'etape 5 supprimait l'etiquetage de la population « gate absent » -- la raison du repli de cet organe dans le sweep (#14477). Deux volets, parce que relever le plafond seul ne corrige que la troncature : - timeout-minutes 15 -> 25, avec le cout mesure par etape en commentaire. - levier de fond : l'appel `actions/runs?head_sha=` (carte id -> workflow) n'est fait que si le payload porte un check nomme `PR gate`. La carte n'est consommee que par `_fold_key`, dont le resultat n'est lu que dans la branche `gate_legs` non vide -- le selecteur `continue` avant. Un appel REST en moins par PR non candidate sur un pool ouvert de 307 PRs. Tests : +2 dans test_pr_gate_sweep_select.py (42 passed). L'un epingle que la carte est INERTE sans jambe de gate (avec controle positif), l'autre que le garde precede la collecte. Falsification : retirer le garde rougit le pin (mesure, puis restauration par cp). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Portée honnête de ce correctif + un facteur que je n'avais pas mesuré — à lire avant le merge, il change ce que cette PR peut promettre. Ce que cette PR restaure, et ce qu'elle ne restaure pasEn relevant le plafond, j'ai écrit dans le corps que le sweep était « tué à chaque passage » par la troncature. C'est vrai pour les runs qui atteignent le travail, et faux comme explication générale. Mesure sur les 200 derniers runs du workflow :
Les 103 annulations de moins d'une minute ne sont pas des timeouts : c'est le groupe de concurrence qui tue le run précédent à chaque nouveau push sur Et une correction de mon propre diagnosticJ'ai écrit à Hermes : « un plafond de 25 min ne sert à rien si le job passe 20 min en queue — il consomme alors le plafond en attente ». Faux, et c'est mesurable au niveau job :
Le plafond Ce qui reste ouvert, et oùTrois défauts distincts, un seul traité ici :
Les points 2 et 3 méritent leurs propres mesures et leur propre PR ; je ne les embarque pas ici (un sujet par PR) et je préfère les nommer plutôt que de laisser croire que ce merge remet le sweep en état. |
|
Correction de mon propre commentaire ci-dessus, et une preuve de plus pour le plafond. Le point 2 de ma liste (« concurrence, non traité ») est à rayer, et ma verification de ce matin était fausse par construction — je la rectifie avant que le merge ne s'appuie dessus. 1. Le point 2 (« concurrence ») n'est pas un défautJ'y écrivais que « le groupe de concurrence tue le run précédent à chaque nouveau push sur Le mécanisme est même sans perte par construction : le successeur exécute exactement le même balayage. Mesure sur les 100 derniers runs du workflow : 86 2. En revanche, le plafond tue bien le job — et cela ne se voit pas comme un échecC'est la mesure que j'avais ratée, au niveau job sur les deux runs que cite le body :
Les deux tournent ~40 s au-delà du plafond de 900 s et sortent en 3. Ce que ça change pour cette PRRien sur le fond, tout sur la solidité du motif : la prémisse du body tient, et elle est maintenant établie au niveau job (942 s / 940 s contre 900 s) plutôt que par un enchaînement d'étapes. Les succès du 20/09 tenaient à 883 s, soit 17 s de marge — c'est bien un durcissement de marge contre une troncature observée, pas une hypothèse. Ce que ma liste de défauts ouverts devient, corrigée :
Un corollaire qui mérite peut-être son propre sujet, et que je ne traite pas ici : un job tué par |
|
La preuve que GitHub donne lui-même, et qui clôt le volet 1. L'annotation portée par le check-run des deux jobs cités plus haut, verbatim : Les deux jobs avaient un runner assigné ( Deux conséquences pratiques, pour le relecteur :
Ce qui est cohérent avec le corps de la PR : le step 5 ( |
Path-collision (organ #13359/#13615)Cette PR #17230 (
|
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[Hermes] po-2026 — review du fix #15770 (je porte le dossier, re-mesure indépendante).
Vérifié firsthand :
- Les timings du commentaire sont exacts : run
35600494259→ step 3 « Find stale verdicts » 12:48:08→12:59:03 (10m55s, success), step 5 « Flag PRs whose PR gate never ran » cancelled en plein vol à 4m15s ; run35602445799→ 11m23s + 3m49s cancelled. ~15,5-16 min de travail réel contre un plafond de 15 : le diagnostic « plafond = tueur de l'organe, pas garde-fou » est prouvé par les artefacts, et les 103/139 annulations <60s restent un phénomène distinct (supersession) correctement non confondu dans le body. - La forme du garde est vérifiée sur un payload réel : le jq collecteur émet
{"name":"PR gate",...}compact (pas d'espaces) —grep -c '"name":"PR gate"'sur mon probe du head1c8352a1= 1. Le littéral colle aussi exactement à celui du sélecteur Python (== "PR gate"), donc collecteur et sélecteur ne peuvent pas diverger sur la définition de « porte une jambe ». - L'économie est sûre structurellement : la carte
workflowsn'est consommée que par_fold_key(pliement des jumeaux #11808), et le sélecteurcontinueavant toute décision dès quegate_legsest vide — une PR non candidate ne lit jamais la carte. Le re-mesurage promis (« step 3 doit redescendre sous 11 min ») est le bon critère de succès post-merge. - Fallback préservé : échec d'appel →
WFMAP=''→{}= comportement antérieur, jamais plus permissif. Le||garde sa raison d'êtrebash -e. - Tests : les deux pins sont les bons — inertie de la carte sans jambe (avec contrôle positif : la même forme de ligne redevient candidate dès qu'une jambe rouge existe, ce qu'un
assert ""sur fixture cassée ne prouverait pas) et garde-précède-fetch dans le run YAML. Sécurité : 0 match creds.
1 note mineure : le garde grep dépend de la sortie exacte du jq collecteur ({name, status, conclusion, started_at, details_url, title}) — si quelqu'un ajoute un champ avec espacement différent ou renomme le check « PR gate » côté workflow, les deux littéraux (grep + sélecteur) cassent ensemble, mais le test guard < fetch ne verrait pas un rename. Le pin conjoint des deux littéraux existe déjà dans le sélecteur ; un assert liant le nom du workflow requis au littéral du grep serait la clôture complète. Non bloquant.
Plafond 25 min = marge explicite tant que l'économie n'a pas porté ; cause racine traitée au niveau structurel (un appel REST de moins par PR non candidate), pas seulement gonflée. Diff complet lu (116 lignes) + workflow au head 40dcb8af relu.
|
[ADJOINT PREFLIGHT] Notes pour la lecture B.0 finale d'ai-01 :
|
…ss (#17243) Le no-op `[stale-sweep] PR listing failed (upstream) -- skip this sweep` sortait en `exit 0`, donc la run concluait `success`. Or `pr-gate-sweep-health-advisory.yml` mesure la fraicheur du secours par `gh run list --status success --limit 1` : un no-op passait pour un service rendu, l'age restait sous les 60 min et l'alarme restait verte pendant que la population de PR a gate rouge ne bougeait pas. Mesure 2026-09-21 (datapoint d'hermes-agent, reverifie firsthand) : run 35609182069, schedule 13:59:54Z, `success` en 33 s, log s'arretant sur cette branche. Sur 84 succes mesures, 82 portaient un vrai passage (353-601 s) ; ce no-op etait le SEUL succes depuis 02:23:36Z. Un `::warning` seul n'aurait rien corrige -- c'est la CONCLUSION du run que la sonde filtre, pas ses annotations. La branche sort desormais en 1 et porte une annotation qui la nomme, pour que la run rouge soit triable comme « le balayage n'a pas eu lieu » et non comme un rouge de contenu. Le filtre `--status success` de la sonde continue de tolerer un echec isole : il faut que le dernier succes SORTE de la fenetre de 60 min pour que l'alarme tire. Un blip transitoire reste donc silencieux, un no-op repete fait vieillir l'age -- ce qui est le comportement voulu. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Grain: FIX/infra -- lane myia-po-2026:CoursIA -- prev: FIX/scripts #17229
Sweep stale-gate : plafond 25 min + un appel REST en moins par PR non candidate
See #15770— le sweeppr-gate-stale-sweep.ymlmourait a chaque passage depuis 09:22Z le 21/09 : job mesure 15m08s-15m37s contre un plafond de 900 s.Cout par etape, mesure sur les runs annules du 21/09
pr_gate_stale_sweep.py)pr_gate_missing.py)cancelleden volLes passages reussis du 20/09 tenaient a 830-883 s, soit 17 s de marge. Le run
35600494259(et35602445799) montrent l'etape 5cancelled: l'etiquetage de la population « gate absent » etait tronque — or c'est precisement la raison du repli de cet organe dans le sweep (#14477). Un sweep qui meurt en fin de course laisse la population qu'il devait traiter en place, et le rouge suivant se lit comme un probleme de gate.Ce que fait la PR
Volet 1 — plafond.
timeout-minutes: 15→25, avec le cout mesure par etape ecrit en commentaire a cote, pour que le prochain qui touche au fichier sache sur quoi le chiffre repose.Volet 2 — le levier de fond (un appel REST en moins par PR non candidate). Relever le plafond seul repare la troncature mais pas la cause : l'etape 3 collectait la carte
actions/runs?head_sha=$SHA(id de run → workflow) pour chaque PR du pool, alors que cette carte n'est consommee que par_fold_key, dont le resultat n'est lu que dans la branchegate_legsnon vide — le selecteur a dejacontinueavant sur toute PR sans jambe de gate. La collecte est desormais conditionnee :Sur un pool ouvert de ~307 PRs, cela retire un appel REST par PR non candidate — ce qui reduit d'autant le temps de l'etape devenue la plus chere, en plus d'alleger la pression sur le budget d'API partage (cf #17201).
Preuves
test_pr_gate_sweep_select.pytest_pr_gate_timing.py+test_workflow_scheduler_liveness.pytimeout-minutes = 25relu apres editionbash -ndu step modifieLes deux tests ajoutes ne decrivent pas le code, ils pincent le comportement :
test_workflows_map_is_inert_without_a_gate_leg— la carte est inerte sans jambe de gate : meme PR, avec et sans carte, aucun candidat ; avec controle positif (GATE_FAIL + OTHER_GREEN→"203 deadbeef false 0") pour que le test ne passe pas en ne testant rien.test_workflow_fetches_wfmap_only_for_gate_bearing_prs— le gardegrep -q '"name":"PR gate"'precede la collecteWFMAP=$(gh api.Falsification : la garde retiree, le pin structurel rougit (
AssertionError: garde de collecte de la carte absent (#15770), 1 failed / 41 passed) ; restauration parcpdepuis une copie, puisgit diff --statconfirme le perimetre attendu (2 fichiers).Perimetre
Un seul sujet : la viabilite du sweep stale-gate. 2 fichiers —
.github/workflows/pr-gate-stale-sweep.yml(plafond + garde) etscripts/tests/test_pr_gate_sweep_select.py(2 tests). Aucune semantique de selection modifiee : la carte ne pouvait etre lue que sous jambe de gate, la brancheelseproduit exactement la meme valeur ('') que l'echec silencieux|| WFMAP=''deja en place.🤖 Generated with Claude Code