Pipelines y automatización
Los pipelines son la mitad determinista de Control Center. Un agente decide qué decir; un pipeline decide qué ocurre después, siempre de la misma manera. Un pipeline es un DAG de pasos que puede despachar agentes, ejecutar scripts, bifurcar según una condición, ramificarse en abanico sobre una colección y pasar datos entre pasos: persistido, reanudable y con costos contabilizados.
Existen por tres razones: encadenar un proceso repetitivo de varios pasos, coordinar varios agentes en secuencia o en paralelo y reaccionar ante algo que ocurrió sin que hubiera una persona presente.
Plantillas y ejecuciones
Sección titulada «Plantillas y ejecuciones»Una plantilla (PipelineDefinition) es el DAG declarativo: pasos, entradas
declaradas y configuración. Vive en la base de datos del propio espacio de
trabajo. Una ejecución (PipelineRun) es una instancia en marcha de esa
plantilla, que conlleva una bolsa de estado mutable, totales de costo y de
tokens, y el estado de cada paso.
Una plantilla tiene muchas ejecuciones. Las ejecuciones sobreviven a un reinicio: el motor vuelve a cargar las que están en curso y las retoma donde se detuvieron.
Dos salvaguardas hacen fallar deliberadamente toda la ejecución en lugar de reanudar un paso, porque reanudar sería peor que detenerse:
- Un paso que lleva
suspendedmás tiempo quesuspendedStepTimeout(24 horas de forma predeterminada) está esperando tickets que nunca alcanzarán un estado terminal, así que se marca como fallido para liberar la ejecución. La excepción esextras['awaitApproval'] == true, que es la puerta de aprobación parcialorchestration.awaitApproval.human.gateno queda exento. - Un paso declarado como
extras: {'idempotent': false}que estaba en curso cuando el servidor se detuvo puede que ya haya aplicado su efecto secundario (una fusión squash, una limpieza de worktree). Volver a ejecutarlo podría aplicarlo dos veces, así que el motor lo marca como fallido y te deja a ti el reintento.
El tipo programa, el cuerpo trabaja
Sección titulada «El tipo programa, el cuerpo trabaja»Un paso tiene dos partes ortogonales, y mantenerlas separadas es lo que permite que el motor siga siendo pequeño.
El tipo (StepKind) le dice al motor cuándo un paso puede ejecutarse en
relación con los pasos previos; eso es lo único que el planificador necesita
saber. Hay seis: trigger, listen, join, router, forEach y terminal.
El cuerpo (bodyKey) es el trabajo que realiza el paso una vez que se
programa. Los cuerpos son clausuras registradas por clave al iniciar el
servidor, así que muchos pasos de tipos distintos pueden compartir un mismo
cuerpo: varios pasos listen que ejecutan conversation.promptAgent, por
ejemplo.
Como la semántica de programación vive en seis tipos, el catálogo de comportamientos puede crecer sin que nadie toque el planificador. Consulta Tipos de pasos de pipeline para ver el catálogo completo de tipos, cuerpos y su configuración.
Vale la pena conocer dos consecuencias del diseño del registro antes de crear una plantilla a mano:
- Un paso cuyo
bodyKeyno está registrado genera una advertencia al cargar la plantilla y después nunca se ejecuta. Nada falla de forma ruidosa en tiempo de ejecución. Los pasostriggeryterminalestán exentos de esa advertencia: el paso trigger igualmente ejecuta el cuerpopipeline.trigger, que no hace nada, para que se activen los pasos de escucha posteriores; un pasoterminalno tiene cuerpo y es un centinela del planificador. - Las claves del mapa
extrasde un nodo se comparan por coincidencia exacta y, en caso contrario, se ignoran en silencio. Soloextras['spaceId']coloca el trabajo de un paso de agente en una conversación existente.extras['mode'](chat/plan/review) se lee en el nodo que abre la sala (messaging.createSpace,reviewde forma predeterminada); un paso de agente posterior no puede volver a definir los permisos de una conversación a la que se unió. Cualquier otra forma de escribir cualquiera de las dos claves se ignora.
Estado y flujo de datos
Sección titulada «Estado y flujo de datos»Los pasos no se llaman entre sí. Leen y escriben en una bolsa de estado
compartida de la ejecución: un paso lee sus entradas por clave, se ejecuta y
vuelve a escribir su salida bajo su propio outputKey. Un StateReducer
combina las escrituras concurrentes cuando ramas paralelas apuntan a la misma
clave, y un TemplateRenderer sustituye los marcadores {{...}} en prompts y
scripts a partir de ese mismo estado.
Por eso un pipeline se puede inspeccionar después de los hechos: la bolsa de estado es toda la historia de la ejecución.
Qué inicia una ejecución
Sección titulada «Qué inicia una ejecución»Las filas de PipelineTrigger declaran cuándo se inicia automáticamente una
plantilla. Hay cuatro tipos:
| Disparador | Inicia una ejecución cuando |
|---|---|
| Evento de dominio | Se dispara un evento de dominio coincidente, por ejemplo PullRequestPublished o TicketAssigned |
| Programación | Vence una expresión de cron o de intervalo |
| Manual | La ejecutas a mano; la presencia de un disparador manual habilitado es lo que pone la plantilla en el selector de ejecuciones |
| Webhook | Un sistema externo hace un POST a /webhooks/<token> en el puerto HTTP del servidor |
Los disparadores de eventos son un conjunto cerrado: el despachador conoce 16
tipos de evento y los ofrece en el selector
(EventPayloadMapper.knownEventTypes), y nada más puede iniciar un pipeline.
Catorce de esos 16 tienen un mapeo de carga útil. Dos —TicketCreated y
TicketStatusChanged— no tienen ninguno, así que un disparador sobre cualquiera
de los dos queda habilitado y nunca se dispara. Consulta
Eventos de dominio para ver la
lista completa.
El token del webhook también hace las veces de secreto HMAC: el encabezado
X-Hub-Signature-256: sha256=<hex> de la solicitud se verifica contra
HMAC-SHA256(body, token), las entregas sin verificar se rechazan y no se
pueden reproducir, y los duplicados se descartan mediante dedupe_key. Como las
filas de disparadores son únicas por (espacio de trabajo, tipo de evento,
plantilla), una plantilla puede tener como máximo un disparador de webhook y un
disparador por cada tipo de evento de dominio.
Las plantillas integradas se crean con sus disparadores manuales y de eventos habilitados; los disparadores programados se crean deshabilitados, así que un barrido recurrente siempre es opcional.
Las plantillas que se entregan deshabilitadas
Sección titulada «Las plantillas que se entregan deshabilitadas»Se siembran trece plantillas de pipeline en cada espacio de trabajo y ocho de
ellas se entregan deshabilitadas: external_pr_welcome, cross_review,
ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit y
pr_digest.
Esta es la parte que sorprende a la gente. Sus disparadores se crean
habilitados, pero PipelineEngine.start rechaza una plantilla deshabilitada y
devuelve null, así que el disparador se activa hacia la nada y no aparece
ninguna ejecución. La plantilla, no el disparador, es el interruptor.
Habilítala en Ajustes → Espacio de trabajo → Plantillas de pipeline antes de
esperar que alguna de esas ocho haga algo.
Volver a sembrar un espacio de trabajo conserva lo que hayas elegido: tu
configuración de isEnabled y tus filas de disparadores existentes se
mantienen. Hay exactamente una excepción deliberada: la programación diaria
de pr_merged_cleanup se habilita a la fuerza aunque antes estuviera
desactivada, porque es recolección de basura. Las filas de worktree, las
particiones del grafo de código y las copias copy-on-write se acumulan sin ella,
y eso nunca debió ser una decisión del usuario.
Otra asimetría que vale la pena conocer: la plantilla integrada pr_review
deliberadamente ya no publica en GitHub. Publicar una revisión es un paso
aparte que requiere aprobación del usuario, y un pipeline que publicara
directamente duplicaría la publicación. cross_review, ticket_to_pr y
pr_triage sí siguen publicando un comentario directamente en GitHub cuando se
ejecutan.
Lo que el motor sabe
Sección titulada «Lo que el motor sabe»PipelineEngine crea el registro de la ejecución, programa el nodo de inicio
que corresponde al tipo de evento de la ejecución y luego recorre el DAG: a
medida que los pasos se completan, evalúa los disparadores posteriores, resuelve
las bifurcaciones del router, espera en las uniones, aplica políticas de
reintento y de continuar en caso de fallo, y persiste el estado, el costo y los
errores en cada paso. Las aristas que salen de otro inicio se ignoran en esa
ejecución, así que el disparo de una programación no espera una condición que
solo es manual.
Lo que no sabe es nada sobre tickets, despacho, GitHub ni mensajería. Su única
costura de extensión es el PipelineBodyRegistry. Los cuerpos registrados ahí
son los que contienen AgentDispatchPort, MessagingPort y sus similares; por
eso agregar una capacidad a los pipelines nunca implica cambiar el planificador.
Manejo de errores
Sección titulada «Manejo de errores»Un paso que falla registra su mensaje de error y pasa a failed. Si el paso
declara continueOnFail, los pasos posteriores continúan y el error se guarda
en el estado de la ejecución; de lo contrario, la ejecución falla. Un paso con
una StepRetryPolicy se reintenta primero, con retroceso lineal o exponencial,
antes de que se aplique cualquiera de los dos casos anteriores.
Ver también
Sección titulada «Ver también»- Eventos de dominio: lo que escucha un disparador de eventos
- Orquestación: un plan que apruebas, compilado en uno de estos pipelines
- Tickets y delegación: lo que crean y completan los pasos de pipeline
- Tipos de pasos de pipeline: el catálogo completo de tipos, cuerpos y configuración
- Crea tu primer pipeline
- Crear una plantilla de pipeline
- Ejecutar un pipeline manualmente
- Monitorear ejecuciones de pipeline
- Configurar disparadores de pipeline