Pipelines en automatisering
Pipelines zijn de deterministische helft van Control Center. Een agent beslist wat er gezegd wordt; een pipeline beslist wat er daarna gebeurt, elke keer op dezelfde manier. Een pipeline is een DAG van stappen die agents kan dispatchen, scripts kan uitvoeren, op een voorwaarde kan vertakken, over een verzameling kan uitwaaieren en gegevens tussen stappen kan doorgeven — vastgelegd, hervatbaar en met kosten bijgehouden.
Ze bestaan om drie redenen: om een repetitief proces met meerdere stappen aan elkaar te rijgen, om meerdere agents na elkaar of parallel te coördineren en om te reageren op iets dat gebeurde zonder dat er een mens aanwezig was.
Sjablonen en runs
Sectie getiteld “Sjablonen en runs”Een sjabloon (PipelineDefinition) is de declaratieve DAG: stappen,
gedeclareerde invoer en configuratie. Het staat in de eigen database van de
werkruimte. Een run (PipelineRun) is één uitvoering van dat sjabloon, met een
muteerbare statusverzameling, kosten- en tokentotalen en de status per stap.
Eén sjabloon heeft veel runs. Runs overleven een herstart: de engine laadt lopende runs opnieuw en pakt ze op waar ze gebleven waren.
Twee vangnetten laten de hele run bewust mislukken in plaats van een stap te hervatten, omdat hervatten erger zou zijn dan stoppen:
- Een stap die langer dan
suspendedStepTimeout(standaard 24 uur) opsuspendedstaat, wacht op tickets die nooit een eindtoestand bereiken, en wordt daarom op mislukt gezet om de run vrij te maken. De uitzondering isextras['awaitApproval'] == true— dat is de poort voor gedeeltelijke goedkeuringorchestration.awaitApproval.human.gatevalt niet onder de uitzondering. - Een stap die is gedeclareerd als
extras: {'idempotent': false}en nog liep toen de server stopte, heeft zijn neveneffect misschien al toegepast (een squash-merge, een worktree-opruiming). Opnieuw uitvoeren zou het dubbel kunnen toepassen, dus zet de engine de stap op mislukt en laat hij de herkansing aan jou over.
Het kind bepaalt de planning, de body doet het werk
Sectie getiteld “Het kind bepaalt de planning, de body doet het werk”Een stap heeft twee orthogonale delen, en juist doordat die gescheiden blijven, kan de engine klein blijven.
Het kind (StepKind) vertelt de engine wanneer een stap uitvoerbaar wordt ten
opzichte van zijn voorgaande stappen — dat is het enige wat de scheduler hoeft te
weten. Er zijn er zes: trigger, listen, join, router, forEach en
terminal. De body (bodyKey) is het werk dat de stap uitvoert zodra hij is
ingepland. Bodies zijn closures die bij het starten van de server op sleutel worden
geregistreerd, zodat veel stappen van verschillende kinds één body kunnen delen:
bijvoorbeeld meerdere listen-stappen die allemaal conversation.promptAgent
uitvoeren.
Omdat de planningssemantiek in zes kinds zit, kan de catalogus met gedragingen groeien zonder dat iemand de scheduler aanraakt. Zie Pipeline-stapsoorten voor de volledige catalogus van kinds, bodies en hun configuratie.
Twee gevolgen van het registerontwerp zijn de moeite waard om te kennen voordat je met de hand een sjabloon schrijft:
- Een stap waarvan de
bodyKeyniet is geregistreerd, geeft een waarschuwing bij het laden van het sjabloon en wordt daarna nooit uitgevoerd. Tijdens runtime mislukt er niets luidruchtig.trigger- enterminal-stappen vallen niet onder die waarschuwing: de trigger voert nog steeds de no-op-bodypipeline.triggeruit, zodat luisteraars stroomafwaarts worden geactiveerd; eenterminal-stap heeft geen body en is een sentinel voor de scheduler. - Sleutels in de
extras-map van een node moeten exact overeenkomen en worden anders stil genegeerd. Alleenextras['spaceId']plaatst het werk van een agentstap in een bestaand gesprek.extras['mode'](chat/plan/review) wordt gelezen op de node die de ruimte opent (messaging.createSpace, standaardreview) — een latere agentstap kan de rechten van een gesprek waaraan hij is toegetreden niet opnieuw instellen. Elke andere spelling van een van beide sleutels wordt genegeerd.
Status en gegevensstroom
Sectie getiteld “Status en gegevensstroom”Stappen roepen elkaar niet aan. Ze lezen van en schrijven naar een gedeelde
statusverzameling op de run: een stap leest zijn invoer op sleutel, voert uit en
schrijft zijn uitvoer terug onder zijn eigen outputKey. Een StateReducer voegt
gelijktijdige schrijfacties samen wanneer parallelle takken dezelfde sleutel raken,
en een TemplateRenderer vervangt {{...}}-placeholders in prompts en scripts door
waarden uit diezelfde status.
Daarom kun je een pipeline achteraf inspecteren — de statusverzameling vertelt het hele verhaal van de run.
Wat een run start
Sectie getiteld “Wat een run start”Rijen in PipelineTrigger verklaren wanneer een sjabloon automatisch start. Er zijn
vier soorten:
| Trigger | Start een run wanneer |
|---|---|
| Domeingebeurtenis | Een overeenkomende domeingebeurtenis treedt op — bijvoorbeeld PullRequestPublished of TicketAssigned |
| Schema | Een cron- of intervaluitdrukking is aan de beurt |
| Handmatig | Je voert de pipeline met de hand uit; doordat er een ingeschakelde handmatige trigger is, verschijnt het sjabloon in de runkiezer |
| Webhook | Een extern systeem doet een POST naar /webhooks/<token> op de HTTP-poort van de server |
Gebeurtenistriggers vormen een gesloten verzameling: de dispatcher kent 16
gebeurtenistypen en biedt ze aan in de kiezer (EventPayloadMapper.knownEventTypes)
en niets anders kan een pipeline starten. Veertien van die 16 hebben een
payload-mapping. Twee ervan — TicketCreated en TicketStatusChanged — hebben er
geen, dus een trigger op een van beide staat ingeschakeld en gaat nooit af. Zie
Domeingebeurtenissen voor de
volledige lijst.
Het webhooktoken doet tegelijk dienst als het HMAC-geheim: de header
X-Hub-Signature-256: sha256=<hex> van het verzoek wordt geverifieerd tegen
HMAC-SHA256(body, token), niet-geverifieerde leveringen worden geweigerd en
kunnen niet opnieuw worden afgespeeld, en duplicaten worden weggegooid op
dedupe_key. Omdat triggerrijen uniek zijn op (werkruimte, gebeurtenistype,
sjabloon), kan een sjabloon hooguit één webhooktrigger en één trigger per type
domeingebeurtenis bevatten.
De ingebouwde sjablonen worden gezaaid met hun handmatige en gebeurtenistriggers ingeschakeld en hun schematriggers uitgeschakeld, dus een terugkerende sweep zet je altijd zelf aan.
De sjablonen die uitgeschakeld worden geleverd
Sectie getiteld “De sjablonen die uitgeschakeld worden geleverd”In elke werkruimte worden dertien pipeline-sjablonen gezaaid, en acht daarvan
worden uitgeschakeld geleverd: external_pr_welcome, cross_review,
ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit en
pr_digest.
Dit is het deel dat mensen verrast. Hun triggers worden ingeschakeld gezaaid,
maar PipelineEngine.start weigert een uitgeschakeld sjabloon en geeft null terug —
dus de trigger gaat af in het niets en er verschijnt geen run. Het sjabloon is de
schakelaar, niet de trigger. Zet het aan bij Instellingen → Werkruimte →
Pipelinesjablonen voordat je van die acht ook maar iets verwacht.
Het opnieuw zaaien van een werkruimte behoudt wat je hebt gekozen: je
isEnabled-instelling en je bestaande triggerrijen blijven bestaan. Er is precies
één bewuste uitzondering — het dagelijkse schema van pr_merged_cleanup wordt
geforceerd ingeschakeld, zelfs als het eerder uit stond, omdat het
garbagecollection is. Zonder dat schema stapelen worktree-rijen, partities van de
codegraaf en copy-on-write-kopieën zich op, en dat was nooit bedoeld als keuze voor
de gebruiker.
Nog een asymmetrie die je moet kennen: de ingebouwde pr_review plaatst bewust
niet langer berichten op GitHub. Een review publiceren is een aparte stap waarvoor
de gebruiker goedkeuring moet geven, en een pipeline die rechtstreeks zou plaatsen,
zou dubbel publiceren. cross_review, ticket_to_pr en pr_triage plaatsen bij
het draaien nog wel rechtstreeks een opmerking op GitHub.
Wat de engine weet
Sectie getiteld “Wat de engine weet”PipelineEngine maakt het runrecord aan, plant het startknooppunt in dat bij het
gebeurtenistype van de run hoort, en loopt daarna de DAG door: naarmate stappen klaar
zijn, evalueert hij triggers stroomafwaarts, lost hij routertakken op, wacht hij op
joins, past hij herkansingsbeleid en doorgaan-bij-mislukken toe, en legt hij status,
kosten en fouten bij elke stap vast. Randen die vanaf een andere start vertrekken,
worden voor die run genegeerd, dus een schema-afvuring wacht niet op een voorwaarde
die alleen voor handmatige runs geldt.
Wat hij niet weet, is iets over ticketing, dispatch, GitHub of messaging. De enige
uitbreidingsnaad is de PipelineBodyRegistry. De bodies die daar zijn
geregistreerd, bevatten AgentDispatchPort, MessagingPort en hun soortgenoten —
daarom betekent een nieuwe mogelijkheid aan pipelines toevoegen nooit dat je de
scheduler moet aanpassen.
Foutafhandeling
Sectie getiteld “Foutafhandeling”Een mislukkende stap legt zijn foutmelding vast en gaat naar failed. Als de stap
continueOnFail declareert, gaan de stappen stroomafwaarts verder en wordt de fout
in de runstatus bewaard; anders mislukt de run. Een stap met een StepRetryPolicy
wordt eerst opnieuw geprobeerd, met lineaire of exponentiële backoff, voordat een
van beide van toepassing is.
Zie ook
Sectie getiteld “Zie ook”- Domeingebeurtenissen: waar een gebeurtenistrigger naar luistert
- Orkestratie: een plan dat je goedkeurt en dat wordt samengesteld tot een van deze pipelines
- Tickets en delegatie: wat pipeline-stappen aanmaken en afronden
- Pipeline-stapsoorten: de volledige catalogus van kinds, bodies en configuratie
- Je eerste pipeline bouwen
- Een pipeline-sjabloon maken
- Een pipeline handmatig uitvoeren
- Pipeline-runs monitoren
- Pipeline-triggers instellen