Jenis langkah pipeline
Sebuah langkah pipeline memiliki dua sumbu yang independen:
- Jenis (
StepKind) — bagaimana mesin menjadwalkan langkah tersebut relatif terhadap pemicunya. - Kunci badan (
BuiltInBodyKeys) — closure badan terdaftar mana yang menjalankan pekerjaan langkah itu.
Jenis langkah
Bagian berjudul “Jenis langkah”Didefinisikan di packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.
| Jenis | Semantik penjadwalan |
|---|---|
trigger |
Titik masuk wajib — tepat satu per templat, selalu node pertama. Tidak melakukan pekerjaan sendiri; apa yang memulai eksekusi (manual / peristiwa / jadwal) dilacak secara terpisah sebagai baris PipelineTrigger. Badannya adalah pipeline.trigger yang no-op. |
listen |
Terpicu ketika semua langkah sumber (di StepTrigger.sourceStepIds) selesai. |
join |
Terpicu ketika semua langkah di waitForStepIds mencapai status terminal. |
router |
Percabangan bersyarat — badan mengembalikan StepResult.route(key), yang hanya memicu sisi hilir yang routeKey-nya cocok; cabang yang tidak dipilih ditandai dilewati. |
forEach |
Dijadwalkan seperti listen (terpicu ketika semua sourceStepIds selesai). Badan flow.forEach yang terdaftar itulah yang melakukan iterasi; mesin tidak membuat satu langkah anak per item. |
terminal |
Sentin penjadwal tanpa badan. Eksekusi pipeline selesai ketika langkah terminal selesai. |
Badan langkah
Bagian berjudul “Badan langkah”Closure badan terdaftar yang dikunci oleh konstanta di BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). Ada 25, semuanya tercantum di bawah. Konfigurasi dibaca dari PipelineNodeConfig milik langkah (lihat di bawah) kecuali disebutkan lain; “kunci state” dibaca dari state pipeline / payload pemicu pada saat eksekusi.
Langkah yang bodyKey-nya tidak terdaftar menghasilkan peringatan saat templat dimuat lalu tidak pernah dijalankan — itu tidak menggagalkan eksekusi. Langkah trigger dan terminal dikecualikan dari peringatan itu: pemicu tetap menjalankan badan pipeline.trigger yang no-op; langkah terminal tidak punya badan.
pipeline.trigger
Bagian berjudul “pipeline.trigger”Badan no-op dari node trigger yang wajib — selesai seketika sehingga mesin menyebar ke pendengar di hilir. Tanpa konfigurasi.
pipeline.bashScript
Bagian berjudul “pipeline.bashScript”Langkah shell tanpa agen (bash -c). Placeholder {{key}} di dalam skrip diganti dari state + payload pemicu dengan nilai yang di-escape untuk shell.
| Kolom konfigurasi | Keterangan |
|---|---|
script |
Perintah shell yang dijalankan (wajib) |
outputKey |
Kunci state yang menerima stdout yang dipangkas saat keluar dengan kode 0 |
Dijalankan dengan cwd <cc_root>/pipelines/<pipelineRunId>/ (dipakai bersama oleh semua langkah bash dalam satu eksekusi) dan GITHUB_TOKEN di environment. Juga menulis <stepId>_runDir ke state. Keluar dengan kode bukan nol menggagalkan langkah beserta ekor stderr.
conversation.promptAgent
Bagian berjudul “conversation.promptAgent”Node prompt-dan-kirim generik: merender prompt, mengirim agen ke sebuah percakapan, dan menangguhkan hingga eksekusi selesai. Payload submit_output agen dipanen ke state di bawah outputKey.
| Kolom konfigurasi | Keterangan |
|---|---|
agentId |
UUID agen dengan cakupan ruang kerja yang dikirim (wajib) |
prompt |
Teks instruksi; mendukung substitusi {{key}} (wajib) |
outputKey |
Kunci state untuk output yang dipanen |
outputSchema |
JSON Schema yang harus dipenuhi output |
label |
Label untuk manusia yang ditampilkan di kanvas |
extras.spaceId |
Percakapan yang sudah ada untuk bekerja di dalamnya (bawaan: percakapan tersembunyi) |
extras.conversationTitle |
Aliran bernama di dalam space itu (opsional) |
extras.mode |
Dibaca pada node yang membuka ruangan (messaging.createSpace), bukan di sini. Bawaan review. |
pipeline.condition
Bagian berjudul “pipeline.condition”Badan router — membaca config.extras, mengevaluasi suatu kondisi, dan mengembalikan StepResult.route(key). Kondisi bukan ekspresi JavaScript; tiga bentuk penulisan didukung, dalam urutan prioritas:
- Pohon predikat (
extras.predicate) — pohon boolean yang merutekan"true"/"false". Setiap node adalah peta dengantype:{"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false}— bernilai true ketika salah satu path yang tercantum ada di disk;negatemembalikkannya;recursivejuga menelusuri subdirektori (melewati.git,node_modules,build,.dart_tool). Path relatif diselesaikan terhadapstate[baseKey](bawaanrepo_local_path), jika tidak terhadap direktori ruang kerja per eksekusi.{"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80}— operatorequals,notEquals,contains,exists,notExists,gt,lt; membaca state pipeline, bukan sistem berkas.{"type": "and" | "or", "of": [<predicate>, ...]}— grup boolean.{"type": "not", "of": <predicate>}— negasi.
- Switch (
extras.switchKey) —{"switchKey": "pr_class", "cases": [...], "default": "standard"}; merutekan ke case pertama yang memuat nilai state tanpa memedulikan huruf besar/kecil, jika tidak makadefault. - Perbandingan (warisan tingkat atas
extras.left/extras.op/extras.right) — setara dengan predikatcomparison, dipertahankan untuk templat yang ditulis sebelum pohon itu.
Sisi yang keluar dari node membawa routeKey yang harus cocok dengan kunci yang dikembalikan.
prReview.comment
Bagian berjudul “prReview.comment”Memposting temuan gabungan sebagai komentar tinjauan PR melalui klien PR GitHub.
| Kunci state | Keterangan |
|---|---|
repo_full_name |
owner/repo (wajib) |
pr_number |
Nomor PR — int atau string numerik (wajib) |
consolidated_findings |
Isi komentar (wajib) |
Mengembalikan StepResult.terminal dengan comment_review_id dan commented_at di state.
messaging.postSpace
Bagian berjudul “messaging.postSpace”Memposting pesan ke space pesan melalui MessagingPort.
| Kunci state | Keterangan |
|---|---|
space_id |
Space tujuan (wajib) |
content |
Isi pesan, dibaca apa adanya dari state (wajib) |
Menulis posted_space_id dan posted_at ke state.
team.dispatch
Bagian berjudul “team.dispatch”Mengirim satu tim utuh alih-alih satu agen, lalu menangguhkan hingga eksekusi para anggotanya selesai.
| Kolom konfigurasi | Keterangan |
|---|---|
teamId |
Tim dengan cakupan ruang kerja yang dikirim (wajib; saling eksklusif dengan agentId) |
prompt |
Teks tujuan; mendukung substitusi {{key}} (wajib) |
dispatchMode |
allParallel (bawaan — satu tugas per anggota) atau manager (hanya mengirim pemimpin untuk mengoordinasikan) |
outputKey |
Kunci state untuk output anggota yang dipanen |
outputSchema, label, extras.spaceId |
Sama seperti conversation.promptAgent |
human.gate
Bagian berjudul “human.gate”Gerbang persetujuan. Mengirim agen penyetuju ke sebuah percakapan (mode tinjauan) dengan prompt gerbang dan kontrak output {decision, reason}, lalu menangguhkan hingga penyetuju mengirimkan melalui submit_output.
| Kolom konfigurasi | Keterangan |
|---|---|
agentId |
Agen penyetuju (wajib) |
prompt |
Prompt gerbang (opsional; bawaan dipakai bila tidak ada) |
outputKey |
Kunci state untuk payload {decision, reason} yang dipanen |
outputSchema |
Bawaannya adalah skema persetujuan (decision: approved / rejected, reason) |
label, extras.spaceId |
Sama seperti conversation.promptAgent |
Gerbang hanya diselesaikan melalui submit_output. BuiltInBodyKeys.humanGate, pustaka jenis node, dan teks prompt salah satu templat bawaan semuanya menyebut alat MCP approve_step dan reject_step; kedua alat itu tidak ada di paket atau registri mana pun.
repos.cleanup
Bagian berjudul “repos.cleanup”Menghapus worktree terisolasi yang sudah usang; memilih modenya dari payload pemicu. Menghormati flag dryRun eksekusi.
| Kunci state | Keterangan |
|---|---|
ticket_id |
Melepaskan worktree tiket tersebut |
repo_full_name + pr_number |
Melepaskan worktree editor PR yang sementara |
space_id |
Membongkar worktree dan folder percakapan tersebut |
| (tidak satu pun di atas) | Menyapu ruang kerja: direktori yang lenyap, space mati, folder percakapan yatim |
outputKey (opsional) menerima string ringkasan.
flow.forEach
Bagian berjudul “flow.forEach”Pemetaan / fan-out atas koleksi state: merender prompt sekali per item, lalu mengirim agen sekali dengan prompt-prompt itu yang digabungkan (pemisah ---), dan menangguhkan hingga eksekusi itu selesai. Koleksi kosong selesai seketika dengan daftar kosong. Ini bukan satu pengiriman paralel per item.
| Kolom konfigurasi | Keterangan |
|---|---|
agentId |
Agen yang dikirim per item (wajib) |
prompt |
Prompt per item; mendukung substitusi {{key}} (wajib) |
extras.iterableKey |
Kunci state yang memuat koleksi (wajib) |
extras.itemKey |
Kunci tempat item saat ini diikat di dalam prompt (bawaan item) |
outputKey |
Kunci state untuk output yang dipanen (satu pengiriman, bukan daftar) |
flow.callPipeline
Bagian berjudul “flow.callPipeline”Menjalankan templat pipeline lain sebagai sub-langkah bersarang (sub-pipeline berjalan sebagai eksekusi anak dengan parentPipelineRunId terisi). Langkah menangguhkan hingga anak mencapai status terminal, lalu state akhir anak digabungkan di bawah outputKey.
| Kolom konfigurasi | Keterangan |
|---|---|
extras.templateId |
Templat anak yang dijalankan (wajib; pipeline tidak dapat memanggil dirinya sendiri) |
inputKeys |
Kunci state yang disalin ke payload pemicu anak |
outputKey |
Kunci state untuk state akhir anak yang digabungkan |
code.index
Bagian berjudul “code.index”Pengindeksan tree-sitter di latar belakang: menelusuri sebuah repo, mengekstrak simbol/tepi di isolate pekerja, dan menyerapnya ke dalam graf kode ruang kerja. Mengalirkan progres ke baris eksekusi langkah; mendukung dry-run. Tree-sitter dan grammar-nya adalah native yang wajib — dylib yang hilang berarti server tidak pernah boot, dan grammar yang gagal diselesaikan di tengah eksekusi menggagalkan langkah alih-alih melewatinya.
| Kunci state | Keterangan |
|---|---|
repo_id |
Repo yang diindeks (wajib) |
repo_local_path |
Path lokal checkout (wajib) |
meeting.diarize
Bagian berjudul “meeting.diarize”Diarisasi pembicara secara offline. Membaca audio rapat yang disimpan, mengelompokkannya menjadi pembicara (Person 1, Person 2, …), memberi label ulang pada segmen transkrip, dan menulis ulang state transcript; juga mengeluarkan diarization_spans untuk langkah meeting.updateTranscript yang paralel. Meneruskan transkrip tanpa perubahan ketika tidak ada audio yang disimpan atau modelnya belum terpasang.
| Kunci state | Keterangan |
|---|---|
meeting_id |
Rapat yang didiarisasi (wajib) |
meeting.saveNotes, meeting.addActionItems, meeting.addDecisions
Bagian berjudul “meeting.saveNotes, meeting.addActionItems, meeting.addDecisions”Badan persist ringkasan rapat yang deterministik. Masing-masing membaca meeting_id dan payload meeting_outcome terstruktur dari langkah agen ({summary, enhancedNotes, actionItems[], decisions[]}) dari state, lalu menulis bagiannya ke tabelnya sendiri — catatan, item tindakan, atau keputusan. Baris yang sudah tersimpan tidak pernah dihapus pada eksekusi yang terdegradasi.
Badan bawaan lainnya
Bagian berjudul “Badan bawaan lainnya”| Kunci badan | Keterangan |
|---|---|
meeting.identifySpeakers |
Pengenalan pembicara lintas rapat: mencocokkan pembicara yang didiarisasi dengan profil suara yang tersimpan dan otomatis menerapkan kecocokan yang meyakinkan. No-op tanpa profil/embedding. |
meeting.updateTranscript |
Menerapkan label Person N dari diarization_spans dan menggabungkan fragmen per jendela menjadi giliran per pembicara, lalu mempersistenkan transkrip yang sudah dibersihkan. No-op ketika diarisasi tidak menghasilkan apa pun. |
meeting.assemblePlayback |
Merakit trek pemutaran campuran rapat dari WAV per kanal yang disimpan. No-op ketika tidak ada audio yang disimpan. |
orchestration.markPhase |
Di akhir DAG pekerjaan, menulis sentinel kegagalan untuk sub-tiket tanpa output dan mengubah orkestrasi ke synthesizing. |
orchestration.persistDeliverable |
Menulis output sintesis ke tiket induk, menyelesaikannya, memposting hasil kerja, dan menandai orkestrasi completed. |
orchestration.awaitApproval |
Gerbang persetujuan sebagian: selesai seketika ketika kunci node langkah ada di himpunan yang disetujui orkestrasi, jika tidak menangguhkan hingga persetujuan berikutnya melanjutkannya. Ini satu-satunya badan yang menetapkan extras.awaitApproval, yang membebaskannya dari timeout 24 jam untuk langkah yang ditangguhkan. |
messaging.createSpace |
Membuka percakapan tempat sisa templat bekerja, yang dipublikasikan di bawah outputKey langkah (bawaan menggunakan pipeline_space_id). Daftar repoIds yang kosong tidak men-checkout apa pun; extras['allRepos'] men-checkout setiap repo ruang kerja. extras['createConversation'] (mati secara bawaan) juga membuka aliran bernama. Langkah agen BERGABUNG ke sebuah ruangan dan tidak pernah membukanya, jadi fan-out tanpa node ini gagal saat pengiriman — dan memasangnya sekali berarti seluruh fan-out berbagi satu checkout alih-alih menyediakan satu untuk masing-masing. |
prReview.finalize |
Langkah terakhir templat tinjauan PR: menyortir node tinjauan yang terkumpul ke dalam keranjang konsensus dan menghitung putusan per PR. |
skills.analyze |
Menjalankan pemindaian rantai pasok skill atas skill yang terpasang di ruang kerja dan mencatat setiap putusan. |
Hasil langkah
Bagian berjudul “Hasil langkah”Badan langkah mengembalikan StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Semua factory menerima peta mutatedState opsional yang digabungkan ke state pipeline, kecuali failed.
| Factory | Tanda tangan | Keterangan |
|---|---|---|
ok |
StepResult.ok({mutatedState}) |
Penyelesaian normal; pendengar di hilir dievaluasi |
route |
StepResult.route(nextRouterKey, {mutatedState}) |
Penyelesaian router; memilih cabang hilir yang routeKey-nya cocok |
suspendUntilEvent |
StepResult.suspendUntilEvent(eventType, {mutatedState}) |
Menjeda hingga jenis peristiwa domain tertentu terpicu |
suspendUntilTasksComplete |
StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) |
Menjeda hingga semua tugas yang tercantum mencapai status terminal |
terminal |
StepResult.terminal({mutatedState}) |
Eksekusi pipeline selesai — tidak ada langkah lagi |
failed |
StepResult.failed(errorMessage) |
Langkah gagal dengan pesan error |
StepRetryPolicy
Bagian berjudul “StepRetryPolicy”Perilaku percobaan ulang untuk badan node yang gagal (PipelineNodeConfig.retryPolicy; null menonaktifkan percobaan ulang).
| Kolom | Jenis | Keterangan |
|---|---|---|
maxAttempts |
int |
Total percobaan termasuk yang pertama (3 = 1 percobaan + 2 percobaan ulang); harus ≥ 1 |
backoff |
String |
linear atau exponential (bawaan) |
initialDelayMs |
int |
Jeda sebelum percobaan ulang pertama, dalam milidetik; jeda berikutnya bertambah sesuai backoff |
PipelineNodeConfig
Bagian berjudul “PipelineNodeConfig”Konfigurasi per node yang dibawa di dalam definisi langkah (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). Node bawaan maupun kustom memakai bentuk yang sama; setiap badan hanya membaca kolom yang diperlukannya.
| Kolom | Jenis | Keterangan |
|---|---|---|
prompt |
String? |
Templat prompt dengan substitusi {{key}} |
script |
String? |
Badan skrip bash dengan substitusi {{key}} |
agentId |
String? |
UUID agen dengan cakupan ruang kerja yang dikirim |
inputKeys |
List<String> |
Kunci state yang dikonsumsi node ini sebagai input |
repoIds |
List<String> |
Repo yang di-checkout node messaging.createSpace (<repoId> atau <repoId>@<branch>). Kosong = tidak ada; extras['allRepos'] = setiap repo ruang kerja. Diabaikan pada langkah agen. |
outputKey |
String? |
Kunci state tempat output node ini ditulis |
label |
String? |
Label untuk manusia yang ditampilkan di kanvas (bawaannya ID langkah) |
outputSchema |
Map<String, dynamic>? |
JSON Schema (subset) yang harus dipenuhi nilai output |
reducer |
String? |
Strategi penggabungan ketika cabang paralel menulis outputKey yang sama: append, mergeLists, mergeMaps, sum, atau override (bawaan) |
retryPolicy |
StepRetryPolicy? |
Kebijakan percobaan ulang saat badan gagal; null = satu percobaan |
continueOnFail |
bool |
Jika true, kegagalan terminal tidak menggagalkan eksekusi; error-nya disimpan di bawah state['_stepErrors'][stepId] |
timeoutMs |
int? |
Timeout waktu nyata untuk badan node dalam milidetik; null = tanpa timeout |
teamId |
String? |
Tim yang dikirim (node team.dispatch); saling eksklusif dengan agentId |
dispatchMode |
String? |
Mode eksekusi tim: allParallel atau manager |
extras |
Map<String, dynamic> |
Konfigurasi khusus badan yang bebas (mis. iterableKey, templateId, pohon kondisi) |
Aksesor turunan: spaceId (extras['spaceId'] — percakapan yang sudah ada untuk bekerja di dalamnya), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, dibaca pada node pembuka space) dan runWhen (extras['runWhen'] — lewati node ketika tidak terpenuhi).