阅读材料: 第9部分。功能验证:从规格到事实

模块「第9部分。功能验证:从规格到事实」中第 1 / 5 节课
您正在未登录状态下查看课程。 请登录,以保存进度并参加测试。

第9部分。功能验证:从规范到事实

功能验证不是编码后的形式主义。这是一种独立的工作模式,文本规范在此转化为可验证的事实。规范解释了意图,但本身并不能证明意图已被实现。事实是一种断言,机器或人可以在不重新解释冗长散文的情况下进行验证。

简短公式:

规范指引方向。
事实决定能否合并。
flowchart LR
  A["文本规范<br/>意图和边界"] --> B["事实集合<br/>validation.md"]
  B --> C["验证<br/>命令、测试、手动场景"]
  C --> D{"事实已确认?"}
  D -- "是" --> E["可以合并分支"]
  D -- "否" --> F["修复代码<br/>或澄清规范"]
  F --> B

对于与编写代码的代理一起工作,这一点至关重要。模型在不同会话或版本中可能对同一文本规范有不同的解读。测试、退出代码、HTTP状态、数据库不变量或显式契约对解释的依赖较小。因此,validation.md 不应仅仅是核对清单,而应是一组用于合并准入的事实。

什么是事实

事实是一种可执行或可明确验证的断言:

  • npm run typecheck 以代码 0 退出;
  • GET / 返回 200;
  • 响应包含 <h1>AgentClinic</h1>
  • 过期的 JWT 返回 401;
  • 不带消息的反馈提交无法通过验证;
  • 迁移可以运行两次而不会意外改变模式;
  • 对于每个初始代理,详情页面返回相关的疾病列表。

糟糕的验证项:

确保页面看起来不错。

更好:

在 375px 宽度下,标题、主要内容区域和页脚不重叠。
主标题保持可见,无需水平滚动。

事实集合的层次

使用四个层次。

示例 验证具体的输入输出对:

curl -s http://localhost:3000 | rg "<<h1>AgentClinic</h1>"

不变量 描述应始终为真的内容:

每条反馈记录都有非空消息。

属性 验证一类情况:

任何超出 1..5 范围的评分都会被拒绝。

契约 固定前置条件、动作和后置条件:

如果会话未认证,
当请求 GET /dashboard 时

响应重定向到 /login。

并非每个功能都需要全部四个层次。但每个功能至少应有一些机器可验证的事实。

根据风险类型确定所需的事实层次

并非所有功能都相同。简单的 UI 横幅和数据库迁移需要不同的验证密度。最低足够的事实层次可以根据矩阵选择:

功能/风险类型示例不变量属性契约手动事实
视觉/UI 更改必需必需
CRUD 路由必需必需
表单验证/输入必需必需必需
数据迁移必需必需
授权/访问必需必需
支付/副作用必需必需
第三方 API 集成必需必需必需
后台任务/调度器必需必需

如何阅读表格:

  • 示例 — 具体的输入输出对(一个命令、一个 curl、一个通过的测试)。
  • 不变量 — 动作后应始终为真的内容。对于迁移,这是"再次运行不会改变模式"。对于后台任务,这是"成功运行后计数器不会减少"。
  • 属性 — 验证一类情况。对于验证,这是"任何超出 1..5 的评分都会被拒绝"。对于授权,这是"任何无会话请求返回 401"。
  • 契约 — 正式的"在条件 X 下,动作 Y 导致 Z"关联。
  • 手动事实 — 人用眼或手检查的内容。对 UI 是必需的,因为自动验证视觉层次通常不充分。

矩阵的目的不是将其变成强制核对清单,而是帮助发现遗漏。如果"数据迁移"类型的功能只通过示例而没有不变量,这是重写 validation.md 的信号。

validation.md 的结构

示例:

# 验证 — 反馈表单

## 事实集合

### F1 — TypeScript 编译通过

- 命令:`npm run typecheck`
- 预期:退出代码 0
- 负责人:自动验证
- 状态:草稿

### F2 — 测试通过

- 命令:`npm test`
- 预期:退出代码 0
- 负责人:自动验证
- 状态:草稿

### F3 — 空消息被拒绝

- 命令:`npm test -- feedback`
- 预期:POST /feedback 带空消息返回 400
- 负责人:自动验证
- 状态:草稿

### F4 — 正确反馈被保存

- 命令:`npm test -- feedback`
- 预期:正确的 POST /feedback 添加一行并重定向到 /feedback
- 负责人:自动验证
- 状态:草稿

### F5 — 页面在移动屏幕上保持可用

- 验证:在 375px 宽度下打开 /feedback
- 预期:表单字段和提交按钮无需水平滚动即可见
- 负责人:手动验证
- 状态:草稿

## 就绪标准

- 所有自动事实通过。
- 手动事实已验证。
- 无法实现的事实从边界中删除或退回草稿并附解释。
- 路线图和变更日志在合并前更新。

生命周期有助于区分意图和证据:

  • 草稿:事实已提出,但尚未确定;
  • 必需:事实被接受为功能的必需项;
  • 已实现:有测试、命令或手动确认;
  • 延期:事实被有意识地推迟到未来阶段。

从差异开始

git status --short
git diff --stat main...HEAD

要求 Qwen Code 不仅验证是否符合规范,还要验证事实集合的状态:

/clear
将此分支与 @specs/2026-05-01-hello-hono/validation.md 进行比较。

显示:
1. 已实现并通过的事实;
2. 缺少的事实;
3. 含糊不清需要重写的事实;
4. 实现中未在 requirements.md 中描述的决策;
5. 规范中的过时断言。

暂时不要修改文件。

如果 Qwen Code 无法确定验证项的通过/未通过状态,那就不是事实,而是散文中的愿望。重写它。

人工参与的验证

代理可以发现机械性不一致,但人必须评估产品和架构方面:

  • 页面是否符合使命;
  • 任务边界是否过度膨胀;
  • 是否存在未说明的依赖;
  • 新开发者是否理解文件为何如此组织;
  • 每个有风险的行为是否有事实;
  • 重要决策是否只留在聊天中。

典型示例:首次实现后,通常会清楚页面结构不应是一个单体组件,而应是一组 HeaderMainFooter。这不仅仅是"修复代码":需要更新 plan.mdvalidation.md 中的事实,以便未来会话不会回到旧解释。

同时修复代码、规范和事实的请求

实现需要更清晰的页面结构。

更新 @specs/2026-05-01-hello-hono/plan.md 并要求:
- Layout 组件;
- Header 组件;
- Main 组件;
- Footer 组件;
- 从 Layout 连接 static/style.css。

更新 @specs/2026-05-01-hello-hono/validation.md 的事实,验证:
- 响应包含 header/main/footer 地标;
- /static/style.css 由服务器提供;
- npm run typecheck 以代码 0 退出。

然后更新实现以符合新计划和事实。
保持在此功能边界内的更改。

这样,您同时防止了规范偏离和事实偏离。

自动验证

最低要求:

npm run typecheck

如果已有测试:

npm test

如果有开发服务器:

npm run dev
curl -s http://localhost:3000
curl -s http://localhost:3000/static/style.css

validation.md 中记录确切的命令和预期结果。如果可以写命令和预期退出代码,就不要写"检查是否正常工作"。

手动事实

如果具体,手动事实不比自动事实弱。弱手动验证:

检查界面。

正常的手动事实:

在 375px 宽度下,/feedback 页面显示姓名字段、消息字段、
提交按钮和最近三条记录,无需水平滚动和重叠。

手动事实对色调、视觉层次、基本无障碍检查和边界膨胀控制很有用。但如果手动事实在每个功能中重复出现,考虑如何通过 Playwright 或单元和集成测试将其自动化。

CI 中的验证

事实应达到合并准入。对于小型项目,本地命令足够。对于团队,最好添加 CI:

在以下情况满足前,不能接受合并请求:
- npm run typecheck 未通过;
- npm test 未通过;
- 必需的路由测试未通过;
- validation.md 中的事实集合未更新。

这是对"规范被解释"批评的实际回应。文本规范指引代理,但合并由事实决定。

合并的证据包

当功能接近合并时,审查者应有一个紧凑的工件,通过它可以理解:具体实现了什么、哪些事实通过了、哪些被推迟了。这个工件方便称为"证据包"(英文来源中 — evidence bundle)。

这不是单独的新文件 — 这是合并请求描述的格式。好的证据包包括:

  • 规范文件夹的链接(specs/YYYY-MM-DD-feature/);
  • validation.md 中的事实列表及状态:已确认失败已推迟
  • 命令执行痕迹:命令名称、退出代码、最后一行输出或简短摘录;
  • 手动验证结果:人具体检查了什么、在什么屏幕上、看到了什么;
  • 实现过程中做出的、原始规范中未包含的决策列表;
  • 反映这些决策的提交链接。

这种合并请求描述模板在附录 C 中。主要思想:审查者不应重新运行一切来确认就绪。他应能根据证据包理解作者具体做了什么和验证了什么,并在有怀疑时精确地重新运行所需命令。

如果证据包中出现"事实在失败后更改"的项,这不是隐藏它的理由。相反:明确解释的事实更改是 SDD 的正常部分,隐藏的更改是反模式(见第20部分)。

更新路线图

通过事实集合后:

## 阶段 1:Hello Hono(已完成)

- [x] 安装 Hono 和 tsx。
- [x] 创建 GET / 路由。
- [x] 返回最小服务器渲染 HTML。
- [x] 添加类型检查脚本。

提交:

git add specs/roadmap.md specs/2026-05-01-hello-hono
git commit -m "Validate Hello Hono feature"

合并:

git checkout main
git merge phase-1-hello-hono

git branch -d phase-1-hello-hono

实践

  1. 运行所有自动事实。
  2. 要求 Qwen Code 将代码与 validation.md 进行比较。
  3. 将含糊的验证项重写为事实。
  4. 如果代码和规范不一致,修复代码或规范。
  5. 标记事实状态。
  6. 在路线图中标记阶段。
  7. 执行合并。

检查问题

  1. 为什么文本规范不应是合并的唯一准入条件?
  2. 事实与验证中的愿望有何不同?
  3. 何时可以将手动验证视为事实?
  4. 如果测试通过但实现不符合 requirements.md,该怎么办?
  5. 为什么"规范指引方向,事实决定能否合并"比"把规范写更好"更好?
我的笔记
0 / 10000

笔记保存在当前浏览器中。在其他设备上将不会显示。

课程菜单

课程

基于 Qwen Code CLI 的规范驱动开发
进度 0 / 135