Zum Inhalt springen

Pipeline-Schrittarten

Ein Pipeline-Schritt hat zwei unabhängige Achsen:

  • Art (StepKind) — wie die Engine den Schritt relativ zu seinen Triggern einplant.
  • Body-Schlüssel (BuiltInBodyKeys) — welche registrierte Body-Closure die Arbeit des Schritts ausführt.

Definiert in packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.

Art Semantik des Einplanens
trigger Pflicht-Einstiegspunkt — genau einer je Vorlage, immer der erste Knoten. Erledigt selbst keine Arbeit; was den Lauf startet (manuell / Ereignis / Zeitplan), wird getrennt als Zeilen PipelineTrigger geführt. Sein Body ist der No-op pipeline.trigger.
listen Feuert, wenn alle Quellschritte (in StepTrigger.sourceStepIds) abgeschlossen sind.
join Feuert, wenn alle Schritte in waitForStepIds einen Endzustand erreichen.
router Bedingte Verzweigung — der Body liefert StepResult.route(key), und nur die nachgelagerte Kante feuert, deren routeKey passt; nicht gewählte Zweige werden als übersprungen markiert.
forEach Eingeplant wie listen (feuert, wenn alle sourceStepIds abgeschlossen sind). Der registrierte Body flow.forEach ist es, der iteriert; die Engine legt nicht je Eintrag einen Kindschritt an.
terminal Sentinel des Schedulers ohne Body. Der Pipeline-Lauf ist abgeschlossen, wenn ein terminaler Schritt endet.

Registrierte Body-Closures, adressiert über die Konstanten in BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). Es sind 25, alle unten aufgeführt. Die Konfiguration wird aus der PipelineNodeConfig des Schritts gelesen (siehe unten), sofern nichts anderes steht; „Zustandsschlüssel“ werden zur Ausführungszeit aus dem Pipeline-Zustand und der Trigger-Payload gelesen.

Ein Schritt, dessen bodyKey nicht registriert ist, erzeugt beim Laden der Vorlage eine Warnung und wird danach nie ausgeführt — der Lauf scheitert daran nicht. Schritte der Art trigger und terminal sind von dieser Warnung ausgenommen: Der Trigger führt trotzdem den No-op-Body pipeline.trigger aus; ein Schritt der Art terminal hat keinen Body.

No-op-Body des Pflichtknotens trigger — endet sofort, damit die Engine zu den nachgelagerten Listenern auffächert. Keine Konfiguration.

Shell-Schritt ohne Agent (bash -c). Platzhalter {{key}} im Skript werden aus Zustand und Trigger-Payload durch für die Shell maskierte Werte ersetzt.

Konfigurationsfeld Beschreibung
script Auszuführender Shell-Befehl (erforderlich)
outputKey Zustandsschlüssel, der bei Exit 0 das getrimmte stdout erhält

Läuft mit dem cwd <cc_root>/pipelines/<pipelineRunId>/ (von allen Bash-Schritten eines Laufs geteilt) und GITHUB_TOKEN in der Umgebung. Schreibt außerdem <stepId>_runDir in den Zustand. Ein Exit ungleich null lässt den Schritt mit dem Ende von stderr scheitern.

Allgemeiner Knoten für Prompt und Dispatch: rendert den Prompt, schickt den Agenten in eine Unterhaltung und hält an, bis der Lauf fertig ist. Die Payload submit_output des Agenten wird unter outputKey in den Zustand übernommen.

Konfigurationsfeld Beschreibung
agentId UUID des Agenten im Arbeitsbereich, der losgeschickt wird (erforderlich)
prompt Anweisungstext; unterstützt die Ersetzung von {{key}} (erforderlich)
outputKey Zustandsschlüssel für die übernommene Ausgabe
outputSchema JSON Schema, das die Ausgabe erfüllen muss
label Sichtbare Bezeichnung auf der Zeichenfläche
extras.spaceId Bestehender Bereich, in dem gearbeitet wird (Vorgabe: ein versteckter)
extras.conversationTitle Benannte Unterhaltung in diesem Bereich (optional)
extras.mode Wird am Knoten gelesen, der den Bereich öffnet (messaging.createSpace), nicht hier. Vorgabe review.

Router-Body — liest config.extras, wertet eine Bedingung aus und liefert StepResult.route(key). Bedingungen sind keine JavaScript-Ausdrücke; drei Schreibformen werden unterstützt, in dieser Priorität:

  • Prädikatbaum (extras.predicate) — ein boolescher Baum, der "true" / "false" routet. Jeder Knoten ist eine Map mit einem type:
    • {"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false} — wahr, wenn irgendein aufgeführter Pfad auf der Platte existiert; negate kehrt das Ergebnis um; recursive durchsucht auch Unterverzeichnisse (und überspringt .git, node_modules, build, .dart_tool). Relative Pfade lösen gegen state[baseKey] auf (Vorgabe repo_local_path), sonst gegen das Arbeitsverzeichnis des Laufs.
    • {"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80} — Operatoren equals, notEquals, contains, exists, notExists, gt, lt; liest den Pipeline-Zustand, nicht das Dateisystem.
    • {"type": "and" | "or", "of": [<predicate>, ...]} — boolesche Gruppen.
    • {"type": "not", "of": <predicate>} — Negation.
  • Verzweigung (extras.switchKey) — {"switchKey": "pr_class", "cases": [...], "default": "standard"}; routet zum ersten Fall, den der Zustandswert ohne Beachtung der Groß- und Kleinschreibung enthält, sonst default.
  • Vergleich (älteres extras.left / extras.op / extras.right auf oberster Ebene) — entspricht einem Prädikat comparison, behalten für Vorlagen, die vor dem Baum geschrieben wurden.

Ausgehende Kanten des Knotens tragen einen routeKey, der zum gelieferten Schlüssel passen muss.

Postet die zusammengeführten Befunde als PR-Review-Kommentar über den GitHub-PR-Client.

Zustandsschlüssel Beschreibung
repo_full_name owner/repo (erforderlich)
pr_number PR-Nummer — int oder numerischer String (erforderlich)
consolidated_findings Kommentartext (erforderlich)

Liefert StepResult.terminal mit comment_review_id und commented_at im Zustand.

Postet eine Nachricht in einen Nachrichtenbereich über MessagingPort.

Zustandsschlüssel Beschreibung
space_id Zielbereich (erforderlich)
content Nachrichtentext, wortgetreu aus dem Zustand gelesen (erforderlich)

Schreibt posted_space_id und posted_at in den Zustand.

Schickt ein ganzes Team los statt eines einzelnen Agenten und hält dann an, bis die Läufe der Mitglieder fertig sind.

Konfigurationsfeld Beschreibung
teamId Team im Arbeitsbereich, das losgeschickt wird (erforderlich; schließt sich mit agentId aus)
prompt Zieltext; unterstützt die Ersetzung von {{key}} (erforderlich)
dispatchMode allParallel (Vorgabe — eine Aufgabe je Mitglied) oder manager (schickt nur die Leitung los, damit sie koordiniert)
outputKey Zustandsschlüssel für die übernommenen Ausgaben der Mitglieder
outputSchema, label, extras.spaceId Wie bei conversation.promptAgent

Freigabetor. Schickt den Genehmiger-Agenten mit dem Prompt des Tors und einem Ausgabevertrag {decision, reason} in eine Unterhaltung (Modus review) und hält an, bis der Genehmiger über submit_output einreicht.

Konfigurationsfeld Beschreibung
agentId Der Genehmiger-Agent (erforderlich)
prompt Prompt des Tors (optional; fehlt er, gilt eine Vorgabe)
outputKey Zustandsschlüssel für die übernommene Payload {decision, reason}
outputSchema Vorgabe ist ein Freigabeschema (decision: approved / rejected, reason)
label, extras.spaceId Wie bei conversation.promptAgent

Das Tor löst sich nur über submit_output auf. BuiltInBodyKeys.humanGate, die Knotenbibliothek und der Prompt-Text einer mitgelieferten Vorlage nennen alle die MCP-Tools approve_step und reject_step; keines der beiden Tools existiert in irgendeinem Paket oder irgendeiner Registry.

Entfernt veraltete isolierte Worktrees und wählt den Modus aus der Trigger-Payload. Beachtet das Flag dryRun des Laufs.

Zustandsschlüssel Beschreibung
ticket_id Gibt die Worktrees dieses Tickets frei
repo_full_name + pr_number Gibt den flüchtigen Worktree des PR-Editors frei
space_id Räumt die Worktrees und den Ordner dieses Bereichs ab
(keines davon) Fegt den Arbeitsbereich: verschwundene Verzeichnisse, tote Bereiche, verwaiste Unterhaltungsordner

outputKey (optional) erhält einen Zusammenfassungstext.

Abbildung / Auffächerung über eine Zustandssammlung: rendert den Prompt einmal je Eintrag, schickt den Agenten dann einmal mit diesen aneinandergereihten Prompts los (Trenner ---) und hält an, bis dieser Lauf fertig ist. Eine leere Sammlung endet sofort mit einer leeren Liste. Es ist kein paralleler Versand je Eintrag.

Konfigurationsfeld Beschreibung
agentId Agent, der je Eintrag losgeschickt wird (erforderlich)
prompt Prompt je Eintrag; unterstützt die Ersetzung von {{key}} (erforderlich)
extras.iterableKey Zustandsschlüssel, der die Sammlung hält (erforderlich)
extras.itemKey Schlüssel, an den der aktuelle Eintrag im Prompt gebunden ist (Vorgabe item)
outputKey Zustandsschlüssel für die übernommene Ausgabe (ein Versand, keine Liste)

Führt eine andere Pipeline-Vorlage als verschachtelten Teilschritt aus (die Teilpipeline läuft als Kindlauf mit gesetztem parentPipelineRunId). Der Schritt hält an, bis das Kind einen Endzustand erreicht; danach wird der Endzustand des Kindes unter outputKey zusammengeführt.

Konfigurationsfeld Beschreibung
extras.templateId Auszuführende Kindvorlage (erforderlich; eine Pipeline kann sich nicht selbst aufrufen)
inputKeys Zustandsschlüssel, die in die Trigger-Payload des Kindes kopiert werden
outputKey Zustandsschlüssel für den zusammengeführten Endzustand des Kindes

Tree-sitter-Indexierung im Hintergrund: geht ein Repo ab, extrahiert Symbole und Kanten in Worker-Isolates und nimmt sie in den Code-Graph des Arbeitsbereichs auf. Streamt den Fortschritt in die Zeile des Schrittlaufs; unterstützt den Trockenlauf. Tree-sitter und seine Grammatiken sind erforderliche Natives — fehlt eine dylib, ist der Server nie gestartet, und eine Grammatik, die sich mitten im Lauf nicht auflösen lässt, lässt den Schritt scheitern, statt ihn zu überspringen.

Zustandsschlüssel Beschreibung
repo_id Zu indexierendes Repo (erforderlich)
repo_local_path Lokaler Pfad des Checkouts (erforderlich)

Offline-Diarisierung der Sprecher. Liest das aufbewahrte Audio der Besprechung, clustert es in Sprecher (Person 1, Person 2, …), beschriftet die Transkriptsegmente neu und schreibt den Zustand transcript neu; gibt außerdem diarization_spans für den parallelen Schritt meeting.updateTranscript aus. Gibt das Transkript unverändert weiter, wenn kein Audio aufbewahrt wurde oder die Modelle nicht installiert sind.

Zustandsschlüssel Beschreibung
meeting_id Zu diarisierende Besprechung (erforderlich)

meeting.saveNotes, meeting.addActionItems, meeting.addDecisions

Abschnitt betitelt „meeting.saveNotes, meeting.addActionItems, meeting.addDecisions“

Deterministische Bodies, die die Besprechungszusammenfassung speichern. Jeder liest meeting_id und die strukturierte Payload meeting_outcome des Agentenschritts ({summary, enhancedNotes, actionItems[], decisions[]}) aus dem Zustand und schreibt seinen Teil in die eigene Tabelle — Notizen, Aufgaben oder Entscheidungen. Zuvor gespeicherte Zeilen werden bei einem eingeschränkten Lauf nie gelöscht.

Body-Schlüssel Beschreibung
meeting.identifySpeakers Sprechererkennung über Besprechungen hinweg: gleicht diarisierte Sprecher mit gespeicherten Stimmprofilen ab und übernimmt sichere Treffer automatisch. No-op ohne Profile oder Embeddings.
meeting.updateTranscript Wendet die Bezeichnungen Person N aus diarization_spans an, führt die Fragmente je Fenster zu Redebeiträgen je Sprecher zusammen und speichert dann das bereinigte Transkript. No-op, wenn die Diarisierung nichts geliefert hat.
meeting.assemblePlayback Setzt die gemischte Wiedergabespur der Besprechung aus den aufbewahrten WAVs je Kanal zusammen. No-op, wenn kein Audio aufbewahrt wurde.
orchestration.markPhase Schreibt am Ende des Arbeits-DAG Fehler-Sentinels für untergeordnete Tickets ohne Ausgabe und stellt die Orchestrierung auf synthesizing.
orchestration.persistDeliverable Schreibt die Ausgabe der Synthese auf das übergeordnete Ticket, schließt es ab, postet das Lieferergebnis und markiert die Orchestrierung als completed.
orchestration.awaitApproval Tor für die teilweise Freigabe: endet sofort, wenn der Knotenschlüssel des Schritts in der freigegebenen Menge der Orchestrierung liegt, sonst hält er an, bis eine spätere Freigabe ihn fortsetzt. Das ist der einzige Body, der extras.awaitApproval setzt und ihn damit vom 24-Stunden-Timeout für angehaltene Schritte ausnimmt.
messaging.createSpace Öffnet den Bereich, in dem der Rest der Vorlage arbeitet, veröffentlicht unter dem outputKey des Schritts (mitgelieferte Vorlagen nutzen pipeline_space_id). Eine leere Liste repoIds checkt nichts aus; extras['allRepos'] checkt jedes Repo des Arbeitsbereichs aus. extras['createConversation'] (standardmäßig aus) öffnet zusätzlich eine benannte Unterhaltung. Ein Agentenschritt BETRITT einen Bereich und öffnet nie einen, deshalb scheitert ein Auffächern ohne diesen Knoten beim Dispatch — und ihn einmal zu verdrahten heißt, dass das ganze Auffächern sich einen Checkout teilt, statt je einen bereitzustellen.
prReview.finalize Der letzte Schritt der PR-Review-Vorlage: sortiert die angesammelten Review-Knoten in Konsensgruppen und berechnet das Urteil je PR.
skills.analyze Führt den Supply-Chain-Scan über die installierten Skills eines Arbeitsbereichs aus und zeichnet jedes Urteil auf.

Ein Schritt-Body liefert ein StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Alle Fabriken nehmen eine optionale Map mutatedState entgegen, die in den Pipeline-Zustand gemischt wird, außer failed.

Fabrik Signatur Beschreibung
ok StepResult.ok({mutatedState}) Normaler Abschluss; nachgelagerte Listener werden ausgewertet
route StepResult.route(nextRouterKey, {mutatedState}) Abschluss eines Routers; wählt den nachgelagerten Zweig, dessen routeKey passt
suspendUntilEvent StepResult.suspendUntilEvent(eventType, {mutatedState}) Pause, bis ein bestimmter Typ von Domain-Ereignis feuert
suspendUntilTasksComplete StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) Pause, bis alle aufgeführten Aufgaben einen Endzustand erreichen
terminal StepResult.terminal({mutatedState}) Der Pipeline-Lauf ist fertig — keine weiteren Schritte
failed StepResult.failed(errorMessage) Der Schritt ist mit einer Fehlermeldung gescheitert

Wiederholungsverhalten eines fehlschlagenden Knoten-Bodys (PipelineNodeConfig.retryPolicy; null schaltet Wiederholungen ab).

Feld Typ Beschreibung
maxAttempts int Versuche insgesamt einschließlich des ersten (3 = 1 Versuch + 2 Wiederholungen); muss ≥ 1 sein
backoff String linear oder exponential (Vorgabe)
initialDelayMs int Wartezeit vor der ersten Wiederholung, in Millisekunden; spätere Wartezeiten wachsen gemäß backoff

Konfiguration je Knoten in der Schrittdefinition (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). Mitgelieferte und eigene Knoten teilen diese Form; jeder Body liest nur die Felder, die ihn angehen.

Feld Typ Beschreibung
prompt String? Prompt-Vorlage mit Ersetzung von {{key}}
script String? Bash-Skript mit Ersetzung von {{key}}
agentId String? UUID des Agenten im Arbeitsbereich, der losgeschickt wird
inputKeys List<String> Zustandsschlüssel, die dieser Knoten als Eingabe liest
repoIds List<String> Repos, die der Knoten messaging.createSpace auscheckt (<repoId> oder <repoId>@<branch>). Leer = keines; extras['allRepos'] = jedes Repo des Arbeitsbereichs. An Agentenschritten ignoriert.
outputKey String? Zustandsschlüssel, unter den die Ausgabe dieses Knotens geschrieben wird
label String? Sichtbare Bezeichnung auf der Zeichenfläche (Vorgabe: die Schritt-ID)
outputSchema Map<String, dynamic>? JSON Schema (Teilmenge), das der Ausgabewert erfüllen muss
reducer String? Zusammenführungsstrategie, wenn parallele Zweige denselben outputKey schreiben: append, mergeLists, mergeMaps, sum oder override (Vorgabe)
retryPolicy StepRetryPolicy? Wiederholungsrichtlinie bei einem Fehler des Bodys; null = ein Versuch
continueOnFail bool Ist der Wert wahr, lässt ein endgültiges Scheitern den Lauf nicht scheitern; der Fehler liegt unter state['_stepErrors'][stepId]
timeoutMs int? Wanduhr-Timeout des Knoten-Bodys in Millisekunden; null = kein Timeout
teamId String? Loszuschickendes Team (Knoten team.dispatch); schließt sich mit agentId aus
dispatchMode String? Ausführungsmodus des Teams: allParallel oder manager
extras Map<String, dynamic> Freie, zum Body gehörende Konfiguration (z. B. iterableKey, templateId, Bedingungsbäume)

Abgeleitete Zugriffe: spaceId (extras['spaceId'] — bestehender Bereich, in dem gearbeitet wird), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, gelesen am Knoten, der den Bereich öffnet) und runWhen (extras['runWhen'] — überspringt den Knoten, wenn die Bedingung nicht erfüllt ist).