Перейти до вмісту

Кроки конвеєра

Крок конвеєра має дві незалежні осі:

  • Тип (StepKind) — як рушій планує крок відносно його тригерів.
  • Ключ тіла (BuiltInBodyKeys) — яке зареєстроване замикання тіла виконує роботу кроку.

Визначено в packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.

Тип Семантика планування
trigger Обов’язкова точка входу — рівно один на шаблон, завжди перший вузол. Сам нічого не робить; те, що починає запуск (вручну / подія / розклад), відстежується окремо як рядки PipelineTrigger. Його тіло — порожнє pipeline.trigger.
listen Спрацьовує, коли завершуються всі вихідні кроки (у StepTrigger.sourceStepIds).
join Спрацьовує, коли всі кроки в waitForStepIds досягають кінцевого стану.
router Умовне розгалуження — тіло повертає StepResult.route(key), що запускає лише те ребро нижче за потоком, чий routeKey збігається; невибрані гілки позначаються як пропущені.
forEach Планується як listen (спрацьовує, коли завершуються всі sourceStepIds). Ітерує саме зареєстроване тіло flow.forEach; рушій не створює окремий дочірній крок на кожен елемент.
terminal Сторожовий елемент планувальника без тіла. Запуск конвеєра завершується, коли завершується термінальний крок.

Зареєстровані замикання тіл, ключовані константами в BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). Їх 25, усі наведені нижче. Конфігурацію читають з PipelineNodeConfig кроку (див. нижче), якщо не зазначено інше; «ключі стану» читаються зі стану конвеєра / корисного навантаження тригера під час виконання.

Крок, чий bodyKey не зареєстровано, видає попередження під час завантаження шаблону і потім ніколи не виконується — він не провалює запуск. Кроки trigger і terminal звільнені від цього попередження: тригер усе одно виконує порожнє тіло pipeline.trigger; крок terminal не має тіла.

Порожнє тіло обов’язкового вузла trigger — завершується негайно, щоб рушій розійшовся на слухачів нижче за потоком. Без конфігурації.

Крок оболонки без агента (bash -c). Плейсхолдери {{key}} у скрипті підставляються зі стану + корисного навантаження тригера зі значеннями, екранованими для оболонки.

Поле конфігурації Опис
script Команда оболонки для виконання (обов’язково)
outputKey Ключ стану, що отримує обрізаний stdout при виході 0

Виконується з cwd <cc_root>/pipelines/<pipelineRunId>/ (спільним для всіх bash-кроків у запуску) і GITHUB_TOKEN у середовищі. Також записує <stepId>_runDir до стану. Ненульовий код виходу провалює крок із хвостом stderr.

Загальний вузол «промпт і запуск»: рендерить промпт, запускає агента в розмову й призупиняється, доки запуск не завершиться. Корисне навантаження submit_output агента збирається до стану під outputKey.

Поле конфігурації Опис
agentId UUID агента в межах робочого простору для запуску (обов’язково)
prompt Текст інструкції; підтримує підстановку {{key}} (обов’язково)
outputKey Ключ стану для зібраного виводу
outputSchema JSON Schema, якій має відповідати вивід
label Зрозуміла людині назва, яка показується на полотні
extras.spaceId Наявна розмова, у якій працювати (типово: прихована)
extras.conversationTitle Іменований потік усередині цього простору (необов’язково)
extras.mode Читається на вузлі, який відкриває кімнату (messaging.createSpace), а не тут. Типово review.

Тіло маршрутизатора — читає config.extras, обчислює умову й повертає StepResult.route(key). Умови — не вирази JavaScript; підтримуються три форми написання, у порядку пріоритету:

  • Дерево предикатів (extras.predicate) — булеве дерево, що спрямовує до "true" / "false". Кожен вузол — це мапа з type:
    • {"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false} — істинний, коли будь-який із перелічених шляхів існує на диску; negate інвертує його; recursive також шукає в підкаталогах (пропускаючи .git, node_modules, build, .dart_tool). Відносні шляхи розв’язуються відносно state[baseKey] (типово repo_local_path), інакше — каталогу робочого простору для цього запуску.
    • {"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80} — оператори equals, notEquals, contains, exists, notExists, gt, lt; читає стан конвеєра, а не файлову систему.
    • {"type": "and" | "or", "of": [<predicate>, ...]} — булеві групи.
    • {"type": "not", "of": <predicate>} — заперечення.
  • Перемикач (extras.switchKey) — {"switchKey": "pr_class", "cases": [...], "default": "standard"}; спрямовує до першого випадку, який значення стану містить без урахування регістру, інакше — до default.
  • Порівняння (застарілі extras.left / extras.op / extras.right верхнього рівня) — еквівалент предиката comparison, збережений для шаблонів, написаних до появи дерева.

Ребра, що виходять із вузла, несуть routeKey, який має збігатися з повернутим ключем.

Публікує зведені знахідки як коментар до рев’ю PR через клієнт PR GitHub.

Ключ стану Опис
repo_full_name owner/repo (обов’язково)
pr_number Номер PR — int або рядок із цифр (обов’язково)
consolidated_findings Тіло коментаря (обов’язково)

Повертає StepResult.terminal з comment_review_id і commented_at у стані.

Публікує повідомлення до простору обміну повідомленнями через MessagingPort.

Ключ стану Опис
space_id Цільовий простір (обов’язково)
content Тіло повідомлення, що читається зі стану дослівно (обов’язково)

Записує posted_space_id і posted_at до стану.

Запускає цілу команду замість одного агента, а потім призупиняється, доки не завершаться запуски її учасників.

Поле конфігурації Опис
teamId Команда в межах робочого простору для запуску (обов’язково; взаємовиключно з agentId)
prompt Текст цілі; підтримує підстановку {{key}} (обов’язково)
dispatchMode allParallel (типово — одне завдання на учасника) або manager (запускається лише лідер для координації)
outputKey Ключ стану для зібраних виводів учасників
outputSchema, label, extras.spaceId Як для conversation.promptAgent

Шлюз схвалення. Запускає агента-схвалювача в розмову (режим рев’ю) з промптом шлюзу й контрактом виводу {decision, reason}, а потім призупиняється, доки схвалювач не подасть результат через submit_output.

Поле конфігурації Опис
agentId Агент-схвалювач (обов’язково)
prompt Промпт шлюзу (необов’язково; за відсутності використовується типовий)
outputKey Ключ стану для зібраного навантаження {decision, reason}
outputSchema Типово — схема схвалення (decision: approved / rejected, reason)
label, extras.spaceId Як для conversation.promptAgent

Шлюз розв’язується лише через submit_output. BuiltInBodyKeys.humanGate, бібліотека типів вузлів і текст промпту одного з вбудованих шаблонів називають інструменти MCP approve_step і reject_step; жодного з цих інструментів не існує в жодному пакеті чи реєстрі.

Прибирає застарілі ізольовані worktree; обирає свій режим із корисного навантаження тригера. Зважає на прапорець dryRun запуску.

Ключ стану Опис
ticket_id Звільняє worktree цього тікета
repo_full_name + pr_number Звільняє тимчасовий worktree редактора PR
space_id Знищує worktree і теку цієї розмови
(нічого з переліченого) Прибирає в робочому просторі: зниклі каталоги, мертві простори, осиротілі теки розмов

outputKey (необов’язково) отримує рядок-підсумок.

Відображення / розгалуження над колекцією зі стану: рендерить промпт по одному разу на елемент, потім запускає агента один раз із цими промптами, з’єднаними (--- як роздільники), і призупиняється, доки цей запуск не завершиться. Порожня колекція завершується негайно з порожнім списком. Це не окремий паралельний запуск на кожен елемент.

Поле конфігурації Опис
agentId Агент для запуску на кожен елемент (обов’язково)
prompt Промпт для кожного елемента; підтримує підстановку {{key}} (обов’язково)
extras.iterableKey Ключ стану, що містить колекцію (обов’язково)
extras.itemKey Ключ, до якого прив’язується поточний елемент у промпті (типово item)
outputKey Ключ стану для зібраного виводу (один запуск, не список)

Запускає інший шаблон конвеєра як вкладений підкрок (підконвеєр виконується як дочірній запуск із заданим parentPipelineRunId). Крок призупиняється, доки дочірній запуск не досягне кінцевого стану, після чого фінальний стан дочірнього зливається під outputKey.

Поле конфігурації Опис
extras.templateId Дочірній шаблон для запуску (обов’язково; конвеєр не може викликати сам себе)
inputKeys Ключі стану, скопійовані в корисне навантаження тригера дочірнього
outputKey Ключ стану для злитого фінального стану дочірнього

Фонове індексування tree-sitter: обходить репозиторій, видобуває символи/ребра в ізолятах воркерів і завантажує їх до графа коду робочого простору. Передає прогрес потоком у рядок запуску кроку; підтримує dry-run. Tree-sitter та його граматики — обов’язкові нативні залежності: відсутня dylib означає, що сервер узагалі не завантажився, а граматика, яку не вдається розв’язати посеред запуску, провалює крок, а не пропускає його.

Ключ стану Опис
repo_id Репозиторій для індексування (обов’язково)
repo_local_path Локальний шлях чекауту (обов’язково)

Офлайнова діаризація спікерів. Читає збережене аудіо зустрічі, кластеризує його на спікерів (Person 1, Person 2, …), перепозначає сегменти транскрипту й переписує стан transcript; також видає diarization_spans для паралельного кроку meeting.updateTranscript. Пропускає транскрипт без змін, коли аудіо не збереглося або моделі не встановлено.

Ключ стану Опис
meeting_id Зустріч для діаризації (обов’язково)

Детерміновані тіла збереження підсумку зустрічі. Кожне читає зі стану meeting_id і структуроване навантаження meeting_outcome кроку-агента ({summary, enhancedNotes, actionItems[], decisions[]}) і записує свою частину до власної таблиці — нотатки, пункти дій або рішення. Раніше збережені рядки ніколи не стираються під час деградованого запуску.

Ключ тіла Опис
meeting.identifySpeakers Розпізнавання спікерів між зустрічами: зіставляє діаризованих спікерів зі збереженими голосовими профілями й автоматично застосовує впевнені збіги. Порожня дія без профілів/ембедингів.
meeting.updateTranscript Застосовує позначки Person N з diarization_spans і зливає фрагменти за вікнами в репліки кожного спікера, після чого зберігає очищений транскрипт. Порожня дія, коли діаризація нічого не дала.
meeting.assemblePlayback Збирає змішану доріжку відтворення зустрічі зі збережених поканальних WAV. Порожня дія, коли аудіо не збереглося.
orchestration.markPhase Наприкінці DAG роботи записує сторожові позначки збою для підтікетів без виводу й переводить оркестрацію в synthesizing.
orchestration.persistDeliverable Записує вивід синтезу до батьківського тікета, завершує його, публікує результат і позначає оркестрацію як completed.
orchestration.awaitApproval Шлюз часткового схвалення: завершується негайно, коли ключ вузла кроку є у схваленій множині оркестрації, інакше призупиняється, доки пізніше схвалення його не відновить. Це єдине тіло, що встановлює extras.awaitApproval, а це звільняє його від 24-годинного тайм-ауту призупиненого кроку.
messaging.createSpace Відкриває розмову, у якій працює решта шаблону, опубліковану під outputKey кроку (вбудовані використовують pipeline_space_id). Порожній список repoIds не створює жодного чекауту; extras['allRepos'] створює чекаут кожного репозиторію робочого простору. extras['createConversation'] (типово вимкнено) також відкриває іменований потік. Крок-агент ПРИЄДНУЄТЬСЯ до кімнати й ніколи її не відкриває, тож розгалуження без цього вузла падає під час запуску — а під’єднання його один раз означає, що все розгалуження ділить один чекаут, а не виділяє окремий кожному.
prReview.finalize Останній крок шаблону рев’ю PR: розкладає накопичені вузли рев’ю по кошиках консенсусу й обчислює вердикт для кожного PR.
skills.analyze Запускає сканування ланцюга постачання навичок для встановлених у робочому просторі навичок і фіксує кожен вердикт.

Тіло кроку повертає StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Усі фабрики приймають необов’язкову мапу mutatedState, що зливається зі станом конвеєра, крім failed.

Фабрика Сигнатура Опис
ok StepResult.ok({mutatedState}) Звичайне завершення; слухачі нижче за потоком оцінюються
route StepResult.route(nextRouterKey, {mutatedState}) Завершення маршрутизатора; обирає гілку нижче за потоком, чий routeKey збігається
suspendUntilEvent StepResult.suspendUntilEvent(eventType, {mutatedState}) Пауза, доки не спрацює конкретний тип доменної події
suspendUntilTasksComplete StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) Пауза, доки всі перелічені завдання не досягнуть кінцевого стану
terminal StepResult.terminal({mutatedState}) Запуск конвеєра завершено — більше немає кроків
failed StepResult.failed(errorMessage) Крок не вдався з повідомленням про помилку

Поведінка повторних спроб для тіла вузла, що падає (PipelineNodeConfig.retryPolicy; null вимикає повторні спроби).

Поле Тип Опис
maxAttempts int Загальна кількість спроб, включно з першою (3 = 1 спроба + 2 повтори); має бути ≥ 1
backoff String linear або exponential (типово)
initialDelayMs int Затримка перед першим повтором, у мілісекундах; подальші затримки зростають відповідно до backoff

Конфігурація для кожного вузла, що переноситься всередині визначення кроку (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). І вбудовані, і власні вузли мають цю форму; кожне тіло читає лише ті поля, які його цікавлять.

Поле Тип Опис
prompt String? Шаблон промпту з підстановкою {{key}}
script String? Тіло bash-скрипту з підстановкою {{key}}
agentId String? UUID агента в межах робочого простору для запуску
inputKeys List<String> Ключі стану, які цей вузол споживає як вхід
repoIds List<String> Репозиторії, для яких вузол messaging.createSpace створює чекаут (<repoId> або <repoId>@<branch>). Порожньо = жодного; extras['allRepos'] = кожен репозиторій робочого простору. Ігнорується на кроках-агентах.
outputKey String? Ключ стану, під яким записується вивід цього вузла
label String? Зрозуміла людині назва, яка показується на полотні (типово — ID кроку)
outputSchema Map<String, dynamic>? JSON Schema (підмножина), якій має відповідати значення виводу
reducer String? Стратегія злиття, коли паралельні гілки пишуть в один outputKey: append, mergeLists, mergeMaps, sum або override (типово)
retryPolicy StepRetryPolicy? Політика повторних спроб при збої тіла; null = одна спроба
continueOnFail bool Коли true, остаточний збій не провалює запуск; помилка зберігається під state['_stepErrors'][stepId]
timeoutMs int? Тайм-аут реального часу для тіла вузла в мілісекундах; null = без тайм-ауту
teamId String? Команда для запуску (вузли team.dispatch); взаємовиключно з agentId
dispatchMode String? Режим виконання команди: allParallel або manager
extras Map<String, dynamic> Довільна конфігурація, специфічна для тіла (наприклад, iterableKey, templateId, дерева умов)

Похідні аксесори: spaceId (extras['spaceId'] — наявна розмова, у якій працювати), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, читається на вузлі, що відкриває простір) і runWhen (extras['runWhen'] — пропустити вузол, коли умову не виконано).