Le garde anti-collision voit tout et n'écrit presque rien — 30 verdicts sur 33 échouent en 404, sous un run vert
Mesuré ce matin après un mandat user (« les collisions se multiplient, le mécanisme de claim doit être renforcé »). Le mécanisme existe et il est bon : pr-path-collision-advisory.yml + scripts/check_pr_path_collisions.py (#13359, #13615, #13097). Il n'a pas besoin d'être conçu — il a besoin d'être branché sur sa sortie.
1. L'ampleur des collisions, sur le pool ouvert d'aujourd'hui
PRs ouvertes : 109
chemins partages par 2+ PR : 26
PR impliquees dans >=1 collision : 45 (41 %)
Les pires :
| PR concurrentes |
chemin |
| 7 |
scripts/check_unaddressed_nits.py |
| 6 |
scripts/tests/test_check_unaddressed_nits.py |
| 4 |
scripts/notebook_tools/pedagogy_density_baseline.json |
| 3 |
slides/S3-acculturation/slides.md · docs/notebook-metadata/production-scope.md · CSP-8-Temporal-Csharp.ipynb · Search-3-Informed-Csharp.ipynb · Search-02c-QuikGraph.ipynb |
Sept PR ouvertes éditent simultanément l'organe du merge-gate lui-même.
Deux de ces collisions ont coûté du travail réel ce matin : #13869 vs #13899 (même correction de grain_tag.py pour #13830, deux classes de caractères concurrentes) et #13846 vs #13913 (deux créations du même docs/curriculum/_inventory.md — un add/add, pas un conflit de lignes réparable par update-branch). Dans les deux cas il a fallu mesurer, arbitrer, et rendre conflictuelle la PR perdante.
2. Pourquoi le garde ne l'a pas empêché — la mesure
Dernier run (schedule, 2026-09-02T06:13Z, conclusion success) :
writes confirmed: post=3 update=0 retract=0
advisory: 30 write(s) failed -- planned 33, confirmed 3
Il a planifié 33 verdicts et en a posé 3. Les 30 autres :
WARN: write failed for #13817 — rc=1, gh: Not Found (HTTP 404)
WARN: write failed for #13831 — rc=1, gh: Not Found (HTTP 404)
WARN: write failed for #13835 — rc=1, gh: Not Found (HTTP 404)
... (30 lignes, toutes 404, en 8 secondes : 06:14:28Z -> 06:14:36Z)
3. Ce que la cause n'est pas — contrôle positif involontaire
La ressource n'est pas en cause. Un commentaire a été posté sur #13869 à 07:26Z avec un jeton de coordinateur (issuecomment-5506024182) : #13869 est la 4e ligne de la liste des 404. La PR existe, est ouverte, et accepte les commentaires.
Ce n'est pas non plus un rate-limit franc : les écritures de label du même run passent (label pr-overlap -> #14181/#14182/#14186/#14221), et 23 PR ouvertes portent effectivement pr-overlap aujourd'hui. Un seul jeton pour les deux canaux (GH_TOKEN: ${{ github.token }}, L83), permissions déclarées pull-requests: write + issues: write (L66-69).
Donc : même run, même jeton, même repo — le canal label écrit, le canal comment rend 404. C'est cet écart-là qui est le défaut, et c'est lui qu'il faut établir firsthand avant de corriger.
4. Le défaut aggravant — le run conclut success
30 écritures perdues sur 33, et la conclusion du job est success. post_comment() fait bien son travail (WARN explicite, return False, #13623) — mais personne n'agrège ces False en statut. Un organe qui échoue à 91 % et se déclare vert est pire qu'un organe absent : il occupe la place, et son vert dit à qui le regarde qu'il n'y a rien à voir. C'est la classe « verdict constant = organe éteint », déjà rencontrée sur ce garde même (#13097 : 176 cancelled pour 50 success, 83 runs consécutifs à zéro verdict sur six heures).
5. Acceptance
- Établir la cause du 404 firsthand, en nommant l'appel exact qui échoue. Piste principale, à tester avant d'être adoptée :
gh pr comment passe par la mutation GraphQL addComment, alors que edit_comment() utilise déjà PATCH /issues/comments/{id} en REST. Basculer le POST sur gh api -X POST /repos/{repo}/issues/{n}/comments alignerait les deux chemins sur le canal qui, lui, n'est pas mis en cause. Ne pas committer ce remède sans l'avoir passé dans le workflow réel : un diagnostic juste plus un remède non testé est un geste inerte payé par la lane suivante.
- Le job doit rougir quand ses écritures échouent. Un seuil explicite (
confirmed < planned -> sortie non nulle, ou un check-run distinct) — le point est qu'un taux d'écriture ne soit plus invisible. Advisory sur le contenu ne veut pas dire silencieux sur sa propre santé.
- Contrôle positif dans l'artefact : le run publie
planned / confirmed / failed en tête de sortie, et un test vérifie qu'un échec d'écriture simulé fait bien rougir. Un compteur qui ne peut pas rendre autre chose que success ne mesure rien.
6. Ce qui n'est PAS demandé ici
7. La part du coordinateur
Les deux collisions du jour se sont ouvertes parce qu'aucun [CLAIMED] n'était posé sur #13830 ni sur #13845. Poser le claim au dispatch, au nom de la lane servie, est la charge du coordinateur (R5 de coordinator-discipline.md), pas celle des workers. Le garde ci-dessus est le filet ; le claim est la première barrière, et elle est restée ouverte.
Grain: MED/guard — lane myia-ai-01:CoursIA
Le garde anti-collision voit tout et n'écrit presque rien — 30 verdicts sur 33 échouent en 404, sous un run vert
Mesuré ce matin après un mandat user (« les collisions se multiplient, le mécanisme de claim doit être renforcé »). Le mécanisme existe et il est bon :
pr-path-collision-advisory.yml+scripts/check_pr_path_collisions.py(#13359, #13615, #13097). Il n'a pas besoin d'être conçu — il a besoin d'être branché sur sa sortie.1. L'ampleur des collisions, sur le pool ouvert d'aujourd'hui
Les pires :
scripts/check_unaddressed_nits.pyscripts/tests/test_check_unaddressed_nits.pyscripts/notebook_tools/pedagogy_density_baseline.jsonslides/S3-acculturation/slides.md·docs/notebook-metadata/production-scope.md·CSP-8-Temporal-Csharp.ipynb·Search-3-Informed-Csharp.ipynb·Search-02c-QuikGraph.ipynbSept PR ouvertes éditent simultanément l'organe du merge-gate lui-même.
Deux de ces collisions ont coûté du travail réel ce matin : #13869 vs #13899 (même correction de
grain_tag.pypour #13830, deux classes de caractères concurrentes) et #13846 vs #13913 (deux créations du mêmedocs/curriculum/_inventory.md— un add/add, pas un conflit de lignes réparable parupdate-branch). Dans les deux cas il a fallu mesurer, arbitrer, et rendre conflictuelle la PR perdante.2. Pourquoi le garde ne l'a pas empêché — la mesure
Dernier run (
schedule, 2026-09-02T06:13Z, conclusionsuccess) :Il a planifié 33 verdicts et en a posé 3. Les 30 autres :
3. Ce que la cause n'est pas — contrôle positif involontaire
La ressource n'est pas en cause. Un commentaire a été posté sur #13869 à 07:26Z avec un jeton de coordinateur (
issuecomment-5506024182) : #13869 est la 4e ligne de la liste des 404. La PR existe, est ouverte, et accepte les commentaires.Ce n'est pas non plus un rate-limit franc : les écritures de label du même run passent (
label pr-overlap -> #14181/#14182/#14186/#14221), et 23 PR ouvertes portent effectivementpr-overlapaujourd'hui. Un seul jeton pour les deux canaux (GH_TOKEN: ${{ github.token }}, L83), permissions déclaréespull-requests: write+issues: write(L66-69).Donc : même run, même jeton, même repo — le canal
labelécrit, le canalcommentrend 404. C'est cet écart-là qui est le défaut, et c'est lui qu'il faut établir firsthand avant de corriger.4. Le défaut aggravant — le run conclut
success30 écritures perdues sur 33, et la conclusion du job est
success.post_comment()fait bien son travail (WARNexplicite,return False, #13623) — mais personne n'agrège cesFalseen statut. Un organe qui échoue à 91 % et se déclare vert est pire qu'un organe absent : il occupe la place, et son vert dit à qui le regarde qu'il n'y a rien à voir. C'est la classe « verdict constant = organe éteint », déjà rencontrée sur ce garde même (#13097 : 176cancelledpour 50success, 83 runs consécutifs à zéro verdict sur six heures).5. Acceptance
gh pr commentpasse par la mutation GraphQLaddComment, alors queedit_comment()utilise déjàPATCH /issues/comments/{id}en REST. Basculer le POST surgh api -X POST /repos/{repo}/issues/{n}/commentsalignerait les deux chemins sur le canal qui, lui, n'est pas mis en cause. Ne pas committer ce remède sans l'avoir passé dans le workflow réel : un diagnostic juste plus un remède non testé est un geste inerte payé par la lane suivante.confirmed < planned-> sortie non nulle, ou un check-run distinct) — le point est qu'un taux d'écriture ne soit plus invisible. Advisory sur le contenu ne veut pas dire silencieux sur sa propre santé.planned / confirmed / faileden tête de sortie, et un test vérifie qu'un échec d'écriture simulé fait bien rougir. Un compteur qui ne peut pas rendre autre chose quesuccessne mesure rien.6. Ce qui n'est PAS demandé ici
7. La part du coordinateur
Les deux collisions du jour se sont ouvertes parce qu'aucun
[CLAIMED]n'était posé sur #13830 ni sur #13845. Poser le claim au dispatch, au nom de la lane servie, est la charge du coordinateur (R5 decoordinator-discipline.md), pas celle des workers. Le garde ci-dessus est le filet ; le claim est la première barrière, et elle est restée ouverte.Grain: MED/guard — lane myia-ai-01:CoursIA