Pular para o conteúdo

Etapas de pipeline

Uma etapa de pipeline tem dois eixos independentes:

  • Tipo (StepKind) — como o motor agenda a etapa em relação aos seus gatilhos.
  • Chave de corpo (BuiltInBodyKeys) — qual closure de corpo registrada executa o trabalho da etapa.

Definidos em packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.

Tipo Semântica de agendamento
trigger Ponto de entrada obrigatório — exatamente um por modelo, sempre o primeiro nó. Não realiza trabalho algum; o que inicia a execução (manual / evento / agendamento) é rastreado à parte como linhas PipelineTrigger. Seu corpo é o no-op pipeline.trigger.
listen Dispara quando todas as etapas de origem (em StepTrigger.sourceStepIds) são concluídas.
join Dispara quando todas as etapas em waitForStepIds alcançam um estado terminal.
router Ramificação condicional — o corpo retorna StepResult.route(key), que dispara apenas a aresta a jusante cujo routeKey corresponde; os ramos não selecionados são marcados como ignorados.
forEach Agendada como listen (dispara quando todos os sourceStepIds são concluídos). O corpo flow.forEach registrado é o que itera; o motor não gera uma etapa filha por item.
terminal Sentinela do agendador, sem corpo. A execução do pipeline é concluída quando uma etapa terminal termina.

Closures de corpo registradas, indexadas pelas constantes em BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). São 25, todas listadas abaixo. A configuração é lida do PipelineNodeConfig da etapa (veja abaixo), salvo indicação em contrário; as “chaves de estado” são lidas do estado do pipeline / payload do gatilho no momento da execução.

Uma etapa cujo bodyKey não está registrado produz um aviso no carregamento do modelo e depois nunca é executada — ela não faz a execução falhar. As etapas trigger e terminal são isentas desse aviso: o gatilho ainda executa o corpo no-op pipeline.trigger; uma etapa terminal não tem corpo.

Corpo no-op do nó trigger obrigatório — conclui imediatamente para que o motor dispare as etapas de escuta a jusante. Sem configuração.

Etapa de shell sem agente (bash -c). Os placeholders {{key}} no script são substituídos a partir do estado + payload do gatilho, com valores escapados para o shell.

Campo de configuração Descrição
script Comando de shell a executar (obrigatório)
outputKey Chave de estado que recebe o stdout aparado quando a saída é 0

Executa com cwd <cc_root>/pipelines/<pipelineRunId>/ (compartilhado por todas as etapas bash de uma execução) e com GITHUB_TOKEN no ambiente. Também grava <stepId>_runDir no estado. Uma saída diferente de zero faz a etapa falhar, com o trecho final do stderr.

Nó genérico de prompt e despacho: renderiza o prompt, despacha o agente para uma conversa e suspende até que a execução termine. O payload submit_output do agente é coletado no estado sob outputKey.

Campo de configuração Descrição
agentId UUID do agente no escopo do espaço de trabalho a despachar (obrigatório)
prompt Texto de instrução; aceita substituição {{key}} (obrigatório)
outputKey Chave de estado do output coletado
outputSchema JSON Schema que o output precisa satisfazer
label Rótulo humano exibido no canvas
extras.spaceId Conversa existente em que trabalhar (padrão: uma oculta)
extras.conversationTitle Fluxo nomeado dentro desse espaço (opcional)
extras.mode Lida no nó que abre a sala (messaging.createSpace), não aqui. Padrão review.

Corpo de roteador — lê config.extras, avalia uma condição e retorna StepResult.route(key). As condições não são expressões JavaScript; três formas de escrita são aceitas, em ordem de prioridade:

  • Árvore de predicados (extras.predicate) — uma árvore booleana que roteia "true" / "false". Cada nó é um mapa com um type:
    • {"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false} — verdadeiro quando qualquer caminho listado existe no disco; negate o inverte; recursive também pesquisa subdiretórios (ignorando .git, node_modules, build, .dart_tool). Caminhos relativos são resolvidos em relação a state[baseKey] (padrão repo_local_path) ou, na ausência dele, ao diretório do espaço de trabalho por execução.
    • {"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80} — operadores equals, notEquals, contains, exists, notExists, gt, lt; lê o estado do pipeline, não o sistema de arquivos.
    • {"type": "and" | "or", "of": [<predicate>, ...]} — grupos booleanos.
    • {"type": "not", "of": <predicate>} — negação.
  • Switch (extras.switchKey) — {"switchKey": "pr_class", "cases": [...], "default": "standard"}; roteia para o primeiro caso que o valor de estado contém, sem diferenciar maiúsculas de minúsculas, ou, na ausência dele, para default.
  • Comparação (campos legados de nível superior extras.left / extras.op / extras.right) — equivalente a um predicado comparison, mantido para modelos criados antes da árvore.

As arestas que saem do nó carregam um routeKey que precisa corresponder à chave retornada.

Publica os achados consolidados como um comentário de revisão de PR pelo cliente de PR do GitHub.

Chave de estado Descrição
repo_full_name owner/repo (obrigatório)
pr_number Número da PR — int ou string numérica (obrigatório)
consolidated_findings Corpo do comentário (obrigatório)

Retorna StepResult.terminal com comment_review_id e commented_at no estado.

Publica uma mensagem em um espaço de mensagens via MessagingPort.

Chave de estado Descrição
space_id Espaço de destino (obrigatório)
content Corpo da mensagem, lido literalmente do estado (obrigatório)

Grava posted_space_id e posted_at no estado.

Despacha uma equipe inteira em vez de um único agente e depois suspende até que as execuções dos membros terminem.

Campo de configuração Descrição
teamId Equipe no escopo do espaço de trabalho a despachar (obrigatório; mutuamente exclusivo com agentId)
prompt Texto do objetivo; aceita substituição {{key}} (obrigatório)
dispatchMode allParallel (padrão — uma tarefa por membro) ou manager (despacha apenas o líder para coordenar)
outputKey Chave de estado dos outputs coletados dos membros
outputSchema, label, extras.spaceId Como em conversation.promptAgent

Portão de aprovação. Despacha o agente aprovador para uma conversa (modo review) com o prompt do portão e um contrato de saída {decision, reason}, depois suspende até que o aprovador envie via submit_output.

Campo de configuração Descrição
agentId O agente aprovador (obrigatório)
prompt Prompt do portão (opcional; um padrão é usado quando ausente)
outputKey Chave de estado do payload {decision, reason} coletado
outputSchema Padrão: um schema de aprovação (decision: approved / rejected, reason)
label, extras.spaceId Como em conversation.promptAgent

O portão só se resolve por meio de submit_output. BuiltInBodyKeys.humanGate, a biblioteca de tipos de nó e o texto de prompt de um modelo integrado nomeiam ferramentas MCP approve_step e reject_step; nenhuma dessas ferramentas existe em qualquer pacote ou registro.

Remove worktrees isoladas obsoletas; escolhe seu modo a partir do payload do gatilho. Respeita a flag dryRun da execução.

Chave de estado Descrição
ticket_id Libera as worktrees desse ticket
repo_full_name + pr_number Libera a worktree efêmera do editor de PR
space_id Derruba as worktrees e a pasta dessa conversa
(nenhuma das anteriores) Varre o espaço de trabalho: diretórios que sumiram, espaços mortos, pastas órfãs de conversas

outputKey (opcional) recebe uma string de resumo.

Map / fan-out sobre uma coleção de estado: renderiza o prompt uma vez por item e depois despacha o agente uma única vez, com esses prompts concatenados (separadores ---), e suspende até que essa execução termine. Uma coleção vazia é concluída imediatamente com uma lista vazia. Não é um despacho paralelo por item.

Campo de configuração Descrição
agentId Agente a despachar por item (obrigatório)
prompt Prompt por item; aceita substituição {{key}} (obrigatório)
extras.iterableKey Chave de estado que contém a coleção (obrigatória)
extras.itemKey Chave à qual o item atual é vinculado no prompt (padrão item)
outputKey Chave de estado do output coletado (um despacho, não uma lista)

Executa outro modelo de pipeline como uma subetapa aninhada (o subpipeline roda como uma execução filha com parentPipelineRunId definido). A etapa suspende até que a filha alcance um estado terminal; em seguida, o estado final da filha é mesclado sob outputKey.

Campo de configuração Descrição
extras.templateId Modelo filho a executar (obrigatório; um pipeline não pode chamar a si mesmo)
inputKeys Chaves de estado copiadas para o payload do gatilho da filha
outputKey Chave de estado do estado final mesclado da filha

Indexação em segundo plano com tree-sitter: percorre um repo, extrai símbolos/arestas em isolates de worker e os ingere no grafo de código do espaço de trabalho. Transmite o progresso para a linha de execução da etapa; aceita dry-run. O tree-sitter e suas gramáticas são natives obrigatórios — uma dylib ausente significa que o servidor nunca iniciou, e uma gramática que falha ao resolver no meio da execução faz a etapa falhar em vez de ignorá-la.

Chave de estado Descrição
repo_id Repo a indexar (obrigatório)
repo_local_path Caminho local do checkout (obrigatório)

Diarização de falantes offline. Lê o áudio retido da reunião, agrupa-o em falantes (Person 1, Person 2, …), reatribui os rótulos dos segmentos da transcrição e reescreve o estado transcript; também emite diarization_spans para a etapa paralela meeting.updateTranscript. Repassa a transcrição inalterada quando nenhum áudio foi retido ou os modelos não estão instalados.

Chave de estado Descrição
meeting_id Reunião a diarizar (obrigatório)

meeting.saveNotes, meeting.addActionItems, meeting.addDecisions

Seção intitulada “meeting.saveNotes, meeting.addActionItems, meeting.addDecisions”

Corpos determinísticos de persistência do resumo de reunião. Cada um lê meeting_id e o payload estruturado meeting_outcome da etapa de agente ({summary, enhancedNotes, actionItems[], decisions[]}) do estado e grava sua parte em sua própria tabela — notas, itens de ação ou decisões. Linhas já salvas nunca são apagadas em uma execução degradada.

Chave de corpo Descrição
meeting.identifySpeakers Reconhecimento de falantes entre reuniões: compara os falantes diarizados com os perfis de voz salvos e aplica automaticamente as correspondências confiáveis. No-op sem perfis/embeddings.
meeting.updateTranscript Aplica os rótulos Person N de diarization_spans e mescla os fragmentos por janela em turnos por falante, depois persiste a transcrição limpa. No-op quando a diarização não produziu nada.
meeting.assemblePlayback Monta a faixa de reprodução mixada da reunião a partir dos WAVs retidos por canal. No-op quando nenhum áudio foi retido.
orchestration.markPhase No fim do DAG de trabalho, grava sentinelas de falha para subtickets sem output e muda a orquestração para synthesizing.
orchestration.persistDeliverable Grava o output da síntese no ticket pai, o conclui, publica o entregável e marca a orquestração como completed.
orchestration.awaitApproval Portão de aprovação parcial: conclui imediatamente quando a chave do nó da etapa está no conjunto aprovado da orquestração; caso contrário, suspende até que uma aprovação posterior a retome. Este é o único corpo que define extras.awaitApproval, o que o isenta do tempo limite de 24 horas para etapas suspensas.
messaging.createSpace Abre a conversa em que o restante do modelo trabalha, publicada sob o outputKey da etapa (os integrados usam pipeline_space_id). Uma lista repoIds vazia não faz checkout de nada; extras['allRepos'] faz checkout de todos os repos do espaço de trabalho. extras['createConversation'] (desativado por padrão) também abre um fluxo nomeado. Uma etapa de agente ENTRA em uma sala e nunca abre uma, então uma distribuição sem esse nó falha no despacho — e conectá-lo uma vez faz com que toda a distribuição compartilhe um único checkout em vez de provisionar um para cada.
prReview.finalize A última etapa do modelo de revisão de PR: classifica os nós de revisão acumulados em grupos de consenso e calcula o veredito da PR.
skills.analyze Executa a varredura de cadeia de suprimentos de skills sobre as skills instaladas de um espaço de trabalho e registra cada veredito.

O corpo de uma etapa retorna um StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Todas as factories aceitam um mapa opcional mutatedState mesclado ao estado do pipeline, exceto failed.

Factory Assinatura Descrição
ok StepResult.ok({mutatedState}) Conclusão normal; as etapas de escuta a jusante são avaliadas
route StepResult.route(nextRouterKey, {mutatedState}) Conclusão do roteador; seleciona o ramo a jusante cujo routeKey corresponde
suspendUntilEvent StepResult.suspendUntilEvent(eventType, {mutatedState}) Pausa até que um tipo específico de evento de domínio seja emitido
suspendUntilTasksComplete StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) Pausa até que todas as tarefas listadas alcancem um estado terminal
terminal StepResult.terminal({mutatedState}) A execução do pipeline terminou — não há mais etapas
failed StepResult.failed(errorMessage) A etapa falhou com uma mensagem de erro

Comportamento de nova tentativa para o corpo de um nó que falha (PipelineNodeConfig.retryPolicy; null desativa as novas tentativas).

Campo Tipo Descrição
maxAttempts int Total de tentativas incluindo a primeira (3 = 1 tentativa + 2 novas tentativas); precisa ser ≥ 1
backoff String linear ou exponential (padrão)
initialDelayMs int Atraso antes da primeira nova tentativa, em milissegundos; os atrasos seguintes crescem conforme o backoff

Configuração por nó carregada dentro da definição da etapa (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). Nós integrados e personalizados compartilham esse formato; cada corpo lê apenas os campos que lhe interessam.

Campo Tipo Descrição
prompt String? Modelo de prompt com substituição {{key}}
script String? Corpo de script bash com substituição {{key}}
agentId String? UUID do agente no escopo do espaço de trabalho a despachar
inputKeys List<String> Chaves de estado que este nó consome como entrada
repoIds List<String> Repos dos quais o nó messaging.createSpace faz checkout (<repoId> ou <repoId>@<branch>). Vazio = nenhum; extras['allRepos'] = todos os repos do espaço de trabalho. Ignorado em etapas de agente.
outputKey String? Chave de estado sob a qual o output deste nó é gravado
label String? Rótulo humano exibido no canvas (o padrão é o ID da etapa)
outputSchema Map<String, dynamic>? JSON Schema (subconjunto) que o valor de output precisa satisfazer
reducer String? Estratégia de mesclagem quando ramos paralelos gravam no mesmo outputKey: append, mergeLists, mergeMaps, sum ou override (padrão)
retryPolicy StepRetryPolicy? Política de novas tentativas em caso de falha do corpo; null = uma única tentativa
continueOnFail bool Quando verdadeiro, uma falha terminal não faz a execução falhar; o erro é guardado em state['_stepErrors'][stepId]
timeoutMs int? Tempo limite em tempo real para o corpo do nó, em milissegundos; null = sem tempo limite
teamId String? Equipe a despachar (nós team.dispatch); mutuamente exclusivo com agentId
dispatchMode String? Modo de execução da equipe: allParallel ou manager
extras Map<String, dynamic> Configuração livre específica do corpo (por exemplo, iterableKey, templateId, árvores de condição)

Acessores derivados: spaceId (extras['spaceId'] — conversa existente em que trabalhar), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, lida no nó que abre o espaço) e runWhen (extras['runWhen'] — ignora o nó quando não atendido).