Que fait Exécuter un Pipeline ?
Vous démarrez ou reprenez l'exécution d'un pipeline. $1 est le PIPELINE à exécuter — son dossier sous ./.pipeline/. (Un pipeline v1 peut encore être nommé par un chemin de fichier d'itération ; voir Prérequis.)
Ce que vous faites
Vous êtes le superviseur qui s'exécute dans la session principale (profondeur 0). Vous ne bouclez pas vous-même sur les itérations. Au lieu de cela, vous générez un unique sous-agent pipeline-manager (profondeur 1) qui conduit toute la chaîne — générant un nouveau step-executor par itération et exécutant pipeline-improver / pipeline-script-creator entre les étapes (le CLI pipeline next qu'il pilote émet automatiquement les événements UI par itération et exécute lui-même les hooks externes worktree) — et retourne un rapport structuré lorsque l'exécution se termine, s'arrête ou rencontre un blocage hors de son périmètre.
Vous restez à la profondeur 0 car trois choses doivent vivre dans la session principale et un sous-agent ne peut pas les faire : (a) posséder un pid stable pour le fichier de verrou de vivacité de l'exécution, (b) attendre des heures une condition externe (une PR bloquante à fusionner) et reprendre, (c) finalement présenter les résultats à l'humain. Votre travail est donc : créer l'ID d'exécution (ou, sur --resume, ré-entrer dans un ID EXISTANT — voir "Procédure de reprise"), configurer le suivi UI, générer le manager, et agir sur son rapport — y compris exécuter l'attente du blocage imbriqué et ré-invoquer le manager pour reprendre.
CRITIQUE — discipline de jetons : ne lisez presque rien
Ce skill est un superviseur, pas un lecteur. Chaque fichier d'itération est lu par un step-executor dans son propre contexte frais ; la résolution du modèle par étape a lieu à l'intérieur du pipeline-manager.
- Ne
Lisezjamais un fichier d'itération (steps/**/*.md). Vous ne les touchez jamais — pas même leur frontmatter. Le manager résout les modèles par étape ; vous ne lui passez que le modèle par défaut du pipeline. - Lisez
PIPELINE.mduniquement comme frontmatter (limit: 50), une fois, au début de la chaîne, pour extraire le champmodel:commepipeline_default_modelet rien d'autre. Ne passez pas son contenu au manager. Sur--resume, sautez entièrement cette lecture —pipeline_default_modelprovient dunext.json.default_modelpersistant de l'exécution existante (voir "Procédure de reprise"), ce qui empêche une reprise d'ajouter une lecture de manifeste en plus de la lecturenext.jsondéjà nécessaire pourcurrent_iteration. - Les seuls fichiers que vous pouvez
Lireen entier sont ceux que vous écrivez vous-même dans le flux de blocage imbriqué (corps d'issues, notes de travail partielles) et, sur--resume, le(s) petit(s) fichier(s) de curseur d'orchestration.runtime/*/next.json— jamais un fichier d'itération ou le corps dePIPELINE.md.
Sélection du mode d'exécution (expérimental)
Quatre modes existent, et pipeline next décide de ce qui s'exécute ensuite dans chacun d'eux (E1) — jamais la boucle qui l'appelle. Ils diffèrent uniquement par où se trouve la boucle et ce qui exécute une étape :
| Mode | Boucle vit dans | Exécute une étape |
|---|---|---|
session | cette session principale | l'outil Agent, dans la session |
manager (défaut) | un sous-agent pipeline-manager | l'outil Agent, dans la session |
driver | un processus possédé par ce plugin, aucun modèle dans la boucle (pipeline drive) | un nouveau processus claude -p par étape |
standalone | le même processus possédé que driver | le SDK Agent avec votre propre clé API — aucune session Claude Code du tout |
driver et standalone sont faciles à confondre car ils partagent une boucle et diffèrent seulement par l'exécuteur ; ne confondez aucun des deux avec pipeline drive, qui est le nom de la commande pour le chemin driver, pas un synonyme du mode. Ce CLI fourni implémente session, manager et driver — les pipelines v1 écrivent driver comme runner: headless dans le frontmatter de PIPELINE.md (voir ci-dessous) ; standalone et une clé runner: au niveau de pipeline.yml appartiennent au même design à quatre modes mais ne sont pas encore câblés dans cette copie fournie, donc ne dites pas à un utilisateur que l'un ou l'autre s'exécutera aujourd'hui.
Résolvez runner: en même temps que la lecture de model: à l'étape 3 de la Procédure. Dans un pipeline v2, c'est une clé de premier niveau dans pipeline.yml — Grep ^runner: là, ce qui lit une ligne et n'ouvre jamais un fichier d'étape ; dans v1, c'est le frontmatter de PIPELINE.md, déjà dans les ≤50 lignes que vous lisez pour model:. Absent ou illisible ⇒ manager, comportement actuel et défaut délibéré (E10). Une valeur qui est déclarée mais n'a pas de branche ci-dessous exécute encore manager — mais dites-le en une ligne avant de commencer, car un mode que le manifeste n'a pas déclaré est un mode que l'auteur n'a pas choisi. Ne déduisez jamais le mode de la longueur de la chaîne : un seuil invisible fait qu'une commande se comporte de deux manières.
runner: session — la session principale exécute elle-même la boucle
Ne générez PAS de pipeline-manager. Lisez la boucle de session et suivez-la à la place des étapes 5.1–5.3 de la Procédure. Tout le reste de ce fichier — étapes 1–4, la Procédure de reprise, le Flux de blocage imbriqué, les invariants du superviseur et le format du rapport — s'applique à vous sans changement, car vous êtes toujours le superviseur à la profondeur 0 et avez seulement absorbé la boucle au lieu de la déléguer.
Ce fichier porte les deux refus de pré-vol dont ce mode a besoin (un CLI trop vieux pour pipeline next --brief-file, et execution: parallel, dont les charges utiles vivent dans un brief que la session ne peut pas ouvrir) et est honnête sur le compromis : chaque action et chaque rapport d'étape atterrit dans le contexte que l'utilisateur regarde, donc une longue chaîne appartient à manager — ce qui est précisément pourquoi manager reste le défaut.
runner: headless (v1) — le mode driver
Lorsque le pipeline est v1 et que le frontmatter PIPELINE.md que vous lisez pour model: porte aussi runner: headless (l'orthographe v1 du mode driver — voir le tableau ci-dessus), ne générez PAS de pipeline-manager. Au lieu de cela, exécutez le pilote fourni comme processus d'arrière-plan et supervisez-le :
bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" drive \
--root "<pipeline_root>" --run-id "<run_id>" [--start "<step-name>"] \
--default-model "<pipeline_default_model-or-null>" \
[--default-effort "<level-or-null>"] \
[--model "<step_id>=<model>" ...] [--effort "<step_id>=<level>" ...] --json [--resume]
Lancez-le avec run_in_background: true et agissez sur son JSON final lorsqu'il se termine : code de sortie 0 → émettre pipeline.completed ; code de sortie 1 → émettre pipeline.halted et remonter la raison ; code de sortie 3 (blocked) → exécuter le flux de blocage imbriqué ci-dessous, puis relancer drive avec --resume. Tout le reste de votre rôle de superviseur (ID d'exécution, vivacité, liaison miroir, rapport humain) est inchangé. Ce chemin driver (v1 : runner: headless) saute les actions d'auto-amélioration et laisse .feedback/<run_id>/ intact — mentionnez-le dans votre rapport final pour que l'utilisateur puisse lancer un passage manuel d'amélioration. Lorsque runner: est absent ou manager, procédez exactement comme ci-dessous.
Sélection du modèle
Un pipeline (et chaque itération) peut choisir un modèle Claude via le champ OPTIONNEL model: du frontmatter. Le champ par défaut est hérité — omettez-le et le step-executor hérite du modèle de session. Vous ne résolvez que le défaut au niveau du pipeline et le transmettez au manager ; le CLI pipeline next résout le modèle effectif par itération (surcharge par exécution ?? step.model ?? pipeline_default_model) et le manager le passe à chaque step-executor via le paramètre model par appel de l'outil Agent. Passez la valeur résolue sans changement — ne traduisez PAS un alias en un ID canonique. Le paramètre model de l'Agent accepte directement les alias, les ID canoniques et inherit.
Vocabulaire model: accepté (de bout en bout) : l'un des alias haiku / sonnet / opus / fable, OU tout ID de modèle Claude canonique exact (une chaîne commençant par claude-, par exemple claude-opus-4-8, claude-sonnet-4-6, claude-fable-5), OU inherit / absent (→ défaut de session).
Résolvez pipeline_default_model : dans un pipeline v2, le CLI lit defaults.model depuis pipeline.yml lui-même — passez null et laissez-le faire. Sinon, si <pipeline-root>/PIPELINE.md existe, Lisez-le avec limit: 50 et prenez la valeur model: du frontmatter lorsqu'il s'agit d'une valeur acceptée (un alias, un claude-* ID, conservé textuellement) ; inherit/absent → null. Si PIPELINE.md est absent, null. Valeurs invalides — tout ce qui n'est pas l'un des alias acceptés, un claude-* ID, inherit, ou absent — avertissez une fois et retombez à null (ne vous arrêtez pas). (Lire ≤ ~10 lignes de frontmatter est une extraction de métadonnées, pas une lecture de contenu — cela ne duplique jamais ce que le step-executor charge.)
Surcharges d'étape par exécution (step_model_overrides) : l'utilisateur peut épingler des étapes individuelles à un modèle différent POUR CETTE EXÉCUTION UNIQUEMENT — sans modifier aucun fichier de pipeline — soit avec des drapeaux explicites après le chemin (--model <step_id>=<model>, répétable), soit en langage naturel ("exécute les étapes 02-implement et 03-refine sur fable"). Normalisez ce qu'ils ont dit en paires <step_id>=<model> : step_id est le step_id du frontmatter de l'étape ou son nom de fichier (par exemple 02-implement pour steps/02-implement.md) ; model utilise le vocabulaire accepté ci-dessus (inherit force le défaut de session pour cette étape). Vous ne lisez AUCUN fichier d'étape pour valider les ID — le CLI avertit sur les ID inconnus et rejette les modèles invalides. Passez les paires au manager comme step_model_overrides (voir 5.1) ou, sur le chemin driver, comme des drapeaux --model répétés sur la commande drive. Une surcharge bat le propre model: du frontmatter de l'étape ; les étapes sans surcharge ne sont pas touchées. Le CLI persiste les surcharges dans l'état de l'exécution à l'initialisation, donc les reprises les conservent automatiquement — repassez les mêmes paires lors de la ré-invocation du manager de toute façon (inoffensif, et cela survit à un .runtime/ supprimé). Pas de surcharges mentionnées ⇒ omettez entièrement.
Sélection de l'effort (effort de raisonnement — le jumeau de model:)
Un pipeline et chaque itération peuvent aussi choisir un effort de raisonnement via le champ OPTIONNEL effort: du frontmatter (niveaux : low / medium / high / xhigh / max ; inherit/absent → l'effort de la session). Il se résout par la même échelle exacte que le modèle — surcharge par exécution ?? effort d'étape effort: ?? effort pipeline effort: ?? héritage — entièrement à l'intérieur du CLI pipeline next. Résolvez pipeline_default_effort depuis la même lecture du frontmatter PIPELINE.md que vous faites déjà pour model: (même règle de valeur invalide : avertissez une fois, retombez à null), transmettez-le au manager comme pipeline_default_effort, et normalisez les demandes utilisateur comme "exécute 03-refine avec effort max" en paires step_effort_overrides (<step_id>=<level>) passées exactement comme les paires de modèle (drapeaux --effort sur le chemin driver). NOTE D'HONNÊTETÉ : l'exécuteur driver applique l'effort pour de vrai (claude --effort par génération) ; en mode manager, l'outil Agent peut ne pas encore exposer un paramètre d'effort par appel — le manager le passe lorsqu'il est pris en charge et sinon l'étape hérite de l'effort de session (voir pipeline-manager.md § run-step).
Prérequis
- Un pipeline existe sous le
./.pipeline/du projet courant (généralement créé avec/pipeline:design). $1est le DOSSIER du pipeline sous./.pipeline/(par exemple./.pipeline/ship-feature), qui démarre une exécution FRAÎCHE — cela crée toujours un NOUVELrun_id.--start <step-name>démarre cette exécution fraîche à une étape nommée au lieu de la première du manifeste. Un chemin de fichier d'itération est toujours accepté, et c'est ainsi qu'un pipeline v1 nomme une étape de départ. OU--resume [<run_id>]pour ré-entrer dans une exécution EXISTANTE sous sonrun_idORIGINAL (voir "Procédure de reprise") — le seul moyen de continuer l'exécution d'une session morte sans orpheliner son état.- Le répertoire de travail courant est la racine du projet consommateur — toutes les modifications de fichiers effectuées par les itérations atterrissent ici.
Émissions d'événements de journal
Vous émettez le cycle de vie au niveau de l'exécution vers <projet>/.pipeline/.runtime/events.jsonl ; les événements par itération (iteration.*, improver.*, script_creator.*, worktree.*) sont automatiquement émis en processus par le CLI pipeline next que le manager pilote (le manager lui-même n'émet que les événements d'amélioration/script du rétrospectif). Comme toute l'exécution partage un même session_id, la liaison miroir que vous enregistrez ci-dessous est ce qui permet au hook d'analyse de corréler les appels d'outils du manager et des step-executor à cette exécution. Les émissions sont au mieux — ne laissez jamais un échec arrêter l'exécution.
Configuration unique au début de la Procédure : créez l'ID d'exécution en appelant le CLI — ne générez jamais un vous-même ou n'inventez pas de format : bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" id. Capturez la valeur littérale qu'il imprime (un UUIDv7, par exemple 019fc762-5762-7000-a9bf-922ed8fa00be).
CRITIQUE — passez run_id littéralement à chaque appel d'écrivain. L'outil Bash de Claude Code ne préserve pas l'état du shell entre les invocations, donc une variable d'environnement exportée n'atteint pas le prochain appel pipeline event. Passez run_id=<l'id-littéral> comme argument k=v sur CHAQUE appel. Les arguments k=v n'ont pas d'espaces autour de = ; mettez entre quotes simples une valeur contenant des espaces.
Aide à l'émission (exécutée avec Bun, silencieuse en cas de succès, sortie 0 même en cas d'échec — ne vérifiez pas la sortie) :
bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" event <event-type> run_id=<literal-id> [k=v ...]
Ce que vous émettez (un appel par puce) :
- Sur
--resume, cette liste entière ne s'applique pas — la Procédure de reprise n'émet quewrite-liveness+register-mirror-bindingpour lerun_idEXISTANT, et jamaispipeline.started(ré-entrer dans une exécution n'en démarre pas une nouvelle). Voir "Procédure de reprise". - Après la bannière :
pipeline.started run_id=<id> pipeline_name=<nom> first_iteration_path=<chemin_abs> pipeline_root=<chemin_abs> default_model=<modèle-ou-null>—default_modelestpipeline_default_model(un aliashaiku/sonnet/opus/fable, un IDclaude-*canonique, ou le littéralnull). - Immédiatement après
pipeline.started, écrivez le fichier de verrou de vivacité pour qu'un lecteur puisse dire que cette exécution est morte sans événement terminal :bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" event write-liveness run_id=<id> pid=$PPID(PowerShell :pid=$PID). Passez le pid OS du processus conduisant ce superviseur —$PPIDest la meilleure poignée portable pour la session Claude persistante. Le démon ne retire automatiquement une exécution que lorsque ce pid est un processus réel et mort, donc une valeur non fiable est un no-op sûr. - Im...