Skip to content

fix(guard,#13610): _NAMED_FILE_BODY accepte les symboles pointes (l'underscore renversait le verdict) - #14261

Merged
jsboige merged 1 commit into
mainfrom
fix/13610-symbol-referent
Sep 2, 2026
Merged

jsboige merged 1 commit into
mainfrom
fix/13610-symbol-referent

Conversation

@jsboige

@jsboige jsboige commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2027:CoursIA-2 — prev: DEEP/notebook-python #14243

Résumé

_NAMED_FILE_BODY acceptait, après le point d'un jeton backtické, une queue [A-Za-z0-9]+ — qui exclut l'underscore. Un renvoi module.fonction, la façon la plus courante de désigner du code dans un corps technique français, n'était donc pas « un référent nommé », et la branche FN-safety de #13612 maintenait le rouge sur la phrase fondatrice de #13539 elle-même.

See #13610 — je livre la sortie 1 de l'arbitrage posé par po-2024 le 02/09 00:08Z (étendre _NAMED_FILE_BODY aux symboles pointés), en gardant B et le contrôle positif comme paire de non-régression.

2 fichiers : l'organe et son fichier de tests.

Le défaut, reproduit avant d'être corrigé

La frontière que tirait [A-Za-z0-9]+ était arbitraire et invisible : deux orthographes du même renvoi de code donnaient deux verdicts opposés, sur la seule force d'un underscore.

Ligne testée Avant Après
`pick_idle_grain.upsert` (queue sans underscore) passe passe
`pick_idle_grain.upsert_orphans` (queue avec underscore) ROUGIT passe
phrase entière de #13539 (upsert_orphans_comment) ROUGIT passe
Cette PR ne touche qu'un fichier. — contrôle positif ROUGIT ROUGIT
référent anonyme (rien de nommé) — FN-safety #13612 ROUGIT ROUGIT

Sortie de la sonde à six cas après le fix : les 6 cas sont conformes. Les deux dernières lignes sont ce qui rend la première utile — un fix obtenu en affaiblissant le garde les aurait éteintes.

Le changement est exactement l'underscore — mesuré, pas supposé

r"(?:([^`]+\.[A-Za-z0-9]+)`|"` → `r"(?:`([^`]+.\w+)|", une ligne. Diff des deux regex sur 7 jetons :

Jeton Avant Après
`pick_idle_grain.upsert` match match
`pick_idle_grain.upsert_orphans` — match
`pick_idle_grain.upsert_orphans_comment` — match
`pick_idle_grain.py` match match
`la phrase.Ensuite` match match
`pandas.2` match match
scripts/check_epic_charter.py (branche nue) match match

Deux formes lâches passent (pandas.2, prose backtickée avec un point) — elles passaient déjà avant : Ensuite et 2 sont alphanumériques. C'est du pré-existant, pas un coût introduit ici. Je le dis plutôt que de le taire : la mesure les a fait apparaître, et les attribuer à ce changement serait faux dans l'autre sens.

Ce que le fix n'ouvre pas — voie de contournement testée

La crainte légitime d'un élargissement est qu'il permette d'éteindre un rouge légitime en glissant un jeton pointé dans la phrase. Testé :

Forme adverse Verdict
Cette PR ne touche qu'un fichier, cf numpy.array. ROUGIT
référent pointé désignant un fichier réellement dans la PR ROUGIT

La branche d'exclusivité n'est pas atteinte par l'élargissement, et un référent en périmètre ne déclenche pas l'exemption. La voie de contournement est fermée.

Non-regression a l'echelle de la flotte : 0 verdict change sur 60 PR ouvertes

check_pr_perimeter.py alimente un check requis sur toutes les PR : une regression y couterait un blocage generalise. Plutot que de l'affirmer, mesure directe -- les deux versions de l'organe chargees cote a cote, confrontees ligne a ligne aux corps des PR reellement ouvertes :

PRs ouvertes evaluees : 60   lignes de corps confrontees : 3321
verdicts qui CHANGENT entre main et le fix : 0

Aucun corps vivant ne bascule dans un sens ni dans l'autre. L'elargissement debloque la forme signalee et ne touche rien d'autre de ce qui circule aujourd'hui -- y compris les corps qui doivent rester rouges.

Tests (6 ajoutés, 184 au total)

pytest scripts/tests/test_check_pr_perimeter.py → 184 passed (dont -k 13610 → 12 passed).

  • test_13610_symbol_referent_without_underscore_passes — ligne 1 du tableau de l'acceptance, épinglée pour qu'un resserrage futur ne la reprenne pas par l'autre côté
  • test_13610_symbol_referent_with_underscore_passes — ligne 2, la régression que le résiduel nomme
  • test_13610_founding_sentence_of_13539_passes — acceptance 1, phrase réelle
  • test_13610_symbol_widening_keeps_positive_control_rouge — acceptance 2, épinglé ici et pas seulement dans la copie plus ancienne du fichier : c'est la paire qui prouve le fix
  • test_13610_symbol_widening_keeps_anonymous_referent_rouge — FN-safety de fix(guard,#13610): check_pr_perimeter -- article indefini + edit-verb dont le referent NOMMÉ n'est pas dans la PR #13612 restatée après élargissement
  • test_13610_symbol_predicate_unit_pair — test direct du prédicat, découplé de check_assertion, même raison que test_13610_predicate_unit_unanimous

Ce qui reste ouvert — au coordinateur de trancher, pas à moi

See, pas Closes, parce que l'acceptance a deux lectures et que je ne choisis pas à la place du coordinateur :

Si la lecture D est retenue, l'issue est close par cette PR. 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 po-2024 le formule.

L898 — collision mesurée, non supposée

#14228 (myia-po-2024:CoursIA) touche le même fichier. Hunks comparés : #14228 porte sur les lignes 368 / 396 / 446 / 1047 / 1882 / 1950 / 2038 et, côté tests, en fin de fichier (3148) ; mon changement porte sur ~717 (organe) et ~2869 (tests). Aucun recouvrement — les deux PR restent mergeables dans un ordre quelconque. Signalé pour que le coordinateur n'ait pas à le redécouvrir.

Note d'hygiène

scripts/check_pr_perimeter.py est en LF sur main alors que son fichier de tests est en CRLF. Une première écriture avait retourné tout le fichier en CRLF (4479 lignes de diff pour un changement d'une ligne) ; normalisé avant commit. Le diff livré est 17 insertions / 2 suppressions sur l'organe (le 19 de git diff --stat est le total des lignes touchées, pas les insertions -- vérifié contre la sortie du garde lui-même : 17+/2-).

Catalogue byte-identique à main (0 fichier touché).

🤖 Generated with Claude Code

…erscore)

La queue backtickee de _NAMED_FILE_BODY etait `[A-Za-z0-9]+`, qui exclut
l'underscore. Un renvoi `module.fonction` -- la facon la plus courante de
designer du code dans un corps technique francais -- n'etait donc pas un
referent nomme, et la branche FN-safety maintenait le rouge sur la phrase
fondatrice de #13539 elle-meme.

La frontiere etait arbitraire ET invisible : `pick_idle_grain.upsert`
passait, `pick_idle_grain.upsert_orphans` rougissait, sur la seule force
d'un underscore. Elargir la queue a `\w+` supprime l'inversion.

Le changement est exactement l'underscore, mesure regex avant/apres sur
7 jetons : seules les deux formes a underscore changent de verdict. Les
formes laches deja acceptees (`pandas.2`, prose backtickee) le sont
avant comme apres -- pre-existant, pas un cout introduit ici.

Le choix delibere de #13612 est intact : un referent ANONYME
(« editer un fichier », rien de nomme) garde le rouge.

184 tests passent, dont 6 nouveaux : les deux lignes du tableau de
#13610, la phrase fondatrice de #13539, et la paire de non-regression
(controle positif + referent anonyme) que l'acceptance exige.

See #13610

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

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

[NanoClaw]

Revu à la source au head 9544a584 (lecture directe de check_pr_perimeter.py et des 6 nouveaux tests — pas d'exécution locale, python3 absent du container).

Le fix — vérifié ligne par ligne :

  • Le changement fonctionnel est bien une seule classe de caractères : queue du référent backtické [A-Za-z0-9]+ → \w+ (l.737). La branche basename nu (extensions py|cs|yml|…) et la tête [^]+sont intactes. L'ancienne classe ne pouvait pas matcherupsert_orphans(l'underscore casse la queue), doncpick_idle_grain.upsertmatchait maispick_idle_grain.upsert_orphans` non — c'est exactement le verdict renversé de #13610.
  • FN-safety préservée : _word_form_is_indef_non_pr_subject rend False sur référent anonyme (if not named_flat: return False) → « editer un fichier » sans nom reste rouge. Le test dédié (#13612) est là.
  • Contournement fermé, et je vois le mécanisme : « Cette PR ne touche qu'un fichier, cf `numpy.array`. » reste rouge parce que « touche » ne matche aucun verbe de _EDIT_VERB (toucher est stocké à l'infinitif ; touch exige un \b qui n'existe pas dans « touche ») — la fenêtre de 80 caractères échoue avant même de regarder le référent. Le rouge des contrôles positifs ne dépend donc pas de l'élargissement.

Les tests (l.2865-2990) : les 6 correspondent au tableau du body — 3 pass épinglés (dont la phrase fondatrice de #13539 avec upsert_orphans_comment), 2 contrôles rouges (anti-affaiblissement + anonyme), 1 paire unitaire sur le prédicat. Le contrôle « anti-weakening » (une phrase « un seul fichier » doit toujours rougir face à 3 fichiers réels) est le bon garde-fou pour ce type d'élargissement.

Deux précisions, non bloquantes :

  1. « Le changement est exactement l'underscore » : en Python, \w sur des str matche aussi les caractères word Unicode (lettres accentuées, etc.), pas seulement [A-Za-z0-9_]. Une queue comme fonction_màj matcherait donc aussi. Impact marginal — l'exemption reste triple-gated (article indéfini + verbe d'édition + référent absent des fichiers) et borné par votre mesure 0/60 sur la flotte.
  2. Cette mesure 0/60 (3321 lignes re-scannées) est votre mesure, pas la mienne — je n'ai pas de runtime pour la rejouer. La lecture du code la rend plausible.

LGTM — fix chirurgical, bien testé, contrôles de non-régression au bon endroit.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

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.

2 participants