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.
Schrittarten
Abschnitt betitelt „Schrittarten“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. |
Schritt-Bodies
Abschnitt betitelt „Schritt-Bodies“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.
pipeline.trigger
Abschnitt betitelt „pipeline.trigger“No-op-Body des Pflichtknotens trigger — endet sofort, damit die Engine zu den nachgelagerten Listenern auffächert. Keine Konfiguration.
pipeline.bashScript
Abschnitt betitelt „pipeline.bashScript“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.
conversation.promptAgent
Abschnitt betitelt „conversation.promptAgent“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. |
pipeline.condition
Abschnitt betitelt „pipeline.condition“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 einemtype:{"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false}— wahr, wenn irgendein aufgeführter Pfad auf der Platte existiert;negatekehrt das Ergebnis um;recursivedurchsucht auch Unterverzeichnisse (und überspringt.git,node_modules,build,.dart_tool). Relative Pfade lösen gegenstate[baseKey]auf (Vorgaberepo_local_path), sonst gegen das Arbeitsverzeichnis des Laufs.{"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80}— Operatorenequals,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, sonstdefault. - Vergleich (älteres
extras.left/extras.op/extras.rightauf oberster Ebene) — entspricht einem Prädikatcomparison, behalten für Vorlagen, die vor dem Baum geschrieben wurden.
Ausgehende Kanten des Knotens tragen einen routeKey, der zum gelieferten Schlüssel passen muss.
prReview.comment
Abschnitt betitelt „prReview.comment“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.
messaging.postSpace
Abschnitt betitelt „messaging.postSpace“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.
team.dispatch
Abschnitt betitelt „team.dispatch“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 |
human.gate
Abschnitt betitelt „human.gate“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.
repos.cleanup
Abschnitt betitelt „repos.cleanup“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.
flow.forEach
Abschnitt betitelt „flow.forEach“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) |
flow.callPipeline
Abschnitt betitelt „flow.callPipeline“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 |
code.index
Abschnitt betitelt „code.index“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) |
meeting.diarize
Abschnitt betitelt „meeting.diarize“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.
Weitere eingebaute Bodies
Abschnitt betitelt „Weitere eingebaute Bodies“| 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. |
Schrittergebnisse
Abschnitt betitelt „Schrittergebnisse“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 |
StepRetryPolicy
Abschnitt betitelt „StepRetryPolicy“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 |
PipelineNodeConfig
Abschnitt betitelt „PipelineNodeConfig“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).