Grain: LIGHT/docs -- lane myia-ai-01:CoursIA -- prev: MED/notebook-python #13466
Le defaut
canonicalize_genre() (scripts/variation_light_cap.py) rend le mot d'entree verbatim quand il n'est ni dans GENRES, ni dans _GENRE_ALIASES, ni un compose <famille>-<genre> dont la tete est aliasee. Ce mot inconnu :
- compte dans le denominateur (
lane_grains, donc dans light_budget(n) = max(1, n//3) — il augmente le budget) ;
- echappe au numerateur (
LIGHT_GENRES ne le contient pas, donc light_genre ne l'incremente pas).
C'est exactement a l'envers : un mot non reconnu relache le cap au lieu de le serrer. Et il ouvre le meme trou sur G-VAR-3 — un genre inconnu ne peut jamais egaler le precedent, donc l'adjacence est inatteignable par simple choix de mot. C'est le trou que #12158 avait deja bouche au cas par cas (scripts, infra, csp) sans fermer la classe.
Mesure — pas une hypothese
Sur la seule journee du 2026-08-29, lane myia-po-2023:CoursIA-2, 16 grains mergés :
prose est le cas net : une PR de prose declaree LIGHT, qui ne consomme pas le budget LIGHT. Le compte rendu par l'organe etait light_genre: 6 (7 avec la candidate #13461) ; le compte vrai est au moins 7 (8 avec la candidate).
Verification directe :
$ python -c "import sys; sys.path.insert(0,'scripts'); from variation_light_cap import canonicalize_genre, LIGHT_GENRES
for g in ['prose','documentation','layout','ict','notebook']:
c=canonicalize_genre(g); print('%-14s -> %-14s light=%s' % (g,c,c in LIGHT_GENRES))"
prose -> prose light=False
documentation -> documentation light=False
layout -> layout light=False
ict -> ict light=False
notebook -> notebook light=False
documentation est le plus parlant : le texte de variation-protocol.md §1 le nomme explicitement parmi les synonymes qui « se normalisent » (test-coverage -> test, documentation -> docs, data -> ledger, slidev -> slides). Trois des quatre sont dans la table ; celui-la n'y est pas. La regle et son organe divergent, et la divergence est silencieuse.
Ce qu'il faut, et ce qu'il ne faut pas
Pas une entree d'alias de plus par mot rencontre : c'est la boucle qui a produit le defaut (chaque incident ajoute son mot, le suivant passe). L'enumeration de variation-protocol.md §GENRE est CLOSE par intention — « La fermeture protege G-VAR-3, qu'un vocabulaire ouvert rendrait inatteignable par simple choix de mot ».
Deux pieces :
- Fermer la chute : un genre non resoluble ne doit pas etre traite comme « pas light ». Options a trancher dans la PR — (a) le classer light par defaut (fail-CLOSED : le cap se serre au lieu de se relacher) ; (b) le remonter en signal
GENRE-UNKNOWN distinct, compte a part et visible dans le JSON, pour que « je n'ai pas su lire » ne se confonde jamais avec « ce n'est pas light ». Ma preference : (b) + (a) — signaler ET compter light, parce qu'un silence qui relache est pire qu'un faux positif qui se conteste.
- Aligner la table sur le texte de la regle : au minimum
documentation -> docs, prose -> docs, notebook -> ? (ce dernier est ambigu — notebook-python / notebook-dotnet / notebook-lean existent ; notebook nu doit probablement etre refuse en GENRE-UNKNOWN plutot que devine).
Controle positif exige
Un test qui, avec le correctif retire, echoue — et pas seulement un test qui passe :
prose, documentation comptent light apres correctif, et le test les voit non-light avant ;
- un mot invente (
zzz-inexistant) leve GENRE-UNKNOWN ;
- les genres canoniques (
notebook-python, research-code) ne regressent pas en light (controle negatif — c'est ce que le guard verbatim protegeait au depart, cf le KeyError: 'notebook-python' du 2026-08-08 documente dans la docstring).
Sans ce controle par faux negatifs, le correctif est indistinguable d'un no-op.
Contexte
Mandat user du 2026-08-28 sur la monoculture : « c'est donc l'echec de ton organe qu'il faut completement repenser ». Ce ticket ne porte pas le picker (c'est #13466) mais le cap qui mesure la monoculture apres coup — un cap qui sous-compte donne un vert qui ne prouve rien, exactement le motif de [[handrolled-pattern-set-undercounts-silently]] : un jeu de motifs se valide par ses faux negatifs, jamais par ses hits.
Constat de terrain : merge de #13461 (2026-08-29T08:39Z).
Grain: LIGHT/docs -- lane myia-ai-01:CoursIA -- prev: MED/notebook-python #13466Le defaut
canonicalize_genre()(scripts/variation_light_cap.py) rend le mot d'entree verbatim quand il n'est ni dansGENRES, ni dans_GENRE_ALIASES, ni un compose<famille>-<genre>dont la tete est aliasee. Ce mot inconnu :lane_grains, donc danslight_budget(n) = max(1, n//3)— il augmente le budget) ;LIGHT_GENRESne le contient pas, donclight_genrene l'incremente pas).C'est exactement a l'envers : un mot non reconnu relache le cap au lieu de le serrer. Et il ouvre le meme trou sur G-VAR-3 — un genre inconnu ne peut jamais egaler le precedent, donc l'adjacence est inatteignable par simple choix de mot. C'est le trou que #12158 avait deja bouche au cas par cas (
scripts,infra,csp) sans fermer la classe.Mesure — pas une hypothese
Sur la seule journee du 2026-08-29, lane
myia-po-2023:CoursIA-2, 16 grains mergés :proseproselayoutlayoutictictnotebooknotebookproseest le cas net : une PR de prose declaree LIGHT, qui ne consomme pas le budget LIGHT. Le compte rendu par l'organe etaitlight_genre: 6(7 avec la candidate #13461) ; le compte vrai est au moins 7 (8 avec la candidate).Verification directe :
documentationest le plus parlant : le texte devariation-protocol.md§1 le nomme explicitement parmi les synonymes qui « se normalisent » (test-coverage->test,documentation->docs,data->ledger,slidev->slides). Trois des quatre sont dans la table ; celui-la n'y est pas. La regle et son organe divergent, et la divergence est silencieuse.Ce qu'il faut, et ce qu'il ne faut pas
Pas une entree d'alias de plus par mot rencontre : c'est la boucle qui a produit le defaut (chaque incident ajoute son mot, le suivant passe). L'enumeration de
variation-protocol.md§GENRE est CLOSE par intention — « La fermeture protege G-VAR-3, qu'un vocabulaire ouvert rendrait inatteignable par simple choix de mot ».Deux pieces :
GENRE-UNKNOWNdistinct, compte a part et visible dans le JSON, pour que « je n'ai pas su lire » ne se confonde jamais avec « ce n'est pas light ». Ma preference : (b) + (a) — signaler ET compter light, parce qu'un silence qui relache est pire qu'un faux positif qui se conteste.documentation->docs,prose->docs,notebook-> ? (ce dernier est ambigu —notebook-python/notebook-dotnet/notebook-leanexistent ;notebooknu doit probablement etre refuse enGENRE-UNKNOWNplutot que devine).Controle positif exige
Un test qui, avec le correctif retire, echoue — et pas seulement un test qui passe :
prose,documentationcomptent light apres correctif, et le test les voit non-light avant ;zzz-inexistant) leveGENRE-UNKNOWN;notebook-python,research-code) ne regressent pas en light (controle negatif — c'est ce que le guard verbatim protegeait au depart, cf leKeyError: 'notebook-python'du 2026-08-08 documente dans la docstring).Sans ce controle par faux negatifs, le correctif est indistinguable d'un no-op.
Contexte
Mandat user du 2026-08-28 sur la monoculture : « c'est donc l'echec de ton organe qu'il faut completement repenser ». Ce ticket ne porte pas le picker (c'est #13466) mais le cap qui mesure la monoculture apres coup — un cap qui sous-compte donne un vert qui ne prouve rien, exactement le motif de [[handrolled-pattern-set-undercounts-silently]] : un jeu de motifs se valide par ses faux negatifs, jamais par ses hits.
Constat de terrain : merge de #13461 (2026-08-29T08:39Z).