feat: pre-compute wave numbers at plan time

Wave numbers now computed during plan-phase and stored in PLAN.md
frontmatter. Execute-phase reads wave directly instead of deriving
from depends_on at runtime.

- Add assign_waves step to plan-phase workflow
- Add wave field to frontmatter (plan-format, phase-prompt template)
- Simplify execute-phase: remove analyze_dependencies and group_into_waves
- Replace with group_by_wave that just reads frontmatter integers

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson
2026-01-13 18:10:20 -06:00
parent ef3a28003c
commit d30893a834
5 changed files with 96 additions and 86 deletions

View File

@@ -45,30 +45,25 @@ Phase: $ARGUMENTS
- Check which have *-SUMMARY.md (already complete)
- Build list of incomplete plans
3. **Analyze dependencies**
- Read each plan's `<context>` section
- Detect cross-references to other plans' outputs
- Build dependency graph
4. **Group into waves**
- Wave 1: Plans with no dependencies
- Wave N: Plans depending only on earlier waves
3. **Group by wave**
- Read `wave` from each plan's frontmatter
- Group plans by wave number
- Report wave structure to user
5. **Execute waves**
For each wave:
4. **Execute waves**
For each wave in order:
- Fill subagent-task-prompt template for each plan
- Spawn all agents in wave simultaneously (parallel Task calls)
- Wait for completion (Task blocks)
- Verify SUMMARYs created
- Proceed to next wave
6. **Aggregate results**
5. **Aggregate results**
- Collect summaries from all plans
- Report phase completion status
- Update ROADMAP.md
7. **Offer next steps**
6. **Offer next steps**
- More phases → `/gsd:plan-phase {next}`
- Milestone complete → `/gsd:complete-milestone`
</process>
@@ -89,22 +84,13 @@ All three run in parallel. Task tool blocks until all complete.
**No polling.** No background agents. No TaskOutput loops.
</wave_execution>
<checkpoint_detection>
Before adding a plan to a parallel wave, scan for checkpoints:
```bash
grep -c 'type="checkpoint' {plan_path}
```
**If checkpoints > 0:**
- Plan requires user interaction
- Execute in main context OR as solo subagent (not parallel)
- User interaction flows through normally
**If checkpoints = 0:**
- Fully autonomous
- Safe for parallel wave execution
</checkpoint_detection>
<checkpoint_handling>
Plans with `autonomous: false` in frontmatter have checkpoints:
- Run in their assigned wave (can be parallel with other plans)
- Pause at checkpoint, return to orchestrator
- Orchestrator presents checkpoint to user
- User responds, orchestrator resumes agent
</checkpoint_handling>
<deviation_rules>
During execution, handle discoveries automatically:

View File

@@ -18,6 +18,7 @@ Every PLAN.md starts with YAML frontmatter:
phase: XX-name
plan: NN
type: execute
wave: N # Execution wave (1, 2, 3...). Pre-computed at plan time.
depends_on: [] # Plan IDs this plan requires (e.g., ["01-01"])
files_modified: [] # Files this plan modifies
autonomous: true # false if plan has checkpoints
@@ -30,17 +31,15 @@ domain: [optional] # Domain skill if loaded
| `phase` | Yes | Phase identifier (e.g., `01-foundation`) |
| `plan` | Yes | Plan number within phase (e.g., `01`, `02`) |
| `type` | Yes | `execute` for standard plans, `tdd` for TDD plans |
| `depends_on` | Yes | Array of plan IDs this plan requires. **Empty = Wave 1 candidate** |
| `files_modified` | Yes | Files this plan touches. Used for conflict detection |
| `wave` | Yes | Execution wave number (1, 2, 3...). Pre-computed during planning. |
| `depends_on` | Yes | Array of plan IDs this plan requires. |
| `files_modified` | Yes | Files this plan touches. |
| `autonomous` | Yes | `true` if no checkpoints, `false` if has checkpoints |
| `domain` | No | Domain skill if loaded (e.g., `next-js`) |
**Wave assignment:** `/gsd:execute-phase` reads `depends_on` and `files_modified` to build execution waves:
- `depends_on: []` + no file conflicts → Wave 1 (parallel)
- `depends_on: ["XX-YY"]` → runs after plan XX-YY completes
- Shared `files_modified` → sequential by plan number
**Wave is pre-computed:** `/gsd:plan-phase` assigns wave numbers based on `depends_on`. `/gsd:execute-phase` reads `wave` directly from frontmatter and groups plans by wave number. No runtime dependency analysis needed.
**Checkpoint detection:** Plans with `autonomous: false` require user interaction. Execute after parallel wave or in main context.
**Checkpoint handling:** Plans with `autonomous: false` require user interaction. They run in their assigned wave but pause at checkpoints.
</frontmatter>
<prompt_structure>
@@ -51,6 +50,7 @@ Every PLAN.md follows this XML structure:
phase: XX-name
plan: NN
type: execute
wave: N
depends_on: []
files_modified: [path/to/file.ts]
autonomous: true

View File

@@ -13,8 +13,9 @@ Template for `.planning/phases/XX-name/{phase}-{plan}-PLAN.md` - executable phas
phase: XX-name
plan: NN
type: execute
depends_on: [] # Plan IDs this plan requires (e.g., ["01-01"]). Empty = parallel candidate.
files_modified: [] # Files this plan modifies. Used for conflict detection.
wave: N # Execution wave (1, 2, 3...). Pre-computed at plan time.
depends_on: [] # Plan IDs this plan requires (e.g., ["01-01"]).
files_modified: [] # Files this plan modifies.
autonomous: true # false if plan has checkpoints requiring user interaction
domain: [optional - if domain skill loaded]
---
@@ -125,12 +126,13 @@ After completion, create `.planning/phases/XX-name/{phase}-{plan}-SUMMARY.md`
| `phase` | Yes | Phase identifier (e.g., `01-foundation`) |
| `plan` | Yes | Plan number within phase (e.g., `01`, `02`) |
| `type` | Yes | Always `execute` for standard plans, `tdd` for TDD plans |
| `depends_on` | Yes | Array of plan IDs this plan requires. **Empty = Wave 1 candidate** |
| `files_modified` | Yes | Files this plan touches. Used for conflict detection |
| `wave` | Yes | Execution wave number (1, 2, 3...). Pre-computed at plan time. |
| `depends_on` | Yes | Array of plan IDs this plan requires. |
| `files_modified` | Yes | Files this plan touches. |
| `autonomous` | Yes | `true` if no checkpoints, `false` if has checkpoints |
| `domain` | No | Domain skill if loaded (e.g., `next-js`) |
**Wave assignment:** `/gsd:execute-phase` reads `depends_on` to build execution waves. Plans with `depends_on: []` and no file conflicts with sibling plans run in Wave 1 (parallel).
**Wave is pre-computed:** Wave numbers are assigned during `/gsd:plan-phase`. Execute-phase reads `wave` directly from frontmatter and groups plans by wave number. No runtime dependency analysis needed.
---
@@ -142,16 +144,19 @@ After completion, create `.planning/phases/XX-name/{phase}-{plan}-SUMMARY.md`
```yaml
# Plan 01 - User feature
wave: 1
depends_on: []
files_modified: [src/models/user.ts, src/api/users.ts]
autonomous: true
# Plan 02 - Product feature (no overlap with Plan 01)
wave: 1
depends_on: []
files_modified: [src/models/product.ts, src/api/products.ts]
autonomous: true
# Plan 03 - Order feature (no overlap)
wave: 1
depends_on: []
files_modified: [src/models/order.ts, src/api/orders.ts]
autonomous: true
@@ -163,28 +168,31 @@ All three run in parallel (Wave 1) - no dependencies, no file conflicts.
```yaml
# Plan 01 - Auth foundation
wave: 1
depends_on: []
files_modified: [src/lib/auth.ts, src/middleware/auth.ts]
autonomous: true
# Plan 02 - Protected features (needs auth)
wave: 2
depends_on: ["01"]
files_modified: [src/features/dashboard.ts]
autonomous: true
```
Plan 02 waits for Plan 01 - genuine dependency on auth types/middleware.
Plan 02 in Wave 2 waits for Plan 01 in Wave 1 - genuine dependency on auth types/middleware.
**Checkpoint plan:**
```yaml
# Plan 03 - UI with verification
wave: 3
depends_on: ["01", "02"]
files_modified: [src/components/Dashboard.tsx]
autonomous: false # Has checkpoint:human-verify
```
Runs after Wave 1, pauses at checkpoint, orchestrator presents to user, resumes on approval.
Wave 3 runs after Waves 1 and 2. Pauses at checkpoint, orchestrator presents to user, resumes on approval.
</parallel_examples>
@@ -289,6 +297,7 @@ See `~/.claude/get-shit-done/references/tdd.md` for TDD plan structure.
phase: 03-features
plan: 01
type: execute
wave: 1
depends_on: []
files_modified: [src/features/user/model.ts, src/features/user/api.ts, src/features/user/UserList.tsx]
autonomous: true
@@ -347,6 +356,7 @@ After completion, create `.planning/phases/03-features/03-01-SUMMARY.md`
phase: 03-features
plan: 03
type: execute
wave: 2
depends_on: ["03-01", "03-02"]
files_modified: [src/components/Dashboard.tsx]
autonomous: false
@@ -446,7 +456,7 @@ files_modified: [...]
## Guidelines
- Always use XML structure for Claude parsing
- Include `depends_on`, `files_modified`, `autonomous` in every plan
- Include `wave`, `depends_on`, `files_modified`, `autonomous` in every plan
- Prefer vertical slices over horizontal layers
- Only reference prior SUMMARYs when genuinely needed
- Group checkpoints with related auto tasks in same plan

View File

@@ -68,68 +68,50 @@ ls -1 "$PHASE_DIR"/*-SUMMARY.md 2>/dev/null | sort
```
For each plan, read frontmatter to extract:
- `depends_on: []` - Plan IDs this plan requires
- `files_modified: []` - Files this plan touches
- `wave: N` - Execution wave (pre-computed)
- `autonomous: true/false` - Whether plan has checkpoints
Build plan inventory:
- Plan path
- Plan ID (e.g., "03-01")
- Dependencies
- Files modified
- Wave number
- Autonomous flag
- Completion status (SUMMARY exists = complete)
Skip completed plans. If all complete, report "Phase already executed" and exit.
</step>
<step name="analyze_dependencies">
Build dependency graph from frontmatter:
<step name="group_by_wave">
Read `wave` from each plan's frontmatter and group by wave number:
**Direct dependencies:**
- `depends_on: ["03-01"]` → this plan depends on 03-01
**File conflict dependencies:**
- If two plans modify same file, later plan (by number) depends on earlier
**Build graph:**
```bash
# For each plan, extract wave from frontmatter
for plan in $PHASE_DIR/*-PLAN.md; do
wave=$(grep "^wave:" "$plan" | cut -d: -f2 | tr -d ' ')
autonomous=$(grep "^autonomous:" "$plan" | cut -d: -f2 | tr -d ' ')
echo "$plan:$wave:$autonomous"
done
```
plan-01: {deps: [], files: [src/user.ts], autonomous: true}
plan-02: {deps: [], files: [src/product.ts], autonomous: true}
plan-03: {deps: ["plan-01"], files: [src/dashboard.tsx], autonomous: false}
plan-04: {deps: ["plan-02"], files: [src/cart.ts], autonomous: true}
plan-05: {deps: ["plan-03", "plan-04"], files: [src/checkout.ts], autonomous: true}
**Group plans:**
```
</step>
<step name="group_into_waves">
Group plans into execution waves based on dependencies:
**Wave assignment algorithm:**
1. Wave 1: All plans with no dependencies AND autonomous=true
2. Non-autonomous plans with no dependencies: execute after Wave 1, before Wave 2
3. Wave N: Plans whose dependencies are all in earlier waves
**Separate checkpoint plans:**
Plans with `autonomous: false` execute in main context (not parallel subagent) to handle user interaction.
**Example:**
```
Wave 1 (parallel): [plan-01, plan-02]
Checkpoint: [plan-03] - has human-verify, runs in main context
Wave 2 (parallel): [plan-04]
Wave 3: [plan-05]
waves = {
1: [plan-01, plan-02],
2: [plan-03, plan-04],
3: [plan-05]
}
```
**No dependency analysis needed.** Wave numbers are pre-computed during `/gsd:plan-phase`.
Report wave structure to user:
```
Execution Plan:
Wave 1 (parallel): 03-01, 03-02
Checkpoint: 03-03 (requires user verification)
Wave 2 (parallel): 03-04
Wave 2 (parallel): 03-03 [checkpoint], 03-04
Wave 3: 03-05
Total: 5 plans in 3 waves + 1 checkpoint
Total: 5 plans in 3 waves
```
</step>

View File

@@ -367,6 +367,37 @@ For each plan, determine:
- `autonomous: true|false` - has checkpoints requiring user interaction?
</step>
<step name="assign_waves">
**Compute wave numbers before writing plans.**
Wave assignment algorithm (run in memory before writing any files):
```
waves = {} # plan_id -> wave_number
for each plan in plan_order:
if plan.depends_on is empty:
plan.wave = 1
else:
# Wave = max wave of dependencies + 1
plan.wave = max(waves[dep] for dep in plan.depends_on) + 1
waves[plan.id] = plan.wave
```
**Example:**
```
Plan 01: depends_on: [] → wave: 1
Plan 02: depends_on: [] → wave: 1
Plan 03: depends_on: ["01"] → wave: 2
Plan 04: depends_on: ["02"] → wave: 2
Plan 05: depends_on: ["03", "04"] → wave: 3
```
Store wave number with each plan in memory. Write to frontmatter in next step.
</step>
<step name="group_into_plans">
**Group tasks into plans based on dependency waves and autonomy.**
@@ -521,14 +552,15 @@ Each plan follows template structure with:
phase: XX-name
plan: NN
type: execute
depends_on: [] # Plan IDs this plan requires. Empty = Wave 1 candidate.
files_modified: [] # Files this plan touches. Used for conflict detection.
wave: N # Execution wave (1, 2, 3...). Computed at plan time.
depends_on: [] # Plan IDs this plan requires.
files_modified: [] # Files this plan touches.
autonomous: true # false if plan has checkpoints requiring user interaction
domain: [optional]
---
```
**Wave assignment is automatic:** `/gsd:execute-phase` reads `depends_on` to build waves. Plans with empty `depends_on` and no file conflicts run in Wave 1 (parallel).
**Wave is pre-computed:** Wave numbers are assigned during planning (see `assign_waves` step). `/gsd:execute-phase` reads `wave` directly from frontmatter and groups plans by wave number. No runtime dependency analysis needed.
**Context section - parallel-aware:**