Skip to content

ci(#13378): tranche 2 -- router 5 gardes PR pure-Python vers la jambe Linux auto-hebergee (file 100+ sur ubuntu-latest, 7/8 slots libres) #14283

Description

@jsboige

Suite de la tranche 1 (#13378, decision ai-01 2026-09-01) qui a route 12 jobs de garde vers la jambe Linux auto-hebergee. Cette issue porte la tranche 2 et rien d'autre : elle ne touche ni le superviseur N-slots ni check_runner_starvation.py, qui restent la propriete de la lane myia-po-2024:CoursIA sous #13378.

Le constat qui la motive, mesure le 2026-09-02

Mandat user : « on veut consommer les runs du container Ubuntu, ce sont eux qui coutent, pas seulement les runs Windows », et « equilibrer la charge au mieux pour ne plus retomber dans une crise de CI ».

Trois mesures prises ce matin, dans cet ordre :

gh api repos/jsboige/CoursIA/actions/runners
  8 runners Linux auto-heberges ONLINE, 7 IDLE (1 busy)

gh api ".../actions/runs?status=queued"   -> 100 (plafond de page)
gh api ".../actions/runs?status=in_progress" -> 18

labels demandes par les jobs en file (echantillon 8 runs) :
  10 queued  ubuntu-latest
   0 queued  self-hosted

Chaque job en file demande ubuntu-latest. Aucun ne demande la jambe auto-hebergee, qui est libre a 7/8. Ce n'est pas un manque de capacite, c'est un defaut de routage : la file s'allonge sur la ressource facturee pendant que la ressource gratuite dort.

La cause est dans les fichiers :

grep -rhoE 'runs-on:.*' .github/workflows/*.yml | sort | uniq -c
  110  ubuntu-latest
   12  [self-hosted, coursia-ephemeral, coursia-linux]
    2  [self-hosted, coursia-ephemeral, coursia-fast-guards]

Perimetre : le meme barreau que la tranche 1, applique a 5 fichiers de plus

La tranche 1 avait pose un critere d'eligibilite volontairement conservateur — garde Python pur, sans secret ni usage du GITHUB_TOKEN, sans action tierce, sans docker. Cette tranche ne le deplace pas : elle l'applique aux fichiers restants qui le satisfont deja.

workflow runs / 7 j eligibilite
scripts-tests.yml 36 checkout + setup-python seuls
notebook-validation.yml 21 checkout + setup-python seuls
markdown-rendering-guard.yml 20 checkout + setup-python seuls
bare-cross-dir-load-gate.yml 18 checkout + setup-python seuls
translation-drift.yml 17 checkout + setup-python seuls

~112 runs par semaine quittent la facturation ubuntu-latest pour une jambe qui est libre 7/8 du temps.

Ce qui est exclu, et pourquoi — la liste des refus vaut la liste des retenus

workflow volume motif d'exclusion
secret-scan.yml 72 invoque docker pull / docker run (gitleaks epingle). Nos runners sont des conteneurs : docker-in-docker n'est pas disponible.
always-on-guards.yml 70 porte secrets.GITHUB_TOKEN et poste commentaires + check-runs. Hors du barreau tranche 1.
always-on-metadata-guards.yml 57 idem.
source-output-ratchet.yml 45 eligible techniquement, mais #14281 (ouverte) le modifie deja. Retire pour ne pas entrer en conflit avec une lane. A reprendre en tranche 3 apres merge de #14281.
markdown-claims-output-advisory.yml 18 action tierce marocchino/sticky-pull-request-comment.
catalog-drift.yml 19 committe sur la branche de PR. Un workflow qui pousse ne doit pas etre le premier essai d'une classe de runner.
validation-matrix.yml, twin-parity.yml 21, 17 actions/upload-artifact — premiere partie, sans doute compatible, mais non verifie sur cette classe de runner. Tranche 3.
notebook-execution-required.yml 18 actions/github-script (token).
lean-axiom.yml, lean-build.yml — reutilisables (workflow_call) : elles heritent du contexte de leur appelant, donc potentiellement d'un fork. Exclusion de principe.

La garde fork, inchangee

Chaque job migre recoit la garde same-repo textuellement identique a celle de la tranche 1 :

if: github.event.pull_request.head.repo.full_name == null || github.event.pull_request.head.repo.full_name == github.repository

Elle couvre pull_request et pull_request_target d'un seul predicat, et le null laisse passer les declencheurs hors-PR (push, schedule). Aucun code de fork n'atteint un runner auto-heberge — contrainte dure, les ~95 forks etudiants ne doivent jamais s'executer chez nous. pr_gate.py compte skipped comme OK, donc une PR de fork n'est pas penalisee par le saut.

Acceptance

  1. Les 5 workflows portent runs-on: [self-hosted, coursia-ephemeral, coursia-linux] et la garde same-repo.
  2. Un run de chacun, sur une PR same-repo, reussit sur un runner myia-*-linux-docker-* — preuve par le nom du runner dans le log, pas par la couleur du check.
  3. Controle positif de la garde fork : sur une PR de fork, les 5 jobs apparaissent skipped, pas queued ni failure.
  4. Apres merge, gh api ".../actions/runs?status=queued" mesure a nouveau la profondeur de file, et la part ubuntu-latest a baisse.

Le point 3 est celui qui compte : un routage qui marche sur nos PRs mais laisserait passer un fork serait un echec de securite, pas un succes de performance.

See #13378.

Activity

  1. jsboige commented on Sep 2, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-ai-01:CoursIA -- paths: .github/workflows/scripts-tests.yml, .github/workflows/notebook-validation.yml, .github/workflows/markdown-rendering-guard.yml, .github/workflows/bare-cross-dir-load-gate.yml, .github/workflows/translation-drift.yml

  2. added a commit that references this issue on Sep 2, 2026
  3. added 2 commits that reference this issue on Sep 2, 2026
  4. added a commit that references this issue on Sep 2, 2026
  5. myia-ai-01 commented on Sep 2, 2026

    @myia-ai-01
    Collaborator

    Checklist « tirage a fond » — etat mesure 2026-09-02T18:30Z

    Demande user : « Tu ne peux pas te faire une checklist qui va jusqu'au tirage a fond de tous les runners ? »
    Chaque ligne porte sa mesure, pas une intention.

    Offre — la capacite existe et elle est saine

    # Item Etat Mesure
    A Image conteneur complete FAIT 4 outils manquants fermes : gh (#14302), lsb_release (#14334), python-is-python3, PEP 668 PIP_BREAK_SYSTEM_PACKAGES=1 + python3-yaml (#14339, mergee 16:20Z)
    B 16 slots ai-01 en ligne, image a jour FAIT docker ps sur les 2 daemons : 16/16 coursia-linux-runner:2.336.0 ; controle positif dans un conteneur vivant : PIP_BREAK=1, gh OK, lsb OK, pyyaml OK
    C 4 slots po-2024 en ligne FAIT API actions/runners 18:15Z : myia-po-2024-linux-docker-1..4 = online (ils etaient offline a 16:15Z)
    D Persistance WSL (la distro ne meurt plus) FAIT ai-01 / greenlight po-2024 Diagnostic po-2024 : un wsl.exe one-shot ne maintient pas la distro, WSL la reape a la sortie du client. Reproduit ici : /proc/uptime = 6836 s alors que la machine tourne depuis des jours, 0 holder. Corrige : holder detache + .vbs au demarrage. Controle positif : execution du .vbs -> holders 1 -> 2
    E Slot cap / recyclage des idle FAIT Recyclage des idle seulement, avec re-verification busy par slot juste avant le kill

    Total offre : 21 runners en ligne (16 ai-01 Linux + 4 po-2024 Linux + 1 po-2024 fast-guards).

    Demande — c'est ici que tout se joue

    Mesure 18:16Z, en croisant actions/runners (occupation) et runs/<id>/jobs -> .labels (ce que les jobs demandent) :

    22 queued        ubuntu-latest
    20 in_progress   ubuntu-latest
     1 in_progress   self-hosted+coursia-ephemeral+coursia-linux
    

    42 jobs sur 43 s'adressent a GitHub. 20 de nos 21 runners sont idle. La file n'attend pas de la capacite, elle attend d'etre adressee.

    # Item Etat Mesure
    F Workflows adresses a nos runners 13 sur main -> 37 avec #14336 + 3c git grep -l coursia-linux .github/workflows : 13 sur main, 37 sur la branche. +1 coursia-fast-guards
    G Tranche 3a/3b — 19 workflows #14336, en attente de 2 jobs Lean 32 checks verts ; proof-integrity (conway_lean) en cours ; PR gate a relancer apres
    H Tranche 3c — 8 jobs dans 5 workflows commitee localement, poussee apres #14336 Routage par job cette fois : bash-syntax-advisory x2, lean-social-choice/certified-no-sorry, notebook-execution-required/{detect-changes,validate-static}, secret-scan/positive-controls, twin-parity x2. check_self_hosted_runner_policy.py rc=0, 42 jobs self-hosted
    I ~37 workflows cron NON COMMENCE Prochain lot apres H, avec un plafond de slots pour ne pas affamer les gardes de PR
    J Pool coursia-fast-guards SOUS-UTILISE 1 runner (po-2024) pour 1 workflow. ai-01 n'a aucun slot fast-guards

    Exclus par decision, pas par oubli

    Ce qui reste avant de pouvoir dire « a fond »

    1. Merger ci(#14283): tranche 3 -- router 19 gardes PR vers la jambe Linux auto-hebergee #14336 (2 jobs Lean) puis pousser 3c.
    2. Confirmer que les 4 slots po-2024 tournent sur l'image post-feat(ci-runner,#14294): installer gh CLI dans l'image runner (pin SHA-256) #14302/feat(ci-runner,#14283): lsb-release + zstd + wget dans l'image runner #14334/fix(ci-runner,#14283): PEP 668 -- rendre pip utilisable dans le conteneur Linux #14339 (question posee).
    3. Lot cron (item I).
    4. Trancher J : donner a ai-01 un slot coursia-fast-guards, ou retirer ce jeu de labels.
    5. CI: pools de runners specialises par labels (cache Mathlib chaud, quarto, navigateurs) plutot qu'une image unique qui grossit #14337 — pools specialises (cache Mathlib chaud, etc.), qui rendrait routables les exclus « lourds ».
  6. added a commit that references this issue on Sep 2, 2026
  7. added 4 commits that reference this issue on Sep 2, 2026
  8. jsboige commented on Sep 2, 2026

    @jsboige
    OwnerAuthor

    Checklist -- avancement mesure (2026-09-02T17:15Z)

    # Item Etat Preuve
    1 Image du container reconstruite post-PEP 668 (#14339) sur les deux machines FAIT ai-01 : rebuild verifie ; po-2024 : image 6aa023ff769b a 16:30:36Z, controle positif 4/4 (yaml 6.0.1 / PIP_BREAK=1 / lsb=Ubuntu / gh OK), 4/4 slots online a 16:35:14Z
    2 Tranche 3 -- 19 gardes PR routes MERGEE #14336
    3 Tranche 3c -- 8 jobs routes par job + always-on-metadata-guards MERGEE #14342, squash c90a0aee4
    4 Tranche 4 -- 32 balayages cron EN VOL #14343, base re-ciblee sur main apres le squash
    5 Persistance systemd + holder (po-2024) MERGEE #14341
    6 Holder WSL sur ai-01 (la distro etait reapee sans lui) FAIT .vbs au dossier Demarrage, controle positif holders 1 -> 2
    7 Lanceur ai-01 : 4 -> 8 slots au demarrage FAIT defaut du script + argument de la tache planifiee passes a 8 (la tache passait 4 : un reboot n'aurait ramene que la moitie du pool)
    8 Volume _work persistant par slot EN VOL #14288, base recalculee (elle etait gelee sur un PR gate annule)
    9 Pool d'attente coursia-waiter BLOQUEE cote lane #14303 : update-branch refuse pour conflits -- seule myia-po-2024:CoursIA peut la rebaser
    10 Recette de persistance copy-paste + Wants= au lieu de Requires= REPORTE, nomme #14347 (les 2 reserves d'Hermes sur #14341)
    11 Enregistrement S4U au boot (avant ouverture de session) BLOQUEUR USER refuse sans clic UAC ; le .vbs couvre le logon, pas le pre-logon

    Ce que les runners tirent reellement

    Mesure a l'instant, croisant l'occupation (actions/runners) et qui a execute chaque job
    (runs/<id>/jobs -> .runner_name, le seul champ qui dit l'executant reel -- .labels ne dit que
    ce qui a ete demande) :

    • Occupation : 19 slots online, 19 BUSY -- saturation complete, file d'attente vide (0 job queued).
    • Debit, sur toutes les executions creees depuis 16:00Z (295 runs, 639 jobs termines) :
      113 jobs sur 639 (18 %) ont tourne chez nous, dont ai-01 98 et po-2024 15. Avant la tranche 3,
      la meme mesure donnait 1 sur 43.
    • Fiabilite : 1 seul non-succes sur 113, et ce n'est pas une panne d'infra --
      Twin parity audit (#8057) sur myia-ai-01-linux-docker-3 a trouve un vrai DRIFT introduit par
      une PR d'enrichissement (feature/11601-csp1-density). Le garde route rougit sur du contenu reel :
      c'est le controle positif qui manquait, et il ecarte le mode de defaut ou une migration rend un
      garde muet et « toujours vert ».

    Piege de mesure a ne pas reproduire

    gh api "actions/runs?status=completed&per_page=60" ne rend pas les 60 derniers runs termines :
    il m'a rendu des runs du 3 aout, soit un mois avant tout routage -- d'ou un « 0 % » qui aurait
    ete lu comme une regression. La fenetre doit etre bornee dans le temps (created=>...), jamais
    par un compte de runs sous filtre de statut.

    Reste

    #14343 au vert (assertion de perimetre corrigee : le diff porte 33 fichiers, pas 32 -- les 32
    workflows plus le script de politique), puis #14288. #14303 attend sa lane. Le S4U attend le user.

  9. added 2 commits that reference this issue on Sep 2, 2026
  10. added a commit that references this issue on Sep 2, 2026
  11. 23 remaining items

  12. added 3 commits that reference this issue on Sep 20, 2026
  13. added a commit that references this issue on Sep 22, 2026
  14. added 2 commits that reference this issue on Sep 23, 2026
  15. added a commit that references this issue on Sep 23, 2026
  16. added a commit that references this issue on Sep 23, 2026
  17. added a commit that references this issue on Oct 5, 2026
  18. added a commit that references this issue on Oct 5, 2026
  19. added 2 commits that reference this issue on Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions