Chapter 5

Writing Plans That Work

Here's what works and here's what breaks.

Plan Structure

Every hardened plan has these mandatory sections. The plan-hardener agent adds them automatically during Step 2, but you should understand what each does and how to edit them:

SectionRequired?Purpose
Scope ContractYesIn-scope paths, out-of-scope, forbidden actions
MUST CriteriaYesNon-negotiable outcomes (checkboxes)
SHOULD CriteriaOptionalBest-effort goals
Execution SlicesYesCheckpointed work chunks with gates
Branch StrategyRecommendedGit branch name and merge approach
Rollback PlanRecommendedHow to undo if things go wrong

Writing a Good Scope Contract

The scope contract is the most important section. It tells the AI exactly what files it can touch — and what's off-limits.

Good: Tight Scope

✅ Clear boundaries
## Scope Contract
**In Scope**: src/services/UserService.cs, src/repositories/UserRepository.cs,
              tests/services/UserServiceTests.cs, tests/repositories/UserRepositoryTests.cs
**Out of Scope**: frontend/**, deployment/**, docs/** (except this plan)
**Forbidden Actions**:
- Do NOT modify src/database/migrations/ (migration is a separate phase)
- Do NOT change AppSettings.json connection strings
- Do NOT add NuGet packages without explicit approval

Bad: Loose Scope

❌ Too vague
## Scope Contract
**In Scope**: anything related to users
**Out of Scope**: nothing specific
**Forbidden Actions**: don't break things

"Anything related to users" gives the AI free rein to refactor 20 files. "Don't break things" isn't enforceable. Be specific about paths, and list forbidden actions as concrete file patterns. That's how you get lasagna code — clean layers, each with a purpose — instead of spaghetti where everything touches everything.

Slicing Strategy

Slices are 30–120 minute chunks of work. Each slice should produce a commit-worthy change — the "one PR" rule.

Rules of Thumb

  • One layer per slice — don't mix database migration with API controller in the same slice
  • Build on foundations — create the model/migration first, then the repository, then the service, then the controller
  • Tests with the code — include tests in the same slice as the code they test (not a separate "add tests" slice at the end)
  • 30 minutes minimum — slices shorter than this have too much gate overhead
  • 120 minutes maximum — slices longer than this accumulate too much risk before the next checkpoint

Example: 6-Slice Plan

Layer-by-layer slicing
Slice 1 — Database migration + model           [30 min]
Slice 2 — Repository + unit tests               [45 min]
Slice 3 — Service layer + business logic tests   [60 min]
Slice 4 — API controller + integration tests     [45 min]
Slice 5 — Error handling + edge case tests       [30 min]
Slice 6 — Documentation + cleanup                [30 min]

Validation Gates

Gates are the quality checkpoints between slices. A gate must be a concrete, executable command — not a human judgment call.

Good Gates

✅ Executable and specific
**Gate**:
  dotnet build                              # zero errors
  dotnet test --filter "UserProfile"        # 6+ tests pass
  grep -rn "string interpolation" src/      # zero hits (security)

Bad Gates

❌ Vague or unenforceable
**Gate**: "tests pass"           ← Which tests? How many?
**Gate**: "code looks clean"     ← Not executable
**Gate**: "review the changes"   ← Human-dependent, blocks automation

Parallel Execution

Mark slices that can run concurrently with the [P] tag. Add dependency declarations when slices must run in order:

Parallel slices with dependencies
### Slice 1 — Database Migration [30 min]
...

### Slice 2 — Repository Layer [P] [depends: Slice 1] [scope: src/repos/**]
...

### Slice 3 — Service Layer [P] [depends: Slice 1] [scope: src/services/**]
...

### Slice 4 — API Controller [depends: Slice 2, Slice 3]
...

Slices 2 and 3 both depend on Slice 1 (the migration) but are independent of each other — they run in parallel. Slice 4 waits for both to finish. The orchestrator builds a DAG (directed acyclic graph) and schedules accordingly.

DAG diagram showing parallel slices 2 and 3 executing concurrently after slice 1, then converging on slice 4
When NOT to parallelize: If two slices modify the same files, they'll conflict. Only use [P] when slices touch different [scope: ...] paths.

Stop Conditions

Stop conditions tell the AI when to halt instead of trying to work around a failure:

Good stop conditions
**Stop if**: Build fails with compilation error
**Stop if**: Any existing test regresses (not just new tests)
**Stop if**: Migration produces data loss warning
**Stop if**: Security scan finds HIGH or CRITICAL vulnerability

Without stop conditions, the AI may try to "fix" a build failure by removing code, or skip a failing test by commenting it out. Stop conditions force it to report the problem instead of hiding it.

Context Files

Each slice can list which instruction files are relevant. Don't load all 18 — load only what's needed:

Targeted context loading
### Slice 1 — Database Migration
**Context**: database.instructions.md, security.instructions.md

### Slice 4 — API Controller
**Context**: api-patterns.instructions.md, auth.instructions.md, errorhandling.instructions.md

This keeps the AI's context window focused. A database slice doesn't need caching instructions; a controller slice doesn't need migration patterns.

Common Mistakes

MistakeWhat HappensFix
Scope too loose AI refactors 20 files instead of 3 List specific file paths, not categories
Scope too tight AI can't create necessary helper files Include reasonable wildcards: src/services/**
No stop conditions AI works around failures silently Add "Stop if" to every slice
Vague gates Gate "passes" without actually validating Use executable commands with expected counts
Tests in last slice 5 slices of code, then discover it's untestable Include tests alongside each code slice
Giant slices 120+ min of work before first checkpoint Break into 30–60 min focused chunks
Missing rollback Panic when something breaks in production Add rollback plan with specific git revert commands

Plan Templates

Eight language-specific plan examples ship with Plan Forge. Use them as starting points:

StackFileFeatures Demonstrated
.NETPhase-DOTNET-EXAMPLE.mdRLS, Dapper, Blazor, GraphQL, 12 slices
TypeScriptPhase-TYPESCRIPT-EXAMPLE.mdExpress, Prisma, Vitest
PythonPhase-PYTHON-EXAMPLE.mdFastAPI, SQLAlchemy, Pytest
JavaPhase-JAVA-EXAMPLE.mdSpring Boot, JPA, JUnit
GoPhase-GO-EXAMPLE.mdChi router, sqlx, testing
SwiftPhase-SWIFT-EXAMPLE.mdVapor, Fluent, XCTest
RustPhase-RUST-EXAMPLE.mdAxum, sqlx, Cargo test
PHPPhase-PHP-EXAMPLE.mdLaravel, Eloquent, PHPUnit

All examples live in docs/plans/examples/.

📄 Full reference: AI-Plan-Hardening-Runbook.md