Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/rules/coordinator-discipline.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ La production des lanes et la digestion (CI, reviews, merges) sont **deux pipeli
- Un check rouge, un `DWELL`, une review en attente, un conflit ou un HOLD bloque **la candidate concernee**, jamais la lane. La lane traite ce qu'elle peut reparer, puis poursuit aussitot un nouveau grain **DEEP de contenu** pendant toute attente externe.
- `candidate-delivered`, forensic sans finding, body-only, attente mecanique, `HORS CAP` et backlog de review ne satisfont ni le plancher de production ni une fin de cycle.
- Quand le debit de digestion baisse, ai-01 maintient les deep queues et ouvre **en parallele** la piste de remise en capacite : diagnostic CI, sweep de merge supplementaire, ou correction de l'organe bloque. Il ne reduit pas les dispatchs pour rendre la queue confortable.
- ai-01 delegue agressivement la preparation verifiable : l'adjoint absorbe en file continue des lots oldest-first de preflights B.0/exact-head, relectures post-fix et recalculs ; Hermes et NanoClaw absorbent la premiere digestion specialisee. Chaque dossier nomme le head exact, les trois surfaces B.0, les gates, le delta depuis la derniere review et l'unique preuve decisive restante ; tout changement de head perime le dossier. **Objectif operatoire sur une fenetre de 4 h : >=20 dossiers READY oldest-first quand le plateau contient au moins 20 candidates eligibles**, remontes un par un sans attendre la fin du lot. Un dossier est un produit consommable par ai-01, pas un compte rendu d'activite. Ces avis preparent la decision sans remplacer la lecture B.0 personnelle finale, les controles qualite ni la signature de merge d'ai-01.
- ai-01 delegue agressivement la preparation verifiable : l'adjoint absorbe en file continue des lots oldest-first de preflights B.0/exact-head, relectures post-fix et recalculs ; Hermes et NanoClaw absorbent la premiere digestion specialisee. Chaque dossier nomme le head exact, les trois surfaces B.0, les gates, le delta depuis la derniere review et l'unique preuve decisive restante ; tout changement de head perime le dossier. **Objectif operatoire sur une fenetre de 4 h : >=20 dossiers READY oldest-first quand le plateau contient au moins 20 candidates eligibles**, remontes un par un sans attendre la fin du lot. Un dossier est un produit consommable par ai-01, pas un compte rendu d'activite. La file se derive par `check_adjoint_prevalidation.py --queue` (#16480), puis chaque tete se relit sans cache par `--consume` immediatement avant le geste exact-head, et toute mutation expulse la candidate au lieu d'ouvrir une investigation. **Ordre du cycle** (reponse user 2026-10-09, option c -- [PR #19980](https://github.com/jsboige/CoursIA/pull/19980#issuecomment-6076750742)) : (1) consommer d'abord, en quelques minutes et sans investigation, la file des PRs deja READY a la tete exacte (`--queue` puis `--consume`) ; (2) puis dispatcher (Phase 3 complete, lanes nourries) ; (3) puis le reste des merges et des lectures longues. Ces avis preparent la decision sans remplacer la lecture B.0 personnelle finale, les controles qualite ni la signature de merge d'ai-01.
- **Aucun de mes messages n'est un prealable (HARD, mandat user 2026-09-12).** Je n'ecris jamais une phrase dont l'effet est de suspendre une lane — « attends », « ne touche pas », « n'investigue pas avant que », « tiens ca jusqu'a » — sans nommer **dans la meme phrase** ce que la lane fait a la place. Une reserve, un HOLD ou un gate que je pose s'attache a la candidate et **me** revient a executer quand il exige une capacite que la lane n'a pas (#15463) ; il ne se delegue jamais en attente.
- **La profondeur de ma file de merge n'est jamais le champ de vision d'une lane.** Une flotte dont la production est garée chez moi finit par prendre la surveillance de ma file pour du travail : c'est **mon** échec de digestion, et il se répare par des merges, jamais en steerant les lanes vers leur propre file (mesure du 2026-09-12 : 71/76 PRs sans aucun geste de lane attendu — [§2.8](../../docs/reference/secrets-and-coord-detail.md)).
- **Deux nombres AVANT la premiere lecture de fichier du depot (HARD, mandat user 2026-09-12).** Au premier geste de chaque cycle, relever (1) les non-lus d'inbox (`deep:true` — sans lui, un `0` est indiscernable d'une inbox vide) et (2) les PRs en attente de mon merge. Tant que ces deux nombres ne sont pas releves, aucune lecture de diff, de workflow ou de script n'est legitime. L'auto-interrogation « suis-je en train de micromanager ? » ne suffit pas : une investigation qui avance se ressent toujours comme du travail, et c'est precisement ce qui la rend indetectable de l'interieur.
Expand Down
8 changes: 6 additions & 2 deletions .claude/skills/coordinate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ Les phases ci-dessous s'executent sous le budget defini par la section `## Budge

**Pourquoi enumerer, et pas nommer** : une skill qui sait d'avance quoi lire est **structurellement aveugle** a une cle qu'elle n'anticipe pas. Le 2026-09-21, `workspace-CoursIA (2)` — cle forkee par collision de noms Google Drive — portait **23 messages vivants** de po-2026 et po-2027, dont deux PRs debloquees en attente du merge-gate, pendant plusieurs jours sans qu'aucun cycle ne la voie. Une cle a suffixe ` (N)` dont le `workspace` declare **ne porte pas** ce suffixe est une moitie de la meme lane, pas une lane voisine : la lire, et escalader la reparation (`action:"merge"`, cf dashboard `global`).

2. **Inbox DM — drainer et EXTRAIRE, jamais survoler** : `roosync_messages(action:"inbox", status:"unread", deep:true)` — **sans `deep:true` le compte de non-lus est un faux zero**. Deux gestes, dans cet ordre. **(a) Purger les classes qui doublonnent une surface deja lue** — `bulk_mark_read(subject_contains:"Worker Report")` et `bulk_mark_read(subject_contains:"[MENTION] Dashboard")` : sans ca l'arriere se reconstruit a ~8 DM/h et noie le signal utile, qui pese moins de 10 % du volume. **(b) Extraire la liste nommee des PRs deja pre-machees** — marqueurs `[ADJOINT PREFLIGHT]`, `[ADJOINT VERIFIED]`, `[ADJOINT DECISION PACK]`, `preflight exact-head`. Cette liste est une **entree obligatoire de la Phase 3.3** : le pre-machage est produit qu'on le lise ou non ; non consomme, il est paye deux fois.
2. **Inbox DM — drainer et EXTRAIRE, jamais survoler** : `roosync_messages(action:"inbox", status:"unread", deep:true)` — **sans `deep:true` le compte de non-lus est un faux zero**. Deux gestes, dans cet ordre. **(a) Purger les classes qui doublonnent une surface deja lue** — `bulk_mark_read(subject_contains:"Worker Report")` et `bulk_mark_read(subject_contains:"[MENTION] Dashboard")` : sans ca l'arriere se reconstruit a ~8 DM/h et noie le signal utile, qui pese moins de 10 % du volume. **(b) Extraire la liste nommee des PRs deja pre-machees** — marqueurs `[ADJOINT PREFLIGHT]`, `[ADJOINT VERIFIED]`, `[ADJOINT DECISION PACK]`, `preflight exact-head`. Cette liste est une **entree obligatoire de la Phase 3.3** : le pre-machage est produit qu'on le lise ou non ; non consomme, il est paye deux fois. **(c) Deriver la file executable UNE fois (#16480)** : `python scripts/check_adjoint_prevalidation.py --queue <PR...>` sur la liste extraite en (b) — JSON oldest-first, sans etat persistant, verdict par entree (`READY` / `REVIEW_READY` / `DWELL_PENDING` / `BLOCKED` / `STALE`) avec cause nommee, `dwell_until` et `tail_to_read`. `REVIEW_READY` = toutes les portes premachees vertes, seule la disposition exact-head d'ai-01 manque : c'est du travail local de review, jamais un redispatch worker ni une attente de l'adjoint. `DWELL_PENDING` porte son echeance — un minuteur, pas un defaut. Re-deriver la file par un sweep manuel annule le travail de l'adjoint.
3. **GitHub** : `gh pr list --state open` (a merger) + le pool **tire, jamais scanne** — `python scripts/pick_idle_grain.py --belt --lane myia-ai-01:CoursIA` (un `gh issue list` nu plafonne a 30, tries par recence : il ne montre que ce que je viens de creer, et c'est ce biais que le steering doit eviter de reproduire).
4. **Cron** : `CronList` — si le job coordinateur a disparu (session-only), le re-armer a la cadence **courante**. Cette cadence est un **etat**, pas une regle : elle ne s'ecrit pas ici (decision user 2026-09-22 — l'etat courant se met a jour dans les memoires et les dashboards, jamais sous git). L'expression a re-armer se lit dans la memoire du coordinateur (`coordinator-handover`, section cron) et sur le dashboard. Ce qui reste durable : une minute off-`:00` (le jitter evite de frapper l'API a la meme seconde que le reste de la flotte), et une cadence unique, PAS de 2e cron ni ScheduleWakeup en plus — un cycle plus long que sa cadence annule deja ses propres declenchements, en empiler un second ne fait qu'ajouter de la conso.

### Phase 3 - Dispatchs, relances, memoire (LES 30 PREMIERES MINUTES)

**Cette phase precede la passe de merge et se ferme avant elle.** L'ordre inverse -- merger d'abord, dispatcher avec ce qui reste -- ne termine jamais : le pool de PRs est non borne et chaque PR ouvre trois surfaces a lire, donc les lanes sont affamees **par construction du cycle**, pas par negligence. Le symptome mesure : un cycle de plus de 4 h pour une cadence de 4 h, passe a rejouer le travail deja fait par l'adjoint et les bots (correction user 2026-09-14).
**Cette phase s'ouvre par la consommation de la file READY a la tete exacte (`--queue` puis `--consume`, quelques minutes, sans investigation), puis dispatche ; elle precede le reste des merges et des lectures longues** (reponse user 2026-10-09, option c -- [PR #19980](https://github.com/jsboige/CoursIA/pull/19980#issuecomment-6076750742)). L'ordre inverse -- merger tout d'abord, dispatcher avec ce qui reste -- ne termine jamais : le pool de PRs est non borne et chaque PR ouvre trois surfaces a lire, donc les lanes sont affamees **par construction du cycle**, pas par negligence. Le symptome mesure : un cycle de plus de 4 h pour une cadence de 4 h, passe a rejouer le travail deja fait par l'adjoint et les bots (correction user 2026-09-14).

1. **Sweep unique, trie par anciennete** -- il sert LES DEUX phases, on ne le capture qu'une fois : `gh pr list --state open --limit 200 --json number,title,author,createdAt,mergeStateStatus,headRefOid --jq 'sort_by(.createdAt) | .[] | [.createdAt[0:10],.number,.mergeStateStatus,.title] | @tsv'`. Sans `--limit`, gh plafonne a 30, et sans tri declare il rend par recence. **Pas de champ `reviews`** (payload lourd -- 504). 200 lignes rendues = plafond touche, paginer plutot que croire la liste complete. **Jamais `reviewDecision` comme colonne de triage** (#16926) : sous token COMMENT-only il vaut `null` a perpetuite sur ~82 % du pool, y compris sur les PRs portant un `VERDICT: LGTM` argumente -- le faux compte « 169 sans review » vient de la. Le verdict se lit avec `python scripts/ci/pool_review_verdicts.py --gradient` (GraphQL pagine borné, ~3 appels, deux surfaces reviews[]+commentaires, latest-wins) qui distingue SANS-REVIEW / VOIX-SANS-VERDICT / LGTM / CONCERNS et rend le gradient d'age des reserves.
2. **Grouper LA QUEUE par lane, et dispatcher le deblocage.** Les ~20 PRs les plus vieilles, regroupees par leur tag `Grain: ... lane`, partent en mandat de deblocage a leur lane. Une PR est vieille **parce qu'**elle est bloquee : ce qui se dispatche est le deblocage, pas le merge qui s'attend. Le lot d'une lane se derive du sweep seul -- aucun re-audit prealable n'est requis pour l'envoyer.
Expand Down Expand Up @@ -87,6 +87,8 @@ Deux circuits asynchrones tournent **pendant** que le cycle merge. Ce sont deux

### Phase 4 - Merge PAR LA QUEUE, sur dossiers premaches

**Ce qui precede** : la file READY a la tete exacte est deja consommee en ouverture de la Phase 3 (`--queue` puis `--consume`) ; cette phase traite le **reste** des merges et des lectures longues (reponse user 2026-10-09, option c -- [PR #19980](https://github.com/jsboige/CoursIA/pull/19980#issuecomment-6076750742)).

**Ordre unique : du plus ancien au plus recent.** Selectionner les PRs CLEAN / vertes / `rc=0` selectionne les PRs **neuves par construction** : une PR est verte parce qu'elle est recente, et vieille parce qu'elle est bloquee. Merger la tete **degrade en plus la queue** -- un merge rend DIRTY les PRs ouvertes qui touchent les memes fichiers (mesure : le merge de #15627 a sali #15799 et #15915). Mandat user 2026-09-14 : merger en batch par la queue, en mandatant le deblocage aux workers.

**Ce que le coordinateur NE refait PAS.** L'audit d'Hermes, de NanoClaw et de l'adjoint **est deja fait** : il se lit, il ne se rejoue pas. Ne sont verifies que (a) le **delta** depuis la derniere review -- les commits pousses apres, qui ne levent rien par eux-memes (B.0 : une phrase leve, pas un SHA) ; (b) les **reserves non levees** ; (c) la **preuve decisive** du claim central. Dix allers-retours sur une PR ne coutent rien tant qu'on ne les reverifie pas dix fois.
Expand All @@ -109,6 +111,8 @@ Deux circuits asynchrones tournent **pendant** que le cycle merge. Ce sont deux
**Consequence** : `jsboige` est aussi le login de toutes les lanes et du user. Le gate ne voit donc pas un commentaire `jsboige` poste apres le dossier, pas meme un nit user. C'est B.0 (`check_unaddressed_nits.py`) qui l'attrape : son `rc=0` reste exige avant tout merge, dossier READY ou pas.

Le bloc `[ADJOINT PREFLIGHT]` est genere par `python scripts/check_adjoint_prevalidation.py <PR> --template [--lane <machine:workspace>]`, puis complete par la lane emettrice -- **qui rend son PROPRE nom** : un dossier sous un nom d'emprunt defait le refus d'auto-attestation. Le compte des commentaires exclut le commentaire-dossier lui-meme. **Interdit de contourner le gate par un sous-agent, une lecture API directe ou un ancien dossier d'inbox.**

**Consommation exact-head (#16480)** : juste avant la lecture personnelle, relire la candidate par `python scripts/check_adjoint_prevalidation.py --consume <PR>` — relecture sans cache a l'instant-T, refus de toute mutation (commentaire poste apres le dossier, jambe en vol, plancher DWELL non ecoule, review qualifiante manquante ou reserve non levee). **Exit 0 seul** ouvre la lecture, et le `head` rendu par CE consume est le SHA du `--match-head-commit` du point 6. La file elle-meme se derive en debut de cycle par `--queue` (Phase 2, point 2c) : toute mutation expulse la candidate vers la Phase 3 au lieu d'ouvrir une investigation dans la rafale.
2. **Exploiter les verdicts deja poses** : `[Hermes] COMMENT_WITH_CONCERNS` (prefixe de `reviews[].body`), `EXEC_PROVED` / `STRUCTURAL_ONLY` / `SUSPECT_REGRESSION` (body). Tout finding NanoClaw suit [audit-reassessment.md](../../rules/audit-reassessment.md) avant fix (~60 % de FP).
3. **L'adjoint fabrique, ai-01 ne re-fabrique pas** : les PRs sans dossier valide partent en lots explicites issus du sweep vers l'adjoint, pas vers des sous-agents ai-01. Dossier attendu : contrat machine-lisible exact-head, trois surfaces B.0, checks latest-wins, scope, domaine et verdict ; un changement de head ou de surface le perime. L'adjoint vise **>=20 READY oldest-first par fenetre de 4 h quand >=20 candidates sont eligibles**, et remonte chaque READY immediatement : le lot n'est pas une barriere. Un dossier insuffisant repart avec UNE question precise. Le login GitHub `jsboige` etant partage, le champ `lane` est une declaration fail-closed, pas une preuve cryptographique d'identite. Ce que le gate exige est que la prevalidation soit **tierce**, pas qu'elle vienne d'une lane nommee : toute lane du cluster (`QUALIFYING_LANES`) peut emettre un dossier pour une PR **qu'elle ne porte pas**, et le gate refuse l'auto-attestation en comparant la lane du dossier au tag `Grain:` du body (#16906). Une lane hors de l'ensemble, ou malformee, echoue toujours ferme.
4. **Lecture B.0 personnelle minimale avant chaque merge -- non delegable, seulement APRES gate vert** : body + comments + reviews + diff ("Read Body Before Any Action") ; etat A L'INSTANT-T via `gh pr view N --json state,mergedAt,mergeStateStatus,reviews` (jamais depuis le dashboard ni le cycle N-1) ; organe `python scripts/check_unaddressed_nits.py <PR>` (exit 1 = ne pas merger ; son vert ne dispense pas de la lecture). Verifier seulement le dossier, le delta et la preuve decisive ; ne pas rejouer l'audit complet. Une levee porte un auteur et une heure.
Expand Down
Loading
Loading