From de90ff264471275a353501a5c6ab3d033086be3c Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 22 Oct 2026 03:22:59 +0200 Subject: [PATCH] Fix(picker,#21949): marqueur de cloture prefixe d'un titre markdown reconnu Les cinq grammaires de cloture du tapis exigeaient `[INFO]`/`[DELIVERED]`/ `[NOT-DELIVERED]`/`[RELEASED]` en TETE de ligne (`^\s*` + MULTILINE). Un commentaire structure en sections ecrit couramment `## [INFO] candidate-delivered -- lane ...`, et `#` n'est pas `\s` : aucune alternative ne matchait. Mesure (draw c354, 2026-10-22) : sur 4 candidats rendus par le tapis, #20445 portait son marqueur en DERNIER commentaire et etait neanmoins servi comme grain neuf ; c'est la seule des quatre picks mortes imputable au filtre (les deux umbrellas sont exemptees par dessein, la quatrieme a un claim vivant). L'ancre tolere desormais un prefixe de titre borne a 1-6 dieses suivis d'espaces. Le temoin negatif `test_marker_no_match_discursive_mention` reste vert : la prose qui cite le marqueur au milieu d'une phrase ne commence par aucun titre. Une borne explicite est testee (7 dieses = pas un titre). Tests : 5 ajoutes, 33 selectionnes verts ; les 12 echecs du fichier sont base-inherited (ShellEscape : `gh auth token`, reproduits sur main non modifie). Closes #21949 --- scripts/pick_idle_grain.py | 23 ++++++--- scripts/tests/test_pick_idle_grain.py | 70 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/scripts/pick_idle_grain.py b/scripts/pick_idle_grain.py index 4d9162298ae..b8209b9efb0 100644 --- a/scripts/pick_idle_grain.py +++ b/scripts/pick_idle_grain.py @@ -276,6 +276,15 @@ # incidentes du type "sans [INFO] candidate-delivered" ou "[INFO] absent # dans ce fil", ou la sous-chaîne `[INFO] candidate-delivered` est presente # mais n'est pas l'en-tête du commentaire. +# #21949 : l'ancre tolere un prefixe de TITRE markdown (`#{1,6}` + espaces +# optionnels). Un commentaire structure en sections ecrit couramment +# `## [INFO] candidate-delivered -- lane ...` ; `#` n'est pas `\s`, la ligne +# n'etait donc reconnue par aucune alternative et le grain mort repassait en +# tete du tapis (mesure du 22/10 : #20445, marqueur en DERNIER commentaire, +# servi comme grain neuf). La tolerance reste BORNEE a 1-6 dieses suivis +# d'espaces : une prose qui cite le marqueur au milieu d'une phrase ne +# commence par aucun titre, elle reste hors du filet (cf. temoin negatif +# `test_marker_no_match_discursive_mention`). # #20008 : la fermeture est un LOOKAHEAD `(?![-_\w])`, jamais un `\b`. Un `\b` # pose apres `delivered` ne separe PAS `candidate-delivered` de # `candidate-delivered-partial` : le tiret est un non-mot, donc il FORME une @@ -289,11 +298,11 @@ # immediatement apres le mot. _DELIVERED_MARKER_RE = re.compile( r"(?:" - r"^\s*\[INFO\]\s+candidate-delivered(?![-_\w])" + r"^\s*(?:#{1,6}\s*)?\[INFO\]\s+candidate-delivered(?![-_\w])" r"|" - r"^\s*\[INFO\s+candidate-delivered(?![-_\w])[^\n\]]*\]" + r"^\s*(?:#{1,6}\s*)?\[INFO\s+candidate-delivered(?![-_\w])[^\n\]]*\]" r"|" - r"^\s*\[INFO\][^\n]*\b(?:lane\s+\S+:\S+|signal|livr[ée]e?|" + r"^\s*(?:#{1,6}\s*)?\[INFO\][^\n]*\b(?:lane\s+\S+:\S+|signal|livr[ée]e?|" r"verification first-hand)[^\n]*\bcandidate-delivered(?![-_\w])" r")", re.IGNORECASE | re.MULTILINE, @@ -314,9 +323,9 @@ # a elle, un marqueur repose apres elle rearme (cf `_delivered_state`). _DELIVERED_MARKER_REVOKED_RE = re.compile( r"(?:" - r"^\s*\[NOT-DELIVERED\]" + r"^\s*(?:#{1,6}\s*)?\[NOT-DELIVERED\]" r"|" - r"^\s*\[INFO\]\s+candidate-delivered-revoked" + r"^\s*(?:#{1,6}\s*)?\[INFO\]\s+candidate-delivered-revoked" r")", re.IGNORECASE | re.MULTILINE, ) @@ -339,7 +348,7 @@ # servi. La negation est donc cherchee explicitement, et non par un mot # generique comme « pas » : une ligne « acceptance deja couverte, pas de # travail restant » est une couverture, pas une negation. -_RELEASED_HEADER_RE = re.compile(r"^\s*\[RELEASED\](?=\s|:|$)") +_RELEASED_HEADER_RE = re.compile(r"^\s*(?:#{1,6}\s*)?\[RELEASED\](?=\s|:|$)") # Les motifs sont compares sur une ligne NORMALISEE (accents retires) : la # flotte ecrit indifféremment « deja couverte », « déjà couverte », « rien a # reparer », « rien à réparer ». Un motif ecrit en ASCII matcherait la @@ -1484,7 +1493,7 @@ def apply_delivered_urn_gate(lane, urns_arg, urns_default, selected_urns): # de composition : les deux PRs sont independantes et se completent dans # l'ordre de merge que donne le coordinateur. _RECENT_DELIVERY_MARKER_RE = re.compile( - r"^\s*\[(?:DELIVERED|FROZEN)\](?=\s|:|$)", + r"^\s*(?:#{1,6}\s*)?\[(?:DELIVERED|FROZEN)\](?=\s|:|$)", re.MULTILINE, ) diff --git a/scripts/tests/test_pick_idle_grain.py b/scripts/tests/test_pick_idle_grain.py index 969bb3f35b1..fe8c4c1698a 100644 --- a/scripts/tests/test_pick_idle_grain.py +++ b/scripts/tests/test_pick_idle_grain.py @@ -4407,6 +4407,76 @@ def test_marker_no_match_discursive_mention(monkeypatch): assert picks[0]["klass"] == "grain" +# --- #21949 : marqueur de cloture prefixe d'un TITRE markdown -------------- +# Mesure (draw c354 du 2026-10-22) : #20445 portait son `[INFO] +# candidate-delivered` en DERNIER commentaire, mais sous un titre +# (`## [INFO] candidate-delivered -- lane ...`). L'ancre `^\s*` n'accepte pas +# `#`, aucune alternative ne matchait, et le grain mort revenait en tete du +# tapis. Le prefixe de titre est une forme COURANTE de redaction (commentaire +# structure en sections), pas une forme exotique. + + +def test_delivered_state_heading_prefixed_marker(): + """`## [INFO] candidate-delivered` est un marqueur, pas une citation. + + Forme relevee sur #20445 (dernier commentaire, 2026-10-21T08:14:44Z) : + c'est elle qui a fait resservir une issue deja livree.""" + assert pig._delivered_state([ + "Voici mon rapport.\n\n## [INFO] candidate-delivered — lane " + "myia-po-2023:CoursIA\n\nDetails ci-dessous.", + ]) is True + + +def test_delivered_state_heading_prefixed_revocation(): + """Une retractation TITREE retracte : sans ca elle ne defaisait rien. + + Le sens est porte par l'ORDRE (`_delivered_state`), pas par la forme : + une retractation titree doit donc desarmer un marqueur anterieur.""" + assert pig._delivered_state([ + "[INFO] candidate-delivered — livraison complete.", + "## [NOT-DELIVERED] — verification ulterieure : livraison partielle.", + ]) is False + + +def test_recent_marker_heading_prefixed_closure_is_seen(monkeypatch): + """Le filtre bon marche (#19768) voit aussi un en-tete titre. + + `_RECENT_DELIVERY_MARKER_RE` est la grammaire qui ecarte SANS consommer + le budget de sonde de livraison : un `### [DELIVERED]` non vu coutait une + sonde pleine, voire laissait passer le candidat.""" + calls = [] + _patch_gh_dispatch( + calls, monkeypatch, pr_payload=[], + comments_payload={"comments": [ + _delivered_marker_comment( + body="### [DELIVERED] lane myia-po-2025:CoursIA — PR #21999"), + ]}) + assert pig.has_recent_delivery_marker(20445) is True + + +def test_released_heading_prefixed_is_recognised(): + """`## [RELEASED] ... acceptance deja couverte` : 3e grammaire de la meme + famille (`_RELEASED_HEADER_RE`, #20351).""" + assert pig._released_covers_acceptance( + "## [RELEASED] lane myia-po-2025:CoursIA — acceptance deja couverte " + "par la PR #21888." + ) is True + + +def test_heading_prefix_tolerance_is_bounded(): + """La tolerance s'arrete a 1-6 dieses, et une prose reste hors du filet. + + Sans cette borne, autoriser un prefixe reviendrait a retirer l'ancre de + debut de ligne -- le temoin negatif ci-dessous le mesure.""" + assert pig._DELIVERED_MARKER_RE.search("# [INFO] candidate-delivered — x") + assert pig._DELIVERED_MARKER_RE.search( + "###### [INFO] candidate-delivered — x") + assert not pig._DELIVERED_MARKER_RE.search( + "####### [INFO] candidate-delivered — x") + assert not pig._DELIVERED_MARKER_RE.search( + "Le mot ### [INFO] candidate-delivered apparait ici en pleine phrase.") + + # --- Geste 3 #18203 : `weight()` mesure le delaissement sur la DERNIERE ---- # livraison reelle (PR mergée qui cite l'issue), pas sur `updatedAt`. # -----------------------------------------------------------------------------