---
name: verify
description: 通过运行应用并观察行为来验证代码变更确实做了它应该做的事情。
---

**验证是运行时观察。** 你构建应用、运行它、驱动它到变更代码执行的地方，并捕获你所看到的。该捕获是你的证据。别无其他。

**不要运行测试。不要进行类型检查。** 在此运行它们证明你可以运行 CI——而不是证明变更有效。不作为预热，不作为"只是为了确认"，不作为事后的回归检查。时间应该花在运行应用上。

**不要 import-and-call。** `import { foo } from './src/...'` 然后 `console.log(foo(x))` 是你编写的单元测试。该函数做了该函数做的事情——你从阅读代码已经知道这点。应用从未运行过。在真实代码库中任何调用 `foo` 的东西都以 CLI、socket 或窗口为终点。去那里。

## 找到变更

范围是你要验证的内容——通常是差异，有时只是"X 是否工作"。在 git 仓库中，建立完整范围（一个分支可能是多次提交，或者变更可能仍未提交）：

```bash
git log --oneline @{u}..              # 计数提交（如果设置了上游）
git diff @{u}.. --stat                # 完整范围，不是 HEAD~1
git diff origin/HEAD... --stat        # 无上游：已提交 vs 基准
git diff HEAD --stat                  # 未提交：工作树 vs HEAD
gh pr diff                            # 如果在 PR 上下文中
```

说明提交数量。大差异被截断？重定向到文件然后 Read。仓库但没有来自任何这些的差异 → 说明，停止。**没有仓库 → 范围是用户命名的任何内容；如果他们没有命名则询问。**

**差异是地面真相。任何描述都是关于它的声明。** 阅读两者。如果它们不一致，那是一个发现。

## 表面

表面是用户——人类或程序——与变更相遇的地方。那是你要观察的地方。

| 变更触及 | 表面 | 你 |
|---|---|---|
| CLI / TUI | 终端 | 输入命令，捕获面板——[示例](examples/cli.md) |
| 服务器 / API | socket | 发送请求，捕获响应——[示例](examples/server.md) |
| GUI | 像素 | 在 xvfb/Playwright 下驱动它，截图 |
| 库 | 包边界 | 通过公共导出的示例代码——`import pkg`，而不是 `import ./src/...` |
| 提示 / 代理配置 | 代理 | 运行代理，捕获其行为 |
| CI 工作流 | Actions | 调度它，读取运行 |

**内部函数？不是表面。** 仓库中的某物调用它，该调用者以以上某一行为终点。跟随它到那里。一个 bash 安全门的表面不是函数的返回值——而是当你输入命令时 CLI 提示或自动允许的行为。

**完全没有运行时表面**——仅文档、无输出的类型声明、不产生行为差异的构建配置——报告 **SKIP——无运行时表面：（原因）。** 不要运行测试来填充空间。

**差异中的测试是作者的证据，不是表面。** CI 来运行它们。你会是重新运行 CI。仅测试 PR → SKIP，一行。混合 src+test → 验证 src，忽略测试文件。阅读测试以了解要检查什么是可以的——它是一个规格。但然后去运行应用。检查断言是否匹配源是代码审查。

## 获取句柄

**首先检查 `.claude/skills/`——即使你已经知道如何构建和运行。** 匹配的 `verifier-*` 技能是该仓库的证据捕获协议：它包装会话，使审查者可以回放你看到的内容（录制、截图）。没有它驱动表面，你得到的是没有回放的裁决。

```bash
ls .claude/skills/
```

- **`verifier-*` 匹配你的表面**（CLI 变化的 CLI 验证器等）→ 使用 Skill 工具调用它并遵循其设置。表面不匹配 → 跳过那个，尝试下一个。过时的验证器（在与变更无关的机制上失败）→ 询问用户是否修补；不要因验证器腐化而 FAIL 变更。
- **`run-*` 但没有匹配的验证器** → 使用其构建/启动原语作为你的句柄。
- **两者都没有** → 从 README/package.json/Makefile 冷启动。时间限制约 15 分钟。卡住了 → BLOCKED，注明确切的卡住位置，加上一个填写好的 `/run-skill-generator` 提示。通过了 → 记录有效的构建/启动配方，使其可以成为一个 `verifier-*` 技能。

## 驱动它

使变更代码执行的最小路径：

- 更改了标志？用它运行。
- 更改了处理程序？命中该路由。
- 更改了错误处理？触发该错误。
- 更改了内部函数？找到到达它的 CLI 命令/请求/渲染。运行那个。

**在运行之前回读你的计划。** 如果每一步都是构建/类型检查/运行测试文件——你计划了一次 CI 重跑，而不是验证。找到触及表面的步骤或报告 BLOCKED。

**裁决是入场券。你的观察是信号。** 一个带有三条尖锐的"嘿，我注意到……"行的 PASS 比一个光秃秃的 PASS 更有价值。你是唯一实际*运行*了此事的审查者——任何让你停顿、绕过、或感到"咦"的事情都是作者没有的信息。不要筛选"这是否是一个 bug"。筛选"如果他们坐在我旁边我会不会提这一点"。

**端到端，通过真实接口。** 各部分在隔离中通过并不意味着流程有效——接缝是 bug 隐藏的地方。如果用户点击按钮，通过点击按钮来测试，而不是 curl 底层的 API。

**破坏性路径？** 如果变更涉及删除、发布、发送或在工作区之外写入，且没有干运行或安全目标，不要直接驱动它。验证你可以在其周围验证的内容，说明你没有执行的路径以及原因。

## 推敲它

声明通过了检查——这是前半部分。确认是第一步，不是全部工作。描述是作者意图做的；你的价值是他们没有做的。

你确切知道什么变了。在你刚刚驱动的同一表面上探查其*周围*：

- **新标志/选项** → 空值，两次传递，与冲突标志组合，拼写错误（错误是否指名？）
- **新处理程序/路由** → 错误的方法，畸形的正文，缺失必需字段，过大的有效载荷
- **更改的错误路径** → 它没有触及的相邻错误——重构是否也捕获了它们，还是仅捕获了差异中的那个？
- **交互式/TUI** → 在操作中按 Ctrl-C，调整面板大小，粘贴垃圾，快速连按该键，在错误时刻按 Esc
- **状态/持久性** → 做两次，在过时状态下做，同时在两个会话中做
- **漫游** → 相邻的是什么？在确认时什么东西看起来不对劲？回到那里。

这些不是检查清单——选择变更指向的那些。当你已经覆盖了明显的相邻部分或碰到了值得⚠️的内容时停止。一个什么也没找到的探查步骤仍然是： "🔍 `--from ''` 通过 → 干净错误 `error: --from requires a value`，退出码2。" 作者没有测试它，正是值得知道它成立的原因。

仍然不是测试运行。你在表面，输入用户可能错误输入的内容。

## 捕获

Stdout、响应正文、截图、面板转储。捕获的输出是证据；你的记忆不是。有意想不到的东西？不要绕过它——捕获、注意、判断是变更还是环境。不相关的破坏是一个发现，不是噪音。

共享进程状态（tmux、端口、锁文件）——隔离。`tmux -L name`，绑定 `:0`，`mktemp -d`。你与宿主共享一个命名空间。

## 报告

内联，最终消息：

```
## 验证：<one-line what changed>

**裁决：** PASS | FAIL | BLOCKED | SKIP

**声明：** <what it's supposed to do — your read of the diff and/or the stated claim; note any mismatch>

**方法：** <how you got a handle — which verifier/run-skill, or cold start; what you launched>

### 步骤

每一步是你对**运行中的应用**做的一件事及其显示的内容。构建/安装/检出是设置，不是步骤。测试运行和类型检查不属于此处——它们是 CI 的输出。

1. ✅/❌/⚠️/🔍 <what you did to the running app> → <what you observed>
   <evidence: the app's own output — pane capture, response body, screenshot path>

🔍 标记一个探查步骤——偏离声明快乐路径的一步，试图破坏它。至少一个。一个只有 ✅ 没有 🔍 的步骤列表是快乐路径重播：仍然是 PASS，但你在前半部分就停下来了。

**截图/样本：** <the one frame a reviewer looks at to see the feature — image path for GUI/TUI, code block for library/API; omit for build/types-only>

### 发现
<你注意到的事情。不仅仅是 bug——摩擦、意外、任何第一次使用者会绊倒的东西。"尝试了三次才找到正确的标志。" "拼写错误时的错误消息没有帮助。" "默认值对于常见情况似乎奇怪。" "有效，但比我预期的慢。" 降低门槛：如果它让你停顿，它就属于这里。但停顿必须是你自己的，来自运行应用——不是来自阅读 PR 页面。红色 CI 检查、审查评论、别人的机器人：对任何人都已经可见，你转述它不是观察。声明/差异不匹配、预先存在的损坏和环境说明也属于这里。

每个探查步骤在此处都有一行，即使它通过了——"🔍 空 `--from` → 干净错误"告诉作者什么*被*覆盖了，他们无法从光秃秃的 PASS 中看到。
>

以 ⚠️ 开头的是值得中断审查者的行；普通项目符号是上下文。如果没有引起注意的事情，空是可以的——但什么都不引起注意本身就是罕见的。
```

**裁决：**
- **PASS** — 你运行了应用，变更在其表面做了它应该做的事。不是：测试通过、构建干净、代码看起来正确。
- **FAIL** — 你运行了它，它没有。或者它破坏了其他东西。或者声明和差异实质不一致。
- **BLOCKED** — 无法达到变更可观察的状态。构建失败，环境缺少依赖项，无法启动句柄。不是对变更的裁决。准确说明在哪里停止了 + `/run-skill-generator` 提示。
- **SKIP** — 不存在运行时表面。仅文档、仅类型、仅测试。没有什么出错；这里就是没有什么可运行的。一行说明原因。

没有部分通过。"4 个中 3 个通过"在 4 个通过或被解释清楚之前是 FAIL。

**当不确定时，FAIL。** 假 PASS 会交付有问题的代码；假 FAIL 只需多一次人工查看。模糊输出是 FAIL 并附上原始捕获——不要解释。
