Skip to content

tooling(#15405): detecteur paragraphes markdown trop longs + workflow advisory - #15455

Merged
myia-ai-01 merged 2 commits into
mainfrom
tooling/paragraph-length-detector
Sep 11, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
tooling/paragraph-length-detector

Conversation

@jsboige

@jsboige jsboige commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner

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

Fichier Type Rôle
scripts/notebook_tools/detect_paragraph_length.py NEW (235 l.) Détecteur (stdlib, re) — signale tout paragraphe markdown > 2000 c
scripts/notebook_tools/tests/test_detect_paragraph_length.py NEW (110 l.) 12 tests pytest, tous verts
scripts/notebook_tools/tests/fixtures/paragraph_wall_md.md NEW Fixture externe (sections 1-5 : mur fondateur, post-fix muet, fence code longue, CATALOG-STATUS, titre+tableau long)
.github/workflows/paragraph-length-advisory.yml NEW Workflow per-PR, label paragraph-length non-bloquant, self-cover #8822, #8819 guard
scripts/notebook_tools/README.md M Section dédiée ### detect_paragraph_length.py (#15405) + entrée dans la table des détecteurs
scripts/ci/check_self_hosted_runner_policy.py M (+13 l.) Admission de paragraph-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) :

Percentile Longueur (caractères)
p50 79
p75 274
p90 536
p95 760
p99 1437
max 13 409
Seuil *.md flaggés Paragraphes flaggés
> 1500 c 86 200
> 2000 c 42 84
> 2500 c 26 43
> 3000 c 18 25

Le 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

  • Ignore : fences code (``` / ~~~), lignes de tableau (|), titres (`#` à `######`), commentaires HTML (`` dont le marqueur `CATALOG-STATUS`), directives Sphinx (`^:[a-z]+:`), alertes markdown (`![NOTE]`).
  • Compte : paragraphes, listes, blockquotes (un item de 2500 c est un mur — la calibration le confirme : 4 des top-15 sont des listes).
  • CLI : --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 0 toujours. Signaux :

Câblage : pull_request sur branches: [main], paths: ['**/*.md', '.github/workflows/paragraph-length-advisory.yml'] (self-cover #8822), runs-on: [self-hosted, coursia-ephemeral, coursia-linux], same-repo if: (policy check_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

# Avant PR A (main) — le paragraphe Probas ligne 16 fait 3246 c
$ python scripts/notebook_tools/detect_paragraph_length.py --json MyIA.AI.Notebooks/Probas/README.md
{
 "files": [{"file": ".../Probas/README.md", "findings": [{
   "type": "oversized_paragraph", "start_line": 15, "chars": 3246,
   "excerpt": "Cette série couvre trois stacks complémentaires : **Infer.NET**…"
 }], "counts": {"total": 1}}],
 "summary": {"file_count": 1, "flagged_count": 1, "total_findings": 1, ...}
}

# Après PR A (docs/15405-probas-paragraph-wall) — silent
$ python scripts/notebook_tools/detect_paragraph_length.py --json MyIA.AI.Notebooks/Probas/README.md
{
 "files": [{"file": ".../Probas/README.md", "findings": [], "counts": {"total": 0}}],
 "summary": {"file_count": 1, "flagged_count": 0, "total_findings": 0, ...}
}

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 *.md pré-existants soit à zéro et stable.

Vérification

# 12 tests pytest verts
python -m pytest scripts/notebook_tools/tests/test_detect_paragraph_length.py -v
# self-test (sans pytest)
python scripts/notebook_tools/detect_paragraph_length.py --self-test
# post-fix muet
python scripts/notebook_tools/detect_paragraph_length.py --json MyIA.AI.Notebooks/Probas/README.md
# fail-on-findings
python scripts/notebook_tools/detect_paragraph_length.py MyIA.AI.Notebooks/Probas/README.md --fail-on-findings
# exit 2 sur main, exit 0 sur docs/15405-probas-paragraph-wall

Hors scope (issue de suivi #15457)

Les 42 *.md pré-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 :

FAILED scripts/tests/test_check_self_hosted_runner_policy.py::test_current_repository_self_hosted_jobs_satisfy_isolation_policy
Violation(workflow='paragraph-length-advisory.yml', job='paragraph-length-advisory', code='WORKFLOW_NOT_ALLOWED')
= 1 failed, 12711 passed, 87 skipped, 6 xfailed, 2 warnings in 938.11s =

SELF_HOSTED_WORKFLOW_ALLOWLIST est 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 :

Exigence du profil Vérification
Garde same-repo au niveau job (#13874) if: github.event.pull_request.head.repo.full_name == github.repository — les PRs de fork se font skipper proprement par pr_gate
Pur-Python, stdlib seule detect_paragraph_length.py n'importe que re et la stdlib — aucune dépendance installée sur le runner
GITHUB_TOKEN borné à l'API labels pull-requests: write, aucun autre scope ; le token ne sert qu'à poser/retirer un label signé
Jamais exit != 0 advisory par construction (décision user 2026-09-10)
Auto-couverture des chemins (#8822) paths inclut le workflow lui-même — sans quoi le poseur de label ne peut plus retirer son propre label une fois les .md sortis du diff

Même profil que markdown-deaccent-advisory.yml et repeated-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.53s sur scripts/tests/test_check_self_hosted_runner_policy.py.

… 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>
@jsboige

jsboige commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Issue de suivi pour la resorption des fichiers > 2000 c : #15457 (liste les 23 fichiers actifs restants apres cette PR).

@jsboige

jsboige commented Sep 10, 2026

Copy link
Copy Markdown
Owner Author

Issue de suivi pour la résorption des fichiers > 2000 c : #15457 (liste les 23 fichiers actifs restants après cette PR).

@github-actions github-actions Bot added the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Grain tag obligatoire (#10045, bloquant).

Grain tag absent (no Grain: / in body).

Pour passer ce gate, le body doit porter en tete une ligne de la forme :

Grain: <DEEP|MED|LIGHT>/<genre> -- lane <machine:workspace> -- prev: <TIER>/<GENRE> #<PR>

Le <genre> doit figurer dans l'enumeration §1 de variation-protocol.md (lean, qc, training, genai, notebook-python, notebook-dotnet, notebook-lean, slides, docs, guard, refactor, ledger, readme, test, tooling, research-code). Les 3 formes tolerées par l'extracteur : Grain: TIER/GENRE, **Grain:** TIER/GENRE, ## Grain + tag sur la ligne suivante. La lane doit suivre le format <machine>:<workspace> (cf. lane-claim-protocol.md).

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

[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_fence toggle, 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 label paragraph-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)

jsboige added a commit that referenced this pull request Sep 10, 2026
…/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>
@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #15455 (tooling(#15405): detecteur paragraphes markdown trop longs + workflow advisory) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-ai-01:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-11) :

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 variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

…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>
@myia-ai-01

Copy link
Copy Markdown
Collaborator

[coordinateur — ai-01] Rouge réparé par moi-même : c'est ma lane, donc mon rouge.

Scripts Tests (CPU) échouait au head b8bd1dfe53 sur un seul test parmi 12 711 :

FAILED scripts/tests/test_check_self_hosted_runner_policy.py::test_current_repository_self_hosted_jobs_satisfy_isolation_policy
Violation(workflow='paragraph-length-advisory.yml', code='WORKFLOW_NOT_ALLOWED')
1 failed, 12711 passed, 87 skipped, 6 xfailed

Cause : le workflow livré par cette PR porte runs-on: [self-hosted, coursia-ephemeral, coursia-linux] sans entrée dans SELF_HOSTED_WORKFLOW_ALLOWLIST. Défaut de cette PR, pas un rouge de main.

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 markdown-deaccent-advisory.yml et repeated-prose-advisory.yml, admis à ce titre. L'entrée porte sa rationale et son rollback, comme les tranches précédentes.

Correctif poussé : 43b6f5be88. Mesure locale après correctif — 58 passed in 1.53s sur scripts/tests/test_check_self_hosted_runner_policy.py, dont le test qui rougissait.

Les deux Always-on guards rouges visibles au rollup sont datés du 2026-09-10 (11:17:41Z) et suivis d'un vert du 2026-09-11T02:16:59Z : discriminant temporel, ce sont des entrées d'histoire, pas un rouge courant. Je reprends la PR au rollup neuf.

jsboige added a commit that referenced this pull request Sep 11, 2026
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).
jsboige added a commit that referenced this pull request Sep 11, 2026
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).
myia-ai-01 pushed a commit that referenced this pull request Sep 11, 2026
…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>
@myia-ai-01
myia-ai-01 merged commit e35cc61 into main Sep 11, 2026
24 of 30 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 11, 2026
…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>
jsboige added a commit that referenced this pull request Sep 12, 2026
…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>
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