Przejdź do głównej zawartości

Kroki potoku

Krok potoku ma dwie niezależne osie:

  • Rodzaj (StepKind) — jak silnik szereguje krok względem jego wyzwalaczy.
  • Klucz ciała (BuiltInBodyKeys) — które zarejestrowane domknięcie ciała wykonuje pracę kroku.

Zdefiniowane w packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.

Rodzaj Semantyka szeregowania
trigger Obowiązkowy punkt wejścia — dokładnie jeden na szablon, zawsze pierwszy węzeł. Sam nie wykonuje żadnej pracy; to, co rozpoczyna uruchomienie (ręcznie / zdarzenie / harmonogram), jest śledzone osobno jako wiersze PipelineTrigger. Jego ciało to puste pipeline.trigger.
listen Wyzwala się, gdy wszystkie kroki źródłowe (w StepTrigger.sourceStepIds) zakończą się.
join Wyzwala się, gdy wszystkie kroki w waitForStepIds osiągną stan końcowy.
router Rozgałęzianie warunkowe — ciało zwraca StepResult.route(key), co uruchamia tylko tę dalszą krawędź, której routeKey się zgadza; niewybrane gałęzie są oznaczane jako pominięte.
forEach Szeregowany jak listen (wyzwala się, gdy wszystkie sourceStepIds się zakończą). To zarejestrowane ciało flow.forEach iteruje; silnik nie tworzy po jednym kroku potomnym na element.
terminal Wartownik harmonogramu bez ciała. Uruchomienie potoku kończy się, gdy zakończy się krok terminalny.

Zarejestrowane domknięcia ciał kluczowane stałymi w BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). Jest ich 25 i wszystkie wymieniono poniżej. Konfiguracja jest odczytywana z PipelineNodeConfig kroku (patrz poniżej), o ile nie zaznaczono inaczej; „klucze stanu” są odczytywane ze stanu potoku / ładunku wyzwalacza w momencie wykonania.

Krok, którego bodyKey nie jest zarejestrowany, generuje ostrzeżenie przy wczytywaniu szablonu i nigdy się nie wykonuje — nie powoduje jednak niepowodzenia uruchomienia. Kroki trigger i terminal są zwolnione z tego ostrzeżenia: wyzwalacz nadal wykonuje puste ciało pipeline.trigger, a krok terminal nie ma ciała.

Puste ciało obowiązkowego węzła trigger — kończy się natychmiast, aby silnik rozgałęził pracę do dalszych kroków nasłuchujących. Bez konfiguracji.

Krok powłoki bez agenta (bash -c). Symbole zastępcze {{key}} w skrypcie są podstawiane ze stanu i ładunku wyzwalacza wartościami escapowanymi dla powłoki.

Pole konfiguracji Opis
script Polecenie powłoki do wykonania (wymagane)
outputKey Klucz stanu, który przy kodzie wyjścia 0 otrzymuje przycięty stdout

Działa z cwd <cc_root>/pipelines/<pipelineRunId>/ (wspólnym dla wszystkich kroków bash w danym uruchomieniu) i GITHUB_TOKEN w środowisku. Zapisuje też <stepId>_runDir do stanu. Kod wyjścia inny niż zero powoduje niepowodzenie kroku z końcówką stderr.

Ogólny węzeł promptu i wysyłki: renderuje prompt, wysyła agenta do rozmowy i wstrzymuje się do zakończenia uruchomienia. Ładunek submit_output agenta jest zbierany do stanu pod outputKey.

Pole konfiguracji Opis
agentId UUID agenta z obszaru roboczego do wysłania (wymagany)
prompt Tekst instrukcji; obsługuje podstawianie {{key}} (wymagany)
outputKey Klucz stanu dla zebranego wyniku
outputSchema JSON Schema, który musi spełniać wynik
label Etykieta wyświetlana na kanwie
extras.spaceId Istniejąca rozmowa, w której pracować (domyślnie: ukryta)
extras.conversationTitle Nazwany strumień wewnątrz tej przestrzeni (opcjonalny)
extras.mode Odczytywany na węźle, który otwiera pokój (messaging.createSpace), a nie tutaj. Domyślnie review.

Ciało routera — odczytuje config.extras, ocenia warunek i zwraca StepResult.route(key). Warunki to nie wyrażenia JavaScript; obsługiwane są trzy sposoby ich tworzenia, w kolejności priorytetu:

  • Drzewo predykatów (extras.predicate) — drzewo logiczne, które kieruje do "true" / "false". Każdy węzeł to mapa z polem type:
    • {"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false} — true, gdy dowolna z wymienionych ścieżek istnieje na dysku; negate odwraca wynik; recursive przeszukuje także podkatalogi (pomijając .git, node_modules, build, .dart_tool). Ścieżki względne są rozwiązywane względem state[baseKey] (domyślnie repo_local_path), a w przeciwnym razie względem katalogu obszaru roboczego danego uruchomienia.
    • {"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80} — operatory equals, notEquals, contains, exists, notExists, gt, lt; odczytuje stan potoku, a nie system plików.
    • {"type": "and" | "or", "of": [<predicate>, ...]} — grupy logiczne.
    • {"type": "not", "of": <predicate>} — negacja.
  • Przełącznik (extras.switchKey) — {"switchKey": "pr_class", "cases": [...], "default": "standard"}; kieruje do pierwszego przypadku, który wartość stanu zawiera bez rozróżniania wielkości liter, a w przeciwnym razie do default.
  • Porównanie (starsze, najwyższego poziomu extras.left / extras.op / extras.right) — równoważne predykatowi comparison, zachowane dla szablonów utworzonych przed wprowadzeniem drzewa.

Krawędzie wychodzące z węzła niosą routeKey, który musi się zgadzać ze zwróconym kluczem.

Zamieszcza skonsolidowane ustalenia jako komentarz recenzji PR przez klienta GitHub PR.

Klucz stanu Opis
repo_full_name owner/repo (wymagany)
pr_number Numer PR — liczba całkowita lub ciąg liczbowy (wymagany)
consolidated_findings Treść komentarza (wymagana)

Zwraca StepResult.terminal z comment_review_id i commented_at w stanie.

Wysyła wiadomość do przestrzeni komunikacyjnej przez MessagingPort.

Klucz stanu Opis
space_id Przestrzeń docelowa (wymagana)
content Treść wiadomości, odczytywana dosłownie ze stanu (wymagana)

Zapisuje posted_space_id i posted_at do stanu.

Wysyła cały zespół zamiast pojedynczego agenta, a następnie wstrzymuje się do zakończenia uruchomień członków.

Pole konfiguracji Opis
teamId Zespół z obszaru roboczego do wysłania (wymagany; wyklucza się z agentId)
prompt Tekst celu; obsługuje podstawianie {{key}} (wymagany)
dispatchMode allParallel (domyślnie — jedno zadanie na członka) lub manager (wysyła tylko lidera do koordynacji)
outputKey Klucz stanu dla zebranych wyników członków
outputSchema, label, extras.spaceId Jak w conversation.promptAgent

Bramka zatwierdzenia. Wysyła agenta zatwierdzającego do rozmowy (tryb recenzji) z promptem bramki i kontraktem wyniku {decision, reason}, a następnie wstrzymuje się, aż zatwierdzający zgłosi wynik przez submit_output.

Pole konfiguracji Opis
agentId Agent zatwierdzający (wymagany)
prompt Prompt bramki (opcjonalny; w razie braku używany jest domyślny)
outputKey Klucz stanu dla zebranego ładunku {decision, reason}
outputSchema Domyślnie schemat zatwierdzenia (decision: approved / rejected, reason)
label, extras.spaceId Jak w conversation.promptAgent

Bramka jest rozwiązywana wyłącznie przez submit_output. BuiltInBodyKeys.humanGate, biblioteka typów węzłów oraz tekst promptu jednego z wbudowanych szablonów wymieniają narzędzia MCP approve_step i reject_step; żadne z tych narzędzi nie istnieje w żadnym pakiecie ani rejestrze.

Usuwa nieaktualne izolowane worktree; tryb wybiera na podstawie ładunku wyzwalacza. Respektuje flagę dryRun uruchomienia.

Klucz stanu Opis
ticket_id Zwalnia worktree tego zgłoszenia
repo_full_name + pr_number Zwalnia efemeryczny worktree edytora PR
space_id Usuwa worktree i folder tej rozmowy
(żaden z powyższych) Przeczesuje obszar roboczy: zniknięte katalogi, martwe przestrzenie, osierocone foldery rozmów

outputKey (opcjonalny) otrzymuje ciąg podsumowania.

Mapowanie / fan-out po kolekcji ze stanu: renderuje prompt raz na element, a następnie wysyła agenta raz z tymi promptami połączonymi (separatory ---) i wstrzymuje się do zakończenia tego uruchomienia. Pusta kolekcja kończy się natychmiast z pustą listą. To nie jest jedno równoległe wysłanie na element.

Pole konfiguracji Opis
agentId Agent do wysłania na element (wymagany)
prompt Prompt dla pojedynczego elementu; obsługuje podstawianie {{key}} (wymagany)
extras.iterableKey Klucz stanu przechowujący kolekcję (wymagany)
extras.itemKey Klucz, do którego wiązany jest bieżący element w prompcie (domyślnie item)
outputKey Klucz stanu dla zebranego wyniku (jedno wysłanie, nie lista)

Uruchamia inny szablon potoku jako zagnieżdżony podkrok (podpotok działa jako uruchomienie potomne z ustawionym parentPipelineRunId). Krok wstrzymuje się, aż potomny osiągnie stan końcowy; następnie końcowy stan potomnego jest scalany pod outputKey.

Pole konfiguracji Opis
extras.templateId Szablon potomny do uruchomienia (wymagany; potok nie może wywołać samego siebie)
inputKeys Klucze stanu kopiowane do ładunku wyzwalacza potomnego
outputKey Klucz stanu dla scalonego stanu końcowego potomnego

Indeksowanie tree-sitter w tle: przechodzi repozytorium, wyodrębnia symbole/krawędzie w izolatach workerów i wprowadza je do grafu kodu obszaru roboczego. Strumieniuje postęp do wiersza uruchomienia kroku; obsługuje dry-run. Tree-sitter i jego gramatyki to wymagane natywne biblioteki — brakujący plik dylib oznacza, że serwer nigdy nie wystartował, a gramatyka, której nie da się rozwiązać w trakcie uruchomienia, powoduje niepowodzenie kroku, a nie jego pominięcie.

Klucz stanu Opis
repo_id Repozytorium do zaindeksowania (wymagane)
repo_local_path Ścieżka lokalna checkoutu (wymagana)

Diaryzacja mówców w trybie offline. Odczytuje zachowane audio spotkania, grupuje je w mówców (Person 1, Person 2, …), zmienia etykiety segmentów transkryptu i nadpisuje stan transcript; emituje też diarization_spans dla równoległego kroku meeting.updateTranscript. Przepuszcza transkrypt bez zmian, gdy nie zachowano audio lub modele nie są zainstalowane.

Klucz stanu Opis
meeting_id Spotkanie do diaryzacji (wymagane)

meeting.saveNotes, meeting.addActionItems, meeting.addDecisions

Dział zatytułowany „meeting.saveNotes, meeting.addActionItems, meeting.addDecisions”

Deterministyczne ciała utrwalania podsumowania spotkania. Każde odczytuje ze stanu meeting_id oraz ustrukturyzowany ładunek meeting_outcome kroku agenta ({summary, enhancedNotes, actionItems[], decisions[]}) i zapisuje swoją część do własnej tabeli — notatki, zadania albo decyzje. Wcześniej zapisane wiersze nigdy nie są czyszczone w zdegradowanym uruchomieniu.

Klucz ciała Opis
meeting.identifySpeakers Rozpoznawanie mówców między spotkaniami: dopasowuje zdiatryzowanych mówców do zapisanych profili głosu i automatycznie stosuje pewne dopasowania. Puste ciało bez profili/osadzeń.
meeting.updateTranscript Stosuje etykiety Person N z diarization_spans i scala fragmenty z poszczególnych okien w tury mówców, a następnie utrwala oczyszczony transkrypt. Puste ciało, gdy diaryzacja nic nie wygenerowała.
meeting.assemblePlayback Składa zmiksowaną ścieżkę odtwarzania spotkania z zachowanych plików WAV poszczególnych kanałów. Puste ciało, gdy nie zachowano audio.
orchestration.markPhase Na końcu DAG pracy zapisuje wartowniki niepowodzenia dla podzgłoszeń bez wyniku i przełącza orkiestrację na synthesizing.
orchestration.persistDeliverable Zapisuje wynik syntezy do zgłoszenia nadrzędnego, kończy je, publikuje produkt końcowy i oznacza orkiestrację jako completed.
orchestration.awaitApproval Bramka częściowego zatwierdzenia: kończy się natychmiast, gdy klucz węzła kroku znajduje się w zatwierdzonym zbiorze orkiestracji; w przeciwnym razie wstrzymuje się, aż wznowi ją późniejsze zatwierdzenie. To jedyne ciało, które ustawia extras.awaitApproval, co zwalnia je z 24-godzinnego limitu czasu wstrzymanego kroku.
messaging.createSpace Otwiera rozmowę, w której pracuje reszta szablonu, publikowaną pod outputKey kroku (wbudowane używają pipeline_space_id). Pusta lista repoIds nie tworzy żadnego checkoutu; extras['allRepos'] tworzy checkouty wszystkich repozytoriów obszaru roboczego. extras['createConversation'] (domyślnie wyłączone) otwiera dodatkowo nazwany strumień. Krok agenta DOŁĄCZA do pokoju i nigdy go nie otwiera, więc fan-out bez tego węzła zawodzi przy wysyłce — a podłączenie go raz sprawia, że cały fan-out współdzieli jeden checkout, zamiast tworzyć osobny dla każdego.
prReview.finalize Ostatni krok szablonu recenzji PR: sortuje zgromadzone węzły recenzji do koszyków konsensusu i oblicza werdykt dla danego PR-a.
skills.analyze Uruchamia skan łańcucha dostaw skillów dla zainstalowanych skillów w obszarze roboczym i zapisuje każdy werdykt.

Ciało kroku zwraca StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Wszystkie fabryki przyjmują opcjonalną mapę mutatedState scalaną ze stanem potoku, z wyjątkiem failed.

Fabryka Sygnatura Opis
ok StepResult.ok({mutatedState}) Normalne zakończenie; dalsze kroki nasłuchujące są oceniane
route StepResult.route(nextRouterKey, {mutatedState}) Zakończenie routera; wybiera dalszą gałąź, której routeKey się zgadza
suspendUntilEvent StepResult.suspendUntilEvent(eventType, {mutatedState}) Wstrzymuje do czasu wystąpienia określonego typu zdarzenia domenowego
suspendUntilTasksComplete StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) Wstrzymuje do czasu, gdy wszystkie wymienione zadania osiągną stan końcowy
terminal StepResult.terminal({mutatedState}) Uruchomienie potoku jest zakończone — nie ma już kroków
failed StepResult.failed(errorMessage) Krok zakończył się niepowodzeniem z komunikatem o błędzie

Zachowanie ponawiania dla ciała węzła, które zawodzi (PipelineNodeConfig.retryPolicy; null wyłącza ponawianie).

Pole Typ Opis
maxAttempts int Łączna liczba prób wraz z pierwszą (3 = 1 próba + 2 ponowienia); musi być ≥ 1
backoff String linear lub exponential (domyślnie)
initialDelayMs int Opóźnienie przed pierwszym ponowieniem w milisekundach; kolejne opóźnienia rosną zgodnie z backoff

Konfiguracja pojedynczego węzła przenoszona wewnątrz definicji kroku (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). Ten kształt mają zarówno węzły wbudowane, jak i niestandardowe; każde ciało odczytuje tylko te pola, które go dotyczą.

Pole Typ Opis
prompt String? Szablon promptu z podstawianiem {{key}}
script String? Treść skryptu bash z podstawianiem {{key}}
agentId String? UUID agenta z obszaru roboczego do wysłania
inputKeys List<String> Klucze stanu, które ten węzeł pobiera jako dane wejściowe
repoIds List<String> Repozytoria, które węzeł messaging.createSpace pobiera do checkoutu (<repoId> lub <repoId>@<branch>). Puste = żadne; extras['allRepos'] = wszystkie repozytoria obszaru roboczego. Ignorowane w krokach agenta.
outputKey String? Klucz stanu, pod którym zapisywany jest wynik tego węzła
label String? Etykieta wyświetlana na kanwie (domyślnie identyfikator kroku)
outputSchema Map<String, dynamic>? JSON Schema (podzbiór), który musi spełniać wartość wyniku
reducer String? Strategia scalania, gdy równoległe gałęzie zapisują ten sam outputKey: append, mergeLists, mergeMaps, sum lub override (domyślnie)
retryPolicy StepRetryPolicy? Polityka ponawiania przy niepowodzeniu ciała; null = pojedyncza próba
continueOnFail bool Gdy true, końcowe niepowodzenie nie powoduje niepowodzenia uruchomienia; błąd trafia do state['_stepErrors'][stepId]
timeoutMs int? Limit czasu zegarowego dla ciała węzła w milisekundach; null = brak limitu
teamId String? Zespół do wysłania (węzły team.dispatch); wyklucza się z agentId
dispatchMode String? Tryb wykonania zespołu: allParallel lub manager
extras Map<String, dynamic> Swobodna konfiguracja specyficzna dla ciała (np. iterableKey, templateId, drzewa warunków)

Wyprowadzone akcesory: spaceId (extras['spaceId'] — istniejąca rozmowa, w której pracować), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, odczytywany na węźle otwierającym przestrzeń) oraz runWhen (extras['runWhen'] — pomija węzeł, gdy warunek nie jest spełniony).