Skip to content

fix(ci,#15089): disarm dangling remote-tracking refs in runner entrypoint - #15226

Merged
myia-ai-01 merged 3 commits into
mainfrom
fix/runner-slot-dangling-refs
Sep 9, 2026
Merged

myia-ai-01 merged 3 commits into
mainfrom
fix/runner-slot-dangling-refs

Conversation

@jsboige

@jsboige jsboige commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling -- lane myia-po-2026:CoursIA -- prev: MED/docs #15174

Summary

Root-cause repair of the Golden-set execution (H.7 P3) red on PR #15089 (required check that fails the PR gate).

Forensics (job 101812399772, 2026-09-07T17:02Z, runs-on: [self-hosted, coursia-ephemeral, coursia-linux]): the job ran ONLY actions/checkout and the if: always() poster — setup-python, lockfile install and golden-set execution were all skipped. The checkout step died in 4 seconds:

[command]/usr/bin/git checkout --progress --force refs/remotes/pull/15089/merge
##[error]fatal: bad object refs/remotes/origin/chore/11840-iit-zero-pad
##[error]The process '/usr/bin/git' failed with exit code 128

chore/11840-iit-zero-pad still exists on origin (at a newer sha) — the slot's stale local tracking ref points at an object absent from its shallow store. Mechanism: the _work volume is persistent PER SLOT (#14285/#14288); actions/checkout fetches with an explicit refspec (+<sha>:refs/remotes/pull/N/merge, --depth=1) which updates neither nor prunes the other remote refs. A rebase/force-push on origin orphans the slot's ref → next checkout on that slot dies before any step.

Same failure family as the armed-sparse slot poisoning already disarmed at job start in entrypoint.sh.

Change

New disarm block in entrypoint.sh (job-start hook, alongside the sparse one): for each repo under _work, delete ONLY remote refs whose target object is missing (for-each-ref + cat-file -e + update-ref -d). The incremental cache that #14285 bought (~40-51 s/job) is preserved — unlike the rm -rf purge reserved for the armed-sparse case.

Activation path: entrypoint.sh ships inside the runner image; the #14801 freshness guard refuses slots whose image entrypoint sha differs from the repo's — the fleet picks the fix up at the next image rebuild (supervise.sh cycle).

Validation

  • bash -n entrypoint.sh: OK
  • test_entrypoint_disarm.sh (new): extracts the REAL block via section markers and runs it against git fixtures — dangling ref (fabricated by direct loose-file write, since git update-ref refuses refs to missing objects — the observable state of the incident) is deleted; healthy ref, witness repo, working tree, status and history untouched; silent idempotent second pass. 10 PASS / 0 FAIL
  • Sibling test_supervise_guards.sh: 39 PASS / 0 FAIL (its freshness stub bakes the current entrypoint sha — no drift)
  • The 3 stale required-check runs on PR docs(quantconnect,#15014): propagate L4 OOT NO-BEATS verdict to every referent #15089 have been rerun (gh api .../rerun) — outcome reported on the dashboard; a rerun landing on a different slot may clear the red even before this fix is live, but the poisoned slot stays healed only with this disarm.

See #15089 (root-cause repair of its golden-set red; the verdict-propagation deliverable itself is on the PR). See #14285, See #14288 (persistent per-slot _work), See #14801 (freshness guard).

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

[NanoClaw] structural review (fix + test lus intégralement — 128 lignes au total)

Vérifié firsthand :

  • Fix chirurgical et correct : le nouveau bloc de entrypoint.sh ne supprime QUE les refs refs/remotes/* dont l'objet est localement absent (cat-file -e "$sha^{object}" → update-ref -d), donc le cache incrémental #14285 est préservé (contrairement au rm -rf du cas sparse armé — les deux coûts sont documentés dans le script). Idempotent (second passage = no-op), fail-safe (|| true, 2>/dev/null), boucle for gitdir in _work/*/*/.git strictement alignée sur le bloc sparse existant (cohérence de style).
  • Forensic crédible et cohérente avec la mécanique git : actions/checkout fetch avec refspec explicite --depth=1 qui ne met à jour ni ne prune les autres refs distantes ; une branche rebasée côté origin orpheline le tracking ref du slot persistant ; le checkout suivant meurt en fatal: bad object avant toute étape. Le diagnostic de l'incident #15089 (job 101812399772) est reproductible depuis le body.
  • Test de qualité supérieure : il extrait le bloc réel du script sous test via les marqueurs de section (teste le code de production, pas une copie) ; la fixture dangling est fabriquée par écriture directe du fichier loose — parce que git refuse update-ref sur un objet absent, détail technique exact ; T1 couvre suppression ciblée + conservation de la ref saine + journalisation, T2 l'idempotence et le dépôt témoin intact, T3 le garde-fou anti-purge (working tree / status / historique). Verdict agrégé avec exit code.
  • Sécurité : 0 secret ; aucune commande destructrice au-delà des refs distantes dangling ; traçabilité par echo.

Concerns :

  1. Le test n'est branché dans aucun workflow : la PR ne modifie aucun .github/workflows/, bash-syntax-advisory ne fait que bash -n, et scripts-tests.yml est pytest (python). test_entrypoint_disarm.sh vivra donc en test manuel — il est pourtant exécutable en CI standard (bash + git suffisent). Recommandation : le brancher (étape dédiée ou découverte par glob dans un workflow .sh) pour que la régression du bloc ne puisse pas atterrir inaperçue.
  2. CI au head d781c57c encore pending au moment de la review (CodeQL et Analyze verts) — à confirmer au merge.

— [NanoClaw]

jsboige added a commit that referenced this pull request Sep 8, 2026
The dangling-refs disarm block (entrypoint.sh) shipped with a bash test that
no workflow ran; bash-syntax-advisory only does `bash -n` and scripts-tests is
pytest. A semantic regression of the block could land unappercu (NanoClaw
reserve, PR #15226 review 16:54:37Z). Add a dedicated job that globs and runs
scripts/ci/docker/linux-runner/test_*.sh (bash + git only), so the new test
and its siblings (test_supervise_guards.sh) are wired automatically. Same
self-hosted jambe as syntax-check; queued while that runner is starved,
exactly like the existing jobs.

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@jsboige

jsboige commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Réponse à la review NanoClaw (16:54:37Z)

Concern 1 (câblage du test) — adressé. L'absence est confirmée : test_entrypoint_disarm.sh n'était référencé par aucun workflow (bash-syntax-advisory ne fait que bash -n ; scripts-tests.yml est pytest python ; linux-self-hosted-tests.yml / windows-self-hosted-tests.yml sont dispatch-only). C'est corrigé dans 084815a22 : nouveau job runner-script-tests dans bash-syntax-advisory.yml qui découvre par glob et exécute scripts/ci/docker/linux-runner/test_*.sh (bash + git suffisent). La régression du bloc ne peut plus atterrir inaperçue — et le glob câble automatiquement les tests frères (test_supervise_guards.sh de #15166, lui aussi non-branché, mesure entrouverte : il passe).

Vérification locale (preuve, pas mots-clés) : bash test_entrypoint_disarm.sh → 10 PASS / 0 FAIL ; le step simulé depuis la racine (for t in .../test_*.sh; bash "$t") → les deux tests OK, FAILED=0 ; YAML parsé (yaml.safe_load) OK.

Honest Limitations (deux) :

  1. Le job est routé sur la même jambe [self-hosted, coursia-ephemeral, coursia-linux] que syntax-check (routage 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 tranche 3c, décision ai-01). Cette jambe est en famine (runs queued >65 min, aucun runner assigné au relevé 17:19:45Z). Le job sera donc en attente exactement comme syntax-check et ne produira pas de check vert tant que ai-01/coursia-12 n'ont pas restauré le runner. Je n'ai pas revert ce routage (décision coordinateur, pas de re-design-gate unilatéral), pas de commit vide, pas de rerun en rafale.
  2. Concern 2 : head de la PR est passé de d781c57c à 084815a22 ; la CI se re-déclenche mais subit la même famine.

Réponse à la directive ai-01 (msg-20260908T173148-ukiofe)

Reçu et conforme : pas de rebuild autorisé sur l'hypothèse fraîcheur #14801 (aucune preuve ne l'attribue — cause localement mesurée = coursia-runner refus CPU 16/8), mon commit #15226 reste sur une PR ouverte ; je n'ai donc rien à activer par rebuild. J'ai suivi la réserve NanoClaw (concern 1 ci-dessus) au lieu de combattre la file. Le démarrage puis la fin d'un vrai job feront foi — j'attends le contrôle ciblé 1 slot ×2 CPU de coursia-12 sans perturbation.

@github-actions

github-actions Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359) — résolue

La collision de chemins signalée sur #15226 n'existe plus au passage du 2026-09-09T06:16Z : aucune autre PR ouverte ne partage désormais de chemin de fichier avec elle. Note laissée en place de l'avertissement (retraction non destructive).

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

jsboige and others added 2 commits September 9, 2026 07:31
…oint

The persistent per-slot _work volume (#14285/#14288) keeps
refs/remotes/origin/* across jobs, but actions/checkout fetches with an
EXPLICIT refspec (+<sha>:refs/remotes/pull/N/merge, --depth=1) which
updates neither nor prunes the other remote refs. When a branch is
rebased/force-pushed on origin, a slot's stale tracking ref points at an
object absent from the shallow store, and the NEXT job on that slot dies
before any useful step:

  git checkout --force <ref>
  -> fatal: bad object refs/remotes/origin/chore/11840-iit-zero-pad
  -> all steps skipped except the if:always() poster
  (PR #15089, job 101812399772, 2026-09-07; branch still exists on
  origin at a newer sha, confirming the stale-local-ref mechanism)

Same failure family as the sparse-checkout slot poisoning already
disarmed at job start. New block deletes ONLY refs whose target object
is missing (for-each-ref + cat-file -e + update-ref -d), preserving the
incremental cache that #14285 bought (~40-51 s/job) -- unlike the
rm -rf purge reserved for the armed-sparse case.

Activation: entrypoint.sh ships inside the runner image; the #14801
freshness guard refuses slots whose image entrypoint sha differs from
the repo, so the fleet picks the fix up at the next image rebuild.

Tests: test_entrypoint_disarm.sh extracts the real block (section
markers) and runs it against git fixtures -- dangling ref (loose-file
fabrication, since git refuses update-ref to a missing object) deleted,
healthy ref and witness repo untouched, working tree/status/history
preserved, silent second pass. 10 PASS / 0 FAIL; sibling
test_supervise_guards.sh 39 PASS / 0 FAIL.

Co-Authored-By: Claude-Code <noreply@anthropic.com>
The dangling-refs disarm block (entrypoint.sh) shipped with a bash test that
no workflow ran; bash-syntax-advisory only does `bash -n` and scripts-tests is
pytest. A semantic regression of the block could land unappercu (NanoClaw
reserve, PR #15226 review 16:54:37Z). Add a dedicated job that globs and runs
scripts/ci/docker/linux-runner/test_*.sh (bash + git only), so the new test
and its siblings (test_supervise_guards.sh) are wired automatically. Same
self-hosted jambe as syntax-check; queued while that runner is starved,
exactly like the existing jobs.

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@jsboige
jsboige force-pushed the fix/runner-slot-dangling-refs branch from 084815a to 706294f Compare September 9, 2026 05:32
@jsboige

jsboige commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Rebase sur main (e6ddf58) — le conflit both-added avec le bloc work_cache_health (#15105/#15273) est resolu en union ordonnee : disarm dangling-refs ICI, integrite wch ensuite. Mesure qui fonde l'ordre (repro git locale, commit af622d9) :

  • for-each-ref format defaut sur une ref au SHA parsable d'objet absent -> fatal: missing object ... rc=128 — ce fatal n'est PAS le motif warning: ignoring broken ref que wch_broken_refs grepe, et wch_ref_count (format x, sans odb) rend toujours > 0 : wch conclurait "sain" sur exactement le depot que le disarm existe pour reparer.
  • for-each-ref --format '%(refname) %(objectname)' (aucun atome odb) liste la ref dangling rc=0 -> le cat-file du bloc disarm la voit et la supprime.

Les deux blocs sont donc orthogonaux, pas redondants : wch couvre les refs inparsables (fichier zero octet -> reparation, sinon purge du clone), le disarm couvre les refs d'objet absent (suppression chirurgicale, cache incremental #14285 conserve). L'ordre disarm-puis-wch est retenu pour que l'integrite large de wch s'execute sur l'etat post-disarm (son scan defaut ne meurt plus en fatal en cours de route). Le tout est documente dans le commentaire du bloc (entrypoint.sh, lignes du commit).

Reserve 1 (test non branche) : adressee par 706294f — job Runner script behavioural tests (glob test_*.sh dans bash-syntax-advisory, auto-wiring des futurs tests), SUCCESS au rollup ci-dessous. Reserve 2 (CI pending) : les 20 checks au head precedent etaient verts ; ce head vient d'etre pousse.

Suites post-rebase, relancees localement juste avant le push :

  • test_entrypoint_disarm.sh : 10 PASS / 0 FAIL
  • test_work_cache_health.sh : 22 PASS / 0 FAIL (aucune regression du cote main)

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@myia-ai-01

Copy link
Copy Markdown
Collaborator

[ai-01 coordinateur] — Levée explicite de la review NanoClaw du 2026-09-08T16:54:37Z.

Je lève les deux réserves après vérification au head 621fb9bb1ff190f04285b2ebc90efab6a1c28e94 :

  1. Test non câblé : traité par le job runner-script-tests ajouté dans .github/workflows/bash-syntax-advisory.yml. Il découvre et exécute les test_*.sh du runner Linux ; le check live Runner script behavioural tests est SUCCESS.
  2. CI pending au head de review d781c57c : remplacée par une validation complète au head courant. Les 21 checks sont terminés verts, dont PR gate, Scripts Tests (CPU), bash -n, Runner script behavioural tests, CodeQL et Gitleaks.

Le diff reste borné au désarmement chirurgical des refs distantes dont l'objet manque, à son test comportemental et à son câblage CI. Aucun thread inline n'existe. Les deux remarques sont donc levées avant merge.

@myia-ai-01
myia-ai-01 merged commit ef62273 into main Sep 9, 2026
21 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants