用途
tdd-repair 将「测试驱动修复」(Test-Driven Repair)循环封装为一个 ruflo 技能。当你有一个失败的测试,并希望自动修复被测源码时使用它。验证门槛是测试本身的通过/失败结果,而不是让 LLM 充当评判者。
何时使用
- 近期提交导致的 CI 失败测试:将本工具指向该测试文件,获得经过验证的修复(或一份明确的「在预算内无法修复」回执)。
- 本地 TDD 工作流:先编写失败测试(tdd-workflow 技能),再运行 tdd-repair 驱动其变绿。
- 回归分诊:一个此前通过的测试变红;在开 issue 之前,花约 1 美元看看修复是否简单。
何时不要使用
- 不存在失败测试。符合模式(
--no-test-oracle)被划归为后续 ADR 的范围,需要在复现生成上使用 MCTS。目前请先编写一个失败测试。 - 架构性变更。本技能用于战术性的「让红变绿」修复。会顺带破坏测试的跨模块重构应由人工或 swarm 完成。
- 不受信任的代码。无头
claude -p以--allowedTools Read,Edit,Bash运行——没有 MCP、没有网络、没有任意文件写入——但 Bash 仍可触及文件系统。不要把它指向你不愿意执行git checkout .的代码。
算法
实现位于 scripts/tdd-repair/tdd-repair.mjs。
- 预检验证——运行测试命令。如果它已经通过,退出码 2(test-already-passes)。修复一个已通过的测试要么是无操作,要么是
--test-command拼写错误。 - 以聚焦的提示启动
claude -p:- 失败测试文件路径(只读意图)
- 测试命令(仅运行)
- 硬约束:不要修改测试
- 硬约束:不要新增依赖
--allowedTools Read,Edit,Bash限制能力。--max-budget-usd限制每次尝试的成本。--permission-mode acceptEdits在允许集合内自动接受文件编辑。
- 重新运行测试以验证。测试的退出码就是适应度函数——无需单独的沙箱或 LLM 评判者。
- 如果变绿:输出
success: true及每次尝试的用量。如果在--max-attempts后仍为红:输出success: false及回执。无论哪种情况,工作区都保持为claude -p修改后的状态(调用方可执行git diff审查)。
输出结构
{
"success": true,
"data": {
"repaired": true,
"attemptsTaken": 1,
"mode": "test-driven",
"before": { "passed": false, "exitCode": 1 },
"after": { "passed": true, "exitCode": 0, "durationMs": 4321 },
"attempts": [
{ "attempt": 1, "claude": { "ok": true, "durationMs": 38421, "usage": { "cost_usd": 0.0234 } }, "verify": { "passed": true } }
],
"totalCostUsd": 0.0234,
"budgetUsd": 5.0,
"budgetExhausted": false,
"shape": { "repo": "...", "test": "...", "testCommand": "...", "maxAttempts": 1, "model": "haiku" }
}
}
退出码
| 代码 | 含义 |
|---|---|
| 0 | 修复后测试变绿(成功) |
| 1 | 在 --max-attempts 后测试仍为红 |
| 2 | 配置错误(测试文件缺失、测试已通过、--no-test-oracle 不受支持等) |
| 3 | Claude CLI 以非零退出(基础设施故障) |
| 99 | 保留给安全触发线(依据 ADR-153) |
安全姿态
| 层级 | 机制 |
|---|---|
| 成本上限 | --max-budget-usd 默认 $5,在 --max-attempts 之间分配。硬性上限——达到时 claude 退出。 |
| 能力上限 | --allowedTools Read,Edit,Bash——没有 MCP、没有网络、没有任意写入。 |
| 范围上限 | 提示禁止修改测试或新增依赖。 |
| 确认门槛 | --confirm 必需——没有它则返回 dry-run 计划(与 harness-evolve / harness-mint 约定一致)。 |
| 硬超时 | 总墙钟时间 15 分钟;每次尝试的预算为 timeoutMs / maxAttempts。 |
| 预检 | 如果测试已通过则拒绝运行(捕获 --test-command 拼写错误)。 |
灵感来源
仿照 agent-harness-generator/packages/darwin-mode ADR-175 中的 Test-Driven Repair 模式。关键设计差异:不包装 metaharness-darwin evolve(基于种群的搜索),而是驱动单次 claude -p 调用。理由:
- 测试命令就是适应度函数——无需变体评分
claude -p已在我们的技术栈中——无需新增可选依赖- 有界成本/能力是一等标志
- 如需迭代,可通过
--session-id恢复
符合模式(无测试,通过 MCTS 自行编写复现)推迟到未来的 ADR。
示例
# 冒烟 / dry-run(尚无 --confirm)
node plugins/ruflo-testgen/scripts/tdd-repair/tdd-repair.mjs \
--repo /path/to/myrepo \
--test tests/auth.test.ts \
--test-command "npx vitest run tests/auth.test.ts"
# 实际修复(Haiku 层级,$5 预算,1 次尝试)
node plugins/ruflo-testgen/scripts/tdd-repair/tdd-repair.mjs \
--repo /path/to/myrepo \
--test tests/auth.test.ts \
--test-command "npx vitest run tests/auth.test.ts" \
--confirm
# 更大模型 + 更多尝试以应对更难的 bug
node plugins/ruflo-testgen/scripts/tdd-repair/tdd-repair.mjs \
--repo . --test tests/regression-2456.test.ts \
--test-command "npm test -- tests/regression-2456.test.ts" \
--model sonnet --max-attempts 3 --budget 15.00 \
--confirm
成本阶梯
| 层级 | 模型 | 每次尝试典型成本 | 使用时机 |
|---|---|---|---|
| 1 | Haiku | $0.02 – $0.20 | 首次尝试——大多数「让红变绿」的 bug 是战术性的 |
| 2 | Sonnet | $0.30 – $2.00 | Haiku 失败,或 bug 涉及多文件范围 |
| 3 | Opus | $1.50 – $8.00 | Sonnet 失败——需要架构性推理(对单个失败测试而言很少值得) |