第十一部分:功能的第二阶段
功能的第二阶段检验流程是否已变得可重复。第一个功能往往靠热情就能完成。第二个功能则检验你是否能够干净地启动、守住边界、更新变更日志,并且不因变更量过大而感到疲惫。
在 AgentClinic 中,第二阶段可以称为「代理与疾病」。它引入 SQLite、代理表和疾病表、初始数据、/agents 和 /ailments 页面、测试以及导航功能。
干净启动检查清单
在创建新功能分支之前:
git checkout main
git pull --ff-only
git status --short
npm test
npm run typecheck
在 Qwen Code 中:
/clear
检查路线图:
读取 @specs/roadmap.md。
下一个未完成的阶段是什么?
不要修改文件。
如果有未完成的变更,不要开始新阶段。否则,代理生成的变更将难以解释。
为第二阶段创建规格说明
提示:
从 @specs/roadmap.md 开始下一个功能阶段。
创建功能分支和带日期的规格说明目录。
在写入文件之前,向我提出恰好三组问题:
1. 边界:哪些领域实体和页面属于该阶段?
2. 决策:数据库、初始数据、接口约定、测试?
3. 上下文:基调、限制、风险以及应排除在边界之外的内容?
使用 @specs/mission.md 和 @specs/tech-stack.md。
暂时不要实现代码。
回答可能如下:
边界:
仅添加用于读取的代理和疾病。包括列表页面和代理详情页面。
暂时不包含预约和表单。
决策:
使用 SQLite 配合手写迁移。添加虚构的初始数据。
使用 Hono 路由和服务器端渲染的组件。
使用 Vitest 添加路由和组件测试。
上下文:
内容应为讽刺风格,但实现必须清晰。
界面应具有响应式。不使用客户端 JavaScript。
认知负荷与边界检查
第二阶段通常比第一阶段更大。当代理在一次运行中修改数十个文件时,人根本无法理解——眼睛一扫而过,错误就被漏掉了。为了避免被这股洪流淹没:
- 逐个或成对实现任务组;
- 进行中间提交;
- 要求 Qwen Code 按任务组总结变更;
- 如果您的 Qwen Code 版本支持内置审查流程,请使用
/review; - 不接受与规格说明不符的变更。
审查提示示例:
/clear
根据 @specs/2026-05-02-agents-ailments/plan.md
和 @specs/2026-05-02-agents-ailments/validation.md 审查当前分支。
重点关注:
1. 超出阶段边界;
2. 数据库迁移安全;
3. 测试覆盖缺口;
4. 与 @specs/tech-stack.md 的冲突。
不要修改文件。
任务组示例
# 计划 — 代理与疾病
## 组 1 — 数据库启动
1. 安装 better-sqlite3。
2. 添加数据库连接模块。
3. 添加可重复运行的迁移机制。
## 组 2 — 领域模式
4. 添加代理表。
5. 添加疾病表。
6. 添加关联表 agent_ailments。
## 组 3 — 初始数据
7. 添加虚构代理。
8. 添加虚构疾病。
9. 关联代理与疾病。
## 组 4 — 路由与组件
10. 添加列表页面 /agents。
11. 添加详情页面 /agents/:id。
12. 添加页面 /ailments。
## 组 5 — 测试与验证
13. 添加路由测试。
14. 添加组件渲染测试。
15. 运行 npm test 和 npm run typecheck。
样式决策也应写入规格说明
如果您决定品牌色为橙色和黑色,这不应只留在聊天记录中。请记录下来:
## 视觉方向
- 主要品牌色:橙色和黑色。
- 将颜色用作点缀,而非整页主题。
- 页面应保持可读且适合演示。
Qwen Code 提示:
更新相关规格说明:AgentClinic 的品牌色为橙色和黑色。
然后仅在需要的地方更新 CSS。
不要更改无关页面的样式。
修复规格说明,而非功能规格说明
并非每个分支都是新功能。部分工作是修复错误:用户发现代理列表排序不对,或表单验证漏过了空消息。对于这类变更,requirements.md 模板并不合适:「应该出现什么」这个问题是次要的,主要的是「什么已经坏了」、「为什么」以及「修复时不应改变什么」。
在这种情况下,使用单独的修复规格说明模式会很有用。文件夹结构相同:
specs/
2026-05-12-feedback-empty-message-bug/
bugfix.md
plan.md
validation.md
只是用 bugfix.md 替代 requirements.md,包含不同的章节。
# 修复 — POST /feedback 漏过空消息
## 当前行为
提交空 message 字段的表单时,服务器返回 302 并将空消息字符串保存到 `feedback` 表中。在 /feedback 页面上,该记录显示为空块。
## 预期行为
当 message 为空时,服务器返回 400 和 `/feedback/new` 页面,高亮字段并显示错误文本「消息必填」。不创建数据库记录。
## 根本原因证据
在 `src/routes/feedback.ts` 中,POST 路由接收请求体而未调用验证:请求体直接传递给记录操作。这在 2026-05-10-feedback-form 阶段的提交 3a7c1b9 中可见,验证被标记为 `// TODO` 而推迟。
## 不应改变的内容
- GET /feedback 路由继续返回列表页面。
- `feedback` 表中的现有记录不被编辑或删除。
- 表单字段名称(`name`、`message`)保持不变。
- 400 错误格式与项目中的其他路由一致。
## 回归场景
- 带有非空 `message` 的 POST 仍保存记录并返回 302。
- 带有空 `name` 但非空 `message` 的 POST 仍被保存(如果修复前就是如此)。
- /feedback 页面显示所有先前保存的记录,不做更改。
修复的 plan.md 通常更短:一个组用于修复本身,一个组用于捕获该错误的回归测试。
validation.md 必须包含事实复现:修复前失败、修复后通过的命令或场景。这就是验证是否修复了实际损坏内容的依据:
### F1 — 空消息被拒绝
- 命令:`npm test -- feedback`
- 预期:POST /feedback 带空 message 返回 400 且不创建行
- 负责人:自动检查
- 状态:草稿
修复与功能的主要区别——必须有「不应改变的内容」章节和回归测试。没有这些,代理经常会「改进」相邻代码并破坏原本正常工作的内容。
如果过程中发现修复变成了重构——多个相关错误、新模式、模块重写——关闭修复分支,将成果转移到常规功能分支并重新规划。这不是失败,而是任务从「修复」模式增长到「重做」模式的正常信号。
合并前更新变更日志
根据本分支的工作更新 @CHANGELOG.md。
使用当前日期作为标题。
提及领域模式、路由、页面、测试和规格说明更新。
保持简洁。
实践
- [ ] 从干净的
main开始第二个功能分支。 - [ ] 通过访谈创建规格说明。
- [ ] 分部分实现任务组。
- [ ] 检查偏离边界的情况。
- [ ] 更新变更日志。
- [ ] 仅在审查后合并。
检查问题
- 与代理合作时的认知负荷来自何处,SDD 的哪些技巧可以降低它?
- 为什么样式决策需要记录在规格说明中?
- 哪些迹象表明阶段过大?