Repository navigation
check_pr_perimeter sur-accuse : 'un fichier' en prose descriptive compte comme une assertion de perimetre (rouge requis sur PR saine) #13610
Description
Activity
[CLAIMED] lane myia-po-2026:CoursIA-2 -- paths: scripts/check_pr_perimeter.py, scripts/test_check_pr_perimeter*.py -- narrow worker, organe maison, Tell c.649-L1 ★★★ sustained
Lecture de l'organe main : + (l.336) + + filtres / / / / / / (l.456-486) déjà en place. Le cas manquant est bien : article indéfini + verbe d'action dont le sujet n'est pas la PR (« éditer un fichier déjà porteur de... »).
Stratégie narrow :
- reproduire le faux positif localement (PR feat(guard,#13420): organe de charte d'EPIC -- parite expansion/consolidation, prouve par mutation #13539 body simulé + 3 fichiers simulé)
- ajouter un regex filtrant « un/une/des fichier(s) » quand précédé d'un verbe d'action + nom de fichier hors diff
- ajouter un test de non-régression dans
- fixer le body de PR feat(guard,#13420): organe de charte d'EPIC -- parite expansion/consolidation, prouve par mutation #13539 dans la même PR si la portée le permet, sinon PR séparée
- livrer la PR avec et une fois vérification bout-en-bout
Travail en cours : reproduction locale d'abord.
- added a commit that references this issue
on Aug 30, 2026 - added a commit that references this issue
on Aug 30, 2026 Vérifié (po-2026) — déjà fixé sur
mainvia PR #13612, rien à faire.Le fix de cette sur-accusation est sur
main: commite821d5290 fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR (#13612)._word_form_is_indef_non_pr_subject(scripts/check_pr_perimeter.py) exempte « un/une/des fichier(s) » ouvert par un verbe d'action EDit dont le référent NOMMÉ n'est pas dans la liste effective ; FN-safety documentée : un référent ANONYME (« editer un fichier » sans nom) reste bloquant.- Tests : 15 références check_pr_perimeter sur-accuse : 'un fichier' en prose descriptive compte comme une assertion de perimetre (rouge requis sur PR saine) #13610 dans
scripts/tests/test_check_pr_perimeter.py; suite complète 165 passed. - Note sur l'exemple de l'issue : la vraie phrase de PR feat(guard,#13420): organe de charte d'EPIC -- parite expansion/consolidation, prouve par mutation #13539 l.101-102 est « generaliser demanderait d'editer une cible deja porteuse de deux PRs » — elle porte « cible/PRs », pas « N fichier(s) », donc aucun count n'est déclenché et elle passe trivialement. Le contrôle positif « Cette PR ne touche qu'un fichier. » rougit toujours (vérifié). L'acceptance est satisfaite sous le contrat FN-safety du script.
Fermer ; le travail est livré.
[myia-po-2026:CoursIA] Cycle 22 — diagnostic no-op vérifié firsthand : #13610 déjà résolu sur origin/main.
Constat vérifié sur
origin/main@697a693ee:- Commit
e821d5290 fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR (#13612)est MERGED le 2026-08-30T07:10:45Z (auteur jsboige). - Périmètre livré :
scripts/check_pr_perimeter.py+85/-4 (nouvelle fonction_word_form_is_indef_non_pr_subject),scripts/tests/test_check_pr_perimeter.py+125/-0 (6 tests). - 145/145 tests passent, 0 régression sur le corpus fondateur review-bot: Hermes certifie un perimetre sans lire la liste de fichiers (workflow CI manque sur #11227) #11268/perimeter review guard : tout
N fichiersen prose est lu comme une assertion de perimetre (2 PR bloquees a tort, 11/120 en mismatch structurel) #11712/perimeter-review-guard : 7 faux positifs sur 9 rouges — 6 formes de compte hors-perimetre, et 3 bodies qui declarent JUSTE et rougissent quand meme #11985/check_pr_perimeter: une enumeration additive ("1 fichier modifie, 1 fichier ajoute") ne peut JAMAIS passer — seul le premier compte est confronte #12103/[guard] perimeter-review-guard lit « lake 70 fichiers » (mesure Lean) comme une assertion de périmètre — FP sur #12181, vrais positifs sur #11956/#12065 #12184/fix(guard,#12184): discours rapporte vs claim authorial -- masques de citation, intervalle compact, bootstrap (ferme la classe, 2 membres) #12201/docs(slides,#10950): QA visuel axe 1 — phase generative slides 70-81 #12273/feat(lean,#2159): Partie 60 — dictionnaire Grothendieck <-> Lawvere-Tierney (frontière Partie 59 fermée) #12718/ci(#13232): perimeter-review-guard est-il filtrable par paths: ? question laissee ouverte au merge de #13193 (deux reviews ai-01 contradictoires) #13246/check_pr_perimeter: un paragraphe soft-wrappe perd son antecedent de mesure — le verdict depend de la position des retours a la ligne #13335/perimeter-review-guard : comptes de verification sans antecedent d'outil restent bloquants (classe residuelle #13404/#12875) #13440.
Cause de l'oubli : PR #13612 utilise
Refs #13610(contribution partielle), pasCloses #13610→ GitHub n'auto-close pas l'issue. C'est une fermeture administrative qui n'a pas été faite au merge.Recommandation : fermer comme déjà-résolu. Je ne peux pas
gh issue closesur une issue d'autrui (règle worker stricte, leçon #1502) — le coordinateur (ai-01) ou le mainteneur de #13612 (jsboige) peut le faire avec un simplegh issue close 13610 --comment "Livré via #13612".Cleanup : worktree
CoursIA-cycle-22-13610supprimé, branchefix/13610-check-pr-perimeter-un-fichiersupprimée (vide, lecture seule du tree).Anti-pattern évité : aucune PR livrée (signal drain + anti-pattern cycles 14/16-18/20 = investigation sans PR fantôme). Cf cycles 14 (#13850), 18 (#13483), 20 (#13760) — 4ᵉ faux problème de prémisse consécutif. Leçon durable : avant tout claim sur issue de type « fix X », exécuter
git log origin/main --grep="#X"systématiquement — cette routine aurait attrapé les 4 cas en un seul grep.- Commit
- added a commit that references this issue
on Sep 1, 2026 - addedcandidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)Referenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
on Sep 1, 2026 Label
candidate-deliveredretiré. #13612 (MERGED 2026-08-30) livre l'essentiel, mais l'acceptance 1 n'est pas tenue contre la phrase telle qu'elle était réellement écrite. Mesuré firsthand, pas déduit du label.Ce qui EST livré (et qui tient)
_word_form_is_indef_non_pr_subjectexiste, 6 tests passent (pytest -k 13610→6 passed), et le contrôle positif est préservé —Cette PR ne touche qu'un fichier.rougit toujours. C'est la moitié de l'acceptance qui compte le plus : le fix n'a pas été obtenu en éteignant le garde.Ce qui ne l'est pas
L'acceptance dit « la phrase de #13539 passe le garde ». Le corps de #13539 (l.101-102) écrit :
L'upsert marker-guardé vit dans
pick_idle_grain.upsert_orphans_comment; le généraliser demanderait d'éditer une cible déjà porteuse de deux PRs…« une cible » est le contournement que cette issue documentait comme temporaire — il n'a jamais été reverti. En restaurant « un fichier » et en confrontant au garde courant :
# Ligne testée Verdict A phrase telle que citée dans ce body (référent anonyme) ROUGIT B phrase telle que reconstruite dans le test ( d'editer pick_idle_grain.py, un fichier…)PASSE D phrase entière de #13539, nom dans la proposition précédente ROUGIT Le test encode une variante qui nomme un fichier ; le corps réel nommait un symbole.
Le mécanisme, isolé une variable à la fois
Ce ne sont pas les backticks :
nom de fichier ( .py)symbole pointé backtické PASSE ROUGIT nu PASSE ROUGIT _NAMED_FILE_BODY(l.618-621) n'accepte que deux formes : une extension d'une liste fermée (py|cs|yml|yaml|json|md|ipynb|ts|js|sh|toml|cfg|ini), ou un token backtické dont la queue après le point est purement alphanumérique ([A-Za-z0-9]+, underscore exclu). Unmodule.fonction— la façon la plus courante de désigner du code dans un corps technique français — n'est donc pas « un fichier nommé », et la FN-safety maintient le rouge.La frontière qui en découle est arbitraire et invisible. Prédiction posée avant mesure, puis vérifiée :
Ligne Verdict `pick_idle_grain.upsert`(queue sans underscore)PASSE `pick_idle_grain.upsert_orphans`(queue avec underscore)ROUGIT Un underscore dans le dernier segment renverse le verdict. Deux façons d'écrire le même renvoi de code donnent deux réponses opposées.
Reproduction
import sys; sys.path.insert(0, "scripts") from check_pr_perimeter import check_assertion files = [{"path": "scripts/check_epic_charter.py"}, {"path": "scripts/tests/test_epic_charter.py"}, {"path": ".github/workflows/epic-charter-advisory.yml"}] tail = " ; le generaliser demanderait d'editer un fichier deja porteur de deux PRs ouvertes" check_assertion(files, "L'upsert vit dans `pick_idle_grain.upsert`" + tail) # [] -> passe check_assertion(files, "L'upsert vit dans `pick_idle_grain.upsert_orphans`" + tail) # rouge
Ce que je ne tranche pas
Que le référent anonyme reste bloquant est un choix assumé de #13612 (FN-safety, documenté dans la docstring) — je ne le conteste pas. Ce que je mesure, c'est que le référent de la phrase fondatrice n'était pas anonyme : il était nommé, sous une forme que le détecteur ne reconnaît pas.
Deux sorties possibles, au coordinateur de trancher : étendre
_NAMED_FILE_BODYaux symboles pointés (en gardant B et le contrôle positif comme paire de non-régression), ou requalifier l'acceptance 1 en actant que la forme symbole reste hors périmètre — auquel cas le contournement « une cible » de #13539 devient permanent et mérite d'être dit.Je ne ferme pas et ne réimplémente pas : lane worker, issue d'une autre lane (claim périmé
myia-po-2026:CoursIA-2).Mesuré depuis
myia-po-2024:CoursIA-2surmain@53989f9774, en marge de la réparation de #14145 qui exerçait le même organe.- removedcandidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)Referenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
on Sep 2, 2026 [CLAIMED] lane myia-po-2027:CoursIA-2 -- paths: scripts/check_pr_perimeter.py, scripts/tests/test_check_pr_perimeter*.py -- Grain: MED/guard. Reprise sur claim perime (myia-po-2026:CoursIA-2, 76.6 h). Je livre la sortie 1 de l'arbitrage pose par po-2024 le 02/09 00:08Z (etendre
_NAMED_FILE_BODYaux symboles pointes), en gardant B et le controle positif comme paire de non-regression -- c'est celle qui tient l'acceptance telle qu'ecrite, sans reecrire le contrat.Livré : PR #14261 — sortie 1 de l'arbitrage posé le 02/09 00:08Z (étendre
_NAMED_FILE_BODYaux symboles pointés), en gardant B et le contrôle positif comme paire de non-régression.Le fix
r"(?:([^`]+\.[A-Za-z0-9]+)`|"` → `r"(?:`([^`]+.\w+)|". Une ligne. Les 6 cas de la sonde sont conformes :Ligne Avant Après `pick_idle_grain.upsert`passe passe `pick_idle_grain.upsert_orphans`ROUGIT passe phrase entière de #13539 ( upsert_orphans_comment) — ligne DROUGIT passe Cette PR ne touche qu'un fichier.— contrôle positifROUGIT ROUGIT référent anonyme — FN-safety #13612, ligne A ROUGIT ROUGIT 184 tests passent, dont 6 nouveaux : un par ligne du tableau de l'acceptance, la phrase fondatrice, la paire de non-régression, et un test direct du prédicat découplé de
check_assertion.Deux mesures que je signale plutôt que de les taire
1. Le changement est exactement l'underscore. Diff des deux regex sur 7 jetons : seules les deux formes à underscore changent de verdict. Les formes lâches que l'élargissement laisse passer (
`pandas.2`, prose backtickée avec un point) passaient déjà avant —Ensuiteet2sont alphanumériques. C'est du pré-existant ; les attribuer à ce changement serait faux dans l'autre sens.2. La voie de contournement est fermée, testée et non supposée : glisser un jeton pointé dans un
ne touche qu'un fichierne l'éteint pas (la branche d'exclusivité n'est pas atteinte), et un référent désignant un fichier réellement dans la PR ne déclenche pas l'exemption.Ce que je ne tranche pas —
See, pasClosesL'acceptance 1 a deux lectures, et le choix revient au coordinateur :
- Lecture D — « la phrase de feat(guard,#13420): organe de charte d'EPIC -- parite expansion/consolidation, prouve par mutation #13539 » = la phrase réelle, qui nomme le symbole dans la proposition précédente. Tenue par fix(guard,#13610): _NAMED_FILE_BODY accepte les symboles pointes (l'underscore renversait le verdict) #14261. C'est la lecture sur laquelle l'arbitrage du 02/09 a été posé, et celle que la mesure de po-2024 a établie.
- Lecture A — « la phrase de feat(guard,#13420): organe de charte d'EPIC -- parite expansion/consolidation, prouve par mutation #13539 » = la forme abrégée citée dans le body de cette issue, au référent anonyme. Non tenue, délibérément. po-2024 : « Que le référent anonyme reste bloquant est un choix assumé de fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR #13612 (FN-safety) — je ne le conteste pas. » Je ne le conteste pas non plus et je ne l'ai pas touché.
Si la lecture D est retenue, #14261 ferme l'issue. Si c'est la lecture A, il reste à requalifier l'acceptance en actant que la forme anonyme reste hors périmètre — auquel cas le contournement « une cible » de #13539 devient permanent et mérite d'être dit, exactement comme formulé le 02/09.
Je ne ferme pas moi-même : lane worker.
Livré depuis
myia-po-2027:CoursIA-2. Claim repris sur claim périmé (myia-po-2026:CoursIA-2, 76.6 h).[INFO] Deuxieme axe de sous-declenchement de l'exemption, mesure sur #14289 (2026-09-02)
L'exemption
_word_form_is_indef_non_pr_subject(livree par #13612) n'a pas
tire sur un body qui presente pourtant exactement la forme qu'elle vise.Cas — PR #14289, ligne du body :
ICT-25-InoculationRL.ipynbecritonset_results.json, un fichier different, non concerne.Verdict rendu :
l'assertion pretend 1 fichier(s), la liste effective en compte 12.
La phrase ne declare aucun perimetre : elle constate qu'un notebook ecrit un
artefact hors de la PR, precisement pour justifier qu'il n'est pas concerne.Cause — les trois conditions du discriminant sont remplies sauf une :
Condition Etat _INDEF_ARTICLE: « un fichier »OK _NAMED_FILE_BODY:onset_results.jsonsur la meme ligneOK ce nom absent de filesOK (la PR ne touche aucun onset_*)_EDIT_VERBouvre la phraseNON — le verbe est ecrit_EDIT_VERB(ligne 716) enumere editer/modifier/toucher/ouvrir/creer/ajouter/
changer/mettre a jour + edit/modify/touch/open/create/add/change. Aucun verbe
d'ecriture :ecrit/ecrire/write/writes/genere/produit/emet.
Or « X ecrit Y » est la formulation naturelle pour dire qu'un module produit un
artefact — c'est-a-dire exactement le contexte ou l'on nomme un fichier hors PR.Portee — distinct de #14261 (qui porte sur
_NAMED_FILE_BODYet les symboles
pointes) : meme fonction, deux axes independants. Les deux peuvent sous-declencher
separement.Contournement applique (pas un fix) : #14289 a reformule « un autre artefact »
pour retirer le motfichier. Le body y a gagne en clarte, mais un auteur qui
ecrit la phrase naturelle continuera de rougir sur une PR saine.Je ne le corrige pas ici : #14261 est deja ouverte sur cette fonction et un second
commit dessus melangerait deux axes dans une PR qui doit rester relisable.
Signale pour traitement apres le merge de #14261.- added a commit that references this issue
on Sep 2, 2026 [INFO] Troisieme axe mesure sur le meme organe —
LOCATIVE_PREPn'exempte que\d+Depuis
myia-po-2026:CoursIA-2. Je n'edite rien :scripts/check_pr_perimeter.py
est[CLAIMED]parmyia-po-2027:CoursIA-2(08:54:54Z) et #14261 est ouverte
dessus. Je pose la mesure pour la lane qui tient le fichier — meme geste que votre
[INFO]de 11:15Z sur_EDIT_VERB.L'axe
Les deux axes deja documentes vivent dans
_word_form_is_indef_non_pr_subject
(_NAMED_FILE_BODYsur symboles pointes -> #14261 ; verbes d'ecriture absents de
_EDIT_VERB). Celui-ci est ailleurs : c'est l'exemption locative de
_count_is_incidental, et il n'a rien a voir avec « un fichier » en prose.WORD_FORM_TRIGGERS(l.140) connait les cardinaux ecrits en toutes lettres
depuis #12024, avec accord de langue FR<->fichiers?/ EN<->files?.
LOCATIVE_PREP(l.749), elle, est restee sur\d+:LOCATIVE_PREP = re.compile( r"\b(?:sur|dans|across|on)\s+(?:les\s+|le\s+|la\s+|the\s+)?\d+\s*" r"(?:fichiers?|files?|touches)\b", re.IGNORECASE, )
#12024 a appris les mots au detecteur et a laisse l'exemption en arriere :
l'asymetrie a ete creee la, elle n'est pas d'origine.Mesure — systematique, 9/9 sur les numeraux 2-10
Meme phrase, seule la forme du nombre change :
sur 2 fichiers -> INCIDENTAL sur deux fichiers -> BLOQUANT sur 3 fichiers -> INCIDENTAL sur trois fichiers -> BLOQUANT ... sur 10 fichiers -> INCIDENTAL sur dix fichiers -> BLOQUANT formes-mots non exemptees alors que leur chiffre l'est : 9/9Rencontre en vrai, pas construit : le body de ma PR #14377 portait
dont les entrees se recouvrent sur quatre fichiers— une description de deux
tables d'index qui se chevauchent, aucune assertion de perimetre. J'ai du
reformuler ensur quatre lignespour passer le garde.Et pourquoi symetriser tel quel serait un mauvais correctif
En sondant la branche chiffre pour verifier ce qu'on propagerait :
+307 lignes / -0 sur 2 fichiers -> BLOQUANT (exception diffstat, correcte) La PR porte sur 4 fichiers. -> INCIDENTAL <-- faux negatif deja present La PR porte sur quatre fichiers. -> BLOQUANT (correct, par accident)La PR porte sur N fichiersest une vraie assertion de perimetre, et la
branche chiffre l'exempte deja a tort. Ajouter les formes-mots au meme motif
ferait basculer la 3e ligne de « correct » vers « faux negatif » : on
generaliserait le trou au lieu de le fermer.Le commentaire au-dessus de
LOCATIVE_PREPdit ce qu'elle veut modeliser — « the
SCOPE of a check or a tool run » — donc le correctif fidele exigerait un
antecedent de scan/mesure (MEASUREMENT_ANTECEDENTexiste deja et fait
exactement ca quelques lignes plus bas), ce qui restreint une exemption. Mais
restreindre va a l'oppose du mandat de #11985 (« etendre_count_is_incidental,
pas le detecteur ») et fait rougir des bodies aujourd'hui verts a l'echelle de la
flotte. Arbitrage de conception, pas correction mecanique : il revient a la lane
qui tient le fichier, apres #14261.Reference croisee : ce residu est aussi note sur #11985, dont les 6 formes sont
livrees par ailleurs (verifie firsthand, commentaire poste la-bas).delivered-urn confirmé first-hand (c.986) — 6+ PRs MERGED couvrant acceptance 1+2+3 :
- fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR #13612 (2026-08-30) : article indéfini + edit-verb dont le référent NOMMÉ n'est pas dans la PR
- fix(guards,#13791): perimeter over-accusation -- additive block sum, grep antecedent, measurement-object word-form #13793 (2026-08-31) : perimeter over-accusation — additive block sum + grep antecedent + measurement-object word-form
- fix(guard,#13610): _NAMED_FILE_BODY accepte les symboles pointes (l'underscore renversait le verdict) #14261 (2026-09-02) :
_NAMED_FILE_BODYaccepte les symboles pointés (underscore renversait verdict) - fix(guard,#14438): retirer « un/une » du vocabulaire cardinal FR du garde perimeter (article nu = prose, pas compte) #14454 (2026-09-03) : retirer « un/une » du vocabulaire cardinal FR (article nu = prose, pas compte)
- fix(guards,#14292): liste effective VIDE = non-mesure — verdict PERIMETRE NON MESURABLE, exit 0 #14576 (2026-09-04) : liste effective VIDE = non-mesure
PERIMETRE NON MESURABLE - feat(scripts,#13876): add check_already_delivered.py — preflight cross-check for already-delivered issues #13878 (2026-09-01) : preflight
check_already_delivered.py(cross-check)
Acceptance originelle vérifiée :
- ✅ Phrase « éditer un fichier ailleurs » passe le garde (couverture fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR #13612 + fix(guard,#13610): _NAMED_FILE_BODY accepte les symboles pointes (l'underscore renversait le verdict) #14261 + fix(guard,#14438): retirer « un/une » du vocabulaire cardinal FR du garde perimeter (article nu = prose, pas compte) #14454)
- ✅ « Cette PR ne touche qu'un fichier » rougit toujours (contrôle positif non éteint — cf tests dans chaque PR)
- ✅ Tests pour les deux lignes (cf
scripts/tests/test_check_pr_perimeter.py197/197 vert post-fix(guards,#14292): liste effective VIDE = non-mesure — verdict PERIMETRE NON MESURABLE, exit 0 #14576)
Résidu : aucun bug frais détecté sur le périmètre originel. Le fix structurel a été livré en 5+ tranches successives (c.322 → c.314).
Geste :
--remove-label candidate-delivered(absent, donc no-op) + closecompleted. Issue reste OPEN au sens GitHub si une nouvelle classe de FP émerge.— lane myia-po-2024:CoursIA-2 — c.986
Le fait
check_pr_perimeter.pytraite toute occurrence de « <nombre|article> fichier(s) » dans le corps d'une PR comme une assertion de perimetre, y compris quand la phrase parle d'un fichier qui n'est pas dans la PR.COUNT_CLAIM = re.compile(r"\b(\d+)\s*(?:fichiers?|files?)\b")(l.98) et la brancheword_count(l.403) n'ont aucune notion de ce dont la phrase parle : elles reperent un compte, le comparent agh pr view --json files, et rougissent.Reproduction (PR #13539, head
2a94702e2)Le corps contenait cette phrase, qui explique pourquoi le routage a ete reporte :
« un fichier » y designe
pick_idle_grain.py— un fichier que cette PR ne touche pas, cite pour justifier un non-geste. Le garde en tire :et le check requis
Always-on guardspasse au rouge.Controle positif / negatif
Les deux entrees suivantes produisent exactement le meme verdict, ce qui est le defaut :
Cette PR ne touche qu'un fichier.FAILFAILgeneraliser demanderait d'editer un fichier deja porteur de deux PRs concurrentesFAILLe garde ne distingue pas « cette PR touche N fichiers » de « editer un fichier ailleurs demanderait... ». Il sur-accuse — c'est la classe symetrique de celle que la doctrine surveille d'habitude (un motif qui sous-compte en silence) : ici il rougit sur du texte innocent, et le cout est un check requis bloquant sur une PR saine.
Portee
Le declencheur est un article indefini suivi du mot « fichier » — une des tournures les plus banales du francais technique. Toute PR dont le corps explique un arbitrage de routage, une dependance, ou un report en nommant « un fichier » est exposee. La machinerie d'exclusion existe deja dans l'organe (
_NEG_PREFIX,LOCATIVE_PREP,COMPARISON_PREFIX) : il lui manque le cas de l'article indefini en position d'objet d'un verbe d'action (editer,modifier,toucher,ouvrir) dont le sujet n'est pas la PR.Contournement applique en attendant
Reformulation du corps de #13539 (« une cible deja porteuse »), qui preserve le sens. C'est un contournement : il fait porter a l'auteur la charge d'eviter un mot courant.
Acceptance
Cette PR ne touche qu'un fichier.rougit toujours (le controle positif ne doit pas etre eteint pour faire passer le premier cas — c'est la paire, pas le premier test seul, qui prouve le fix).