| name | safe-refactor |
| description | Perform safe, reviewable refactors. Use when restructuring code, moving responsibilities, renaming across modules, or reducing duplication without intended behavior changes. Triggers on "refactor X", "rename Y across the project", "extract helper", "重构". |
safe-refactor
把"行为不变 / 结构变好"的改动做对:小步、可 review、行为可验证。禁止在 refactor 过程中混入功能修改;功能改动应该单独走 experiment-runner / 普通 edit。
触发条件
- 用户说"重构 X"、"refactor", "rename Y across project"、"extract helper", "move Z to module W"
- workflow
/orchestrate-task 路由到 refactor 类任务
code-review 报告里"有可以简化的部分"被用户接受为待办
前置条件
- 工作区干净(无未提交的功能改动)—— 否则 refactor 改动会跟功能改动混在一起难 review。如有未提交改动,先
git stash 或 commit
- 有可以跑的测试套件 —— refactor 的正确性证明是"测试在 refactor 前后都绿"
执行步骤
Step 1: 定义"什么不能变"
明确写下不变量:
- 公开 API 行为(输入/输出/异常)
- 测试通过情况
- 性能关键路径(如果适用)
把不变量写进对话或临时 NOTES 里,refactor 结束时逐条验。
Step 2: 影响面映射
- 用
Grep 找所有调用 / 引用被重构对象的地方
- 用
Read 确认每个调用点上下文
- 列出受影响的测试文件(同样 grep)
- 估算 diff 规模——如果 >500 行,拆分
Step 3: 分离机械改动 vs 行为改动
最好的 refactor commit 是"纯机械"的:rename、提取函数、move file。这些可以工具化检查。
如果同一个 PR 不可避免地有行为微调,分两个 commit:
- commit 1: pure mechanical(diff 容易看懂)
- commit 2: behavioral tweak(小且明确)
Step 4: 小步执行 + 频繁验证
每个原子改动后:
- 跑被影响范围的测试(不一定全套,但至少相关模块)
- 验证打印 / 日志输出形态没变(除非这是 refactor 目标)
git diff 自己看一遍
不要"一气呵成 30 个文件改完再跑测试"——出错时定位成本太高。
Step 5: 收尾验证
完成后输出:
- Before/After 结构对比: 模块边界、文件分布、call graph
- Compatibility impact: 公开 contract 是否变?如果变了,谁需要跟着改
- Verification evidence: 哪些测试在 refactor 前后都跑过 + 都绿;哪些被影响测试需要用户审
输出契约
文本报告 + 实际改完的代码。不写 artifacts/ 文件。如果重构跨越多个模块、影响外部消费者,建议同时建议用户调 code-review 做独立审查。
失败处理
- 测试在 refactor 中变红: 立即停止,回滚到上一个绿点,分析"什么不变量被破坏了"。不要"先全改完最后修测试"。
- 发现 refactor 触及未声明的边界(例如改了配置文件 schema): 停下,向用户报告"这不是纯 refactor,需要分功能改动单独处理"
- 跨模块大 rename 在某些路径下 grep 不到(例如反射调用、字符串中的符号名): 标出"潜在遗漏点"清单,让用户决定是否搜更广
与其他技能的关系
- code-review: refactor 完用 review 验证语义保留
- systematic-debugging: refactor 引入回归 → debug 定位
- test-author: refactor 时发现测试不足 → 先补测试再 refactor(refactor 的安全前提是测试覆盖到位)
- verification-runner: refactor 后跑完整测试套件
反模式(明确避免)
- ❌ "顺手清理"——把无关的格式 / 注释改动塞进同一个 refactor commit
- ❌ "大爆炸 refactor"——一次改 20 个文件不分阶段,diff 没人能 review
- ❌ "trust the type checker"——只看类型不跑测试就声明 done
- ❌ refactor 同时改 schema / wire format / public API —— 这是 breaking change,不是 refactor
RED-GREEN-REFACTOR(TDD enforcement 协议)
refactor 的"安全"来自一个不变量:改动前后行为完全相同。证明这个不变量的唯一办法是测试。
因此 NeXus 的 refactor 必须走 RED → GREEN → REFACTOR 三段式,不允许跳过任何一段。
RED 阶段:先写 failing test 覆盖 refactor 目标行为
进入 refactor 前,先问:"如果我把代码改坏了,哪个测试会变红?"如果答案是"我不知道"或"没有覆盖",说明 refactor 的前置条件没满足。
操作:
- 找到(或新写)一个测试,能 精确覆盖被重构对象的对外行为(输入→输出,含异常路径、边界)
- 故意把被重构对象的实现破坏一行(比如
return result 改成 return None),确认该测试 变红
- 把测试恢复绿之前不能开始 refactor —— RED 阶段证明的是"测试真的会抓住回归"
如果项目里这块逻辑根本没测试,先用 test-author 补 characterization test 再进 refactor。"先 refactor 后补 test" 等价于"无安全网走钢丝"。
GREEN 阶段:最小改动让 test 过(允许"丑代码")
如果 refactor 触发了 test 红,第一目标是回到绿,不是"漂亮地解决问题":
- 允许临时的丑代码(重复、长函数、shim 层)—— 这些会在 REFACTOR 阶段被清理
- 不允许"先全部改完再修测试"——出现红立刻定位,回到上一个绿点
- GREEN 的判据是全套相关测试都绿,不是"我觉得改对了"
REFACTOR 阶段:在 test 保护下改结构,每一步都跑 test
进入这一阶段时,所有测试是绿的。然后小步改结构:
- 一次只做一个原子变换(rename / extract / inline / move)
- 每个变换后 立刻 跑相关测试,绿了再做下一步
- 任何一步变红 → 回退该步,分析为什么测试在 RED 阶段没预测到 → 补测试 → 重来
- 整个 REFACTOR 阶段结束时,所有原 test 仍然绿(且 git diff 应该不触及测试本身——如果测试被改了,说明你改的不是 refactor 而是行为变更)
反模式(TDD-specific)
- ❌ 先 refactor 再补 test:等于事后给自己签合规章,无法证明行为不变
- ❌ test 和 refactor 一起写:测试可能被无意识地写成"匹配新行为"而不是"约束旧行为"
- ❌ 一次性改完不跑 test:"等我改完一起验" → 红了根本不知道是哪一步引入的
- ❌ 在 REFACTOR 阶段顺手改 test:除非用户明确同意"测试也要 refactor",否则改测试 = 改 contract,不再是 refactor
- ❌ 跳过 RED 阶段:"这个测试肯定能覆盖" 是断言不是证据,必须实际看到红再看到绿
遵守规则
.agents/rules/repo-architecture.md: refactor 不能跨模块违规
.agents/rules/citation-integrity.md / evidence-discipline.md: 如果 refactor 触及 schemas.py / audit_chain / compliance_checker,对应测试必须在 refactor 前后都绿(这些是 NeXus 的核心契约)