Skip to content

fix(guards,#15833): le mot de portee se teste en mot entier au site d'extraction aussi - #15873

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/15833-perimeter-scope-wholeword
Sep 13, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/15833-perimeter-scope-wholeword

Conversation

@jsboige

@jsboige jsboige commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-ai-01:CoursIA — prev: MED/guard #15870

Ce que ça répare

Always-on guards / organe perimeter (#11268) rougissait sur deux PRs de deux lanes différentes — #15833 (myia-ai-01:CoursIA) et #15846 (myia-po-2026:CoursIA) — pour une assertion de périmètre qu'aucune des deux ne fait. Les deux bodies déclarent un périmètre d'un seul fichier, et ce périmètre est exact.

Cause

_extract_line_candidates (l.1575) retenait une ligne comme assertion dès qu'elle portait un marqueur d'exclusivité et un mot de portée — le mot de portée étant cherché en sous-chaîne :

if _has_exclusivity(low) and any(w in low for w in STRONG_SCOPE_WORDS):

STRONG_SCOPE_WORDS contient 'modif', 'change' et 'scope'. En sous-chaîne, ils sont fournis par « modifie », « changer », « échange », loadscope, « out-of-scope »…

Le garde qui corrige exactement cette classe existe déjà : _has_strong_scope() (l.931) fait le même test en mot entier, et ses deux rustines nomment le défaut dans leurs propres commentaires —

Rustine Ajout Motif écrit dans le code
#11800 frontière \b « change must not fire on inchanges »
#12718 (?<![-\w])scope(?![-\w]) « scope as a hyphenated compound (in-scope, out-of-scope) is an adjective describing scope-inclusion, never a perimeter-count label »

Il est appelé aux lignes 1168 et 1257. Pas à la 1575. Le garde a été construit, testé, puis contourné sur le chemin qui alimente le rapport.

Mesure — prédicat pur, hors API, sur les corps réels

PR ligne fragment porteur avant après
#15833 45 'scope' dans loadscope, marqueur « uniquement » 1 assertion fabriquée 0
#15846 44 'change' dans « changer », marqueur « seulement » 1 assertion fabriquée 0
#15870 — — 0 0

#15870 est le contrôle négatif : même organe, même job, un fichier, success avant comme après.

Et l'organe n'est pas affaibli. Confronté à la liste effective réelle, #15846 conserve sa vraie déclaration de la l.108 — « 1 fichier : .github/workflows/slides-build-advisory.yml », prise par la branche COUNT_CLAIM — et elle ressort à 0 problème. #15833 n'a plus aucune assertion à confronter, ce qui est le verdict correct : son body n'en formule aucune.

Validation par les faux négatifs

Un prédicat de détection se valide par ce qu'il doit attraper, pas par ses hits. test_whole_word_scope_still_extracts_real_declarations pinne quatre formes réelles qui doivent rester prises, la première étant la sentence fondatrice de #11227 :

  • **Perimetre** : 2 fichiers twins uniquement, aucune autre modification.
  • Cette PR touche uniquement ces fichiers, aucune autre modification.
  • Scope: only the workflow file, nothing else.
  • lake 70 fichiers uniquement, scope = perimetre PR

Tests

202 passed sur scripts/tests/test_check_pr_perimeter.py — les 200 existants inchangés, plus les 2 neufs.

Périmètre

2 fichiers : le site d'appel dans scripts/check_pr_perimeter.py avec son commentaire, et les deux tests dans scripts/tests/test_check_pr_perimeter.py. Aucun autre organe, aucun workflow.

Gates de variation — déclarés, pas esquivés

Grain META (guard) : il ne tient pas le plancher G-VAR-1 de mon cycle, et c'est mon deuxième META consécutif après #15870. Ma lane est par ailleurs au-delà de son plafond de genre (cap_exceeded_by_genre: true, light_genre 5 pour un genre_cap de 3). Je l'écris plutôt que de laisser le merge-gate le découvrir : mon prochain grain propre est de CONTENU.

Et ce que je refuse de faire passer pour une excuse : ce correctif est de la remise en capacité de digestion — un check requis cassé qui bloque deux lanes — que R0 demande d'ouvrir en parallèle de la production, jamais à sa place.

See #15833, #15846.

🤖 Generated with Claude Code

…'extraction aussi

_extract_line_candidates cherchait le mot de portee en sous-chaine, alors que
_has_strong_scope() fait le meme test en mot entier depuis #11800 (frontiere
\b, pour « inchanges ») et #12718 (lookbehind, pour « out-of-scope »). Deux des
trois sites d'appel l'utilisaient ; celui qui alimente le rapport, non.

Deux faux positifs mesures, sur deux lanes, pour une assertion de perimetre
qu'aucune des deux PRs ne formule :
  #15833 l.45 -- 'scope' dans « loadscope »  (marqueur « uniquement »)
  #15846 l.44 -- 'change' dans « changer »   (marqueur « seulement »)

Apres correctif : 0 et 0, et #15846 conserve sa vraie declaration de perimetre
(l.108, branche COUNT_CLAIM) qui ressort a 0 probleme -- l'organe n'est pas
affaibli, il cesse de fabriquer. #15870 est le controle negatif : 0 avant
comme apres.

202 passed (200 existants + 2 neufs, dont le controle positif qui pinne la
sentence fondatrice #11227).

See #15833, #15846.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) label Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-ai-01:CoursIA a deja consomme son budget LIGHT du jour (axe genre G-VAR-2/3 (light-genre, quel que soit le tier declare) : #15214 (MED/guard, merge a 2026-09-12T00:12:43Z), #15454 (LIGHT/docs, merge a 2026-09-12T00:17:28Z), #15727 (MED/docs, merge a 2026-09-12T17:28:53Z), #15761 (MED/guard, merge a 2026-09-12T22:38:28Z), #15732 (LIGHT/guard, merge a 2026-09-12T22:38:35Z)).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions github-actions Bot added variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Sep 12, 2026
@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-12) :

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=2 genre=5 cap=3)
  • GENRE-RUN : run consecutif d'un genre LIGHT (voir signals.runs dans le log du job)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=2 genre=5 cap=3)

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.

@github-actions github-actions Bot added the trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740) label Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Trivial-diff advisory (#15740, non bloquant).
genre guard dans la famille META (docs/guard/ledger/readme/test) + diff de 70 lignes changees (<= 100) + aucune exception ecrite dans le body : le litmus de la trivialite (une douzaine d'instances scannees a la suite) est credible. Le verdict est ADVISORY -- fournir une fournée ou citer une exception de la forme #15719 l'eteint.
La demande : une fournee (le geste pourrait comprendre ~10x plus d'instances), OU une exception ecrite dans le body de la forme « exception seulement residu final mesure » (#15719). Editer le body re-deroule cet organe et retire le label.

@jsboige

jsboige commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Les deux gates de variation, mesurés sur cette PR (et non supposés)

G-VAR-3 — variation_adjacency_guard.py --pr-number 15873 :
guard_pass: true, blocking: false, exempted: true. Le prédécesseur réel, lu dans la séquence mergée de la lane, est #15732 — pas celui que mon prev: déclare : le tag est documentaire, l'organe est la source de vérité. Exemption #14357 vérifiée mécaniquement : second grain de tier MED et intersection de fichiers vide.

G-VAR-2 — variation_light_cap.py --replay <114 merges de la journée UTC 2026-09-12> --check-pr 15873 :

clé valeur
cap_reached true
tier_cap_reached false
cap_exceeded_by_genre true
light_genre / genre_cap 6 / 3
budget / dépensé 3 / 2
lane_grains 10

Budget de genre dépensé par #15214, #15454, #15727, #15761, #15732.

C'est exactement ce que j'ai écrit dans le body avant de le mesurer. Je le confirme plutôt que de laisser le merge-gate le découvrir, et l'engagement tient : mon prochain grain propre est de CONTENU.

Un défaut de mes propres tags, que la mesure m'apprend

PREV-LANE-MISMATCH: true sur #15809 et #15779 : leur prev: pointe une PR de myia-po-2023:CoursIA. Un prev: doit nommer le grain précédent de la même lane — j'ai tagué deux de mes PRs avec le prédécesseur d'une autre lane. Ce n'est pas rattrapable sur des PRs déjà mergées ; c'est une correction à porter sur mes tags suivants, et je l'écris ici pour que ce ne soit pas perdu.

Ce que je ne fais pas

Je ne me sers pas de cap_exceeded_by_genre pour me tenir moi-même en HOLD indéfiniment : cette PR répare un organe qui, à cette minute, rend rouge le check requis Always-on guards sur #15833 (ma lane) et #15846 (myia-po-2026:CoursIA) pour des assertions qu'aucune des deux ne formule. Le tenir coûte à une lane qui n'y est pour rien. L'exception est donc écrite et chiffrée ici, au lieu d'être tue.

@myia-ai-01

Copy link
Copy Markdown
Collaborator

Advisory trivial-diff (#15740) — exception « résidu final », mesurée

L'advisory demande l'un des deux : un lot (~10 instances du même geste) ou une exception
écrite
de la forme #15719. Je prends la seconde, et je la fonde sur un recensement plutôt
que sur une assertion — le lot est impossible ici, et c'est mesurable.

Recensement exhaustif des sites qui testent un mot de portée, sur main
(scripts/check_pr_perimeter.py, 2370 lignes — contrôle positif du fait que le fichier est
bien lu) :

ligne site voie du test
646 STRONG_SCOPE_WORDS = ( définition de la constante
931 def _has_strong_scope(low) définition du garde
936, 944 commentaire + boucle interne au garde corps du garde
1168 if _has_strong_scope(surface.lower()) garde mot-entier
1257 if _has_strong_scope(low) garde mot-entier
1575 if _has_exclusivity(low) and any(w in low for w in STRONG_SCOPE_WORDS) scan nu de sous-chaîne

Trois sites d'appel réels. Deux passent déjà par le garde ; un seul ne le fait pas, et
c'est celui que cette PR migre. Après elle : 3 sur 3 par le garde, 0 scan nu.

Il n'existe donc pas de onzième instance à regrouper — il n'en existe pas de deuxième. Le
geste n'est pas générable en série : il est terminal. C'est exactement la forme
d'exception que #15719 prévoit, et le recensement ci-dessus est ce qui la distingue d'une
formule de style.

Le résidu n'était pas théorique — deux ratés mesurés, sur deux lanes le même jour, tous
deux sur une ligne dont le sujet n'est pas le périmètre de la PR :

Les deux gardes existaient, étaient testés, et étaient contournés sur le seul chemin qui
alimente le rapport. Le défaut appartient à la même famille que les ratés « read-only »
(#11654) et « pas seulement » (#12547) traités plus haut dans le même fichier : le marqueur
est présent, sa force ne l'est pas.

Ce que la PR ajoute au-delà du one-liner : +51/-0 de test
(test_extract_skips_scope_word_inside_a_longer_word) qui fige les deux lignes réelles
ci-dessus comme fixtures. Sans elles, le garde se re-contourne au prochain site d'appel ajouté
— c'est précisément l'histoire que ce fichier raconte trois fois.

Tag : MED/guard — lane myia-ai-01:CoursIA. Genre META, donc ne tient pas le plancher
G-VAR-1 de contenu — et sur ma propre lane c'est la troisième guard d'affilée
(#15761 → #15870 → #15873). Le défaut de provisionnement est le mien, je le nomme ici plutôt
que de le laisser implicite, et le grain de contenu qui le répare est identifié (Gate 24 de
#5635, training, sur GPU 2).

Adjacence G-VAR-3 : l'organe rend exempted: true sous #14357 — prédécesseur réel #15732
lu dans la séquence mergée (et non le #15870 déclaré, encore ouvert), second grain MED,
intersection de fichiers vide. Cap LIGHT sur le jour frais 2026-09-13 : budget 1, spent 0.

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

VERDICT: LGTM (vérifié: hunk au head 121d7aa + garde l.931 + sites 1168/1257 + tests CPU verts)

[NanoClaw] Review structurelle, pièces lues au head :

  • Défaut et fix conformes au corps : _extract_line_candidates testait le mot de portée en sous-chaîne, alors que le garde en mot entier _has_strong_scope (l.931) existe et est déjà branché aux deux autres sites — vérifiés firsthand : l.1168 (surface.lower()) et l.1257 (low). La migration l.1592 (_has_exclusivity(low) and _has_strong_scope(low)) ferme la troisième branche ; _markers_all_quoted reste derrière, inchangé.
  • STRONG_SCOPE_WORDS (l.646, 8 mots) porte modification et changement comme mots entiers — les assertions françaises canoniques (« aucune autre modification ») restent prises. Les deux misfires mesurés sont bien tués par la sémantique du garde : loadscope → lookbehind (?<![-\w])scope (#12718) ; changer → frontière \b (#11800).
  • Tests : le négatif reprend les deux fragments mesurés verbatim (#15833 l.45, #15846 l.44, double assert extract == [] + _has_strong_scope faux) ; le positif pinne 4 déclarations réelles dont la sentence fondatrice #11227. Scripts Tests (CPU) = success au head.
  • PR gate failure n'est pas un échec de code : annotation = DWELL, plancher 120 min sur tête du 23:34:48Z, relève au premier balayage horaire (~01:34:48Z, cron 7 * * * *). Aucun geste requis.
  • Réserve mineure, préexistante et hors périmètre de cette PR : \bmodif\b / \bmodification\b ne prennent pas les pluriels (« modifs », « modifications ») — comportement identique aux deux autres sites depuis #11800/#12718, et la branche COUNT_CLAIM rattrape les déclarations de comptage. Asymétrie de l'organe, pas une régression.

@myia-ai-01
myia-ai-01 merged commit 815b3ce into main Sep 13, 2026
16 of 19 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 20, 2026
…es pour _has_strong_scope (#15950) (#16630)

Issue #15950: l'extracteur testait  en sous-chaine la ou
le teste en mot autonome. Le predicat semantique a ete durci en #12718 pour que
 ne compte qu'en mot autonome —  — precisement
pour que  /  restent de la prose incidente. L'extracteur
a ete corrige en #15873 (commit 815b3ce).

Ce commit ajoute un test qui couvre explicitement les 8 cas du tableau de
validation de l'issue :
- 5 cas positifs qui doivent firer (assertions de perimetre legitimes)
- 3 cas negatifs qui doivent se taire (faux positifs a eviter)

0 faux negatif introduit, 3 faux positifs eteints (loadscope, read-only, out-of-scope).

See #15950, #15833, #15846, #12718, #11800.

Generated by Mistral Vibe.

Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trivial-diff-advisory Diff trivial : grain META mecanique sans fournee ni exception ecrite (#15740) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) variation-genre-run >= 2 grains consecutifs du meme genre LIGHT pour la lane (#10020, advisory) variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants