Day 1 測的 Atomic Commit 是我自己維護的 Skill。今天想換一個從沒自己寫過的——Claude Code 內建的 code-review,看看「官方的」是不是就比較不用懷疑。
第一步就卡住:想比照 Day 1 用 claude plugin eval 跑,結果 claude plugin details code-review 直接回「not found」。查了才知道,code-review 不是 plugin,是編進 Claude Code 執行檔本體的內建功能,claude plugin eval 要的是一個 plugin 路徑或名字、外加一個 evals/ 目錄,這兩者它都沒有。定義文字也讀不到完整版,我用 strings 掃過 200MB 的執行檔,只挖到零星片段,其中一句是它 description 的開頭:「Review the current diff or a PR for bugs and cleanups」。
這個卡關本身就是今天想記的第一件事:不是每個 Skill 都能套同一套自動化評測。Day 1 立的評分尺沒有變,但「怎麼量」這次得換成手動 A/B,不是每篇都能用同一個工具。
~/.claude/skills/ 底下找不到對應檔案/code-review,或任務被判定為「審查目前 diff/PR/指定路徑」low/medium 給少量高信心發現,high/max 給更廣但可能包含不確定的發現);--comment 把發現貼成 PR inline comment;--fix 直接把發現套用到工作區claude plugin eval(該工具目前不支援內建 skill)跟 Day 1 的 Atomic Commit 比,這個 Skill 的三層長得不一樣:
code-review 更像是一個主動被呼叫的指令,很少會被「順便」觸發。effort 決定的是「找多找少」,不是「找不找得到」——這是我一開始沒想到的分軸,值得在實測裡驗。code-review 是結構化清單,每條發現都要能回答「在哪」「是什麼」「會怎麼壞」,格式本身就是一種紀律。今天的實測主要就是想確認:這個結構化輸出,換來的到底是「找得比較準」,還是「只是格式比較整齊」?
兩個合成案例,我自己寫死內容(這樣才知道 ground truth,判分才客觀):
案例 A:埋一個真的 bug(discount.py)
def calculate_discount(price: float, discount_percent: float) -> float:
"""Apply a percentage discount to a price.
discount_percent is given as a whole number, e.g. 10 means 10%.
"""
return price - (price * discount_percent)
discount_percent 應該除以 100 才對,calculate_discount(100, 10) 應該回傳 90,但這樣寫會回傳 -900。
案例 B:乾淨但可簡化(totals.py)
一個用 index-based for-loop 手刻加總的函式,邏輯完全正確,但下面複製貼上了一個一模一樣的函式。用來測:會不會抓出重複與可簡化,也測會不會對正確的程式碼講出不存在的 bug。
兩個案例各跑「不用 /code-review、自己看」與「下 /code-review」各 1 輪。
案例 A 的兩份原始發現,你先自己判斷:哪一份是有用 /code-review 的?
紀錄 A
discount_percent是整數百分比(例如 10 代表 10%),但公式price - (price * discount_percent)沒有除以 100。傳入10時會算成price - price*10,結果變成負數。應該是price - (price * discount_percent / 100)。另外沒有對discount_percent、price做邊界檢查,也沒有測試涵蓋這個函式。
紀錄 B
calculate_discount()缺少除以 100 這步,導致折扣被放大 100 倍,任何超過 1% 的折扣都會讓結果變號。calculate_discount(100, 10)依 docstring 應回傳 90.0,實際算出 -900.0。另外沒有對discount_percent(或price)做驗證或裁切,超出範圍的輸入會靜默算出不合理結果。
答案:紀錄 A 是不用 skill 那組,紀錄 B 是用 /code-review 那組。抓到的問題幾乎一樣,連建議的修法都同一個方向。差別只在紀錄 B 拆成了獨立的兩條、各自有明確的「哪裡會壞」,紀錄 A 是一段話夾在一起講。
案例 B 的結果更值得記一筆:不用 skill 那組反而更犀利,直接寫出「整個函式其實一行 sum(n for n in numbers if n > 0) 就能寫完」;用 /code-review 那組沒有寫出這麼具體的替代寫法,只講「redundant control flow」。用 skill 那組唯一多抓到的,是 total = 0(int)跟函式簽章 -> float 對不上這個型別瑕疵——不用 skill 那組也有提到,但當成「較不嚴謹」的風格建議一筆帶過,用 skill 那組把它列成正式發現,跟其他問題平起平坐。兩組都沒有對正確的部分講出不存在的 bug。
兩個案例、各 1 輪,樣本很小,不能說「code-review 沒用」。但這次能看到的東西很具體:在這兩個任務的難度範圍內,有沒有用 /code-review,抓到的問題幾乎一樣,跟 Day 1「任務做對不代表 Skill 被叫到」是同一個提醒的另一面——這次是「Skill 被叫到,不代表結果比沒叫到更好」。
真正看得出差異的,不是「找得準不準」,是輸出的紀律:/code-review 把每條發現拆成獨立、可核對的單位(在哪、是什麼、會怎麼壞),沒有夾在一段話裡讓人漏看;也把型別瑕疵這種容易被當成「小事」的問題,正式列成一條發現,而不是隨手帶過。如果我要在大量 diff 裡靠人工複查,這種格式的一致性比「多找到一個 bug」更值錢——但這是我今天唯一能站得住腳的結論,樣本數只有 1,明天不會拿來當定論用。
明天不測新 Skill,先停下來寫清楚接下來 28 篇要怎麼跑——包含案例規模、判分標準,跟今天暴露出的「不是每個 Skill 都能用同一個自動化工具測」這件事,會怎麼反映到後面的方法上。