Skip to content

guard(prev_guard): citer un tag prev: en prose CREE une declaration — 3 PRs gelees sur une phrase de documentation correcte #14550

Description

@myia-ai-01

Le défaut

scripts/ci/variation_prev_guard.py applique son prédicat au corps entier de la PR :

_PREV_PR_REF_RE = re.compile(
    r"prev\s*:?\s*[A-Za-z]+\s*/\s*[A-Za-z0-9_-]+\s*#(\d+)\b", re.IGNORECASE)

find_prev_self_references() et find_prev_target_pr_numbers() l'itèrent tous deux sur text sans distinguer la ligne de tag (la déclaration) de la prose (une citation). Conséquence : écrire le tag d'une autre PR dans une phrase explicative crée une déclaration au sens du garde.

Le docstring de find_prev_target_pr_numbers montre que la distinction a bien été pensée — mais sur le mauvais axe :

« A #N that appears OUTSIDE a prev: clause (e.g. Refs #13439) is NOT a target here. »

Le garde sait donc écarter un #N hors clause prev:. Il n'a aucun moyen d'écarter une clause prev: complète et bien formée qui se trouve être une citation.

Cas mesuré — PR #14522 (2026-09-04)

La lane myia-po-2023:CoursIA-2 avait un tag correct dès le départ :

Grain: MED/qc -- lane myia-po-2023:CoursIA-2 -- prev: LIGHT/guard #14459

#14459 est mergée (2026-09-03T18:50:47Z). Le tag satisfait les trois invariants de #13475.

Son dernier paragraphe documentait le successeur, ce qui est exactement la bonne pratique :

- #14538 — Position I' (`MED/guard -- prev: MED/qc #14522`) qui se débloque tout seul au merge de celle-ci

Le garde a compté cette citation comme une seconde déclaration et rendu deux invariants en échec sur la même occurrence :

prev: close-keyword genre detected: `prev:` reference(s) fail invariant(s)
  (prev-not-merged -> [14522], prev-self -> [14522])

prev-self et prev-not-merged tombent nécessairement ensemble dans ce cas : une PR qui se cite elle-même n'est jamais mergée au moment de son évaluation. Cette conjonction est la signature du faux positif — un vrai prev-self (typo de tag, comme #14431 qui déclare authentiquement prev: MED/guard #14431) produit la même paire, mais sur la ligne 1.

Coût mesuré : #14522 gelée, et derrière elle #14538 puis #14543, dont les prev: pointent la chaîne — 3 PRs immobilisées sur une phrase de documentation correcte. Débloqué le 2026-09-04T00:4x par une édition de 8 caractères en prose (prev: → prev pointant), le tag n'ayant jamais eu besoin d'être touché.

Pourquoi ce n'est pas un cas marginal

Le garde punit la lane qui documente le mieux. Une PR qui explique quel successeur elle débloque, et cite son tag pour être vérifiable, se sabote ; une PR muette passe. L'incitation est exactement inversée par rapport à ce que le protocole de variation cherche à obtenir.

C'est la même classe de défaut que « citer un [CLAIMED] verbatim en pose un », déjà rencontrée sur le protocole de claim.

Remèdes candidats

# Remède Coût Effet de bord
1 N'évaluer que la ligne de tag — celle que grain_tag.parse_grain_tag() identifie déjà faible perd la détection dans les messages de commit, que le garde évalue aussi (commits[i]) et qui est légitime
2 Ignorer toute occurrence dans un span code (backticks simples ou bloc clôturé) faible une citation hors backticks passe encore ; mais c'est la forme dominante en pratique
3 N'évaluer que la PREMIÈRE occurrence par emplacement très faible un tag réellement dupliqué et divergent ne serait plus vu
4 Exiger l'ancrage en début de ligne (^ + tolérance de puce/décoration) faible dépend de la stabilité de forme du tag, que le guard de tag garantit déjà par mot-clé

Recommandation : 1 + 2. Le (1) traite la cause — le tag est la déclaration, le reste est du texte ; le (2) couvre les messages de commit, où (1) ne s'applique pas et où la citation en backticks reste la forme usuelle.

Acceptance

  1. Un test de non-régression portant le corps réel de fix(backtester,#14370): documenter la disposition upstream CalibrateComplexity (issue MyIntelligenceAgency/Lean#40) #14522 (tag valide + citation en prose du tag d'une autre PR) rend guard_pass: true.
  2. Un contrôle positif dans le même test : le corps réel de chore(genai,#13581): T2 hub .NET — EFCore+CopilotSDK -> Integrations-DotNet/ #14431 (prev: MED/guard #14431 en ligne 1, auto-référence authentique) rend toujours guard_pass: false avec prev-self. Sans ce second cas, le correctif serait indiscernable d'une désactivation du garde.
  3. Les cas existants de scripts/tests/test_variation_prev_guard.py restent verts, y compris ceux qui portent sur commits[i].

Le point (2) est le cœur : un prédicat qu'on assouplit se valide par ce qu'il attrape encore, jamais par ce qu'il cesse d'attraper.

See #13475

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions