方案评审循环(spec / plan 双轨)
主请求(编排者,唯一写权限) ⇄ codex CLI(只读评审者)交替推进,直到文档收敛。
取代「人肉复制粘贴」和「自己批自己」:评审者默认是 codex CLI(外部模型、独立上下文),每轮 resume 同一会话保持记忆;改稿永远由主请求完成。评审者绝不是本会话里的自己。
适用文档类型与入参解析
| 类型 | 性质 | 判据基准 | 越界/缺失判定 |
|---|---|---|---|
| spec | 需求与约束基线("必须满足什么") | 自身内部自洽 + 用户范围基线 | 实现细节缺失=plan 职责,不算 P0(标 owner=plan) |
| plan | 可执行实施计划("怎么做") | 其声明的 spec 基线版本 + 自身可执行性 | ①违反 spec 冻结契约=P0;②spec 未定义却自行拍板=P0/P1;③实现细节缺失=P1(属本类型职责) |
判定方法:文档头部若自称"需求与约束基线 / 不含实现方式"→ spec;若含任务步骤、文件路径、命令、执行顺序 → plan。
入参解析(用户只说「评审 <path>」时)
用户往往只给一句话 + 一个路径。按此表解析,不要为可自行判断的事反复追问:
| 用户输入 | 动作 |
|---|---|
「评审 <文件路径>」(单独) |
建议模式(默认):判定类型,调用 codex 单轮评审并给建议;完成后结束,不动文档、不续跑。 |
建议模式写入 review-state.json:doc、mode:"advisory"、round:0、codex_thread_id、reviewed_at、doc_fingerprint 与 size_baseline。 |
|
「评审并改进 / 按评审改 / 循环评审 <文件路径>」 |
循环模式:直接起轮次(见编排流程·循环模式),评审→改稿→评审→改稿直到收敛 |
| 「评审」+ 自然语言描述(无路径) | 按描述定位候选文件,列 2~3 个让用户一次选定 |
「评审 <目录>」 |
列出该目录候选文档(md 优先、按修改时间倒序),让用户指定 |
「评审 <代码文件>」(.py/.go/.ts/.js/.sql/.rs 等) |
不适用本技能 —— 明确告知这是代码评审请求,改走代码评审流程(已装 superpowers 走其 references/05-code-review.md,或 requesting-code-review),不要硬套 spec/plan 维度 |
| 路径不存在 / 读不到 | 直接报错,并列出同目录最接近的候选文件名,不要静默猜测或退化成"评审上一次那个" |
| 未声明类型且头部无自述 | 按内容判定;仍不确定 → 在评审 prompt 里同时给出 A/B 两套维度,让评审者自行归类并在 verdict_reason 说明 |
两种运行模式(核心,务必区分)
| 模式 | 触发 | 行为 | 改文档? | 续跑? |
|---|---|---|---|---|
| 建议模式(默认) | 用户单独说「评审 <path>」 | 调 codex 评审一轮,输出 P0/P1/P2 建议 + verdict + strongest_objection | 否(但落盘 review-state.json,mode=advisory) |
否(一轮即止) |
| 循环模式 | ①新建/写完方案后由主请求主动进入;②用户给建议、主请求改完方案后进入;③显式词「评审并改进/按评审改/循环评审」 | codex 评审 → 主请求按评审改稿 → codex resume 再审 → 再改 → 直到收敛(PASS / 连续 2 轮无新 P0·P1 / 达轮次上限(默认 10)) | 是(主请求改) | 是(review-state.json 续跑) |
原则:用户只说"评审"= 只给建议,绝不自动改文档、绝不自动续跑。完整循环只在方案生成/修订工作流中由主请求主动驱动,或用户显式要求"改进/迭代"时触发。
续跑优先原则(仅循环模式):循环模式的「评审 <path>」请求,先查 review-state.json。已 PASS 的文档不自动重跑 —— 重跑前必须让用户确认基线已变更(否则会重复消耗轮次)。
advisory 会话复用(建议模式 → 循环模式):循环第 1 轮前,若存在 mode=advisory 的 review-state.json,重算当前文档 SHA-256 并与 doc_fingerprint 对比。一致 → resume advisory 会话作为循环第 1 轮,不重跑评审、不消耗额外轮次,仅补齐 scope-snapshot.md 等循环模式特有产物;不一致 → 禁止复用,重开会话并注入「当前文档全文 + 旧评审结论(audit-r*.md
若存在,否则对话内 P0/P1 清单)」,轮次从 1 计。advisory resume 调用失败(包括超时或会话不存在)→ 按调用纪律第 4 条 停止并报告,不按本节规则自动重开。上述不一致导致的重开情形中,新会话 thread_id 建立后写回 review-state.json.codex_thread_id,旧 id 追加进 prev_thread_ids[],不得覆盖丢失;旧 advisory 会话保留不删。无 mode=advisory 字段的旧 state
按原「续跑优先原则」处理,保持向后兼容。
默认只读 + 改前确认:本技能会修改目标文档。首次对某文档起轮次时,用一句话说明"评审意见回来后我将按 P0/P1 改该文件(改前备份到评审工作区 .agent/plans/<doc-slug>/,不污染文档目录)",除非用户此前已明确授权自动修。
核心约束(必读)
| 约束 | 内容 |
|---|---|
| 单向编排 | 主请求是编排者,codex 是被调用的只读评审者。评审者不改稿,也不得反向派单 |
| 评审者只读 | codex 以 -s danger-full-access 运行(2026-10-12 用户拍板:-s read-only 沙箱在本机有自竞态故障,见 §4.8 第 8 条);只读靠三件套兜底——① prompt 明确禁止改稿,② jsonl 逐轮写操作审计(正则扫写盘类命令),③ 文档指纹(SHA-256)前后比对 |
| 修改权在主请求 | 所有文档改动由主请求在会话里做 |
| 上下文连续 | 首选 resume 同一 thread_id(换文档 / 换基线才开新会话);resume 已实测恢复可用(2026-10-12,alpha.17.2)。resume 失败 → 按调用纪律第 4 条 退出报用户,不做自动降级兜底(2026-10-12 用户拍板) |
| 基线版本锁定 | plan 评审时对照组=该 plan 声明依赖的 spec 版本,不得拿"最新 spec"去挑 plan;版本不符则先记「基线漂移」(P1),再决定是否同步 |
| 证据优于声称 | 引用问题必须带 章节号/行号 或原文关键短语,禁止「感觉不够严谨」类空泛意见 |
| 对抗性 | 每轮评审者必须给出「推翻这份文档的最强三条理由」;确实没有时显式声明 no_strong_objection |
| 范围扩展必须重评审 | PASS spec 因用户扩大范围而违反冻结契约时,不得直接据此写 plan;须先更新并重评审。 |
| 执行:①同步范围、术语、需求、硬约束、验收、风险、影响面、前置与回滚;②版本 +1,注明范围扩展重评审;③ resume 同一会话至收敛;④ plan 依赖新版本。 | |
| 会话 id 必报 | 每轮汇报与最终结束汇报都必须向用户给出 codex thread_id,并同步落盘 review-state.json.codex_thread_id(两处必须一致)—— 用户下次要接着评审时直接 resume 它,不重开会话 |
评审者优先级与 Claude 路由
评审者选择顺序
新评审循环开始时,按以下优先级选择评审者(顺序不可变):
- 当前循环 Codex override(
review-state.json.override.scope=current_cycle且未取消) - 持续 Codex override(
reviewer-routing.json.force_codex=true且未取消) - 安全外部同名 Claude 专职子评审员(
spec-reviewer/plan-reviewer,read_only=true且可调用) - Skill 内捆绑 Claude 专职子评审员(
agents/reviewers.json,通过--agents注入) - Codex CLI
记录 discovery.source 为 external、bundled 或 none。文档类型必须精确匹配:spec 只用 spec-reviewer,plan 只用 plan-reviewer。仅存在另一类型评审员或通用代理时按无匹配处理,直接走 Codex CLI。非用户事件不得携带 user_choice;携带时返回 stop_report,不发起评审调用。
Claude 调用与模型锁定
Claude 调用统一使用 references/run-claude-review.ps1,以原生 claude.exe 执行,限制 --tools Read,Grep,Glob 与 --strict-mcp-config。所有模型分辨率规则:
- 新循环:
reviewer-routing.json.claude_model→ agent 定义中的显式模型 → Claude CLI 默认 - 第一次成功调用后记录实际模型到
reviewer.model与reviewer.model_source;后续轮次锁定该模型 - 修改
claude_model只影响新循环,不影响本循环后续轮次 - 无法取得实际模型时记
unknown,按失败规则处理
Claude 会话续接使用 --resume <session_id>;同一循环内所有轮次使用同一会话。不同循环不复用旧循环会话。
失败与回退
一次 Claude 评审调用成功要求返回可解析 JSON、包含有效 verdict 且符合输出契约。启动失败、超时、非零退出、envelope 错误、结果解析失败或 schema 不合格均为一次调用失败。初始调用失败自动重试一次;重试仍失败后按编排模型分派:
| 编排模型 | 动作 |
|---|---|
| Codex | 弹出结构化选择:再发起一次 Claude 调用 / 改用 Codex CLI / 停止本轮 |
| 非 Codex | 自动改用 Codex CLI 完成本轮,并记录 fallback |
| 无法判定 | 暂停并询问用户,不静默选择 |
用户选择"改由 Codex"后,本循环后续所有轮次保持 Codex;Codex 始终以新会话开始,不得复用 Claude session ID。用户选择"停止本轮"后状态标记 interrupted,不报 PASS,不改文档。
reviewer-routing.json 配置
位于 skill 根目录 reviewer-routing.json(即 $env:USERPROFILE\.agents\skills\spec-review-loop\reviewer-routing.json),不存在时使用默认值:
{
"force_codex": false,
"timeout_seconds": 240,
"claude_model": null,
"set_at": null,
"cancelled_at": null
}
force_codex:true期间即使发现匹配 Claude 代理也调用 Codextimeout_seconds:Claude CLI 超时(1–2147483,默认 240)claude_model:非空时覆盖 agent 定义与 CLI 默认- 无效 JSON 或无效值返回
configuration_error,不发起评审调用,不修改 review state - 持续 override 取消时设置
cancelled_at;取消后新循环恢复发现流程
状态字段
review-state.json 新增字段(兼容旧状态):
reviewer.provider:claude或codex-clireviewer.type:spec-reviewer/plan-reviewer/codex-clireviewer.session_id:Claude 会话 ID(可续接时记录);Codex 继续写codex_thread_idreviewer.model与reviewer.model_source:Claude 实际模型与来源reviewer.selected_at:选择时间discovery.source/discovery.matched/discovery.candidates/discovery.callablehistory[]每轮:provider、reviewer_type、session_id(若有)、context_mode(resume/context_pass/none)fallback:from/to/stage/reason/auto_retry_count/decision_source/time/failed_session_id(Claude 失败后切 Codex 时记录)override:scope/set_at/cancelled_at
旧状态若只有 codex_thread_id,按 Codex CLI 循环续跑,不重新发现 Claude 评审员,不迁移。切换到 Codex 后,Claude session ID 只保留在 history[] 和 fallback 记录中。
.pi junction 不变式
$env:USERPROFILE\.pi\agent\skills\spec-review-loop 是指向 $env:USERPROFILE\.agents\skills\spec-review-loop 的目录 junction,不是独立副本。不得在 .pi\references 或 .pi\agents 维护内容副本。
评审者调用:codex CLI
4.1 能力与默认模型
| 能力 | codex CLI(默认主路径) | 同模型子代理(仅限无 codex 的环境且用户显式授权) |
|---|---|---|
| 起一轮评审 | codex exec ... - < prompt.txt(见 §4.5 模板) |
Agent(subagent_type=general-purpose) |
| 续跑同一评审者 | codex exec resume <thread_id> ...,id 存 review-state.json.codex_thread_id |
Agent(resume=<agent_id>),id 存 review-state.json.agent_id |
| 只读保证 | 三件套,见 §4.3 | prompt 约束 + 环境只读子代理 |
| 结构化输出 | --output-schema <schema.json>(强制 JSON,直接可解析;2026-10-12 拍板解禁:alpha.2 的 node.exe 持锁故障在 0.162.0-alpha.17.2 三组探针复测未复现;编排者保留 json.loads 解析失败 → 该轮不计的兜底) |
靠 prompt 约束 |
| 会话内消息续轮 | 不适用(用 resume) |
⚠ 不可用:SendMessage 报 Not in a team |
| 独立性 | 高(外部模型、独立上下文;与改稿者同一套权重和先验易产生确认偏差) | 低(同模型自评,必须在 verdict_reason 标注) |
| 成本 / 延迟 | 较高(启动 40–60s 为 0.155 时代实测;2026-10-10 评审轮单轮全程 133–413s) | 低 |
| 默认模型 | -m gpt-6-luna(2026-09-24 用户指定;本机 ~/.codex/config.toml 的 model 值若不同,以模板显式写的为准)。该值随账号/套餐可能变化,每次调用都显式带上,不依赖配置默认;换模型只改本行;全文其余模板/示例一律写 <REVIEW_MODEL> 占位符,执行时替换为本行实际值 |
评审者 = codex CLI(外部模型),每轮都走,不允许静默换成同模型。
4.2 调用纪律(五条,2026-09-30 用户确认)
| # | 纪律 | 内容 |
|---|---|---|
| 1 | 禁止 WSL | codex 一律在 Windows 原生环境调用(Git Bash / PowerShell 直接跑 exe),不得借道 WSL |
| 2 | prompt 走 stdin | codex exec ... - < review-prompt.txt 是官方正规用法(帮助文档明示),v1.2.0 起多轮实测可靠、不受 Windows 32KB 参数限制。prompt 只写路径 + 评审维度 + 输出要求,保持精简 |
| 3 | 让 codex 自己读文件 | 评审材料不内联进 prompt——文档路径、spec 基线路径、代码位置用路径引用,由 codex 在 -C 项目根下自己读,resume 模式下保留上轮上下文。禁止把文档全文、源码 grep 结果塞进 prompt |
| 4 | 失败即停即报 | 单轮总尝试≤3,首调计1。CLI启动前准备错(缺二进制、参数/配置、prompt/schema)修正后可重跑。启动后仅退出码0且JSONL空、JSON解析失败、schema不符或缺verdict可重跑;记录命令、退出码、宿主错误流(空文件无ERROR行)。非零退出(含参数解析/鉴权/限流/版本)立即停止汇报。网络刷新超时至多重试1次,计入总数。 |
| 5 | 执行器适配 | 只有 PowerShell 执行器的会话禁止照抄 Bash 模板(炸点:< 输入重定向、VAR=x cmd 前缀、[ -x ]、$(ls)),一律用§4.5的 pwsh 版模板 |
4.3 只读三件套(2026-10-12 实测)
评审者使用 -s danger-full-access。只读由三件套保证:prompt 明确禁止改稿;逐轮审计 JSONL 写操作;调用前后比对目标文档 SHA-256。其他文件另以 git status --short 核查。
4.4 代理策略(2026-09-24 用户确认)
codex 调用固定走 http://127.0.0.1:10808,不使用可能指向不可达 sandbox-cli.exe 的会话代理。先按各执行器下列命令探测;非 200 按调用纪律第 4 条停止。config.toml 的 proxy 键无效。
Bash 会话先 export 四个代理变量,再探测(curl.exe,与 pwsh 同一判据):
export HTTP_PROXY=http://127.0.0.1:10808 HTTPS_PROXY=http://127.0.0.1:10808
export http_proxy=http://127.0.0.1:10808 https_proxy=http://127.0.0.1:10808
PROXY=http://127.0.0.1:10808
code=$(curl.exe -s -o /dev/null -w '%{http_code}' --max-time 8 -x "$PROXY" https://chatgpt.com/) || { echo "代理探测失败: curl exit $?" >&2; exit 1; }
if [ "$code" != 200 ]; then echo "代理探测失败: HTTP $code" >&2; exit 1; fi
4.5 调用模板:按执行器选择
- 按当前执行器选择模板:PowerShell 会话用 pwsh,Bash 会话用 Bash;不确定时先探测,不猜。pwsh 不支持
<或VAR=value cmd,PATH 上的bash可能是 WSL;需要 Bash 时写死 Git Bash 路径。
以下模板仅用于 pwsh 7.4+。先设置本轮变量,再按轮次执行对应模板。
PowerShell 模板共用以下环境保存/恢复函数;每个模板调用 Set-ReviewProxy,并在 finally 调 Restore-ReviewProxy:
$ReviewProxyNames = @('HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy')
function Set-ReviewProxy([string]$Proxy) {
$script:ReviewProxyPrevious = @{}
foreach ($name in $ReviewProxyNames) { $script:ReviewProxyPrevious[$name] = [Environment]::GetEnvironmentVariable($name, 'Process') }
$env:HTTP_PROXY = $Proxy; $env:HTTPS_PROXY = $Proxy
$env:http_proxy = $Proxy; $env:https_proxy = $Proxy
}
function Restore-ReviewProxy {
foreach ($name in $ReviewProxyNames) {
if ($null -eq $script:ReviewProxyPrevious[$name]) { Remove-Item "Env:$name" -ErrorAction SilentlyContinue }
else { Set-Item -Path "Env:$name" -Value $script:ReviewProxyPrevious[$name] }
}
}
PowerShell:变量与二进制定位(三保险)
$Model = 'gpt-6-luna' # 换模型只改本行
$Project = 'D:\path\to\project-root'
$OutDir = 'D:\path\to\.agent\plans\<doc-slug>'
$PromptFile = Join-Path $OutDir 'review-prompt.txt'
$CodexBin = Join-Path $HOME '.local\bin\codex.cmd'
if (-not (Test-Path $CodexBin)) {
$p = (Select-String (Join-Path $HOME '.codex\config.toml') 'CODEX_CLI_PATH' | Select-Object -First 1).Line -replace "^[^']*'", '' -replace "'\s*$", ''
if ($p -and (Test-Path $p)) { $CodexBin = $p }
}
if (-not (Test-Path $CodexBin)) {
$d = Get-ChildItem (Join-Path $env:LOCALAPPDATA 'OpenAI\Codex\bin') -Directory | Sort-Object LastWriteTime -Descending | Where-Object { Test-Path (Join-Path $_.FullName 'codex.exe') } | Select-Object -First 1
if ($d) { $CodexBin = Join-Path $d.FullName 'codex.exe' }
}
if (-not (Test-Path $CodexBin)) { throw '[codex] 未找到可用的 codex CLI' }
& $CodexBin --version
PowerShell 代理探测
$proxy = 'http://127.0.0.1:10808'
$code = & curl.exe -s -o NUL -w '%{http_code}' --max-time 8 -x $proxy https://chatgpt.com/
if ($code -ne '200') { throw "代理 $proxy 探测失败: $code(按调用纪律第 4 条 停止)" }
必须调用 curl.exe(真实 curl),不要调用 pwsh 的 curl 别名(即 Invoke-WebRequest,没有 -w 参数)。
PowerShell 首轮(新会话)
$proxy = 'http://127.0.0.1:10808'
$JsonlPath = Join-Path $OutDir 'codex-r1.jsonl'
try {
Set-ReviewProxy $proxy
Get-Content -Raw -Encoding UTF8 $PromptFile | & $CodexBin exec -m $Model -s danger-full-access --skip-git-repo-check `
-C $Project --json --output-schema (Join-Path $OutDir 'review-schema.json') - 2>&1 | Out-File -Encoding utf8NoBOM $JsonlPath
$codexExitCode = $LASTEXITCODE
if ($codexExitCode -ne 0) { throw "codex exec 失败,退出码: $codexExitCode" }
if ((Get-Item $JsonlPath).Length -eq 0) { throw 'codex-r1.jsonl 为 0 字节;按调用纪律第 4 条记录命令与宿主错误流后,方可重跑同轮' }
} finally {
Restore-ReviewProxy
}
# thread_id 取首行:{"type":"thread.started","thread_id":"..."}
PowerShell 后续轮(resume)
$ThreadId = (Get-Content (Join-Path $OutDir 'review-state.json') -Raw | ConvertFrom-Json).codex_thread_id
$PromptFile = Join-Path $OutDir 'review-prompt-rN.txt'
$proxy = 'http://127.0.0.1:10808'
$JsonlPath = Join-Path $OutDir 'codex-rN.jsonl'
Push-Location $Project # resume 继承首轮 cwd;显式切到同一项目根
try {
Set-ReviewProxy $proxy
Get-Content -Raw -Encoding UTF8 $PromptFile | & $CodexBin exec resume $ThreadId -m $Model --skip-git-repo-check `
--json --output-schema (Join-Path $OutDir 'review-schema.json') - 2>&1 | Out-File -Encoding utf8NoBOM $JsonlPath
$codexExitCode = $LASTEXITCODE
if ($codexExitCode -ne 0) { throw "codex exec resume 失败,退出码: $codexExitCode" }
if ((Get-Item $JsonlPath).Length -eq 0) { throw 'codex-rN.jsonl 为 0 字节;按调用纪律第 4 条记录命令与宿主错误流后,方可重跑同轮' }
} finally {
try {
Restore-ReviewProxy
} finally {
Pop-Location
}
}
resume 在同一工作目录执行,不带 -C、-s 或 --color。
Bash 二进制定位(三保险)
本机并存多份 codex CLI,且桌面应用那份不在 PATH(路径含哈希、随应用更新变化)。按下述顺序解析,每一步都必须落到真实可执行的文件上:
# 仅限 Bash 执行器;PowerShell 会话使用 §4.5 中的 pwsh 模板。
CODEX=""
# ① 稳定入口 shim
[ -x "$HOME/.local/bin/codex" ] && CODEX="$HOME/.local/bin/codex"
# ② config.toml 记录的桌面应用 CLI 路径
if [ -z "$CODEX" ]; then
p=$(grep -m1 '^[[:space:]]*CODEX_CLI_PATH' "$HOME/.codex/config.toml" 2>/dev/null \
| sed "s/^[^']*'//; s/'[[:space:]]*$//" | tr -d '\r' | tr '\\' '/')
[ -n "$p" ] && [ -x "$p" ] && CODEX="$p"
fi
# ③ 按修改时间取最新的哈希目录(务必过滤「确实含 codex.exe」的目录)
if [ -z "$CODEX" ]; then
for d in $(ls -1dt "$LOCALAPPDATA/OpenAI/Codex/bin"/*/ 2>/dev/null); do
[ -x "${d}codex.exe" ] && CODEX="${d}codex.exe" && break
done
fi
[ -n "$CODEX" ] || { echo "[codex] 未找到可用的 codex CLI" >&2; exit 127; }
"$CODEX" --version # 自检:版本过低会驱动不了新模型(见下)
| 事项 | 说明 |
|---|---|
| CLI 定位 | 禁止用 ls ... | head -1;按时间倒序,并过滤真实含 codex.exe 的目录。 |
| 版本 | "$CODEX" --version 可用;若提示模型要求更新客户端,按 §4.8 第 5 条处理。 |
| 稳定入口与历史坑 | 优先本机 ~/.local/bin/codex shim;standalone PATH 案例见 references/troubleshooting.md#历史坑。 |
| #### Bash 首轮 | |
| 先执行 §4.4 的 Bash export 块。 |
"$CODEX" exec -m <REVIEW_MODEL> -s danger-full-access --skip-git-repo-check \
-C <被测代码/项目根> --json \
--output-schema <out-dir>/review-schema.json \
- < <out-dir>/review-prompt.txt > <out-dir>/codex-r1.jsonl 2>&1
- 从首行读取
thread_id;最终结论取最后一个带text的item.completed项。
Bash 后续轮(resume)
复用当前 shell 中 §4.4 设置的代理变量。
"$CODEX" exec resume <thread_id> -m <REVIEW_MODEL> --skip-git-repo-check \
--json --output-schema <out-dir>/review-schema.json \
- < <out-dir>/review-prompt-rN.txt > <out-dir>/codex-rN.jsonl 2>&1
resume 恢复同一会话上下文;失败按调用纪律第 4 条停止。
4.6 参数坑
| 坑 | 事实 |
|---|---|
-s |
只在首轮 exec 写(现行值 -s danger-full-access)。resume 不接受 -s;带上会报 unexpected argument '-s' found,且 JSONL 可为 0 字节。按纪律 4 依据退出码分类,不从空文件读 ERROR 或重跑。 |
--color |
exec 接受、resume 不接受,带上直接退出码 2(unexpected argument '--color') |
-C |
exec 接受、resume 不接受(2026-10-10 实测退出码 2);resume 继承首轮 cwd,会话按 cwd 过滤,必须在同一工作目录执行 |
--ephemeral |
不持久化会话 → 与 resume 互斥,绝不要加(会话落盘是 jsonl 审计的依据) |
--output-schema |
恢复使用(2026-10-12 用户拍板解禁,见 §4.8 第 8 条);strict 对象须 additionalProperties:false 且列全 required;最终 JSON 解析失败按调用纪律第 4 条处理 |
sandbox_mode 默认 |
首轮 exec 显式设置,避免配置漂移(2026-10-12 拍板) |
参数坑背景与实测速查见 references/troubleshooting.md#参数坑;完整事实以本节表格为准。
4.7 审计与状态恢复
每轮自检:记录退出码、JSONL 字节和尝试数(首调计1,共≤3)。非零退出按纪律4停;退出码0但JSONL空、解析失败、schema不符或缺verdict时,记命令、退出码及宿主错误流(空文件无ERROR行)后可重跑。成功提取thread_id/verdict并落audit-rN.md。
无效轮次:verdict 依据是无法读取文件/环境故障(§4.8 第 7 条)时不计轮次;关键是评审者确实读到材料。
写操作审计(三件套第 2 项,2026-10-12 沉淀):每轮解析 codex-rN.jsonl 全部命令事件,按正则扫写操作嫌疑:Set-Content|Out-File|Add-Content|Remove-Item|New-Item|rm -|mv |cp |copy |>>|echo .*>|tee|sed -i|del |rmdir|touch;命中 → 立即停下报用户。判败标记:helper_unknown_error(见失败处理第 8 条)。resume
会话审计盲区(2026-10-12 实测):resume 路径的 JSONL 中命令事件可能只有 item.started(aggregated_output 为空、状态 in_progress、无 completed)——命令真实执行但过程记录缺失,故审计须同时扫 started 事件的 command 字段做写操作比对(命令原文在 started 里就有,只是没有退出码和输出);该盲区下 git status + 文档指纹兜底权重更高。
其他文件变更兜底(三件套之三):每轮审计后在项目根跑 git status --short,确认除评审工作区(.agent/)外无任何变更;有 → 立即报告。非 git 项目跳过此检查,仅依赖 jsonl 审计 + 文档指纹。
4.8 失败处理:报错停下,不降级
CLI 已启动后退出码非零(未登录 / 限流 / 超时 / 版本不符 / 参数解析失败等)时,立即停止并向用户报错,不得静默改用同模型子代理。网络刷新请求超时仅可按第 3 项重试一次,计入调用纪律第 4 条的单轮总尝试上限 3 次。排查顺序:
"$CODEX" --version:核对模型所需的 CLI 版本。- 登录态:鉴权失败即停下报告。
codex-rN.jsonl的ERROR:模型刷新超时最多重试一次;再次失败即停,不重开或降级。config.toml告警:核对proxy、disable_response_storage等未识别项是否影响链路。requires a newer version of Codex:换最新版 CLI 或更新桌面应用;不要反复重试。- 代理探测:按 §4.4 检查 10808;非 200 即停。
- os error 231(命令宿主管道握手失败,服务端下发 code_mode_only 时触发):按调用纪律第 4 条 停止并报告,不自行改模型缓存。详见 references/failure-archive.md#os-error-231。
- helper_unknown_error(-s read-only 沙箱自竞态):评审一律 -s danger-full-access + 三件套兜底,禁用 -s read-only。详见 references/failure-archive.md#helper_unknown_error。
停下后在 review-state.json 记 stop_reason=codex_unavailable + 具体错误,并把上述排查结果反馈用户;不要自行改成子代理继续跑。
- 评审者不可用矩阵(2026-10-23~26 plan 24 轮实战沉淀;全部限定在现行纪律内,不推翻「非零退出即停 / resume 失败即停 / 网络刷新超时最多重试一次」任一条):
| 故障 | 判定 | 处置(均不改变现行纪律) |
|---|---|---|
| codex 限额(usage limit,含恢复时点提示) | 明确拒绝,非瞬态 | 停止循环并报用户(调用纪律第 4 条);记录服务端提示的恢复时点;是否在恢复后 resume 原 thread 由用户决定——用户说「继续」即构成授权,恢复评审前先探测模型可用性 + 文档指纹未变 + 任务书就绪 |
| 网络故障(代理出口/直连均不通,curl 探测 000) | 连通性中断 | 停止并报用户(调用纪律第 4 条);重试一次仅适用于「失败处理」第 3 条的模型刷新超时,连通性探测失败不自动重试——恢复后由用户决定是否续跑 |
| 子代理(获授权代跑时)连续 2 次无输出失败 | 无输出 ≠ 瞬态,是路由不可用 | 停止代跑并报用户;间隔较久(≥1 天)可再探测一次,仍失败即弃用该路由;每次代跑视为一次性任务,交付后不可复用(实测 send_message 报 not found) |
| 恢复评审(任何中断后) | — | 先探测(模型可用性 + 文档指纹未变 + 任务书就绪),再按用户指示 resume 原 thread(上下文连续)或重开;任务书缺失时按消息内嵌 fallback(已实测可靠) |
4.9 评审输出 JSON Schema
{
"type": "object",
"properties": {
"verdict": { "type": "string", "enum": ["PASS", "FAIL"] },
"p0": { "type": "array", "items": { "type": "object", "properties": { "loc": {"type":"string"}, "issue": {"type":"string"}, "fix": {"type":"string"}, "evidence": {"type":"string"} }, "required": ["loc","issue","fix","evidence"], "additionalProperties": false } },
"p1": { "type": "array", "items": { "type": "object", "properties": { "loc": {"type":"string"}, "issue": {"type":"string"}, "fix": {"type":"string"}, "evidence": {"type":"string"} }, "required": ["loc","issue","fix","evidence"], "additionalProperties": false } },
"p2": { "type": "array", "items": { "type": "object", "properties": { "loc": {"type":"string"}, "issue": {"type":"string"}, "fix": {"type":"string"}, "evidence": {"type":"string"} }, "required": ["loc","issue","fix","evidence"], "additionalProperties": false } },
"spec_conflicts": { "type": "array", "items": { "type": "object", "properties": { "plan_loc": {"type":"string"}, "spec_clause": {"type":"string"}, "kind": {"type":"string","enum":["违反","未覆盖","扩展"]}, "fix": {"type":"string"} }, "required": ["plan_loc","spec_clause","kind","fix"], "additionalProperties": false } },
"out_of_scope": { "type": "array", "items": { "type": "string" } },
"unresolved_ack": { "type": "array", "items": { "type": "string" } },
"already_declared": { "type": "array", "items": { "type": "string" } },
"strongest_objection": { "type": "array", "items": { "type": "string" } },
"verdict_reason": { "type": "string" }
},
"required": ["verdict","p0","p1","p2","spec_conflicts","out_of_scope","unresolved_ack","already_declared","strongest_objection","verdict_reason"],
"additionalProperties": false
}
数组字段没有内容时返回 [],不要省略字段。
评审 prompt 模板
通用段落(两类共用)
codex 是独立进程。按调用纪律第 2、3 条,prompt 只给路径、维度与输出要求,材料由 codex 自己读取:
你是只读评审者。禁止修改、创建、删除任何文件,禁止一切写盘操作(当前运行无沙箱物理限制,只读靠你自觉遵守,调用方会做事后写操作审计与文档指纹比对)。
目标文档(唯一真源):<绝对路径>
冻结范围基线:<out-dir>/scope-snapshot.md
未决意见清单:<out-dir>/unresolved.md(可能不存在)
项目约束:<可选,如 AGENTS.md 路径>
(可选增强)若当前环境已装 `superpowers` 技能,可加载该技能目录下的 `references/05-code-review.md` 作为补充纪律;未装则跳过,直接按下方维度评审——本模板已自包含,不依赖任何外部技能,不要为缺少 superpowers 而中断评审。
A 类:spec 评审维度
1. 内部一致性——章节/术语/数字互相矛盾之处
2. 可执行性——步骤能否照做;命令、路径、参数、文件名是否齐全且真实存在
3. 范围边界——是否超出冻结范围;超出项标 in_scope=false
4. 风险与回滚——失败模式是否覆盖,回滚是否可执行
5. 验收标准——是否给出可验证断言(不是「测试通过」这种空话)
6. 遗漏——该有而没有的章节/字段/边界情况
B 类:plan 评审维度(第 1 条最重要,不得省)
引用 spec 条款时须给 §号 + 原文关键短语。
1. 【最高优先】spec 符合度——逐条核对 plan 的每个决定是否与 <spec 路径>(版本 <spec rev>)的冻结契约一致。
必须分三类列出:
- 违反:plan 的决定与 spec 冻结契约冲突
- 未覆盖:spec 要求了,plan 没有对应编排
- 擅自扩展:spec 未定义,plan 自行拍板(须回写 spec 或降为待定项)
2. 未闭合契约误用——plan 是否把 spec 声明为「未闭合 / 待实测」的契约当作已闭合使用
3. 可执行性——每个任务步骤能否直接照做:文件路径、命令、参数、前置条件、预期输出是否齐全
4. 任务粒度与顺序——是否小到可单次完成;依赖是否显式;有无循环依赖;可否并行
5. 验证步骤——每个任务是否带验证方法(如何确认做对了),是否可自动断言
6. 回滚点——破坏性操作(改表、删数据、改配置、切换状态)是否有回滚/备份步骤
7. 范围——是否超出 spec 范围
8. 遗漏——spec 要求但 plan 未编排的部分
9. Spec 判据宽严 diff——把 plan 的每个验收判据/触发条件与 spec 冻结条文逐条对比;差异**按基线既有三分类映射**(不得把宽严差异都记「扩展」):
与冻结条文**冲突**(含"更严但无授权"——spec 允许的场景被无依据收紧)→ kind=违反;
spec **要求**了但 plan 没有对应编排 → kind=未覆盖;
spec **未定义**且 plan 自行拍板(更宽的触发/豁免/降级,或新增约束)→ kind=扩展;
扩展若属安全方向,可标注「待 spec 升版追认」,但**必须登记**——只看"plan 是否自洽"抓不到这类漂移,必须回到 spec 原文逐条对。
10. 关键命令实测级验证——对方案中的关键脚本/判据,做超出"通读"的实测验证:
`bash -n` 语法检查、纯内建替身推演(**按方案实际使用的外部命令/判据**构造函数替身模拟各分支——shell 方案常用 ps/test 作替身对象,其他类型方案按需选择,非固定要求)、配置 JSON 解析、
正则与测试函数名的匹配计数。纯"读"抓不到运行时缺陷(如恒假表达式、退出码被管道掩盖)。
输出格式(两类共用,plan 多一栏)
{"verdict":"FAIL","p0":[{"loc":"§1","issue":"...","fix":"...","evidence":"..."}],"p1":[],"p2":[],"spec_conflicts":[{"plan_loc":"§2","spec_clause":"§1 范围","kind":"违反","fix":"..."}],"out_of_scope":[],"unresolved_ack":[],"already_declared":[],"strongest_objection":["..."],"verdict_reason":"..."}
说明:p1/p2 元素同 p0;kind 取「违反/未覆盖/扩展」;空数组保持 []。
判定规则:p0 非空 → FAIL;p0 空且 p1 非空 → FAIL;p0/p1 均空 → PASS(机读判定不变)。
分级映射(引用 05-code-review.md L11-15,已装时):Critical(不符合规格/会炸生产/丢功能)→ P0;
Major(明显质量债/验收判据失真/不可执行)→ P1;Minor(风格/命名/文档一致性)→ P2。
未装 05-code-review 时按同语义自行分级并在 verdict_reason 声明所用口径。
p0 = 会导致实现出错或验收失效的阻断项;p1 = 应修但不阻断;p2 = 可选优化。
首轮任务书组装清单(循环模式第 1 轮按此组装;后续轮按需增删)
通用段落 + A/B 类维度之外,按方案特征追加专项指令——首轮就下发,不要等评审者自己发现
(实战:状态组合漏洞晚加 5 轮才被点名):
| 方案特征 | 必追加的专项指令 |
|---|---|
| 含状态机 / 清理流程 / 多门槛联动 | 状态组合穷举:先从方案(或其声明的 spec/设计)提取状态集合与判定规则——不预设互斥性、优先级、fail-closed 的答案,仅在方案自身声明了这些性质时核验其声明与实现一致;穷举各组合路径,核对「状态分类 → 判定 → 分流 → 证据保全」多处的一致性;对「方案未定义但实现暗含」的状态组合,标为遗漏 |
| 含回滚 / 分级恢复 | 回滚材料唯一源核对 + 回滚后状态与恢复守卫的联动 + 备份自身的完整性(自哈希/自覆盖类缺陷) |
| 含后台进程 / watchdog / 采样 | 进程生命周期核对:启动参数与 PID 记录、身份校验(唯一标识,非通用字符串)、退出路径、证据归档、遗留进程对后续任务的影响 |
| 含数学阈值 / 计数断言 | 阈值可达性验算(采样粒度 × 点数 vs 窗口时长的边界余量;判据在"最自然执行"下是否可达) |
| 含外部接口 / 环境依赖 | 可达性探针的时点覆盖(只在初始化时测一次不够——使用点前须复测)、依赖工具存在性检查 |
编排流程与产物布局
6.1 产物布局
<out-dir>/
├── scope-snapshot.md
├── audit-r1.md / audit-rN.md
├── unresolved.md
├── review-prompt.txt / review-prompt-rN.txt
├── codex-rN.jsonl(含 stderr)
├── review-schema.json
├── doc.rev<N>.bak.md
└── review-state.json
| 字段 | 必填 | 用途 |
|---|---|---|
doc |
必填 | 目标文档路径 |
doc_rev |
必填 | 目标文档版本 |
round |
必填 | 当前轮次 |
reviewer |
必填 | 评审者类型 |
model |
必填 | 评审模型 |
codex_thread_id |
必填 | 当前评审会话 ID |
last_p0p1_signature |
必填 | 最近 P0/P1 签名 |
no_new_p0p1_streak |
必填 | 连续无新 P0/P1 轮数 |
stop_reason |
必填 | 停止原因 |
history[] |
必填 | 历轮记录 |
mode |
必填 | advisory 或循环模式 |
reviewed_at |
必填 | 评审时间(ISO 8601) |
doc_fingerprint |
必填 | 评审对象 SHA-256 |
baseline_input_tokens |
条件必填 | 复用模式的输入 token 基线 |
size_baseline |
必填 | 初始体积基线 |
prev_thread_ids[] |
条件必填 | 重开会话时保存旧 ID |
doc_fingerprint_final |
条件必填 | PASS 后修正的终稿指纹 |
doc_fingerprint_note |
条件必填 | 两个指纹差异说明 |
regression_note |
可选 | 同类回归记录 |
orchestration_note |
可选 | 编排疏漏记录 |
表中必填性针对循环模式;建议模式仅需 doc/mode/round/codex_thread_id/reviewed_at/doc_fingerprint/size_baseline 七项。
PASS 后工作区处置:建议保留 audit-r*.md / review-state.json / unresolved.md;备份和临时评审材料可删除,是否删除由用户决定,不自动删。
改文档前先备份:备份文件必须放进评审工作区 <out-dir>/doc.rev<N>.bak.md(N=轮次,每轮改稿前复制一份当前版本)。
<out-dir> = .agent\plans\<doc-slug>($OutDir 已含)。
禁止把 .bak 落在正式文档所在目录;历史坑详见 references/troubleshooting.md#历史坑。
review-state.json 是断点续跑的唯一依据。会话上下文丢失后读它即可恢复,不要从第 1 轮重来。
unresolved.md 只登记三类:改不动(技术/成本限制)、等用户拍板、明确不采纳。一行一条:
- [P1] 章节 4.2 建议补 backfill 断点续传 —— 等用户拍板(成本待评估)
多会话并行写同一批产物(2026-09-24 实战):实施会话可能也在更新同一 spec/plan 的 unresolved.md(如实施中登记负责人确认)。改前必须重读最新文件再 Edit(撞 Edit 冲突就是并行写的信号);合并时保留两边记录、注明各自时间与来源会话,不要用旧内容覆盖新登记。
建议模式(单独评审,默认)
- 读文件头部判定类型(spec / plan)。
- 调 codex 评审一轮(见 §4.5模板),按 A/B 类维度输出 P0/P1/P2 建议 + verdict + strongest_objection。
- 落盘轻量
review-state.json:记录doc、mode:"advisory"、round:0、codex_thread_id、reviewed_at(ISO 8601,落盘时点)、doc_fingerprint(SHA-256)与size_baseline。 - 向用户汇报建议;结束,不改文档、不续跑。
- 若用户认可建议并要落地,转入下方循环模式,先按§3 核心约束·advisory 会话复用判定;指纹一致则直接复用评审结论,不消耗新一轮。
6.2 第 1 轮与循环启动
循环第 1 轮
0. 若存在 mode=advisory 的 review-state.json,先按「advisory 会话复用(建议模式 → 循环模式)」判定;一致时复用 advisory 会话并将其作为第 1 轮,不重跑评审、不消耗轮次。
1. 冻结范围基线:写 <out-dir>/scope-snapshot.md。
- spec:优先抽取 <!-- SCOPE:START -->...<!-- SCOPE:END --> 区块,没有则提取 §1 范围段 + 章节标题。
- plan:额外冻结「依赖的 spec 路径 + spec rev + 该 spec 的未闭合项清单」(plan 评审的对照锚点)。
2. 调 codex 起新会话评审:先把 <out-dir>/review-schema.json 与评审 prompt 落盘,执行后从 JSONL 首行取 thread_id 存进 review-state.json.codex_thread_id。
3. 读结论 → 落盘 audit-r1.md → 按 P0/P1 改文档 → 更新 unresolved.md。
4. 向用户汇报:本轮改了什么、还剩什么、下一步。
6.3 第 N+1 轮
- 优先
resume同一 codex 会话(codex exec resume <thread_id> ...),保留上下文、避免重复提已修意见。实测该方式下 codex 能回忆上轮读过哪些文件,不会重读盘。resume 已实测恢复可用(2026-10-12,alpha.17.2);调用失败 → 按调用纪律第 4 条 退出报用户,不自动降级(用户拍板)。
-thread_id取自review-state.json.codex_thread_id。出现以下任一信号时触发统一兜底:①thread_id丢失;② 单轮 JSONL token 计数事件的input_tokens> 基线轮的 3 倍(基线轮为复用时的 advisory 轮,否则为循环第 1 轮;基线值记入review-state.json.baseline_input_tokens);③ 结论明显退化(重复提已修意见、自相矛盾、verdict
依据与前轮冲突)。JSONL 无input_tokens字段或值为 0 时,token 倍率判据停用,不算故障,仅信号③仍可触发。
- 兜底时重开会话,注入「最近audit-rN.md摘要 +unresolved.md」;若unresolved.md不存在,注入「无未决意见」或跳过该文件,不得阻止切换。新会话thread_id写回review-state.json.codex_thread_id,旧 id 追加进prev_thread_ids[],不得覆盖丢失。
- 本节「重开会话」仅针对信号①②③;resume 命令本身调用失败仍按调用纪律第 4 条 停止,二者不冲突。
- 任何情况下都以review-state.json为断点续跑唯一依据;会话上下文丢失后读它恢复,不要从第 1 轮重来。 - 重复第 3~4 步,轮次 +1。
codex 调用参数选择
| 参数 | 用法 |
|---|---|
-m, --model |
显式写 <REVIEW_MODEL>(实际值见§4.1 模型行;不写则吃 config.toml 静默漂移) |
-C, --cd |
指向被测代码 / 项目根(评审 plan 时给项目根,不是文档所在目录) |
-s danger-full-access |
必带,显式写防配置漂移(read-only 沙箱有自竞态故障,失败处理第 8 条) |
--output-schema |
必带(强制 JSON 输出,2026-10-12 解禁,见参数坑表;schema 文件每轮起轮次前落盘到 <out-dir>/review-schema.json) |
--json |
必带,用于提取 thread_id 与事件流 |
--ephemeral |
禁用(与 resume 互斥;会话落盘是 jsonl 审计的依据) |
改稿纪律(循环模式每轮改稿必做,按顺序四步)
改稿完成后、发起下一轮评审前,必须完成以下机械验证;任何一步以输出为准,不以记忆为准。
未通过 → 修复后重跑该步,不得带着未验证的"已改"进入下一轮。
- 逐条意见标志字符串实测:本轮每条 P0/P1 的修法确定一个"标志字符串"(新增的关键短语、
删除后应消失的旧表述、改写后的新判据),逐条 grep 目标文档:该在的必须在、该删的必须为 0。
在对话中给出 grep 命令与命中行原文,不写"已修改"三个字。
标志字符串必须锚定到结构位置、不得只取裸短语(实测反例:grep -c '改稿纪律'会命中
§变更记录里"新增改稿纪律节"这句声称本身,5/5 次全部误判为"已改";而grep -c '^## 改稿纪律'
锚定标题行后 5/5 正确判为"未改")。新增内容优先锚定标题/行首(^##、^\| v、^[0-9]+\.),
删除内容优先取"改前独有、改后必消失"的原句;命中后必须人工读命中行,确认它是正文而非变更记录/审计记录里的自述。 - 状态与版本行实测:文档头状态行(版本号 + 轮次)、§变更记录末行(新版本行)逐一核对。
版本序核验按执行器分别使用;正则必须对基线真实格式实测(SKILL.md 更新记录
的真实格式是| v10 |、| v11 |——整数版本、无小数点;v[0-9]+\.[0-9]+这类要求小数点的
正则会提取不到任何行,等于校验空转。写正则前先grep -n "^| v" <文件>看真实格式):
- Bash 提取:grep -E '^\| v[0-9]+(\.[0-9]+)? \|' <文件> | sed -E 's/^\| (v[0-9]+(\.[0-9]+)?) \|.*/\1/' | tr -d '\r'→ 版本号数组(按出现顺序)。
必须从锚定的表格首格提取,不得用grep -oE "v..."扫全文——变更说明正文里的v1.5.0/v1.7.0会被一并抓出,实测得到v10,v1.5,v11,v12,v1.7(假命中);
- PowerShell 提取:$vs = (Select-String -Path <文件> -Pattern '^\| (v[0-9]+(\.[0-9]+)?) \|').Matches | ForEach-Object { $_.Groups[1].Value }(按Matches顺序,实测 v10,v11,v12 正确);
- 逐索引序列断言(Compare-Object比较集合差异、不验证顺序,乱序仍通过——禁止使用):
断言 ① 提取数组与预期数组长度相等;② 逐索引相等(PowerShell:$vs[$i] -eq $expect[$i]循环;Bash:数组下标${vs[$i]}与${expect[$i]}循环比对);
③ 数值化后逐项递增 +1 无缺号;④sort | uniq -d(Bash)/Group-Object | Where Count -gt 1(PowerShell)查重为零。
- 三个失败样例验证断言本身有效:[v10,v11,v11,v12](重复)必须被抓;[v10,v12](缺号)必须被抓;
[v10,v12,v11](乱序,集合相同、行数相同)必须被抓——三个样例分别跑断言脚本,确认全部报错。
断言脚本必须实际运行验证(历史方案里曾写出${expect[i]]}这类语法错误,
静态通读发现不了):写完断言脚本后,用三个失败样例数组构造实际输入运行,每个都必须按预期报错;
再用一个正确样例运行确认通过。运行记录(命令 + 输出)附在实施记录里。
任何「递增/缺号/重复/乱序」判断以上述断言输出为准,不做肉眼比对。
PowerShell 正则替换对含中文括号/引号的行经常静默不命中(不报错、只是没改到)——
替换后必须 grep 复核;重要行建议改用「行数组按行号直写」或 Edit 工具(old_string 不命中会报错,
比 -replace 静默失败安全)。 - 同模式残留扫描:修完评审者点名的具名位置后,全文 grep 同类模式(同款命令、同款判据、
同款措辞)一次清干净;含代码块的文档额外做词法围栏扫描(列出全部```配对行号,
检查围栏配平与重复/残留块——成对数量突变或孤立围栏即残留信号)。 - bash 块质量门:本轮新增/修改的 shell 代码块,逐块执行
bash -n(语法);语法通过不等于
语义正确——对关键判据表达式(test/条件判断/退出码依赖)逐条做语义实测或纯内建替身推演
(例:[ ! echo x ]是无效 test 表达式,bash -n 通过但运行返回 2 且恒假)。
方案中的验收判据若本身是脚本,按"生产代码"标准对待。
停止条件(三条件制,取最先触发)
| # | 条件 | 动作 |
|---|---|---|
| 1 | verdict = PASS |
循环结束,文档可执行 |
| 2 | 连续 2 轮无新增 P0/P1 | 视为收敛,结束;剩余 P2 记入 unresolved.md |
| 3 | 轮次 = 上限(默认 10;用户启动时可显式调高——含状态机/清理流程/多门槛联动的复杂 plan 建议 15~25,此类方案的执行细节长尾需要逐轮暴露) | 硬停,不自动续跑,列出残留分歧给用户拍板 |
「无新增」判定以问题内容为准,不看措辞 —— 同一问题换个说法不算新增。
「收敛中」判定(硬停时向用户提供的决策参考;可操作定义):同时满足以下三条,判定为
「执行细节长尾收敛中」:
① P2 已清零(连续 ≥1 轮 p2=[],取 review-state.json.history[] 实测计数);
② p1 连续 ≥3 轮 ≤2 且逐轮下降或持平(同上实测计数);
③ 本轮 p1 按意见内容归类全部属"命令级可执行性/文档一致性长尾"(分类依据 = 本文件
§8.4 收敛经验;归类时须引用意见原文并说明为何属长尾,不得凭修复位置数推断)。
「每条修法改动位置数」仅作描述性信息记录,不作为缺陷性质的判定依据(设计层缺陷也可能单点修复;
执行细节缺陷也可能多点多改)。
三条都满足时,向用户报告继续轮次的历史观察值(如"R20~R23 各轮 p1 为 1~2 条,R24 p1=0 且达成 PASS"(p1 数量 ≠ 每轮修复数,仅作负载观察))
与继续成本,由用户拍板是否延长——不自动续跑、不承诺收益(样本观察 ≠ 普遍效果)。
延长时点只有一种:硬停发生后凭用户显式授权延长(更新轮次上限并记录授权);
「启动时调高上限」仅适用于尚未开始的新循环,二者不混用。
硬停且末轮 FAIL 时的收口动作(必做,实战校准):
1. 结论必须显式回答"能否进入实施" —— 末轮有 P0(或 P1 阻断项)→ 不可进入实施,不得含糊。
2. 不自动出 vN+1:把末轮三项修法写成「授权选项」(① 授权出 vN+1 + 重置轮次重启循环 / ② 按硬停交付并限制范围 / ③ 先解外部阻断再评),由用户拍板。
3. 同轮输出决策清单(一张表覆盖:授权路径 + 所有 unresolved.md 待拍板项 + 本轮新暴露的结构选择),每项给推荐值 + 影响 + 不决定的后果,让用户一次拍完 —— 避免"授权后才发现还有 N 项没定"。
4. 落盘 audit-rN.md(含停止登记表)+ review-state.json(round / stop_reason / history[])+ unresolved.md(新增末轮遗留段)三件套后再回话。
终检轮的特殊口径:若某轮仅剩「纯文本 / 渲染层」P2(措辞、悬垂引用、加粗嵌套),可声明本轮判据为「无 P0/P1 且前述已指明 P2 闭合即 PASS」,并在 verdict_reason 说明 —— 避免为格式问题多跑一轮。
趋势观察(判断何时该收紧口径):把每轮 P0/P1/P2 计数记进 review-state.json.history[],看趋势而非单轮绝对值。实战样本(spec,7 轮收敛):
轮次 1 2 3 4 5 6 7
P0 0 1 2 2 1 0 0
P1 8 9 4 3 0 1 0
P2 1 0 0 0 1 1 0
读法:P0 归零是分水岭 —— 此后残留的 P1 通常是"摘要/措辞与强契约不同步"的一致性类问题,可安全地用"同类一次清干净 + 收敛提示"推进;反之若 P0 仍在 2~3 徘徊,说明契约设计本身未定型,不要急着在 prompt 里写"不要在边角问题上阻塞 PASS"。
8.4 收敛经验:plan 类文档的 P1 长尾(2026-09-24 实战校准):P0 归零后,plan 的 P1 长尾高度集中在命令级可执行性——执行 shell 未声明(Bash vs PowerShell)、路径形态(Windows D:/ 在 WSL/scp 里被当远端语法)、进程管理(宽泛 pkill -f 自匹配、无 PID 记录)、临时文件/样本的生命周期(失败分支不清理、目录权限)、样本/测试数据的格式与冻结契约的字节级一致性(尾部 LF/CR
这类 framing 问题连续两轮才抓干净)、凭据出现在命令行。预防性写法:plan 里的每条命令写死「在哪个 shell、绝对路径、精确 PID、失败即停 + 清理」,样本类操作先对齐字节级契约再校验——这五类一次写死,可省 3~5 轮。评审 prompt 也可在 B 类维度第 3 条(可执行性)里点名这几项。
判据层 / 执行层分层(2026-10-26 plan 24 轮实证):plan 把完整命令块内联时,评审轮次会分两段——
前半抓判据层缺陷(验收断言、触发条件、映射遗漏),后半抓执行层缺陷(bash 状态机、清理守卫、
证据链)。两层问题性质不同、可并行收敛。若 plan 只写「验收判据 + 流程编排 + 证据要求」、
单一案例(24 轮 plan)观察:约前 19 轮耗在判据/契约层、后 5 轮耗在执行层——若两者并行(判据写 plan、命令进受评审的独立脚本/附录),预计有可观节省,但比例未经对照实验验证,不作为普遍承诺。是否分层由方案作者权衡(内联的可读性优势 vs 分层的收敛速度)。
反模式(必避)
| 反模式 | 后果 | 对策 |
|---|---|---|
每轮新开 codex 会话(未 resume 且未注入历轮摘要) |
重复提已修意见 | 必须 resume <thread_id>;resume 失败 → 调用纪律第 4 条 退出报用户,不做 fresh-exec 降级(用户拍板) |
| 主请求替评审者辩护 | 评审独立性失真 | 先落盘意见再改,改完在 report 里逐条回应 |
| 无范围基线 | 范围无限扩张 | 第 1 轮冻结 scope-snapshot.md,超出项标 out_of_scope |
| 死磕到上限 | 边角反复打磨 | 条件 2 早停;上限默认 10,复杂方案可在启动时调高(条件 3) |
| 评审者放水(全 PASS) | 评审失去对抗性 | 强制 strongest_objection |
| 只改 P0 不记 P1/P2 | 低优先级意见丢失 | 全部落盘,P1/P2 挂 unresolved.md |
| codex 失败就偷偷改用同模型子代理 | 静默丢失独立性 | 报错停下 + 给出排查清单,不降级 |
| codex 调用失败自动重试 / 降级续跑 / 换模型硬闯(2026-09-30 用户明令禁止) | 故障被吞、结论不可信 | 非零退出即停止并原样汇报;网络刷新超时仅可按「失败处理」第 3 项重试一次,其他非零退出不重试;处置方案由用户拍板 |
| 借道 WSL 调 codex | Windows 环境差异难排查 | 一律 Windows 原生(Git Bash / PowerShell)直接跑 exe |
| 在 pwsh 会话照抄 Bash 模板 | PowerShell 解析或执行错误 | 先判定执行器,pwsh 会话用§4.5的 pwsh 版模板;需要 bash 时写死 Git Bash 绝对路径 |
| 把文档全文 / 源码 grep 结果塞进 prompt | prompt 超限且独立性下降 | prompt 只给路径 + 维度 + 输出要求,材料让 codex 自己读(调用纪律第 2、3 条) |
| 同类问题只修被点名的那几处 | 同类遗漏重复返工 | 收到某类意见后全文扫一遍同类表述(grep 关键词),一次清干净;在 变更记录 显式写"同类残留一次性清干净" |
| 评审进行中改文档 | 评审版本不确定 | 起轮次后等结论落盘再改稿;若确实在窗口内改了,必须单独标记版本(如 v7 → v7.1)并在变更记录写明"与 vN 契约内容相同,仅版本标记",保证 PASS 归属可追溯 |
| 用 Python 脚本对 Markdown 做切片拼接式编辑 | 重复内容或字节损坏 | 优先使用 Edit / 小步替换;脚本写入前后均做标题唯一性、体积和重复段落检查;案例全文见 references/troubleshooting.md#历史坑 |
| 变更记录插行时把新行写在旧行"之前" | 版本顺序失真 | 追加变更记录永远追加在表格末尾:Edit 的 old_string 取上一版那一行的行首作锚点,new_string = 该行首 + 换行 + 新行。写完立刻 grep -n '^\| v' 核对版本序 |
| 拿最新 spec 挑旧 plan | 基线漂移导致误判 | plan 评审锁定其声明的 spec 版本;漂移单独记 P1 |
| plan 评审漏掉 spec 符合度 | spec 冲突漏检 | B 类维度第 1 条不得省,spec_conflicts 必填 |
| 复用上轮 prompt 模板未改版本号 | 版本噪声干扰评审 | 每轮 prompt 的「当前版本」现写;或统一声明「以目标路径当前内容为准,版本号仅参考」 |
| 声称已改、实际未改(正则替换静默不命中 / 变更记录漏追加 / 状态行未更新) | 改稿声称与实际不符 | 每轮改稿后执行「改稿纪律」四步机械验证(本表"同类表述"行的操作化落地——把"全文扫一遍"升级为可断言的 grep 实测清单);以 grep 输出为准,不以记忆为准 |
| bash 判据块只做语法自查 | 错误判据阻断正常路径 | 新增/修改的判据表达式逐条语义实测或内建替身推演;验收判据按生产代码标准对待 |
换成独立的外部模型,是本循环唯一的独立性来源。
汇报格式
每轮报告 verdict、三态、逐项改动、残留级别、未决项、触发条件、耗时与评审会话 ID。续评使用 codex exec resume <thread_id> …;Bash 与 pwsh 模板见 §4.5。
停止时使用固定格式报告 verdict、三态、改动文件、残留、停止原因、开销和 thread_id。thread_id 同时写入 review-state.json.codex_thread_id。
更新记录
完整记录见 references/update-log.md;本轮按 2026-10-27 Plan 执行。
| 版本 | 日期 | 变更 |
|---|---|---|
| v12 | 2026-10-26 | v1.7.0:改稿纪律、停止条件与失败矩阵。 |
| v13 | 2026-10-26 | v1.8.0:合并调用章节,拆分更新与失败记录,优化布局并同步 .pi。 |
| v14 | 2026-10-27 | v1.8.1:修复重试口径、Bash 探测、schema 对齐、工作区路径、err 日志与 state 语义;统一术语格式。 |
| v15 | 2026-10-28 | v1.8.2:发布可见性调整为公开。 |
```