Codex
让 Claude Code 写、Codex 审:一套可照做的 review gate 搭建经验
作者把写代码交给 Claude Code、把审查交给只读模式下的 Codex,再用三个 bash 脚本把「测试通过」和「审查 ACCEPT」绑定到具体 commit 上,没有这两样就不许合并。文中记录了只读沙箱被 MCP 和 hook 破坏、测试命令被悄悄收窄仍能通过等真实踩坑,并给出可复现的验证方法。
今天就能做
- 在自己的仓库里先跑一遍最小闭环:写代码 → 只读审查出 verdict → 测试出 pass 记录 → merge-check 拒绝无证据的 commit。
- 审查前把 ~/.codex/config.toml 里能改外部状态的 MCP server 设 enabled = false,并检查有没有 Stop hook 会吞掉最终 verdict。
- 给 gate 设 GATE_COUNT_RE 和 GATE_MIN_TESTS,并养成看 pass 记录里 Command: 行的习惯,防止测试命令被悄悄收窄。
先分工:谁写、谁审
- Claude Code 在自己的 git worktree 里写代码,Codex 只读审查,最后一条消息必须以单行 Verdict: ACCEPT 或 Verdict: CHANGES REQUIRED 开头,后面跟问题清单。
- 把写作交给 Claude Code 是因为写作会堆积上下文(计划、brief、worktree、gate 日志);审查只需要 diff、规则和这次改动的目的。
- 审查方永远不改代码,prompt 里明确写 You review; you never fix,每个问题描述到作者能在新 commit 里修,修完再走一次审查。
- 角色可互换:脚本里设 BUILDER=codex 就变成 Codex 写、Claude Code 审。
让只读真的只读
- Codex 的审查调用是 codex exec -s read-only --color never --output-last-message <file> -,沙箱允许读文件和跑 git diff 这类只读命令,不能写。
- ~/.codex/config.toml 里配置的 MCP server 在审查时照样启动,能改动沙箱外东西的 server 要在审查期间设 enabled = false。
- hook 也会在 codex exec 下运行:一个让 Codex 继续工作的 Stop hook 可能把最终消息替换成空,gate 找不到 verdict 直接 exit 1,只留诊断日志。
- Claude Code 当审查方时只给 Read、Grep、Glob 三个工具,它跑不了 git,所以脚本要把 commit 和完整 diff 塞进 prompt。
共享规则要短
- 两个引擎需要同一套规则,作者在 CLAUDE.md 和 AGENTS.md 各放一小段:写的一方一次只提交一个改动就停、不碰 .ai/ 目录、绝不合并;审的一方保持只读。
- Codex 的指令文件累计到 project_doc_max_bytes(默认 32 KiB)就不再加载,规则写在超长 AGENTS.md 末尾可能根本到不了它手里。
- 指令文件也是入口:两个引擎都会加载它们读到的文件,包括不是你写的文件,要检查本机到底加载了哪些。
证据必须绑定 commit
- gate.sh 在干净 checkout 上跑测试命令,写一条以 commit 命名的 pass 记录;review.sh 把 commit 和 diff 发给另一个引擎,把 verdict 存到被审的 commit 名下。
- merge-check.sh 只在当前 commit 同时有 pass 和 ACCEPT、且 ACCEPT 来自非 builder 的审查方时才放行。
- 新 commit 从零开始,旧 commit 的 ACCEPT 覆盖不了没人审过的新代码。作者演示的例子里,sum.sh 漏掉第一个参数,测试只测 sum 0 4 恰好通过,审查要求修改,修复 commit 必须自己再拿一次 pass 和 ACCEPT。
- 三个脚本都拒绝有未提交或未跟踪文件的 checkout,因为测试可能在一个没进 commit 的文件上通过;测试跑完还会再查一次,测试留下快照或构建产物也算失败。
- 有个自指的坑:按说明把 .ai/ 加进 .gitignore,这个改动本身就是未提交变更,gate 第一个拒绝的就是你刚加的那行。
最大的坑:收窄的测试命令照样通过
- gate 只认测试命令在干净 checkout 上 exit 0 且计数检查接受输出,它不知道项目本来有哪些测试。
- Node 20.19 的例子里,add() 返回错误结果,GATE_CMD="node --test test/" 得到 FAIL;换成 GATE_CMD="node --test test/format.test.js" 就得到 PASS 并写下 pass 记录,add() 依然是错的。
- 默认计数检查只拦空输出或汇总为零的情况,这里跑了一个测试所以放行;pass 记录里存了 Command: 行,但 merge-check.sh 不拿它做比对。
- 缓解办法是设 GATE_COUNT_RE 和 GATE_MIN_TESTS(例如 '^# pass ([0-9]+)$' 配 2),加测试时要手动调大这个数,而且它只比总数、看不出跑了哪些测试。
- 第二层是审查:prompt 要求审查方检查任何削弱既有检查的改动,但命令行里敲的命令不会出现在 diff 里,计数检查是唯一能看见它的东西。信任一条 pass 记录前,先读它的 Command: 行。
原文:How to use Claude Code and Codex together: setup lessons from my review gate
来源:DEV Community · 2026-11-04
查看原文本文由 AI 阅读原文后整理,未经人工逐字核对,内容以原文为准。
登录后可以点赞、查看和发表评论