FlotteProtocole — les regles de circulation

Protocole : les règles de circulation

Ce que chaque agent applique, à chaque item, sans exception. Court par conception : un protocole qu'on ne relit pas est un protocole qu'on ne suit pas.

Ce que chaque agent applique, a chaque item, sans exception. Court par conception : un protocole qu'on ne relit pas est un protocole qu'on ne suit pas.


1. Cycle de vie d'un item

inbox/  ──triage──>  <pillar>/ ready  ──claim──>  branche  ──PR──>  merge  ──>  _archive/

Avant de commencer, dans cet ordre :

git fetch origin main
git ls-remote --heads origin "fleet/*"     # qui travaille sur quoi, maintenant

Si une branche fleet/<pillar>/<id>-* existe deja, l'item est pris. Prends le suivant. La branche est le verrou : elle est atomique, visible par tout le monde, et ne demande d'editer aucun fichier partage.

Claim :

git checkout -b fleet/<pillar>/<id>-<slug> origin/main
git commit --allow-empty -m "chore(<pillar>): claim <id>"
git push -u origin fleet/<pillar>/<id>-<slug>

Le commit vide pousse immediatement rend le verrou visible avant que le travail commence. C'est la difference entre "je vais prendre cet item" et "cet item est pris".

Le worktree est partage par plusieurs sessions. Une session ne fait jamais git checkout d'une branche qu'une autre session a en cours : ca emporte ou ecrase son travail non commite. Chaque session part d'une branche neuve depuis origin/main, au nom unique, et n'edite que celle-la. Un stash d'une autre session ne se drop pas et ne se pop pas.

Liberation. Une branche sans nouveau commit depuis 72h est consideree abandonnee. @conductor la supprime et repasse l'item en ready.


2. Une PR = un item = un pilier

Exception contrôlée : chantiers continus Dev Studio

La règle ci-dessous reste le défaut pour les agents de flotte autonomes et les demandes indépendantes. Pour un chantier explicitement piloté par le propriétaire depuis Dev Studio, appliquer .claude/skills/dev-studio-ship/SKILL.md : examiner les doublons dans le code, le backlog (y compris archives), les PR et les branches ; regrouper les items réellement interdépendants dans une PR de chantier avec plusieurs commits lisibles, sans déploiement pour chaque commit. Réutiliser la PR déjà ouverte du même chantier avec accord du propriétaire, pas celle d'un autre agent. Les critères d'acceptation, les preuves et les IDs d'items doivent apparaître dans la PR.

pnpm fleet:scope, les verrouillages, les hot files et la séparation des piliers restent en vigueur. Si la PR traverse un pilier nécessairement, documenter Cross-pillar: avec la raison et les relectures concernées. Ne jamais regrouper deux changements sans rapport pour réduire le nombre de PR. Ne pas créer d'exception silencieuse à un gate.

C'est la regle qui rend dix agents simultanes relisables.

pnpm fleet:scope verifie mecaniquement, en local et en CI, que les fichiers touches appartiennent a un seul pilier. Lance-le avant de pousser.

Traverser un pilier est autorise quand c'est indissociable (une migration plus son usage, un endpoint plus son ecran). Il faut alors :

Cross-pillar: le grant de credits et la route webhook doivent changer ensemble, sinon la fenetre d'idempotence casse

dans le corps de la PR, et une relecture par les deux piliers. Une PR cross-pilier sans raison ecrite est refusee par la CI, pas par un humain.

Ce qui n'est jamais une raison valable : "c'etait sur mon chemin", "petit fix au passage", "tant qu'a faire". Ces changements deviennent un item dans backlog/inbox/. Le cout d'ouvrir un item est de trente secondes ; le cout d'une PR fourre-tout est une relecture impossible et un conflit garanti.


3. Hot files (fichiers chauds)

Certains fichiers sont touches par tout le monde. On les serialise au lieu d'esperer.

prisma/schema.prisma, package.json, pnpm-lock.yaml, vercel.json, next.config.mjs, tsconfig.json, eslint.config.mjs, components.json, CLAUDE.md, AGENTS.md, .env.example, src/env/**, src/config/**, src/types/**, src/app/layout.tsx, src/app/providers.tsx, src/instrumentation*.ts, .claude/fleet/**, .claude/agents/**, docs/team/**.

La liste vivante est dans ownership.json (hotFiles).

Regle. Avant de toucher un hot file : verifier qu'aucune PR ouverte ne le touche deja. Puis declarer dans le corps de la PR :

Hot-file: prisma/schema.prisma — ajout de Credit.expiresAt (workstream 1)

Priorite en cas de collision. Le premier a avoir pousse garde la main. Le second attend le merge et rebase. On ne resout pas un conflit sur schema.prisma a deux : on sequence.


4. Gates : push local et PR

Un push par unite logique. Commit local != push distant != deploiement Vercel. Chaque push d'une branche fleet/* ou claude/* etait un Preview complet ; le build saute desormais ces previews sauf [deploy preview] dans le message du commit (scripts/lib/build-policy.mjs). Regroupe les micro-commits (lien de backlog, stamp de PR, correctif de garde) dans le push de l'unite, et ne pousse pas apres chaque edition. Detail et chiffres : docs/ops/finops-2026-10-03.md.

Push local = hook pre-push (gardes offline rapides uniquement). Voir AGENTS.md : la liste est derivee de package.json.

PR / CI (bloquant au merge) :

pnpm fleet:scope        # aussi dans fleet-guard.yml
pnpm lint               # eslint --max-warnings 0
pnpm typecheck          # tsc --noEmit
pnpm test               # vitest run
pnpm db:guard:check     # si le schema a bouge

pnpm build est lourd : reserve aux PR qui touchent le graphe de routes, les frontieres serveur/client, ou la config de build. Le hook pre-push local lance 21 commandes, pas trois : la liste exacte vit dans package.json (simple-git-hooks.pre-push) et est reprise, derivee par pnpm docs:claims, dans AGENTS.md. Ce sont les gardes offline rapides. lint, typecheck et test ne sont PAS dans le hook : ils tournent en CI sur chaque PR (platform-ops/2696).

En CI : typecheck, lint, test (avec Postgres), build, audit-memory-scope, fleet-guard. Une PR rouge ne se merge pas, et ne se merge pas non plus « parce que c'est un test instable » (flake) sans que le test instable devienne un item.


5. Format de PR

Titre : conventional commit, en anglais, scope = pilier.

feat(billing): grant rollover credits with a 65-day expiry

Corps :

Backlog: backlog/billing/0042-credit-rollover.md
Roadmap-Item: <id>            # si un RoadmapItem existe (alimente auto-changelog)
Cross-pillar: <raison>        # seulement si plusieurs piliers
Hot-file: <fichier> — <raison> # seulement si hot file

## Ce que ca change
## Comment c'est verifie
## Risque et rollback

La PR est ouverte en draft. Un agent ne merge pas, n'approuve pas, ne ferme pas d'issue, ne publie pas de release. Ces gestes appartiennent au proprietaire.

Arret apres la PR draft (boucle obligatoire)

Pour un chantier Dev Studio expressément itératif, l'agent peut continuer à pousser des commits sur la même PR du chantier tant que le propriétaire a demandé de garder cette PR ouverte pour ses demandes successives. Il n'en crée pas une nouvelle pour chaque backlog lié. Avant de transmettre au propriétaire, l'agent doit relire le diff total de la PR, corriger les défauts, exécuter et tracer les contrôles disponibles, puis signaler soit « à corriger », soit « en attente de validation technique », soit « prête pour review humaine ». La review de l'agent n'est ni une approbation GitHub, ni un substitut à la review humaine. Merge uniquement par le propriétaire ou son équipe. Voir .claude/skills/dev-studio-ship/SKILL.md.

claim → branche → commits → push → PR draft → STOP
                                      ↑
              owner review + merge (manuel)
                                      ↓
              origin/main a jour → claim item suivant → NOUVELLE branche

Apres gh pr create --draft (ou equivalent), l'agent s'arrete sur cet item. Pas de commit supplementaire sur la meme branche "en attendant", pas de deuxieme fonctionnalite greffee, pas de rebase opportuniste. La suite attend le merge (ou une instruction explicite du proprietaire pour amender cette PR).

Apres le merge, toujours :

git fetch origin main
git checkout -b fleet/<pillar>/<next-id>-<slug> origin/main

Jamais git checkout de l'ancienne branche mergee pour "continuer". Une branche mergee n'est plus un verrou de travail : c'est de l'histoire.

Suppression de branche. Le depot a delete_branch_on_merge: true. Si la branche fleet/... est encore sur origin apres merge, la supprimer (git push origin --delete <branch>) plutot que d'y pousser de nouveaux commits (ce qui la recree et casse le verrou). Une branche recreee apres merge n'est pas un claim : c'est une fuite.


6. Nourrir l'ecosysteme

Une PR n'est finie que quand elle a rendu le systeme plus lisible qu'avant. Dans la meme PR :

SituationCe qui doit bouger
Toujoursl'item de backlog passe done et va dans backlog/_archive/<pillar>/
Le comportement changela doc du pilier dans docs/architecture/ ou docs/ops/
Une convention nouvelle interne au pilierle CLAUDE.md local du repertoire concerne (auto-charge par les agents qui y travaillent)
Un choix structurel, un compromis assume, une contrainte externeun ADR dans docs/decisions/
Une approche essayee et abandonneeun ADR rejected : ca evite qu'on la re-propose dans six mois
Une nouvelle route / table / cronCLAUDE.md (hot file : trailer requis, regroupe si possible)
Un travail revele une suiteun nouvel item dans backlog/inbox/

Un ADR n'est pas un formalisme par PR. Il s'ecrit quand la reponse a "pourquoi pas plus simple ?" n'est pas evidente en lisant le code. Il n'est jamais modifie apres acceptation : il est remplace par un nouveau qui le reference.

Regle anti-conflit sur la doc : on n'ajoute jamais a la fin d'un fichier partage. Un rapport, un audit, un insight = un fichier date qui lui est propre (docs/audits/2026-08-25-billing.md). Deux agents qui ajoutent une section au meme fichier le meme jour, c'est un conflit ; deux fichiers, c'est zero.


7. Escalade

Un agent ne sort jamais de son perimetre de sa propre initiative. Quand il a besoin d'autre chose :

SituationGeste
Besoin d'un changement dans un autre pilierouvrir un item dans backlog/<pillar-cible>/, referencer sa PR, continuer sur ce qu'il peut faire
Bloque net par cette dependancepousser ce qui est fait, marquer la PR blocked-by: <item>, prendre l'item suivant
Faille de securite@security-engineer en item prioritaire, immediatement, sans attendre le triage
Contradiction entre deux docscorriger la source de verite si c'est son pilier, sinon item vers @conductor
Ambiguite produititem vers @conductor avec les deux lectures possibles et une recommandation
Le code contredit la doccroire le runtime, verifier le plan de controle, puis corriger la doc (regle AGENTS.md)

Attendre n'est jamais la bonne reponse. Il y a toujours un item suivant.


8. Limites dures

Ce qu'aucun agent ne fait, quel que soit le pilier :

  • pnpm db:reset, prisma migrate reset, db:push --force-reset sur une base partagee
  • push sur main, force-push sur la branche d'un autre
  • merge, approbation, fermeture d'issue, publication de release
  • ecriture sur le theme Shopify MAIN sans { allowLive: true }
  • ajout d'une dependance sans item de backlog et sans trailer Hot-file: sur package.json
  • commit d'un secret, d'un .env*, ou d'une donnee personnelle
  • desactivation d'un gate CI pour faire passer sa propre PR

9. Prompt d'ouverture de session

Le seul texte que tu ecris a la main. Tout le reste est deja dans le repo.

Tu es @<agent>. Lis .claude/agents/<agent>.md, docs/team/protocol.md, AGENTS.md.
Verifie les branches fleet/ existantes, prends l'item prioritaire de backlog/<pillar>/,
et deroule le protocole jusqu'a la PR draft.

Pour orchestrer :

Tu es @conductor. Lance pnpm fleet:status, trie backlog/inbox/, propose le plan
de la semaine par pilier, et signale les collisions de hot files sur les PR ouvertes.

10. Savoir ou on en est

pnpm fleet:status

Derive du reel : items de backlog, branches fleet/* et leur inactivite, rapports dates de docs/audits/ et docs/insights/. Rien n'est tenu a la main, donc rien ne perime.

Lance-le avant de prendre un item (pour voir ce qui est deja en vol) et au triage (pour voir ce qui pourrit). En CI il tourne sur chaque PR et chaque lundi 08:00 UTC, dans le job summary. Il ne bloque jamais : fleet:scope est le gate, fleet:status est le tableau de bord.