Code Review skill for Codex

Find the 3 a.m. bug before the naming nits. Codex reviews pull requests in priority passes, correctness and security before style, and only reports a defect when it can describe the input that breaks it. Every comment carries a severity label, file and line, and a suggested fix in a respectful tone. It reads PRs through gh, confirms suspicions with throwaway tests, ships area checklists for migrations, auth and async code, and posts nothing until you approve.

  • Engineering Workflow
  • $code-review
  • 175 lines
  • 3 files
  • ~1,800 tokens
  • MIT
  • Updated
  • Editor's pick

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- code-review

Installs to ~/.agents/skills/code-review/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $code-review, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Reviews someone else's diff or pull request the way a senior engineer does - intent first, then correctness, security, data safety, concurrency, compatibility and tests before style - with every finding tied to a file and line, a concrete failure scenario, a severity label (blocker, major, minor, nit, question) and a suggested fix, written in a respectful tone. Reads PRs with gh pr diff, confirms suspicions by running code, and posts reviews only after approval. Use when asked to review a PR, a branch, a patch or staged changes, or for a second opinion before merging.

Use it when

  • Someone asks for a review of a PR, branch, patch or staged diff
  • You want a second opinion before merging a risky change
  • Review comments on a team tend to be nits while real bugs slip through

Use something else when

How to check the result

Each blocker or major names a file and line plus a concrete failing scenario, ideally confirmed by a throwaway test, and the verdict matches the highest severity found.

The mistake it prevents

Burying one real defect under twenty style nits, or flagging speculative bugs as blockers without a scenario that triggers them.

~/.agents/skills/code-review/SKILL.md

--- name: code-review description: Reviews someone else's diff or pull request the way a senior engineer does - intent first, then correctness, security, data safety, concurrency,… license: MIT ---

A review is useful when the author can act on every comment without a follow-up question, the blocking issues are real (each has a concrete way to fail), and nothing important is buried under nits. Find the bug that would page someone at 3 a.m. before you mention a variable name.

Get the change and its context

gh pr view 123 --json title,body,author,baseRefName,headRefName,additions,deletions,files
gh pr diff 123                              # needs network and gh auth
gh pr checks 123                            # CI state; do not duplicate what CI already caught
git diff origin/main...HEAD                 # local branch: three dots = since merge base
git diff --cached                           # staged changes before a commit

To run or test the change locally, gh pr checkout 123 switches branches; first check git status and ask before touching a dirty working tree. Prefer git worktree add to keep the user’s checkout untouched.

Before judging code, establish:

  1. Intent. What problem does the PR claim to solve? Read the description and linked issue. A correct implementation of the wrong thing is the most expensive bug.
  2. Conventions. Read AGENTS.md, CONTRIBUTING.md and neighbouring code. Review against the repo’s standards, not your preferences.
  3. Blast radius. Which callers, consumers, data and deployments does this touch? rg -n "<changed function>" --type <lang> to find call sites outside the diff.

Review passes, in priority order

Make separate passes. Each pass asks one question of the whole diff.

#PassAsk
1CorrectnessDoes it do what the intent says for normal, empty, boundary and error inputs?
2SecurityUntrusted input reaching queries, shells, paths, templates, redirects, deserialisers? Authz on every new entry point? Secrets in code or logs?
3Data safetyMigrations reversible and safe under load? Writes idempotent? Anything that can lose or corrupt data?
4Failure and concurrencyTimeouts, retries, partial failure, races, shared mutable state, resource cleanup?
5CompatibilityPublic API, wire format, config, CLI flags, DB schema: does any existing consumer break?
6TestsWould the tests fail if the new logic were wrong? Are the risky paths covered?
7DesignRight layer, right abstraction, duplication of existing helpers, complexity the problem does not need?
8ReadabilityNames, comments that explain why, dead code. Only what linters and formatters do not catch.

For per-area checklists (SQL migrations, auth, async code, React, Go, Python, Java and more), read references/review-checklist.md when the diff touches that area.

Proving a finding

A suspected bug becomes a finding only with a concrete scenario: specific input or sequence, the wrong result, and why. Cheap ways to confirm:

  • Trace the call path by reading the callers, not just the changed lines.
  • Write a throwaway test in a scratch file or worktree that exercises the case; run it. Do not commit or push it to the author’s branch.
  • Check the base branch behaviour too: a problem that predates the PR is not this PR’s blocker; mention it separately.

If you cannot confirm it, phrase it as a question, not a defect.

Severity

LabelMeaningMerge?
blockerBug, security hole, data loss, or breaks consumers. Concrete scenario givenNo
majorLikely bug in an edge case, missing test for risky logic, design that will hurt soonNot without discussion
minorReal improvement, low risk if deferredAuthor’s call
nitTaste or polishNever blocks
questionYou need information to judgeBlocks only until answered
praiseSomething done notably well-

Cap nits. More than five nits means you should state the pattern once (“several names abbreviate configuration; prefer full words”) instead of commenting on each.

Writing comments

  • Lead with the label, then the problem, the scenario, and a suggested fix:
**blocker:** `retry()` re-sends the payment request after a timeout, but the
provider may have processed the first one. A slow 200 followed by a retry charges the
customer twice. Pass the existing `idempotency_key` on retries (see `charge()` line 88).
  • Talk about the code, not the person: “this loop re-reads the file” rather than “you re-read the file”. Ask real questions; do not phrase demands as questions.
  • Explain why. “Use a set here” teaches nothing; “membership checks on this list are O(n) inside an O(n) loop, ~4M operations for the 2k-item case” does.
  • Offer code for small fixes with a GitHub suggestion block so the author can apply it:
```suggestion
if not items:
    return Decimal("0.00")
```
  • Do not comment on formatting a formatter would fix, on code outside the diff (unless the diff makes it newly wrong), or on preferences the repo does not codify.
  • Acknowledge good work briefly and specifically when it is real.

Verdict

  • Request changes if any blocker exists.
  • Comment if majors or questions are open but nothing is proven broken.
  • Approve when only minors and nits remain; say they are optional.

Posting the review

Posting is visible to others, so show the review to the user and get a clear yes first.

gh pr review 123 --comment --body-file "$TMPDIR/review.md"
gh pr review 123 --request-changes --body-file "$TMPDIR/review.md"
gh pr review 123 --approve --body "LGTM, nits optional."

Inline comments go through the reviews API in one batch:

gh api repos/{owner}/{repo}/pulls/123/reviews --method POST --input "$TMPDIR/review.json"
{
  "commit_id": "<head sha from gh pr view 123 --json headRefOid>",
  "event": "COMMENT",
  "body": "Summary: one blocker in retry handling, two minors.",
  "comments": [
    { "path": "src/payments/retry.py", "line": 42, "side": "RIGHT", "body": "**blocker:** ..." }
  ]
}

line must fall inside a diff hunk on that side, or the API returns 422. Use start_line plus line for multi-line ranges.

Output template

When reporting in chat rather than posting:

## Review: <PR title> (#123)
**Verdict:** request changes | comment | approve
**Summary:** <2-3 sentences: what the PR does, overall assessment, the main risk>

### Blockers
1. `src/payments/retry.py:42` - <problem>. Scenario: <input -> wrong result>. Fix: <suggestion>.

### Major
### Minor
### Nits
### Questions
### Not reviewed / out of scope
<generated files skimmed, areas you could not run or verify, pre-existing issues noticed>

Gotchas

  • Reviewing only the changed lines misses the caller that now passes None. Read one level out from every changed function signature.
  • Deleted code is part of the diff: removed validation, removed tests, removed error handling are frequent sources of regressions.
  • Green CI proves only what the tests cover. Check that new tests would fail without the change, not merely that they exist.
  • Large generated diffs (lockfiles, snapshots, codegen) hide hand edits. Spot-check that generated files match their generator.

What the zip contains

Codex reads bundled files only when a step points to them. Every one is shown here, so you can review it before installing.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Code Review"
  short_description: "Find the 3 a.m. bug before the naming nits"
  default_prompt: "$code-review Review PR 482 and tell me if it is safe to merge"
references/review-checklist.md125 lines, 5.9 KB
# Area checklists for code review

Use only the sections the diff touches. Each item is a question that has caught real
production bugs; a "no" is a candidate finding that still needs a concrete scenario.

## Database migrations and queries

- Does the migration lock a large table? Adding a column with a volatile default, adding
  `NOT NULL` without a default, changing a column type, or building an index without
  `CONCURRENTLY` (Postgres) / `ALGORITHM=INPLACE, LOCK=NONE` (MySQL) can block writes.
- Is the deploy order safe? New code must work with the old schema and old code with the
  new schema during a rolling deploy (expand, migrate, contract).
- Is there a down migration, or a stated reason why not?
- Are queries built with parameters, never string formatting?
- N+1: does a loop issue one query per item? Look for ORM lazy loads inside loops.
- Unbounded reads: `SELECT *` without `LIMIT` on a growing table, or loading a whole
  table into memory.
- Transactions: is the unit of work atomic? Any external call (HTTP, queue) inside a
  transaction that can hold locks for seconds?

## Authentication and authorisation

- Does every new route, resolver, RPC or job check who is calling and whether they may
  access this specific object (not just "is logged in")? IDOR is the common miss.
- Are permission checks on the server, not only hidden in the UI?
- Are tokens, passwords and keys kept out of logs, error messages and URLs?
- Are comparisons of secrets constant-time (`hmac.compare_digest`, `crypto.timingSafeEqual`)?
- Session or token lifetime changes: what happens to tokens already issued?

## Input handling

- Shell: any user-influenced string reaching `subprocess(..., shell=True)`, `exec`,
  backticks or `os.system`?
- Paths: user input joined into a filesystem path without normalising and checking it
  stays inside the intended directory?
- Redirects and URLs: open redirects; server-side fetches of user-supplied URLs (SSRF).
- Deserialisation of untrusted data (`pickle`, `yaml.load` without SafeLoader, Java
  native serialisation).
- HTML output: values inserted with `innerHTML`, `dangerouslySetInnerHTML`, `|safe`,
  `v-html` without sanitising.
- Size limits on request bodies, uploads, regexes on user input (catastrophic backtracking).

## Error handling and resilience

- Are errors swallowed (`except: pass`, empty `catch`, ignored Go `err`)?
- Do retries have a cap, backoff and jitter, and are retried operations idempotent?
- Does every network call have a timeout?
- Are resources closed on every path (`with`, `defer`, `try/finally`, `using`)?
- Do error messages returned to clients leak stack traces or internals?

## Concurrency

- Shared mutable state accessed from multiple threads, goroutines or async tasks without
  a lock or a single owner?
- Check-then-act races: "if not exists then create" without a unique constraint or lock.
- Async: un-awaited promises or coroutines; `forEach` with an async callback; blocking
  I/O inside an event loop.
- Go: goroutines without a way to stop (context, done channel); writes to a map from
  multiple goroutines; loop variable capture (fixed per-iteration since Go 1.22, but
  check the module's `go` directive).
- Caches: invalidation on write, stampede on expiry, unbounded growth.

## API and compatibility

- Removed or renamed fields, endpoints, CLI flags, env vars, config keys, events?
- Changed defaults or semantics of an existing parameter?
- New required fields on requests that old clients do not send?
- Enum additions that old consumers will not handle (exhaustive switches, strict parsers)?
- Versioning and changelog updated if the repo requires them?

## Tests

- Would the new test fail if the implementation were reverted? Look for assertions that
  only check "no exception" or that a mock was called.
- Are time, randomness and network controlled, or will the test flake?
- Are the edge cases that the code explicitly handles also tested (empty, null, max)?
- Were existing tests deleted or loosened? Ask why.

## Frontend (React and similar)

- Effects: missing or over-broad dependency arrays; effects that should be event
  handlers; missing cleanup for subscriptions and timers.
- Keys: array index as `key` on lists that reorder or filter.
- State derived from props copied into state and then stale.
- Accessibility: interactive elements that are not buttons or links, missing labels,
  focus not managed in dialogs.
- User-visible strings not passed through i18n when the app is localised.

## Language traps

### JavaScript / TypeScript
- `==` vs `===`; `||` defaults that swallow `0` or `""` (use `??`).
- `any`, non-null assertions (`!`) and `as` casts hiding real type errors.
- Floating-point money; `parseInt` without a radix on user input.
- Mutating function arguments or shared module-level objects.

### Python
- Mutable default arguments (`def f(x=[])`).
- Naive vs aware `datetime` mixed; `datetime.utcnow()` is deprecated since 3.12.
- Broad `except Exception` around large blocks.
- Iterating and mutating the same dict or list.

### Go
- Ignored errors; errors wrapped without `%w` so `errors.Is` fails.
- `defer` inside loops holding resources until the function returns.
- Nil map writes; nil interface vs typed nil pointer comparisons.
- Context not propagated into downstream calls.

### Java / Kotlin
- `equals` without `hashCode`; mutable objects as map keys.
- Unclosed streams or connections outside try-with-resources / `use`.
- `Optional.get()` without a presence check; platform types from Java in Kotlin.
- Blocking calls on reactive or coroutine dispatchers.

### SQL
- `NULL` semantics in `NOT IN` subqueries; `=` comparisons against NULL.
- Implicit casts that defeat indexes.
- Time zones in timestamp columns.

## Performance (only when the change is on a hot path)

- Work moved inside a loop that used to be outside it.
- New synchronous calls on request paths (network, disk, heavy CPU).
- Payload size changes: new fields serialised on every list item.
- Missing pagination on new list endpoints.

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem