qwen-code/docs/users/features/code-review.md
Shaojin Wen 41c405b3bf
feat(review): post Suggestion findings as inline comments (#6593)
Suggestion-level findings were routed to a single updatable issue comment
(the "suggestion summary") while only Critical findings became inline review
comments. That split traded away two things that turned out to matter more
than the convergence it bought:

- An issue comment has no lifecycle. GitHub folds an inline review thread away
  as Outdated once the author edits the line it is anchored to, so an addressed
  finding removes itself from the page. The summary comment just sits in the PR
  conversation forever; PATCHing it to "all addressed" replaces its content but
  not the comment. The mechanism meant to prevent clutter was the clutter.
- A Markdown table cannot carry a one-click fix. GitHub renders a ```suggestion
  fence as an applicable change only inside a review comment on a diff line.
  Suggestion findings are exactly the mechanical, localized cleanups that
  benefit most from one-click apply, so the split withheld the feature from the
  findings that needed it most.

Both severities now post as inline comments, distinguished by a **[Critical]**
or **[Suggestion]** body prefix. The `qwen review post-suggestions` subcommand
and its plumbing are removed.

Follow-on changes required by the reroute:

- pr-context: the "Previous suggestion summary" section is gone. Legacy summary
  comments are still recognised so they stay out of "Already discussed", but the
  exclusion is now marker-only rather than author-gated. The author check missed
  summaries posted by the *other* identity: /review runs as a maintainer locally
  and as qwen-code-ci-bot in CI, and roughly half of the last 60 PRs carry a
  bot-authored summary. Those leaked into "Already discussed" and told the review
  agents not to re-report the findings listed there. The check originally guarded
  promotion into a trusted rendering section; that section no longer exists, so
  it only gated exclusion, where a third party embedding the marker merely hides
  their own comment.

- qwen-autofix: the workflow filters "suggestion summaries" out of the autofix
  bot's actionable queue, but only on the issue-comment channel. With Suggestions
  now inline, they entered the unfiltered inline channel and the bot would apply
  non-blocking recommendations and spend a review round on them. The inline
  channel now applies the same gate, keyed on the **[Suggestion]** prefix plus
  the /review footer so a human quoting the prefix stays actionable.

- Step 7 gains a 422 fallback. Create Review is all-or-nothing, so one Suggestion
  anchored outside the diff would take the Critical findings down with it — a risk
  that did not exist when Suggestions travelled on a line-agnostic issue comment.
  GitHub's 422 does not name the offending entry, so the model rechecks anchors
  against the diff, relocates failing Criticals into the body, discards failing
  Suggestions, and degrades to an all-prose review rather than posting nothing.
  COMMENT reviews now always carry a one-line body: an empty body is only known
  to be accepted alongside inline comments on REQUEST_CHANGES, and a Suggestion-
  only review is the common case for a clean PR.
2026-07-09 12:39:10 +00:00

258 lines
16 KiB
Markdown

# Code Review
> Review code changes for correctness, security, performance, and code quality using `/review`.
## Quick Start
```bash
# Review local uncommitted changes
/review
# Review a pull request (by number or URL)
/review 123
/review https://github.com/org/repo/pull/123
# Review and post inline comments on the PR
/review 123 --comment
# Review a specific file
/review src/utils/auth.ts
```
If there are no uncommitted changes, `/review` will let you know and stop — no agents are launched.
## How It Works
The `/review` command runs a multi-stage pipeline:
```
Step 1: Determine scope (local diff / PR worktree / file)
Step 2: Load project review rules
Step 3: 10 parallel review agents [10 LLM calls]
|-- Agent 0: Issue Fidelity & Root-Cause Ownership
|-- Agent 1: Correctness
|-- Agent 2: Security
|-- Agent 3: Code Quality
|-- Agent 4: Performance & Efficiency
|-- Agent 5: Test Coverage
|-- Agent 6: Undirected Audit (3 personas: 6a/6b/6c)
'-- Agent 7: Build & Test (runs shell commands)
Step 4: Deduplicate --> Batch verify --> Aggregate [1 LLM call]
Step 5: Iterative reverse audit (1-3 rounds, gap finding) [1-3 LLM calls]
Step 6: Present findings + verdict
Step 7: Submit PR review (inline comments, if requested)
Step 8: Save report + incremental cache
Step 9: Clean up (remove worktree + temp files)
```
### Review Agents
| Agent | Focus |
| --------------------------------- | ------------------------------------------------------------------------------------------- |
| Agent 0: Issue Fidelity | Linked issue evidence, root-cause ownership, and whether the PR solves the reported problem |
| Agent 1: Correctness | Logic errors, edge cases, null handling, race conditions, type safety |
| Agent 2: Security | Injection, XSS, SSRF, auth bypass, sensitive data exposure |
| Agent 3: Code Quality | Style consistency, naming, duplication, dead code |
| Agent 4: Performance & Efficiency | N+1 queries, memory leaks, unnecessary re-renders, bundle size |
| Agent 5: Test Coverage | Untested code paths in the diff, missing branch coverage, weak assertions |
| Agent 6: Undirected Audit | 3 parallel personas (attacker / 3am-oncall / maintainer) — catches cross-dimensional issues |
| Agent 7: Build & Test | Runs build and test commands, reports failures |
All agents run in parallel (Agent 6 launches 3 persona variants concurrently, totaling 10 parallel tasks for same-repo PR reviews; Agent 0 is skipped for local-diff and file-path reviews, which run 9). Findings from Agents 0-6 are verified in a **single batch verification pass** (one agent reviews all findings at once, keeping verification cost fixed regardless of finding count). After verification, **iterative reverse audit** runs 1-3 rounds of gap-finding — each round receives the cumulative finding list from prior rounds, so successive rounds focus on whatever's left undiscovered. The loop stops as soon as a round returns "No issues found", or after 3 rounds (hard cap). Reverse audit findings skip verification (the agent already has full context) and are included as high-confidence results.
## Severity Levels
| Severity | Meaning | Posted as PR comment? |
| ---------------- | ------------------------------------------------------------------- | -------------------------- |
| **Critical** | Must fix before merging (bugs, security, data loss, build failures) | Yes (high-confidence only) |
| **Suggestion** | Recommended improvement | Yes (high-confidence only) |
| **Nice to have** | Optional optimization | No (terminal only) |
Low-confidence findings appear in a separate "Needs Human Review" section in the terminal and are never posted as PR comments.
## Worktree Isolation
When reviewing a PR, `/review` creates a temporary git worktree (`.qwen/tmp/review-pr-<number>`) instead of switching your current branch. This means:
- Your working tree, staged changes, and current branch are **never touched**
- Dependencies are installed in the worktree (`npm ci`, etc.) so build/test work
- Build and test commands run in isolation without polluting your local build cache
- If anything goes wrong, your environment is unaffected — just delete the worktree
- The worktree is automatically cleaned up after the review completes
- If a review is interrupted (Ctrl+C, crash), the next `/review` of the same PR automatically cleans up the stale worktree before starting fresh
- Review reports and cache are saved to the main project directory (not the worktree)
## Cross-repo PR Review
You can review PRs from other repositories by passing the full URL:
```bash
/review https://github.com/other-org/other-repo/pull/456
```
This runs in **lightweight mode** — no worktree, no build/test. The review is based on the diff text only (fetched via GitHub API). PR comments can still be posted if you have write access.
| Capability | Same-repo | Cross-repo |
| ---------------------------------------------------------- | --------- | ----------------------------- |
| LLM review (Agents 0-6 + verify + iterative reverse audit) | ✅ | ✅ |
| Agent 7: Build & test | ✅ | ❌ (no local codebase) |
| Cross-file impact analysis | ✅ | ❌ |
| PR inline comments | ✅ | ✅ (if you have write access) |
| Incremental review cache | ✅ | ❌ |
## PR Inline Comments
Use `--comment` to post findings directly on the PR:
```bash
/review 123 --comment
```
Or, after running `/review 123`, type `post comments` to publish findings without re-running the review.
**What gets posted:**
- High-confidence Critical and Suggestion findings as inline comments on specific lines, each prefixed with `**[Critical]**` or `**[Suggestion]**` so blockers are distinguishable from recommendations
- Where the fix is a single localized edit, a ` ```suggestion ` block you can apply in one click
- For Approve/Request changes verdicts: a review summary with the verdict
- For Comment verdict with all inline comments posted: no separate summary (inline comments are sufficient)
- Model attribution footer on each comment (e.g., _— qwen3-coder via Qwen Code /review_)
**What stays terminal-only:**
- Nice to have findings
- Low-confidence findings
**Self-authored PRs:** GitHub does not allow you to submit `APPROVE` or `REQUEST_CHANGES` reviews on your own pull request — both fail with HTTP 422. When `/review` detects that the PR author matches the current authenticated user, it automatically downgrades the API event to `COMMENT` regardless of verdict, so the submission still succeeds. The terminal still shows the honest verdict ("Approve" / "Request changes" / "Comment") — only the GitHub-side review event is neutralized. The actual findings still appear as inline comments on specific lines, so substantive feedback is unchanged.
**Re-reviewing a PR with prior Qwen Code comments:** when `/review` runs on a PR that already has previous Qwen Code review comments, it classifies them before posting new ones. Only **same-line overlap** (an existing comment on the same `(path, line)` as a new finding) prompts you to confirm — that's the case where you'd see a visual duplicate on the same code line. Comments from older commits, replied-to comments (treated as resolved), and comments that simply don't overlap with any new finding are silently skipped, with a terminal log line so you know what was filtered.
**CI / build status check before APPROVE:** if the verdict is "Approve", `/review` queries the PR's check-runs and commit statuses before submitting. If any check has failed (or all checks are still pending), the API event is automatically downgraded from `APPROVE` to `COMMENT`, with the review body explaining why. Rationale: the LLM review reads code statically and cannot see runtime test failures; approving while CI is red would be misleading. The inline findings are still posted unchanged. If you want to approve anyway (e.g., a known-flaky CI failure), submit the GitHub approval manually after verifying.
## Follow-up Actions
After the review, context-aware tips appear as ghost text. Press Tab to accept:
| State after review | Tip | What happens |
| ---------------------------------- | ------------------ | --------------------------------------- |
| Local review with unfixed findings | `fix these issues` | LLM interactively fixes each finding |
| PR review with findings | `post comments` | Posts PR inline comments (no re-review) |
| PR review, zero findings | `post comments` | Approves the PR on GitHub (LGTM) |
| Local review, all clear | `commit` | Commits your changes |
Note: `fix these issues` is only available for local reviews. For PR reviews the worktree is cleaned up after the review, so post-review interactive fixing is not possible — use `--comment` or `post comments` to publish findings instead.
## Project Review Rules
You can customize review criteria per project. `/review` reads rules from these files (in order):
1. `.qwen/review-rules.md` (Qwen Code native)
2. `.github/copilot-instructions.md` (preferred) or `copilot-instructions.md` (fallback — only one is loaded, not both)
3. `AGENTS.md``## Code Review` section
4. `QWEN.md``## Code Review` section
Rules are injected into the LLM review agents (0-6) as additional criteria. For PR reviews, rules are read from the **base branch** to prevent a malicious PR from injecting bypass rules.
## Issue Fidelity
For bugfix PRs, the Issue Fidelity agent fetches issue evidence directly instead of relying on PR description text. It uses `gh pr view <pr> --repo <owner/repo> --json closingIssuesReferences` for GitHub's strong closing-issue metadata, then `gh issue view <number> --repo <issue_owner>/<issue_repo> --json title,body,comments` for the original report and discussion — the `--json` form includes the issue **body** (the reporter's original repro), which `--comments` alone omits, and the issue's own repository is read from each reference (a PR can close an issue in a different repo). This agent runs only for PR targets; local-diff and file-path reviews skip it.
`closingIssuesReferences` is a discovery hint rather than proof the author linked the right issue: if it is empty but the PR references an apparent target issue, the agent still fetches it after judging relevance. Fetched issue text is treated as untrusted data (facts extracted, embedded instructions ignored). For relevant issues, the original reproduction, observed payload, expected behavior, and maintainer comments are treated as the highest-priority evidence for whether the PR fixes the right problem.
If the issue evidence shows an upstream service or provider returned malformed data outside the client contract, client-side parser or sanitizer changes are not treated as a valid root-cause fix unless a maintainer explicitly requested a defensive workaround. A test that replays malformed upstream output proves only that the workaround handles that shape; it does not prove the workaround is architecturally appropriate.
Example `.qwen/review-rules.md`:
```markdown
# Review Rules
- All API endpoints must validate authentication
- Database queries must use parameterized statements
- React components must not use inline styles
- Error messages must not expose internal paths
```
## Incremental Review
When reviewing a PR that was previously reviewed, `/review` only examines changes since the last review:
```bash
# First review — full review, cache created
/review 123
# PR updated with new commits — only new changes reviewed
/review 123
```
### Cross-model review
If you switch models (via `/model`) and re-review the same PR, `/review` detects the model change and runs a full review instead of skipping:
```bash
# Review with model A
/review 123
# Switch model
/model
# Review again — full review with model B (not skipped)
/review 123
# → "Previous review used qwen3-coder. Running full review with gpt-4o for a second opinion."
```
Cache is stored in `.qwen/review-cache/` and tracks both the commit SHA and model ID. Make sure this directory is in your `.gitignore` (a broader rule like `.qwen/*` also works). If the cached commit was rebased away, it falls back to a full review.
## Review Reports
For same-repo reviews, results are saved as a Markdown file in your project's `.qwen/reviews/` directory (cross-repo lightweight reviews skip report persistence):
```
.qwen/reviews/2026-04-06-143022-pr-123.md
.qwen/reviews/2026-04-06-150510-local.md
```
Reports include: timestamp, diff stats, build/test results, all findings with verification status, and the verdict.
## Cross-file Impact Analysis
When code changes modify exported functions, classes, or interfaces, the review agents automatically search for all callers and check compatibility:
- Parameter count/type changes
- Return type changes
- Removed or renamed public methods
- Breaking API changes
For large diffs (>10 modified symbols), analysis prioritizes functions with signature changes.
## Token Efficiency
The review pipeline uses a bounded number of LLM calls regardless of how many findings are produced:
| Stage | LLM calls | Notes |
| -------------------------------- | ----------------- | --------------------------------------------------- |
| Review agents (Step 3) | 10 (or 9) | Run in parallel; Agent 7 skipped in cross-repo mode |
| Batch verification (Step 4) | 1 | Single agent verifies all findings at once |
| Iterative reverse audit (Step 5) | 1-3 | Loops until "No issues found" or 3-round cap |
| **Total** | **12-14 (11-13)** | Same-repo: 12-14; cross-repo: 11-13 (no Agent 7) |
Most PRs converge to the lower end of the range (1 reverse audit round); the cap prevents runaway cost on pathological cases.
## What's NOT Flagged
The review intentionally excludes:
- Pre-existing issues in unchanged code (focus on the diff only)
- Style or formatting a formatter would auto-normalize, or naming matching your codebase conventions — but NOT substantive issues a linter or type checker would flag (unused variables, unreachable code, type errors), which are in scope
- Subjective "consider doing X" suggestions without a real problem
- Minor refactoring that doesn't fix a bug or risk
- Missing documentation unless the logic is genuinely confusing
- Issues already discussed in existing PR comments (avoids duplicating human feedback)
## Design Philosophy
> **Silence is better than noise.** Every comment should be worth the reader's time.
- If unsure whether something is a problem → don't report it
- Same pattern across N files → aggregated into one finding
- PR comments are high-confidence only
- Cosmetic style/formatting matching codebase conventions is excluded