计分问题修好了。回答里有终端输出、计分板截图,还有一句简短的"完成了";文件不少,问题却很简单:其中哪些内容能证明满足了任务要求?

证据不是碰巧和修复放在一起的任意文件,而是一次具体检查的结果。它应能说明检查了什么、代码处于什么状态,以及检查结果如何。不同材料回答不同问题,没有哪一种能一次说明所有事情。

我们会把需求转化为可验证的陈述,看看命令运行记录该写什么,以及日志、diff 和屏幕截图分别能证明什么。最后把这些内容整理进一份包含六个字段的简短日志 EVIDENCE_LOG,再评估一组材料是否足够。

验收标准决定如何检查

贯穿全文的例子是一款带计分板的小型奖励游戏。初始分数为 0,凑成三个相同图案应得 3 分,修复前却会加 30 分。计分规则是为这款游戏设定的,另一个项目可能完全不同,不要把这条规则照搬过去。

"分数已修复"这句话无法验证,因为不知道该运行什么、该看哪里。可验证的陈述由三部分组成:初始状态、操作和预期观察结果。例如:"初始分数为 0 时,凑成三个相同图案会加 3 分,计分板显示 3。"现在这句话对应着一项检查。

接下来要把这条陈述拆成单独的观察项。计分计算和玩家看到的内容是两回事,需要不同的证据来确认。表格第三行讨论排行榜中的分数:任务未必要求这一点,但如果有要求,就应有对应的观察结果。

标准证明材料结论范围
计算:初始为零时,三个相同图案得 3 分在修复后的代码上运行测试,并记录测试名称和结果能说明代码中的这个场景已检查,但不能说明玩家看到了什么
界面:凑成三个相同图案后,计分板显示 3复现过程的记录及对应的屏幕截图能证明屏幕上显示的数字,但无法解释数字从何而来
结果已保存到排行榜单独复现一次,并重新读取已保存的数值计分板截图完全无法证明这一点;没有这样的场景,这项要求就仍未验证

这里最有用的是第三列。证据能证明的内容往往比我们希望的少,边界就在实际执行的操作结束之处。在这个例子里,计分测试没有检查计分板,而计分板截图也看不出代码哪一行改了。

还要把两件事分开:需求描述最终应该成立的情况,结果则是实际得到的情况。如果把它们混在一个段落里,"应该加 3 分"很容易悄悄变成"加了 3 分",即使根本没人运行过检查。

命令及其结果

一次检查运行需要记录五项信息。对一周后再来复现的人来说,其中没有一项是不言自明的:

  • 运行命令时所在的目录;
  • 完整的原始命令;
  • 代码状态:修改前、修改后,以及带有哪些未提交的更改;
  • 运行结果:退出码和最终计数;
  • 哪些检查实际执行了,哪些没有。

要区分"应该运行 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;
}

这段内容下方应加一行说明,写清楚比较了哪两个状态。例如:"工作区与修改前的提交 a1b2c3d 对比,命令 git diff HEAD";这里的标识符仅用于教学,证据日志中应填写真实值。该命令会比较当前受 Git 跟踪的文件与 HEAD,也包含已暂存的更改。普通的 git diff 不带 HEAD 时,只显示工作区与暂存状态的差异;运行过 git add 后,结果可能已经为空。没有说明,diff 看起来就像"拿这个和那个比了比",别人也无法复现。

另外,请确认测试的预期值仍是 3。如果预期值和代码一起改了,那就是另一回事:检查是在迁就现有行为,而不是让行为符合需求。

还有个容易弄错的技术细节。更改尚未提交时,可以通过比较工作区和最近一次提交看到它。提交之后,同样的比较会显示为空,但这不代表任务中什么都没改,只是更改已经进入历史记录,此时应把该提交与之前的状态进行比较。空 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 分。每次都能复现:打开游戏,凑成三个相同图案,查看计分板。

日志证据:运行输出中,测试 "three matches give 3 points" 出现 "30 !== 3",完整输出在 logs/test-fail.txt。
另根据阅读代码:src/bonus-game/score.js 中的乘数 POINTS_PER_MATCH 为 10。这是阅读代码的结果,测试输出中并没有数字 10。

缺陷状态下的失败:HEAD a1b2c3d(教学用标识符),工作区干净;该提交中包含有缺陷的计算代码和测试。目录为 /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,比较工作区与该提交;保存在 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 的最后两行。未执行的检查要直接用一句话标明:"未运行"或"未检查"。这不是承认偷懒,而是在说明结论的边界。没有这些内容的日志看起来更完整,实际上只是对没人做过的事保持沉默。

检查一下自己是否掌握

任务有两项验收标准:初始分数为 0 时,凑成三个相同图案可得 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 中的图像。