maestro-flow 0.3.11 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/.claude/agents/conceptual-planning-agent.md +1 -0
  2. package/.claude/agents/workflow-analyzer.md +114 -114
  3. package/.claude/agents/workflow-collab-planner.md +144 -144
  4. package/.claude/agents/workflow-debugger.md +102 -103
  5. package/.claude/agents/workflow-executor.md +127 -128
  6. package/.claude/agents/workflow-integration-checker.md +82 -82
  7. package/.claude/agents/workflow-nyquist-auditor.md +85 -84
  8. package/.claude/agents/workflow-phase-researcher.md +85 -85
  9. package/.claude/agents/workflow-plan-checker.md +90 -90
  10. package/.claude/agents/workflow-planner.md +178 -178
  11. package/.claude/agents/workflow-roadmapper.md +81 -83
  12. package/.claude/agents/workflow-verifier.md +119 -119
  13. package/.claude/commands/learn-retro.md +2 -2
  14. package/.claude/commands/learn-second-opinion.md +2 -2
  15. package/.claude/commands/maestro-brainstorm.md +1 -0
  16. package/.claude/commands/maestro-fork.md +133 -111
  17. package/.claude/commands/maestro-merge.md +85 -77
  18. package/.claude/commands/maestro-plan.md +88 -2
  19. package/.claude/commands/maestro-roadmap.md +113 -2
  20. package/.claude/commands/maestro.md +1 -0
  21. package/.claude/commands/quality-business-test.md +5 -5
  22. package/.claude/commands/quality-debug.md +3 -2
  23. package/.claude/commands/quality-retrospective.md +6 -4
  24. package/.claude/commands/quality-review.md +1 -1
  25. package/.claude/commands/quality-test-gen.md +5 -4
  26. package/.codex/skills/maestro-brainstorm/SKILL.md +1 -1
  27. package/.codex/skills/maestro-fork/SKILL.md +98 -68
  28. package/.codex/skills/maestro-init/SKILL.md +4 -3
  29. package/.codex/skills/maestro-merge/SKILL.md +69 -62
  30. package/.codex/skills/maestro-ui-design/SKILL.md +1 -1
  31. package/.codex/skills/maestro-verify/SKILL.md +12 -12
  32. package/.codex/skills/manage-learn/SKILL.md +1 -1
  33. package/.codex/skills/manage-status/SKILL.md +4 -4
  34. package/.codex/skills/quality-business-test/SKILL.md +2 -2
  35. package/.codex/skills/quality-integration-test/SKILL.md +1 -1
  36. package/.codex/skills/quality-retrospective/SKILL.md +7 -7
  37. package/.codex/skills/quality-review/SKILL.md +2 -2
  38. package/.codex/skills/quality-test/SKILL.md +2 -2
  39. package/.codex/skills/quality-test-gen/SKILL.md +1 -1
  40. package/README.md +2 -0
  41. package/README.zh-CN.md +2 -0
  42. package/dashboard/dist/assets/{ArtifactsPage-DZNCi6tn.js → ArtifactsPage-CUrrDGgN.js} +1 -1
  43. package/dashboard/dist/assets/ChatInput-pUOLJIKE.js +49 -0
  44. package/dashboard/dist/assets/ChatPage-B8Xqkt0v.js +27 -0
  45. package/dashboard/dist/assets/{CollabPage-B4NAHXS2.js → CollabPage-DIUXeazv.js} +1 -1
  46. package/dashboard/dist/assets/{ExecutionPanel-CFt4LJyq.js → ExecutionPanel-VmYeADFj.js} +1 -1
  47. package/dashboard/dist/assets/KanbanPage-DLq8v7hg.js +21 -0
  48. package/dashboard/dist/assets/{MarkdownRenderer-X4af_WNb.js → MarkdownRenderer-D7AehrnR.js} +1 -1
  49. package/dashboard/dist/assets/{McpPage-BKfCVIyU.js → McpPage-BY0SjTgw.js} +2 -2
  50. package/dashboard/dist/assets/{OutputPanel-BlBQFJSW.js → OutputPanel-B-Rjwgmv.js} +1 -1
  51. package/dashboard/dist/assets/{ProblemsPanel-De3DLvoI.js → ProblemsPanel-GEpF-oi4.js} +1 -1
  52. package/dashboard/dist/assets/RequirementBoardPage-xs8uDM7I.js +6 -0
  53. package/dashboard/dist/assets/{RequirementPage-Bllxe2XI.js → RequirementPage-BKDSFwjA.js} +5 -10
  54. package/dashboard/dist/assets/SpecsPage-DLFb9ZH0.js +36 -0
  55. package/dashboard/dist/assets/SupervisorPage-SOki_kgz.js +6 -0
  56. package/dashboard/dist/assets/TeamsPage-BO2kP70F.js +11 -0
  57. package/dashboard/dist/assets/{TreeBrowser-Q12qobZs.js → TreeBrowser-B9DHdULE.js} +1 -1
  58. package/dashboard/dist/assets/{WorkflowPage-D_Fzdy3_.js → WorkflowPage-C8hWbYim.js} +1 -1
  59. package/dashboard/dist/assets/{check-u6fGOwQO.js → check-DJDk3A2a.js} +1 -1
  60. package/dashboard/dist/assets/{chevron-right-Csu22t58.js → chevron-right-C7bVDreZ.js} +1 -1
  61. package/dashboard/dist/assets/{circle-CMrkbRNg.js → circle-Qfgy4LB_.js} +1 -1
  62. package/dashboard/dist/assets/{circle-alert-c3tH1P4z.js → circle-alert-Na1vf6qQ.js} +1 -1
  63. package/dashboard/dist/assets/{circle-check-gYxxSYuH.js → circle-check-CEGgy3NV.js} +1 -1
  64. package/dashboard/dist/assets/{circle-check-big-TDSeWstm.js → circle-check-big-3JB8zRYj.js} +1 -1
  65. package/dashboard/dist/assets/{code-CFN2uX9V.js → code-Ble63Idz.js} +1 -1
  66. package/dashboard/dist/assets/{columns-3-38xIDlzy.js → columns-3-BUcKlxve.js} +1 -1
  67. package/dashboard/dist/assets/{download-DC7KkKyP.js → download-CMqkfn8x.js} +1 -1
  68. package/dashboard/dist/assets/{folder-CWq_lAnf.js → folder-B9ewx9LL.js} +1 -1
  69. package/dashboard/dist/assets/index-C2Mcb4TJ.js +231 -0
  70. package/dashboard/dist/assets/index-DyBbPc18.css +1 -0
  71. package/dashboard/dist/assets/{index-Do71weNR.js → index-JTmGteaT.js} +1 -1
  72. package/dashboard/dist/assets/{list-CgIP_2A-.js → list-DI8Wn2aT.js} +1 -1
  73. package/dashboard/dist/assets/loader-B5F6PzFT.js +11 -0
  74. package/dashboard/dist/assets/{minus-DYoN5UGk.js → minus-Lp_BfctG.js} +1 -1
  75. package/dashboard/dist/assets/{pen-line-Bh_WKYHm.js → pen-line-Ch7sphzZ.js} +1 -1
  76. package/dashboard/dist/assets/pencil-_yRMHmGT.js +6 -0
  77. package/dashboard/dist/assets/{proxy-BKxDAKTj.js → proxy-D72Y8a4Y.js} +1 -1
  78. package/dashboard/dist/assets/{search-SieXnOgr.js → search-BS6fI6Bg.js} +1 -1
  79. package/dashboard/dist/assets/{shallow-Bme1JY57.js → shallow-BXasQBvr.js} +1 -1
  80. package/dashboard/dist/assets/table-CeGlFjlP.js +6 -0
  81. package/dashboard/dist/assets/{terminal-BB3Xfuv5.js → terminal-BJic2yW-.js} +1 -1
  82. package/dashboard/dist/assets/{trash-2-C8f4vFFM.js → trash-2-Czz4X8Fb.js} +1 -1
  83. package/dashboard/dist/assets/{zap-4uwlzVm0.js → zap-C3H0jVFA.js} +1 -1
  84. package/dashboard/dist/index.html +2 -2
  85. package/dashboard/dist-server/dashboard/src/server/agents/agent-manager.js +16 -1
  86. package/dashboard/dist-server/dashboard/src/server/agents/agent-manager.js.map +1 -1
  87. package/dashboard/dist-server/dashboard/src/server/agents/delegate-broker-monitor.js +1 -2
  88. package/dashboard/dist-server/dashboard/src/server/agents/delegate-broker-monitor.js.map +1 -1
  89. package/dashboard/dist-server/dashboard/src/server/commander/commander-prompts.d.ts +1 -1
  90. package/dashboard/dist-server/dashboard/src/server/commander/commander-prompts.js +2 -1
  91. package/dashboard/dist-server/dashboard/src/server/commander/commander-prompts.js.map +1 -1
  92. package/dashboard/dist-server/dashboard/src/server/index.js +3 -0
  93. package/dashboard/dist-server/dashboard/src/server/index.js.map +1 -1
  94. package/dashboard/dist-server/dashboard/src/server/routes/collab.js +124 -0
  95. package/dashboard/dist-server/dashboard/src/server/routes/collab.js.map +1 -1
  96. package/dashboard/dist-server/dashboard/src/server/ws/handlers/team-handler.d.ts +10 -0
  97. package/dashboard/dist-server/dashboard/src/server/ws/handlers/team-handler.js +73 -0
  98. package/dashboard/dist-server/dashboard/src/server/ws/handlers/team-handler.js.map +1 -0
  99. package/dashboard/dist-server/dashboard/src/shared/collab-types.d.ts +31 -0
  100. package/dashboard/dist-server/dashboard/src/shared/collab-types.js +28 -0
  101. package/dashboard/dist-server/dashboard/src/shared/collab-types.js.map +1 -1
  102. package/dashboard/dist-server/dashboard/src/shared/constants.js +5 -0
  103. package/dashboard/dist-server/dashboard/src/shared/constants.js.map +1 -1
  104. package/dashboard/dist-server/dashboard/src/shared/coordinate-types.d.ts +22 -0
  105. package/dashboard/dist-server/dashboard/src/shared/issue-types.d.ts +12 -0
  106. package/dashboard/dist-server/dashboard/src/shared/issue-types.js +12 -0
  107. package/dashboard/dist-server/dashboard/src/shared/issue-types.js.map +1 -1
  108. package/dashboard/dist-server/dashboard/src/shared/team-types.d.ts +1 -0
  109. package/dashboard/dist-server/dashboard/src/shared/team-types.js +7 -0
  110. package/dashboard/dist-server/dashboard/src/shared/team-types.js.map +1 -1
  111. package/dashboard/dist-server/dashboard/src/shared/ws-protocol.d.ts +27 -1
  112. package/dashboard/dist-server/dashboard/src/shared/ws-protocol.js.map +1 -1
  113. package/dashboard/dist-server/shared/agent-types.d.ts +4 -0
  114. package/dashboard/dist-server/src/hooks/constants.d.ts +1 -1
  115. package/dashboard/dist-server/src/hooks/constants.js +2 -2
  116. package/dashboard/dist-server/src/hooks/constants.js.map +1 -1
  117. package/dist/shared/agent-types.d.ts +4 -0
  118. package/dist/shared/agent-types.d.ts.map +1 -1
  119. package/dist/src/commands/install-backend.d.ts.map +1 -1
  120. package/dist/src/commands/install-backend.js +29 -18
  121. package/dist/src/commands/install-backend.js.map +1 -1
  122. package/dist/src/hooks/__tests__/statusline-visual-test.js +23 -1
  123. package/dist/src/hooks/__tests__/statusline-visual-test.js.map +1 -1
  124. package/dist/src/hooks/constants.d.ts +1 -1
  125. package/dist/src/hooks/constants.d.ts.map +1 -1
  126. package/dist/src/hooks/constants.js +2 -2
  127. package/dist/src/hooks/constants.js.map +1 -1
  128. package/dist/src/hooks/skill-context.d.ts +3 -0
  129. package/dist/src/hooks/skill-context.d.ts.map +1 -1
  130. package/dist/src/hooks/skill-context.js +95 -9
  131. package/dist/src/hooks/skill-context.js.map +1 -1
  132. package/dist/src/hooks/statusline.d.ts.map +1 -1
  133. package/dist/src/hooks/statusline.js +6 -3
  134. package/dist/src/hooks/statusline.js.map +1 -1
  135. package/dist/src/tools/merge-validator.d.ts.map +1 -1
  136. package/dist/src/tools/merge-validator.js +114 -16
  137. package/dist/src/tools/merge-validator.js.map +1 -1
  138. package/package.json +1 -1
  139. package/shared/agent-types.ts +4 -0
  140. package/templates/worktree-scope.json +9 -10
  141. package/templates/worktrees.json +26 -27
  142. package/workflows/brainstorm.md +10 -1
  143. package/workflows/debug.md +16 -6
  144. package/workflows/fork.md +100 -36
  145. package/workflows/integration-test.md +14 -2
  146. package/workflows/issue.md +14 -4
  147. package/workflows/learn.md +19 -5
  148. package/workflows/maestro.md +1 -0
  149. package/workflows/merge.md +113 -55
  150. package/workflows/retrospective.md +61 -22
  151. package/workflows/review.md +17 -4
  152. package/workflows/test.md +12 -2
  153. package/workflows/ui-style.md +9 -2
  154. package/dashboard/dist/assets/ChatInput-Bvr-FeEq.js +0 -49
  155. package/dashboard/dist/assets/ChatPage-D9zTkJZo.js +0 -22
  156. package/dashboard/dist/assets/KanbanPage-C8USth6H.js +0 -21
  157. package/dashboard/dist/assets/RequirementBoardPage-Bf1trzqs.js +0 -11
  158. package/dashboard/dist/assets/SpecsPage-9lwxKT27.js +0 -36
  159. package/dashboard/dist/assets/SupervisorPage-SusdfHFq.js +0 -6
  160. package/dashboard/dist/assets/TeamsPage-DsuM6OwC.js +0 -6
  161. package/dashboard/dist/assets/arrow-left-Bqtb2hle.js +0 -6
  162. package/dashboard/dist/assets/index-DWG-WrzT.js +0 -231
  163. package/dashboard/dist/assets/index-GUNJodSR.css +0 -1
  164. package/dashboard/dist/assets/table-llyEtj-7.js +0 -6
package/workflows/fork.md CHANGED
@@ -121,12 +121,25 @@ IF syncMode:
121
121
  ## Step 5: Validate & Confirm
122
122
 
123
123
  ```
124
- // Load phase index files for all milestone phases
124
+ // Load phase status artifact registry first, fallback to legacy phases/
125
125
  phaseList = []
126
- for (phaseNum of milestonePhases):
127
- Glob: .workflow/phases/{NN}-*/index.json where NN matches phaseNum
128
- Read index.json → phaseIndex
129
- phaseList.push(phaseIndex)
126
+ artifacts = projectState.artifacts ?? []
127
+ useArtifactRegistry = artifacts.length > 0
128
+
129
+ IF useArtifactRegistry:
130
+ // Derive phase status from artifact registry
131
+ for (phaseNum of milestonePhases):
132
+ execArtifacts = artifacts.filter(a => a.type === 'execute' && a.phase === phaseNum)
133
+ status = execArtifacts.some(a => a.status === 'completed') ? 'completed'
134
+ : execArtifacts.length > 0 ? 'in_progress'
135
+ : 'pending'
136
+ phaseList.push({ phase: phaseNum, title: "Phase " + phaseNum, status })
137
+ ELSE:
138
+ // Legacy: load from phases/ directory
139
+ for (phaseNum of milestonePhases):
140
+ Glob: .workflow/phases/{NN}-*/index.json where NN matches phaseNum
141
+ Read index.json → phaseIndex
142
+ phaseList.push(phaseIndex)
130
143
 
131
144
  // Validate: milestone should have at least one non-completed phase
132
145
  nonCompleted = phaseList.filter(p => p.status !== "completed")
@@ -172,7 +185,7 @@ Bash("git branch -D {branch}") // ignore errors (may not exist)
172
185
  Bash("git worktree add -b {branch} {wtPath} {baseBranch}")
173
186
 
174
187
  // 6c: Create .workflow/ structure in worktree
175
- Bash("mkdir -p {wtPath}/.workflow/phases")
188
+ Bash("mkdir -p {wtPath}/.workflow/scratch")
176
189
 
177
190
  // 6d: Copy shared context (read-only)
178
191
  Copy .workflow/project.md → {wtPath}/.workflow/project.md
@@ -182,46 +195,92 @@ IF file_exists(".workflow/config.json"):
182
195
  IF directory_exists(".workflow/specs"):
183
196
  Copy .workflow/specs/ → {wtPath}/.workflow/specs/
184
197
 
185
- // 6e: Copy ALL phase directories for this milestone
186
- ownedPhaseNumbers = []
187
- for (p of phaseList):
188
- NN = String(p.phase).padStart(2, '0')
189
- Copy .workflow/phases/{NN}-{p.slug}/ → {wtPath}/.workflow/phases/{NN}-{p.slug}/
190
- ownedPhaseNumbers.push(p.phase)
191
-
192
- // 6f: Copy completed dependency phase dirs outside this milestone (read-only reference)
193
- allDeps = new Set()
194
- for (p of phaseList):
195
- for (dep of p.depends_on):
196
- IF NOT ownedPhaseNumbers.includes(dep):
197
- allDeps.add(dep)
198
+ // 6e: Copy milestone artifacts to worktree
199
+ ownedPhaseNumbers = milestonePhases.slice() // all phases in this milestone
198
200
 
199
- for (dep of allDeps):
200
- depNN = String(dep).padStart(2, '0')
201
- Glob: .workflow/phases/{depNN}-*/index.json
202
- Read depIndex
203
- Copy .workflow/phases/{depNN}-{depIndex.slug}/ → {wtPath}/.workflow/phases/{depNN}-{depIndex.slug}/
201
+ IF useArtifactRegistry:
202
+ // Copy scratch dirs for this milestone's artifacts
203
+ milestoneArtifacts = artifacts.filter(a =>
204
+ a.milestone === milestoneName && a.path
205
+ )
206
+ for (art of milestoneArtifacts):
207
+ IF directory_exists(".workflow/" + art.path):
208
+ Copy .workflow/{art.path}/ → {wtPath}/.workflow/{art.path}/
209
+ ELSE:
210
+ // Legacy: copy phase directories
211
+ Bash("mkdir -p {wtPath}/.workflow/phases")
212
+ for (p of phaseList):
213
+ NN = String(p.phase).padStart(2, '0')
214
+ Copy .workflow/phases/{NN}-{p.slug}/ → {wtPath}/.workflow/phases/{NN}-{p.slug}/
215
+
216
+ // 6f: Copy dependency artifacts (phases outside this milestone)
217
+ IF useArtifactRegistry:
218
+ // Collect dependency phases from roadmap milestone entry
219
+ // (cross-milestone dependencies are defined in milestoneEntry.depends_on or roadmap)
220
+ depPhases = new Set()
221
+ IF milestoneEntry.depends_on:
222
+ for (dep of milestoneEntry.depends_on):
223
+ IF NOT ownedPhaseNumbers.includes(dep):
224
+ depPhases.add(dep)
225
+ // Copy dependency artifacts from main
226
+ for (dep of depPhases):
227
+ depArtifacts = artifacts.filter(a => a.phase === dep && a.path)
228
+ for (art of depArtifacts):
229
+ IF directory_exists(".workflow/" + art.path):
230
+ Copy .workflow/{art.path}/ → {wtPath}/.workflow/{art.path}/
231
+ ELSE:
232
+ // Legacy: copy completed dependency phase dirs
233
+ allDeps = new Set()
234
+ for (p of phaseList):
235
+ IF p.depends_on:
236
+ for (dep of p.depends_on):
237
+ IF NOT ownedPhaseNumbers.includes(dep):
238
+ allDeps.add(dep)
239
+ for (dep of allDeps):
240
+ depNN = String(dep).padStart(2, '0')
241
+ Glob: .workflow/phases/{depNN}-*/index.json
242
+ Read → depIndex
243
+ Copy .workflow/phases/{depNN}-{depIndex.slug}/ → {wtPath}/.workflow/phases/{depNN}-{depIndex.slug}/
244
+
245
+ // 6g: Build phase_dependencies map for worktree-scope
246
+ phaseDeps = {}
247
+ IF useArtifactRegistry:
248
+ IF milestoneEntry.depends_on:
249
+ for (phaseNum of ownedPhaseNumbers):
250
+ phaseDeps[String(phaseNum)] = milestoneEntry.depends_on
251
+ .filter(d => !ownedPhaseNumbers.includes(d))
252
+ ELSE:
253
+ for (p of phaseList):
254
+ IF p.depends_on:
255
+ externalDeps = p.depends_on.filter(d => !ownedPhaseNumbers.includes(d))
256
+ IF externalDeps.length > 0:
257
+ phaseDeps[String(p.phase)] = externalDeps
204
258
 
205
- // 6g: Write worktree-scope.json
259
+ // 6h: Write worktree-scope.json
206
260
  Write {wtPath}/.workflow/worktree-scope.json:
207
261
  {
208
262
  "worktree": true,
209
263
  "milestone_num": milestoneNum,
210
264
  "milestone": milestoneName,
211
265
  "owned_phases": ownedPhaseNumbers,
266
+ "phase_dependencies": phaseDeps,
212
267
  "main_worktree": resolve(cwd),
213
268
  "branch": branch,
214
269
  "base_commit": baseCommit,
215
270
  "created_at": getUtc8ISOString()
216
271
  }
217
272
 
218
- // 6h: Write scoped state.json
273
+ // 6i: Write scoped state.json
219
274
  Read .workflow/state.json → mainState
220
275
  firstPending = phaseList.find(p => p.status !== "completed")
221
276
  scopedState = {
222
277
  ...mainState,
223
278
  current_phase: firstPending?.phase ?? phaseList[0].phase,
224
- current_milestone: milestoneName
279
+ current_milestone: milestoneName,
280
+ // Carry over milestone-scoped artifacts to worktree
281
+ artifacts: useArtifactRegistry
282
+ ? artifacts.filter(a => a.milestone === milestoneName || ownedPhaseNumbers.includes(a.phase))
283
+ : []
225
284
  }
226
285
  Write {wtPath}/.workflow/state.json: scopedState
227
286
  ```
@@ -260,14 +319,20 @@ registry.fork_sessions.push({
260
319
 
261
320
  Write .workflow/worktrees.json: registry
262
321
 
263
- // Mark milestone phases as "forked" in main
264
- for (p of phaseList):
265
- IF p.status !== "completed":
266
- NN = String(p.phase).padStart(2, '0')
267
- Read .workflow/phases/{NN}-{p.slug}/index.json idx
268
- idx.status = "forked"
269
- idx.updated_at = getUtc8ISOString()
270
- Write .workflow/phases/{NN}-{p.slug}/index.json: idx
322
+ // Mark milestone phases as "forked"
323
+ IF useArtifactRegistry:
324
+ // In artifact registry model, worktrees.json tracks forked state.
325
+ // No per-phase marking needed — the registry entry signals ownership.
326
+ // (worktrees.json already updated above with owned_phases)
327
+ ELSE:
328
+ // Legacy: mark phase index.json as forked
329
+ for (p of phaseList):
330
+ IF p.status !== "completed":
331
+ NN = String(p.phase).padStart(2, '0')
332
+ Read .workflow/phases/{NN}-{p.slug}/index.json → idx
333
+ idx.status = "forked"
334
+ idx.updated_at = getUtc8ISOString()
335
+ Write .workflow/phases/{NN}-{p.slug}/index.json: idx
271
336
 
272
337
  mainState.last_updated = getUtc8ISOString()
273
338
  Write .workflow/state.json: mainState
@@ -295,7 +360,6 @@ Display:
295
360
  /maestro-plan {firstPending.phase}
296
361
  /maestro-execute {firstPending.phase}
297
362
  /maestro-verify {firstPending.phase}
298
- /maestro-phase-transition {firstPending.phase}
299
363
  # ... repeat for next phases in milestone
300
364
 
301
365
  Or delegate (automated):
@@ -15,13 +15,25 @@ L0-L3 progressive layers: Static Analysis -> Unit -> Integration -> E2E
15
15
  | Input | Result |
16
16
  |-------|--------|
17
17
  | No arguments | Error E001 |
18
- | Phase number | Resolve `.workflow/phases/{NN}-{slug}/` |
18
+ | Phase number | Resolve phase dir (artifact registry or legacy `.workflow/phases/{NN}-{slug}/`) |
19
19
  | `--max-iter N` | Set MAX_ITER = N (default 5) |
20
20
  | `--layer L2` | Start from L2 layer |
21
21
 
22
+ **Resolve phase dir:**
23
+ ```
24
+ Read .workflow/state.json → state
25
+ artifacts = state.artifacts ?? []
26
+ IF artifacts.length > 0:
27
+ art = artifacts.find(a => a.type === 'execute' && a.phase === phaseNum)
28
+ PHASE_DIR = ".workflow/" + art.path
29
+ ELSE:
30
+ Glob: .workflow/phases/{NN}-*/
31
+ PHASE_DIR = resolved path
32
+ ```
33
+
22
34
  Check for existing integration test session:
23
35
  ```bash
24
- ls .workflow/phases/{NN}-*/.tests/integration/state.json 2>/dev/null
36
+ ls ${PHASE_DIR}/.tests/integration/state.json 2>/dev/null
25
37
  ```
26
38
 
27
39
  If session exists: offer resume or restart.
@@ -178,8 +178,13 @@ Cross-milestone conflict check (for supplement issues):
178
178
  6. IF source == "supplement" AND milestone_ref is not null:
179
179
  a. Read .workflow/roadmap.md
180
180
  b. Identify phases belonging to OTHER milestones (not milestone_ref)
181
- c. For each other-milestone phase, check if plan.json exists:
182
- Read .workflow/phases/{NN}-{slug}/plan.json (if exists)
181
+ c. For each other-milestone phase, resolve phase dir and check if plan.json exists:
182
+ Read .workflow/state.json state; artifacts = state.artifacts ?? []
183
+ IF artifacts.length > 0:
184
+ art = artifacts.find(a => a.type === 'plan' && a.phase === phaseNum)
185
+ IF art: Read .workflow/{art.path}/plan.json (if exists)
186
+ ELSE:
187
+ Read .workflow/phases/{NN}-{slug}/plan.json (if exists)
183
188
  Collect files_to_create[] as planned_files
184
189
  d. IF affected_components in the new issue overlap with planned_files:
185
190
  WARNING: "Conflict detected: this supplement issue affects components planned in milestone {other_milestone}"
@@ -449,8 +454,13 @@ Process bidirectional link:
449
454
  If not found → error: "Issue {ID} not found"
450
455
 
451
456
  2. Locate task file:
452
- - Search .workflow/phases/*/.task/{TASK_ID}.json
453
- - If not found search .workflow/scratch/*/.task/{TASK_ID}.json
457
+ - Read .workflow/state.json → state
458
+ - artifacts = state.artifacts ?? []
459
+ - IF artifacts.length > 0:
460
+ Search .workflow/scratch/*/.task/{TASK_ID}.json (artifact registry scratch paths)
461
+ ELSE:
462
+ Search .workflow/phases/*/.task/{TASK_ID}.json (legacy)
463
+ - Also search .workflow/scratch/*/.task/{TASK_ID}.json (standalone scratch tasks)
454
464
  - If still not found → error: "Task {TASK_ID} not found"
455
465
 
456
466
  3. Update issue record:
@@ -86,9 +86,14 @@ IF .workflow/state.json exists:
86
86
  IF state.current_phase is not null:
87
87
  phase = state.current_phase
88
88
 
89
- # Resolve slug
90
- Glob ".workflow/phases/{NN}-*/" where NN == phase
91
- phase_slug = matched directory basename (e.g. "01-auth")
89
+ # Resolve slug — artifact registry first, fallback to legacy phases/
90
+ artifacts = state.artifacts ?? []
91
+ IF artifacts.length > 0:
92
+ art = artifacts.find(a => a.phase === phase)
93
+ phase_slug = art?.slug ?? "phase-" + phase
94
+ ELSE:
95
+ Glob ".workflow/phases/{NN}-*/" where NN == phase
96
+ phase_slug = matched directory basename (e.g. "01-auth")
92
97
  ```
93
98
 
94
99
  If `--phase 0` is passed, force `phase = null, phase_slug = null` regardless.
@@ -299,8 +304,17 @@ IF row is null → error E004: "Insight {target_id} not found"
299
304
  ```
300
305
  phase_context = null
301
306
  IF row.phase_slug is not null:
302
- phase_dir = ".workflow/phases/" + row.phase_slug
303
- IF directory exists:
307
+ // Resolve phase dir — artifact registry first, fallback to legacy phases/
308
+ Read .workflow/state.json → state
309
+ artifacts = state.artifacts ?? []
310
+ phase_dir = null
311
+ IF artifacts.length > 0:
312
+ art = artifacts.find(a => a.phase === row.phase && a.path)
313
+ IF art: phase_dir = ".workflow/" + art.path
314
+ ELSE:
315
+ phase_dir = ".workflow/phases/" + row.phase_slug
316
+
317
+ IF phase_dir AND directory exists:
304
318
  phase_context = {
305
319
  title: read index.json.title from phase_dir,
306
320
  status: read index.json.status,
@@ -789,6 +789,7 @@ const context = {
789
789
  const AUTO_FLAG_MAP = {
790
790
  'maestro-analyze': '-y',
791
791
  'maestro-brainstorm': '-y',
792
+ 'maestro-roadmap': '-y',
792
793
  'maestro-ui-design': '-y',
793
794
  'maestro-plan': '--auto',
794
795
  'maestro-spec-generate': '-y',
@@ -103,15 +103,31 @@ IF NOT target:
103
103
  completedPhases = []
104
104
  incompletePhases = []
105
105
 
106
- for (phaseNum of target.owned_phases):
107
- NN = String(phaseNum).padStart(2, '0')
108
- Glob: {target.path}/.workflow/phases/{NN}-*/index.json
109
- Read wtIndex
106
+ // Read worktree state for artifact registry check
107
+ Read {target.path}/.workflow/state.json → wtState (if exists)
108
+ wtArtifacts = wtState?.artifacts ?? []
109
+ useArtifactRegistry = wtArtifacts.length > 0
110
+
111
+ IF useArtifactRegistry:
112
+ // Check phase completeness via artifact registry
113
+ for (phaseNum of target.owned_phases):
114
+ execArtifacts = wtArtifacts.filter(a => a.type === 'execute' && a.phase === phaseNum)
115
+ IF execArtifacts.some(a => a.status === 'completed'):
116
+ completedPhases.push({ phase: phaseNum })
117
+ ELSE:
118
+ status = execArtifacts.length > 0 ? execArtifacts[0].status : 'no_execute_artifact'
119
+ incompletePhases.push({ phase: phaseNum, status })
120
+ ELSE:
121
+ // Legacy: check phases/ directory
122
+ for (phaseNum of target.owned_phases):
123
+ NN = String(phaseNum).padStart(2, '0')
124
+ Glob: {target.path}/.workflow/phases/{NN}-*/index.json
125
+ Read → wtIndex
110
126
 
111
- IF wtIndex.status === "completed":
112
- completedPhases.push({ phase: phaseNum, index: wtIndex })
113
- ELSE:
114
- incompletePhases.push({ phase: phaseNum, status: wtIndex.status })
127
+ IF wtIndex.status === "completed":
128
+ completedPhases.push({ phase: phaseNum, index: wtIndex })
129
+ ELSE:
130
+ incompletePhases.push({ phase: phaseNum, status: wtIndex.status })
115
131
 
116
132
  IF incompletePhases.length > 0 AND NOT force:
117
133
  WARN W002: "M{target.milestone_num} ({target.milestone}) has incomplete phases:"
@@ -181,51 +197,90 @@ Step_7:
181
197
 
182
198
  Display "Syncing workflow artifacts for M{target.milestone_num} ({target.milestone})..."
183
199
 
184
- // 7a: Copy all owned phase directories from worktree to main
185
- for (phaseNum of target.owned_phases):
186
- NN = String(phaseNum).padStart(2, '0')
187
- Glob: {target.path}/.workflow/phases/{NN}-*/
188
- phaseDir = matched directory name
189
-
190
- srcDir = target.path + "/.workflow/phases/" + phaseDir + "/"
191
- dstDir = ".workflow/phases/" + phaseDir + "/"
192
-
193
- Bash("cp -r {srcDir}* {dstDir}")
194
-
195
- // 7b: Atomic state reconciliation
196
200
  Read .workflow/state.json → mainState
197
201
 
198
- for (phaseNum of target.owned_phases):
199
- NN = String(phaseNum).padStart(2, '0')
200
- Glob: .workflow/phases/{NN}-*/index.json
201
- Read phaseIndex
202
-
203
- IF phaseIndex.status === "completed":
204
- mainState.phases_summary.completed += 1
205
- mainState.phases_summary.pending -= 1
206
- IF mainState.phases_summary.pending < 0:
207
- mainState.phases_summary.pending = 0
208
-
209
- mainState.transition_history = mainState.transition_history ?? []
210
- mainState.transition_history.push({
211
- milestone_num: target.milestone_num,
212
- milestone: target.milestone,
213
- phase: phaseNum,
214
- action: "worktree_merge",
215
- completed_at: phaseIndex.completed_at ?? getUtc8ISOString(),
216
- branch: target.branch
217
- })
218
- ELSE IF phaseIndex.status !== "forked":
219
- mainState.phases_summary.in_progress += 1
220
- mainState.phases_summary.pending -= 1
221
- IF mainState.phases_summary.pending < 0:
222
- mainState.phases_summary.pending = 0
223
-
224
- phaseIndex.updated_at = getUtc8ISOString()
225
- Write .workflow/phases/{NN}-{slug}/index.json: phaseIndex
226
-
227
- // Merge accumulated context from worktree
228
- Read target.path + "/.workflow/state.json" → wtState (if exists)
202
+ IF useArtifactRegistry:
203
+ // 7a: Copy scratch dirs from worktree to main
204
+ Read {target.path}/.workflow/state.json → wtState
205
+ wtArtifacts = wtState?.artifacts ?? []
206
+ milestoneArtifacts = wtArtifacts.filter(a =>
207
+ a.path && (a.milestone === target.milestone || target.owned_phases.includes(a.phase))
208
+ )
209
+ for (art of milestoneArtifacts):
210
+ srcDir = target.path + "/.workflow/" + art.path
211
+ dstDir = ".workflow/" + art.path
212
+ IF directory_exists(srcDir):
213
+ Bash("mkdir -p {dstDir} && cp -r {srcDir}/* {dstDir}/")
214
+
215
+ // 7b: Merge artifact registries
216
+ mainArtifacts = mainState.artifacts ?? []
217
+ existingIds = new Set(mainArtifacts.map(a => a.id))
218
+ for (art of wtArtifacts):
219
+ IF existingIds.has(art.id):
220
+ // Update existing artifact status
221
+ idx = mainArtifacts.findIndex(a => a.id === art.id)
222
+ mainArtifacts[idx] = art
223
+ ELSE:
224
+ mainArtifacts.push(art)
225
+ mainState.artifacts = mainArtifacts
226
+
227
+ // Record merge in transition history
228
+ mainState.transition_history = mainState.transition_history ?? []
229
+ mainState.transition_history.push({
230
+ milestone_num: target.milestone_num,
231
+ milestone: target.milestone,
232
+ action: "worktree_merge",
233
+ completed_at: getUtc8ISOString(),
234
+ branch: target.branch,
235
+ phases: target.owned_phases
236
+ })
237
+
238
+ ELSE:
239
+ // Legacy: copy phase directories from worktree to main
240
+ // 7a: Copy all owned phase directories
241
+ for (phaseNum of target.owned_phases):
242
+ NN = String(phaseNum).padStart(2, '0')
243
+ Glob: {target.path}/.workflow/phases/{NN}-*/
244
+ phaseDir = matched directory name
245
+
246
+ srcDir = target.path + "/.workflow/phases/" + phaseDir + "/"
247
+ dstDir = ".workflow/phases/" + phaseDir + "/"
248
+
249
+ Bash("cp -r {srcDir}* {dstDir}")
250
+
251
+ // 7b: Update phase summaries
252
+ for (phaseNum of target.owned_phases):
253
+ NN = String(phaseNum).padStart(2, '0')
254
+ Glob: .workflow/phases/{NN}-*/index.json
255
+ Read → phaseIndex
256
+
257
+ IF phaseIndex.status === "completed":
258
+ mainState.phases_summary.completed += 1
259
+ mainState.phases_summary.pending -= 1
260
+ IF mainState.phases_summary.pending < 0:
261
+ mainState.phases_summary.pending = 0
262
+
263
+ mainState.transition_history = mainState.transition_history ?? []
264
+ mainState.transition_history.push({
265
+ milestone_num: target.milestone_num,
266
+ milestone: target.milestone,
267
+ phase: phaseNum,
268
+ action: "worktree_merge",
269
+ completed_at: phaseIndex.completed_at ?? getUtc8ISOString(),
270
+ branch: target.branch
271
+ })
272
+ ELSE IF phaseIndex.status !== "forked":
273
+ mainState.phases_summary.in_progress += 1
274
+ mainState.phases_summary.pending -= 1
275
+ IF mainState.phases_summary.pending < 0:
276
+ mainState.phases_summary.pending = 0
277
+
278
+ phaseIndex.updated_at = getUtc8ISOString()
279
+ Write .workflow/phases/{NN}-{slug}/index.json: phaseIndex
280
+
281
+ // Merge accumulated context from worktree (both paths)
282
+ IF NOT useArtifactRegistry:
283
+ Read target.path + "/.workflow/state.json" → wtState (if exists)
229
284
  IF wtState?.accumulated_context:
230
285
  for (decision of (wtState.accumulated_context.key_decisions ?? [])):
231
286
  IF NOT mainState.accumulated_context.key_decisions.includes(decision):
@@ -236,12 +291,15 @@ IF wtState?.accumulated_context:
236
291
  mainState.last_updated = getUtc8ISOString()
237
292
  Write .workflow/state.json: mainState
238
293
 
239
- // 7c: Update roadmap.md
294
+ // 7c: Update roadmap.md (mark completed phases)
240
295
  Read .workflow/roadmap.md → roadmap
241
296
  for (phaseNum of target.owned_phases):
242
- Read phase index → check if completed
243
- IF completed:
244
- Append " ✅ COMPLETED" to phase title line
297
+ IF useArtifactRegistry:
298
+ isCompleted = completedPhases.some(p => p.phase === phaseNum)
299
+ ELSE:
300
+ Read phase index → isCompleted = (status === "completed")
301
+ IF isCompleted:
302
+ Append " ✅ COMPLETED" to phase title line in roadmap
245
303
  Write .workflow/roadmap.md: roadmap
246
304
  ```
247
305
 
@@ -9,7 +9,7 @@ This is a **post-execution analysis** workflow. It reads only — until the rout
9
9
  ## Prerequisites
10
10
 
11
11
  - `.workflow/` initialized (`.workflow/state.json` exists)
12
- - At least one phase directory under `.workflow/phases/{NN}-{slug}/`
12
+ - At least one completed phase (via artifact registry in state.json, or legacy `.workflow/phases/{NN}-{slug}/`)
13
13
  - Target phase has been executed (has `.task/` and `.summaries/`)
14
14
  - `maestro delegate` available (used for the four lens analyses via Agent calls)
15
15
 
@@ -61,19 +61,46 @@ This is a **post-execution analysis** workflow. It reads only — until the rout
61
61
 
62
62
  ```
63
63
  candidates = []
64
- FOR each .workflow/phases/{NN}-{slug}/index.json:
65
- Read index.json
66
- IF index.json.status == "completed":
67
- has_retro = file exists at "{phase_dir}/retrospective.json"
68
- candidates.push({
69
- number: NN,
70
- slug: slug,
71
- title: index.json.title or slug,
72
- completed_at: index.json.completed_at,
73
- has_retro: has_retro,
74
- gaps: index.json.verification?.gaps?.length or 0,
75
- review_verdict: index.json.review?.verdict or "—"
76
- })
64
+
65
+ Read .workflow/state.json → state
66
+ artifacts = state.artifacts ?? []
67
+ useArtifactRegistry = artifacts.length > 0
68
+
69
+ IF useArtifactRegistry:
70
+ // Resolve completed phases from artifact registry
71
+ phaseNums = [...new Set(artifacts.map(a => a.phase).filter(Boolean))]
72
+ FOR each phaseNum in phaseNums:
73
+ execArt = artifacts.find(a => a.type === 'execute' && a.phase === phaseNum && a.status === 'completed')
74
+ IF execArt:
75
+ scratchDir = ".workflow/" + execArt.path
76
+ has_retro = file exists at "{scratchDir}/retrospective.json"
77
+ candidates.push({
78
+ number: phaseNum,
79
+ slug: execArt.slug ?? "phase-" + phaseNum,
80
+ title: execArt.title ?? execArt.slug ?? "Phase " + phaseNum,
81
+ completed_at: execArt.completed_at,
82
+ has_retro: has_retro,
83
+ phase_dir: scratchDir,
84
+ gaps: 0,
85
+ review_verdict: "—"
86
+ })
87
+ ELSE:
88
+ // Legacy: scan phases/ directory
89
+ FOR each .workflow/phases/{NN}-{slug}/index.json:
90
+ Read index.json
91
+ IF index.json.status == "completed":
92
+ phase_dir = ".workflow/phases/{NN}-{slug}"
93
+ has_retro = file exists at "{phase_dir}/retrospective.json"
94
+ candidates.push({
95
+ number: NN,
96
+ slug: slug,
97
+ title: index.json.title or slug,
98
+ completed_at: index.json.completed_at,
99
+ has_retro: has_retro,
100
+ phase_dir: phase_dir,
101
+ gaps: index.json.verification?.gaps?.length or 0,
102
+ review_verdict: index.json.review?.verdict or "—"
103
+ })
77
104
  ```
78
105
 
79
106
  ### Display backlog
@@ -103,24 +130,26 @@ FOR each .workflow/phases/{NN}-{slug}/index.json:
103
130
 
104
131
  If overwriting existing retrospective.json:
105
132
  ```
106
- mkdir -p "{phase_dir}/.history"
133
+ mkdir -p "{candidate.phase_dir}/.history"
107
134
  TIMESTAMP = format(now(), "YYYY-MM-DDTHH-mm-ss")
108
- mv "{phase_dir}/retrospective.json" "{phase_dir}/.history/retrospective-{TIMESTAMP}.json"
109
- mv "{phase_dir}/retrospective.md" "{phase_dir}/.history/retrospective-{TIMESTAMP}.md"
135
+ mv "{candidate.phase_dir}/retrospective.json" "{candidate.phase_dir}/.history/retrospective-{TIMESTAMP}.json"
136
+ mv "{candidate.phase_dir}/retrospective.md" "{candidate.phase_dir}/.history/retrospective-{TIMESTAMP}.md"
110
137
  ```
111
138
 
112
139
  ---
113
140
 
114
141
  ## Stage 3: load_artifacts (per phase)
115
142
 
116
- For each selected phase, build the in-memory artifacts bundle:
143
+ For each selected phase (using `candidate.phase_dir` resolved in Stage 2), build the in-memory artifacts bundle:
117
144
 
118
145
  ```
146
+ phase_dir = candidate.phase_dir // already resolved: scratch path (artifact registry) or legacy phases/ path
147
+
119
148
  artifacts = {
120
149
  phase_num: NN,
121
150
  phase_slug: slug,
122
- phase_dir: ".workflow/phases/{NN}-{slug}",
123
- index: read JSON,
151
+ phase_dir: phase_dir,
152
+ index: read "{phase_dir}/index.json" or null,
124
153
  state: read .workflow/state.json,
125
154
  plan: read "{phase_dir}/plan.json" or null,
126
155
  verification: read "{phase_dir}/verification.json" or null,
@@ -130,7 +159,17 @@ artifacts = {
130
159
  task_jsons: read all "{phase_dir}/.task/TASK-*.json",
131
160
  phase_issues: filter ".workflow/issues/issues.jsonl" + ".workflow/issues/issue-history.jsonl"
132
161
  where issue.phase_ref == phase_slug or issue.phase_ref == NN,
133
- prior_retro: (if --compare M) read .workflow/phases/{MM}-*/retrospective.json
162
+ prior_retro: null
163
+ }
164
+
165
+ // Resolve --compare target
166
+ IF --compare M:
167
+ IF useArtifactRegistry:
168
+ compareArt = state.artifacts.find(a => a.type === 'execute' && a.phase === M)
169
+ IF compareArt:
170
+ artifacts.prior_retro = read ".workflow/" + compareArt.path + "/retrospective.json"
171
+ ELSE:
172
+ artifacts.prior_retro = read .workflow/phases/{MM}-*/retrospective.json
134
173
  }
135
174
  ```
136
175
 
@@ -422,7 +461,7 @@ retrospective = {
422
461
  | {INS-id} | note | ... |
423
462
  ```
424
463
 
425
- Write both files:
464
+ Write both files (phase_dir already resolved in Stage 2):
426
465
  ```
427
466
  Write "{phase_dir}/retrospective.json"
428
467
  Write "{phase_dir}/retrospective.md"
@@ -15,10 +15,23 @@ Tiered multi-dimensional code review with parallel agents, severity classificati
15
15
 
16
16
  ```
17
17
  Input: <phase> argument (number or slug)
18
- 1. If number: find .workflow/phases/{NN}-*/index.json
19
- 2. If slug: find .workflow/phases/*-{slug}/index.json
20
- 3. Validate execution has occurred (index.json.execution.tasks_completed > 0)
21
- 4. Set PHASE_DIR = resolved path
18
+
19
+ Read .workflow/state.json → state
20
+ artifacts = state.artifacts ?? []
21
+ useArtifactRegistry = artifacts.length > 0
22
+
23
+ IF useArtifactRegistry:
24
+ IF number: art = artifacts.find(a => a.type === 'execute' && a.phase === number)
25
+ IF slug: art = artifacts.find(a => a.type === 'execute' && a.slug?.includes(slug))
26
+ IF art: PHASE_DIR = ".workflow/" + art.path
27
+ ELSE: ERROR "Phase not found in artifact registry"
28
+ ELSE:
29
+ // Legacy: resolve from phases/ directory
30
+ IF number: find .workflow/phases/{NN}-*/index.json
31
+ IF slug: find .workflow/phases/*-{slug}/index.json
32
+ PHASE_DIR = resolved path
33
+
34
+ Validate execution has occurred (tasks_completed > 0 or .task/ exists)
22
35
  ```
23
36
 
24
37
  ---