From df26d9f3e0c52584f042b0bf1ff317f3c5778831 Mon Sep 17 00:00:00 2001 From: jsboige Date: Wed, 7 Oct 2026 15:26:16 +0200 Subject: [PATCH] fix(coordination,#17040): levee du veto user sur la campagne densite #13410 Reouverture par decision user du 2026-10-07 (session directe), sous les lecons consignees au fil de #13410. #13410 sort de FROZEN_UMBRELLAS et son prefixe de relais wt/vibe- de FROZEN_BRANCH_PREFIXES ; #11601 (densite QC round 2) reste gelee -- la reouverture ne la couvre pas. Les mecanismes (gel par prefixe de branche, levee par campagne autorisee) restent en place, couverts par des tests a donnees injectees. 240 tests verts. Co-Authored-By: Claude Sonnet 5.5 --- scripts/coordination/frozen_campaigns.py | 43 +++--- .../tests/test_check_adjoint_prevalidation.py | 29 ++-- scripts/tests/test_frozen_campaigns.py | 132 ++++++++++++------ scripts/tests/test_merge_ready.py | 33 +++-- 4 files changed, 155 insertions(+), 82 deletions(-) diff --git a/scripts/coordination/frozen_campaigns.py b/scripts/coordination/frozen_campaigns.py index 5f51d8eea6..06a832cb57 100644 --- a/scripts/coordination/frozen_campaigns.py +++ b/scripts/coordination/frozen_campaigns.py @@ -6,6 +6,10 @@ dossier READY porte par une PR gelee). Une seule definition pour les deux, jamais deux qui derivent -- le gate ne peut pas importer merge_ready (merge_ready importe deja le gate), donc les deux importent ce module. + +Etat au 2026-10-07 : le veto #17040 couvrait deux parapluies ; #13410 +(campagne densite carnets) est ROUVERT par decision user (lecons consignees +au fil de #13410), #11601 (densite QC round 2) reste gele. """ from __future__ import annotations @@ -14,29 +18,34 @@ # Parapluies GELES par un veto user : une PR qui s'en reclame (titre ou body) # sort du perimetre (b), quel que soit son dossier. Ni le gate d'entree ni B.0 # ne lisent un veto pose sur une issue : l'organe le lit ici, fail-closed. -# 13410 = campagne densite, gelee par le veto #17040 (mandat user 2026-09-20). -# #17021 y a ete mergee le 2026-09-22 sur un dossier READY et un B.0 vert : -# c'est l'incident qui fonde cette liste. +# 13410 = campagne densite, gelee par le veto #17040 (mandat user 2026-09-20) +# puis ROUVERTE par decision user du 2026-10-07 (session directe), sous les +# lecons consignees au fil de #13410 -- l'entree de campagne se fait sous +# elles. #17021 y a ete mergee le 2026-09-22 sur un dossier READY +# et un B.0 vert : c'est l'incident qui fonde cette liste. # 11601 = densite QC round 2 (« 1200->2000+ »), gelee au meme titre le # 2026-09-23 (#11601 c.5786602361) : sa cible EST un seuil, ce que le point 4 -# de #17040 interdit. -FROZEN_UMBRELLAS = {"13410": "17040", "11601": "17040"} +# de #17040 interdit. RESTE gelee -- la reouverture du 2026-10-07 ne la +# couvre pas. +FROZEN_UMBRELLAS: dict[str, str] = {"11601": "17040"} # Branches d'une campagne gelee dont les PRs ne citent PAS le parapluie : les -# relais g-XX de #13410 (`wt/vibe-g62-...`) n'ont #13410 ni dans le titre ni -# dans le body. 17 d'entre eux etaient ouverts et invisibles au filtre +# relais g-XX de #13410 (`wt/vibe-g62-...`) n'avaient #13410 ni dans le titre +# ni dans le body. 17 d'entre eux etaient ouverts et invisibles au filtre # ci-dessus le 2026-09-23 (fermes au titre du veto, solde markdown net > 0). -FROZEN_BRANCH_PREFIXES = {"wt/vibe-": "13410"} +# Vide depuis la reouverture user de #13410 (2026-10-07) : `wt/vibe-*` +# n'etait gele que comme relais de cette campagne. La structure reste pour le +# prochain veto, mecanisme couvert par les tests a donnees injectees. +FROZEN_BRANCH_PREFIXES: dict[str, str] = {} -# Campagnes AUTORISEES apres le veto qui reutilisent les memes relais -# ``wt/vibe-*`` : le prefixe de branche ne dit plus a lui seul « campagne -# gelee ». 17636 = resorption des mesures d'artefact en prose markdown (GO -# ai-01 du 2026-09-27, recette #17636 c.5860054240) : elle RETIRE des chiffres, -# elle ne vise aucun seuil. Mesure fondatrice (2026-09-28) : #18252, #18283 et -# #18316 portaient `fix(prose,#17636)` en titre et aucun #13410, et restaient -# gelees par leur seule branche. La levee se lit sur le TITRE (le body peut -# citer n'importe quoi) et tombe des qu'un parapluie gele est cite. -BRANCH_PREFIX_RELEASES = {"wt/vibe-": ("17636",)} +# Campagnes AUTORISEES apres un veto a reutiliser des relais geles : la levee +# se lit sur le TITRE (le body peut citer n'importe quoi) et tombe des qu'un +# parapluie gele est cite. Mesure fondatrice (2026-09-28, prefixe wt/vibe-) : +# #18252, #18283 et #18316 portaient `fix(prose,#17636)` en titre et +# restaient gelees par leur seule branche (17636 = resorption des mesures +# d'artefact, GO ai-01 du 2026-09-27). Vide depuis la reouverture de #13410 +# (2026-10-07) : plus aucun relais gele ne vit. +BRANCH_PREFIX_RELEASES: dict[str, tuple[str, ...]] = {} # Redressements EXEMPTES du gel, sur le TITRE seul (insensible a la casse) : # une PR qui REPREARE les degats d'une campagne gelee cite le parapluie comme diff --git a/scripts/tests/test_check_adjoint_prevalidation.py b/scripts/tests/test_check_adjoint_prevalidation.py index 643fac679f..33c8547f3d 100644 --- a/scripts/tests/test_check_adjoint_prevalidation.py +++ b/scripts/tests/test_check_adjoint_prevalidation.py @@ -1649,10 +1649,11 @@ def test_fingerprint_refusal_names_the_live_surface_landscape(): # --- Campagnes gelees par veto user (#17040) ---------------------------------- -# Titres reels (2026-09-23/24) : le second repare les degats de la campagne -# densite et cite le parapluie dans son body -- l'exemption se lit sur le -# titre seul (module partage frozen_campaigns). -FROZEN_TITLE = "Densite Lab13-Web-Search-SOTA (#13410)" +# Titres reels (2026-09-23/24) : le second repare les degats d'une campagne +# gelee et cite le parapluie dans son body -- l'exemption se lit sur le +# titre seul (module partage frozen_campaigns). #13410 ROUVERT le 2026-10-07 : +# le gel se mesure sur #11601, seul parapluie gele en vie. +FROZEN_TITLE = "enrich(qc,#11601): densite QC-Py-06b" REDRESSEMENT_TITLE = ( "fix(semanticweb,#17066): redressement critique de SW-4-CSharp-SPARQL " "-- reference de campagne" @@ -1709,7 +1710,7 @@ def test_ready_dossier_on_frozen_campaign_returns_rc3(monkeypatch, capsys): out = capsys.readouterr().out assert out.startswith( "FROZEN -- PR #123 belongs to a frozen campaign " - "(frozen:#13410(veto #17040)); do not merge, dispatch to the lane author." + "(frozen:#11601(veto #17040)); do not merge, dispatch to the lane author." ) @@ -1722,29 +1723,31 @@ def test_ready_dossier_frozen_json_payload(monkeypatch, capsys): payload = json.loads(capsys.readouterr().out) assert payload["ready"] is False assert payload["verdict"] == "FROZEN" - assert payload["frozen"] == "frozen:#13410(veto #17040)" + assert payload["frozen"] == "frozen:#11601(veto #17040)" assert payload["dossier"]["verdict"] == "READY" def test_ready_redressement_citing_the_umbrella_stays_rc0(monkeypatch, capsys): """Redressement : le titre exempte du gel, meme quand le body cite - #13410 -- sinon la PR qui REPARE les degats ne serait plus mergeable.""" + #11601 -- sinon la PR qui REPARE les degats ne serait plus mergeable.""" snapshot = _snapshot_with( title=REDRESSEMENT_TITLE, - pr_body=_base_snapshot()["body"] + "\n\nSee #13410 (campagne densite).", + pr_body=_base_snapshot()["body"] + "\n\nSee #11601 (densite QC).", ) rc = _run_main(monkeypatch, snapshot) assert rc == mod.EXIT_READY assert capsys.readouterr().out.startswith("READY -- PR #123") -def test_ready_dossier_on_wt_vibe_branch_returns_rc3(monkeypatch, capsys): - """Relais de campagne : la branche gelee suffit, sans citation aucune -- - et sans exemption (ce sont des relais, jamais des redressements).""" +def test_ready_dossier_on_wt_vibe_branch_after_reopening(monkeypatch, capsys): + """Reouverture user de #13410 (2026-10-07) : les relais wt/vibe-* ne + gelent plus -- un dossier READY sur ces branches redevient READY. Le + mecanisme de gel par prefixe reste prouve par test_frozen_campaigns + (donnees injectees).""" snapshot = _snapshot_with(head_ref="wt/vibe-g77-search-26") rc = _run_main(monkeypatch, snapshot) - assert rc == mod.EXIT_BLOCKED_WITH_SUBSTANCE - assert "frozen:#13410(veto #17040,branch wt/vibe-*)" in capsys.readouterr().out + assert rc == mod.EXIT_READY + assert capsys.readouterr().out.startswith("READY -- PR #123") def test_blocked_dossier_on_frozen_campaign_keeps_its_own_message( diff --git a/scripts/tests/test_frozen_campaigns.py b/scripts/tests/test_frozen_campaigns.py index f3f3291d91..19bfacd97c 100644 --- a/scripts/tests/test_frozen_campaigns.py +++ b/scripts/tests/test_frozen_campaigns.py @@ -2,10 +2,16 @@ Le module est charge par importlib (meme convention que test_check_adjoint_prevalidation et test_merge_ready) : aucun reseau, aucun gh. -Les trois titres reels ci-dessous sont les titres mesures qui fondent -l'exemption de redressement -- ils ne se devinent pas. + +Etat depuis la reouverture user du 2026-10-07 : #13410 (campagne densite +carnets) est ROUVERT, seule #11601 (densite QC round 2) reste gelee. Les +mecanismes de gel par prefixe de branche et de levee par campagne autorisee +ne sont plus exerces par les donnees de production : ils restent couverts par +les tests a donnees injectees (``_simulated_veto``) pour que le prochain veto +herite d'un mecanisme prouve. """ +import contextlib import importlib.util import sys from pathlib import Path @@ -17,8 +23,8 @@ sys.modules["frozen_campaigns_under_test"] = fc _spec.loader.exec_module(fc) -# Titres reels (2026-09-23/24) : les deux premiers reparent les degats de la -# campagne densite et citent le parapluie -- ils doivent rester mergeables. +# Titres reels (2026-09-23/24) : les deux premiers reparent les degats d'une +# campagne gelee et citent le parapluie -- ils doivent rester mergeables. REVERT_TITLE = ( "revert(density,#17040): App-1-NQueens + App-14b-ConnectFour " "restaures avant #17021 (remplissage sous veto)" @@ -27,23 +33,40 @@ "fix(semanticweb,#17066): redressement critique de SW-4-CSharp-SPARQL " "-- reference de campagne" ) -FROZEN_TITLE = "Densite Lab13-Web-Search-SOTA (#13410)" +FROZEN_TITLE = "enrich(qc,#11601): densite QC-Py-06b" +# Titre reel d'une PR de la campagne densite, releve avant la reouverture : +# la citation de #13410 ne gele plus depuis le 2026-10-07. +REOPENED_TITLE = "Densite Lab13-Web-Search-SOTA (#13410)" def test_the_three_real_titles(): - assert fc.frozen_umbrella_exclusion(REVERT_TITLE, "See #13410") is None - assert fc.frozen_umbrella_exclusion(REDRESSEMENT_TITLE, "See #13410") is None + assert fc.frozen_umbrella_exclusion(REVERT_TITLE, "See #11601") is None + assert fc.frozen_umbrella_exclusion(REDRESSEMENT_TITLE, "See #11601") is None assert ( fc.frozen_umbrella_exclusion(FROZEN_TITLE, None) - == "frozen:#13410(veto #17040)" + == "frozen:#11601(veto #17040)" + ) + + +def test_13410_reopened_by_user_decision(): + # Reouverture user du 2026-10-07 : la citation du parapluie (titre OU + # body) ne gele plus, et les relais wt/vibe-* non plus. Les lecons du + # gel vivent au fil de #13410 ; #11601 reste gelee, elle. + assert fc.frozen_umbrella_exclusion(REOPENED_TITLE, None) is None + assert fc.frozen_umbrella_exclusion("fix(x): ordinaire", "See #13410", None) is None + assert ( + fc.frozen_umbrella_exclusion( + "fix(search,g77): relocate 12 lectures", None, "wt/vibe-g77-search-26" + ) + is None ) def test_prefix_number_not_matched(): - # #134100 ne vaut pas #13410 -- le garde-fou (?!\d) couvre le parapluie + # #116010 ne vaut pas #11601 -- le garde-fou (?!\d) couvre le parapluie # ET le numero du veto. - assert fc.frozen_umbrella_exclusion("fix: #134100", "voir #134101") is None - assert fc.frozen_umbrella_exclusion("fix(x,#170400): titre", "See #13410") is not None + assert fc.frozen_umbrella_exclusion("fix: #116010", "voir #116011") is None + assert fc.frozen_umbrella_exclusion("fix(x,#170400): titre", "See #11601") is not None def test_exemption_is_title_only(): @@ -51,23 +74,14 @@ def test_exemption_is_title_only(): # le titre seul, sinon n'importe quelle PR de campagne s'exempterait en # ecrivant le mot dans son body. assert ( - fc.frozen_umbrella_exclusion("fix(x): ordinaire", "redressement, voir #13410") + fc.frozen_umbrella_exclusion("fix(x): ordinaire", "redressement, voir #11601") is not None ) def test_exemption_is_case_insensitive(): - assert fc.frozen_umbrella_exclusion("REVERT(density): restaures", "See #13410") is None - assert fc.frozen_umbrella_exclusion("Fix: REDRESSEMENT SW-4", "See #13410") is None - - -def test_branch_prefix_stays_frozen_despite_exempt_title(): - # Les branches wt/vibe-* sont des relais de campagne : aucune exemption, - # meme sur un titre de redressement. - assert ( - fc.frozen_umbrella_exclusion(REVERT_TITLE, None, "wt/vibe-g62-density-9") - == "frozen:#13410(veto #17040,branch wt/vibe-*)" - ) + assert fc.frozen_umbrella_exclusion("REVERT(density): restaures", "See #11601") is None + assert fc.frozen_umbrella_exclusion("Fix: REDRESSEMENT SW-4", "See #11601") is None def test_umbrella_in_body_freezes_despite_ordinary_title(): @@ -77,6 +91,12 @@ def test_umbrella_in_body_freezes_despite_ordinary_title(): ) +# --- Mecanismes de relais, a donnees injectees --------------------------------- +# +# Depuis la reouverture de #13410, aucune donnee de production n'exerce le +# gel par prefixe de branche : on rejoue ici l'etat d'avant (13410 gele + +# relais wt/vibe-*, campagne autorisee 17636) pour garder le mecanisme prouve. + # Titre reel de #18283 (2026-09-28) : relais wt/vibe-* d'une campagne # autorisee apres le veto, sans aucune citation de #13410. RELEASED_TITLE = ( @@ -85,31 +105,65 @@ def test_umbrella_in_body_freezes_despite_ordinary_title(): ) -def test_authorized_campaign_title_releases_vibe_branch(): - assert ( - fc.frozen_umbrella_exclusion(RELEASED_TITLE, "See #17636", "wt/vibe-g2d-tweety") - is None +@contextlib.contextmanager +def _simulated_veto(): + saved = ( + fc.FROZEN_UMBRELLAS, + fc.FROZEN_BRANCH_PREFIXES, + fc.BRANCH_PREFIX_RELEASES, ) + fc.FROZEN_UMBRELLAS = {"13410": "17040", "11601": "17040"} + fc.FROZEN_BRANCH_PREFIXES = {"wt/vibe-": "13410"} + fc.BRANCH_PREFIX_RELEASES = {"wt/vibe-": ("17636",)} + try: + yield + finally: + ( + fc.FROZEN_UMBRELLAS, + fc.FROZEN_BRANCH_PREFIXES, + fc.BRANCH_PREFIX_RELEASES, + ) = saved + + +def test_branch_prefix_freezes_despite_exempt_title(): + # Les branches wt/vibe-* sont des relais de campagne : aucune exemption, + # meme sur un titre de redressement. + with _simulated_veto(): + assert ( + fc.frozen_umbrella_exclusion(REVERT_TITLE, None, "wt/vibe-g62-density-9") + == "frozen:#13410(veto #17040,branch wt/vibe-*)" + ) + + +def test_authorized_campaign_title_releases_vibe_branch(): + with _simulated_veto(): + assert ( + fc.frozen_umbrella_exclusion(RELEASED_TITLE, "See #17636", "wt/vibe-g2d-tweety") + is None + ) def test_release_falls_when_a_frozen_umbrella_is_cited(): # Le body cite la campagne gelee : la branche regele la PR. - assert ( - fc.frozen_umbrella_exclusion(RELEASED_TITLE, "suite de #13410", "wt/vibe-g2d-tweety") - == "frozen:#13410(veto #17040,branch wt/vibe-*)" - ) + with _simulated_veto(): + assert ( + fc.frozen_umbrella_exclusion(RELEASED_TITLE, "suite de #13410", "wt/vibe-g2d-tweety") + == "frozen:#13410(veto #17040,branch wt/vibe-*)" + ) def test_release_is_title_only(): # #17636 dans le body seul ne libere pas un relais muet. - assert ( - fc.frozen_umbrella_exclusion("Densite Lab13", "See #17636", "wt/vibe-g62-density-9") - == "frozen:#13410(veto #17040,branch wt/vibe-*)" - ) + with _simulated_veto(): + assert ( + fc.frozen_umbrella_exclusion("Densite Lab13", "See #17636", "wt/vibe-g62-density-9") + == "frozen:#13410(veto #17040,branch wt/vibe-*)" + ) def test_release_needs_exact_campaign_number(): - assert ( - fc.frozen_umbrella_exclusion("fix(prose,#176360): x", None, "wt/vibe-g2x") - == "frozen:#13410(veto #17040,branch wt/vibe-*)" - ) + with _simulated_veto(): + assert ( + fc.frozen_umbrella_exclusion("fix(prose,#176360): x", None, "wt/vibe-g2x") + == "frozen:#13410(veto #17040,branch wt/vibe-*)" + ) diff --git a/scripts/tests/test_merge_ready.py b/scripts/tests/test_merge_ready.py index b7c24a3757..ca2aabbb41 100644 --- a/scripts/tests/test_merge_ready.py +++ b/scripts/tests/test_merge_ready.py @@ -327,26 +327,30 @@ def test_skip_file_under_github_dir(tmp_path): def test_skip_frozen_umbrella_in_title(tmp_path): # #17021 : mergee sous le veto densite #17040 sur un dossier READY. - view = default_view(title="fix(pedagogy,#13410): g59-search-1 — 9 lectures") + # #13410 ROUVERT le 2026-10-07 : le skip se mesure sur #11601, seul gele. + view = default_view(title="enrich(qc,#11601): densite QC-Py-06b — tranche g2") runner = ScriptedRunner(views={123: view}) rc, lines, _ = run_organ(tmp_path, runner, extra=("--apply",)) assert rc == 0 - assert lines[-1]["reason"] == "frozen:#13410(veto #17040)" + assert lines[-1]["reason"] == "frozen:#11601(veto #17040)" assert not any("check_adjoint_prevalidation.py" in flat for flat in runner.flat()) assert not any(" merge " in f" {flat} " for flat in runner.flat()) def test_skip_frozen_umbrella_in_body(tmp_path): - view = default_view(body=GRAIN_MED + "\n\nSee #13410 (densite).") + view = default_view(body=GRAIN_MED + "\n\nSee #11601 (densite QC).") runner = ScriptedRunner(views={123: view}) rc, lines, _ = run_organ(tmp_path, runner, extra=("--apply",)) assert rc == 0 - assert lines[-1]["reason"] == "frozen:#13410(veto #17040)" + assert lines[-1]["reason"] == "frozen:#11601(veto #17040)" def test_frozen_umbrella_prefix_number_not_matched(): - assert mr.frozen_umbrella_exclusion("fix: #134100", "voir #134101") is None - assert mr.frozen_umbrella_exclusion("fix: #13410.", None) is not None + # #13410 ne gele plus (reouverture user 2026-10-07) ; le garde-fou (?!\d) + # se mesure sur #11601, seul parapluie gele en vie. + assert mr.frozen_umbrella_exclusion("fix: #13410.", None) is None + assert mr.frozen_umbrella_exclusion("fix: #116010", "voir #116011") is None + assert mr.frozen_umbrella_exclusion("fix: #11601.", None) is not None def test_frozen_umbrella_qc_density_round2(): @@ -697,25 +701,28 @@ def test_precheck_dossier_illisible_laisse_decider_le_gate(tmp_path): assert lines[-1]["verdict"] == "merged" -def test_frozen_branch_prefix_without_umbrella_reference(): - # Relais g-XX de #13410 : ni le titre ni le body ne citent le parapluie. +def test_wt_vibe_prefix_lifted_with_13410_reopening(): + # Reouverture user du 2026-10-07 : les relais g-XX de #13410 ne gelent + # plus, meme muets -- le prefixe n'etait gele que pour cette campagne. assert ( mr.frozen_umbrella_exclusion( "fix(search,g77): relocate 12 lectures", "Grain: MED/notebook", "wt/vibe-g77-search-26" ) - == "frozen:#13410(veto #17040,branch wt/vibe-*)" + is None ) assert mr.frozen_umbrella_exclusion("fix(x): ordinaire", None, "fix/vibe-check") is None assert mr.frozen_umbrella_exclusion("fix(x): ordinaire", None, None) is None -def test_skip_frozen_branch_before_gate(tmp_path): - view = default_view(title="fix(search,g71): lectures reprises") - view["headRefName"] = "wt/vibe-g71-search-20" +def test_skip_frozen_umbrella_before_gate(tmp_path): + # Le skip de perimetre precede le gate : la PR gelee n'y est pas passee. + # (Forme branche wt/vibe-* levee avec la reouverture de #13410, 2026-10-07.) + view = default_view(title="enrich(qc,#11601): densite QC-Py-07") + view["headRefName"] = "feature/densite-qc-7" runner = ScriptedRunner(views={123: view}) rc, lines, _ = run_organ(tmp_path, runner, extra=("--apply",)) assert rc == 0 - assert lines[-1]["reason"] == "frozen:#13410(veto #17040,branch wt/vibe-*)" + assert lines[-1]["reason"] == "frozen:#11601(veto #17040)" assert not any("check_adjoint_prevalidation.py" in flat for flat in runner.flat())