Le défaut
Une clause paths: syntaxiquement valide qui nomme un chemin inexistant produit un claim qui ne bloque personne, et l'organe le rapporte comme parfaitement sain : malformed_markers: 0, blocking_lanes: [], active_claims: {}.
C'est la polarité inverse de #12072. Là-bas, une clause paths: hors ligne de marqueur bascule le claim en epic-wide et gèle tout le monde — fail-CLOSED, conforme à la garantie écrite. Ici la clause est sur la bonne ligne, bien formée, et fail-OPEN : elle ouvre le périmètre au lieu de le fermer.
lane-claim-protocol.md promet pourtant l'inverse, noir sur blanc :
toute déviation fabrique un scope mort et fait passer la claim en epic-wide (fail-CLOSED : un claim cassé n'est PAS permissif, il bloque toutes les autres lanes)
La garantie tient pour les déviations syntaxiques (accolade non fermée, annotation non délimitée, glob sans / ni métacaractère — #10597, #10958, #11064, #12052). Elle ne tient pas pour la déviation sémantique : un glob bien formé qui ne correspond à aucun fichier du dépôt.
Mesure firsthand — c'est ce défaut qui a produit une livraison en double
Issue #12620, claim posé le 2026-08-23T22:45:32Z par myia-po-2023:CoursIA :
[CLAIMED] #12620 — myia-po-2023:CoursIA 2026-08-24T02:10Z —
paths: scripts/notebook_tools/check_code_in_markdown.py — durcir le predicat …
Le fichier nommé n'existe pas. Le détecteur s'appelle detect_code_in_markdown_cells.py :
$ ls scripts/notebook_tools/ | grep -iE "code.*markdown"
code_in_markdown_cells_baseline.json
detect_code_in_markdown_cells.py
$ test -e scripts/notebook_tools/check_code_in_markdown.py && echo OUI || echo NON
NON
Les deux PR qui ont travaillé l'issue touchent, l'une comme l'autre, les mêmes deux fichiers réels :
| PR |
lane |
fichiers |
| #12693 (mergée 01:22:57Z) |
myia-po-2023:CoursIA |
detect_code_in_markdown_cells.py, code_in_markdown_cells_baseline.json |
| #12621 (fermée, doublon) |
myia-ai-01:CoursIA |
les mêmes deux |
check_code_in_markdown.py ∩ detect_code_in_markdown_cells.py = ∅. Le claim ne pouvait donc bloquer aucune lane — y compris celle de son propre auteur, dont la PR touchait un chemin hors de son scope déclaré.
Le WARN existe, et il ne sort pas là où on le lit
L'organe détecte la condition — mais sur stderr, hors du JSON :
$ python scripts/check_lane_claim.py 12620 --lane myia-ai-01:CoursIA
WARN: glob sans correspondance : "scripts/notebook_tools/check_code_in_markdown.py" (lane myia-po-2023:CoursIA)
{
"my_lane": "myia-ai-01:CoursIA",
"blocking_lanes": [],
"active_claims": {},
"unattributed_markers": 0,
"malformed_markers": 0,
"malformed_marker_lines": [],
...
}
$ python scripts/check_lane_claim.py 12620 --lane myia-ai-01:CoursIA 2>/dev/null | grep -iE "warn|unmatch|glob"
(rien — seul "caller_empty_scope": [] existe, qui porte autre chose)
Les appelants — CI, pick_idle_grain, scripts de lane — lisent le JSON. Le seul canal qui porte l'information est celui que personne ne consomme. malformed_markers: 0 affirme positivement que rien ne cloche.
Pourquoi ça compte plus qu'un cas isolé
Sur les 5 livraisons en double constatées en deux jours, 3 se sont produites malgré un [CLAIMED] posé :
| issue |
commentaires |
marqueurs |
lanes ayant claim |
| #12329 |
13 |
7 |
po-2023:CoursIA-2, po-2024:CoursIA, po-2024:CoursIA-2 |
| #12478 |
7 |
6 |
po-2025:CoursIA, po-2027:CoursIA |
| #12340 |
4 |
2 |
po-2026:CoursIA-2 |
| #12489 |
9 |
0 |
— aucune — |
| #12620 |
2 |
1 |
— aucune (scope mort, ci-dessus) — |
Le marqueur est posé et le doublon arrive quand même. Ce n'est pas une discipline de worker à resserrer : deux des cinq doublons sont de moi, coordinateur, dont celui-ci.
Correctif proposé
-
Porter la condition dans le JSON : un champ dead_scope_globs (ou unmatched_scope_globs) listant, par lane, les globs qui ne correspondent à aucun chemin suivi. Un appelant qui lit le JSON doit pouvoir la voir sans lire stderr.
-
Trancher la politique, explicitement, dans lane-claim-protocol.md — deux options, et je ne m'auto-accorde pas le choix :
- (a) fail-CLOSED, cohérent avec la garantie écrite : un scope dont aucun glob ne matche est traité comme absent → claim epic-wide. Aligne le comportement sur la phrase déjà publiée ; risque de geler une lane dont le fichier est légitimement à créer (nouveau module).
- (b) signaler sans bloquer : le claim reste scopé, mais le JSON expose
dead_scope_globs non vide et le gate lane-claim-guard passe en advisory rouge sur l'issue. Ne gèle personne, rend l'erreur visible à celui qui l'a écrite.
L'option (a) a pour elle la cohérence avec le texte ; (b) a pour elle le cas légitime du fichier pas encore créé — fréquent sur un grain Lean ou un nouveau notebook, où le scope nomme la cible avant qu'elle existe. Ce cas légitime est réel et fait pencher vers (b) + une exigence de forme : quand le glob vise un fichier à créer, l'écrire avec un métacaractère de répertoire (scripts/notebook_tools/*markdown*) plutôt qu'un chemin littéral, ce qui matche à la fois l'existant et le futur.
-
Test de non-régression avec son contrôle positif : un claim à scope mort ne doit pas rendre blocking_lanes: [] silencieusement ; et un claim à scope vivant doit continuer à bloquer — sans le second, un garde qui refuserait tout serait indiscernable d'un détecteur qui marche.
Ce que cette issue ne demande pas
Aucune sanction de lane. myia-po-2023:CoursIA a posé son claim correctement, dans les temps, avec une clause paths: — la discipline était bonne, c'est la faute de frappe qui est passée à travers un organe conçu pour l'attraper. La PR gagnante #12693 est la bonne livraison ; c'est la mienne qui était le doublon.
Refs #12072 (polarité inverse, fail-closed) · #12719 (extract_lane, lane fantôme) · #12327 (lint sur marqueurs superseded)
Le défaut
Une clause
paths:syntaxiquement valide qui nomme un chemin inexistant produit un claim qui ne bloque personne, et l'organe le rapporte comme parfaitement sain :malformed_markers: 0,blocking_lanes: [],active_claims: {}.C'est la polarité inverse de #12072. Là-bas, une clause
paths:hors ligne de marqueur bascule le claim en epic-wide et gèle tout le monde — fail-CLOSED, conforme à la garantie écrite. Ici la clause est sur la bonne ligne, bien formée, et fail-OPEN : elle ouvre le périmètre au lieu de le fermer.lane-claim-protocol.mdpromet pourtant l'inverse, noir sur blanc :La garantie tient pour les déviations syntaxiques (accolade non fermée, annotation non délimitée, glob sans
/ni métacaractère — #10597, #10958, #11064, #12052). Elle ne tient pas pour la déviation sémantique : un glob bien formé qui ne correspond à aucun fichier du dépôt.Mesure firsthand — c'est ce défaut qui a produit une livraison en double
Issue #12620, claim posé le 2026-08-23T22:45:32Z par
myia-po-2023:CoursIA:Le fichier nommé n'existe pas. Le détecteur s'appelle
detect_code_in_markdown_cells.py:Les deux PR qui ont travaillé l'issue touchent, l'une comme l'autre, les mêmes deux fichiers réels :
myia-po-2023:CoursIAdetect_code_in_markdown_cells.py,code_in_markdown_cells_baseline.jsonmyia-ai-01:CoursIAcheck_code_in_markdown.py∩detect_code_in_markdown_cells.py= ∅. Le claim ne pouvait donc bloquer aucune lane — y compris celle de son propre auteur, dont la PR touchait un chemin hors de son scope déclaré.Le WARN existe, et il ne sort pas là où on le lit
L'organe détecte la condition — mais sur
stderr, hors du JSON :Les appelants — CI,
pick_idle_grain, scripts de lane — lisent le JSON. Le seul canal qui porte l'information est celui que personne ne consomme.malformed_markers: 0affirme positivement que rien ne cloche.Pourquoi ça compte plus qu'un cas isolé
Sur les 5 livraisons en double constatées en deux jours, 3 se sont produites malgré un
[CLAIMED]posé :Le marqueur est posé et le doublon arrive quand même. Ce n'est pas une discipline de worker à resserrer : deux des cinq doublons sont de moi, coordinateur, dont celui-ci.
Correctif proposé
Porter la condition dans le JSON : un champ
dead_scope_globs(ouunmatched_scope_globs) listant, par lane, les globs qui ne correspondent à aucun chemin suivi. Un appelant qui lit le JSON doit pouvoir la voir sans lirestderr.Trancher la politique, explicitement, dans
lane-claim-protocol.md— deux options, et je ne m'auto-accorde pas le choix :dead_scope_globsnon vide et le gatelane-claim-guardpasse en advisory rouge sur l'issue. Ne gèle personne, rend l'erreur visible à celui qui l'a écrite.L'option (a) a pour elle la cohérence avec le texte ; (b) a pour elle le cas légitime du fichier pas encore créé — fréquent sur un grain Lean ou un nouveau notebook, où le scope nomme la cible avant qu'elle existe. Ce cas légitime est réel et fait pencher vers (b) + une exigence de forme : quand le glob vise un fichier à créer, l'écrire avec un métacaractère de répertoire (
scripts/notebook_tools/*markdown*) plutôt qu'un chemin littéral, ce qui matche à la fois l'existant et le futur.Test de non-régression avec son contrôle positif : un claim à scope mort ne doit pas rendre
blocking_lanes: []silencieusement ; et un claim à scope vivant doit continuer à bloquer — sans le second, un garde qui refuserait tout serait indiscernable d'un détecteur qui marche.Ce que cette issue ne demande pas
Aucune sanction de lane.
myia-po-2023:CoursIAa posé son claim correctement, dans les temps, avec une clausepaths:— la discipline était bonne, c'est la faute de frappe qui est passée à travers un organe conçu pour l'attraper. La PR gagnante #12693 est la bonne livraison ; c'est la mienne qui était le doublon.Refs #12072 (polarité inverse, fail-closed) · #12719 (
extract_lane, lane fantôme) · #12327 (lint sur marqueurs superseded)