rust-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust Security Review
Rust安全审查
Runs in the main conversation (invoke via ). Orchestrator owns the ledger as bookkeeping for retries; workers and judges have no Task tools. Workers and judges are named plugin subagents (, , ); tool sets are declared in . Findings are exchanged via markdown-with-YAML files in a shared output directory.
/rust-review:rust-reviewTask*rust-review:rust-review-workerrust-review:rust-review-dedup-judgerust-review:rust-review-fp-judgeplugins/rust-review/agents/*.md在主对话中运行(通过调用)。编排器负责维护分类账以处理重试;工作器和评审器没有Task工具。工作器和评审器是命名的插件子代理(、、);工具集在中声明。检查结果通过共享输出目录中的带YAML的Markdown文件交换。
/rust-review:rust-reviewTask*rust-review:rust-review-workerrust-review:rust-review-dedup-judgerust-review:rust-review-fp-judgeplugins/rust-review/agents/*.mdWhen to Use
使用场景
Rust application/library security review: safe/unsafe boundary auditing, memory safety in blocks, concurrency hazards, panic-induced DoS on servers, FFI safety, async-runtime mistakes.
unsafeRust应用/库安全审查:safe/unsafe边界审计、块中的内存安全、并发风险、服务器上panic导致的DoS、FFI安全、异步运行时错误。
unsafeWhen NOT to Use
不适用场景
- Pure-C / pure-C++ codebases — use instead.
c-review - Smart contracts (Solana programs / NEAR contracts / Ink!) — use or the contract-specific skill.
solana-vulnerability-scanner - Kernel-mode Rust drivers without userspace allocator — coverage is incomplete; flag as advisory only.
- Secrets/key memory hygiene (zeroization, /
Zeroize/ZeroizeOnDropusage, lingering stack/heap copies) — use thesecrecyskill; rust-review does not cover memory zeroization.zeroize-audit
- 纯C/纯C++代码库——改用。
c-review - 智能合约(Solana程序/NEAR合约/Ink!)——使用或合约专用技能。
solana-vulnerability-scanner - 无用户空间分配器的内核模式Rust驱动——覆盖范围不完整;仅作为建议标记。
- 密钥/机密内存卫生(零化、/
Zeroize/ZeroizeOnDrop使用、栈/堆残留副本)——使用secrecy技能;rust-review不涵盖内存零化。zeroize-audit
Subagents
子代理
| Subagent type | Purpose | Tool set |
|---|---|---|
| Run assigned cluster, write findings | Read, Write, Edit, Bash |
| Merge duplicates (runs first) | Read, Write, Edit, Glob |
| FP + severity + final reports (runs second) | Read, Write, Edit, Bash |
Tools come from each agent's frontmatter at spawn time. The orchestrator's ///etc. come from this skill's . Search-tool / interaction: in current Claude Code, an agent granted is not also granted the dedicated or tools (the calls return ; the harness expects // via instead). So only the dedup-judge — the one agent that holds no — uses ; the worker, fp-judge, and the orchestrator resolve and search paths with / / / / instead. Because the cluster/finder prompt seeds are written in ripgrep regex syntax (, , ), -holding agents must run them with . If is not installed its call fails loudly () — fall back to with POSIX classes (→, →, drop ), never a raw- whose silent empty becomes a bad . Do not reintroduce / into a -holding agent's protocol.
Task*AgentBashallowed-toolsBashBashGlobGrepNo such tool availablefindgreprgBashBashGlobReadBashfindrggreptest -f\s\d\bBashrgrgcommand not foundgrep -E\s[[:space:]]\d[[:digit:]]\b\sgrepclearedGlobGrepBash| 子代理类型 | 用途 | 工具集 |
|---|---|---|
| 运行指定集群,编写检查结果 | Read, Write, Edit, Bash |
| 合并重复项(首先运行) | Read, Write, Edit, Glob |
| 误报处理 + 严重性评估 + 最终报告(其次运行) | Read, Write, Edit, Bash |
工具来自每个代理启动时的前置内容。编排器的//等工具来自本技能的。搜索工具 / 交互:在当前Claude Code中,被授予的代理不会同时获得专用的或工具(调用会返回;工具框架期望通过使用//)。因此只有去重评审器——唯一不持有的代理——使用;工作器、误报评审器和编排器通过 / 的 / / / 来解析和搜索路径。由于集群/查找提示种子使用ripgrep正则语法(、、),持有的代理必须通过****运行它们。如果未安装,调用会明显失败()——回退到使用POSIX类的(→,→,移除),绝不要使用原始的,其静默空输出会导致错误的状态。不要将/重新引入持有的代理协议中。
Task*AgentBashallowed-toolsBashBashGlobGrepNo such tool availableBashfindgreprgBashGlobReadBashfindrggreptest -f\s\d\bBashrgrgcommand not foundgrep -E\s[[:space:]]\d[[:digit:]]\b\sgrepclearedGlobGrepBashArchitecture
架构
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 → report safety net (SARIF + REPORT.md) → return REPORT.mdOutput directory contains: , , , , (per-worker shards), , (per-worker coverage-gate files), , , , , .
context.mdplan.jsonworker-prompts/findings/findings-index.d/findings-index.txtcoverage/run-summary.mddedup-summary.mdfp-summary.mdREPORT.mdREPORT.sarifPath convention: every later phase shells out to , so resolve that variable first to the plugin directory that contains (and ). Try in order, first hit wins:
${RUST_REVIEW_PLUGIN_ROOT}/scripts/*.pyprompts/clusters/unsafe-boundary.mdscripts/build_run_plan.py- Native Claude Code — , accepted if
${CLAUDE_PLUGIN_ROOT}resolves.Bash: ls "${CLAUDE_PLUGIN_ROOT}/prompts/clusters/unsafe-boundary.md" - Codex — (set it the same way if that var is present and resolves the marker).
${CODEX_PLUGIN_ROOT} - Fallback search — covers Codex installs under , Claude installs under
~/.codex, and a local checkout / repo run:~/.claude. Take the match and strip the trailingBash: find ~/.claude ~/.codex . -path '*/plugins/rust-review/prompts/clusters/unsafe-boundary.md' -print -quit 2>/dev/nullto get the root (the home dirs are searched before/prompts/clusters/unsafe-boundary.mdso an installed copy wins over any vendored copy in the audited repo)..
Set to the resolved root. If all three fail, abort with a message naming the roots searched — do not enter Phase 4 with an empty variable (every call would fail with a confusing path error).
RUST_REVIEW_PLUGIN_ROOTpython3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/..."Scope convention: keep two scopes separate throughout the run:
- — the user-requested audit subtree. Workers may only file findings whose vulnerable location is inside this subtree.
finding_scope_root - — read-only repo roots/files workers and judges may inspect to verify reachability, callers, wrappers, build flags, mitigations, and threat-model details. Default to
context_rootsunless the user explicitly forbids broader context. Reading context outside.is allowed; filing findings there is not.finding_scope_root
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 → report safety net (SARIF + REPORT.md) → return REPORT.md输出目录包含:、、、、(按工作器分片)、、(每个工作器的覆盖检查文件)、、、、、。
context.mdplan.jsonworker-prompts/findings/findings-index.d/findings-index.txtcoverage/run-summary.mddedup-summary.mdfp-summary.mdREPORT.mdREPORT.sarif路径约定:后续每个阶段都会调用,因此首先要将该变量解析为包含(以及)的插件目录。按以下顺序尝试,第一个匹配项生效:
${RUST_REVIEW_PLUGIN_ROOT}/scripts/*.pyprompts/clusters/unsafe-boundary.mdscripts/build_run_plan.py- 原生Claude Code — ,如果
${CLAUDE_PLUGIN_ROOT}能解析则接受。Bash: ls "${CLAUDE_PLUGIN_ROOT}/prompts/clusters/unsafe-boundary.md" - Codex — (如果该变量存在且能解析标记文件,则按相同方式设置)。
${CODEX_PLUGIN_ROOT} - 回退搜索 — 覆盖下的Codex安装、
~/.codex下的Claude安装以及本地检出/仓库运行:~/.claude。获取匹配项并移除尾部的Bash: find ~/.claude ~/.codex . -path '*/plugins/rust-review/prompts/clusters/unsafe-boundary.md' -print -quit 2>/dev/null以得到根目录(先搜索主目录再搜索/prompts/clusters/unsafe-boundary.md,因此已安装的副本优先于审计仓库中的任何 vendored 副本)。.
将设置为解析后的根目录。如果三者都失败,终止并返回包含已搜索根目录的消息——不要在变量为空的情况下进入第4阶段(所有调用都会因路径错误而失败)。
RUST_REVIEW_PLUGIN_ROOTpython3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/..."范围约定:在整个运行过程中保持两个范围分离:
- — 用户请求的审计子树。工作器只能提交漏洞位置在此子树内的检查结果。
finding_scope_root - — 工作器和评审器可检查的只读仓库根/文件,用于验证可达性、调用者、包装器、构建标志、缓解措施和威胁模型细节。默认值为
context_roots,除非用户明确禁止更广泛的上下文。允许读取.之外的上下文,但禁止在此范围内提交检查结果。finding_scope_root
Rationalizations to Reject
需拒绝的合理化理由
- "is rare, so hand-skip the memory-safety cluster." Don't edit the cluster list — set
unsafeaccurately in Phase 1 and lethas_unsafedecide. Every memory-safety bug class (UAF, double-free, uninitialized reads,build_run_plan.py, union UB) requiresVec::set_len, so the planner runs the wholeunsafecluster whenmemory-safetyand correctly omits it whenhas_unsafe=true— there is no "run it anyway." The unsafe-boundary cluster is different: it has nofalseand always runs (consolidated; its safety-doc andrequireshygiene apply to FFI declarations even without visiblerepr(C)blocks).unsafe { } - "The compiler caught it." The borrow checker proves absence of safe-code data races; it proves nothing about unsafe blocks, panic reachability, ABBA deadlocks, atomic-load/store sequencing, or FFI ABI mismatch.
- "is fine if it's
unwrap()."// SAFETY: documented infallibledocuments// SAFETY:operations, not infallibility claims. Anunsafeon documented-infallible input is still risky if the documentation is wrong — file as low severity and let the FP judge decide.unwrap() - "so skip the run." Pure safe-Rust crates still have panic-DoS, atomic races, drop-panics, and trait-implementation hazards. Run the always-on clusters.
has_unsafe=false - "Background spawns parallelize the workers." They do not — calls in a single assistant message already run concurrently.
Agentdefeats the Phase 6a primer cache, so every worker pays full cache-creation (run_in_background=true) and the ~15 K-token primer is wasted M times. Default: omitcache_read_input_tokens=0from worker spawns.run_in_background - "I'll re-derive the cluster list / paths / pass prefixes inline instead of running ." The script is the only authority for selection and rendering. Paraphrasing it drops fields that the worker self-check requires, producing
build_run_plan.py. Always run the script andworker-N abort: spawn prompt malformed.Read plan.json - "The run partially succeeded — I'll just write from what completed." Hiding partial runs behind a successful report is a correctness bug. If any Phase-5 cluster task is not
REPORT.md, surface it prominently incompletedand the final response.run-summary.md - "Zero findings — skip Phase 8." Always run both judges and Phase 8b: dedup-judge writes a minimal no-op on an empty index, fp-judge writes empty
dedup-summary.md/REPORT.md, and Phase 8b's SARIF generator emitsREPORT.sariffor the empty case. SARIF consumers depend on a stable artifact set.results: [] - "is fine for the preflight." Under zsh, an unmatched glob aborts the whole compound command before
Bash: ls README*runs. Use2>/dev/null(never fails on no-match) — and notfind, which is unavailable to an agent that also holdsGlob.Bash
- "很少见,所以手动跳过内存安全集群。" 不要编辑集群列表——在第1阶段准确设置
unsafe,让has_unsafe决定。每个内存安全漏洞类别(UAF、双重释放、未初始化读取、build_run_plan.py、联合UB)都需要Vec::set_len,因此当unsafe时,规划器会运行整个has_unsafe=true集群,当memory-safety时会正确省略——没有“无论如何都运行”的情况。unsafe-boundary集群不同:它没有false,始终运行(合并后的集群;其安全文档和requires卫生规则适用于FFI声明,即使没有可见的repr(C)块)。unsafe { } - "编译器已经发现了问题。" 借用检查器证明安全代码中不存在数据竞争;但它无法证明unsafe块、panic可达性、ABBA死锁、原子加载/存储顺序或FFI ABI不匹配的安全性。
- "如果有,
// SAFETY: documented infallible是没问题的。"unwrap()用于记录// SAFETY:操作,而不是无错误声明。即使输入被记录为无错误,unsafe仍然存在风险——如果文档错误的话。将其标记为低严重性,让误报评审器决定。unwrap() - "所以跳过运行。" 纯安全Rust crate仍然存在panic-DoS、原子竞争、drop-panics和 trait实现风险。运行始终启用的集群。
has_unsafe=false - "后台启动可并行化工作器。" 并非如此——单个助手消息中的调用已经是并发运行的。
Agent会破坏第6a阶段的 primer缓存,因此每个工作器都要完全创建缓存(run_in_background=true),约15K-token的primer会被浪费M次。默认:在工作器启动中省略cache_read_input_tokens=0。run_in_background - "我将在线重新推导集群列表/路径/前缀,而不是运行。" 该脚本是选择和渲染的唯一权威。改写它会丢失工作器自检查所需的字段,导致
build_run_plan.py。始终运行脚本并worker-N abort: spawn prompt malformed。Read plan.json - "运行部分成功——我将根据已完成的内容编写。" 在成功报告背后隐藏部分运行是正确性错误。如果任何第5阶段集群任务未
REPORT.md,在completed和最终响应中突出显示。run-summary.md - "没有检查结果——跳过第8阶段。" 始终运行两个评审器和第8b阶段:去重评审器在空索引上编写最小的无操作,误报评审器编写空的
dedup-summary.md/REPORT.md,第8b阶段的SARIF生成器为空情况输出REPORT.sarif。SARIF消费者依赖稳定的工件集。results: [] - "用于预检查没问题。" 在zsh下,不匹配的glob会在
Bash: ls README*运行前终止整个复合命令。使用2>/dev/null(无匹配时绝不会失败)——不要使用find,因为持有Glob的代理无法使用它。Bash
Orchestration Workflow
编排工作流
Run these phases in the main conversation.
在主对话中运行这些阶段。
Phase 0: Parameter Collection
阶段0:参数收集
Entry: skill invoked. Exit: , , resolved; resolved or set to ; ; resolved.
threat_modelworker_modelseverity_filterscope_subpath"."finding_scope_root=scope_subpathcontext_rootsThe skill is invoked directly (no command wrapper). Parse any free-text arguments the user passed on the line (e.g. , , ) and pre-fill the answers they imply — then ask for any missing required parameters with one call. Never silently default the required parameters.
/rust-review:rust-reviewflamenco onlyhigh severity onlyuse haikuAskUserQuestionRequired parameters:
| Parameter | Values | How to infer from args |
|---|---|---|
| | Words like "remote", "network", "attacker" → |
| | Explicit model name in args. Otherwise ask (no silent default). |
| | "all", "every", "noisy" → |
| repo-relative directory (optional) | Phrases like "X only", "just audit X/", "review subdirectory X" → |
Call exactly once with only unresolved required parameters (, , ) plus 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.
AskUserQuestionthreat_modelworker_modelseverity_filterscope_subpathAfter resolving , set . Set 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 and note that reachability confidence may be lower.
scope_subpathfinding_scope_root="${scope_subpath:-.}"context_roots="."context_roots="${finding_scope_root}"入口:技能被调用。出口:、、已解析;已解析或设置为;;已解析。
threat_modelworker_modelseverity_filterscope_subpath"."finding_scope_root=scope_subpathcontext_roots直接调用技能(无命令包装器)。解析用户在行中传递的任何自由文本参数(例如、、)并预填充它们暗示的答案——然后用一次调用询问任何缺失的必填参数。绝不静默默认必填参数。
/rust-review:rust-reviewflamenco onlyhigh severity onlyuse haikuAskUserQuestion必填参数:
| 参数 | 取值 | 如何从参数推断 |
|---|---|---|
| | 类似"remote"、"network"、"attacker"的词 → |
| | 参数中明确的模型名称。否则询问(无静默默认)。 |
| | "all"、"every"、"noisy" → |
| 仓库相对目录(可选) | 类似"X only"、"just audit X/"、"review subdirectory X"的短语 → |
仅对未解析的必填参数(、、)加上(仅当用户明确请求缩小范围但范围有歧义时)调用一次。如果必填参数都已预填充且范围不存在或已解析,则跳过询问。
threat_modelworker_modelseverity_filterscope_subpathAskUserQuestion解析后,设置。默认设置,以便工作器可以验证缩小子树之外的调用者/构建设置,而不会提交超出范围的检查结果。如果用户明确要求禁止更广泛的上下文,设置并注意可达性置信度可能较低。
scope_subpathfinding_scope_root="${scope_subpath:-.}"context_roots="."context_roots="${finding_scope_root}"Phase 1: Prerequisites
阶段1:先决条件
Entry: Phase 0 complete. Exit: , , , , , flags determined. Abort with a clear message if no files exist under .
has_unsafehas_ffihas_concurrencyhas_asynchas_packed_reprhas_fs_io*.rs${finding_scope_root}Probe within with the commands below (non-empty output ⇒ flag true). The dedicated / tools are unavailable to this orchestrator because it holds — use // via . (The probe regexes use /; if your lacks GNU support, run them with — which honors and still searches ignored files — or, if is not installed either, replace → and drop . Widening is safe here: a false-positive capability flag only adds a harmless extra worker, whereas a missed match would skip a whole pass.)
${finding_scope_root:-.}BashGrepGlobBashgreprgfindBash\s\bgrep\srg -uu\srg\s[[:space:]]\bbash
undefined入口:阶段0完成。出口:已确定、、、、、标志。如果下不存在文件,终止并返回清晰消息。
has_unsafehas_ffihas_concurrencyhas_asynchas_packed_reprhas_fs_io${finding_scope_root}*.rs使用以下命令在内探测(非空输出 ⇒ 标志为true)。由于编排器持有,专用的/工具不可用——通过使用//。(探测正则使用/;如果不支持GNU ,使用运行——它支持且仍会搜索被忽略的文件——或者如果未安装,将替换为并移除。此处放宽是安全的:误报的能力标志只会添加一个无害的额外工作器,而漏匹配会跳过整个检查。)
Bash${finding_scope_root:-.}BashGrepGlobBashgreprgfind\s\bgrep\srg -uu\srg\s[[:space:]]\bbash
undefinedRust source presence (precondition)
Rust源文件存在性(前置条件)
find "${finding_scope_root:-.}" -name '*.rs' -print -quit
find "${finding_scope_root:-.}" -name '*.rs' -print -quit
has_unsafe
has_unsafe
grep -rlE '\bunsafe\s+(extern|fn|impl|trait)\b|\bunsafe\s*{' --include='*.rs' "${finding_scope_root:-.}" | head -1
grep -rlE '\bunsafe\s+(extern|fn|impl|trait)\b|\bunsafe\s*{' --include='*.rs' "${finding_scope_root:-.}" | head -1
has_ffi
has_ffi
grep -rlE 'extern\s+"(C|system|stdcall|cdecl|win64|sysv64|aapcs|fastcall|thiscall|vectorcall|efiapi)(-unwind)?"|\bextern\s+fn\b|extern\s+{|#[repr((C|transparent)\b|\b(CString|CStr)\b|use\s+(libc|core::ffi|std::ffi|std::os::raw|cty)|\blibc::|\b(bindgen|cbindgen)\b|\bc_void\b' --include='*.rs' "${finding_scope_root:-.}" | head -1
grep -rlE 'extern\s+"(C|system|stdcall|cdecl|win64|sysv64|aapcs|fastcall|thiscall|vectorcall|efiapi)(-unwind)?"|\bextern\s+fn\b|extern\s+{|#[repr((C|transparent)\b|\b(CString|CStr)\b|use\s+(libc|core::ffi|std::ffi|std::os::raw|cty)|\blibc::|\b(bindgen|cbindgen)\b|\bc_void\b' --include='*.rs' "${finding_scope_root:-.}" | head -1
has_concurrency
has_concurrency
grep -rlE '\b(std::(thread|sync)|parking_lot::|crossbeam|rayon::|tokio::sync|core::sync::atomic|std::sync::atomic|Atomic[A-Za-z0-9_]|UnsafeCell|static\s+mut|unsafe\s+impl\s+(Send|Sync)|memmap2::|Mmap(Options|Mut)?|MAP_SHARED|shm_open|mmap\s(|memfd_create|shared_memory|raw_sync|CreateFileMapping|MapViewOfFile|once_cell|sigaction|signal_hook|nix::sys::signal|libc::signal|libc::sigaction)' --include='*.rs' "${finding_scope_root:-.}" | head -1
grep -rlE '\b(std::(thread|sync)|parking_lot::|crossbeam|rayon::|tokio::sync|core::sync::atomic|std::sync::atomic|Atomic[A-Za-z0-9_]|UnsafeCell|static\s+mut|unsafe\s+impl\s+(Send|Sync)|memmap2::|Mmap(Options|Mut)?|MAP_SHARED|shm_open|mmap\s(|memfd_create|shared_memory|raw_sync|CreateFileMapping|MapViewOfFile|once_cell|sigaction|signal_hook|nix::sys::signal|libc::signal|libc::sigaction)' --include='*.rs' "${finding_scope_root:-.}" | head -1
has_async
has_async
grep -rlE '\basync\s+(fn|move|{)|.await\b|tokio::|async_std::|futures::' --include='*.rs' "${finding_scope_root:-.}" | head -1
grep -rlE '\basync\s+(fn|move|{)|.await\b|tokio::|async_std::|futures::' --include='*.rs' "${finding_scope_root:-.}" | head -1
has_packed_repr (outer #[repr(...packed...)] and inner #![repr(...packed...)])
has_packed_repr (外部#[repr(...packed...)]和内部#![repr(...packed...)])
grep -rlE '#!?[repr([^]]packed' --include='.rs' "${finding_scope_root:-.}" | head -1
grep -rlE '#!?[repr([^]]packed' --include='.rs' "${finding_scope_root:-.}" | head -1
has_fs_io (path types / construction)
has_fs_io (路径类型/构造)
grep -rlE '\bPathBuf\b|\bPath\b' --include='*.rs' "${finding_scope_root:-.}" | head -1
grep -rlE '\bPathBuf\b|\bPath\b' --include='*.rs' "${finding_scope_root:-.}" | head -1
has_fs_io (fs module and file APIs)
has_fs_io (fs模块和文件API)
grep -rlE '\bfs::|\bFile::(open|create)\b|OpenOptions|.exists()|.metadata(|symlink_metadata|read_dir|read_to_string' --include='*.rs' "${finding_scope_root:-.}" | head -1
As with the other flags, non-empty output (from either probe) means the flag is true. These detectors are intentionally conservative: when in doubt they set the flag true, because a false-positive flag only costs a harmless extra worker while a false-negative would skip a real pass. `has_fs_io` keys on path types (`PathBuf`/`Path`, which also covers `&Path` parameters and bare `Path::` calls) and filesystem anchors (`fs::`/`File::`/`OpenOptions`/`read_dir`/…) rather than the bare `.join(`/`.push(` calls — path construction is reached via the path-type anchors, so leaving join/push out of the gate avoids matching unrelated iterator/`JoinHandle` joins and `Vec::push` that would make the gate fire on nearly every crate.
Note for `Cargo.toml`: also probe for `[dependencies] tokio`, `async-std`, etc., to set `has_async=true` even if the scope subpath has no `.await` yet (library crates often re-export).
Also probe `Cargo.toml` presence (informational — note in `run-summary.md` whether the audit was over a Cargo workspace, single crate, or loose `.rs` files):
```bashgrep -rlE '\bfs::|\bFile::(open|create)\b|OpenOptions|.exists()|.metadata(|symlink_metadata|read_dir|read_to_string' --include='*.rs' "${finding_scope_root:-.}" | head -1
与其他标志一样,(任一探测的)非空输出意味着标志为true。这些检测器故意保守:有疑问时设置标志为true,因为误报标志只会增加一个无害的额外工作器,而漏匹配会跳过真正的检查。`has_fs_io`基于路径类型(`PathBuf`/`Path`,也涵盖`&Path`参数和裸`Path::`调用)和文件系统锚点(`fs::`/`File::`/`OpenOptions`/`read_dir`/…),而不是裸`.join(`/`.push(`调用——路径构造通过路径类型锚点检测,因此将join/push排除在检测之外可避免匹配无关的迭代器/`JoinHandle` joins和`Vec::push`,这些会导致几乎每个crate都触发检测。
关于`Cargo.toml`的注意事项:还要探测`[dependencies] tokio`、`async-std`等,即使范围子目录中还没有`.await`,也要设置`has_async=true`(库crate通常会重新导出)。
还要探测`Cargo.toml`的存在性(信息性——在`run-summary.md`中记录审计是针对Cargo工作区、单个crate还是松散的`.rs`文件):
```bashcontext_roots may be comma-separated (build_run_plan.py treats it as a list),
context_roots可能是逗号分隔的(build_run_plan.py将其视为列表),
so probe each root rather than passing "a,b" as one (nonexistent) path.
因此要探测每个根目录,而不是将"a,b"作为一个(不存在的)路径传递。
echo "${context_roots:-.}" | tr ',' '\n' | while IFS= read -r root; do
find "${root:-.}" -name 'Cargo.toml' -print -quit
done | head -1
undefinedecho "${context_roots:-.}" | tr ',' '\n' | while IFS= read -r root; do
find "${root:-.}" -name 'Cargo.toml' -print -quit
done | head -1
undefinedPhase 2: Output Directory
阶段2:输出目录
Entry: Phase 1 flags set. Exit: absolute resolved; and exist.
output_dir${output_dir}/findings/${output_dir}/coverage/Resolve an absolute path for (default: ):
output_dir$(pwd)/.rust-review-results/$(date -u +%Y%m%dT%H%M%SZ)/bash
mkdir -p "${output_dir}/findings" "${output_dir}/coverage"The subdirectory holds per-worker coverage-gate audit files (). Workers write to it instead of embedding the table in their reply — see step 5.
coverage/coverage/worker-{N}.mdagents/rust-review-worker.md入口:阶段1标志已设置。出口:已解析绝对路径;和已存在。
output_dir${output_dir}/findings/${output_dir}/coverage/解析的绝对路径(默认:):
output_dir$(pwd)/.rust-review-results/$(date -u +%Y%m%dT%H%M%SZ)/bash
mkdir -p "${output_dir}/findings" "${output_dir}/coverage"coverage/coverage/worker-{N}.mdagents/rust-review-worker.mdPhase 3: Codebase Context
阶段3:代码库上下文
Entry: exists. Exit: written.
${output_dir}${output_dir}/context.mdSkim and any build/manifest file (, , , ) — preflight with (via ) before any (a on a missing file aborts the turn; is unavailable to this orchestrator because it holds ). Do not use for the preflight: under zsh, an unmatched glob aborts the whole compound command before runs. Use , which never fails on no-match.
README.{md,rst,txt}Cargo.tomlCargo.lockrust-toolchain.tomlbuild.rsfindBashReadReadGlobBashBash: ls README*2>/dev/nullfind . -maxdepth 2 -name 'README*' -o -name 'Cargo.toml' -o -name 'rust-toolchain.toml' -o -name 'build.rs'Write with: YAML frontmatter (, , , , , , , , , , , , as // plus path when present), then a short markdown body with five sections — Purpose (1-3 sentences), Scope (what's in , and that findings outside it are out of scope), Entry points (where untrusted data enters: network, files, CLI, IPC, deserialization, FFI inputs), Trust boundaries (sandboxed vs trusted peers vs arbitrary remote), Existing hardening (fuzzing harnesses, MIRI runs, , , ).
${output_dir}/context.mdthreat_modelseverity_filterscope_subpathfinding_scope_rootcontext_rootshas_unsafehas_ffihas_concurrencyhas_asynchas_packed_reprhas_fs_iooutput_dircargo_manifestworkspacesingle-crateabsentfinding_scope_rootserdeclippy::pedanticcargo-denycargo-audit入口:已存在。出口:已写入。
${output_dir}${output_dir}/context.md浏览和任何构建/清单文件(、、、)——在任何之前用(通过)进行预检查(对不存在的文件进行会终止当前轮次;由于编排器持有,不可用)。不要使用进行预检查:在zsh下,不匹配的glob会在运行前终止整个复合命令。使用,无匹配时绝不会失败。
README.{md,rst,txt}Cargo.tomlCargo.lockrust-toolchain.tomlbuild.rsReadfindBashReadBashGlobBash: ls README*2>/dev/nullfind . -maxdepth 2 -name 'README*' -o -name 'Cargo.toml' -o -name 'rust-toolchain.toml' -o -name 'build.rs'写入,包含:YAML前置内容(、、、、、、、、、、、、为//加上存在时的路径),然后是简短的Markdown正文,包含五个部分——用途(1-3句话)、范围(包含的内容,以及超出此范围的检查结果不纳入范围)、入口点(不受信任数据进入的位置:网络、文件、CLI、IPC、反序列化、FFI输入)、信任边界(沙盒化vs可信对等方vs任意远程)、现有强化措施(模糊测试 harness、MIRI运行、、、)。
${output_dir}/context.mdthreat_modelseverity_filterscope_subpathfinding_scope_rootcontext_rootshas_unsafehas_ffihas_concurrencyhas_asynchas_packed_reprhas_fs_iooutput_dircargo_manifestworkspacesingle-crateabsentfinding_scope_rootserdeclippy::pedanticcargo-denycargo-auditPhase 4: Build Run Plan (deterministic)
阶段4:构建运行计划(确定性)
Entry: capability flags + known; exists. Exit: and written; known.
threat_model${output_dir}/findings/${output_dir}/plan.json${output_dir}/worker-prompts/*.txtM = worker_countSelection, filtering, path resolution, and spawn-prompt rendering are delegated to the script to keep spawn prompts complete and consistent:
bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/build_run_plan.py" \
--plugin-root "${RUST_REVIEW_PLUGIN_ROOT}" --output-dir "${output_dir}" \
--threat-model "${threat_model}" --severity-filter "${severity_filter}" \
--scope-subpath "${finding_scope_root:-.}" --context-roots "${context_roots:-.}" \
--has-unsafe "${has_unsafe}" --has-ffi "${has_ffi}" \
--has-concurrency "${has_concurrency}" --has-async "${has_async}" \
--has-packed-repr "${has_packed_repr}" --has-fs-io "${has_fs_io}" \
--max-passes-per-worker 4The script writes + + (if , the default) , and prints a JSON summary on stdout. Exits non-zero on any missing prompt — surface the message and stop. With the default the planner selects ~8 clusters → M ≈ 13 workers for pure safe Rust (no FFI / concurrency / async; is always on), ~10 clusters → M ≈ 15 for concurrent safe Rust, and ~15 clusters → M ≈ 23 for full Rust (unsafe + FFI + concurrency + async, plus when and when ). M is the post-chunk worker count (), so it runs above the cluster count — chunking splits multi-pass non-consolidated clusters (e.g. , ), while the two consolidated clusters (, ) are never chunked: one worker each builds the shared inventory once and runs all its phases. is one pass per worker. After it returns, for the structured selection — never re-derive filtering or paths.
plan.jsonworker-prompts/worker-N.txt--cache-primer=trueworker-prompts/cache-primer.txt--max-passes-per-worker 4info-disclosureinput-os-safetyhas_fs_iolayout-safetyhas_packed_reprplan.workers.lengthpanic-dosmemory-safetyunsafe-boundaryconcurrency-lockingrecursion-dosRead plan.json--max-passes-per-worker NNceil(K/N)rust-review-worker-{i}cluster_idpanic-dos-1panic-dos-2unsafe-boundaryconcurrency-lockingCluster prompt:manifest.jsonmax_passes_per_workerrecursion-dos--max-passes-per-worker 0入口:能力标志 + 已知;已存在。出口:已写入和;已知。
threat_model${output_dir}/findings/${output_dir}/plan.json${output_dir}/worker-prompts/*.txtM = worker_count选择、过滤、路径解析和启动提示渲染委托给脚本,以保持启动提示完整一致:
bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/build_run_plan.py" \
--plugin-root "${RUST_REVIEW_PLUGIN_ROOT}" --output-dir "${output_dir}" \
--threat-model "${threat_model}" --severity-filter "${severity_filter}" \
--scope-subpath "${finding_scope_root:-.}" --context-roots "${context_roots:-.}" \
--has-unsafe "${has_unsafe}" --has-ffi "${has_ffi}" \
--has-concurrency "${has_concurrency}" --has-async "${has_async}" \
--has-packed-repr "${has_packed_repr}" --has-fs-io "${has_fs_io}" \
--max-passes-per-worker 4脚本会写入 + +(如果,默认值),并在stdout上打印JSON摘要。如果缺少任何提示,非零退出——显示消息并停止。使用默认的,规划器会选择约8个集群 → 对于纯安全Rust(无FFI/并发/异步;始终启用),M ≈ 13个工作器;对于并发安全Rust,约10个集群 → M ≈ 15;对于完整Rust(unsafe + FFI + 并发 + 异步,加上时的和时的),约15个集群 → M ≈ 23。M是分块后的工作器数量(),因此它大于集群数量——分块会将多轮非合并集群(例如、)拆分,而两个合并集群(、)永远不会被分块:每个集群由一个工作器构建一次共享清单,然后运行所有阶段。每个工作器一轮。返回后,获取结构化选择——绝不重新推导过滤或路径。
plan.jsonworker-prompts/worker-N.txt--cache-primer=trueworker-prompts/cache-primer.txt--max-passes-per-worker 4info-disclosurehas_fs_ioinput-os-safetyhas_packed_reprlayout-safetyplan.workers.lengthpanic-dosmemory-safetyunsafe-boundaryconcurrency-lockingrecursion-dosRead plan.json--max-passes-per-worker NNceil(K/N)-{i}cluster_idrust-review-workerpanic-dos-1panic-dos-2unsafe-boundaryconcurrency-lockingCluster prompt:manifest.jsonmax_passes_per_workerrecursion-dos--max-passes-per-worker 0Phase 5: Create Bookkeeping Tasks (orchestrator-internal)
阶段5:创建记账任务(编排器内部)
Entry: exists; . Exit: created (1:1 with ), all .
${output_dir}/plan.jsonM = plan.workers.lengthcluster_task_ids[]plan.workerspendingThe task ledger is orchestrator bookkeeping only (TUI visibility + Phase-7 retry tracking) — workers never read or write it. One per worker, populating with , , , , , — all values copied verbatim from . Track in order.
TaskCreatemetadatakind="cluster"worker_ncluster_idspawn_prompt_pathpass_prefixesattempt=1plan.workers[i]cluster_task_ids[]plan.workers入口:已存在;。出口:已创建(与一一对应),所有状态为。
${output_dir}/plan.jsonM = plan.workers.lengthcluster_task_ids[]plan.workerspending任务分类账仅用于编排器记账(TUI可见性 + 阶段7重试跟踪)——工作器从不读取或写入它。每个工作器对应一个,在中填充、、、、、——所有值均直接复制自。按顺序跟踪。
TaskCreatemetadatakind="cluster"worker_ncluster_idspawn_prompt_pathpass_prefixesattempt=1plan.workers[i]plan.workerscluster_task_ids[]Phase 6: Spawn workers (optional cache-primer first, then M in parallel)
阶段6:启动工作器(可选先启动缓存primer,然后并行启动M个)
Entry: populated; per-worker spawn prompt files exist at . Exit: all M calls — across every wave — have returned (the parallel spawn block(s) completed).
cluster_task_ids[]${output_dir}/worker-prompts/worker-N.txtAgent入口:已填充;每个工作器的启动提示文件位于。出口:所有M个调用——跨所有波次——已返回(并行启动块已完成)。
cluster_task_ids[]${output_dir}/worker-prompts/worker-N.txtAgentPhase 6a: Cache primer (gated on plan.run.cache_primer
)
plan.run.cache_primer阶段6a:缓存primer(受plan.run.cache_primer
控制)
plan.run.cache_primerA 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 , has written . Spawn it in its own assistant message: the file, pass verbatim as with , , , no . The script wrote the prefix byte-identical to through the block — that byte-identity is what gives the parallel workers their cache hit. The primer trailer contains , which the worker system prompt treats as a first-class mode and returns exactly in one text response with zero tool calls. Discard the abort line — Phase 7 ignores it (no id).
plan.run.cache_primer == truebuild_run_plan.py${output_dir}/worker-prompts/cache-primer.txtReadAgentpromptsubagent_type=rust-review:rust-review-workermodel=${worker_model}description="Rust review cache primer"run_in_backgroundworker-1.txt<context>Cache primer: trueworker-PRIMER abort: cache primer (no analysis performed)worker-NForeground spawn already serializes — no needed before Phase 6b. Skip Phase 6a entirely if .
sleepplan.run.cache_primer == false冷启动的并行批处理无法共享缓存(所有M个请求同时调度,没有一个完成写入)。为了预热前缀,先启动一个小型primer——前台(后台启动不会与后续前台启动共享缓存)。
如果,已写入。在单独的助手消息中启动它:该文件,将内容直接作为的传递,,,,不设置。脚本写入的前缀与的块字节相同——这种字节相同性使并行工作器能够命中缓存。primer尾部包含,工作器系统提示将其视为一等模式,并在一个文本响应中返回完全相同的,没有工具调用。丢弃终止行——阶段7会忽略它(没有 id)。
plan.run.cache_primer == truebuild_run_plan.py${output_dir}/worker-prompts/cache-primer.txtReadAgentpromptsubagent_type=rust-review:rust-review-workermodel=${worker_model}description="Rust review cache primer"run_in_backgroundworker-1.txt<context>Cache primer: trueworker-PRIMER abort: cache primer (no analysis performed)worker-N前台启动已经是串行的——阶段6b之前不需要。如果,完全跳过阶段6a。
sleepplan.run.cache_primer == falsePhase 6b: Spawn M real workers in parallel (one message per wave of ≤16)
阶段6b:并行启动M个真实工作器(每个波次≤16个,一个消息)
STOP — read this before composing the spawn message.Workers MUST be spawned foreground (nofield, orrun_in_background). "Parallel" here means one assistant message containing the wave'srun_in_background=falsecalls — that already runs them concurrently. (For largeAgent, split into consecutive waves of ≤16 calls, one message per wave — see "Required spawn shape" below.) Background spawns are NOT how you parallelize this skill.MBackground spawns defeat Phase 6a's primer cache: every worker pays full cache-creation on its first turn (), and the primer's ~15 K tokens are wasted M times over. Two real runs had exactly this symptom — every worker started withcache_read_input_tokens=0.first_cr=0Before sending the spawn message, audit your draft: everycall must have noAgentkey. If you wroterun_in_background, delete it.run_in_background=true
Required spawn shape: emit a single assistant message containing the wave's tool invocations — that one message is what runs them concurrently. Sequential spawning (one call per message) serializes the review and is also wrong, but that failure is loud (timing); the background-spawn failure is silent (cost).
AgentAgentWaves when exceeds the per-message cap. The harness caps the number of calls it will dispatch from a single assistant message (observed: ~20 in Claude Code — a real 25-worker run silently kept only the first 20 and had to spawn the remaining 5 in a second message). So when , plan the waves up front: split the workers into consecutive waves of ≤16 calls, each wave its own single assistant message. Rules:
MAgentM > 16Agent- Within a wave: all calls in one message, foreground (no
Agent) — identical shape to a single-wave run.run_in_background - Across waves: wave k+1 is a separate message that can only be sent after wave k's calls all return (a tool-use message ends the turn). Waves are therefore serialized with respect to each other — that is correct and loud; accept it. Do not try to overlap them.
Agent - Never reach for to fit more workers in one message. More waves, never background — background defeats the primer cache (see the STOP box) and is the cardinal error this skill guards against.
run_in_background=true - Cache across waves: the primer prefix has a ~5-minute cache TTL that refreshes on every hit, so back-to-back waves keep hitting it (the 25-worker run confirmed on its second wave). If a later wave will start more than ~5 minutes after the previous one (very large
cache_read≈14 Kor slow workers), re-spawn the Phase-6a primer in its own message first to re-warm the prefix before that wave.M - Balance the waves (e.g. → 13+12, not 20+5) so no wave hugs the cap and the last wave isn't a tiny straggler.
M=25 - After every wave has returned, proceed to Phase 7 with the full set of M worker results.
For each worker (in its assigned wave):
N ∈ [1..M]Read: ${output_dir}/worker-prompts/worker-N.txt- Pass the file contents verbatim as the tool's
Agentargument:prompt
| Parameter | Value |
|---|---|
| |
| |
| |
| the full text of |
| field MUST be omitted, OR set to |
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 malformedAnti-patterns to reject:
- Passing (see warning above).
run_in_background=true - Cramming more than ~16 calls into one message when
Agentis large — the harness silently keeps only the first ~20 and drops the rest. Use balanced waves of ≤16, never background spawns, to cover all M.M - 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,拆分为连续的≤16个调用的波次,每个波次一个消息——参见下面的“必填启动格式”。)后台启动不是并行此技能的方式。Agent后台启动会破坏阶段6a的primer缓存:每个工作器在第一轮都要完全创建缓存(),primer的约15K-token会被浪费M次。两次真实运行都出现了完全相同的症状——每个工作器都从cache_read_input_tokens=0开始。first_cr=0在发送启动消息前,检查你的草稿:每个调用必须没有Agent键。如果你写了run_in_background,删除它。run_in_background=true
必填启动格式:生成一个包含波次工具调用的助手消息——这个消息就是并发运行它们的方式。顺序启动(每个消息一个调用)会序列化审查,这也是错误的,但这种失败很明显(计时问题);后台启动的失败是静默的(成本问题)。
AgentAgent当M超过每条消息限制时的波次处理。工具框架限制单个助手消息可以调度的调用数量(观察到:Claude Code中约20个——一次25个工作器的真实运行静默保留了前20个,不得不在第二个消息中启动剩下的5个)。因此当时,提前规划波次:将工作器拆分为连续的**≤16个调用**的波次,每个波次一个单独的助手消息。规则:
AgentM > 16Agent- 波次内:所有调用在一个消息中,前台(无
Agent)——与单波次运行格式相同。run_in_background - 波次间:波次_k+1_是一个单独的消息,只能在波次_k_的调用全部返回后发送(工具使用消息结束当前轮次)。因此波次之间是串行的——这是正确且明显的;接受它。不要尝试重叠它们。
Agent - 绝不使用在一个消息中容纳更多工作器。使用更多波次,绝不使用后台——后台会破坏primer缓存(参见停止框),是此技能防范的主要错误。
run_in_background=true - 波次间缓存:primer前缀有约5分钟的缓存TTL,每次命中都会刷新,因此连续波次会持续命中缓存(25个工作器的运行确认第二波次)。如果后续波次在前一个波次之后约5分钟以上才启动(M非常大或工作器缓慢),先在单独的消息中重新启动阶段6a的primer,以在该波次前重新预热前缀。
cache_read≈14 K - 平衡波次(例如→ 13+12,而不是20+5),这样没有波次接近限制,最后一个波次也不是很小的零散工作器。
M=25 - 所有波次返回后,使用完整的M个工作器结果进入阶段7。
对于每个工作器(在其分配的波次中):
N ∈ [1..M]Read: ${output_dir}/worker-prompts/worker-N.txt- 将文件内容直接作为工具的
Agent参数传递:prompt
| 参数 | 值 |
|---|---|
| |
| |
| |
| |
| 必须省略此字段,或设置为 |
启动提示是唯一权威。直接传递——每个字段都是工作器自检查所需的;任何偏差都会触发。
worker-N abort: spawn prompt malformed需拒绝的反模式:
- 传递(参见上面的警告)。
run_in_background=true - 当M很大时,在一个消息中塞入超过约16个调用——工具框架会静默保留前约20个并丢弃其余的。使用≤16个的平衡波次,绝不使用后台启动,以覆盖所有M个工作器。
Agent - 手动输入启动提示而不是读取。
worker-N.txt - 插入与Task相关的指令("首先调用TaskList"、"分配的任务id: <N>")。工作器没有Task工具。
- 在传递前编辑渲染后的提示(修剪“冗余”字段,合并轮次列表)。
Phase 7: Wait for Workers and Classify Outcomes
阶段7:等待工作器并分类结果
Entry: all M Phase-6 calls have returned. Exit: every cluster has either succeeded or been retried up to the cap; written.
Agent${output_dir}/findings-index.txtThe Phase-6 invocations block until each worker returns. Inspect each worker's return text and apply this classifier in order — first match wins:
Agent| # | Match (in return text) | Outcome | Action |
|---|---|---|---|
| 1 | | provisional success | Parse the |
| 2 | | non-retryable orchestrator bug | Stop 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. |
| 3 | other | retryable | Mark |
| 4 | | retryable | Same as #3 (transient worker crash). |
If any non-retryable, stop. Otherwise, before re-spawning, clear each retryable worker's prefix-space on disk — the Phase-7 index is built from disk, so a crashed attempt's higher-id stragglers (files the replacement never re-emits) would otherwise be resurrected into the report. Loop over the worker's actual (from its task ), substituting each real prefix for — do not run the command with a literal :
pass_prefixesmetadata${pfx}PREFIXbash
undefined入口:所有阶段6的调用已返回。出口:每个集群要么成功,要么已重试到上限;已写入。
Agent${output_dir}/findings-index.txt阶段6的调用会阻塞直到每个工作器返回。检查每个工作器的返回文本,并按顺序应用以下分类器——第一个匹配项获胜:
Agent| # | 匹配项(在返回文本中) | 结果 | 操作 |
|---|---|---|---|
| 1 | | 临时成功 | 解析 |
| 2 | | 不可重试的编排器错误 | 停止运行,显示终止信息 + 启动提示路径。重新运行相同提示会重复失败——预工作预算耗尽始终意味着工作器无法通过自检查,重试无法修复。 |
| 3 | 其他 | 可重试 | 标记为 |
| 4 | | 可重试 | 与#3相同(临时工作器崩溃)。 |
如果有任何不可重试的错误,停止。否则,在重新启动前,清除每个可重试工作器在磁盘上的前缀空间——阶段7的索引是从磁盘构建的,因此崩溃尝试的高id零散文件(替换工作器永远不会重新生成的文件)会被重新引入报告。遍历工作器实际的(来自其任务),将每个真实前缀替换为——不要使用字面运行命令:
pass_prefixesmetadata${pfx}PREFIXbash
undefinedzsh-safe: find … -delete
never aborts on no-match (an rm PREFIX-*.md
glob would).
find … -deleterm PREFIX-*.md对zsh安全:find … -delete
无匹配时绝不会终止(rm PREFIX-*.md
glob会终止)。
find … -deleterm PREFIX-*.mdReplace PREFIX1 PREFIX2
with the worker's actual space-separated pass_prefixes.
PREFIX1 PREFIX2将PREFIX1 PREFIX2
替换为工作器实际的空格分隔pass_prefixes。
PREFIX1 PREFIX2for pfx in PREFIX1 PREFIX2; do
find "${output_dir}/findings" -maxdepth 1 -type f -name "${pfx}-*.md" -delete
done
Then re-spawn each `pending` retryable with `attempt <= 2` in one parallel block (cap = 2 attempts per cluster). `attempt` was just incremented to `2` on the first failure, so the guard must admit `2` to allow the single retry — `attempt < 2` would block every retry. A second failure increments to `3`, which fails `<= 2` and ends retries. Replacement workers reuse deterministic finding IDs per prefix, so a cleared prefix-space plus a fresh write yields a consistent shard / coverage / disk set.for pfx in PREFIX1 PREFIX2; do
find "${output_dir}/findings" -maxdepth 1 -type f -name "${pfx}-*.md" -delete
done
然后在一个并行块中重新启动每个`pending`且`attempt <= 2`的可重试工作器(每个集群上限=2次尝试)。第一次失败时`attempt`刚递增到`2`,因此守卫必须允许`2`以允许单次重试——`attempt < 2`会阻止所有重试。第二次失败会递增到`3`,不满足`<= 2`,结束重试。替换工作器会为每个前缀重用确定性的检查结果ID,因此清除前缀空间加上重新写入会产生一致的分片/覆盖/磁盘集。Sanity-check + write index
健全性检查 + 写入索引
For every provisional cluster, validate the worker-owned shard, coverage file, coverage rows, filed IDs, and claimed finding count against before marking the task completed. Run one command per completed worker, or validate multiple workers in one command. Both claimed-count forms below are valid; do not pass bare values without either grouping them after a flag or repeating the flag.
complete:plan.jsonworker-N=N--claimed-countbash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-N --claimed-count worker-N=<claimed_count_from_complete_line>bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-1 --worker worker-2 \
--claimed-count worker-1=0 worker-2=3bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-1 --worker worker-2 \
--claimed-count worker-1=0 --claimed-count worker-2=3If validation exits non-zero, treat the completion as malformed and retryable (classifier row #4): mark the task , store the validator output in , set , and increment . Missing , missing , missing coverage rows, invalid rows, filed IDs absent from the shard or disk, and claimed-count mismatches are all malformed completions. After the retry cap, leave the cluster task incomplete and surface the validator output in and the final response. Only validation-clean provisional completions may be d to .
pendingmetadata.abort_reasonneeds_respawn=trueattemptfindings-index.d/worker-N.txtcoverage/worker-N.mdskipped:run-summary.mdTaskUpdatecompletedThen build the index. The canonical index is the set of finding files actually on disk, not the shard union — building from disk guarantees that a finding written without a matching shard entry (a worker that crashed between its and its shard-append, or the single-prefix empty-shard trap the worker prompt warns about) is still picked up by dedup → fp-judge → REPORT/SARIF instead of silently vanishing, and that every index entry resolves to a real file:
Writebash
undefined对于每个临时集群,在标记任务为完成之前,根据验证工作器拥有的分片、覆盖文件、覆盖行、提交的ID和声称的检查结果计数。为每个完成的工作器运行一个命令,或在一个命令中验证多个工作器。下面两种声称计数格式都有效;不要在没有标志分组或重复标志的情况下传递裸值。
complete:plan.json--claimed-countworker-N=Nbash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-N --claimed-count worker-N=<claimed_count_from_complete_line>bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-1 --worker worker-2 \
--claimed-count worker-1=0 worker-2=3bash
python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/validate_artifacts.py" "${output_dir}/plan.json" \
--worker worker-1 --worker worker-2 \
--claimed-count worker-1=0 --claimed-count worker-2=3如果验证非零退出,将完成视为格式错误且可重试(分类器第4行):标记任务为,将验证器输出存储在中,设置,并递增。缺少、缺少、缺少覆盖行、无效行、提交的ID在分片或磁盘中不存在、声称计数不匹配都是格式错误的完成。达到重试上限后,将集群任务保留为未完成,并在和最终响应中显示验证器输出。只有验证通过的临时完成才能为。
pendingmetadata.abort_reasonneeds_respawn=trueattemptfindings-index.d/worker-N.txtcoverage/worker-N.mdskipped:run-summary.mdTaskUpdatecompleted然后构建索引。规范索引是磁盘上实际存在的检查结果文件集,而不是分片的并集——从磁盘构建保证了没有匹配分片条目的检查结果(工作器在和分片追加之间崩溃,或工作器提示警告的单前缀空分片陷阱)仍然会被去重→误报评审器→REPORT/SARIF拾取,而不是静默消失,并且每个索引条目都指向真实文件:
Writebash
undefinedCanonical index = every finding file on disk. find
never fails on no-match
find规范索引 = 磁盘上的每个检查结果文件。find
无匹配时绝不会失败
find(an empty findings/ yields an empty index — the unambiguous "zero findings"
—
signal). sort -u
collapses Phase-7 retry duplicates: replacement workers reuse
sort -u—
deterministic ids, so the same path appears once.
—
find "${output_dir}/findings" -maxdepth 1 -type f -name '*.md' 2>/dev/null
| sort -u > "${output_dir}/findings-index.txt"
| sort -u > "${output_dir}/findings-index.txt"
#(空findings/会产生空索引——明确的“无检查结果”信号)。合并阶段7的重试重复项:替换工作器重用
sort -uReconcile against the per-worker shards: any path on disk but in NO shard is an
确定性id,因此相同路径只出现一次。
orphan whose worker failed to record it. It is already in the index above (so it
—
is NOT dropped) — print it so the bookkeeping gap can be surfaced. Non-fatal.
—
if [ -d "${output_dir}/findings-index.d" ]; then
Reconcile by basename (finding ids are unique), so a path-format difference
between the worker find
and this one (trailing slash, /var↔/private/var)
findcannot manufacture false orphans. Any basename on disk but in no shard is an
orphan whose worker failed to record it.
comm -13
<(find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null | sed 's#./##; /^[[:space:]]$/d' | sort -u)
<(find "${output_dir}/findings" -maxdepth 1 -type f -name '.md' 2>/dev/null | sed 's#.*/##' | sort -u) fi
<(find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null | sed 's#./##; /^[[:space:]]$/d' | sort -u)
<(find "${output_dir}/findings" -maxdepth 1 -type f -name '.md' 2>/dev/null | sed 's#.*/##' | sort -u) fi
The shards stay the per-worker audit trail (`validate_artifacts.py` checks them) and the dedup-judge's crash-recovery fallback, but they no longer gate what reaches the pipeline. For each orphan basename the reconcile prints, map its `<PREFIX>` to the owning worker via `plan.json` and note in `run-summary.md` that that worker's shard was incomplete — the finding is already in the index (so it is not lost), but the bookkeeping gap should be visible. Still cross-check the index 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`, capability flags `has_unsafe`/`has_ffi`/`has_concurrency`/`has_async`, Cargo manifest status)
- worker outcome table (`worker_n`, `cluster_id`, claimed finding count, shard line count, coverage-file path (`coverage/worker-{N}.md`), 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` — **or** any worker returned a `complete:` line carrying the `truncated at hard cap` token (it hit the tool-call cap before searching every pass; its coverage file will show one or more `cleared (NOT SEARCHED — truncated at hard cap)` rows) — include it prominently in `run-summary.md` and the final response. A hard-cap-truncated worker is marked `completed` for ledger purposes but is a **partial** result: do not let that `completed` status hide the incomplete coverage 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.find "${output_dir}/findings" -maxdepth 1 -type f -name '*.md' 2>/dev/null
| sort -u > "${output_dir}/findings-index.txt"
| sort -u > "${output_dir}/findings-index.txt"
Phase 8: Judge Pipeline (sequential, dedup → fp+severity)
与每个工作器的分片协调:磁盘上存在但不在任何分片中的路径是工作器未能记录的孤立文件。它已在上面的索引中(因此
—
不会被丢弃)——打印它以便显示记账缺口。非致命。
Entry: exists. Exit: dedup-judge and fp-judge have returned; , , , and ideally are written.
findings-index.txtdedup-summary.mdfp-summary.mdREPORT.mdREPORT.sarifEach judge's full protocol is its system prompt (); spawn prompts pass only per-run variables. Do not reference — those files don't exist.
agents/rust-review-{dedup,fp}-judge.mdprompts/internal/judges/STOP — these two judges run in SEQUENCE, not in parallel. Unlike the Phase-6b workers (which you spawn as Mcalls in one message precisely because that runs them concurrently), the judges have a hard data dependency: fp-judge must see theAgent/merged_intoannotations dedup-judge writes, and it only skips files already carryingalso_known_as. If you emit bothmerged_intocalls in one message they run concurrently — fp-judge reads findings before any merge annotations exist, judges every duplicate as a separate primary, and (becauseAgentdoesn't exist yet) trips its "dedup did not run" fallback, producing an inflated, duplicateddedup-summary.md/SARIF.REPORT.mdSpawn dedup-judge in its own assistant message, wait for its(ordedup-judge complete:) return, then spawn fp-judge in a separate message. Before composing the fp-judge spawn, confirm dedup finished —abort:must succeed (or you saw the dedupBash: test -f ${output_dir}/dedup-summary.mdtoken). Never put both judgecomplete:calls in the same message.Agent
- First message — . Wait for its return and classify it (below) before continuing.
Agent(subagent_type="rust-review:rust-review-dedup-judge", description="Dedup judge", prompt=f"output_dir: {output_dir}") - Then, in a separate message — — resolve
Agent(subagent_type="rust-review:rust-review-fp-judge", description="FP + severity judge", prompt=f"output_dir: {output_dir}\nsarif_generator_path: {sarif_generator_path}")tosarif_generator_path.${RUST_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py
Judge failure handling. Same shape as Phase 7's classifier, applied to judge return text:
- → success.
… complete: - → non-retryable for that judge. Surface the abort line plus
… abort:, then still run Phase 8b (its SARIF +ls -l ${output_dir}/findings-index.txtsafety net guarantees the artifact set even when a judge aborts — see Phase 8b's "fp-judge returned, or the run aborted early" entry), and stop without spawning further judges. "Stop" means do not continue the judge pipeline — it does not mean skip Phase 8b.REPORT.md - No (help message / error / question) → retryable once.
complete:rather than a fresh spawn (the agent already paid the protocol-parse cost). Include the explicit finding paths fromSendMessage(to=<agentId>, …). If the second try still fails, surface the transcript and continue to Phase 8b.findings-index.txt
if [ -d "${output_dir}/findings-index.d" ]; then
按基名协调(检查结果id是唯一的),因此工作器find
和此命令之间的路径格式差异
find#(尾部斜杠、/var↔/private/var)不会产生假孤立文件。磁盘上存在但不在任何分片中的基名是
工作器未能记录的孤立文件。
comm -13
<(find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null | sed 's#./##; /^[[:space:]]$/d' | sort -u)
<(find "${output_dir}/findings" -maxdepth 1 -type f -name '.md' 2>/dev/null | sed 's#.*/##' | sort -u) fi
<(find "${output_dir}/findings-index.d" -maxdepth 1 -type f -name 'worker-.txt' -exec awk 1 {} + 2>/dev/null | sed 's#./##; /^[[:space:]]$/d' | sort -u)
<(find "${output_dir}/findings" -maxdepth 1 -type f -name '.md' 2>/dev/null | sed 's#.*/##' | sort -u) fi
分片保留每个工作器的审计跟踪(`validate_artifacts.py`会检查它们)和去重评审器的崩溃恢复回退,但它们不再控制哪些内容进入管道。对于协调打印的每个孤立基名,通过`plan.json`将其`<PREFIX>`映射到所属工作器,并在`run-summary.md`中记录该工作器的分片不完整——检查结果已在索引中(因此不会丢失),但记账缺口应该可见。仍然交叉检查索引行数与工作器声称的`wrote N`总和;记录不匹配但不终止。
任务更新和索引创建后,运行`TaskList`并写入`${output_dir}/run-summary.md`,包含:
- 已解析的参数(`threat_model`、`severity_filter`、`finding_scope_root`、`context_roots`、能力标志`has_unsafe`/`has_ffi`/`has_concurrency`/`has_async`、Cargo清单状态)
- 工作器结果表(`worker_n`、`cluster_id`、声称的检查结果计数、分片行数、覆盖文件路径(`coverage/worker-{N}.md`)、任务状态、重试/终止状态)
- `findings-index.txt`行数以及与工作器声称的任何不匹配
- 阶段8完成后的评审器状态,或评审器被跳过/失败的原因
如果任何阶段5的集群任务未`completed`——**或**任何工作器返回带有`truncated at hard cap`标记的`complete:`行(在搜索所有轮次前达到工具调用上限;其覆盖文件会显示一个或多个`cleared (NOT SEARCHED — truncated at hard cap)`行)——在`run-summary.md`和最终响应中突出显示。硬上限截断的工作器出于分类账目的标记为`completed`,但它是**部分**结果:不要让`completed`状态在成功报告背后隐藏不完整的覆盖范围。
**即使没有检查结果也要始终运行阶段8**——两个评审器在空索引上会短路:去重评审器编写最小的无操作`dedup-summary.md`,误报评审器编写空的`REPORT.md`/`REPORT.sarif`,以便SARIF消费者获得稳定的工件集。Phase 8b: Report safety net (SARIF + REPORT.md)
阶段8:评审器流水线(串行,去重 → 误报+严重性)
Entry: fp-judge returned, or the run aborted early. Exit: and both exist.
${output_dir}/REPORT.sarif${output_dir}/REPORT.mdbash
test -d "${output_dir}/findings" && python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py" "${output_dir}"Run the SARIF generator unconditionally whenever exists — it is idempotent (full overwrite), emits for zero-survivor runs, and handles partial runs (findings without are emitted as , exempt from the since their severity was never judge-validated, and marked / with an message prefix — so an inferred severity guess can never silently drop them under a / filter). Always overwriting protects against an fp-judge that crashed mid-write and left a corrupt on disk.
findings/results: []fp_verdictLIKELY_TPseverity_filterunjudged: trueseverity_validated: false[UNVALIDATED SEVERITY — not judged]mediumhighREPORT.sarifIf the generator prints a line on stdout (it also records in the SARIF and a notification per dropped file), one or more finding files were unreadable or had no parseable frontmatter and were excluded from the report. This is a dropped result — surface it prominently in and the final response with the count and paths, the same way a non- cluster task is surfaced. Do not let the otherwise-clean SARIF hide the loss.
WARNING: skipped N …invocations[].properties.skipped_findingswarningrun-summary.mdcompletedThen guarantee exists. Unlike SARIF (mechanical), is the fp-judge's curated artifact, so do not overwrite a judge-written one. (The fp-judge writes with a heredoc, not the tool, because the harness blocks the tool for subagent report files — do not "fix" the judge by re-mandating . The orchestrator is the main agent and is not subject to that block, so its own below works.) Check for it, and if it is missing (the judge crashed, even its -heredoc write failed, or it returned the report as chat text instead of writing the file), the orchestrator writes itself rather than failing the run:
REPORT.mdREPORT.mdREPORT.mdBashWriteWriteWriteWriteBashREPORT.md- If the fp-judge returned the report body in its transcript, that text verbatim to
Write.${output_dir}/REPORT.md - Otherwise synthesize it from the on-disk findings: take the survivor primaries (, no
fp_verdict ∈ {TRUE_POSITIVE, LIKELY_TP}; if the judge never ran, treat a finding with nomerged_intoas a survivor) listed infp_verdict, applyfindings-index.txtfromseverity_filterto judged survivors only — unjudged findings (nocontext.md) are included regardless of filter and rendered under anfp_verdictsection with aUnvalidated (severity not judged)label, mirroring the SARIF behavior so a strict filter never silently drops them — and[UNVALIDATED SEVERITY — not judged]aWritemirroring the fp-judge template — YAML frontmatter (REPORT.md,stage: final-report,threat_model,severity_filter,total_primaries), a severity-distribution table, then one section per reported finding grouped by severity (embed the Description / Code / Data flow / Impact / Recommendation body for CRITICAL/HIGH; reference the finding file for MEDIUM/LOW).reported_findings
Either way, note in that was orchestrator-synthesized (not judge-authored). Skip the SARIF generator and this check only if doesn't exist (Phase 2 failed). After this phase, update with judge / SARIF / report status.
${output_dir}/run-summary.mdREPORT.md${output_dir}/findings/${output_dir}/run-summary.md入口:已存在。出口:去重评审器和误报评审器已返回;已写入、、,理想情况下还有。
findings-index.txtdedup-summary.mdfp-summary.mdREPORT.mdREPORT.sarif每个评审器的完整协议是其系统提示();启动提示仅传递每个运行的变量。不要引用——这些文件不存在。
agents/rust-review-{dedup,fp}-judge.mdprompts/internal/judges/停止——这两个评审器必须串行运行,不能并行。 与阶段6b的工作器(你作为一个消息中的M个调用启动,正是因为这样可以并发运行)不同,评审器有严格的数据依赖:误报评审器必须看到去重评审器写入的Agent/merged_into注释,并且它只会跳过已带有also_known_as的文件。如果你在一个消息中发出两个merged_into调用,它们会并发运行——误报评审器在合并注释存在前读取检查结果,将每个重复项视为单独的主项,并且(因为Agent还不存在)触发其“去重未运行”回退,产生膨胀、重复的dedup-summary.md/SARIF。REPORT.md在单独的助手消息中启动去重评审器,等待其(或dedup-judge complete:)返回,然后在单独的消息中启动误报评审器。编写误报评审器启动前,确认去重已完成——abort:必须成功(或你看到了去重的Bash: test -f ${output_dir}/dedup-summary.md标记)。绝不要将两个评审器的complete:调用放在同一个消息中。Agent
- 第一个消息 — 。等待其返回并分类(如下)后再继续。
Agent(subagent_type="rust-review:rust-review-dedup-judge", description="Dedup judge", prompt=f"output_dir: {output_dir}") - 然后,在单独的消息中 — — 将
Agent(subagent_type="rust-review:rust-review-fp-judge", description="FP + severity judge", prompt=f"output_dir: {output_dir}\nsarif_generator_path: {sarif_generator_path}")解析为sarif_generator_path。${RUST_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py
评审器失败处理。与阶段7的分类器格式相同,应用于评审器返回文本:
- → 成功。
… complete: - → 该评审器不可重试。 显示终止行加上
… abort:,然后仍然运行阶段8b(其SARIF +ls -l ${output_dir}/findings-index.txt安全网即使在评审器终止时也能保证工件集——参见阶段8b的“fp-judge返回,或运行提前终止”入口),并停止而不启动更多评审器。“停止”意味着不要继续评审器流水线——不是跳过阶段8b。REPORT.md - 无(帮助消息/错误/问题)→ 可重试一次。 使用
complete:而不是重新启动(代理已经支付了协议解析成本)。包含SendMessage(to=<agentId>, …)中的明确检查结果路径。如果第二次尝试仍然失败,显示记录并继续到阶段8b。findings-index.txt
Phase 9: Return Report
阶段8b:报告安全网(SARIF + REPORT.md)
Entry: Phase 8b complete. Exit: every item in Success Criteria verified true; returned to the caller.
REPORT.mdBefore composing the response, walk the Success Criteria checklist below and confirm each bullet against on-disk artifacts ( for cluster tasks, / for the files). If any criterion fails, surface the failure prominently in the response — do not hide a partial run behind a successful report.
TaskListlsReadThen and return its content to the caller. Append an Artifacts list pointing at , , , , , , .
Read ${output_dir}/REPORT.mdfindings/findings-index.txtrun-summary.mddedup-summary.mdfp-summary.mdREPORT.mdREPORT.sarif入口:误报评审器返回,或运行提前终止。出口:和都已存在。
${output_dir}/REPORT.sarif${output_dir}/REPORT.mdbash
test -d "${output_dir}/findings" && python3 "${RUST_REVIEW_PLUGIN_ROOT}/scripts/generate_sarif.py" "${output_dir}"只要存在,就无条件运行SARIF生成器——它是幂等的(完全覆盖),无存活检查结果时输出,并处理部分运行(无的检查结果会以输出,不受限制,因为其严重性从未经过评审器验证,并标记为/,前缀为消息——因此推断的严重性猜测永远不会被/过滤器静默丢弃)。始终覆盖可防止误报评审器崩溃时在磁盘上留下损坏的。
findings/results: []fp_verdictLIKELY_TPseverity_filterunjudged: trueseverity_validated: false[UNVALIDATED SEVERITY — not judged]mediumhighREPORT.sarif如果生成器在stdout上打印行(它还会在SARIF的中记录,并为每个丢弃的文件添加通知),则一个或多个检查结果文件无法读取或没有可解析的前置内容,被排除在报告之外。这是丢失的结果——在和最终响应中突出显示计数和路径,就像处理未的集群任务一样。不要让看似干净的SARIF隐藏丢失的内容。
WARNING: skipped N …invocations[].properties.skipped_findingswarningrun-summary.mdcompleted然后保证存在。与SARIF(机械生成)不同,是误报评审器的精心策划的工件,因此不要覆盖评审器编写的版本。(误报评审器使用 heredoc写入,而不是工具,因为工具框架禁止子代理使用工具写入报告文件——不要通过重新强制使用来“修复”评审器。编排器是主代理,不受此限制,因此其自身的可以正常工作。)检查它是否存在,如果缺失(评审器崩溃,甚至其 heredoc写入失败,或它返回报告作为聊天文本而不是写入文件),编排器自行写入,而不是让运行失败:
REPORT.mdREPORT.mdBashREPORT.mdWriteWriteWriteWriteBashREPORT.md- 如果误报评审器在记录中返回了报告正文,该文本到
Write。${output_dir}/REPORT.md - 否则从磁盘上的检查结果合成:获取中列出的存活主项(
findings-index.txt,无fp_verdict ∈ {TRUE_POSITIVE, LIKELY_TP};如果评审器从未运行,将无merged_into的检查结果视为存活项),对已评审的存活项应用fp_verdict中的context.md——未评审的检查结果(无severity_filter)无论过滤器如何都包含在内,并在fp_verdict部分下渲染,带有Unvalidated (severity not judged)标签,与SARIF行为一致,因此严格的过滤器永远不会静默丢弃它们——然后[UNVALIDATED SEVERITY — not judged]一个镜像误报评审器模板的Write——YAML前置内容(REPORT.md、stage: final-report、threat_model、severity_filter、total_primaries)、严重性分布表,然后按严重性分组的每个报告检查结果部分(CRITICAL/HIGH级嵌入Description/Code/Data flow/Impact/Recommendation正文;MEDIUM/LOW级引用检查结果文件)。reported_findings
无论哪种情况,在中记录是编排器合成的(不是评审器编写的)。仅当不存在(阶段2失败)时,才跳过SARIF生成器和此检查。此阶段后,更新中的评审器/SARIF/报告状态。
${output_dir}/run-summary.mdREPORT.md${output_dir}/findings/${output_dir}/run-summary.mdFinding file frontmatter — three stages
阶段9:返回报告
Authoritative schema: ("Finding File Format"). Three-stage write:
agents/rust-review-worker.md- Worker — base fields (,
id,bug_class,title,location,function,confidence) + seven body sections.worker - Dedup-judge — adds on duplicates, or
merged_into+also_known_ason primaries that absorbed.locations - FP+Severity judge — adds +
fp_verdicton every primary; on survivors (fp_rationale/TRUE_POSITIVE) also addsLIKELY_TP,severity,attack_vector,exploitability.severity_rationale
Bug classes / clusters
检查结果文件前置内容——三个阶段
Authoritative: . 37 bug classes live in -gated clusters (so the cluster always runs); of those, 35 always fire and 2 — (TRAITADV) and (CLOSUREPANIC) in — additionally carry , so they only fire when . 69 bug classes across all clusters when every conditional gate is enabled. The cluster is gated on (all its bug classes require ); PATHJOIN and TOCTOU are gated behind via , and PACKEDREF lives in the conditional cluster (); PTREXPOSE stays always-on via the cluster. and are fully consolidated (their sub-prompts are not re-read at runtime).
prompts/clusters/manifest.jsonalwaysadversarial-traitclosure-paniclogic-correctnessrequires: has_unsafehas_unsafe=truememory-safetyhas_unsafeunsafehas_fs_ioinput-os-safetylayout-safetyhas_packed_reprinfo-disclosureunsafe-boundaryconcurrency-locking权威模式:(“Finding File Format”)。三阶段写入:
agents/rust-review-worker.md- 工作器 — 基础字段(、
id、bug_class、title、location、function、confidence)+ 七个正文部分。worker - 去重评审器 — 在重复项上添加,或在吸收了重复项的主项上添加
merged_into+also_known_as。locations - 误报+严重性评审器 — 在每个主项上添加+
fp_verdict;在存活项(fp_rationale/TRUE_POSITIVE)上还添加LIKELY_TP、severity、attack_vector、exploitability。severity_rationale
Success Criteria
漏洞类别/集群
The phase exits already cover most of this; the orchestrator-visible end-state is:
- Every Phase-5 cluster task is (verify via
completed).TaskList - exists and records resolved scope/context, Cargo manifest probe result, worker claims vs index count, task status, and judge/SARIF status.
${output_dir}/run-summary.md - Every primary finding (no ) has
merged_into+fp_verdict; every survivor (fp_rationale/TRUE_POSITIVE) also hasLIKELY_TP,severity,attack_vector,exploitability.severity_rationale - exists, severity-filtered per
REPORT.md(Phase 8b safety net guarantees this even when the fp-judge fails to write it).severity_filter - exists (Phase 8b safety net guarantees this).
REPORT.sarif
权威来源:。37个漏洞类别位于 gated集群中(因此集群始终运行);其中35个始终触发,2个——中的(TRAITADV)和(CLOSUREPANIC)——还带有,因此仅当时触发。启用所有条件门时,所有集群共有69个漏洞类别。集群受 gated(其所有漏洞类别都需要);PATHJOIN和TOCTOU通过受 gated,PACKEDREF位于条件集群中();PTREXPOSE通过集群始终启用。和完全合并(其子提示不会在运行时重新读取)。
prompts/clusters/manifest.jsonalwayslogic-correctnessadversarial-traitclosure-panicrequires: has_unsafehas_unsafe=truememory-safetyhas_unsafeunsafeinput-os-safetyhas_fs_iolayout-safetyhas_packed_reprinfo-disclosureunsafe-boundaryconcurrency-locking—
成功标准
—
阶段退出已经涵盖大部分内容;编排器可见的最终状态是:
- 所有阶段5的集群任务都(通过
completed验证)。TaskList - 存在,并记录了已解析的范围/上下文、Cargo manifest探测结果、工作器声称与索引计数、任务状态以及评审器/SARIF状态。
${output_dir}/run-summary.md - 每个主检查结果(无)都有
merged_into+fp_verdict;每个存活项(fp_rationale/TRUE_POSITIVE)还有LIKELY_TP、severity、attack_vector、exploitability。severity_rationale - 存在,按
REPORT.md进行严重性过滤(阶段8b安全网保证即使误报评审器未能写入也能存在)。severity_filter - 存在(阶段8b安全网保证)。
REPORT.sarif