feat(execute-phase): support wave-specific execution
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: gsd:execute-phase
|
||||
description: Execute all plans in a phase with wave-based parallelization
|
||||
argument-hint: "<phase-number> [--gaps-only]"
|
||||
argument-hint: "<phase-number> [--wave N] [--gaps-only]"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Write
|
||||
@@ -18,6 +18,10 @@ Execute all plans in a phase using wave-based parallel execution.
|
||||
|
||||
Orchestrator stays lean: discover plans, analyze dependencies, group into waves, spawn subagents, collect results. Each subagent loads the full execute-plan context and handles its own plan.
|
||||
|
||||
Optional wave filter:
|
||||
- `--wave N` executes only Wave `N` for pacing, quota management, or staged rollout
|
||||
- phase verification/completion still only happens when no incomplete plans remain after the selected wave finishes
|
||||
|
||||
Context budget: ~15% orchestrator, 100% fresh per subagent.
|
||||
</objective>
|
||||
|
||||
@@ -30,6 +34,7 @@ Context budget: ~15% orchestrator, 100% fresh per subagent.
|
||||
Phase: $ARGUMENTS
|
||||
|
||||
**Flags:**
|
||||
- `--wave N` — Execute only Wave `N` in the phase. Use when you want to pace execution or stay inside usage limits.
|
||||
- `--gaps-only` — Execute only gap closure plans (plans with `gap_closure: true` in frontmatter). Use after verify-work creates fix plans.
|
||||
|
||||
Context files are resolved inside the workflow via `gsd-tools init execute-phase` and per-subagent `<files_to_read>` blocks.
|
||||
|
||||
@@ -100,17 +100,19 @@ Research, plan, and verify a phase.
|
||||
|
||||
### `/gsd:execute-phase`
|
||||
|
||||
Execute all plans in a phase with wave-based parallelization.
|
||||
Execute all plans in a phase with wave-based parallelization, or run a specific wave.
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `N` | **Yes** | Phase number to execute |
|
||||
| `--wave N` | No | Execute only Wave `N` in the phase |
|
||||
|
||||
**Prerequisites:** Phase has PLAN.md files
|
||||
**Produces:** `{phase}-{N}-SUMMARY.md`, `{phase}-VERIFICATION.md`, git commits
|
||||
**Produces:** per-plan `{phase}-{N}-SUMMARY.md`, git commits, and `{phase}-VERIFICATION.md` when the phase is fully complete
|
||||
|
||||
```bash
|
||||
/gsd:execute-phase 1 # Execute phase 1
|
||||
/gsd:execute-phase 1 --wave 2 # Execute only Wave 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -12,6 +12,16 @@ Read STATE.md before any operation to load project context.
|
||||
|
||||
<process>
|
||||
|
||||
<step name="parse_args" priority="first">
|
||||
Parse `$ARGUMENTS` before loading any context:
|
||||
|
||||
- First positional token → `PHASE_ARG`
|
||||
- Optional `--wave N` → `WAVE_FILTER`
|
||||
- Optional `--gaps-only` keeps its current meaning
|
||||
|
||||
If `--wave` is absent, preserve the current behavior of executing all incomplete waves in the phase.
|
||||
</step>
|
||||
|
||||
<step name="initialize" priority="first">
|
||||
Load all context in one call:
|
||||
|
||||
@@ -71,13 +81,19 @@ PLAN_INDEX=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase-plan-ind
|
||||
|
||||
Parse JSON for: `phase`, `plans[]` (each with `id`, `wave`, `autonomous`, `objective`, `files_modified`, `task_count`, `has_summary`), `waves` (map of wave number → plan IDs), `incomplete`, `has_checkpoints`.
|
||||
|
||||
**Filtering:** Skip plans where `has_summary: true`. If `--gaps-only`: also skip non-gap_closure plans. If all filtered: "No matching incomplete plans" → exit.
|
||||
**Filtering:** Skip plans where `has_summary: true`. If `--gaps-only`: also skip non-gap_closure plans. If `WAVE_FILTER` is set: also skip plans whose `wave` does not equal `WAVE_FILTER`.
|
||||
|
||||
**Wave safety check:** If `WAVE_FILTER` is set and there are still incomplete plans in any lower wave that match the current execution mode, STOP and tell the user to finish earlier waves first. Do not let Wave 2+ execute while prerequisite earlier-wave plans remain incomplete.
|
||||
|
||||
If all filtered: "No matching incomplete plans" → exit.
|
||||
|
||||
Report:
|
||||
```
|
||||
## Execution Plan
|
||||
|
||||
**Phase {X}: {Name}** — {total_plans} plans across {wave_count} waves
|
||||
**Phase {X}: {Name}** — {total_plans} matching plans across {wave_count} wave(s)
|
||||
|
||||
{If WAVE_FILTER is set: `Wave filter active: executing only Wave {WAVE_FILTER}`.}
|
||||
|
||||
| Wave | Plans | What it builds |
|
||||
|------|-------|----------------|
|
||||
@@ -87,7 +103,7 @@ Report:
|
||||
</step>
|
||||
|
||||
<step name="execute_waves">
|
||||
Execute each wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`, sequential if `false`.
|
||||
Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`, sequential if `false`.
|
||||
|
||||
**For each wave:**
|
||||
|
||||
@@ -278,6 +294,37 @@ After all waves:
|
||||
```
|
||||
</step>
|
||||
|
||||
<step name="handle_partial_wave_execution">
|
||||
If `WAVE_FILTER` was used, re-run plan discovery after execution:
|
||||
|
||||
```bash
|
||||
POST_PLAN_INDEX=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase-plan-index "${PHASE_NUMBER}")
|
||||
```
|
||||
|
||||
Apply the same "incomplete" filtering rules as earlier:
|
||||
- ignore plans with `has_summary: true`
|
||||
- if `--gaps-only`, only consider `gap_closure: true` plans
|
||||
|
||||
**If incomplete plans still remain anywhere in the phase:**
|
||||
- STOP here
|
||||
- Do NOT run phase verification
|
||||
- Do NOT mark the phase complete in ROADMAP/STATE
|
||||
- Present:
|
||||
|
||||
```markdown
|
||||
## Wave {WAVE_FILTER} Complete
|
||||
|
||||
Selected wave finished successfully. This phase still has incomplete plans, so phase-level verification and completion were intentionally skipped.
|
||||
|
||||
/gsd:execute-phase {phase} # Continue remaining waves
|
||||
/gsd:execute-phase {phase} --wave {next} # Run the next wave explicitly
|
||||
```
|
||||
|
||||
**If no incomplete plans remain after the selected wave finishes:**
|
||||
- continue with the normal phase-level verification and completion flow below
|
||||
- this means the selected wave happened to be the last remaining work in the phase
|
||||
</step>
|
||||
|
||||
<step name="close_parent_artifacts">
|
||||
**For decimal/polish phases only (X.Y pattern):** Close the feedback loop by resolving parent UAT and debug artifacts.
|
||||
|
||||
|
||||
@@ -107,14 +107,16 @@ Result: Creates `.planning/phases/01-foundation/01-01-PLAN.md`
|
||||
### Execution
|
||||
|
||||
**`/gsd:execute-phase <phase-number>`**
|
||||
Execute all plans in a phase.
|
||||
Execute all plans in a phase, or run a specific wave.
|
||||
|
||||
- Groups plans by wave (from frontmatter), executes waves sequentially
|
||||
- Plans within each wave run in parallel via Task tool
|
||||
- Optional `--wave N` flag executes only Wave `N` and stops unless the phase is now fully complete
|
||||
- Verifies phase goal after all plans complete
|
||||
- Updates REQUIREMENTS.md, ROADMAP.md, STATE.md
|
||||
|
||||
Usage: `/gsd:execute-phase 5`
|
||||
Usage: `/gsd:execute-phase 5 --wave 2`
|
||||
|
||||
### Smart Router
|
||||
|
||||
|
||||
107
tests/execute-phase-wave.test.cjs
Normal file
107
tests/execute-phase-wave.test.cjs
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Execute-phase wave filter tests
|
||||
*
|
||||
* Validates the /gsd:execute-phase --wave feature contract:
|
||||
* - Command frontmatter advertises --wave
|
||||
* - Workflow parses WAVE_FILTER
|
||||
* - Workflow enforces lower-wave safety
|
||||
* - Partial wave runs do not mark the phase complete
|
||||
*/
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const COMMAND_PATH = path.join(__dirname, '..', 'commands', 'gsd', 'execute-phase.md');
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
|
||||
const COMMANDS_DOC_PATH = path.join(__dirname, '..', 'docs', 'COMMANDS.md');
|
||||
const HELP_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'help.md');
|
||||
|
||||
describe('execute-phase command: --wave flag', () => {
|
||||
test('command file exists', () => {
|
||||
assert.ok(fs.existsSync(COMMAND_PATH), 'commands/gsd/execute-phase.md should exist');
|
||||
});
|
||||
|
||||
test('argument-hint includes --wave and --gaps-only', () => {
|
||||
const content = fs.readFileSync(COMMAND_PATH, 'utf-8');
|
||||
const hintLine = content.split('\n').find(l => l.includes('argument-hint'));
|
||||
assert.ok(hintLine, 'should have argument-hint line');
|
||||
assert.ok(hintLine.includes('--wave N'), 'argument-hint should include --wave N');
|
||||
assert.ok(hintLine.includes('--gaps-only'), 'argument-hint should keep --gaps-only');
|
||||
});
|
||||
|
||||
test('objective describes wave-filter execution', () => {
|
||||
const content = fs.readFileSync(COMMAND_PATH, 'utf-8');
|
||||
const objectiveMatch = content.match(/<objective>([\s\S]*?)<\/objective>/);
|
||||
assert.ok(objectiveMatch, 'should have <objective> section');
|
||||
assert.ok(objectiveMatch[1].includes('--wave N'), 'objective should mention --wave N');
|
||||
assert.ok(
|
||||
objectiveMatch[1].includes('no incomplete plans remain'),
|
||||
'objective should mention phase completion guardrail'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute-phase workflow: wave filtering', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'workflows/execute-phase.md should exist');
|
||||
});
|
||||
|
||||
test('workflow parses WAVE_FILTER from arguments', () => {
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
assert.ok(content.includes('WAVE_FILTER'), 'workflow should reference WAVE_FILTER');
|
||||
assert.ok(content.includes('Optional `--wave N`'), 'workflow should parse --wave N');
|
||||
});
|
||||
|
||||
test('workflow enforces lower-wave safety', () => {
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
assert.ok(
|
||||
content.includes('Wave safety check'),
|
||||
'workflow should contain a wave safety check section'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('finish earlier waves first'),
|
||||
'workflow should block later-wave execution when lower waves are incomplete'
|
||||
);
|
||||
});
|
||||
|
||||
test('workflow has partial-wave completion guardrail', () => {
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
assert.ok(
|
||||
content.includes('<step name="handle_partial_wave_execution">'),
|
||||
'workflow should have a partial wave handling step'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('Do NOT run phase verification'),
|
||||
'partial wave step should skip phase verification'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('Do NOT mark the phase complete'),
|
||||
'partial wave step should skip phase completion'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute-phase docs: user-facing wave flag', () => {
|
||||
test('COMMANDS.md documents --wave usage', () => {
|
||||
const content = fs.readFileSync(COMMANDS_DOC_PATH, 'utf-8');
|
||||
assert.ok(content.includes('`--wave N`'), 'COMMANDS.md should mention --wave N');
|
||||
assert.ok(
|
||||
content.includes('/gsd:execute-phase 1 --wave 2'),
|
||||
'COMMANDS.md should include a wave-filter example'
|
||||
);
|
||||
});
|
||||
|
||||
test('help workflow documents --wave behavior', () => {
|
||||
const content = fs.readFileSync(HELP_PATH, 'utf-8');
|
||||
assert.ok(
|
||||
content.includes('Optional `--wave N` flag executes only Wave `N`'),
|
||||
'help.md should describe wave-specific execution'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('Usage: `/gsd:execute-phase 5 --wave 2`'),
|
||||
'help.md should include wave-filter usage'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user