Lekker Review
FAANG-grade code review. Isolated worktree checkout, full context gathering (issue tracker + chat + docs + framework docs + monitoring + CI, whichever you have MCP tools configured for), then 5 parallel specialized review agents, a finding-verification pass, and one unified markdown output.
An optional --engine revmux flag can replace the Step 2 Workflow-tool pipeline
with revmux (profiles and lenses under references/revmux/); default stays
workflow, and both engines feed the same Step 3 synthesis.
Two modes, same review engine:
| Mode | Target | Entry |
|---|---|---|
| pr | An open GitHub PR | a PR url / repo N / repo "title" |
| branch | A local branch, before it is pushed | branch [ref], or --branch |
Branch mode exists to move the review left: everything a bot reviewer
(Greptile, CodeRabbit, or similar) would charge for on the PR is found and
FIXED locally first, so the PR opens clean and there is little left to bill
for. It reviews the same diff a PR would show - merge-base against the branch
the PR would target - and it defaults to --fix.
Each finding contains: file + risk · bad code verbatim · why it's
wrong · fix with code example - ready to paste directly into GitHub.
Reviews are saved to ~/code-reviews/ for future reference.
Optionally the skill then applies its own findings (--fix): fix agents edit
the worktree, a read-only verifier checks each edit against the real git diff,
static checks run, one commit lands per file, and nothing is pushed until the
user says so. Procedure in references/fix-mode.md.
No nitpicking. Critical and Important findings are reserved for things that could cause bugs, outages, data loss, security incidents, or real performance problems at scale.
HARD RULE: the ## 💰 Review Cost block is mandatory. Every review MUST
end with a fully-populated cost block (token + price breakdown, real numbers,
no <N> placeholders). A review without the cost block is incomplete. If you
are about to present the review without it, stop and compute it first.
Set up before first use
This skill ships with no hard rules of its own: references/house-rules.md
is a template. Fill it in with your team's own non-negotiable conventions
(type safety, pagination, PR-title format, repo-placement taxonomy, stack
context) before relying on the Critical-severity hard-rule gate. Until then,
the 5 specialist agents still run and still find real bugs. They just don't
have a codified "always Critical" rule list to check against.
If any hard rule you define carries a rule tag (e.g. "TS-1"), reviewer
agents attach that tag to matching findings. The verifier checks the diff
anchor and rule applicability, but skips its five runtime-failure challenges.
Those challenges cannot evaluate a standards violation. A tagged finding keeps
Critical severity only when both rule-specific checks pass. The workflow
returns the number checked as hardRuleCount.
${CLAUDE_PLUGIN_ROOT} below refers to this skill's own installed directory:
resolve every references/... and script path relative to it.
Step 0 - Parse input
Pick the mode first
MODE=branch when ANY of these hold:
- the argument is the literal word
branch(optionally followed by a ref), or the flag--branch [ref]is present - the user asked for a review "before push", "before the PR", "of my branch", "of these commits", or named a local branch that has no open PR
- no PR reference was given at all and
$PWDis inside a git repo on a branch that is not the base branch
Otherwise MODE=pr.
Never guess between the two. If a PR reference is present, it is pr mode
even in a git repo. If neither a PR reference nor a git repo is available,
stop and ask - do not review the wrong thing.
MODE=pr
Accept any of:
- Full GitHub URL:
https://github.com/owner/repo/pull/123 - Repo + number:
my-service 42 - Repo + partial title:
my-service "add offline orders"
If the user gives a short repo name without an org/owner, ask once which
org/owner it belongs to (or use a default you've configured), then build
REPO_SLUG as <owner>/<name>.
Derive and carry these variables through every subsequent step:
REPO_SLUG(e.g.my-org/my-service)PR_NUMBERPR_BRANCH(fromgh pr view)PR_URL=https://github.com/<REPO_SLUG>/pull/<PR_NUMBER>TARGET_LABEL=PR #<PR_NUMBER>
MODE=branch
Resolve everything from the local repo - no gh pr call, no network:
LOCAL_REPO=$(git -C "$PWD" rev-parse --show-toplevel)
# Use the ref the user named when present. Otherwise require an attached branch.
LOCAL_BRANCH="${USER_PROVIDED_REF:-}"
if [ -z "$LOCAL_BRANCH" ]; then
LOCAL_BRANCH=$(git -C "$LOCAL_REPO" symbolic-ref --short -q HEAD)
fi
if [ -z "$LOCAL_BRANCH" ]; then
echo "Detached HEAD: name the local ref to review." >&2
exit 1
fi
ORIGIN_URL=$(git -C "$LOCAL_REPO" remote get-url origin 2>/dev/null || true)
if [ -n "$ORIGIN_URL" ]; then
REPO_SLUG=$(printf '%s\n' "$ORIGIN_URL" \
| sed -E 's#(git@github.com:|https://github.com/)##; s#\.git$##')
else
LOCAL_REPO_NAME=$(basename "$LOCAL_REPO" \
| sed -E 's#[^[:alnum:]_.-]+#-#g; s#^-+|-+$##g')
REPO_SLUG="local/${LOCAL_REPO_NAME:-repository}"
fi
An explicitly named ref may be reviewed from detached HEAD. Before landing
fixes, resolve it to a local branch name or stop; never build
refs/heads/<LOCAL_BRANCH> from a commit SHA or another non-branch ref. The
local/<directory> slug is reporting metadata for repositories without an
origin; preserve the origin-derived slug whenever that remote exists.
Then:
BASE_REF: the branch the PR would target. Honour--base <ref>when given. Otherwise letscripts/changed-files.shdetect it (origin's default branch, then whatever your convention falls back to) and report which one it picked. If detection lands on something that contradicts your team's actual convention, say so in the review header rather than reviewing silently against the wrong base.PR_NUMBER= null,PR_URL= null,TARGET_LABEL=branch <LOCAL_BRANCH>.- The reviewed diff is
merge-base(HEAD, BASE_REF)..HEAD- committed work only. Uncommitted and untracked changes are NOT reviewed. Ifgit status --porcelainis non-empty, say so in one line before starting (⚠️ N uncommitted file(s) are not part of this review) so nobody assumes coverage that does not exist. - If that diff is empty, stop:
nothing to review - <branch> matches <base>.
Guard: never run branch mode on a long-lived integration branch. If
LOCAL_BRANCH is main, master, develop, staging, or production, stop
and say so.
Depth: explicit keyword scan, medium, or deep wins. If absent, get
the diff stat - in pr mode from
gh pr view <PR_NUMBER> --repo <REPO_SLUG> --json additions,deletions,changedFiles,
in branch mode from git diff --shortstat <MERGE_BASE> HEAD plus
git diff --name-only <MERGE_BASE> HEAD | wc -l - and apply AUTO-DEPTH:
scanif additions+deletions < 150 AND changedFiles <= 5deepif additions+deletions > 800 OR changedFiles > 25 OR diff touchesmigrations/or*.sqlmediumotherwise
State the chosen depth (and whether it was auto-selected) in the review header.
--post flag: parse and store as POST_REVIEW=true.
--fix flag: parse and store as FIX_MODE=true. Fix mode needs a real
checkout, so --fix forces Track A (worktree setup) to run even when
depth=scan. If the user did NOT pass --fix, leave FIX_MODE=false for now -
Step 4 offers it after the review is printed.
Branch mode defaults to fix. In MODE=branch, FIX_MODE=true and
FIX_SCOPE=critical+important unless the user passed --no-fix. That is the
whole point of reviewing pre-push: findings get applied before the PR exists,
so a bot reviewer never sees them. Landing those fixes on the local branch
still needs explicit confirmation (Step 4).
--base <ref> flag (branch mode only): overrides base-ref detection. Passed
through to the scripts as LEKKER_BASE_REF. Ignored in pr mode, where the
base comes from the PR.
--no-fix flag: parse and store as FIX_MODE=false, and do not offer fixes
in Step 4. Use it for a read-only pre-push look.
--no-artifact flag: parse and store as ARTIFACT=false (default true).
Skips Step 3.5 (living review artifact) silently.
--engine revmux|workflow flag: parse and store as ENGINE, default
workflow. revmux is allowed only at depth medium or deep - revmux's
scripts reject scan (see Depth gate). If depth resolves to scan (explicit
or auto), force ENGINE=workflow regardless of the flag and note engine forced to workflow - revmux needs medium/deep in the review header.
Re-review detection: run
ls ~/code-reviews/*-<TARGET_SLUG>-<repo-short-name>.md 2>/dev/null | sort | tail -1
(TARGET_SLUG = pr-<PR_NUMBER> in pr mode, branch-<LOCAL_BRANCH sanitized to [a-z0-9-]> in branch mode)
to find the newest prior review for this PR (repo-short-name = last segment of
REPO_SLUG; keeps PR numbers from colliding across repos). If found, grep it for
\*\*Head:\*\* and extract the short sha. Set PREV_SHA=<sha> and
PREV_REVIEW_FILE=<path>. If no Head line exists in the file (older format),
treat as a full review and leave PREV_SHA unset. Also grep the same file for
\*\*Artifact:\*\* and set PREV_ARTIFACT_URL=<url> (null when absent) - Step
3.5 republishes to the SAME url so the artifact stays a living page for this PR.
Depth gate
| Step | scan | medium | deep |
|---|---|---|---|
| Context: issue-tracker/CI/diff/existing-reviews | always | always | always |
| Context: chat/docs/framework-docs/monitoring/prior-review-memory (optional, MCP-dependent) | skip | included | included + broader recall |
| Worktree + static checks | skip (WORKTREE_PATH=null) unless --fix |
included | included |
| Review agents | 2 triage (haiku) | 5 specialists (sonnet) | 5 specialists (sonnet) |
| Per-finding verification | Criticals + Importants | Criticals + Importants | Criticals + Importants |
| Completeness critic | skip | skip | included |
| Proof-of-bug (failing test per Critical) | skip | included (max 5) | included (max 5) |
| Living review artifact | included | included | included |
| Housekeeping (optional memory/notes writeback) | skip | included | included |
--post |
supported | supported | supported |
--fix / fix offer |
supported (forces worktree) | supported | supported |
For scan: note ⚡ scan - worktree unavailable, static checks skipped in the
review header. When --fix forced the worktree at scan depth, drop that note
and say ⚡ scan - worktree created for --fix instead.
Branch mode always has a worktree (fix mode is the default, and there is no remote diff to fall back on), so the scan row's "skip worktree" never applies. Everything else in the table is unchanged: branch mode is not a shallower review, it is the same review earlier. Two rows differ because their inputs do not exist yet:
| Step | branch mode |
|---|---|
| CI checks | N/A - not pushed; local compiler/linter/tests are the only signal |
| Existing bot reviews | none by definition - that is the saving |
--post |
unsupported until a PR exists (Step 4.6 can create one) |
Step 1 - Context + worktree (concurrent)
Fire both tracks in the same turn.
Track A - worktree setup (skip when depth=scan, unless --fix)
Run via Bash with run_in_background:
# MODE=pr - checks out origin/<PR_BRANCH>, fetching if needed
${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktree.sh \
<REPO_SLUG> <PR_BRANCH> <scratchpad>/worktree.json [PREV_SHA]
# MODE=branch - LOCAL ref only. LEKKER_LOCAL_REPO skips repo discovery, the
# fetch, and the origin/<branch> checkout entirely, and creates the worktree
# DETACHED so a branch already checked out in the user's own working copy can
# still be reviewed. LEKKER_BASE_REF is set only when --base was given.
LEKKER_LOCAL_REPO=<LOCAL_REPO> [LEKKER_BASE_REF=<BASE_REF>] \
${CLAUDE_PLUGIN_ROOT}/scripts/setup-worktree.sh \
<REPO_SLUG> <LOCAL_BRANCH> <scratchpad>/worktree.json [PREV_SHA]
On completion, read worktree.json. Keys emitted:
worktreePath, repoRoot, headSha, headShaShort, tscTail,
tscChangedTail, tscErrorCount, eslintTail, eslintScope, changedFiles,
baseRef, mergeBase, projectRules, deltaFile, notes.
mergeBase is the commit the file list was scoped against. Branch mode
generates its diff from that exact sha (below), so the diff the agents read and
the files the static checks were scoped to can never disagree. notes contains
local-branch-mode; when the local path ran.
Static checks are scoped so you can tell this PR's errors from the repo's standing debt - do not try to infer that from the raw tail:
eslintTailis the result of linting onlychangedFiles(eslintScopesayschanged-files). Everything in it belongs to this PR. WheneslintScopeisfull-fallback, base-ref detection failed and the lint is repo-wide again - in that case treat its contents as unattributed and say so rather than blaming the author.tscTailis the raw repo-wide tail (tsc needs the whole program, so it cannot be scoped).tscChangedTailholds only the errors in files this PR touched - that is the attributable set.tscErrorCountis the repo-wide total; a large count with an emptytscChangedTailmeans pre-existing debt, not a finding.- If your stack doesn't use tsc/eslint, adapt
setup-worktree.sh's static check step to your language's compiler/linter equivalents.
A failing CI build or test = Critical finding input.
When depth=scan: set WORKTREE_PATH=null without launching the script - unless
FIX_MODE=true, in which case run the script anyway (fix mode cannot edit code
from a diff). In MODE=branch the script always runs, at every depth.
A failing local compiler/linter/test run in branch mode is the direct replacement for the failing-CI signal - treat it exactly the same way. Catching it here is a CI run the PR never has to burn.
Track B - metadata and signals (all calls fired in parallel)
Run ALL of the following in the same message. Full query details are in
references/context-gathering.md - follow it, do not paste it wholesale into
agent contexts.
In MODE=branch, every gh pr * call below is skipped - there is no PR.
Wait for Track A's worktree.json (you need mergeBase), then substitute:
# The diff, from the SAME merge-base the file list was scoped to.
git -C <worktreePath> diff <mergeBase> HEAD > <scratchpad>/pr.diff
# Commit log, for ticket ids and for the PR title the branch would need.
git -C <worktreePath> log --format='%s%n%b' <mergeBase>..HEAD
- The title/ticket-prefix check in branch mode is a PRE-check, not a
violation: scan the commit subjects for your team's ticket pattern, collect
the distinct ids, and set
RECOMMENDED_PREFIX. There is no PR title to be wrong yet, soPR_TITLE_ISSUE=false- report the prefix as the title to use, in the Step 4.6 PR offer. - Ticket ids come from the commit log and the branch name instead of the PR title/body; the issue-tracker lookup below is otherwise identical.
ciStatus=N/A - not pushed.existingReviews= null.- Everything else in Track B (issue tracker, chat, docs, framework docs, monitoring, prior-review memory) runs unchanged, keyed off the ticket ids and the branch name.
MODE=pr calls:
gh pr view <PR_NUMBER> --repo <REPO_SLUG>with fields:number,title,body,author,headRefName,baseRefName,labels,linkedBranches,mergeStateStatus,additions,deletions,changedFiles,isDraft,headRefOid. ExtractheadRefOid(full sha) andheadShaShort(first 7).- Title/ticket-prefix check (if
house-rules.mddefines one): scan commit log for the ticket pattern; setPR_TITLE_ISSUEandRECOMMENDED_PREFIX. gh pr diff <PR_NUMBER> --repo <REPO_SLUG> > <scratchpad>/pr.diff- fetched ONCE; all agents read this file viaDIFF_FILE.gh pr checks <PR_NUMBER> --repo <REPO_SLUG>gh pr reviews <PR_NUMBER> --repo <REPO_SLUG>and review comments- Issue-tracker lookup (Linear/Jira/GitHub Issues MCP, if configured) per
ticket ID found in title/body/branch; collect ACs as numbered list (
acList). - (medium/deep only, optional) Chat search (Slack/Discord MCP, if configured): PR-title keywords and ticket ID.
- (medium/deep only, optional) Docs search (Notion/Confluence/wiki MCP, if configured): feature name or ticket title.
- (medium/deep only, optional, only when relevant) Framework/API docs MCP for the specific framework or third-party API the diff touches.
- (medium/deep only, optional) Monitoring search (Sentry/Rollbar/etc. MCP, if configured) for filenames or service names from the diff.
- (medium/deep only, optional) Prior-review-memory recall, if you maintain such a system: patterns and false positives specific to this repo.
Assemble CONTEXT_FILE
Write <scratchpad>/context.json with keys:
{
"acList": "<numbered ACs from your issue tracker, or empty>",
"projectRules": "<worktree.json projectRules + any recalled review patterns appended under '## Recalled patterns'>",
"sentrySignals": "<monitoring issue summaries, or null>",
"ciStatus": "<passing | failing: <names> | pending | N/A>",
"existingReviews": "<prior review summaries>",
"deltaFile": "<worktree.json deltaFile, or null>",
"houseRulesFile": "${CLAUDE_PLUGIN_ROOT}/references/house-rules.md",
"reviewTarget": "<TARGET_LABEL, e.g. 'PR #412' or 'branch feat/offline-orders'>",
"prePush": "<true in MODE=branch, false in MODE=pr>"
}
prePush: true tells the agents there is no CI verdict and no bot review to
defer to, and that anything they flag is cheaper to fix now than after the PR
opens. It does NOT lower the bar: same severities, same no-nitpicking rule.
Agents read keys from this file. Nothing from CONTEXT_FILE is pasted into their prompts wholesale - the workflow script delivers it by path.
Step 2 - Review engine (review + verify + critic)
ENGINE=workflow (default)
Invoke the Workflow tool:
scriptPath: ${CLAUDE_PLUGIN_ROOT}/workflow.js
args: {
repoSlug,
prNumber, // null in MODE=branch
prUrl, // null in MODE=branch
targetLabel, // TARGET_LABEL - REQUIRED when prNumber is null
depth,
diffFile: "<scratchpad>/pr.diff",
contextFile: "<scratchpad>/context.json",
worktreePath: <null for scan, else from worktree.json>,
promptDir: "${CLAUDE_PLUGIN_ROOT}/references/agents",
prevSha: <null unless re-review>
}
targetLabel is what every agent is told it is reviewing. The workflow falls
back to PR #<prNumber> when it is absent, so pr mode may omit it; branch mode
must pass it, or the workflow throws on its missing-args guard.
Two optional args tune concurrency; omit both unless a run needs it:
reviewBatchPlan- batch sizes for the review dimensions, default[5](all five at once). The last entry repeats to cover any remainder, so[2]would mean two at a time.maxConcurrent- cap for the per-finding fan-outs (verify, critic, prove), default5.
Lowering them is always safe: it costs wall-clock, never findings. Reach for that only when a specific run needs a smaller footprint.
The workflow runs three phases:
- Review: scan uses
[triage-quality, triage-logic]onhaiku; medium/deep use 5 specialists (quality, implementation, simplification, conventions, test-quality) onsonnet. Agents receive DIFF_FILE + CONTEXT_FILE by path. All five run concurrently: the stage is self-limiting at one agent per dimension. All reviewers run to completion before verification starts. Override per run with thereviewBatchPlanarg (e.g.[1]runs them strictly one at a time on a constrained machine). - Dedup: findings are merged across dimensions on
file:line+ title token-similarity, so one issue found by three agents is verified once, not three times. A merge keeps the highest severity and the longest description/badCode/fix of the set - a Critical is never demoted by an Observation someone else filed at the same line - and records every contributing dimension inagreedBy. - Verify: every Critical and Important is checked at every depth because it
can affect the verdict. Hard-rule findings (
ruleset) use the verifier's diff-anchor and rule-applicability checks instead of runtime challenges. Each verifier runs the five-challenge adversarial refutation fromreferences/agents/verifier.mdagainst one finding, returns{verdict, newSeverity?, reasoning}. Verifiers runmaxConcurrentat a time (default 5): this stage spawns one agent PER FINDING, so without a ceiling a thirty-finding PR launches thirty concurrent agents. - Critic (deep only): completeness critic gets the full deduped finding list + DIFF_FILE; its findings go through verifier agents before promotion.
- Prove (medium/deep, worktree required): each non-hard-rule Critical gets
one prover agent (
references/agents/prover.md, sonnet, max 5, runmaxConcurrentat a time) that writes a test asserting the CORRECT behaviour, runs it in the worktree, and captures it failing because of the bug. The proof rides on the finding asproof: {attempted, proven, outcome, reason, testCode?, testCommand?, redOutput?}. A proof that comes back GREEN (code behaved correctly) is counter-evidence - the workflow automatically downgrades the finding from Critical to Important. Hard-rule findings are never proved (policy violations have no failing test).
Findings have schema:
{file, line, severity, title, description, badCode, fix, rule?, precedent?, agreedBy?, verificationStatus?, verifierReasoning?, proof?}
badCode and fix are schema-required: an empty string is allowed only on
observation / idiomatic findings.
Model tiers: triage on haiku, specialists on sonnet, verifiers + critic +
provers on sonnet, housekeeping on haiku. Only the synthesis in Step 3 runs
on the session model.
Return value from the workflow:
{engine, findings, droppedCount, downgradedCount, hardRuleCount, proveAttemptCount, provenCount, acCoverage, coverageVerdict, mutationSlip, mockSmells, agentCount, outputTokens, turnTokensTotal}
outputTokens is this workflow's own output spend; turnTokensTotal is the
whole turn's shared pool (main loop included).
Wait for the workflow to complete before proceeding to Step 3.
ENGINE=revmux
revmux replaces Review, Dedup, Verify, and Critic with its own multi-agent
round; Prove still runs inside workflow.js. Steps:
TASK_SLUG=<repo-short-name>-<TARGET_SLUG>,RUN=01-review.Run the engine:
${CLAUDE_PLUGIN_ROOT}/scripts/revmux-engine.sh \ --task <TASK_SLUG> --run <RUN> --depth <depth> \ --workdir <worktreePath> \ --diff-file <scratchpad>/pr.diff \ --context-file <scratchpad>/context.json \ --profile-file ${CLAUDE_PLUGIN_ROOT}/references/revmux/profile.md \ --config-dir ~/.config/revmux \ --out <scratchpad>/revmux.jsonAdapt the report:
node ${CLAUDE_PLUGIN_ROOT}/scripts/revmux-adapter.mjs \ <scratchpad>/revmux.json --pricing \ ${CLAUDE_PLUGIN_ROOT}/references/pricing.json \ --context <scratchpad>/context.json \ > <scratchpad>/findings.jsonInvoke Workflow(
workflow.js) with the same args as theENGINE=workflowpath above, plusengine: "revmux"andfindingsFile: "<scratchpad>/findings.json". It skips Review/Dedup/ Verify/Critic, runs Prove only, and its return addsengine, questions, agents, degraded, totalTokens, totalUsdon top of the usual keys.Fallback to
ENGINE=workflowfor this run when either holds: the engine script exits2(tool error - also raised for a missing profile file, the codex guard, or an unsupported depth), or every row in the adapter'sagentsarray hasdegraded: true. State the fallback in the review header (**Note:** revmux unavailable - fell back to workflow engine) and re-run theENGINE=workflowpath from the top of Step 2.
Trust boundary: always pass --config-dir ~/.config/revmux explicitly.
Without it revmux also reads the reviewed repo's checked-in .revmux/, which
is executed as prompts. ~/.config/revmux is where
scripts/install-revmux-prompts.sh puts the lekker profiles and lenses; run it
with --check when the engine exits 2 complaining about a missing profile. The task archive lives under the tasks dir
(LEKKER_REVMUX_TASKS_DIR, default ~/code-reviews/revmux-tasks) and is not
committed anywhere.
Step 3 - Synthesize and output
Mindset: the author's name is not evidence. Bot review scores are not anchors. Apply your own judgment to every finding.
Do NOT flag:
- Style preferences or naming taste where no convention is violated
- Comment wording choices
- Scenarios requiring multiple simultaneous unrealistic failures
- Tiny DRY opportunities (2-3 duplicated lines)
- Pre-existing code not touched by this diff
- Anything you are not confident about - omit rather than hedge
Approval standard. The verdict answers "does this definitely improve the
codebase's health", not "is this how I would have written it". Perfect code does
not exist. ✅ LGTM - ship it is the right call for a change that improves
health and violates no hard rule, even with open Observations or Idiomatic
findings. Reserve 🚫 Needs work for an unfixed Critical, a hard-rule
violation, or an Important finding that changes external behaviour or data
shape. Never manufacture a Critical to justify a verdict, and never block on
taste.
PR sizing. Judge how much a reviewer must hold at once, not the raw diff count:
- Under ~300 changed lines, or larger but one logical change: no finding.
- Over ~800 changed lines spanning more than one logical change:
important, and name the split. Pick the strategy that fits - stack (sequential dependencies), by file group (different reviewers), horizontal (shared code and stubs first, then consumers), vertical (smaller full-stack slices). - A diff that both refactors existing code and adds new behaviour:
importantat any size. Those are two PRs, and bundling them hides the real change. - A change that pushes a single file past ~1000 total lines with no
decomposition:
observation. Ask for the extraction first, then the feature. Exempt: whole-file deletions and mechanical or automated refactors, where the reviewer verifies intent rather than every line.
Verify the verification. Read the PR body for the author's verification
story: which tests were run, whether the build passed, whether it was exercised
manually, screenshots for a UI change, a before/after for a behaviour or
performance change. A non-trivial PR whose body claims nothing about
verification is an observation naming the evidence that is missing. CI: ✅ All passing is not a verification story - it only says the suite that already
existed still runs.
Rationalizations to reject. If one of these is the reason a finding is about to be dropped or softened, keep the finding:
| Rationalization | Reality |
|---|---|
| "The tests pass, so it's fine" | Tests do not catch architecture, security, or data-shape problems. |
| "It works, that's good enough" | Working code that is unreadable or insecure is debt that compounds. |
| "The refactor makes it cleaner" | Relocating complexity is not reducing it. Count the concepts a reader holds. |
| "It's only a small addition to this file" | Judge the resulting structure, not the diff size. |
| "It's just a version bump" | A bump is a behaviour change nobody in the PR wrote. |
| "They'll clean it up later" | Later does not come. Require it now, or require a ticket. |
| "An agent wrote it, so it's probably fine" | Generated code needs more scrutiny, not less: it is confident and plausible when wrong. |
Idiomatic & Consistency exception: the conventions agent raises non-blocking suggestions ONLY when a concrete better pattern provably already exists in the codebase. Never on taste alone. These land in their own section, not in Critical/Important. A finding without a cited precedent from the codebase is dropped.
Format the review per references/output-format.md (read it now). Key
requirements:
- Header must include
**Head:** <headShaShort>(enables future delta mode). - Branch mode header: title the review
Pre-push review - <LOCAL_BRANCH>, and include**Target:** branch <LOCAL_BRANCH> -> <BASE_REF> (not pushed),**Base:** <mergeBase short sha>, and**CI:** N/A - not pushed. State the uncommitted-file warning here ifgit status --porcelainwas non-empty. There is no PR link and no bot-review section; do not invent either. - When
isDraft=true: add**DRAFT PR** - findings recorded for when this is ready to merge.after the header block. - When
mergeStateStatusis not CLEAN: note it (e.g. conflicts, failing required checks). - When
PR_TITLE_ISSUE=true: insert the⛔ CANNOT MERGEblock before the Summary. In branch mode there is no title yet, so instead print one line -**PR title to use:** <RECOMMENDED_PREFIX> <summary>- and carry it into the Step 4.6 offer. - When
sentrySignalsis non-empty: include## 🔥 Production Signals. - When
PREV_SHAis set: include## 🔁 Since last reviewcomparingPREV_REVIEW_FILEfindings against the new head - list each as fixed or still open, before any new findings. - Test Quality section: populate from the workflow return fields
(
coverageVerdict,mutationSlip,mockSmells). - Idiomatic section: populated from severity=idiomatic findings only.
- 💰 Review Cost block:
outputTokensfrom the workflow return is the ACTUAL output spend of the review workflow's own agents.turnTokensTotalis the whole turn's shared pool - report it separately, never as the workflow's cost. Input tokens are estimated (diff tokens x agent passes + context- prompt files). Use the pricing table in
references/output-format.md. Real numbers only - no<N>placeholders. WhenENGINE=revmux: build the rows from the adapter'sagentsarray (name,model,tokens,usdper row) and reporttotalUsdas the total. ReadpricingMissing; when it is non-empty, explicitly marktotalUsdas incomplete and name the unpriced models. State plainly that the USD figures are an API list-price estimate computed fromreferences/pricing.json's placeholder prices (verified: false) until that file is verified against real invoices. Prove-phase tokens still come from the workflow return'soutputTokens, same as the workflow engine. Whenquestions(from the adapter) is non-empty, render them under a## Questions for the authorsection. Whendegradedis non-empty, add one banner line in the review header naming the degraded agents, e.g.⚠️ degraded: implementation, test-quality.
- prompt files). Use the pricing table in
Save the review:
mkdir -p ~/code-reviews
# TARGET_SLUG: "pr-<PR_NUMBER>" in pr mode,
# "branch-<LOCAL_BRANCH sanitized to [a-z0-9-]>" in branch mode
REVIEW_FILE=~/code-reviews/$(date +%Y-%m-%d)-<TARGET_SLUG>-<repo-short-name>.md
# write the review to $REVIEW_FILE
After writing, re-read the file and emit a receipt:
✓ Review saved -> <path>
Also write the workflow's findings array verbatim to
<scratchpad>/findings.json - fix mode reads its selection from there (the
proof objects ride along for the Step 5b proof flip), and it is the receipt
that what was reported equals what was found.
Branch mode also drops a review marker, so a pre-PR gate you build on top of this skill can tell a reviewed branch from an unreviewed one:
mkdir -p ~/.cache/lekker-review/reviewed
printf '%s\n' "$REVIEW_FILE" > ~/.cache/lekker-review/reviewed/<headSha>
Write it against the sha that was actually reviewed. When fix mode later adds commits, write a marker for the NEW head sha too - a gate keys on the tip that is about to be pushed, and a marker for a superseded sha would be a lie.
Then print the full review as the response.
Step 3.5 - Living review artifact (skip when ARTIFACT=false)
Immediately after printing the review, follow references/artifact-page.md:
launch ONE background sonnet agent that renders the review as a self-contained
HTML page and publishes it via the Artifact tool - passing PREV_ARTIFACT_URL
when set, so a re-review UPDATES the same page instead of minting a new URL.
The page is the living version of the review: verdict header, since-last-review
timeline, findings with proof panels, all private by default.
Never block on it: the printed review and the saved file are the deliverable;
the artifact is an enhancement. When the URL comes back, append/refresh the
**Artifact:** <url> header line in the saved review file (re-read to confirm)
and print one line: 🔗 Living review: <url>.
Step 4 - Fix mode (after the review is printed)
Trigger
FIX_MODE=true(the user passed--fix, orMODE=branchdefaulted it on) -> go straight toreferences/fix-mode.md.FIX_MODE=falseand at least one Critical or Important finding has afixfield -> ask once, via AskUserQuestion:Apply these fixes to the PR branch?
- Critical + Important (N findings) - fix agents edit the worktree, verified, committed; push needs your confirmation
- Critical only (N findings)
- No, review only
Set
FIX_MODE=trueandFIX_SCOPE=<critical+important | critical>from the answer. On "No", skip to Step 5.No fixable findings, or the review found nothing -> do not ask. Say
nothing to auto-fixin one line and skip to Step 5.Unattended run (cron,
/loop, background agent): never ask. Run fix mode only when--fixwas passed explicitly, and stop before pushing (branch mode: stop before landing).
Procedure
Read references/fix-mode.md and follow it. Shape of the run:
- Preconditions.
MODE=pr: worktree exists + clean,origin/<PR_BRANCH>still atheadSha, PR open, head repo writable.MODE=branch: worktree exists + clean, andrefs/heads/<LOCAL_BRANCH>inrepoRootis still atheadSha. If the branch moved while the review ran, stop - do not land onto a tip you did not review.
- Select eligible findings (Critical/Important with a
fix, real file, non-generated). Never auto-fix Observation, Idiomatic, or a title/process rule. - Invoke the fix workflow:
scriptPath: ${CLAUDE_PLUGIN_ROOT}/fix-workflow.js args: { repoSlug, prNumber, targetLabel, worktreePath, diffFile, contextFile, promptDir, findings: [<selected findings verbatim>] }targetLabelis required wheneverprNumberis null, same as the review workflow. Onesonnetfix agent per file (never two on the same file), then a read-onlysonnetfix-verifier per file reading the actualgit diff. One retry max on a non-goodverdict. - Revert every group the verifier did not pass.
- Run
scripts/verify-fixes.sh <WORKTREE_PATH> <scratchpad>/fix-verify.json testsand diff the output against the baselinetscTail/eslintTailfrom worktree.json. Newly introduced errors -> revert that group. 5b. Proof flip: for findings withproof.proven, re-run the captured failing test after the fix. Still red -> the fix did not fix the bug: revert the group even if the fix-verifier saidgood. An executed test outranks an agent's opinion. Green -> recordproofFlip: greenin the status table. - Commit one commit per file with an explicit
git add -- <files>. - Land the commits, ONLY after the user confirms.
MODE=pr:git push origin "HEAD:refs/heads/$pr_branch"with a re-fetch sha guard. Never force, never rebase, never push to main/master/staging/develop. Verify viagh pr view --json headRefOid.MODE=branch: nothing is pushed - the fixes move onto the LOCAL branch, so the work is one branch again before it ever reaches the remote.# Fast-forward the local branch onto the reviewed-and-fixed worktree tip. # --ff-only and the old-value guard together mean this can only ever # advance the exact commit the review started from. git check-ref-format --branch "$local_branch" git -C "$repo_root" update-ref "refs/heads/$local_branch" \ "$worktree_head_sha" "$head_sha"Treat every repository path, ref, branch, title, and body as untrusted data. Pass each value as a separately shell-quoted argument, never by concatenating it into shell source. Put multiline PR bodies in a file and pass the quoted path with
--body-file.If
$local_branchis the branch checked out in$repo_root,update-refwould leave the user's working tree looking like it had deleted the fixes. In that case require a cleangit -C "$repo_root" status --porcelainand usegit -C "$repo_root" merge --ff-only "$worktree_head_sha"instead. If the tree is dirty, stop, keep the worktree, and print the exact command - never stash or discard someone's uncommitted work.Verify with
git -C "$repo_root" rev-parse "refs/heads/$local_branch"and confirm it equals$worktree_head_sha. Then refresh the review marker for the new sha (Step 3).
- Print the per-finding status table and append
## 🔧 Fixes appliedto the saved review file.
Fix-agent tokens are additional spend: add a Fix agents: line to the
## 💰 Review Cost block.
Step 4.6 - Open the PR (MODE=branch only)
Skip entirely in MODE=pr.
The branch has now been reviewed and, where it had fixable findings, fixed. The PR is the next step, but it is the USER'S call and it is the one irreversible thing in this whole flow - once the PR exists a bot reviewer may start running and start costing money. Never create it silently.
Ask once, via AskUserQuestion:
Branch reviewed<, N fixes applied>. Open the PR now?
- Yes, create the PR -
<RECOMMENDED_PREFIX>title, base<BASE_REF>, pushes<LOCAL_BRANCH>first- Push the branch only - no PR yet
- No, stop here - nothing leaves this machine
Rules for each answer:
- Yes: push with
git push -u origin "$local_branch", then create the PR withgh pr create --repo "$repo_slug" --base "$base_ref" --head "$local_branch" --title "$pr_title" --body-file "$pr_body_file". Derive the title from the commit log (the recommended prefix plus a summary of the change) and the body from a short summary of what the branch does - do not leave either as a placeholder. Report the PR url. The review is already saved locally; offer--poston the
…(truncated)