a2a — 对抗式 Code Review
谁写的代码,就让对手来审。Self-review is self-deception.
核心机制
对抗原则:review 优先在对立模型上执行。Claude 产出的代码由 Codex 审查,Codex 产出的代码由 Claude 审查。
为什么有效:不同模型有不同的 blind spot。Claude 倾向 over-engineer,Codex 倾向跳过 boundary check。让它们互相找茬,bug 无处藏身。
运行模式
| 模式 | 条件 | 说明 |
|---|---|---|
| adversarial | Claude + Codex 双向均可用 | 跨模型对抗审查,最高质量 |
| single-model-multi-lens | 仅一侧可用 | 同模型多视角审查,质量降低但仍有多视角覆盖 |
| blocked | 无法执行 | 无法执行 review,报告标注 mode: blocked 并中止 |
跨模型双向 dispatch:
- Claude 写的代码 → Codex 审:
codex exec(Codex 有自己的 API key) - Codex 写的代码 → Claude 审:
claude -p+ token bridge(见下方说明)
Token Bridge 机制(解决 Codex sandbox 无法访问 macOS Keychain 的问题):
preflight 在有 Keychain 访问权限的环境中(如 Claude Code、正常终端)运行时,会自动提取 OAuth access token 并缓存到 ~/.cache/a2a/.claude-token(权限 600)。Codex sandbox 内调 claude -p 时,通过读取此文件设置 ANTHROPIC_API_KEY 环境变量,绕过 Keychain 限制。
# Codex 环境内调 Claude 的标准方式:
TOKEN=$(cat ~/.cache/a2a/.claude-token 2>/dev/null)
ANTHROPIC_API_KEY="$TOKEN" claude -p "review prompt..."
⚠️ 从 Claude Code 内部不要直接调
claude -p:CLAUDECODE=1环境变量会触发 nested session 保护。如需从 Claude 侧 review,使用 Agent tool spawn sub-agent。
报告中 Mode 字段必须如实标注当前模式。
触发方式
- 用户说 "review"、"审查"、"code review"、"帮我 review 一下"
- 用户说 "a2a"、"a2a review"、"对抗审查"
- plan-to-codex 的 Phase 3 收口环节
- 手动指定:
/a2a src/path/to/file.js
审查规模判定
先算变更量,再决定派几个 reviewer。必须严格遵守,不得跳级。
# 硬性检查(dispatch 前执行)
diff_lines=$(git diff --stat HEAD~1 | tail -1 | grep -oE '[0-9]+' | head -1)
new_lines=$(git diff HEAD~1 --diff-filter=A --stat | tail -1 | grep -oE '[0-9]+' | head -1)
dir_count=$(git diff --name-only HEAD~1 | xargs -I{} dirname {} | sort -u | wc -l)
| 规模 | 条件 | Reviewer 配置 | 预估耗时 |
|---|---|---|---|
| Light | < 50 行 | Challenger only | ~30s |
| Medium | 50–200 行 | Challenger + Architect | ~1min |
| Heavy | 200+ 行,新增 ≤ 100 行 | Challenger + Architect | ~1.5min |
| Heavy+ | 200+ 行,新增 > 100 行 | 三人全上 | ~2min |
| Cross-module | 涉及 3+ 目录 | 三人全上 | ~2min |
三大审查视角
详见 references/review-lenses.md。
- The Challenger — 质疑者:找 edge case、未处理的 error path、race condition
- The Architect — 架构师:审视 coupling、职责边界、component 拆分合理性
- The Subtractor — 删减者:找 over-engineering、premature abstraction、多余 config
Preflight — 环境检测
首次使用或分享给同事时,跑 preflight 确认环境就绪:
~/.claude/skills/a2a/scripts/preflight.sh
# JSON 格式(供脚本读取):
~/.claude/skills/a2a/scripts/preflight.sh --json
# 登录状态刚变化时强制刷新:
~/.claude/skills/a2a/scripts/preflight.sh --refresh
检测项:Claude CLI、Codex CLI、本地登录态。默认缓存 900 秒,只做本地检查,不会发起 review 请求。
前置校验(Gate)
a2a 支持审查代码变更和Plan / 设计方案,自动判定模式:
- Code Review 模式:
git diff --stat或用户指定的文件有实际代码修改 → 进入代码审查 - Plan Review 模式:无 diff,但用户明确指定了 plan / 设计文档 → 进入 Plan 审查
- 无审查对象:无 diff 也无用户指定 → 提示:
"未检测到审查对象。请指定代码变更范围或要审查的 plan 文件。"
执行流程
Step 1: 环境检测 + 确定变更范围
🔍 运行 preflight,确认模式,确定审查范围。Token bridge 会自动维护,无需手动操作。
~/.claude/skills/a2a/scripts/preflight.sh --json
如果刚执行过 codex login / claude auth login,用 --json --refresh 跳过缓存。
Token bridge 自动补全:preflight 完成后,检查 token 文件是否存在。如果缺失,立即自动补全:
TOKEN_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/a2a/.claude-token"
if [[ ! -s "$TOKEN_FILE" && "$(uname)" == "Darwin" ]]; then
security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null \
| python3 -c "import json,sys; t=json.load(sys.stdin).get('claudeAiOauth',{}).get('accessToken',''); print(t) if t else None" \
> "$TOKEN_FILE" 2>/dev/null && chmod 600 "$TOKEN_FILE"
fi
这段在每次 /a2a 启动时自动执行,用户完全无感。
确定变更范围:
# 自动检测
git diff --stat HEAD~1
# 指定范围
git diff --stat main...HEAD
# 指定文件
cat src/pages/target.vue
根据 Gate 规则判定模式:有 diff → Code Review;无 diff 但有 plan → Plan Review;否则拒绝。
Step 2: 加载审查基准
📋 读取视角定义(red-line 扫描延迟到 Step 5 统一执行)
references/review-lenses.md— 三大视角定义- 目标项目的
AGENT.md/AGENTS.md(如存在)— 仅用于理解项目 context,不传给 reviewer - 目标项目的
.ai/constraints.json(如存在)— 同上,留给 Step 5 统一扫描
Step 3: 明确变更意图
必须在 review 前声明:这次改动要解决什么问题?
Intent: {一句话描述这次改动的目的}
Constraint: {这次改动不能碰的东西}
Step 4: 派遣 Reviewer(并行)
🤖 Claude 始终是 orchestrator,双向跨模型通过不同 dispatch 通道实现
非 git 目录检测(dispatch 前必须执行):
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
SKIP_GIT_FLAG=""
else
SKIP_GIT_FLAG="--skip-git-repo-check"
fi
Step 4a: 判定代码作者
根据以下信号判定变更代码的作者模型:
- 用户明确告知("这是 Codex 写的"、"刚用 Codex 生成的")
- Git commit message 含
codex、openai等关键词 - 默认:假定为 Claude 产出(因为 a2a 从 Claude Code 发起,大多数场景是 Claude 刚写完代码后 review)
Step 4b: 按作者 + 当前环境选择 dispatch 通道
路径 A: Claude 产出 → Codex 审查(从 Claude Code 发起)
codex exec --cd "$(pwd)" $SKIP_GIT_FLAG --full-auto -- "按照以下审查清单 review 代码变更..."
--cd确保 Codex 在正确的项目目录下执行--skip-git-repo-check用于非 git 目录(自动检测,按需添加)--separator 防止 prompt 内容被误解析为 flag- 禁止传
--model参数——让 Codex 使用用户自己在~/.codex/config.toml配置的模型
路径 B: Codex 产出 → Claude 审查(从 Claude Code 发起)
通过 Agent tool spawn 隔离 sub-agent 执行 review(in-process,天然有 auth):
使用 Agent tool,为每个 lens 分别 spawn 一个独立 agent:
- agent 1: Challenger lens + 完整 diff
- agent 2: Architect lens + 文件列表/签名/摘要
- agent 3: Subtractor lens + 完整 diff + 新增文件
每个 agent 独立执行,互不可见(满足隔离要求)。
路径 C: Codex 产出 → Claude 审查(从 Codex 发起,token bridge)
当 a2a 从 Codex 环境发起时,通过 token bridge 调用 claude -p:
# 读取 preflight 缓存的 OAuth token
TOKEN=$(cat ~/.cache/a2a/.claude-token 2>/dev/null)
if [[ -z "$TOKEN" ]]; then
echo "错误:未找到 Claude token 缓存。请先在 Claude Code 或终端中运行:"
echo " ~/.claude/skills/a2a/scripts/preflight.sh --refresh"
exit 1
fi
# 通过环境变量传递 token,绕过 Keychain 限制
ANTHROPIC_API_KEY="$TOKEN" claude -p "按照以下审查清单 review 代码变更..."
前提:必须至少运行过一次
preflight.sh(从 Claude Code 或正常终端,非 Codex sandbox),以便将 OAuth token 从 macOS Keychain 缓存到文件。
路径 D: Codex 不可用 → Claude 单模型多视角(降级)
同路径 B 的 Agent tool 方式,但报告标注 mode: single-model-multi-lens。
每个 reviewer 收到专属 review packet(按 lens 裁剪,严格精简):
通用字段(所有 reviewer 共享):
- 变更 intent:1-2 句话概括目的,不传原始 plan 全文
- 审查视角定义:只给自己那个 lens(从 review-lenses.md 摘取对应段落)
Lens 专属 diff 裁剪:
| Lens | 收到的 diff 内容 |
|---|---|
| Challenger | 完整 diff(需要看具体代码找 bug) |
| Architect | 文件列表 + 函数签名 + 变更摘要(审结构不需要逐行) |
| Subtractor | 完整 diff + 新增文件列表(找多余代码) |
不传给 reviewer 的内容:
项目 red-line 约束→ 移到 Step 5 由 Claude 统一扫描CLAUDE.md / AGENT.md→ 不传给 reviewer- Plan / 设计文档 → 提炼为 1-2 句 intent,不传原文
Reviewer 输出限制(必须附加到每个 reviewer 的 prompt 末尾):
输出限制:
- 每条 finding ≤ 3 行(trigger + impact + fix)
- 总 findings ≤ 10 条
- 无 finding 时只输出 "LGTM",不要写分析过程
所有 reviewer 并行执行,互不可见对方结果(防止从众效应)。
Step 5: 汇总 Findings + Red-Line Scan
📊 收集所有 reviewer 发现,然后 Claude 统一执行一次 red-line scan
Red-Line Scan(仅在此步骤执行一次):
- 读取项目的
AGENT.md、CLAUDE.md、.ai/constraints.json - 提取 red-line 约束条目
- 对完整 diff 扫描一次,违规项作为 🔴 High finding 加入汇总
通用 red line(项目无配置时的兜底):
eval() / new Function() / innerHTML = → security risk
未经封装的 process.env 直接读取 → environment leak
硬编码 secret / token → credential exposure
汇总规则:保留 reviewer provenance,按 severity 分级
| Severity | 定义 | 示例 |
|---|---|---|
| 🔴 High | 会导致线上问题或数据丢失 | 未处理的 null、XSS injection、state race |
| 🟡 Medium | 不影响当前功能但埋坑 | coupling 过紧、缺少 error boundary、hardcode |
| 🟢 Low | 风格或可维护性建议 | 命名不清、注释缺失、可简化 |
主审裁定每条 finding:
- Accept — 确实是问题,需要修
- Dismiss — reviewer 过度解读或不适用当前场景
- Flag — 有道理但 non-blocking,记录为 TODO
Step 6: 输出 Verdict Report
表格自适应规则:根据 findings 数量和内容长度选择宽/窄模式。
固定 5 列:#, Sev, Lens, 问题, 裁定
| 字段 | 规则 |
|---|---|
| Sev | 只写 emoji:🔴🟡🟢,不加文字 |
| Lens | 缩写:Ch = Challenger, Ar = Architect, Su = Subtractor;多人 Ch+Ar |
| 问题 | `file:line` 嵌在开头,后接完整问题描述,不省略内容 |
| 裁定 | 自适应(见下方宽窄规则) |
宽模式(≤ 5 条 findings 或问题描述普遍短):
- 裁定列写完整:
Accept — 理由说明、Dismiss — 为什么不适用、`Flag — 记 TODO