Règles du convoyage de livraison
Ce projet exécute un convoyage de livraison par ticket au-dessus de GSD
(voir docs/gsd_multilevel_delivery_pipeline.md). Les plans font office de tickets ;
ces règles les maintiennent exploitables par la couche déterministe du convoyage.
Langue. Tous les artefacts — plans, frontmatter, commits, code, corps de PR — sont rédigés en anglais. Lorsque vous vous adressez directement à l'utilisateur, répondez dans la langue de l'utilisateur ; l'anglais est réservé aux artefacts, la langue de l'utilisateur l'est pour la conversation.
Pour les planificateurs (rédaction de PLAN.md)
-
Frontmatter complet, toujours :
phase,plan,title,type,wave,depends_on: [],files_modified: [],requirements: []. Unrequirementsvide est un BLOCAGE — le vérificateur de plan de GSD ainsi que la Porte 2 (validate-graph) le rejettent ; référencez les identifiants d'exigence du ROADMAP, ou l'identifiant du suivi externe lorsque le plan a été importé.waveest une documentation pour le lecteur humain : le graphe fait autorité et le validateur recalcule la profondeur réelle des dépendances, avertissant lorsque votre valeur diffère. Gardez-la véridique plutôt que décorative. -
Bloc de livraison (additif, ne remplace jamais les champs GSD) :
delivery: ticket: T-<phase>-<plan> risk: low|medium|high # high REQUIERT human_checkpoint: true human_checkpoint: false repo: owner/name # UNIQUEMENT lorsque le travail réside dans un autre dépôtrepoest obligatoire pour tout ticket qui ne touche PAS à ce dépôt. Si vous l'omettez, le convoyage suit le ticket par rapport au mauvais dépôt : sa PR peut être verte et fusionnée ailleurs alors que le tableau indiquependingpour toujours, et chaque ticket dépendant reste bloqué derrière un ticket qui ne bougera jamais. Les chemins dansfiles_modifiedsont alors relatifs à CE dépôt — jamais../other-repo/x.ts, qu'aucun worktree ne peut atteindre (la Porte 2 avertit, la synchronisation d'état met le ticket en attente). Les dépendances inter-dépôts ne cascadent pas : le parent doit d'abord FUSIONNER, donc découpez le côté contrat (types/noms d'outils) dans son propre ticket plutôt que de faire attendre le consommateur pour une fonctionnalité complète. -
N'inventez pas de noms de branches. La branche canonique est
ticket/<ID>-<slug>où le slug est le titre du ticket nettoyé (minuscules, translittéré, les caractères non alphanumériques réduits à un seul trait d'union, ≤40 caractères). Omettezdelivery.branch— le validateur de graphe la génère ; une valeur explicite est validée selon la même règle. -
files_modified est un contrat, pas une estimation — listez chaque chemin que le plan touche. Un
files_modifiedVIDE échoue la Porte 2 (c'est ce qui rend « les tickets sans ordre de dépendance ne se heurtent jamais » vérifiable, et c'est le périmètre de l'exécuteur). Les plans sans ordre de dépendance avec des chemins qui se chevauchent échouent également à la Porte 2 ; résolvez avec une dépendance ou une redécoupe, jamais en élargissant des globs (un glob nu qui correspond à tout est signalé). -
Nommez ce qui fait déjà cela, avant de planifier de l'écrire. La section Contexte de chaque plan doit soit pointer vers l'implémentation existante que le ticket étend (
chemin:ligne— le transformateur, le trieur, le middleware, l'assistant, le point de terminaison qu'il construit), soit enregistrer la vérification qui n'en a trouvé aucune : quel comportement vous avez recherché et les deux ou trois emplacements plausibles que vous avez consultés. Recherchez par COMPORTEMENT, pas par le nom que vous avez l'intention de donner au nouveau code — un doublon est presque toujours déjà appelé autrement, ce qui explique pourquoi il survit à la relecture. « Je n'en ai pas vu » n'est pas la même affirmation que « J'ai regardé » ; seule la seconde est un plan qu'un lecteur peut croire. C'est la porte la moins coûteuse du convoyage : un doublon non remarqué est découvert par l'intégrateur toute une phase plus tard, après que d'autres tickets ont déjà construit dessus, et à ce moment-là, le supprimer est son propre ticket. Contrairement au §4, cela ne peut pas être vérifié par machine à partir du frontmatter — la Porte 2 ne le voit jamais, la vérification de dérive ne fait que rattraper les tickets qu'elle rejuge, donc l'écrire est le contrôle. -
Les commandes de vérification sont limitées au ticket, pas au projet. Écrivez les commandes les plus restreintes qui couvrent réellement
files_modified— les fichiers de test ou filtres spécifiques, la vérification de type et le lint sur les chemins touchés. Un simplenpm test/make test/ toute la suite e2e n'est PAS une commande de vérification ici : elle appartient à l'IC, qui l'exécute une fois par push sur du matériel conçu pour cela. L'exécuteur lance celles-ci à CHAQUE tentative et les rôles de correction les relancent à chaque tour, donc une commande non limitée n'est pas lente une fois — c'est la cadence de tout le convoyage, multipliée par chaque ticket exécuté en parallèle dans son propre worktree. Deux propriétés rendent une commande utilisable : elle doit s'exécuter dans un worktree nu sans services externes, et elle doit échouer pour les erreurs de CE ticket. Une commande qui ne peut pas être limitée (une suite qui nécessite une base de données active, un navigateur, un environnement déployé) est une vérification réservée à l'IC — dites-le sous Stratégie de test et laissez-la en dehors des commandes de vérification plutôt que de donner à l'exécuteur quelque chose qu'il ne peut pas exécuter. -
depends_onpilote la cascade, pas seulement l'ordre. Sous une livraison empilée par épopée (par défaut), un ticket racine (depends_onvide) PR dans la branche de l'épopée de phase ; un ticket dépendant cascade — il PR dans la branche de son parent principal SANS attendre une fusion, donc le flux ne se bloque jamais. Déclarez les dépendances avec précision : une dépendance parasite sérialise le flux, une dépendance manquante donne à l'exécuteur une base incomplète. Le validateur dérive l'épopée (epic/<phase-dir>), le parent principal, etpr_base; plusieurs parents de même phase (un diamant) reçoivent un avertissement — linéarisez lorsque c'est pratique. Gardez les dépendances à l'intérieur d'une seule phase. Une dépendance inter-phase ne peut pas cascader — il n'y a pas de branche partagée sur laquelle s'empiler — donc le ticket dépendant reste bloqué jusqu'à ce que toute la phase du parent atterrisse sur la branche par défaut. Le validateur avertit pour chacune d'elles ; recoupez plutôt. -
Les identifiants sont canoniques :
T-<2 chiffres phase>-<2 chiffres plan>. ÉcrivezT-01-02, jamaisT-1-2— le validateur normalise les deux, mais les branches, les titres de PR et les libellés Jira sont comparés par chaîne partout ailleurs. -
Pas de commentaire
#en fin de valeur. Placez un commentaire sur sa propre ligne.files_modified: [a.ts] # noteavait l'habitude de plier le commentaire dans le dernier chemin ; l'analyseur le supprime maintenant correctement, mais la Porte 2 rejette encore toute valeur qui contient#car c'est presque toujours une fuite. -
La Porte 2 est mécanique : elle réussit uniquement lorsque le validateur de graphe existe 0. Les problèmes Jira/GitHub, ROLLOUT.md, ou les résumés en prose ne remplacent jamais les fichiers PLAN matérialisés.
-
Jira est une projection, pas la source de vérité. Par défaut (sauf si
pipeline.jira.enabled: falseou aucun MCP Jira n'est connecté) l'étape de décomposition exporte le graphe validé vers Jira (une Épopée par phase, un problème par ticket) en anglais, après la Porte 2 — jamais avant, jamais à la place. Le projet est résolu automatiquement et mis en cache ; un échec Jira ne doit pas bloquer la décomposition ; les fichiers PLAN restent canoniques.
Pour les exécuteurs (implémentation d'un ticket)
- Restez à l'intérieur de
files_modified. Les modifications hors périmètre appartiennent à un autre ticket — escaladez plutôt que d'étendre le périmètre en silence. - Lisez l'implémentation existante avant d'en écrire une nouvelle. Tout ce que le plan a nommé
au §5, plus les éventuels
reuse_candidatesque le convoyage vous a remis depuis la vérification de dérive : lisez chacun d'abord et étendez-le plutôt que d'ajouter une couche parallèle à côté. Si un candidat ne convient vraiment pas, dites pourquoi dans votre preuve — c'est un résultat légitime, l'ignorer en silence ne l'est pas. La réutilisation ne donne jamais le droit de quitterfiles_modified: si construire dessus vous emmènerait hors périmètre, il s'agit d'une escalade hors périmètre (§1), pas d'un diff plus large. - Commits atomiques préfixés par l'identifiant du ticket :
feat(T-01-02): …. - Exécutez localement les commandes de vérification du plan jusqu'au vert avant de
déclarer terminé ; ne prétendez jamais à une vérification sans sortie de commande. Exécutez ces
commandes — ne soyez pas « exhaustif » en les élargissant à toute la suite (§6) : l'IC
possède l'exécution complète, et une suite que vous lancez ici bloque votre propre worktree et
chaque autre exécuteur partageant la machine. Si les commandes du plan sont cassées
ou ne couvrent clairement pas la modification, corrigez-les ou réduisez-les dans le plan et dites-le
dans votre preuve ; remplacer en silence par
npm testn'est pas une correction. - Worktree et branche proviennent du convoyage (
tickets.json) — ne créez pas de branches ou de worktrees ad hoc. Votre base PR est également résolue par le convoyage (delivery-state.json→base: la branche de l'épopée pour un ticket racine, la branche parente pour un ticket dépendant) — n'ouvrez jamais une PR directement dans main/master sous une pile d'épopées. - Arrêtez-vous au commit. Ne faites pas
push, n'ouvrez pas la pull request, ne touchez pas aux réviseurs. L'orchestrateur vérifie le worktree mécaniquement (git log <base>..HEAD) avant de publier quoi que ce soit — cette vérification est ce qui détecte « l'agent a signalé un succès et n'a rien changé », et elle ne fonctionne que si la publication n'est pas entre les mains de l'agent vérifié. Remettez plutôt votre preuve de vérification et un corps de PR.