xiaohongshu
xiaohongshu
code-review-wasp
Comprehensive code review skill for Wasp applications. Use when reviewing code, PRs, or architectural changes. Covers Wasp-specific patterns, import rules, auth checks, TDD compliance, multi-tenant permissions, and performance. Integrates all CLAUDE.md critical rules.
Full skill instructions
When to use this skill:
Key Focus Areas:
Review Philosophy:
This skill catches Wasp-specific issues that generic code review tools miss. It enforces the patterns documented in CLAUDE.md and your custom skills.
Use this checklist systematically for every code review:
Verify correct import syntax based on context:
// ✅ CORRECT - In .ts/.tsx files
import { HttpError } from "wasp/server";
import type { Task } from "wasp/entities";
import { useQuery } from "wasp/client/operations";
import { TaskStatus } from "@prisma/client"; // For enum VALUES
// ❌ WRONG - Common mistakes
import { Task } from "@wasp/entities"; // Wrong prefix
import { helper } from "@src/utils/helper"; // Wrong in .ts/.tsx
import { TaskStatus } from "wasp/entities"; // Wrong for enum values
// ✅ CORRECT - In main.wasp files
query getTasks {
fn: import { getTasks } from "@src/server/a3/operations",
entities: [Task]
}
// ❌ WRONG - Common mistakes
fn: import { getTasks } from "src/server/a3/operations", // Missing @
fn: import { getTasks } from "../src/server/a3/operations", // Relative path
Red Flags:
@wasp/ prefix instead of wasp/@src/ in .ts/.tsx files (should be relative paths)@src/)wasp/entities (should be @prisma/client)Cross-reference: See wasp-operations skill for complete import patterns
CRITICAL: Type annotations are REQUIRED for context.entities access
// ✅ CORRECT - Type annotation present
export const getTasks: GetTasks<void, Task[]> = async (args, context) => {
return context.entities.Task.findMany(); // Works!
};
// ❌ WRONG - No type annotation
export const getTasks = async (args, context) => {
return context.entities.Task.findMany(); // context.entities is undefined!
};
Check:
Get{Name}<Args, Return> type annotationCreate/Update/Delete{Name}<Args, Return> type annotationfrom "wasp/server/operations"Red Flags:
Cross-reference: See wasp-operations skill, lines 88-155
MANDATORY: Auth check must be FIRST line of every operation
// ✅ CORRECT - Auth check first
export const getTasks: GetTasks = async (args, context) => {
if (!context.user) throw new HttpError(401); // FIRST line
return context.entities.Task.findMany({ where: { userId: context.user.id } });
};
// ❌ WRONG - Auth check missing or late
export const getTasks: GetTasks = async (args, context) => {
const tasks = await context.entities.Task.findMany(); // NO AUTH CHECK!
return tasks;
};
Check:
if (!context.user) throw new HttpError(401)context.user (NOT custom logic)userId or check ownershipRed Flags:
context.userCross-reference: See wasp-operations skill, Section 3 (lines 120-220)
CRITICAL: Enforce correct error sequence
Order (MUST follow):
// ✅ CORRECT - Proper error sequence
export const updateTask: UpdateTask = async (args, context) => {
// 1. Auth check (401)
if (!context.user) throw new HttpError(401);
// 2. Fetch resource
const task = await context.entities.Task.findUnique({
where: { id: args.id },
});
// 3. Check existence (404)
if (!task) throw new HttpError(404, "Task not found");
// 4. Check permission (403)
if (task.userId !== context.user.id) {
throw new HttpError(403, "Not authorized");
}
// 5. Validate input (400)
if (!args.data.description?.trim()) {
throw new HttpError(400, "Description required");
}
// 6. Perform update
return context.entities.Task.update({
where: { id: args.id },
data: args.data,
});
};
// ❌ WRONG - Wrong error order
export const updateTask: UpdateTask = async (args, context) => {
// Checking permission before auth and existence - WRONG ORDER!
const task = await context.entities.Task.findUnique({
where: { id: args.id },
});
if (task.userId !== context.user.id) throw new HttpError(403);
// ... rest of checks
};
Check:
Red Flags:
Cross-reference: See error-handling skill for complete patterns
CRITICAL: Entities must be listed for context.entities access AND auto-invalidation
// ✅ CORRECT - Entities listed
query getTasks {
fn: import { getTasks } from "@src/server/a3/operations",
entities: [Task] // Enables context.entities.Task + auto-invalidation
}
action createTask {
fn: import { createTask } from "@src/server/a3/operations",
entities: [Task] // Same as getTasks → auto-invalidation works!
}
// ❌ WRONG - No entities
query getTasks {
fn: import { getTasks } from "@src/server/a3/operations",
// Missing entities: [] → context.entities undefined!
}
// ❌ WRONG - Different entities prevent auto-invalidation
action createTask {
entities: [Task, User] // Extra entity prevents auto-invalidation of getTasks
}
Check:
Red Flags:
entities: [] arrayCross-reference: See wasp-operations skill, lines 40-87
DEFAULT: Use direct async/await for actions, NOT useAction
// ✅ CORRECT - Direct call (default pattern)
const handleCreate = async (description: string) => {
try {
await createTask({ description }); // Simple + enables auto-invalidation
toast.success("Task created");
} catch (err) {
toast.error(err.message);
}
};
// ❌ WRONG - useAction by default
const createTaskFn = useAction(createTask);
const handleCreate = async (description: string) => {
try {
await createTaskFn({ description }); // Blocks auto-invalidation!
toast.success("Task created");
} catch (err) {
toast.error(err.message);
}
};
ONLY use useAction for optimistic UI updates (advanced):
// ✅ CORRECT - useAction ONLY for optimistic updates
const deleteTaskFn = useAction(deleteTask, {
optimisticUpdates: [
{
getQuerySpecifier: () => [getTasks],
updateQuery: (oldTasks, { id }) => oldTasks.filter((t) => t.id !== id),
},
],
});
Check:
await action(args) by defaultuseAction ONLY used with optimisticUpdates configwasp/client/operationsRed Flags:
useAction without optimistic updatesCross-reference: See CLAUDE.md, lines 275-304 (Client-Side Action Usage)
MANDATORY: Restart after schema.prisma or main.wasp changes
Files that require restart:
schema.prisma - Database schema changesmain.wasp - Routes, operations, entities changeswasp db migrate-devVerify:
# After changes, MUST restart with safe-start.sh (multi-worktree safe)
../scripts/safe-start.sh
# ❌ WRONG - Direct wasp start (not multi-worktree safe)
wasp start
Check:
../scripts/safe-start.sh (not direct wasp start)Red Flags:
wasp start (multi-worktree conflicts)Cross-reference: See wasp-database skill for complete workflow
MANDATORY: ≥80% statements, ≥75% branches
Check test quality using 5 MUST PASS criteria:
1. Tests Business Logic (NOT Existence):
// ❌ WRONG - Test theater
it("should exist", () => {
expect(createTask).toBeDefined(); // Meaningless!
});
// ✅ CORRECT - Tests behavior
it("should create task with description", async () => {
const result = await createTask({ description: "Test" }, mockContext);
expect(result.description).toBe("Test");
expect(mockContext.entities.Task.create).toHaveBeenCalledWith({
data: { description: "Test", userId: "user-1" },
});
});
2. Meaningful Assertions:
expect(result).toBeDefined() - Genericexpect(result.name).toBe('Acme') - Specific3. Tests Error Paths:
4. Tests Edge Cases:
5. Behavior NOT Implementation:
component.state.loading)screen.getByText('Loaded'))Check:
Red Flags:
Cross-reference: See tdd-workflow skill, lines 226-555
CRITICAL: ONLY use ShadCN v2.3.0
# ✅ CORRECT - Version locked
npx [email protected] add button
# ❌ WRONG - Uses latest (Tailwind v4, breaks Wasp)
npx shadcn add button
After EVERY component installation - Fix import path:
// In src/components/ui/{component}.tsx
-import { cn } from "s/lib/utils"
+import { cn } from "../../lib/utils"
Check:
[email protected] (NOT latest)../../lib/utilsRed Flags:
shadcn without version (uses latest)s/lib/utilsCross-reference: See shadcn-ui skill and CLAUDE.md lines 82-107
CRITICAL: Verify multi-tenant isolation
Architecture:
parentId)UserDepartmentPermission checks REQUIRED:
// ✅ CORRECT - Multi-tenant filtering
export const getTasks: GetTasks = async (args, context) => {
if (!context.user) throw new HttpError(401);
// Get user's departments
const userDepartments = await context.entities.UserDepartment.findMany({
where: { userId: context.user.id },
});
const departmentIds = userDepartments.map((ud) => ud.departmentId);
// Filter by departments user has access to
return context.entities.Task.findMany({
where: {
departmentId: { in: departmentIds },
},
});
};
// ❌ WRONG - No multi-tenant filtering
export const getTasks: GetTasks = async (args, context) => {
if (!context.user) throw new HttpError(401);
return context.entities.Task.findMany(); // Returns ALL tasks (data leak!)
};
Check:
Red Flags:
Cross-reference: See permissions skill for complete patterns
These issues are UNIQUE to Wasp - generic tools won't catch them:
Missing type annotations on operations
context.entities is undefined at runtimeGetQuery<Args, Return> type annotationAuth check not first line
if (!context.user) to first lineUsing @wasp/ prefix
wasp/ (no @)ShadCN version > 2.3.0
@2.3.0No restart after schema/main.wasp changes
../scripts/safe-start.shTest theater (5 criteria fail)
Using useAction by default
await action(args)Different entities in query + action
N+1 query problem
include for relationsNo multi-tenant filtering
Wrong error sequence
Direct wasp start instead of safe-start.sh
../scripts/safe-start.shSingle Responsibility:
Open/Closed:
Liskov Substitution:
Interface Segregation:
Dependency Inversion:
Extract common patterns:
// ✅ CORRECT - Extracted helper
function requireAuth(context: any) {
if (!context.user) throw new HttpError(401);
return context.user;
}
export const getTasks: GetTasks = async (args, context) => {
requireAuth(context); // Reused
return context.entities.Task.findMany();
};
// ❌ WRONG - Repeated auth check
export const getTasks: GetTasks = async (args, context) => {
if (!context.user) throw new HttpError(401); // Repeated
// ...
};
export const getTask: GetTask = async (args, context) => {
if (!context.user) throw new HttpError(401); // Repeated
// ...
};
Check:
Code should be easy to test:
CRITICAL: Never trust client
// ❌ WRONG - Client-side check only
if (user.role !== "ADMIN") return <div>Access denied</div>;
// Easy to bypass! Client-side is cosmetic only.
// ✅ CORRECT - Server-side enforcement
export const sensitiveOperation: Operation = async (args, context) => {
if (!context.user) throw new HttpError(401);
if (context.user.role !== "ADMIN") throw new HttpError(403);
// Safe - enforced server-side
};
Check:
Validate ALL user input:
// ✅ CORRECT - Validation
if (!args.description?.trim()) {
throw new HttpError(400, "Description required");
}
if (args.description.length > 500) {
throw new HttpError(400, "Description too long");
}
// ❌ WRONG - No validation
const task = await context.entities.Task.create({
data: { description: args.description }, // Accepts empty string, null, etc.
});
Check:
Use Prisma (auto-escapes):
// ✅ CORRECT - Prisma auto-escapes
const tasks = await context.entities.Task.findMany({
where: { status: args.status }, // Safe
});
// ❌ WRONG - Raw SQL (dangerous!)
const tasks = await context.entities.$queryRaw`
SELECT * FROM Task WHERE status = ${args.status}
`; // SQL injection risk!
Check:
CRITICAL: Avoid multiple queries in loop
// ❌ WRONG - N+1 queries
const tasks = await context.entities.Task.findMany();
for (const task of tasks) {
const user = await context.entities.User.findUnique({
where: { id: task.userId },
}); // N queries!
}
// ✅ CORRECT - Single query with include
const tasks = await context.entities.Task.findMany({
include: { user: true }, // 1 query with JOIN
});
Check:
includeRequired for large lists:
// ❌ WRONG - Loading all records
const tasks = await context.entities.Task.findMany(); // Could be millions!
// ✅ CORRECT - Paginated
const tasks = await context.entities.Task.findMany({
skip: page * pageSize,
take: pageSize,
orderBy: { createdAt: "desc" },
});
Check:
Memoization for expensive components:
// ✅ CORRECT - Memoized
const ExpensiveComponent = memo(({ data }) => {
const processed = useMemo(() => expensiveCalc(data), [data]);
return <div>{processed}</div>;
});
// ❌ WRONG - Re-renders every time
function ExpensiveComponent({ data }) {
const processed = expensiveCalc(data); // Runs on every render!
return <div>{processed}</div>;
}
Check:
memouseMemouseCallbackUse this template for structured feedback:
## Code Review Summary
**Overall:** [APPROVE / REQUEST CHANGES / BLOCK]
**Wasp-Specific Issues:** [count] found
**Security Issues:** [count] found
**Performance Issues:** [count] found
**Test Quality:** [PASS / FAIL - specify criteria]
---
## 🚨 Critical Issues (MUST FIX)
### Issue 1: [Title]
**Location:** `app/src/server/a3/operations.ts:45`
**Problem:**
Missing type annotation on operation. `context.entities` will be undefined at runtime.
**Current Code:**
```typescript
export const getTasks = async (args, context) => {
return context.entities.Task.findMany(); // FAILS
};
```
Fix:
export const getTasks: GetTasks<void, Task[]> = async (args, context) => {
return context.entities.Task.findMany(); // Works
};
Why: Type annotations are CRITICAL for Wasp operations to enable context.entities access.
Reference: wasp-operations skill, lines 88-155
[Same structure as Critical Issues]
../scripts/safe-start.sh after schema/main.wasp changes
---
## Summary
**This skill provides comprehensive Wasp-specific code review.**
**Key Features:**
- ✅ Catches Wasp-specific issues (generic tools miss these)
- ✅ Enforces all CLAUDE.md critical rules
- ✅ Integrates with existing skills (cross-references)
- ✅ Provides actionable feedback (not just "good/bad")
- ✅ Context-efficient (activates only when explicitly asked)
**When to activate:**
- Reviewing PRs before merge
- Pre-commit quality checks
- Architectural reviews
- Identifying framework-specific issues
**Expected outcome:**
Structured review with:
1. Critical issues (block PR)
2. Warnings (request changes)
3. What went well (positive feedback)
4. Actionable fixes (code examples)
5. Cross-references (related skills)
**For complete patterns:** See cross-referenced skills listed throughout this document.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience