O que Executar um Pipeline faz?
Você está iniciando ou retomando a execução de um pipeline. $1 é o PIPELINE a ser executado — sua pasta sob ./.pipeline/. (Um pipeline v1 ainda pode ser nomeado por um caminho de arquivo de iteração; consulte Pré-requisitos.)
O que você está fazendo
Você é o supervisor executando na sessão principal (profundidade 0). Você não itera sobre as iterações manualmente. Em vez disso, você cria um único subagente pipeline-manager (profundidade 1) que conduz toda a cadeia — criando um novo step-executor por iteração e executando pipeline-improver / pipeline-script-creator entre os passos (o CLI pipeline next que ele aciona emite automaticamente os eventos de UI por iteração e executa quaisquer hooks de worktree externos) — e retorna um relatório estruturado quando a execução termina, é interrompida ou encontra um bloqueador fora do escopo.
Você permanece na profundidade 0 porque três coisas devem viver na sessão principal e um subagente não pode fazê-las: (a) possuir um pid estável para o lockfile de vivacidade da execução, (b) esperar horas por uma condição externa (um PR bloqueador para mesclar) e retomar, (c) eventualmente apresentar resultados ao humano. Portanto, seu trabalho é: gerar o id da execução (ou, em --resume, reentrar em um EXISTENTE — veja "Procedimento de Retomada"), configurar o rastreamento de UI, criar o gerenciador e agir sobre seu relatório — incluindo executar a espera de bloqueio aninhado e reinvocar o gerenciador para retomar.
CRÍTICO — disciplina de tokens: leia quase nada
Este skill é um supervisor, não um leitor. Cada arquivo de iteração é lido por um step-executor em seu próprio contexto novo; a resolução de modelo por passo acontece dentro do pipeline-manager.
- Nunca
Leiaum arquivo de iteração (steps/**/*.md). Você nunca toca neles — nem mesmo o frontmatter. O gerenciador resolve modelos por passo; você só passa a ele o padrão do pipeline. - Leia
PIPELINE.mdapenas como frontmatter (limit: 50), uma vez, no início da cadeia, para extrair o campomodel:e nada mais. Não passe seu conteúdo para o gerenciador. Em--resume, pule esta leitura completamente —pipeline_default_modelvem donext.json.default_modelpersistido da execução existente (veja "Procedimento de Retomada"), o que evita que uma retomada adicione uma leitura de manifesto além da leitura denext.jsonque já precisa paracurrent_iteration. - Os únicos arquivos que você pode
Lerpor completo são aqueles que você mesmo escreve no fluxo de bloqueio aninhado (corpos de issue, notas de trabalho parcial) e, em--resume, o(s) pequeno(s) arquivo(s) de cursor de orquestração.runtime/*/next.json— nunca um arquivo de iteração ou o corpo dePIPELINE.md.
Seleção de executor (experimental)
Existem quatro modos, e pipeline next decide o que executar em seguida em todos eles (E1) — nunca o loop que o chama. Eles diferem apenas em onde o loop vive e o que executa um passo:
| Modo | Loop vive em | Executa um passo |
|---|---|---|
session | nesta sessão principal | a ferramenta Agent, na sessão |
manager (padrão) | um subagente pipeline-manager | a ferramenta Agent, na sessão |
driver | um processo que este plugin possui, sem modelo no loop (pipeline drive) | um novo processo claude -p por passo |
standalone | o mesmo processo possuído que driver | o Agent SDK com sua própria chave de API — nenhuma sessão Claude Code |
driver e standalone são fáceis de confundir porque compartilham um loop e diferem apenas no executor; não confunda nenhum deles com pipeline drive, que é o nome do comando para o caminho driver, não um sinônimo para o modo. Este CLI empacotado implementa session, manager e driver — pipelines v1 escrevem driver como runner: headless no frontmatter de PIPELINE.md (veja abaixo); standalone e uma chave runner: no nível de pipeline.yml pertencem ao mesmo design de quatro modos, mas ainda não estão conectados nesta cópia empacotada, portanto não diga a um usuário que qualquer um deles será executado hoje.
Resolva runner: junto com a leitura de model: no Passo 3 do Procedimento. Em um pipeline v2, é uma chave de nível superior em pipeline.yml — Grep ^runner: lá, que lê uma linha e nunca abre um arquivo de passo; em v1, é o frontmatter de PIPELINE.md, já dentro das ≤50 linhas que você lê para model:. Ausente ou ilegível ⇒ manager, comportamento atual e padrão deliberado (E10). Um valor que é declarado mas não possui um ramo abaixo ainda executa manager — mas diga isso em uma linha antes de começar, porque um modo que o manifesto não declarou é um modo que o autor não escolheu. Nunca infira o modo pelo comprimento da cadeia: um limite invisível faz um comando se comportar de duas maneiras.
runner: session — a sessão principal executa o loop sozinha
NÃO crie um pipeline-manager. Leia o loop de sessão e siga-o no lugar dos Passos 5.1–5.3 do Procedimento; todo o resto neste arquivo — Passos 1–4, o Procedimento de Retomada, o Fluxo de Bloqueio Aninhado, as invariantes do Supervisor e o formato do Relatório — se aplica a você inalterado, porque você ainda é o supervisor na profundidade 0 e apenas absorveu o loop em vez de delegá-lo.
Esse arquivo carrega as duas recusas de pré-voo que este modo precisa (um CLI muito antigo para pipeline next --brief-file, e execution: parallel, cujas cargas úteis vivem em um brief que a sessão não pode abrir) e é honesto sobre a troca: cada ação e cada relatório de passo pousa no contexto que o usuário está assistindo, portanto uma cadeia longa pertence ao manager — que é exatamente por que manager ainda é o padrão.
runner: headless (v1) — o modo driver
Quando o pipeline é v1 e o frontmatter de PIPELINE.md que você leu para model: também carrega runner: headless (a grafia v1 do modo driver — veja a tabela acima), NÃO crie um pipeline-manager. Em vez disso, execute o driver empacotado como um processo em segundo plano e supervisione-o:
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]
Inicie-o com run_in_background: true e aja sobre seu JSON final quando ele sair: saída 0 → emita pipeline.completed; saída 1 → emita pipeline.halted e mostre o motivo; saída 3 (blocked) → execute o fluxo de bloqueio aninhado abaixo, depois execute novamente drive com --resume. Todo o resto sobre seu papel de supervisor (id da execução, vivacidade, vínculo de espelho, relatório humano) permanece inalterado. Este caminho driver (v1: runner: headless) pula ações de autoaperfeiçoamento e deixa .feedback/<run_id>/ intacto — mencione isso em seu relatório final para que o usuário possa executar uma passagem manual de melhorias. Quando runner: estiver ausente ou for manager, prossiga exatamente como abaixo.
Seleção de modelo
Um pipeline (e cada iteração) pode optar por um modelo Claude através do campo OPCIONAL model: no frontmatter. O campo padrão é herdado — omiti-lo faz com que o executor de passo herde o modelo da sessão. Você resolve apenas o padrão de nível de pipeline e o entrega ao gerenciador; o CLI pipeline next resolve o modelo efetivo por iteração (substituição por execução ?? step.model ?? pipeline_default_model) e o gerenciador o passa para cada step-executor através do parâmetro model por chamada da ferramenta Agent. Passe o valor resolvido inalterado — NÃO traduza um alias para um id canônico. O parâmetro model da Agent aceita os aliases, ids canônicos e inherit diretamente.
Vocabulário model: aceito (ponta a ponta): um dos aliases haiku / sonnet / opus / fable, OU qualquer id de modelo Claude canônico exato (uma string começando com claude-, ex.: claude-opus-4-8, claude-sonnet-4-6, claude-fable-5), OU inherit / ausente (→ padrão da sessão).
Resolva pipeline_default_model: em um pipeline v2 o CLI lê defaults.model de pipeline.yml ele mesmo — passe null e deixe-o fazer. Caso contrário, se <pipeline-root>/PIPELINE.md existir, Leia com limit: 50 e pegue o valor model: do frontmatter quando for um valor aceito (um alias, um id claude-*, mantido literalmente); inherit/ausente → null. Se PIPELINE.md estiver ausente, null. Valores inválidos — qualquer coisa que não seja um dos aliases aceitos, um id claude-*, inherit, ou ausente — avise uma vez e caia para null (não pare). (Ler ≤ ~10 linhas de frontmatter é extração de metadados, não leitura de conteúdo — nunca duplica o que o executor de passo carrega.)
Substituições de passo por execução (step_model_overrides): o usuário pode fixar passos individuais a um modelo diferente APENAS PARA ESTA EXECUÇÃO — sem editar nenhum arquivo de pipeline — seja com flags explícitas após o caminho (--model <step_id>=<model>, repetível) ou em linguagem natural ("execute os passos 02-implement e 03-refine no fable"). Normalize o que eles disseram em pares <step_id>=<model>: step_id é o step_id do frontmatter do passo ou seu radical de nome de arquivo (ex.: 02-implement para steps/02-implement.md); model usa o vocabulário aceito acima (inherit força o padrão da sessão para aquele passo). Você NÃO lê nenhum arquivo de passo para validar os ids — o CLI avisa sobre ids desconhecidos e rejeita modelos inválidos. Passe os pares para o gerenciador como step_model_overrides (veja 5.1) ou, no caminho driver, como flags --model repetidas no comando drive. Uma substituição vence o próprio model: do passo; passos sem substituição não são tocados. O CLI persiste as substituições no estado da execução na inicialização, então retomadas as mantêm automaticamente — repasse os mesmos pares ao reinvocar o gerenciador de qualquer forma (inofensivo, e sobrevive a um .runtime/ deletado). Nenhuma substituição mencionada ⇒ omita completamente.
Seleção de esforço (esforço de raciocínio — o gêmeo do model:)
Um pipeline e cada iteração também podem optar por um esforço de raciocínio através do campo OPCIONAL effort: no frontmatter (níveis: low / medium / high / xhigh / max; inherit/ausente → o esforço da sessão). Ele resolve através da mesma escada exata que o modelo — substituição por execução ?? step.effort ?? pipeline.effort ?? herança — inteiramente dentro do CLI pipeline next. Resolva pipeline_default_effort da mesma leitura de frontmatter de PIPELINE.md que você já faz para model: (mesma regra de valor inválido: avise uma vez, caia para null), entregue-o ao gerenciador como pipeline_default_effort, e normalize solicitações do usuário como "execute 03-refine com esforço máximo" em pares step_effort_overrides (<step_id>=<level>) passados exatamente como os pares de modelo (flags --effort no caminho driver). NOTA DE HONESTIDADE: o executor driver aplica esforço de verdade (claude --effort por spawn); no modo manager, a ferramenta Agent pode não expor um parâmetro de esforço por chamada ainda — o gerenciador o passa quando suportado e, caso contrário, o passo herda o esforço da sessão (veja pipeline-manager.md § run-step).
Pré-requisitos
- Um pipeline existe sob o diretório
./.pipeline/do projeto atual (tipicamente criado com/pipeline:design). $1é a PASTA do pipeline sob./.pipeline/(ex.:./.pipeline/ship-feature), que inicia uma execução NOVA — isso sempre gera um NOVOrun_id.--start <step-name>inicia essa nova execução em um passo nomeado em vez do primeiro do manifesto. Um caminho de arquivo de iteração ainda é aceito, e é como um pipeline v1 nomeia um passo inicial. OU--resume [<run_id>]para reentrar em uma execução EXISTENTE sob seurun_idORIGINAL (veja "Procedimento de Retomada") — a única maneira de continuar uma execução de sessão morta sem órfão seu estado.- O diretório de trabalho atual é a raiz do projeto consumidor — todas as edições de arquivo realizadas pelas iterações pousam aqui.
Emissões de eventos de diário
Você emite o ciclo de vida de nível de execução para <project>/.pipeline/.runtime/events.jsonl; os eventos por iteração (iteration.*, improver.*, script_creator.*, worktree.*) são emitidos automaticamente no processo pelo CLI pipeline next que o gerenciador aciona (o próprio gerenciador emite apenas os eventos de melhoria/script do retrospecto). Como toda a execução compartilha um session_id, o vínculo de espelho que você registra abaixo é o que permite ao hook de análise correlacionar as chamadas de ferramenta do gerenciador e dos executores de passo a esta execução. As emissões são de melhor esforço — nunca deixe uma falha parar a execução.
Configuração única no início do Procedimento: gere o id da execução chamando o CLI — nunca gere um você mesmo ou invente um formato: bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" id. Capture o valor literal que ele imprime (um UUIDv7, ex.: 019fc762-5762-7000-a9bf-922ed8fa00be).
CRÍTICO — passe run_id literalmente em cada chamada de escritor. A ferramenta Bash do Claude Code não preserva o estado do shell entre invocações, portanto uma variável de ambiente exportada não alcança a próxima chamada pipeline event. Passe run_id=<o-id-literal> como um argumento k=v em CADA chamada. Argumentos k=v não têm espaços ao redor de =; coloque entre aspas simples um valor contendo espaços.
Auxiliar de emissão (execute com Bun, silencioso em sucesso, sai com 0 mesmo em falha — não verifique a saída):
bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" event <event-type> run_id=<literal-id> [k=v ...]
O que você emite (uma chamada por marcador):
- Em
--resume, esta lista toda não se aplica — o Procedimento de Retomada emite apenaswrite-liveness+register-mirror-bindingpara orun_idEXISTENTE, e nuncapipeline.started(reentrar em uma execução não é iniciar uma). Veja "Procedimento de Retomada". - Após o banner:
pipeline.started run_id=<id> pipeline_name=<name> first_iteration_path=<abs> pipeline_root=<abs> default_model=<model-or-null>—default_modelépipeline_default_model(um aliashaiku/sonnet/opus/fable, um idclaude-*canônico, ou literalnull). - Imediatamente após
pipeline.started, escreva o lockfile de vivacidade para que um leitor possa dizer que esta execução morreu sem um evento terminal:bun "${CLAUDE_PLUGIN_ROOT}/apps/pipeline-cli/src/cli.ts" event write-liveness run_id=<id> pid=$PPID(PowerShell:pid=$PID). Passe o pid do SO do processo dirigindo este supervisor —$PPIDé o melhor handle portável para a sessão Claude persistente. O daemon só aposenta automaticamente uma execução quando este pid é um processo real e morto, portanto um valor não confiável é um no-op seguro. - I