「這個畫面是英文」:當模糊的人類回報遇到太過聰明的 AI 排查
機上盒的設定畫面,自動測試測不到焦點和遙控器按鍵——一個按鍵落到哪個選項、焦點會不會在 dialog 關掉後漂走,這些只有在實機上拿遙控器按才知道。所以這個專案的驗收方式是實機 walkthrough:我坐在電視前面操作遙控器、口述畫面發生了什麼,Claude 在另一頭抓 adb logcat,把我說的「發生了什麼」跟 log 裡的軌跡對上。對上了,這一項才算過關。
這個分工有一個沒明講的前提:我口述的情報,是 Claude 決定去 log 裡找什麼的唯一線索。
我說的「這個畫面」,被接成了手邊那個畫面
walkthrough 走到工程認證那一帶。畫面的流動是:選「搜尋頻道」→ 跳出工程認證密碼輸入框(要輸入 Master Key)→ 通過 → 進入掃頻畫面。
我在某個當口看到一個畫面是英文,回報:「這個畫面是英文。」
我沒有指名是哪個畫面。Claude 當時手上正在測的是工程認證(密碼輸入框),於是把這句話接了上去,在測項紀錄裡寫成:工程認證密碼框在中文模式顯示英文。
接下來一整輪排查,都繞著「工程認證密碼框為什麼是英文」這個問題打轉。
根因:在一個不存在的 bug 上,分析得頭頭是道
Claude 抱著「密碼框為什麼英文」這個前提開始查。app 的語系明明是中文——設定清單的標題、七個選項的標籤全是中文,系統 persist.sys.locale = zh-TW。那為什麼獨獨密碼框英文?它列了五個假設,逐一驗證,全部排除:
| 假設 | 結果 | 證據 |
|---|---|---|
| 密碼框的字串缺中文翻譯 | 排除 | values-zh-rTW/strings.xml 有 工程認證密碼、請輸入工程認證密碼 |
| 誤用了鎖系統語系的 API | 排除 | 設定元件全用 appStringResource,grep 不到另一個會鎖系統語系的寫法 |
| 是某個 build variant 的資源覆蓋 | 排除 | 只有 src/main/res,沒有 variant 專屬的資源目錄 |
| 密碼框在獨立 window、拿不到語系 provider | 排除 | 它是設定頁內的 inline overlay,不是獨立 window |
| 打包時把中文值掉掉了 | 排除 | aapt dump --values 確認 APK 裡 engineering_authorization 同時帶著 default="Authorization" 和 zh-rTW="工程認證密碼" |
五個假設全排除之後,它下了這段結論:
同一個 SettingsScreen composition、同一個 LocalAppResources provider、同一個 appStringResource helper:
appStringResource(R.string.setting_system_status)→ 中文(清單證實)= provider 解析到 zh-rTW;appStringResource(R.string.engineering_authorization)(PinAuthDialog title,SettingsScreen.kt:204)→ 英文(實機)= 解析到 default。APK 兩個字串的 zh-rTW config 都帶中文值。同 provider、同 Resources,一個解析中文一個英文——靜態分析層面絕對矛盾,指向 Compose 層微妙的 runtime resolution / CompositionLocal / recomposition / configurationContext 邊界案例,需 runtime logging 才能定論。
這段分析讀起來嚴謹、專業、有邏輯支撐。它還附了一份五步的 debug session 計畫,打算去查 Compose 的 CompositionLocal、recomposition、configurationContext。
它唯一的問題是——前提錯了。密碼框根本是中文。這五個假設的排除也全部都是對的:密碼框的字串確實有中文翻譯、確實沒用錯 API、確實沒被 variant 覆蓋。過程沒有出錯,錯的只有起點——那個「密碼框是英文」的前提,從頭到尾沒被檢驗過。
一行 log 推翻了整個前提
Claude 沒有直接照著 debug 計畫去深挖 Compose 的 runtime,而是先做了一件便宜得多的事:加一行臨時 log,在密碼框打開的那一瞬間,把它實際解析出來的字串值印出來。
實機上一跑,一行 log 定局:
PROBE_B site=engAuth provided=true locale=zh_TW engauth=工程認證密碼 sysstatus=系統狀態
provided=true,資源 provider 有送到;locale=zh_TW,當下語系是中文;engauth=工程認證密碼——密碼框的標題在實機上解析出來,是中文。
「絕對矛盾」當場崩塌。根本沒有矛盾,因為密碼框從來沒有英文過。那個「靜態分析層面絕對矛盾、指向 Compose runtime 邊界案例」的判斷,是在回答一個不存在的問題。
臨時 log 用完已經 revert,working tree 乾淨。
倒推回去,英文在下一個畫面
前提一倒,問題反而清楚了:密碼框確定是中文,那我當時說的「這個畫面是英文」,指的是哪個畫面?
畫面的流動裡,密碼框通過之後,緊接著就是掃頻畫面。查掃頻畫面的字串——16 個 scan_* 字串,default 英文都在,zh-rTW 全缺:
scan_dialog_title / scan_btn_close / scan_btn_search
scan_field_frequency / scan_field_mode / scan_field_nid / scan_field_qam / scan_field_symbol_rate
scan_mode_auto / scan_panel_progress / scan_panel_status
scan_status_done / scan_status_failed / scan_status_saving / scan_status_scanning / scan_status_tuning
純粹的缺譯,不是什麼 runtime resolution bug。我那天看到的英文畫面,是掃頻畫面,不是密碼框。
解決方案
掃頻畫面補上 16 條 zh-rTW 中譯(其中 scan_status_scanning 帶格式參數 掃描中 %1$d%%,不能寫壞)。aapt2 dump 確認中英同構:
() "Scanning %1$d%%"
(zh-rTW) "掃描中 %1$d%%"
但補譯只是症狀。真正的根,是我在回報時省略了「哪個畫面」三個字,而 Claude 沒有回問,直接用「手上正在測的那個畫面」把這個缺口填了起來。我的省略跟它的填補,兩邊都合情合理——人對話時假設對方跟自己看著同一個畫面,而 AI 用最可能的對象(當下正在測的那個)補上模糊的指涉,這個賭注大部分時候是贏的。這次剛好輸了,而且輸得讓一輪縝密的排查全空轉。
這不是哪一邊的單方面失誤。是我製造了模糊,它把模糊放大成了一個看起來值得認真對待的 bug。
附帶收穫
log 只回答你問的問題
整件事裡,log 從頭到尾沒有說謊。但「log 不會腦補」這句話,藏了一個它做不到的事——它不會主動告訴你真相,它只回答你問的問題。
最弔詭的一刻,是排查中段:Claude 去讀密碼框的 log,看到的當然是中文(因為密碼框本來就是中文)。這個中文的 log,反而成了「絕對矛盾」這個錯覺的燃料——「清單中文、密碼框也中文,那實機上密碼框為什麼英文?一定是有什麼 runtime 的鬼」。log 誠實地報告了密碼框是中文,問的卻是「密碼框為什麼英文」,於是誠實的答案餵養了一個錯的前提。
直到那行 probe 直接觀測密碼框當下解析出的值——不是從症狀推論,而是去看它真正吐出什麼——log 才把前提本身戳破。
教訓不是「log 很可靠」。log 確實可靠,可靠到它會忠實地配合你問錯的問題。在「人操作、AI 對 log」這種模式裡,對 log 之前,得先確認彼此講的是同一個畫面。
一則沒進紀錄的軼聞
以下這段不在任何過程紀錄裡——對 Claude 而言,這件事得到確認就直接過關了,不值得寫下來。它只留在我的記憶裡,因為出錯的是我。我事後憶起,憑的是記憶,沒有 log 或筆記的逐字佐證,細節可能不精確。留下它,是因為它說明了這整套流程裡某個沒被紀錄捕捉的東西。
同一類型的事,同一輪 walkthrough 裡還發生過一次,方向相反。
測項的流程是這樣跑的:Claude 把某個測項要做的步驟念給我,我拿遙控器操作、做完回報,它去對 log。某一項,我看漏了幾個步驟,做到一半發現,於是回頭把漏掉的補做了一遍。
我以為補完就好了。Claude 對 log 的時候,看到這一項的軌跡跑了兩遍,反過來問我:為什麼兩遍?
我這才招認:我剛剛做了兩遍。
log 跟我的記憶對不上的那一刻,是 log 對了。我看漏、又補做,以為無痕,但 log 把兩遍都記了下來。這一項就是這樣對上、才過關的。
這件事之所以沒進任何紀錄,恰恰是它值得寫下來的理由:對 Claude 來說,「使用者多做了一遍、確認完畢」只是一個中途的雜訊,得到解釋就消化掉了,不值得寫進筆記。但對我來說它印象最深,因為那是我出的錯。一個只活在人記憶裡、因而不會出現在 AI 生成內容裡的失誤——把這種事寫出來,本身就反向證明了這套流程裡有「人」真的在場、真的會犯只有人才會犯的錯。純 AI 生成的文章,不會有這一則,因為它從來不會被記得。