anti-bug-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Anti-Bug Review

Anti-Bug Review

Read a codebase hard, prove what is broken, and hand back a report someone can act on — without changing a single byte.
深入阅读代码库,证明哪些地方有问题,并交出一份别人可以据此行动的报告——不改变一个字节。

What makes a review different from a repair

审查与修复有何不同

This is not the anti-bug skill minus the fixing. The stance is different, and one fact drives the difference:
In a repair run, a wrong finding usually dies quietly. The fix fails, the test will not go red, the sibling search comes up empty — the work itself filters mistakes out before anyone sees them.
In a review, there is no filter. Every finding goes straight to a human who will spend real hours on it, or dismiss the whole report because two items were imagined. Google's static-analysis team measured where that cliff sits: developers abandon a tool once roughly 10% of its reports are findings nobody acts on, and the real findings die with the false ones.
So the burden shifts. In a repair, the fix is the product. Here the report is the product, and its accuracy, ordering and honesty are the whole job.
Every rule below traces to published evidence, collected in
references/research-basis.md
. Read it when a judgement call needs grounding, when calibrating how much to trust a signal, or when someone asks why the review insists on something.
这并不是 anti-bug skill 去掉修复部分。立场不同,且一个事实驱动着这种差异:
在修复运行中,错误的发现通常会悄悄消亡。 修复失败、测试不会变红、同类搜索一无所获——工作本身在任何人看到之前就把错误过滤掉了。
在审查中没有过滤器。 每一条发现都会直接交给一个会花上数小时的人,或者因为其中两条是凭空想象的而否定整份报告。Google 的静态分析团队测量过这道悬崖的位置:当一个工具大约 10% 的报告是没人会去处理的发现时,开发者就会放弃它,而真实的发现也会随虚假的发现一起消亡。
因此负担发生了转移。在修复中,修复方案就是产品。在这里,报告才是产品,它的准确性、排序和诚实性就是全部工作。
下面的每条规则都追溯到已发表的证据,收集在
references/research-basis.md
中。当你需要为判断提供依据、校准对某个信号的信任程度,或者有人问为什么审查坚持某事时,请阅读它。

Philosophy

理念

Rules cover anticipated situations. These are for the rest, and each resolves to a behavior.
A bug is a gap between intent and behavior — so establish intent first. With no written intent, reading code only reflects it back, and every finding is an opinion.
The code cannot testify about itself. An implementation is the defendant, not the witness. Expected behavior comes from a specification, a business rule, a contract, or a person — never from running the code and recording what it printed.
Green is a clue, not a verdict. A passing suite says "nothing here caught it," which is a fact about the tests, not the code.
A finding that cannot be reproduced is a question, not a defect — so file it as one. Questions are welcome in a review and belong in their own section. Questions dressed as findings are what destroy the report's credibility.
A bug found once is a habit, not an accident. Report every occurrence, not the one that happened to surface.
The most expensive bugs never crash. They return 200, complete successfully, and are wrong. No linter has an opinion about those, which is why they reach production.
The report is the product. No later step will catch a mistake in it. Rank by what it costs the reader to be wrong.
Silence about what was not checked is a form of lying. Say what was covered, what was skipped, and why.
Leave no trace. The repository is byte-identical when the review ends. A reviewer who edits the thing under review has destroyed the evidence and the trust in one move.
When the rules run out, ask what the user loses. Severity and ordering resolve to blast radius: money, data, access, trust, time.
规则覆盖可预见的情况。这些规则适用于其余情况,每一条都归结为一种行为。
Bug 是意图与行为之间的差距——因此首先要确立意图。 没有书面意图时,阅读代码只会把代码本身反射回来,每条发现都只是一种观点。
代码不能为自己作证。 实现是被告,不是证人。预期行为来自规格说明、业务规则、契约或人——绝不应来自运行代码并记录它打印了什么。
绿灯是一条线索,而不是判决。 一套通过的测试套件只说明“这里什么都没抓到”,这是关于测试的事实,而不是关于代码的事实。
无法复现的发现是问题,而不是缺陷——因此应按问题来记录。 问题在审查中是受欢迎的,并且放在自己的章节里。伪装成发现的问题正是摧毁报告可信度的东西。
发现一次的 Bug 是一种习惯,而不是意外。 报告每一处出现,而不是只报告恰好浮出水面的那一处。
最昂贵的 Bug 从不崩溃。 它们返回 200,成功完成,但结果是错误的。没有任何 linter 会对这些有意见,这就是它们能进入生产环境的原因。
报告就是产品。 后续没有任何步骤会捕获其中的错误。按读者犯错所付出的代价来排序。
对未检查部分保持沉默是一种撒谎的形式。 说明覆盖了什么、跳过了什么,以及为什么。
不留痕迹。 审查结束时仓库必须保持字节级一致。审查者如果编辑被审查的对象,就一举摧毁了证据和信任。
当规则用完时,问用户会失去什么。 严重性和排序最终归结为爆炸半径:金钱、数据、访问权限、信任、时间。

The read-only contract

只读契约

"Read-only" needs a precise boundary, because some ordinary review actions do write.
Work on a disposable copy whenever possible. This turns every question below into a non-question:
bash
git -C <repo> worktree add /tmp/review-copy HEAD    # preferred, if git
“只读”需要精确的边界,因为一些普通的审查动作确实会产生写入。
尽可能在一次性副本上工作。 这把下面每个问题都变成了不是问题的问题:
bash
git -C <repo> worktree add /tmp/review-copy HEAD    # preferred, if git

or

or

cp -r <repo> /tmp/review-copy # any repo

Then install, build, and run freely in the copy. Nothing touches the original.

If reviewing in place, the tiers are:

**Always allowed** — reading files; `grep`/`rg`/`find`; `git log`, `git diff`, `git show`, `git blame` (never `checkout`, `reset`, `stash`, `clean`); linters, type checkers, static analysis, dependency audits that read manifests.

**Allowed only with permission, because it writes** — installing dependencies (`npm install` writes `node_modules/` and can rewrite lockfiles; use `npm ci` or a copy); building (writes `dist/`, `target/`, `__pycache__/`); running the test suite (may write to a dev database, create fixtures, or hit the network). Ask once, covering all of them, rather than per command.

**Never** — editing source; running migrations; writing to any shared or production database; `git` operations that change refs, history, or the working tree; killing processes this session did not start; deleting anything.

The review's own artifacts — ledger, reproductions, notes — live in a workspace **outside** the repository, so the tree stays clean. `scripts/ledger.py init` sets this up and prints where.

Prove it rather than asserting it. Snapshot before, check after:

```bash
python scripts/integrity.py snapshot --repo <path>   # at the start
python scripts/integrity.py check --repo <path>      # before writing the report
The check output goes in the report. A reviewer's claim to have changed nothing is exactly the kind of unverifiable claim this skill refuses to accept from anyone else.
cp -r <repo> /tmp/review-copy # any repo

然后在副本中自由安装、构建和运行。任何操作都不会触及原始仓库。

如果在原位置审查,权限层级如下:

**始终允许** — 读取文件;`grep`/`rg`/`find`;`git log`、`git diff`、`git show`、`git blame`(绝不使用 `checkout`、`reset`、`stash`、`clean`);linter、类型检查器、静态分析、读取清单的依赖审计。

**仅在获得许可后才允许,因为它会写入** — 安装依赖(`npm install` 会写入 `node_modules/` 并可能重写 lockfile;请使用 `npm ci` 或副本);构建(写入 `dist/`、`target/`、`__pycache__/`);运行测试套件(可能写入开发数据库、创建 fixture 或访问网络)。一次性请求涵盖所有这些,而不是逐个命令询问。

**绝不** — 编辑源代码;运行迁移;写入任何共享或生产数据库;改变引用、历史或工作树的 `git` 操作;终止本次会话未启动的进程;删除任何东西。

审查自身的产物——账本、复现、笔记——存放在仓库**外部**的工作区中,以保持工作树干净。`scripts/ledger.py init` 会完成设置并打印位置。

用证明代替断言。开始前做快照,结束后检查:

```bash
python scripts/integrity.py snapshot --repo <path>   # at the start
python scripts/integrity.py check --repo <path>      # before writing the report
检查输出放入报告中。审查者声称什么都没改动,正是此技能拒绝接受任何人做出的那种无法验证的声称。

The four rules

四条规则

1. Evidence bar, tightened. Every finding in the main table carries a reproduction — a command with its real output, a failing test, or an input that triggers it — or a complete trace with exact
file:line
per hop. Anything less goes in the Questions section instead, phrased as a question. A report of eight proven findings outranks forty where twelve are imagined, because the second report gets nothing fixed.
2. Root cause, and every occurrence. Identify the cause, then search for the same pattern everywhere and list every hit under the finding. A defect found once is usually a habit. NIST's SSDF makes root-cause analysis its own practice (RV.3) because it is what reduces future defect frequency rather than today's count.
3. No claim without an artifact. "Tests pass", "the build is clean", "this is exploitable" — each means the command and its real output are recorded. Never write a result that was not observed. If something could not be run, say so and mark the finding
Unverified
. Across 547 real agentic-coding incidents, 15.7% involved falsely claiming an action was completed and 9.7% fabricating terminal output; those two numbers are why this rule outranks thoroughness.
4. Recommend, never apply. Every finding carries a proposed fix described precisely enough to act on — the approach, the files, the risk — and nothing is edited. If a fix seems urgent, say so in the verdict; that is the reviewer's lever, not the keyboard.
1. 证据门槛,提高。 主表中的每条发现都带有复现——带有真实输出的命令、一个失败的测试,或触发它的输入——或一条每一步都标明确切
file:line
的完整追溯。不足以上任何一项的内容则放入 Questions(问题) 章节,并以问题的形式表述。一份八个已证实发现的报告胜过四十个其中十二个是想象出来的报告,因为后一份报告无法修复任何东西。
2. 根因,以及每一处出现。 找出原因,然后在所有地方搜索相同的模式,并把每条命中记录在该发现下面。发现一次的缺陷通常是一种习惯。NIST 的 SSDF 将根因分析作为一项独立实践(RV.3),因为它降低的是未来的缺陷频率,而不是今天的数量。
3. 没有产物就没有声称。 “测试通过”、“构建干净”、“这可以被利用”——每一句都意味着命令及其真实输出已被记录。绝不写未观察到的结果。如果某些内容无法运行,请说明并将该发现标记为
Unverified
。在 547 起真实的 agentic-coding 事件中,15.7% 涉及虚假声称某个动作已完成,9.7% 涉及伪造终端输出;这两个数字就是为什么这条规则优先于详尽性。
4. 只建议,绝不应用。 每条发现都带有一个描述得足够精确、可以据以行动的修复建议——方法、文件、风险——但不会编辑任何东西。如果某个修复看起来很紧急,在结论中说明;那是审查者的杠杆,而不是键盘。

The ledger

账本(Ledger)

Long contexts degrade in the middle and a repository sweep overflows any window, so findings live on disk — outside the repo:
bash
python scripts/ledger.py init --repo <path>          # prints the workspace location
python scripts/ledger.py rule --area checkout \
  --text "Discount code redeemable once per customer per campaign" \
  --source "docs/campaigns.md:14" --confidence stated
python scripts/ledger.py add --severity critical --category business-logic \
  --title "Discount redeemable N times under concurrency" \
  --location "src/billing/redeem.py:88-94" --status reproduced --rule R-01 \
  --perspective attacker \
  --repro "30 parallel POST /redeem with one code -> 30 credits applied" \
  --evidence "read-check-write, no unique constraint, no transaction" \
  --recommendation "Unique constraint on (customer, campaign) plus SELECT FOR UPDATE inside one transaction" \
  --effort S --fix-risk medium
python scripts/ledger.py question --about "src/auth/session.py:40" \
  --text "Sessions are never invalidated on password change. Intended, or an oversight?"
python scripts/ledger.py stats      # flags anything unproven sitting in the findings table
python scripts/ledger.py report     # the review document
--effort
is S/M/L and
--fix-risk
is low/medium/high. Both exist because a reader deciding what to do this week needs cost next to severity; a report that ranks only by severity hands them half the decision.
长上下文会在中间部分退化,而仓库扫描会溢出任何窗口,因此发现存放在磁盘上——仓库之外:
bash
python scripts/ledger.py init --repo <path>          # prints the workspace location
python scripts/ledger.py rule --area checkout \
  --text "Discount code redeemable once per customer per campaign" \
  --source "docs/campaigns.md:14" --confidence stated
python scripts/ledger.py add --severity critical --category business-logic \
  --title "Discount redeemable N times under concurrency" \
  --location "src/billing/redeem.py:88-94" --status reproduced --rule R-01 \
  --perspective attacker \
  --repro "30 parallel POST /redeem with one code -> 30 credits applied" \
  --evidence "read-check-write, no unique constraint, no transaction" \
  --recommendation "Unique constraint on (customer, campaign) plus SELECT FOR UPDATE inside one transaction" \
  --effort S --fix-risk medium
python scripts/ledger.py question --about "src/auth/session.py:40" \
  --text "Sessions are never invalidated on password change. Intended, or an oversight?"
python scripts/ledger.py stats      # flags anything unproven sitting in the findings table
python scripts/ledger.py report     # the review document
--effort
取值 S/M/L,
--fix-risk
取值 low/medium/high。两者都存在,是因为读者决定本周要做什么时,需要在严重性旁边看到成本;一份只按严重性排序的报告只递给他们一半的决策。

Phase 0 — Context, business rules, read-only setup

阶段 0 — 上下文、业务规则、只读设置

Finding defects requires more complete understanding than any other review activity, and reviewers working in unfamiliar code produce measurably shallower feedback — the defect comments they do produce skew toward "uncomplicated logical errors." This phase is where review quality is actually decided.
Set up first: take the integrity snapshot, initialize the ledger, and decide copy-vs-in-place.
Run
python scripts/recon.py <repo>
— it detects languages, entry points, tests, config and risk surfaces, and prints available commands. Then read what it points at: README, architecture docs, manifests, CI config, migrations. With git history,
python scripts/hotspots.py <repo>
ranks files by change frequency crossed with bug-fix density; start there, but roughly a third of files are needed to cover 80% of distinct bugs, so never stop there.
Write a Context Summary: what the system does and for whom; stack and versions from manifests; entry points; the main data flow end to end; data layer and transaction boundaries; external integrations; the auth model including where authorization is merely assumed; and a blast-radius ranking.
Then the Business Rules Register, which is what makes the business-logic pass possible:
IDAreaRuleSourceConfidence
R-01CheckoutDiscount redeemable once per customer per campaign
docs/campaigns.md:14
stated
R-02RefundsRefund total may never exceed captured amountinferred from
refund.py:40
inferred
Confidence is load-bearing. A
stated
rule is a fact; an
inferred
rule is a question
, and a finding resting on one belongs in the Questions section, not the findings table — testing code against a rule inferred from that same code proves only that the code agrees with itself. When a rule is unclear and the user is reachable, ask.
发现缺陷比任何其他审查活动都需要更完整的理解,而工作在陌生代码中的审查者会产生明显更浅的反馈——他们确实产出的缺陷评论偏向于“不复杂的逻辑错误”。这个阶段才是真正决定审查质量的地方。
先进行设置:拍摄完整性快照、初始化账本,并决定是使用副本还是原地审查。
运行
python scripts/recon.py <repo>
——它会检测语言、入口点、测试、配置和风险面,并打印可用命令。然后阅读它指向的内容:README、架构文档、清单、CI 配置、迁移。结合 git 历史,
python scripts/hotspots.py <repo>
按变更频率与 bug-fix 密度的交叉来对文件排序;从那里开始,但大约三分之一的文件才能覆盖 80% 的不同 bug,所以绝不要停在那里。
编写一份 Context Summary(上下文摘要):系统做什么、为谁做;来自清单的栈和版本;入口点;端到端的主要数据流;数据层和事务边界;外部集成;认证模型,包括哪些地方授权仅仅是被假定的;以及爆炸半径排序。
然后是 Business Rules Register(业务规则登记表),它使业务逻辑扫描成为可能:
IDAreaRuleSourceConfidence
R-01CheckoutDiscount redeemable once per customer per campaign
docs/campaigns.md:14
stated
R-02RefundsRefund total may never exceed captured amountinferred from
refund.py:40
inferred
置信度是承重的。
stated
规则是事实;
inferred
规则是一个问题
,基于推断规则得出的发现应放在 Questions 章节,而不是 findings 表——针对从同一代码推断出的规则来测试代码,只能证明代码与自己一致。当规则不清晰且用户可联系时,要询问。

Phase 1 — Environment survey

阶段 1 — 环境调查

Establish what can actually be observed, and record it. Run the build, the test suite, linters, type checkers, and the dependency audit — in the copy, or after the one permission request. Record exact commands and real output with
ledger.py env
.
Pre-existing failures are findings in their own right: a suite that was already red tells the reader something true about the project's state, and it belongs in the report rather than being quietly worked around.
If nothing can be executed — no dependencies, no services, unsupported platform — record why and continue with static review only. Then say so in the report's scope section and mark findings accordingly. A static-only review honestly labeled is a real deliverable; one that implies execution is a fabrication.
确定哪些内容实际可以被观察到,并记录下来。运行构建、测试套件、linter、类型检查器和依赖审计——在副本中,或在获得一次性许可之后。使用
ledger.py env
记录确切的命令和真实输出。
预先存在的失败本身就是发现:一个本来就红的测试套件会告诉读者关于项目状态的真相,它应该出现在报告中,而不是被悄悄绕过。
如果什么都无法执行——没有依赖、没有服务、平台不受支持——记录原因,并仅继续静态审查。然后在报告的范围章节中说明这一点,并相应标记发现。诚实标注为纯静态审查的交付物是真实的;暗示已经执行过的则是伪造。

Phase 2 — The sweep

阶段 2 — 扫描

Read
references/detection-playbook.md
for patterns and grep recipes,
references/business-logic.md
for Pass BL,
references/security-audit.md
for Pass B,
references/toolchains.md
for per-stack commands.
Read each pass in a named perspective. Reviewers assigned a specific viewpoint find more defects than reviewers reading generally — around 30% better in controlled experiments, because a narrow focus produces deeper analysis and distinct perspectives overlap less. Passing over the same code as the attacker, then as the accountant, then as the operator surfaces different things than three general readings. Record the perspective on each finding (
--perspective
) so the report shows which lenses were actually used, and which were not.
Pass BL — Business logic. The accountant's and the abuser's eye. First, because it is the only class no tool covers: CWE-840 exists precisely because judging these requires "domain-specific knowledge or business rules," and they "do not produce clear errors or undefined behavior at the code level." OWASP is blunter — automated tools cannot understand context, so this is manual work. Walk each register rule against the code: limits and quotas, calculations (price, discount, tax, balance, rounding, currency), flow and state (skippable steps, illegal transitions, replayable actions), logic-level authorization, concurrency on stock and transactions, hidden assumptions, and feature abuse.
references/business-logic.md
has each subcategory with detection scenarios.
Pass A — Error and failure handling. The operator's eye, at 3am. The highest-yield mechanical pass: 92% of catastrophic production failures came from mishandling errors the software had already detected, and 35% from three trivial patterns — empty or log-only handlers, over-broad catches that abort, handlers with TODO/FIXME. Also: swallowed rejections, ignored return codes, retries without cap, cleanup skipping the error path.
Pass B — Security. The attacker's eye. Trace taint from entry points to sinks rather than grepping sink names. Walk the CWE Top 25 and OWASP Top 10 against the real attack surface. Authorization deserves the most attention because no scanner can infer who should be allowed. Apply extra scrutiny to AI-generated code: roughly 40% of LLM-generated programs in controlled security scenarios contained a CWE.
Pass C — Correctness and edge cases. The hostile user's eye. Off-by-one, inverted conditions, precedence, integer/float confusion, timezone and DST, currency in floats, mutated shared defaults, boundaries.
Pass D — Concurrency and state. The second user arriving at the same millisecond. 96% of concurrency bugs involve only two threads and 69% are atomicity violations, so look for pairs: check-then-act, non-atomic read-modify-write, missing transaction boundaries, unawaited async calls, request data in global state.
Pass E — Data integrity. The DBA's eye. Missing constraints and indexes, irreversible or destructive migrations, multi-statement writes without a transaction, cascade deletes, nullable columns assumed non-null, dual writes that can diverge.
Pass F — Performance and resources. The eye of someone with 100× the data. N+1 queries, unbounded results, missing indexes on filter columns, unclosed handles, unbounded caches, blocking I/O on an event loop.
Pass G — Configuration and deployment. The new hire's first deploy. Hardcoded secrets and hosts, unvalidated env vars, development settings reachable in production, dependency advisories,
.env.example
drift.
Pass H — Test quality. The eye of whoever inherits this. Tests asserting nothing meaningful, tests mocking the unit under test, and flakiness — 45% of flaky tests come from async wait, 20% concurrency, 12% order dependency.
Batching. Defect detection collapses as review volume rises; effectiveness drops sharply past roughly 400 lines in one sitting. Keep each batch to one module or a few hundred lines, write findings immediately, and move on. When several batches in an area come back empty, move on rather than grinding.
阅读
references/detection-playbook.md
获取模式和 grep 配方,
references/business-logic.md
获取 Pass BL,
references/security-audit.md
获取 Pass B,
references/toolchains.md
获取各技术栈的命令。
以指定视角阅读每一遍扫描。 被分配特定视角的审查者比泛读的审查者发现更多缺陷——在受控实验中大约好 30%,因为狭窄的焦点产生更深入的分析,且不同视角之间重叠更少。以攻击者、再以会计、再以运维人员的身份过同一份代码,会浮现出与三次泛读不同的东西。在每条发现上记录视角(
--perspective
),以便报告显示实际使用了哪些镜头、没有使用哪些。
Pass BL — 业务逻辑。会计与滥用者的眼睛。 这放在第一位,因为它是唯一没有工具覆盖的类别:CWE-840 的存在正是因为判断这些需要“领域特定知识或业务规则”,而且它们“不会在代码层面产生清晰的错误或未定义行为”。OWASP 更直接——自动化工具无法理解上下文,所以这是人工工作。对照登记表中的每条规则逐一检查代码:限制和配额、计算(价格、折扣、税费、余额、舍入、货币)、流程和状态(可跳过的步骤、非法状态转换、可重放操作)、逻辑级授权、库存和事务上的并发、隐藏假设,以及功能滥用。
references/business-logic.md
包含每个子类别及检测场景。
Pass A — 错误与失败处理。凌晨 3 点运维人员的眼睛。 产出最高的机制性扫描:92% 的灾难性生产事故来自对软件已检测到的错误的错误处理,35% 来自三种琐碎模式——空处理器或仅记录日志的处理器、过度宽泛导致中止的 catch、带 TODO/FIXME 的处理器。此外:吞掉的 rejection、被忽略的返回码、无上限的重试、跳过错误路径的清理。
Pass B — 安全。攻击者的眼睛。 从入口点到危险函数(sink)追踪污点,而不是只对 sink 名称做 grep。将 CWE Top 25 和 OWASP Top 10 对照真实攻击面逐一走查。授权最值得关注,因为没有扫描器能推断谁应该被允许。对 AI 生成的代码要额外仔细审查:在受控安全场景中,大约 40% 的 LLM 生成程序包含某个 CWE。
Pass C — 正确性与边界情况。敌对用户的眼睛。 差一错误、条件取反、优先级、整数/浮点混淆、时区和夏令时、浮点数中的货币、共享默认值被修改、边界。
Pass D — 并发与状态。同一毫秒到达的第二个用户。 96% 的并发 bug 只涉及两个线程,69% 是原子性违规,所以要寻找成对出现的情况:先检查后行动、非原子的读-改-写、缺失的事务边界、未等待的异步调用、全局状态中的请求数据。
Pass E — 数据完整性。DBA 的眼睛。 缺失约束和索引、不可逆或破坏性的迁移、没有事务的多语句写入、级联删除、被假定为非空的可空列、可能分叉的双写。
Pass F — 性能与资源。拥有 100 倍数据量的人的眼睛。 N+1 查询、无界结果、过滤列上缺失索引、未关闭的句柄、无界缓存、事件循环上的阻塞 I/O。
Pass G — 配置与部署。新员工的首次部署。 硬编码的密钥和主机、未经验证的环境变量、生产环境可访问的开发设置、依赖公告、
.env.example
漂移。
Pass H — 测试质量。继承者的眼睛。 没有断言任何有意义内容的测试、mock 被测单元本身的测试,以及脆弱性(flakiness)——45% 的 flaky 测试来自异步等待,20% 来自并发,12% 来自顺序依赖。
分批处理。 随着审查量上升,缺陷检测效率会崩塌;一次性超过大约 400 行时效果急剧下降。每批保持在一个模块或几百行以内,立即写下发现,然后继续。当一个区域的几个批次都空手而归时,继续前进,而不是死磕。

Phase 3 — Prove it

阶段 3 — 证明它

This is the phase that repair runs get for free and reviews have to do deliberately. The revert-check is unavailable here, so reproduction is the only proof a finding is real.
For each candidate finding, produce the strongest evidence available, in this order:
  1. A runnable reproduction — a command, script or request with its real output, saved in the workspace so the reader can run it themselves. Minimize it: strip everything not required to trigger the failure, until removing one more thing makes it stop reproducing.
  2. A failing test written in the workspace (never committed to the repo) whose expected value comes from the specification, not from observed behavior.
  3. A complete trace — source to sink,
    file:line
    at each hop, with the missing guard named. This is the right evidence for authorization and taint findings, where a live reproduction may require credentials.
Then set status: Reproduced (1 or 2), Traced (3), or Unverified — could not be checked, with the reason recorded. Unverified items are legitimate; unverified items presented as confirmed are not.
Classify with cumulative evidence, not one observation. A single failed reproduction attempt does not disprove a finding — the harness may be wrong, a precondition missing, the environment different. Record what was tried.
这是修复运行中免费获得、而审查必须刻意去做的阶段。这里没有 revert-check 可用,所以复现是证明发现为真的唯一证据。
对每条候选发现,按以下顺序产出可用的最强证据:
  1. 可运行的复现 — 一条带真实输出的命令、脚本或请求,保存在工作区中,以便读者可以自己运行。将其最小化:剥离掉所有不是触发失败所必需的内容,直到再移除一项就无法复现为止。
  2. 一个失败的测试,写在工作区中(绝不提交到仓库),其预期值来自规格说明,而不是来自观察到的行为。
  3. 一条完整追溯 — 从源头到危险函数,每一步都标注
    file:line
    ,并指出缺失的防护。这是授权和污点类发现的正确证据,因为现场复现可能需要凭据。
然后设置状态:Reproduced(已复现)(1 或 2)、Traced(已追溯)(3)或 Unverified(未验证)——无法检查,并记录原因。未验证的条目是合理的;但把未验证条目呈现为已确认则不合理。
用累积证据来判断,而不是单次观察。一次失败的复现尝试并不能推翻一条发现——测试装置可能是错的、前置条件可能缺失、环境可能不同。记录已经尝试过的内容。

Phase 4 — Triage and calibrate

阶段 4 — 分诊与校准

Severity is assigned by blast radius, not by how annoying the code is: Critical (security hole, data loss, core-flow crash, money wrong), High (core feature wrong in common conditions), Medium (edge cases, notable performance, missing error handling), Low (maintainability). State the preconditions for each — a finding needing local root access is not the one needing a browser, and conflating them is how the Critical list stops being read.
Then add the reader's half of the decision:
--effort
(S/M/L) and
--fix-risk
(low/medium/high) for the recommended fix. A Critical/S/low goes first this week; a Medium/L/high may reasonably wait a quarter.
Sort everything into four buckets: Findings (reproduced or traced), Questions (unproven, or resting on an inferred rule — phrased as questions), Observations (true but not defects: dead code, inconsistencies, upgrade opportunities), and Not assessed (what the scope did not cover, and why).
严重性按爆炸半径分配,而不是按代码有多烦人:Critical(严重)(安全漏洞、数据丢失、核心流程崩溃、金钱错误)、High(高)(常见条件下核心功能错误)、Medium(中)(边界情况、明显性能问题、缺失错误处理)、Low(低)(可维护性)。说明每条发现的前提条件——需要本地 root 权限的发现与需要浏览器的发现不是一回事,把它们混为一谈正是 Critical 列表不再被阅读的原因。
然后添加读者那一半的决策:建议修复的
--effort
(S/M/L)和
--fix-risk
(low/medium/high)。一个 Critical/S/low 应该本周优先处理;一个 Medium/L/high 合理地等一个季度也无妨。
把所有内容分入四个桶:Findings(发现)(已复现或已追溯)、Questions(问题)(未证实,或基于推断规则——以问题的形式表述)、Observations(观察)(真实但不是缺陷:死代码、不一致、升级机会),以及 Not assessed(未评估)(范围未覆盖的内容及原因)。

Phase 5 — Report and verdict

阶段 5 — 报告与结论

bash
python scripts/integrity.py check --repo <path>      # prove nothing changed
python scripts/ledger.py report > anti-bug-review-report.md
Write the report to the workspace, not the repo, then hand it over. Expand it with the narrative sections using
assets/report-template.md
: scope and method (including which perspectives were used and what was not assessed), context summary, business rules register, findings with evidence and recommendations, questions, observations, the integrity check result, and a prioritized action list.
Lead with the verdict — Ready / Ready with conditions / Not ready — and name the conditions. A review that ends without a recommendation has handed the decision back unmade.
Close the conversation with the short version: findings by severity, the verdict, the single most urgent item, and the top open question. The reader opens the report for the rest.
bash
python scripts/integrity.py check --repo <path>      # prove nothing changed
python scripts/ledger.py report > anti-bug-review-report.md
将报告写到工作区,而不是仓库,然后交付。使用
assets/report-template.md
扩展叙事章节:范围和方法(包括使用了哪些视角以及未评估的内容)、上下文摘要、业务规则登记表、带证据和建议的发现、问题、观察、完整性检查结果,以及一份按优先级排序的行动清单。
以结论开头——Ready(就绪)/ Ready with conditions(附条件就绪)/ Not ready(未就绪)——并说明具体条件。一份以没有建议而结束的审查,等于把未做出的决定交还给了对方。
以简短版本结束对话:按严重性排列的发现、结论、最紧急的一项,以及最需要回答的问题。其余内容由读者打开报告查看。

When to stop and ask

何时停下来询问

Ask when: a business rule is unclear and cannot be settled from documentation or tests; running the tests would write to something shared; the scope is larger than the time available and something must be dropped; or a security finding depends on an authorization policy that lives in someone's head.
If the user is unavailable, take the most reasonable reading, say at the top of the report which reading was taken, and put the rest in Questions. That is more useful than a confident guess about someone else's business.
在以下情况要询问:业务规则不清晰且无法从文档或测试中确定;运行测试会写入共享内容;范围比可用时间更大,必须舍弃某些部分;或者某条安全发现依赖于只存在于某人头脑中的授权策略。
如果用户不可用,采取最合理的解读,在报告开头说明采用了哪种解读,并把其余内容放入 Questions。这比针对别人业务的有把握猜测更有用。