$maestro-milestone-complete "M1"
$maestro-milestone-complete # uses current_milestone from state.json
$maestro-milestone-complete --force "M1" # skip audit check
Output: .workflow/milestones/{milestone}/ archive directory
Step 1: Parse & Validate
Read .workflow/state.json for current_milestone, artifacts[], milestones[]. Determine target from args or current_milestone (E001 if none).
Validate audit report at .workflow/milestones/{milestone}/audit-report.md:
- Parse for
## Verdictsection (or**Verdict:**inline) - PASS condition: verdict line matches strict pattern
^PASS$(case-insensitive, trimmed) — rejects partial matches like "PASS_WITH_WARNINGS", "BYPASS", or "COMPASS" - Any other verdict (FAIL, INCOMPLETE, PARTIAL, missing section) → E002 unless
--force
Verify all milestone artifacts completed (E003 unless --force).
Step 2: Archive Scratch Dirs
Copy each milestone artifact's directory to .workflow/milestones/{milestone}/artifacts/.
Source path resolution: For each entry in state.json.artifacts[], resolve the source directory from artifact.path:
- If
artifact.pathis relative (e.g.scratch/M1-auth), resolve from.workflow/(→.workflow/scratch/M1-auth) - If
artifact.pathis absolute, use as-is - Copy the entire resolved directory to
.workflow/milestones/{milestone}/artifacts/{artifact.name}/
After each copy (per archived session dir):
a. If destination contains archive.json with lifecycle.status == "sealed":
- Set
lifecycle.status = "archived",lifecycle.archived_at = now,lifecycle.linked_milestone = {milestone}(if null).
b. If destination contains context-package.json, prune (scheme C — non-destructive):
- Compute
pruned= {open_questionswithout answer/resolved_in;constraintsstatus=open;insightsbeyond top 20;referenceswhose path does not exist on disk } - If any
pruned.*non-empty: writecontext-package.pruned.jsonwith the dropped items; rewritecontext-package.jsonkeeping only answered/resolved questions, locked constraints,insights[0..20], valid-path references; updatearchive.json.pruned = { at: now, counts, ref: "context-package.pruned.json" } - Otherwise: set
archive.json.pruned = { at: now, counts: zeros, ref: null }
c. If session dir lacks archive.json (legacy): skip a+b silently — legacy sessions are not indexed.
Snapshot roadmap.md as roadmap-snapshot.md in the milestone archive.
Step 3: Extract Learnings
Source files (read in order):
.workflow/milestones/{milestone}/artifacts/**/.summaries/*.md— task completion summaries.workflow/milestones/{milestone}/artifacts/**/reflection-log.md— retrospective entries
Extraction: Parse each source for patterns, pitfalls, strategy adjustments. Look for recurring themes across summaries and explicit lessons in reflection logs.
Dedup: Run maestro load --type spec --category learning to load existing entries. Skip any extracted learning whose keywords fully overlap with an existing entry.
Write: Append to .workflow/specs/learnings.md using <spec-entry> closed-tag format:
<spec-entry category="learning" keywords="kw1, kw2" date="YYYY-MM-DD" title="{title}" description="{one-line summary}" source="milestone-complete:{milestone}">
### {title}
Learning content here.
</spec-entry>
Step 3b: Knowledge Promotion Inquiry
- High-frequency patterns: Scan all
<spec-entry category="learning">entries for keyword overlap. Trigger threshold: >=2 entries sharing the same keyword. For each triggered keyword, ask: "Keyword '{keyword}' appears in {N} learning entries. Promote to formal coding convention?" - Convention drift: Compare executed task summaries against
coding-conventions.mdandarchitecture-constraints.md. Trigger threshold: any deviation found (technique used but not documented, or documented convention not followed). Ask: "Convention '{convention}' was bypassed during this milestone. Update conventions?" - Wiki island check: Suggest
wiki-connect --fixto link new knowledge. Ask user viarequest_user_input: "Run wiki-connect to link new knowledge entries?" with options: "Yes, link now" / "No, skip". Only run if user confirms (or-yis set).
If -y: auto-accept all promotions without asking.
If not -y: ask user for confirmation via request_user_input:
{ "questions": [{ "id": "promote_learning", "header": "Knowledge Promotion", "question": "Keyword '{keyword}' appears in {N} learning entries. Promote to coding convention?", "options": [{ "label": "Yes, promote (Recommended)", "description": "Add as formal coding convention via spec-add" }, { "label": "No, keep as learning", "description": "Leave in learnings.md without promotion" }] }] }
If user confirms, append <spec-entry> to target category file preserving original date and source.
Step 4: Archive Artifact Entries
Move milestone artifacts from state.json.artifacts[] to milestone_history[] with completion metadata (id, name, status, completed_at, archive_path, archived_artifacts). Remove from active artifacts[].
Step 5: Advance State
Set current_milestone to next pending milestone (mark it active), or set project status: "completed" if none remain. Atomic write to state.json.
Step 6: Clean Scratch
Remove archived artifact directories from .workflow/.
Step 7: Generate Summary & Report
Write .workflow/milestones/{milestone}/summary.md with outcomes and learnings. Update .workflow/project.md Context section. Display completion report with next steps: $maestro-milestone-release, $maestro-analyze, $manage-status, $manage-wiki health, $wiki-digest.
<error_codes>
| Code | Severity | Description | Recovery |
|---|---|---|---|
| E001 | error | Milestone identifier required | Specify milestone |
| E002 | error | Audit not passed | Run milestone-audit first |
| E003 | error | Incomplete artifacts remain | Complete work first |
</error_codes>
<success_criteria>
- Audit report validated (or --force used)
- Scratch directories archived to milestones/
- Learnings extracted and appended to specs/learnings.md
- Artifact entries moved to milestone_history in state.json
- State advanced to next milestone (or project marked complete)
- Scratch directories cleaned
- Summary and completion report generated </success_criteria>