Writing Plans
Plans are not wishlists. A plan is a contract that, if followed, produces the feature. Validating a finished plan belongs to the plan-checker persona, not to this skill.
Before writing ops/TASKS.md: the incomplete-plan guard
ops/TASKS.md may already hold a plan for a different goal with open rows. Check before writing:
- Read
ops/TASKS.md. If it has unchecked[ ]or in-progress[-]rows whose goal differs from the current goal, do not overwrite it. - Attended: stop and ask the user whether to finish, wrap, or archive the old plan (the session-continuity skill's Wrap step moves it to
ops/archive/<date>/), or to fold its open rows into the new plan. - Unattended: rule and ledger it (
Ruling: archived <N> open rows of goal <X> to ops/archive/<date>/TASKS.md — superseded by <current goal>), then proceed. Never silently drop open work.
Structure
Every plan must contain:
1. Goal statement (1-2 sentences)
What the user wants and why. Not how.
2. Interface context extraction
Extract and embed the relevant types/interfaces directly in the plan. Agents reading this plan should NOT need to explore the codebase to find type definitions.
Key interfaces (from CONTRACTS.md):
- User: { id: string; email: string; role: 'admin' | 'member' }
- CreateUserRequest: { email: string; password: string; role?: string }
- UserResponse: Omit<User, 'passwordHash'>
3. Task decomposition
A task is right-sized when one builder can finish it in one lease without a mid-task decision the lead would weigh. If the builder would have to choose between designs, split the task at that decision or make the decision in the plan.
Each task must be:
- Atomic: right-sized as above (as a rule of thumb, 1-2 hours of focused work)
- Testable: has an observable pass condition AND a concrete failure condition
- Assigned: role specified from the roster (builder | reviewer | tester | analyst | documenter) —
resolve_rolemaps it to a CLI viaops/roster.toml - Scoped: files listed, dependencies explicit
Format:
- [ ] T1: [imperative verb] [what] [where]
Role: builder | reviewer | tester | analyst | documenter
Files: [specific file paths]
Depends: T0 | none
Precondition: [what must already be true before dispatch — a read-only check the lead can run]
Context: [what the agent needs to know — include relevant types]
Accept: [the observable acceptance — a command or observation and its expected result]
Fails when: [the concrete failure condition that proves Accept is not met]
Reversibility: one-way | checkpointed | reversible
Field rules:
- Accept: observable, and runnable wherever a command exists (
npm testexits 0 with the suite's totals quoted; the endpoint returns 201 with the user object). For documenter and analyst tasks, name the artifact and the sections it must contain. - Fails when: the concrete signal that proves the Accept is not met (
exit code nonzero or any failing test;any 5xx on the happy path). It is the falsifying direction of Accept, not a restatement. Placeholders such asTBD,N/A,none,unknown, or?are rejected by the plan-checker. - Precondition: a read-only check the lead runs before dispatch (
ops/CONTRACTS.mddefinesUser; branchfeat/xexists; the fixture directory is empty). Omit only when the task has no prerequisite state beyondDepends:. - Reversibility:
reversible(a VCS revert undoes it),checkpointed(undoable only from a checkpoint taken first: a backup, a tagged commit, a scratch copy; name the checkpoint inPrecondition:), orone-way(cannot be undone: data deletion, schema drop, external publish, a push to a shared branch). Aone-waytask is a hard stop in wave execution; it needs a user checkpoint in the plan, and the plan-checker flags one without it.
4. Shadow path tracing
For each task, enumerate what could go wrong:
| Task | Happy path | Shadow paths |
|---|---|---|
| T1: Create user endpoint | Returns 201 with user | Duplicate email → 409; invalid email format → 400; DB connection lost → 503; password too weak → 422 |
Every shadow path must have a handling status:
- Handled: implementation covers this
- ?: unknown — automatically becomes a subtask
5. Error/rescue map
For tasks involving external calls, DB operations, or async work:
| Operation | Failure mode | Handling |
|---|---|---|
| DB insert | Unique constraint violation | Return 409 Conflict |
| DB insert | Connection timeout | Retry 1x, then 503 |
| Email send | SMTP failure | Queue for retry, continue |
| API call | Rate limited | Exponential backoff, max 3 |
| API call | ? | Unknown — needs investigation |
Any "?" row becomes a subtask assigned to the investigating agent.
6. Dependency graph
T1 (types) ──→ T2 (implementation) ──→ T4 (tests)
──→ T5 (docs)
T3 (config) ──→ T2
Tasks with no dependency arrows between them can run in parallel.
7. Wave assignment (for parallelization)
Group tasks into waves:
Wave 1 (parallel): T1, T3 — no shared files
Wave 2 (parallel): T2a, T2b — shared dependency on T1, but different files
Wave 3 (sequential): T4 — depends on T2
Wave 4 (parallel): T5, T6 — independent
Output
Produce ops/TASKS.md containing:
- All sections above (goal, interfaces, tasks, shadow paths, error map, dependency graph, waves)
- Each task in the standardized format with Role, Files, Depends, Precondition, Context, Accept, Fails when, and Reversibility fields
- Wave assignments for the build phase
- The plan quality checklist (all items checked)
Plan quality checklist
Before finalizing: