你叫 AI 發一個新版。它回報:全部完成,檢查全綠。
你大概不會再去 GitHub 看一眼。它都說全綠了。
可是一個專案裡的檢查,常常不只一組。AI 說的全綠,可能只是它盯著的那一組。
usage 是我做的開源小工具,放在 Mac 螢幕最上面那排選單列,顯示 Claude Code、Codex 這幾家 AI 工具的額度。它原本只有 Mac 版。
7 月 15 日早上 7 點半,Windows 版做完,推上 GitHub。7 點 44 分發布成 v0.28.0,更新說明寫著「完整支援 Windows」。
發版時會自動跑一套發版流程,Claude 在旁邊盯著,修到四個步驟全綠。發版完成,Claude 的回報開頭是:
全部完成,v0.28.0 已發版。Windows 版可以下載了。
後面附了證據,其中兩行是:發版流程四個步驟全綠;本機驗證,822 個測試全過。
那天下午 4 點 25 分,我在 Windows 電腦上打開它,跳出錯誤。我問 Claude,錯誤要怎麼傳給它看。
usage 放在 GitHub 上。每次推上新的程式,GitHub 會自動跑一輪檢查(這叫 CI,自動檢查):格式對不對、測試過不過。跑完在那次改動旁邊亮一顆燈,綠的是過了,紅的是沒過。
usage 的 CI 有兩組:一組在 Mac 上跑,一組在 Windows 上跑。Windows 那組,是當天早上跟 Windows 版一起加上去的。
Windows 那組第一次跑,是早上 7 點 36 分,v0.28.0 發布前 8 分鐘。結果是紅的,之後一路紅到晚上 7 點半。
我打開 Windows 版跳錯的時候,這顆燈已經紅了快 9 小時。
Claude 說的全綠沒有說錯。它盯的是另一組燈:發版流程。
發版流程有四個步驟:先跑一次測試,過了才打包 Mac 版、打包 Windows 版、更新 Homebrew(Mac 上裝軟體的工具)。
問題在第一步。發版流程的測試,只在 Mac 上跑。打包 Windows 版那一步,只負責把程式包成安裝檔、放上下載頁,沒有在 Windows 上跑任何一個測試。
Claude 附的那兩行證據,兩行都是 Mac:一行是發版流程在 Mac 上跑的測試,一行是在我的 Mac 上跑的 822 個測試。那天在 Windows 上跑過測試的,只有 CI 那顆紅燈。

點開那顆紅燈,6 個測試檔在載入時就失敗了,錯誤幾乎都是同一句:No module named 'objc',找不到一個叫 objc 的套件。
objc 是 Mac 專用的套件,Python 要呼叫 Mac 系統功能時靠它,Windows 上根本裝不起來。錯誤訊息還印出了是誰要它:Windows 版一啟動,就連帶載入一段只有 Mac 版用得到的程式,那段程式要 objc。Windows 上找不到,當場失敗。
6 個測試檔連載入都過不了,整組測試沒開始跑。這顆燈紅,不是哪個數字算錯,是 Windows 版在 Windows 上連啟動都過不了。
晚上 6 點半,把原因講出來的,是一位第一次來這個專案的開發者。對方送了一個 PR(改好的程式,請你合併),說明第一段寫:Windows 那組檢查從加上去那天就是紅的;發布出去的 Windows 安裝檔,根本啟動不了。對方查到的原因,就是紅燈裡印的那條:Windows 版一啟動就去找 objc。
我把這個 PR 交給 Claude 處理。Claude 回報進度時,有一句是這樣說的:
其中一個剛才失敗,因為 check-windows 本來就紅,屬預期內。
check-windows 就是 Windows 那組檢查。這個 PR 跑 CI 也紅了,Claude 覺得正常,因為 main(專案的主線程式碼,發版都從這裡出)上那顆本來就是紅的。
一顆燈從第一次跑就是紅的,所有人,包括 AI,很容易把紅當成它的底色。之後它再紅,就沒有人會去點開看。
它蓋住的還不只一件事。啟動的問題修好,測試終於在 Windows 上跑起來,底下又冒出 6 個失敗。usage 會列出每個專案各用了多少額度;在 Windows 上,專案名稱會顯示成一長串路徑,像 Users-runneradmin-AppData-...-alpha,而不是 alpha。這個 bug 早就在 main 上,只是測試從來沒在 Windows 上跑到那裡。
兩個問題都修好,燈在晚上 7 點 31 分轉綠,8 分鐘後發了修好的 v0.28.1。從第一次紅到轉綠,11 小時 55 分。
7 月 15 日不是特例。回頭數整個專案:5 月 17 日到 9 月 22 日,usage 的 CI 跑了 987 次(每推一次程式就跑一次,推到 PR 上也算),紅了 76 次。其中 22 次亮在還沒合併的 PR 上,改動還沒進 main;另外 54 次,是程式已經推上 main 之後才亮的。
這兩種紅燈,意思不一樣。亮在 PR 上,改動還在門外,燈紅了可以先不合併,main 就不會被動到,這時 CI 是一道門。推上 main 之後才亮,程式已經進去了,有時已經發出去了,燈只能告訴你「剛剛那次壞了」,這時 CI 是一則通知。
這個專案大部分改動是直接推上 main、直接發版,所以大部分的燈都是通知。

通知只要有人讀,修得也快:推上去才亮的紅燈,多半十分鐘內就修回綠。7 月 15 日那顆紅了將近 12 小時,是最久的一次。那天我和 Claude 盯著的,都是發版流程。
通知有沒有用,看有沒有人去讀它。
AI 回報全綠的時候,多問一句:是哪幾顆燈?叫它把這次推上去的那個版本,在 GitHub 上跑過的每一項檢查都列出來:
把這次推上去的版本,在 GitHub 上跑過的每一項檢查列出來:
名字、在哪個系統上跑、結果。有紅的,貼出錯誤訊息的第一段。
然後兩條規矩:
工作單(交給 AI 的任務說明)裡有一格「做完怎麼算對」,Day 4、Day 8、Day 9 各在這格加了幾行。今天再加兩行:
做完怎麼算對:
- (Day 4、Day 8、Day 9 加的幾行)
- 推上去之後:列出這個版本在 GitHub 上跑的每一項檢查(名字、在哪個系統跑、結果),貼在回報裡;有一項紅就不算完成,先貼那顆紅燈錯誤訊息的第一段
- 發版前:同一個版本的每一項檢查都綠了才准發;新加的檢查第一次就紅,當天查清楚、修綠,不准當成「本來就紅」
今天講的是推上去之後,燈亮了要有人讀。明天往前一步,看推上去之前:8 月 9 日深夜,一張工作單的驗收只寫了「跑 pytest 全部通過」。AI 回報 1468 個測試全過,推上去,CI 的格式檢查報了 12 個錯。AI 說的「全過」,是照哪張清單驗的?