diff --git a/commands/gsd/milestone-summary.md b/commands/gsd/milestone-summary.md
index bd379c158..084cb9ff4 100644
--- a/commands/gsd/milestone-summary.md
+++ b/commands/gsd/milestone-summary.md
@@ -29,7 +29,7 @@ Output: MILESTONE_SUMMARY written to `.planning/reports/`, presented inline, opt
- `.planning/RETROSPECTIVE.md`
- `.planning/milestones/v{version}-ROADMAP.md` (if archived)
- `.planning/milestones/v{version}-REQUIREMENTS.md` (if archived)
-- `.planning/phases/*-*/` (SUMMARY.md, VERIFICATION.md, CONTEXT.md)
+- `.planning/phases/*-*/` (SUMMARY.md, VERIFICATION.md, CONTEXT.md, RESEARCH.md)
**User input:**
- Version: $ARGUMENTS (optional — defaults to current/latest milestone)
@@ -38,3 +38,13 @@ Output: MILESTONE_SUMMARY written to `.planning/reports/`, presented inline, opt
Read and execute the milestone-summary workflow from @~/.claude/get-shit-done/workflows/milestone-summary.md end-to-end.
+
+
+- Milestone version resolved (from args, STATE.md, or archive scan)
+- All available artifacts read (ROADMAP, REQUIREMENTS, CONTEXT, SUMMARY, VERIFICATION, RESEARCH, RETROSPECTIVE)
+- Summary document written to `.planning/reports/MILESTONE_SUMMARY-v{version}.md`
+- All 7 sections generated (Overview, Architecture, Phases, Decisions, Requirements, Tech Debt, Getting Started)
+- Summary presented inline to user
+- Interactive Q&A offered
+- STATE.md updated
+
diff --git a/get-shit-done/workflows/milestone-summary.md b/get-shit-done/workflows/milestone-summary.md
index 75740f270..188d62f98 100644
--- a/get-shit-done/workflows/milestone-summary.md
+++ b/get-shit-done/workflows/milestone-summary.md
@@ -27,7 +27,7 @@ Determine whether the milestone is **archived** or **current**:
```
ROADMAP_PATH=".planning/milestones/v${VERSION}-ROADMAP.md"
REQUIREMENTS_PATH=".planning/milestones/v${VERSION}-REQUIREMENTS.md"
-AUDIT_PATH=".planning/v${VERSION}-MILESTONE-AUDIT.md"
+AUDIT_PATH=".planning/milestones/v${VERSION}-MILESTONE-AUDIT.md"
```
**Current/in-progress milestone** (no archive yet):
@@ -37,6 +37,8 @@ REQUIREMENTS_PATH=".planning/REQUIREMENTS.md"
AUDIT_PATH=".planning/v${VERSION}-MILESTONE-AUDIT.md"
```
+Note: The audit file moves to `.planning/milestones/` on archive (per `complete-milestone` workflow). Check both locations as a fallback.
+
**Always available:**
```
PROJECT_PATH=".planning/PROJECT.md"
@@ -63,6 +65,8 @@ This returns phase metadata. For each phase in the milestone scope:
Track which phases have which artifacts.
+**If no phase directories exist** (empty milestone or pre-build state): skip to Step 5 and generate a minimal summary noting "No phases have been executed yet." Do not error — the summary should still capture PROJECT.md and ROADMAP.md content.
+
## Step 4: Gather Git Statistics
```bash
@@ -153,8 +157,18 @@ Present as a bulleted list of decisions with brief rationale:
- **Contributors:** {list}
```
-## Step 6: Commit
+## Step 6: Write and Commit
+**Overwrite guard:** If `.planning/reports/MILESTONE_SUMMARY-v${VERSION}.md` already exists, ask the user:
+> "A milestone summary for v{VERSION} already exists. Overwrite it, or view the existing one?"
+If "view": display existing file and skip to Step 8 (interactive mode). If "overwrite": proceed.
+
+Create the reports directory if needed:
+```bash
+mkdir -p .planning/reports
+```
+
+Write the summary, then commit:
```bash
gsd-tools.cjs commit "docs(v${VERSION}): generate milestone summary for onboarding" \
--files ".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md"
diff --git a/tests/milestone-summary.test.cjs b/tests/milestone-summary.test.cjs
index 12d622fa6..3acec2bf2 100644
--- a/tests/milestone-summary.test.cjs
+++ b/tests/milestone-summary.test.cjs
@@ -111,4 +111,82 @@ describe('milestone-summary workflow', () => {
'should update STATE.md via gsd-tools'
);
});
+
+ test('workflow has overwrite guard for existing summaries', () => {
+ const content = fs.readFileSync(workflowPath, 'utf-8');
+ assert.ok(
+ content.includes('already exists'),
+ 'should check for existing summary before overwriting'
+ );
+ });
+
+ test('workflow handles empty phase directories gracefully', () => {
+ const content = fs.readFileSync(workflowPath, 'utf-8');
+ assert.ok(
+ content.includes('no phase directories') || content.includes('No phases'),
+ 'should handle case where no phases exist'
+ );
+ });
+
+ test('workflow checks both audit file locations for archived milestones', () => {
+ const content = fs.readFileSync(workflowPath, 'utf-8');
+ assert.ok(
+ content.includes('.planning/milestones/v${VERSION}-MILESTONE-AUDIT.md'),
+ 'should check milestones/ directory for archived audit file'
+ );
+ });
+});
+
+describe('milestone-summary command structure', () => {
+ test('command has success_criteria section', () => {
+ const content = fs.readFileSync(commandPath, 'utf-8');
+ assert.ok(
+ content.includes(''),
+ 'should have success_criteria section (follows complete-milestone pattern)'
+ );
+ });
+
+ test('command context lists RESEARCH.md', () => {
+ const content = fs.readFileSync(commandPath, 'utf-8');
+ assert.ok(
+ content.includes('RESEARCH.md'),
+ 'should list RESEARCH.md in context block'
+ );
+ });
+});
+
+describe('milestone-summary artifact path resolution', () => {
+ const { createTempProject, cleanup } = require('./helpers.cjs');
+ let tmpDir;
+
+ test('archived milestone paths point to milestones/ directory', () => {
+ const content = fs.readFileSync(workflowPath, 'utf-8');
+ // Archived roadmap path should be under milestones/
+ assert.ok(
+ content.includes('.planning/milestones/v${VERSION}-ROADMAP.md'),
+ 'archived ROADMAP path should be under .planning/milestones/'
+ );
+ assert.ok(
+ content.includes('.planning/milestones/v${VERSION}-REQUIREMENTS.md'),
+ 'archived REQUIREMENTS path should be under .planning/milestones/'
+ );
+ assert.ok(
+ content.includes('.planning/milestones/v${VERSION}-MILESTONE-AUDIT.md'),
+ 'archived AUDIT path should be under .planning/milestones/'
+ );
+ });
+
+ test('current milestone paths point to .planning/ root', () => {
+ const content = fs.readFileSync(workflowPath, 'utf-8');
+ // Current milestone should read from .planning/ root
+ const lines = content.split('\n');
+ const currentSection = lines.slice(
+ lines.findIndex(l => l.includes('Current/in-progress')),
+ lines.findIndex(l => l.includes('Current/in-progress')) + 10
+ ).join('\n');
+ assert.ok(
+ currentSection.includes('ROADMAP_PATH=".planning/ROADMAP.md"'),
+ 'current ROADMAP path should be at .planning/ root'
+ );
+ });
});