claude_md/ci_review_prompt.md
Thorough multi-agent code review for RocksDB commits following CLAUDE.md guidelines, with structured codebase exploration, inter-agent debate, and verification.
IMPORTANT — Incremental output: After completing EACH major phase (Setup, Codebase Context, Initial Review, Debate, Synthesis), append your findings to review-findings.md using the Write tool. This ensures partial results are saved even if the review is interrupted by the turn limit. After the final synthesis, write the complete review to review-findings.md, replacing previous content. Also output the final review as your response text.
Why this matters: Review agents that only see the diff miss systemic bugs. For example, a change to an iterator's return value might look correct in isolation but break multi-SST iteration 5 layers up the call stack. A change to error handling might silently drop errors that a caller 3 levels up relies on for recovery. This phase builds the context that prevents those blind spots.
Go deep. Surface-level reading leads to reviews that miss caching layers, existing helper functions, or concurrency requirements. The most expensive failure mode is findings that look correct in isolation but miss how the change breaks the surrounding system.
The team lead spawns one or more research agents to perform the following
analyses using local file search tools (Grep, Glob, Read). The research
must be written to context.md BEFORE any review agent is spawned.
Read the changed files AND their surrounding subsystem in depth — not just signatures, but actual logic, edge cases, data flows, locking protocols, concurrency patterns, and interactions between components.
For each changed file, also read:
IndexBlockIter handles the
same scenario — it's the reference implementation)For each changed function/method, trace the call chain UPWARD 3-5 levels to understand who consumes the changed behavior and what invariants they rely on. Focus on control flow decisions — if/else branches, while loop conditions, early returns — that depend on the changed return values or state.
How to do this:
For each changed function, trace what it calls and whether those calls have new preconditions or changed semantics. Critically, trace SIDE EFFECTS — not just return values. Many bugs hide in side effects on shared state (counters, sequence numbers, flags) that are invisible if you only check return values.
For each callee, ask:
Identify the "reference" or "standard" implementation that does the same thing the changed code does, and compare their behavior. This is critical for plugin/ extension APIs where the implementation must match an implicit contract.
Document the key invariants that the changed code must maintain. Read existing comments, assertions, and test expectations to discover these invariants.
Identify related functionality that already exists in the codebase:
Why this is separate from caller-chain analysis: Sections 2b-2c trace who CALLS the changed functions. This section traces who CONSUMES the data that the changed code PRODUCES. These are often completely different subsystems.
When the change writes data to a shared structure (memtable, SST block, cache entry, statistics counter), ask: "Who reads this data, under what rules, and do those rules match the writer's assumptions?"
How to do this:
The same code path may run in different contexts with different assumptions. Enumerate all contexts and check each one. This is where "works in the common case but breaks in edge configurations" bugs hide.
For RocksDB, always check whether the changed code interacts differently with:
| Context | Key difference | Common failure mode |
|---|---|---|
| WritePreparedTxnDB / WriteUnpreparedTxnDB | read_callback_ controls visibility, not just seqno | Visibility bypass |
| ReadOnly DB / SecondaryInstance | No mutable memtable, writes not allowed | Null pointer or no-op needed |
| CompactionService / Remote compaction | Different process, serialized state | Serialization mismatch |
| User-defined timestamps | Extra dimension in key comparison and visibility | Wrong ordering |
| MemPurge | Memtable-to-memtable, not memtable-to-SST | Missing data |
| Column family with BlobDB | Values may be in blob files, not inline | Missing dereference |
| Snapshots (old, held long-term) | Snapshot seqno may be far behind current state | Metadata corruption |
| Concurrent writers (allow_concurrent_memtable_write) | Lock-free paths vs locked paths | Lost updates |
| FIFO / Universal compaction | Different compaction invariants than Level | Wrong assumptions |
| Prefix seek / total order seek | Different iterator behavior | Wrong iteration results |
For each context, ask:
When the change claims a property (e.g., "logically redundant," "no-op in this case," "safe because X"), systematically break it:
Anti-pattern: treating asserts as proofs. When you see an assert, do NOT conclude "the invariant holds." Instead ask: "Can I construct an input where this assert fires?" If yes, it's a bug (the assert catches it in debug but release builds silently corrupt). If you cannot construct a counterexample after trying, document WHY it's impossible.
Write the complete analysis to context.md in the review folder. This document
is provided to ALL review agents as part of their prompt.
Context document template:
# Codebase Context for Review
## How the Relevant Subsystem Works Today
[Detailed description of the subsystem architecture, data flows,
and component interactions. Not just "what" but "how" and "why."]
## Changed Functions and Their Call Chains
### function_name() (file:line)
**What changed**: [brief description of the behavioral change]
**Upstream callers** (who depends on this):
1. CallerA::method() (file:line) — uses return value to decide X
2. CallerB::method() (file:line) — passes result to CallerC
3. CallerC::method() (file:line) — THE CRITICAL DECISION POINT: ...
**Downstream callees** (what this depends on):
1. calleeA() — behavior unchanged
2. calleeB() — NEW dependency, requires X
**Sibling implementation** (how the "standard" version handles this):
- StandardImpl::method() does Y at file:line, which ensures invariant Z
- The changed code must match or document any deviation
**Key invariants**:
- Must never return X when Y is true because CallerC will...
- Must always set Z before returning because CallerB assumes...
## System Architecture Context
[How the changed components fit into the overall system]
## Known Invariants That Must Be Preserved
1. ...
2. ...
## Existing Conventions and Related Code
- [helper functions, patterns, existing tests that are relevant]
## Cross-Component Data Consumers
For each piece of data the change WRITES to a shared structure:
### [data item] written to [structure]
**Writer assumptions**: [what the writer assumes about how this data is consumed]
**All readers**:
1. ReaderA::method() (file:line) — reads under [visibility rules]
- Compatible with writer? YES/NO. If NO, explain the mismatch.
2. ReaderB::method() (file:line) — reads under [different rules]
- Compatible with writer? YES/NO.
## Alternative Execution Contexts
| Context | Does code execute? | Assumptions hold? | Action needed? |
|---------|-------------------|-------------------|----------------|
| WritePreparedTxnDB | YES/NO | YES/NO | [disable/guard/safe] |
| Old snapshots | YES/NO | YES/NO | ... |
| User-defined timestamps | YES/NO | YES/NO | ... |
| ReadOnly DB | YES/NO | YES/NO | ... |
| ... | ... | ... | ... |
## Assumption Stress Test
### Claim: "[the design claim, e.g., logically redundant]"
**Preconditions for claim to hold:**
1. [precondition] — counterexample: [scenario]. Guarded? YES/NO.
2. [precondition] — counterexample: [scenario]. Guarded? YES/NO.
## Potential Pitfalls
- [specific scenarios where the change could interact badly with
the surrounding system, identified from the caller-chain analysis]
Create a team and spawn 5 review agents in parallel. Include the context document from Phase 2 in each agent's prompt. Each agent writes findings to its own file and sends a summary message to the team lead.
Each agent's prompt should include:
## Codebase Context (READ THIS FIRST)
[paste or reference the context.md file]
You MUST consider how your findings interact with the upstream callers
and system invariants documented above. A finding that looks correct
in isolation may be a critical bug when you consider the full call chain.
This agent exists because the most critical bugs hide at component boundaries, not within a single component. It uses a fundamentally different methodology than the correctness reviewer: instead of verifying "does the code do what it intends?", it asks "what breaks when we change the assumptions?"
Data-flow analysis: Perform the cross-component data consumer analysis described in section 2g. For every piece of data the change WRITES to a shared structure, trace ALL READERS and verify their visibility rules are compatible with the writer's assumptions. This is a DATA-FLOW question, not a CONTROL-FLOW question — readers may be in completely different subsystems.
Alternative execution contexts: Use the canonical table from section 2h. Enumerate all contexts where the changed code executes and verify assumptions hold in each.
Assumption stress-testing and assert-breaking: Follow the methodology from section 2i. Identify every design claim, enumerate preconditions, construct counterexamples. Treat asserts as hypotheses to break, not proofs. Red flag words: "logically redundant", "safe because", "no-op", "always true", "cannot happen", "invariant holds"
Callee side-effect audit: Perform the callee side-effect audit described in section 2c. For every callee, ask what it RETURNS and what it MUTATES.
Write findings to findings-cross-component.md.
This agent does NOT read the diff in detail. It takes the feature summary and systematically tries to break it. While other agents verify "does the code do what it says?", this agent asks "what existing system invariants does this feature violate?" and "under what inputs do the design claims fail?"
Steps 1-4: Assumption stress-testing. Follow the methodology from section 2i: extract design claims, enumerate preconditions, construct counterexamples, and attempt to break every assert. Be exhaustive about input parameter ranges, shared state configurations, and concurrent interleaving.
Step 5: Callee side-effect audit. Perform the callee side-effect audit
described in section 2c. For every function the changed code calls, list ALL
mutations to shared state (atomic CAS loops, counter increments, flag/metadata
updates, cache invalidation). A function returning Status::OK() does NOT
mean its side effects are correct.
Write findings to findings-invariant-adversary.md.
This agent traces BACKWARD from every entry point the changed code hooks into. While other agents read the changed code forward ("what does it do?"), this agent enumerates WHO invokes it and WITH WHAT parameter ranges.
The key insight: a function that is correct for all typical inputs may be catastrophically wrong for atypical-but-reachable inputs. This agent's job is to find the atypical inputs.
Step 1: Identify entry points. List every function/constructor/method in the changed code that receives external input (parameters, config values, pointers to shared state). Include:
active_mem, read_callback, sequence)Step 2: Enumerate all callers (3-5 levels up). For each entry point,
search the codebase for ALL callers. Do NOT stop at the first caller — trace
the full call chain. Use Grep and Glob to find:
Step 3: Parameter range analysis. For each caller, determine:
kMaxSequenceNumber,
nullptr, old snapshot seqnos, non-null read_callback_)Build a parameter range table for each entry point, listing all callers and the values they pass for each parameter.
Step 4: Configuration matrix. Enumerate option/config combinations that affect the changed code path:
allow_concurrent_memtable_write, use_trie_index)Step 5: Cross-reference with context.md. Compare your caller analysis with the invariants and execution contexts documented in context.md. Flag any caller that violates an assumed precondition.
Write findings to findings-caller-audit.md.
After all 5 agents complete their initial review, the team lead orchestrates a structured debate:
Each agent reviews the findings from the other agents and sends messages to challenge, support, or refine them:
The debate assignment follows a round-robin pattern:
correctness-reviewer → critiques → invariant-adversary, serialization
cross-component-reviewer → critiques → correctness, caller-audit
invariant-adversary → critiques → correctness, cross-component
caller-audit → critiques → invariant-adversary, cross-component
performance-reviewer → critiques → api, cross-component
api-reviewer → critiques → correctness, performance
serialization-reviewer → critiques → correctness, invariant-adversary
test-reviewer → critiques → serialization, caller-audit
design-reviewer → critiques → cross-component, invariant-adversary
Note: The invariant-adversary and caller-audit agents are deliberately cross-linked with correctness and cross-component because their findings often reveal the same underlying bug from different angles (invariant violation vs reachable bad input vs data-flow mismatch).
Each agent should:
Team lead synthesizes the debate into a consensus document:
Final report quality rules:
REQUIRED output structure (so the PR page stays scrollable):
The final response (and contents of review-findings.md) MUST follow this
exact structure. The summary appears first so reviewers can see HIGH findings
at a glance; everything else is hidden behind a <details> block.
## Summary
<!-- One or two sentences of overall assessment. -->
**High-severity findings (N):**
- **[file.cc:123]** One-line description of the issue. <!-- repeat per HIGH finding -->
<!-- If there are NO high-severity findings, write exactly: -->
<!-- _No high-severity findings._ -->
<details>
<summary>Full review (click to expand)</summary>
### Findings
#### :red_circle: HIGH
##### H1. <Title> — `file.cc:123`
- **Issue:** ...
- **Root cause:** ...
- **Suggested fix:** ...
#### :yellow_circle: MEDIUM
... (same structure: M1, M2, ...)
#### :green_circle: LOW / NIT
... (same structure: L1, L2, ...)
### Cross-Component Analysis
<!-- Execution-context table and assumption stress-test results. -->
### Positive Observations
<!-- Optional: good patterns, clever optimizations. -->
</details>
Rules for this structure:
## Summary and the bullet list of HIGH findings MUST stay
outside the <details> block — they are always visible.<details> block.<details> inside another <details>.</details> — the comment-builder
appends its own footer.<details> block.Write all review artifacts to the working directory root:
review-findings.md — Incremental findings (appended after each phase),
then replaced with the final synthesized review at the endcontext.md — Codebase context (call chains, invariants)findings-*.md — Per-agent findings (design, correctness, cross-component,
invariant-adversary, caller-audit, performance, api, serialization, tests)consensus.md — Cross-review consensus (post-debate)Team Lead (you)
│
├── Phase 2: Codebase Context (team lead or dedicated research agent)
│ └── context-researcher (general-purpose agent)
│ ├── Trace caller chains (3-5 levels up)
│ ├── Trace callee chains (dependencies AND side effects)
│ ├── Trace data consumers (who reads what the change writes?)
│ └── Document invariants
│
├── Phase 3: Initial Review (parallel, run_in_background)
│ │ (all agents receive context.md in their prompt)
│ ├── design-reviewer (general-purpose agent)
│ ├── correctness-reviewer (general-purpose agent)
│ ├── cross-component-reviewer (general-purpose agent)
│ ├── invariant-adversary (general-purpose agent) ← NEW
│ ├── caller-audit (general-purpose agent) ← NEW
│ ├── performance-reviewer (general-purpose agent)
│ ├── api-reviewer (general-purpose agent)
│ ├── serialization-reviewer (general-purpose agent)
│ └── test-reviewer (general-purpose agent)
│
├── Phase 4: Debate (agents message each other)
│ ├── correctness ↔ invariant-adversary, serialization
│ ├── cross-component ↔ correctness, caller-audit
│ ├── invariant-adversary ↔ correctness, cross-component
│ ├── caller-audit ↔ invariant-adversary, cross-component
│ ├── performance ↔ api, cross-component
│ ├── api ↔ correctness, performance
│ ├── serialization ↔ correctness, invariant-adversary
│ ├── test-coverage ↔ serialization, caller-audit
│ └── design ↔ cross-component, invariant-adversary
│
To: correctness-reviewer
Re: Your Finding F1
AGREE/DISAGREE/REFINE - [reasoning with code evidence].
[Suggested severity adjustment if any.]
These recurring failure modes lead to missed bugs. Each is detailed in the referenced section; this table is a quick-reference checklist.
| Anti-Pattern | Fix | Reference |
|---|---|---|
| Return-Value Tunnel Vision | Trace callee MUTATIONS, not just returns | Section 2c |
| Default-Configuration Bias | Enumerate all execution contexts | Section 2h |
| Assert-as-Proof | Try to BREAK every assert | Section 2i |
| Write-Path-Only Analysis | Trace data readers, not just writers | Section 2g |
| Confirmation-Seeking Research | Use adversarial prompts ("find where X fails") | Invariant Adversary agent |
| Data-Flow vs Control-Flow Confusion | Separate who CALLS from who READS the data | Section 2g |