Files
dotfiles/.claude/skills/check/SKILL.md
T

11 KiB

name, description
name description
check Daily-driver review of in-flight changes (or specified files) for reuse, simplification, efficiency, and high-confidence bugs. Spawns one Opus subagent to keep file reads off the main thread. Default mode diffs the working tree and staged changes against the main branch. Pass paths (`/check src/foo.rs`) to review file state instead. Use as the fast pre-PR review pass. For a deeper whole-tree retrospective, use /audit.

/check

Pre-PR review pass over work-in-progress. The main thread spawns one Opus subagent to do the analysis in isolation, then receives a structured finding list to present and apply.

Invocation

/check                          # diff working tree + staged vs main/master
/check --staged                 # only staged changes
/check --branch develop         # diff against a different base
/check src/foo.rs               # review one file as-is (ignores diff)
/check src/foo/ src/bar.rs      # review multiple paths as-is

Paths win: if any are given, ignore the diff and review file state.

Step 1: Determine scope

Diff mode (no paths):

  1. Resolve the base branch. Try git symbolic-ref refs/remotes/origin/HEAD, then main, then master. Ask the user if neither exists.
  2. With --branch <name>, use that base instead.
  3. With --staged, scope to staged changes only (git diff --cached <base>). Otherwise include working tree (git diff <base>...HEAD plus uncommitted).
  4. Enumerate touched files (git diff --name-only ...).

File mode (paths passed): take the paths verbatim. The subagent recurses into directories.

Do not read the in-scope source files in the main thread. The subagent owns review-time reads.

Step 2: Spawn the review subagent

One call to the Agent tool with:

  • subagent_type: general-purpose
  • model: opus (regardless of session model)
  • description: "/check review pass"
  • prompt: composed as <scope block>\n\n<review instructions, verbatim from Step 3>

The scope block tells the subagent what to review:

# Scope

Mode: <diff|file>
Base branch: <name>           # diff mode only
Staged only: <yes|no>         # diff mode only
Touched files:                # diff mode: from `git diff --name-only`
  - path/to/file.rs
  - ...
Paths to review:              # file mode
  - src/foo.rs
  - src/bar/
Repo root: <absolute path>

Step 3: Review instructions (passed verbatim to the subagent)

You are reviewing the in-flight work (or specified paths) described in the scope block above. Surface concrete, high-confidence improvements before this work ships. Be useful, not exhaustive.

Context

Read each in-scope file fully, not just the diff hunks. Reuse and efficiency findings need surrounding context. Also read CLAUDE.md at the repo root and in any touched subdirectories, plus TODO.md / BACKLOG.md if present. Honor local conventions. Do not re-raise anything already deferred.

When writing finding text, do not use em-dashes, en-dashes, semicolons, or -- as em-dash substitutes. Use periods or commas instead.

What to flag

Reuse (look here first)

  • New helpers or inline logic that duplicates an existing utility in this codebase. Scan the file's directory and shared locations (utils/, lib/, helpers/, modules adjacent to the changed ones) before concluding nothing exists.
  • Reinvented standard library or framework primitives (manual path joining, ad-hoc env var parsing, custom type guards, hand-rolled string manipulation, bespoke deep-equals, etc.) where the language or an in-repo helper already covers it.
  • Inconsistent import paths for the same thing (some sites use a top-level re-export, others reach into deep paths). Pick the path the rest of the codebase uses.
  • For each duplication finding, name the existing helper with file:line so the fix is concrete.

Overengineering

  • New helpers, traits, generics, or indirection introduced for one caller. Inline.
  • Flags, parameters, or fields with only one realistic value or use site.
  • Layers added speculatively without a current second caller.

Dead code in the same change

  • Functions added but uncalled elsewhere in the diff.
  • Branches that cannot be reached given the rest of the change.
  • New types, fields, or variants that nothing constructs.

Bugs (high-confidence only)

Flag only when the issue is clearly wrong on inspection. "This might fail under some inputs" is not a finding.

  • Logic errors: off-by-one, wrong condition in if, inverted comparison, incorrect loop termination, swapped arguments.
  • Missing bounds checks where the input is not otherwise validated.
  • Type confusion that compiles but is wrong (mixing units, conflating IDs, using one enum where another was meant, comparing values of different shapes).
  • Null or undefined access in JS/TS where the type allowed it but the path is reachable.
  • Async lifetime mistakes: missing await, awaiting the wrong promise, dropping a future, fire-and-forget where the result was meant to be used.
  • Rust borrow or lifetime patterns that compile but violate the intended invariant (lifetimes too permissive, Send/Sync boundaries crossed by accident).
  • Resource leaks: opened but never closed, listeners attached but never removed on the failure path.

Efficiency

Focus on hot paths (per-render, per-request, per-frame, per-record) and on patterns that get worse with scale.

  • Redundant work: duplicate computations, repeated file reads, redundant API or network calls, N+1 query patterns.
  • Independent operations run sequentially that could run in parallel.
  • New blocking work added to startup or per-render/per-request paths that did not have it before.
  • State updates inside polling loops or event handlers that fire even when nothing changed. Add change-detection so downstream consumers are not woken for no-ops. If a setter takes an updater callback, verify same-value returns are honored so callers' own no-op guards are not silently defeated.
  • Pre-checking a file or resource for existence before operating on it. This creates a race where the resource can change between the check and the use. Operate directly and handle the error from the operation instead.
  • Unbounded data structures, missing cleanup, leaked event listeners or timers.
  • Operations broader than they need to be (reading entire files when only a portion is needed, loading all rows when filtering for one, scanning a whole collection to find a single item where an index already exists).

Premature flexibility

  • Option<T> where the value is always Some.
  • Trait bounds or generic parameters wider than the actual use.
  • Enum variants for cases that do not yet exist.
  • impl Trait where the concrete type would read just as clearly.

Comments to delete

  • Narration of what the code does (// increments counter).
  • Section dividers (// ---, // === FOO ===).
  • References to the task or commit that prompted the change ("added for X", "used by Y", "fixes #123").
  • Stale docs where the code has moved past them.

Tests not pulling weight

  • Tests that assert the language or framework's behavior rather than the new logic.
  • Tests duplicated across files with no extra coverage.
  • Fixtures wrapping a one-liner for a single test.
  • Mocked tests where an integration test already covers the same path.

Scope creep

  • Unrelated formatting changes mixed in with functional changes.
  • Drive-by refactors in files outside the stated goal.
  • "While I'm here" cleanup that belongs in a separate commit.

Style-rule violations

Flag these whenever they appear in the diff or files:

  • Em-dashes, en-dashes, -- as em-dash substitutes, and semicolons in prose (comments, docs, commit messages, PR descriptions).
  • Programming-culture acronyms (YAGNI, DRY, KISS, SOLID, TDD, BDD, DDD, MVP, NBO, TOCTOU) in any prose. Replace with what they actually mean.
  • Section divider comments.
  • Milestone references in code docs ("for M2", "added in M1").
  • Co-Authored-By: Claude trailers in commit messages.

Fold in any project-local CLAUDE.md conventions on top of these.

What NOT to flag

  • Low-confidence speculation. "This might break under some inputs" is not a bug finding. "This could be faster in theory" is not an efficiency finding. If you cannot point at a concrete failure mode or a concrete cost, leave it out.
  • Issues a linter or formatter already catches.
  • Missing tests or coverage gaps (separate concern, out of scope here).
  • "You could rewrite this with library X" or other broad architectural rewrites.
  • Items already in TODO.md / BACKLOG.md.
  • Performance theatre: micro-optimizations outside hot paths.

Output format

Return a single markdown block with this exact structure. No preamble or commentary outside the block.

## Findings

<N> findings on <scope description>.

1. path/to/file.rs:42-58
   Issue: <one line, concrete>.
   Fix: <one line, specific enough that the orchestrator can apply without re-reading the file>.

2. path/to/file.rs:88
   Issue: <one line>.
   Fix: <one line>.

For findings that touch public API surface or change behavior beyond cleanup, replace the Fix: line with:

   Needs discussion: <reason>.
   Option A: <option>.
   Option B: <option>.
   Tradeoff: <one line>.

If nothing is worth flagging, return exactly Nothing flagged and stop. Do not pad.

End of review instructions.

Step 4: Present findings

Display the subagent's findings to the user as-is. If any are marked Needs discussion, present those after the auto-apply list as a separate block.

End with: Apply all? "Go ahead", or tell me which numbers to skip.

Step 5: Apply approved findings

Only after the user approves:

  1. Read each touched file once.
  2. Apply the approved findings with the Edit tool. The subagent's Fix: line should be specific enough to apply without ambiguity. If a fix is too vague, surface that and ask the user.
  3. Diff mode: do not commit. The user is mid-PR.
  4. File mode (no in-flight diff): default to one commit per theme using Conventional Commits.
  5. Run the project gate (scripts/prepare.sh, pnpm check, cargo test, equivalent) when applying multiple substantive changes.

Do not

  • Read files under review in the main thread before Step 5. The subagent owns review-time reads.
  • Rewrite history. The user is preparing a PR. Do not squash or amend existing commits.
  • Auto-apply changes to public API surface. Lift those into Needs discussion.
  • Reference the check run in commit messages ("from /check", "review cleanup"). Describe the change.
  • Surface a "bug" or "perf" finding the subagent was not confident about. The signal value comes from the user trusting flagged bugs are real.
  • Pad. If the subagent returns Nothing flagged, that is the right answer.