tdd-workflow
v3.10.1
Published
TDD Workflow CLI — install TDD skills for AI coding tools
Maintainers
Readme
TDD Workflow
规范驱动的 TDD 全流程工具,为 AI 编程助手安装 TDD 技能和斜杠命令。
一条命令把完整的 需求收集 → 规范文档 → TDD 循环(red → green → refactor) → E2E 验收 → Issue 追踪 → 交付检查 工作流装进你的项目,让 AI 助手按流程驱动开发,而不是随意写代码。
支持 Claude Code · Cursor · Cline · Windsurf · CodeBuddy · GitHub Copilot。
English: README.md
目录
- 快速开始
- 安装方式
- CLI 命令参考
- Multi-Agent 架构
- 框架无关设计
- UseCase-First 工作流
- Verification System
- 自定义文档路径
- 完整工作流示例
- 斜杠命令详解
- Harness Hooks
- Issue 追踪机制
- Three-Strike Protocol
- 目录结构约定
- 支持的工具
- 与 OpenSpec 的对比
- 常见问题
快速开始
# 在项目根目录运行(@latest 强制从 registry 拉最新版,不走本地缓存)
npx tdd-workflow@latest init
# 打开你的 AI 编程助手(如 Claude Code),输入:
/tdd:new
# 按提示完成需求收集后:
/tdd:ff
# 开始 TDD 循环:
/tdd:loop30 秒即可把 TDD 工作流注入项目。
安装方式
方式一:npx 直接运行(推荐)
无需全局安装,在项目根目录下直接运行:
npx tdd-workflow@latest initCLI 会自动扫描项目中的 .claude/、.cursor/ 等目录,弹出交互式多选让你确认要安装到哪些工具。
为什么强烈建议加
@latest:npx tdd-workflow init(不带@latest)会优先用本机已有的全局/缓存版本,可能命中你几个月前装的旧版而不是 npm registry 上的最新版。加上@latest强制从 registry 解析,确保拿到最新代码。
方式二:全局安装
不推荐 —— 全局包不会自动升级,时间长了会落后于 registry。如果一定要装:
npm install -g tdd-workflow@latest
tdd-workflow init
# 升级时
npm install -g tdd-workflow@latest方式三:从源码构建
git clone <repo-url>
cd tdd
npm install && npm run build然后在目标项目中运行:
node /path/to/tdd/bin/tdd.js init要求: Node.js >= 18
CLI 命令参考
tdd-workflow init
在当前项目安装 TDD workflow 技能文件和斜杠命令。
tdd-workflow init [options]| 选项 | 说明 | 默认值 |
|------|------|--------|
| --tools <tools> | 逗号分隔的工具 ID(跳过交互选择) | 自动检测 |
| --delivery <mode> | 安装模式:skills、commands 或 both | both |
| --force | 覆盖已有文件 | false |
示例
# 只为 Claude Code 安装
tdd-workflow init --tools claude
# 同时为 Claude Code 和 Cursor 安装
tdd-workflow init --tools claude,cursor
# 只安装斜杠命令(不安装 SKILL.md 和模板)
tdd-workflow init --delivery commands
# 只安装技能文件(不安装斜杠命令)
tdd-workflow init --delivery skills
# 覆盖已有文件
tdd-workflow init --force安装内容
| 类型 | 文件 | 安装位置 |
|------|------|----------|
| 技能文档 | SKILL.md | .{tool}/skills/tdd-workflow/SKILL.md |
| 规范模板 | usecases.md、requirements.md、design.md、tasks.md、review-checklist.md、verify-project.md、verify-feature.md | .{tool}/skills/tdd-workflow/templates/ |
| Cleanup 预设库 | cleanup.md | .{tool}/skills/tdd-workflow/verify-presets/ |
| 斜杠命令 | 13 个命令(见下方详解) | .{tool}/commands/tdd/ |
| Hooks 脚本 | 5 个 shell 脚本(仅 Claude Code) | .claude/hooks/tdd/ |
| Hooks 配置 | 自动合并到 settings.json(仅 Claude Code) | .claude/settings.json |
同时会自动创建 tdd-specs/ 目录(带 .gitkeep),用于存放生成的规范文件。
tdd-workflow update
检测项目中已安装 TDD workflow 的工具,强制更新所有文件到最新版本:
tdd-workflow update
tdd-workflow update --delivery commands # 只更新命令文件Multi-Agent 架构
TDD Workflow v3.0.0 升级为 Orchestrator + Coder + Tester 三角分工,在 1.x Coder/Reviewer 分离的基础上进一步强化角色隔离。
v3.0 三角分工
Orchestrator (主 Agent)
│ 读 tasks.md → 拆分任务 → 调度 → 最终评审
│
├── spawn Coder Agent ──→ 只知道当前任务描述
│ └── 写测试(RED)/ 写实现(GREEN)/ 重构(REFACTOR)
│ └── 多 UC 并行时,每个 UC 独立一个 Coder(isolation:worktree 物理隔离)
│
├── Orchestrator 评审 Coder 的产出:
│ ✗ 不通过 → 具体反馈给 Coder,重试(最多 2 次)
│ ✓ 通过 → 标 [x],下一个任务
│ 连续两次不通过 → 升级给用户
│
└── spawn Tester Agent(/tdd:e2e)
└── 只读 usecases.md + spec,不读实现代码
→ 信息隔离:测试由用户视角推导,不被实现细节污染
→ 真实服务栈优先,mock 需注释理由核心原则:
- Coder 不知道评审标准——评审标准只在 Orchestrator 读取的
templates/review-checklist.md里 - Tester 不知道实现细节——只能看 spec(usecases / requirements),从用户行为推导 E2E,无法为实现细节定制测试
- 物理隔离——多 UC 并行时
isolation:worktree确保 Coder 间不互相污染
并行 Coder(多 UC 同时推进)
当 /tdd:loop 发现多个独立 UC 的 Phase 2 任务时,Orchestrator 可以并行 spawn 多个 Coder:
Orchestrator
├── Coder-A (worktree-a): UC-01 所有层 (unit → impl → frontend)
├── Coder-B (worktree-b): UC-02 所有层 (unit → impl → frontend)
└── 等待两个 Coder 完成 → 合并评审 → 全量回归依赖同一模块的 UC 不并行(Orchestrator 自动检测依赖链)。
跨工具兼容
| 工具 | 模式 | 实现角色 | 测试角色 | 评审角色 | |------|------|---------|---------|---------| | Claude Code | Multi-Agent | Coder Agent | Tester Agent | Orchestrator | | Cursor / CodeBuddy / Cline / 其他 | Single-Agent + Self-Review | 自己 | 自己 | 强制 self-review |
非 Claude Code 工具会自动降级。Reviewer 步骤仍然强制——必须输出评审结果才能继续:
[Review:RED] ✓ covers task scenario | ✓ behavior test | ✓ fails correctly | Issues: none
[Review:GREEN] ✓ minimum code | ✓ no test mods | ✓ full suite passes | Issues: noneReview Checklist
Orchestrator 按 templates/review-checklist.md 评审,主要检查项:
测试评审(RED 阶段):
- 测试描述行为而非实现
- 测试名可读:
should <行为> when <条件> - 覆盖 tasks.md 中描述的场景
- 测试因「功能未实现」而失败,而非语法错误
实现评审(GREEN 阶段):
- 最小代码——无提前抽象或过度设计
- 未修改测试文件
- 全量测试通过(无回归)
框架无关设计(v3.0)
v3.0 移除了所有项目特有假设,命令文档对任何技术栈都适用:
| 维度 | v2.x(项目绑定) | v3.0(框架无关) |
|------|----------------|----------------|
| 目录结构 | backend/ web-admin/ miniprogram/ | src/** app/** lib/** |
| 测试框架 | jest / auto-regression.sh | 自动检测项目测试命令 |
| 前端格式 | .wxml .wxss(微信小程序) | .tsx .vue .svelte .html+js+css |
| 外部服务 | 微信 / 腾讯云 / COS | 通用 mock 注释策略 |
| E2E 启动 | NestJS 固定脚本 | 从 package.json scripts 推导 |
AI 助手会根据项目实际的 package.json / pyproject.toml / go.mod 等自动适配,不再需要手动调整命令文档。
UseCase-First 工作流
TDD Workflow 2.4.0 把 UseCase 提升为 /tdd:ff 的主产出。其他所有文档——requirements、design、tasks、E2E 测试——都从 UseCase 派生。
为什么
之前的问题:
- requirements.md 只有声明性 AC("系统应支持评论"),缺用户视角的完整交互故事
/tdd:e2e让 AI 凭空发明 E2E 用例,容易漏关键路径/tdd:change影响分析跳过用户交互层,直接从 REQ 推 task- PM/QA 看不懂 EARS 格式的 requirements,但能看懂 UseCase
文档派生关系
usecases.md (主产出)
│
├─→ requirements.md (每个 REQ 标注 "来源 UC-01, UC-02")
├─→ design.md (每个接口标注 "支持 UC-01 step 2")
├─→ tasks.md (Phase 2 按 UC 分组,每个 task 标注 "Covers UC-N")
└─→ E2E tests (每个 UC 路径一个 E2E,命名 "UC-N: <路径>")为什么 UseCase 应该是主产出
UseCase 解决的问题是 requirements.md 替代不了的——requirements 是声明性的("系统应支持评论"),UseCase 是过程性的(用户点什么 → 系统做什么 → 用户看到什么)。
1. UseCase 和 requirements 解决的问题不同
- requirements.md:REQ-01 "系统应支持用户发表评论"(声明性,what)
- UseCase:从用户视角的完整交互故事(过程性,how & when)
UseCase 能表达 requirements 无法表达的东西:前端反馈、状态保留、错误恢复、用户感知。例如"评论超长时要高亮超出部分并保留输入"——这是用户体验,不是 AC 能说清的。
2. UseCase 是 E2E 测试的天然蓝图
之前 /tdd:e2e 让 AI 自己发明 E2E 测试用例,容易漏掉关键路径。现在:
UC-01 的每条路径 → 一个 E2E 测试用例
成功路径 → 正向 E2E
备选 3a (内容为空) → 前端校验 E2E
备选 3b (内容超长) → 边界校验 E2E
备选 4a (DB 失败) → 错误恢复 E2EE2E 测试覆盖率直接由 UseCase 决定,不再是"AI 觉得该测什么"。
3. UseCase 是 Verification System 的 flows 来源
Verification System 里的 feature_specific_flows 其实就是 UseCase 的另一种表达。有了 usecases.md,Stage 2 验证流程可以自动从 UseCase 派生,省去手工填写 verify.md。
4. UseCase 是需求变更的影响分析载体
之前 /tdd:change 直接从 REQ 推 task,缺少中间的用户视角桥梁。有了 UseCase:
变更:"评论支持 Markdown"
↓
UC-01 的哪些步骤变了?
步骤 1: 输入框 → 改为支持 Markdown 编辑器
步骤 6: 追加到列表 → 渲染需要解析 Markdown
新增备选 3c: Markdown 语法错误 → 如何反馈?
↓
自然推导出 requirements / design / tasks 的变更点分析链路完整、可追溯,不会漏改文档。
5. UseCase 是跨角色沟通的通用语言
非开发角色看不懂 EARS 格式的 requirements 或 design 的技术细节。但 UseCase 他们能看懂:
| 角色 | 能看懂 requirements.md? | 能看懂 usecases.md? | |------|--------------------------|----------------------| | 产品经理 | ❌ EARS 语法太技术 | ✅ "用户做 X → 系统做 Y" | | QA | 部分能 | ✅ 直接能照着测 | | 设计师 | ❌ | ✅ 流程清晰 | | 新同事 | 难以快速理解 | ✅ 从用户视角入手最快 |
这也是为什么 /tdd:done Stage 4.2 要把 UC 同步到项目全局 docs/usecases/——让 PM/QA/新同事都能查到最新的用户流程。
UseCase 结构
## UC-01 用户发表评论
**主角色** (Actor): 已登录用户
**前置条件** (Precondition): 用户已登录且在文章详情页
**触发事件** (Trigger): 用户点击"发表评论"按钮
### 成功路径
1. 用户输入评论内容
2. 系统校验内容长度(≤500 字)
3. 系统写入数据库
4. 系统返回评论对象
5. 前端追加到列表
### 备选路径
**3a.** 内容为空 → 系统提示"请输入内容",停留输入框
**3b.** 内容超长 → 系统提示"最多 500 字",高亮超出
**4a.** 写库失败 → 显示"提交失败,请重试",保留输入
### 后置条件
新评论已写入数据库,前端列表已更新
### 相关数据
| 字段 | 类型 | 约束 |
| content | string | 1-500 字 |工作流
/tdd:new blog-comments
# 需求收集时,"核心场景"维度收集到步骤粒度
# 创建 usecases.draft.md 作为 /tdd:ff 的输入
/tdd:ff
# Step 3: 生成 usecases.md(主产出,UC-01、UC-02...)
# Step 4: 生成 requirements.md(每个 REQ 标注 UC 来源)
# Step 5: 生成 design.md(每个接口标注支持的 UC)
# Step 6: 生成 tasks.md(Phase 2 按 UC 分组)
# Step 7: 覆盖检查(每个 UC 路径都有对应 task)
/tdd:loop
# TDD 循环,每个 task 覆盖一个 UC 路径
/tdd:e2e
# 从 usecases.md 派生 E2E,每个 UC 路径一个测试
# 测试命名:UC-01: 成功路径 / UC-01: 内容超长校验 (备选 3b)
/tdd:done
# Stage 4.2: 交互式同步 UC 到项目全局 docs/usecases/
# 生成 usecases.synced.md 记录同步
/tdd:archive
# 检查是否已同步 docs/,未同步则提示
# docs/usecases/ 保持不动,feature 目录整体归档UC 编号:Feature 内 vs 项目全局
Feature 内部用局部编号 UC-01、UC-02:
tdd-specs/blog-comments/usecases.md
- UC-01 用户发表评论
- UC-02 作者删除评论同步到 docs/usecases/ 时自动映射为项目全局编号:
docs/usecases/comments.md
- UC-025 用户发表评论 (原 UC-01)
- UC-026 作者删除评论 (原 UC-02)映射关系记录在 tdd-specs/<feature>/usecases.synced.md,便于追溯。
同步时机
- 默认:
/tdd:doneStage 4.2 交互式同步 - 变更:
/tdd:change检测到已同步 UC 变更时,询问同步策略(待同步/立即/仅 tdd-specs) - 归档前:
/tdd:archive如发现未同步会警告
变更影响分析
/tdd:change 的影响分析把 UseCase 维度放最前面:
## Change Impact Assessment
### Affected UseCases(主维度)
| UseCase | Affected Step | Impact Type | Description |
| UC-01 | step 2, step 6 | Modify | 评论输入改为 Markdown 编辑器 |
| UC-01 | 新增 3c 备选 | Add | Markdown 语法错误提示 |
### Affected Requirements (从 UC 推导)
...
### Affected Tasks
...
### UC 同步状态
- usecases.synced.md 记录: 2026-04-20 → docs/usecases/comments.md
- 推荐同步策略: 标记待同步,交付时再同步Verification System
TDD Workflow 2.3.0 引入 分层验证系统,解决「AI 自己跑测试、自己判断通过」的质量黑洞。
核心问题
之前 /tdd:done 的交付检查是通用、静态的——AI 说"测试通过了、覆盖率达标",但:
- 这个具体项目怎么跑测试?(
npm test还是pytest?) - 这个具体 feature 要怎么验证?(登录能成功?JWT claims 对不对?)
- 业务侧验收靠什么?(手动点?脚本?人工确认?)
解决方案:两层配置 + 四阶段执行
┌─ 项目级 (tdd-specs/.verify/project.md) ──────────────────────┐
│ 团队共享配置:commands / environments / common_flows │
│ - 怎么跑单测/集成/E2E?覆盖率目标? │
│ - dev 环境怎么启动?健康检查怎么做? │
│ - staging 怎么部署?部署后怎么冒烟? │
│ - pre/post cleanup 清什么?(端口、容器、数据库) │
└──────────────────────────────────────────────────────────────┘
┌─ 个人参数 (tdd-specs/.verify/project.local.md, gitignored) ─┐
│ 每人不同的参数:MY_USER / DEV_PORT / 测试账号密码 │
│ 敏感参数建议从 shell env 或 1Password 读 │
└──────────────────────────────────────────────────────────────┘
┌─ Feature 级 (tdd-specs/<name>/verify.md) ──────────────────┐
│ 只写项目级没覆盖的部分 │
│ - feature 特有的流程 │
│ - 依赖的项目级验证 │
└──────────────────────────────────────────────────────────────┘4 阶段执行(/tdd:done)
Stage 1: 本地代码验证(全自动)
typecheck → lint → build → unit → integration → coverage
全过才继续
Stage 2: 本地 E2E 验证(人机交互)
pre_verify_cleanup
→ 杀占用端口的进程
→ 停 Docker Compose
→ 重置数据库
启动 dev server + 等待 readiness
跑 common_flows(登录、创建资源...)
→ 每个 flow 展示给用户,等待"通过"或问题描述
跑 feature_specific_flows
post_verify_cleanup
Stage 3: 测试环境验证(可选)
执行 deploy 脚本
等待 staging readiness
跑 post_deploy_smoke
Stage 4: 交付确认
生成 verification-report.md
输出监控链接
提示 /tdd:notes 和 /tdd:archive工作流
# 首次使用(项目生命周期一次)
/tdd:verify-setup # 交互式配置项目级验证,帮你生成 deploy 脚本骨架
/tdd:verify-local # 填你的个人参数
# 每个 feature
/tdd:new xxx # 需求收集最后一步询问 feature 特有的验证需求
/tdd:ff # 生成规范
/tdd:loop # TDD 循环
/tdd:done # 4 阶段验证
/tdd:archive # 归档(verification-report.md 随之归档)
# 手动
/tdd:cleanup dev # 验证跑崩后手动清理Cleanup 预设库
内置 8 个 cleanup 预设,/tdd:verify-setup 根据项目技术栈自动推荐:
| 预设 | 用途 |
|------|------|
| kill_port | 杀占用端口的进程 |
| kill_node_process | 按模式杀进程 |
| docker_compose_down | 停 Docker Compose(可选清 volumes) |
| docker_container_rm | 删匹配名的容器 |
| reset_db | 重置数据库 |
| clean_tmp_files | 清临时文件 |
| clear_redis | 清 Redis |
| git_clean | 清 git 未跟踪文件 |
每个步骤支持 on_fail(continue/abort/ask)、timeout、condition、parallel 字段。
参数化
配置文件用 ${VAR} 和 ${VAR:-default} 语法。解析优先级:
- 命令行
--var VAR=xxx - Shell 环境变量(推荐用于敏感参数)
project.local.md(个人参数)project.md的默认值- 必填但未提供 → 报错
敏感参数(*_TOKEN / *_PASSWORD / *_SECRET 等)建议从 shell env 或 1Password CLI 读,不写入任何文件。
自定义文档路径
不是所有项目都用 docs/usecases/ 和 docs/issues/。有些项目用 documentation/、spec/,有些团队干脆把 UseCase 管在 Confluence、把 Issue 管在 Jira。TDD Workflow 2.4.1 起支持在 tdd-specs/.verify/project.md 统一配置这些路径。
配置方式
/tdd:verify-setup 的 Phase F 会交互式问:
🤖 UseCase 要放在哪?
[A] docs/usecases/(默认)
[B] documentation/usecases/
[C] spec/usecases/
[D] 自定义路径
[E] 外部工具(Confluence / Notion / 飞书 / 语雀)
[F] 不需要
🤖 Issue 要放在哪?
[A] docs/issues/(默认)
[B] documentation/issues/
[C] 自定义
[D] 外部工具(Jira / GitHub Issues / Linear)
[E] 不需要结果写入 tdd-specs/.verify/project.md:
paths:
usecases:
enabled: true
dir: "documentation/usecases"
index_file: "documentation/usecases/README.md"
numbering: "auto" # auto | feature_local | manual
issues:
enabled: true
dir: "documentation/issues"
index_file: "documentation/issues/README.md"
numbering: "auto"
filename_pattern: "<NNN>-<module>-<keyword>.md"外部工具模式
如果 UseCase 由 Confluence 管理、Issue 由 Jira 管理,选 enabled: false + 填 external_tool / external_url:
paths:
usecases:
enabled: false
external_tool: "Confluence"
external_url: "https://company.atlassian.net/wiki/spaces/PROD/pages/123456"
issues:
enabled: false
external_tool: "Jira"
external_url: "https://company.atlassian.net/jira/projects/PROJ"这时:
/tdd:bug不创建本地文件,而是输出 Issue 内容让你粘贴到 Jira,然后记录返回的 Issue ID(如PROJ-1234)到追溯链/tdd:doneStage 4.2 不写本地文件,而是输出 UC 内容 + 提示你打开 Confluence 链接手动更新/tdd:change对已同步 UC 的变更默认走"待同步"策略,交付时再人工更新外部
为什么不自动调 Jira/Confluence API? 团队的权限、审批、模板各不相同,自动化容易踩坑。输出内容 + 提示是最稳的做法。
向后兼容
没配 paths: 节或没有 project.md 的项目会自动用 docs/usecases/ 和 docs/issues/ 作为默认,不会 break。
完整工作流示例
下面用一个「给博客系统添加评论功能」的例子,演示从零到交付的完整流程。
第 1 步:启动新功能
> /tdd:new blog-commentsAI 助手会逐步问你 6 个维度的问题:
🤖 谁会使用这个功能?
👤 已登录用户可以发表评论,文章作者可以删除评论
🤖 最重要的 1-3 个使用场景?
👤 1. 用户在文章底部提交评论 2. 作者删除不当评论 3. 访客查看评论列表
🤖 输入和输出分别是什么?
👤 输入:评论文本(最长 500 字)、文章 ID。输出:评论对象(含作者、时间、内容)
🤖 哪些情况会失败?
👤 未登录提交、文章不存在、评论内容为空或超长
🤖 什么明确不做?
👤 不做嵌套回复、不做评论编辑、不做点赞
🤖 怎样算完成?
👤 能发评论、能看列表、能删评论、有权限控制确认后,AI 创建 tdd-specs/blog-comments/ 目录。
第 2 步:一键生成规范文档
> /tdd:ffAI 自动生成三份文档:
OK requirements.md — 4 requirements, 8 acceptance criteria
OK design.md — 3 modules, 5 interfaces
OK tasks.md — Phase 1: 3 items / Phase 2: 17 items (by UC, all layers) / Phase 3: 3 E2E items
Issues reviewed: none
Ready! Run /tdd:loop to start TDD implementation.生成的 tasks.md 按 UC 纵切(每个 UC 包含它触及的所有技术层:DB / 后端 / 前端 / Client),Phase 1 只放跨 UC 的基础设施:
## Phase 1: Infrastructure (跨 UC 共享)
- [ ] 1.1 DB migration: create `comments` table (execute on local dev DB + SHOW TABLES 验证)
- [ ] 1.2 CommentEntity 骨架 + module 注册
- [ ] 1.3 schema:dump
## Phase 2: Unit Tests + Implementation (按 UC 纵切)
### UC-01 用户发表评论
- [ ] 2.1 backend unit test — CommentService.create happy path
- [ ] 2.2 backend impl — CommentService.create
- [ ] 2.3 backend unit test — 输入校验(空、超长)
- [ ] 2.4 backend impl — validation
- [ ] 2.5 integration test — POST /api/comments 完整链路(含 DB 写入校验)
- [ ] 2.6 frontend page — 评论输入框 + 提交按钮(js/wxml/wxss/json 或 tsx)
### UC-02 作者删除评论
- [ ] 2.7 backend unit test — CommentService.delete by author
- [ ] 2.8 backend impl — delete with permission check
- [ ] 2.9 integration test — DELETE /api/comments/:id 权限 4xx
- [ ] 2.10 frontend — 删除按钮 + 确认对话框
### UC-03 访客查看列表
- [ ] 2.11 backend unit test — CommentRepo.listByPost 分页
- [ ] 2.12 integration test — GET /api/posts/:id/comments
- [ ] 2.13 frontend — 列表渲染 + 加载更多
## Phase 3: E2E Acceptance
- [ ] 3.1 E2E: user submits comment and sees it in list
- [ ] 3.2 E2E: author deletes comment
- [ ] 3.3 E2E: unauthenticated user sees comments but cannot post关键约束(由 /tdd:ff 和 /tdd:loop 强制):
- 禁止单独 Phase 4 前端 — 前端页面 task 必须归到它实现的那个 UC 下的 Phase 2
- DB migration 必须真跑 — Phase 1 的 migration task 不只是写 SQL,还要真执行到本地 dev DB 并用
SHOW TABLES验证 - [x] 必须有证据 — 禁止"待后续 / TODO / skip"同行标
[x],未完成改为[!]+ blocker 理由
第 3 步:TDD 自动循环
> /tdd:loopAI 自动循环执行(Claude Code 中 Orchestrator 调度 Coder 子 agent,支持多 UC 并行):
- RED — Coder 写一个失败的测试 → Orchestrator 评审测试质量
- GREEN — Coder 写最少代码让测试通过 → Orchestrator 评审实现 + 跑全量回归
- REFACTOR — 消除重复、改善命名
[Orchestrator] UC-01 & UC-02 互相独立,并行启动 Coder-A / Coder-B
[Coder-A] writing test: comment-service.test.ts: createComment
[Review:RED] ✓ covers task scenario | ✓ behavior test | ✓ fails correctly
[Coder-A] writing impl: comment-service.ts: createComment
[Review:GREEN] ✓ minimum code | ✓ no test mods | ✓ 5/5 tests pass
[Coder-B] writing test: comment-delete.test.ts: deleteByAuthor
...
Phase 1 + 2 complete: 20/20 tasks, 28 tests, 3.2s elapsed
Run /tdd:e2e for E2E acceptance.如果某个测试连续失败 3 次,触发 Three-Strike Protocol,暂停并等你决策。
第 4 步:E2E 验收
> /tdd:e2eAI 检测项目的 E2E 框架(Playwright / Cypress 等),编写并运行端到端测试。
第 5 步:交付
> /tdd:doneAI 执行完整交付检查清单:
✓ Compilation clean
✓ All tests passing (28 unit + 3 integration + 3 E2E), coverage 91%
✓ Full regression passing
✓ Issues logged: #012-comment-validation-edge-case
✓ tdd-specs/blog-comments/tasks.md all [x]
## Delivery Report — blog-comments
- Added: src/services/comment-service.ts, src/routes/comments.ts, ...
- Modified: src/routes/index.ts
- Tests: 28 unit PASS, 3 integration PASS, 3 E2E PASS
- Issues: #012
Run /tdd:archive to archive specs.第 6 步:归档
> /tdd:archive将已完成的规范移入 tdd-specs/archive/2026-04/blog-comments/,清空 .current。
斜杠命令详解
/tdd:new <name> — 启动新功能
交互式收集需求,覆盖 6 个维度:目标用户、核心场景、输入输出、错误处理、范围边界、验收标准。
> /tdd:new payment-retry只创建目录 tdd-specs/payment-retry/,不生成文档——确保需求确认后再动笔。
/tdd:ff — 快速前进(UseCase-First)
一次性产出四份规范,以 usecases.md 为权威源头,其余三份从它派生:
| 产出 | 角色 |
|---|---|
| usecases.md | 主产出(Actor / 前置 / 触发 / 成功路径 / 备选路径 / 后置 / 相关数据),PM/QA 的权威文档 |
| requirements.md | EARS 格式需求,每条引用 UC 编号 |
| design.md | 架构/接口,标注"支持的 UseCase" |
| tasks.md | 按 UC 纵切(每个 UC 含它触及的所有层:DB / 后端 / 前端);Phase 1 放跨 UC 基础设施;Phase 2 每 task 标注 Covers UC-N step M;Phase 3 是 E2E |
usecases.md 详见 UseCase-First 工作流 章节。
自动执行的检查(cannot skip):
- 测试覆盖检查 — 单元/集成/E2E 三层任何一层 task 为 0 时主动补
- Vertical Slice Rule — Phase 2 里每个 UC 必须包含它涉及的所有技术层(禁止单独的"Phase 4 前端")
- DB migration 执行 — 有 migration 的 Phase 1 必须真跑到本地 dev DB +
SHOW TABLES验证,不是只写 SQL
如果项目启用了 截图验证,/tdd:ff 会在 UC 生成时主动为成功路径/错误态打 @screenshot:<label> 标注。
/tdd:loop — TDD 自动循环(Phase 1 + Phase 2)
自动循环直到 tasks.md 的 Phase 1(基础设施)+ Phase 2(代码实现)全部 [x]。
循环伪代码:
WHILE tasks.md 有任何 [ ] 或 [~] task:
IF Phase 1 任务 (migration / scaffolding):
直接执行 + 验证(例:SHOW TABLES)→ 标 [x]
ELSE IF Phase 2 "unit test" 任务:
RED phase — 写 failing test
ELSE IF Phase 2 "implement" 任务:
GREEN phase — 写最小实现(查 issues 目录防重犯)
ELSE IF Phase 2 前端页面任务:
直接写页面文件(js/wxml/wxss/json 或 tsx)+ app 注册 → 标 [x]
REFACTOR phase — 去重改名(可选)
IF 同测试失败 3 次:
Three-Strike Protocol → 停下问用户说明:上面的 RED / GREEN / REFACTOR 是
/tdd:loop内部的阶段标记,不是独立的斜杠命令。不用也不能手工敲/tdd:red——loop 会按任务类型自动进入对应阶段。
关键行为:loop 按 UC 纵切处理 —— 先把 UC-01 的所有层(backend test → backend impl → frontend page)跑完,再进 UC-02,保证每个 UC 随它的任务完成整体可交付。
标 [x] 必须有证据(MANDATORY):
| 任务类型 | 标 [x] 前必须验证 |
|---|---|
| Unit test | 测试文件存在 + 运行框架显示它通过 |
| Implementation | 源文件存在 + 相关测试通过 |
| Frontend page | 所有页面文件齐 + 在 app config 注册 |
| DB migration | SHOW TABLES 确认新表存在 |
| 任意 | 禁止同行带"待后续 / TODO / skip";阻塞的任务改用 [!] + 理由 |
每轮循环开始前执行 任务完整性扫描,检查是否遗漏:migration 执行、真实 DB 集成测试、错误响应解析、崩溃恢复、超时处理、HTTP 集成测试等场景。
为什么 /tdd:loop 和 /tdd:e2e 分开
/tdd:loop 跑 Phase 1 + Phase 2(基础设施、单测、集成、前端页面),/tdd:e2e 跑 Phase 3(端到端)。分开是因为两者的速度、依赖、失败语义、触发频率完全不同:
| 维度 | /tdd:loop (Phase 1 + 2) | /tdd:e2e (Phase 3) |
|------|----------------------|----------------------|
| 速度 | 秒级(单测几秒,集成几十秒) | 分钟级(E2E 动辄几分钟) |
| 依赖 | 纯代码 + 本地 DB,可 mock 外部 | 需要跑起来真实服务、浏览器自动化 |
| 失败语义 | 实现有 bug,改代码 | 可能是 bug / 集成问题 / 环境问题 |
| 触发频率 | 每个 task 一轮(十几次~几十次) | Phase 1 + 2 都完成后一次 |
| 硬规则 | 测试行为而非实现 | 必须经过真实网络层、关键字段断言 |
速度是最关键的。TDD 的核心价值是快速反馈循环——写个测试立刻看它失败、改代码立刻看它通过。如果每轮循环要等 5 分钟 E2E,TDD 就废了。
合并的代价:每 loop 一轮就重启环境、拉浏览器、跑 E2E,大部分 E2E 是重复在跑(因为底层代码没变就只是加了下一个单测)。单测覆盖的场景,E2E 再跑一遍没价值。
分开的好处:
/tdd:loop保持快速紧凑反馈/tdd:e2e集中在 Phase 1 + Phase 2 都完成后一次性验收所有用户路径(从usecases.md派生,见 UseCase-First 工作流)- 失败排查路径清晰——单测失败看 assertion,E2E 失败查日志/浏览器/数据库
/tdd:bug — Bug 修复流程
完整的 bug 修复链路:
> /tdd:bug
🤖 请描述 bug 症状:
👤 提交评论时偶尔返回 500,日志显示 "duplicate key"
[Analysis] Root cause: race condition in comment-service.ts, no idempotency key
[RED] duplicate-comment.test.ts — FAIL ✗ (bug reproduced)
[GREEN] Added idempotency check — 29/29 tests PASS ✓
[Done] Issue #013 archived, docs/issues/013-comment-duplicate-key.md自动创建 Issue 文档,包含:症状、根因分析、修复方案、验证步骤、预防措施。
/tdd:change — 中途变更需求
安全地修改进行中的需求,自动分析影响范围:
> /tdd:change
🤖 请描述变更内容:
👤 评论需要支持 Markdown 格式
## Change Impact Assessment
### Affected Spec Entries
| Document | Entry | Impact Type | Description |
|----------|-------|-------------|-------------|
| requirements.md | REQ-01 | Modify | Comment input supports Markdown |
| design.md | CommentService | Modify | Add markdown parsing step |
### Affected Tasks
| Task | Current Status | Action Needed |
|------|---------------|---------------|
| 2.1 createComment test | [x] Completed | WARNING: needs redo |
| 2.2 createComment impl | [x] Completed | WARNING: needs redo |
| 2.N markdown parsing | — | New task |
### Risk Notes
- Completed tasks affected: 2
- Estimated additional work: medium
Confirm to proceed? [A] Confirm [B] Adjust [C] Cancel安全机制:如果变更导致 10+ 个已完成任务需要回退,建议重新跑 /tdd:ff。
/tdd:continue — 恢复进度
从上次中断处继续。自动定位第一个未完成的任务:
> /tdd:continue blog-comments
Resumed: tdd-specs/blog-comments/
Completed: 8/12 tasks
Current phase: Phase 2
Next step: 2.9 integration test — GET /api/posts/:id/comments pagination
Run /tdd:loop to continue./tdd:e2e — E2E 验收测试
自动检测项目的 E2E 框架,编写并运行端到端测试。
硬规则:
- 必须经过真实网络层(不允许直接操作 store)
- 跳过的测试必须写明原因(累计超过 3 个必须搭建 mock 环境解决)
- 每个关键动作后必须断言结果
- 关键业务字段必须断言具体值
/tdd:done — 交付检查
每一项检查必须通过才能继续:
[ ] 编译通过(编译型语言必检)
[ ] 全量测试通过,覆盖率 >= 80%
[ ] 全量回归通过
[ ] E2E 通过
[ ] 功能文档已更新
[ ] Issue 已记录(如有符合条件的 bug)
[ ] 环境变量示例已同步
[ ] tasks.md 全部 [x]/tdd:archive — 归档规范
验证所有任务完成后,移入 tdd-specs/archive/YYYY-MM/。
Harness Hooks
仅 Claude Code 支持。其他工具的质量约束靠 Prompt 层 + self-review。
tdd-workflow init --tools claude 会自动安装 5 个 hook 脚本到 .claude/hooks/tdd/ 并注入 .claude/settings.json:
| Hook | 触发时机 | 作用 |
|------|---------|------|
| pre-write-edit.sh | 写/编辑文件前 | RED 阶段阻止写 src/ 文件(exit 2),只允许写测试文件 |
| pre-bash.sh | 执行命令前 | 代码变更后未跑测试时提醒 |
| post-write-edit.sh | 写/编辑文件后 | 记录 last_edit_time 到 .harness |
| post-bash.sh | 执行命令后 | 检测测试命令结果,追踪 strikes,3 次失败触发 Three-Strike |
| user-prompt-submit.sh | 用户发消息时 | 注入当前 phase/task/strikes 状态到 Claude 上下文 |
.harness 状态文件
每个功能目录下有独立的 .harness 文件(tdd-specs/<name>/.harness),记录:
phase=red # 当前阶段 (requirements/spec/red/green/refactor/e2e/deliver)
task=2.3 # 当前任务
strikes=0 # GREEN 阶段连续失败次数
last_test_time=1713234567 # 上次跑测试的时间戳
last_edit_time=1713234500 # 上次编辑代码的时间戳多功能并行开发时,每个功能的状态独立——切换 .current 就切换了 harness 上下文。
注意事项
- init 后需要重启 Claude Code session,hooks 才会被加载(settings.json 只在启动时读取)
- 已有的
.claude/settings.json不会被覆盖,TDD hooks 是追加合并的 - hooks 使用
sed -i''语法,兼容 macOS 和 Linux
Issue 追踪机制
TDD Workflow 内置轻量级 Issue 追踪,确保每个有价值的 bug 都被记录和追溯。
何时必须创建 Issue
满足以下任一条件即必须创建:
- Bug 修复耗时超过 5 分钟
- 同类错误出现 2 次以上
- 修复涉及 2 个以上文件
Issue 文档格式
Issue 存放在 docs/issues/ 目录(如果项目有该目录):
# Issue #012 — 评论验证边界条件未处理
## Basic Info
| Field | Content |
|-------|---------|
| Discovered | 2026-04-08 |
| Module | `src/services/comment-service.ts` |
| Severity | Medium |
| Status | Fixed |
## Symptoms
提交 501 字评论时返回 500 而非 400
## Root Cause Analysis
validateComment() 使用 > 而非 >= 比较,500 字恰好绕过校验,
导致数据库 VARCHAR(500) 约束抛出未处理异常。
## Fix
| File | Change |
|------|--------|
| src/services/comment-service.ts | `> 500` → `>= 500` |
| test/comment-service.test.ts | Added boundary test for 500/501 chars |
## Verification Steps
npm test -- --testPathPattern="comment-service"
## Prevention Measures
- 数值边界检查统一使用 >= 比较
- 所有字段长度限制必须有边界值测试Issue 何时被查阅
| 时机 | 方式 |
|------|------|
| /tdd:ff 生成规范前 | 浏览项目 issues 目录,关联已知问题 |
| 每次 GREEN 阶段(/tdd:loop 内部)写实现前 | 搜索相关错误关键词,避免重复踩坑 |
| Three-Strike Protocol 触发后 | 全文搜索 + 模块过滤 |
Three-Strike Protocol
当同一个测试连续失败 3 次时,自动触发暂停机制:
⚠️ WARNING: Three-Strike Protocol
Test: comment-service.createComment.should-handle-concurrent-requests
Attempt history:
1. Added mutex lock -> "deadlock detected"
2. Switched to optimistic locking -> "version conflict not handled"
3. Added retry logic -> "max retries exceeded in test"
Issues search: found #009-database-connection-pool (similar concurrency issue)
Please choose:
A. Try a different approach (describe your idea)
B. Split into smaller test granularity
C. Mark [!] skip, move to next task
D. Need more context这个机制防止 AI 在同一个问题上无限循环,把决策权交还给你。
目录结构约定
安装后,项目中会出现以下结构:
your-project/
├── .claude/ # (或 .cursor/ .cline/ 等)
│ ├── settings.json # hooks 配置(仅 Claude Code,自动合并)
│ ├── hooks/tdd/ # harness hook 脚本(仅 Claude Code)
│ │ ├── pre-write-edit.sh
│ │ ├── pre-bash.sh
│ │ ├── post-write-edit.sh
│ │ ├── post-bash.sh
│ │ └── user-prompt-submit.sh
│ ├── skills/tdd-workflow/
│ │ ├── SKILL.md # 完整技能定义
│ │ ├── templates/ # 规范文档模板
│ │ │ ├── requirements.md
│ │ │ ├── design.md
│ │ │ ├── tasks.md
│ │ │ ├── review-checklist.md # Reviewer 评审标准
│ │ │ ├── verify-project.md # 项目级验证模板
│ │ │ └── verify-feature.md # Feature 级验证模板
│ │ └── verify-presets/ # Cleanup 预设库
│ │ └── cleanup.md
│ └── commands/tdd/
│ ├── new.md # /tdd:new
│ ├── ff.md # /tdd:ff
│ ├── loop.md # /tdd:loop
│ ├── bug.md # /tdd:bug
│ ├── change.md # /tdd:change
│ ├── continue.md # /tdd:continue
│ ├── e2e.md # /tdd:e2e
│ ├── verify-setup.md # /tdd:verify-setup
│ ├── verify-local.md # /tdd:verify-local
│ ├── cleanup.md # /tdd:cleanup
│ ├── done.md # /tdd:done (4 阶段验证)
│ ├── notes.md # /tdd:notes
│ └── archive.md # /tdd:archive
└── tdd-specs/ # 规范文件存放目录
├── .current # 当前活跃的功能名
├── .verify/ # 项目级验证配置
│ ├── project.md # 团队共享(提交 git)
│ └── project.local.md # 个人参数(gitignored)
├── blog-comments/ # 进行中的功能
│ ├── .harness # harness 状态
│ ├── requirements.md
│ ├── design.md
│ ├── tasks.md
│ ├── verify.md # feature 级验证(独有部分)
│ └── verification-report.md # /tdd:done 生成的验证报告
└── archive/ # 已完成的功能
└── 2026-04/
└── user-auth/任务状态标记
| 标记 | 含义 |
|------|------|
| - [ ] | 未开始 |
| - [~] | 进行中(RED 已写,GREEN 未完成) |
| - [x] | 已完成 |
| - [!] | 阻塞(Three-Strike Protocol 触发,等待决策) |
支持的工具
| 工具 | ID | 技能目录 | 命令目录 |
|------|----|----------|----------|
| Claude Code | claude | .claude/skills/tdd-workflow/ | .claude/commands/tdd/ |
| Cursor | cursor | .cursor/skills/tdd-workflow/ | .cursor/commands/tdd/ |
| Cline | cline | .cline/skills/tdd-workflow/ | .cline/commands/tdd/ |
| Windsurf | windsurf | .windsurf/skills/tdd-workflow/ | .windsurf/commands/tdd/ |
| CodeBuddy | codebuddy | .codebuddy/skills/tdd-workflow/ | .codebuddy/commands/tdd/ |
| GitHub Copilot | copilot | .github/skills/tdd-workflow/ | .github/commands/tdd/ |
工具检测逻辑:扫描项目根目录下是否存在对应的配置目录(.claude/、.cursor/ 等)。
与 OpenSpec 的对比
OpenSpec 是另一个流行的规范驱动开发工具。两者解决的问题不同:
OpenSpec 解决「AI 不知道该做什么」——让 AI 按规范写代码。
TDD Workflow 解决「AI 写的代码不靠谱」——用测试驱动 + 质量门禁确保代码真正能用。
流程对比
OpenSpec: Propose → Review specs → Apply (AI 自由发挥写代码) → Archive
TDD Workflow: 需求收集 → 规范生成 → RED → GREEN → REFACTOR → E2E → 交付检查 → 归档
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OpenSpec 没有覆盖的部分OpenSpec 的 Apply 就是一句 /opsx:apply——怎么写代码、怎么测试、质量如何,全靠 AI 自由发挥。TDD Workflow 的规范生成只是起点,重点在后面整个 TDD 循环和质量门禁。
TDD Workflow 有而 OpenSpec 没有的
| 能力 | TDD Workflow | OpenSpec |
|------|-------------|---------|
| 强制测试先行 | RED 必须先看到测试失败才能写实现代码 | 无测试约束,Apply 直接生成代码 |
| 自动 TDD 循环 | /tdd:loop 自动 red→green→refactor 直到完成 | 无对应机制 |
| Three-Strike Protocol | 同一测试失败 3 次自动暂停,等人决策 | 无失败熔断,AI 可能无限循环 |
| 测试覆盖检查 | 生成 tasks 后自动检查单元/集成/E2E 三层是否都有任务,缺了自动补 | 不关心测试分层 |
| 回归保护 | 每次 GREEN 都跑全量回归,不只跑单个测试 | 无回归机制 |
| Issue 追踪 | 内置 Issue 文档生成,修复超 5 分钟/跨文件/重复出现必须记录 | 无 |
| Bug 修复流程 | /tdd:bug 完整链路:报告→Issue→复现测试→修复→验证→归档 | 无专门 bug 流程 |
| 交付门禁 | /tdd:done 8 项检查必须全过才能交付 | 无交付验证 |
| 中途变更管理 | /tdd:change 自动影响分析 + 已完成任务自动回退 | 可以改规范但没有影响分析和任务回退 |
| 任务状态跟踪 | [ ] [~] [x] [!] 四种状态实时更新 | tasks.md 有 checklist 但无进行中/阻塞状态 |
| Multi-Agent | Orchestrator / Coder / Tester 三角分工:写代码、E2E 测试、评审三个角色物理隔离;多 UC 支持并行 Coder(isolation:worktree) | 单 Agent 自写自审 |
| Bug 回顾机制 | 每个 bug 强制分类根因 + 立即执行预防措施 + 搜索同类问题 | 无 |
OpenSpec 有而 TDD Workflow 没有的
| 能力 | OpenSpec | TDD Workflow |
|------|---------|-------------|
| 系统级规范 | specs/ 维护「当前系统全貌」活文档,变更是 delta | 只有功能级规范,没有系统全貌 |
| Delta 规范格式 | ADDED/MODIFIED/REMOVED 结构化标记 | 无 delta 格式 |
| CLI 管理命令 | list、view、validate、show 等 | 只有 init 和 update |
| 支持工具数量 | 17+(含 Codex、Amazon Q、Augment 等) | 6 个 |
| Brownfield 优化 | 专门为改造存量项目设计 | 更偏新功能开发 |
实际场景对比
用一个例子说明区别——「给 API 添加分页功能」:
OpenSpec 的做法:
/opsx:propose add-pagination
→ 生成 proposal.md、design.md、tasks.md(delta spec 标记哪些接口要改)
→ 人工 review 规范
/opsx:apply
→ AI 按规范写代码(怎么测试?不知道。写对了吗?希望吧。)
/opsx:archive
→ 完成TDD Workflow 的做法:
/tdd:new add-pagination
→ 交互式收集:分页参数格式?默认每页几条?总数怎么算?超出范围返回什么?
/tdd:ff
→ 生成四份规范(usecases.md 主产出 → requirements / design / tasks),自动检查:UC 覆盖了错误路径吗?单元测试有吗?集成测试覆盖 HTTP 链路了吗?每个 UC 都含它的所有层(DB / 后端 / 前端)吗?E2E 有吗?缺了自动补
/tdd:loop
→ RED: 写测试 — GET /api/posts?page=2&limit=10 应返回第 11-20 条 — 运行,失败 ✓
→ GREEN: 写最少实现 — 跑全量回归确认没有破坏已有功能 ✓
→ RED: 写测试 — page=0 应返回 400 — 运行,失败 ✓
→ GREEN: 加参数校验 — 全量回归 ✓
→ RED: 写测试 — page 超出总页数应返回空数组 — 运行,失败 ✓
→ GREEN: 加边界处理 — 全量回归 ✓
(某个测试连续失败 3 次 → Three-Strike Protocol 暂停,你来决定怎么办)
→ ... N 轮后 Phase 1 + Phase 2 全部 [x]
/tdd:e2e
→ 用 Playwright 写端到端测试,跑真实网络请求
/tdd:done
→ 8 项检查:编译 ✓ 覆盖率 92% ✓ 回归 ✓ E2E ✓ Issue 记录 ✓ ...
→ 全过才算交付能组合使用吗?
可以。两者不冲突:
- 用 OpenSpec 管理系统级规范(
specs/描述系统全貌) - 用 TDD Workflow 管理每个功能的实现质量(测试驱动 + 质量门禁)
但如果只选一个:规范写得再好,没有测试验证就只是「希望 AI 做对」。TDD Workflow 确保每一行代码都有测试背书。
常见问题
这个工具适合什么项目?
任何需要 TDD 流程的项目——前端、后端、CLI 工具、库。支持的语言和测试框架不受限制,AI 助手会根据项目实际的 package.json / pyproject.toml / go.mod 等自动适配。
什么不适合用 TDD Workflow?
- 单行 typo 修复
- 纯文档 / 配置修改
- 简单样式调整
这些直接 git commit 就好。
我只用 Claude Code,需要装其他工具的文件吗?
不需要。--tools claude 只安装 Claude Code 对应的文件。交互模式下也可以只勾选一个工具。
文件已存在会覆盖吗?
默认不会。已存在的文件会跳过,CLI 会提示 ⚠ N files skipped (use --force to overwrite)。加 --force 覆盖。
如何更新到新版本?
npx tdd-workflow@latest update自动检测已安装的工具并更新所有文件。
npx tdd-workflow init 装上的版本不是最新的?
npx 默认会优先用本机已经全局安装或缓存里的版本,不会主动去 registry 拉最新。如果你以前 npm i -g tdd-workflow 装过,全局那个会被无限期复用。
永久修法(推荐):始终带 @latest:
npx tdd-workflow@latest init
npx tdd-workflow@latest update也可以:删掉全局安装,未来 npx 就不会被它劫持:
npm uninstall -g tdd-workflowinit 后 hooks 没有生效?
Claude Code 的 settings.json 只在 session 启动时加载。tdd-workflow init 之后需要 退出并重新进入 Claude Code,hooks 才会生效。
Multi-Agent 在 Cursor/CodeBuddy 里能用吗?
会自动降级为单 Agent + 强制 self-review。Coder/Reviewer 分离是 Claude Code 特有的(依赖 Agent tool),其他工具写完代码后会强制按 review-checklist.md 自检,输出 [Review:RED] / [Review:GREEN] 评审结果。
Hooks 会影响我已有的 settings.json 吗?
不会覆盖。TDD hooks 是追加合并的——你已有的 permissions、env、其他 hooks 都会保留。重复 init 会先移除旧的 TDD hooks 再写入新的,不会重复。
