3 個小需求、6 輪 review、87 分鐘
防線建好之後,接下來修的都是同一件事的反面:gate 擋得住,但擋到不該擋的。流程成本要和它擋下的風險成比例。
防線的反面#
上線五天的決策,被一場 rebase 掀翻說到 7 月 10 日傍晚的翻轉。同一天下午 4 點半,另一條戰線已經開打。
這個系列前半的主旋律是「擋住」:堵洞、加價、立法。防線蓋完之後,最後這個階段修的全是同一件事的反面:gate 擋得住了,但開始擋到不該擋的。而且此時的 kit 已經不接受「我覺得可以更好」這種理由,只接受事故報告。三張收據,三刀。
第一刀:小改不該驚動跨模型 review#
收據是 7 月 7 日我自己的真實 session:只是做調適期的小改(加個欄位記 log、埋個偵查點),也被收工閘門逼著跑一整輪跨模型 review。
AI 在設計文件裡對根因的描述很誠實:工作流規則明明說「小任務直接做」,但閘門是 size-blind 的,任何業務檔變更一律攔下。prose 與物理層自相矛盾時,物理層贏。規則寫得再對,hook 說了算。
我拍板的修法,是給 gate 裝一個量測器。基準是「上次認證樹」,也就是上一次審查通過那一刻的程式碼快照;量的是從那一刻到現在累積改了多少,用 git 的行數統計指令(numstat)算,三個條件全過才放行:
- 累積不超過 50 行;
- 不超過 2 個業務檔;
- 沒碰任何敏感或受保護路徑。
放行,但不推進 baseline。這半句是整刀的靈魂:小改持續累積,一旦破檻,那次 review 批次涵蓋之前放行的全部。所以這不是「50 行以下免審」,是「50 行以下緩審」。
如果想靠拆小 commit 繞過 review 呢?累積計數會追上來,防切香腸是刻意設計。而且門檻由 numstat 實測,模型自稱「這是小改」無效。這個系列講過了:自述不是證據。
第二刀:輪數會自我繁殖#
收據在 7 月 11 日、另一個專案的真實 session:3 個小 UI 需求,走成 6 輪跨模型 review、87 分鐘。是我親自喊停的,原話進了 kit 的 eval 紀錄:「請問目前開發流程主要卡在哪邊? subagent review 是否有濫用?」
接著 AI 事後覆盤,診斷出來的機制很有意思。每一輪 review 都重掃整條分支,於是形成一個迴圈:
- 已審過、沒再動過的舊碼,每輪被拿新角度重新挖;
- 挖出新 findings,觸發新一輪修與審;
- 新一輪又重掃全分支,回到第 1 步。
輪數自我繁殖。 review 不收斂,不是因為代碼一直在壞,是因為掃描範圍一直不縮。
修好的規則改成:第 1 輪審全集,之後每輪只審三樣——
- fix 本身改動的那幾行;
- fix 改動處的下游,也就是呼叫這段碼的地方(上一篇說過的「過期綠燈」最愛從這裡溜進來:你改了函式,呼叫它的人還揣著改動前的測試綠燈);
- 先前 findings 碰過的碼。
已審且未受影響的舊碼不再進掃描範圍;敏感路徑例外,每輪全集。改完的條文現在長駐在每個專案的規則檔裡(原文節錄):
Do NOT point the reviewer at the whole branch every round —
re-scanning already-reviewed, unchanged code mines new angles
out of old code and makes rounds breed rounds
(receipt: 6 rounds on 3 small UI changes, 2026-07-11).
連那張收據都直接寫進條文。若半年後有人(或 AI)想把掃描範圍改回全分支,得先跟這行括號吵贏。
同一場事故還附贈一個發現:6 輪裡至少 3 輪,reviewer 在打回同一個根因的不同變體。AI 的修法反覆拿螢幕寬度當「輸入能力」的代理變數,被打回就換一格再犯。於是判斷規則的藉口對照表多了一條:同根因的第 2 個變體被打回,就停下來把那個維度的完整取值列出來一次覆蓋,不要逐格擠牙膏。
第三刀:越認真補測試,越容易被罰#
收據在 7 月 12 日出現:我改一個 margin,真正的樣式改動 19 行。但共用元件連動了 3 個測試檔,合計 6 檔 55 行,第一刀立的兩個門檻雙雙爆掉,小改放行失效。
AI 診斷出一個反向誘因:測試檔也被算進計數,所以越認真替改動補測試,越容易破檻被罰跑 review。一個懲罰好習慣的 gate,遲早教出壞習慣。測試檔是驗證的產物,不是業務邏輯,卻成了破檻主力。
修法有個細節值得抄。我原本的提案是「測試檔不算檔案數」,但 AI 實作時發現只排檔案數修不到另外半邊:55 行還是大於 50,行數門檻照樣擋。所以測試檔要從兩個計數同時排除,檔數上限順勢放寬。改完之後,hook 頂部的門檻常數與那條敏感路徑判定長這樣(原文):
SMALL_MAX_LINES=50
SMALL_MAX_FILES=4
SENSITIVE_PATH_REGEX='(^|[/_.-])(auth|oauth|sso|login|password|payment|billing|migrat|security|secret|crypto)'
那條 regex 自己就有一個故事:它只綁左邊界、不綁右邊界,因為寧可誤擋不誤放。而 oauth、sso 之所以顯式列出,是因為左邊界的要求反而讓 auth 匹配不到 oauth.py,這個洞是跨模型 review 在設計階段抓到的。敏感命名的測試檔也因此不享豁免:帶 auth 字樣的東西,一行都是大事。
驗證方式也講究:拿事故的原始 numstat 形狀重放。同樣 6 檔 55 行,新 gate 放行;接著多塞一個 auth 檔,立刻攔。
(第五篇埋的那根線在這裡收口:當年立的「不超過 2 個檔案」,就是被這張收據推翻的。判準會過期不是醜聞,立有收據、改有收據,才是重點。)
加一刀送的:註解是寫給誰看的#
這條收據是我自己的抱怨,逐字進了紀錄:「vibe coding的模式下老實說我已經很少親自看程式碼的註解了,因此我認為實在沒必要每次撰寫code都產生大量註解」。
AI 給的核心判斷是:「對 AI 友善」與「大幅減量」是同一刀。機隊代碼的實際讀者是未來的 AI session,它讀 code 比讀散文快,「下一行在做什麼」的敘述性註解對它是純噪音。它真正需要的,是代碼自己顯示不了的四類:
- 不變量與外部約束;
- 跨檔耦合;
- 非顯然的 why;
- 附日期的收據。
那向 reviewer 辯護的註解呢?歸 commit message,別留在代碼裡。
配套一條自我約束:規則檔的總量有 20KB 預算,因為它是每個 session 的固定 context 稅。這輪動手前先精簡舊規則(砍字不砍義務)再新增。新規則要先還 context 債——這條線從砍掉 757 行說明書那天開始,貫穿到今天,一次都沒鬆過。
寫到今天為止#
這一篇(也是目前的最後一篇)講的是防線的反面:三張真實收據,換來三刀修形(小改緩審、re-review 縮範圍、測試檔不計數),外加一條註解紀律。它們共用同一句核心:流程的成本,要和它擋下的風險成比例。防線蓋得起來是上半場,捨得把它調鬆才是下半場。
回到時間軸。2026 年 7 月 12 日凌晨 4 點整,第 79 個 commit 進了 repo。當天,新版鋪開全機隊。
這不是結局,只是我寫到這裡的時候,進度條剛好停在這一格。順帶一提:你正在讀的這個部落格,就是那 11 個專案之一。這篇文章從撰稿、review 到發佈驗證,全程跑在這套 kit 底下,收工閘門攔過它,跨模型 review 審過它。這個系列寫的不是一個工具的說明書,是一套制度長出來的過程:從人肉路由器,到不滿三個月後,人真的只剩決策和 feedback 兩件事。
而制度還在被現實修理。門至少還有兩扇沒關:熔斷器至今只比對完全相同的調用,「微改一個字元」的繞法在交接文件裡躺著;superpowers 每次大版本更新,邊界都要重新實測。也可能兩扇都沒事,先壞的是我今天完全沒想到的地方——這個系列的每一篇都是這樣來的。設計文件的最後一節寫著「每三個月重新檢視這份文件」,屆時會發現自己踩過前一版沒講到的坑。
所以這裡不寫「完結」。下一篇的觸發條件不是我的寫作計畫,是下一張收據:哪天 kit 又被真實的失敗打臉一次、又長出一個版本,我就回來把它記下去。沒有收據的日子,這個系列就安靜地停在這一格。那反而是好消息。