cm-fix — 缺陷修复小闭环
执行前读取 ../../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 才拿到硬证据)
- 复现不了 → 不猜着修,走观测闭环(偶现 bug 专用,两段式):
① 先判断是否命中跨边界证据条件;命中时按参考先列“边 → 预期证据 → 实际证据”,再在可疑路径加最小观测点(日志/埋点——观测点本身按最小改动+审查纪律入库,观测点不是修复尝试)
② 缺陷档案先落半份,状态记
观测中,写清"等什么证据(哪个日志出现什么内容)" ③ 本次命令正常收口退出,不挂着等——运行日志记run_done,detail 写「观测中:等{什么证据}」;状态文件 state 复位,不留悬挂的 running ④ 证据到手后再次运行$cm-fix附上证据,按 slug 定位fixes/下的半份档案,从第 2 步定位续跑,档案续写、状态改修复中,运行日志记resume(detail 注证据摘要) ——"我改了点东西你再试试"依然被禁止
2. 定位(先找根因,不是找改哪行能让现象消失)
- 有业务地图(
docs/codebase-context/)→ 先查 07 业务线路定位所在链路,08 修改影响映射表查波及面 - 无地图 → 从失败点向上追调用链,找到根因层(现象在 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)
- 写不了自动化测试的形态(如纯视觉)→ 截图/录屏留"修前"证据
4. 修复(最小改动)
- 只改根因层,禁止顺手重构(N3 同款纪律:看不惯的代码记 LESSONS 待触发备忘,事后走
$cm-refactor,不在修 bug 时动) - 修法有多个方案 → 自主决策选最优,
decision事件留痕 - 升级出口:定位发现是设计缺陷/需要跨模块大改 → 停止硬修,报告根因并建议走
$cm-prd --change变更模式立项——bug 命令不承接架构手术。已建资产不弃:第 3 步的失败测试保留入库(它是缺陷的客观复现,新方案转绿即验收),档案状态记升级立项,注明测试路径供立项后的流程直接接手
5. 审查(独立审查同 N4)
- 审查前按
../../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. 落盘(审计链闭合)
- 收口前核对复盘记录、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 跳过