Flotte : déléguer la codebase à plusieurs agents
Modèle opératoire pour faire travailler N agents Claude Code en parallèle sur BoostEcom, en continu, sans collision ni dérive. Écrit pour un opérateur solo qui passe à l'échelle : tu passes de « je code » à « je pilote une équipe ».
Modele operatoire pour faire travailler N agents Claude Code en parallele sur BoostEcom, en continu, sans collision ni derive. Ecrit pour un operateur solo qui passe a l'echelle : tu passes de « je code » a « je pilote une equipe ».
| Fichier | Repond a |
|---|---|
README.md (ce fichier) | Comment le systeme fonctionne, et comment on demarre |
roster.md | Qui fait quoi, avec quels outils, jusqu'ou |
protocol.md | Les regles de circulation (branches, PR, conflits, escalade) |
../../.claude/fleet/ownership.json | La frontiere, en version machine |
../../backlog/README.md | Comment le travail entre, se prend, et sort |
Le principe
Une equipe humaine tient parce que les gens se parlent. Une flotte d'agents ne se parle pas : chaque session demarre amnesique, ne voit pas les autres, et ne saura jamais qu'un collegue edite le meme fichier a la meme seconde. Donc on n'essaie pas de les faire communiquer. On rend la collision structurellement impossible, et on ecrit la coordination dans le repo.
Quatre lois. Tout le reste en decoule.
1. Une question, une source de verite.
| Question | Fichier |
|---|---|
| Qu'est-ce qu'on construit, et pourquoi | CLAUDE.md |
| Comment on ecrit du code ici | AGENTS.md |
| Qui fait quoi | roster.md |
| Ou s'arrete mon perimetre | ownership.json |
| Qu'est-ce qui reste a faire | backlog/ |
| Ou on en est, maintenant | pnpm fleet:status |
| Lancer un item en un clic, suivre la session et sa PR | /ops/dev (sections Backlog et Runs) (docs/architecture/dev-console.md) |
| Comment le systeme marche aujourd'hui | docs/architecture/ |
| Pourquoi ce choix a ete fait | docs/decisions/ |
| Ce qui ne va pas, a une date donnee | docs/audits/ |
| Ce que les chiffres disent | docs/insights/ |
Un agent qui hesite ne demande pas : il lit. Si la reponse n'existe nulle part, c'est un bug de doc, et le fix c'est d'ecrire la reponse, pas de deviner.
Corollaire : le contexte descend jusqu'au repertoire. Un CLAUDE.md local
(src/components/CLAUDE.md, src/components/patterns/CLAUDE.md,
messages/CLAUDE.md, src/features/tracking/CLAUDE.md…) se charge
automatiquement quand un agent travaille dedans. C'est la ou vivent les
conventions d'un pilier : elles arrivent a l'agent sans qu'il ait a les
chercher, et chaque pilier ecrit dans son propre fichier, donc deux piliers ne
peuvent pas entrer en conflit sur leurs conventions. Quand un pilier se donne
une regle, elle va la, pas dans un fichier racine que tout le monde edite.
2. La frontiere est un chemin de fichier, pas une intention.
"Reste dans ton domaine" est une consigne qu'un agent viole de bonne foi.
src/modules/billing/** appartient a billing-engineer est une regle qu'un
script verifie. Toute la carte vit dans
ownership.json, et
pnpm fleet:scope la fait respecter en CI. Un agent ne "fait pas attention" :
il est borne.
3. Le verrou, c'est le nom de la branche.
Le reflexe naturel (un todo.md ou chacun coche sa ligne) est exactement ce
qui produit les conflits : tout le monde ecrit dans le meme fichier. Ici, un
item est pris quand une branche fleet/<pillar>/<id>-<slug> existe sur
origin. Verifiable en une commande, atomique, zero fichier partage.
4. Chaque PR nourrit l'ecosysteme.
Une PR qui livre du code sans mettre a jour son item de backlog et la doc du pilier qu'elle change n'est pas finie. C'est ce qui fait que le systeme s'enrichit tout seul au lieu de pourrir : la doc n'est pas une corvee de fin de trimestre, c'est une condition de merge.
L'organigramme
TOI (owner / arbitre)
│
@conductor ← triage, priorites, deblocages,
│ serialisation des hot files
┌────────────────────┼────────────────────┐
│ │ │
PILIERS (1 owner) TRANSVERSES (lecture) TOI (decision)
│ │
data-platform qa-engineer merge des PR
security-identity data-analyst arbitrage produit
billing codebase-auditor budget / priorites
ai-platform
integrations
commerce-systems
intelligence
marketplace
design-system
growth-web
platform-ops
app-shell
Un pilier = un perimetre exclusif en ecriture. Un transverse = droit de lire partout, droit d'ecrire nulle part sauf ses propres surfaces (tests, rapports, backlog). Personne ne merge sa propre PR : c'est toi, ou une regle d'auto-merge que tu decides.
Le detail par role (mission, chemins, outils autorises, definition de termine,
regle d'escalade) est dans roster.md. La definition executable
est dans .claude/agents/<nom>.md : chaque agent y a son prompt systeme et sa
liste d'outils. Pour les transverses, la restriction est aussi mecanique : une
PR qui declare Agent: data-analyst et touche du code dans src/ est refusee
par pnpm fleet:scope. L'observateur qui corrige devient juge et partie, donc
il ne peut pas.
La boucle
Ce qui tourne en continu, indefiniment.
BACKLOG ──claim──> BRANCHE ──travail──> PR draft ──STOP──> MERGE (owner)
▲ │
└──────── nouvelle branche depuis origin/main ─────────┘
- Entree. Un item arrive dans
backlog/inbox/: pousse par toi, par un audit decodebase-auditor, par un rapport dedata-analyst, par un incident, ou par un agent qui a trouve un probleme hors de son perimetre. - Triage.
@conductorclasse : pilier, priorite, taille, dependances. L'item passe dansbacklog/<pillar>/enstatus: ready. - Claim. L'agent du pilier prend l'item du haut de sa pile, cree
fleet/<pillar>/<id>-<slug>depuisorigin/main. La branche est le verrou. - Travail. Une PR = un item = un pilier. Push local = hook pre-push
(gardes offline rapides). Avant merge, CI :
pnpm lint,pnpm typecheck,pnpm test(+pnpm fleet:scopevia fleet-guard). - PR draft puis stop. L'agent ouvre la PR en draft et s'arrete. Review + merge = owner. Pas d'empilement sur la meme branche.
- Merge. Toi. L'item passe
done, la doc du pilier est a jour dans la meme PR,auto-changelogpublie viaRoadmapItem.commitSha. Apres merge : nouvelle branche depuisorigin/mainpour l'item suivant. Ne jamais reutiliser une branche mergee (la supprimer si elle reste sur le remote). - Retour. Ce que le travail a revele (dette, question ouverte, suite)
repart en
backlog/inbox/. La boucle se referme.
Cadence recommandee
| Rythme | Qui | Quoi |
|---|---|---|
| Continu | piliers | claim -> PR -> merge |
| Quotidien | @conductor | pnpm fleet:status, triage de inbox/, branches orphelines |
| Hebdo | codebase-auditor | un audit sur un pilier en rotation -> docs/audits/ |
| Hebdo | data-analyst | KPI + usage -> docs/insights/ -> items backlog |
| Hebdo | qa-engineer | couverture des chemins critiques (billing, auth, credits) |
| Hebdo | CI | fleet-status tourne lundi 08:00 UTC, rapport dans le job summary |
| Mensuel | toi | relecture de roster.md et ownership.json : la carte suit le code |
Le suivi
Savoir en permanence ou on en est, sans qu'aucun humain ne tienne un tableau a jour. Rien n'est ecrit a la main : une page de statut maintenue manuellement est perimee le lendemain, et une flotte qui lit un statut perime prend des decisions perimees.
pnpm fleet:status # lisible
pnpm fleet:status --markdown # pour un job summary CI
pnpm fleet:status --json # pour tout le reste
Tout est derive du reel au moment ou tu lances la commande : les fichiers de
backlog/, les refs fleet/* sur origin et la date de leur dernier commit,
les rapports dates de docs/audits/ et docs/insights/, la liste des piliers
dans ownership.json.
Le rapport donne trois choses :
| Bloc | Repond a |
|---|---|
| Totaux | combien en attente, en cours, bloque, livre |
| Piliers | charge par pilier, qui est en vol, date du dernier audit |
| Signaux | ce qui pourrit en silence |
Les signaux sont la vraie valeur. Les compteurs, tu les devinerais ; les signaux, non :
| Signal | Ce qu'il detecte |
|---|---|
untriaged | un item dort dans inbox/ depuis plus de 7 jours |
stalled | un P0 pret depuis 7j, ou un P1 depuis 14j, que personne n'a pris |
dead-lock | une branche fleet/* tient un verrou sans commit depuis 72h |
audit-overdue | un pilier n'a pas ete audite depuis 90 jours |
insight-stale | aucun rapport analyste depuis 14 jours : on construit sans mesurer |
phantom-blocker | un item bloque par un item qui n'existe plus |
no-triage | des items en entree et rien de triable : la flotte n'a rien a tirer |
Le rapport ne bloque jamais rien (exit 0 systematique). C'est fleet:scope qui
bloque. Un rapport qui fait echouer un build devient un rapport qu'on desactive.
Le "pourquoi" se suit ailleurs. Un statut dit ou on en est ; il ne dit pas
pourquoi le code est comme ca. C'est le role de docs/decisions/ :
un ADR par choix structurel, jamais reecrit, remplace et non modifie. C'est ce
qui empeche un agent amnesique de "corriger" dans six mois un compromis
deliberement pris aujourd'hui. La section Alternatives ecartees d'un ADR vaut
souvent plus que la decision elle-meme : elle evite de re-etudier tous les
trimestres une option deja tranchee.
Demarrage
Une fois.
- Lis
roster.mdet coupe les piliers que tu n'exploites pas encore. Douze agents actifs sur un repo qui n'a pas douze chantiers, c'est douze PR a relire pour rien. Commence a trois ou quatre. - Verifie que
ownership.jsoncouvre bien ton code :pnpm fleet:scopeliste les cheminsunowned(visible sur n'importe quelle branche non triviale). - Remplis
backlog/inbox/avec ce que tu as en tete aujourd'hui. Le systeme ne demarre pas a vide.
A chaque session d'agent. Le prompt d'ouverture tient en deux lignes :
Tu es @billing-engineer. Lis .claude/agents/billing-engineer.md, docs/team/protocol.md,
puis prends l'item prioritaire de backlog/billing/. Respecte le protocole de bout en bout.
Le reste (perimetre, outils, definition de termine, escalade) est deja ecrit. C'est le point : tu ne re-expliques jamais le contexte, tu pointes un fichier.
Quand tu veux l'orchestrer toi-meme. @conductor sait deja dispatcher :
Tu es @conductor. Fais le triage de backlog/inbox/, dis-moi ce qui devrait tourner
cette semaine et sur quel pilier, et signale les hot files en conflit.
Les modes de panne, et ce qui les bloque
| Panne | Ce qui l'empeche |
|---|---|
| Deux agents editent le meme fichier | Perimetres exclusifs + pnpm fleet:scope en CI |
| Deux agents prennent le meme item | Le verrou = la branche fleet/... sur origin |
Conflit sur schema.prisma / package.json | Hot files serialises par @conductor, trailer Hot-file: obligatoire |
| Deux implementations du meme pattern | Loi 1 : on grep et on lit docs/architecture/ avant d'inventer (regle deja dans AGENTS.md) |
| PR fourre-tout impossible a relire | Une PR = un item = un pilier, gate CI |
| La doc decroche du code | La doc du pilier se met a jour dans la meme PR, sinon pas de merge |
| Un agent part en roue libre | Trailer Agent: verifie par fleet:scope : writeRestrictedAgents dans .claude/fleet/ownership.json borne les fichiers qu'un transverse peut changer. La liste d'outils des seize fiches .claude/agents/ est IDENTIQUE, Edit/Write compris : cette ligne a annonce une « toolbox restreinte » qui n'a jamais existe, la restriction est mecanique cote PR |
| Une branche abandonnee bloque un item | Revue quotidienne des branches par @conductor : une branche sans commit depuis 72h est liberee |
| Le systeme devient obsolete | Revue mensuelle de ownership.json ; les chemins unowned sont rapportes a chaque run |
Ce que le systeme ne fait pas
Il ne remplace pas ton arbitrage. Les agents produisent des PR correctes et scopees ; quoi construire, dans quel ordre, et a quel prix, reste ta decision. Le conductor propose, il ne tranche pas.
Il ne garantit pas la qualite d'une PR isolee : c'est le role des gates CI et de la relecture. Il garantit que dix PR simultanees ne se marchent pas dessus.
Et il ne survit pas a une carte perimee. Si tu ajoutes un module et que
personne ne le declare dans ownership.json, il devient une zone grise ou les
collisions reviennent. pnpm fleet:scope te le dit a chaque PR : ecoute-le.