Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions scripts/check_unaddressed_nits.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,150 @@ def _formal_concern_precedes_lift(body: str) -> bool:
)


# #13938 — quand un reviewer pose `[Hermes] COMMENT_WITH_CONCERNS` (verdict
# de pure emission, sans autorite de blocage, cf #12311) ET que le corps de
# la review declare explicitement que rien n'est bloquant, la review n'est
# PAS une reserve — c'est un commentaire FYI que la convention « reponse
# ecrite / thread inline / issue de suivi » (Tell c.589-L1 ★★★ strict)
# assimile a une APPROVED. Sans cette exemption, l'organe punit la
# precaution : plus l'auteur desambigue (« rien de bloquant (contrainte
# token : COMMENT only) »), plus le verdict formel matche CONCERN_MARKERS,
# plus le preflight rougit. Mesure : PR #13935 (GenAI tranche orphelins,
# substance OK, 63 checks SUCCESS, scope clean) bloquee sur Hermes
# COMMENT_WITH_CONCERNS + corps « Rien de bloquant. » — Tell NEW c.840
# sustained « un detecteur qui matche des phrases doit ignorer les
# occurrences en position de citation ou de refutation ».
#
# Garde STRICTE : l'exemption n'est JAMAIS elargie a CHANGES_REQUESTED /
# REQUEST_CHANGES / NEEDS_CHANGES / BLOCKED. Ces prefixes-la gardent leur
# autorite de blocage — seul COMMENT_WITH_CONCERNS est le verdict «
# comment-only par design » (force a state:COMMENTED par #12311, le seul
# etat que Hermes self-bot peut poster avec ce label).
def _comment_only_prefix(body: str) -> bool:
"""Le verdict formel en tete est-il exclusivement COMMENT_WITH_CONCERNS ?

On distingue les formes EMISES des formes CITEES (mentionnees dans une
prose qui les refute) par la seule fenetre de citation de 30 caracteres
de `_is_cited` — la POSITION dans le corps n'est PAS verifiee, et le nom
« prefixe » designe l'usage attendu, pas un controle. Le verdict
``[Hermes] COMMENT_WITH_CONCERNS — ...`` compte ; le corps ``pas de
COMMENT_WITH_CONCERNS ici`` ne compte pas.

Rejette si un verdict de blocage strict est aussi emis (CHANGES_REQUESTED,
REQUEST_CHANGES, NEEDS_CHANGES, BLOCKED, SUSPECT_*, STRUCTURAL_ONLY).
"""
if not body:
return False
normalised = _unaccent(body)
# Marqueurs de blocage strict : leur presence simultanee a COMMENT_WITH_CONCERNS
# annule l'exemption (le reviewer etale les deux = « concerns + change »,
# pas un simple « comment only »).
blocking_markers = (
"CHANGES_REQUESTED", "REQUEST_CHANGES", "NEEDS_CHANGES",
"**BLOCKED**", "BLOCKED PR", "SUSPECT_", "STRUCTURAL_ONLY",
)
for marker in blocking_markers:
if _unaccent(marker) in normalised:
# Verifier que l'occurrence n'est pas CITEe (meme logique que
# `has_live_marker`, mais inline : on n'a besoin que d'une
# occurrence vivante).
start = 0
while (i := normalised.find(_unaccent(marker), start)) != -1:
if not _is_cited(normalised[max(0, i - 30):i]):
return False
start = i + 1
# COMMENT_WITH_CONCERNS doit etre emis (vivant, non cite).
target = _unaccent("COMMENT_WITH_CONCERNS")
start = 0
while (i := normalised.find(target, start)) != -1:
if not _is_cited(normalised[max(0, i - 30):i]):
return True
start = i + 1
return False


# Formulations explicites de non-blocage, dans le corps nettoye des verdicts
# mentionnes (cf `_strip_mentioned_verdicts` + `_strip_quoted` utilises
# ailleurs dans `classify`). Insensible a la casse et aux accents via
# `_unaccent`. Compile une seule fois au chargement du module.
_NON_BLOCKING_PHRASES = tuple(
phrase.encode("unicode_escape").decode("ascii").replace(r"\u", r"\u")
for phrase in (
r"rien de bloquant",
r"rien (?:a|à) corriger",
r"rien (?:a|à) signaler",
r"rien (?:a|à) traiter",
r"rien (?:a|à) addresser",
r"pas (?:de |d')bloquant",
r"pas (?:de |d')blocage",
r"aucun bloquant",
r"aucun blocage",
r"aucune bloque",
r"aucune reserve",
r"non.?bloquant",
r"comment only",
r"comment-only",
r"no blocker",
r"nothing blocking",
r"all (?:is |looks )?good",
r"tout (?:est )?ok",
r"tout (?:est )?bon",
)
)
_NON_BLOCKING_RE = re.compile(
r"(?:" + "|".join(_NON_BLOCKING_PHRASES) + r")",
re.IGNORECASE,
)


def _review_explicit_non_blocking(body: str) -> bool:
"""Le corps NETTOYE des mentions porte-t-il une formulation non-bloquante ?

Le nettoyage (``_strip_mentioned_verdicts(_strip_quoted(body))``) aligne
la surface analysee sur celle utilisee par `has_live_marker` pour
CONCERN_MARKERS — une formulation de non-blocage posee dans une citation
ou un bloc de code ne doit pas eteindre une reserve vivante.
"""
if not body:
return False
surface = _strip_mentioned_verdicts(_strip_quoted(body))
return bool(_NON_BLOCKING_RE.search(_unaccent(surface)))


# #13951 (Concern 1) -- l'exemption ne tient que si le SEUL concern EMIS est le
# prefixe COMMENT_WITH_CONCERNS lui-meme.
#
# `_comment_only_prefix` ne rejette que la famille des verdicts de BLOCAGE
# STRICT (CHANGES_REQUESTED / REQUEST_CHANGES / NEEDS_CHANGES / BLOCKED /
# SUSPECT_ / STRUCTURAL_ONLY). Elle est structurellement AVEUGLE aux deux
# autres familles de `CONCERN_MARKERS` :
# (a) la famille PROSE -- « avant merge », « a changer », « il va falloir »
# (b) les GLYPHES de severite -- 🟡 (constat substantiel), 🔴 (bloquant strict)
#
# Un corps CONTRADICTOIRE passait donc l'exemption :
#
# [Hermes] COMMENT_WITH_CONCERNS -- relu.
# 🟡 la cellule 12 est a changer avant merge.
# Rien de bloquant par ailleurs.
#
# La phrase de non-blocage effacait un marqueur vivant emis dans la MEME
# review. On retire donc les occurrences de COMMENT_WITH_CONCERNS de la surface
# nettoyee (sans quoi le marqueur « CONCERNS » qu'il contient se compterait
# lui-meme) et on exige qu'il ne reste AUCUN concern vivant.
def _sole_live_concern_is_comment_prefix(body: str) -> bool:
"""Hors le prefixe CWC, le corps porte-t-il encore un concern VIVANT ?

Retourne ``True`` quand le prefixe est le seul concern emis (l'exemption
peut tenir), ``False`` des qu'un marqueur de prose ou un glyphe de
severite survit au nettoyage (l'exemption tombe).
"""
if not body:
return False
surface = _strip_mentioned_verdicts(_strip_quoted(body))
residuel = re.sub("COMMENT_WITH_CONCERNS", " ", surface, flags=re.IGNORECASE)
return not has_live_marker(residuel, CONCERN_MARKERS)


def _excerpt(body: str) -> str:
"""Tete + queue : le verdict d'un reviewer vit en QUEUE de body.

Expand Down Expand Up @@ -2064,6 +2208,26 @@ def classify(author: str, body: str) -> str | None:
# de reserve. Uniquement pour CONCERN_MARKERS et l'etage lift (symetrie
# #13083 ci-dessus) : VERDICT_POSITIVE garde le body brut.
live_concern = has_live_marker(_strip_mentioned_verdicts(_strip_quoted(body)), CONCERN_MARKERS)
# #13938 — exemption de « comment-only Hermes » : quand un reviewer pose
# `[Hermes] COMMENT_WITH_CONCERNS` (verdict de pure emission, force a
# state:COMMENTED par #12311) ET que le corps declare explicitement
# que rien n'est bloquant, la review n'est PAS une reserve. Convention
# Tell c.589-L1 ★★★ strict assimile un tel commentaire a une APPROVED
# pour le merge-gate. Garde stricte : l'exemption ne s'applique PAS
# aux verdiicts de blocage strict (CHANGES_REQUESTED, REQUEST_CHANGES,
# NEEDS_CHANGES, BLOCKED, SUSPECT_*, STRUCTURAL_ONLY) — verifie par
# `_comment_only_prefix`. Fuite classee Tell NEW c.840 ★★★ sustained.
if (
live_concern
and _comment_only_prefix(body)
and _review_explicit_non_blocking(body)
# #13951 Concern 1 : la phrase de non-blocage ne peut pas effacer un
# marqueur de prose (« avant merge ») ni un glyphe (🟡) emis dans la
# MEME review. L'exemption ne tient que si le prefixe CWC est le SEUL
# concern vivant du corps.
and _sole_live_concern_is_comment_prefix(body)
):
return None
if not live_concern and _HUMAN_VERDICT_RE.search(body):
return None # verdict humain positif (APPROVE / APPROVED / LGTM) SANS reserve vivante : equivalent state:APPROVED
if not live_concern and has_live_marker(body, POSITIVE_MARKERS):
Expand Down
139 changes: 139 additions & 0 deletions scripts/tests/test_check_unaddressed_nits.py
Original file line number Diff line number Diff line change
Expand Up @@ -3311,6 +3311,93 @@ def test_13635_conditionnel_je_leve_masculin_reste_bloquant():
"myia-ai-01",
"Une seule chose a changer — corrige la ligne 19 et je leve le concern."
) == "BOT-CONCERN"
def test_13938_comment_only_avec_rien_de_bloquant_passe():
"""#13938 FP fondateur (PR #13935) : un reviewer pose `[Hermes]
COMMENT_WITH_CONCERNS` et le corps declare explicitement « rien de
bloquant ». L'exemption doit classer la review en ``None`` (comment-only
par design, cf #12311) et non en ``BOT-CONCERN``.

Reproduit le body verbatim de la review jsboige sur #13935 (compte-rendu
de checkout local + delta scope + balise explicite de non-blocage).
"""
body = (
"[Hermes] COMMENT_WITH_CONCERNS — vérifié en local (checkout de la "
"branche), pas juste lu le diff :\n"
"- Cibles réelles des 3 liens ajoutés au README : `tutorials/README.md`, "
"`shared/helpers/README.md`, `_research/e2e_quant_validation.ipynb`.\n"
"- Nav relative corrigée, scope clean, security scan néant.\n"
"Delta +18/-2.\n"
"Rien de bloquant. (contrainte token : COMMENT only)"
)
assert mod.classify("jsboige", body) is None
# Les helpers unitaires doivent retourner True sur ce body.
assert mod._comment_only_prefix(body) is True
assert mod._review_explicit_non_blocking(body) is True


def test_13938_comment_with_concerns_avec_concerns_substantiels_reste_bloquant():
"""#13938 FN-safety : `[Hermes] COMMENT_WITH_CONCERNS` + concerns FYI
reels (« 2 concerns sur la cellule 12 ») SANS formulation non-bloquante
reste un ``BOT-CONCERN``. L'exemption ne s'applique pas par defaut —
seul un aveu explicite de non-blocage la declenche.

Reproduit le pattern du test fondateur l.255-258 (notebook solide + 2
concerns FYI).
"""
body = (
"[Hermes] **[COMMENT_WITH_CONCERNS]** — notebook solide, 2 concerns FYI "
"sur la cellule 12 : la sortie du solver ne couvre pas le cas n=0 ; "
"le bloc de test dépend de l'ordre des fixtures."
)
assert mod.classify("jsboige", body) == "BOT-CONCERN"
assert mod._comment_only_prefix(body) is True
assert mod._review_explicit_non_blocking(body) is False


def test_13938_changements_requestes_avec_rien_de_bloquant_reste_bloquant():
"""#13938 FN-safety : un reviewer pose `CHANGES_REQUESTED` et glisse «
rien de bloquant » dans le corps. L'exemption NE DOIT PAS s'appliquer
(le verdict de blocage strict prime sur la formulation de non-blocage).

Reproduit le pieges classique : un reviewer tente de baisser le niveau
d'un CHANGES_REQUESTED en ajoutant une clause de non-blocage. Le gate
doit resister.
"""
body = (
"[Hermes] CHANGES_REQUESTED — refactor la cellule 5 pour respecter "
"PEP 8. Rien de bloquant, je laisse au choix de l'auteur."
)
assert mod.classify("jsboige", body) == "BOT-CONCERN"
assert mod._comment_only_prefix(body) is False
# La formulation « rien de bloquant » EST bien reconnue (helper OK),
# mais le verdict formel CHANGES_REQUESTED bloque l'exemption au
# niveau du pipeline.
assert mod._review_explicit_non_blocking(body) is True


def test_13938_comment_with_concerns_cite_dans_un_autre_commentaire_ne_passe_pas():
"""#13938 FN-safety : un commentaire qui CITE `[Hermes]
COMMENT_WITH_CONCERNS` dans une prose qui refute (« pas de
COMMENT_WITH_CONCERNS ici ») NE beneficie PAS de l'exemption — la
detection `_is_cited` doit annuler l'occurrence au niveau du helper
`_comment_only_prefix`.

Reproduit le pattern inverse de #12871 : `_strip_mentioned_verdicts`
neutralise les verdicts mentionnes, mais `_comment_only_prefix` opere
sur le body brut. Un commentaire d'auteur qui enumere les verdicts
d'Hermes pour les refuter ne doit pas etre auto-exempte.
"""
body = (
"Pour clarifier : il n'y a PAS de COMMENT_WITH_CONCERNS dans cette "
"review. Les seuls verdicts emis sont APPROVED et LGTM. Je ne leve "
"aucune reserve parce qu'il n'y en a pas."
)
# Ni verdict emis ni formulation non-bloquante au sens de l'exemption :
# le helper de préfixe doit retourner False (l'occurrence est CITEE).
assert mod._comment_only_prefix(body) is False
# Le body doit classifier None (verdict positif APPROVED/LGTM + aucune
# reserve vivante), mais PAS par la voie de l'exemption #13938.
assert mod.classify("jsboige", body) is None


# --- #13512 -- Position G : verbe de mention + verdict NU (sans parenthese) -
Expand Down Expand Up @@ -3841,3 +3928,55 @@ def test_14130_mutation_si_pattern_retire_le_test_rougit():
)
finally:
mod._MENTION_VERDICT_REPORTED = saved_reported


def test_13951_concern1_corps_contradictoire_avec_marqueur_prose_ne_passe_pas():
"""#13951 Concern 1 (NanoClaw structural review) : un corps CONTRADICTOIRE
pose `[Hermes] COMMENT_WITH_CONCERNS` + rien de bloquant MAIS contient
aussi un CONCERN_MARKER prose vivant (avant merge, a changer).
L'exemption NE DOIT PAS s'appliquer : un concern prose emis dans la meme
review ne doit pas etre ecrase par la phrase de non-blocage.

Reproduit verbatim le piege identifie par NanoClaw dans la review
COMMENTED du 2026-09-02T01:18:42Z :
``[Hermes] COMMENT_WITH_CONCERNS -- fond solide, rien de bloquant. En
revanche, corriger le lien mort du README avant merge.``
Avant le fix (commit ``fdd589cac``), l'exemption s'appliquait et
``classify`` rendait None -- la phrase rien de bloquant ecrasait le
marqueur avant merge (CONCERN_MARKER prose vivant). Apres le fix,
``_sole_live_concern_is_comment_prefix`` detecte le residuel et fait
tomber l'exemption, ce qui laisse classify rendre ``BOT-CONCERN``.
"""
body_prose = (
"[Hermes] COMMENT_WITH_CONCERNS -- fond solide, rien de bloquant. "
"En revanche, corriger le lien mort du README avant merge."
)
# Les trois pre-conditions de l'exemption sont reunies :
assert mod._comment_only_prefix(body_prose) is True
assert mod._review_explicit_non_blocking(body_prose) is True
# ... MAIS le 4e helper detecte le marqueur prose avant merge :
assert mod._sole_live_concern_is_comment_prefix(body_prose) is False
# Verdict final : BOT-CONCERN (pas None) -- la phrase de non-blocage n'a
# pas ecrase le concern vivant.
assert mod.classify("jsboige", body_prose) == "BOT-CONCERN"


def test_13951_concern1_glyphe_severite_avec_rien_de_bloquant_ne_passe_pas():
"""#13951 Concern 1 (NanoClaw) : variante glyphe. Un corps pose
`[Hermes] COMMENT_WITH_CONCERNS` + rien de bloquant + glyphe (constat
substantiel). L'exemption NE DOIT PAS s'appliquer : un glyphe de
severite emis dans la meme review ne doit pas etre ecrase.

Reproduit la 2e classe de piege listee par NanoClaw dans la review
COMMENTED du 2026-09-02T01:18:42Z -- Meme classe pour glyphe (constat
substantiel promu, #12143) coexistant avec rien de bloquant.
"""
body_glyphe = (
"[Hermes] COMMENT_WITH_CONCERNS -- diff coherent, rien de bloquant.\n"
"\U0001F7E1 la cellule 12 merite un refactor (commentaire FYI, hors gate)."
)
assert mod._comment_only_prefix(body_glyphe) is True
assert mod._review_explicit_non_blocking(body_glyphe) is True
# Le glyphe est dans CONCERN_MARKERS (cf. PR #12143) :
assert mod._sole_live_concern_is_comment_prefix(body_glyphe) is False
assert mod.classify("jsboige", body_glyphe) == "BOT-CONCERN"
Loading