Product · Code Review

6 specialists per PR.
Not 1 generalist guessing.

Every pull request is reviewed by six agents in parallel — each looking for one specific kind of problem. Findings must quote the offending code verbatim, get run past an adversarial verifier that refutes the weak ones, dedup across agents, tier as Actionable / Suggestion / Nitpick, and post one-click Apply-suggestion blocks on mechanical fixes. Bring your own OpenAI or OpenRouter key — or use Managed AI (zero setup, LGTM covers the tokens).

10 reviews/mo free · No card · Counts roll over

How a review actually runs

One pipeline. Five stages. Streamed live to the dashboard so you watch the agents finish in real time.

Step 1

Webhook received

GitHub fires pull_request opened/synchronize/reopened. Signature verified via X-Hub-Signature-256, idempotency keyed by X-GitHub-Delivery.

Step 2

Diff + context

Diff fetched via App installation token. Context bundle: tree-sitter symbols, smart file-content windows (±100 lines around each touched line on files >400 lines), repo conventions (CLAUDE.md / AGENTS.md / .cursorrules / etc.), detected frameworks (from package.json / pyproject.toml / go.mod / ...), and per-language hints. All untrusted content wrapped in <untrusted_KIND> tags.

Step 3

6 LLM agents (parallel) + ci-security

One BullMQ job per LLM agent. The deterministic ci-security agent runs alongside if the PR touches CI/CD or lockfile paths. Every LLM finding must include an evidenceQuote that quotes the offending code verbatim; unverifiable quotes are dropped.

Step 4

Adversarial verifier

Each surviving finding is passed to a skeptic LLM that tries to refute it. Refuted findings drop out. Concurrency-capped at 6; JSON-parse errors safely default to KEEP so real bugs don't vanish on a bad LLM day.

Step 5

Cross-agent dedup + tiering

Findings grouped by (file, line). Highest-severity variant kept, consensus tag added ('Flagged by: bugs, security, best-practices'), cap 25 → 10 comments. Each surviving finding tiered as Actionable (critical/high), Suggestion (medium), or Nitpick (low/info).

Step 6

Synthesizer

Reads surviving findings + diff + per-repo guidelines. Emits verdict + summary + inline comments — including ```suggestion``` blocks on mechanical single-line fixes when the LLM opted in via codeSuggestion.

Step 7

Posted to GitHub

Single POST to /repos/{...}/pulls/{pr}/reviews. If GitHub rejects 422 (force-push race), we retry without inline comments, posting just the summary.

Meet the six agents

Each agent has one job. Specialists outperform generalists because they don't spread attention thin trying to check everything at once.

Bugs

Catches what causes incidents

What it catches

  • ·Null derefs and missing await
  • ·Off-by-one and boundary bugs
  • ·Race conditions in async code
  • ·Returning wrong types under edge cases
  • ·Unhandled error paths

Sample finding

`getUser(id)` can return undefined when the cache misses — but line 42 calls `.email` on it without a guard.

Security

40-class taxonomy on the diff hunks

What it catches

  • ·SQL / NoSQL injection · XSS · SSRF
  • ·JWT alg confusion · prototype pollution
  • ·Broken auth + missing authz
  • ·Secrets in the diff · weak crypto
  • ·40-class enum with CWE cross-refs

Sample finding

User-controlled `repoId` flows into `Repo.findOne({ _id: repoId })` without ObjectId validation — Mongoose will accept arbitrary objects. category: nosql-injection (CWE-943).

Performance

Hot-path regressions, not micro-opts

What it catches

  • ·N+1 queries in loops
  • ·Blocking I/O on the request path
  • ·Regex catastrophic backtracking
  • ·Unnecessary re-renders / hook deps
  • ·Missing pagination on unbounded lists

Sample finding

The loop on line 88 hits `User.findById(c.author)` per comment — 1 + N queries. Use a single `find({ _id: { $in: authors } })`.

Readability

Future you will thank present you

What it catches

  • ·Variables named `data` / `result` / `temp`
  • ·Functions over 60 lines or 4 nesting levels
  • ·Magic numbers + strings
  • ·Dead code, unreachable branches
  • ·Clever > clear

Sample finding

`fn(d, t, x)` — what are `d`, `t`, `x`? The body uses them as `diff`, `target`, and `xform` — say that.

Best practices

Language and framework idioms

What it catches

  • ·TS: `any` where a real type is obvious
  • ·React: missing useEffect deps
  • ·Express: middleware order wrong
  • ·Go: error wrapping skipped
  • ·Python: `mut_default = []` in args

Sample finding

`useEffect(() => { fetchPR(); }, [])` — missing `id` in the dep array. PR ID changes won't trigger re-fetch.

Documentation

Stale comments are worse than none

What it catches

  • ·Missing JSDoc on exported functions
  • ·Comments that contradict the code
  • ·Stale TODOs and FIXMEs
  • ·Public API without examples
  • ·README claims vs code reality

Sample finding

The function comment says 'returns null on missing user' but the body now throws. Update the comment or restore the null return.

The merger

One verdict, not six conflicting opinions

After the six agents finish, a synthesizer agent reads all six reports plus the diff plus any per-repo review guidelines you've set. It produces:

  • Overall verdict

    approve · request_changes · comment

  • Executive summary

    2-4 sentences explaining the verdict to a busy reviewer.

  • Confidence score

    0-100 — how certain the model is. Below 50 = manual review recommended.

  • Dedup'd inline comments

    Same bug flagged by Bugs + Security = one comment, not two.

Live verdict in dashboard

request_changes

Confidence 78%

"Adds the new export but doesn't handle the cache-miss case the existing tests exercise — see line 42 in user.service.ts. Suggest pulling the cache check up one frame."

Agents: 6 / 6 · Findings: 3 critical · 5 medium · 2 low · Run time: 47s

Tune it per repo

Different repos want different reviews. A monorepo for a payment API needs paranoia. A side-project Astro blog needs speed.

Focus areas

Pick a subset of the 6 agents. Repo A runs all six; Repo B only runs Bugs + Security. Saves tokens for teams who only care about a slice.

Custom review guidelines

Free-text appended to every review prompt for this repo. Drop your style guide, your no-go list, your 'we prefer X over Y' preferences.

Per-repo provider override (Pro)

Pin OpenAI on the Rust monorepo. Pin Claude Sonnet on the React app. Different repos can run on different models without changing your default. Readability + documentation agents auto-route to the same-provider mini/flash model — about 3.3× cheaper on those two.

Auto-review on every PR (Pro)

opened / synchronize / reopened — Pro fires automatically. Free users keep the manual 'Review' button on the dashboard or `lgtm review --pr <num>`.

Real-time

Watch the agents finish, live

Socket.IO streams every step to the dashboard: review:started, agent:started / completed / failed per agent, synthesizer:started, review:completed. No polling, no spinners that never finish.

If the socket drops the dashboard shows a discreet "reconnecting…" pill — never a frozen UI.

Bugs
Security
Performance
Readability
Best-practices
Documentation

FAQ

Does LGTM store our source code?

No. We persist the parsed diff hunks the reviewers ran against, not the full repo. The tree-sitter context index stores symbol names and signatures, not source bodies. Full source is fetched fresh from GitHub at review time, held in worker memory for 30-90s, then discarded.

Can I block merges based on the verdict?

Yes — the review posts a check_run with status: failure when the verdict is request_changes. GitHub branch protection then refuses the merge. For CI/CD-specific gates (36 deterministic detectors across GitHub Actions, GitLab CI, CircleCI, and Azure Pipelines) see LGTM Security.

What if the diff is huge?

Reviews scale linearly until the model's context window. For PRs over ~500 lines we chunk the diff per agent and have the synthesizer merge across chunks. Reviews on 2,000-line refactors still come back, just at the 90-second end of the range.

How does dedup work between agents?

The synthesizer keys findings by (file, startLine, ruleId) and merges descriptions when two agents flag the same spot. A SQL-injection callout from Bugs and Security becomes one comment with both lenses noted.

Which AI providers are supported?

Three modes on /dashboard/ai-providers. BYOK OpenAI: gpt-5.4 / pro / mini / nano, gpt-5.3-codex, gpt-5.2, gpt-4.1-mini via Chat + Responses APIs with strict json_schema. BYOK OpenRouter: one key unlocks Claude Opus 5 / Sonnet 5 / Haiku 4.5, Gemini 3.1 Pro / Flash, Grok 4.5, DeepSeek V4 Pro, Llama 4, Mistral Large, Qwen 3.8 Max, and 400+ more — Claude models keep prompt caching via cache_control forwarding. Managed AI: LGTM covers the LLM cost on DeepSeek V4 Flash (CoreWeave, :nitro routing) — zero setup, works on Free tier. On BYOK, we never see your provider tokens unencrypted. Multiple keys per provider with labels; per-repo mode + key overrides available on every plan.

Does LGTM hallucinate findings?

The prompt was rewritten to remove old "MUST find at least one issue" pressure. Every LLM finding must include an evidenceQuote that quotes the offending code verbatim from the diff or file window; a validator drops any unverifiable quote. On top of that, an adversarial verifier LLM tries to refute each surviving finding — refuted ones are dropped before you see them. Cross-agent dedup caps inline comments at 10 per review.

Is LGTM safe against prompt injection in reviewed code?

Diff, file contents, related files, conventions, and history are wrapped in <untrusted_KIND>…</untrusted_KIND> tags. The system prompt tells the LLM that content inside these tags is data, never instructions. Red-teamed against unicode ZWJ payloads, base64- encoded prompts, non-English (Hindi) injections, and fake-schema payloads — all ignored while real SQL-injection findings continue to fire.

Do reviewers get one-click fixes?

Yes — mechanical single-line fixes (== → ===, parseInt radix, md5 → sha256, etc.) ship as native GitHub ```suggestion``` blocks. Reviewers Apply-suggestion in one click without leaving the PR.

Does LGTM understand my project's conventions?

Yes — the convention loader fetches CLAUDE.md, AGENTS.md, .cursorrules, .github/copilot-instructions.md, .windsurfrules, CONTRIBUTING.md, and .claude/CLAUDE.md if present, and injects them into every agent prompt. Findings that contradict a declared convention are dropped. Framework detection from package.json / pyproject.toml / go.mod / Gemfile / pom.xml / build.gradle / Cargo.toml / composer.json additionally injects per-framework gotchas (Next.js, tRPC, Prisma, Django, FastAPI, Rails, Spring Boot, Actix Web, …) into the best-practices prompt.

Can I disable specific agents per repo?

Yes — the focus-areas setting per repo. Set focusAreas: ["security", "bugs"] and only those two agents run. Lower token spend, faster reviews.

Stop reviewing alone

10 reviews/month on Free. No card. Same 6 agents as Pro, same dashboard, same CLI.