Files
AGENTS/.pi/gsd/workflows/execute-milestone.md

427 lines
12 KiB
Markdown
Raw Normal View History

2026-04-24 20:00:33 +02:00
<gsd-version v="1.12.4" />
<gsd-arguments>
<settings><keep-extra-args /></settings>
<arg name="from" type="number" optional />
<arg name="uat-threshold" type="number" optional />
</gsd-arguments>
<gsd-execute>
<shell command="pi-gsd-tools">
<args>
<arg string="roadmap" />
<arg string="analyze" />
<arg string="--raw" />
</args>
<outs>
<out type="string" name="roadmap" />
</outs>
</shell>
<shell command="pi-gsd-tools">
<args>
<arg string="state" />
<arg string="json" />
<arg string="--raw" />
</args>
<outs>
<out type="string" name="state" />
</outs>
</shell>
<shell command="pi-gsd-tools">
<args>
<arg string="progress" />
<arg string="json" />
<arg string="--raw" />
</args>
<outs>
<out type="string" name="progress" />
</outs>
</shell>
<!-- SET _auto_chain_active when milestone execution chain starts -->
<shell command="pi-gsd-tools">
<args>
<arg string="config-set" />
<arg string="workflow._auto_chain_active" />
<arg string="true" />
</args>
<outs><suppress-errors /></outs>
</shell>
</gsd-execute>
## Execution Context (pre-injected)
**Roadmap:**
<gsd-paste name="roadmap" />
**State:**
<gsd-paste name="state" />
**Progress:**
<gsd-paste name="progress" />
# execute-milestone workflow
Execute all planned phases in the current milestone with scope guardian, UAT gates, and configurable recovery.
---
## Worktree Check (always first)
```bash
git worktree list
```
If not in an isolated worktree:
> "Large-scale milestone execution should run in an isolated worktree to protect your main branch. Create one now? (y/n, default: y)"
If yes: `Skill(skill="gsd-new-workspace", args="milestone-exec")`, then continue in the new worktree.
If no: warn once, proceed on current branch.
---
## Mode Selection (step 1 - always second)
Ask the user ONE binary question:
> **"How should I behave when I hit a doubt, error, or scope deviation?"**
>
> - **Interactive** - Stop and ask me; I'll guide you through it
> - **Silent** - Try to self-correct; only surface unrecoverable blockers
Store as `MODE` (interactive | silent). Do not ask again.
---
## Phase Discovery
```bash
pi-gsd-tools roadmap analyze --raw
pi-gsd-tools progress json --raw
```
Build execution queue: phases with ≥1 PLAN.md and status ≠ Complete, in roadmap order.
If queue is empty: "All phases are already complete. Run /gsd-audit-milestone." Stop.
---
## Per-Phase Execution Loop
For each pending phase `N`:
### A. Scope Pre-check (lightweight, one LLM call)
Read:
- `.planning/REQUIREMENTS.md`
- Phase goal + success criteria from ROADMAP.md
Prompt (internal): *"Does executing this phase risk implementing anything not covered by active requirements, or conflict with what previous phases delivered? Rate: low / medium / high. One sentence reason."*
- **low** - continue silently
- **medium** - log in scope-log, continue
- **high + interactive** - surface to user, ask: proceed / adjust phase goal / stop
- **high + silent** - log prominently, continue, include in final report
### B. Execute Phase
```
Skill(skill="gsd-execute-phase", args="${N}")
```
### C. Scope Post-audit (full, one LLM call)
Read new SUMMARY.md files from the phase directory.
Check:
1. **Undelivered must-haves** - PLAN.md `must_haves` entries absent from SUMMARY
2. **Scope creep** - files modified that are outside this phase's stated scope
3. **Requirement drift** - work done that has no matching REQUIREMENTS entry
Classify result as `SCOPE_STATUS`:
- **clean** - continue
- **drift** - log + warn, continue
- **violation** - trigger recovery (see §F)
### D. Verify
```
Skill(skill="gsd-verify-work", args="${N}")
```
Compute UAT pass rate = passing items / total items.
Default threshold: **80%**. Override with `--uat-threshold N`.
### E. Gate Check
| Condition | Interactive | Silent |
| ------------------------- | ------------------------------ | ---------------------- |
| UAT pass rate < threshold | Ask: fix gaps now or continue? | → Recovery loop |
| Context remaining < 20% | Warn, ask: stop or continue? | → Write HANDOFF, stop |
| SCOPE_STATUS = violation | Surface details, ask | → Recovery loop |
| All gates pass | Continue to checkpoint | Continue to checkpoint |
### F. Recovery Loop
When triggered:
```
1. pi-gsd-tools validate health --repair
2. Self-correct: identify root cause, patch, re-run verification
3. Re-check gates
4. Gates pass → continue to checkpoint
5. Still failing:
- Interactive: explain issue, ask user how to resolve, loop from step 2
- Silent: write HANDOFF files (see §G), stop
```
### G. Hard Stop - HANDOFF Files
On unrecoverable stop, write two files matching original GSD pause-work convention:
**`.planning/HANDOFF.json`** (machine-readable, consumed by `/gsd-resume-work`):
```json
{
"stopped_at": "ISO-timestamp",
"phase": "N",
"phase_name": "phase name",
"stop_reason": "uat_failure | scope_violation | context_exhausted | unrecoverable_error | audit_exhausted | audit_no_result",
"outer_cycles": 0,
"gaps_store": [],
"debt_store": [],
"gaps_phases_tried": [],
"debt_phases_tried": [],
"uat_pass_rate": 75,
"scope_status": "violation",
"phases_completed": ["1", "2", "3"],
"phases_remaining": ["N", "N+1"],
"scope_log": ["note 1", "note 2"],
"next_action": "Run /gsd-execute-milestone --from N to resume"
}
```
**`.planning/phases/NN-name/.continue-here.md`** (human-readable):
```markdown
---
phase: N
status: stopped
stop_reason: [reason]
last_updated: [timestamp]
---
## What happened
[Clear explanation of why execution stopped]
## State at stop
- UAT pass rate: X%
- Scope status: [clean/drift/violation]
- Scope notes: [any flags]
## How to resume
Run: /gsd-execute-milestone --from N
Or fix the specific issue first: [specific suggestion]
```
### H. Checkpoint (on success)
```bash
pi-gsd-tools state update current_phase ${N}
pi-gsd-tools state update last_activity $(date -u +%Y-%m-%d)
pi-gsd-tools commit "chore: complete phase ${N}" --files .planning/
```
Announce: `✓ Phase ${N} complete - UAT: ${pass_rate}% Scope: ${scope_status}`
---
## After All Phases - Mode Split
### Interactive mode
Do NOT auto-invoke the lifecycle. Surface the execution summary and hand back:
```
━━ execute-milestone: all phases done ━━━━━━━━━━━━
✓ Phases: ${done}/${total} complete
📊 Avg UAT: ${avg_uat}%
⚠ Scope: ${scope_flag_count} flag(s) (details above)
Next: /gsd-audit-milestone when you are ready to review
and close the milestone.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Stop. The user owns the audit decision.
---
### Silent mode - Auto Lifecycle
Only in silent mode. Display transition banner:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
All phases complete → lifecycle: audit → complete → cleanup
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Read config once:
- `config.workflow.auto_retry_audit` (default: `true`)
- `config.workflow.auto_retry_audit_budget` (default: `1`)
- `config.workflow.auto_retry_tech_debt` (default: `true`)
- `config.workflow.auto_retry_tech_debt_budget` (default: `1`)
Initialise accumulators (persist across the outer loop):
- `gaps_store = []` - unsatisfied requirements not yet resolved
- `debt_store = []` - tech debt items not yet resolved
- `gaps_phases_tried = []` - inserted phases attempted for gap closure
- `debt_phases_tried = []` - inserted phases attempted for debt resolution
- `outer_cycles = 0`
---
#### OUTER LOOP - Full audit cycle
`LABEL: outer_loop`
**Step A - Full audit**
```
Skill(skill="gsd-audit-milestone")
```
If no result / malformed → Write HANDOFF (§G), stop.
Extract from audit result:
- `current_gaps[]` - unsatisfied requirement IDs + affected phase numbers
- `current_debt[]` - tech debt items + affected phase numbers
If both empty → AUDIT PASSED. Proceed to Step D (complete).
---
**Step B - Gap closure loop** (only if `current_gaps` non-empty)
If `auto_retry_audit = false`: add `current_gaps` to `gaps_store`, skip to Step C.
While `current_gaps` non-empty and `auto_retry_audit_budget > 0`:
```
1. Decrement auto_retry_audit_budget
2. Insert a gap-closure phase (decimal after last phase):
Skill(skill="gsd-insert-phase", args="${last_phase}.${gap_cycle} 'Gap closure: ${gap_summary}'")
3. Plan it with gap context:
Skill(skill="gsd-plan-phase", args="${new_phase} --auto")
(Pass unsatisfied requirement details as planning context)
4. Execute it:
Skill(skill="gsd-execute-phase", args="${new_phase}")
5. Track: append new_phase to gaps_phases_tried
6. Targeted re-audit - affected phases only:
Skill(skill="gsd-audit-milestone", args="--phases ${gap_affected_phases}")
7. Re-read current_gaps from result
- Resolved? current_gaps = [], break loop
- Reduced? loop again if budget > 0
- Same/worse? loop again if budget > 0
```
After loop:
- If `current_gaps` empty → gaps resolved ✅, `gaps_store = []`
- If still non-empty → `gaps_store = current_gaps` (budget exhausted or disabled)
---
**Step C - Tech debt loop** (only if `current_debt` non-empty)
If `auto_retry_tech_debt = false`: add `current_debt` to `debt_store`, skip to final gate.
While `current_debt` non-empty and `auto_retry_tech_debt_budget > 0`:
```
1. Decrement auto_retry_tech_debt_budget
2. Insert a debt-resolution phase (decimal after last phase):
Skill(skill="gsd-insert-phase", args="${last_phase}.${debt_cycle} 'Tech debt: ${debt_summary}'")
3. Plan it with debt context:
Skill(skill="gsd-plan-phase", args="${new_phase} --auto")
(Pass tech debt item details as planning context)
4. Execute it:
Skill(skill="gsd-execute-phase", args="${new_phase}")
5. Track: append new_phase to debt_phases_tried
6. Targeted re-audit - affected phases only:
Skill(skill="gsd-audit-milestone", args="--phases ${debt_affected_phases}")
7. Re-read current_debt from result
- Resolved? current_debt = [], break loop
- Reduced? loop again if budget > 0
- gaps_found in re-audit? add to gaps_store, break (handled in next outer cycle)
```
After loop:
- If `current_debt` empty → debt resolved ✅, `debt_store = []`
- If still non-empty → `debt_store = current_debt`
---
**Final gate (end of outer loop body)**
If `gaps_store` empty AND `debt_store` empty:
→ AUDIT CLEAN. Proceed to Step D.
If anything remains in stores:
- Increment `outer_cycles`
- If outer budget remaining (derived from max of both budgets > 0):
`GOTO outer_loop` (re-run full audit from top, fresh eyes)
- If exhausted:
→ Write enriched HANDOFF (§G), stop.
---
#### Step D - Complete Milestone
```
Skill(skill="gsd-complete-milestone", args="${milestone_version}")
```
Verify archive produced:
```bash
ls .planning/milestones/v${milestone_version}-ROADMAP.md 2>/dev/null || true
```
If absent → Write HANDOFF, stop. Message: "complete-milestone did not produce archive files."
#### Step E - Cleanup
```
Skill(skill="gsd-cleanup")
```
Cleanup handles its own dry-run and confirmation internally.
---
## Worktree Merge (both modes, after lifecycle or summary)
If running in an isolated worktree, ask:
> "Merge this worktree back to your main branch? (y/n, default: y)"
If yes:
```bash
git checkout main
git merge --no-ff milestone-exec -m "feat: complete milestone ${milestone_version}"
git worktree remove milestone-exec
```
If no: leave the worktree open. Tell the user how to merge manually.
---
## Final Banner (silent mode only, after full lifecycle)
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► EXECUTE-MILESTONE ▸ COMPLETE 🎉
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Milestone: ${milestone_version} - ${milestone_name}
Phases: ${done}/${total} complete
Avg UAT: ${avg_uat}%
Lifecycle: audit ✅ → complete ✅ → cleanup ✅
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```