乐于分享
好东西不私藏

Codex 插件深度拆解——用源码看懂 AI 审 AI 的工程实现

Codex 插件深度拆解——用源码看懂 AI 审 AI 的工程实现

本文所有技术论断均来自以下可验证来源,推断之处会明确标注:

  • • openai/codex-plugin-cc[1] v1.0.2 源码(下文源码链接均指向该仓库)
  • • Anthropic Hooks 官方文档[2]
  • • Anthropic Plugins 官方文档[3]
  • • 笔者实际使用记录

一个你可能已经遇到的场景

你让 Claude 写了 200 行代码。看了一眼,逻辑通顺,测试通过,合入了。

三天后线上炸了——一个竞态条件。回头看那段代码,其实问题挺明显的,但当时你和 Claude 都没注意到。

如果当时有另一个 AI 站在旁边说:”等等,你考虑过并发场景吗?”——这就是 OpenAI 的 codex-plugin-cc[1] 要解决的问题。

AI 写代码已经不稀奇了。AI 审 AI 写的代码,才是下一个工程问题。它不是概念演示,而是需要协议设计、状态管理、失败兜底的真实工程。codex-plugin-cc 是目前公开的、在笔者看来相当完整的一个”AI 审 AI”实现——近 5000 行代码,零运行时依赖,以 Claude Code 插件系统为宿主,外接 Codex CLI 和 app-server 作为运行时。

这篇文章不讲安装教程(README[4] 已经很清楚),而是从源码看三个问题:核心机制怎么运作(Stop Hook 门控)、底层架构怎么设计(Broker + JSON-RPC)、边界在哪里(限制、成本和失效模式)。


核心机制:Stop Hook 审查门控

Claude 每次”交卷”,都要先过 Codex 这一关。

这是整篇文章的主线,也是这个插件最有价值的部分。

什么是 Stop Hook 门控

Claude Code 有一个 Stop 事件:当 Claude 认为任务完成、准备结束回复时触发。这是 Anthropic 官方 Hook 系统[2]提供的生命周期事件之一。

codex-plugin-cc 利用这个 Stop Hook,在 Claude “交卷”前自动拦截,让 Codex 审查上一轮直接编辑产生的代码改动。值得注意的是,”验证改动是否真实发生”这件事,是插件通过提示词明确要求 Codex 去做的——这是提示词层面的约束,不是宿主代码自己做的硬校验。

开启方式很简单:

/codex:setup --enable-review-gate

源码位置:hooks/hooks.json[5] → stop-review-gate-hook.mjs[6]

门控的完整工作流程

一旦开启,每次 Claude 准备停止回复时,都会走这样一条链路:

Claude 写完代码,准备停止    ↓Stop Hook 触发(timeout: 900 秒 = 15 分钟)    ↓stop-review-gate-hook.mjs 读取 Claude 上一轮的 last_assistant_message    ↓注入到 stop-review-gate.md 提示词模板({{CLAUDE_RESPONSE_BLOCK}})    ↓同步调用 codex-companion.mjs task --json <prompt>    ↓解析 Codex 输出:第一行必须是 ALLOW: <reason> 或 BLOCK: <reason>    ↓ALLOW → Claude 正常结束BLOCK → 返回 {"decision": "block", "reason": "..."} → Claude 必须继续修复

这里有几个关键细节:

15 分钟超时。Stop Hook 的 timeout 设为 900 秒,是这个插件定义的 3 个 Hook 里最长的。因为 Codex 需要时间去理解上下文、分析代码、给出判断。

同步阻塞。这不是后台异步的——Claude 在等 Codex 给结论。如果 Codex 说 BLOCK,Claude 就得继续干活。

输出格式是硬约束。第一行必须是 ALLOW: 或 BLOCK: 开头,后面跟理由。不是 JSON,不是自由文本,是最简单的行协议。这种设计让解析逻辑极其简单,降低了 LLM 输出格式错误导致误判的概率。

避免无编辑轮次空转的设计

一个显而易见的问题:如果 Claude 只是回复了一句”好的,我来看看”,没有实际改代码,Codex 也要审查吗?

不会。门控提示词(源码:prompts/stop-review-gate.md[7])里有明确的约束:

“Only review the work from the previous Claude turn.”“Only review it if Claude actually did code changes in that turn.”“Pure status, setup, or reporting output does not count as reviewable work.”

纯状态更新、摘要、设置检查——这些都直接 ALLOW,不做审查。这避免了”Claude 回复一句话 → Codex 审查 → 没代码改动 → 又 block”的无编辑轮次空转。

但要注意:这只解决了”没改代码时不瞎审”的问题。如果 Claude 真的在改代码,而 Codex 每次都 BLOCK,这个循环是没有计数器限制的(详见后文”限制与失效模式”一节)。

“检查深度”的提示词工程

当确认有代码改动时,Codex 不只是表面看一遍。提示词里有一个 <dig_deeper_nudge> 块,要求 Codex 检查:

  • • 二阶故障:这个改动引入了什么新的失败路径?
  • • 空状态行为:边界条件处理了吗?
  • • 重试/幂等:失败重试会不会产生副作用?
  • • 回滚风险:能安全回滚吗?
  • • 设计权衡:是不是在用复杂度换正确性?

同时,提示词还要求 Codex 在做出 BLOCK 决定之前,先从 repo state 验证那一轮是否真的发生了代码改动——而不是仅仅根据 Claude 的回复文本来判断。这是 <grounding_rules> 的要求:”Do not treat the previous Claude response as proof that code changes happened; verify that from the repository state before you block.”

这种设计思路是:不信任任何一方的自我报告,用仓库状态做 ground truth

一个被 BLOCK 的真实场景

来看一个具体的例子。以下是笔者基于实际使用经验整理的示意场景(代码和输出经简化脱敏):

我让 Claude 写一个处理用户上传文件的 API endpoint:

// Claude 写的代码app.post(&#x27;/upload&#x27;, async (req, res) => {  const file = req.files?.document;  const result = await processFile(file.path);  await saveToDatabase(result);  res.json({ success: true, data: result });});

Claude 认为任务完成,准备停止。Stop Hook 触发,Codex 开始审查。

几十秒后,Codex 返回:

BLOCK: The upload handler has three unaddressed failure modes:(1) req.files?.document uses optional chaining but file.pathon the next line will throw if document is undefined;(2) no error handling around processFile or saveToDatabase —a processing failure leaves the client hanging;(3) no file size or type validation before processing.

Claude 被打回,继续修复。下一轮它补上了空值检查、try-catch 和文件校验,再次尝试停止——这次 Codex 返回 ALLOW: Error handling and input validation now cover the identified failure paths.

这就是门控的实际体感:不是 Claude 说完了就完了,是 Codex 同意了才算完


引擎拆解:Broker + JSON-RPC 架构

理解了门控机制后,下一个问题是:插件和 Codex 之间到底怎么通信?

答案是一套基于 JSON-RPC 2.0 的协议,中间插了一个 Broker 做共享复用。

为什么需要 Broker

如果每次跑 /codex:review 都要 spawn 一个新的 codex app-server 进程,冷启动太慢。而一个 Claude Code 会话里可能跑多次 review、adversarial-review、rescue——每次都等冷启动,体验很差。

解决方案:首个 Codex 请求发生时,按需拉起一个长驻 Broker 进程(detached),后续命令通过 Unix Socket 共享。

注意一个容易搞混的点:SessionStart Hook 只负责注入环境变量(session ID 和 plugin data path),不启动 Broker。Broker 的按需启动发生在 ensureBrokerSession()[8],由 CodexAppServerClient.connect() 触发。也就是说,如果你装了插件但从没跑过任何 Codex 命令,Broker 根本不会启动。

架构全景

整个会话的生命周期是这样的:

Claude Code 会话    │    ├── SessionStart Hook → session-lifecycle-hook.mjs    │   ├── 写入 CODEX_COMPANION_SESSION_ID 到 CLAUDE_ENV_FILE    │   └── 后续命令通过环境变量获取 session scope    │    ├── Broker 进程(首次请求时按需启动,Detached, Unix Socket)    │   ├── app-server-broker.mjs + broker-lifecycle.mjs    │   ├── 单活跃流串行化:activeRequestSocket + activeStreamSocket    │   └── 转发 JSON-RPC ↔ codex app-server(真实进程)    │    ├── /codex:review              → companion.mjs → Broker → review/start RPC    ├── /codex:adversarial-review  → companion.mjs → Broker → turn/start RPC + 对抗提示词    │    ├── Stop Hook → stop-review-gate-hook.mjs → 同步 Codex 审查    │    └── SessionEnd Hook → broker/shutdown → 清理进程 + 状态 + socket 文件

Broker 本身是一个非常轻的转发层,核心逻辑在 app-server-broker.mjs[9] 里。

Busy 互斥:Broker 层的单活跃流串行化

Broker 维护两个所有权槽:activeRequestSocket(正在等待响应的 socket)和 activeStreamSocket(正在接收通知流的 socket)。

当新请求来自的 socket 不持有任一槽位,且另一个 socket 已持有活跃 request 或活跃 stream 时,Broker 直接返回 -32001 Busy

唯一例外turn/interrupt(取消操作)可以穿透 busy 状态——否则你就没法取消一个正在跑的任务了。

优雅降级:收到 -32001 或 Broker 不可达时,插件自动 fallback 到直接 spawn 一个全新的 app-server 进程。Fallback 意味着两个 Codex 进程同时运行,无法共享同一个 Broker 上的活跃流和运行时会话——但任务线程本身可以通过 thread/list + thread/resume 跨连接继续。

笔者理解,这是可接受的工程权衡:宁可多花一个进程的开销,也不让用户等着。

这里需要严谨说明:从插件源码能确认的是 Broker 这一层做了单活跃流串行化(源码:codex.mjs → withAppServer() fallback 逻辑),不能断言 Codex app-server 本体是否支持并发。

JSON-RPC 协议要点

插件和 Codex app-server 之间用 JSONL(换行分隔的 JSON)传输 JSON-RPC 消息。核心方法:

方法
方向
用途
initialize
Client→Server
握手,交换能力声明
review/start
Client→Server
启动原生审查(/codex:review 用)
turn/start
Client→Server
启动通用任务(对抗审查和 rescue 用)
turn/interrupt
Client→Server
取消运行中的任务
turn/completed
Server→Client
通知任务完成
item/started

 / item/completed
Server→Client
子操作进度通知
broker/shutdown
Client→Broker
优雅关闭 Broker

其中 item 级别的通知粒度很细,覆盖文件改动、工具调用、子 Agent 消息、推理过程等。但插件有选择性地忽略了流式 delta 通知(如 item/agentMessage/deltaitem/reasoning/textDelta),只关心开始和结束事件。

子 Agent 追踪:TurnCaptureState 状态机

Codex 执行任务时可能 spawn 子 Agent(通过 collabAgentToolCall),这些子 Agent 可能又产生新的线程。插件用一个 TurnCaptureState[10] 状态机跟踪所有线程的生命周期。

状态机维护几个关键集合:

  • • pendingCollaborations — 正在等待的协作 Agent 工具调用
  • • activeSubagentTurns — 活跃的子 Agent 轮次
  • • finalAnswerSeen — 主线程是否已给出最终回答

完成条件有两种路径:

  1. 1. 正常完成:root 线程收到 turn/completed 事件
  2. 2. 推断完成:主线程已出现 finalAnswerSeen,尚未收到 turn/completed,且 pendingCollaborations 和 activeSubagentTurns 都已清空——此时用 250ms timer 推断任务完成

250ms 是一个启发式的 drain timer。太短可能漏收迟到的子 Agent 结果,太长浪费用户等待时间。


两种审查模式的协议差异

插件提供两种审查:/codex:review(标准审查)和 /codex:adversarial-review(对抗性审查)。它们走的是完全不同的协议路径。

对比

/codex:review /codex:adversarial-review
RPC 方法
review/start

(Codex 原生审查)
turn/start

(提示词驱动的通用任务)
输入
自动收集 git diff
git diff + 对抗提示词 + 可选 focus text
输出格式
Codex 原生审查格式
JSON Schema 约束的结构化输出
沙箱模式
read-only read-only
审查姿态
全面代码质量检查
“默认持怀疑态度,假设改动会以微妙、高代价的方式失败”
适用场景
日常 review
迁移、认证、基础设施、高风险重构

/codex:review 调用的是 Codex 内置的 review/start 方法,输出什么格式由 Codex 自己决定。而 /codex:adversarial-review 走的是通用的 turn/start,插件自己注入对抗性提示词,并通过 JSON Schema 约束输出格式。

对抗性审查的提示词设计

对抗性审查的提示词(源码:prompts/adversarial-review.md[11])用 XML-tagged 结构组织,几个关键设计决策值得一看:

<operating_stance>:”Default to skepticism. Assume the change can fail in subtle, high-cost, or user-visible ways until the evidence says otherwise.”

这句定了调:不是帮你确认改动是对的,是主动找它哪里会出事。

<attack_surface> 定义了审查优先级:认证/权限 > 数据丢失/损坏 > 回滚安全 > 竞态条件 > 空状态/null/超时 > 版本偏差 > 可观测性。这个排序本身就是一份安全意识清单。

<finding_bar>:不报风格、命名、cleanup 类问题。每个 finding 必须回答四个问题——什么会出错?为什么脆弱?预期影响多大?具体怎么改?

<calibration_rules>:”Prefer one strong finding over several weak ones.” 宁少勿滥。

<grounding_rules>:”Do not invent files, lines, code paths, incidents, attack chains, or runtime behavior you cannot support.” 不许编造证据。这是对 LLM 幻觉问题的直接工程应对。

这套提示词设计的核心思路是:通过结构化约束,把 LLM 的”自由发挥”限定在可验证的范围内

Review Target 的自动解析

/codex:review 和 /codex:adversarial-review 都需要知道”审查什么”。git.mjs[12] 里的 resolveReviewTarget() 做了智能选择:

  • • 工作树脏(有 staged、unstaged 或 untracked 文件)→ 审查工作树变更
  • • 工作树干净 → 自动检测默认分支(尝试 mainmastertrunk),审查分支差异
  • • 可以通过 --base <ref> 或 --scope 手动覆盖

一个实用的细节:未跟踪文件有 24KB 大小限制(MAX_UNTRACKED_BYTES),超过的被跳过,避免把大型生成文件或 vendor 目录塞进审查上下文。


旁支能力:rescue 任务委派

/codex:rescue 不是审查,是”协作委派”——把任务交给 Codex 去做,而不是让 Codex 评判 Claude 的工作。把它和审查机制混为一谈,是理解这个插件时最常见的误区。

怎么工作的

rescue 命令在 frontmatter 里标记了 context: fork,意味着它在一个独立的 Claude 上下文中运行,不影响当前对话。它路由到 codex-rescue 子 Agent[13]

这个子 Agent 被设计为”薄转发层”——不做 repo 层面的独立分析,但可以在转发前利用 gpt-5-4-prompting skill 对 prompt 做有限整形。源码里写得很直白:

“Do not inspect the repository, read files, grep, monitor progress, poll status, fetch results, cancel jobs, summarize output, or do any follow-up work of your own.”

线程持久化和恢复

rescue 任务使用 ephemeral: false,线程被持久化。这意味着你可以:

  • • 用 --resume 继续上一次 Codex 对话
  • • 直接用 codex resume <session-id> 在 Codex CLI 里继续

这在调试复杂问题时非常实用:Claude 搞不定的问题扔给 Codex,Codex 搞到一半你可以随时捡回来继续。

GPT-5.4 提示工程体系

skills 目录包含一套提示词工程框架(gpt-5-4-prompting/[14]):14 个可复用 XML prompt blocks、5 个场景模板(诊断、精准修复、根因审查、调研建议、Prompt 修补)、6 个反模式及 before/after 示例。这是旁支,不展开细节,但它说明了一件事:这个插件不只是”给 Codex 发个消息”,而是系统性地在提示词层面降低 LLM 的幻觉和漂移。


这个插件用了哪些 Claude Code 官方能力

以下是这个插件实际使用的官方能力,不是 Claude Code 插件系统的完整规范。完整规范参见 Anthropic 官方文档[3]

目录结构

plugins/codex/  .claude-plugin/plugin.json    — 插件身份(name: "codex", v1.0.2)  commands/*.md                 — 7 个斜杠命令  agents/codex-rescue.md        — 1 个子 Agent 定义  hooks/hooks.json              — 3 个生命周期钩子  prompts/                      — 提示词模板  schemas/                      — JSON Schema(结构化输出约束)  scripts/                      — 运行时代码(近 5000 行 .mjs/.d.ts,零运行时依赖)  skills/                       — 3 个内部技能文档

整个运行时只依赖 Node.js 内置模块,package.json 里的 typescript 和 @types/node 都是 devDependencies,运行时不需要。

命令的 frontmatter:两种权限隔离模式

以下为示意摘录,仅保留与本文讨论相关的字段。完整 frontmatter 请查看仓库原文件。

review.md[15] 的关键字段(审查类):

  • • disable-model-invocation: true — 直接执行脚本,不走 LLM 解释。这意味着 Claude 不会”理解”命令然后自由发挥,而是严格按脚本执行
  • • allowed-tools: Read, Glob, Grep, Bash(node:*), Bash(git:*), AskUserQuestion — 工具白名单,最小权限原则

rescue.md[16] 的关键字段(委派类):

  • • context: fork — 在独立 Claude 上下文中运行,不影响当前对话
  • • 同样有 allowed-tools 限制(具体列表见源码)

在这个插件里,可以看到两种典型的权限隔离模式:一个是”受限执行”(review),一个是”隔离委派”(rescue)。前者限制工具能力,后者隔离运行上下文。

这个插件用到的 Hook 事件

事件
这个插件的用法
超时
SessionStart
注入环境变量(session ID + plugin data path)到 CLAUDE_ENV_FILE
5s
SessionEnd
关闭 Broker、清理进程和状态文件
5s
Stop 审查门控

——拦截 Claude 的回复结束,运行 Codex 审查
900s

Claude Code 的 Hook 系统截至写作时支持 25+ 个事件(包括 PreToolUsePostToolUseUserPromptSubmitPermissionRequestSubagentStart/SubagentStopTaskCreated/TaskCompletedFileChangedWorktreeCreate 等),这个插件只用了上述三个。

环境变量注入:CLAUDE_ENV_FILE

这是 Claude Code 允许 Hook 向后续工具调用注入环境变量的官方机制。源码:session-lifecycle-hook.mjs[17]

fs.appendFileSync(process.env.CLAUDE_ENV_FILE,  `export ${name}=${shellEscape(value)}\n`, "utf8");

插件通过它传递 CODEX_COMPANION_SESSION_ID 和 CLAUDE_PLUGIN_DATA,实现跨 hook/command 的状态共享。这是一个很巧妙的设计:Hook 执行是短暂的进程,但通过写入 CLAUDE_ENV_FILE,状态可以在整个会话期间持久化。


限制、成本与失效模式

不谈边界的技术文章是广告。这一节拆解这个插件的硬约束。

Stop Hook 的 15 分钟超时

门控超时 900 秒。如果 Codex 在这个时间内没返回结果,默认走保守策略:BLOCK

具体来说:审查任务超时、非零退出、返回异常格式时,都走保守 BLOCK。但如果 Codex 本身未登录或未就绪(比如 token 过期),Hook 仅记录一条 note 后直接返回,不会 block——因为这不是审查失败,是前提条件不满足。

代价很直观:一次超时 = Claude 等了 15 分钟,然后被告知”你的代码有问题”。而实际上可能只是 Codex 挂了。

单流 Busy 和并发限制

Broker 同时只允许一个活跃请求或流(activeRequestSocket 和 activeStreamSocket 任一被其他 socket 持有即触发 busy)。

第二个请求收到 -32001 Busy 后自动 fallback 到独立 spawn。Fallback 意味着两个 Codex 进程同时运行,无法共享同一个 Broker 上的活跃流和运行时会话。但任务线程本身可以通过 thread/list + thread/resume 跨连接继续。

笔者理解,这是可接受的工程权衡:宁可多花一个进程的开销,也不让用户在 Broker busy 时被卡住。

状态文件竞态条件

// state.mjs — 没有文件锁export function updateState(cwd, mutate) {  const state = loadState(cwd);   // read  mutate(state);                   // mutate in memory  return saveState(cwd, state);   // write — last-write-wins}

多个后台任务 worker 同时更新各自的 job 状态,或任务状态更新与 /codex:setup 改配置并发写入时,可能互相覆盖。极端情况下可能丢失 job 记录或配置变更(概率低,但不是零)。

笔者推断:这是在接受小概率竞态与保持零运行时依赖之间的取舍(源码未明确说明设计动机)。引入文件锁意味着要么依赖第三方库,要么自己实现——对一个”零运行时依赖”的插件来说,这个代价可能不值得。

审查门控的循环风险

插件代码中没有循环计数器。循环是否终止完全取决于三件事:

  1. 1. Claude 是否真的修复了 Codex 指出的问题
  2. 2. Claude Code 自身的 usage limit
  3. 3. 用户手动中断

README 里有明确警告:”The review gate can create a long-running Claude/Codex loop and may drain usage limits quickly.”

最坏情况:Codex 反复 BLOCK,Claude 反复改但没改到点上 → 烧完额度。这不是理论风险——笔者推测,如果 Codex 对某种代码风格有偏好,而 Claude 不理解这个偏好,就可能陷入死循环。

推断完成的 250ms 窗口

触发条件:主线程已出现 finalAnswerSeen,尚未收到 turn/completed,且 pendingCollaborations 和 activeSubagentTurns 都已清空。此时用 250ms timer 推断任务完成。

风险:如果 timer 内有迟到的子 Agent 通知,可能漏收最终结果。这是”完整性 vs 响应速度”的权衡,250ms 是经验值。

只审上一轮 direct edits

Stop Hook 的审查范围是”上一轮 Claude 直接编辑产生的代码改动”,不是累积的所有改动。如果 Claude 分多轮完成一个 feature,早期轮次的问题可能被遗漏。

这是设计选择而非 bug。推测原因是审查全量改动的 token 成本过高,但源码中未明确说明动机。


实战工作流速览

不展开安装步骤(README[4] 已经很清楚),聚焦有决策价值的使用模式。

日常流程 + 一个按需启用的强化选项

  1. 1. 写完代码 → /codex:review --background(基础审查,后台跑,不阻塞当前工作)
  2. 2. 高风险改动 → /codex:adversarial-review "检查并发安全性"(带 focus 的对抗审查)
  3. 3. 高风险会话按需开启 Stop Hook → /codex:setup --enable-review-gate

第三步要谨慎:它可能导致长循环和 usage 消耗,不建议作为默认工作流。适合在做认证迁移、数据库 schema 变更等高风险操作时临时开启。

什么时候用 adversarial 而不是普通 review

  • • 迁移、认证变更、基础设施脚本、涉及多服务的重构
  • • “隐患比明显语法错误更危险”的场景
  • • 可以用 focus text 引导审查方向,比如:”challenge whether this was the right caching design”,或者”focus on race conditions in the worker pool”

什么时候用 rescue

  • • Claude 在某个问题上连续几轮没进展——换个”大脑”试试
  • • 需要不同模型视角的深度排查
  • • 想保留 Codex 的线程上下文以便后续继续(--resume

AI 审 AI 的工程现实

这个插件证明了”AI 审 AI”不是概念演示,是可落地的工程方案。近 5000 行代码,覆盖了从协议设计到状态管理到提示词工程的完整链路。

但它也暴露了现阶段的硬约束:单流、超时、无循环限制、只审上一轮 direct edits。这些不是这个插件的问题,而是”AI 审 AI”这个范式本身尚未解决的工程难题。

竞争对手给你写插件的意义,不是示弱,是在说:”你的平台足够开放,值得我来占位。”Claude Code 的插件系统提供了 Hook、命令、Agent、技能四层扩展能力,而 OpenAI 四层全用上了(commands、hooks、agents、skills)来实现一个完整的审查工具链。这本身就是对 Claude Code 平台能力的一次有力验证。

下一步的未解问题:

  • • 审查者的校准:Codex 自己的 false positive / false negative 率是多少?目前没有公开数据
  • • 多轮累积审查:只审上一轮的限制怎么突破?全量审查的 token 成本怎么控制?
  • • 审查成本的可控性:Stop Hook 门控没有循环限制,怎么避免烧完额度?

AI 写代码的能力在飞速提升。但”谁来审查 AI 写的代码”这个问题,才刚刚开始被认真对待。这个插件是一个很好的起点,但远不是终点。

引用链接

[1] openai/codex-plugin-cc: https://github.com/openai/codex-plugin-cc[2] Anthropic Hooks 官方文档: https://docs.anthropic.com/en/docs/claude-code/hooks[3] Anthropic Plugins 官方文档: https://docs.anthropic.com/en/docs/claude-code/plugins[4] README: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/README.md[5] hooks/hooks.json: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/hooks/hooks.json[6] stop-review-gate-hook.mjs: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/stop-review-gate-hook.mjs[7] prompts/stop-review-gate.md: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/prompts/stop-review-gate.md[8] `ensureBrokerSession()`: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/lib/broker-lifecycle.mjs[9] app-server-broker.mjs: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/app-server-broker.mjs[10] `TurnCaptureState`: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/lib/codex.mjs[11] prompts/adversarial-review.md: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/prompts/adversarial-review.md[12] git.mjs: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/lib/git.mjs[13] `codex-rescue` 子 Agent: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/agents/codex-rescue.md[14] `gpt-5-4-prompting/`: https://github.com/openai/codex-plugin-cc/tree/v1.0.2/plugins/codex/skills/gpt-5-4-prompting[15]review.mdhttps://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/commands/review.md[16]rescue.mdhttps://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/commands/rescue.md[17] session-lifecycle-hook.mjs: https://github.com/openai/codex-plugin-cc/blob/v1.0.2/plugins/codex/scripts/session-lifecycle-hook.mjs