# 更新日志

按版本记录用户可感知的新增、优化与修复；最新内容放在最前面。
尚未进入发版候选的改动放在「未发布」；版本条目记录该版本的交付内容，npm 发布状态以注册表为准。

## 未发布

（暂无）

## 0.16.5 — 2026-09-29

- cm-ai 审查轮次用完（`review_limit`／`review_blocked`）后用 supersede 新建的运行，第 1 轮开发与独立审查会看到上个运行最后一次审查的 findings（标明只是参考、不是结论，不跳过审查、不改轮次），开发不再重犯同样问题；该内容写入新运行记录，回放时不可替换，旧记录照常回放。
- cm-ai 当前会话驱动 bootstrap 规范任务时，会话返回未通过的 `init_verify`（第 1 轮或第 2 轮）不再停在无法恢复的 `unknown/execution_error`，改为同一运行、同一轮可重试的 `blocked/bootstrap_verification_failed`（原因列出未通过的核验组），规范未写入、已记录的规范证据保留，修正后在原运行 `advance` 重新生成与核验；旧运行存档照原样重放。
- cm-prd 审查门禁的每个拒绝带稳定错误码，宿主 stderr 诊断写明回执与改动文件（只含校验过的规格相对路径），不再只报 `host_request_failed` / `detail: unavailable`；内容为 `null` 的回执与非法 UTF-8 也带码，CLI 报错文字不变。
- cm-ai 单任务驾驶员可驾驶 bootstrap 规范任务（通常 T-002）及第 2 轮修订：启动前校验 `init-generate.json`／`init-verify.json`（第 2 轮只读 `*-a2.json`，须在读取首轮 findings 后编写），`init_verify` 的命令组只由驾驶员在宿主接受启动后、发送操作前实跑会话列出的草稿命令得出（先用宿主自己的读取器与 admission 函数核对写入授权、配置、任务选择与 nextTask，受保护模式在 specs 沙箱内运行，跑后重核规范目标与绑定文件），未通过不发送操作，其余四组核验与 Learning 来自会话答案；规范写入后检查未通过可在原运行修好后同轮重试，宿主与驾驶员只接受本运行存档记录的写入；含业务文件、多代码根或 `--protected-config` 的规范任务及批次驾驶员仍在启动前拒绝。
- cm-ai N6 的 `qa_assess` 应答超时不再落盘为永久阻塞决定，改为可重试的 `rejected/qa_decision_timeout`，恢复原运行后再次 `advance` 重新询问；旧版本已记录的 `阻塞:host_request_timeout` 决定默认照旧返回 `qa_blocked`（附原因），显式 `--rerun-blocked-qa` 只重新询问一次并以 `previous_decision_id` 追加替代决定，历史不改写。修正 js-host.md 中 `qa.timeoutMs` 上限为 3600000。
- cm-ai `--rerun-blocked-qa` 可在同一代码上重跑会话如实回答的 BLOCKED（如模拟器不可用）与没有退出码的 QA 命令结果（超时、被杀、启动或输出失败）及受其牵连的 logic 用例；非零退出仍是产品 FAIL，只有同时加 `--qa-environment-failure "原因"` 显式声明环境故障才替代，理由与失败用例写入 superseded 记录。每次重跑都占用同一个最多三轮的 QA 预算。
- cm-ai N6 的 `qa_assess` 应答超时不再落盘为永久阻塞决定，改为可重试的 `rejected/qa_decision_timeout`，恢复原运行后再次 `advance` 重新询问；旧版本已记录的 `阻塞:host_request_timeout` 决定默认照旧返回 `qa_blocked`（附原因），显式 `--rerun-blocked-qa` 只重新询问一次并以 `previous_decision_id` 追加替代决定，历史不改写；替代决定写入后、首轮 QA 开跑前中断时，同一命令可直接续跑。修正 js-host.md 中 `qa.timeoutMs` 上限为 3600000。
- cm-ai `--rerun-blocked-qa` 可在同一代码上重跑会话如实回答的 BLOCKED（如模拟器不可用）与没有退出码的 QA 命令结果（超时、被杀、启动或输出失败）及只因其阻断的 logic 用例（`[需确认]` 以 test-cases.json 为准一律除外，报告标记只作交叉核对）；非零退出仍是产品 FAIL，只有同时加 `--qa-environment-failure "原因"` 显式声明环境故障才替代，理由与失败用例写入 superseded 记录。每次重跑都占用同一个最多三轮的 QA 预算。
- cm-ai `--revise-qa-config` 支持首轮 QA 前修订（开发中、待审、N5 完成但 QA 未开跑），记为不消耗轮次的 round-0 修订并在运行日志留痕，首轮仍为 qaRound 1；QA 开跑后的修订规则不变。
- cm-ai 独立审查 CLI 未登录、限流、服务端过载、模型不存在、输出无法识别的事件或没有结论就退出（未调用工具、未给结论、进程已退出）时，不再停在 unknown，而是 `pending_review/review_provider_failed`，reason 写明失败类别和下一步（先登录或等额度），同一 attempt 与传输超时、abandon 共用一次重派；超出后为 `blocked/review_provider_failed`。已有最终消息但被超时截断的审查改为提示 `abandon_review`，操作员留痕放弃后同样只重派一次；旧 journal 照原样回放，旧 unknown 不自动改类。
- cm-ai 审查提示写明 verdict 规则（P0–P3 含义、approved 不能带 P0–P2、changes_requested 至少一条 P0–P2、只有代码无法在范围内修好时才用 blocked、规格文件不是 finding 路径）；审查答复违反这些规则时记为 `pending_review/review_verdict_invalid` 并保留具体代码（如 `contradictory_verdict`），同一预算内重派一次；`blocked` 仍是终态但 reason 写明审查给出的原因。
- cm-ai 调大 `--review-config` 的 `timeoutMs`（超过 30 分钟）不再在 30 分钟被运行器截断并耗掉唯一重试：运行器的审查计时改为审查预算加 1 分钟余量，且不写入 journal，恢复时可继续调大。
- cm-ai 可重试的开发阻断（如检查产物越界、开发检查未通过）反复出现、剩余调用名额已不够再交付一次并送审，或剩余 effect 名额已不够再交付并送审时，在派发开发前停在终态 `blocked/develop_retry_limit`（journal 记 `develop-retry-limit`，不写 intent、不调用开发者），reason 写明上次阻断原因并提示 supersede 新建运行，不再让第 7 次调用跑完后检查点被拒、运行变成 unknown，也不再停在 complete 被 `limit_exceeded` 拒绝、永远无法完成的 approved；显式放弃的审查调用也不再占调用名额。
- cm-ai 完成（complete）不再占六个 effect 名额：审查已批准的运行总能进入完成，包括旧版本已用满六个 develop／review 名额后停在 approved、complete 被 `limit_exceeded` 拒绝的运行。完成前复查因检查结果或新文件变化被拦下（`completion_checks_changed`／`completion_package_changed`）时改为单独最多重试 3 次；第 4 次仍被拦下即写入 `completion-retry-limit` 并停在终态 `blocked/completion_retry_limit`（`pendingAction: none`），reason 提示先修好检查环境再 supersede 新建运行，不再出现已批准却因 `limit_exceeded` 永远完成不了的任务。
- cm-ai 第 2 轮交付与第 1 轮被要求修改的代码逐字节相同时，停在可重试的 `blocked/develop_unchanged_after_review`，不送审、不耗第 2 轮审查；同时修正第 2 轮开发检查被门禁拦下时 journal 回放失败、运行变成 unknown 的问题。
- cm-ai 带 QA 的运行不再因项目 `.cm-workflow.yml`、用户 `~/.cm-workflow/runtimes.yml` 或插件内置默认值变化而无法恢复：新运行的指纹只绑定宿主给出的 QA 输入，执行计划由每轮 QA 在 N6 冻结并记入 `test_run`；`--revise-qa-config` 也不再用当前配置重建旧计划。此前创建的运行照原指纹打开，配置已变时仍 `fingerprint_mismatch`，并附原因提示恢复创建时的配置。
- cm-ai 收尾 `finish`／`run_finalize` 在任一已批准 feature 的最新 QA 未通过（FAIL、BLOCKED、已触发未执行或结果未知），或任务已全部完成的 feature 没有 feature 完成时的 QA PASS（无 QA 记录或最新为 skipped）时返回 `project_qa_not_passed`，列出 feature、任务和 runId，不做文档核验、不写 run_done；准入选下一任务时在 `warnings` 中提示。
- cm-ai 已完成运行的 QA 恢复、QA 修复、配置修订与收尾不再被其他任务后续的已审交付锁住：同一代码根、其他任务已完成并提交运行的审查包（含其已登记 QA 修复、AGENTS.md 教训行和对同一文件的修改），凡审查晚于本运行批准审查的，全部按审查时间顺序逐个严格接续（审查前状态必须等于当时的组合），本运行的 QA 修复按其最终审查时间插在同一序列中，不挑选、不搜索；最终组合须与当前内容和文件权限完全一致，任务范围外的项目根 CM 配置可以修改。接受修复时尚未记录、审查早于该修复的交付排在它之前，以只含运行 ID 与包摘要的 version 2 关联记录存进 journal，此后不变，接受之后才提交的交付排在修复之后现场接续；每条都须能从所列运行的真实交付包推出，存档在时回放即核实、不在时后续操作失败关闭，记录超出存储限制时以 `fix_record_too_large` 拒绝而不写入（一条记录最多 2048 个后续交付；未覆盖存储目录 32 MiB 物理上限与崩溃残留临时文件，见文档），旧记录照原样回放；对不上时（含已审交付之后的手工回退）为 `correction_review_required`，并在 `reason` 列出路径。
- cm-fix 原因审查与第二轮最终审查登记后没有结论（宿主被杀、超时、断连或取消）时，可用 `abandon_review`（专用 `--allow-abandon-review`，父宿主 `--allow-qa-fix-abandon-review`）各留痕放弃一次，再以新审查线程重审；审查等待改用审查配置的 `timeoutMs`（默认 15 分钟），不再沿用复现命令超时，原因审查超时会记下结果。替代旧审查证据时，QA FAIL 不再被误报为「已完成或 QA 已通过」，并提示改走 QA 修复。
- cm-ai 批次驾驶员对尚未开跑的后续任务也按答案本身预检审查材料合计：答案写入的 scope 文件合计已超过 2 MiB 时在启动第一个任务前退出 2，不再等到开跑后才被审查包拒绝。
- cm-ai 单任务、批次与 cm-fix 宿主的工具应答上限随 `--input-limit` 一起放大（cm-fix 宿主新增该参数，cm-fix 驾驶员用 PLAN `inputLimit`），超长输入行报 `request_too_large` 并说明上限；宿主拒收应答时驾驶员立即结束会话并退出 1，受保护模式在启动前估算应答大小并提示应加的值，不再无限等待。
- cm-ai 开发答案 `develop.json.edits` 支持 `{mode:"0755"|"0644"}`、`{file,mode}` 和 `{delete:true}`，改名即删旧写新；启动前校验（同时是 requirements 的路径不能删除，当前会话仍删除时为可重试的 `blocked/develop_requirement_missing`），审查包原有的权限位与删除记录在审查提示中说明，事后改权限按包漂移阻断。
- cm-ai 驾驶员在启动前拒绝单文件超过 1 MiB、审查材料（与审查包快照同一选取，含树中全部 AGENTS.md）超过 2 MiB 或 256 个文件、与基线完全相同的开发答案并写明路径和上限；第 2 轮的开发阶段阻断（如 `develop_checks_not_passed`）此前回放即失败为 `store_failure`，现可在原轮次重试；审查包装不进运行存档单条 1 MiB 记录（按有界审查结果推出的预留计，小任务约 550 KiB 以上的单文件交付，或过大的任务基线）时驾驶员以宿主同一套构建代码（含 handoff 与 AGENTS.md 回写）启动前拒绝，宿主在建存档前拒绝过大基线，当前会话交付则为可重试的 `blocked/develop_package_too_large`；审查结果超过 12 KiB 时按固定规则截断并省略末尾 finding（保留 verdict 与至少一条阻断 finding）；列出路径的阻断原因统一有界（「等 N 个」），长路径不再让检查点超出回放上限；当前会话交付空改动改为可重试的 `blocked/develop_empty_changes`，旧 `unknown/empty_changes` 历史按原样回放。
- cm-ai 单任务驾驶员恢复时按存档里的当前轮次发送 identity，第 2 轮的 decision、complete、qa 等不再报 `identity_mismatch`。
- cm-ai 批次驾驶员带首轮审查授权但缺 `develop-a2.json` 时不再启动前拒绝：审查要求修改时任务停在 `changes_requested/revision_answer_required`，读完 findings 写好第 2 轮答案再继续。
- cm-ai 受保护当前会话模式在启动前拒绝非 UTF-8 的开发内容（不再被替换字符悄悄改坏），新文件以 0644 创建。
- cm-ai 新建运行前检查 `runId` 须为 8–128 个字符（与运行日志同一规则），不再在开发 intent 写入后才失败；已有运行恢复不受影响。
- cm-ai 任务进行中规格经正规流程改动并重新批准后，状态报 `spec_drift`、列出变化文件与真实出口，不再提示会被拒绝的动作；只动了其他任务的条目、依赖或用例（requirements.md、design.md 整份未变，本任务条目含续行及任何点名本任务的行与用例未变，由新记录的 `taskScopeDigest` 证明）时可在原运行用 `--rebind-spec-material --spec-rebind-reason` 显式换绑（journal 记 `specification-rebound`，只换批准哈希；旧版本创建的运行不可换绑），已有开发与审查结论保留；内容有变则拒绝并点名，可还原规格继续或 supersede 重做。
- cm-ai 启动参数错误不再只报裸错误码：`fingerprint_mismatch` 点名与创建时不同的输入（runtime、审查配置、会话等），`invalid_arguments` 写明参数；单任务 create 与新批次都必须带 `--review-config`，避免运行到待审后才发现无法补加；批次成员不提示换绑，改为说明可用出口。
- cm-ai `tasks.md` 任务行统一为一套语法（无冒号、全角冒号、缩进嵌套均可，围栏内示例不算），准入、cm-prd 自检、N5 勾选与审批 manifest 归一化一致，勾选后不再误报整个项目规格漂移；语法随批准绑定（新批准记 `taskGrammar: 2`），旧批准继续按原解析器读取，重新批准时若有 feature 在新语法下无效、或从旧语法切换会改变任务集合，则 `task_grammar_conflict` 拒绝并点名行号，旧版批准的 manifest 仍原样匹配。
- cm-ai `--task` 选择依赖已 DROPPED 任务的下一任务时与 `nextTask` 同样视为依赖已满足；`task_selection_mismatch` 附带原因。
- cm-ai 普通新建运行会拒绝悄悄接收同任务旧运行留下的未审改动（同 supersede 的漂移检查与 `--accept-superseded-code-drift` 记录）；第 1 轮就结束、没有可归档证据的旧运行也可以 supersede。

- cm-ai 单任务驾驶员对 bootstrap 规范任务在启动前拒绝缺少实时 `init_verify` runner，并将 unknown／reconcile 宿主结果作为失败退出；补充当前会话宿主路径与旧运行恢复说明。
- cm-ai 开发检查失败或不可用时以独立的 `develop_checks_not_passed` 在审查前阻断并可在原运行重试；旧完成门禁的 `checks_not_passed` 保持终态。检查产物越界重试改用新 effect id，完成复查失败保留原审查重试，单任务和批次驾驶员支持每项及默认检查超时（默认 15 分钟）。
- cm-ai 代码根快照跳过 macOS/iOS IDE 与构建杂项及 Git 忽略路径，保留任务范围、AGENTS.md 与规格检查；基线保存有界忽略决定并在后续比较两侧取并集，允许任务修改 `.gitignore`、运行中 `git init` 及无关 Git 配置变化。漂移诊断列出路径，审查中漂移保存 verdict 并可在原运行清理后继续，完成复核漂移可重试。
- cm-ai 单任务 V3 新增 `abandon_effect`：当前会话 develop/complete 在 intent 后中断且无 task commit 时，操作员确认旧 host 与子进程退出后可在原 run 留痕退出为 `cancelled/effect_abandoned`；新运行仍执行原有审查证据及代码漂移门禁。
- cm-ai 单任务与批次驾驶员按请求轮次读取开发答案；第 2 轮只接受 `develop-a2.json`，审查授权可能跨轮时提前检查，避免把首轮编辑重复用于修订。cm-fix 驾驶员的修订测试／修复答案同样改为 `*-a2.json`。
- cm-task-gate Python 适配器按 UTF-8 读取 Node 输出，避免 Windows 系统默认编码使中文诊断解码失败。
- cm-ai 规格审批接受六种明确开始语及尾部标点，泛化授权仍拒绝；cm-prd 草稿与审查校验为时间、标题、依赖、大小和测试理由提供字段级诊断，原门禁不变。
- cm-ai 新建运行替代旧审查证据前，只核对尚未被其他旧运行替代的直接前驱运行的 V2 逐文件代码基线；漂移默认以 `supersede_code_drift` 拒绝，操作员也可显式加 `--accept-superseded-code-drift` 保留并记录前驱 runId、路径和当前摘要。更早运行仍照常归档；检查发生在新 journal 与新基线创建前。
- 补充 iOS 系统服务检查的分根与沙箱边界、当前会话驱动脚本和 Claude Code 权限建议，并在 cm-ai 宿主帮助中指向驱动。
- cm-ai bootstrap支持已批准的单个编号`*.bootstrap`（含`1.bootstrap`），拒绝多个候选，并在规范写入前保留AGENTS.md既有Learning段及其他约束。
- cm-ai 已批准任务在完成前重跑检查时，仅比较检查身份与结果（id、command、outcome、exitCode），允许 evidence 摘要文字变化；检查结果变化保留原审查并以 `blocked/completion_checks_changed` 在同一 run 重试完成，代码、范围、handoff 或检查身份漂移仍终态阻断。
- cm-ai 改进检查产物越界诊断和同 run 恢复、独立审查 15 分钟默认超时、Claude 模型拒绝提示及交互 QA 载体说明。
- cm-ai 单任务可显式选择同 feature 的依赖就绪任务；新运行提前拒绝已审 handoff 冲突，同会话恢复可省略 `originalHostContext`，换会话继续校验创建会话与配置指纹。
- cm-ai 单任务 V3 新增 `abandon_review`：操作员确认中断的独立审查进程已退出后，在原 run 上留痕放弃无结果调用，恢复到可重派审查；修正 unknown 上 `cancel` 虚报已取消。

## 0.16.4 — 2026-09-27

- Claude review 与 developer 的 CLI 流解析器接受 init 前后的 `dev_intent` 通知，校验会话并计入 32 条通知上限。
- 修复 cm-prd 自检命令产生输出时误报 `output_capture_failed`；cm-ai 单任务和批次宿主可用 `--input-limit BYTES` 将输入上限从默认 64 KiB 提高到最多 4 MiB，恢复时可调整。
- 修复 `cm-prd --change` 的 `revisionDigest` 在汇总发布或规格审批后丢失，导致后续变更误判旧审查记录；补充旧状态恢复说明与明确的“开始”审批提示。
- 修复 `cm-check` 机械检查把当前运行时硬当成 Codex 的问题：从 Claude Code 跑自检时，`reviewer` 会被误标成 `declared-adapter`（已声明未派发），看起来像独立审查通道没派出去。
- `cm-check-runtime.sh` 新增 `--runtime codex|claude`，取值优先级为 `--runtime` > `CM_RUNTIME` > 未判定；两者都没有时打印「未判定」并说明如何指定，不再对是否派发下结论。
- 该参数经 `cm-check-host.mjs`、`cm-check-entry.mjs`、`cm-check-drive.mjs` 一路透传到检查脚本，并同步更新 `cm-check` 的 SKILL 与接线文档。

## 0.16.3 — 2026-09-26

- `cm-ai` 单步驾驶员按任务适用 case 与 `[需确认]` 标记预检 runner；已映射 logic 仍按当前 executor 的真实请求拒绝。
- cm-ai Claude review preflight 现在识别 CLI 的 `unrecognized_model` stderr 标记，返回失败及被拒模型 id，避免错误模型配置进入真实审查。
- CI 新增独立、不阻断合并的 `experiments-tests` 任务，跑历史兼容夹具目录，防止再次陈旧。
- 修复 cm-ai runner 读取异常码 getter 的回归，补充回归测试并更新 JS orchestration 历史兼容夹具。
- 更新 JS orchestration 历史兼容测试，适配当前开发准入与审查超时合同，并修复未完成 Promise。
- 更新 `experiments/js-orchestration/task-owner.test.mjs` 历史兼容夹具，按当前 JS 任务门禁和运行级写入锁验证所有权、证书与崩溃恢复。
- 修复 `experiments/js-orchestration/task-commit.test.mjs` 的历史兼容夹具：交接证据绑定当前实现，旧子进程注入用例改用现行 JS 完成门禁与文件变更边界。
- `cm-ai` 交接文件与 cm-fix 审查证据现共用一份「归档让路」实现；归档命名、权限、旧字节保留和重试行为不变。
- 在 CI 执行的测试中补充 B 类内部协议分支与变异验证，覆盖授权凭据过期、时钟回退、会话收尾、状态校验、参数传输和补正结果；压缩编码（gzip、zstd）与未声明编码的回环用例在沙箱外验证通过（仅 macOS 运行）。

**cm-ai 已审 handoff 后任务重跑出口**

- 旧运行的 review 已消费 handoff、随后 QA blocked 时，新运行再发布同名 handoff 会报 `handoff_exists`；现在 blocked 结果和 `[host]` 诊断提示先按 QA 问题恢复原运行，确需重跑则显式授权并说明原因。
- N5 会先于 N6 把任务勾为 `[x]`；QA BLOCKED 后若确需重做开发，先在 `tasks.md` 将该任务改回 `- [ ]`，再用新 runId 带 `--supersede-reviewed-evidence --supersede-reason` 创建运行。只在任务未勾选、全部旧运行终止且旧 writer 已关闭后，先记新 journal，再按文件 SHA-256 归档旧交接与审查凭证并写 `supersede` 日志；中断恢复补齐归档，旧 journal 原字节保留。正常完成、活动运行或无旧证据拒绝。
- 单步驾驶员接受这对创建参数，缺项或用于恢复时在启动宿主前拒绝；旧 writer 检查允许 `lsof: WARNING:` 提示，但其他诊断仍拒绝归档。
- 临时项目夹具先复现缺少提示的红灯，再验证归档、拒绝和中途恢复；旧 QA 的 UUID 报告仍留原位，受保护模式和真实项目 QA 尚需独立验收。

**cm-ai 单步驾驶员与 cm-fix 共用传输核心**

- 新增 `cm-prd-drive.mjs`，按新建、变更和会话恢复状态预检人工答案、文件范围与绑定字段；自检命令由驾驶员实际执行并记录退出证据，PDF/HTML 材料缺执行器时在启动前拒绝。只读状态、审查、处置、摘要与发布仍走原宿主门禁。

- 原先使用 cm-ai JSONL 宿主要临时写中间人，缺一份应答可能把运行留在 `unknown`。现在 `cm-ai-drive.mjs` 从计划文件启动宿主，先查运行定义、恢复存档、所需答案与结构，再发一条操作；cm-fix 驾驶员改用相同的传输核心，原计划和输出行为保持兼容。
- 人工文件只提供开发、文档和 QA 判断及 QA 修复内容；`check` 由驾驶员在代码根实际执行命令，原始输出送 stderr，宿主得到真实退出码和精简证据。缺真实 runner 的 `qa_logic`、`qa_browser`、`verification_precheck` 在发送前拒绝，静态文件不能充当执行证据。受保护执行仍由宿主负责检查。
- 用真实临时项目和宿主验证创建、恢复、预检、只读状态与 QA 修复转发；cm-fix 原夹具不改。尚未实现三个证据类 runner，也未覆盖其它 7 个宿主。
- 新增 cm-check、cm-init、cm-idea 三个单步驾驭员：发送前按宿主阶段核验人工答案、路径与恢复绑定。cm-check 真跑计划中与宿主一致的机械命令；cm-init 经本次写入授权后真写审查草稿；cm-idea 由宿主独占保存并回读。三个真实宿主夹具覆盖成功、拒绝和状态查询；剩余 4 个宿主尚无驾驭员。

**cm-fix 本地步骤卡在 unknown 时可显式放弃并重做**

- 原运行在复现、诊断、测试、修复、回归、复盘或走查的结果丢失后，可由宿主带独立授权和单行原因调用 `abandon_step`；最多 8 次，旧记录及部分结果留史，新调用使用 retry ID，红灯输出另存。QA-fix 子流程有单独启动旗标，驾驶员从计划读取原因。
- 根因审查、最终审查、Learning 写回和交接文件不能走此出口；最终审查沿用原人工续审。普通执行不会自动放弃，原独立审查及完成门禁不变。

**重构审查要求补判官测试时，第二轮可以受控修订**

- 以前审查允许要求补测试，但第二轮只能改业务文件，判官又被原始摘要锁住。现在第一轮要求修改时可声明原测试资产内的文件子集，由宿主提议文本、控制器保存修订；业务范围与测试范围仍分离，配置和两轮上限不变。
- 新版测试先在启动时业务原稿上重建答案并重做变异自验证，再与第二轮重构稿比较，防止把行为回归吸收到预期答案。提案、原审查绑定、写入和新报告另存，可恢复中断；不走新路径的旧日志、回执和摘要保持原样。
- 已归档的旧文字审查可追加登记，绑定原审查和被拒提案；只允许第二轮尚未受控写入、验证或审查的运行，保留历史并用新调用名征集业务提案，不重置轮次。
- 使用临时项目复现死锁，先红测再实现，覆盖双版本证据、越界、漏检、原稿失败、恢复与旧运行兼容，并执行至少四项变异检查。夹具宿主和审查回答为模拟数据，不代表真实模型审查或外部业务验收。
**开发已完成后，填错的 QA 配置可以修订并继续验证**

- 以前漏配测试命令会得到 `commands-unavailable`，补上命令后又被运行指纹拒绝，已完成的开发任务无法继续 QA。现在单任务恢复可显式提供上一版配置、修订原因和 QA 授权，追加前后配置绑定后继续原运行。
- 只使旧配置下的 QA 结果失效并留史；开发、独立审查、N5 和原指纹不变。新 QA 消耗下一轮，最多三轮；未知执行须先核对，其他配置漂移仍拒绝。审查传输超时沿用原有规则，批量入口不增加修订参数。
- 用临时项目跑真实状态机、N5 和本地 QA 命令，先复现原阻断并确认红测，再验证恢复、历史保留、修订链、中断重放、第二次开发审查后的续跑和五项变异检查；模拟审查结果不代表真实模型或外部项目验收。
**修复审查要求补测试、或根因范围包含测试时的流程卡住**

- 第一轮最终审查要求补边界测试时，第二轮可登记独立的测试编写与执行记录，再走原修复、回归和审查；已准备但未开始修复的旧第二轮可追加计划。原红灯输出、存量基线和旧审查不改写，只补测试也不再强制改业务代码。
- 新测试统一作为有理由、绑定审查发现的覆盖补充，修订前记录真实执行结果，修后及审后必须通过；不冒充原代码上的新红灯。根因包继续保留全部原受审字节，只接受宿主登记的精确测试变化，业务源码漂移仍拒绝。
- 原受保护编辑、两轮上限、已审任务重名保护及未知结果不自动重试不变；不支持借此改配置、换任务身份或修复损坏证据。用真实临时文件、命令和状态机先复现两处阻断，再验证完整收尾、历史字节、恢复及变异检查；模型回答为夹具，不代表真实模型或外部项目验收。
**原材料中途改变，可以正式结束旧规格批次并关联新批次**

- 以前同批 A 已处置、B 未审时，修正原材料会同时挡住 B 的审查、整批修订和旧会话恢复。现在须明确授权并说明原因，才能写入不可覆盖的“输入已替换”终止记录；完整保留旧会话、审查原文、未审阶段及新旧输入摘要，旧批次不能继续审查、汇总或发布。
- 新批次使用独立的干净 specs 目录，并核验前序记录、指定会话和新输入摘要；旧批准全部留在历史，新批次从分析到审查重新执行，摘要显示前序关系。已发布、已取消或已进入变更模式的批次不适用；不改旧记录、不把未审项当批准、不自动开发或发布。
- 使用临时材料和真实状态机先复现阻断，再验证授权拒绝、原字节保留、不可覆盖记录、未知调用归档、跨进程终止与历史读取、新批次无批准继承，并进行至少四项变异检查。宿主返回使用模拟数据，未调用真实模型或真实用户项目。

**设计接受后，自检发现需求或设计问题也能修正**

- 以前整稿自检要求改需求，却被已接受设计锁住，拆分审查和后续修订都无法继续。现在仅自检明确失败后，允许附原因修改同批已有需求、设计正文；功能与文件清单不变，失败记录和两轮上限保留。
- 修订与原失败、文件摘要和轮次一起持久保存；后续优先认已完成的拆分处置，其次自检修订，再次原设计。拆分审查必须审新版，旧版不能授权新版；不增加设计审查或审查调用。
- 摘要在风险信息旁逐功能列明原因、改动文件及“已由拆分审查审阅，未另做设计审查”。未使用此路径的旧会话、绑定和回执保持原样。

**拆分补正自检失败也能交人裁决**

- 以前修正已经保存，自检一旦失败却写不出处置回执，摘要和受控修订都被卡住。现在记录 `self_check_failed`，保留原检查、逐项决定和已保存版本；摘要风险点明确列出失败及证据，可发布待审，由人批准或拒绝。
- 失败回执只用于确认处置和文件版本，不代表自检通过。需求、设计补正可继续同批其他功能及受控修订，原失败留在历史；设计阶段、缺少真实失败记录或检查实际通过时不能使用该值。未知结果仍须恢复原调用，不补审、不自动修复、不重试。
- 复用原夹具先复现死锁，再验证上下文与机械失败、真实宿主命令链、历史保留、旧字节兼容及四项变异检查；模拟宿主结果不代表真实模型或外部项目验收。

**拆分审查补正需求或设计后，原会话与整批审查可以继续**

- 以前设计阶段处置后，拆分审查要求修正 design.md 会卡住原会话；requirements.md 虽能完成单项处置，却会阻断同批其他功能的审查。现在两份文档都可按原发现补正，先保存、通过原自检，再把新 SHA 写入同一 feature 的拆分回执。
- 待处置时只放行绑定原审查包、逐项改动与完整文件 SHA 的补正读取和处置；完成后，保存、后续 feature 审查、整批汇总、待审发布和受控修订统一接受回执版本。低风险项没有设计审查时也适用，不补建审查回执。未列出的改动、错包、跨 feature 回执和回执后的再次修改仍拒绝；原回执、旧会话格式和未改文档的绑定摘要不变。
- 复用原夹具，先在旧实现复现设计死锁、需求批次阻断和混合风险阻断，再覆盖旧会话恢复、自动与手工补正、自检失败、下游入口和旧字节兼容。设计的四项变异检查之外，再验证放宽未声明需求变化、借用其他 feature 的需求回执、继续用设计阶段需求字节检查整批，均被测试抓住。测试使用模拟宿主，不代表真实模型审查或外部项目验收。

**说规则缺失或错误，就必须交回修订后的手册**

- 以前双译对比裁决为 `rule_missing` 或 `rule_wrong` 后，即使原样交回手册，也能发布报告并进入试点；现在只要有一项这样的裁决（包括混合裁决），手册没有有效文本变化就以 `refactor_rule_revision_required` 停机，不发布对比报告、不启动试点。
- 比较前统一换行符、去掉每行行尾空白和首尾空行，这些纯排版变化不算修订；全部为规则正确或没有裁决时，手册不变仍可通过。守卫只检查文本有无修订，是否解决问题仍由后续独立审查判断。
- 复用真实批次流程夹具，先在旧代码上确认拒绝用例失败，再验证准确错误码、报告与试点均未发生、真实修订传到后续步骤，以及规则正确和无裁决的兼容路径；分别删掉守卫、只查规则缺失、跳过空白归一化、错误地与裁决后手册比较，测试都能抓住。测试使用模拟宿主，不代表真实模型审查或外部项目验收。

**cm-fix 的设计升级出口终于能退出**

- 以前诊断为 `design_change`，根因审查通过后就停在 `design_change_required`：跑不了失败测试，也写不了升级档案和退出日志。
- 现在配置了红测就先按原权限编写并确认真实失败，再进入升级归档；意外绿灯照常阻断。非视觉运行没有红测配置时可直接升级，并明确记录没有失败测试及原因；视觉运行必须配置视觉 `redTest`（`testFiles:[]`），先核验视觉红测再升级，缺少该配置仍在打开时拒绝。档案状态为“升级立项”，带上根因、影响范围、审查凭证、测试和红证据路径，建议 `$cm-prd --change` 接手，原失败测试保留。
- 授权 finish 写升级退出日志并关闭 owner；原运行中断后可继续收口，重复操作不重复记退出，冲突日志拒绝。`escalated` 是未修复的终态，不走基线、修复、回归、最终实现审查或完成指标。QA 子运行向父级报告 `qa_fix_incomplete`，不自动创建变更项目，也不执行 Git。
- 新增 `cm-fix-escalation.test.mjs`，先确认旧代码失败，再验证真实红测、测试编写、无红测、绿灯阻断、崩溃恢复、冲突、终态、普通路径字节兼容和 QA 父子接线；逐项故意改坏状态路由、红测要求、完成资格、幂等检查、档案状态和视觉配置守卫，确认测试抓住。审查使用模拟适配器，不代表真实模型或外部项目验收。

**QA 修复子运行也能换会话接着跑**

- 父任务能换会话了，里面的 QA 修复子运行却还要求当前会话等于创建会话，换到 B 就报 `qa_fix_host_mismatch`。子审查授权也一直用旧会话签名，子运行不知道真正接手的是谁。
- 现在每次打开子运行（包括只看状态）都传当前真实会话，审查授权也由它签名。原配置不改；新会话首次签授权前，由 cm-fix 原有逻辑记下 `fix-host-joined-N`，第三个会话仍能认出前一个会话签的授权。只读打开不添这条记录，原因审查员不能同时是当前宿主。
- 新建子运行的配置宿主只能是当前会话或父运行存档中的创建会话；其它身份在创建子目录前被拒绝。已有子运行沿用原配置并校验指纹，子运行的 runtime 仍须匹配当前宿主。旧 protected 兼容入口、batch、bootstrap 和 cm-fix 内部逻辑不变，原动作权限仍须逐项提供。
- 先写红灯测试再修：新增可在 CI 跑的真实父子存储测试，覆盖 A 创建、B 续跑并签授权、C 再打开，以及非法宿主、运行时不匹配和审查员独立性；另用 CLI 验证实际接线。故意改回旧签名、漏传当前会话、放宽创建身份或删掉 runtime 检查都会被测试抓住。审查程序为模拟程序，不代表真实模型审查或发布验收。

**cm-ai 父运行可以换会话接着跑**

- 以前换一个聊天会话就打不开原来的运行：新会话的 ID 与存档中的不一致，报 `fingerprint_mismatch`；沿用旧 ID 又是在冒充旧会话。现在恢复时用 `--original-host-context` 报上创建运行的会话，`--host-context` 仍填当前真实会话。原配置和已有记录一字不改。
- 新会话第一次签审查授权前，才在存档里追加一条 `host-joined` 记下自己。以后再换会话，之前签过的授权仍然认；只打开看状态不写这一条，创建会话回来也不写。审查员必须独立于创建会话、已登记的接手会话和当前会话，当过审查员的会话不能回来接管运行。
- 最多登记 16 个接手会话，新参数只能用于恢复，创建运行时传它会被拒绝。该父运行改动只涉及当前会话接管的 cm-ai 主任务；QA-fix 子运行的后续修复见上条。旧 protected 保护模式兼容入口和 batch 多任务批次保持原行为。
- 先写会失败的测试再修，验证 A 创建、B 审查、C 恢复，只读打开不添记录、旧记录字节不变，以及历史审查员不能被登记为接手会话；故意删掉这道检查也会被测试抓到。测试使用临时目录和模拟审查程序，不代表真实模型审查或发布验收。

**换两次会话，cm-fix 的运行就打不开了**

- 0.16.1 修的跨会话恢复只认两个会话：创建运行的那个和当前这个。会话 A 建的运行，换到会话 B 做过一次审查，B 签的授权留在存档里；再换到会话 C 时，C 不认识 B，整条运行报 `runner_grant` 打不开。实测复现。
- 现在新会话第一次签审查授权前，先在存档里追加一条 `fix-host-joined-N` 记下自己。以后任何会话打开，都把创建会话、记下的接手会话和当前会话一起当宿主，B 签的授权照常认；审查员必须独立于其中每一个，当过审查员的会话不能回来当宿主。
- 只打开看状态不写这一条；创建会话回来也不写。已有记录一字不动，接手会话最多记 16 个。
- 限制：0.16.1、0.16.2 期间已经换会话签过授权的旧运行没有这条记录，只能由当时签授权的会话继续。
- 先写会失败的测试再修；四种故意改坏（不记、不读、打开就记、把创建会话也记上）都会被测试抓到。本机 4 条真实 cm-fix 存档的副本上，新旧代码打开结果完全一致，存档字节不变。
- 修复旧运行换会话补审后打不开的问题；接手记录必须早于所签授权，不能事后补记就算有效。

**补上 A 类六条从没被走过的分支**

- `auto_fix: never`：`policies.auto_fix` 三个取值模板里都写给用户看，但 `never` 从来没有测试走过。新增 `cm-ai-qa-fix-policy.test.mjs` 钉死三取值各自的去向（堵死 / 要人点头 / 派发），并覆盖「QA 轮次耗尽压过策略」这条优先级——只测一两个取值时最容易写反的地方。
- 视觉证据 `kind: video`：帮助里写着截图和录屏都行，但现有用例只造过截图，一个文件头常量都没出现过。新增 `cm-fix-visual-carrier.test.mjs`，验的不是「字符串能不能过」，而是真正值钱的那条：**载体的声明必须和文件的实际字节双向对上**——声明成录屏却塞张图、或者反过来，都必须被拒，否则「视觉证据」就只是个能随便填的字段。
- cm-check `configured`：可选能力有 `configured`、`degraded`、`unknown` 三种状态，但原有测试全填 `degraded`，`configured` 从没被走过。`cm-check-host.test.mjs` 现在钉死三态及混合报告的顺序、状态和原因都原样保留；可选状态不能改变核心判定，也不能掩盖核心失败；非法状态、空白原因、缺项、重复或多余 id 都会阻断，并返回准确的原因码。故意删掉 `configured`、让可选状态改写核心结果或漏掉原因校验，测试都会红；这次让审计缺口从 49 项降到 48 项。
- cm-test 中断步骤恢复：`snapshot`、`evaluation` 是中断后允许重做的两种纯读步骤，先前两份清单误写成了用户模式，现已纠正。`scripts/cm-test-session.test.mjs` 用真实日志重开运行，钉住纯读步骤重做、其它步骤不重做、command/host 只接纳身份匹配的原结果回执，以及七种已完成步骤直接回放；清单标为已补，并给待定的本地动作恢复设计补上这个已有先例。
- cm-refactor 规则三态：新增 `scripts/cm-refactor-gaps.test.mjs`，用已有假宿主和真实批次流程验证三种裁决、混合裁决、逐文件覆盖、隔离通道和准确拒绝码；报告保留全部裁决，裁决后的手册实际传到下一次生成请求。相同输出可以没有裁决。
- cm-refactor 四个停机码：真实恢复时改动范围内或范围外文件、在原子写入落盘间隙插入第三份内容、拒绝认领结果不明的调用，都必须保留外来字节、停机且不写批次失败报告。普通语法检查失败则恢复原文、写报告并成功重试。复用夹具移到 `scripts/fixtures/cm-refactor.mjs`，无需原生 Codex 沙箱。
- 六条都做了**变异验证**：故意把被测代码改坏（去掉 `never` 分支、调换优先级、放宽字节校验、漏判 MP4 文件头；让 command 重做、禁止 evaluation 或 snapshot 重做、漏验回执请求摘要；本次放行第四种裁决、漏验理由或差异覆盖、逐个移除其余三个停机码、让普通失败直接停机），确认测试会红。补测试时这一步不能省——新写的测试本来就该过，过了不代表验到了点上。
- 变异验证的边界：`refactor_unknown_effect` 列表成员是冗余防线，`unknown()` 已覆盖所有公开触发路径。单独移除该成员不改变停机行为，这个变异存活，不宣称被测试捕获；保留真实恢复测试验证结果不明时停机。
- `audit-untested-enums.mjs` 自查：加上上文 cm-fix 升级出口的补测，缺覆盖累计从 51 项降到 38 项、分布在 40 处；本次从 45 项、42 处降下，cm-refactor 三态和四码已离开缺口列表，A 类七条清单全部完成。

**cm-fix 有了随仓库发布的驾驭员，不用再临场手搓中间人**

- 新增 `cm-refactor-drive.mjs` 与 `cm-test-drive.mjs`：按宿主代码推导反问，先验人工答案、作用域及恢复绑定；重构判官与测试声明命令仍由宿主实跑，浏览器执行及未知原命令回执不接受静态答案。两份真实宿主测试覆盖状态、恢复、拒绝与变异。
- 新增 `scripts/cm-ai-batch-drive.mjs`：按批次宿主的真实路由预检每个任务的人工答案、scope 与真实检查命令，支持单步推进、同会话恢复及只读状态。QA 浏览器/逻辑、验证预检和 bootstrap 验证缺少可信执行 runner 时在启动前拒绝；受保护检查仍由宿主执行。
- cm-fix-host 是服务端，靠标准输入一行一行喂 JSON；而 AI 助手每次只能执行一条命令、抱不住长活进程，于是每个用它的人——包括模型自己——都得先临时写一个中间人。一次真实实跑里的两次配错（走查模块对不上、任务编号重名）根源都在这儿：没有现成的护栏。
- 新增 `scripts/cm-fix-drive.mjs`：`--plan PLAN.json <operation>`，一次只做一步——开宿主、发指令、从答案目录按种类代答反问、打结果、关进程。恢复时学习记录自动复用存档里已记的那份（一个必踩的坑）。
- 最要紧的护栏：按「这一步会反问什么」**先查齐答案文件再发指令**。宿主一步做了一半就永远卡住，缺答案硬发会把整轮做死；现在停在发指令之前，一个运行都不留。建运行前另做体检：任务编号是否已被审查结论占住、引用的文件是否存在、受保护模式下有没有沙箱跑不了的 tsx/vitest 类命令。
- 它绝不替你编诊断、测试或修复内容，缺哪份就说清要写哪个文件；`--allow-*` 原样传给宿主，不另造一套词，不新增任何权限面。
- 拿真宿主验过：开一轮、走两步、答对反问、缺答案时停在发指令之前且不留半个运行、换会话恢复时自动接上学习记录。`js-host.md` 改为指向驾驭员。

**「重名当场拦下」把任务编号变成了一次性的，现在改成让路但不删**

- 0.16.2 里加的建运行时重名检查（#110）一刀切：名字被占就永久不能再用，而 `cancel` 只标记状态、证据文件照样留着，也没有任何释放的操作。一个半路死掉的运行就能把编号永久烧掉，用户只能手工删文件——那正是这套东西一直不让人做的事。这是上一版自己造出来的摩擦。
- 现在照 host-handoff 早已定下的同一条规则办：**审查结论（`-r{n}.md`、`-cause-r{n}.md`）是别人签过字的证据，绝不挪动，名字就此占住**；红灯输出和交接文件只是半路死掉的运行留下的，归档到 `.reviews/.superseded/` 让路，字节原样保留、带内容哈希戳。先整体判定再动手：只要有一份审查结论在，什么都不挪。
- 新增共享的 `supersedeWorkflowFile`（link-then-unlink，崩在中间也不丢字节），cm-fix 不再长出第二套崩溃安全模式。
- 真实项目上确认：两个带审查结论的编号仍被拦，`.reviews` 一个文件没动。

## 0.16.2 — 2026-09-21

**把「文档写了但没测」这个形状本身盘了一遍**

- 这一轮修掉的四个缺陷是同一个形状：文档承诺了某个选项，代码里也有那条分支，但从来没有测试走过它——交付方式三选一只测了一种、METRICS 列名「语义升级」只测了新名字、跨会话恢复一条都没有、iOS 软链接没测过。它们不是被写坏的，是没人走过。
- 新增 `scripts/audit-untested-enums.mjs`：扫 runtime 里 `['a','b'].includes(x)` 形式的枚举，逐个对照测试有没有碰过。当前结果 51 个取值缺覆盖、分布在 49 处。
- 新增 `docs/untested-branches.md`：把这 51 个人工分成三类——A 类是用户能配的选项或文档明确承诺的行为（`auto_fix: never`、视觉证据 `video`、诊断 `design_change` 升级出口、cm-check 三态、cm-test 两种模式、cm-refactor 规则三态与四个失败码），每条写清「补测试前要先弄清什么」；B 类是内部协议分支；C 类是噪声，明说不要为它们写测试。
- 文档同时写明这份清单的局限：只认一种写法所以 51 是下限；「测试提到过某字符串」不等于那条分支被真正走到；以及最要紧的一条——那四个缺陷是跑真实项目跑出来的，不是读代码读出来的。

**走查配置和诊断对不上，要等独立审查做完才发现**

- 走查声明的模块必须和诊断结论一一对上，这条规矩一直有，但只在走查那一步才查——而走查在独立审查之后。配错了的一轮，要先把复现、编写测试、修复、回归、外部审查全跑完，才在最后一步被拦下，整轮白跑。实测踩过一次。
- 判定抽成独立函数，诊断一落盘就问一次，对不上当场停在 `walkthrough_configuration_mismatch`。
- 只在「刚诊断完、还没往下走」时改阶段：已经跑过头的存量记录保持原样。这个判断放在记录回放**之后**——回放循环会拿当前阶段去校验下一条记录，插在循环里会让存量记录直接打不开（实现过程中真踩了一次，靠存量运行验出来的）。

**任务编号重名要等跑到红灯测试才炸，而且那一轮直接报废**

- 证据文件名由任务 slug 和轮次拼出来，跟 runId 无关，所以两次运行用同一个 taskId 就会写同一批文件。这个冲突原本要等红灯测试去发布输出时才撞上 `review_file_conflict`——那时 intent 已经登记，运行留在 `unknown` 且不可重派，等于前面复现、诊断、编写测试全部白跑。实测踩过一次。
- 名字在建运行时就全都算得出来。现在 `--mode create` 会先检查这批名字有没有被占，占了就报 `fix_evidence_name_taken`，发生在任何记录落盘之前。只列这次配置真的会写的文件，不拦根本不会发生的冲突；只在 create 时检查，恢复已有运行不受影响。

**出了错查不出是哪一步拦的**

- runtime 里有 1032 种不同的失败码（光 cm-fix 就 204 种），但同一个码常常在好几处抛出——`fix_closeout_unavailable` 就有四处——而对端只看得到一句笼统的 `host_request_failed`。真实的码虽然写到了 stderr，却不说位置。实测代价：为了定位一次收尾失败，只能把整个宿主脚本复制一份、自己包一层打印调用栈才找到，普通使用者做不到这个。
- 现在 `need()` 在造错误时就把现场记成一个普通字段，诊断行多出 `origin`，例如 `{"diagnostic":"host_request_failed","operation":"finish","code":"fix_finish_authorization_required","origin":"cm-fix/execution.mjs:1003"}`。
- 两条边界没动：位置按 runtime 根取相对路径，不会带出绝对路径和用户名；只报这套 runtime 里的帧，不报调用方文件，更不会输出 `error.message`。读取方式也仍然是「只看自有数据属性」——`stack` 是访问器属性，只在 `need` 内部对刚造出来的原生 Error 上读，绝不去碰外来对象的访问器。
- 协议回复、日志、运行状态、任何摘要都逐字节不变：`origin` 只走 stderr。

**CI 只跑了 118 份自动检查里的 15 份**

- 其余 103 份从来没在 CI 跑过，包括 cm-fix 的整个持久化核心。本轮修的四个缺陷，本地跑全套都能暴露，CI 一个都发现不了。根因是那份逐个点名的清单：新写的检查不会自动进去，攒着攒着就成了这样。
- 改成通配符跑全部，清单不会再漂。Node 从 22 提到 24.14——JS 流程本来就要求 24.14+，跑 22 属于测一个没人声明支持的配置，核心那批还会被平台闸门当场挡掉。
- 有 7 份进不了 CI：它们要启动 Codex 沙箱，而沙箱在 Linux 上靠 bubblewrap 建网络隔离，GitHub 托管机器不给这个权限（`bwrap: loopback: Failed RTM_NEWADDR`），跟登录无关、装上 codex 也没用。这 7 份改由 `cm-release-smoke.sh` 在发版时覆盖——那个脚本本来就要求本机装了 codex 和 byz。于是**每个 PR 覆盖 111 份，每次发版覆盖满 118 份**，没有看不见的缺口。
- CI 里加了一句断言：排除名单必须正好是这 7 份。想往里加东西就得改那句，改动会出现在评审里。
- 上线前用一条一次性流水线在 Linux + Node 24.14 上实跑了 4 次，111 份每次全绿、耗时稳定在 4 分上下，确认不是把随机红灯搬进 CI。

**改过 METRICS 列名之后，所有存量表都被硬拒且没有迁移路径**

- `N4-review.md` 把这次改名写成「保持原列顺序，将原『Codex拦截』语义升级为『独立审查拦截』」——同一列换个名字。但代码是整行精确匹配表头，于是任何改名前就存在的 `METRICS.md` 都过不去：cm-fix 收尾报 `metrics_table_invalid`，cm-refactor 报 `refactor_metrics_format`，两条收尾路径一起停，且没有任何升级办法。
- 现在两处共用同一份表头定义，认得出这张表用过的所有列名。新文件仍写当前列名，**绝不改写用户文件里已有的表头**；真正不是这张表的文件照旧拒绝。
- 真实项目上验证：一张 `Codex拦截` 的旧表，收尾成功追加新行、表头原样保留、运行走到 completed。

## 0.16.1 — 2026-09-21

**cm-fix 收尾只认 delivery:diff，把 cm-init 自己生成的默认配置锁在门外**

- `finish` 原本硬要求 `policies.delivery === 'diff'`，否则 `fix_delivery_authorization_required`。而 `cm-init` 给任何带 remote 的仓库生成的就是 `draft-mr`，于是这类项目的 JS 运行**永远走不到收尾**，档案、METRICS、`task_done` 一样都写不成。这条限制原本写在 `js-host.md` 里，但它和同一套体系的其它约定互相矛盾：`runtime/logging.md` 把 Git 交付定义成独立的 `delivery` 事件（`commit`/`push`/`pull_request`），cm-ai 在任何交付模式下都照常完成任务，cm-fix 自己的 SKILL 第 7 步也明写 branch/draft-mr 怎么提交。
- 更要命的是它无法绕开：`findConfig` 只读项目根，而项目根在业务快照范围内，所以临时把 delivery 改成 `diff` 这个动作本身就改了快照，阶段立刻从 `closeout_required` 掉成 `learning_writeback_evidence_required`，收尾照样过不去；改回来又变回 `draft-mr`。两头堵，唯一出路是重跑整轮。
- 现在 finish 接受任一合法取值，只要求它在写完成记录期间**不变**（`fix_delivery_changed`）。写的还是档案/METRICS/`task_done`，**依然不执行 Git**：branch/draft-mr 的提交、推送、开 MR 仍由执行者单独完成并记 `delivery` 事件。

**受保护模式跑不了任何需要本地 IPC 的测试命令（无代码修复，只补文档）**

- 受保护模式的沙箱 `network={enabled=false}`，而这个开关同时管着 unix domain socket 的 `listen`。于是 `tsx` CLI（启动时在 TMPDIR 建 `<pid>.pipe`）、`vitest` 之类一律 `Error: listen EPERM`；写 TMPDIR 文件本身不受影响。实测确认过两者的差别。
- **没有安全的代码修法**：放开它就等于给所有受保护测试命令放开整个外网，那是比问题本身更大的倒退。`js-host.md` 改为写清这条约束、症状、以及可用的替代写法（把 `npm test` 串联的 `tsx xxx.test.ts` 换成逐文件 `node --import .../tsx/dist/loader.mjs <file>`，覆盖面不变），并说明沙箱内失败为什么只留 `host check exited N`（原始输出有意不进证据）以及怎么手工重放看真实原因。

**cm-fix 两处让修复跑不下去的阻断**

- **换一个会话就再也打不开自己的运行**。`--mode resume` 一直存在，但 durable 配置里存着创建这次运行的会话 ID，指纹又是整份配置的摘要，于是新会话换上自己的 `--host-context` 必然 `fingerprint_mismatch`，而沿用旧 ID 又正是文档禁止的冒用旧会话——两条路都堵死，跨会话恢复实际上从来没能用过。现在把「这次运行绑定的宿主」和「此刻在跑的会话」分开：durable 配置与旧记录一字不改，恢复时用 `--original-host-context` 报上原会话，`--host-context` 仍是当前真实会话。两个身份都算宿主，授权凭据两者皆可、重放旧凭据不受影响，reviewer 必须同时独立于两者；改动 durable 宿主仍然 `fingerprint_mismatch` 关闭。
- **iOS 项目连基线快照都拍不成**。CocoaPods 装出来的 `ios/Pods/` 是几千个头文件软链接，而快照对软链接一律 `unsupported_file` 失败关闭，于是任何做过 `pod install` 的仓库在编写测试这一步直接阻断（实测 6179 个软链接）。现在把它按 `node_modules` 那样当依赖目录跳过，但**光看名字不算**：目录名要是 `pods`，里面要有 `Manifest.lock` 和 `Pods.xcodeproj`，旁边还要有 `Podfile.lock`，四样齐全才认。业务代码里自己写的 `pods/` 目录照常入快照、其中的软链接照常拒绝。软链接保护一行未放宽，Pods 内的文件也不能选进 scope 或 requirements。

  判定本身就是安全边界——被它认下的目录会**无声**离开评审视野，所以遍历侧和 scope 守卫必须问同一个函数。初版这两处口径不一致：遍历侧不看名字、只探测生成物，于是在任意目录（例如 `src/`）放一个空 `Manifest.lock` 加一个空 `Pods.xcodeproj/`，整棵目录连同**已声明进 scope 的文件**一起被静默跳过，且不报任何错。现已统一为同一个判定，并补了三条回归测试钉住「两处必须一致」。

## 0.16.0 — 2026-09-20

一次真实实跑把 cm-ai 从「跑不完」推到「跑得完」，本版是那趟跑出来的修复与新增。

**不修就跑不下去的三处**

- **交接文件撞名不再让任务永久卡死**。交接文件 `{feature}-{任务}-a{轮次}-handoff.json` 以不覆盖方式发布，于是一个在审查前就死掉的运行留下的文件，会让同一任务此后**每一次**重跑都崩在 `linkSync` 上，且异常被塌缩成 `execution_error`，既不知道原因也没有恢复路径。实跑中同一任务连续 5 个批次全挂于此，换批次号无效。现按「这份旧交接有没有被审查用过」分流：字节相同视为已发布；无回执指名则归档到 `.reviews/.superseded/` 让路；有回执指名则绝不覆盖，返回 `handoff_exists`。判据只认回执 front matter 内的 `handoff:` 行，按 front matter 边界解析而非固定行数，读取侧不依赖写入侧字段顺序；读不懂一律按「已消费」失败关闭，绝不因解析不了就去动审查证据。
- **独立审查超时可单独配置**。审查进程超时此前写死 60 秒，唯一能调大它的入口是受保护模式，而那个模式又禁止开发者跑命令——两者互斥，等于没有出路。实测三次真实审查耗时 69 / 104 / 174 秒，在此前的实现下全都会被掐断、重试一次仍超时、任务直接判死。`--review-config` 新增可选 `timeoutMs`（1–3600000 毫秒，默认仍 60000），与受保护模式解耦，且不进入已授权配置的摘要，所以 `review_transport_timeout` 之后可以在恢复时调大再续跑。
- **QA 单用例应答窗口可调，批次也能只重跑被阻断的 QA**。`qa.timeoutMs` 上限此前写死 60000，而一轮真实浏览器走查按分钟计，必然超时判 BLOCKED 拖垮整轮；上限提到 3600000，默认不变。批次宿主补上 `--rerun-unknown-qa` / `--rerun-blocked-qa`，此前批次里一条用例超时之后唯一补救是重做整个任务、连已 approved 的审查也要重跑，而任务已被勾完成又过不了准入，等于没有补救路径。

**新增：交付前验证闸门（可选 `--verification-precheck`）**

- 实测一个 feature 的耗时里末任务占 79%、返工率 80%（其他任务 4%），而打回理由**全部**是「证据没满足任务书里早就写明的验证要求」，没有一条是代码缺陷。启用后，宿主在收齐任务自身检查之后、**构建审查包与派发独立审查之前**，把该任务的验证要求原文与检查结果交给对端逐条核对；任一条不满足即停在 `blocked / verification_precheck_failed`，`pendingAction` 为 `resume`，走与 `developer_result_invalid` 相同的重做通道，**不消耗审查轮次**。
- **只能拦，不能放**：通过闸门不产生审查回执、不授予批准、不替代后续的完整独立审查，定位与 `prd_self_check` 一致。任务没写验证要求时自动让路。不启用该开关时流程逐字节不变。
- 一条如实说明的限制：验证要求在 `tasks.md` 里是自由文本，**JS 无法机械证明对端把每一条都枚举到了**。机械强制的只有回答形状、每条必须给出非空证据落点、全部满足才放行。形状合规不等于语义覆盖。

**故障可诊断**

- 失败码不在白名单内时会塌缩成 `execution_error`，此前原始错误既不进日志也不进 stderr 也不进状态。上面那个 EEXIST 当初是临时加打印才看到的，普通使用者做不到。现在塌缩的同时往宿主 stderr 输出一行结构化诊断，只含 `code`/`syscall`/`path`/`dest` 具名字段，**绝不输出 `error.message`**。宿主请求被遮蔽成 `host_request_failed` 时同样如此，并带上是哪个 `operation`。现有脱敏边界一行未动：协议回复逐字节不变，诊断只走 stderr，不进日志镜像、不进运行状态、不进任何摘要或 digest。

**规则与文档**

- cm-ai N3 要求交付前把任务的 `verification` 当清单逐条核对，写清每一项的证据落点，对不上就报阻塞而不是交付。
- 文档补齐：批次启动与推进的四项前置条件；cm-prd 宿主的调用契约与 split 章节标题要求（五处坑连同「字段集严格、多给字段同样被拒」这一共同根源）；`pendingAction: reconcile` 出现时该做什么（含两个来源、各自的恢复路径，以及三种不管用的做法）。

## 0.15.5 — 2026-09-20

- **安装时按实际状态询问是否开启更新播报**。安装器结尾此前无条件打印「自动更新器未自动启用」：对已启用的人这句是错的（实测 hook 已配、后台更新器也在跑，仍这么说），对未启用的人它读着像普通说明而不像少配了一步——两边都没起作用，这才是装了包的人收不到新版提示的真实原因，不是缺功能。现改为检测后再说话：已配好则确认一句；未配好且交互安装则询问一次，答 y 才写入；`--yes` 与非交互安装不写任何东西，改为打印确切的 hook 条目；此前拒绝过则不再打扰（记在 `~/.cm-workflow/announce-hook-declined`，删掉该文件并重装即可重新被问）。
- 只添加播报 hook，**绝不添加后台升级器**——告知有新版与让工具自我替换是两个决定。`settings.json` 属于用户：无法解析时原文件逐字节不动、写入走临时文件加原子改名、已存在则不重复添加。安装器此前从不碰该文件，本次开的口子范围写死，只加这一条 hook 且只在明确同意时。
- 注意：只开播报而不开后台升级器时没有东西会产生播报内容（`cm-announce.sh` 只消费 `cm-update.sh` 留下的文件），两者需一起开启才有效果，详见 `docs/installation.md`。

## 0.15.4 — 2026-09-20

- **`cm-check --quick`：约 12 秒收口的快速检查**。完整检查耗时拆分为查更新 0.7 秒、机械检查 12.4 秒，其余几乎全在八组语义检查上——那不是程序在跑，而是执行者逐个文件读取并取证行号，且控制器强制八组齐全、passed 必须附真实行号，慢是设计换来的。日常「装没装对、版本对不对」这类问题机械检查已能回答，`--quick` 让更新与机械检查照常跑、机械通过后直接收口，不进语义检查。结论为 `MECHANICAL_ONLY` 而**不是 PASSED**：八组一组没读，不得据此宣称安装完整无断链；结果带 `semanticChecked:false` 与 `reason:quick_mode_semantic_not_run`。机械失败仍照常报 FAILED/BLOCKED，快速模式不跳过任何失败。默认不带该参数时行为不变。

## 0.15.3 — 2026-09-20

- **cm-check 在 Claude 安装上可以拿到 PASSED**：第 1 组查 `.codex-plugin/plugin.json`、第 7 组查根 `VERSION` 与根 `README.md`，三者都是 Codex 插件模式的产物，claude-compat 安装本就不该有（`install.sh` 只写 `templates/cm-VERSION`）。此前这两组只能判 blocked，健康的 Claude 安装永远停在 BLOCKED。现按安装模式分流：控制器推出 `installMode` 并下发给语义检查，语义结果新增 `not_applicable` 状态，仅第 1、7 组可用且必须写明缺的是哪个产物、为何本模式不该有；汇总时它既不算失败也不算阻塞。本模式确实拥有的产物证据不足仍记 blocked，不得借它放行。
- **修复 cm-check 报告的版本号在 Claude 安装上恒为 null**：结果中的 `version` 此前固定读根 `VERSION`，而 claude-compat 安装没有该文件。现按模式读取，claude-compat 读 `templates/cm-VERSION`。报告另附 `installMode`。

## 0.15.2 — 2026-09-19

- **同 feature 并行开发现在可用**：`parallel` 字段此前只存在于代码，没有任何文档或 Skill 提过它，而执行规则要求「不发明字段」，功能因此从入口够不着。现补齐 `docs/js-workflow-control.md` 的「并行组」一节（字段形状、五条约束与失败码、工作树与分支命名、串行合并、QA 延后、review 前置），并在 `cm-ai` N1 增加提议规则：只对 scope 全为新建文件、互不重叠、无依赖路径的同 feature 任务成组，组不起来就串行，不为组而组。
- **并行提速的真实范围**：当前会话模式下成员的开发请求逐个应答，重叠的是 runner 推进与各自的独立审查进程，不是写代码本身。文档与规则均写明这一点，避免按成倍提速预期使用。
- **修复链式依赖被并发执行**：并行组的依赖校验此前只看直接边，`A → B → C` 时 `{A, C}` 会被放行并发开发。两者在各自工作树基于 `HEAD` 创建、看不到彼此产物，而组内 scope 本就不重叠，Git 也不会报冲突，错误不会暴露。改为按依赖图的传递闭包判定。
- **修复保留的 WIP 分支挡住后续批次**（**升级注意**）：成员被阻断时会有意保留分支作为证据，但分支名不含 `batchId`，那个名字被永久占住，同一任务的任何后续批次都在 `git worktree add -b` 处报 `batch_worktree_failed`，换 `batchId` 也绕不开，只能手工删分支。分支名改为 `cm/{batchId 前 8 字符}/{feature}/{taskId}`。**升级前创建、尚未收口的并行批次恢复时会报 `batch_worktree_mismatch`，请用旧版本收尾该批次**；`parallel` 此前无文档，预期不存在这类在途批次。
- **QA 环境补齐桌面与后端/CLI/库形态**：`kind`/`carrier` 此前只有 web/app/miniprogram，而 cm-prd 与用户确认的交付形态包含桌面与多端，并另行承认「纯本地工具、库等无部署形态的项目」。这些项目要开 QA 只能谎报 `web`+`browser`，而该字段会进 QA 执行配置并随每个用例请求下发。新增 `desktop`→`app-window`、`service`→`cli`/`http-api`、`library`→`none`。同一张取值表原本在四个模块里逐字重复，现抽为 `runtime/js/cm-ai/qa-environment.mjs` 单一来源，并修掉四份拷贝共有的隐患：`kind` 传 `__proto__` 时解析到 `Object.prototype` 而抛 TypeError，现用 `Object.hasOwn` 判否。
- **METRICS 增加「执行方式」列**：记录该任务是串行执行还是并行组成员（`并行:组内{N}个`），否则无法区分耗时变化来自并行还是任务本身。看板模板同步适配列位。
- **文档补两处契约坑**：`preflight --review-model` 必须是 CLI 实际写进请求体的完整模型 id，别名会以 `model_matches:false` 判失败而回执不给期望值；`check` 回复的 `result` 就是检查数组本身，不套 `{status,value}`，用错会以 `invalid_input` 停在 `reconcile` 且重试无效。

## 0.15.1 — 2026-09-19

- **cm-security 接入统一运行日志合同**：`cm-security` 此前从未引用 `runtime/logging.md`，cm-check 第 3 组「共用运行日志与统一 writer」因此判 failed。该断链自 0.12.0 引入安全扫描时就存在——同一个 commit 把 `cm-security` 写进了检查清单，却没给新建的 Skill 加上引用。现补齐引用，并明确落盘时机：范围与安全边界确认后写 `run_start`，`--finalize` 返回终态后写 `run_done`，只记录扫描范围、结论词、发现数量、覆盖率与报告路径；工具原始输出、密钥原文、源码片段与 findings 正文不进日志，`BLOCKED` 同样收尾。这是文本约定补齐，扫描逻辑与报告门禁未变，只读边界不变。无 specs 目录时仍按既有设计只写全局镜像。
- **版本标志按安装模式解析**：`cm-check-update.mjs` 此前只看根目录有没有 `VERSION` 文件就判定「这是源码仓库」。Claude 兼容安装的根目录就是 `~/.claude`，那里任何与 CM 无关的 `VERSION` 都会顶替 `templates/cm-VERSION` 成为当前版本；若其版本号更高，降级守卫会抛错并让整个 cm-check 返回 `blocked`。现改为优先读当前 runtime 自己的标志，仅在该标志缺失时（源码仓库与 tarball 没有 `templates/cm-VERSION`）才回退到根 `VERSION`，Codex 路径行为不变。同时对齐 `runtime/project-context.md` 的工作流根校验——`install.sh` 只写 `templates/cm-VERSION`，从不往安装目录写根 `VERSION`，原先要求根 `VERSION` 的规则在所有 claude-compat 安装上都无法满足。

## 0.15.0 — 2026-09-19

- 浏览器验收能力改为启动时断言：开启 QA，且规格含阻塞 browser 用例或被 `policies.tests` 选中的 browser 用例时，`cm-ai-host` 与 `cm-ai-batch-host` 必须显式给 `--browser-qa available|unavailable`；因任务未完成而延后的适用用例也计入。`unavailable` 以 `browser_capability_unavailable` 拒绝启动，错误 JSON 仍只输出 code；使用者需换到具备浏览器能力的会话，或调整验收范围并重新审批规格。不命中时给该参数报 `invalid_arguments`。这是**声明不是探测**，声明不持久化，创建与恢复均须重新声明。
- **配置兼容性变更**：共享配置加载器现在要求 `policies.tests` 含 `browser` 时，`roles.browser_qa.adapter` 必须为 `browser`。此前可用的显式 `browser_qa: {adapter: local, model: none, source: local}` 配合默认测试策略（包含 browser），升级后会被拒绝，影响所有使用共享配置加载器的入口。需要浏览器 QA 的使用者应将适配器改为 `browser`（保留 `model: none, source: local`），或删除该角色覆盖以继承默认 browser 角色；不需要浏览器 QA 时，应将 `policies.tests` 显式设为如 `[logic, commands]`。省略整个 browser_qa 角色仍可继承默认值；移除策略中的 browser 不会排除阻塞 browser 用例，相关验收范围仍需明确处理。
- 统一 `.cm-specs-status` 读写：cm-prd 使用共享原子 writer；cm-ai 新增 `--approve --approval-response`，只放行指定准入原因，禁止 `--yes` 写审批位，写后重跑准入。审批沿用原 `summaryDigest`（缺失为 null），兼容读取旧文件并在下次写入去掉 `via` 等自由字段；cm-idea 明确保持在 specs 上游。

- `cm-security` 新增报告门禁 `--finalize --scan ... --review ...`：严格校验逐路径复核输入，机械补齐漏报、重验复核窗口漂移并保留扫描窗口证据，统一在项目外生成报告并给出四种结论之一。无发现且覆盖为 FULL 仍是 `REVIEWED_PARTIAL`，不存在「干净」的结论词；模型不能传入 `result`、`coverage` 或 `aiReview`。报告原样保留通过校验的分析结论与修复建议，stdout 只含摘要字段。
- 批次首次推进要求 Git 主工作区干净（含未跟踪文件）；否则以 `batch_main_dirty` 列出脏文件，在任务启动及创建工作树之前阻断。串行任务在 `start_next_task` 交接前自动提交，`batch_handoff.task_commit` 记录 SHA（无改动为 null）。并行组先合并 ready 成员，再保存终态成员 WIP、移除工作树并保留分支；通过 `batch_member_blocked` 日志恢复一次性的 generation 2 串行降级。
- 开发者结构化输出模式（Codex 两个 schema 与 Claude 提案 schema）新增可空 `reason`，`validateClaudeProposal` 同步放行该键：此前模式为 `additionalProperties:false` 且不含该键，CLI 派发下模型无法给出 blocked 原因，落盘功能在真实路径上等同未生效。`validateDeveloperValue` 将任意 outcome 下的 `reason: null` 归一为缺省，`implemented` 带非空 reason 仍按 `invalid_result` 拒绝。
- 开发结果 `blocked` 的可选 `reason` 以 `blockedReason` 持久化到开发调用记录：字符串 trim 后非空、至多 1000 UTF-8 字节，禁止 NUL 及除换行、制表符外的 C0 控制字符。终态仍为 `failed/result:null`，入口仍为 `blocked/failed`，重试与结果摘要语义不变；并行成员日志及 WIP 提交正文保留原因。旧日志无该字段照常回放；含新字段的日志会被旧运行时按未知键拒绝，不做版本协商。契约拆分明确告知调用方与实现方：抛错占位体是预期状态，不应等待或据此 blocked。
- 批次入口支持 `bundle.batch.parallel`：通过 `eligibleTasks` / 受信 `parallelSelection` 绑定并行准入与恢复，`parallelMember` 将成员 QA 延后到主分支串行末任务；成员工作树逐个执行审查预检并缓存，恢复时仅复用匹配凭证，预检失败则整组在 start 前阻断。新增 `--protected-config` 与逐任务 `--allow-provider-development` 授权，按成员工作树的项目声明派发 CLI 写码，并复用配置执行合并后检查。
- 执行存储锁改为运行级，不同 runId 可独立持锁；创建新运行时先探测旧布局锁，成功释放后继续，失败返回 `store_busy`，旧文件不迁移、不改写。
  恢复已有运行时，缺少本运行 `writer.sqlite` 返回 `store_layout_legacy`，须用旧版本收尾或退休该 runId；已有运行级锁则正常恢复。

## 0.14.0 — 2026-09-18

- 第 32 步：`cm-fix` 按描述未复现后先做最多 3 个场景或 15 分钟的单维度复现探索；档案新增复现尝试，JS 结果兼容旧记录并校验尝试结论，观测闭环要求至少一条尝试。
- 第 33 步：`cm-ai` 准入新增只读 `--print-run-definition` 直接生成运行定义（`--scope` 必填），`invalid_config` 点名多余/缺失字段、版本与文件类型，usage 与文档说明键集精确且参与摘要绑定。
- 第 34 步：受保护检查证据仅追加白名单测试计数摘要，同标签保留首次、最多 8 条、总长不超过 200 字符；不记录原始输出、凭证、路径或耗时，无计数及 unavailable 行为保持不变。

## 0.13.4 — 2026-09-18

- 第 29 步：`cm-init` 机械核验配置草稿的运行时五字段与所选预设一致，不符时阻断；从模板新建配置时按版本控制与 UI 模块事实裁剪 delivery/tests，并对不适用的默认策略给出非阻塞 warning，已有配置仍保留无关字段。
- 第 30 步：修复 `cm-ai` N6 中途 QA 提前执行未完成任务用例；延后用例写入日志与报告，命令按选中逻辑用例映射，显式恢复可用新 testRunId 绑定更新后的计划。`five_tasks_without_qa` 不再把已收尾 feature 缺失的 N6 历史行计为当前积压。第 30b 步：中途用例全部延后且无可调度命令时，以单条 `no-applicable-cases` BLOCKED 行显式报告空计划。
- 第 31 步：`cm-ai` 单任务 resume 新增 `--rerun-blocked-qa`，显式授权后仅将最新已完成的纯宿主/环境证据 BLOCKED 记为 superseded，并在最多三轮内全量重跑；保留原 QA 决策、开发/审查和任务状态，QA complete 同步 N6 状态镜像。

## 0.13.3 — 2026-09-17

- 第 28 步：`cm-runtime` 无参数在 TTY 中进入范围、工具/写码方、确认三问向导，复用安装器提问与既有 set/decision 日志，完成后显示有效配置；非 TTY 退出 2。安装器、向导与诊断共用系统语言中英文文案，机器字段不变；会话无参数按对话语言提问，文档与帮助同步。
- 测试隔离：7 个宿主测试文件把 `CM_WORKFLOW_HOME` / `CM_WORKFLOW_LOG_HOME` 指向进程专属临时目录，本机存在用户级 `runtimes.yml` 时不再有 6 个夹具误判为 declared-adapter。

## 0.13.2 — 2026-09-17

- 第 27 步：三种安装器交互声明单/双 AI 与谁写代码，原子保存用户级 `~/.cm-workflow/runtimes.yml`；非交互/yes 不写。配置按项目 > 用户 > 未声明解析并输出来源，cm-init 优先继承。新增独立 `cm-runtime show/set/set --user/unset --user` 与兼容别名，保留项目其他原文、复用诊断与决策日志，只影响新 run；同步检查、分发清单与文档。

## 0.13.1 — 2026-09-17

真项目 dogfood（`cm-init → cm-prd → cm-ai`，Codex 写码、Claude CLI 独立审查，首次跑到 `run_done`）暴露并修复的 11 处运行时缺陷，全部由真实事故触发：

- 修复 `cm-ai` N6 宿主请求可无限等待的问题：QA 请求按 workflow QA `timeoutMs`（默认 60 秒）独立超时并记 BLOCKED，迟到应答与旧发送失败不能污染下一请求；resume 可显式以 `--rerun-unknown-qa --allow-qa` 将无结果调用记为 abandoned，以新 testRunId 在原 qaRound 重跑，部分结果及清理欠账仍阻断。合成宿主确认同步应答本身正常；事故驱动的 JSONL 循环提前 return 会漏读同一数据块中的下一请求，宿主必须排空完整行。
- 第 26b 步放宽 `--rerun-unknown-qa`：无 complete、已记录结果全为 PASS 且无固定执行报告时可显式重跑，abandoned 新增 `partial_pass_cases`；全部用例仍重新执行，旧 PASS 仅作历史，FAIL/BLOCKED 与清理欠账继续阻断。

- 修复 `cm-ai` 已完成且原无 QA 的 run 无法补做强制 N6：resume 显式提供含 QA 的 `--workflow-config` 与 `--allow-qa` 后一次性追加不可变 `qa-attached` 和 `decision/qa_attach` 日志；保留原指纹及任务完成证据，绑定完整恢复配置，重复恢复去重，换配置或未完成附加拒绝。QA 仍走原宿主请求、执行、结果及收尾门禁，不重跑开发/审查。

- 修复 `cm-ai` 双根布局下开发进程与审查者看不到已批准任务及接口契约：开发请求和审查包新增绑定摘要的只读 `specification`（任务/验证要求、AC、设计摘录、相关用例、来源哈希）；复用审批 manifest，规格漂移以 `spec_drift` 阻断，设计超 64 KiB 标记截断。代码根 `requirements` 可选，旧包、journal 与 receipt 按原规则兼容，规格不进入可写 scope。补齐全量夹具的真实批准清单、运行时声明及模块复制依赖；旧 baseline 已记录的依赖文件保留完整内容与漂移检查，batch 开发/审查均验证规格传递。

- 修复 Claude 长审查被第 65 条 `thinking_tokens` 心跳误杀：审查上限放宽为 4096 条，保留 worker 总输出 1,000,000 字节及独立的 32 条通知限制；审查包新增绑定摘要的 `unchangedScope` 路径/哈希清单，允许其进入 examinedPaths 和 finding 路径但不要求改动，旧包与历史 receipt 按原规则验证。

- 修复 Claude 审查流将 `system/api_retry`、`hook_started`、`hook_response`、`commands_changed` 通知误判为失败：与 `rate_limit_event` 共用 32 条上限，经 `onNotice` 仅上报白名单摘要，不转发 hook/commands 正文或生成 observation；未知 system 子类型仍拒绝，重试后的失败结果仍按 `provider_failed` 处理。

- 修复 `cm-ai` 受保护当前会话审查误用 60 秒默认超时：Codex/Claude reviewer 共用配置的 `timeoutMs`；没有结果事件的审查传输超时可在同一 attempt 经新授权重派一次，第二次超时阻断。保留 observation/inspection，含结果的超时及旧 unknown 历史仍须 reconcile。

- 修复 `cm-ai` 受保护当前会话开发结果先落盘后校验的问题：本地 value/Learning 校验失败记录 `failed/invalid_result`，保留原原因并以 `developer_result_invalid` 阻断；修正后可沿同一 runId、同一 attempt resume。已应用提案仅在磁盘哈希一致时继续，冲突返回 `protected_edit_stale`；worker 异常与超时仍保留 unknown，不重置旧历史。

- 修复 `cm-ai` 准入被历史依赖行尾随句末标点阻断：依赖 ID 容忍末尾的 `。．.；;、` 与空白，内部非法字符仍拒绝；任务或依赖解析失败返回 feature、行号及最多 120 字符原文，并保留已解析 feature 状态，任务选择顺序不变。

- 修复 `cm-prd` 会话恢复错误码被宿主脱敏隐藏及 cancel 留下 active 导致的死锁：会话状态错误返回明确 blocked 原因，cancel 同时落盘终态并清空 active；resume 支持带证据的 abandon，回到操作前 checkpoint 后可重新发起，含 `prd_review` 的操作仍只能按原审查合同恢复。

- 修复 `cm-prd` 新增 feature 时，历史 specs 的任务/AC 运行期完成标记导致摘要回执门禁误报篡改：复用规格清单规范化规则并透传只读标记，其他内容、JSON、当前草稿处置与发布快照仍严格比对。摘要只裁决当前会话 feature，历史 feature 的旧版归档、缺失回执与已完成任务只登记为说明，完整规格哈希仍全部发布；无草稿从当前会话恢复范围，无法确定时明确阻断。

- 修复 `cm-ai` Codex 开发与审查子进程仍加载用户全部个人 skills 的问题：`--ignore-user-config`、`skills.config=[]`、`--disable plugins` 都拦不住 `<skills_instructions>` 目录注入，现在两条路径统一追加 `-c skills.include_instructions=false`。2026-09-16 空目录空操作提示词实测：input_tokens 18249 → 11706（−36%），“Skill descriptions were shortened to fit the skills context budget” 提示消失，模型不再看到 skill 工具；sandbox、审批与登录不受影响。实测无效的候选：`skills.config` 按根目录禁用、`skills.bundled.enabled=false`、`--enable skip_host_skill_discovery`（开发中特性，只多一条警告）、`--ignore-rules`、`--disable skill_search`；`skills.max_context_tokens=1` 虽降到 11813 但仍报预算超限提示，`=0` 直接拒绝启动。审查参数指纹已改变，旧凭证须重跑 preflight。已知剩余缺口：`~/.codex/AGENTS.md` 用户全局指令（本机 11.7KB）仍被注入，`project_doc_max_bytes=0`、`instructions`、`developer_instructions` 均不能移除（后者还会顶掉权限说明），唯一手段是隔离 `CODEX_HOME`，但登录态同样从该目录读取，复制或软链 auth.json 属凭据外放，本次不实施。

## 0.13.0 — 2026-09-17

- 修复 Claude 开发提案依赖自由文本 JSON 的问题：传入专用结构化输出 schema，配对内部 `StructuredOutput` 并优先读取 `structured_output`；无结构化结果时保留 JSON 回退，解析失败明确返回 `invalid_output_json`，严格校验成功/失败字面值与提案形状，原只读工具权限不变。

- 修复审查 finding 指向交接文件时被拒绝的问题：Codex 与 Claude 共享提示词明确允许包内 handoff 路径，保留 examinedPaths 精确匹配；非法 finding 路径、标识、严重度及形状分别返回具名错误码，便于诊断。

- 修复 Claude 回环预检将新版 CLI 的两次合规请求误判为失败：允许 1–2 次且逐条验证模型、传输与工具列表（空列表或唯一的 `StructuredOutput`），第三次请求或任一违规仍拒绝；结果新增 `message_requests_expected:'1-2'`，保留首次合规即停止、不转发模型与进程清理检查。

- 修复 Claude CLI 思考事件与审查结构化输出兼容：开发与审查流各容忍最多 64 条 `thinking_tokens`，思考块不推进状态；审查传入去除 `$schema`/`$id` 的结果 schema，配对 CLI 内部 `StructuredOutput`，最多容忍 16 次明确被拒的其他工具尝试，任何其他工具执行成功立即失败并终止进程组。开发流保留原只读工具与提案校验；审查参数指纹已改变，旧凭证须重跑 preflight。

- 修复 Claude CLI 回答前的 `rate_limit_event` 导致 `cm-ai` 开发与审查流失败的问题：同一会话终态前最多容忍 8 条，通过可选 `onNotice({kind:'rate_limit',info})` 仅上报标量字段；保持状态、观察事件及返回值形状不变，会话不匹配、超限和未知事件仍拒绝。

- 修复 Codex CLI 0.153.4 起 `cm-ai` 开发子进程禁用 `code_mode_host` 导致工具执行失败的问题：开发路径保留该工具通道，不启用 `code_mode` 特性；workspace-write、网络关闭和 specs 只读沙箱边界不变，审查路径仍禁用 `code_mode_host`，参数保持不变。

- 修复 `cm-ai` 开发与审查 worker 将 Codex CLI 0.153.4 启动提示误判为失败的问题：终态前最多容忍 8 条 `item.completed/error`，两类 worker 均通过独立 `onNotice` 回调上报 message 前 200 字符，回调异常不影响流程，保持事件流与返回值形状不变；超限或终态后提示仍拒绝，顶层 `error`、`turn.failed`、非零退出、缺结果与未知条目的失败判定保持不变。

- `cm-ai` protected host 按项目声明派发 coder/reviewer CLI，支持 Claude 只读文本提案经宿主校验后落盘；保留逐轮授权和跨家独立审查，实际启动进程后记录 `cli-dispatch`，审查凭证如实标注 `claude-cli`。2026-09-17 已完成真实模型双向验收各一次：Claude 宿主→Codex 写→Claude 审（`reviewer: claude-cli / independent: true / approved`）；Codex 宿主→Claude 只读提案→宿主沙箱落盘→Codex 审（`reviewer: codex-cli / independent: true / approved`）。均为单文件小任务，停在 N6 QA 待决；QA/N8 不在验收范围、仍未验收。

- 修复 `cm-init` 运行时声明无法进入宿主草稿的问题：可选 `selection.runtimes` 按预设追加配置目标，沿用已有文件名，核验声明并保护无关配置，接续原确认、审查与写入流程；直接导入 `scripts/cm-workflow-config.mjs` 复用唯一解析器（现有规则未禁止该方向，避免为下沉实现越过本次 cm-init 修改边界）。
- 新增运行时声明：`.cm-workflow.yml` 增加 `runtimes.available`（codex/claude/both），`cm-init` 首次初始化询问一次并按四个预设写入 coder/reviewer；配置校验拦截「单家声明却指向另一家」与「两家都有却写审同家」；`cm-check --project` 输出声明与本机 CLI 的对照及 coder/reviewer 的 `route_state`；`--failover` 改为声明优先、探测校验。声明不等于跨运行时派发。
- 新增双运行时容灾两层能力。`scripts/cm-failover.mjs` 只读推导 CM 断点（N3/N4/N5/N6）并生成另一端的续跑简报，不写 `tasks.md`、不标记完成、不授权发布；`cm-ai-host.mjs serve --failover` 在构造 execution 前按角色主从（developer 主 codex、reviewer 主 claude）探测选路，只决定起跑运行时，切换必播报，两端都不可解析时以 `no_runtime_available` 阻塞而非回退到不可用一端。不传 `--failover` 行为完全不变；与 `--protected-config` 互斥。边界与限制见 `docs/runtime-failover.md`。
- 修复自动更新器 `cm-update.sh` 硬编码 PATH 覆盖 launchd 环境变量的问题：改为保留继承的 PATH 再追加兜底目录，非 Homebrew 安装的 Node.js 不再导致定时更新一直报「Node.js 18+ 未找到」。

## 0.12.0 — 2026-09-16

- 新增 `cm-security`：默认扫描主分支差异与已跟踪未提交修改，支持 `--all`；结合业务地图复核安全问题，保留工具缺失与未覆盖范围；不自动修复、安装或上传。

- `cm-check` 默认查询 npm 稳定版，有新版就升级已管理的 CM 安装，再从升级后的目录检查；无需另加升级参数。
- 离线明确提示无法确认最新版；源码仓库和其他包管理器的安装保留原管理方式；底层机械检查保持只读，避免 CI 和安装流程递归升级。

## 0.11.0 — 2026-09-16

### 新增

- 直接运行 `cm-test`，自动分析当前分支相对 `main` / `master` 的已提交差异，列出受影响业务与重点回归场景。
- 影响分析后接续增量单测覆盖率检查，复用项目已有命令，支持 LCOV 和 Istanbul 报告；未配置工具时明确标记「未测得」。
- 支持 `cm-test，并补齐单测`，或看完报告后回复「补齐单测」，继续补测试、重跑并独立审查。
- 新增本更新日志，统一查看版本变化。

### 优化

- 开发需求和修复 bug 时，先核对业务地图与影响范围；地图缺失或失真时补充相关上下文，按需扩大分析范围。
- 需求与修复完成后增量更新相关业务地图，并在最终独立审查前定稿；文档同步覆盖同批次已完成任务。
- `cm-ai` / `cm-fix` 在最终独立审查前检查单测缺口，并在已授权范围内补测。

### 修复

- 检查已提交代码的覆盖率时，阻止未授权的未提交源码或测试混入执行。
- 缺失分支覆盖数据时保留缺口，不再把已测部分的百分比当作完整分支覆盖率。
- Codex 安装器同步分发更新日志，并检查安装后的内容一致性。
- 修复独立审查输出格式包含接口不支持字段而导致请求被拒绝的问题，本地审查路径校验保持严格。

> 覆盖率不代表业务验收通过；补单测不自动授权修改产品代码或安装测试框架。
> JS 修复流程的补测须在测试冻结前完成；当前 owner 无法合法写入覆盖率报告时，会明确报告接线受阻。

## 0.10.6

### 新增

- 增加工作流重复失败信号的只读分析工具，输出供人工判断的改进建议。
- 补齐 Pi/BYZ 与 Codex 发版面冒烟检查。

### 修复

- 修复安全扫描对已跟踪状态目录及部分私钥格式的漏检。
- 收紧重复失败分析工具的输入边界与证据校验。

## 维护方式

- 开发或修复完成后，将用户可感知的变化补到「未发布」，不逐条复制内部提交记录。
- 准备发版时，将对应条目移到目标版本标题下并标注计划日期；发布后核对实际日期，npm 状态以注册表为准。
- 本文件先补录 0.10.6 及之后的变化；更早版本和实现细节查阅 Git 提交历史。
