管線與自動化
管線是 Control Center 中確定性的一半。代理決定要說甚麼;管線決定接下來發生 甚麼,而且每次都一樣。管線是由步驟組成的 DAG,可以派發代理、執行指令碼、按條件 分支、對集合扇出,並在步驟之間傳遞資料 — 可持久化、可續跑,而且會計算成本。
它們的存在基於三個原因:串連重複的多步驟流程、以序列或並行方式協調多個代理, 以及對在沒有人值守時發生的事情作出反應。
範本與執行
標題為「範本與執行」的章節範本(PipelineDefinition)是宣告式 DAG:步驟、宣告的輸入與設定。它存放在
工作區自身的資料庫中。執行(PipelineRun)是該範本的一次執行,帶有可變的
狀態包、成本與 Token 總計,以及每個步驟的狀態。
一個範本可以有多個執行。執行能跨越重新啟動:引擎會重新載入進行中的執行,並從 它們停止的地方繼續。
有兩道防線會刻意讓整次執行失敗,而不是繼續某個步驟,因為繼續會比停下來更糟:
- 某個步驟若處於
suspended狀態超過suspendedStepTimeout(預設 24 小時), 表示它正在等待永遠不會到達終端狀態的工單,因此會讓它失敗以釋放該次執行。 豁免情況是extras['awaitApproval'] == true— 也就是orchestration.awaitApproval部分核准閘門。human.gate不獲豁免。 - 若某個步驟宣告了
extras: {'idempotent': false},而在伺服器停止時仍執行到 一半,它可能已經套用了副作用(squash 合併、worktree 清理)。重新執行它可能會 重複套用,因此引擎會讓它失敗,並把重試留給你決定。
種類負責排程,主體負責工作
標題為「種類負責排程,主體負責工作」的章節一個步驟有兩個互相正交的部分,把它們分開正是讓引擎得以保持精簡的原因。
種類(StepKind)告訴引擎,相對於上游步驟,一個步驟_何時_變得可執行 — 這是
排程器唯一需要知道的事。種類有六種:trigger、listen、join、router、
forEach 與 terminal。主體(bodyKey)是步驟被排程後所執行的_工作_。
主體是在伺服器啟動時按鍵註冊的閉包,因此許多不同種類的步驟可以共用同一個主體:
例如多個 listen 步驟全都執行 conversation.promptAgent。
由於排程語意存在於六個種類之中,行為目錄可以在無人修改排程器的情況下擴充。完整的 種類、主體及其設定目錄,請參閱管線步驟。
在你手動撰寫範本之前,有兩個註冊表設計的後果值得了解:
- 若某個步驟的
bodyKey未註冊,會在載入範本時產生警告,之後永遠不會執行。 執行階段不會有任何明顯的失敗。trigger與terminal步驟不受該警告影響: 觸發器仍會執行空操作的pipeline.trigger主體,讓下游監聽器觸發;terminal步驟沒有主體,是排程器哨兵。 - 節點
extras對應表內的鍵採完全比對,其他寫法一律會被靜默忽略。只有extras['spaceId']會把某個代理步驟的工作放進現有對話。extras['mode'](chat/plan/review)是在開啟該房間的節點上讀取 (messaging.createSpace,預設為review)— 之後的代理步驟無法為它所加入的 對話重新授予權限。這兩個鍵的任何其他寫法都會被忽略。
狀態與資料流
標題為「狀態與資料流」的章節步驟之間不會互相呼叫。它們讀寫執行上的共用狀態包:步驟按鍵讀取輸入、執行,然後把
輸出寫回自己的 outputKey。當並行分支以同一個鍵為目標時,StateReducer 會合併
並行寫入;而 TemplateRenderer 會從同一個狀態替換提示與指令碼中的 {{...}}
佔位符。
這就是為何管線可以在事後被檢視 — 狀態包就是該次執行的完整來龍去脈。
甚麼會啟動一次執行
標題為「甚麼會啟動一次執行」的章節PipelineTrigger 資料列宣告範本何時自動啟動。共有四種:
| 觸發器 | 啟動一次執行的時機 |
|---|---|
| 領域事件 | 相符的領域事件觸發時 — 例如 PullRequestPublished 或 TicketAssigned |
| 排程 | cron/間隔運算式到期時 |
| 手動 | 你手動執行;範本是否出現在執行選擇器,取決於是否存在已啟用的手動觸發器 |
| Webhook | 外部系統向伺服器 HTTP 連接埠上的 /webhooks/<token> 發出 POST |
事件觸發器是一組封閉集合:分派器已知並在選擇器中提供 16 種事件類型
(EventPayloadMapper.knownEventTypes),除此之外沒有任何東西能啟動管線。這 16
種之中有 14 種具有載荷對應。有兩種 — TicketCreated 與 TicketStatusChanged —
沒有任何載荷對應,因此以它們為基礎的觸發器即使處於啟用狀態也永遠不會觸發。完整
清單請參閱領域事件。
Webhook 權杖同時用作 HMAC 密鑰:請求的 X-Hub-Signature-256: sha256=<hex> 標頭
會對照 HMAC-SHA256(body, token) 驗證,未通過驗證的遞送會被拒絕且不可重播,重複
項目則由 dedupe_key 丟棄。由於觸發器資料列在(工作區、事件類型、範本)上具有
唯一性,一個範本最多只能有一個 webhook 觸發器,以及每個領域事件類型一個觸發器。
內建範本植入時,手動與事件觸發器為啟用;排程觸發器則為停用,因此週期性 清掃一律需要主動選擇啟用。
出廠時停用的範本
標題為「出廠時停用的範本」的章節每個工作區會植入 13 個管線範本,其中 8 個出廠時為停用:
external_pr_welcome、cross_review、ticket_to_pr、pr_triage、
pre_merge_gate、release_notes、dep_audit 與 pr_digest。
這就是令人意外的地方。它們的觸發器植入時是_啟用_的,但
PipelineEngine.start 會拒絕已停用的範本並傳回 null — 因此觸發器觸發後毫無作用,
也不會出現任何執行。開關在範本,而不是觸發器。 在你期望這 8 個範本有任何作用
之前,請先到 設定 → 工作區 → 管線範本 啟用它。
重新植入工作區會保留你所作的一切選擇:你的 isEnabled 設定與現有觸發器資料列都
會保留。只有一個刻意安排的例外 — pr_merged_cleanup 的每日排程會被強制啟用,
即使先前已關閉,因為它屬於垃圾回收。沒有它,worktree 資料列、程式碼圖分割區與
寫入時複製副本會不斷累積,而這從來不該由使用者選擇。
還有一個值得知道的不對稱之處:內建的 pr_review 刻意不再發佈到 GitHub。發佈審查
是另一個需經使用者核准的步驟,而直接發佈的管線會造成重複發佈。cross_review、
ticket_to_pr 與 pr_triage 在執行時仍然會直接將留言發佈到 GitHub。
引擎知道甚麼
標題為「引擎知道甚麼」的章節PipelineEngine 建立執行記錄、排程與該次執行事件類型相符的起始節點,然後走訪
DAG:當步驟完成時,它會評估下游觸發器、解析路由器分支、等待 join、套用重試政策與
失敗後繼續,並在每個步驟保存狀態、成本與錯誤。對該次執行而言,從另一個起始節點
離開的邊會被忽略,因此排程觸發不會等待僅手動的條件。
它_不_知道的,是有關工單、派發、GitHub 或訊息的一切。它唯一的擴充接縫是
PipelineBodyRegistry。在那裡註冊的主體,正是持有 AgentDispatchPort、
MessagingPort 及其同類項目的地方 — 這就是為何為管線新增能力永遠不需要修改
排程器。
錯誤處理
標題為「錯誤處理」的章節失敗的步驟會記錄其錯誤訊息並轉為 failed。若該步驟宣告了 continueOnFail,
下游步驟會繼續進行,而錯誤會暫存在執行狀態中;否則該次執行會失敗。具有
StepRetryPolicy 的步驟會先重試,採用線性或指數退避,之後才套用上述其中一種
處理。