Skip to content

Claude review: LGTM-shaped by construction — give it the repo's traps, a blind read, a structured verdict and the callers rule #3710

Closed

Description

@erikdarlingdata

The bot review is LGTM-shaped, and the shape is structural

Across the last several hundred PRs the Claude review posted LGTM on very nearly all of them and caught a real defect a handful of times. Every defect it missed that later surfaced was found by behavior — a post-install read, a production page, an EXPLAIN on a rig — never by the review: the sweep-id precision loss (#3487), the twelve file-growth cards from a per-collection fire on an hourly collector (#3636, the same shape as #3628 which the review had passed), MAX(max_dop) grouped across plans (#3648), an ORDER BY 3 + 4 DESC folding to a constant (#3613), the Slack fifty-block breach (#3612), integer division in the Full-edition rate columns.

None of those is visible in a diff read by a generalist, and the review has three handicaps that make LGTM its honest output rather than a lazy one:

  1. No memory of this repository's traps. Every review starts from nothing. Our bugs recur in shapes — truncated = rows.Count >= limit, ordinal arithmetic in ORDER BY, cumulative-versus-delta on an aggregate across plans, per-observation versus per-collection alert firing, restart-spanning windows, BaseSeverity <= 0 skipping amplifiers, a non-virtual method called through a base reference, a census test that fires on a new string constant, pg_total_relation_size including TOAST — and the reviewer never learns them.
  2. It reads the diff alongside the author's essay. Our PR bodies are persuasive by design. A reviewer that checks the diff against the story is checking consistency, not correctness; the two agree whenever the author was wrong in a consistent way.
  3. It cannot execute anything. Claude review runs finish green having posted nothing: the allowlist forbids every read tool the prompt invites, denial churn ends sessions without a verdict, and the job never checks that it posted #3650 gave it read-only tools; the runtime and SQL-semantics classes remain invisible.

Slice one (this issue)

  • .github/REVIEW-TRAPS.md — one line per trap with the PR that bit, loaded into the review prompt; the review must name which traps the diff is near and say why each does or does not apply.
  • Blind read first. Two phases in one job: the diff with the PR body withheld — what does this change do, what could it break — then the body revealed, and a report of where the author's account and the blind read diverge. Divergence is the finding.
  • Structured verdict. For each claim the PR body makes: VERIFIED (how) / UNVERIFIED (why) / REFUTED; the three riskiest changed lines and the test that pins each; a reviewer checklist. A bare LGTM is no longer a valid verdict — the guard's "verdict posted" check should require the structured block.
  • Callers rule. For every changed public signature or behavior, enumerate its callers with the read tools and state whether each still holds.
  • A ledger. The verdict artifact gains a machine-readable line (verdict, claims counted by state, traps named) so a later pass can compute the miss rate against post-merge defects instead of an impression.

Slice two (follow-up, after slice one has a few weeks of verdicts)

Reuse the PostgreSQL tests job's TimescaleDB container so the review can execute new SQL; tier the review so the full pass runs only on Analysis/, Storage/, Collectors/, install/ changes and a light pass elsewhere. Roughly triples the review's cost and runtime; worth it only if slice one's ledger shows the SQL class is what remains.

Timing

The review is dark on dev until the next release sync (the action refuses on workflow drift against main), so changing claude-review.yml now costs nothing further and lands with the restore.

Activity

  1. erikdarlingdata commented on Sep 19, 2026

    @erikdarlingdata
    OwnerAuthor

    Model (slice one, ruled 2026-09-19): the workflow sets no model today, so the review runs Claude Code's default. claude_args gains --model "claude-opus-5[1m]" (the [1m] selector opts into the 1M context window, which the callers rule and the traps file need on a large Storage/ diff) and a --max-turns ceiling. Tiering rides in the same slice: a paths-filter step routes changes under Analysis/, Storage/, Collectors/, install/, Darling/*/Compose, Darling/*/Mcp to the full Opus pass and everything else (tests, docs, viewer XAML, wwwroot) to a Sonnet pass with the same structured verdict — the verdict contract is identical either way, so the guard does not care which tier ran. Expected cost: ~5× per full-pass review, offset by the light tier on roughly half the PRs. Verified against anthropics/claude-code-action docs/configuration.md ("Custom Model", "Claude Code Settings": claude_args takes precedence over settings.model; no effort flag exists — --append-system-prompt is the lever for reasoning depth).

  2. erikdarlingdata commented on Sep 19, 2026

    @erikdarlingdata
    OwnerAuthor

    Closed by #3718 (merged to dev).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions