跳至內容

管線與自動化

管線是 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 的步驟會先重試,採用線性或指數退避,之後才套用上述其中一種 處理。