昨天把 GitLab API 的端點與 Note / Discussion 的資料模型整理完了。
但報告發出去並不是結束:今天處理「發出去之後」的兩件事:作者反駁怎麼處理,以及版本怎麼控管。兩件事底下是同一個要求:AI 改變立場可以,但每一次改變都要留下能被程式檢查的痕跡。
報告發出去之後,作者回了一句「這個我不同意」。很多 Code Review 流程的說明會停在報告發佈;但另一個難點是在發佈之後:作者不同意時,AI 要怎麼重新判斷,又要留下哪些紀錄?
這時 AI 有兩種最容易犯的極端:過度順從(作者皺個眉頭就全面撤回)和過度固執(聽不進作者帶來的新資訊)。兩者都偏離了「讓 Codebase 變好」這個核心目標。
我的做法是要求 AI 收到反駁後,先走一輪六題自我審查。這六題是我參考 Google 工程實踐的 Handling pushback in code reviews 後,為 AI Code Review 重新整理出的判斷程序。前半保留它對作者脈絡、證據與 reviewer 立場的判準;後半再補上規則與適用情境、連鎖效應,以及這份 Skill 所需的欄位與結果:
六題不是建議,是欄位。報告 schema 裡 pushback 紀錄的 review 是六個必填字串(new_information、author_proximity、grounded_in_evidence、rule_or_preference、rule_versus_context、knock_on_effects),連同作者原話、結果(withdraw/hold/split)、回覆內容與時間一起存。少答一題,report_model.py validate 不放行,這條 pushback 就進不了報告。
AI 可以被說服,但不能只用一句「你說得對」帶過。
走完六題,結局通常只有三種,每種都有明確的做法:
撤回還有一個容易被跳過的後果:它會動到結論。 報告的 conclusion 不是 AI 挑的,是從仍然存活的 findings 算出來的:只要還有一條有效的 Critical,結論就是 Request Changes;否則只要還有 Suggestion,就是 Approved with Comments;只剩 Nit 或沒有 finding,才是 Approved。撤回把那條 finding 的狀態改成 withdrawn,它就不再算數;如果它是最後一條活著的 Critical,結論跟著變。所以撤回不是刪一行,是重跑一次 report_model.py conclusion 取新結論回填;結論欄與 findings 對不上的報告,validator 直接拒收。
貫穿三種結局的底線:對事不對人、用「這段 code/這個 MR」而不是「你」、作者情緒上來時 AI 保持冷靜。還有一條防線值得寫死:作者用「這個很趕」、「單位在等」施壓時,時程壓力不改變技術判斷,很急不等於緊急,等級不因此下修。
Skill 本身當然應跟隨 Commit 進行控管,但同時也該有對應的版本號。
我將版本號格式訂為:YYYY.mm.dd.{流水號-例如01, 02, ...},例如:2026.08.03.01,寫在 SKILL.md 開頭的 version: 欄位。
但版本號真正要跟著走的不是 skill,是每一份報告。報告 meta.skill_version 是必填欄位,空著就過不了驗證。理由很實際:三個月後有人回頭問「這條漏掉的問題,當時是哪一版規則在跑」,答案得在報告裡,不能靠回憶或 git blame 去對時間。這個追溯成立的前提,是每次規則異動都同步更新版本號,並讓該版本能在 repo 裡回指到對應內容;版本號是索引,不是規則內容本身。規則會改(後面會講到規則怎麼退場),改了之後舊報告還要能被讀懂是在哪套規則下寫的。
報告本身存哪、檔名怎麼組,那是 Day 8 交付流程的一部分,已經在那天講完了。
從 Day 3 到今天,八天寫的全是規格:萃取、流程、工具、九面向、報告、API、反駁。明天這一整份會交給 LLM,讓它把 skill 生出來。所以這裡不是複習,是交接前把東西攤開來看一遍。
先講定位:我不是要打贏市面上既存的 LLM Code Review Solution,我是要打造一套懂你,符合你甚至是像你的 AI Agent 流程。另一個角度,可以借**康威定律(Conway's Law)**來說明。
Conway's Law:設計系統的組織,產出的設計必然複製該組織的溝通結構。
我借 Conway's Law 做一個類比:工具往往帶著設計者的組織假設,而審查標準還必須容納你的團隊慣例、環境與風險現實。通用工具可以提供共同的核心,但組織特定的部分仍需要另外補上;對我來說,這份 Skill 就是承接那一層差異的地方。
與其繼續試用第 N 個「只差一點」的工具,我選擇把自己的組織情境、慣例與風險偏好,編進一份工具讀得懂的文件裡。這個 Skill 就是這麼來的。
下面這張圖是同一件事的全貌:六個 Phase,每一個只回答一個問題。順序不能對調——每一步都在替下一步限縮範圍。
host、project path 與 MR iid。呼叫 GitLab API 的 /projects/:id 時,:id 可使用數字 project ID 或 URL-encoded project path;目前流程使用從 MR URL 取得的 project path,並由 urllib.parse.quote(..., safe="") 完成編碼,不交給 LLM 手動替換字元一條 GitLab MR URL 的結構:https://gitlab.example.com/his/abc/abc-backend/-/merge_requests/61
host: gitlab.example.com
project path: his/abc/abc-backend
iid: 61
quote('his/abc/abc-backend', safe='')→his%2Fabc%2Fabc-backend
/tmp 中該 MR 的獨立工作目錄,並以 git diff --merge-base 在本地產生 diff。實作上,session 會把一般 GitLab HTTPS/SSH URL 自動改寫至每位使用者的憑證代理,GitLab token 不會交給 AI session。AI 隨即依標題、說明與異動範圍快速自問三題:該不該做?該在這個 MR 做?該在這個時機做?有疑慮不擋審查,但必須寫進最終報告,把決策留給人discussion_id 與時間回填到最終完整報告 JSON 的對應欄位中明天開始,我們就把 Day 3 到今天所寫下的這些內容,全部交給 LLM,讓它幫我們把 skill 真的生出來🤩!