← Files QodoARCHIVED FILE

skills/qodo-review/SKILL.md

34.3 KB · Oct 4, 2026 · 12:32 UTC

↓ Download file

---
name: qodo-review
description: Review local changes with Qodo during substantive coding milestones and before a PR or completed-work handoff. Use light background checkpoints while coding, collect and assess findings with session context, and run a final review before handing off. Also use for "review my local diff", "pre-PR review", or "run qodo review". Use qodo-review-resolver for findings already posted on a PR.
owner: Qodo
metadata:
  vendor: qodo
  version: "1.10.5"
  recommended: "true"
  package: "qodo"
  distribution: "marketplace"
  instruction_mode: "embedded"
---

# Local Review

## Description

Use the `qodo` CLI to review **local changes during coding and before handing off completed work**. `qodo review` diffs your working tree against a base branch, includes new/untracked files, and sends the diff plus any
**coding-session context** you supply to Qodo's review engine. It returns structured findings you then evaluate and — with the user's say-so (or `autofix`) — fix in code. Nothing is pushed and no
PR is created; only the base commit must already be on the remote (the reviewer clones it).

Use this skill for local changes, including unpushed work on an existing PR. Use `qodo-review-resolver`
to work on findings from the PR's remote review; do not duplicate that review for an unchanged pushed snapshot.

## Prerequisites

- The Qodo CLI is installed and authenticated, and the review capability is enabled.
- The comparison base exists on the remote; local changes and context need not be pushed.
- The coding-session context and any ticket or design references are ready to attach.

## Choose when and how deeply to review

| Situation | Selection |
|---|---|
| A coherent milestone during substantive coding | `--fast --async`: light checkpoint; continue useful coding and collect the result. |
| Ordinary final review before opening/updating a PR or handing off completed work | Omit both depth flags: auto. Collect and assess the result before claiming review completion. |
| User intent calls for unusually thorough scrutiny, or a known high blast radius warrants it | `--deep`, with a brief reason tied to the request or affected behavior. |

Automatic coding checkpoints always use `--fast`. A deliberate deep review can happen earlier when
one of the exceptions above applies. A request such as "examine subtle races thoroughly" can justify
it without the word "deep"; generic "review" or "double-check" does not. Identify concrete impact,
such as shared authorization, a destructive migration, or a contract used by multiple services.
Diff size, session length, PR readiness, or a security-related filename alone do not justify deep.

With **no flag**, the CLI omits depth and the reviewer/deployment selects it. Auto can select deep;
it is not guaranteed standard and does not impose a spending cap. There is no `--standard` flag.
`--fast` and `--deep` are mutually exclusive; depth is selected anew for each invocation.

## Checkpoints and final handoff

- Review a completed, testable slice when its feedback can guide the remaining work. Do not review
  unfinished exploration, every edit, trivial changes automatically, or simply because time passed.
  Respect the user's review scope and budget. A pause for a question or approval is not a final handoff.
- Keep at most one automatic checkpoint active per task. Retain its operation ID, submitted scope
  and snapshot/context association in the task's existing state. Poll status at natural pauses while
  doing useful work; do not launch another review to check progress or re-review unchanged inputs.
- Collect every submitted result within its retention window. Recheck findings against current code
  before acting: the working tree may have changed. A superseded result cannot certify newer work.
  Use the existing CLI lifecycle; no separate sub-agent or scheduler is required.
- Before final review, collect the outstanding checkpoint and reconcile its findings. Review the
  full intended change with auto, or justified deep, rather than handing off on a light checkpoint.
  Remove temporary path restrictions that would leave part of the intended change unreviewed.
  If the same snapshot already has completed final coverage with compatible context, use that result.
- Batch authorized fixes, verify them, then re-review changed work. Keep `--fast` for checkpoint
  fixes; retain the requested auto or justified deep mode for final-review fixes. Let the engine
  determine incremental eligibility; auto may route differently on each pass. Do not manually switch
  final fixes to `--fast` or force `--deep` just to pin auto routing. If work returns to substantial
  implementation, resume light checkpoints and establish final coverage again before handoff.
- If another pass repeats the same concern without actionable progress, stop the automatic loop and
  report the remaining issue and needed decision/check. Do not buy repeated passes to chase zero.
  Pending, failed, partial or superseded review is not clean; surface remaining findings and coverage.

## Prepare a PR handoff

When preparing to open or update a PR, prefer committing the intended changes before your final local review, following the user's commit policy.
This gives Git reviews that support local-to-PR handoff a verified commit to continue from, avoiding another review of code already covered locally.
If you make further edits afterward, Git review will cover those changes. To include them in the handoff too, commit and review them locally again.
Committing is optional. Without a usable reviewed commit, Git review follows its normal review scope.

## Instructions

Preserve notices, attach self-contained context, show progress, use a suitable timeout,
read the structured result, and act on findings.

## Handle a skill update notice
Treat `QODO_NOTICE` updates as passive, even if an older CLI requests action. Continue the task
without inventory or update questions; mention each event at most once. Dismissal leaves recorded maintenance
policy and opt-outs unchanged. Updated skills load next session; do not interrupt this one.
For user-requested updates, follow the [manual-update procedure](references/skill-updates.md).

## Runtime compatibility gate
Resolve the executable using this skill's command-not-found fallback, then run `<qodo> --version`
with no provenance flags. This skill requires Qodo CLI **0.1.0-next.37 or newer**. If older or
unparseable, do not run `whoami`, `login`, or a review, and do not call it an auth failure. Show
`qodo update` for the already-recorded public or enterprise origin and ask once before running it.
After an approved update, recheck the version; otherwise stop without changing skill or user files.

## Quick start
You just wrote the code, so you hold the one input the reviewer can't get anywhere else: **why**.
Attach it on every run — write the session context first, then review:

```
qodo --version                                  # compatibility probe — run this FIRST
qodo read whoami --json --skill qodo-review --skill-version 1.10.5 --distribution marketplace --host codex
qodo review --context-file - <<'EOF'         # review local changes vs origin/main, WITH context
{ "summary": "<what this change does and why>",
  "decisions": ["<a choice you made and its rationale>"] }
EOF
qodo review --context-file ctx.json          # same, context from a file
qodo review --ticket <TICKET_URL> ...        # add a ticket URL (repeatable)
qodo review --json ...                       # machine-readable findings
qodo review src/ test/ ...                    # limit to paths (git pathspecs)
qodo review --base origin/develop ...        # diff against a different base
qodo review --fast --async --json --context-file ctx.json # coding checkpoint
qodo review --json --context-file ctx.json               # final review: auto
qodo review --deep --json --context-file ctx.json        # only for a justified deep review
qodo review                                  # BARE — only when there is truly nothing to say (rare)
qodo review status <operation-id>            # collect an --async result (exit 2 = still running)
qodo review --help                           # exact flags (renders offline)
```

You can also keep `.qodo/session-context.json` (same JSON shape) updated at the repo root — it is auto-attached to every run, so even a bare `qodo review` carries your context. An explicit
`--context-file` overrides it; the file itself is never part of the reviewed diff. Don't commit it
(add `.qodo/` to `.gitignore` or `.git/info/exclude`).
Add `--json` to anything you parse. For connected execution, allow a multi-minute timeout or
background the process; async submission and status collection are separate short calls.
**Confirm the exact flags with `qodo review --help`** (offline) — the examples here are illustrative.

## Choose execution for the selected review

Use `--async` for coding checkpoints: submit, continue useful work, then collect with `review status`.
For a final review, async is also valid, but collect and assess it before the handoff. If live progress
is useful, read [connected progress](references/connected-progress.md) and use `--json --progress`
with a host-native background process. Never combine `--progress` with `--async`.
A review can take minutes; keep a connected process alive or use async so client exit cannot cancel it.
If async is unavailable in the installed CLI, keep the selected depth and use the connected fallback.

For connected progress, the canonical execution rules are:

- Attach context through a file; stdin heredocs are unsuitable for background execution.
- Use a unique per-run temporary directory. Separate the single result JSON on stdout from NDJSON
  progress on stderr. Poll the growing progress file through the host's nonblocking process tools;
  never run a foreground `tail` that blocks the agent until completion.
- Relay short status messages, not raw JSON or model output. Translate events by `kind`:
  `cli.status` gives a readable message; `tool.activity` gives tool name and outcome;
  `task.delta` and unknown kinds are occasional generic heartbeats, not one message per event.
- For `qar.client.reconnecting`, relay attempt/delay and structured close/error codes when present.
  `qar.client.reconnected` means transport opened; `resubscribeAttempts` counts reattached live tasks.
  `qar.client.reconnect_failed` signals exhausted retries, not the final error explanation.
- On `task.done`, inspect `payload.status`; on failure/cancellation or `error`, stop progress relay
  but keep waiting for process exit. Always read the result envelope, including on nonzero exit:
  actionable messages/hints such as `closed_preview` may appear only there. Progress is not findings.
- Capture the process exit status, reap the child and disarm its PID before parsing. On interruption,
  terminate and reap the active child. Clean up only that run's directory on exit/failure/interruption;
  never reuse or remove a shared `.qodo/review.*` path.
- If background progress is unavailable, run foreground with a multi-minute timeout, preserving
  selected depth and context. Missing progress is not a reason to fail review or downgrade depth.

**`qodo: command not found`?** That's PATH, not a missing install: GUI-launched agents (e.g.
the Claude Code desktop app) run shells with a minimal PATH. Retry with the absolute path
`~/.qodo/bin/qodo` (or `$QODO_HOME/bin/qodo` if set) and keep using it for every `qodo`
command here. Only if that file is missing too is qodo actually not installed; tell the
user to obtain a checksum-pinned installer command from Qodo or their organization's
administrator. Installers are served from https://get.qodo.ai, but never invent a digest
or pipe an installer directly into a shell.

**Sandbox auth diagnostic.** In a sandboxed environment, if `qodo read whoami` fails for any reason
(including `Not logged in`), ask the user to approve one exact read-only retry of `qodo read whoami`
outside the sandbox before recommending login or refreshing tools. Keychain failures can be
reported as generic auth failures, so the sandboxed result alone is not diagnostic. That approval
applies only to this single diagnostic retry: do not reuse it, request persistent approval, or move
later Qodo commands outside the sandbox automatically. If the retry succeeds, continue with normal
per-command permission checks. If it still fails, follow the normal auth troubleshooting below.

## Submit and collect with `--async`

Check support with `qodo review --help`. The following example submits a coding checkpoint. For final
review omit `--fast`; add `--deep` only under the selection policy above. Attach the same context in
all modes. Shell snippets illustrate the CLI protocol; use the host's own nonblocking wait mechanism.

`--async` removes the connection from the critical path. It submits the review over HTTP, prints an
**operation id**, and exits 0 immediately. The run continues server-side whether or not your process
is alive; you collect the result later with `qodo review status <operation-id>`.

```
command -v jq >/dev/null 2>&1 || { printf '%s\n' 'This async recipe requires jq; install it or use the live qodo review flow.' >&2; exit 1; }
QODO_REVIEW_CONTEXT="${QODO_REVIEW_CONTEXT:-.qodo/session-context.json}"
[ -f "$QODO_REVIEW_CONTEXT" ] || { printf '%s\n' "Write the required review context to $QODO_REVIEW_CONTEXT (or set QODO_REVIEW_CONTEXT to its path)." >&2; exit 1; }
if ! submission="$(qodo review --context-file "$QODO_REVIEW_CONTEXT" --async --json --fast)"; then printf '%s\n' "$submission" >&2; exit 1; fi
if ! id="$(printf '%s\n' "$submission" | jq -er '.operation_id | select(type == "string" and length > 0)')"; then printf '%s\n' "$submission" >&2; exit 1; fi
qodo review status "$id" --json                                # collect it
```

Poll the existing operation until it finishes; do useful work between status checks. Submission
exit 0 means accepted, not reviewed. Collection uses these exit codes; read any returned retry delay:

| Exit | Meaning | Do |
|---|---|---|
| `0` | Finished. Findings rendered — **identical** output to a live run (`{findings, meta}` under `--json`). | Act on the findings as usual. |
| `2` | Still running or polling throttled. | Respect `retry_after` when present, then poll the same ID. |
| `1` | Failed, canceled, expired, or no such operation. Read the `error` envelope. | Follow the bounded recovery below; never assume clean. |

```
# This collection attempt returns failures to the host for classification under Recover a review.
# On nonzero exit, preserve the operation ID and emitted error; apply bounded recovery there.
QODO_REVIEW_TMP="$(mktemp -d "${TMPDIR:-/tmp}/qodo-review.XXXXXX")"
cleanup_qodo_review() { [ -n "${QODO_REVIEW_TMP:-}" ] && [ -d "$QODO_REVIEW_TMP" ] && rm -r -- "$QODO_REVIEW_TMP"; }
trap cleanup_qodo_review EXIT; trap 'exit 130' INT; trap 'exit 143' TERM
while :; do
  status=0; qodo review status "$id" --json > "$QODO_REVIEW_TMP/result.json" || status=$?
  case "$status" in
    0) cat "$QODO_REVIEW_TMP/result.json" || exit 1; break ;;
    2) delay=$(jq -r 'if (.retry_after | type) == "number" and .retry_after > 0 then .retry_after else 15 end' "$QODO_REVIEW_TMP/result.json") || exit 1
       sleep "$delay" ;;
    *) cat "$QODO_REVIEW_TMP/result.json" >&2; exit "$status" ;;
  esac
done
```

**What it costs — know these before you choose it:**

- **No streaming progress.** There are no progress events on this path — the run isn't attached to
  your process — so `--async` and `--progress` are rejected together rather than emitting a stream
  that never arrives. There is no intermediate status beyond "still running". If the user is
  watching and wants to see life, use the [connected progress](references/connected-progress.md) recipe instead.
- **No human-in-the-loop.** This surface runs deterministic agents only; a review that asks for
  input fails instead of waiting. (`qodo review status` says so and tells you to re-run without
  `--async`.)
- **The result is kept for 1 hour** after the review finishes, then it is discarded. Collect it
  inside that window. Past it, `qodo review status` cannot tell "expired" from "never existed" or
  "belongs to someone else" — the runtime answers all three identically, on purpose — so it reports
  all of them. Report the missing review evidence before deciding whether a new run is needed.
- **Do not assume a new submission is free or deduplicated.** Use `review status` to collect an
  accepted run. If admission is uncertain, follow the CLI's returned recovery command (on versions
  that support it, `--async --retry-submission <id>`); never substitute a fresh `qodo review` to poll.
  Preserve the retained request during recovery instead of gathering the changing working tree again.
- **Retain the operation id.** It identifies the accepted run; a submission-recovery ID has a different
  purpose. Do not throw away either handle before collecting or reconciling its outcome.

**The operation id is not the `trace` id.** `qodo` prints a `trace <id>` line on failures — that's
an OpenTelemetry id for support to diagnose a run with, and it cannot fetch anything. The
`operation_id` from `--async` is the resumable handle. Don't pass one where the other is wanted.

## Preflight

1. **Auth first.** Run `qodo read whoami`. After the sandbox retry above when applicable, a non-zero
   exit → tell the user to re-run the exact login command supplied by their installer,
   organization, or configured endpoint, then stop. With no custom endpoint, use `qodo login`;
   with an explicit endpoint, preserve it as `qodo login --auth-url <their-url>`. Never replace a
   custom deployment with the cloud default or invent an endpoint. `Not logged in` /
   `No tool catalog cached` require login. If `whoami`
   succeeds but the built-in `qodo review` command is unknown, the runtime is too old; ask the
   user to update the CLI from the official source. Re-login and catalog refresh cannot add this
   built-in command.
2. **Push the base.** The reviewer clones the base commit from the remote, so the base branch
   (default `origin/main`) must be pushed. If `qodo review` says the base isn't pushed, push it or
   pass a pushed `--base <ref>`. Your own local changes do NOT need to be committed or pushed —
   uncommitted edits and untracked new files are included automatically.
3. **Write your context.** Before running, capture the session narrative — a 2–3 sentence summary
   of what you changed and why, plus the decisions you made along the way — as the context JSON
   (stdin heredoc, a file, or `.qodo/session-context.json`). You always have this: you just wrote
   the code. Run bare only when there is genuinely nothing to say.

## What gets reviewed

`qodo review` gathers, all client-side:

- The **tracked diff** vs the base, plus **new/untracked files** (secrets, binaries, oversized,
  and gitignored files are filtered out and reported — never silently dropped).
- The **branch name**, **HEAD commit**, and a **description** synthesized from your commit messages.
- Any **ticket refs** and **session context** you attach (below).

`--json` returns `findings` from this call, with optional `meta` and `finding_state` on newer engines.
For repeated reviews, read `finding_state.introduced` **and** `.still_open`; an empty `findings`
array alone does not mean clean. `.resolved` records detected fixes; `.dismissed` preserves dismissals.
If `finding_state.complete` or `meta.coverage.complete` is false, report the incomplete coverage.

`meta.analysis.mode` is `full`, `incremental`, or `reused`. Reused means the same reviewed snapshot
and compatible context; earlier open findings remain open. The CLI privately saves the submitted patch.
It advances its checkpoint after collecting an eligible result, including via `review status`.
Failed or older completions cannot replace a newer checkpoint.
Keep the same base, path scope, requested depth mode and context during a fix loop. Changed context, expired or
unverifiable checkpoints, and unsupported deltas fall back to full review. Never fabricate a checkpoint.
For a deliberately fresh assessment use `--full` (confirm support with `--help`). It controls scope,
not depth; it is not needed on every fix. Changing the requested depth mode invalidates compatible
checkpoint coverage. Auto does not promise a fixed effective tier; rely on returned analysis/coverage.
Older engines omit these fields: use their findings and coverage without claiming reuse.
`meta.reviewers.ran` / `.skipped`, `meta.depth`, and `meta.safety_net.reinjected` describe coverage.
A reused result has no new reviewer execution. Attach missing input for a material skipped dimension.
Never remove context merely to make an incremental checkpoint eligible.

## Attach coding-session context (this is the point)

Attach the intent and decisions behind your change. Three channels:

- `--ticket <url>` — a ticket/issue URL (repeat for several). Pass the **full URL** (e.g. a
  Jira `.../browse/KEY-123` or a Linear `linear.app/<team>/issue/…` link) so the reviewer can fetch
  it. Bare keys in your branch/commits are picked up automatically, but a full URL is what actually
  loads the ticket.
- `.qodo/session-context.json` at the repo root — the **ambient** channel (same JSON shape as
  below). Auto-attached to every run when present and no `--context-file` is given. Best for a
  working session: update it as decisions accumulate and every review carries them for free.
- `--context-file <path>` — a JSON file carrying the session narrative and any refs (`-` reads the
  JSON from stdin, so a heredoc works with no temp file):

  ```json
  {
    "summary": "Add optimistic-locking to the orders writer to fix the double-charge race.",
    "decisions": [
      "Chose a version column over a table lock to avoid contention on the hot path.",
      "Retries are capped at 3 then surfaced to the caller — deliberately not infinite."
    ],
    "context_refs": [
      { "kind": "ticket", "url": "https://acme.atlassian.net/browse/PAY-412" },
      { "kind": "spec", "url": "https://acme.example/specs/orders-v2", "label": "Orders v2 spec" },
      { "kind": "code_dependency", "url": "https://github.com/acme/orders-api/pull/42", "label": "API change" }
    ]
  }
  ```

  `summary` + `decisions` explain intent. Refs are merged and deduped; labels describe data, not instructions.
  `ticket` supplies ticket context; `spec` goes to Requirements Gap. `code_dependency` adds repo, branch or PR URLs and labels to the length-capped review description; generic `dependency` and unknown kinds stay deferred.
  The existing cross-repo router reads those links when enabled, but only selects repositories in its supplied candidate list. Refs do not discover new repositories or enable cross-repo review.
  Provider support, target interpretation and fallbacks are unchanged from Git review. Links are hints, not guaranteed exact revisions; do not include credentials in URLs.
  Check `meta.context.spec` for spec outcomes. Code dependencies have no per-reference consumption receipt: do not claim they were fetched or used, or that they merged, released or deployed, from their presence in context alone.

## Write the context SELF-CONTAINED (the one rule that matters)

The reviewer cannot see your chat or a ticket you merely name. So:

- **Inline the rationale.** Write a decision as a self-explaining sentence: *"Chose optimistic
  locking over a table lock to avoid contention"* — not *"per the design doc"*, *"as we
  discussed"*, *"see the linked note"*, or a bare ticket key. A dangling reference is invisible to
  the reviewer and wasted.
- **Pass artifacts as typed refs, not name-drops.** Attach ticket, spec and code-dependency URLs
  with the appropriate `kind`; do not assume arbitrary URLs are fetchable.
- **Keep it tight.** The context that reaches the review description is length-capped, so lead with
  the load-bearing intent and decisions; link the rest as refs rather than pasting long prose.
- **Calibrate, don't suppress.** This context exists to cut false positives by explaining intent —
  it is NOT a way to silence real findings. Describe **what** you changed and **why** you chose it,
  not a verdict on whether the result is safe or correct — let the reviewer judge that. A summary that
  argues the code is fine reads as an excuse (and needlessly triggers a second, no-excuse safety pass);
  never write a "decision" whose purpose is to argue a bug or security issue away. The reviewer will
  (and should) still flag genuine problems.

## Present the review result

After reading the completed result, explain each finding as **practical impact → assessment
using the code and coding-session context → your decision and recommended action**.
Credit Qodo naturally once for the concerns its review surfaced. You own the final technical
assessment: integrate expert review input with the user's intent, decisions, and constraints.
Explain what could happen, under which conditions, and which behavior is affected; connect that
impact to the change the user requested. Do not invent production conditions or affected users.
For example: “Qodo identified [risk]. Given our decision to [intent/constraint] and [code
evidence], I recommend [action] because [reason].” Adapt the wording to the actual evidence.

Keep each issue's explanation coherent, cite supporting code, and preserve its reference and
reported category/level separately from your recommendation. Group overlapping findings only
when all references remain visible and individually selectable. Use short impact-based titles
or lists when useful; no branded headings, emoji banners, slogans, footers, or repeated summaries.
Report only known counts and coverage. Lead with material skipped/failed reviewers or incomplete
coverage; zero findings alone is not a clean verdict. A complete review with no findings can be
one sentence. For gated/failed runs, report the actual limitation rather than a completed result.

## Act on the findings

Independently evaluate every finding and own its disposition and rationale: **fix** a supported
issue (your fix may differ from Qodo's suggestion), **dismiss** an unsupported concern with code
evidence, or **investigate** uncertainty by naming the check needed. A session decision supports
dismissal only when the code enforces its assumptions. Keep the tone collaborative and factual;
do not routinely qualify Qodo's capability or turn a wrong finding into a broader judgment.
Your technical recommendation does not grant edit permission: follow the approval gate below.

**Present and ask (default).** Use the assessment above for every finding, keeping its
`[category/level]` and your recommendation, then ask **in a single prompt** which findings to apply. Use whatever the
host gives you: a multi-select if it has one (Claude Code's `AskUserQuestion`, say), otherwise a
numbered list and "reply with the numbers to apply". One prompt either way — don't ask per finding.
**Nothing is pre-selected.** Mark which ones you recommend, but the user must actively choose: this
prompt is the last thing standing between a finding and an edit, so a bare Enter must apply nothing.
Apply only what the user picks (edit as normal, matching the surrounding style); report the rest as
skipped with your reason. Do not edit any code before the user has chosen.

**Autofix (skip the gate).** Only an **explicit `autofix` token** in the invocation (e.g.
`qodo-review autofix`) skips the prompt outright. Phrasing that merely sounds like opting in ("just
fix them", "don't ask me") is not enough by itself — reading intent wrong here edits code the user
never approved, which is the exact failure this gate exists to prevent. On inferred intent, name the
exact scope you'd apply and get one confirmation — "Reading that as autofix — apply the N fixes I
recommended?" — not "all N", which reads as the whole set and widens scope on the very ambiguity
this check exists to catch. Either way apply exactly what the evaluation decided and nothing beyond
it (fix the sound ones; skip the wrong/deliberate ones with a reason), and report what you applied
and what you skipped.

When the user explicitly authorizes declining a local finding, follow
[Record local triage](references/local-triage.md) to persist the decision. A conversational
"skip" alone is not a stored dismissal and must not be reported as one.

After a batch of authorized fixes, verify and re-review changed work using the lifecycle policy above.
Assess outstanding findings and coverage before calling the result clean; stop an unproductive loop.
Commit/push per the user's workflow — ask before pushing unless they've told you to.

## Recover a review

For an accepted async run, collect by operation ID even if the submit process exited. On a status
transport error, retry collection of the same ID at most once after the returned retry delay
(or 15 seconds if absent). If collection still fails, preserve the ID for later recovery and report
the coverage gap; do not submit again or keep polling automatically. The polling example returns
nonzero errors to the host for this classification and bounded recovery. On a confirmed terminal failure,
read the error and correct a recoverable cause before retrying at most once, with the selected
mode and context preserved. Honor entitlement, auth, permission and rate-limit stops; do not retry
those as transient failures. Further failure or an expired/unavailable result means reporting the
coverage gap; never claim completion or silently keep buying retries.

The following connection rules apply to connected execution, not an accepted `--async` run. A review can take
minutes; if the host cannot keep a process alive, choose async rather than repeatedly timing out.

- **Keep the run alive and connected for its whole duration.** The CLI holds a streaming
  connection to the review; the server keeps a run whose client vanished for only a short grace
  window before cancelling it. So a harness that times out and kills the CLI kills the review —
  not instantly, but a couple of minutes later, which is why the cancel can look like it came out
  of nowhere. Background the run rather than foregrounding it under a tool timeout; use the
  [connected progress](references/connected-progress.md) recipe, and backgrounding is also what lets you stream
  status.

**Concurrent reviews can complete independently.** For the same owner/repository/branch, only the
newest checkpoint run can publish finding updates; an older result may report `superseded`.

The failure shapes are distinct, so read which one you got instead of guessing:

- **No output, the process died** → your side killed it (tool timeout, SIGTERM, Ctrl-C). The
  server-side run does not stop with it; it is cancelled a short while later, so this and the
  cancel below are often the same incident seen from two ends.
- **`review canceled by the server …`** → the server ended the run. When it says the connection
  dropped, that's the cause: the CLI lost its stream and did not get back in time. Not a size,
  complexity, or concurrency limit.
- **`review ended without a result (no task.done)`** → the stream dropped mid-run.
- **`review failed: <detail>`** → a real backend failure; the detail says what.

For the first three, collect any retained result using the CLI's recovery hint before submitting again.
If the run is confirmed canceled or unrecoverable, retry at most once with uninterrupted execution
and the same selected depth/context. A pending run is not a reason to restart. Further failure means
reporting incomplete review, not looping, dropping context or downgrading depth to obtain a result.

## If the run is gated: closed preview

`qodo review` is currently in **closed preview** — the server rejects runs from organizations that
aren't enrolled. A gated run exits non-zero with a stable machine-readable error: `--json` emits
`{"error": {"code": "closed_preview", "message": ..., "hint": ...}}`; without `--json` the same
message and hint print as prose.

On `closed_preview`:

1. **Surface the `message` and `hint` to the user unchanged** (don't paraphrase or truncate;
   the `--json` payload carries them verbatim, while the CLI's prose output strips terminal
   control characters), then
2. **STOP.** The gate is an entitlement, not a transient fault — retrying, backing off, watching,
   or looping **cannot** succeed until the user's organization is enrolled. Do not re-run
   `qodo review` unless the user says enrollment happened (after enrollment, access activates
   within ~10 minutes; no re-login needed).

Only the review itself is gated — auth (`qodo read whoami`) and the other qodo commands are unaffected.

## Configuration

Use `--fast --async --json` for coding checkpoints, auto for ordinary final review,
and `--deep` only under the stated exceptions. Use `--json --progress` for connected progress and
an explicit `--base` when origin/main is not correct. Stamp exact skill/version/distribution provenance
on the first Qodo call after the unadorned version probe and keep session context out of the reviewed diff.

## Error Handling

Read the structured result even after a non-zero command. Preserve closed-preview, cancellation,
rate-limit, connection, and tool-loop states; follow the bounded recovery above and never discard
context or widen authority merely to obtain a green result.

## Guardrails

- **Local scope.** Review coding milestones and local work before PR/update or completed-work handoff.
  Use `qodo-review-resolver` for findings already posted on a PR; avoid duplicate unchanged reviews.
- **No forge writes.** `qodo review` reads your local diff and returns findings; it never pushes,
  comments, or opens a PR. Resolving a finding means editing code, not posting anywhere.
- **The base must be pushed;** your local work need not be. New/untracked files are reviewed by
  default; secrets/binaries/oversized/gitignored files are filtered and reported.
- **Don't guess creds or the base** — resolve auth first, and pass `--base` when it isn't
  `origin/main`.
- **Collect every run.** Background connected runs or use async; preserve recovery handles.
  A superseded result does not advance the incremental baseline or certify current code.
- **Never strip context to beat the clock.** Dropping `--context-file` doesn't make a run faster —
  it just buys a worse review. Give it time; choose depth by lifecycle, never by timeout pressure.
- An `MT-TOOL-LOOP` or `MT-RATE-LIMITED` error means stop/back off and change approach, not retry.
- A `closed_preview` error means the org isn't enrolled in the preview — surface message + hint to
  the user and stop; never retry or loop on it (see "If the run is gated" above).

After authorized fixes, report what changed, how it was verified, and what remains with reasons.

SHA-256: 63d14a3885da016f64f3f2b854490d9a05af5cd5d44f0ad71021dfe9c61b80ca