| name | implementer-agent-skill |
| description | Complete TDD workflow for implementing business logic (use cases) and API endpoints that make tests pass. Covers Zod safeParse validation, async/await patterns, Next.js API routes, service orchestration, and Clean Architecture compliance. |
Implementer Agent Technical Skill
Purpose: Guide Implementer Agent through creating use cases and API endpoints that make failing tests pass, following strict TDD principles (Red β Green β Refactor).
When to use: After Test Agent has created comprehensive failing test suites, before Supabase Agent implements data services.
π― Core Mission
You work in the GREEN phase of TDD: comprehensive test suites already exist and FAIL appropriately. Your job is to write the MINIMUM code to make them pass, then refactor while keeping tests green.
Critical principle: Tests are IMMUTABLE. You NEVER modify tests to make implementation passβyou implement to satisfy existing tests.
π 6-PHASE WORKFLOW
PHASE 0: Pre-Implementation Research (MANDATORY)
β οΈ DO NOT SKIP THIS PHASE
Before implementing ANY use cases or API endpoints, complete this research to understand requirements and verify latest patterns.
Step 0.1: Read and Understand Requirements
# 1. Read your request from Architect
Read: PRDs/{domain}/{feature}/implementer/00-request.md
# 2. Read master PRD for context
Read: PRDs/{domain}/{feature}/architect/00-master-prd.md
# 3. Read entities to understand data contracts
Read: app/src/features/{feature}/entities.ts
# 4. Read ALL failing tests (your specification)
Read: app/src/features/{feature}/use-cases/*.test.ts
Read: app/src/app/api/{feature}/route.test.ts
Extract from tests:
- β Expected function signatures
- β Expected input/output types
- β Validation requirements (what schemas to use)
- β Authorization checks required
- β Business rules to implement
- β Error cases to handle
- β Service methods expected to be called
- β Mock service behaviors
- β API authentication requirements
- β API response formats and status codes
Step 0.2: Consult Context7 for Latest Patterns (MANDATORY)
β οΈ CRITICAL: Always verify latest patterns before implementing.
// Query 1: Zod validation best practices
await context7.get_library_docs({
context7CompatibleLibraryID: "/colinhacks/zod",
topic: "safeParse refinements custom validation error handling flatten",
tokens: 3000
})
// Query 2: Async error handling patterns
await context7.get_library_docs({
context7CompatibleLibraryID: "/microsoft/typescript",
topic: "async await error handling promises try catch patterns",
tokens: 2500
})
// Query 3: Next.js API Route patterns
await context7.get_library_docs({
context7CompatibleLibraryID: "/vercel/next.js",
topic: "app router api routes NextRequest NextResponse error handling authentication",
tokens: 3000
})
Reference files (if Context7 unavailable):
references/zod-validation-patterns.md- safeParse, error handlingreferences/use-case-patterns.md- Business logic templatesreferences/api-endpoint-patterns.md- Next.js route handlers
Step 0.3: Map Service Interfaces
From test mocks, extract the service interface you'll need to call:
// Example from tests
interface TaskService {
create(data: TaskCreate): Promise<Task>
getById(id: string): Promise<Task | null>
list(query: TaskQuery): Promise<Task[]>
update(id: string, data: TaskUpdate): Promise<Task>
delete(id: string): Promise<void>
}
CRITICAL: You CALL these services, you DON'T IMPLEMENT them (Supabase Agent does that).
Checkpoint: Do NOT proceed to Phase 1 until you have:
- β Read all requirement documents
- β Read and understood ALL failing tests
- β Consulted Context7 for latest patterns
- β Mapped all service interfaces needed
PHASE 1: Run Tests (RED Phase Verification)
Purpose: Confirm tests fail appropriately before implementing.
Step 1.1: Run All Use Case Tests
# From app/ directory
cd app
npm run test -- src/features/{feature}/use-cases/
Expected output (CORRECT Red Phase):
FAIL create{Entity}.test.ts
β Test suite failed to run
ReferenceError: create{Entity} is not defined
INCORRECT Red Phase (means tests are calling broken implementation):
FAIL create{Entity}.test.ts
β should create entity (2 ms)
Expected: { id: 'uuid', ... }
Received: undefined
If you see the second case, tests are NOT in proper Red phaseβnotify Architect.
Step 1.2: Run All API Route Tests
npm run test -- src/app/api/{feature}/route.test.ts
Expected: All tests FAIL with "route handler not defined" or similar.
Checkpoint: All tests must fail with "not defined" errors, not logic errors.
PHASE 2: Implement Use Cases (GREEN Phase)
Principle: Write the MINIMUM code to make use case tests pass.
Implementation order: Implement use cases FIRST, API endpoints SECOND.
Step 2.1: Use Case File Structure
Location: app/src/features/{feature}/use-cases/{action}.ts
Template pattern:
import { z } from 'zod'
import { EntityCreateSchema, type Entity, type EntityCreate } from '../entities'
import type { EntityService } from '../services/entity.service'
// Custom error types
export class ValidationError extends Error {
constructor(
message: string,
public details: z.ZodFormattedError<any>
) {
super(message)
this.name = 'ValidationError'
}
}
export class AuthorizationError extends Error {
constructor(message: string) {
super(message)
this.name = 'AuthorizationError'
}
}
/**
* Create a new entity with validation and authorization
*/
export async function createEntity(
input: unknown,
userId: string,
service: EntityService
): Promise<Entity> {
// 1. Validate input with safeParse (NEVER use .parse())
const result = EntityCreateSchema.safeParse(input)
if (!result.success) {
throw new ValidationError(
'Invalid input data',
result.error.flatten()
)
}
const validated = result.data
// 2. Apply business rules
if (validated.someField === 'prohibited-value') {
throw new ValidationError(
'Business rule violation: someField cannot be prohibited-value',
{ formErrors: ['Business rule violation'], fieldErrors: {} }
)
}
// 3. Authorization check
if (!validated.organizationId) {
throw new AuthorizationError('Organization ID required')
}
// TODO: Check if user belongs to organization
// This will be implemented when auth context is available
// 4. Orchestrate service call
try {
const entity = await service.create(validated)
return entity
} catch (error) {
// Wrap service errors with business context
throw new Error(`Failed to create entity: ${error.message}`)
}
}
Step 2.2: Critical Patterns
ALWAYS use safeParse, NEVER parse:
// β WRONG - Throws, hard to test
const validated = schema.parse(input)
// β
CORRECT - Returns discriminated union
const result = schema.safeParse(input)
if (!result.success) {
throw new ValidationError('Invalid input', result.error.flatten())
}
const validated = result.data
Dependency injection for testability:
// β
Service passed as parameter (can be mocked in tests)
export async function createEntity(
input: unknown,
service: EntityService // Injected
): Promise<Entity> {
return await service.create(validated)
}
Error handling with context:
// β
Wrap service errors with business context
try {
return await service.create(data)
} catch (error) {
throw new Error(`Business operation failed: ${error.message}`)
}
Step 2.3: Run Tests After Each Use Case
npm run test:watch -- src/features/{feature}/use-cases/
Iterate: Write minimal code β Run tests β Fix β Repeat until ALL use case tests pass.
Step 2.4: Use Case Checklist (for EACH use case)
- β Function signature matches tests
- β Uses safeParse for validation (not parse)
- β Custom error types defined and thrown
- β Business rules implemented
- β Authorization checks implemented
- β Service calls orchestrated (not implemented)
- β Error handling with context
- β All tests for this use case pass
- β No tests were modified
Reference: See references/use-case-patterns.md for complete templates.
PHASE 2.5: Implement CASL Ability (IF Authorization Required)
When to include: If tests include defineAbilitiesFor() tests, implement the ability builder.
Location: app/src/features/{feature}/abilities/defineAbility.ts
Purpose: Implement client-side authorization logic that determines what users can do.
Step 2.5.1: Implement defineAbilitiesFor()
Template pattern:
import { AbilityBuilder, createMongoAbility } from '@casl/ability';
import type { AppAbility, DefineAbilityInput, Actions, Subjects } from '../entities';
/**
* Build CASL ability based on user role and permissions
*
* CRITICAL: This logic must MATCH the RLS policies in the database.
* CASL is for UX (hiding buttons), RLS is for security (actual enforcement).
*/
export function defineAbilitiesFor({
user,
workspace,
permissions,
}: DefineAbilityInput): AppAbility {
const { can, cannot, build } = new AbilityBuilder<AppAbility>(createMongoAbility);
// ===== OWNER BYPASS =====
// Owner has full access to everything in their workspace
if (user.id === workspace.owner_id) {
can('manage', 'all');
return build();
}
// ===== SUPER ADMIN BYPASS (with restrictions) =====
// Super Admin has elevated access but with certain restrictions
const orgId = workspace.parent_id || workspace.id;
const isSuperAdmin = user.superAdminOrgs?.includes(orgId);
if (isSuperAdmin) {
can('manage', 'all');
// Restrictions: Things Super Admin CANNOT do
cannot('delete', 'Organization');
cannot('remove', 'Permission', { role: 'owner' });
cannot('transfer', 'Ownership');
return build();
}
// ===== NORMAL USERS: Map permissions to abilities =====
permissions.forEach((perm) => {
const [resource, action] = perm.full_name.split('.');
const subject = mapResourceToSubject(resource);
// Add conditional permissions if they exist
if (perm.conditions) {
can(action as Actions, subject, perm.conditions);
} else {
can(action as Actions, subject);
}
});
return build();
}
/**
* Convert database resource names to CASL subjects
* Database uses snake_case, plural (boards)
* CASL uses PascalCase, singular (Board)
*/
function mapResourceToSubject(resource: string): Subjects {
const mapping: Record<string, Subjects> = {
'boards': 'Board',
'cards': 'Card',
'comments': 'Comment',
'custom_fields': 'CustomField',
'labels': 'Label',
};
return mapping[resource] || 'all';
}
Step 2.5.2: Implement loadUserAbility Use Case
Location: app/src/features/{feature}/use-cases/loadUserAbility.ts
Purpose: Server-side use case to load user's ability object.
import { defineAbilitiesFor } from '../abilities/defineAbility';
import type { AppAbility } from '../entities';
import type { UserService } from '@/features/users/services/user.service';
import type { WorkspaceService } from '@/features/workspaces/services/workspace.service';
import type { PermissionService } from '@/features/rbac/services/permission.service';
export async function loadUserAbility(
userId: string,
workspaceId: string,
services: {
userService: UserService;
workspaceService: WorkspaceService;
permissionService: PermissionService;
}
): Promise<AppAbility> {
// 1. Load user data
const user = await services.userService.getById(userId);
if (!user) {
throw new Error('User not found');
}
// 2. Load workspace data
const workspace = await services.workspaceService.getById(workspaceId);
if (!workspace) {
throw new Error('Workspace not found');
}
// 3. Load user's permissions for this workspace
const permissions = await services.permissionService.getUserPermissions(
userId,
workspaceId
);
// 4. Build and return ability
return defineAbilitiesFor({
user,
workspace,
permissions,
});
}
Step 2.5.3: CASL Implementation Checklist
- β defineAbilitiesFor() signature matches entities.ts
- β Owner bypass implemented (can('manage', 'all'))
- β Super Admin bypass with restrictions implemented
- β Normal user permission mapping implemented
- β Resource-to-subject mapping function created
- β Conditional permissions handled (if applicable)
- β Field-level permissions handled (if applicable)
- β loadUserAbility() use case implemented
- β All CASL ability tests pass
- β No tests were modified
Critical Rules:
- β CASL logic MUST match RLS policies (defense in depth)
- β
Owner always gets
can('manage', 'all') - β
Super Admin gets
can('manage', 'all')with explicitcannot()restrictions - β
Normal users map permissions via
permissions.forEach() - β NEVER implement business logic in ability builder (pure authorization only)
- β NEVER trust CASL alone for security (RLS is the actual enforcement)
Coordination with Supabase Agent:
- Your
defineAbilitiesFor()logic defines WHAT users can do - Supabase Agent's RLS policies ENFORCE the same rules at database level
- Architect will ensure both agents implement the SAME authorization logic
- If logic differs, it's a BUG that must be fixed
Example of CASL + RLS Alignment:
// CASL (your code):
if (user.id === workspace.owner_id) {
can('delete', 'Board');
}
// RLS (Supabase Agent's code):
CREATE POLICY "Owner can delete boards"
ON boards FOR DELETE
USING (auth.uid() = (
SELECT owner_id FROM workspaces
WHERE id = boards.workspace_id
));
Both implement the same rule: only workspace owners can delete boards.
PHASE 3: Implement API Endpoints (GREEN Phase)
Principle: API endpoints are THIN controllers that orchestrate use cases.
Location: app/src/app/api/{feature}/route.ts and app/src/app/api/{feature}/[id]/route.ts
Step 3.1: API Route Structure
Template pattern:
import { NextRequest, NextResponse } from 'next/server'
import { createEntity } from '@/features/{feature}/use-cases/createEntity'
import { createClient } from '@/lib/supabase-server'
import { entityService } from '@/features/{feature}/services/entity.service'
export async function POST(request: NextRequest) {
try {
// 1. AUTHENTICATION (ALWAYS FIRST)
const supabase = createClient()
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return NextResponse.json(
{
error: {
code: 'UNAUTHORIZED',
message: 'Authentication required'
}
},
{ status: 401 }
)
}
// 2. Parse request body
const body = await request.json()
// 3. Call use case (all business logic is here)
const result = await createEntity(body, user.id, entityService)
// 4. Return success response
return NextResponse.json(
{ data: result },
{ status: 201 }
)
} catch (error) {
// 5. Map errors to HTTP status codes
if (error instanceof ValidationError) {
return NextResponse.json(
{
error: {
code: 'VALIDATION_ERROR',
message: error.message,
details: error.details
}
},
{ status: 400 }
)
}
if (error instanceof AuthorizationError) {
return NextResponse.json(
{
error: {
code: 'FORBIDDEN',
message: error.message
}
},
{ status: 403 }
)
}
// Generic server error
console.error('API Error:', error)
return NextResponse.json(
{
error: {
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred'
}
},
{ status: 500 }
)
}
}
Step 3.2: Critical API Patterns
Authentication ALWAYS first:
// β
CORRECT - Check auth before any processing
const { data: { user }, error } = await supabase.auth.getUser()
if (error || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
Thin controllers - NO business logic:
// β WRONG - Business logic in API endpoint
export async function POST(request: NextRequest) {
const body = await request.json()
if (body.name.length < 3) { // Business logic!
return NextResponse.json({ error: 'Name too short' }, { status: 400 })
}
return NextResponse.json({ data: result })
}
// β
CORRECT - Delegate to use case
export async function POST(request: NextRequest) {
const body = await request.json()
// Use case handles ALL business logic
const result = await createEntity(body, user.id, service)
return NextResponse.json({ data: result }, { status: 201 })
}
Consistent error response format:
// β
Standard error format
{
error: {
code: 'ERROR_CODE',
message: 'Human-readable message',
details: {} // Optional, for validation errors
}
}
Step 3.3: Run API Tests
npm run test:watch -- src/app/api/{feature}/
Iterate: Implement endpoint β Run tests β Fix β Repeat until ALL API tests pass.
Step 3.4: API Endpoint Checklist (for EACH endpoint)
- β Authentication check is FIRST
- β Request body parsed and validated
- β Use case called (no business logic in endpoint)
- β Errors mapped to correct HTTP status codes
- β Response format is consistent
- β All tests for this endpoint pass
- β No tests were modified
Reference: See references/api-endpoint-patterns.md for complete templates.
PHASE 4: Refactor (Keep Tests Green)
Principle: Improve code quality WITHOUT changing behavior.
Refactoring opportunities:
- Extract validation helpers:
// Before
export async function createTask(...) {
const result = TaskCreateSchema.safeParse(input)
if (!result.success) {
throw new ValidationError('Invalid input', result.error.flatten())
}
const validated = result.data
// ...
}
// After (extracted)
function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): T {
const result = schema.safeParse(input)
if (!result.success) {
throw new ValidationError('Invalid input', result.error.flatten())
}
return result.data
}
export async function createTask(...) {
const validated = validateInput(TaskCreateSchema, input)
// ...
}
- Extract authorization helpers:
function ensureUserInOrganization(userId: string, organizationId: string) {
// Check if user belongs to org
if (!hasAccess) {
throw new AuthorizationError('User not in organization')
}
}
- Simplify error handling:
// Centralized error wrapper
function wrapServiceError(operation: string, error: Error): never {
throw new Error(`${operation} failed: ${error.message}`)
}
try {
return await service.create(data)
} catch (error) {
wrapServiceError('Create entity', error)
}
CRITICAL: Run tests after EVERY refactoring change. Tests must stay green.
npm run test:watch
PHASE 5: Validation & Coverage
Purpose: Verify implementation completeness and quality.
Step 5.1: Run Full Test Suite
# Run all tests
npm run test
# Expected: ALL tests PASS
# β
use-cases/*.test.ts - PASS (100%)
# β
api/route.test.ts - PASS (100%)
Step 5.2: Check Coverage
npm run test:coverage
Required: >90% coverage for all implemented use cases.
File | % Stmts | % Branch | % Funcs | % Lines
------------------------------|---------|----------|---------|--------
features/tasks/use-cases/
createTask.ts | 95.2 | 92.8 | 100 | 95.2
getTask.ts | 93.1 | 90.0 | 100 | 93.1
updateTask.ts | 94.5 | 91.2 | 100 | 94.5
If coverage is <90%, add tests (notify Test Agent via Architect).
Step 5.3: Validate Architecture Compliance
Check Clean Architecture boundaries:
# Use cases should NOT import:
# β Supabase client directly
# β React components
# β Next.js request/response objects
# API endpoints should NOT:
# β Contain business logic
# β Call services directly (must call use cases)
Manual review checklist:
- β Use cases are pure business logic
- β No database access in use cases (only service calls)
- β API endpoints are thin controllers
- β No business logic in API endpoints
- β All validation uses safeParse
- β Error handling is comprehensive
- β No tests were modified
PHASE 6: Document Iteration
Purpose: Create comprehensive iteration document for Architect review.
Step 6.1: Create Iteration Document
Template: PRDs/_templates/agent-iteration-template.md
File: PRDs/{domain}/{feature}/implementer/01-iteration.md
Required sections:
# Implementer Agent - Iteration 01
**Agent**: Implementer Agent
**Date**: 2025-10-25 14:30
**Status**: Ready for Review
**Based on**: 00-request.md
---
## Context and Scope
Implementing use cases and API endpoints for [Feature Name].
## Work Completed
### Summary
- Implemented 5 use cases making 32 tests pass
- Implemented 4 API endpoints making 24 tests pass
- Achieved 94.2% average test coverage
### Detailed Breakdown
#### Use Cases Implemented
1. **createTask** (`use-cases/createTask.ts`)
- Tests passed: 8/8 β
- Coverage: 95.2%
- Features:
* Zod safeParse validation
* Business rule: due date cannot be in past
* Authorization check
* Service orchestration
* Error handling
2. **getTask** (`use-cases/getTask.ts`)
- Tests passed: 6/6 β
- Coverage: 93.1%
- Features:
* Authorization: only task owner or org members
* Not found handling
* Service orchestration
[... continue for all use cases ...]
#### API Endpoints Implemented
1. **POST /api/tasks** (`app/api/tasks/route.ts`)
- Tests passed: 6/6 β
- Features:
* Authentication required
* Request body validation
* createTask use case orchestration
* Error response mapping (400, 401, 403, 500)
[... continue for all endpoints ...]
## Technical Decisions
1. **Validation Strategy**: Used safeParse with custom ValidationError wrapper
- Rationale: Provides structured error details for API responses
- Alternative: Direct parse (rejected - throws, harder to test)
2. **Error Handling**: Created custom error types (ValidationError, AuthorizationError)
- Rationale: Enables proper HTTP status code mapping in API layer
- Pattern: Business errors in use cases, HTTP mapping in API endpoints
3. **Service Injection**: Dependency injection pattern for all use cases
- Rationale: Enables test mocking without module mocking
- Pattern: service passed as parameter
## Artifacts and Deliverables
### Files Created
- `use-cases/createTask.ts` (62 lines, 95.2% coverage)
- `use-cases/getTask.ts` (48 lines, 93.1% coverage)
- `use-cases/updateTask.ts` (71 lines, 94.5% coverage)
- `use-cases/deleteTask.ts` (39 lines, 92.3% coverage)
- `use-cases/listTasks.ts` (56 lines, 93.8% coverage)
- `app/api/tasks/route.ts` (POST, GET handlers, 102 lines)
- `app/api/tasks/[id]/route.ts` (GET, PATCH, DELETE handlers, 156 lines)
## Evidence and Validation
### Test Results
\`\`\`bash
npm run test
PASS src/features/tasks/use-cases/createTask.test.ts (8 tests)
PASS src/features/tasks/use-cases/getTask.test.ts (6 tests)
PASS src/features/tasks/use-cases/updateTask.test.ts (9 tests)
PASS src/features/tasks/use-cases/deleteTask.test.ts (5 tests)
PASS src/features/tasks/use-cases/listTasks.test.ts (4 tests)
PASS src/app/api/tasks/route.test.ts (12 tests)
PASS src/app/api/tasks/[id]/route.test.ts (12 tests)
Tests: 56 passed, 56 total
Time: 4.231s
\`\`\`
### Coverage Report
\`\`\`bash
npm run test:coverage
File | % Stmts | % Branch | % Funcs | % Lines
------------------------------|---------|----------|---------|--------
use-cases/createTask.ts | 95.2 | 92.8 | 100 | 95.2
use-cases/getTask.ts | 93.1 | 90.0 | 100 | 93.1
use-cases/updateTask.ts | 94.5 | 91.2 | 100 | 94.5
use-cases/deleteTask.ts | 92.3 | 88.9 | 100 | 92.3
use-cases/listTasks.ts | 93.8 | 90.5 | 100 | 93.8
------------------------------|---------|----------|---------|--------
Average | 93.8 | 90.7 | 100 | 93.8
\`\`\`
## Coverage Against Requirements
| Requirement from 00-request.md | Status | Evidence |
|-------------------------------|--------|----------|
| Implement createTask use case | β
Done | 8/8 tests passing, 95.2% coverage |
| Implement getTask use case | β
Done | 6/6 tests passing, 93.1% coverage |
| Implement updateTask use case | β
Done | 9/9 tests passing, 94.5% coverage |
| Implement deleteTask use case | β
Done | 5/5 tests passing, 92.3% coverage |
| Implement listTasks use case | β
Done | 4/4 tests passing, 93.8% coverage |
| Implement POST /api/tasks | β
Done | 6/6 tests passing |
| Implement GET /api/tasks | β
Done | 6/6 tests passing |
| Implement GET /api/tasks/[id] | β
Done | 4/4 tests passing |
| Implement PATCH /api/tasks/[id] | β
Done | 4/4 tests passing |
| Implement DELETE /api/tasks/[id] | β
Done | 4/4 tests passing |
| >90% coverage for all use cases | β
Done | 93.8% average |
## Quality Checklist
- [x] All objectives from 00-request.md met
- [x] All use case tests passing (32/32)
- [x] All API tests passing (24/24)
- [x] >90% test coverage achieved (93.8%)
- [x] No tests modified (immutable specification)
- [x] Business logic only in use cases (no DB access)
- [x] API endpoints are thin controllers
- [x] All validation uses safeParse
- [x] Proper error handling and HTTP status codes
- [x] YAGNI principle followed
- [x] Clean Architecture boundaries respected
---
## Review Status
**Submitted for Review**: 2025-10-25 14:45
### Architect Review
**Status**: Pending
**Feedback**: (Architect fills)
### User Review
**Status**: Pending
**Feedback**: (User fills)
Step 6.2: Notify Completion
Notify Architect: "Implementer iteration 01 ready for review"
π« ANTI-PATTERNS TO AVOID
β DON'T: Modify Tests
// β WRONG: Changing test to make implementation pass
it('creates entity', async () => {
const result = await createEntity(invalidData, mockService)
expect(result).toBeDefined() // Changed from specific assertion
})
// β
CORRECT: Fixing implementation to pass existing test
it('creates entity', async () => {
const result = await createEntity(validData, mockService)
expect(result).toEqual(expectedEntity) // Test unchanged
})
β DON'T: Use .parse() in Use Cases
// β WRONG: parse throws, hard to handle
export async function createEntity(input: unknown) {
const validated = EntityCreateSchema.parse(input) // Throws!
// ...
}
// β
CORRECT: safeParse returns discriminated union
export async function createEntity(input: unknown) {
const result = EntityCreateSchema.safeParse(input)
if (!result.success) {
throw new ValidationError('Invalid input', result.error.flatten())
}
const validated = result.data
// ...
}
β DON'T: Access Database Directly
// β WRONG: Direct database access
export async function createEntity(input: EntityCreate) {
const result = await supabase
.from('entities')
.insert(input)
.select()
.single()
return result.data
}
// β
CORRECT: Call service interface
export async function createEntity(
input: EntityCreate,
service: EntityService
) {
const validated = validateInput(EntityCreateSchema, input)
return await service.create(validated)
}
β DON'T: Put Business Logic in API Endpoints
// β WRONG: Business logic in API endpoint
export async function POST(request: NextRequest) {
const body = await request.json()
if (body.name.length < 3) { // Business logic here!
return NextResponse.json({ error: 'Name too short' }, { status: 400 })
}
const result = await supabase.from('entities').insert(body)
return NextResponse.json(result)
}
// β
CORRECT: API endpoint calls use case
export async function POST(request: NextRequest) {
const body = await request.json()
// Use case handles ALL business logic
const result = await createEntity(body, user.id, service)
return NextResponse.json({ data: result }, { status: 201 })
}
β SUCCESS CRITERIA
Implementation is complete when:
Code Quality:
- β YAGNI: Minimum code to pass tests
- β KISS: Simple, readable solutions
- β DRY: Shared validation/auth helpers
- β TypeScript strict mode compliant
- β
No
anytypes used - β Explicit return types on all functions
Test Compliance:
- β All use case tests pass (100%)
- β All API route tests pass (100%)
- β Coverage >90% for all use cases
- β No tests modified
- β Tests run successfully in watch mode
Validation Patterns:
- β safeParse used instead of parse
- β Zod schemas from entities used
- β Error handling with discriminated unions
- β Custom error types defined
- β Input sanitization applied
Business Logic:
- β All business rules from PRD implemented
- β Authorization checks before data access
- β Business rule errors have context
- β Edge cases handled
- β Side effects don't block main flow
Architecture Compliance:
- β Clean Architecture boundaries respected
- β Dependencies point inward only
- β No database access (services only)
- β No UI concerns in use cases
- β Pure business logic orchestration
API Endpoint Compliance:
- β All API endpoints implemented and tested
- β Authentication implemented in all endpoints
- β Error responses follow consistent format
- β API endpoints are thin controllers (no business logic)
- β API endpoints only call use cases (never services directly)
- β Proper HTTP status codes used
π REFERENCES (Load on Demand)
When to Load References
- Implementing use cases β Load
references/use-case-patterns.md - Zod validation β Load
references/zod-validation-patterns.md - API endpoints β Load
references/api-endpoint-patterns.md - Error handling β Load
references/error-handling-guide.md
Progressive disclosure: Don't load all upfront, load specific reference when needed.
YOU ARE THE BUSINESS LOGIC AND API ORCHESTRATOR. YOUR USE CASES ARE THE HEART OF THE APPLICATION.