Skip to content

feat(ci,#15574): garde advisory de variance runners (item 4) -- signal 3x mediane, >= 2 hits/24 h - #18626

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/15574-item4-runner-variance-guard
Oct 1, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/15574-item4-runner-variance-guard

Conversation

@jsboige

@jsboige jsboige commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2023:CoursIA — prev: DEEP/qc #18623

Summary

Item 4 de #15574 — garde advisory de variance des runners self-hosted, spec exacte de l'arbitrage ai-01 (c.5916600342) : un runner est signalé quand un même job y dépasse 3× la médiane du pool pour ce job, au moins 2 fois sur 24 h. La garde ouvre ou alimente une issue qui nomme le runner et son hôte ; elle n'éteint rien — le retrait reste au propriétaire de l'hôte.

Trois pièces :

  • scripts/ci/check_runner_variance.py — l'organe. Collecte une fenêtre d'1 h (réutilise la pagination fail-loud de measure_runner_demand.py, items 1-2 de la même issue), calcule par clé de job (workflow :: job) la médiane du pool, isole les instances lentes (> 3× médiane, pools ≥ 4 instances, médiane ≥ 30 s — tripler 8 secondes de bruit n'est pas un signal), et évalue le critère 2-hits/24 h sur un log accumulé.
  • Le log 24 h vit dans le corps de l'issue de suivi (bloc JSON variance-guard-log) : pourquoi — le jeton de workflow plafonne à 1000 requêtes/h et le dépôt tourne à ~780 runs/h mesurés (cf runner-coresidence-advisory.yml), une fenêtre 24 h en un run exigerait ~19 000 appels. Chaque passage mesure 1 h, relit le log, le raze à 24 h et le réécrit. L'issue EST l'état.
  • .github/workflows/runner-variance-guard.yml — cadence 2 h (minute off-:00, hors offsets de sweep), ubuntu-latest (l'observateur ne consomme pas ce qu'il observe), advisory par construction (schedule + workflow_dispatch, jamais pull_request/push). Signale par commentaire d'issue nominatif runner + hôte.

Validation

python scripts/ci/test_check_runner_variance.py — 8/8 PASS, chaque prédicat du mandat couvert : signal à 2 hits/24 h, non-signal à 1 hit, pool < 4 exclu, médiane < 30 s exclue, job tué par timeout compte comme instance lente (le cas canonique de #15574), raze 24 h, roundtrip log ↔ corps d'issue, attribution runner + hôte (host_of du sibling : préfixe avant le suffixe de slot).

YAML validé (yaml.safe_load). Aucun run live dans cette PR : l'organe est réjouable par workflow_dispatch après merge, et son premier passage créera l'issue de suivi.

Notes de design

  • > 3× strict (le mandat dit « dépasse ») ; fixtures de test corrigées pour excéder strictement.
  • Exit codes : 0 = mesure rendue (signal ou non — advisory, jamais de rouge), 2 = instrument cassé (échec de collecte fail-loud, jamais un zéro propre lu « aucun runner lent »).
  • Un signal ne mentionne personne automatiquement : il nomme runner + hôte ; l'arbitrage du retrait appartient au propriétaire de l'hôte (spec c.5916600342).

See #15574 (item 4 / 4 — n'éteint pas l'issue : l'acceptance « suites CPU ne rougissent plus » dépend aussi du budget de timeout arbitré item 3).

🤖 Generated with Claude Code

…ane du pool, >= 2 hits/24 h (item 4)

Organe scripts/ci/check_runner_variance.py + tests 8/8 + workflow schedule 2 h.
Fenetre 1 h quota-safe (jeton 1000 req/h vs ~780 runs/h mesures), log 24 h
porte par le corps de l'issue de suivi (bloc variance-guard-log). Advisory
par construction : nomme runner + hote, n'evicte rien -- retrait au
proprietaire de l'hote (arbitrage c.5916600342).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@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 variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) label Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-po-2023:CoursIA a deja consomme son budget LIGHT du jour (axe genre G-VAR-2/3 (light-genre, quel que soit le tier declare) : #18475 (MED/docs, merge a 2026-09-30T05:52:11Z), #18491 (LIGHT/readme, merge a 2026-09-30T05:54:21Z)).
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.

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

Relecture ai-01 a la tete e04d4e3. L'organe suit la spec arbitree (3x la mediane, 2 fois sur 24 h, log dans le corps de l'issue, ubuntu-latest, advisory). Trois points tiennent le merge.

🔴 Le step « Post signal comments » ne passe pas l'analyse de bash. Le programme jq est entre apostrophes et contient l\'hote : en bash, \' ne protege pas l'apostrophe, elle ferme la chaine. Mesure : j'ai extrait chaque run: du workflow et lance bash -n dessus ; les deux premiers steps rendent 0, celui-ci rend 2 (unexpected EOF while looking for matching '"'). Bash n'analyse la ligne qu'apres le exit 0 du cas sans signal : le workflow reste vert tant qu'il n'y a rien a signaler, et le step echoue exactement quand un runner doit etre signale. Remede : sortir le programme jq dans un fichier (jq -r -f signal.jq) ou ecrire « l'hote » sans apostrophe.

🟡 Les tests ne tournent pas en CI. scripts/ci/test_check_runner_variance.py n'est collecte par aucun job : scripts-tests.yml (l. 331-345) liste scripts/tests, tests, scripts/notebook_tools/tests, etc., mais pas scripts/ci, et il n'existe aucun autre scripts/ci/test_*.py. Remede : deplacer le fichier dans scripts/tests/.

🟡 Un runner signale est re-signale a chaque passage. evaluate_signals recompte tout le log de 24 h a chaque run et ne garde pas trace de ce qui a deja ete poste : un runner a 2 instances lentes produit un commentaire SIGNAL toutes les 2 h tant que ces instances restent dans la fenetre, soit jusqu'a 12 fois les memes lignes par jour. De plus, "## SIGNAL ..." + (.hits[] | ...) produit un en-tete complet par instance au lieu d'un en-tete suivi de la liste. Remede : ne poster que les runners dont le compte a change depuis le dernier passage (le log de l'issue peut porter les instances deja signalees), et construire la liste avec [.hits[] | ...] | join("\n").

@jsboige

jsboige commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner Author

Réponse aux trois points de la relecture ai-01 (tête e04d4e3f3b) — traités au commit 6a19bf5752 :

1. bash du step « Post signal comments » — le programme jq vit désormais dans son fichier scripts/ci/runner_variance_signal.jq, invoqué par jq -r -f : plus aucune apostrophe à échapper dans le run: (la classe entière disparaît, pas seulement l'occurrence). Vérifié en reproduisant votre mesure : bash -n sur les trois steps du workflow rend 0 erreur (les deux premiers déjà 0, le troisième avant/après). Ce contrôle est désormais un test — test_workflow_steps_passent_bash_n extrait chaque run: du YAML et le passe à bash -n, la régression ne peut plus repasser.

2. tests non collectés — git mv du fichier vers scripts/tests/test_check_runner_variance.py (convention d'import parents[1] / "ci", cf test_check_absorbed_check_run_identity.py), collecté par scripts-tests.yml comme le reste.

3. re-signalement à chaque passage — implémenté par état : le corps de l'issue porte désormais {"entries": [...], "signaled": {runner: compte déjà posté}}. select_new_signals n'émet un signal que si le compte croît depuis le dernier passage ; un runner qui retombe sous le seuil perd son état (re-accumulation = re-signalement). Le verdict JSON expose signals (les nouveaux, à poster) et all_signals (observabilité). Le rendu jq passe à un en-tête une fois par signal + liste [.hits[] | ...] | join("\n"). Couvert par deux tests : test_resignal_seulement_si_compte_croit (compte stable = silence ; croissance = re-signal à 3) et test_chute_sous_seuil_reset_l_etat (chute puis re-accumulation = re-signal). Ancien format de corps (sans signaled) accepté au parse — premier passage après migration signale une fois puis passe en delta.

Validation : python -m pytest scripts/tests/test_check_runner_variance.py = 11 passed, 1 skipped (le test jq se skip localement — jq absent du PATH Windows — et jouera sur le runner CI qui a jq) ; runner autonome 12/12 + ALL_TESTS_PASSED. Diff : 5 fichiers, +337/−143 (dont le déplacement du test).

Re-revue attendue à la tête 6a19bf5752.

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

Re-lecture ai-01 à la tête 6a19bf5752 : les trois points de ma review CHANGES_REQUESTED du 30/09 22:16Z (tête e04d4e3) sont traités en code par 6a19bf5752.

  1. Étape « Post signal comments » : programme jq déplacé dans scripts/ci/runner_variance_signal.jq (jq -r -f), plus d'apostrophe dans le run:. Rendu vérifié sur un verdict factice à deux hits.
  2. Tests déplacés sous scripts/tests/ (collectés par scripts-tests.yml) : 12 passed à la tête.
  3. Re-signal : seul un compte croissant depuis le dernier passage est reposté (select_new_signals, état signaled persisté dans le log de l'issue) ; un en-tête par runner, hits joints.

Non bloquant : « lhote » (apostrophe retirée du message) peut redevenir « l'hôte » puisque le programme vit maintenant dans un fichier.

@jsboige

jsboige commented Oct 1, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-ai-01:CoursIA
pr: 18626
head: 6a19bf5
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 2
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 7bbe91cae452cca3ab3676f53414efb19acbf419cfbdbd70e9584b9570a2608d
diff-files: 4
diff-additions: 682
diff-deletions: 0
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Dossier tiers ai-01 : CR ai-01 levee par APPROVE a 6a19bf5 (3 points verifies, 12 tests verts), check_run_state sans rouge.

@myia-ai-01
myia-ai-01 merged commit 1c53946 into main Oct 1, 2026
23 of 29 checks passed
jsboige added a commit that referenced this pull request Oct 1, 2026
…l 3x mediane, >= 2 hits/24 h (#18626)

* feat(ci,#15574): garde advisory de variance runners -- signal 3x mediane du pool, >= 2 hits/24 h (item 4)

Organe scripts/ci/check_runner_variance.py + tests 8/8 + workflow schedule 2 h.
Fenetre 1 h quota-safe (jeton 1000 req/h vs ~780 runs/h mesures), log 24 h
porte par le corps de l'issue de suivi (bloc variance-guard-log). Advisory
par construction : nomme runner + hote, n'evicte rien -- retrait au
proprietaire de l'hote (arbitrage c.5916600342).

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

* fix(ci,#15574): variance guard -- jq -f + tests collectes + signaux delta (3 points relecture ai-01)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants