From ec75eb605c06dbf5d492f0f92af1541901649f41 Mon Sep 17 00:00:00 2001 From: jsboige Date: Sat, 26 Sep 2026 09:26:51 +0200 Subject: [PATCH] fix(ci): split-reading guard no longer read as advisory by the PR gate The Split-reading ratchet was promoted to blocking by #17044, but its identity workflow kept the name "Split-reading advisory". pr_gate's derive_advisory_jobs reads workflow names on disk, so it filed the guard's red check-run under advisory. The merge stayed blocked through the aggregated fast-lane job, but the gate summary named that opaque job instead of the real guard. Rename the workflow (file name unchanged: it is the Guard.source identity) and add an invariant test: no blocking guard of the fast-lane registry may be classified advisory by pr_gate. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/split-reading-advisory.yml | 2 +- scripts/tests/test_fast_lane.py | 43 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/split-reading-advisory.yml b/.github/workflows/split-reading-advisory.yml index 759af4e723..c10e1be3d5 100644 --- a/.github/workflows/split-reading-advisory.yml +++ b/.github/workflows/split-reading-advisory.yml @@ -1,4 +1,4 @@ -name: Split-reading advisory +name: Split-reading ratchet (identity, manual dispatch) # Source d'identite pour la garde absorbee par fast-lane-shadow (registre # TRANCHE14 (ex-TRANCHE12 du registre avant renommage), issue #16762 — parapluie user « fusionner, pas empiler »). diff --git a/scripts/tests/test_fast_lane.py b/scripts/tests/test_fast_lane.py index 8661402de2..840e5cbf54 100644 --- a/scripts/tests/test_fast_lane.py +++ b/scripts/tests/test_fast_lane.py @@ -292,6 +292,49 @@ def test_delta_placeholders_are_resolvable(): assert "/tmp/b.json" in resolved and "/tmp/h.json" in resolved +def test_no_blocking_guard_is_read_as_advisory_by_pr_gate(): + """Un garde BLOQUANT ne doit pas etre classe advisory par `pr_gate`. + + `pr_gate` range en advisory tout check-run dont le nom de job contient + `advisory`, OU dont le workflow d'origine sur disque porte `advisory` dans + son `name:` (`derive_advisory_jobs`). Un garde absorbe garde son workflow + d'origine comme cible d'identite (`job.name == Guard.name`) : si ce + workflow s'appelle encore « ... advisory » apres la promotion du garde en + bloquant, `pr_gate` range son rouge dans le seau advisory. Le merge reste + bloque par le job agrege de la voie rapide, mais le resume du gate nomme + ce job opaque au lieu du vrai garde, et classe le vrai garde comme un + simple signal. Mesure du 2026-09-26 : `Split-reading ratchet (base vs PR)`, + promu bloquant par #17044, sorti de `split-reading-advisory.yml` (workflow + `Split-reading advisory`) -- seule instance sur 40 gardes. + """ + sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + import fast_lane_registry + import pr_gate + + advisory_jobs = pr_gate.derive_advisory_jobs() + assert advisory_jobs, "roster advisory vide : le controle serait aveugle" + + guards = {} + for value in vars(fast_lane_registry).values(): + if isinstance(value, list) and value and all( + isinstance(item, Guard) for item in value): + for guard in value: + guards[guard.name] = guard + assert len(guards) >= 30, f"registre lu partiellement : {len(guards)}" + + misread = sorted( + name for name, guard in guards.items() + if guard.blocking and pr_gate._is_advisory_name(name, advisory_jobs)) + assert misread == [], ( + "gardes bloquants classes advisory par pr_gate (renommer le `name:` " + f"du workflow d'origine) : {misread}") + + # controle positif : un garde advisory reste bien reconnu comme tel + advisory = [name for name, guard in guards.items() if not guard.blocking + and pr_gate._is_advisory_name(name, advisory_jobs)] + assert advisory, "aucun garde advisory reconnu : le predicat est muet" + + def test_advisory_flags_match_the_source_workflows(): """Le caractere advisory est une propriete du garde, pas du moteur.