我們在前一天已經完成 Skill 的建立。
Repo 從今天起公開:nathanfhh/30-days-ironman。
先說一件事免得對不上:這個 repo 是三十天的終點狀態,不是今天的狀態。
裡面已經有後面幾天才會講到的東西(外部 benchmark、可拋棄的審查容器、擋在 GitLab 前面的代理、觀測、流量側錄、網頁終端),README 開頭那張「連載 Day → 目錄」的對照表就是為這件事放的。文章講為什麼這樣選,程式碼是怎麼做的。
上圖是同一件事的另一種畫法:repo 分五層,每一層對到一段連載日。今天真正跑過的只有最底下那一層;上面四層是後面幾天的事,現在先知道它們在哪裡就好。
我從內部選用一份已經 Merged 的 MR 作為測試基底,請 Claude Code 使用這份全新的 Skill 來試試水溫。
選用已 Merged 的原因是:審查期間的留言可以作為基準,用來比較甚至校準這次的發現。
我想要使用既有已經合併的 Merge Request 來校準剛製作好的 nathan-code-review Skill。
我會提供你一個 GitLab MR URL,請你按照 Skill 中制定的流程與規範開始進行程式碼審查。
校準專用的調整(僅此次有效):
1. 此 MR 無條件視為第一次審查。審查期間**切勿**取回 discussions 與 notes:它們是稍後對答案用的比較基準,先讀就不是公平評估了。
2. 這是已合併的 MR:diff 不可用 target...source(合併後為空、branch 可能已刪)。請改用「取回 MR 詳情」那個端點回傳的 diff_refs,diff base_sha...head_sha;若需要 fetch,使用 GitLab 保留的 ref:git fetch origin refs/merge-requests/{iid}/head
3. 審查完成後先只做本地渲染(Markdown 給我看),暫時不要 Post。發佈是對外且不可逆的動作,等我看過報告與比較結果、明確指示後才執行。(這也正是 Skill 裡 human-in-the-loop 規則的實際運作方式)
報告渲染完成後,再取回此 MR 的所有 discussions 與 notes,逐條與新 Skill 的審查結果比較,
每條歸入四類:
- 命中:人審有提、skill 也抓到(並比較 severity 是否一致)
- 漏抓:人審有提、skill 沒抓到、且該問題仍存在於受審的 diff 中
- 不可比:人審有提,但作者已依回饋在後續 commit 修正:skill 審的是修正後的 code,抓不到是正常的,不算漏抓
- 多抓:skill 有、人審沒提(再判斷是真問題還是噪音)
最後報告你的發現。對每一條「漏抓」,先判斷這是「規則不存在」還是「規則存在但執行時沒走到」:前者才建議新增規則(附建議條文與該放的檔案);後者不要加規則,改建議修執行流程。
目標的 MR URL 是:xxxxx
別被這段 prompt 的長度嚇到,它是這一天專屬的。
日常使用只要一句:
/nathan-code-review貼上 MR 連結;或站在專案目錄裡不帶參數直接下(審當前的變更),給 branch 名或指定幾個檔案也行。今天會這麼長,是因為這不是一次日常審查,是一場校準實驗。那些「僅此次有效」的指示全是實驗控制:盲測公平、已合併 MR 的 diff 繞道、先渲染不發佈的對答案程序。它們控制的是實驗,不是 skill。
記得先提供 GitLab 金鑰於環境變數中再啟動 Claude:
GITLAB_TOKEN=xxxx claude
它抓到了幾條人審當時漏掉的東西,也有幾條漏抓。漏抓的部分可以要求 AI 把它變成規則,避免下次再犯。
這條漏抓有意思的地方不在它漏了什麼,在它怎麼解釋自己為什麼漏。它的說法是:
我讀到了矛盾、在心裡調和掉,因為流程沒有告訴我那是一個 finding。
它看到了,然後自己消化掉了。這跟「沒看到」是兩件不同的事,後果也不同:沒看到要補的是眼力,看到了不當一回事要補的是流程。
所以它接下來做的判斷才是重點:這是「規則不存在」,不是「規則存在但這次沒執行到」。前者才該加規則,後者加規則只會讓一份已經沒被執行的文件變得更長。它判定是前者,然後直接指出條文該放哪個檔案的哪個區塊,理由是這件事跟九個面向都無關、屬於前置動作。
我接受了這個判斷。
這六條裡有一條我沒有照做。
第 3 條它說 ty 噴了 173 筆假的 unresolved-import,建議「跑 ty 前先 uv sync」。噪音是真的,但這個解法會把 dev container 的攻擊面撐開:uv sync 會依照被審查那個分支自己的 pyproject.toml 去解析、安裝相依,而 PEP 517 的 build backend 也是由那份檔案指定的。等於為了讓型別檢查乾淨一點,讓對方的程式碼在我的審查環境裡跑起來。
我要的是這個容器被攻擊的構面愈小愈好,所以額外的套件一律不裝。這條後來寫成硬規則:掃描工具不得為了掃描安裝任何東西,ty 解析不到第三方型別就是它的正常狀態,報告如實揭露即可。
還有第 6 條它自己也承認了:skill 取 diff 的做法是比 target 分支與 source 分支,MR 一合併這個 diff 就是空的,分支也可能已經刪了。今天這場能跑起來,是因為我在 prompt 裡手動指定改用 diff_refs 的 base_sha...head_sha(Day 9 說留到後面講的 diff_refs 就是它)。這條路徑到現在還沒寫進 skill:正式流程審的是還沒合併的 MR,只有像今天這樣拿舊 MR 回頭校準才會撞到。
大家可以發現這些錯誤或是不足真的很難在跑之前就知道怎麼一次設計到定位。
最後的比較階段,給了我三個現在就能做的具體行動:
我就只有下達這樣的 Prompt:
我們來優化這部分吧
接著 Claude Code 就開始協助更新 Skills 與調整 Scripts。
上面那條需求文件的規則就是這一輪進去的,現在在 references/review-dimensions.md 九面向前面的前置段落裡,叫 The requirement document is also under review:需求文件本身也是被審的對象,同一件事在不同章節有兩種說法、決策清單與正文互相矛盾、正文引用了程式碼裡已經不存在的常數,這些都要指出來。嚴重度依「照錯誤的那一節去做會造成什麼」而定,通常是 Nit,涉及安全或資料完整性時是 Suggestion。
落地的版本比它當初的建議多了一句,而那句正好回頭指著它自己的自陳:
Reconciling the two versions in your head and moving on is exactly the failure mode: you resolved it for yourself, not for them.
(在腦中把兩個版本調和掉然後繼續走,正是這個失敗模式本身:你替自己解決了,沒有替他解決。)
報告與比較結果我都看過、也優化完了,於是我指示 AI 把報告發佈上去,順便把發佈這段管線也實際驗一遍。
確實在報告中看到具體的建議與修改方向;次要內容摺疊起來,一則留言不會長到沒人讀到最後。
我也用測試覆蓋率的想法問了 LLM:
GitLab API 那段有哪些沒有執行到的?
它就開始盤點,也發現了些瑕疵。然後指出目前端點中僅有一個 Endpoint 沒有真正呼叫、執行:回覆。
所以我就叫 LLM 回覆隨意內容到剛剛發出報告的那個 Discussion 中。
MR 留言上:
AI 建議我使用單元測試的思維針對 Python 與 GitLab API 互動的邏輯進行相關的驗證與保護。
我請 AI 透過 uv 在專案根目錄進行專案的初始化,並且開始實作相應的測試案例。
ty 那 173 筆假警報,就照前面說的方向修:當成離線環境,不裝任何東西,解析不到就如實揭露。
完成後我請 LLM 為此專案生成相應的 CLAUDE.md 檔案,並且更新 .gitignore。
另外把 Day 10 訂的版本號規則寫進 CLAUDE.md,讓 AI 每次改 nathan-code-review 都得跟著 bump:
2026.08.02.01 → 2026.08.02.02
2026.08.02.03 → 2026.08.10.01
這是這套 Skill 第一次真的拿去審一份 MR,帳也記一下:12 美金。跟 Day 11 那場一樣,我走的是訂閱制,這個數字是把 token 用量乘上 API 牌告價回推出來的,不是真的扣走的錢。
Day 11 建整套 skill 花了 27.02 美金,那是一次性的;這 12 美金才是「跑一次審查」的量級。
我們藉由實際案例的流程逼出一些字面上不可能察覺的問題。
同時也藉由這個機會演示怎麼讓 Skill 迭代、優化的過程。因為僅有今天一場次的測試絕對是不足的。所以我自己後續有做另一個 Skill 來優化這個 Skill,它能讀入一些檔案(演講逐字稿、簡報、講義等等的)看看這些素材中有沒有可以用來強化 Skill 的觀點、做法,有就納入。
今天公開的 repo 在這裡:nathanfhh/30-days-ironman。skill 本體、它的測試,以及之後每一天的程式產出物,都對照在裡面。
但迭代得越勤,越需要確定自己沒把原本會的東西改壞。明天先補這道保險:給 skill 寫測試!