Skip to content
Merged
57 changes: 57 additions & 0 deletions .claude/rules/coordinator-discipline.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,63 @@ S'applique au **coordinateur ai-01** (`myia-ai-01:CoursIA`), **chef de flotte**

Detail complet (workflow batch merge + commandes + audit pre-merge + incidents + verbatims + mapping lanes + listes de rollout + 4-mecanismes de chaque regle) : [docs/secrets-and-coord-detail.md §2](../../docs/reference/secrets-and-coord-detail.md#2-coordinator-discipline-ai-01).

## Garde d'identite — mesurer sa lane AVANT d'armer une cadence (HARD)

Mandat user 2026-09-11. Cette section est **nommee, pas numerotee** : R1-R6 sont
referencees par [lane-claim-protocol.md](lane-claim-protocol.md),
[proactive-coordination.md](proactive-coordination.md),
[variation-protocol.md](variation-protocol.md) et
[submodule-maintenance.md](submodule-maintenance.md) — les renumeroter casserait
ces renvois.

**Avant tout `CronCreate` et avant tout merge**, une session qui s'apprete a
coordonner mesure son identite. Trois mesures, dont la troisieme n'est pas
automatisable :

| # | Ce qui se mesure | Comment |
|---|---|---|
| 1 | **machine** | hostname normalise (`COMPUTERNAME` prime sur Windows) |
| 2 | **workspace** | basename du **clone** (worktree principal), pas du worktree courant |
| 3 | **unicite de session** | `ListAgents` **puis** un aller-retour `SendMessage` par pair `coursia-*` |

Les deux premieres sont portees par l'organe — `exit 1` = ne pas armer :

```bash
python scripts/check_coordinator_identity.py --expect coordinator
```

**La troisieme ne l'est pas, et l'organe l'ecrit dans chacun de ses verdicts**
(`uniqueness_measured: false`). Un `exit 0` dit « la lane est la bonne »,
**jamais** « il est sur d'armer `/coordinate` » : les noms de session
(`coursia-0f`) **n'encodent pas la lane**, seule une reponse du pair la qualifie.
Deux sessions lancees du **meme** clone rendent d'ailleurs toutes deux `exit 0` et
le role COORDINATOR — `clone_ok` ne separe que des clones *distincts*. L'organe
porte la racine canonique et **retrograde en worker** (fail-CLOSED) une session
lancee depuis un clone jumeau, mais il ne tranche jamais l'unicite : c'est
l'aller-retour de la mesure 3 qui le fait.

### Table de decision

| Lane mesuree | Cadence a armer |
|---|---|
| `myia-ai-01:CoursIA`, **et** unique | `/coordinate` |
| `myia-ai-01:CoursIA`, **une autre session active sur la meme lane** | arbitrer : **une seule** garde `/coordinate`. A defaut d'accord, elle revient a **celle qui detient deja un cron `/coordinate` arme** ; si aucune ne l'a ou si les deux l'ont, a **la session demarree le plus tot** (`ListAgents` horodate les demarrages). L'autre cede **et passe en `git worktree add`** — deux sessions d'un meme clone partagent HEAD, l'index et le stash, et aucune garde ne rougit sur cette corruption-la ([[concurrent-sessions-share-the-working-tree]]) |
| `myia-po-2025:CoursIA-2` | `/coordinate-adjoint` |
| toute autre lane | `/continue` (worker) |

Les deux criteres de defaut sont choisis pour etre **lisibles des deux cotes** :
chaque session peut rendre son `CronList` et son heure de demarrage.

**`CronList` est session-locale** ([[session-local-view-read-as-global]]) : une
liste vide ne prouve rien au-dela de la session courante — ni pour un autre
workspace de la meme machine. Enchainer avec
[[handover-must-disarm-outgoing-cron]] : **desarmer la cadence sortante d'abord**,
armer ensuite.

Incident fondateur (2026-09-11), justification des deux criteres de defaut, mesure
`coursia-1c`/`coursia-0f` et les deux pieges que cette garde ferme :
[§2.6](../../docs/reference/secrets-and-coord-detail.md#26-garde-didentite-de-lane--recit-arbitrage-et-pieges-2026-09-11).

## Regle 0 : production avant digestion, sans perte de qualite (HARD)

La production des lanes et la digestion (CI, reviews, merges) sont **deux pipelines paralleles**. Une saturation du second est un symptome a reparer ou a capaciter ; elle ne devient jamais une politique de ralentissement du premier.
Expand Down
56 changes: 56 additions & 0 deletions docs/reference/secrets-and-coord-detail.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,62 @@ Les 4 mecanismes correctifs (chaque cycle, par lane) :

**Tell d'auto-detection** (avant de poster un steer) : « (a) ce grain est-il verifie OPEN/non-sature firsthand a l'instant ? (b) la decision atteint-elle l'inbox du worker ? (c) ai-je tranche, ou defere ? ». Trois oui requis. Sinon = phantom, le worker idlera.

### 2.6 Garde d'identite de lane — recit, arbitrage et pieges (2026-09-11)

> Deporte de [`.claude/rules/coordinator-discipline.md`](../../.claude/rules/coordinator-discipline.md)
> le 2026-09-13 (#15204, slimming du harnais auto-charge). **La garde n'a PAS ete deportee** : la table
> des trois mesures, l'invocation de l'organe, l'avertissement sur `exit 0` et la table de decision
> restent dans la rule, ou elles sont operatoires. Ce qui suit est le recit de l'incident et la
> justification des defauts d'arbitrage — le contexte qui explique *pourquoi* la garde a cette forme,
> pas la garde elle-meme. L'organe reel est `scripts/check_coordinator_identity.py` et ses tests : ils
> portent les deux premieres mesures et **ne coutent rien au contexte**.

**Incident fondateur (2026-09-11).** Un reboot machine a tue la session coordinateur et son cron
(`CronCreate` est session-only, L740). Deux sessions CoursIA se sont retrouvees vivantes sur
`myia-ai-01` sans qu'aucun signal ne dise laquelle devait coordonner — `ListAgents` listait des **noms
de session**, pas des lanes. L'arbitrage s'est regle **par accord** au premier aller-retour : le defaut
deterministe n'a pas eu a jouer, et il ne faut pas lire cet episode comme son precedent. Ce qui a
departage est ce que la table de decision nomme desormais — une session portait le cron arme, l'autre
avait un `CronList` vide.

**Pourquoi les deux criteres de defaut sont asymetriques.** « Celle qui detient deja un cron arme »,
puis « la session demarree le plus tot » : chaque session peut rendre son `CronList` et son heure de
demarrage, donc les deux criteres sont **lisibles des deux cotes**. « Celle qui a detecte la collision
cede » ne l'est pas — une detection **simultanee** ferait ceder les deux et ne laisserait **aucun**
coordinateur, precisement ce que le defaut existe pour empecher.

**La session qui cede change d'arbre, pas seulement de cadence.** Deux sessions sur la meme lane **et
le meme clone** partagent HEAD, l'index et le stash. Le double-cron n'est qu'un probleme de cadence ;
l'arbre de travail partage est un probleme de **corruption silencieuse** — un `checkout` / `rebase` /
`stash` d'un cote pendant une lecture de l'autre ne fait rougir aucune garde
([[concurrent-sessions-share-the-working-tree]]). La session qui cede passe donc en
`git worktree add`, et pas seulement sous `/continue`.

**Ce que la garde ne discrimine pas — mesure du 2026-09-11.** `clone_ok` ne separe que des clones
*distincts*. Deux sessions lancees depuis `D:/CoursIA` rendent toutes deux `exit 0` et le role
COORDINATOR : mesure faite entre `coursia-1c` et `coursia-0f`. Ce qui tranche est **l'aller-retour de
la mesure 3**, jamais le code de sortie de l'organe — lequel le dit de lui-meme en rendant
`uniqueness_measured: false`. Il documente cet angle mort, il ne le resout pas.

**Piege 1 — deux clones partagent une lane.** Sur ai-01, `D:/CoursIA` et `D:/dev/CoursIA` rendent tous
deux `myia-ai-01:CoursIA` : la chaine de lane ne les discrimine pas, seul le chemin le fait. L'organe
porte la racine canonique et **retrograde en worker** (fail-CLOSED) une session lancee depuis le
jumeau.

**Piege 2 — `CronList` est session-locale** ([[session-local-view-read-as-global]]). Une liste vide ne
prouve rien au-dela de la session courante — surtout pas qu'aucun cron de coordination ne tourne
ailleurs sur la machine. **La recurrence est mesuree, et elle n'a pas la forme de la premiere** : le
2026-09-13, deux cadences de coordination tournaient simultanement sur `myia-ai-01`, aveugles l'une a
l'autre — un cycle 4 h arme depuis la lane `CoursIA`, un cycle 6 h arme depuis la lane
`roo-extensions`. La collision n'opposait donc pas deux sessions d'un meme workspace, mais **deux
workspaces d'une meme machine** : la chaine `machine:workspace` les distingue correctement, et
c'est la **vue** qui manquait, pas la mesure. Resolu par une cadence unique. L'ironie vaut d'etre
inscrite : #15648 est la PR qui construit la garde contre exactement cela, et l'incident s'est
reproduit sur son auteur pendant qu'elle attendait d'etre mergee.

Enchainer avec [[handover-must-disarm-outgoing-cron]] : **desarmer la cadence sortante d'abord**,
armer ensuite.




Expand Down
Loading
Loading