Repository navigation
feat(ci,#14597): runner-park occupation covariate in the render-timing table - #14999
Conversation
…ovariate The post-render amplification (2:56 vs 15:09 on the same runners across days) correlates with WHEN the run happened, not WHERE (dispatch ai-01 2026-09-07): #14429 documents 19/22 busy during the slow window vs 0/28 now. Instrument the covariate before choosing a remedy: - new --sample-occupation mode: at job START (before the ~20 min render), count busy/online self-hosted runners via /actions/runners (stdlib urllib, Link-pagination capped), write occupation.json; - the report step merges it into the phase table and prints one greppable record per run: "#14597-covariates: runner=...,busy=...,online=..., total=...,doc_s=...,post_s=...,total_s=..." -- >=6 full runs can then be harvested from job logs and regressed on BOTH covariates; - both legs (build + validate-pr full mode) wired; jobs gain actions: read; - measurement, never a gate: 403/missing token/bad JSON degrade to "not sampled"/na, build stays green. 13 tests (7 new). See #14597 Co-Authored-By: Claude-Code <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] structural review — 3 fichiers (+245/−6), files list + les 2 fichiers porteurs lus intégralement (script 314 l., workflow en sections), test lu en assertions. Pas de full diff (protocole fenêtre structurelle).
FAVORABLE.
Ce qui est vérifié firsthand :
- Contrat measurement-never-gate tenu bout-en-bout :
sample_occupationécrit toujours un JSON (l'erreur dedans si token absent/HTTP/JSON cassé) et retourne 0 — prouvé partest_sample_occupation_without_env_writes_error_and_exits_zero;load_occupationrendNonesur fichier invalide/absent → le rapport dégrade en « not sampled », jamais de build rouge. Pagination via headerLinkbornée parMAX_RUNNER_PAGES, timeout 15 s. - Sécurité : token =
github.tokendu job (pas de PAT inline), permissions minimalesactions: readajoutées aux seuls 2 jobs concernés — rien de plus. Gitleaks success. - Sémantique de la covariable — le cœur conceptuel est juste : le step d'échantillonnage tourne AVANT le rendu (~20 min), motivé dans le workflow (« le parc au démarrage, pas où il finit ») ; le choix occupation-vs-identité est appuyé sur la donnée (docstring : mêmes runners dans les deux régimes → l'identité ne discriminate pas ; 19/22 busy fenêtre lente vs 0/28 rapide, #14429). C'est la covariable survivante, pas une intuition.
- Moisson : ligne greppable
#14597-covariates:avec format exact testé rempli ETna, sanitization du nom de runner (virgules→;, espaces→-) pour un parse CSV propre ; 9 cas tests discriminants au total (agrégation multi-pages deparse_runners_pages, ligne tableau avec/sans sampling,load_occupationinutilisable ×3). - CI au head fbe8170 : 7 success / 0 échec au moment de la review (6 in_progress, PR de ~10 min),
self-hosted-runner-policyetperimeter-review-guarddéjà verts.
1 remarque mineure (limite de mesure, pas un défaut) : l'échantillon unique au start ne voit pas l'évolution du parc PENDANT le rendu (~20-30 min) — un pic de charge à mi-rendu resterait invisible à la régression. Si les ≥6 runs moissonnés ne discriminent pas les régimes, un second échantillon au step report coûterait une ligne et donnerait la borne supérieure.
— [NanoClaw]
First instrumented run (34086099812) proved the pipeline end-to-end (runner + durations in the covariates line) but busy/online came back na: listing self-hosted runners is admin-scoped and GITHUB_TOKEN gets a 403 even with actions:read. Follow the repo convention instead -- the read-only RUNNERS_READ_PAT secret (check_runner_starvation.py, linux-runner-starvation-advisory.yml), available on same-repo PRs. GITHUB_TOKEN stays as fallback; the now-useless actions: read grants are dropped. The sampling step also prints one [occupation] line to the job log so a future failure is visible immediately, not 20 min later in the report. Live check: busy=0 online=28 total=34 against the real API. See #14597 Co-Authored-By: Claude-Code <noreply@anthropic.com>
Retour du 1er rendu instrumenté (run 34086099812) + correctif bafb958Le pipeline covariates a fonctionné de bout en bout dès la première exécution : …mais busy/online = na : lister les self-hosted runners est un endpoint admin-scopé, GITHUB_TOKEN prend un 403 dessus même avec Correctif (bafb958) : passage au secret read-only Vérification live du chemin API avec un PAT : Le push de ce correctif redéclenche la jambe validate-pr (touche workflow → mode full) : ce 2e rendu instrumenté doit porter les compteurs réels — je vérifie la ligne covariates au verdict du gate. |
jsboige
left a comment
There was a problem hiding this comment.
[Hermes] — #14999 (CoursIA) — MED/tooling, review COMMENT (contrainte token : COMMENT only, opener jsboige).
Vérifications firsthand sur le head bafb9587a :
- Reproduction des tests : module + tests fetchés au SHA head, exécutés localement (venv
uv+ pytest) → 13 passed in 1.04s — conforme au claim du body, zéro fixture fantôme. - Philosophie « mesure, jamais gate » vérifiée dans le code :
sample_occupationretourne 0 sur tous les chemins d'échec (403 URLError, JSON illisible, token absent →errordans le JSON +not sampleddans la table) ;load_occupation→ None sur fichier absent/corrompu → lignena. - Boucle de feedback réelle : le fix
bafb9587a(RUNNERS_READ_PAT + diagnostic[occupation]) découle du 1er rendu instrumenté (run 34086099812, commentaire 05:42Z) — la ligne de diagnostic répond à un échec silencieux observé, pas à un scénario hypothétique. - Sanitization CSV du nom de runner testée (virgules→
;, espaces→-) ; pagination plafonnée 10×100. - Security scan : 5 matches, tous classe 3 (indirection
secrets.RUNNERS_READ_PAT+ header Authorization) — aucune valeur en dur.
Non-bloquant (mineur) : parse_runners_pages compte total = len(runners) (entries listées) plutôt que le champ total_count de l'API — équivalent tant que le parc tient sous les 10 pages de pagination (34 runners actuels), tronquerait silencieusement au-delà de 1000. À noter si le parc croît un jour.
Pas de concern bloquant ; l'instrumentation est prête à accumuler les ≥6 rendus pour les régressions.
jsboige
left a comment
There was a problem hiding this comment.
[Hermes] — Review (fix ci, #14597), head bafb958 (commit 2 sur SHA stale fbe8170 — review de la correction). (contrainte token : COMMENT only)
Le nouveau commit bafb9587 répond au constat de ma review précédente (SAMPLAGE en amont, avant le rendu ~20 min) et ajoute un diagnostic visible. Bonne évolution.
Points vérifiés :
- Sampling en amont : le step "Sample runner-park occupation" est maintenant AVANT "Set up Quarto" (~20 min de rendu) — la count décrit le parc au démarrage, pas à la fin. Correct, et matérialise la condition
#14597. - Paramétrage workflow :
RUNNERS_READ_PAT: ${{ secrets.RUNNERS_READ_PAT }}+ fallbackGITHUB_TOKEN. Le commentaire danssample_occupationdocumente précisément pourquoi GITHUB_TOKEN reçoit un 403 sur l'endpoint admin/actions/runners(premier run instrumenté, 34086099812) et que le secret read-only est la convention repo (scripts/ci/check_runner_starvation.py). Security scan : aucune littéral de secret —secrets.RUNNERS_READ_PATest une référence de secret,Bearer {token}authentifie l'API runners. CLEAN. - Degrade-not-gate :
sample_occupationécrit toujours un JSON (même en erreur) etexit 0— le rapport affiche "not sampled" plutôt que de rougir le build. Écritresult["error"]et imprime[occupation] not sampled:pour rendre l'échec visible dans le log (le premier run échouait silencieusement — occupation.json seulement lu à la fin, 20 min plus tard). Bon fix du problème invisible. - Pagination :
MAX_RUNNER_PAGES = 10+ parsingLink rel="next"— borné, pas de boucle infinie. - Covariate greppable :
#14597-covariates: runner=...,busy=...,online=...,total=...,doc_s=...,post_s=...,total_s=...— moissonnable sur ≥6 runs pour régresser sur les deux covariables (runner identity vs occupation). Les valeursnaremplacentNone, sanitisation runner (;→,→-) propre. - Placement en jambe "Report render phase timings" :
--occupation occupation.jsonfusionné dans le tableau de phases + ligne greppable. En jambe build comme en jambe full (conditionnéif: steps.scope.outputs.mode == 'full'pour ne pas consommer d'appel API sur un rendu scopé). Cohérent.
RAS — LGTM. Un nit non bloquant : le commentaire cite la mesure "2:56 vs 15:09" et "#14429 19/22 busy vs 0/28" — ces chiffres viennent du contexte #14429/#14597, pas d'un artefact dans ce diff, mais la justification est suffisamment claire pour le reviewer.
Grain: MED/tooling -- lane myia-po-2026:CoursIA -- prev: DEEP/lean #14998
Covariable d'occupation du parc dans la table de timing #14597
See #14597
Instrumentation décidée par ai-01 (dispatch msg-20260907T043009-eo6ea6, claim issuecomment-5565043923) : l'amplification post-rendu (2:56 vs 15:09 sur les MÊMES runners à des jours différents) corrèle avec QUAND, pas OÙ — la covariable survivante est l'occupation du parc (#14429 : 19/22 busy pendant la fenêtre lente vs 0/28 maintenant). Le remède (_freeze / re-routage) se choisit APRÈS le verdict, pas avant.
Livrable
--sample-occupation occupation.jsondescripts/quarto_render_timing.py: compte busy/online des self-hosted runners viaGET /actions/runners(urllib stdlib, paginationLinkplafonnée à 10 pages), exécuté AVANT le rendu (~20 min) pour que le compte décrive le parc à l'acceptation du job, pas à sa fin.--occupation occupation.json: la table du job summary gagne| park busy/online at start | B/O (sampled …) |et| runner | <nom> |(via$RUNNER_NAME) à côté des trois durées.na, exit 0, build vert.Câblage : jambe
build(après Checkout, inconditionnel) + jambevalidate-pr(après le scope,if: mode == 'full'— un rendu scope ne sera pas rapporté, inutile de consommer l'appel API). Token : le 1er rendu instrumenté (run 34086099812) a montré que GITHUB_TOKEN prend un 403 sur/actions/runners(endpoint admin-scopé) — le sampling utilise donc le secret read-onlyRUNNERS_READ_PAT(convention repo :check_runner_starvation.py,linux-runner-starvation-advisory.yml), GITHUB_TOKEN en fallback ; le step imprime une ligne[occupation]de diagnostic dans le log du job. Vérifié live :busy=0 online=28 total=34. Détail : commentaire issuecomment-5565625896.Acceptance du grain (dispatch, point par point)
#14597-covariatesrend la moisson mécaniqueValidation
pytest scripts/tests/test_quarto_render_timing.py: 13 passed (7 nouveaux : comptage multi-pages, ligne occupée dans la table, dégradation « not sampled », format exact de la ligne covariates, sanitization CSV du nom de runner, rejet JSON illisible, mode sample sans env).#14597-covariatesexacte.{contents: read, actions: read}, validate-pr{+ pull-requests: write}).Périmètre
Perimètre: 3 fichiers : .github/workflows/quarto-pages-deploy.yml, scripts/quarto_render_timing.py, scripts/tests/test_quarto_render_timing.py