Lỗi tính điểm đã được sửa. Câu trả lời có đầu ra terminal, ảnh chụp bảng điểm và một câu "xong rồi" ngắn gọn; tệp thì nhiều, còn câu hỏi rất đơn giản: thứ nào trong số đó đáp ứng yêu cầu của nhiệm vụ?
Bằng chứng không phải là bất kỳ tệp nào nằm cạnh bản sửa. Đó là kết quả của một lần kiểm tra cụ thể, cho biết đã kiểm tra điều gì, trên trạng thái mã nào và thu được kết quả ra sao. Mỗi loại tài liệu trả lời những câu hỏi khác nhau, không loại nào trả lời được tất cả.
Ta sẽ xem cách biến yêu cầu thành một khẳng định có thể kiểm tra, cần ghi lại gì về lần chạy lệnh, log xác nhận điều gì, diff xác nhận điều gì và ảnh màn hình cho thấy điều gì. Sau đó, ta sẽ gom chúng vào nhật ký ngắn EVIDENCE_LOG gồm sáu trường và đánh giá xem một bộ tài liệu mẫu đã đủ chưa.

Tiêu chí xác định cách kiểm tra
Ví dụ xuyên suốt bài viết là một trò chơi bonus nhỏ có bảng điểm. Điểm ban đầu là 0, ba lần ghép trùng phải cộng thêm 3 điểm, nhưng trước khi sửa thì cộng 30. Quy tắc tính điểm này chỉ dành cho trò chơi này; dự án khác có thể dùng quy tắc hoàn toàn khác, đừng áp dụng máy móc sang đó.
Không thể kiểm tra câu "đã sửa điểm": không rõ cần chạy gì và nhìn vào đâu. Khẳng định có thể kiểm tra gồm ba phần: trạng thái ban đầu, hành động và điều dự kiến quan sát được. Ta có thể viết: "Khi điểm đang là 0, ba lần ghép trùng sẽ cộng 3 điểm và bảng điểm hiển thị số 3". Giờ đây, khẳng định này có cách kiểm tra cụ thể.
Tiếp theo, ta tách khẳng định đó thành các quan sát riêng. Phép tính và những gì người chơi nhìn thấy là hai việc khác nhau, cần bằng chứng khác nhau. Hàng thứ ba của bảng nói về việc ghi điểm vào bảng xếp hạng. Nhiệm vụ có thể không yêu cầu điều này, nhưng nếu có thì cũng cần quan sát riêng.
| Tiêu chí | Bằng chứng | Giới hạn của kết luận |
|---|---|---|
| phép tính: ba lần ghép trùng khi điểm đang là 0 cho kết quả 3 điểm | chạy test trên mã đã sửa, kèm tên test và kết quả | nói lên kịch bản đã được kiểm tra trong mã, nhưng không cho biết người chơi nhìn thấy gì |
| giao diện: bảng điểm hiển thị 3 sau ba lần ghép trùng | bản ghi kịch bản đã thực hiện kèm ảnh chụp màn hình | xác nhận con số hiển thị, nhưng không giải thích con số đó từ đâu mà có |
| kết quả được lưu vào bảng xếp hạng | kịch bản riêng, đọc lại giá trị đã lưu | ảnh bảng điểm hoàn toàn không xác nhận được điều này; nếu thiếu kịch bản đó thì tiêu chí vẫn chưa được kiểm tra |
Cột thứ ba là phần hữu ích nhất. Bằng chứng hầu như luôn xác nhận ít hơn điều ta mong muốn, và ranh giới nằm ở chỗ hành động đã thực hiện kết thúc. Trong ví dụ này, test phép tính không kiểm tra bảng điểm, còn ảnh bảng điểm không cho thấy dòng mã đã thay đổi.
Hãy tách riêng hai điều: yêu cầu là điều cần đúng khi hoàn tất, còn kết quả là điều bạn thực sự nhận được. Nếu gộp chúng vào cùng một đoạn, câu "phải cộng 3 điểm" có thể âm thầm biến thành "đã cộng 3 điểm", dù chưa ai chạy thử gì cả.
Lệnh chạy và kết quả
Một lần chạy kiểm tra cần được mô tả bằng năm thông tin. Người cần lặp lại việc kiểm tra sau một tuần sẽ không tự biết những điều này:
- thư mục đã chạy lệnh;
- nguyên văn lệnh đã chạy;
- trạng thái mã: trước hay sau khi sửa, cùng những thay đổi chưa commit nào;
- kết thúc ra sao: mã thoát và các bộ đếm cuối cùng;
- những kiểm tra nào thực sự chạy, kiểm tra nào không.
Cần phân biệt "hãy chạy npm test" với "đã chạy npm test, đây là đầu ra". Câu đầu là chỉ dẫn cho tương lai; câu sau mới là sự việc đã xảy ra. Rất dễ nhầm ý định thành kết quả, nhất là khi câu trả lời trình bày chỉn chu những điều lẽ ra cần kiểm tra.
npm test không chạy "tất cả test trên đời". Lệnh này thực hiện nội dung trong trường test thuộc mục scripts của package.json trong dự án npm đang chọn. Để khỏi phải đoán dự án nào được chọn, hãy chạy lệnh từ thư mục gốc của dự án và ghi lại thư mục đó trong nhật ký. Cùng một tên lệnh ở dự án khác có thể gọi công cụ khác, chuỗi bước khác, hoặc chẳng có gì cả.
Lệnh kết thúc với mã thoát: giá trị 0 thường có nghĩa là "công cụ cho rằng lần chạy thành công". Trong bash, xem mã này ngay sau lệnh bằng echo $?; trong PowerShell, với chương trình bên ngoài như npm, dùng $LASTEXITCODE. Nhưng số 0 không cho biết test cần thiết có thực sự chạy hay không: test có thể bị bỏ qua, lọc theo tên hoặc chưa hề được viết. Vì vậy, bên cạnh mã thoát luôn cần có các bộ đếm và tên test bạn quan tâm.
Hãy yêu cầu hiển thị lần chạy thực tế: nguyên văn lệnh, thư mục và toàn bộ đầu ra gốc; đồng thời ghi riêng những bước dự kiến nhưng không thực hiện được. Lấy lệnh từ chính dự án, đừng lấy từ cheat sheet chung chung: package.json cho biết npm test thực sự làm gì ở đây.
Nên lưu lại gì từ log
Đầu ra của lần chạy có thể dài, không ai chép toàn bộ vào nhật ký. Đoạn trích ngắn nên có bốn thông tin: tên test thất bại, giá trị mong đợi và thực tế, nguyên nhân thất bại và các bộ đếm cuối cùng. Phần còn lại nằm trong tệp mà bạn dẫn liên kết. Dưới đây là ví dụ đầu ra rút gọn dùng cho học tập, không phải bản ghi của một lần chạy thực tế.
✖ 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Đây là đoạn trích; toàn bộ đầu ra được lưu trong tệp, chẳng hạn logs/test-fail.txt. Chỉ cần nhìn đoạn này đã thấy điều chính: test về ba lần ghép trùng thất bại, mong đợi 3 nhưng nhận được 30, không có kiểm tra nào bị bỏ qua.
Đầu ra gốc được đưa vào nhật ký nguyên văn. Kể lại bằng lời mình ("test thất bại vì hệ số nhân sai") có vẻ dễ hiểu hơn, nhưng đó đã là kết luận chứ không còn là quan sát, và không thể kiểm chứng qua nhật ký. Hãy đặt hai thứ cạnh nhau: trước hết là dòng đầu ra của công cụ, sau đó là phần giải thích của bạn.
Đường dẫn trong dòng stack trace chỉ nơi phép kiểm tra xảy ra, tức tệp test. Muốn tìm nguyên nhân thì xem mã trò chơi; hệ số nhân tìm thấy ở đó là kết quả đọc mã, không phải dòng trong log. Trong nhật ký, hãy ghi tách bạch hai nguồn này, nếu không sau này sẽ khó biết con số 10 xuất phát từ đâu.
Diff xác nhận điều gì
Diff cho thấy sự khác biệt giữa hai trạng thái của tệp. Nó không cho biết mã sau khi sửa đã từng được chạy hay chưa, càng không cho biết hành vi đã thay đổi đúng như bạn mong muốn.
--- 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;
}Bên dưới đoạn này cần một dòng chú thích cho biết đã so sánh những trạng thái nào. Ví dụ: "working tree so với commit a1b2c3d trước khi sửa, lệnh git diff HEAD"; mã định danh ở đây chỉ để học, nhật ký của bạn cần mã thật. Lệnh này so sánh các tệp đang được theo dõi với HEAD, bao gồm cả thay đổi đã đưa vào staging. Lệnh git diff thông thường, không có HEAD, chỉ hiển thị khác biệt so với trạng thái đã staging và có thể cho đầu ra trống ngay sau git add. Không có chú thích thì diff chỉ như "cái gì đó so với cái gì đó", không thể lặp lại việc so sánh.
Hãy kiểm tra riêng xem giá trị mong đợi trong test có còn là 3 không. Nếu con số mong đợi cũng thay đổi cùng với mã, thì câu chuyện đã khác: phép kiểm tra được điều chỉnh theo hành vi, thay vì hành vi đáp ứng yêu cầu.
Tiếp theo là một chi tiết kỹ thuật dễ nhầm. Trước khi commit, thay đổi hiện ra khi so sánh working tree với commit gần nhất. Sau commit, cùng phép so sánh đó sẽ cho kết quả trống; không có nghĩa nhiệm vụ chẳng thay đổi gì, mà chỉ là thay đổi đã nằm trong lịch sử. Lúc này cần so sánh commit với trạng thái trước đó. Diff trống tự nó chẳng chứng minh được gì; điều quan trọng là bạn đang so sánh hai trạng thái nào.
Ảnh chụp màn hình cho thấy gì
Trạng thái và hành động
Ảnh có số 3 trên bảng điểm chỉ xác nhận đúng một điều: tại thời điểm chụp, bảng điểm hiển thị số 3. Điều đó không đồng nghĩa với "ba lần ghép trùng đã cộng 3 điểm". Ảnh không cho thấy những gì cần để nối hai khẳng định này: điểm ban đầu là bao nhiêu và đã thực hiện hành động nào.
Để ảnh trở thành bằng chứng, hãy ghi kèm kịch bản đã thực hiện:
- đã mở gì: địa chỉ trang hoặc cách khởi chạy, và phiên bản có hay chưa có bản sửa;
- trạng thái ban đầu: điểm lúc đầu là 0, không có dữ liệu cũ được tải lên;
- hành động: ghép trùng ba lần;
- quan sát: bảng điểm hiện số 3, ảnh được chụp ngay sau hành động.
Không có bản ghi này thì bất kỳ ảnh nào cũng có thể được tạo ra theo hàng chục cách. Một tab mở từ hôm qua với phiên bản cũ, một lần khởi chạy khác, điểm ban đầu vốn đã lớn hơn 0 - tất cả đều có thể tạo ra hình ảnh trông thuyết phục nhưng chẳng chứng minh được gì.
Bài viết cố ý không có ảnh chụp bảng điểm. Ảnh dùng để kiểm tra phải được chụp sau khi chạy kịch bản trên đúng phiên bản, bằng tay hoặc qua công cụ tự động; bảng điểm được vẽ hay dựng để minh họa không trở thành bằng chứng, dù trông y hệt thật. Khi tự chụp ảnh, hãy chụp toàn bộ bảng điểm, ghi địa chỉ trang trong bản ghi kịch bản và lưu ảnh cạnh bản ghi đó. Nếu không, một tuần sau bạn sẽ chẳng nhớ đây là lần chạy nào.
Mô hình có thể đọc được gì
Bạn có thể gửi ảnh vào phiên làm việc và yêu cầu mô hình đọc. Cách đặt câu hỏi quyết định tất cả: hãy yêu cầu nêu con số nhìn thấy và các điểm chưa rõ ("có thấy trạng thái ban đầu không", "có biết đây là phiên bản nào không"), thay vì yêu cầu xác nhận một kết luận định sẵn. Hỏi "hãy xác nhận lỗi đã được sửa" rất dễ nhận được lời đồng ý lịch sự nhưng chẳng kiểm tra điều gì.
Khả năng gửi ảnh phụ thuộc vào mô hình và môi trường bạn đang dùng. Trong Claude Code, bạn có thể chỉ định đường dẫn tới ảnh đã lưu và yêu cầu đọc ảnh. Nếu ảnh không tải được, trước tiên hãy kiểm tra đường dẫn và quyền truy cập tệp; chỉ đổi mô hình thôi thì không đảm bảo sẽ giải quyết được vấn đề.
Câu trả lời của Claude Code về ảnh chỉ là cách diễn giải hình ảnh, cần đối chiếu lại với chính ảnh đó. Mô hình có thể đọc sai con số hoặc tưởng tượng thêm chi tiết không có trong ảnh. Quan trọng nhất, chỉ nhìn ảnh thì không thể xác định dòng mã nào đã được sửa. Bảng điểm cho thấy kết quả, không phải nguyên nhân.
Cùng một kịch bản trước và sau khi sửa
Một cặp kết quả hữu ích là cùng một phép kiểm tra thất bại trên phiên bản lỗi và thành công trên phiên bản đã sửa. Kết quả thất bại cho thấy phép kiểm tra phát hiện được lỗi; kết quả thành công cho thấy lỗi không còn xuất hiện trong kịch bản đã kiểm tra sau khi sửa.
Cách so sánh này chỉ có ý nghĩa khi đầu vào và kỳ vọng giống hệt nhau: cùng điểm ban đầu, cùng số lần ghép trùng và cùng giá trị mong đợi là 3. Đây là kết quả thành công của cùng test sau khi sửa:
✔ three matches give 3 points (1.3983ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0
ℹ skipped 0Giữa hai lần chạy, chỉ phần triển khai thay đổi; test, dữ liệu đầu vào, giá trị mong đợi và điều kiện chạy phải được giữ nguyên. Nếu phải sửa yêu cầu hoặc giá trị mong đợi trong test, hãy chạy một cặp mới với phép kiểm tra đó: lời giải thích bằng chữ không thể thay cho việc chạy trên cả hai trạng thái. Trong tình huống này, chỉ nói "trước đỏ, sau xanh" sẽ khiến người đọc hiểu nhầm.
Hai tệp tên before.txt và after.txt tự chúng cũng chẳng đảm bảo điều gì. Tên tệp không phải bằng chứng. Hãy xem nội dung: có cùng lệnh, cùng test và cùng giá trị mong đợi không? Việc cố tình khôi phục trạng thái lỗi để kiểm tra (cách làm với git stash) là một phép kiểm tra riêng với quy tắc riêng. Ở đây chỉ cần nhớ rằng kết quả thất bại trước khi sửa phải đến từ lần chạy thật, không phải từ trí nhớ.
EVIDENCE_LOG ngắn gọn
EVIDENCE_LOG trong bài này là nhật ký văn bản thông thường gồm sáu trường theo thứ tự: TRIỆU CHỨNG, BẰNG CHỨNG TỪ LOG, LỖI TRÊN BẢN CHƯA SỬA, BẢN SỬA, KẾT QUẢ THÀNH CÔNG ĐÁNG TIN, ẢNH CHỤP MÀN HÌNH. Cách này giúp đối chiếu triệu chứng, bản sửa và kết quả. Tuy nhiên, thứ tự các trường không đảm bảo nội dung ghi lại là đáng tin.
Mọi thứ ta đã thu thập ở trên đều được xếp vào các trường này. Lệnh và thư mục nằm ở trường về lần chạy thất bại; các trạng thái dùng để so sánh nằm ở trường về bản sửa; liên kết tới tệp đầy đủ nằm ở trường mà chúng thuộc về. Không cần lập thêm một bộ tài liệu riêng.
Dưới đây là mẫu điền dùng cho học tập. Mẫu cho thấy mỗi trường cần ghi gì, chứ không báo cáo các lần kiểm tra đã thực hiện. Trong nhật ký của bạn, hãy thay nội dung mẫu bằng kết quả thực tế.
TRIỆU CHỨNG: trong trò chơi bonus, khi điểm ban đầu là 0,
ba lần ghép trùng cho 30 điểm thay vì 3. Lỗi lặp lại mỗi lần:
mở trò chơi, ghép trùng ba lần, xem bảng điểm.
BẰNG CHỨNG TỪ LOG: đầu ra lần chạy có "30 !== 3" trong test
"three matches give 3 points", toàn bộ đầu ra ở logs/test-fail.txt.
Thông tin riêng từ việc đọc mã: trong src/bonus-game/score.js,
hệ số POINTS_PER_MATCH bằng 10. Đây là kết quả đọc mã,
không có số 10 trong đầu ra của test.
LỖI TRÊN BẢN CHƯA SỬA: HEAD a1b2c3d (mã định danh dùng để học),
working tree sạch; commit chứa phép tính lỗi và test.
Thư mục - /project/bonus-game, lệnh - npm test.
Trong package.json, scripts.test = node --test. Test "three matches give 3 points"
thất bại, nguyên nhân "30 !== 3", tests 1, pass 0, fail 1, skipped 0, mã thoát 1.
Đoạn trích đầu ra ở trên, toàn bộ đầu ra - logs/test-fail.txt.
BẢN SỬA: POINTS_PER_MATCH đổi từ 10 thành 1 trong tệp
src/bonus-game/score.js; không có thay đổi nào khác. HEAD vẫn là a1b2c3d.
Diff: git diff HEAD, working tree so với commit này;
lưu tại diffs/score-fix.diff.
Giá trị mong đợi trong test vẫn là 3.
KẾT QUẢ THÀNH CÔNG ĐÁNG TIN: cùng lệnh npm test, từ cùng thư mục,
chạy trên mã sau bản sửa đã mô tả. Cùng test chạy thành công: tests 1, pass 1,
fail 0, skipped 0, mã thoát 0.
Toàn bộ đầu ra - logs/test-pass.txt.
Chưa chạy kịch bản với năm lần ghép trùng.
ẢNH CHỤP MÀN HÌNH: screenshots/bonus-score-3.png. Trò chơi được mở lại
ở địa chỉ http://localhost:3000 từ cùng working tree
sau khi sửa. Điểm ban đầu là 0, đã ghép trùng ba lần,
bảng điểm hiện số 3. Chưa kiểm tra việc lưu kết quả vào bảng xếp hạng.
Các đường dẫn trong ví dụ chỉ là minh họa. Hãy lưu log, diff và ảnh ở nơi dự án đã có các tệp tương tự; không cần tạo cấu trúc thư mục bắt buộc riêng.
Hãy chú ý hai dòng cuối ở trường 5 và 6. Việc chưa làm được ghi thẳng, chỉ bằng một câu: "chưa chạy", "chưa kiểm tra". Đây không phải lời thú nhận lười biếng, mà là ranh giới của kết luận. Thiếu những dòng này, nhật ký trông có vẻ đầy đủ hơn, nhưng thực ra chỉ im lặng về những việc chưa ai làm.
Tự kiểm tra
Nhiệm vụ có hai tiêu chí: phép tính cho 3 điểm sau ba lần ghép trùng khi điểm ban đầu là 0, và giao diện hiển thị số 3 sau ba lần ghép trùng đó. Bạn có sáu tài liệu:
- tin nhắn của agent: "xong rồi, đã sửa lỗi";
- chỉ dẫn chạy
npm test; - kết quả chạy thực tế của cùng một test: thất bại trước khi sửa, thành công sau đó;
- diff thể hiện thay đổi hệ số nhân;
- một ảnh duy nhất có số 3, không mô tả cách chạy;
- bản ghi kịch bản đã thực hiện, kèm ảnh tương ứng.
Tài liệu nào đủ để xác nhận hai tiêu chí, và điều gì vẫn chưa được kiểm tra? Hãy thử nghĩ câu trả lời trước khi đọc tiếp.
Phân tích: tiêu chí thứ nhất được xác nhận bằng mục 3, một cặp kết quả chạy thật trên cùng test. Diff ở mục 4 giải thích mã đã thay đổi gì, nhưng không thể thay cho lần chạy; thay đổi mã và thực thi là hai việc khác nhau. Tiêu chí thứ hai được xác nhận bằng mục 6: ảnh kèm bản ghi kịch bản cho thấy trạng thái ban đầu và hành động. Mục 1 và 2 chẳng xác nhận được gì: tin nhắn báo xong không có bằng chứng kiểm tra được, còn chỉ dẫn chỉ mô tả việc sẽ làm. Mục 5 chỉ cho thấy số 3, không xác định được cách tính điểm. Và việc lưu kết quả vào bảng xếp hạng vẫn chưa được kiểm tra, dù có đủ cả sáu tài liệu: không có kịch bản nào đọc lại giá trị đã lưu.
Bạn cũng có thể lập nhật ký như vậy trong một phiên làm việc. Đây là prompt mẫu:
Hãy lập EVIDENCE_LOG gồm sáu trường. Với mỗi kết luận, hãy nêu
phép kiểm tra thực tế, trạng thái mã và nguồn của kết quả.
Ghi riêng những gì chưa chạy. Với ảnh chụp màn hình, chỉ mô tả
những gì nhìn thấy, không suy ra phép tính bên trong là chính xác.Cách thu thập bằng chứng với Claude Code
Có thể làm theo trình tự này:
- agent liên kết từng tiêu chí với một phép kiểm tra cụ thể: chạy gì, trên trạng thái mã nào và kết quả nào được tính;
- thực hiện những phép kiểm tra có thể chạy trong môi trường này, đồng thời cung cấp lệnh và đầu ra gốc;
- đính kèm các quan sát: đoạn trích đầu ra, liên kết tới tệp đầy đủ và mô tả kịch bản đã thực hiện;
- nêu rõ giới hạn: điều gì chưa chạy, điều gì không thể kết luận từ tài liệu hiện có.
Bạn vẫn cần tự kiểm tra bằng chứng: mở các tệp đính kèm để xem nội dung, đối chiếu kịch bản với tiêu chí và đặt một câu hỏi khó về phần chưa có kết quả kiểm tra. Thường như vậy là đủ để phân biệt bằng chứng đã thu thập với một bản tóm tắt được trình bày chỉn chu.
Nhiều tệp không thể thay thế một phép kiểm tra còn thiếu. Mười tệp đính kèm mà không tệp nào chạy hành vi cần kiểm tra thì cũng chứng minh được đúng bằng việc không đính kèm gì. Tốt hơn là có ba bản ghi ngắn cho thấy lệnh, trạng thái và kết quả.
Tài liệu kỹ thuật: git diff, git stash, trình chạy test của Node.js, npm test, hình ảnh trong Claude Code.
GO TO FULL VERSION