c-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C/C++ Security Review

C/C++安全审查

Runs in the main conversation (invoke via
/c-review:c-review
). Orchestrator owns the
Task*
ledger as bookkeeping for retries; workers and judges have no Task tools. Workers and judges are named plugin subagents (
c-review:c-review-worker
,
c-review:c-review-dedup-judge
,
c-review:c-review-fp-judge
); tool sets are declared in
plugins/c-review/agents/*.md
. Findings are exchanged via markdown-with-YAML files in a shared output directory.
在主对话中运行(通过
/c-review:c-review
调用)。编排器负责维护
Task*
账本以记录重试情况;工作子代理(worker)和评审子代理(judge)没有Task工具。工作子代理和评审子代理是命名的插件子代理(
c-review:c-review-worker
c-review:c-review-dedup-judge
c-review:c-review-fp-judge
);工具集在
plugins/c-review/agents/*.md
中声明。检测结果通过共享输出目录中的带YAML的Markdown文件进行交换。

When to Use

适用场景

Native C/C++ application security review: memory safety, integer overflow, races, type confusion, Linux/macOS daemons, Windows userspace services.
原生C/C++应用程序安全审查:内存安全、整数溢出、竞争条件、类型混淆、Linux/macOS守护进程、Windows用户空间服务。

When NOT to Use

不适用场景

  • Kernel drivers/modules (Linux, Windows, macOS).
  • Managed languages (Java, C#, Python, Go, Rust).
  • Embedded/bare-metal code without libc.
  • 内核驱动/模块(Linux、Windows、macOS)。
  • 托管语言(Java、C#、Python、Go、Rust)。
  • 无libc的嵌入式/裸机代码。

Subagents

子代理

Subagent typePurposeTool set
c-review:c-review-worker
Run assigned cluster, write findingsRead, Write, Edit, Grep, Glob, Bash
c-review:c-review-dedup-judge
Merge duplicates (runs first)Read, Write, Edit, Glob
c-review:c-review-fp-judge
FP + severity + final reports (runs second)Read, Write, Edit, Grep, Glob, Bash
Tools come from each agent's frontmatter at spawn time. The orchestrator's
Task*
/
Agent
/
Bash
/etc. come from this skill's
allowed-tools
.

子代理类型用途工具集
c-review:c-review-worker
运行指定的审查集群,撰写检测结果Read、Write、Edit、Grep、Glob、Bash
c-review:c-review-dedup-judge
合并重复结果(优先运行)Read、Write、Edit、Glob
c-review:c-review-fp-judge
误报处理 + 严重性评估 + 最终报告(其次运行)Read、Write、Edit、Grep、Glob、Bash
工具来自每个代理启动时的前置信息。编排器的
Task*
/
Agent
/
Bash
等工具来自本技能的
allowed-tools
配置。

Architecture

架构

coordinator: write context.md → build_run_plan.py → TaskCreate × M
          → spawn primer (foreground) → spawn M workers (parallel)
          → classify Phase-7 outcomes + write findings-index.txt
          → dedup-judge → fp-judge → SARIF safety net → return REPORT.md
Output directory contains:
context.md
,
plan.json
,
worker-prompts/
,
findings/
,
findings-index.d/
(per-worker shards),
findings-index.txt
,
run-summary.md
,
dedup-summary.md
,
fp-summary.md
,
REPORT.md
,
REPORT.sarif
.
Path convention: set
${C_REVIEW_PLUGIN_ROOT}=${CLAUDE_PLUGIN_ROOT}
if that resolves (
Bash: ls "${CLAUDE_PLUGIN_ROOT}/prompts/clusters/buffer-write-sinks.md"
), otherwise
Bash: find ~/.claude -path '*/plugins/c-review/prompts/clusters/buffer-write-sinks.md' -print -quit
.
Scope convention: keep two scopes separate throughout the run:
  • finding_scope_root
    — the user-requested audit subtree. Workers may only file findings whose vulnerable location is inside this subtree.
  • context_roots
    — read-only repo roots/files workers and judges may inspect to verify reachability, callers, wrappers, build flags, mitigations, and threat-model details. Default to
    .
    unless the user explicitly forbids broader context. Reading context outside
    finding_scope_root
    is allowed; filing findings there is not.

coordinator: write context.md → build_run_plan.py → TaskCreate × M
          → spawn primer (foreground) → spawn M workers (parallel)
          → classify Phase-7 outcomes + write findings-index.txt
          → dedup-judge → fp-judge → SARIF safety net → return REPORT.md
输出目录包含以下文件:
context.md
plan.json
worker-prompts/
findings/
findings-index.d/
(按工作子代理分片)、
findings-index.txt
run-summary.md
dedup-summary.md
fp-summary.md
REPORT.md
REPORT.sarif
路径约定:如果
${CLAUDE_PLUGIN_ROOT}
能解析,则设置
${C_REVIEW_PLUGIN_ROOT}=${CLAUDE_PLUGIN_ROOT}
(Bash命令:
ls "${CLAUDE_PLUGIN_ROOT}/prompts/clusters/buffer-write-sinks.md"
);否则执行Bash命令:
find ~/.claude -path '*/plugins/c-review/prompts/clusters/buffer-write-sinks.md' -print -quit
范围约定:在整个运行过程中保持两个范围分离:
  • finding_scope_root
    — 用户请求的审计子树。工作子代理只能提交漏洞位置在此子树内的检测结果。
  • context_roots
    — 工作子代理和评审子代理可查看的只读仓库根目录/文件,用于验证可达性、调用者、包装器、构建标志、缓解措施和威胁模型细节。默认值为
    .
    ,除非用户明确禁止更广泛的上下文。允许读取
    finding_scope_root
    之外的上下文,但不允许在此范围内提交检测结果。

Rationalizations to Reject

需要避免的错误做法

  • "Background spawns parallelize the workers." They do not —
    Agent
    calls in a single assistant message already run concurrently.
    run_in_background=true
    defeats the Phase 6a primer cache, so every worker pays full cache-creation (
    cache_read_input_tokens=0
    ) and the ~15 K-token primer is wasted M times. This is the single most common defect — multiple recent runs spawned 7-of-8 (or all) workers with
    bg=true
    . Default: omit
    run_in_background
    from worker spawns.
  • "I'll re-derive the cluster list / paths / pass prefixes inline instead of running
    build_run_plan.py
    ."
    The script is the only authority for selection and rendering. Paraphrasing it drops fields that the worker self-check requires, producing
    worker-N abort: spawn prompt malformed
    . Always run the script and
    Read plan.json
    .
  • "The run partially succeeded — I'll just write
    REPORT.md
    from what completed."
    Hiding partial runs behind a successful report is a correctness bug. If any Phase-5 cluster task is not
    completed
    , surface it prominently in
    run-summary.md
    and the final response.
  • "Zero findings — skip Phase 8." Always run both judges and Phase 8b: dedup-judge writes a minimal no-op
    dedup-summary.md
    on an empty index, fp-judge writes empty
    REPORT.md
    /
    REPORT.sarif
    , and Phase 8b's SARIF generator emits
    results: []
    for the empty case. SARIF consumers depend on a stable artifact set.
  • "
    Bash: ls README*
    is fine for the preflight."
    Under zsh, an unmatched glob aborts the whole compound command before
    2>/dev/null
    runs. Use
    Glob
    (preferred) or
    find
    (never fails on no-match).

  • “后台启动工作子代理以实现并行化。” 这不可行 — 单个助手消息中的
    Agent
    调用已经会并发运行。
    run_in_background=true
    会破坏Phase 6a的缓存预热机制,导致每个工作子代理都要重新创建缓存(
    cache_read_input_tokens=0
    ),预热的约15K token会被浪费M次。这是最常见的缺陷 — 近期多次运行中,7/8(或全部)的工作子代理都以
    bg=true
    启动。默认做法:在工作子代理启动时省略
    run_in_background
    参数。
  • “我将直接推导集群列表/路径/前缀,而不是运行
    build_run_plan.py
    。”
    该脚本是选择和渲染的唯一权威。改写脚本会丢失工作子代理自检所需的字段,导致
    worker-N abort: spawn prompt malformed
    错误。务必运行脚本并读取
    plan.json
  • “运行部分成功 — 我将根据已完成的内容撰写
    REPORT.md
    。”
    在成功报告背后隐藏部分运行结果是正确性缺陷。如果任何Phase-5集群任务未标记为
    completed
    ,需在
    run-summary.md
    和最终响应中突出显示。
  • “未发现任何漏洞 — 跳过Phase 8。” 无论是否发现漏洞,都必须运行两个评审子代理和Phase 8b:当索引为空时,去重评审子代理会生成一个最小化的无操作
    dedup-summary.md
    ,误报评审子代理会生成空的
    REPORT.md
    /
    REPORT.sarif
    ,Phase 8b的SARIF生成器会针对空情况输出
    results: []
    。SARIF依赖稳定的工件集。
  • Bash: ls README*
    用于预检没问题。”
    在zsh中,不匹配的通配符会在
    2>/dev/null
    执行前终止整个复合命令。优先使用
    Glob
    工具,或使用
    find
    (无匹配时不会失败)。

Orchestration Workflow

编排工作流

Run these phases in the main conversation.
主对话中按以下阶段运行。

Phase 0: Parameter Collection

Phase 0:参数收集

Entry: skill invoked. Exit:
threat_model
,
worker_model
,
severity_filter
resolved;
scope_subpath
resolved or set to
"."
;
finding_scope_root=scope_subpath
;
context_roots
resolved.
The skill is invoked directly (no command wrapper). Parse any free-text arguments the user passed on the
/c-review:c-review
line (e.g.
flamenco only
,
high severity only
,
use haiku
) and pre-fill the answers they imply — then ask for any missing required parameters with one
AskUserQuestion
call. Never silently default the required parameters.
Required parameters:
ParameterValuesHow to infer from args
threat_model
REMOTE
/
LOCAL_UNPRIVILEGED
/
BOTH
Words like "remote", "network", "attacker" →
REMOTE
; "local", "unprivileged" →
LOCAL_UNPRIVILEGED
; otherwise ask.
worker_model
haiku
/
sonnet
/
opus
Explicit model name in args. Otherwise ask (no silent default).
severity_filter
all
/
medium
/
high
"all", "every", "noisy" →
all
; "medium and above" →
medium
; "high only", "criticals only" →
high
. Otherwise ask — no silent default.
scope_subpath
repo-relative directory (optional)Phrases like "X only", "just audit X/", "review subdirectory X" →
src/X/
or the matching subdir. Apply fuzzy matching against top-level subdirectories of the repo. If absent, set
"."
; if ambiguous, ask.
Call
AskUserQuestion
exactly once with only unresolved required parameters (
threat_model
,
worker_model
,
severity_filter
) plus
scope_subpath
only when the user explicitly requested a narrowed scope but it is ambiguous. If the required parameters were all pre-filled and scope is absent or resolved, skip the question.
After resolving
scope_subpath
, set
finding_scope_root="${scope_subpath:-.}"
. Set
context_roots="."
by default so workers can verify callers/build settings outside a narrowed subtree without filing out-of-scope findings. If the user explicitly asks to forbid broader context, set
context_roots="${finding_scope_root}"
and note that reachability confidence may be lower.
进入条件:技能被调用。退出条件
threat_model
worker_model
severity_filter
已确定;
scope_subpath
已确定或设为
"."
finding_scope_root=scope_subpath
context_roots
已确定。
直接调用技能(无命令包装)。解析用户在
/c-review:c-review
行中传递的任意自由文本参数(例如
flamenco only
high severity only
use haiku
)并预填充对应的答案 — 然后通过一次
AskUserQuestion
调用询问任何缺失的必填参数。切勿静默设置必填参数的默认值。
必填参数:
参数可选值如何从参数推断
threat_model
REMOTE
/
LOCAL_UNPRIVILEGED
/
BOTH
出现“remote”、“network”、“attacker”等词 →
REMOTE
;出现“local”、“unprivileged”等词 →
LOCAL_UNPRIVILEGED
;否则询问用户。
worker_model
haiku
/
sonnet
/
opus
参数中明确指定模型名称。否则询问用户(无静默默认值)。
severity_filter
all
/
medium
/
high
出现“all”、“every”、“noisy” →
all
;出现“medium and above” →
medium
;出现“high only”、“criticals only” →
high
。否则询问用户 — 无静默默认值
scope_subpath
仓库相对目录(可选)出现“X only”、“just audit X/”、“review subdirectory X”等表述 →
src/X/
或匹配的子目录。对仓库顶级子目录进行模糊匹配。如果未指定,设为
"."
;如果存在歧义,询问用户。
仅对未解决的必填参数(
threat_model
worker_model
severity_filter
)以及用户明确要求缩小范围但范围不明确时的
scope_subpath
,调用一次
AskUserQuestion
。如果所有必填参数已预填充且范围已确定或未指定,则跳过此询问。
确定
scope_subpath
后,设置
finding_scope_root="${scope_subpath:-.}"
。默认设置
context_roots="."
,以便工作子代理可以验证缩小范围的子树之外的调用者/构建设置,而不会提交超出范围的检测结果。如果用户明确禁止更广泛的上下文,设置
context_roots="${finding_scope_root}"
并注明可达性置信度可能降低。

Phase 1: Prerequisites

Phase 1:前置检查

Entry: Phase 0 complete. Exit:
is_cpp
,
is_posix
,
is_windows
flags determined.
Probe within
${finding_scope_root:-.}
. Prefer
Glob
/
Grep
when available in the orchestrator's tool set; some sessions only expose
Bash
, so fall back to the equivalents below — both forms produce identical signals (non-empty output ⇒ flag true):
bash
undefined
进入条件:Phase 0完成。退出条件
is_cpp
is_posix
is_windows
标志已确定。
${finding_scope_root:-.}
范围内探测。优先使用编排器工具集中的
Glob
/
Grep
;某些会话仅支持
Bash
,因此可回退到以下等效命令 — 两种形式产生相同的信号(非空输出 ⇒ 标志为true):
bash
undefined

is_cpp

is_cpp

find "${finding_scope_root:-.}" -type f ( -name '.cpp' -o -name '.cxx' -o -name '.cc' -o -name '.hpp' -o -name '*.hh' ) -print -quit
find "${finding_scope_root:-.}" -type f \( -name '.cpp' -o -name '.cxx' -o -name '.cc' -o -name '.hpp' -o -name '*.hh' \) -print -quit

is_posix

is_posix

grep -rlE '#include[[:space:]]<(pthread|signal|sys/(socket|stat|types|wait)|unistd|errno).h>'
--include='
.c' --include='.h'
--include='
.cpp' --include='.cxx' --include='.cc' --include='.hpp' --include='.hh'
"${finding_scope_root:-.}" | head -1
grep -rlE '#include[[:space:]]<(pthread|signal|sys/(socket|stat|types|wait)|unistd|errno)\.h>' \ --include='.c' --include='.h' \ --include='.cpp' --include='.cxx' --include='.cc' --include='.hpp' --include='.hh' \ "${finding_scope_root:-.}" | head -1

is_windows

is_windows

grep -rlE '#include[[:space:]]<(windows|winbase|winnt|winuser|winsock|ntdef|ntstatus).h>'
--include='
.c' --include='.h'
--include='
.cpp' --include='.cxx' --include='.cc' --include='.hpp' --include='.hh'
"${finding_scope_root:-.}" | head -1

`compile_commands.json` is informational (no agent currently uses LSP), but the probe is mandatory so the run summary records whether richer local tooling is available. Probe via `Glob: **/compile_commands.json` under `${context_roots}`. If `Glob` is unavailable, use:

```bash
printf '%s\n' "${context_roots:-.}" | tr ',' '\n' | while IFS= read -r root; do
  [ -n "$root" ] && find "$root" -name compile_commands.json -print -quit
done | head -1
grep -rlE '#include[[:space:]]<(windows|winbase|winnt|winuser|winsock|ntdef|ntstatus)\.h>' \ --include='.c' --include='.h' \ --include='.cpp' --include='.cxx' --include='.cc' --include='.hpp' --include='.hh' \ "${finding_scope_root:-.}" | head -1

`compile_commands.json`仅作参考(当前没有代理使用LSP),但必须进行探测,以便运行摘要记录是否有更丰富的本地工具可用。通过`Glob: **/compile_commands.json`在`${context_roots}`下探测。如果`Glob`不可用,使用:

```bash
printf '%s\
' "${context_roots:-.}" | tr ',' '\
' | while IFS= read -r root; do
  [ -n "$root" ] && find "$root" -name compile_commands.json -print -quit
done | head -1

find "$root"
is quoted intentionally so a context root containing spaces

find "$root"
被引号包裹,因此包含空格的上下文根目录

(e.g. "/Users/me/My Repo") survives word-splitting. Do not unquote it.


If absent, suggest CMake `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`/Bear/compiledb to the user but continue.
#(例如"/Users/me/My Repo")不会被拆分。请勿去掉引号。

如果不存在,建议用户使用CMake的`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`参数、Bear或compiledb工具,但继续运行流程。

Phase 2: Output Directory

Phase 2:输出目录

Entry: Phase 1 flags set. Exit: absolute
output_dir
resolved;
${output_dir}/findings/
exists.
Resolve an absolute path for
output_dir
(default:
$(pwd)/.c-review-results/$(date -u +%Y%m%dT%H%M%SZ)/
):
bash
mkdir -p "${output_dir}/findings"
进入条件:Phase 1的标志已设置。退出条件:绝对路径
output_dir
已确定;
${output_dir}/findings/
已存在。
解析
output_dir
的绝对路径(默认值:
$(pwd)/.c-review-results/$(date -u +%Y%m%dT%H%M%SZ)/
):
bash
mkdir -p "${output_dir}/findings"

Phase 3: Codebase Context

Phase 3:代码库上下文

Entry:
${output_dir}
exists. Exit:
${output_dir}/context.md
written.
Skim
README.{md,rst,txt}
and any build file (
Makefile
,
CMakeLists.txt
,
meson.build
,
configure.ac
) — preflight with the
Glob
tool before any
Read
(a
Read
on a missing file aborts the turn). Do not use
Bash: ls README*
for the preflight: under zsh, an unmatched glob aborts the whole compound command before
2>/dev/null
runs (observed: a Phase-3
ls src/X/README*
call failed with
no matches found
and dropped the entire preflight). If you must use
Bash
, use
find . -maxdepth 2 -name 'README*' -o -name 'Makefile' -o -name 'CMakeLists.txt' -o -name 'meson.build'
, which never fails on no-match.
Write
${output_dir}/context.md
with: YAML frontmatter (
threat_model
,
severity_filter
,
scope_subpath
,
finding_scope_root
,
context_roots
,
is_cpp
,
is_posix
,
is_windows
,
output_dir
,
compile_commands
as
present
/
absent
plus path when present), then a short markdown body with five sections — Purpose (1-3 sentences), Scope (what's in
finding_scope_root
, and that findings outside it are out of scope), Entry points (where untrusted data enters: network, files, CLI, IPC), Trust boundaries (sandboxed vs trusted peers vs arbitrary remote), Existing hardening (fuzzing corpora, sanitizers, privilege separation).
进入条件
${output_dir}
已存在。退出条件
${output_dir}/context.md
已写入。
浏览
README.{md,rst,txt}
和任何构建文件(
Makefile
CMakeLists.txt
meson.build
configure.ac
) — 在进行任何
Read
操作前先用
Glob
工具预检(读取不存在的文件会中断流程)。请勿使用
Bash: ls README*
进行预检:在zsh中,不匹配的通配符会在
2>/dev/null
执行前终止整个复合命令(已发现:Phase-3的
ls src/X/README*
调用因
no matches found
失败,导致整个预检流程中断)。如果必须使用
Bash
,使用
find . -maxdepth 2 -name 'README*' -o -name 'Makefile' -o -name 'CMakeLists.txt' -o -name 'meson.build'
,该命令在无匹配时不会失败。
写入
${output_dir}/context.md
,包含:YAML前置信息(
threat_model
severity_filter
scope_subpath
finding_scope_root
context_roots
is_cpp
is_posix
is_windows
output_dir
compile_commands
设为
present
/
absent
,存在时附加路径),然后是简短的Markdown正文,包含五个部分 — 用途(1-3句话)、范围
finding_scope_root
包含的内容,以及超出此范围的结果不纳入报告)、入口点(不可信数据的进入位置:网络、文件、CLI、IPC)、信任边界(沙盒环境 vs 可信对等方 vs 任意远程攻击者)、现有加固措施(模糊测试语料库、 sanitizer、权限分离)。

Phase 4: Build Run Plan (deterministic)

Phase 4:构建运行计划(确定性)

Entry: language flags +
threat_model
known;
${output_dir}/findings/
exists. Exit:
${output_dir}/plan.json
and
${output_dir}/worker-prompts/*.txt
written;
M = worker_count
known.
Selection, filtering, path resolution, and spawn-prompt rendering are delegated to the script to prevent the "orchestrator paraphrases the spawn template and drops fields" failure mode:
bash
python3 "${C_REVIEW_PLUGIN_ROOT}/scripts/build_run_plan.py" \
  --plugin-root "${C_REVIEW_PLUGIN_ROOT}" --output-dir "${output_dir}" \
  --threat-model "${threat_model}" --severity-filter "${severity_filter}" \
  --scope-subpath "${finding_scope_root:-.}" --context-roots "${context_roots:-.}" \
  --is-cpp "${is_cpp}" --is-posix "${is_posix}" --is-windows "${is_windows}"
The script writes
plan.json
+
worker-prompts/worker-N.txt
+ (if
--cache-primer=true
, the default)
worker-prompts/cache-primer.txt
, and prints a JSON summary on stdout. Exits non-zero on any missing prompt — surface the message and stop. Typical M: 7 (C POSIX), 8 (C++ POSIX), 10 (C POSIX + Windows), 11 (C++ POSIX + Windows). After it returns,
Read plan.json
for the structured selection — never re-derive filtering or paths.
进入条件:语言标志 +
threat_model
已知;
${output_dir}/findings/
已存在。退出条件
${output_dir}/plan.json
${output_dir}/worker-prompts/*.txt
已写入;
M = worker_count
已知。
选择、过滤、路径解析和启动提示渲染委托给脚本,以避免“编排器改写启动模板并丢失字段”的失败模式:
bash
python3 "${C_REVIEW_PLUGIN_ROOT}/scripts/build_run_plan.py" \\
  --plugin-root "${C_REVIEW_PLUGIN_ROOT}" --output-dir "${output_dir}" \\
  --threat-model "${threat_model}" --severity-filter "${severity_filter}" \\
  --scope-subpath "${finding_scope_root:-.}" --context-roots "${context_roots:-.}" \\
  --is-cpp "${is_cpp}" --is-posix "${is_posix}" --is-windows "${is_windows}"
该脚本会写入
plan.json
+
worker-prompts/worker-N.txt
+(如果
--cache-primer=true
,默认启用)
worker-prompts/cache-primer.txt
,并在标准输出打印JSON摘要。如果缺少任何提示,会返回非零退出码 — 显示错误信息并停止运行。典型的M值:7(C POSIX)、8(C++ POSIX)、10(C POSIX + Windows)、11(C++ POSIX + Windows)。脚本返回后,读取
plan.json
获取结构化选择结果 — 切勿重新推导过滤规则或路径。

Phase 5: Create Bookkeeping Tasks (orchestrator-internal)

Phase 5:创建记账任务(编排器内部)

Entry:
${output_dir}/plan.json
exists;
M = plan.workers.length
. Exit:
cluster_task_ids[]
created (1:1 with
plan.workers
), all
pending
.
The task ledger is orchestrator bookkeeping only (TUI visibility + Phase-7 retry tracking) — workers never read or write it. One
TaskCreate
per worker, populating
metadata
with
kind="cluster"
,
worker_n
,
cluster_id
,
spawn_prompt_path
,
pass_prefixes
,
attempt=1
— all values copied verbatim from
plan.workers[i]
. Track
cluster_task_ids[]
in
plan.workers
order.
进入条件
${output_dir}/plan.json
已存在;
M = plan.workers.length
退出条件
cluster_task_ids[]
已创建(与
plan.workers
一一对应),状态均为
pending
任务账本仅用于编排器记账(TUI可见性 + Phase-7重试跟踪) — 工作子代理不会读取或写入它。每个工作子代理对应一个
TaskCreate
,在
metadata
中填充
kind="cluster"
worker_n
cluster_id
spawn_prompt_path
pass_prefixes
attempt=1
— 所有值均直接复制自
plan.workers[i]
。按
plan.workers
的顺序跟踪
cluster_task_ids[]

Phase 6: Spawn workers (optional cache-primer first, then M in parallel)

Phase 6:启动工作子代理(可选先缓存预热,再并行启动M个)

Entry:
cluster_task_ids[]
populated; per-worker spawn prompt files exist at
${output_dir}/worker-prompts/worker-N.txt
. Exit: all M
Agent
calls have returned (the parallel spawn block completed).
进入条件
cluster_task_ids[]
已填充;每个工作子代理的启动提示文件位于
${output_dir}/worker-prompts/worker-N.txt
退出条件:所有M个
Agent
调用已返回(并行启动块已完成)。

Phase 6a: Cache primer (gated on
plan.run.cache_primer
)

Phase 6a:缓存预热(受
plan.run.cache_primer
控制)

A parallel batch from cold start cannot share cache (all M requests dispatch simultaneously, none has finished writing). To warm the prefix, spawn a tiny primer first — foreground (background spawns don't share cache with subsequent foreground spawns).
If
plan.run.cache_primer == true
,
build_run_plan.py
has written
${output_dir}/worker-prompts/cache-primer.txt
. Spawn it in its own assistant message:
Read
the file, pass verbatim as
Agent
prompt
with
subagent_type=c-review:c-review-worker
,
model=${worker_model}
,
description="C review cache primer"
, no
run_in_background
. The script wrote the prefix byte-identical to
worker-1.txt
through the
<context>
block — that byte-identity is what gives the parallel workers their cache hit. The primer trailer contains
Cache primer: true
, which the worker system prompt treats as a first-class mode and returns exactly
worker-PRIMER abort: cache primer (no analysis performed)
in one text response with zero tool calls. Discard the abort line — Phase 7 ignores it (no
worker-N
id).
Foreground spawn already serializes — no
sleep
needed before Phase 6b. Skip Phase 6a entirely if
plan.run.cache_primer == false
.
冷启动的并行批次无法共享缓存(所有M个请求同时分发,没有请求完成写入缓存)。为了预热前缀,先启动一个小型预热代理 — 前台启动(后台启动的代理不会与后续前台启动的代理共享缓存)。
如果
plan.run.cache_primer == true
build_run_plan.py
会写入
${output_dir}/worker-prompts/cache-primer.txt
。在单独的助手消息中启动它:读取该文件,将内容直接作为
Agent
prompt
参数,设置
subagent_type=c-review:c-review-worker
model=${worker_model}
description="C review cache primer"
,不设置
run_in_background
。脚本写入的前缀与
worker-1.txt
<context>
块完全一致 — 这种字节一致性是并行工作子代理命中缓存的关键。预热代理的结尾包含
Cache primer: true
,工作子代理的系统提示会将其视为一等模式,并返回
worker-PRIMER abort: cache primer (no analysis performed)
的文本响应,无工具调用。忽略此终止行 — Phase 7会忽略它(无
worker-N
ID)。
前台启动已自动序列化 — Phase 6b前无需
sleep
。如果
plan.run.cache_primer == false
,则完全跳过Phase 6a。

Phase 6b: Spawn M real workers in ONE message

Phase 6b:在一条消息中启动M个实际工作子代理

STOP — read this before composing the spawn message.
Workers MUST be spawned foreground (no
run_in_background
field, or
run_in_background=false
). "Parallel" here means one assistant message containing M
Agent
calls
— that already runs them concurrently. Background spawns are NOT how you parallelize this skill.
Background spawns defeat Phase 6a's primer cache: every worker pays full cache-creation on its first turn (
cache_read_input_tokens=0
), and the primer's ~15 K tokens are wasted M times over. Two real runs (audit logs available) had exactly this symptom — every worker started with
first_cr=0
.
Before sending the spawn message, audit your draft: every
Agent
call must have no
run_in_background
key. If you wrote
run_in_background=true
, delete it.
Required spawn shape: emit a single assistant message containing M
Agent
tool invocations. Sequential spawning serializes the review and is also wrong, but that failure is loud (timing); the background-spawn failure is silent (cost).
For each worker
N ∈ [1..M]
:
  1. Read: ${output_dir}/worker-prompts/worker-N.txt
  2. Pass the file contents verbatim as the
    Agent
    tool's
    prompt
    argument:
ParameterValue
subagent_type
c-review:c-review-worker
model
${worker_model}
(haiku / sonnet / opus)
description
C review worker N
prompt
the full text of
worker-N.txt
(no edits)
run_in_background
field MUST be omitted, OR set to
false
.
Never
true
. See the foreground-spawn warning above.
The spawn prompt is the single authority. Pass it verbatim — every field is required by the worker's self-check; any deviation triggers
worker-N abort: spawn prompt malformed
.
Anti-patterns to reject:
  • Passing
    run_in_background=true
    (the dominant historical defect — see warning above).
  • Hand-typing the spawn prompt instead of reading
    worker-N.txt
    .
  • Inserting Task-related instructions ("first call TaskList", "Assigned task id: <N>"). Workers have no Task tools.
  • Editing the rendered prompt before passing it (trimming "redundant" fields, collapsing pass lists).
注意 — 撰写启动消息前请阅读此内容。
工作子代理必须前台启动(不设置
run_in_background
字段,或设为
run_in_background=false
)。 此处的“并行”指一条助手消息包含M个
Agent
调用
— 这已经会并发运行它们。后台启动不是此技能实现并行的方式。
后台启动会破坏Phase 6a的预热缓存:每个工作子代理在第一轮都要重新创建缓存(
cache_read_input_tokens=0
),预热的约15K token会被浪费M次。两次实际运行(审计日志可用)都出现了此症状 — 每个工作子代理都以
first_cr=0
启动。
发送启动消息前,检查草稿:每个
Agent
调用必须没有
run_in_background
键。如果写了
run_in_background=true
,请删除它。
启动消息的必填格式:生成一条包含M个
Agent
工具调用的助手消息。顺序启动会序列化审查流程,这也是错误的,但这种失败很明显(耗时);而后台启动的失败是静默的(成本浪费)。
对于每个工作子代理
N ∈ [1..M]
  1. 执行
    Read: ${output_dir}/worker-prompts/worker-N.txt
  2. 将文件内容直接作为
    Agent
    工具的
    prompt
    参数:
参数
subagent_type
c-review:c-review-worker
model
${worker_model}
(haiku / sonnet / opus)
description
C review worker N
prompt
worker-N.txt
的完整文本(不修改)
run_in_background
必须省略此字段,或设为
false
切勿设为
true
。参见上述前台启动警告。
启动提示是唯一权威。直接传递内容 — 工作子代理的自检需要每个字段;任何偏差都会触发
worker-N abort: spawn prompt malformed
错误。
需要避免的反模式
  • 设置
    run_in_background=true
    (历史上最常见的缺陷 — 参见上述警告)。
  • 手动输入启动提示而非读取
    worker-N.txt
  • 插入与Task相关的指令(“先调用TaskList”、“分配的任务ID:<N>”)。工作子代理没有Task工具。
  • 在传递前修改渲染后的提示(修剪“冗余”字段、合并前缀列表)。

Phase 7: Wait for Workers and Classify Outcomes

Phase 7:等待工作子代理并分类结果

Entry: all M Phase-6
Agent
calls have returned. Exit: every cluster has either succeeded or been retried up to the cap;
${output_dir}/findings-index.txt
written.
The Phase-6
Agent
invocations block until each worker returns. Inspect each worker's return text and apply this classifier in order — first match wins:
#Match (in return text)OutcomeAction
1
worker-N complete:
success
TaskUpdate
to
completed
.
2
abort: spawn prompt malformed
,
abort: pre-work budget exceeded
, or
abort: TaskList unavailable
(legacy)
non-retryable orchestrator bugStop the run, surface the abort + spawn-prompt path. Re-running the same prompt repeats the failure — pre-work-budget exhaustion always means the worker couldn't pass its self-check, which a retry won't fix.
3other
worker-N abort:
retryableMark
pending
, set
metadata.abort_reason
,
needs_respawn=true
, increment
attempt
.
4
Agent
errored or no
complete:
/
abort:
token
retryableSame as #3 (transient worker crash).
If any non-retryable, stop. Otherwise re-spawn each
pending
retryable with
attempt < 2
in one parallel block (cap = 2 attempts per cluster). Replacement workers can safely overwrite partial files — finding IDs are deterministic per prefix.
进入条件:所有Phase-6的
Agent
调用已返回。退出条件:每个集群要么成功,要么已重试至上限;
${output_dir}/findings-index.txt
已写入。
Phase-6的
Agent
调用会阻塞直到每个工作子代理返回。检查每个工作子代理的返回文本,并按以下顺序分类 — 匹配第一个规则即停止:
#匹配内容(返回文本中)结果操作
1
worker-N complete:
成功
TaskUpdate
将状态设为
completed
2
abort: spawn prompt malformed
abort: pre-work budget exceeded
abort: TaskList unavailable
(旧版)
不可重试的编排器错误停止运行,显示终止信息和启动提示路径。重新运行相同的提示会重复失败 — 预工作预算耗尽通常意味着工作子代理无法通过自检,重试无法解决问题。
3其他
worker-N abort:
可重试标记为
pending
,设置
metadata.abort_reason
needs_respawn=true
,递增
attempt
4
Agent
调用出错或无
complete:
/
abort:
标记
可重试与#3处理方式相同(工作子代理临时崩溃)。
如果存在不可重试的错误,停止运行。否则,将每个
pending
attempt < 2
的可重试任务在一个并行块中重新启动(每个集群最多重试2次)。替换的工作子代理可以安全地覆盖部分文件 — 检测结果ID按前缀是确定性的。

Sanity-check + write index

sanity检查 + 写入索引

For every
complete:
cluster, list
${output_dir}/findings/${prefix}-*.md
for each
pass_prefix
(from
plan.json
). A worker that says "wrote N finding files" with N>0 but zero files on disk is suspicious — treat as retryable (classifier row #4). Zero claimed + zero on disk is fine.
Then build the index — workers wrote per-worker shards under
${output_dir}/findings-index.d/
, prefer those:
bash
undefined
对于每个
complete:
的集群,列出
plan.json
中每个
pass_prefix
对应的
${output_dir}/findings/${prefix}-*.md
文件。如果工作子代理声称“已写入N个检测结果文件”且N>0,但磁盘上没有文件,这是可疑的 — 按可重试处理(分类规则#4)。声称0个且磁盘上无文件是正常的。
然后构建索引 — 工作子代理在
${output_dir}/findings-index.d/
下写入了按工作子代理分片的文件,优先使用这些文件:
bash
undefined

Use
find
rather than a
worker-*.txt
glob: zsh aborts the compound command on no-match

使用
find
而非
worker-*.txt
通配符:zsh在无匹配时会终止复合命令

even with
2>/dev/null
, so an empty findings-index.d would otherwise drop the index file.

即使使用
2>/dev/null
,因此空的findings-index.d会导致索引文件丢失。

awk 1
(vs
cat
) normalizes a missing trailing newline on any shard, so a future

awk 1
(替代
cat
)会规范化任何分片末尾缺失的换行符,因此当未来的工作子代理

worker that writes shards via Write/printf instead of
ls -1 | sort
can't silently glue

通过Write/printf而非
ls -1 | sort
写入分片时,不会在去重时将一个分片的最后路径与下一个分片的第一个路径拼接在一起。

the last path of one shard onto the first of the next when sort -u dedupes.

if [ -d "${output_dir}/findings-index.d" ]; then find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null
| sort -u > "${output_dir}/findings-index.txt" else find "${output_dir}/findings" -maxdepth 1 -type f -name '
.md' 2>/dev/null | sort > "${output_dir}/findings-index.txt" fi

`sort -u` collapses duplicates from Phase-7 retries. Empty file is the unambiguous "zero findings" signal. Cross-check the line count against the sum of `wrote N` worker claims; log mismatches but don't abort.

After task updates and index creation, run `TaskList` and write `${output_dir}/run-summary.md` with:

- resolved parameters (`threat_model`, `severity_filter`, `finding_scope_root`, `context_roots`, language/platform flags, compile-commands status)
- worker outcome table (`worker_n`, `cluster_id`, claimed finding count, shard line count, task status, retry/abort state)
- `findings-index.txt` line count and any mismatch against worker claims
- judge status once Phase 8 finishes, or the reason a judge was skipped/failed

If any Phase-5 cluster task is not `completed`, include it prominently in `run-summary.md` and the final response. Do not hide a partial run behind a successful report.

**Always run Phase 8 even on zero findings** — both judges short-circuit on an empty index: dedup-judge writes a minimal no-op `dedup-summary.md`, and fp-judge writes empty `REPORT.md`/`REPORT.sarif` so SARIF consumers get a stable artifact set.
if [ -d "${output_dir}/findings-index.d" ]; then find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null \ | sort -u > "${output_dir}/findings-index.txt" else find "${output_dir}/findings" -maxdepth 1 -type f -name '.md' 2>/dev/null | sort > "${output_dir}/findings-index.txt" fi

`sort -u`会合并Phase-7重试产生的重复项。空文件明确表示“未发现任何漏洞”。将行数与工作子代理声称的总数交叉核对;记录不匹配情况但不终止运行。

完成任务更新和索引创建后,运行`TaskList`并写入`${output_dir}/run-summary.md`,包含:

- 已解析的参数(`threat_model`、`severity_filter`、`finding_scope_root`、`context_roots`、语言/平台标志、compile-commands状态)
- 工作子代理结果表(`worker_n`、`cluster_id`、声称的检测结果数量、分片行数、任务状态、重试/终止状态)
- `findings-index.txt`的行数以及与工作子代理声称数量的任何不匹配情况
- Phase 8完成后的评审子代理状态,或评审子代理被跳过/失败的原因

如果任何Phase-5集群任务未标记为`completed`,需在`run-summary.md`和最终响应中突出显示。切勿在成功报告背后隐藏部分运行结果。

**即使未发现任何漏洞,也必须运行Phase 8** — 两个评审子代理在索引为空时会短路:去重评审子代理会生成最小化的无操作`dedup-summary.md`,误报评审子代理会生成空的`REPORT.md`/`REPORT.sarif`,以便SARIF消费者获得稳定的工件集。

Phase 8: Judge Pipeline (sequential, dedup → fp+severity)

Phase 8:评审流水线(顺序执行,去重 → 误报+严重性评估)

Entry:
findings-index.txt
exists. Exit: dedup-judge and fp-judge have returned;
dedup-summary.md
,
fp-summary.md
,
REPORT.md
, and ideally
REPORT.sarif
are written.
Each judge's full protocol is its system prompt (
agents/c-review-{dedup,fp}-judge.md
); spawn prompts pass only per-run variables. Do not reference
prompts/internal/judges/
— those files don't exist.
Spawn sequentially (dedup first, fp-judge sees only merged primaries):
  • Agent(subagent_type="c-review:c-review-dedup-judge", description="Dedup judge", prompt=f"output_dir: {output_dir}")
  • Agent(subagent_type="c-review:c-review-fp-judge", description="FP + severity judge", prompt=f"output_dir: {output_dir}\nsarif_generator_path: {sarif_generator_path}")
    — resolve
    sarif_generator_path
    to
    ${C_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py
    .
Judge failure handling. Same shape as Phase 7's classifier, applied to judge return text:
  • … complete:
    success.
  • … abort:
    non-retryable. Surface the abort line plus
    ls -l ${output_dir}/findings-index.txt
    ; stop.
  • No
    complete:
    (help message / error / question) → retryable once.
    SendMessage(to=<agentId>, …)
    rather than a fresh spawn (the agent already paid the protocol-parse cost). Include the explicit finding paths from
    findings-index.txt
    . If the second try still fails, surface the transcript and continue to Phase 8b.
进入条件
findings-index.txt
已存在。退出条件:去重评审子代理和误报评审子代理已返回;
dedup-summary.md
fp-summary.md
REPORT.md
(理想情况下还有
REPORT.sarif
)已写入。
每个评审子代理的完整协议在其系统提示中(
agents/c-review-{dedup,fp}-judge.md
);启动提示仅传递运行时变量。请勿引用
prompts/internal/judges/
— 这些文件不存在。
顺序启动(先去重,误报评审子代理仅查看合并后的主结果):
  • Agent(subagent_type="c-review:c-review-dedup-judge", description="Dedup judge", prompt=f"output_dir: {output_dir}")
  • Agent(subagent_type="c-review:c-review-fp-judge", description="FP + severity judge", prompt=f"output_dir: {output_dir}\ sarif_generator_path: {sarif_generator_path}")
    — 将
    sarif_generator_path
    解析为
    ${C_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py
评审子代理失败处理:对评审子代理的返回文本应用与Phase 7相同的分类规则:
  • … complete:
    成功。
  • … abort:
    不可重试。 显示终止行以及
    ls -l ${output_dir}/findings-index.txt
    的结果;停止运行。
  • complete:
    (帮助信息/错误/问题) → 可重试一次。 使用
    SendMessage(to=<agentId>, …)
    而非重新启动(代理已支付协议解析成本)。包含
    findings-index.txt
    中的明确检测结果路径。如果第二次尝试仍失败,显示对话记录并继续执行Phase 8b。

Phase 8b: SARIF safety net

Phase 8b:SARIF安全网

Entry: fp-judge returned, or the run aborted early. Exit:
${output_dir}/REPORT.sarif
exists.
bash
test -d "${output_dir}/findings" && python3 "${C_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py" "${output_dir}"
Run unconditionally whenever
findings/
exists — generator is idempotent (full overwrite), emits
results: []
for zero-survivor runs, and handles partial runs (findings without
fp_verdict
are emitted as
LIKELY_TP
rather than being silently dropped). Always overwriting protects against the case where fp-judge crashed mid-write and left a corrupt
REPORT.sarif
on disk. Skip only if
${output_dir}/findings/
doesn't exist (Phase 2 failed). After this phase, update
${output_dir}/run-summary.md
with judge/SARIF status.
进入条件:误报评审子代理已返回,或运行提前终止。退出条件
${output_dir}/REPORT.sarif
已存在。
bash
test -d "${output_dir}/findings" && python3 "${C_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py" "${output_dir}"
只要
findings/
存在,就无条件运行 — 生成器是幂等的(完全覆盖),对无存活结果的运行输出
results: []
,并处理部分运行结果(无
fp_verdict
的检测结果会被标记为
LIKELY_TP
而非静默丢弃)。始终覆盖可以防止误报评审子代理崩溃时磁盘上留下损坏的
REPORT.sarif
的情况。仅当
${output_dir}/findings/
不存在(Phase 2失败)时跳过此阶段。此阶段完成后,更新
${output_dir}/run-summary.md
中的评审子代理/SARIF状态。

Phase 9: Return Report

Phase 9:返回报告

Entry: Phase 8b complete. Exit: every item in Success Criteria verified true;
REPORT.md
returned to the caller.
Before composing the response, walk the Success Criteria checklist below and confirm each bullet against on-disk artifacts (
TaskList
for cluster tasks,
ls
/
Read
for the files). If any criterion fails, surface the failure prominently in the response — do not hide a partial run behind a successful report.
Then
Read ${output_dir}/REPORT.md
and return its content to the caller. Append an Artifacts list pointing at
findings/
,
findings-index.txt
,
run-summary.md
,
dedup-summary.md
,
fp-summary.md
,
REPORT.md
,
REPORT.sarif
.

进入条件:Phase 8b完成。退出条件成功标准中的每一项均已验证为true;
REPORT.md
已返回给调用者。
撰写响应前,按以下成功标准清单检查,并对照磁盘上的工件(集群任务查看
TaskList
,文件查看
ls
/
Read
)确认每一项。如果任何标准未满足,需在响应中突出显示失败情况 — 切勿在成功报告背后隐藏部分运行结果。
然后读取
${output_dir}/REPORT.md
并将内容返回给调用者。附加工件列表,指向
findings/
findings-index.txt
run-summary.md
dedup-summary.md
fp-summary.md
REPORT.md
REPORT.sarif

Finding file frontmatter — three stages

检测结果文件前置信息 — 三个阶段

Authoritative schema:
agents/c-review-worker.md
("Finding File Format"). Three-stage write:
  1. Worker — base fields (
    id
    ,
    bug_class
    ,
    title
    ,
    location
    ,
    function
    ,
    confidence
    ,
    worker
    ) + seven body sections.
  2. Dedup-judge — adds
    merged_into
    on duplicates, or
    also_known_as
    +
    locations
    on primaries that absorbed.
  3. FP+Severity judge — adds
    fp_verdict
    +
    fp_rationale
    on every primary; on survivors (
    TRUE_POSITIVE
    /
    LIKELY_TP
    ) also adds
    severity
    ,
    attack_vector
    ,
    exploitability
    ,
    severity_rationale
    .
权威 schema:
agents/c-review-worker.md
(“Finding File Format”)。分三个阶段写入:
  1. 工作子代理 — 基础字段(
    id
    bug_class
    title
    location
    function
    confidence
    worker
    ) + 七个正文部分。
  2. 去重评审子代理 — 在重复结果中添加
    merged_into
    字段,或在合并后的主结果中添加
    also_known_as
    +
    locations
    字段。
  3. 误报+严重性评审子代理 — 在每个主结果中添加
    fp_verdict
    +
    fp_rationale
    字段;对存活结果(
    TRUE_POSITIVE
    /
    LIKELY_TP
    )还添加
    severity
    attack_vector
    exploitability
    severity_rationale
    字段。

Bug classes / clusters

漏洞类别/集群

Authoritative:
prompts/clusters/manifest.json
. 47 always-on bug classes, up to 64 with all conditional clusters enabled.
buffer-write-sinks
is fully consolidated (its sub-prompts are not re-read at runtime).

权威来源:
prompts/clusters/manifest.json
。47个始终启用的漏洞类别,启用所有条件集群后最多可达64个。
buffer-write-sinks
已完全整合(其子提示不会在运行时重新读取)。

Success Criteria

成功标准

The phase exits already cover most of this; the orchestrator-visible end-state is:
  • Every Phase-5 cluster task is
    completed
    (verify via
    TaskList
    ).
  • ${output_dir}/run-summary.md
    exists and records resolved scope/context, compile-commands probe result, worker claims vs index count, task status, and judge/SARIF status.
  • Every primary finding (no
    merged_into
    ) has
    fp_verdict
    +
    fp_rationale
    ; every survivor (
    TRUE_POSITIVE
    /
    LIKELY_TP
    ) also has
    severity
    ,
    attack_vector
    ,
    exploitability
    ,
    severity_rationale
    .
  • REPORT.md
    exists, severity-filtered per
    severity_filter
    .
  • REPORT.sarif
    exists (Phase 8b safety net guarantees this).
各阶段的退出条件已涵盖大部分内容,编排器可见的最终状态为:
  • 所有Phase-5集群任务均为
    completed
    (通过
    TaskList
    验证)。
  • ${output_dir}/run-summary.md
    已存在,并记录了已解析的范围/上下文、compile-commands探测结果、工作子代理声称数量与索引数量的对比、任务状态以及评审子代理/SARIF状态。
  • 每个主结果(无
    merged_into
    字段)都有
    fp_verdict
    +
    fp_rationale
    字段;每个存活结果(
    TRUE_POSITIVE
    /
    LIKELY_TP
    )还有
    severity
    attack_vector
    exploitability
    severity_rationale
    字段。
  • REPORT.md
    已存在,并按
    severity_filter
    进行了严重性过滤。
  • REPORT.sarif
    已存在(Phase 8b安全网保证这一点)。",