mcpField
A
audit-and-migrate

audit-and-migrate

github.com/petekp开发4854 次安装2 天前更新
代码仓库

关于 audit-and-migrate

Systematic Codebase Audit & Migration 为编码 Agent 提供处理技术债务和大型重构的分阶段方法。它通过审计、逐片迁移和收尾检查,记录决策与进度,并要求用可重复的检查证明变更和发布准备情况。

主要功能

  1. 01分阶段审计、迁移与收尾
  2. 02用清单和切片跟踪进度
  3. 03通过 CI ratchet 限制反模式
  4. 04按行为测试验证变更
  5. 05清理替代代码与发布残留

适用场景

  1. 01规划大型代码库重构
  2. 02跨多个会话持续推进迁移
  3. 03逐步减少已量化的技术债务
  4. 04在发布前检查清理项与阻塞问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/petekp/claude-code-setup --skill audit-and-migrate

安装后在 Claude Code 中输入/audit-and-migrate确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
guard-script-pattern.md
references/templates/
CHARTER.md
DECISIONS.md
MAP.csv
RATCHETS.yaml
SHIP_CHECKLIST.md
SLICES.yaml

详细介绍

系统化代码库审计与迁移

一种用于处理积累了技术债务的代码库的结构化方法。分为三个阶段——审计(了解并冻结问题)、迁移(分阶段逐步偿还,并提供证明),然后是收尾(清除残留,并证明代码库确实已准备好发布)。

为什么需要这种方法

缺乏结构的 Agent 在大型重构过程中容易在四个方面出问题:

  1. 偏移——忘记已完成的工作、重复完成的工作,或在多个会话中留下未完成的事项。这会浪费时间并引发冲突。
  2. 残留代码——添加新实现,却不删除被替代的旧实现。代码库本该缩小,却反而越来越大。
  3. 凭直觉建立信心——只说“这应该能用”,却不加以证明。如果 Agent 无法指出一个已通过的确定性检查,那么它的信心就毫无根据。
  4. 虚假的完成——新路径可用就停止,但临时适配器、过时文档、孤立依赖项或发布阻碍仍然存在。

此方法中的每条规则都是为了防止这四种问题之一。当某条规则显得繁琐时,这就是它存在的理由。


会话流程

每次会话开始时都要执行这些操作。 这是确保此方法在上下文压缩和会话切换后仍然有效的关键。

开始会话
  1. 检查控制平面文件是否存在(查看 .claude/migration/ 或项目存放这些文件的位置):
    • CHARTER.md、DECISIONS.md、SLICES.yaml、MAP.csv、RATCHETS.yaml、SHIP_CHECKLIST.md
    • 如果之前的会话处理过迁移,还要查看最新的 HANDOFF.md
  2. 如果这些文件不存在 → 你是从头开始。进入阶段 1(审计)。
  3. 如果这些文件存在 → 按以下顺序阅读: a. CHARTER.md — 重新了解使命和不变量 b. SHIP_CHECKLIST.md — 重新加载项目特定的“准备好发布”定义 c. DECISIONS.md — 了解已做出的决定及其原因 d. 最新的 HANDOFF.md — 快速了解当前状态和阻碍 e. SLICES.yaml 中所有状态为 in_progress 的切片 — 这些就是你当前的工作 f. 运行防护脚本,查看当前状态
结束会话

编写或更新 HANDOFF.md(如果交接内容较少,也可以追加到最新的提交说明中)。分为五个部分,不得省略:

  1. 变更内容 — 修改的文件、推进的切片、做出的决定
  2. 当前已确认事项 — 新增的不变量、已完成的切片、更新后的预算
  3. 待处理事项 — 未完成的切片、已知风险、阻碍问题
  4. 当前发布阻碍 — 明确列出阻碍,或填写 None
  5. 确切的后续步骤 — 下一步要运行的具体命令或测试序列

示例:

## Handoff — 2026-03-01
### Changed
- Completed slice-003 (user routes auth migration)
- Ratchet budget for jwt.verify decreased from 8 → 5

### Now True
- All user route auth goes through withAuth() middleware
- Behavioral tests cover user routes at 100%

### Remains
- slice-004 (data routes) ready to start
- slice-005 (admin routes) blocked on Decision 4 re: webhook auth

### Shipping Blockers
- None

### Next Steps
1. Run `./guard.sh --status` to confirm baseline
2. Start slice-004: mark in_progress in SLICES.yaml
3. Write behavioral tests for src/api/posts.ts and src/api/comments.ts

此格式可以在上下文压缩后继续使用。下一次会话(或另一个 Agent)可以接手工作,而不会丢失信息,也不必猜测代码库是否真的已具备发布条件。


阶段 1:审计

审计阶段旨在了解问题空间,然后再更改生产代码。先进行度量,才能为安全迁移奠定基础;及早定义发布门槛,则可避免常见问题:实现已经合入,但清理工作仍不断拖到后续运行中。

第 1 步:创建章程

使用 references/templates/CHARTER.md 中的模板创建 CHARTER.md。其中应定义:

  • 使命 — 用一句话说明要迁移什么,以及原因
  • 关键工作流 — 不得出现回归的用户可见操作路径
  • 外部接口 — 迁移可能影响当前代码仓库之外的公共 API、环境变量、仪表板、Webhook、CLI 入口、数据契约或自动化钩子
  • 不变量 — 整个迁移过程中必须始终成立的条件(例如,“所有现有测试均通过”“不改变面向用户的行为”)
  • 非目标 — 明确说明不做什么(防止范围蔓延)
  • 护栏 — 流程规则
  • 发布门槛 — 判定迁移完成前所需的确切自动化和人工验证证据

章程就是宪法。对范围有疑问时,请查阅章程。

第 2 步:盘点问题范围

全面梳理目标领域中的所有内容,包括代码、测试、脚本、文档、配置、工具和依赖项。根据杠杆作用对每项进行分类:

  • 高 — 保留并扩展。迁移可在其基础上构建的优质代码。
  • 中 — 保留但重构。结构合理,但需要更新。
  • 低 — 替换。这是要偿还的技术债务。

务必全面。任何遗漏的内容日后都可能变成遗留代码。如果某项的杠杆作用较低,请记录将用什么替代它,或会在哪个切片中将其删除。

第 2b 步:梳理关键工作流和热点

在设计切片之前,先检查系统中最可能在之后带来麻烦的部分:

  • 关键工作流 — 绝不能出现回归的面向用户操作路径
  • 已知热点 — 近期频繁修复缺陷的区域、TODO/FIXME/HACK 集中区、不稳定的测试或脆弱的子系统
  • 仅限人工验证的接口 — 自动化无法完全验证的视觉检查、硬件或第三方 UI 流程

这些内容将作为切片冒烟测试、人工检查和发布门槛的依据。

第 3 步:衡量反模式

使用 grep/ripgrep 统计具体的薄弱模式。目标是得到具体数字,而非凭感觉判断。

对每种模式,记录:

  • 用于查找它的 grep 模式
  • 用于查找它的搜索范围
  • 当前数量
  • 它为何有问题

示例(根据你的代码库调整):

Pattern: source\.contains\(        Scope: test/          Count: 43    Problem: Tests code shape, not behavior
Pattern: Task\.sleep\(             Scope: test/          Count: 17    Problem: Non-deterministic timing
Pattern: struct Scenario           Scope: test/          Count: 22    Problem: Duplicated test scaffolding
Pattern: require\(.*/utils/old-    Scope: src/ docs/     Count: 8     Problem: Imports from deprecated module
第 4 步:通过 CI 递进门槛冻结现状

针对每种已衡量的反模式,创建递进门槛,将允许的最大数量设为当前数量。相关方法请参见CI 递进门槛模式。

这是最重要的一步。设置递进门槛后,技术债务只会减少。任何人——无论是人还是 Agent——都无法意外重新引入已消除的模式。只有明确指定查询和搜索范围,并且统计的是匹配项而非仅匹配文件的数量,递进门槛才值得信赖。

使用 references/templates/RATCHETS.yaml 中的模板,将这些递进门槛写入 RATCHETS.yaml。不要让守护脚本成为存放预算的唯一位置。

第 5 步:编写审计文档

创建一份全面的审计文档。包括:

  • 使用的方法(如何盘点和衡量)
  • 当前指标(所有反模式的数量)
  • 杠杆作用评估(高/中/低)
  • 关键工作流和已知热点
  • 明确结论(需要改变什么——直截了当地说明)
  • 已添加的护栏(目前已纳入 CI 的检查)
  • 发布标准和仅限人工验证的接口
  • 拟定的切片(第 2 阶段的初步计划)
步骤 5b:编写实用参考资料

除了控制平面产物外,还要编写特定领域的参考资料,以加快实际迁移工作:

  • 翻译指南 — 编制查找表,将旧模式映射到新模式(例如,将原始 SQL 查询映射到 ORM 调用,将 Redux 模式映射到 Zustand 模式)。这样每个切片都无需重新摸索转换方式。
  • 边缘情况和注意事项 — 记录在新旧方法之间转换时已知的陷阱。有些转换看似直接,但行为上存在细微差异。
  • 代码模式示例 — 为最常见的迁移场景提供具体的迁移前后代码示例。

这些资料的回报会成倍增长——没有它们,每个切片(以及每次 Agent 会话)都会独自重新发现相同的转换模式。有了这些资料,知识只需记录一次,就能在整个过程中重复使用。

步骤 6:创建其余产物

使用 references/templates/ 中的模板:

  • DECISIONS.md — 汇总审计中得出的架构决策
  • SLICES.yaml — 填入审计文档中提出的切片
  • MAP.csv — 填入清单中的所有文件
  • RATCHETS.yaml — 以机器可读格式记录反模式预算和范围
  • SHIP_CHECKLIST.md — 一次性定义准确的发布门槛,然后在收尾阶段执行

第 2 阶段:迁移

迁移是按切片逐步偿还技术债务。每个切片都是一个垂直的、以结果为导向的工作单元,可独立验证。

开始前进行校准

如果审计是在无法访问完整代码库的情况下完成的(例如,依据描述或部分探索),那么第 2 阶段的第一步就是根据实际情况校准棘轮预算。运行审计文档中的每个 grep 模式,并根据实际计数更新守护脚本预算。这是快速且收益很高的一步——预算不准确会导致误报失败(预算过低)或无声回归(预算过高)。

此外,在开始任何切片工作之前,使用 --status 标志运行守护脚本(如果你添加了该标志),以确认基线状态良好且无问题。

还要确认 SHIP_CHECKLIST.md 中包含准确的命令和场景,而不是占位符。收尾阶段的质量取决于所执行的检查清单。

预留收尾切片

在开始实施之前,先在 SLICES.yaml 中预留一个最终的收敛切片。它应依赖所有实施切片,并负责整个代码库的清理:残留项清扫、依赖项精简、文档/配置协调,以及执行 SHIP_CHECKLIST.md。

如果不提前预留这个切片,清理工作就会变成“以后再做”,而以后再做的工作很容易悄然演变成又一次迁移。

选择下一个切片

查看 SLICES.yaml,选择杠杆效益最高且未被阻塞的切片:

  1. 检查依赖项——依赖项尚未标记为 done 的切片不要开始
  2. 优先选择杠杆效益高的切片(即能消除最多反模式实例或解除其他切片阻塞的切片)
  3. 如果杠杆效益相同,则优先选择影响范围较小的切片
切片生命周期

每个实施切片都包含五个阶段。

A. 开始
  1. 在 SLICES.yaml 中将切片标记为 in_progress
  2. 确保此切片将要修改的所有文件都已列入 MAP.csv
  3. 在编写任何代码之前,先在 SLICES.yaml 中定义 deletion_targets、temp_artifacts、dependency_changes、residue_queries、verification_commands、docs_to_update,以及任何涉及的 external_surfaces
  4. 运行守护脚本,获取干净的基线
B. 构建(TDD 优先)
  1. 编写在新行为实现前会失败的测试。 这些测试定义了此切片的“完成”标准。通过公共接口测试行为,而非实现细节。
  2. 实现规范路径。 即完成此事的全新正确方式。
  3. 迁移调用点。 更新所有使用旧路径的地方,使其改用新路径。
  4. 在此切片中更新配套产物。 文档、配置、环境示例、脚本、固定数据和 CI 引用都属于迁移内容,而非后续杂务。
  5. 在此切片中删除被替换的代码。 这是硬性规则。理由:将删除工作推迟的 Agent 往往会忘记。旧代码会变成遗留物。等到有人注意到时,调用点早已退回使用旧代码。在同一变更中删除代码,能让迁移保持原子性。
  6. 移除或明确跟踪临时脚手架。 适配器、调试辅助工具、一次性脚本和临时标志,要么在此切片中移除,要么作为 temp_artifacts 记录,并注明负责的切片。
C. 清理检查

在将切片标记为“已验证”前,针对已修改路径及其相邻模块进行一次有重点的残留检查:

  1. 搜索此切片暴露出的孤立文件、未使用的导出、无效测试、过时适配器、无效分支、过时文档/配置和未使用的依赖项
  2. 立即移除确认无用的项目
  3. 如果某项可能无用但目前无法证实,则将其列为明确的阻塞后续切片,或在 DECISIONS.md 中记录,并注明负责人和退出条件

清理不只是“删除原本计划删除的内容”,而是“证明此切片没有在附近留下残留”。

残留查询必须具体且范围明确。避免在整个代码仓库中使用 legacy 这样的宽泛模式,因为仍有正当保留的名称。优先使用 import.*legacy-auth、LEGACY_COOKIE_NAME 或 TODO\\(migration\\) 等查询,并明确指定搜索路径。

D. 验证

运行所有适用的检查:

  1. 守护脚本通过——没有未映射文件、违反拒绝列表或残留规则的情况
  2. verification_commands 通过——运行 SLICES.yaml 中记录的确切自动化命令
  3. 回放/冒烟场景通过——如果 SLICES.yaml 中有定义,则运行这些场景
  4. residue_queries 返回零条匹配——旧名称、标志、TODO 标记和临时标记确实已清除
  5. 章程不变量和相关发布检查清单要求仍然满足
  6. 仅对自动化无法覆盖的界面进行手动检查,并在切片说明中记录结果
  7. 外部界面仍可正常工作——明确重新检查受此切片影响的仪表板、公共导出、环境约定、Webhook 或 CLI 入口点
E. 收尾
  1. 确认所有 deletion_targets 均已从磁盘上删除(不是注释掉,也不是藏在标志之后,而是彻底移除)
  2. 确认所有 temp_artifacts 均已移除,或已明确交由后续负责的切片处理
  3. 核对 docs_to_update 和 dependency_changes
  4. 如果此切片使空目录和仅用于迁移的脚手架变得多余,则将其移除
  5. 更新 MAP.csv——标记已迁移的文件、移除已删除的文件,并确保文档/配置/工具条目保持同步
  6. 更新 SLICES.yaml——将切片标记为 done,记录已完成的工作和通过的验证
  7. 更新 RATCHETS.yaml——递减预算,并为降至零的棘轮添加说明
  8. 为已移除的名称/路径添加拒绝列表模式
  9. 在 DECISIONS.md 中记录任何新决策

如果清理检查发现原始 deletion_targets 之外还有额外的无效代码,则应立即删除,或在将当前切片标记为完成前创建明确的后续切片。不要将新发现的残留留作隐性债务。


阶段 3:收尾与发布门禁

最后一个实现切片合并后,迁移并未完成。只有当整个代码仓库都干净,且项目专属的发布门禁通过后,迁移才算完成。

将收尾视为一个真正的收敛切片,而不是事后补做的工作。

  1. 重新运行清单盘点。 对照实际情况核对 MAP.csv。确保没有未映射文件、孤立的替换项或遗漏的附属产物。
  2. 全面扫描迁移残留。 搜索代码、测试、文档、配置、工具和依赖,查找遗留内容:旧符号、无用文件、过时适配器、未使用的包、临时标记、临时脚本、调试日志,以及迁移过程中引入的 TODO/FIXME/HACK 标记。
  3. 清理并验证依赖。 移除未使用的包、脚本、功能标记、锁文件冗余和仅供迁移使用的辅助工具。
  4. 核对各类运维界面。 README、ADR、环境示例、CI 工作流、仪表板、公共 API、Webhook、CLI 入口和开发者文档都必须描述新架构,而非旧架构。
  5. 执行 SHIP_CHECKLIST.md。 运行审计期间定义的确切 lint/typecheck/build/test/replay/smoke/手动检查。尽可能自动化所有检查;手动检查仅用于自动化无法验证的视觉、体验、硬件或第三方界面。
  6. 以发布候选版本的标准审查差异。 查找意外改动、空目录、备份文件、已注释掉的代码、残留日志,或 old/new/final 产物。
  7. 撰写最终交接说明。 明确写出 Ready to ship,或列出仍未解决的确切阻碍。如果有意保留技术债务,请在 DECISIONS.md 中记录负责人和退出条件。

CI 递减门禁模式

这是该方法论的标志性技术。不要一次性消除某种不良模式的所有实例,而是:

  1. 计数该模式(例如,43 个 source.contains 断言)
  2. 设定预算——CI 上限等于当前数量(43)
  3. 递减——每个消除部分实例的切片都会降低预算
  4. 强制执行——如果数量超过预算,CI 就会失败
  5. 降至零——预算最终达到 0,该模式被彻底消除
  6. 保留守护检查——将检查永久维持在 0,作为防止回归的保障

这种方法为何特别适合 Agent: 它提供了明确、机械化的通过/失败信号,无需判断少数实例是否“可以接受”。递减门禁会给出确定答案。进度一目了然:43 → 38 → 29 → 15 → 7 → 0,是一条具体的推进轨迹。

实现方式: 通常使用 shell 脚本搜索该模式、统计匹配项、与预算比较,并在数量超过预算时以非零状态码退出。应统计精确匹配项,而不只是匹配文件数,并明确限定搜索范围。预算应存放在 RATCHETS.yaml 中,而不应只写在脚本里。模式详见 references/guard-script-pattern.md。将其接入 CI 或提交前钩子。


反残留纪律

残留代码——被新代码替代却从未删除的旧代码——是 Agent 辅助开发中代码库膨胀的最大来源。Agent 会优化“新功能能否正常工作?”,却不会追问“旧功能是否已经移除?”

每个切片都要:

  1. 在实现前定义 deletion_targets。 在开始构建前将其写入 SLICES.yaml。这会明确责任——预先定义了要删除的内容,就不能再“忘记”删除。

  2. 为已移除的名称和路径添加拒绝名单模式。 删除代码后,在守卫脚本中添加 grep 模式;如果名称重新出现,脚本就会失败。这比测试覆盖更有力,因为它能捕获代码库中任何位置的重新引入。

  3. 删除介绍已移除架构的文档。 关于已移除系统的过时文档会积极误导未来的 Agent 和开发者。

  4. 移除过时脚本、配置和环境变量回退方案。 已移除功能的配置是隐形债务。

  5. **只有满足以下条件,才能使用“临时”适配器:**有负责的切片 ID、到期条件,以及明确的删除目标。没有计划退役时间的适配器会永远存在。

  6. 清除迁移标记。 TODO(migration)、临时调试日志、临时文件,以及 old/new/final 副本都是残留,不是无害的备注。


切片设计规则

每个切片都必须:

  • 纵向且以结果为导向——提供完整能力,而不是“重命名所有文件”或“更新所有导入”这类横向改动。纵向切片应各自具有独立意义。
  • 决策完备——明确逻辑归属。不得存在边界不清的共同所有权。
  • 可独立验证——具备确定性检查。如果无法编写守卫或测试来确认正确性,说明切片定义过于模糊。
  • 清理完整——负责清理其暴露出的相关死代码、文档、配置和临时脚手架。不能只做到“功能可用,清理以后再说”。
  • 影响范围有限——规模应足够小,便于审查和干净地回滚。如果回滚意味着要理清 40 个文件,就应拆分。

置信度模型

验证切片正确性时,应按以下优先级使用分层证据:

  1. 确定性重放(置信度最高)——重新运行输入,并与已知良好的基线对比输出差异
  2. 自动化冒烟测试——端到端运行关键的用户工作流
  3. 完整测试套件——运行代码库中的所有测试
  4. 手动检查(置信度最低)——仅用于高风险 UX 变更

如果证据来源相互冲突,应以确定性重放为准。“测试通过,但重放差异不同”意味着存在问题——应调查,而非置之不理。

如果某项检查可以自动化,就应将其加入 verification_commands,而不是留在人类记忆中或作为未来的 TODO。


控制平面

迁移由七项工件管理。模板位于 references/templates/。

工件用途更新频率
CHARTER.md使命、 invariants、非目标、护栏少量更新(仅范围变更时)
DECISIONS.md仅追加的架构决策每个制定决策的切片
SLICES.yaml机器可读的切片台账每个切片的开始和结束时
MAP.csv源路径映射的事实来源每个切片的开始和结束时
RATCHETS.yaml机器可读的反模式预算、范围和理由审计时初始化,预算降低时更新
SHIP_CHECKLIST.md项目专属的发布门槛和收尾清单审计时初始化,收尾期间执行
守卫脚本由 CI 强制执行的不变量检查添加或移除棘轮规则时
DECISIONS.md 协议
  • 仅追加。 永远不要编辑过去的条目。
  • 撤销决策时新增条目。 想要改变决策 #3?新增决策 #7,写明“取代 #3”并解释原因。
  • 不得静默改写。 决策历史有助于理解代码库的演进。
  • 豁免会过期。 如果发布时保留了债务或临时例外,应记录负责人和退出条件。
SLICES.yaml 字段

每个切片跟踪:id、type(implementation 或 convergence)、name、status(proposed → in_progress → done)、dependencies、touched_paths、external_surfaces、contracts(added/changed/removed)、denylist_patterns、deletion_targets、temp_artifacts、docs_to_update、dependency_changes、residue_queries、verification_commands、replay_scenarios、smoke_scenarios、manual_checks、risks、notes。

MAP.csv 列

slice_id, kind, capability, current_path, target_path, status, delete_in_pr, verification, notes

迁移中涉及的每个文件都必须列入其中。这样可以避免遗漏文件。使用 kind 将文档、配置和工具列为一等事项,而不是隐形的附带工作;使用 verification 记录用于证明产物仍然有效的命令或场景。


Agent 协作

使用多个 Agent 或并行会话时:

  1. 并行工作只分配给真正独立的文件集。 两个 Agent 向同一分支提交更改,可能会悄无声息地覆盖对方的工作。切片边界应对应互不重叠的文件。

  2. CI 阈值是最适合 Agent 的防护措施。 机械化的通过/失败判断,胜过要求判断更改是否“安全”。

  3. 拒绝列表模式比测试更能可靠地防止回归。 CI 正则检查可以规定“此函数名不得出现在此目录中”,这比寄希望于测试覆盖率捕获重新引入的问题更有力。

  4. 交接约定不容妥协。 缺少约定时,上下文压缩会导致 Agent 重做已完成的工作,或跳过尚未完成的工作。

  5. 由一个切片负责收尾。 当文件集互不重叠时,可以并行推进实现切片,但应保留一个负责最终残留项检查和发布门禁的收敛切片。


不止代码

这种方法也适用于非代码领域,例如脚本、文档、工具和配置。整个循环不变:盘点 → 衡量 → 冻结 → 清理。整理文档或消除冗余脚本时,“在同一次更改中删除”的规范和反遗留检查同样有价值。

评论

支持 Markdown