cm-fix — 缺陷修复小闭环

SkillDev tools

Use when the user says "fix this reproducible bug" or asks to fix code based on a failing report. Runs red-light testing, root-cause localization, minimal fix, independent review, and regression; for unconfirmed issues use cm-test first, and hand new features or architectural redesigns to cm-prd.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the cm-fix — 缺陷修复小闭环 skill

What this skill tells your AI

The instructions your AI receives, as published by kingxiaozhe/cm-workflow in skills/cm-fix/SKILL.md and read by ahel’s review.

执行前读取 ../../runtime/project-context.md、../../runtime/orchestration.md、 ../../runtime/review.md、../../runtime/model-efficiency.md 与 ../../runtime/logging.md。Codex 入口为 $cm-fix;Claude Code 跨平台入口为 /cm-fix,macOS/Linux 另有历史别名 /cm:fix。

每个缺陷开始/恢复时按 ../../runtime/project-learning.md 重读项目根 AGENTS.md, 筛选相关教训辅助复现与定位;同一合同约束收尾写回,不以旧经验代替本次证据。

用户明确要求外部专家,或为本次修复开启 AUTO 时,仍必须先完成第 1 步本地复现, 再按 ../../runtime/external-expert.md 执行 ../external-expert/SKILL.md 的任务 路由。代码、修复、测试和审查保持 LOCAL;只有竞争根因或高风险事实查证可路由到 CONSULT/VERIFY。外部假设必须回到本地证伪;咨询记录不能代替 2.5 或第 5 步独立 审查。

用法:$cm-fix {specs路径} {代码项目路径} 缺陷描述(现象/报错/截图均可)

JS 只读准入

在读取项目内容、解析角色、写 run_start、运行复现命令或创建档案前,先确认本轮包含非空缺陷 描述,但不要把描述正文拼进 shell;随后执行:

node "{CM_WORKFLOW_ROOT}/scripts/cm-fix-entry.mjs" \
  --skill-dir "{CM_WORKFLOW_ROOT}/skills/cm-fix" --project "{CODE_PROJECT}" \
  [--specs "{SPECS_DIR}"] --defect-present

没有 specs 的裸项目省略 --specs。缺少描述时不传 --defect-present,入口返回 blocked / defect_required 后只向用户补要描述。只有 ready / reproduce 才进入下方既有闭环; 它不提前声称缺陷可复现、不可复现或属于设计问题,只声明复现失败仍走 observation、确认设计 问题仍转 $cm-prd --change。返回的角色、日志和 Learning 均为 pending,执行/写入权限为 false; 入口不运行命令、不调用 provider/browser/外部专家、不创建日志/测试/档案,也不替代七步流程。

执行入口选择

准入通过后,具备当前会话双向进程通道、分离的 specs/代码根、命令式复现与测试配置时, 读取 references/js-host.md,使用既有 cm-fix-host.mjs 执行;Codex/Claude 共用同一 owner。 下文七步仍是业务要求,但 JS 分支的日志、交接、Review 发布及完成全部交给 owner, 不得再手工执行对应写入步骤。只读准入的 ready 不是执行、外发或完成许可。

裸项目、无自动测试/纯视觉替代、父 N6 运行衔接等尚未接通 JS 的场景,要明确报告缺口; 不得宣称已完成 JS 迁移。只有用户明确选择既有非 JS 流程且尚未创建 JS 运行时,才执行 下文手工流程;JS 已启动后遇到阻断,不得切换旁路、换身份或双写状态。

以下手工流程中,两个路径校验通过后调用统一写入器记录 run_start;暂停/续跑沿用同一 .cm-run.json,本次缺陷闭环或观测闭环退出时写 run_done。不得直接拼 JSON。

项目角色路由

从代码项目根解析 coder、tester、reviewer(命令、参数和日志字段见 runtime/workflow-routing.md)。coder 只作为最小修复的请求路由元数据,tester 负责防护网/回归,reviewer 只描述独立审查候选通道;declared-adapter 必须记录为 未观测适配器,不能伪造调用或绕过本地执行与独立审查。resolver 返回非零或配置错误 时立即 BLOCKED,不得复现、修改或写入缺陷档案;配置不存在时保持当前默认行为。 managed-adapter 按 runtime/model-efficiency.md 返回文本建议并自动记录真实 usage; 复现、修复落盘、测试和独立审查仍由本地流程执行。

角色调用按 runtime/model-efficiency.md 只传当前缺陷的复现证据、根因范围、修复 diff、回归结果和对应规则;不重复投喂整仓、完整历史日志或其他缺陷上下文。失败输出 保留首个可行动错误与证据路径,防护网、独立审查和回归要求不因精简而变化。

修 bug 专用的轻量闭环——不走 N1–N8 全链(那是 feature 流程),也不许脱离工作流裸改(裸改没防护网没审查,修一个坏三个)。

多缺陷输入:先对全部缺陷做第 1-2 步(复现+定位),按根因聚类——同根缺陷合并为一次修复(多个失败测试、一次改动、档案互链),修复顺序按严重度排,不按输入顺序。不聚类的代价:三个现象一个根因跑三个闭环,且第一个修复落地后,后两个的复现步骤可能已失效(第 1 步卡死)。

转交进场(消费上游落盘物,不改上游流程):缺陷描述可附上游档案引用——$cm-test 的只读测试报告、$cm-refactor 档案的未修缺陷清单、N6 业务走查报告的偏差项、观测闭环的半份档案(按 slug 在 fixes/ 检索)。带引用进场的缺陷,第 1 步采信上游已有证据(位置/现象/日志原文),仍须实际复现一次核实,但不从零摸排。

$cm-ai 全局规则在本流程内同等生效:灾难级与节点显式卡点暂停、多方案自主决策留痕、状态落盘(node 写 FIX)、运行日志照记、独立审查按 runtime/review.md 执行。 修改代码前预检 fresh 独立审查通道;无可用通道时暂停修复,已有改动保持待审。 当前支持 Codex 子代理/隔离 CLI;未验证的 Claude-native 适配不能改名冒充 Codex。

跨边界证据(条件触发):缺陷涉及跨进程/跨服务、异步队列或流、路由目标、缓存/状态不一致或时序偶现时,读取 references/cross-boundary-debugging.md;它只补定位证据,不新增入口、状态或完成标准。普通可复现缺陷不补表,仍走以下七步。

闭环七步(每个缺陷)

1. 复现(不能复现的 bug 不许修)

  • 按描述复现:实际操作/运行一次,拿到失败证据(报错原文、错误截图、错误返回值);证据要用严格裁判——宽容裁判会把坏产物蒙混成功(实跑:补丁类缺陷 GNU patch 的 fuzz 容错险些吞掉复现,换 git apply --check 才拿到硬证据)
  • 未复现先做复现探索:主动构造输入值(边界/空/超长/非法编码)、前置状态(空数据/脏数据/并发写入中间态)、时序(先后顺序/失焦与点击/异步未完成)、环境(版本/区域设置/权限/离线)、规模(单条/大量)场景;每次只改一个维度,记录「场景 → 结果」,沿用既有授权,不扩执行权限。
  • 探索最多 3 个场景或 15 分钟(先到为准);命中即进第 2 步定位,该场景脚本/步骤作为第 3 步红灯测试骨架。到上限仍未复现 → 不猜着修,才走观测闭环(偶现 bug 专用,两段式): ① 先判断是否命中跨边界证据条件;命中时按参考先列“边 → 预期证据 → 实际证据”,再在可疑路径加最小观测点(日志/埋点——观测点本身按最小改动+审查纪律入库,观测点不是修复尝试) ② 缺陷档案先落半份,状态记 观测中,列出已试场景,说明观测点为何这样埋,写清"等什么证据(哪个日志出现什么内容)" ③ 本次命令正常收口退出,不挂着等——运行日志记 run_done,detail 写「观测中:等{什么证据}」;状态文件 state 复位,不留悬挂的 running ④ 证据到手后再次运行 $cm-fix 附上证据,按 slug 定位 fixes/ 下的半份档案,从第 2 步定位续跑,档案续写、状态改 修复中,运行日志记 resume(detail 注证据摘要) ——"我改了点东西你再试试"依然被禁止

2. 定位(先找根因,不是找改哪行能让现象消失)

  • 按 ../codebase-context/references/writeback.md 确定项目地图及本次文档范围;有地图先读相关链路与影响映射,项目指定架构文档同样适用
  • 无地图 → 从失败点向上追调用链,找到根因层(现象在 UI,根因可能在数据层)
  • 命中跨边界证据条件 → 将调用链、每条边的最小证据、最后正常边与首个失败边写入缺陷档案;同时写“假设 → 支持证据 → 反证试验 → 结果”,一次只检验一个假设。日志与试验必须本地且脱敏,不自动联网、不外发日志、不安装依赖、不重启服务、不清理缓存。
  • 输出一句话根因结论 + 波及面清单(本次修改会牵连哪些模块)——写进缺陷档案(第 7 步)

2.5 根因与修法对抗确认(条件触发;根因错误是本流程最贵的错误,必须在防护网之前拦)

任一客观条件命中才触发(简单缺陷零负担,判断依据同"门槛是客观项不是判断题"):波及面 ≥3 个模块 / 根因层与现象层不同层 / 观测闭环续跑的缺陷 / 拟走升级出口。

  • 把根因结论 + 复现证据 + 波及面清单 + **拟采用修法(含放弃的备选)**交给新上下文的独立审查者;命中跨边界证据条件时一并交调用链、最后正常边、首个失败边和已完成的反证试验。提示词要义:「假设这个根因判断是错的,找出更深层的解释;再审修法:治本还是治症?有没有更小的改动?会不会引入新耦合?」。通道与降级规则同 N4
  • 仅 1 轮:推翻 → 回第 2 步重定位;分歧 → 交人裁决;通过 → 进第 3 步
  • 凭证落 {SPECS_DIR}/.reviews/fix-{slug}-cause-r1.md——命名带 cause 是有意的:不落入第 5 步 fix-{slug}-r*.md 的匹配域,两个卡点各自独立,根因凭证不会误满足 diff 审查卡点

3. 防护网(先让 bug 有测试,再修)

  • 写一个能复现此 bug 的失败测试(红)——它是"修好了"的客观定义,也是永久回归资产;红的原始输出落进档案(第 5 步审查要核对红证据,从未红过的测试转绿是空话)
  • 项目有存量测试 → 先跑一遍记录基线(修完对照,防止修 A 坏 B)
  • JS owner:首轮补测走原 test-author;保留原红灯和存量基线。最终审查要求补测时,按 references/test-extension.md 在第二轮登记扩充,修复回调仍不得改测试。
  • 写不了自动化测试的形态(如纯视觉)→ 截图/录屏留"修前"证据

4. 修复(最小改动)

  • 只改根因层,禁止顺手重构(N3 同款纪律:看不惯的代码记 LESSONS 待触发备忘,事后走 $cm-refactor,不在修 bug 时动)
  • 修法有多个方案 → 自主决策选最优,decision 事件留痕
  • 升级出口:定位发现是设计缺陷/需要跨模块大改 → 停止硬修,先通过第 2.5 步根因审查。JS 返回 design_change_required 后,有 redTest 就沿原测试编写/红测入口取得真实失败;意外通过或失败原因不符照常阻断。红测确认后进入 escalation_required,不跑基线、修复、回归或最终实现审查。
  • 已建资产不弃:失败测试留在仓库;档案状态记 升级立项,列出根因、影响范围、诊断方案、根因审查凭证、测试路径和红证据路径,建议用 $cm-prd --change 立项,以新方案使该测试变绿为验收。非视觉运行未配置 redTest 时直接进入升级归档,并明写没有失败测试及原因;视觉运行必须配置视觉 redTest(testFiles:[]),先走视觉红测核验真实修前载体再升级,不冒充自动红测。
  • JS 可先 publish_dossier,再用获准的 finish 写 run_done / escalation / escalated 并关闭 owner;中断后在原运行重开收口,冲突则阻断。重开后的 escalated 是终态,completionEligible 始终为 false,不记 task_done 或修复完成指标;立项建议不自动创建变更项目。

5. 审查(独立审查同 N4)

  • 修后按 ../cm-test/references/unit-coverage.md 检查已授权修复范围的增量单测覆盖率, 核对正常/异常/边界及相邻场景并重跑,纳入下面的同一份 handoff;原失败测试红绿证据必须保留。 普通流程在审前补足授权测试;JS owner 若第一轮最终审查要求补测,按 references/test-extension.md 登记测试编写和实跑结果, 再走第二轮修复、回归与独立审查;不在 owner 外修改测试或重建原红灯、基线。

  • 先按 ../codebase-context/references/writeback.md 完成地图评估与必要回写;无地图建本次局部地图,有地图只更新受影响章节。普通流程记 handoff evidence,JS 复用已绑定的 plan、修后文件与 Review;改动纳入摘要与独立审查,不能等第 7 步再写。

  • 审查前按 ../../runtime/project-learning.md 复盘并完成必要的 AGENTS.md 增量写回,纳入本次审查 diff;无新增记入缺陷档案。微缺陷通道也必须复盘,新增 AGENTS.md 改动导致不再满足单文件门槛时走完整流程。

  • 失败测试转绿 + 存量基线不退化后,按 runtime/review.md 审查本缺陷 diff(重点:根因是否真被修掉、有无只治症状、波及面有无遗漏)

  • 防护网测试本身是审查对象(实测最大问题类:测试是戏台):红的原因是否=该缺陷、断言测的是根因还是症状、有无安慰剂/前提共谋;核对第 3 步落档的红证据——没有红过的记录,测试可信度按不成立处理

  • 所有缺陷零豁免;独立通道不可用则待审,self-degraded 仅作诊断,不得成功收口;通道故障不算代码 finding/实现审查轮次,有效 finding 不能靠换人消除;≤2 轮上限同样生效

  • {slug} 先规范成跨平台安全的 ASCII kebab;令 REVIEW_FEATURE=fix-{slug}、 REVIEW_TASK=T-FIX-{slug}。主执行者按真实 diff 写 {SPECS_DIR}/.reviews/fix-{slug}-T-FIX-{slug}-a{attempt}-handoff.json,格式与 runtime/task-handoff.schema.json 相同。先按 handoff 的完整 changed_files 运行 cm-task-gate.py hash-implementation --project-root {CODE_PROJECT} --file ...,把返回的 implementation_sha256 写入 handoff,再真跑:

python3 {CM_WORKFLOW_ROOT}/scripts/cm-task-gate.py check-n4 \
  --handoff {HANDOFF_PATH} --reviews-dir {SPECS_DIR}/.reviews \
  --feature fix-{slug} --task T-FIX-{slug} --project-root {CODE_PROJECT}
  • 独立审查凭证严格落 {SPECS_DIR}/.reviews/fix-{slug}-T-FIX-{slug}-r{attempt}.md,包含当前 handoff 文件名和 SHA。审查完成后必须真跑下列命令;只有当前 attempt 的 independent: true 且 verdict: approved 才能进入第 6 步:
python3 {CM_WORKFLOW_ROOT}/scripts/cm-task-gate.py check-n5 \
  --handoff {HANDOFF_PATH} --reviews-dir {SPECS_DIR}/.reviews \
  --feature fix-{slug} --task T-FIX-{slug} --project-root {CODE_PROJECT}
  • changes_requested 后修改代码必须生成 attempt 2 handoff 并复审;第 2 轮仍有阻断项 写 blocked 并停止。文件存在、旧凭证或 ls 输出都不构成批准。
  • 后续回归、文档或经验整理如修改被审代码、测试或执行指令,原批准失效;重新形成证据并独立审查,不能重置轮次或在收口时顺手改实现

6. 回归(按波及面,不是只看 bug 消失)

  • 跑第 3 步防护网测试(红→绿)+ 存量测试全量(对照基线)
  • 按第 2 步波及面清单逐项走一遍关键流(同 B2 口径:波及面=回归范围)
  • 回归失败的回路(显式分支,不许临场发挥):任何一项红 → 退回第 4 步重修,重修后必须复审且轮次并入第 5 步的 ≤2 轮总上限——上限耗尽仍打转 = 根因判断可疑,按升级出口处置,不许无限修-回归循环

7. 落盘(审计链闭合)

  • 核验地图评估结果和已审文件版本,缺评估、待同步或审后变化不写成功 task_done;遵守回写合同的项目规则豁免,不把缺失地图静默跳过。
  • 收口前核对复盘记录、AGENTS.md 的审查范围与磁盘摘要;有新增则回读确认,无新增如实记录。缺记录、无法写回或批准后变化时不写成功 task_done,按学习合同与第 5 步处理。
  • 缺陷档案:{SPECS_DIR}/fixes/{YYYYMMDD}-{简短slug}.md——现象 / 复现步骤 / 复现尝试(逐条「场景 → 改了哪个维度 → 结果(复现/未复现/环境不支持)」;按描述一次命中只写一行)/ 根因 / 修法(含放弃的方案)/ 波及面与回归结果 / 测试文件路径;命中跨边界证据条件时追加“证据链与假设”(调用链、边证据、最后正常边、首个失败边、反证结果)。这是缺陷知识库,同类 bug 再犯先查这里
  • METRICS.md 追加一行:Feature 列写 fix,任务列写档案文件名,其余列同口径(轮次/拦截数/人工介入)
  • 根因具普遍性(如"平台 API 返回结构变了")→ 追记 LESSONS.md([已结构化]/[仅记忆] 分级同 N5)
  • Git 按有效 policies.delivery:diff 不 stage/commit;branch/draft-mr 提交 fix: {一句话} (档案: fixes/xxx.md),审查摘要进 commit message(同 N4)
  • 运行日志事件:task_start/review/task_done/run_done 照记,node 字段写 FIX

微缺陷快速通道(四个硬门槛全中才准走)

门槛是客观项不是判断题——"感觉这个 bug 很小"不构成理由,四条全中才走,任一不中走完整七步:

  • 只改文案/样式/配置常量——不新增、不修改任何条件分支与函数签名
  • 单文件且 diff ≤ 10 行
  • 波及面为零(改动处无被其他模块引用的行为;有业务地图查 08 映射表核实)
  • 有截图/文案前后对照可作验收证据

快速通道可省:第 3 步防护网测试、第 6 步全量回归(用前后对照截图代替)。 不可省:独立审查(凭证照落)、地图评估(回写导致多文件则走完整流程)、缺陷档案(显式标注 快速通道)、METRICS 行(Feature 列写 fix-lite)。 快速通道的审查特化:独立审查是该通道的主要质量防线,第一职责是复核四个客观门槛;diff 任一项不符或波及面存疑即打回完整七步。

fix-lite 的占比进运行日志——快速通道被滥用(占比异常高/出现分支改动混入)时收紧门槛,数据说了算。

输出格式(每个缺陷收口时)

🔧 缺陷闭环: {slug}
根因: {一句话}
修法: {一句话} | 放弃方案: {有则一句话,无则省}
防护网: 新增 {测试文件}(红→绿) · 存量基线 {N} 项无退化
审查: 独立审查({channel}) {通过/N轮N条} | 回归: 波及面 {N} 项通过
档案: fixes/{文件名}   METRICS 已记
学习: {AGENTS.md已写回并回读/已复盘,无新增}
业务地图: {已更新/已建局部地图/无需更新/项目规则豁免/待同步} {路径或原因}

边界

  • 不承接:新功能(走 $cm-prd)、需求变更(走 $cm-prd --change)、架构级返工(升级出口交人立项)
  • specs 目录没有 fixes/ 子目录时自动创建;没有 specs 目录的裸项目也可用:档案落代码项目 docs/fixes/,审查凭证落 docs/fixes/.reviews/(第 5 步卡点同样生效),METRICS 跳过

Signals

GitHub stars
23
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
cm-fix
Source
github.com/kingxiaozhe/cm-workflow