From 24c9410970d44ee20280d1945e8821c61f640fd0 Mon Sep 17 00:00:00 2001 From: jsboige Date: Sun, 13 Sep 2026 10:20:45 +0200 Subject: [PATCH] docs(harness,#15793): plancher de session pluriel -- >=2 grains dont >=1 DEEP de CONTENU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R1 de proactive-coordination et G-VAR-1 passent de « >=1 PR entre 2 wakeups » / « plat principal DEEP ou MED » a « >=2 grains livres, dont >=1 DEEP portant un genre de CONTENU ». Le MED et le META restent admis au-dela du plancher. Motif, depose date dans le detail doc (mesure 2026-09-12T16:39Z, attribution par le tag Grain:) : 15 % de DEEP sur 7 j, et sur 48 h le META passe devant le CONTENU (109 contre 82). Contre-poids anti-inflation nomme, pas invente : le signal TIER-INFLATION et la re-qualification du tag par le merge-gate (section 3). Coherence -- les porteurs de la definition du plancher sont alignes : template de PR, commande continue, coordinator-discipline, skill coordinate, tables du merge-gate, README Vibe-Coding, citations de R1 dans variation_prev_guard. Le DEEP/MED du picker (3 chaines) n'est pas touche : le fichier est tenu par trois PRs ouvertes (#15764/#15778/#15813) -- suit en tache separee. Co-Authored-By: Claude Sonnet 5 --- .claude/commands/continue.md | 2 +- .claude/rules/coordinator-discipline.md | 2 +- .claude/rules/proactive-coordination.md | 10 +++++----- .claude/rules/variation-protocol.md | 18 ++++++++++-------- .claude/skills/coordinate/SKILL.md | 2 +- .github/pull_request_template.md | 2 +- MyIA.AI.Notebooks/GenAI/Vibe-Coding/README.md | 2 +- .../proactive-coordination-detail.md | 19 +++++++++++++++++++ docs/reference/variation-protocol-detail.md | 2 +- scripts/ci/variation_prev_guard.py | 12 +++++++----- scripts/tests/test_variation_prev_guard.py | 6 ++++-- 11 files changed, 51 insertions(+), 26 deletions(-) diff --git a/.claude/commands/continue.md b/.claude/commands/continue.md index 70acdf5ae9..a1baf1c556 100644 --- a/.claude/commands/continue.md +++ b/.claude/commands/continue.md @@ -28,7 +28,7 @@ python scripts/pick_idle_grain.py --lane --prev-genre `) avant de produire. @@ -46,7 +46,7 @@ Tirage pondéré dans **trois urnes** : **grain** (issue unitaire → la livrer) **Trois évasions mortes** (labels bannis : [détail, section Vocabulaire](../../docs/reference/proactive-coordination-detail.md)) : - **« Pas ma famille »** — FAUX : famille = préférence de **reporting**, pas frontière. **Seules deux vraies barrières** : (a) **GPU-only** ; (b) **vision-only** (→ lanes MiniMax/ai-01). Le reste est piochable partout. - **« Tout ce qui reste est gated »** — un *gate* qualifie une **prochaine action**, pas une issue entière : énumérer chaque issue ouverte + son gate précis ; si UNE a un sous-grain exécutable (doc, notebook CPU, audit, test), **le prendre**. - - **« Les micro-fixes suffisent »** — non : nettoyage/tooling/doc plafonnés, jamais le plat principal ; viser **DEEP/MED** chaque cycle (tiers : [variation-protocol.md](variation-protocol.md)). + - **« Les micro-fixes suffisent »** — non : nettoyage/tooling/doc plafonnés, jamais le plat principal ; le plancher exige un **DEEP de CONTENU** chaque cycle, le MED et le META venant au-delà (tiers et genres : [variation-protocol.md](variation-protocol.md)). **Mécanisme never-empty (ordre strict, inversé le 2026-08-20).** (1) **Tirer** — `pick_idle_grain.py`, au démarrage du cycle, systématiquement. (2) Un **steering nommé** du coordinateur (deep-queue, DM `[DISPATCH→inbox]`) **s'ajoute** au tirage quand il est déjà là : le brûler dans l'ordre, ne pas attendre entre items, `[CLAIMED]` avant chaque. Son absence ne se constate pas. (3) Ni tirage exploitable ni steering → le pool global reste la source : « rien à faire » demeure **structurellement impossible** tant que `gh issue list` renvoie >0. La deep-queue est un **bootstrap éphémère**, jamais la condition du travail. diff --git a/.claude/rules/variation-protocol.md b/.claude/rules/variation-protocol.md index ede831e8fa..37579102b7 100644 --- a/.claude/rules/variation-protocol.md +++ b/.claude/rules/variation-protocol.md @@ -59,8 +59,8 @@ Un genre hors liste est un **alias** que le merge-gate normalise : pas une viola Un grain dont le livrable principal est **réparer une PR pré-existante** (la débloquer après un PR-gate rouge, lever une review REQUEST_CHANGES, fixer un ratchet Papermill, etc.) **hérite du genre de la PR qu'il répare** : -- Réparer une PR `notebook-python` (ex : #12141 tranche E #12128) → tag `MED/notebook-python` → genre **CONTENU** → tient le plancher G-VAR-1. -- Réparer une PR `lean` (ex : #12252 Lean-21b companion) → tag `MED/notebook-lean` → genre **CONTENU** → tient le plancher. +- Réparer une PR `notebook-python` (ex : #12141 tranche E #12128) → tag `MED/notebook-python` → genre **CONTENU** → **grain de contenu au-delà du plancher** (le MED ne tient plus G-VAR-1 depuis le durcissement 2026-09-12). +- Réparer une PR `lean` (ex : #12252 Lean-21b companion) → tag `MED/notebook-lean` → genre **CONTENU** → **grain de contenu au-delà du plancher**, même raison. - Réparer une PR `guard` (ex : #11997 fix #11732 abort --update) → tag `MED/guard` → genre **META** → ne tient **pas** le plancher, comme toute PR META. Le raisonnement : G-VAR-1 demande « qu'est-ce qui atteint `main` quand ce travail aboutit ? » — la réponse regarde ce qui **arrive sur `main`**, pas ce que le REPAIR a fait. Quand une PR de notebook passe au vert et merge, ce qui arrive sur `main` est un notebook. Le REPAIR est de la fabrication qui sortait de l'entrepôt, pas de l'outillage. @@ -75,7 +75,9 @@ Le raisonnement : G-VAR-1 demande « qu'est-ce qui atteint `main` quand ce trava ## 2. Les trois gates durs -- **G-VAR-1 — Plat principal DEEP ou MED, dans un genre de CONTENU.** La PR-plancher du cycle (R1 de proactive-coordination) **DOIT** être DEEP ou MED **et** porter un genre de la classe CONTENU. **Une LIGHT ne satisfait JAMAIS le plancher ; un genre META non plus, quel que soit son tier.** Le pool global porte toujours du DEEP/MED de contenu : la monoculture vient du choix du plus facile *disponible*, pas d'une absence de substance. +- **G-VAR-1 — Plat principal DEEP, dans un genre de CONTENU.** La PR-plancher du cycle (R1 de proactive-coordination) **DOIT** être DEEP **et** porter un genre de la classe CONTENU. **Une LIGHT ne satisfait JAMAIS le plancher ; un genre META non plus, quel que soit son tier ; un MED non plus.** Le MED et le META restent **bienvenus au-delà** du plancher (sous budget G-VAR-2 pour leurs composantes LIGHT) : ce qui est exigé, c'est qu'**au moins un** grain DEEP de contenu porte le cycle. Le pool global porte toujours du DEEP de contenu : la monoculture vient du choix du plus facile *disponible*, pas d'une absence de substance. + + **Durcissement 2026-09-12 (#15793) et son contre-poids anti-inflation.** Le plancher passe de « DEEP **ou MED** » à **DEEP** : la mesure qui le motive (15 % de DEEP sur 7 j, META devant CONTENU sur 48 h, contraste par lane) est déposée datée dans [détail, section Plancher durci](../../docs/reference/proactive-coordination-detail.md). Exiger un DEEP crée une incitation à **sur-coter le tier** ; ce qui la couvre est **déjà en place et n'est pas inventé ici** — le signal bot `TIER-INFLATION`, et le merge-gate qui **re-qualifie lui-même un tag mal dérivé** (§3, ligne « Tag mal dérivé »). Le litmus DEEP reste objectif — *`main` contient-il désormais un résultat ou une capacité qui n'existait pas, dont la production a demandé du raisonnement de domaine ?* Le durcissement se paie en **lecture de tags par ai-01**, jamais en confiance. **Pourquoi la clause de genre existe** : le tier seul laissait une porte que la flotte a prise sans jamais mentir — un `tooling`/`guard` qui attrape un vrai défaut « change quelque chose », donc **MED** est défendable, donc le plancher paraît tenu, et **zéro contenu livré** (mesuré sur six semaines, aucun gate n'a rougi : [chiffres](../../docs/reference/variation-protocol-detail.md)). Un cycle dont le plat principal est META **n'a pas de plancher tenu**, même avec dix PR livrées. Le remède n'est pas de bannir le META (bienvenu au-delà du plancher, sous budget G-VAR-2 pour ses composantes LIGHT) mais d'exiger qu'**au moins un** grain de contenu porte le cycle. @@ -95,8 +97,8 @@ Le protocole ne mord que si `ai-01` cesse de merger passivement. À chaque passe |---|---| | LIGHT d'une lane à budget épuisé (G-VAR-2) | **HOLD** : citer la sortie de `variation_light_cap.py` (`N` LIGHT pour `M` grains), pas une estimation | | 2ᵉ même-GENRE consécutif (G-VAR-3) | **HOLD**, sauf exception mécanique #14357 rendue par l'organe (`exempt_runs` : second grain MED/DEEP **et** zéro fichier partagé) | -| Plancher tenu par une LIGHT (G-VAR-1) | steer vers un grain DEEP/MED de **contenu** du pool, **nommé** | -| Plancher tenu par un genre **META**, même tagué DEEP/MED (G-VAR-1) | le cycle n'a pas de plancher : merger la PR si elle est bonne, **et** nommer dans le même geste le grain de contenu qui portera le cycle suivant. Ne **pas** HOLD une PR META saine — la sanction porterait sur le mauvais objet ; c'est le **provisionnement** qui a manqué (obligation §4) | +| Plancher tenu par une LIGHT (G-VAR-1) | steer vers un grain **DEEP de contenu** du pool, **nommé** | +| Plancher tenu par un **MED** ou un genre **META**, même tagué DEEP (G-VAR-1) | le cycle n'a pas de plancher : merger la PR si elle est bonne, **et** nommer dans le même geste le grain DEEP de contenu qui portera le cycle suivant. Ne **pas** HOLD une PR META saine — la sanction porterait sur le mauvais objet ; c'est le **provisionnement** qui a manqué (obligation §4) | | Tag mal dérivé (tier sur-coté, genre pris sur la famille, alias/composé) | **re-qualifier le tag soi-même**, puis traiter selon le tag corrigé | | `lane` absente | **HOLD** jusqu'à déclaration — un grain sans lane est **structurellement incomptable**, et le cap devient inapplicable sans que personne ne le contourne | @@ -104,14 +106,14 @@ Le protocole ne mord que si `ai-01` cesse de merger passivement. À chaque passe **Ne jamais tenir une LIGHT plus d'une journée** : un hold prolongé fait réécrire le même travail par une autre lane. Passé 24 h : merger, ou fermer **en nommant le remplaçant**. -Le HOLD est **attache a la candidate**, jamais a la cadence de sa lane. Il ne sanctionne jamais la lane en idle ([coordinator-discipline.md](coordinator-discipline.md) R0/R4) et ne bloque jamais un nouveau grain DEEP/MED de contenu : il est **toujours accompagne d'un grain nomme** du pool, pousse en **double canal** (DM inbox + `[DISPATCH→inbox]` dashboard). HOLD sans remplacement, ou HOLD utilise pour reduire les dispatchs, = echec coordinateur. +Le HOLD est **attache a la candidate**, jamais a la cadence de sa lane. Il ne sanctionne jamais la lane en idle ([coordinator-discipline.md](coordinator-discipline.md) R0/R4) et ne bloque jamais un nouveau grain **DEEP de contenu** : il est **toujours accompagne d'un grain nomme** du pool, pousse en **double canal** (DM inbox + `[DISPATCH→inbox]` dashboard). HOLD sans remplacement, ou HOLD utilise pour reduire les dispatchs, = echec coordinateur. ## 4. Obligation de provisionnement — ce qui lie ai-01 (HARD) La cause racine est **autant** un défaut de provisionnement qu'un réflexe de facilité worker : sans substance stockée, le worker tombe sur les veines faciles. Chaque cycle `/coordinate`, `ai-01` : 0. **Le tirage est la voie par défaut** (règle 5 de [proactive-coordination.md](proactive-coordination.md), mandat user 2026-08-20) : une lane qui n'a pas reçu de steering **tire** et n'attend rien. Le provisionnement ci-dessous reste dû — il devient l'**exception nommée**, et c'est *parce que* c'est une exception qu'il doit être le plus équilibré possible : un steering qui répète le genre du cycle précédent fait pire que le tirage, puisqu'il **écarte** un mécanisme conçu pour ne pas biaiser. -1. **Provisionne ≥1 grain DEEP/MED de CONTENU par lane**, **groundé firsthand** (`gh issue view`), varié en genre d'une lane à l'autre. Un provisionnement uniquement `guard`/`tooling`/`docs` ne satisfait pas l'obligation — il garantit que toutes les lanes manqueront leur plancher. +1. **Provisionne ≥1 grain DEEP de CONTENU par lane**, **groundé firsthand** (`gh issue view`), varié en genre d'une lane à l'autre. Un provisionnement `MED`, ou uniquement `guard`/`tooling`/`docs`, ne satisfait pas l'obligation — il garantit que toutes les lanes manqueront leur plancher. Deux corollaires mesurés : **agréger les GENRES des merges récents** avant de provisionner, pas seulement leurs tiers (« 15 MED sur 21 » avait l'air sain et cachait 15 grains de harnais pour 0 `qc`/`genai`/`notebook`) ; et **un batch-close de famille crée une dette de provisionnement**, à honorer dans le même cycle (précédent ICT). 2. **Varie la loterie** d'un cycle à l'autre — le coordinateur applique G-VAR-3 à son propre dispatch. 3. **Dissocie admission et production** : plusieurs candidates en HOLD, `DWELL`, review ou attente de merge ne diminuent jamais le provisionnement. La queue d'admission se résorbe par une piste de digestion parallèle ; elle n'applique aucune backpressure globale aux producteurs. @@ -120,7 +122,7 @@ Sous-provisionner puis merger la monoculture qui en résulte est **le** manqueme ## 5. Auto-détection -Avant de claim / de merger : **« ce grain est-il générable-en-série (LIGHT) ET (budget épuisé OU même-genre-que-le-précédent) ? »** Si oui, c'est la monoculture — le worker pioche un DEEP/MED, le coordinateur HOLD+redirige. +Avant de claim / de merger : **« ce grain est-il générable-en-série (LIGHT) ET (budget épuisé OU même-genre-que-le-précédent) ? »** Si oui, c'est la monoculture — le worker pioche un **DEEP de contenu**, le coordinateur HOLD+redirige. Et la question que le tier seul ne posait pas, à se poser en fin de cycle : **« qu'est-ce que ce cycle a ajouté au dépôt qu'un lecteur ou un étudiant puisse utiliser ? »** Si la seule réponse honnête est « un détecteur de plus, un guard de plus, une doc de plus », le plancher n'est pas tenu — quel que soit le nombre de PR mergées et quels que soient les tiers déclarés. Côté worker : piocher un grain de contenu. Côté coordinateur : c'est un défaut de provisionnement (§4), pas une faute de lane. diff --git a/.claude/skills/coordinate/SKILL.md b/.claude/skills/coordinate/SKILL.md index f445ef261c..0b5163bfaf 100644 --- a/.claude/skills/coordinate/SKILL.md +++ b/.claude/skills/coordinate/SKILL.md @@ -33,7 +33,7 @@ Cycle de coordination du cluster CoursIA. **Reserve au coordinateur ai-01** : un ### Phase 3 - Production et digestion en parallèle -**Production avant digestion (HARD)** : lancer ou maintenir les deep queues productives avant d'attendre la fin de la passe de merge. CI, review, `DWELL`, HOLD ou backlog bloquent une candidate, jamais une lane. Chaque worker draine a chaque session **tous** les nits et reserves reparables de **toutes** ses PRs, sequentiellement, puis enchaine plusieurs grains DEEP/MED de contenu ; traiter un seul nit ou livrer une seule PR ne clot pas la session. La saturation de digestion déclenche une piste de réparation/capacité parallèle ; elle ne réduit jamais les dispatchs. +**Production avant digestion (HARD)** : lancer ou maintenir les deep queues productives avant d'attendre la fin de la passe de merge. CI, review, `DWELL`, HOLD ou backlog bloquent une candidate, jamais une lane. Chaque worker draine a chaque session **tous** les nits et reserves reparables de **toutes** ses PRs, sequentiellement, puis enchaine plusieurs grains dont **au moins un DEEP de contenu** (plancher durci #15793) ; traiter un seul nit ou livrer une seule PR ne clot pas la session. La saturation de digestion déclenche une piste de réparation/capacité parallèle ; elle ne réduit jamais les dispatchs. Sweep batché, dossiers préparés en parallèle, décision séquentielle au coordinateur — pas de re-audit intégral PR par PR. Déléguer agressivement la préparation vérifiable : adjoint pour preflight B.0/exact-head, Hermes et NanoClaw pour la première digestion spécialisée ; ai-01 garde la lecture B.0 personnelle finale, le verdict et le merge. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 789570fd86..1b5cea104d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -19,7 +19,7 @@ TIER (test objectif, detail dans .claude/rules/variation-protocol.md) : GENRE : lean . qc . training . genai . notebook-python . notebook-dotnet . notebook-lean . slides . research-code . docs . guard . refactor . ledger . readme . test . tooling CONTENU (satisfait le plancher G-VAR-1) = les 9 premiers ; META = les 7 derniers. -Gates durs : plancher du cycle = DEEP ou MED (G-VAR-1) ; budget LIGHT = max(1, grains_du_jour//3) par lane, agrege (G-VAR-2) ; pas 2x le meme genre LIGHT consecutif (G-VAR-3). +Gates durs : plancher du cycle = DEEP dans un genre de CONTENU (G-VAR-1, durci #15793) ; budget LIGHT = max(1, grains_du_jour//3) par lane, agrege (G-VAR-2) ; pas 2x le meme genre LIGHT consecutif (G-VAR-3). --> ## Summary diff --git a/MyIA.AI.Notebooks/GenAI/Vibe-Coding/README.md b/MyIA.AI.Notebooks/GenAI/Vibe-Coding/README.md index df45fa01d1..4b65a94c94 100644 --- a/MyIA.AI.Notebooks/GenAI/Vibe-Coding/README.md +++ b/MyIA.AI.Notebooks/GenAI/Vibe-Coding/README.md @@ -191,7 +191,7 @@ Le harnais réel fonctionne comme une **flotte** d'agents qui se coordonnent san **Coordinateur** : un seul agent dans ce rôle. Il review et merge les PRs, dispatche le travail via DM + dashboard, lit les deux dashboards workspace co-égaux (jamais un seul), et garde la cohérence cross-machine. Il ne commite jamais dans une branche feature et ne ferme jamais une issue qui ne lui appartient pas. -**Workers** : N agents, chacun portant typiquement une *lane* (machine × workspace). Un worker pioche dans le pool d'issues ouvertes (cross-lane, pas siloté par famille), livre une PR, et poste un rapport court sur son dashboard. Il peut aussi déléguer des side-tracks à des sous-agents spécialisés (exécution notebook, audit, prover Lean) en arrière-plan pendant qu'il tient une track principale. Chaque cycle vise **au moins une PR** ; le plancher est une substance DEEP ou MED, jamais un scan-générable. +**Workers** : N agents, chacun portant typiquement une *lane* (machine × workspace). Un worker pioche dans le pool d'issues ouvertes (cross-lane, pas siloté par famille), livre une PR, et poste un rapport court sur son dashboard. Il peut aussi déléguer des side-tracks à des sous-agents spécialisés (exécution notebook, audit, prover Lean) en arrière-plan pendant qu'il tient une track principale. Chaque cycle vise **plusieurs grains, dont au moins un DEEP de contenu** ; le plancher est une substance, jamais un scan-générable. **Gouvernance** : les règles de coordination (dispatch DM-first, jamais d'idle sanctionné, deep-queue par lane, fallback perenne quand la queue s'épuise) vivent dans `.claude/rules/coordinator-discipline.md` et `.claude/rules/proactive-coordination.md`. Elles sont **auto-chargées** au début de chaque session — c'est ce qui fait que le comportement du cluster est stable même quand un worker change. diff --git a/docs/reference/proactive-coordination-detail.md b/docs/reference/proactive-coordination-detail.md index fe9745f482..7cc48b6ac8 100644 --- a/docs/reference/proactive-coordination-detail.md +++ b/docs/reference/proactive-coordination-detail.md @@ -2,6 +2,25 @@ Detail de [.claude/rules/proactive-coordination.md](../../.claude/rules/proactive-coordination.md). Voir aussi [subagents-reference.md](subagents-reference.md), [scripts-reference.md](scripts-reference.md). +## Plancher durci — « plusieurs grains dont un DEEP de CONTENU » (mandat user 2026-09-12, #15793) + +R1 de [proactive-coordination.md](../../.claude/rules/proactive-coordination.md) et G-VAR-1 de [variation-protocol.md](../../.claude/rules/variation-protocol.md) portent un plancher **pluriel et durci** : **≥ 2 grains livrés entre 2 wakeups, dont ≥ 1 DEEP dans un genre de CONTENU**. Le MED et le META restent **bienvenus au-delà** du plancher — ils ne le tiennent plus. + +**Ce qui le motive — mesure du 2026-09-12T16:39Z**, attribution par le tag `Grain:` (jamais `--author` : l'identité de poussée `jsboige` est partagée, L721) : + +| Fenêtre | merges | DEEP | MED | LIGHT | CONTENU | META | +|---|---:|---:|---:|---:|---:|---:| +| 7 jours (depuis 09-05) | **583** (83,3/j) | 88 (**15 %**) | 400 | 88 | 314 | 260 | +| 48 heures | **196** (98,0/j) | 35 (**18 %**) | 113 | 45 | **82** | **109** | + +Le volume n'est pas le défaut. Deux choses le sont : (1) la **dureté** — 15 % de DEEP sur 7 j, le plancher R1 d'alors (« ≥ 1 PR ») étant tenu par n'importe quoi, massivement par du MED ; (2) la **bascule CONTENU/META** — sur 7 j le CONTENU tient encore (314 contre 260), sur 48 h le META passe devant (109 contre 82) : la flotte construit l'atelier plus vite qu'elle n'y fabrique. + +Le contraste par lane rend le mécanisme lisible : la lane au plus gros volume sur 7 j (`myia-po-2023:CoursIA`, 108 merges) est la plus faible en substance (39 CONTENU, **8 DEEP**), tandis que `myia-po-2027:CoursIA-2` rend 32 CONTENU et 10 DEEP sur 41 merges. La lane la plus META de la flotte est celle du coordinateur (`myia-ai-01:CoursIA` : 38 merges sur 7 j pour **1 DEEP** et 7 CONTENU — 0 DEEP et 1 CONTENU sur 48 h). + +**Contre-poids anti-inflation, nommé et non inventé.** Exiger un DEEP crée une incitation à **sur-coter le tier**. Ce qui la couvre est déjà en place : le signal bot `TIER-INFLATION`, et le merge-gate qui **re-qualifie lui-même un tag mal dérivé** (§3 de [variation-protocol.md](../../.claude/rules/variation-protocol.md), ligne « Tag mal dérivé »). Le litmus DEEP reste objectif — *`main` contient-il désormais un résultat ou une capacité qui n'existait pas, dont la production a demandé du raisonnement de domaine ?* Le durcissement se paie en **lecture de tags par ai-01**, jamais en confiance. + +**Éditer `.claude/rules/**` exige un sign-off user** (CLAUDE.md §A) : la PR #15793 qui a porté ce durcissement ne se self-merge pas. + ## Backlog pickup — sources autorisees (ordre de priorite decroissant) Wakeup vide : prendre la **premiere source non-vide**, **un seul item**, produire **1 PR concrete**. diff --git a/docs/reference/variation-protocol-detail.md b/docs/reference/variation-protocol-detail.md index 80af54efc4..52fe1f153a 100644 --- a/docs/reference/variation-protocol-detail.md +++ b/docs/reference/variation-protocol-detail.md @@ -29,7 +29,7 @@ Le `prev:` déclaré garde une valeur **documentaire** (il dit ce que l'auteur c **Ce que ça coûte quand on lit la règle à la lettre.** Le 2026-09-11, au merge-gate, le coordinateur a dérivé l'adjacence à la main depuis les `prev:` déclarés et posé **deux HOLD motivés G-VAR-3** sur #15551 (`prev: LIGHT/readme #15550`) et #15552 (`prev: LIGHT/readme #15551`) — une chaîne de trois `LIGHT/readme` déclarés, qualifiée d'« inexemptable par construction ». L'organe, interrogé ensuite, rend l'inverse sur les deux : `adjacent: false`, `prev_genre: "guard"`, `prev_pr: 15569`, `prev_source: "merged-sequence"`. Le prédécesseur réel était #15569 (`MED/guard`), mergé à 11:34:51Z, qui s'interpose dans la séquence et **rompt** l'adjacence. Deux rétractations ont dû être postées (`issuecomment-5633877872`, `issuecomment-5633878069`). Le défaut n'est donc pas réservé aux workers : il a fait écrire un motif faux au coordinateur, dans le geste même que la règle existe pour outiller. -**Conséquence opérationnelle, à ne pas inverser** : un `prev:` qui pointe une PR **encore ouverte n'est pas un défaut**, et ne doit pas être signalé. La piste « vérifier que le `prev:` référence une PR mergée » a été **implémentée, mesurée, puis retirée** le 2026-09-08 : invariant `PREV-ABANDONED`, [`validate_prev_targets`](../../scripts/ci/variation_prev_guard.py) — le gate ne rougit désormais que sur une PR **fermée sans merge** (lignée abandonnée), jamais sur une PR en vol. Mesure : **cinq** PRs ouvertes bloquées (#15156, #15190, #15207, #15209, #15210) citant **quatre** prédécesseurs distincts (#15129, #15175, #15199, #15203) — **les quatre OPEN, pas un seul abandonné**. Flaguer `OPEN` punissait exactement le comportement que **R1** de [`proactive-coordination.md`](../../.claude/rules/proactive-coordination.md) *impose* (« 1 PR entre 2 wakeups = PLANCHER, jamais plafond »). Seule la relecture trompeuse est un défaut — et elle est traitée par la prose ci-dessus, pas par un gate. +**Conséquence opérationnelle, à ne pas inverser** : un `prev:` qui pointe une PR **encore ouverte n'est pas un défaut**, et ne doit pas être signalé. La piste « vérifier que le `prev:` référence une PR mergée » a été **implémentée, mesurée, puis retirée** le 2026-09-08 : invariant `PREV-ABANDONED`, [`validate_prev_targets`](../../scripts/ci/variation_prev_guard.py) — le gate ne rougit désormais que sur une PR **fermée sans merge** (lignée abandonnée), jamais sur une PR en vol. Mesure : **cinq** PRs ouvertes bloquées (#15156, #15190, #15207, #15209, #15210) citant **quatre** prédécesseurs distincts (#15129, #15175, #15199, #15203) — **les quatre OPEN, pas un seul abandonné**. Flaguer `OPEN` punissait exactement le comportement que le **R1 d'alors** de [`proactive-coordination.md`](../../.claude/rules/proactive-coordination.md) *imposait* (« 1 PR entre 2 wakeups = PLANCHER, jamais plafond » — R1 porte depuis #15793 un plancher pluriel, la conclusion du 2026-09-08 est inchangée). Seule la relecture trompeuse est un défaut — et elle est traitée par la prose ci-dessus, pas par un gate. **Hors périmètre** : le vocabulaire de genre fail-OPEN de `canonicalize_genre` est un autre défaut, suivi par **#13475**. diff --git a/scripts/ci/variation_prev_guard.py b/scripts/ci/variation_prev_guard.py index 618ec456d5..72804937e4 100644 --- a/scripts/ci/variation_prev_guard.py +++ b/scripts/ci/variation_prev_guard.py @@ -339,11 +339,13 @@ def validate_prev_targets( ========== ===================================================== Flagging ``OPEN`` punished the exact behaviour R1 of - `proactive-coordination.md` *mandates*: "1 PR entre 2 wakeups = - PLANCHER, jamais plafond -- une PR livree ne clot pas la session, - re-pioche IMMEDIATEMENT". A lane that opens its next PR before the - previous one merges is working as instructed, and this gate rejected - it for that. Measured on 2026-09-08 at 13:20Z, by replaying this + `proactive-coordination.md` mandated at the time of this measurement: + "1 PR entre 2 wakeups = PLANCHER, jamais plafond -- une PR livree ne + clot pas la session, re-pioche IMMEDIATEMENT" (R1 porte depuis #15793 + un plancher pluriel ; la conclusion ci-dessous est inchangee). A lane + that opens its next PR before the previous one merges is working as + instructed, and this gate rejected it for that. Measured on + 2026-09-08 at 13:20Z, by replaying this organ against the real bodies and commit messages: **five** open PRs blocked (#15156, #15190, #15207, #15209, #15210), citing **four** distinct predecessors (#15129, #15175, #15199, #15203) -- **all four diff --git a/scripts/tests/test_variation_prev_guard.py b/scripts/tests/test_variation_prev_guard.py index 5df2e34319..c49b950570 100644 --- a/scripts/tests/test_variation_prev_guard.py +++ b/scripts/tests/test_variation_prev_guard.py @@ -216,8 +216,10 @@ def test_prev_abandoned_blocks(): def test_prev_open_abstains_the_predecessor_is_in_flight(): # THE REPAIR. Same body, same current PR, ONE field different -- and the # verdict flips. A predecessor still open is not a broken lineage: it is - # the state R1 of `proactive-coordination.md` mandates ("1 PR entre 2 - # wakeups = PLANCHER, jamais plafond -- re-pioche IMMEDIATEMENT"). + # the state R1 of `proactive-coordination.md` mandated when this + # invariant was retired ("1 PR entre 2 wakeups = PLANCHER, jamais + # plafond -- re-pioche IMMEDIATEMENT"; R1 porte depuis #15793 un + # plancher pluriel, la conclusion est inchangee). # # The witness the original invariant cited, #13473, was tagged # `prev: ... #13465` while #13465 was OPEN -- and it MERGED on