Repository navigation
tooling(#15405): detecteur paragraphes markdown trop longs + workflow advisory - #15455
Conversation
… advisory Le README Probas (PR #15405, commit 76d7a5b, remarque user 2026-09-10) portait un paragraphe unique de 3336 caracteres / 24 phrases sur une seule ligne physique -- et aucun garde CI ne l'a signale. Cet organe est l'instrument qui aurait du l'intercepter. ## Nouveaux fichiers - `scripts/notebook_tools/detect_paragraph_length.py` : detecteur (stdlib uniquement) qui signale tout paragraphe markdown > 2000 c. Ignore les fences code (```/~~~), lignes de tableau (`|`), titres (`#` ... `######`), commentaires HTML (`<!-- ... -->` dont CATALOG-STATUS), directives Sphinx. Listes et blockquotes **comptent** (la calibration confirme : un item de 2500 c est un mur). CLI : `--json` (sortie CI-friendly), `--fail-on-findings`, `--self-test`. - `scripts/notebook_tools/tests/test_detect_paragraph_length.py` + `tests/fixtures/paragraph_wall_md.md` : 12 tests, tous verts. Couverture : temoin fondateur tire, post-fix muet, seuil pile 2000 / 2001 exact, fences/tableaux/CATALOG-STATUS ignores, listes longues signalees, tri des findings par longueur decroissante. - `.github/workflows/paragraph-length-advisory.yml` : workflow advisory per-PR, label `paragraph-length` (jamais bloquant a ce stade). Self-cover (#8822). Permission same-repo only. #8819 guard (payload illisible => label `-unmeasured`, jamais de claim vert). ## Fichiers modifies - `scripts/notebook_tools/README.md` : section dediee au detecteur, mention dans la table des detecteurs anti-regression. ## Calibration rglob sur 791 `*.md` / 23049 paragraphes : p50=79 p75=274 p90=536 p95=760 p99=1437 max=13409 c > 2000 c : 42 fichiers / 84 paragraphes (hors _archives, vendored) Le seuil 2000 capture l'incident fondateur avec ~1.6x de marge. ## Hors scope (issue de suivi a ouvrir) Les 42 fichiers pre-existants > 2000 c ne sont PAS corriges ici (sweep de resorption = sujet separe, ouverture d'issue a la suite de cette PR). Adresser ce pre-existant dans cette PR ferait un composite > 1 domaine (regle G.4). ## Bascule blocking Decision user 2026-09-10 : **advisory d'abord**. Bascule bloquante = PR dediee apres que le sweep de resorption soit a zero et stable. Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
|
Issue de suivi pour la resorption des fichiers > 2000 c : #15457 (liste les 23 fichiers actifs restants apres cette PR). |
|
Issue de suivi pour la résorption des fichiers > 2000 c : #15457 (liste les 23 fichiers actifs restants après cette PR). |
|
Grain tag obligatoire (#10045, bloquant).
Pour passer ce gate, le body doit porter en tete une ligne de la forme : Le |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[Hermes] — CoursIA#15455 review (b8bd1df, opener=jsboige, contrainte token : COMMENT only).
Organe de détection paragraphes-murs (#15405) — vérifié :
- Calibration au-dessus de la moyenne : percentiles p50/p95/p99 + table seuil→flaggés (2000 c = 42 fichiers, l'incident fondateur 3336 c capturé avec 1,6× de marge). Le seuil n'est pas tiré d'un chapeau. ✓
- État fences correct (
in_fencetoggle, les deux fermetures ```/~~~ testées), ignore titres/tableaux/commentaires HTML/Sphinx directives/alertes![NOTE]— les 4 classes d'ignorance ont chacune un self_test dédié + 12 tests pytest + fixture externe à 5 sections (dont « post-fix muet » = garde anti-régression sur le README Probas corrigé). ✓ - Sémantique paragraphe bien choisie : une ligne de tableau au milieu d'un paragraphe NE CASSE PAS le bloc (skip de structure, prose conservée) — c'est le bon choix, sinon un mur traversé par une ligne de table passerait inaperçu. ✓
- Workflow advisory propre : exit 0 garanti, label = signal, self-cover #8822 (le workflow se liste dans ses propres
paths:), labels idempotents ensure/set/unset, et le labelparagraph-length-unmeasuredévite le silence quand la mesure manque — pattern fail-visible, pas fail-silent. ✓ - Issue de suivi #15457 pour les 23 fichiers restants — la resorption n'est pas noyée dans cette PR. ✓
Housekeeping (déjà signalé par le gate bot, je ne fais que confirmer) : le body manque la ligne Grain: <TIER>/<GENRE> (#10045) — l'edit du body suffira, aucun commit nécessaire.
Rien de bloquant sur le code.
(opener=jsboige, contrainte token : COMMENT only)
…/2374/2144 c → 14+2+2 paragraphes ≤ 1300 c) Sous-grain du sweep #15457 (résorption des fichiers markdown actifs > 2000 c, gate baseline pour bascule bloquante de detect_paragraph_length). Le README de la série Search/Part4-Metaheuristics portait 3 paragraphes-mur : - L. 311 (9972 c) : bloc 'Ce que vous avez appris' de 14 bullets MGS-1..21 agrégés en un paragraphe contigu. Solution : insérer une ligne vide entre chaque bullet → 14 paragraphes indépendants (max 1229 c). - L. 171 (2374 c) : 'La grammaire de composition...' — paragraphe tenant sur 1 ligne, mélangeant 2 sujets (agnosticisme représentation + cadrage honnête G.9). Découpé en 2 paragraphes au point 'Cadrage honnête (G.9)'. - L. 201 (2144 c) : 'Lecture honnête (G.9), fonction par fonction...' — paragraphe 1 ligne, 3 résultats (Sphere/Rastrigin/Ackley) + leçon. Découpé en 2 paragraphes au point 'Leçon affûtée'. Detector (PR #15455) : - Avant : 3 findings (9972, 2374, 2144 c) - Après : 0 findings Aucun mot, aucun nombre, aucune référence modifié. Seules les frontières de paragraphes changent. Voir #15457 (sweep), #15455 (detector), #15454 (PR de référence Probas). Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
Path-collision (organ #13359/#13615)Cette PR #15455 (
|
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels |
…self-hosted Le workflow livre par cette PR porte runs-on [self-hosted, coursia-ephemeral, coursia-linux] sans entree correspondante dans SELF_HOSTED_WORKFLOW_ALLOWLIST, ce qui faisait echouer test_current_repository_self_hosted_jobs_satisfy_isolation_policy (WORKFLOW_NOT_ALLOWED) -- 1 echec sur 12711 tests. Decision ai-01 : le workflow satisfait le profil exige par la rationale de la liste (garde same-repo au niveau job #13874, scan Python pur stdlib-only, GITHUB_TOKEN borne a l'API labels, jamais exit != 0), soit exactement celui de markdown-deaccent-advisory.yml et repeated-prose-advisory.yml deja admis. L'entree porte sa rationale et son rollback, comme les tranches precedentes. Mesure locale apres correctif : 58/58 sur scripts/tests/test_check_self_hosted_runner_policy.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
[coordinateur — ai-01] Rouge réparé par moi-même : c'est ma lane, donc mon rouge.
Cause : le workflow livré par cette PR porte Design-gate tranché plutôt que différé. L'admission au pool self-hosted est une décision coordinateur — chaque tranche de cette liste en porte une, nommée. Je l'accorde : le workflow satisfait point par point le profil que la rationale de la liste exige, vérifié sur son source et non supposé —
C'est exactement le profil de Correctif poussé : Les deux |
Detection pre-fix par scripts/notebook_tools/detect_paragraph_length.py (PR #15455, sweep baseline #15457 fichiers actifs > 2000 c) : L.56 : 7638 c monoligne (B1..B9 + connecteurs) -> 10 paragraphes <= 2000 c L.83 : 9721 c (15 bullets Notebook 01..18 agreg.) -> 15 paragraphes L.272 : 2709 c (5 bullets geste fondateur agreg.) -> 5 paragraphes Aucune modification de contenu : seules les frontieres de paragraphes changent (insertion de '\n\n' au point de transition semantique / lignes vides entre bullets contigus). Substantifique moelle strictement preservee -- le detecteur ne voit que la structure, pas la prose. Post-fix : detecteur `findings: []` (counts.total = 0). Sweep #15457 progression 3/24 (CaseStudies/README.md #15464, Search/Part4-Metaheuristics #15465, Z3-Linq2Z3/README.md cette PR).
Detection pre-fix par scripts/notebook_tools/detect_paragraph_length.py (PR #15455, sweep baseline #15457 fichiers actifs > 2000 c) : L.56 : 7638 c monoligne (B1..B9 + connecteurs) -> 10 paragraphes <= 2000 c L.83 : 9721 c (15 bullets Notebook 01..18 agreg.) -> 15 paragraphes L.272 : 2709 c (5 bullets geste fondateur agreg.) -> 5 paragraphes Aucune modification de contenu : seules les frontieres de paragraphes changent (insertion de '\n\n' au point de transition semantique / lignes vides entre bullets contigus). Substantifique moelle strictement preservee -- le detecteur ne voit que la structure, pas la prose. Post-fix : detecteur `findings: []` (counts.total = 0). Sweep #15457 progression 3/24 (CaseStudies/README.md #15464, Search/Part4-Metaheuristics #15465, Z3-Linq2Z3/README.md cette PR).
…EADME.md (#15466) Sous-grain du sweep #15457 (resorption des fichiers markdown actifs > 2000 c). Trois paragraphes-mur detectes par detect_paragraph_length.py sont eclates en paragraphes courts, sans perte de contenu. Mesure coordinateur avant merge (B.0 personnelle, tete c7b65f8) : - Perimetre : un seul fichier, MyIA.AI.Notebooks/SymbolicAI/SMT/Z3-Linq2Z3/README.md, +81/-1. Aucun catalogue touche, aucun notebook (H.4 sans objet). - Reserve Hermes du 2026-09-10T14:27:40Z (fractionnement a largeur fixe, mots coupes en deux au head 6894c76) : levee par moi en tiers le 2026-09-11T04:34:19Z apres re-mesure, et non par l'auteur. - Ma CHANGES_REQUESTED du 2026-09-11T05:20:58Z (blocs residuels de 2770 c et 4867 c contre un en-tete promettant <= 2000 c) est levee par ma review APPROVED du 2026-09-11T07:11:52Z, posee sur la tete courante. - Mon HOLD G-VAR-2 etait une erreur de mesure de ma part, retractee publiquement le 2026-09-11T07:38:04Z : j'avais alimente variation_light_cap.py avec 32 entrees du jour la ou l'axe genre est defini sur une fenetre de 21 jours. Le budget n'est pas atteint. - G-VAR-3 : deuxieme grain readme consecutif de la lane (prev #15465), exempte par la clause mecanique #14357 -- second grain de tier MED, et intersection des fichiers vide (Search/Part4-Metaheuristics/README.md contre SymbolicAI/SMT/Z3-Linq2Z3/README.md). - Gates : PR gate success a 08:07:00Z apres reagregation du balayage pr-gate-stale-sweep ; zero check-run non-vert sur la tete ; check_unaddressed_nits rc=0. See #15457 See #15455 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nce passe sur main (#15591) Le pool de candidats etait `--state open` exclusivement : une collision DISPARAISSAIT du rapport a l'instant precis ou elle devenait irreversible (un cote merge). L'organe etait donc le plus bruyant tant que le risque etait theorique, et muet une fois la substance sur `main`. Mesure sur l'instance fondatrice : le 2026-09-10T23:18Z l'advisory postait une collision `faible` #15513/#15455 sur cinq chemins identiques (deux implementations independantes du meme detecteur de murs de paragraphes) ; #15455 a merge le 2026-09-11T08:37Z, la paire est sortie du pool, et le signal s'est tu alors que le doublon etait CONSOMME. - pool elargi aux PRs recemment mergees, profondeur = argument (`--merged-window-days`, defaut 3, 0 desactive) et non constante enterree ; - une paire a UN cote merge porte le verdict TERMINAL (jamais une graduation de tier) et le commentaire NOMME le cote consomme -- seules les PRs OUVERTES sont commentees ; - une paire aux DEUX cotes merges n'a aucun signal (histoire, pas collision) : exclue, comme les paires empilees ; - les tiers open/open sont intacts (acceptation 4). Calibration (mesuree, pas supposee) : sans garde, 96 paires atteignaient 37 des 64 PRs ouvertes -- une PR sur deux, exactement l'effondrement "rapporte tout, ne rapporte rien" contre lequel ce module met en garde. La garde `min(recouvrement des DEUX cotes) >= 0.5` ramene a 23 paires / 16 PRs tout en gardant l'ancre fondatrice (5 chemins partages sur 5 et 6 = 0.83). Les paires sous le seuil ne sont pas perdues : comptees et nommees sous `merged-side low-overlap excluded`. Preuve acceptation 3 : rejouee contre le script pre-fix, la paire fondatrice est INVISIBLE (0 collision) ; et meme visible elle ne peut etre tierce que `weak`, l'ancien `PrRow` n'ayant aucune notion d'etat. Self-test 14/14, suite unitaire 65 passed. See #15578 Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…nce passe sur main (#15591) Le pool de candidats etait `--state open` exclusivement : une collision DISPARAISSAIT du rapport a l'instant precis ou elle devenait irreversible (un cote merge). L'organe etait donc le plus bruyant tant que le risque etait theorique, et muet une fois la substance sur `main`. Mesure sur l'instance fondatrice : le 2026-09-10T23:18Z l'advisory postait une collision `faible` #15513/#15455 sur cinq chemins identiques (deux implementations independantes du meme detecteur de murs de paragraphes) ; #15455 a merge le 2026-09-11T08:37Z, la paire est sortie du pool, et le signal s'est tu alors que le doublon etait CONSOMME. - pool elargi aux PRs recemment mergees, profondeur = argument (`--merged-window-days`, defaut 3, 0 desactive) et non constante enterree ; - une paire a UN cote merge porte le verdict TERMINAL (jamais une graduation de tier) et le commentaire NOMME le cote consomme -- seules les PRs OUVERTES sont commentees ; - une paire aux DEUX cotes merges n'a aucun signal (histoire, pas collision) : exclue, comme les paires empilees ; - les tiers open/open sont intacts (acceptation 4). Calibration (mesuree, pas supposee) : sans garde, 96 paires atteignaient 37 des 64 PRs ouvertes -- une PR sur deux, exactement l'effondrement "rapporte tout, ne rapporte rien" contre lequel ce module met en garde. La garde `min(recouvrement des DEUX cotes) >= 0.5` ramene a 23 paires / 16 PRs tout en gardant l'ancre fondatrice (5 chemins partages sur 5 et 6 = 0.83). Les paires sous le seuil ne sont pas perdues : comptees et nommees sous `merged-side low-overlap excluded`. Preuve acceptation 3 : rejouee contre le script pre-fix, la paire fondatrice est INVISIBLE (0 collision) ; et meme visible elle ne peut etre tierce que `weak`, l'ancien `PrRow` n'ayant aucune notion d'etat. Self-test 14/14, suite unitaire 65 passed. See #15578 Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Grain: MED/tooling — lane myia-ai-01:CoursIA — prev: MED/docs #15500
Résumé
Le README Probas (PR #15405, commit
76d7a5bc) livrait un paragraphe unique de 3336 caractères / 24 phrases sur une seule ligne physique (« Cette série couvre trois stacks… »). Remarque user 2026-09-10 : « IL aurait dû être intercepté par le CI comme un bloc sans espaces indigeste ».Cette PR ajoute l'organe manquant : un détecteur de paragraphes markdown trop longs + un workflow CI advisory qui pose un label sur la PR.
PR A (séparée,
docs/15405-probas-paragraph-wall) corrige le contenu du README Probas — découpage en 6 paragraphes aérés.Périmètre
Cette PR touche 6 fichiers et uniquement ceux-ci :
.github/workflows/paragraph-length-advisory.yml,scripts/ci/check_self_hosted_runner_policy.py,scripts/notebook_tools/detect_paragraph_length.py,scripts/notebook_tools/tests/test_detect_paragraph_length.py,scripts/notebook_tools/tests/fixtures/paragraph_wall_md.md,scripts/notebook_tools/README.md.Changements
scripts/notebook_tools/detect_paragraph_length.pyre) — signale tout paragraphe markdown > 2000 cscripts/notebook_tools/tests/test_detect_paragraph_length.pyscripts/notebook_tools/tests/fixtures/paragraph_wall_md.md.github/workflows/paragraph-length-advisory.ymlparagraph-lengthnon-bloquant, self-cover #8822, #8819 guardscripts/notebook_tools/README.md### detect_paragraph_length.py (#15405)+ entrée dans la table des détecteursscripts/ci/check_self_hosted_runner_policy.pyparagraph-length-advisory.ymlàSELF_HOSTED_WORKFLOW_ALLOWLIST, avec sa rationale, son owner et son rollback (voir ci-dessous)Pourquoi le seuil 2000
Calibration 2026-09-10, rglob sur 791
*.md/ 23 049 paragraphs (excluant_archives, submodules, vendored) :*.mdflaggésLe seuil 2000 capture l'incident fondateur (3336 c) avec ~1.6× de marge. Au-dessus, la mesure commence à mordre sur de la prose technique légitime ; en-dessous, on perd l'incident fondateur sur certaines reformulations.
Comportement du détecteur
|), titres (`#` à `######`), commentaires HTML (`` dont le marqueur `CATALOG-STATUS`), directives Sphinx (`^:[a-z]+:`), alertes markdown (`![NOTE]`).--json(sortie agrégée{files: [...], summary: {...}}CI-friendly),--fail-on-findings(exit 2),--self-test(joue le témoin fondateur + 4 contrôles négatifs sans I/O).Workflow CI
.github/workflows/paragraph-length-advisory.yml— advisory d'abord (décision user 2026-09-10),exit 0toujours. Signaux :paragraph-length(jauneFBCA04) : au moins un paragraphe > 2000 c détecté sur un*.mdmodifié par la PR.paragraph-length-unmeasured(bleuBFD4F2) : payload illisible, mesure NOT done (ci(exercises-advisory): un notebook illisible est compte comme conforme (parse_errors absent de la decision de label) #8819 — jamais de claim vert sans preuve).Câblage :
pull_requestsurbranches: [main],paths: ['**/*.md', '.github/workflows/paragraph-length-advisory.yml'](self-cover #8822),runs-on: [self-hosted, coursia-ephemeral, coursia-linux], same-repoif:(policycheck_self_hosted_runner_policy.py).Diff 3 points
merge-base...HEAD,--diff-filter=d, sans scan repo-wide.Preuve : le détecteur fonctionne sur l'incident fondateur
Bascule bloquante
Décision user 2026-09-10 : advisory d'abord, pas bloquant à ce stade. Bascule = PR dédiée après que le sweep de résorption sur les 42
*.mdpré-existants soit à zéro et stable.Vérification
Hors scope (issue de suivi #15457)
Les 42
*.mdpré-existants du corpus dont un paragraphe dépasse déjà 2000 c ne sont pas corrigés ici. La résorption exhaustive est un sweep multi-PR qui dépasse le périmètre de cet organe (composite > 1 domaine, règle G.4) — elle est suivie par #15457, qui les liste avec leur longueur actuelle.🤖 Generated with Claude Code
Admission à l'allow-list self-hosted (design-gate coordinateur, 2026-09-11)
Le 6ᵉ fichier n'est pas un élargissement de périmètre : c'est le défaut propre de cette PR, révélé par son propre test.
Scripts Tests (CPU)a rougi sur exactement 1 test des 12 711 :SELF_HOSTED_WORKFLOW_ALLOWLISTest une liste curée : tout workflow tournant sur[self-hosted, coursia-ephemeral, coursia-linux]doit y figurer nommément, avec une rationale citant une décision, un owner et un rollback. Le workflow introduit ici n'y figurait pas — la PR ne pouvait donc pas passer son propre gate, et c'était correct.L'admission est une décision de design réservée au coordinateur, tranchée ici plutôt que déférée. Le profil exigé par la liste a été vérifié point par point sur la source du workflow, pas sur son intention :
if: github.event.pull_request.head.repo.full_name == github.repository— les PRs de fork se font skipper proprement parpr_gatedetect_paragraph_length.pyn'importe quereet la stdlib — aucune dépendance installée sur le runnerGITHUB_TOKENborné à l'API labelspull-requests: write, aucun autre scope ; le token ne sert qu'à poser/retirer un label signéexit != 0pathsinclut le workflow lui-même — sans quoi le poseur de label ne peut plus retirer son propre label une fois les.mdsortis du diffMême profil que
markdown-deaccent-advisory.ymletrepeated-prose-advisory.yml, déjà admis à ce titre. Rollback = revert de cette PR : l'entrée disparaît de l'allow-list avec le workflow qu'elle couvre, sans résidu.Mesure locale après insertion :
58 passed in 1.53ssurscripts/tests/test_check_self_hosted_runner_policy.py.