97f4b29661
build / build (push) Successful in 52s
补两处残余(本轮不推、不打真实 tag、不部署;全部用临时仓库与 spy 验):
1) version_in_commit 必须比"目标提交 CHANGELOG 的**顶部**已发布版本",不是"历史里出现过":
- 新增 top_version_in_commit(),version_in_commit() 直接比它;预检输出把该提交的顶部版本打出来
(诊断可审计:例如"顶部是 `## [0.3.0]`,不是 0.2.0"),阻断信息里也带上真实顶部版本。
- 顺带兜住"最新在最上面 / 版本只增不减"的排序要求。
- 负例(自检 + 夹具各一遍):顶部 0.3.0、历史里 0.2.0 ⇒ 候选 0.2.0 拒绝;候选 0.3.0 通过。
2) 按只读约定**禁用 --execute 自动发布路径**:
- 删掉写路径(打 tag / 推 tag / 建 Release 的代码整段移除),`--execute` / `--approve-version` /
`--approve-sha` 只保留参数兼容(不报 unknown argument),传了就**明确拒绝**(rc=2)。
- 拒绝发生在收集事实之前 ⇒ **零写操作、零 API 调用**;自检用 spy 断言拒绝路径
"一个子进程都没跑、一个 API 都没发",并扫描生产路径不许出现直接执行 git 写命令的行。
- 输出里也不再推荐自动路径;RELEASE_RUNBOOK 第 4 步改为由人执行。
验证:release_preflight --self-test 绿(就绪 20 分支 + 顶部/历史两态 + --execute 拒绝零写 +
源码写路径扫描);反向夹具 84/0(含 --execute rc=2 且跑完仓里 tag 不变);make verify 15/15 success。