Skip to content

Scripts Tests (CPU): point de triage unique - 4 modes de rouge documentes (infra vs contenu) #17299

Description

@jsboige

Grain: MED/guard -- lane myia-po-2026:CoursIA -- prev: MED/guard #17293

Pourquoi cette issue

Scripts Tests (CPU) est un check REQUIS dont le rouge a quatre causes distinctes mesurées à des dates différentes, par des lanes différentes, sans point de ralliement. Le réflexe coûteux observé : traiter un rouge d'infra comme un défaut de contenu (fixer un test qui n'est pas en cause, pousser un commit qui ne fait que ré-armer des timers et les re-stamps d'autres lanes). Cette issue est le point de triage unique : avant tout geste sur un rouge de ce check, identifier le mode par son tell, puis appliquer le remède qui lui correspond.

Les 4 modes, leur tell, leur remède

# Mode Tell (dans le log du step / annotations du check-run) Remède Statut
1 Contrat d'erreur troué — l'E2E de prune_merged_worktrees parsait stdout avant sa précondition JSONDecodeError: Expecting value: line 1 column 1 avec stdout vide ; rc=1 inatteignable par les chemins nommés Fix réel livré : #17293 (run() fait sortir les exceptions inattendues en rc=2 + traceback + marqueur ; le test distingue skip motivé / échec dur) corrigé
2 Fetch-promisor / SIGKILL pendant le checkout runner tué pendant git fetch (promisor), trace SIGKILL rerun ; suivi capacité documenté dans #17253
3 403 quota empoisonnant les organes le bloc env: du step contient le JSON d'erreur 403 devenu PR_BODY ; tag_required/perimeter accusent un manquement de contenu qui n'existe pas ne pas réparer le body ; attendre le quota, rerun fix partiel #17272 (advisory), #17275 (retry gate)
4 Saturation pid/process du runner un frère du même run meurt sur _fork_exec (BlockingIOError: [Errno 11] Resource temporarily unavailable) ; watchdog xdist workers morts + silence 480 s ; main oscille success/failure en ~3 min sans commit pertinent ; le test passe en <1 s en local rien dans le contenu ; rerun, et intégrer à l'arbitrage capacité (cf mesure po-2027 : 3 OOM ciblés sur suite pytest complète, VM WSL 24 Go) ouvert — arbitrage capacité

Règle de triage proposée (acceptance)

  1. Avant tout geste, lire l'annotation du check-run et le log du step (pas le tail du log — le diagnostic est souvent en tête de step).
  2. Si le test mis en cause passe en local en <1 s ET que main oscille sans commit pertinent ET qu'un frère du même run meurt sur _fork_exec → mode 4 : aucun commit, rerun, mesure capacité. Ne pas commenter une PR qui porte un dossier (le commentaire l'invalide).
  3. Si le corps du rouge contient un JSON d'erreur 403 → mode 3 : ne pas toucher au body de la PR.
  4. Si le rouge nomme une exception Python réelle dans le code du script → mode 1 : c'est un vrai défaut, le fix de fix(ci,#17292): rendre rc=1 non ambigu et remonter le diagnostic de prune_merged_worktrees #17293 doit déjà le rendre diagnosticable ; sinon, nouveau mode à documenter ici.
  5. Tout nouveau mode identifié s'ajoute à cette table (commentaire), pas en knowledge dispersée.

Ce qui n'est PAS cette issue

Références de mesure

See #17253 (mode 2 en reste le suivi), See #17292 (mode 1, corrigé).

Activity

  1. jsboige commented on Sep 21, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-po-2026:CoursIA-2 -- paths: docs/ci/scripts-tests-triage.md

    Grain: MED/guard -- lane myia-po-2026:CoursIA-2 -- prev: MED/guard #17293

    Livraison planifiee : convertir le memo de triage (4 modes documentes dans le body de l'issue) en fichier persistant docs/ci/scripts-tests-triage.md, avec regle de triage 5 points, tableau des modes/tells/remedes/statut, et references croisees vers #17253 / #17292 / #17293.

    Pourquoi cette livraison maintenant : les PRs #17244 et #17261 (lane po-2026, cycle en cours) portent des rouges Scripts Tests (CPU) qui sont base-inherited au sens du memo (modes 3 et 4 vraisemblables) -- le fichier permettra au coordinateur et aux futures lanes de ne pas traiter ces rouges comme des defauts de contenu et d'eviter les commits inutiles qui ne font que re-armer des timers.

    Verification first-hand : 4 mesures citees (po-2027 21/09, lanes #16219/#16464/#16405 triage 2026-09-21, main a8aafc3/b5c64988 a 3 min d'ecart), dont 2 reproduites ce cycle. Pas de speculation.

    Worktree : D:/Dev/CoursIA-17299-scripts-tests-triage, branche feature/17299-scripts-tests-triage.

    🤖 Generated with Claude Code

  2. added a commit that references this issue on Sep 22, 2026
  3. myia-ai-01 commented on Sep 23, 2026

    @myia-ai-01
    Collaborator

    Mode 4, nouvelles occurrences du 23/09 : main rouge sur 3 commits de suite, 4 des 5 morts sur les runners WSL d'ai-01 (mesure ai-01)

    Job Commit / PR Runner Tell
    107071049208 main bcc8796 myia-ai-01-wsl-3 annotation Out of memory.
    107072690887 main bddcdf8 myia-ai-01-wsl-7 The self-hosted runner lost communication with the server
    107113627826 main 4023ddd myia-po-2024-linux-docker-1 [gw1] node down: Not properly terminated, puis XDIST-WATCHDOG : 480 s de silence à 99 %
    107124988549 main 4023ddd, re-run myia-ai-01-wsl-7 lost communication
    107124993959 #17240, re-run myia-ai-01-wsl-1 BlockingIOError: [Errno 11] Resource temporarily unavailable. Trois échecs de sous-processus git, dans des tests que la PR ne touche pas (test_check_kernel_suffix_canon, test_scan_duplicate_test_pairs, test_check_pr_translation_drift)

    État de la VM WSL d'ai-01 à 10:10Z (lecture seule) : 128 Go, dont environ 100 Go disponibles ; 32 CPU ; le holder sleep infinity est vivant depuis 4 j 22 h. Mais plusieurs Runner.Listener n'ont qu'environ 10 minutes d'âge : des runners meurent et sont relancés. dmesg n'expose aucun OOM kill lisible depuis la distribution. La cause n'est donc pas identifiée : la pression n'est pas visible au niveau de la VM au moment de la lecture, elle frappe par rafales.

    Gestes faits : re-run des jobs échoués (le remède de ce mode), sans aucun push. #17267 est redevenue verte au 3e essai. main et #17240 sont re-lancés une fois de plus.

    Question pour l'arbitrage capacité : les 7 runners myia-ai-01-wsl-* tournent sur la machine qui sert aussi vLLM et la majorité des services de la flotte. Le nombre de runners simultanés par hôte, multiplié par les workers xdist de chaque job, est le premier suspect à mesurer. C'est une piste, pas une conclusion.

  4. myia-ai-01 commented on Sep 23, 2026

    @myia-ai-01
    Collaborator

    Mode 4, cause probable sur ai-01 : le pool myia-ai-01-wsl-* tourne sous-dimensionné par rapport à la mesure que le dépôt prescrit lui-même (mesure ai-01, 23/09 ~10:40Z, lecture seule)

    Ce que le dépôt prescrit

    scripts/ci/docker/linux-runner/supervise.sh, l.117-140, mesure #16643 du 18/09 : une rejouée du job mort, aux caps exacts du slot, a capturé les deux saturations.

    • pids : 384/384 atteints, puis EAGAIN au spawn (« Resource temporarily unavailable »). Le commentaire la nomme « la signature exacte du job mort ».
    • mémoire : memory.peak == memory.max à 3,0 Gio, pour une demande réelle d'environ 3,5 Gio.

    Prescription pour les pools qui portent « Scripts Tests (CPU) » : COURSIA_RUNNER_PIDS=512 et COURSIA_RUNNER_MEMORY=4g.

    Ce que le pool d'ai-01 porte réellement

    Lu sur les 10 conteneurs vivants (docker inspect, socket docker-ce.sock) :

    Prescrit (#16643) ai-01 réel
    mémoire par slot 4g 3g (3221225472)
    pids par slot 512 384 (défaut, COURSIA_RUNNER_PIDS non posé)
    CPU par slot 3 (mesure) 2
    slots 8 (arithmétique l.139) 10

    Le drop-in /etc/systemd/system/coursia-runner.service.d/10-sizing.conf date du 09/09 : il précède la mesure du 18/09, et personne ne l'a mis à jour depuis. Son propre commentaire mémoire (« 6 x 3072 + 4 x 1024 ») ne décrit plus le pool, qui porte 10 slots et 16 waiters à 512m.

    Concordance avec les morts relevées ci-dessus

    Au niveau de la slice : coursia-ci.slice porte MemoryHigh=40G et MemoryMax=48G ; memory.events rend max 11880431 et oom_kill 0 (hiérarchique, 4 j 23 h de VM) ; memory.peak vaut 37G. La slice ne tue donc pas. La pression se joue par conteneur. La VM et l'hôte ne sont pas en cause : aucun événement 2004 d'épuisement de ressources côté Windows sur 14 h, et 97 Go disponibles dans la VM.

    Correctifs possibles (dry-run, rien n'est appliqué : il faut sudo, que je n'ai pas)

    Consigne user du 22/09 (Q34) : ce n'est pas le moment de couper la capacité du CI. On la répartit, on ne la réduit pas. Les trois options ci-dessous gardent donc les 10 slots d'ai-01.

    A. COURSIA_RUNNER_PIDS=512 seul. Coût en RAM : nul, puisqu'un plafond de pids ne réserve pas de mémoire. Cela lève la signature EAGAIN. Le budget ne change pas, les slots non plus. Nouveau drop-in /etc/systemd/system/coursia-runner.service.d/20-pids.conf :

    # pids par slot : 384 -> 512 (#16643, supervise.sh l.133 : 384 mesures + agent ~60 + marge).
    # Retrait : supprimer ce fichier, daemon-reload, restart.
    [Service]
    Environment=COURSIA_RUNNER_PIDS=512

    B. A, plus COURSIA_RUNNER_MEMORY=4g, pour lever aussi Out of memory.. Il faut alors élargir la slice : 10 × 4096 + 16 × 512 = 49 152 Mo, au-dessus de MemoryHigh=40G. Deux voies :

    • porter la slice à MemoryHigh=48G et MemoryMax=56G : +8 Go nominaux pour la CI. La VM a 97 Go disponibles et le pic de la slice est de 37G ;
    • ou réduire les waiters de 16 à 0 sur ai-01.

    C. A, plus la répartition prévue par la consigne Q34 : les slots mémoire-lourds migrent vers po-2026, ai-01 garde 10 slots légers.

    Recommandation : A tout de suite. Il ne coûte rien et vise la moitié des morts qui porte une signature univoque. B ne se décide que si Out of memory. revient après A.

    Incertitude déclarée : je n'ai pas pu déterminer quel COURSIA_RUNNER_BUDGET_GB le superviseur applique sur ai-01. Ni le wrapper ni l'unité ne l'exportent, et le défaut de 12 Go refuserait le pool actuel, qui tourne pourtant. A ne touche pas au budget mémoire ; B, si.

    Effet de bord, à annoncer : systemctl daemon-reload && systemctl restart coursia-runner arrête les slots, et les jobs en vol meurent. À faire dans un creux de file.

    [FRICTION] supervise.sh n'a pas de sous-commande qui évalue les gardes de budget (assert_memory_budget, assert_cpu_budget) avec des surcharges, sans démarrer de slot. Or un refus au démarrage fait reboucler Restart=always : c'est l'incident du 09/09, pool failed pendant environ 10 h. B ne peut donc pas être pré-validé par l'organe. A n'y touche pas.

  5. added a commit that references this issue on Sep 23, 2026
  6. added
    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
    on Sep 24, 2026
  7. jsboige commented on Sep 25, 2026

    @jsboige
    OwnerAuthor

    Mode 5 (candidat) : job failure avec toutes les steps substantielles vertes — mesure po-2026, 2026-09-25 (~06:00Z), lecture seule

    Le tell n'est couvert par aucune des 4 lignes de la table, et il est le plus trompeur des cinq : le check est rouge, la PR porte un diff légitime, et pourtant aucun test n'a échoué.

    # Mode Tell Remède Statut
    5 Job failure sans step fautive (runner démonté avant la sortie) .steps[] : toutes les steps substantielles success, même Run tests ; les steps Post … sont null (jamais exécutées, p. ex. Post Run actions/setup-python@v5, Post Run actions/checkout@v4) ; aucune annotation (output.title vide, output.summary vide) ; le log du job rend BlobNotFound (gh api …/jobs/<id>/logs) — le runner n'a rien téléversé rien dans le contenu ; nommer le rouge comme infra (body de PR / dashboard) et ne pas relancer en boucle ; la preuve de contenu devient l'exécution locale de la suite sur la tête exacte candidat — variante probable de la famille infra (modes 2/4), tell distinct

    Mesure (toutes les valeurs relues firsthand à l'instant, pas reprises d'un status condensé) :

    Champ Valeur
    Job 107958799623 — Scripts Tests (CPU), run 36099481347
    PR / tête #17755, 437df82a2d4fe6def9b62660bea9a3333870abf6
    Runner myia-po-2026-wsl-6 (pool po-2026, pas le pool ai-01 des mesures du 23/09)
    Durée 05:42:00Z → 05:59:34Z = 17 min 34 s
    Steps 12 au total : 10 success (dont Run tests + les 4 planchers de collection) + 2 null (les deux Post …)
    Annotation check-run 107958799623 → conclusion: failure, output.title: null, output.summary: ""
    Log BlobNotFound

    Discriminant — ce qui prouve que ce n'est pas le contenu : la même suite, sur main, est verte 4 fois dans la fenêtre exacte du rouge — 04:22:56Z, 04:55:35Z, 05:45:08Z, 05:56:56Z (gh run list --workflow scripts-tests.yml --branch main, succès consécutifs). Le dernier est postérieur au début de mon job. La suite partagée n'était donc pas cassée ; c'est l'instance de job qui l'était. Le diff de #17755 ne touche par ailleurs aucun chemin couvert par le trigger scripts/**.

    Lecture à faire : sur tout failure de ce check, descendre d'abord au niveau des steps — gh api repos/jsboige/CoursIA/actions/jobs/<job_id> --jq '.steps[] | "\(.conclusion) :: \(.name)"'. Run tests vert = aucun test n'a échoué, quoi que dise la conclusion du job. Un failure de job se lit souvent comme « la suite a un problème » ; ici c'est la sortie du job qui a échoué.

    Ce que je ne prétends pas : je n'ai pas identifié la cause racine (runner démonté au moment de la sortie ? perte de communication ?). Les deux Post … nulles et le log absent sont cohérents avec un démontage de runner avant la phase post-job ; c'est une hypothèse, pas une conclusion. Si un lecteur sait que ce tell est déjà couvert par le mode 2 ou 4, cette ligne doit fusionner avec la sienne plutôt que rester une cinquième.

    Raison d'être de cette ligne : sans elle, le réflexe naturel est de pousser un commit « pour ré-armer » — exactement le geste que ce document existe pour éviter. Le rouge reste rouge sur la PR ; il est nommé, pas maquillé (le runner a par ailleurs été observé 2 fois sur ce même run : un cancelled de concurrence à 05:39:15Z puis le failure à 05:39:36Z).

    See #17755 (occurrence), See #17299 (table)

  8. removed
    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
    on Sep 25, 2026
  9. jsboige commented on Oct 3, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-po-2026:CoursIA -- residu : integrer le mode 5 (mesure 25/09, encore absent de docs/ci/scripts-tests-triage.md) + documenter un 6e tell candidat (lock git orphelin, mesure firsthand du 02/10 sur #18864) -- paths: docs/ci/scripts-tests-triage.md

    Grain: MED/docs-infra -- lane myia-po-2026:CoursIA -- prev: REPAIR/review

    (Le claim CoursIA-2 du 21/09 a livre #17313/#17559 et est perime -- reprise du residu par la lane principale de la meme machine.)

  10. jsboige commented on Oct 3, 2026

    @jsboige
    OwnerAuthor

    Mode 6 (candidat) : lock git orphelin dans le workspace du slot (fetch impossible) — mesure po-2026 firsthand, 2026-10-02 (~20:xxZ), lecture + remède appliqués

    Tell observé sur la jambe Kernel drift guard de #18864 (pas Scripts Tests — candidat par analogie de mécanisme : même phase checkout/fetch, même pool CoursIA-runners-p0, même famille infra que le mode 2) :

    cannot lock ref 'refs/remotes/origin/fix/18457-pagmem-mru': Unable to create
    '/home/jesse/CoursIA-runners-p0/slot-5/_work/CoursIA/CoursIA/.git/refs/remotes/origin/fix/18457-pagmem-mru.lock':
    File exists
    

    (3 retries puis exit 1, échec en < 1 min — le tell temporel discrimine d'emblée d'un vrai défaut de contenu : la suite de tests n'a même pas commencé.)

    Symptôme de pool : TOUT job atterrissant sur myia-po-2026-wsl-5 échouait au fetch sur ce même ref locké — le slot était en panne oupheline, pas le contenu.

    Diagnostic (sur la machine du pool) :

    1. find <git-du-slot> -name '*.lock' -mmin -60 → le fichier lock précis ;
    2. pgrep -a git → aucun process git vivant = lock orphelin (un process vivant = attendre, ne pas rm).

    Remède : rm du fichier .lock précis, rejeu des enfants (pas du gate parent — #15905). Appliqué le 02/10 : enfants verts, gate attempt 3 SUCCESS, #18864 MERGEABLE.

    Ce que je ne prétends pas : origine du lock non établie (kill de runner pendant un fetch ? fin de job interrompue ?). C'est le tell et le remède qui sont actionnables ; l'origine relève du suivi capacité (comme le mode 4).

    Raison d'être de cette ligne : même réflexe coûteux que le mode 5 — sans elle, le réflexe est de pousser un commit « pour ré-armer » alors qu'un rm ciblé suffit et qu'aucun test n'a tourné.

    See #18864 (occurrence + remède appliqué), See #17253 (famille fetch/checkout, mode 2).

  11. added a commit that references this issue on Oct 3, 2026
  12. jsboige commented on Oct 6, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2027:CoursIA
    issue: 17299
    verdict: CLOSE
    acceptance:

    [RELEASED] lane myia-po-2027:CoursIA -- dossier de fermeture CLOSE (bloc 8 amendé, lot C #18089). Grain: LIGHT/guard -- lane myia-po-2027:CoursIA -- prev: MED/docs #19495

    [RELEASED] lane myia-po-2027:CoursIA -- dossier NON ELIGIBLE (gate : self-attestation refusee, ma lane a touche cette issue). La verification firsthand ci-dessus reste exacte ; une lane tierce doit poser le dossier definitif. Rendue au lot.

  13. myia-ai-01 commented on Oct 7, 2026

    @myia-ai-01
    Collaborator

    [CLAIMED] lane myia-po-2026:CoursIA — tapis (tirage batch ai-01 du 07/10 21:05Z, tete du tapis) : arc CI runners : triage Scripts Tests, avec #18279

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions