你修好一個 bug:測試綠了,AI 回報「入口已被移除」,你回覆使用者「修好了」,他也沒再回來。
這個 bug,真的修好了嗎?
Day 1 的路線圖分五段:下指令、驗收、品質、上線、收尾。今天開始最後一段,收尾。
Day 2 到 Day 7 講的是怎麼對 AI 下指令,最後收成一張工作單(交給 AI 的任務說明,分成改哪裡、哪裡不准動、做完怎麼算對等幾格,完整的一張放在文末)。之後每一篇,又往「做完怎麼算對」那一格加了幾行。今天不重講這些格子,直接拿來用:挑一個真的 bug,用這張工作單重修一次,看它會不會多抓到什麼。
挑的是 Day 26 的 #42:使用者在 usage(我做的開源小工具,顯示 Claude Code、Codex 這些 AI 工具用了多少額度)的面板(點選單列圖示彈出的視窗,裡面是一頁網頁)上按右鍵、點 Reload,面板變成一片空白。6 月 22 日當晚就修了,v0.22.5 發出。
我用 git 把 usage 退回 6 月 22 日修正之前的版本,開兩份一樣的資料夾,交給同一個 AI(Codex)各修一次。
B 單多出來的格子是這幾個:
症狀怎麼量:在面板上右鍵點 Reload 之後,量面板畫面上的文字;
修完再量同一個地方,貼出前後的畫面文字;
你量不到的話,說你量不到,交回來給我量;不要改量別的(Day 6)
哪裡不准動:每一條附上原因(Day 3)
已經決定的:右鍵選單保留「複製」,因為使用者會複製面板上的數字(Day 7)
做完怎麼算對:
- 拿掉修正要紅,紅在「面板畫面上的文字變成空的」(Day 9)
- 修正改了幾處,每一處分開拿掉、各跑一次(Day 23)
- 照使用者寫的步驟重跑一次,貼出成功的輸出(Day 25)
- 驗不到的:先說哪一類你驗不到,再列出這次落在裡面的項目(Day 4)
停損:同一處改超過 3 次還是不過,就停(Day 5)
你自己決定的:工作單沒寫、你自己拿主意的地方,全部列出來(Day 7)
Day 26、Day 27 加的那幾行,我刻意沒放。那兩篇就是從 #42 學來的,放進去等於先給答案。
A 改了兩個檔,新增了右鍵選單的測試,相關的 5 個測試都過。全套測試在 AI 的執行環境裡跑到一半中止,那個環境開不了視窗。它的回報最後老實寫了一句:本環境不能驗證真實右鍵選單及視窗重繪。
B 一行程式都沒改,觸發了停損(工作單裡寫好的「做不下去就停」條件)。6 月 22 日那張單本來就寫著一條:WKMenuItemIdentifierReload 拿不到,就停手回報;兩張單都有這一條。它的回報說,任務書要它找選單項目的 itemIdentifier,可是 Mac 的選單項目沒有這個方法,只有 identifier;實際的右鍵選單會給什麼值,它沒辦法證實。它也開不了視窗,量不到畫面上的字。這裡的「量」,是讀出面板畫面上實際顯示的文字。
所以它把量不到的四件事交回給我:
回報裡還有一句:「未改量假 view、HTML 原始碼或其他文字來冒充畫面結果。」這是 Day 6 那一格的作用:量不到,就交回來,不要改量別的。
方法名稱不對,A 其實也發現了。它的回報開頭寫著:「NSMenuItem 沒有 itemIdentifier(),但提供 identifier()。」它改用 identifier,找到別的路繼續寫下去;同一條停損,A 沒有停。兩張單各只跑一次,這不能證明哪張單比較好。可是 B 交回來的那張清單,我拿去檢查了已經上線的版本。

清單的第二項,就是 Day 26 加進「做完怎麼算對」的那一行:「對每個畫面按右鍵、把選單每一項點一次……程式自己會重新載入畫面的地方,要在真的網頁元件上跑一次。」
我寫了一支小程式,開一個真的視窗,放進 usage 自己的面板,送一次右鍵,在選單打開前印出每一項;接著點 Reload,再讀面板上的字數。跑的是 v0.22.5 之後一直沒變的那段過濾程式(輸出只留字數):
送資料之後:字數 323
usage 過濾後的右鍵選單:['Reload', '']
點 Reload 之後:字數 0
再送一樣的資料:字數 0
送不一樣的資料:字數 323
Reload 還在。點下去,面板空了。
6 月 22 日的修正,從來沒有拿掉 Reload。這段過濾程式從那天起一行沒改,所以從 v0.22.5 到寫這篇時的 v0.33.2,139 個版本都是這樣。
面板會回來,是靠同一天改的另一處:程式要重新載入時,把原本的 HTML 再交一次。可是 usage 送資料前會先比對,資料跟上次一樣就不送。所以使用者按了 Reload 之後,面板會一直空著,等到送進面板的資料有變化,才回來。

修正明明寫了要拿掉 Reload,為什麼沒拿掉?6 月 22 日的過濾程式,是這樣讀選單項目的:
item_identifier = getattr(item, "itemIdentifier", None)
真的右鍵選單裡,每一項是 Mac 一般的選單項目。系統把選單顯示出來之前,會先通知 App 一次;我在這時把每一項印出來:
'Reload' | class: NSMenuItem | respondsTo itemIdentifier: False | identifier(): WKMenuItemIdentifierReload
沒有 itemIdentifier 這個方法,識別碼放在 identifier。程式讀不到,就當作「不是要拿掉的項目」,一項都沒拿掉。
那測試為什麼是綠的?因為測試用的選單項目是假的,假物件自己寫了一個 itemIdentifier:
class FakeMenuItem: # 節錄
def itemIdentifier(self) -> str | None:
return self.identifier
方法名稱是哪裡來的?6 月 22 日那張任務書的「預期行為」(寫改完應該怎樣的那一格)裡,有一句實作建議:移除 itemIdentifier() 含 Reload 的項目。這個名稱沒有人在真的選單上查過。同一張單的根因那格,還標著「已查清,照此修,勿重新發明」。修的 AI 照著寫,假物件照著寫,測試拿假物件證明自己對。
Day 6 那格寫的是:猜的就寫「猜的」,附上證據指到哪。Day 26、Day 27 講的是:只用假物件驗,不算數。這一次兩個都踩到了:方法名稱是猜的,沒標「猜的」;測試又只拿照著猜寫出來的假物件來驗。
寫這篇時,我在 usage 開了一個分支修它。程式只改一個名稱:itemIdentifier 換成 identifier。測試不再用假物件,改用 Mac 真的選單項目:
item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("", None, "")
item.setIdentifier_("WKMenuItemIdentifierReload")
先只改測試、不改程式,跑一次,紅了(測試失敗):
At index 0 diff: 'WKMenuItemIdentifierReload' != 'WKMenuItemIdentifierCopy'
翻成白話:過濾完,選單第一項還是 Reload。這就是使用者按右鍵會看到的:Reload 還在。改了程式再跑,綠了。CI 在 Mac 上跑的七項檢查,在本機全跑一遍,全過。最後用同一支小程式在真的面板上按右鍵(輸出整理過):
usage 過濾後的右鍵選單:['']
選單裡沒有 Reload 可點;面板字數:323
Reload 不見了,面板還在。
你的 App 如果有網頁畫面,可以叫 AI 寫一支同樣的小程式:開一個真的視窗,在右鍵選單打開前,把每一項的名稱和識別碼印出來。Mac 的 WKWebView(App 裡顯示網頁的元件)會在選單打開前呼叫 willOpenMenu_withEvent_,在裡面印 item.title() 和 item.identifier() 就行。
重點不在印出來的內容,在「真的」兩個字。過濾程式的測試,至少要有一個是拿真的選單項目跑的;假物件長什麼樣,是寫測試的人決定的,真的選單長什麼樣,是系統決定的。
工作單的完整格子,是 Day 7 那張加上 Day 6 的「症狀怎麼量」;「做完怎麼算對」累積的行,明天分組列出:
改哪裡:___(猜的就寫「猜的」,附上證據指到哪)(Day 2、Day 6)
症狀怎麼量:在___量到___,正常應該是___;修完再量同一個地方,貼出前後;
量不到就說量不到,交回來給我量,不要改量別的(Day 6)
哪裡不准動:___,因為動了會___壞掉(Day 3)
已經決定的(不要自己改):___,因為___(Day 7)
做完怎麼算對:
- 你自己驗:___(跑了什麼、結果是什麼貼在回報裡)(Day 4)
- 我自己按:打開___,按___,應該看到___(Day 4)
- 驗不到的:先說哪一類你驗不到,再列出這次落在裡面的項目(Day 4)
- (Day 8 到 Day 27 加的幾行,明天照情境分組)
停損:同一處改超過 3 次,檢查還是不過,就停,回報卡在哪(Day 5)
你自己決定的:工作單沒寫、你自己拿主意的地方,全部列出來(Day 7)
「做完怎麼算對」今天再加一行:
- AI 交回來的「驗不到的」:每一項寫上誰來做、做完貼什麼;沒人做完,就不准回覆使用者「修好了」
6 月 22 日當天,AI 只回報「入口已被移除」,驗不到的那一項「在真的面板上按右鍵」沒被列出來。當天只要有人在真的面板上按過一次,就會看到 Reload 還在。今天 A 重跑同一張單,倒是寫了驗不到;可是那張單沒有一格要人接手。
明天,把 Day 4、Day 8 到今天加進「做完怎麼算對」的每一行,照情境分組,排成一張清單;再拿今天這個修正,照清單從頭驗一次。
b24bd20)
NSMenuItem.identifier(NSUserInterfaceItemIdentification)
WKWebView