昨天寫到,這條用 skill 編排的 AI 開發 pipeline,最後變成了一篇沒有人敢修改的散文。
但它到底是怎麼長胖的?
我回頭翻 Git 紀錄,才發現它不是慢慢長大的。最早的 coordinator 文件只有 221 行,兩天後已經變成 419 行。
這 198 行不是預先設計好的流程,而是事故留下來的痕跡。
每次出事,我都會補一條看起來很合理的規則。問題確實解掉了,文件也確實愈來愈難維護。
使用這套流程時,為了更容易理解 Spec,我要求 agent 把規格轉成 HTML 頁面,發布到專用網站,方便團隊審核。
其中一次,agent 完成了規格文件,也做出一份放在 public/mock 的靜態頁面。
接著,它回報功能已經實作完成。
問題是,真正負責執行功能的程式碼(runtime code)根本沒有改。沒有 route handler、沒有 domain object,也沒有真正可以操作的功能。QA 測試的只是設計稿,commit message 寫的內容也和實際 diff 對不起來。
於是我補了一整組規則:
src/、tests/、migration 或 script 的變更。public/mock。這次修正增加了 95 行,刪掉 2 行。
一個「不要把 mock 當成實作」的觀念,就這樣散落在 dispatch、implementation gate、QA、final gate 和 guardrail 五個地方。
當下我覺得這樣比較保險。現在回頭看,這已經是第一個警訊:同一個規則開始出現第二份、第三份副本。
原本的 planning 階段已經替卡片建立 branch 和 draft PR,但 implementation worker 沒有接著使用,反而又開了一組新的 branch 和 PR。
事情還不只這樣。
它的新 branch 是從 local main 切出去的。當時另一條並行中的 pipeline 已經把別張卡片的 mock commit 留在 local main,那些不相干的檔案就一起被帶進新的 PR。
最後,一張卡有兩個 branch、兩個 PR,還混進了另一張卡的檔案。
這次我又補上:
origin/main 為基準,不能相信 local main。這次修改增加 41 行、刪除 13 行,文件淨增加 28 行。
真正缺少的其實只有兩個不變量(invariant):
一張卡只有一個 branch 和一個 PR。
新工作只能從乾淨的基準開始。
但當流程只能用散文表達時,我只好把這兩件事分別塞進工作步驟、gate、指令範例和 guardrail。
後來,問題從流程文件進到資源清理。
teardown 會執行 git worktree remove,然後從流程帳本(ledger)刪除這張卡的紀錄。但當移除 worktree 失敗時,程式沒有處理失敗結果,仍然刪除了流程帳本裡的紀錄。
目錄還留在硬碟上,系統卻已經忘記它的位置。
當時在這台機器上實際找到 7 個 worktree,流程帳本裡卻只剩 2 筆紀錄。每個 worktree 裡的 node_modules 大約占 1 GB。
為了處理這件事,我補上:
這次修改跨了文件與程式,增加 95 行、刪除 9 行。
一句「清理失敗時要重試」已經處理不了這個問題。它牽涉資源生命週期、錯誤回復、資料所有權,以及哪些東西可以安全刪除。
但我的修法仍然一樣:把這次學到的事繼續寫進流程。
另一個事故發生在 code review。
當時的規則只寫了兩種處理方式:code review 提出的問題(finding)合理就修掉,不合理就回覆說明。可是,如果問題合理,卻不值得在這個 PR 處理呢?
規則沒有答案,worker 只好自己決定。
它為了修改 UI 上的一個單字,建立了一張新的 Trello Backlog 卡。13 分鐘後,重新檢查才發現這個問題其實應該直接在原 PR 修掉,那張卡也隨即被封存。
於是我又補了第三條路:
這次增加 33 行、刪除 4 行。
為了一個存在 13 分鐘的 Backlog 卡,永久規則又多了 29 行。
只看最早期 coordinator 文件的 Git 歷史,它的成長是這樣:
06/22 初版 coordinator 221 行
06/23 整併 QA、review、PR 247 行
06/24 補 implementation gate 340 行
06/24 加入 CardSize gate 391 行
06/24 修正 branch/PR 與 clean base 419 行
兩天內,文件幾乎翻倍。
後來我做了整體重構,檔案結構已經不同,不能直接接在同一條曲線上比較。不過,重構後的第一個版本有 23 個檔案,共 4,783 行,其中 Markdown 占 1,523 行。
隔天修完 worktree 和 review finding 兩個事故後,總行數來到 4,898 行,Markdown 則增加到 1,591 行。
檔案拆開了,規則仍然繼續增加。
這四次修改都有充分的理由。
mock 不能假裝是正式功能;一張卡不該有兩個 PR;worktree 還沒移除,就不能刪掉唯一的紀錄;review 提出的問題也不該無限制地製造 Backlog 卡。
真正的問題是,我每次都把事故留下來,卻沒有把事故背後的不變量留下來。
於是,同一個觀念會同時出現在:
下一次修改時,agent 必須記得一起更新所有副本。只漏一個地方,文件說的、程式做的和 agent 理解的就會開始漂移。
散文沒有 compiler,也不會告訴我同一條規則已經被寫了五次。
流程寫得愈詳細,agent 就愈不容易犯錯。
兩天,221 行變成 419 行。我一開始並非少寫了 198 行;只是每次出事時,我都只能再補一段文字。
每次事故真正該留下的,是一個可以被驗證的不變量。