# Coordination proactive — plusieurs grains/wakeup plancher + pool global + never-idle

S'applique à **tous les workers du cluster CoursIA** (po-2023/2024/2025/2026) et au **coordinateur ai-01**. Source : mandat user 2026-05-23, durci 2026-06-30 → 2026-07-19.

**Détail (backlog 8 sources, cadence, anti-patterns, incidents, leçons L721/L1356 complètes, picker, veine)** : [docs/reference/proactive-coordination-detail.md](../../docs/reference/proactive-coordination-detail.md).

## Règles HARD

0. **Avant de TIRER un grain neuf : faire avancer tout ce qui peut avancer (HARD, mandat user 2026-09-19).** L'ordre de session est ferme — **(a)** toutes tes PRs ouvertes qui peuvent bouger : réserve à lever, nit à traiter, rebase, dossier `[ADJOINT PREFLIGHT]` à refabriquer, check rouge à réparer ; **(b) ensuite seulement** le tirage du picker. Cette règle est **en amont** de la règle 5 : elle ne remplace pas le tirage, elle le conditionne.

   **Pourquoi un ORDRE, et pas seulement un plancher.** La règle 1 compte les grains *livrés* — un compte qu'on satisfait en empilant du neuf par-dessus du non-fini. Mesure du 2026-09-19 sur **213 PRs ouvertes** : **104 CLEAN** (zéro conflit, mergeables sur-le-champ) et **174 sans aucune review**. Le dépôt ne souffre pas d'un manque de grains ouverts ; il souffre de grains ouverts qui n'atterrissent pas. Verbatim user : « *nombreuses sont celles qui n'auront pas besoin de plus de 4-5 commits de plus, donc si vous travaillez bien, les PRs en souffrance devraient pouvoir partir en moins de 24 h. Or ce n'est pas ce qu'on observe.* »

   **Trier par COÛT DE DOSSIER, pas par ancienneté seule.** Une PR **CLEAN et sans review** ne demande qu'une attestation tierce : c'est le grain le moins cher du dépôt. Une BLOCKED demande un diagnostic. L'ancienneté ordonne **à l'intérieur** du sous-ensemble le moins cher ; elle ne le choisit pas.

   **N PRs portant la MÊME classe de défaut = une cause, pas N grains.** Traiter la cause une fois plutôt que d'ouvrir N allers-retours.

   **Un script exécuté sur la branche remplace plusieurs allers-retours de commentaires.** Quand le défaut est **mécanique** — fixer canonique existant, geste déterministe, diff vérifiable — l'exécuter sur la branche de la PR plutôt que poster un commentaire qui coûte une session entière à son porteur. Quatre bornes, toutes requises : la PR est à **lane unique** (un `[CLAIMED]` vivant d'une autre lane renvoie au commentaire, cf [lane-claim-protocol.md](lane-claim-protocol.md)) · le commentaire ou le dossier **nomme le script et le commit** (un commit muet sur la branche d'autrui est indiscernable d'un détournement) · ça **ne lève aucune réserve de tiers** (B.0 : une phrase lève, pas un SHA) · si une cellule source de notebook est touchée, **la re-exécution redevient due** (C.2).

1. **≥2 grains livrés entre 2 wakeups, dont ≥1 DEEP portant un genre de CONTENU = PLANCHER, jamais plafond.** Une PR livrée ne **clôt pas** la session : re-pioche **immédiatement** et enchaîne autant de PRs que la fenêtre le permet (débit nominal ~2 PR/h). S'arrêter après 1 PR alors qu'il reste du temps **et** 60+ issues ouvertes = **sous-régime**, pas « cycle terminé ». Le plancher est **pluriel et durci** (mandat user 2026-09-12) : un cycle dont le plat principal est MED ou META **n'a pas de plancher tenu**, même avec plusieurs PR livrées. La mesure qui motive ce durcissement — 15 % de DEEP sur 7 j, et sur 48 h le META passant devant le CONTENU — est déposée datée dans [détail, section Plancher durci](../../docs/reference/proactive-coordination-detail.md) ; le tier et le genre se tranchent en [variation-protocol.md](variation-protocol.md) (G-VAR-1).
2. **2 tracks en flight minimum** : une **track principale** (mission dispatchée, sinon la tête du tapis) + une **side-track autonome** que le worker avance **même si le coordinateur s'absente 1-2 jours**.
3. **Side-tracks → sous-agents spécialistes async (HARD).** Quand un specialist `.claude/agents/` couvre la side-track, la déléguer en **`run_in_background: true`** pendant que le worker interactif tient la main track. Roster : [docs/reference/subagents-reference.md](../../docs/reference/subagents-reference.md).
4. **Backlog pickup au wakeup vide (HARD).** Sans nouveau feedback / directive / tâche : **ne pas s'arrêter** — piocher dans le backlog et produire la PR du cycle.

5. **Pool = TOUT l'ouvert, cross-lane, jamais siloté (HARD).** Cette règle **domine 1-4** : le backlog n'est PAS ta « famille » ni ta « lane », c'est **`gh issue list --state open` en entier**. Ta lane (`machine × workspace`) est une étiquette de **reporting**, PAS une frontière de travail. Tu **NE demandes PAS un grain au coordinateur** : tu **tires** (ci-dessous), prends **n'importe quelle issue techniquement exécutable** (autre famille, autre langage, autre lane — **rien n'est « le turf d'un autre »**), poses `[CLAIMED] <#N> — <machine:workspace> <ts>` (anti-double-claim), livres. **Conclure « rien à faire » alors que `gh issue list` renvoie >0 = échec de méthode.** Sur-te-spécialiser = te starver toi-même.

**Filtre `candidate-delivered` (#10466)** : une part du pool est du travail **déjà livré mais non fermé** (`See #N` même en résolution complète → GitHub ne ferme pas). Le label, posé par le workflow advisory (PR merged référençant l'issue + silence post-merge, EPICs exclus), **signale sans fermer** — ai-01 tranche en lecture body (G.9). Ne pas re-piocher une issue ainsi labellisée sans vérification firsthand.

**Le pool ne se scanne plus à la main — il se TIRE, et le tirage est le PREMIER GESTE DE CHAQUE CYCLE (HARD, mandats user 2026-08-14 et 2026-08-20).** `gh issue list` plafonne à **30 résultats** triés par récence : un scan manuel ne voit que le récent et referme la boucle de monoculture — la troncature demande un organe, pas plus de vigilance (mesures : [détail, section Picker](../../docs/reference/proactive-coordination-detail.md)). Un steering nommé du coordinateur est **un grain de plus dans la main de la lane**, jamais une condition de départ : on le brûle s'il est déjà là, on ne constate pas son absence. Son équilibrage entre familles et genres reste dû au coordinateur (§4 de [variation-protocol.md](variation-protocol.md)).

```bash
python scripts/pick_idle_grain.py --belt --lane <machine:workspace>
```

**Le tapis roulant** (`--belt`, #18832, mandat user 2026-10-02) sert l'ouvert entier par **dernière visite** : la plus ancienne en tête, une issue servie repart en queue, ni pondération ni refus. Les urnes restent un **filtre** : **grain** (issue unitaire → la livrer) · **umbrella** (un EPIC → piocher ou **créer un sous-grain dedans**, jamais claimer l'EPIC entier) · **delivered** (une `candidate-delivered` → **réservée au coordinateur et à l'adjoint**, mandat user 2026-09-07 [#15069, garde `DELIVERED_URN_LANES` dans `pick_idle_grain.py`] : une lane worker qui en rencontre une poste `[INFO] candidate-delivered` avec sa preuve et rend la main). Le tirage pondéré historique (âge de création + délaissement : [détail, section Picker](../../docs/reference/proactive-coordination-detail.md)) ne sert plus qu'au diagnostic.

**Sur un tirage d'urne `umbrella`, confronter le body au réel AVANT de juger l'actionnabilité (#11900).** Le tirage pondère le **délaissement** ; or plus une EPIC est ancienne, plus son body a de chances d'être faux — **plus le picker fait ce qu'on lui demande, plus il remonte d'impasses apparentes**. Deux tirages consécutifs ont remonté deux EPICs entièrement actionnables dont le paragraphe bloquant avait survécu à sa propre résolution (PR déjà mergée ; décision déjà prise par le user le jour même). Conclure « bloquée sur autrui » sans mesurer, c'est reposer un grain disponible et croire le pool saturé. Le **geste** obligatoire et ses trois organes (l'artefact sur `main`, le plateau `gh pr list --search head:`, le commentaire de fermeture) sont portés par [verify-before-claiming.md](verify-before-claiming.md) règle 5, auto-chargée ; le corpus des cas et la **précondition toxique** — une condition de reprise que *rien ne mesure* ne peut jamais être constatée atteinte, et diffère indéfiniment contre la volonté de qui l'a posée — vivent dans [picker-delaisse-detail.md](../../docs/reference/picker-delaisse-detail.md).

**Le tapis ordonne, l'agent ne re-trie pas.** Il prend les candidats dans l'ordre et pose son `[CLAIMED]` (`check_lane_claim.py` avant d'**éditer**, cf [lane-claim-protocol.md](lane-claim-protocol.md)) ; un candidat sauté l'est pour une barrière réelle (GPU-only, vision-only, second LIGHT du même genre d'affilée selon G-VAR-3 de [variation-protocol.md](variation-protocol.md)), dite dans le rapport de cycle. Les exclusions factuelles (`--exclude-issue`, labels, `--urns`) restent disponibles. **Aucun résultat vide filtré ne justifie un `[ASK coordinator]` ni un statut idle.**

**Sur un tirage d'urne `umbrella`, confronter le body au réel AVANT de juger l'actionnabilité (HARD, #11900).** Un body est daté de sa rédaction, jamais de sa lecture — et le picker pondère le **délaissement**, donc plus une EPIC est ancienne, plus son body a de chances d'être faux : **plus le picker fait son travail, plus il remonte d'impasses apparentes.** Le paragraphe bloquant y a souvent survécu à sa propre résolution (mesuré sur #2874 et #7357, dont les deux conditions étaient levées depuis 68 j et 31 j). Sans ce réflexe, le régime produit des rerolls en boucle et la lane conclut à tort que le pool est saturé — l'inverse exact de l'intention de la règle 5.

Trois surfaces mentent vite, et **deux sont décidables mécaniquement** :

1. **un numéro de contenu** (`#N`) → l'organe résout son type et son état. Une EPIC dont **tous** les enfants cités sont CLOSED n'a plus rien à piocher : le picker a offert un **conteneur**, pas un grain ;
2. **un chemin de fichier** → vérifié contre l'arbre de travail (`find`, **jamais** `ls` : un `ls` sur un chemin périmé rend vide, et « rien trouvé » y est indiscernable de « pas regardé ») ;
3. **une demande de décision** → **non automatisable** : `gh issue view N --json comments`, la réponse est souvent le premier commentaire.

**Organe** (la règle ne tient pas par la vigilance) :

```bash
python scripts/ci/check_umbrella_freshness.py <N> [<N>...]   # exit 1 = SATURATED, ne pas piocher ici
```

`SATURATED` (tous les enfants cités sont fermés) est le **seul** verdict qui prend la sortie **1** — c'est la seule lecture non ambiguë. Un chemin cité introuvable sort en **avertissement**, jamais en rouge : il a pu légitimement déménager. `UNRESOLVED` (aucun enfant cité) reste **advisory** et renvoie à la surface 3.

**Ce que l'organe ne dit pas** : il mesure la vacuité du **conteneur**, pas la viabilité du **grain**. Une EPIC `FRESH` peut n'avoir que du résiduel `INTRINSIC` — mesuré le 2026-09-15 : les 8 `sorry` distincts de `knot_lean`, que #1453 désigne comme « le grain DEEP de cet EPIC », portent chacun, dans le fichier, l'annotation qui les déclare hors d'atteinte (`decades` away / *not in Mathlib*). La lecture du contenu reste due.

**Précondition toxique** : une condition de reprise que *rien ne mesure* ne peut jamais être constatée atteinte — elle diffère indéfiniment contre la volonté de qui l'a posée. La rendre observable est **plus petit** que le travail différé, et le débloque entièrement.

**Réparation d'un body périmé** : préfixer d'un bloc `> Etat mesure au <date>` (« ce que le body dit » / « ce qui est mesuré », préconditions et leur état, ce qu'une lane peut faire), **conserver l'historique intact en dessous**, écrire la **portée** de ce qui n'a pas été vérifié. Jamais de réécriture en place : le lecteur suivant doit voir ce qui a changé.

**Reparer son propre rouge, dans le meme cycle que la production (HARD, mandat user 2026-08-22 ; l'ordre retire le 2026-09-12).** Le picker rend la reparation (sortie **0** — le grain rendu *est* la reprise) tant que la lane porte une PR **bloquee et ouverte depuis plus de 24 h** ; il nomme la liste, la cause et le geste. Ce n'est **pas un prealable a produire** : au debut de chaque session, la lane inventorie **toutes** ses PRs portant nits, reserves, `CHANGES_REQUESTED` ou threads inline non resolus, puis traite sequentiellement **tous** les points reparables qui la concernent — jamais un seul nit ou une seule PR. Chaque remarque recoit une correction reelle si necessaire et une reponse ecrite qui la nomme ; les attentes externes (`CI`, `DWELL`, re-review, merge ou dependance) **sortent du champ de vision de la lane** — elles appartiennent a la candidate, qui attend seule, et une candidate n'attend jamais avec sa lane. La file reparable ne precede pas le tirage : la lane tire son grain DEEP de contenu dans le **meme** cycle, sequentiellement, tant que la fenetre reste ouverte.

- **Une PR reparee et mergee compte dans le plancher R1** : ce n'est pas un a-cote, c'est du travail deja ecrit porte a son terme. Elle ne le tient pas **seule** — le plancher exige ≥1 DEEP de CONTENU, et un REPAIR est au mieux MED ([variation-protocol.md](variation-protocol.md), §Grain REPAIR) : il compte comme grain, jamais comme plancher. Une PR seulement corrigee mais encore en attente externe ne compte pas encore ; la lane continue donc a produire. Elle garde son tag `Grain:` d'origine — la reparation ne re-qualifie pas le tier.
- **Ce qui compte comme a reprendre en premiere action** est ce qui **empeche vraiment le merge**, en **quatre** causes : un check **requis** en echec (champ GraphQL `isRequired`, pas un motif de nom), un conflit avec `main`, un `CHANGES_REQUESTED` non leve, et — **en tete des trois autres** — un **point de review non leve**. Il est propose a chaque cycle et n'interdit jamais le grain suivant. Un advisory rouge n'est **pas** un rouge ; `mergeStateStatus: BLOCKED` vaut aussi « en attente de review », que la lane ne peut pas lever — une telle PR n'est donc **pas** un grain de reparation, et sort du champ de la lane jusqu'a ce qu'un geste de lane redevienne possible.
- **Les points de review passent en premier** car ils sont structurellement aveugles aux trois surfaces de [§B.0](../../CLAUDE.md) (nits du user en issue comments, réserves d'Hermes en préfixe de body, threads inline) : une PR peut être verte, sans conflit, sans `CHANGES_REQUESTED` — et rester non mergeable. Le picker délègue la détection à `check_unaddressed_nits.analyse`, **le même organe que le merge-gate** — s'ils divergeaient, une lane serait autorisée à produire du neuf sur une PR que le merge-gate refusera. La cause est listée d'abord parce que ce qui lève une remarque est **une phrase, pas un SHA**.
- **Organe injoignable ≠ ardoise propre** : quand la lecture des points échoue, le picker tire quand même mais le **dit** — vérifier à la main (`python scripts/check_unaddressed_nits.py <N>`) avant de produire.
- **Rouge non réparable par cette lane** (garde cassée sur `main`, dépendance d'une autre PR) : l'**écrire en commentaire sur la PR**, puis `--ignore-red`. L'échappatoire se justifie par écrit, elle ne se prend pas en silence.
- **Portée écrite** : une PR sans tag `Grain:` lisible n'est imputable à aucune lane et reste invisible à ce garde. Son tag manquant est lui-même le défaut à corriger — c'est au coordinateur de les reprendre.

6. **Variété obligatoire — le tarissement est structurellement interdit (HARD).** Les règles 1-5 interdisent l'idle ; celle-ci interdit la **monotonie**, et pose l'auto-alimentation comme **principe**, pas comme rattrapage du coordinateur. Une lane ne PEUT PAS se tarir : le worker pioche **de lui-même**, **varié**, même si le coordinateur est absent plusieurs cycles.
   - **Substance en plat principal** : chaque cycle, viser un grain de fond (preuve Lean, backtest/training, série notebook, moteur SOTA, sécu/infra).
   - **Nettoyage/doc = à-côté plafonné** (budget G-VAR-2, cf [variation-protocol.md](variation-protocol.md)) : nécessaire, jamais le plat unique. Une journée entière sur un seul registre monotone = sous-régime à corriger **de soi-même**.
   - **Rotation genres ET familles** : alterner Lean / .NET / Python / QC / GenAI / docs ; le pool global rend la variété toujours accessible ; ne jamais tunneliser un mono-thème.

7. **Never-idle ancré — le « forensic-floor » n'est PAS un livrable (HARD).** Les workers ont contourné 1-6 en inventant un **vocabulaire d'idle-honnête** qui *sonne* comme du travail. Parce qu'un nouveau synonyme est toujours inventable, l'autorité n'est **pas une liste de mots** mais un **test de résultat** :

   > **Test de fin de cycle.** Ai-je, ce cycle, sorti un grain de **substance** du pool global et l'ai-je transformé en PR (ou fait avancer un livrable multi-cycle) ? **Si non, et que `gh issue list` renvoie >0** — *quel que soit le label* que je m'apprête à poster — **c'est un échec de méthode, pas un cycle honnête.** Un scan forensic qui trouve 0 défaut, une `candidate-delivered`, une édition de body seule, un HOLD ou une attente mécanique ne sont **pas** des livrables : ce sont des préludes au pick suivant, jamais leur substitution.

   **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 ; 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.

8. **Veine plafonnée = interdit ciblé (HARD, #11343 tranche 2 ; amendé 2026-08-20).** Au-delà de `vein_cap=2` PRs citant la même umbrella dans la journée de la lane, la **PR suivante** DOIT appeler le picker avec la commande exposée par `variation_light_cap.py --check-pr N --genre-signals` (`picker_command` du JSON) pour **écarter l'umbrella saturée**. **Le plafond ne bloque PAS la tranche en cours** (« on ne jette pas du travail écrit », amendement ai-01 2026-08-16) : c'est la PR suivante qui est contrainte. Historique : [détail, section Veine plafonnée](../../docs/reference/proactive-coordination-detail.md).

9. **L'absence d'instruction n'est pas un etat (HARD, mandat user 2026-09-12).** Aucun geste du coordinateur — steer, review, capture, arbitrage, merge, reponse a un DM — n'est un **prealable** a produire. Une lane qui n'a rien recu **tire**, et c'est son regime normal, pas un mode degrade. Trois formulations disent la meme faute et sont bannies comme statut de cycle : « j'attends la re-review », « j'attends le merge », « j'attends la decision ». Ce qui attend, c'est la **candidate** ; la lane, elle, est deja sur le grain suivant.

   **Ce que le coordinateur ne peut pas te demander** : un message de sa part qui te suspend sans te dire quoi faire a la place est un **defaut de sa part, pas une consigne** — tu le signales en une ligne et tu tires. Lui non plus n'a pas le droit de l'ecrire ([coordinator-discipline.md](coordinator-discipline.md) R0).

## Leçons ancrées (checks pré-`[DONE]`)

- **L721 ★ — stale-tracker guard.** Avant tout claim « 0 PR / saturated / idle » : interroger le **tag de lane** (`Grain:`), **PAS** `--author <self>` (compte `jsboige` partagé, #13870) ; la requête se valide par ses **faux négatifs symétriques**. Repli : PR sans tag lisible → sweep `GRAIN-ORPHANS-SWEEP` (#13086). Forme canonique + mesures : [détail, section L721](../../docs/reference/proactive-coordination-detail.md).
- **L740 ★ — CronList 7-day verify.** Les crons `CronCreate` sont **session-only, expirent à 7 j** : avant `[DONE]`, vérifier `CronList` et **re-armer** — un cron expiré = lane-idle silencieuse.
- **L898 ★★★ — collision guard : avant d'ÉCRIRE, pas avant de pousser.** Avant `[CLAIMED]`/steer/édit/push : `git worktree list` + `gh pr list --search head:<branch>` + `--search "<mots-clés>"` + `--state open --json files` sur le **chemin** visé. Relire `main` ne remplace pas ce check (`main` ne montre pas l'*ouvert*). Coût ~10 s ; omission = travail dupliqué + rétractation publique.
- **L1356 ★★★ — preflight de claim : `--state all`, jamais open (incidents #13562/#13608).** Une PR **MERGÉE** ayant livré en rider (sans `Closes #N`) est invisible aux filtres open : avant tout `[CLAIMED]`, (1) `gh pr list --state all --search "<N>"`, (2) inspecter les **worktrees orphelins** nommés pour l'issue. Si livré : `[INFO] candidate-delivered` avec preuve — pas de réimplémentation, pas de close soi-même. Recette complète : [détail, section L1356](../../docs/reference/proactive-coordination-detail.md).

## Règle de sélection

Prendre **un item à la fois** (`[CLAIMED]` avant), livrer, **re-piocher aussitôt** — G.5 interdit N deep-tracks **parallèles**, pas N PRs **séquentielles**. **Anti-pattern interdit** : auditer la tranche étroite de sa famille puis poster un `[ASK coordinator]` alors que le pool offrait des grains cross-lane — c'est le silo (incident fondateur R5/R7, [détail §Incident fondateur R5/R7](../../docs/reference/proactive-coordination-detail.md)). **Le coordinateur n'est PAS un distributeur de grains** : il merge, scoper les issues, déconflitter les claims.

## Voir aussi

- [docs/reference/proactive-coordination-detail.md](../../docs/reference/proactive-coordination-detail.md) — backlog 8 sources, mapping, cadence, anti-patterns, incidents, leçons complètes, picker, veine
- [variation-protocol.md](variation-protocol.md) — **opérationnalise R6/R7** : tag `Grain:` + 3 gates + merge-gate + provisionnement
- [coordinator-discipline.md](coordinator-discipline.md) — ai-01 merge actif, no languishing
- [docs/reference/subagents-reference.md](../../docs/reference/subagents-reference.md) — roster spécialistes
