您把一個陌生專案交給 Claude Code,得到的回答是:"紀錄儲存在瀏覽器中,保留最近 100 筆,重新載入後也會還原。"這聽起來很具體,似乎可以直接寫進 README。不過,先看看這個回答引用了哪些來源。

我們來檢視一個這樣的判斷:先把需求拆成幾個問題,再讀一小段程式碼,接著確認執行時該觀察哪些行為。最後整理成一份簡短報告,清楚記下哪些部分正常、哪些還需要查證。

聚焦一項需求,不必檢視整個專案

Haiku 50 是一個小型應用程式,會根據關鍵字產生三行詩。使用者選擇語言後取得文字,也能查看自己的生成紀錄。這次只需聚焦一項需求:瀏覽器必須保存最近 100 筆生成紀錄,重新載入頁面後仍可使用。

Haiku 50 示意圖。下文會透過程式碼與情境來檢視應用程式的行為。

這裡的 Haiku 50 是教學範例。程式碼與代理的回答都是為了分析而編寫;我們根據程式碼推斷預期行為,不檢查原始專案封存檔。文章已提供所需內容,您不必下載應用程式、取得 API 金鑰或執行生成作業。

與其要求"解釋這裡的一切如何運作",不如先問三個問題:新的生成結果有沒有寫入儲存空間?應用程式下次開啟時有沒有讀取已保存的資料?紀錄超過 100 筆時會怎麼處理?

給 Claude Code 的第一個請求可以像這樣:

只調查生成紀錄,先不要修改程式碼。
找出從取得新詩到保存詩作的流程,
再找出從開啟頁面到顯示紀錄的流程。
確認在哪裡限制保存紀錄的數量。
每項判斷都附上檔案、行號和簡短程式片段。
另外列出仍須實際執行才能確認的部分。

這樣的請求能明確界定搜尋範圍。如果代理發現紀錄功能還牽涉其他檔案,就能說明為什麼需要查看那些檔案。您只要查證幾個關鍵位置,比起核對整份應用程式摘要輕鬆得多。

找到檔案後,還得讀內容

假設有個教學檔案 history.js。成功生成後,介面會呼叫 addToHistory(item),而 renderHistory 只負責顯示傳入的清單。在這個例子中,沒有其他處理紀錄的函式。

const STORAGE_KEY = "haiku-history";
let history = [];

function addToHistory(item) {
  history.unshift(item);
  localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
  renderHistory(history.slice(0, 100));
}

function startHistory() {
  renderHistory(history);
}
startHistory();

閱讀這段程式碼時,只要分清兩種儲存位置即可。陣列 history 存在已開啟頁面的記憶體中。localStorage 則在瀏覽器中保存網站資料,下次開啟頁面時仍然存在;應用程式必須另外將資料寫入其中,並在之後讀回。這項儲存依附於通訊協定、主機和連接埠,因此查證時要使用相同網址和瀏覽器設定檔。詳細行為請參閱 localStorage 說明。

假設代理這樣回答:

紀錄會保存最近 100 筆生成結果,並在重新載入後還原。
依據:history.js:4-13,
使用了 localStorage 和 slice(0, 100)。

檔案路徑和行號都有了。現在確認它們是否足以支持整段說法。教學檔案的第一行是 const STORAGE_KEY;空白行也會計入行號。

  • 第 5 行的 history.unshift(item) 會把新紀錄加到陣列開頭;
  • 第 6 行呼叫 localStorage.setItem,把整個陣列寫入儲存空間。轉成 JSON 是為了將它保存為字串;
  • 第 7 行的 renderHistory(history.slice(0, 100)) 會把前 100 個項目傳到畫面上。slice 會建立新陣列,不會縮短原陣列,詳情請參閱這個方法的說明;
  • 啟動時,第 2 行的 let history = [] 會建立空陣列,而第 10-13 行的 startHistory 函式及其呼叫會將它顯示在畫面上。這裡沒有從儲存空間讀取資料。

讀完程式碼後,結論就得修正了:程式會寫入紀錄,也只顯示 100 筆;但這個實作並未在啟動時還原資料,也沒有找到限制保存筆數的程式碼。

在實際專案中,這段程式碼可能不足以判斷,因為資料載入功能也許放在其他模組。您可以再問代理:"列出讀取和寫入這個 key 的所有位置,以及哪些地方會呼叫這些函式。"這個教學範例的範圍已經很明確,可以繼續往下看。

查證重新載入後的行為

讀過程式碼後,我們大致知道該從哪裡找出落差。接著設定觀察條件:以一般瀏覽器設定檔開啟同一個網址,允許保存資料,且紀錄一開始是空的。教學情境中成功生成一筆帶有辨識標記 A 的內容;各步驟之間不修改程式碼。

以下是根據程式碼推演的情境流程。我們再加一步:重新載入後,新增一筆 B。

步驟畫面預期haiku-history key 的儲存內容預期
開啟紀錄為空的頁面空清單沒有紀錄
新增生成結果 A一筆紀錄 A包含紀錄 A 的陣列
重新載入同一頁面空清單仍保留包含紀錄 A 的陣列
重新載入後新增生成結果 B只有紀錄 B陣列只剩 B;紀錄 A 被覆寫

實際觀察時,可以在瀏覽器開發者工具的網站資料儲存區查看內容。開啟特定 key 的值,並比對剛才新增的紀錄。只看畫面上的空清單還不夠。

說"重新載入後,所有資料都消失了"並不準確。資料仍留在儲存空間,只是應用程式沒有讀取。在這個實作中,流程在已保存的字串和傳到畫面上的陣列之間中斷了。

接著新增一筆資料,就能看到這個錯誤的後果。重新載入後,記憶體中的陣列是空的,因此只會加入 B;localStorage.setItem 也會用只含一筆紀錄的陣列覆寫已保存的紀錄。需求要求保留 B 和 A,但依照程式碼,最後只剩 B。顯示和紀錄保存都受到影響。

現在可以先寫下程式碼層面的結論:"startHistory 沒有讀取已保存的紀錄;下一次新增資料會覆寫舊紀錄。"若要確認執行時的行為,請依序完成四個步驟,並在每一步比對畫面上的清單與儲存內容。

查證 100 筆的限制

資料還原的部分已經釐清。需求還有另一部分:保存最近 100 筆生成結果。只新增一筆紀錄,無法驗證這個上限。

我們另外設計一個紀錄為空的情境。把 101 筆事先準備好的紀錄依序傳給同一個新增函式,標記從 1 到 101,中間不重新載入頁面。要查證紀錄本身,不必真的向生成器送出 101 次請求;這裡只關心取得現成紀錄後的處理方式。

新紀錄會加到陣列開頭。因此最後一次新增後,畫面會顯示標記 101 到 2,共 100 筆。但保存那一行取得的是完整陣列:儲存空間會留下標記 101 到 1,共 101 筆。筆數和內容都要查證,否則可能看不出應用程式保留的是最舊紀錄,而不是最近的紀錄。

代理的回答就在這裡說得太廣了。程式碼裡確實有數字 100,但它限制的是顯示筆數。根據程式碼,精確的結論應該是:"介面會顯示最近 100 筆紀錄;在這個情境中,儲存空間有 101 筆,因此保存紀錄數量的需求並未達成。"

這個情境查證了應用程式的哪一部分

預先準備好的紀錄能讓我們單獨查證歷史紀錄功能,但無法說明生成服務是否可用、詩作品質如何,或所選語言有沒有正確傳遞。這些需求都需要各自的情境來查證。

整理一份簡短報告

研究結果適合保存在專案旁邊,免得明天又得重讀整段對話。範例檔案取名為 HANDOFF_REVIEW.md。名稱只是示意;重點是報告要能還原查證過程。

以下是閱讀教學程式碼後的報告。預期行為欄位會提示您執行時該觀察什麼:

範圍:生成紀錄,文章中的 history.js 教學版本。
調查方式:閱讀教學程式片段。
未在瀏覽器中執行。

需求:在瀏覽器中保存最近 100 筆生成紀錄,
並在重新載入後顯示。

程式碼依據:
- history.js:6,localStorage.setItem,寫入整個陣列;
- history.js:7,renderHistory(history.slice(0, 100)),
  只限制顯示筆數;
- history.js:10-13,startHistory 及其呼叫,
  顯示紀錄時沒有讀取已保存的資料。

根據程式碼的預期行為:
- 新增後,畫面和儲存空間都會出現紀錄 A;
- 重新載入後,儲存空間仍有 A,但清單會是空的;
- 重新載入後新增 B,會覆寫已保存的紀錄,
  最後只剩 B,而不是 B 和 A;
- 在另一個情境中,從空狀態連續新增 101 筆後,
  畫面有 100 筆(101..2),儲存空間有 101 筆(101..1)。

程式碼中的落差:啟動時不會還原紀錄,
所以下次新增會覆寫先前的紀錄;
已保存的紀錄沒有被限制為最近 100 筆。

尚未查證:生成器是否正常、保存時是否發生錯誤,
以及多個分頁同時操作的情況。

下一步:重現兩個情境並記錄觀察結果。
接著修正啟動時的讀取和保存陣列的限制,
再重複相同的情境。

在自己的報告中,請用已查證的專案版本取代"教學版本",例如註明 commit 以及是否有尚未 commit 的變更。執行後,新增"實際觀察結果"欄位,附上結果或手動操作紀錄。將觀察結果與預期行為比較;如果未能執行,這個欄位就留白,並註明尚未執行。本文已提供所有原始資料;但在大型專案中,只有行號還不足以重現查證過程。

這份報告也能幫助您為代理安排下一項工作。現在有兩個具體落差可供處理,而不是只說"讓紀錄更可靠"。同時也能看出哪些結論現在下還太早。

自己查證這項判斷

我們在相同的教學實作中加入清除按鈕。它的事件處理函式如下:

function clearHistory() {
  history = [];
  renderHistory(history);
}

按下按鈕前,清單和儲存空間中都有紀錄 A。按下後,清單變成空的。代理寫道:"按鈕會完整刪除已保存的紀錄。"

哪些結論有程式碼支持:紀錄從畫面消失、紀錄從 localStorage 刪除,還是兩者都有?接下來還該查證什麼?請先試著回答,再看解析。

解析

目前只能確認清單從畫面消失。程式碼也顯示記憶體中的陣列被清空了。不過,事件處理函式沒有修改 localStorage,而 renderHistory 依照前提只會繪製清單,因此已保存的 A 仍然留著。

下一步是在按下按鈕後,立即讀取 haiku-history key 的值。實作紀錄還原功能後,也可以重新載入頁面,確認已刪除的紀錄沒有再出現。原始版本即使不清除紀錄,啟動時也會顯示空清單,所以只重新載入一次無法驗證清除是否成功。這個干擾不太容易察覺。

如何向 Claude Code 提出這類任務

在實際專案中,先挑出一項會影響您下一步決策的說法。例如,紀錄功能目前能不能用,還是應該先修正資料還原。接著指定代理的回答格式,方便您開啟來源並查證結論:

請查證這項說法:[應用程式的具體行為]。
先找出相關檔案並閱讀必要的程式碼。
先不要修改程式碼。

請回覆:
1. 程式碼支持哪些結論:檔案、行號、簡短片段。
2. 哪個情境可以查證執行時的行為。
3. 實際執行了什麼,以及觀察到什麼。
4. 明確結論和仍未知的部分。

如果無法執行,請直接說明。
不要把建議的情境寫成已完成的查證。

收到回答後,開啟關鍵結論的來源,比對觀察結果與需求,確認後再把結果寫進筆記或文件。如果來源不足,下一個請求也很明確:找出讀取紀錄的位置、查閱 key 的值,再重做新增 101 筆的情境。這樣就能透過一連串可查證的小步驟推進調查。

如果只是詢問函式名稱,附上程式碼位置就夠了。如果回答涉及資料保存、還原或數量限制,就值得一路追查相關行為。查證範圍取決於您打算下什麼結論。