Elixir Code Review
Review the changed code and enough surrounding code to understand its callers,
public contract, process ownership, supervision context, and existing tests.
Apply the authority order
- Follow the target repository's established public APIs, tests, architecture,
and intentional conventions.
- Follow official Elixir, Erlang/OTP, Phoenix, Ecto, and dependency
documentation.
- Apply the review statements and rule cards from this skill.
- Use general model knowledge last.
Do not apply Phoenix- or Ecto-specific statements unless the repository uses
that framework. When local architecture intentionally differs from a valid
statement's default, do not report the difference unless the change creates a
concrete risk.
If the repository contains an ELIXIR_REVIEW_EXCEPTIONS.md file, treat the
statement slugs listed there as accepted deviations for that repository and do
not report them.
Perform the review
- Identify the behavior changed by the pull request.
- Read the statements index and select the
statements whose triggers match the changed code. Ignore statements whose
triggers do not appear in the diff or its immediate context.
- Evaluate the changed code against each selected statement. Open the linked
rule card only when a borderline case needs the fuller decision guidance:
- PM001 — pattern matching,
guards, external input, dynamic atoms
- CF001 —
with, case,
cond, if, branching
- FN001 — public
functions, visibility, arities, options, return contracts
- DATA001 — maps, structs,
keyword lists, schemas, changesets
- ERR001 — tagged tuples,
exceptions, error translation, process failure
- OTP001 — GenServer, Task, Agent,
Registry, ETS, supervision, concurrency
- TEST001 — ExUnit, mocks, HTTP
testing, process tests, dependency injection
- OBS001 — telemetry, metrics, tags,
durations, instrumentation, logging
- REV001 — aliasing,
supervision altitude, invariants at the source, config threading,
numeric tunables, unconsumed metrics, spec-doc hygiene, sibling-domain
parity (distilled from recurring human review feedback)
- Use the design principles when
a change crosses several of these concerns.
- Confirm that each suspected violation is reachable and introduced or
materially worsened by the pull request.
- Check whether existing tests, callers, or documented behavior disprove the
suspected violation.
- Report only findings with a concrete correctness, reliability, security,
operability, or maintainability consequence.
- Explain the failure mode and the smallest reasonable correction.
Report findings by statement
Every finding must cite the statement slug it violates (for example
PM001.no-dynamic-atoms). A concern that maps to no statement may still be
reported when it meets the concrete-consequence bar in step 7; mark it
(no-statement) so it can be considered for a future statement.
Severity follows the statement level:
- MUST violation — report as a defect that must be fixed before merge.
- SHOULD deviation — report as a non-blocking suggestion, and only when it
has a concrete consequence in this change.
Do not report the same slug twice for the same root cause; group repeated
instances under one finding with all locations listed.
When the host environment provides a dedicated findings-reporting tool (for
example ReportFindings), report through that tool using the severity
semantics above, and put the statement slug in each finding's category or
summary so it stays visible. Otherwise report the findings as text.
Prioritize high-value findings
- Unsafe handling of untrusted external data or dynamic atom creation.
- Broken or inconsistent public return contracts.
- Expected failures that are swallowed, leaked, or raised unexpectedly.
- Processes without a concrete state, concurrency, lifecycle, isolation, or
fault-recovery requirement.
- Missing restart, state-recovery, timeout, or supervision semantics.
- Race-prone process tests and arbitrary sleeps.
- Tests that mock ordinary internal implementation instead of observable
behavior.
- Missing coverage for newly introduced success, failure, or boundary paths.
- Children started under a supervisor that does not match their scope, and
defensive clauses downstream of an invariant the source should enforce.
- Renamed aliases (
as:) or aliases that hide which of two same-named
domain modules is being called.
- Unjustified numeric tunables and metrics or telemetry with no consumer.
Out of scope: mechanically checkable issues
Do not spend findings on anything the project's standard tooling already
catches deterministically:
- Formatting and layout —
mix format owns these.
- Standard lint findings — Credo owns these (unused variables, module and
function naming, alias ordering, nesting depth,
Enum vs Stream in
pipelines, and any check enabled in the project's .credo.exs).
- Type and spec mismatches Dialyzer reports.
- Security patterns Sobelow flags, unless the change adds a concrete
exploitable path Sobelow's static patterns cannot see.
Exception: dynamic atom creation (PM001.no-dynamic-atoms) stays in scope
even where Credo's UnsafeToAtom check exists, because indirect paths
(interpolated module names, atoms built in helper functions) evade the lint.
Review-tooling files are not code under review. Exclude this skill's own
directory and any agent or review configuration under paths like
.github/skills/ or .claude/ from findings, even when they appear in the
diff.
Do not report formatter issues, subjective style preferences, speculative
rewrites, or an alternative design that is merely equally valid. Do not require
a broad refactor when the changed lines can be corrected locally.
1---2name: elixir-code-review3description: Review Elixir, Phoenix, Ecto, and OTP pull requests for concrete correctness, reliability, data-boundary, error-contract, public-API, concurrency, supervision, and testing problems. Use for GitHub Copilot code review or any review containing .ex or .exs changes, especially changes involving external input, tagged tuples, processes, background work, HTTP clients, or ExUnit tests.4---5# Elixir Code Review67Review the changed code and enough surrounding code to understand its callers,8public contract, process ownership, supervision context, and existing tests.910## Apply the authority order11121. Follow the target repository's established public APIs, tests, architecture,13 and intentional conventions.142. Follow official Elixir, Erlang/OTP, Phoenix, Ecto, and dependency15 documentation.163. Apply the review statements and rule cards from this skill.174. Use general model knowledge last.1819Do not apply Phoenix- or Ecto-specific statements unless the repository uses20that framework. When local architecture intentionally differs from a valid21statement's default, do not report the difference unless the change creates a22concrete risk.2324If the repository contains an `ELIXIR_REVIEW_EXCEPTIONS.md` file, treat the25statement slugs listed there as accepted deviations for that repository and do26not report them.2728## Perform the review29301. Identify the behavior changed by the pull request.312. Read [the statements index](references/statements.md) and select the32 statements whose triggers match the changed code. Ignore statements whose33 triggers do not appear in the diff or its immediate context.343. Evaluate the changed code against each selected statement. Open the linked35 rule card only when a borderline case needs the fuller decision guidance:36 - [PM001](references/PM001_pattern_matching_guards.md) — pattern matching,37 guards, external input, dynamic atoms38 - [CF001](references/CF001_control_flow_selection.md) — `with`, `case`,39 `cond`, `if`, branching40 - [FN001](references/FN001_function_design_visibility.md) — public41 functions, visibility, arities, options, return contracts42 - [DATA001](references/DATA001_data_structures.md) — maps, structs,43 keyword lists, schemas, changesets44 - [ERR001](references/ERR001_error_handling.md) — tagged tuples,45 exceptions, error translation, process failure46 - [OTP001](references/OTP001_process_design.md) — GenServer, Task, Agent,47 Registry, ETS, supervision, concurrency48 - [TEST001](references/TEST001_test_architecture.md) — ExUnit, mocks, HTTP49 testing, process tests, dependency injection50 - [OBS001](references/OBS001_observability.md) — telemetry, metrics, tags,51 durations, instrumentation, logging52 - [REV001](references/REV001_recurring_reviewer_themes.md) — aliasing,53 supervision altitude, invariants at the source, config threading,54 numeric tunables, unconsumed metrics, spec-doc hygiene, sibling-domain55 parity (distilled from recurring human review feedback)564. Use [the design principles](references/01_elixir_design_principles.md) when57 a change crosses several of these concerns.585. Confirm that each suspected violation is reachable and introduced or59 materially worsened by the pull request.606. Check whether existing tests, callers, or documented behavior disprove the61 suspected violation.627. Report only findings with a concrete correctness, reliability, security,63 operability, or maintainability consequence.648. Explain the failure mode and the smallest reasonable correction.6566## Report findings by statement6768Every finding must cite the statement slug it violates (for example69`PM001.no-dynamic-atoms`). A concern that maps to no statement may still be70reported when it meets the concrete-consequence bar in step 7; mark it71`(no-statement)` so it can be considered for a future statement.7273Severity follows the statement level:7475- **MUST** violation — report as a defect that must be fixed before merge.76- **SHOULD** deviation — report as a non-blocking suggestion, and only when it77 has a concrete consequence in this change.7879Do not report the same slug twice for the same root cause; group repeated80instances under one finding with all locations listed.8182When the host environment provides a dedicated findings-reporting tool (for83example `ReportFindings`), report through that tool using the severity84semantics above, and put the statement slug in each finding's category or85summary so it stays visible. Otherwise report the findings as text.8687## Prioritize high-value findings8889- Unsafe handling of untrusted external data or dynamic atom creation.90- Broken or inconsistent public return contracts.91- Expected failures that are swallowed, leaked, or raised unexpectedly.92- Processes without a concrete state, concurrency, lifecycle, isolation, or93 fault-recovery requirement.94- Missing restart, state-recovery, timeout, or supervision semantics.95- Race-prone process tests and arbitrary sleeps.96- Tests that mock ordinary internal implementation instead of observable97 behavior.98- Missing coverage for newly introduced success, failure, or boundary paths.99- Children started under a supervisor that does not match their scope, and100 defensive clauses downstream of an invariant the source should enforce.101- Renamed aliases (`as:`) or aliases that hide which of two same-named102 domain modules is being called.103- Unjustified numeric tunables and metrics or telemetry with no consumer.104105## Out of scope: mechanically checkable issues106107Do not spend findings on anything the project's standard tooling already108catches deterministically:109110- Formatting and layout — `mix format` owns these.111- Standard lint findings — Credo owns these (unused variables, module and112 function naming, alias ordering, nesting depth, `Enum` vs `Stream` in113 pipelines, and any check enabled in the project's `.credo.exs`).114- Type and spec mismatches Dialyzer reports.115- Security patterns Sobelow flags, unless the change adds a concrete116 exploitable path Sobelow's static patterns cannot see.117118Exception: dynamic atom creation (`PM001.no-dynamic-atoms`) stays in scope119even where Credo's `UnsafeToAtom` check exists, because indirect paths120(interpolated module names, atoms built in helper functions) evade the lint.121122Review-tooling files are not code under review. Exclude this skill's own123directory and any agent or review configuration under paths like124`.github/skills/` or `.claude/` from findings, even when they appear in the125diff.126127Do not report formatter issues, subjective style preferences, speculative128rewrites, or an alternative design that is merely equally valid. Do not require129a broad refactor when the changed lines can be corrected locally.