Skip to content

Writing Effective Plans

How to write plan files that Ralph can execute successfully.

Basic Structure

A plan file is a markdown document describing what you want Ralph to build.

# [Type]: [Short Title]

## Goal
What you want to achieve in 1-2 sentences.

## Requirements
- Specific, actionable items
- Measurable acceptance criteria
- Clear scope boundaries

## Context (optional)
- Relevant files or patterns
- Constraints or preferences
- Technical decisions already made

Pro Tip

The better your plan, the better Ralph's output.

Plan Types

Bug Fix

# Fix: Login Button Not Responding

## Problem
The login button on /login page doesn't submit the form when clicked.

## Expected Behavior
Clicking login should POST to /api/auth/login and redirect to /dashboard on success.

## Reproduction Steps
1. Go to /login
2. Enter valid credentials
3. Click "Login" button
4. Nothing happens (no network request)

## Relevant Files
- src/pages/login.tsx
- src/components/LoginForm.tsx

New Feature

# Feature: User Profile Avatars

## Goal
Allow users to upload and display profile avatars.

## Requirements
- Upload endpoint accepting JPEG/PNG up to 5MB
- Resize images to 200x200 on upload
- Store in /uploads/avatars/{user_id}.jpg
- Display avatar in header and profile page
- Fallback to initials if no avatar

## Acceptance Criteria
- POST /api/users/avatar accepts image upload
- GET /api/users/{id}/avatar returns image or 404
- Profile page shows avatar or initials fallback
- Header shows small avatar thumbnail

## Technical Notes
- Use sharp for image resizing
- Follow existing upload pattern in src/lib/uploads.ts

Refactoring

# Refactor: Extract API Client

## Goal
Consolidate scattered fetch calls into a centralized API client.

## Current State
- fetch() calls duplicated across 15+ components
- No consistent error handling
- Auth headers added manually each time

## Target State
- Single ApiClient class in src/lib/api.ts
- Automatic auth header injection
- Consistent error handling with typed errors
- All components use ApiClient instead of raw fetch

## Constraints
- Don't change API response shapes
- Maintain existing error messages for users
- Keep all existing tests passing

Migration

# Migrate: React Router v5 to v6

## Goal
Upgrade from React Router v5 to v6 for improved performance.

## Scope
- Update react-router-dom to v6
- Migrate all route definitions
- Update all useHistory to useNavigate
- Update all <Switch> to <Routes>

## Out of Scope
- Adding new routes
- Changing URL structure
- Authentication flow changes

## Testing
- All existing routes must work identically
- Deep links must resolve correctly

Best Practices

Be Specific

Important

Vague plans produce vague results.

# Bad
## Requirements
- Make the app faster
- Improve the UI

# Good
## Requirements
- Reduce initial load time to under 2 seconds
- Add loading skeletons to dashboard cards

Define Acceptance Criteria

## Acceptance Criteria
- User can upload avatar via drag-and-drop or file picker
- Uploads over 5MB show error: "File too large (max 5MB)"
- Invalid file types show: "Please upload a JPEG or PNG"
- Avatar appears immediately after upload (no refresh)

Include Context

## Technical Context
- We use Prisma for database access (see prisma/schema.prisma)
- Auth is handled by NextAuth (see pages/api/auth/[...nextauth].ts)
- Follow the existing pattern in src/lib/users.ts

Set Boundaries

## In Scope
- User avatar upload
- Avatar display in header
- Avatar display on profile

## Out of Scope
- Avatar cropping UI
- Multiple avatar sizes
- Avatar deletion

Tips for Complex Plans

Break Into Phases

For large features, create multiple plan files:

plans/
├── auth-phase1-basic-login.md
├── auth-phase2-password-reset.md
├── auth-phase3-oauth.md
└── auth-phase4-mfa.md

Reference Existing Patterns

## Technical Notes
- Follow the validation pattern in src/lib/validators/user.ts
- Use the existing Modal component from src/components/Modal.tsx
- Error handling should match src/lib/errors.ts patterns

Provide Examples

## API Design

### Request
POST /api/avatars
Content-Type: multipart/form-data
file: <binary>

### Response (success)
{
  "url": "/uploads/avatars/123.jpg",
  "size": 45678
}

### Response (error)
{
  "error": "File too large",
  "maxSize": 5242880
}

Common Mistakes

Mistake Problem Fix
Too vague Ralph guesses wrong Add specific requirements
Too large Takes forever Break into smaller plans
No context Reinvents patterns Reference existing code
No criteria Can't verify completion Add acceptance criteria

Plan Directives

DO_NOT_COMMIT

Add DO_NOT_COMMIT on its own line in your plan to disable automatic commits:

# Feature: Experimental Widget

DO_NOT_COMMIT

## Goal
Try out a new widget without cluttering git history.

How it works

  • Must be on its own line (not inside a code block)
  • Case-sensitive: use exactly DO_NOT_COMMIT
  • Whitespace before/after is allowed
  • Takes effect for the entire plan execution

When to use:

Scenario Why
Experimental work Try ideas without commits
Large refactors Commit manually with a meaningful message
Learning/testing Explore Ralph without affecting repo

Template

Copy this to start a new plan:

# [Fix/Feature/Refactor]: [Title]

## Goal
[One sentence describing the objective]

## Current State
[What exists now, if relevant]

## Requirements
- [ ] Requirement 1
- [ ] Requirement 2
- [ ] Requirement 3

## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2

## Technical Notes
- [Relevant files, patterns, or constraints]

## Out of Scope
- [What this plan does NOT include]