分數計算修好了。回覆裡附了終端機輸出、計分板截圖和一句簡短的"完成",檔案一大堆,問題其實很簡單:哪些內容能證明需求已達成?

佐證資料不是剛好放在修正旁邊的任意檔案,而是某項具體檢查的結果,能看出檢查了什麼、當時的程式碼狀態,以及最後得到什麼結果。不同資料回答不同問題,沒有任何一種能一次說明全部。

本文會說明如何把需求轉成可驗證的敘述、指令執行記錄該寫些什麼,以及日誌、diff 和畫面截圖各自能證明什麼。接著,我們會把這些內容整理成六個欄位的精簡紀錄 EVIDENCE_LOG,再看看一組現成資料是否足以佐證結果。

驗收標準決定要檢查什麼

全文會用同一個例子:一款有計分板的小型獎勵遊戲。開始時分數是 0,三次配對應該加 3 分,修正前卻加了 30 分。這個計分規則是為這款遊戲設計的,其他專案可能完全不同,不要直接套用。

"分數已修正"這句話無從驗證,因為不知道該執行什麼、該看哪裡。可驗證的敘述由三部分組成:初始狀態、執行動作,以及預期觀察結果。比如:"分數為 0 時,配對三次會增加 3 分,計分板顯示 3。"這樣就有明確的檢查方式了。

接著要把這個敘述拆成幾項觀察。計算結果和玩家看到的畫面是兩回事,得用不同方式佐證。表格第三列談的是排行榜是否保存分數;需求未必包含這一項,但如果有,就要有對應的觀察結果。

驗收標準佐證方式結論的界線
計算:分數為 0 時,配對三次得到 3 分在修正後的程式碼上執行測試,並記下測試名稱和結果能說明程式碼中的該情境已測過,但不能說明玩家看到什麼
介面:配對三次後,計分板顯示 3重現情境的記錄和畫面截圖能證明畫面上的數字,但無法說明數字從何而來
結果會儲存在排行榜另外執行一個情境,並讀回已儲存的數值計分板截圖完全無法證明這點;沒有這種情境就代表尚未驗證

這裡最有用的是第三欄。佐證資料能證明的,幾乎總是比我們希望的少;界線就在實際執行的動作結束之處。在這個例子裡,計算測試沒有檢查計分板,而計分板截圖也看不到哪一行程式碼被修改。

還要分清兩件事:需求描述最後應該成立的條件,結果則是實際得到的狀況。如果把兩者混在同一段裡,"應該增加 3 分"就會不知不覺變成"增加了 3 分",即使根本沒執行過任何檢查。

指令與執行結果

要描述一次檢查的執行過程,得記下五件事。對一週後要重做檢查的人來說,這些資訊可都不會自動出現:

  • 執行指令時所在的資料夾;
  • 指令原文;
  • 程式碼狀態:修正前或修正後,以及有哪些尚未 commit 的變更;
  • 執行結果:結束代碼和最終統計數字;
  • 哪些檢查實際有執行,哪些沒有。

要分清"請執行 npm test"和"我執行了 npm test,這是輸出"。前者是指示,是之後要做的事;後者才是事實。尤其回覆裡仔細列出應該檢查什麼時,很容易把意圖說成結果。

npm test 並不會執行"所有測試"。它只會執行目前選定的 npm 專案的 package.json 中,scripts 區段裡 test 欄位所指定的內容。為了清楚知道選到哪個專案,請在專案根目錄執行指令,並把資料夾記錄下來。另一個專案也可能使用相同名稱,背後卻是不同工具、不同步驟,甚至什麼都沒有。

指令結束時會回傳結束代碼:0 通常表示"工具認為執行成功"。在 bash 中,執行完指令後可立即用 echo $? 查看;在 PowerShell 中,像 npm 這類外部程式的結束代碼則是 $LASTEXITCODE。但代碼為 0 不代表目標測試真的有執行:它可能被跳過、依名稱篩掉,甚至根本沒寫。因此,除了結束代碼,也要記下統計數字和您關心的測試名稱。

可以向 Claude Code 詢問什麼

請它提供實際執行的內容:指令、資料夾、完整原始輸出,並另外列出原定執行但沒能完成的項目。指令要以專案內的設定為準,不要照抄通用速查表:查看 package.json 就能知道這個專案的 npm test 實際執行什麼。

從日誌中保留什麼

執行輸出可能很長,不會整份搬進紀錄裡。精簡片段只要保留四項:失敗的測試名稱、預期值與實際值、失敗原因,以及最終統計。其他內容留在連結所指向的檔案中。以下是縮短的教學用輸出範例,不是實際執行的紀錄。

✖ three matches give 3 points (2.4108ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
  30 !== 3
      at TestContext.<anonymous> (src/bonus-game/score.test.js:8:10)
ℹ tests 1
ℹ pass 0
ℹ fail 1
ℹ skipped 0

這只是輸出片段,完整輸出另存於 logs/test-fail.txt 之類的檔案。光看這段就能掌握重點:失敗的是配對三次的測試,預期值是 3,實際得到 30,沒有跳過任何檢查。

原始輸出應直接放進紀錄。用自己的話重述("測試因乘數錯誤而失敗")看起來比較好懂,但那已經是推論,不是觀察結果,無法從日誌核對。把兩者放在一起:先列出工具輸出的原文,再補上您的說明。

堆疊追蹤中的位置指出檢查在哪裡觸發,也就是測試檔案。失敗原因要從遊戲程式碼中找;在程式碼裡找到的乘數是閱讀程式碼的結果,不是日誌中的一行。在紀錄裡要分別標示這兩種來源,否則之後會搞不清楚數字 10 是從哪裡來的。

diff 能證明什麼

Diff 顯示檔案在兩種狀態之間的差異。它無法證明修正後的程式碼曾經執行過,更不能證明行為已如預期改變。

--- a/src/bonus-game/score.js
+++ b/src/bonus-game/score.js
@@ -1,5 +1,5 @@
-const POINTS_PER_MATCH = 10;
+const POINTS_PER_MATCH = 1;

function calcScore(matches) {
   return matches * POINTS_PER_MATCH;
}

這類片段下方需要一行說明,指出比較了哪些狀態。例如:"工作目錄與修正前的 commit a1b2c3d 比較,指令為 git diff HEAD";這裡的識別碼是教學用的,實際紀錄要填自己的識別碼。這個指令會比較目前受追蹤的檔案與 HEAD,也包含已加入暫存區、準備 commit 的變更。一般不帶 HEAD 的 git diff 只會顯示工作目錄與暫存區的差異,在執行 git add 後可能就會是空的。沒有說明的 diff 看起來只是"某些東西和某些東西的差異",也就無法重做比較。

另外也要確認測試的預期值仍然是 3。如果程式碼和預期數字一起改了,那就是另一回事:檢查是在配合現有行為,而不是讓行為符合需求。

接下來是個容易弄錯的小細節。修正尚未 commit 時,可以比較工作目錄和最新的 commit。commit 之後,同樣的比較會顯示空白,但這不代表專案沒有變更,只是變更已進入歷史紀錄,此時要比較該 commit 和前一個狀態。空的 diff 本身不能證明任何事,重點是您比較了哪兩種狀態。

螢幕截圖能顯示什麼

狀態與動作

計分板上顯示 3 的截圖,只能證明一件事:拍攝當下計分板上的數字是 3。這和"配對三次增加了 3 分"並不相同。兩者之間還隔著截圖裡看不到的資訊:一開始的分數是多少,以及實際做了什麼。

要讓截圖成為佐證,請一併記下重現情境:

  1. 開啟了什麼:頁面網址或啟動方式,以及使用的是修正前還是修正後的版本;
  2. 初始狀態:起始分數為 0,沒有載入舊資料;
  3. 動作:完成三次配對;
  4. 觀察結果:計分板顯示 3,並在動作完成後立即截圖。

沒有這些記錄,同一張截圖可能有許多種取得方式。昨天就開著、仍顯示舊版的分頁,使用了不同的啟動方式,或原本就不是零的分數,都可能產生看似可信、實際上卻毫無證明力的畫面。

本文刻意不附上計分板截圖。用來驗證的截圖,應在指定版本上重現情境後再拍攝,可以手動操作,也可以用自動化工具;為了插圖而繪製或拼出的計分板,即使看起來一模一樣,也不能當作佐證。您要拍自己的畫面時,請完整截下計分板、在情境記錄中寫下頁面網址,並把截圖和記錄放在一起,不然一週後可能就想不起來那次是怎麼啟動的。

模型能從畫面讀出什麼

您可以把圖片傳到工作階段,請模型讀取內容。怎麼問很重要:請它說出畫面上看到的數字,並列出不確定之處("看得出初始狀態嗎?"、"能判斷這是哪個版本嗎?"),而不是要求它確認您預設的結論。若您只說"確認 bug 已修正",很容易得到客氣的認同,卻沒有真正檢查任何事。

能否傳送圖片,取決於您使用的模型和環境。在 Claude Code 中,您可以提供已儲存圖片的路徑,請它讀取圖片。如果圖片載入失敗,先檢查路徑和檔案是否可存取;光是更換模型不保證問題會解決。

請對照圖片核實模型的讀取結果

Claude Code 對圖片的回答是對畫面的解讀,仍要對照圖片本身核實。圖片分析可能會看錯數字,或補出畫面中不存在的細節。最重要的是,光看截圖無法判斷哪一行程式碼已修正。計分板顯示的是結果,不是原因。

修正前後使用同一個情境

有用的一組結果應該是:同一項檢查在有問題的實作上失敗,在修正後的實作上通過。失敗表示這項檢查確實能抓到缺陷;通過則表示修正後的程式碼在該情境中已沒有這個缺陷。

這樣比較有個前提:輸入資料和預期結果都必須相同。起始分數相同、配對次數相同,預期值也同樣是 3。以下是修正後同一項測試的通過結果:

✔ three matches give 3 points (1.3983ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0
ℹ skipped 0

兩次執行之間只改變實作,測試、輸入資料、預期結果和執行條件都保持不變。如果不得不修改需求或測試的預期值,就要用這項檢查重新跑一組結果;只靠文字解釋,不能取代在兩種狀態下實際執行。此時若只說"之前是紅燈,現在變綠燈",會造成誤解。

另外,兩個名為 before.txt 和 after.txt 的檔案本身也不能保證任何事。檔名不是佐證,要檢查內容:指令、測試和預期結果是否相同。還原有問題的狀態(也就是使用 git stash 的做法)是另一項有自己規則的檢查;此處只要記住,修正前的失敗結果應來自實際執行,而不是憑記憶描述。

精簡版 EVIDENCE_LOG

EVIDENCE_LOG 在本文中指一份一般的文字紀錄,包含六個欄位,順序如下:症狀、日誌線索、故障狀態下的失敗、修正內容、修正後的成功結果、螢幕截圖。這樣比較容易對照症狀、修正和結果。欄位順序本身並不能保證紀錄可信。

前面整理的內容都能放進這些欄位。指令和資料夾記在失敗欄位,比較用的狀態放在修正欄位,完整檔案的連結則附在對應資料旁邊。不必另外建立一整套文件。

以下是教學用的填寫範例,展示每個欄位該寫什麼,並不是已完成檢查的報告。請在自己的紀錄中填入實際結果。

症狀:獎勵遊戲開始分數為 0 時,配對三次會得到 30 分,而非 3 分。每次都能重現:開啟遊戲、完成三次配對、查看計分板。

日誌線索:執行輸出中的"30 !== 3"出現在測試 "three matches give 3 points",完整輸出位於 logs/test-fail.txt。
另外,從程式碼讀到:src/bonus-game/score.js 中的乘數 POINTS_PER_MATCH 為 10。這是閱讀程式碼的結果;測試輸出中沒有數字 10。

故障狀態下的失敗:HEAD a1b2c3d(教學用識別碼),工作目錄乾淨;這個 commit 包含有問題的計算和測試。
資料夾為 /project/bonus-game,指令為 npm test。
package.json 中 scripts.test = node --test。失敗測試為 "three matches give 3 points",原因是 "30 !== 3";tests 1、pass 0、fail 1、skipped 0,結束代碼為 1。
上方是輸出片段,完整輸出位於 logs/test-fail.txt。

修正內容:檔案 src/bonus-game/score.js 中的 POINTS_PER_MATCH 從 10 改為 1;沒有其他變更。HEAD 仍是 a1b2c3d。
Diff:git diff HEAD,比較工作目錄和這個 commit;已儲存至 diffs/score-fix.diff。
測試的預期值維持原本的 3。

修正後的成功結果:在修正後的程式碼中,從同一個資料夾執行相同指令 npm test。同一項測試通過:tests 1、pass 1、fail 0、skipped 0,結束代碼為 0。
完整輸出位於 logs/test-pass.txt。
沒有執行配對五次的情境。

螢幕截圖:screenshots/bonus-score-3.png。修正後,從同一個工作目錄重新開啟遊戲,網址為 http://localhost:3000。起始分數為 0,完成三次配對後,計分板顯示 3。尚未檢查結果是否保存到排行榜。

範例中的路徑只是示意。完整日誌、diff 和圖片放在專案已有的類似位置即可,不必另外建立固定的資料夾結構。

請留意第 5 和第 6 個欄位各自的最後一行。沒做的事就直接用一句話標明:"未執行"、"未檢查"。這不是承認偷懶,而是清楚標示結論的界線。少了這些句子,紀錄看起來似乎更完整,實際上卻對沒做的事隻字未提。

自我檢查

這項需求有兩個驗收標準:起始分數為零時,配對三次會得到 3 分;完成這三次配對後,介面顯示 3。手邊有六份資料:

  1. 代理的訊息:"完成,bug 已修正";
  2. 執行 npm test 的指示;
  3. 同一項測試的實際執行結果:修正前失敗,修正後通過;
  4. 包含乘數變更的 diff;
  5. 只有一張顯示數字 3 的截圖,沒有啟動方式的說明;
  6. 重現情境的記錄,以及與之對應的截圖。

哪些資料足以證明這兩項標準?還有哪些部分尚未驗證?先想想答案,再往下看。

解析:第 3 項資料能證明第一項標準,因為它是同一項測試實際執行前後的一組結果。第 4 項的 diff 說明程式碼改了什麼,但不能取代測試執行,因為修改和執行是兩回事。第 6 項資料能證明第二項標準:截圖搭配情境記錄,可以看出初始狀態和執行動作。第 1、2 項都無法證明任何事:完成訊息沒有可驗證的佐證,指示則描述的是未來要做的動作。第 5 項只能顯示數字 3,無法確認分數是怎麼增加的。還有,即使六份資料都在手邊,排行榜是否保存結果仍未驗證:其中沒有任何情境讀回已儲存的數值。

您也可以在工作階段中整理這份紀錄。以下是可直接使用的 prompt:

請依照六個欄位整理 EVIDENCE_LOG。每項結論都要標明
實際檢查方式、程式碼狀態和結果來源。
另外註明哪些項目沒有執行。描述截圖時只說明
畫面上看得到的內容,不要據此推斷內部計算正確。

如何使用 Claude Code 整理佐證資料

工作流程大致如下:

  1. 代理將每項驗收標準連結到具體檢查:要執行什麼、使用哪種程式碼狀態,以及如何判斷結果;
  2. 在目前環境中執行可用的檢查,並提供指令和原始輸出;
  3. 附上觀察結果:輸出片段、完整檔案的連結,以及重現情境的說明;
  4. 明確說明證據的界線:哪些項目尚未執行,以及根據現有資料不能斷言什麼。

您仍要親自檢查佐證資料:開啟附上的檔案看看內容、確認情境符合驗收標準的描述,並針對看不出檢查紀錄的項目提出一個尖銳的問題。通常這樣就足以分辨真正整理出的證據,和包裝得很漂亮的轉述。

建議

檔案數量再多,也不能取代缺少的檢查。十份附件如果都沒有測過目標行為,證明力和零份附件一樣。三筆簡短紀錄,只要看得出指令、狀態和結果,就更有用。


技術參考:git diff、git stash、Node.js test runner、npm test、Claude Code 中的圖片。