From laravel-api-tool-kit
Performs multi-phase code review: gathers ticket/diff context, validates acceptance criteria via evidence table, checks PHP/Laravel structural standards, separates new bugs from existing debt.
npx claudepluginhub ahmedesa/laravel-api-tool-kitlaravel-api-tool-kit/workflows/# Workflow: Code Review A defense-oriented multi-phase code review. Validates feature completeness against acceptance criteria, catches real bugs (not just style), and separates introduced issues from pre-existing debt. **Trigger phrases**: "review this code", "code review", "check this PR", "review this branch", `/code-review` --- ## Phase 1 — Gather Context Collect ALL context before reading a single line of code: ### 1a. Understand the ticket - Extract every acceptance criterion as a testable checklist item - Note what should NOT happen (negative criteria are easy to miss) ### 1b....
/reviewRenders REQ → IMPL → TEST traceability tree for Beads bd task ID or feature slug, listing requirements, implementations, tests, and coverage summary. Exits before full review.
/code-reviewPerforms technical code review on git changes for logic errors, security vulnerabilities, performance issues, code quality, and project standards like Spring Data JDBC, generating a markdown report with prioritized issues and compliance checklist.
/vt-reviewReviews staged git changes, files, or features for code quality, security issues, codebase patterns, and acceptance criteria using specialized agents.
/reviewPerforms checklist-driven code review on staged files or specified paths. Supports --sanity for quick 14 core pre-commit checks or --pr for full 546-check PR review.
/review-prRuns context-aware PR review on PR number/URL (or infers current branch), gathering metadata, tickets, CLAUDE.md guidelines via parallel agents, and produces validation checklist.
/full-reviewOrchestrates phased multi-dimensional code reviews across architecture, security, performance, testing, and best practices using subagents. Produces structured reports in .full-review/. Accepts target and optional flags.
Share bugs, ideas, or general feedback.
A defense-oriented multi-phase code review. Validates feature completeness against acceptance criteria, catches real bugs (not just style), and separates introduced issues from pre-existing debt.
Trigger phrases: "review this code", "code review", "check this PR", "review this branch", /code-review
Collect ALL context before reading a single line of code:
# Find the merge base
git merge-base <branch> origin/master
# See exactly which files changed
git diff <base>...<branch> --stat
# Read every change — no file filters
git diff <base>...<branch>
# Understand commit history
git log <base>...<branch> --oneline
Read EVERY changed file including constants, configs, migrations, and small utility files. A change to a constants array has more behavioral impact than most new classes.
Read SKILL.md → Project Defaults. These are mandatory constraints — violations are findings.
For EACH acceptance criterion:
Output table:
| Criterion | Status | Evidence |
|---|---|---|
| [criterion text] | PASS / FAIL / VERIFY | [file:line or explanation] |
If a criterion says "X should NOT happen" — find the explicit code preventing it. Absence is not protection.
Work through each section. Flag every violation.
declare(strict_types=1) presentmixed unless truly unavoidablematch used insteadenv() calls outside config/*.phpprivate readonly promotionnewresolve() or app() inside constructors (acceptable only in traits/static methods)Controller$request->validated() — no $request->all() or $request->except()trans()ApiResponse trait methodsresponseCreated() for store, responseDeleted() for destroy$this->authorize()throttle middleware$fillable explicitly defined — no $guarded = []$casts covers booleans, enums, arrays, datesFilterable trait and $default_filters bound for filterable modelsHasUlids trait + $keyType = 'string' + $incrementing = false$userId — no hardcoded auth()->id()QueryFilters$allowedFilters and $allowedSorts populated$this->builder, return voidauth()->id() hardcoded$default_filterswith() for relationships listed in $allowedIncludeswhenLoaded()$this->when() — no inline ternaries that null a field->exists(), ->count(), ->first() inside toArray()dateTimeFormat()toArray()sometimes for optional fieldsauthorize() returns true (auth in Policy, not here)Rule::in(Enum::values())final readonly classexecute() method$request, response(), redirect())final readonly class with private constructor promotionarray or exceed 3private readonly with getter methodsShouldQueue for I/ODB::transaction()ShouldQueue$tries, $backoff, and $timeoutauth()->id(), request(), or HTTP-context helpers inside handle()DB::transaction()SerializesModels)handle() — not trusted from serialized constructor dataDB::transaction()DB::table() NOT used when an Eloquent model exists — raw query builder bypasses soft deletes, global scopes, and model eventswhereIn()->update()increment()/decrement()If a constants array was modified (item added or removed):
RefreshDatabase$this->travelTo()Switch to adversarial mode. For each piece of logic, ask:
DB::transaction() needed?)increment() vs read-modify-save)Do NOT treat method calls as black boxes. When new code calls a method to retrieve records:
getActiveOrders() might include recently-completed orders, transitional states, or records within a grace window that make them stale for the new purpose->get() load the entire table when it should paginate/chunk?->pluck('key') on a collection with duplicate keys silently drops records — use ->keyBy() and validate cardinalityauth()->id() used in a queued job? (always null in queue context)uniqid() is microsecond-based and collision-prone under concurrent requests — use Str::uuid() or Str::ulid() instead>= correct vs >? Is the boundary tested?Classify every finding before reporting:
| Finding | Introduced or Pre-existing? | Verdict |
|---|---|---|
| N+1 in UserResource | Pre-existing (file not changed) | Note for backlog, don't block |
Missing trans() on new message | Introduced | Must fix |
Rules:
One paragraph: what the change does, overall quality assessment.
The table from Phase 2.
| # | Severity | File:Line | Finding | Action |
|---|---|---|---|---|
| 1 | CRITICAL | CarController.php:45 | $request->all() used | Must fix |
| 2 | HIGH | CarResource.php:12 | $this->owner without whenLoaded() | Must fix |
| 3 | MEDIUM | CarFilters.php:8 | Missing created_at in $allowedSorts | Should fix |
CRITICAL (block merge) — real production risk only:
$request->all() used anywhereresponse()->json() used directlyDB::transaction() on multi-table writeauth()->id() in queued job/listenerDB::table() used when Eloquent model exists (bypasses soft deletes)HIGH (must fix before merge):
trans())new ServiceClass() instead of DI->paginate() instead of ->dynamicPaginate()publicuniqid() used for identifiers (collision-prone)pluck() with duplicate key risk causing data lossMEDIUM (fix or note for backlog):
match$casts for booleans->format() instead of dateTimeFormat()Before rating severity, evaluate actual real-world impact:
Rule::exists() on array inputs: Laravel runs one query per item. For small fixed-size arrays (< 10 items) this is acceptable. For variable-size arrays, use a single whereIn()->count() check.exists:table,id is usually sufficient. Reserve complex closures for cases where invalid data causes actual harm.Key principles: