@bhargavvc/sdd-cc 1.30.1 → 1.42.3

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 (1392) hide show
  1. package/README.ja-JP.md +165 -129
  2. package/README.ko-KR.md +161 -123
  3. package/README.md +103 -679
  4. package/README.pt-BR.md +92 -52
  5. package/README.zh-CN.md +145 -103
  6. package/agents/sdd-advisor-researcher.md +23 -0
  7. package/agents/sdd-ai-researcher.md +133 -0
  8. package/agents/sdd-code-fixer.md +668 -0
  9. package/agents/sdd-code-reviewer.md +387 -0
  10. package/agents/sdd-codebase-mapper.md +86 -3
  11. package/agents/sdd-debug-session-manager.md +314 -0
  12. package/agents/sdd-debugger.md +157 -78
  13. package/agents/sdd-doc-classifier.md +168 -0
  14. package/agents/sdd-doc-synthesizer.md +204 -0
  15. package/agents/sdd-doc-verifier.md +217 -0
  16. package/agents/sdd-doc-writer.md +615 -0
  17. package/agents/sdd-domain-researcher.md +153 -0
  18. package/agents/sdd-eval-auditor.md +191 -0
  19. package/agents/sdd-eval-planner.md +154 -0
  20. package/agents/sdd-executor.md +283 -40
  21. package/agents/sdd-framework-selector.md +160 -0
  22. package/agents/sdd-integration-checker.md +30 -3
  23. package/agents/sdd-intel-updater.md +342 -0
  24. package/agents/sdd-nyquist-auditor.md +31 -4
  25. package/agents/sdd-pattern-mapper.md +335 -0
  26. package/agents/sdd-phase-researcher.md +254 -24
  27. package/agents/sdd-plan-checker.md +223 -18
  28. package/agents/sdd-planner.md +286 -362
  29. package/agents/sdd-project-researcher.md +28 -5
  30. package/agents/sdd-research-synthesizer.md +4 -4
  31. package/agents/sdd-roadmapper.md +14 -5
  32. package/agents/sdd-security-auditor.md +155 -0
  33. package/agents/sdd-ui-auditor.md +60 -4
  34. package/agents/sdd-ui-checker.md +11 -2
  35. package/agents/sdd-ui-researcher.md +27 -4
  36. package/agents/sdd-user-profiler.md +2 -2
  37. package/agents/sdd-verifier.md +258 -41
  38. package/bin/install.js +6862 -618
  39. package/bin/sdd-sdk.js +37 -0
  40. package/commands/sdd/add-tests.md +3 -2
  41. package/commands/sdd/ai-integration-phase.md +37 -0
  42. package/commands/sdd/audit-fix.md +34 -0
  43. package/commands/sdd/audit-milestone.md +3 -2
  44. package/commands/sdd/autonomous.md +10 -5
  45. package/commands/sdd/capture.md +62 -0
  46. package/commands/sdd/cleanup.md +7 -1
  47. package/commands/sdd/code-review.md +59 -0
  48. package/commands/sdd/complete-milestone.md +11 -4
  49. package/commands/sdd/config.md +58 -0
  50. package/commands/sdd/debug.md +23 -144
  51. package/commands/sdd/discuss-phase.md +22 -10
  52. package/commands/sdd/docs-update.md +49 -0
  53. package/commands/sdd/eval-review.md +33 -0
  54. package/commands/sdd/execute-phase.md +9 -4
  55. package/commands/sdd/explore.md +27 -0
  56. package/commands/sdd/extract-learnings.md +23 -0
  57. package/commands/sdd/fast.md +2 -1
  58. package/commands/sdd/forensics.md +3 -2
  59. package/commands/sdd/graphify.md +199 -0
  60. package/commands/sdd/health.md +12 -3
  61. package/commands/sdd/help.md +3 -1
  62. package/commands/sdd/import.md +41 -0
  63. package/commands/sdd/inbox.md +39 -0
  64. package/commands/sdd/ingest-docs.md +42 -0
  65. package/commands/sdd/manager.md +9 -3
  66. package/commands/sdd/map-codebase.md +15 -3
  67. package/commands/sdd/milestone-summary.md +1 -1
  68. package/commands/sdd/mvp-phase.md +45 -0
  69. package/commands/sdd/new-milestone.md +3 -2
  70. package/commands/sdd/new-project.md +7 -2
  71. package/commands/sdd/ns-context.md +23 -0
  72. package/commands/sdd/ns-ideate.md +24 -0
  73. package/commands/sdd/ns-manage.md +29 -0
  74. package/commands/sdd/ns-project.md +22 -0
  75. package/commands/sdd/ns-review.md +26 -0
  76. package/commands/sdd/ns-workflow.md +28 -0
  77. package/commands/sdd/pause-work.md +6 -1
  78. package/commands/sdd/phase.md +56 -0
  79. package/commands/sdd/plan-phase.md +19 -4
  80. package/commands/sdd/plan-review-convergence.md +59 -0
  81. package/commands/sdd/pr-branch.md +2 -1
  82. package/commands/sdd/profile-user.md +2 -2
  83. package/commands/sdd/progress.md +27 -5
  84. package/commands/sdd/quick.md +132 -5
  85. package/commands/sdd/resume-work.md +2 -12
  86. package/commands/sdd/review-backlog.md +4 -2
  87. package/commands/sdd/review.md +7 -3
  88. package/commands/sdd/secure-phase.md +36 -0
  89. package/commands/sdd/settings.md +2 -9
  90. package/commands/sdd/ship.md +1 -0
  91. package/commands/sdd/sketch.md +60 -0
  92. package/commands/sdd/spec-phase.md +63 -0
  93. package/commands/sdd/spike.md +57 -0
  94. package/commands/sdd/stats.md +2 -1
  95. package/commands/sdd/surface.md +129 -0
  96. package/commands/sdd/thread.md +8 -111
  97. package/commands/sdd/ui-phase.md +3 -2
  98. package/commands/sdd/ui-review.md +3 -2
  99. package/commands/sdd/ultraplan-phase.md +34 -0
  100. package/commands/sdd/undo.md +35 -0
  101. package/commands/sdd/update.md +21 -10
  102. package/commands/sdd/validate-phase.md +3 -2
  103. package/commands/sdd/verify-work.md +4 -3
  104. package/commands/sdd/workspace.md +52 -0
  105. package/commands/sdd/workstreams.md +15 -8
  106. package/hooks/dist/sdd-check-update-worker.js +116 -0
  107. package/hooks/dist/sdd-check-update.js +19 -69
  108. package/hooks/dist/sdd-context-monitor.js +43 -7
  109. package/hooks/dist/sdd-phase-boundary.sh +47 -0
  110. package/hooks/dist/sdd-prompt-guard.js +1 -0
  111. package/hooks/dist/sdd-read-guard.js +101 -0
  112. package/hooks/dist/sdd-read-injection-scanner.js +152 -0
  113. package/hooks/dist/sdd-session-state.sh +59 -0
  114. package/hooks/dist/sdd-statusline.js +439 -21
  115. package/hooks/dist/sdd-update-banner.js +134 -0
  116. package/hooks/dist/sdd-validate-commit.sh +57 -0
  117. package/hooks/dist/sdd-workflow-guard.js +2 -2
  118. package/hooks/lib/git-cmd.js +150 -0
  119. package/hooks/sdd-check-update-worker.js +116 -0
  120. package/hooks/sdd-check-update.js +64 -0
  121. package/hooks/sdd-context-monitor.js +192 -0
  122. package/hooks/sdd-phase-boundary.sh +47 -0
  123. package/hooks/sdd-prompt-guard.js +97 -0
  124. package/hooks/sdd-read-guard.js +101 -0
  125. package/hooks/sdd-read-injection-scanner.js +152 -0
  126. package/hooks/sdd-session-state.sh +59 -0
  127. package/hooks/sdd-statusline.js +537 -0
  128. package/hooks/sdd-update-banner.js +134 -0
  129. package/hooks/sdd-validate-commit.sh +57 -0
  130. package/hooks/sdd-workflow-guard.js +94 -0
  131. package/package.json +34 -9
  132. package/scripts/audit-workflow-script-paths.cjs +73 -0
  133. package/scripts/build-hooks.js +114 -9
  134. package/scripts/changeset/cli.cjs +269 -0
  135. package/scripts/changeset/github-release-notes.cjs +198 -0
  136. package/scripts/changeset/lint.cjs +110 -0
  137. package/scripts/changeset/new.cjs +137 -0
  138. package/scripts/changeset/parse.cjs +60 -0
  139. package/scripts/changeset/render.cjs +34 -0
  140. package/scripts/changeset/serialize.cjs +74 -0
  141. package/scripts/command-contract-helpers.cjs +61 -0
  142. package/scripts/diff-touches-shipped-paths.cjs +147 -0
  143. package/scripts/fix-slash-commands.cjs +106 -0
  144. package/scripts/gen-inventory-manifest.cjs +109 -0
  145. package/scripts/lint-command-contract.cjs +108 -0
  146. package/scripts/lint-descriptions.cjs +83 -0
  147. package/scripts/lint-no-source-grep-extras.cjs +81 -0
  148. package/scripts/lint-no-source-grep.cjs +174 -0
  149. package/scripts/lint-shell-command-projection-drift.cjs +57 -0
  150. package/scripts/lint-skill-deps.cjs +180 -0
  151. package/scripts/pr-template-policy.cjs +169 -0
  152. package/scripts/prompt-injection-scan.sh +3 -0
  153. package/scripts/rebrand-gsd-to-sdd.sh +222 -220
  154. package/scripts/run-tests.cjs +5 -1
  155. package/scripts/strip-prose-atrefs.cjs +106 -0
  156. package/scripts/verify-tarball-sdk-dist.sh +69 -0
  157. package/sdd/bin/check-latest-version.cjs +104 -0
  158. package/sdd/bin/lib/active-workstream-store.cjs +85 -0
  159. package/sdd/bin/lib/adr-parser.cjs +394 -0
  160. package/sdd/bin/lib/artifacts.cjs +53 -0
  161. package/sdd/bin/lib/audit.cjs +755 -0
  162. package/sdd/bin/lib/cjs-command-router-adapter.cjs +39 -0
  163. package/sdd/bin/lib/clusters.cjs +135 -0
  164. package/sdd/bin/lib/command-aliases.generated.cjs +838 -0
  165. package/sdd/bin/lib/commands.cjs +179 -107
  166. package/sdd/bin/lib/config-schema.cjs +135 -0
  167. package/sdd/bin/lib/config.cjs +313 -86
  168. package/sdd/bin/lib/context-utilization.cjs +47 -0
  169. package/sdd/bin/lib/core.cjs +1146 -391
  170. package/sdd/bin/lib/decisions.cjs +48 -0
  171. package/sdd/bin/lib/docs.cjs +270 -0
  172. package/sdd/bin/lib/drift.cjs +379 -0
  173. package/sdd/bin/lib/fallow-runner.cjs +109 -0
  174. package/sdd/bin/lib/frontmatter.cjs +389 -336
  175. package/sdd/bin/lib/gap-checker.cjs +197 -0
  176. package/sdd/bin/lib/graphify.cjs +577 -0
  177. package/sdd/bin/lib/init-command-router.cjs +70 -0
  178. package/sdd/bin/lib/init.cjs +692 -97
  179. package/sdd/bin/lib/install-profiles.cjs +572 -0
  180. package/sdd/bin/lib/installer-migration-authoring.cjs +117 -0
  181. package/sdd/bin/lib/installer-migration-report.cjs +328 -0
  182. package/sdd/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
  183. package/sdd/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
  184. package/sdd/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
  185. package/sdd/bin/lib/installer-migrations.cjs +703 -0
  186. package/sdd/bin/lib/intel.cjs +643 -0
  187. package/sdd/bin/lib/learnings.cjs +379 -0
  188. package/sdd/bin/lib/milestone.cjs +313 -252
  189. package/sdd/bin/lib/model-catalog.cjs +136 -0
  190. package/sdd/bin/lib/model-profiles.cjs +25 -68
  191. package/sdd/bin/lib/phase-command-router.cjs +96 -0
  192. package/sdd/bin/lib/phase.cjs +868 -335
  193. package/sdd/bin/lib/phases-command-router.cjs +39 -0
  194. package/sdd/bin/lib/plan-scan.cjs +138 -0
  195. package/sdd/bin/lib/planning-workspace.cjs +361 -0
  196. package/sdd/bin/lib/profile-output.cjs +197 -35
  197. package/sdd/bin/lib/profile-pipeline.cjs +1 -1
  198. package/sdd/bin/lib/review-reviewer-selection.cjs +125 -0
  199. package/sdd/bin/lib/roadmap-command-router.cjs +23 -0
  200. package/sdd/bin/lib/roadmap.cjs +416 -124
  201. package/sdd/bin/lib/runtime-homes.cjs +178 -0
  202. package/sdd/bin/lib/schema-detect.cjs +238 -0
  203. package/sdd/bin/lib/sdd2-import.cjs +511 -0
  204. package/sdd/bin/lib/secrets.cjs +33 -0
  205. package/sdd/bin/lib/security.cjs +131 -9
  206. package/sdd/bin/lib/shell-command-projection.cjs +548 -0
  207. package/sdd/bin/lib/state-command-router.cjs +100 -0
  208. package/sdd/bin/lib/state-document.cjs +12 -0
  209. package/sdd/bin/lib/state-document.generated.cjs +127 -0
  210. package/sdd/bin/lib/state.cjs +1253 -367
  211. package/sdd/bin/lib/surface.cjs +398 -0
  212. package/sdd/bin/lib/template.cjs +11 -5
  213. package/sdd/bin/lib/uat.cjs +9 -2
  214. package/sdd/bin/lib/validate-command-router.cjs +55 -0
  215. package/sdd/bin/lib/verify-command-router.cjs +34 -0
  216. package/sdd/bin/lib/verify.cjs +648 -140
  217. package/sdd/bin/lib/workstream-inventory.cjs +159 -0
  218. package/sdd/bin/lib/workstream-name-policy.cjs +33 -0
  219. package/sdd/bin/lib/workstream.cjs +78 -196
  220. package/sdd/bin/lib/worktree-safety.cjs +563 -0
  221. package/sdd/bin/sdd-tools.cjs +528 -222
  222. package/sdd/bin/verify-reapply-patches.cjs +247 -0
  223. package/sdd/contexts/dev.md +21 -0
  224. package/sdd/contexts/research.md +22 -0
  225. package/sdd/contexts/review.md +23 -0
  226. package/sdd/references/agent-contracts.md +79 -0
  227. package/sdd/references/ai-evals.md +156 -0
  228. package/sdd/references/ai-frameworks.md +186 -0
  229. package/sdd/references/artifact-types.md +131 -0
  230. package/sdd/references/autonomous-smart-discuss.md +277 -0
  231. package/sdd/references/checkpoints.md +36 -0
  232. package/sdd/references/common-bug-patterns.md +114 -0
  233. package/sdd/references/context-budget.md +85 -0
  234. package/sdd/references/continuation-format.md +30 -26
  235. package/sdd/references/debugger-philosophy.md +76 -0
  236. package/sdd/references/decimal-phase-calculation.md +5 -5
  237. package/sdd/references/doc-conflict-engine.md +91 -0
  238. package/sdd/references/domain-probes.md +125 -0
  239. package/sdd/references/execute-mvp-tdd.md +81 -0
  240. package/sdd/references/executor-examples.md +110 -0
  241. package/sdd/references/few-shot-examples/plan-checker.md +73 -0
  242. package/sdd/references/few-shot-examples/verifier.md +109 -0
  243. package/sdd/references/gate-prompts.md +100 -0
  244. package/sdd/references/gates.md +70 -0
  245. package/sdd/references/git-integration.md +9 -6
  246. package/sdd/references/git-planning-commit.md +6 -4
  247. package/sdd/references/ios-scaffold.md +123 -0
  248. package/sdd/references/mandatory-initial-read.md +2 -0
  249. package/sdd/references/model-profile-resolution.md +2 -0
  250. package/sdd/references/model-profiles.md +128 -22
  251. package/sdd/references/mvp-concepts.md +49 -0
  252. package/sdd/references/phase-argument-parsing.md +3 -3
  253. package/sdd/references/planner-antipatterns.md +89 -0
  254. package/sdd/references/planner-chunked.md +49 -0
  255. package/sdd/references/planner-gap-closure.md +62 -0
  256. package/sdd/references/planner-human-verify-mode.md +57 -0
  257. package/sdd/references/planner-mvp-mode.md +53 -0
  258. package/sdd/references/planner-reviews.md +39 -0
  259. package/sdd/references/planner-revision.md +87 -0
  260. package/sdd/references/planner-source-audit.md +73 -0
  261. package/sdd/references/planning-config.md +276 -7
  262. package/sdd/references/project-skills-discovery.md +19 -0
  263. package/sdd/references/revision-loop.md +97 -0
  264. package/sdd/references/scout-codebase.md +51 -0
  265. package/sdd/references/skeleton-template.md +48 -0
  266. package/sdd/references/sketch-interactivity.md +41 -0
  267. package/sdd/references/sketch-theme-system.md +94 -0
  268. package/sdd/references/sketch-tooling.md +45 -0
  269. package/sdd/references/sketch-variant-patterns.md +81 -0
  270. package/sdd/references/spidr-splitting.md +69 -0
  271. package/sdd/references/tdd.md +67 -0
  272. package/sdd/references/thinking-models-debug.md +44 -0
  273. package/sdd/references/thinking-models-execution.md +50 -0
  274. package/sdd/references/thinking-models-planning.md +62 -0
  275. package/sdd/references/thinking-models-research.md +50 -0
  276. package/sdd/references/thinking-models-verification.md +55 -0
  277. package/sdd/references/thinking-partner.md +96 -0
  278. package/sdd/references/ui-brand.md +4 -4
  279. package/sdd/references/universal-anti-patterns.md +63 -0
  280. package/sdd/references/user-story-template.md +58 -0
  281. package/sdd/references/verification-overrides.md +227 -0
  282. package/sdd/references/verify-mvp-mode.md +85 -0
  283. package/sdd/references/workstream-flag.md +63 -10
  284. package/sdd/references/worktree-path-safety.md +89 -0
  285. package/sdd/templates/AI-SPEC.md +246 -0
  286. package/sdd/templates/DEBUG.md +7 -2
  287. package/sdd/templates/README.md +77 -0
  288. package/sdd/templates/SECURITY.md +61 -0
  289. package/sdd/templates/VALIDATION.md +3 -3
  290. package/sdd/templates/claude-md.md +27 -4
  291. package/sdd/templates/config.json +20 -2
  292. package/sdd/templates/discovery.md +2 -2
  293. package/sdd/templates/research.md +40 -0
  294. package/sdd/templates/spec.md +307 -0
  295. package/sdd/templates/state.md +10 -2
  296. package/sdd/workflows/add-backlog.md +90 -0
  297. package/sdd/workflows/add-phase.md +12 -12
  298. package/sdd/workflows/add-tests.md +6 -3
  299. package/sdd/workflows/add-todo.md +8 -6
  300. package/sdd/workflows/ai-integration-phase.md +294 -0
  301. package/sdd/workflows/analyze-dependencies.md +96 -0
  302. package/sdd/workflows/audit-fix.md +177 -0
  303. package/sdd/workflows/audit-milestone.md +35 -18
  304. package/sdd/workflows/audit-uat.md +1 -1
  305. package/sdd/workflows/autonomous.md +202 -304
  306. package/sdd/workflows/check-todos.md +12 -10
  307. package/sdd/workflows/cleanup.md +3 -1
  308. package/sdd/workflows/code-review-fix.md +501 -0
  309. package/sdd/workflows/code-review.md +613 -0
  310. package/sdd/workflows/complete-milestone.md +115 -28
  311. package/sdd/workflows/debug.md +231 -0
  312. package/sdd/workflows/diagnose-issues.md +14 -5
  313. package/sdd/workflows/discovery-phase.md +3 -1
  314. package/sdd/workflows/discuss-phase/modes/advisor.md +175 -0
  315. package/sdd/workflows/discuss-phase/modes/all.md +28 -0
  316. package/sdd/workflows/discuss-phase/modes/analyze.md +44 -0
  317. package/sdd/workflows/discuss-phase/modes/auto.md +56 -0
  318. package/sdd/workflows/discuss-phase/modes/batch.md +52 -0
  319. package/sdd/workflows/discuss-phase/modes/chain.md +97 -0
  320. package/sdd/workflows/discuss-phase/modes/default.md +141 -0
  321. package/sdd/workflows/discuss-phase/modes/power.md +44 -0
  322. package/sdd/workflows/discuss-phase/modes/text.md +55 -0
  323. package/sdd/workflows/discuss-phase/templates/checkpoint.json +18 -0
  324. package/sdd/workflows/discuss-phase/templates/context.md +136 -0
  325. package/sdd/workflows/discuss-phase/templates/discussion-log.md +50 -0
  326. package/sdd/workflows/discuss-phase-assumptions.md +41 -20
  327. package/sdd/workflows/discuss-phase-power.md +291 -0
  328. package/sdd/workflows/discuss-phase.md +242 -792
  329. package/sdd/workflows/do.md +13 -7
  330. package/sdd/workflows/docs-update.md +1161 -0
  331. package/sdd/workflows/edit-phase.md +294 -0
  332. package/sdd/workflows/eval-review.md +155 -0
  333. package/sdd/workflows/execute-phase/steps/codebase-drift-gate.md +81 -0
  334. package/sdd/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  335. package/sdd/workflows/execute-phase/steps/post-merge-gate.md +116 -0
  336. package/sdd/workflows/execute-phase.md +1062 -108
  337. package/sdd/workflows/execute-plan.md +118 -107
  338. package/sdd/workflows/explore.md +143 -0
  339. package/sdd/workflows/extract-learnings.md +242 -0
  340. package/sdd/workflows/forensics.md +17 -4
  341. package/sdd/workflows/graduation.md +195 -0
  342. package/sdd/workflows/health.md +45 -3
  343. package/sdd/workflows/help.md +265 -88
  344. package/sdd/workflows/import.md +253 -0
  345. package/sdd/workflows/inbox.md +387 -0
  346. package/sdd/workflows/ingest-docs.md +339 -0
  347. package/sdd/workflows/insert-phase.md +37 -16
  348. package/sdd/workflows/list-phase-assumptions.md +2 -2
  349. package/sdd/workflows/list-workspaces.md +3 -3
  350. package/sdd/workflows/manager.md +62 -32
  351. package/sdd/workflows/map-codebase.md +90 -24
  352. package/sdd/workflows/milestone-summary.md +6 -6
  353. package/sdd/workflows/mvp-phase.md +221 -0
  354. package/sdd/workflows/new-milestone.md +168 -20
  355. package/sdd/workflows/new-project.md +273 -47
  356. package/sdd/workflows/new-workspace.md +8 -6
  357. package/sdd/workflows/next.md +127 -4
  358. package/sdd/workflows/note.md +7 -5
  359. package/sdd/workflows/pause-work.md +79 -12
  360. package/sdd/workflows/plan-milestone-gaps.md +14 -7
  361. package/sdd/workflows/plan-phase.md +987 -62
  362. package/sdd/workflows/plan-review-convergence.md +329 -0
  363. package/sdd/workflows/plant-seed.md +145 -85
  364. package/sdd/workflows/pr-branch.md +41 -13
  365. package/sdd/workflows/profile-user.md +20 -18
  366. package/sdd/workflows/progress.md +186 -44
  367. package/sdd/workflows/quick.md +470 -58
  368. package/sdd/workflows/reapply-patches.md +390 -0
  369. package/sdd/workflows/remove-phase.md +12 -12
  370. package/sdd/workflows/remove-workspace.md +24 -7
  371. package/sdd/workflows/resume-project.md +18 -15
  372. package/sdd/workflows/review.md +242 -11
  373. package/sdd/workflows/scan.md +104 -0
  374. package/sdd/workflows/secure-phase.md +179 -0
  375. package/sdd/workflows/session-report.md +2 -2
  376. package/sdd/workflows/settings-advanced.md +579 -0
  377. package/sdd/workflows/settings-integrations.md +281 -0
  378. package/sdd/workflows/settings.md +221 -16
  379. package/sdd/workflows/ship.md +140 -13
  380. package/sdd/workflows/sketch-wrap-up.md +285 -0
  381. package/sdd/workflows/sketch.md +360 -0
  382. package/sdd/workflows/spec-phase.md +262 -0
  383. package/sdd/workflows/spike-wrap-up.md +306 -0
  384. package/sdd/workflows/spike.md +452 -0
  385. package/sdd/workflows/stats.md +20 -1
  386. package/sdd/workflows/sync-skills.md +182 -0
  387. package/sdd/workflows/thread.md +221 -0
  388. package/sdd/workflows/transition.md +44 -22
  389. package/sdd/workflows/ui-phase.md +39 -14
  390. package/sdd/workflows/ui-review.md +33 -6
  391. package/sdd/workflows/ultraplan-phase.md +198 -0
  392. package/sdd/workflows/undo.md +314 -0
  393. package/sdd/workflows/update.md +350 -29
  394. package/sdd/workflows/validate-phase.md +10 -6
  395. package/sdd/workflows/verify-phase.md +307 -18
  396. package/sdd/workflows/verify-work.md +153 -10
  397. package/sdk/dist/cli-transport.d.ts +19 -0
  398. package/sdk/dist/cli-transport.d.ts.map +1 -0
  399. package/sdk/dist/cli-transport.js +104 -0
  400. package/sdk/dist/cli-transport.js.map +1 -0
  401. package/sdk/dist/cli.d.ts +46 -0
  402. package/sdk/dist/cli.d.ts.map +1 -0
  403. package/sdk/dist/cli.js +511 -0
  404. package/sdk/dist/cli.js.map +1 -0
  405. package/sdk/dist/config.d.ts +84 -0
  406. package/sdk/dist/config.d.ts.map +1 -0
  407. package/sdk/dist/config.js +135 -0
  408. package/sdk/dist/config.js.map +1 -0
  409. package/sdk/dist/context-engine.d.ts +49 -0
  410. package/sdk/dist/context-engine.d.ts.map +1 -0
  411. package/sdk/dist/context-engine.js +142 -0
  412. package/sdk/dist/context-engine.js.map +1 -0
  413. package/sdk/dist/context-truncation.d.ts +33 -0
  414. package/sdk/dist/context-truncation.d.ts.map +1 -0
  415. package/sdk/dist/context-truncation.js +197 -0
  416. package/sdk/dist/context-truncation.js.map +1 -0
  417. package/sdk/dist/errors.d.ts +46 -0
  418. package/sdk/dist/errors.d.ts.map +1 -0
  419. package/sdk/dist/errors.js +64 -0
  420. package/sdk/dist/errors.js.map +1 -0
  421. package/sdk/dist/event-stream.d.ts +53 -0
  422. package/sdk/dist/event-stream.d.ts.map +1 -0
  423. package/sdk/dist/event-stream.js +321 -0
  424. package/sdk/dist/event-stream.js.map +1 -0
  425. package/sdk/dist/golden/capture.d.ts +15 -0
  426. package/sdk/dist/golden/capture.d.ts.map +1 -0
  427. package/sdk/dist/golden/capture.js +67 -0
  428. package/sdk/dist/golden/capture.js.map +1 -0
  429. package/sdk/dist/golden/golden-integration-covered.d.ts +6 -0
  430. package/sdk/dist/golden/golden-integration-covered.d.ts.map +1 -0
  431. package/sdk/dist/golden/golden-integration-covered.js +30 -0
  432. package/sdk/dist/golden/golden-integration-covered.js.map +1 -0
  433. package/sdk/dist/golden/golden-mutation-covered.d.ts +7 -0
  434. package/sdk/dist/golden/golden-mutation-covered.d.ts.map +1 -0
  435. package/sdk/dist/golden/golden-mutation-covered.js +17 -0
  436. package/sdk/dist/golden/golden-mutation-covered.js.map +1 -0
  437. package/sdk/dist/golden/golden-policy.d.ts +10 -0
  438. package/sdk/dist/golden/golden-policy.d.ts.map +1 -0
  439. package/sdk/dist/golden/golden-policy.js +98 -0
  440. package/sdk/dist/golden/golden-policy.js.map +1 -0
  441. package/sdk/dist/golden/init-golden-normalize.d.ts +8 -0
  442. package/sdk/dist/golden/init-golden-normalize.d.ts.map +1 -0
  443. package/sdk/dist/golden/init-golden-normalize.js +14 -0
  444. package/sdk/dist/golden/init-golden-normalize.js.map +1 -0
  445. package/sdk/dist/golden/read-only-golden-rows.d.ts +20 -0
  446. package/sdk/dist/golden/read-only-golden-rows.d.ts.map +1 -0
  447. package/sdk/dist/golden/read-only-golden-rows.js +67 -0
  448. package/sdk/dist/golden/read-only-golden-rows.js.map +1 -0
  449. package/sdk/dist/golden/registry-canonical-commands.d.ts +6 -0
  450. package/sdk/dist/golden/registry-canonical-commands.d.ts.map +1 -0
  451. package/sdk/dist/golden/registry-canonical-commands.js +30 -0
  452. package/sdk/dist/golden/registry-canonical-commands.js.map +1 -0
  453. package/sdk/dist/index.d.ts +125 -0
  454. package/sdk/dist/index.d.ts.map +1 -0
  455. package/sdk/dist/index.js +298 -0
  456. package/sdk/dist/index.js.map +1 -0
  457. package/sdk/dist/init-runner.d.ts +90 -0
  458. package/sdk/dist/init-runner.d.ts.map +1 -0
  459. package/sdk/dist/init-runner.js +613 -0
  460. package/sdk/dist/init-runner.js.map +1 -0
  461. package/sdk/dist/logger.d.ts +50 -0
  462. package/sdk/dist/logger.d.ts.map +1 -0
  463. package/sdk/dist/logger.js +70 -0
  464. package/sdk/dist/logger.js.map +1 -0
  465. package/sdk/dist/model-catalog.d.ts +31 -0
  466. package/sdk/dist/model-catalog.d.ts.map +1 -0
  467. package/sdk/dist/model-catalog.js +31 -0
  468. package/sdk/dist/model-catalog.js.map +1 -0
  469. package/sdk/dist/phase-prompt.d.ts +72 -0
  470. package/sdk/dist/phase-prompt.d.ts.map +1 -0
  471. package/sdk/dist/phase-prompt.js +213 -0
  472. package/sdk/dist/phase-prompt.js.map +1 -0
  473. package/sdk/dist/phase-runner.d.ts +145 -0
  474. package/sdk/dist/phase-runner.d.ts.map +1 -0
  475. package/sdk/dist/phase-runner.js +1206 -0
  476. package/sdk/dist/phase-runner.js.map +1 -0
  477. package/sdk/dist/plan-parser.d.ts +55 -0
  478. package/sdk/dist/plan-parser.d.ts.map +1 -0
  479. package/sdk/dist/plan-parser.js +389 -0
  480. package/sdk/dist/plan-parser.js.map +1 -0
  481. package/sdk/dist/planning-journal.d.ts +64 -0
  482. package/sdk/dist/planning-journal.d.ts.map +1 -0
  483. package/sdk/dist/planning-journal.js +88 -0
  484. package/sdk/dist/planning-journal.js.map +1 -0
  485. package/sdk/dist/planning-runtime.d.ts +67 -0
  486. package/sdk/dist/planning-runtime.d.ts.map +1 -0
  487. package/sdk/dist/planning-runtime.js +58 -0
  488. package/sdk/dist/planning-runtime.js.map +1 -0
  489. package/sdk/dist/prompt-builder.d.ts +44 -0
  490. package/sdk/dist/prompt-builder.d.ts.map +1 -0
  491. package/sdk/dist/prompt-builder.js +180 -0
  492. package/sdk/dist/prompt-builder.js.map +1 -0
  493. package/sdk/dist/prompt-sanitizer.d.ts +35 -0
  494. package/sdk/dist/prompt-sanitizer.d.ts.map +1 -0
  495. package/sdk/dist/prompt-sanitizer.js +101 -0
  496. package/sdk/dist/prompt-sanitizer.js.map +1 -0
  497. package/sdk/dist/query/active-workstream-store.d.ts +7 -0
  498. package/sdk/dist/query/active-workstream-store.d.ts.map +1 -0
  499. package/sdk/dist/query/active-workstream-store.js +56 -0
  500. package/sdk/dist/query/active-workstream-store.js.map +1 -0
  501. package/sdk/dist/query/agent-failure-classifier.d.ts +38 -0
  502. package/sdk/dist/query/agent-failure-classifier.d.ts.map +1 -0
  503. package/sdk/dist/query/agent-failure-classifier.js +83 -0
  504. package/sdk/dist/query/agent-failure-classifier.js.map +1 -0
  505. package/sdk/dist/query/audit-open.d.ts +46 -0
  506. package/sdk/dist/query/audit-open.d.ts.map +1 -0
  507. package/sdk/dist/query/audit-open.js +662 -0
  508. package/sdk/dist/query/audit-open.js.map +1 -0
  509. package/sdk/dist/query/check-auto-mode.d.ts +13 -0
  510. package/sdk/dist/query/check-auto-mode.d.ts.map +1 -0
  511. package/sdk/dist/query/check-auto-mode.js +40 -0
  512. package/sdk/dist/query/check-auto-mode.js.map +1 -0
  513. package/sdk/dist/query/check-completion.d.ts +10 -0
  514. package/sdk/dist/query/check-completion.d.ts.map +1 -0
  515. package/sdk/dist/query/check-completion.js +157 -0
  516. package/sdk/dist/query/check-completion.js.map +1 -0
  517. package/sdk/dist/query/check-decision-coverage.d.ts +33 -0
  518. package/sdk/dist/query/check-decision-coverage.d.ts.map +1 -0
  519. package/sdk/dist/query/check-decision-coverage.js +472 -0
  520. package/sdk/dist/query/check-decision-coverage.js.map +1 -0
  521. package/sdk/dist/query/check-gates.d.ts +10 -0
  522. package/sdk/dist/query/check-gates.d.ts.map +1 -0
  523. package/sdk/dist/query/check-gates.js +89 -0
  524. package/sdk/dist/query/check-gates.js.map +1 -0
  525. package/sdk/dist/query/check-ship-ready.d.ts +10 -0
  526. package/sdk/dist/query/check-ship-ready.d.ts.map +1 -0
  527. package/sdk/dist/query/check-ship-ready.js +93 -0
  528. package/sdk/dist/query/check-ship-ready.js.map +1 -0
  529. package/sdk/dist/query/check-verification-status.d.ts +10 -0
  530. package/sdk/dist/query/check-verification-status.d.ts.map +1 -0
  531. package/sdk/dist/query/check-verification-status.js +142 -0
  532. package/sdk/dist/query/check-verification-status.js.map +1 -0
  533. package/sdk/dist/query/command-aliases.generated.d.ts +31 -0
  534. package/sdk/dist/query/command-aliases.generated.d.ts.map +1 -0
  535. package/sdk/dist/query/command-aliases.generated.js +135 -0
  536. package/sdk/dist/query/command-aliases.generated.js.map +1 -0
  537. package/sdk/dist/query/command-catalog.d.ts +9 -0
  538. package/sdk/dist/query/command-catalog.d.ts.map +1 -0
  539. package/sdk/dist/query/command-catalog.js +17 -0
  540. package/sdk/dist/query/command-catalog.js.map +1 -0
  541. package/sdk/dist/query/command-definition.d.ts +19 -0
  542. package/sdk/dist/query/command-definition.d.ts.map +1 -0
  543. package/sdk/dist/query/command-definition.js +44 -0
  544. package/sdk/dist/query/command-definition.js.map +1 -0
  545. package/sdk/dist/query/command-family-handlers.d.ts +3 -0
  546. package/sdk/dist/query/command-family-handlers.d.ts.map +1 -0
  547. package/sdk/dist/query/command-family-handlers.js +94 -0
  548. package/sdk/dist/query/command-family-handlers.js.map +1 -0
  549. package/sdk/dist/query/command-manifest.d.ts +2 -0
  550. package/sdk/dist/query/command-manifest.d.ts.map +1 -0
  551. package/sdk/dist/query/command-manifest.init.d.ts +6 -0
  552. package/sdk/dist/query/command-manifest.init.d.ts.map +1 -0
  553. package/sdk/dist/query/command-manifest.init.js +23 -0
  554. package/sdk/dist/query/command-manifest.init.js.map +1 -0
  555. package/sdk/dist/query/command-manifest.js +17 -0
  556. package/sdk/dist/query/command-manifest.js.map +1 -0
  557. package/sdk/dist/query/command-manifest.non-family.d.ts +9 -0
  558. package/sdk/dist/query/command-manifest.non-family.d.ts.map +1 -0
  559. package/sdk/dist/query/command-manifest.non-family.js +59 -0
  560. package/sdk/dist/query/command-manifest.non-family.js.map +1 -0
  561. package/sdk/dist/query/command-manifest.phase.d.ts +6 -0
  562. package/sdk/dist/query/command-manifest.phase.d.ts.map +1 -0
  563. package/sdk/dist/query/command-manifest.phase.js +15 -0
  564. package/sdk/dist/query/command-manifest.phase.js.map +1 -0
  565. package/sdk/dist/query/command-manifest.phases.d.ts +7 -0
  566. package/sdk/dist/query/command-manifest.phases.d.ts.map +1 -0
  567. package/sdk/dist/query/command-manifest.phases.js +10 -0
  568. package/sdk/dist/query/command-manifest.phases.js.map +1 -0
  569. package/sdk/dist/query/command-manifest.roadmap.d.ts +6 -0
  570. package/sdk/dist/query/command-manifest.roadmap.d.ts.map +1 -0
  571. package/sdk/dist/query/command-manifest.roadmap.js +10 -0
  572. package/sdk/dist/query/command-manifest.roadmap.js.map +1 -0
  573. package/sdk/dist/query/command-manifest.state.d.ts +9 -0
  574. package/sdk/dist/query/command-manifest.state.d.ts.map +1 -0
  575. package/sdk/dist/query/command-manifest.state.js +30 -0
  576. package/sdk/dist/query/command-manifest.state.js.map +1 -0
  577. package/sdk/dist/query/command-manifest.types.d.ts +12 -0
  578. package/sdk/dist/query/command-manifest.types.d.ts.map +1 -0
  579. package/sdk/dist/query/command-manifest.types.js +2 -0
  580. package/sdk/dist/query/command-manifest.types.js.map +1 -0
  581. package/sdk/dist/query/command-manifest.validate.d.ts +6 -0
  582. package/sdk/dist/query/command-manifest.validate.d.ts.map +1 -0
  583. package/sdk/dist/query/command-manifest.validate.js +10 -0
  584. package/sdk/dist/query/command-manifest.validate.js.map +1 -0
  585. package/sdk/dist/query/command-manifest.verify.d.ts +6 -0
  586. package/sdk/dist/query/command-manifest.verify.d.ts.map +1 -0
  587. package/sdk/dist/query/command-manifest.verify.js +14 -0
  588. package/sdk/dist/query/command-manifest.verify.js.map +1 -0
  589. package/sdk/dist/query/command-static-catalog-domain.d.ts +3 -0
  590. package/sdk/dist/query/command-static-catalog-domain.d.ts.map +1 -0
  591. package/sdk/dist/query/command-static-catalog-domain.js +116 -0
  592. package/sdk/dist/query/command-static-catalog-domain.js.map +1 -0
  593. package/sdk/dist/query/command-static-catalog-foundation.d.ts +7 -0
  594. package/sdk/dist/query/command-static-catalog-foundation.d.ts.map +1 -0
  595. package/sdk/dist/query/command-static-catalog-foundation.js +98 -0
  596. package/sdk/dist/query/command-static-catalog-foundation.js.map +1 -0
  597. package/sdk/dist/query/command-topology.d.ts +32 -0
  598. package/sdk/dist/query/command-topology.d.ts.map +1 -0
  599. package/sdk/dist/query/command-topology.js +66 -0
  600. package/sdk/dist/query/command-topology.js.map +1 -0
  601. package/sdk/dist/query/commands-list.d.ts +14 -0
  602. package/sdk/dist/query/commands-list.d.ts.map +1 -0
  603. package/sdk/dist/query/commands-list.js +18 -0
  604. package/sdk/dist/query/commands-list.js.map +1 -0
  605. package/sdk/dist/query/commit.d.ts +79 -0
  606. package/sdk/dist/query/commit.d.ts.map +1 -0
  607. package/sdk/dist/query/commit.js +340 -0
  608. package/sdk/dist/query/commit.js.map +1 -0
  609. package/sdk/dist/query/config-gates.d.ts +12 -0
  610. package/sdk/dist/query/config-gates.d.ts.map +1 -0
  611. package/sdk/dist/query/config-gates.js +66 -0
  612. package/sdk/dist/query/config-gates.js.map +1 -0
  613. package/sdk/dist/query/config-mutation.d.ts +86 -0
  614. package/sdk/dist/query/config-mutation.d.ts.map +1 -0
  615. package/sdk/dist/query/config-mutation.js +518 -0
  616. package/sdk/dist/query/config-mutation.js.map +1 -0
  617. package/sdk/dist/query/config-query.d.ts +57 -0
  618. package/sdk/dist/query/config-query.d.ts.map +1 -0
  619. package/sdk/dist/query/config-query.js +208 -0
  620. package/sdk/dist/query/config-query.js.map +1 -0
  621. package/sdk/dist/query/config-schema.d.ts +36 -0
  622. package/sdk/dist/query/config-schema.d.ts.map +1 -0
  623. package/sdk/dist/query/config-schema.js +147 -0
  624. package/sdk/dist/query/config-schema.js.map +1 -0
  625. package/sdk/dist/query/decisions.d.ts +58 -0
  626. package/sdk/dist/query/decisions.d.ts.map +1 -0
  627. package/sdk/dist/query/decisions.js +161 -0
  628. package/sdk/dist/query/decisions.js.map +1 -0
  629. package/sdk/dist/query/detect-custom-files.d.ts +11 -0
  630. package/sdk/dist/query/detect-custom-files.d.ts.map +1 -0
  631. package/sdk/dist/query/detect-custom-files.js +89 -0
  632. package/sdk/dist/query/detect-custom-files.js.map +1 -0
  633. package/sdk/dist/query/detect-phase-type.d.ts +9 -0
  634. package/sdk/dist/query/detect-phase-type.d.ts.map +1 -0
  635. package/sdk/dist/query/detect-phase-type.js +124 -0
  636. package/sdk/dist/query/detect-phase-type.js.map +1 -0
  637. package/sdk/dist/query/docs-init.d.ts +26 -0
  638. package/sdk/dist/query/docs-init.d.ts.map +1 -0
  639. package/sdk/dist/query/docs-init.js +231 -0
  640. package/sdk/dist/query/docs-init.js.map +1 -0
  641. package/sdk/dist/query/fallow-audit.d.ts +44 -0
  642. package/sdk/dist/query/fallow-audit.d.ts.map +1 -0
  643. package/sdk/dist/query/fallow-audit.js +44 -0
  644. package/sdk/dist/query/fallow-audit.js.map +1 -0
  645. package/sdk/dist/query/frontmatter-mutation.d.ts +77 -0
  646. package/sdk/dist/query/frontmatter-mutation.d.ts.map +1 -0
  647. package/sdk/dist/query/frontmatter-mutation.js +317 -0
  648. package/sdk/dist/query/frontmatter-mutation.js.map +1 -0
  649. package/sdk/dist/query/frontmatter.d.ts +93 -0
  650. package/sdk/dist/query/frontmatter.d.ts.map +1 -0
  651. package/sdk/dist/query/frontmatter.js +365 -0
  652. package/sdk/dist/query/frontmatter.js.map +1 -0
  653. package/sdk/dist/query/helpers.d.ts +191 -0
  654. package/sdk/dist/query/helpers.d.ts.map +1 -0
  655. package/sdk/dist/query/helpers.js +613 -0
  656. package/sdk/dist/query/helpers.js.map +1 -0
  657. package/sdk/dist/query/index.d.ts +8 -0
  658. package/sdk/dist/query/index.d.ts.map +1 -0
  659. package/sdk/dist/query/index.js +6 -0
  660. package/sdk/dist/query/index.js.map +1 -0
  661. package/sdk/dist/query/init-complex.d.ts +47 -0
  662. package/sdk/dist/query/init-complex.d.ts.map +1 -0
  663. package/sdk/dist/query/init-complex.js +718 -0
  664. package/sdk/dist/query/init-complex.js.map +1 -0
  665. package/sdk/dist/query/init.d.ts +106 -0
  666. package/sdk/dist/query/init.d.ts.map +1 -0
  667. package/sdk/dist/query/init.js +1159 -0
  668. package/sdk/dist/query/init.js.map +1 -0
  669. package/sdk/dist/query/intel.d.ts +43 -0
  670. package/sdk/dist/query/intel.d.ts.map +1 -0
  671. package/sdk/dist/query/intel.js +416 -0
  672. package/sdk/dist/query/intel.js.map +1 -0
  673. package/sdk/dist/query/mutation-event-decorator.d.ts +5 -0
  674. package/sdk/dist/query/mutation-event-decorator.d.ts.map +1 -0
  675. package/sdk/dist/query/mutation-event-decorator.js +28 -0
  676. package/sdk/dist/query/mutation-event-decorator.js.map +1 -0
  677. package/sdk/dist/query/mutation-event-mapper.d.ts +4 -0
  678. package/sdk/dist/query/mutation-event-mapper.d.ts.map +1 -0
  679. package/sdk/dist/query/mutation-event-mapper.js +70 -0
  680. package/sdk/dist/query/mutation-event-mapper.js.map +1 -0
  681. package/sdk/dist/query/mvp.d.ts +113 -0
  682. package/sdk/dist/query/mvp.d.ts.map +1 -0
  683. package/sdk/dist/query/mvp.js +225 -0
  684. package/sdk/dist/query/mvp.js.map +1 -0
  685. package/sdk/dist/query/phase-filesystem-adapter.d.ts +4 -0
  686. package/sdk/dist/query/phase-filesystem-adapter.d.ts.map +1 -0
  687. package/sdk/dist/query/phase-filesystem-adapter.js +33 -0
  688. package/sdk/dist/query/phase-filesystem-adapter.js.map +1 -0
  689. package/sdk/dist/query/phase-lifecycle-policy.d.ts +34 -0
  690. package/sdk/dist/query/phase-lifecycle-policy.d.ts.map +1 -0
  691. package/sdk/dist/query/phase-lifecycle-policy.js +138 -0
  692. package/sdk/dist/query/phase-lifecycle-policy.js.map +1 -0
  693. package/sdk/dist/query/phase-lifecycle.d.ts +116 -0
  694. package/sdk/dist/query/phase-lifecycle.d.ts.map +1 -0
  695. package/sdk/dist/query/phase-lifecycle.js +1486 -0
  696. package/sdk/dist/query/phase-lifecycle.js.map +1 -0
  697. package/sdk/dist/query/phase-list-queries.d.ts +18 -0
  698. package/sdk/dist/query/phase-list-queries.d.ts.map +1 -0
  699. package/sdk/dist/query/phase-list-queries.js +129 -0
  700. package/sdk/dist/query/phase-list-queries.js.map +1 -0
  701. package/sdk/dist/query/phase-ready.d.ts +9 -0
  702. package/sdk/dist/query/phase-ready.d.ts.map +1 -0
  703. package/sdk/dist/query/phase-ready.js +132 -0
  704. package/sdk/dist/query/phase-ready.js.map +1 -0
  705. package/sdk/dist/query/phase-roadmap-mutation.d.ts +13 -0
  706. package/sdk/dist/query/phase-roadmap-mutation.d.ts.map +1 -0
  707. package/sdk/dist/query/phase-roadmap-mutation.js +65 -0
  708. package/sdk/dist/query/phase-roadmap-mutation.js.map +1 -0
  709. package/sdk/dist/query/phase.d.ts +48 -0
  710. package/sdk/dist/query/phase.d.ts.map +1 -0
  711. package/sdk/dist/query/phase.js +451 -0
  712. package/sdk/dist/query/phase.js.map +1 -0
  713. package/sdk/dist/query/pipeline.d.ts +53 -0
  714. package/sdk/dist/query/pipeline.d.ts.map +1 -0
  715. package/sdk/dist/query/pipeline.js +198 -0
  716. package/sdk/dist/query/pipeline.js.map +1 -0
  717. package/sdk/dist/query/plan-scan.d.ts +14 -0
  718. package/sdk/dist/query/plan-scan.d.ts.map +1 -0
  719. package/sdk/dist/query/plan-scan.js +70 -0
  720. package/sdk/dist/query/plan-scan.js.map +1 -0
  721. package/sdk/dist/query/plan-task-structure.d.ts +9 -0
  722. package/sdk/dist/query/plan-task-structure.d.ts.map +1 -0
  723. package/sdk/dist/query/plan-task-structure.js +59 -0
  724. package/sdk/dist/query/plan-task-structure.js.map +1 -0
  725. package/sdk/dist/query/profile-extract-messages.d.ts +40 -0
  726. package/sdk/dist/query/profile-extract-messages.d.ts.map +1 -0
  727. package/sdk/dist/query/profile-extract-messages.js +195 -0
  728. package/sdk/dist/query/profile-extract-messages.js.map +1 -0
  729. package/sdk/dist/query/profile-output.d.ts +11 -0
  730. package/sdk/dist/query/profile-output.d.ts.map +1 -0
  731. package/sdk/dist/query/profile-output.js +873 -0
  732. package/sdk/dist/query/profile-output.js.map +1 -0
  733. package/sdk/dist/query/profile-questionnaire-data.d.ts +21 -0
  734. package/sdk/dist/query/profile-questionnaire-data.d.ts.map +1 -0
  735. package/sdk/dist/query/profile-questionnaire-data.js +171 -0
  736. package/sdk/dist/query/profile-questionnaire-data.js.map +1 -0
  737. package/sdk/dist/query/profile-sample.d.ts +22 -0
  738. package/sdk/dist/query/profile-sample.d.ts.map +1 -0
  739. package/sdk/dist/query/profile-sample.js +136 -0
  740. package/sdk/dist/query/profile-sample.js.map +1 -0
  741. package/sdk/dist/query/profile-scan-sessions.d.ts +49 -0
  742. package/sdk/dist/query/profile-scan-sessions.d.ts.map +1 -0
  743. package/sdk/dist/query/profile-scan-sessions.js +137 -0
  744. package/sdk/dist/query/profile-scan-sessions.js.map +1 -0
  745. package/sdk/dist/query/profile.d.ts +61 -0
  746. package/sdk/dist/query/profile.d.ts.map +1 -0
  747. package/sdk/dist/query/profile.js +307 -0
  748. package/sdk/dist/query/profile.js.map +1 -0
  749. package/sdk/dist/query/progress.d.ts +77 -0
  750. package/sdk/dist/query/progress.d.ts.map +1 -0
  751. package/sdk/dist/query/progress.js +481 -0
  752. package/sdk/dist/query/progress.js.map +1 -0
  753. package/sdk/dist/query/query-cli-adapter.d.ts +8 -0
  754. package/sdk/dist/query/query-cli-adapter.d.ts.map +1 -0
  755. package/sdk/dist/query/query-cli-adapter.js +32 -0
  756. package/sdk/dist/query/query-cli-adapter.js.map +1 -0
  757. package/sdk/dist/query/query-cli-output.d.ts +9 -0
  758. package/sdk/dist/query/query-cli-output.d.ts.map +1 -0
  759. package/sdk/dist/query/query-cli-output.js +28 -0
  760. package/sdk/dist/query/query-cli-output.js.map +1 -0
  761. package/sdk/dist/query/query-command-diagnosis.d.ts +6 -0
  762. package/sdk/dist/query/query-command-diagnosis.d.ts.map +1 -0
  763. package/sdk/dist/query/query-command-diagnosis.js +6 -0
  764. package/sdk/dist/query/query-command-diagnosis.js.map +1 -0
  765. package/sdk/dist/query/query-command-resolution-strategy.d.ts +29 -0
  766. package/sdk/dist/query/query-command-resolution-strategy.d.ts.map +1 -0
  767. package/sdk/dist/query/query-command-resolution-strategy.js +103 -0
  768. package/sdk/dist/query/query-command-resolution-strategy.js.map +1 -0
  769. package/sdk/dist/query/query-command-semantics.d.ts +7 -0
  770. package/sdk/dist/query/query-command-semantics.d.ts.map +1 -0
  771. package/sdk/dist/query/query-command-semantics.js +7 -0
  772. package/sdk/dist/query/query-command-semantics.js.map +1 -0
  773. package/sdk/dist/query/query-dispatch-contract.d.ts +21 -0
  774. package/sdk/dist/query/query-dispatch-contract.d.ts.map +1 -0
  775. package/sdk/dist/query/query-dispatch-contract.js +2 -0
  776. package/sdk/dist/query/query-dispatch-contract.js.map +1 -0
  777. package/sdk/dist/query/query-dispatch-error-mapper.d.ts +6 -0
  778. package/sdk/dist/query/query-dispatch-error-mapper.d.ts.map +1 -0
  779. package/sdk/dist/query/query-dispatch-error-mapper.js +6 -0
  780. package/sdk/dist/query/query-dispatch-error-mapper.js.map +1 -0
  781. package/sdk/dist/query/query-dispatch-formatting.d.ts +6 -0
  782. package/sdk/dist/query/query-dispatch-formatting.d.ts.map +1 -0
  783. package/sdk/dist/query/query-dispatch-formatting.js +6 -0
  784. package/sdk/dist/query/query-dispatch-formatting.js.map +1 -0
  785. package/sdk/dist/query/query-dispatch-input-validation.d.ts +6 -0
  786. package/sdk/dist/query/query-dispatch-input-validation.d.ts.map +1 -0
  787. package/sdk/dist/query/query-dispatch-input-validation.js +6 -0
  788. package/sdk/dist/query/query-dispatch-input-validation.js.map +1 -0
  789. package/sdk/dist/query/query-dispatch-observability.d.ts +2 -0
  790. package/sdk/dist/query/query-dispatch-observability.d.ts.map +1 -0
  791. package/sdk/dist/query/query-dispatch-observability.js +7 -0
  792. package/sdk/dist/query/query-dispatch-observability.js.map +1 -0
  793. package/sdk/dist/query/query-dispatch-plan.d.ts +6 -0
  794. package/sdk/dist/query/query-dispatch-plan.d.ts.map +1 -0
  795. package/sdk/dist/query/query-dispatch-plan.js +6 -0
  796. package/sdk/dist/query/query-dispatch-plan.js.map +1 -0
  797. package/sdk/dist/query/query-dispatch-result-builder.d.ts +6 -0
  798. package/sdk/dist/query/query-dispatch-result-builder.d.ts.map +1 -0
  799. package/sdk/dist/query/query-dispatch-result-builder.js +6 -0
  800. package/sdk/dist/query/query-dispatch-result-builder.js.map +1 -0
  801. package/sdk/dist/query/query-dispatch.d.ts +48 -0
  802. package/sdk/dist/query/query-dispatch.d.ts.map +1 -0
  803. package/sdk/dist/query/query-dispatch.js +175 -0
  804. package/sdk/dist/query/query-dispatch.js.map +1 -0
  805. package/sdk/dist/query/query-error-details-schema.d.ts +19 -0
  806. package/sdk/dist/query/query-error-details-schema.d.ts.map +1 -0
  807. package/sdk/dist/query/query-error-details-schema.js +10 -0
  808. package/sdk/dist/query/query-error-details-schema.js.map +1 -0
  809. package/sdk/dist/query/query-error-taxonomy.d.ts +38 -0
  810. package/sdk/dist/query/query-error-taxonomy.d.ts.map +1 -0
  811. package/sdk/dist/query/query-error-taxonomy.js +74 -0
  812. package/sdk/dist/query/query-error-taxonomy.js.map +1 -0
  813. package/sdk/dist/query/query-fallback-bridge-adapter.d.ts +14 -0
  814. package/sdk/dist/query/query-fallback-bridge-adapter.d.ts.map +1 -0
  815. package/sdk/dist/query/query-fallback-bridge-adapter.js +33 -0
  816. package/sdk/dist/query/query-fallback-bridge-adapter.js.map +1 -0
  817. package/sdk/dist/query/query-fallback-executor.d.ts +11 -0
  818. package/sdk/dist/query/query-fallback-executor.d.ts.map +1 -0
  819. package/sdk/dist/query/query-fallback-executor.js +31 -0
  820. package/sdk/dist/query/query-fallback-executor.js.map +1 -0
  821. package/sdk/dist/query/query-fallback-output-classifier.d.ts +6 -0
  822. package/sdk/dist/query/query-fallback-output-classifier.d.ts.map +1 -0
  823. package/sdk/dist/query/query-fallback-output-classifier.js +27 -0
  824. package/sdk/dist/query/query-fallback-output-classifier.js.map +1 -0
  825. package/sdk/dist/query/query-fallback-policy.d.ts +6 -0
  826. package/sdk/dist/query/query-fallback-policy.d.ts.map +1 -0
  827. package/sdk/dist/query/query-fallback-policy.js +7 -0
  828. package/sdk/dist/query/query-fallback-policy.js.map +1 -0
  829. package/sdk/dist/query/query-native-dispatch-adapter.d.ts +7 -0
  830. package/sdk/dist/query/query-native-dispatch-adapter.d.ts.map +1 -0
  831. package/sdk/dist/query/query-native-dispatch-adapter.js +6 -0
  832. package/sdk/dist/query/query-native-dispatch-adapter.js.map +1 -0
  833. package/sdk/dist/query/query-policy-capability.d.ts +10 -0
  834. package/sdk/dist/query/query-policy-capability.d.ts.map +1 -0
  835. package/sdk/dist/query/query-policy-capability.js +17 -0
  836. package/sdk/dist/query/query-policy-capability.js.map +1 -0
  837. package/sdk/dist/query/query-runtime-context.d.ts +19 -0
  838. package/sdk/dist/query/query-runtime-context.d.ts.map +1 -0
  839. package/sdk/dist/query/query-runtime-context.js +31 -0
  840. package/sdk/dist/query/query-runtime-context.js.map +1 -0
  841. package/sdk/dist/query/query-unknown-command-hints.d.ts +2 -0
  842. package/sdk/dist/query/query-unknown-command-hints.d.ts.map +1 -0
  843. package/sdk/dist/query/query-unknown-command-hints.js +6 -0
  844. package/sdk/dist/query/query-unknown-command-hints.js.map +1 -0
  845. package/sdk/dist/query/registry-assembly-descriptor.d.ts +12 -0
  846. package/sdk/dist/query/registry-assembly-descriptor.d.ts.map +1 -0
  847. package/sdk/dist/query/registry-assembly-descriptor.js +61 -0
  848. package/sdk/dist/query/registry-assembly-descriptor.js.map +1 -0
  849. package/sdk/dist/query/registry-assembly-invariants.d.ts +30 -0
  850. package/sdk/dist/query/registry-assembly-invariants.d.ts.map +1 -0
  851. package/sdk/dist/query/registry-assembly-invariants.js +77 -0
  852. package/sdk/dist/query/registry-assembly-invariants.js.map +1 -0
  853. package/sdk/dist/query/registry-assembly.d.ts +10 -0
  854. package/sdk/dist/query/registry-assembly.d.ts.map +1 -0
  855. package/sdk/dist/query/registry-assembly.js +53 -0
  856. package/sdk/dist/query/registry-assembly.js.map +1 -0
  857. package/sdk/dist/query/registry.d.ts +90 -0
  858. package/sdk/dist/query/registry.d.ts.map +1 -0
  859. package/sdk/dist/query/registry.js +129 -0
  860. package/sdk/dist/query/registry.js.map +1 -0
  861. package/sdk/dist/query/requirements-extract-from-plans.d.ts +9 -0
  862. package/sdk/dist/query/requirements-extract-from-plans.d.ts.map +1 -0
  863. package/sdk/dist/query/requirements-extract-from-plans.js +76 -0
  864. package/sdk/dist/query/requirements-extract-from-plans.js.map +1 -0
  865. package/sdk/dist/query/roadmap-update-plan-progress.d.ts +11 -0
  866. package/sdk/dist/query/roadmap-update-plan-progress.d.ts.map +1 -0
  867. package/sdk/dist/query/roadmap-update-plan-progress.js +124 -0
  868. package/sdk/dist/query/roadmap-update-plan-progress.js.map +1 -0
  869. package/sdk/dist/query/roadmap.d.ts +137 -0
  870. package/sdk/dist/query/roadmap.d.ts.map +1 -0
  871. package/sdk/dist/query/roadmap.js +753 -0
  872. package/sdk/dist/query/roadmap.js.map +1 -0
  873. package/sdk/dist/query/route-next-action.d.ts +9 -0
  874. package/sdk/dist/query/route-next-action.d.ts.map +1 -0
  875. package/sdk/dist/query/route-next-action.js +318 -0
  876. package/sdk/dist/query/route-next-action.js.map +1 -0
  877. package/sdk/dist/query/schema-detect.d.ts +21 -0
  878. package/sdk/dist/query/schema-detect.d.ts.map +1 -0
  879. package/sdk/dist/query/schema-detect.js +146 -0
  880. package/sdk/dist/query/schema-detect.js.map +1 -0
  881. package/sdk/dist/query/secrets.d.ts +27 -0
  882. package/sdk/dist/query/secrets.d.ts.map +1 -0
  883. package/sdk/dist/query/secrets.js +42 -0
  884. package/sdk/dist/query/secrets.js.map +1 -0
  885. package/sdk/dist/query/skill-manifest.d.ts +50 -0
  886. package/sdk/dist/query/skill-manifest.d.ts.map +1 -0
  887. package/sdk/dist/query/skill-manifest.js +171 -0
  888. package/sdk/dist/query/skill-manifest.js.map +1 -0
  889. package/sdk/dist/query/skills.d.ts +27 -0
  890. package/sdk/dist/query/skills.d.ts.map +1 -0
  891. package/sdk/dist/query/skills.js +137 -0
  892. package/sdk/dist/query/skills.js.map +1 -0
  893. package/sdk/dist/query/state-document.d.ts +14 -0
  894. package/sdk/dist/query/state-document.d.ts.map +1 -0
  895. package/sdk/dist/query/state-document.js +110 -0
  896. package/sdk/dist/query/state-document.js.map +1 -0
  897. package/sdk/dist/query/state-mutation.d.ts +224 -0
  898. package/sdk/dist/query/state-mutation.d.ts.map +1 -0
  899. package/sdk/dist/query/state-mutation.js +1539 -0
  900. package/sdk/dist/query/state-mutation.js.map +1 -0
  901. package/sdk/dist/query/state-project-load.d.ts +23 -0
  902. package/sdk/dist/query/state-project-load.d.ts.map +1 -0
  903. package/sdk/dist/query/state-project-load.js +75 -0
  904. package/sdk/dist/query/state-project-load.js.map +1 -0
  905. package/sdk/dist/query/state.d.ts +78 -0
  906. package/sdk/dist/query/state.d.ts.map +1 -0
  907. package/sdk/dist/query/state.js +430 -0
  908. package/sdk/dist/query/state.js.map +1 -0
  909. package/sdk/dist/query/summary.d.ts +18 -0
  910. package/sdk/dist/query/summary.d.ts.map +1 -0
  911. package/sdk/dist/query/summary.js +249 -0
  912. package/sdk/dist/query/summary.js.map +1 -0
  913. package/sdk/dist/query/template.d.ts +46 -0
  914. package/sdk/dist/query/template.d.ts.map +1 -0
  915. package/sdk/dist/query/template.js +210 -0
  916. package/sdk/dist/query/template.js.map +1 -0
  917. package/sdk/dist/query/uat.d.ts +34 -0
  918. package/sdk/dist/query/uat.d.ts.map +1 -0
  919. package/sdk/dist/query/uat.js +339 -0
  920. package/sdk/dist/query/uat.js.map +1 -0
  921. package/sdk/dist/query/utils.d.ts +59 -0
  922. package/sdk/dist/query/utils.d.ts.map +1 -0
  923. package/sdk/dist/query/utils.js +74 -0
  924. package/sdk/dist/query/utils.js.map +1 -0
  925. package/sdk/dist/query/validate.d.ts +67 -0
  926. package/sdk/dist/query/validate.d.ts.map +1 -0
  927. package/sdk/dist/query/validate.js +908 -0
  928. package/sdk/dist/query/validate.js.map +1 -0
  929. package/sdk/dist/query/verify.d.ts +110 -0
  930. package/sdk/dist/query/verify.d.ts.map +1 -0
  931. package/sdk/dist/query/verify.js +631 -0
  932. package/sdk/dist/query/verify.js.map +1 -0
  933. package/sdk/dist/query/websearch.d.ts +24 -0
  934. package/sdk/dist/query/websearch.d.ts.map +1 -0
  935. package/sdk/dist/query/websearch.js +68 -0
  936. package/sdk/dist/query/websearch.js.map +1 -0
  937. package/sdk/dist/query/workspace.d.ts +62 -0
  938. package/sdk/dist/query/workspace.d.ts.map +1 -0
  939. package/sdk/dist/query/workspace.js +104 -0
  940. package/sdk/dist/query/workspace.js.map +1 -0
  941. package/sdk/dist/query/workstream-inventory.d.ts +52 -0
  942. package/sdk/dist/query/workstream-inventory.d.ts.map +1 -0
  943. package/sdk/dist/query/workstream-inventory.js +141 -0
  944. package/sdk/dist/query/workstream-inventory.js.map +1 -0
  945. package/sdk/dist/query/workstream.d.ts +35 -0
  946. package/sdk/dist/query/workstream.d.ts.map +1 -0
  947. package/sdk/dist/query/workstream.js +298 -0
  948. package/sdk/dist/query/workstream.js.map +1 -0
  949. package/sdk/dist/query/worktree.d.ts +3 -0
  950. package/sdk/dist/query/worktree.d.ts.map +1 -0
  951. package/sdk/dist/query/worktree.js +36 -0
  952. package/sdk/dist/query/worktree.js.map +1 -0
  953. package/sdk/dist/query-command-executor.d.ts +22 -0
  954. package/sdk/dist/query-command-executor.d.ts.map +1 -0
  955. package/sdk/dist/query-command-executor.js +22 -0
  956. package/sdk/dist/query-command-executor.js.map +1 -0
  957. package/sdk/dist/query-execution-policy.d.ts +24 -0
  958. package/sdk/dist/query-execution-policy.d.ts.map +1 -0
  959. package/sdk/dist/query-execution-policy.js +27 -0
  960. package/sdk/dist/query-execution-policy.js.map +1 -0
  961. package/sdk/dist/query-failure-classification.d.ts +9 -0
  962. package/sdk/dist/query-failure-classification.d.ts.map +1 -0
  963. package/sdk/dist/query-failure-classification.js +32 -0
  964. package/sdk/dist/query-failure-classification.js.map +1 -0
  965. package/sdk/dist/query-hotpath-methods.d.ts +19 -0
  966. package/sdk/dist/query-hotpath-methods.d.ts.map +1 -0
  967. package/sdk/dist/query-hotpath-methods.js +34 -0
  968. package/sdk/dist/query-hotpath-methods.js.map +1 -0
  969. package/sdk/dist/query-native-direct-adapter.d.ts +20 -0
  970. package/sdk/dist/query-native-direct-adapter.d.ts.map +1 -0
  971. package/sdk/dist/query-native-direct-adapter.js +52 -0
  972. package/sdk/dist/query-native-direct-adapter.js.map +1 -0
  973. package/sdk/dist/query-native-hotpath-adapter.d.ts +15 -0
  974. package/sdk/dist/query-native-hotpath-adapter.d.ts.map +1 -0
  975. package/sdk/dist/query-native-hotpath-adapter.js +32 -0
  976. package/sdk/dist/query-native-hotpath-adapter.js.map +1 -0
  977. package/sdk/dist/query-raw-output-projection.d.ts +6 -0
  978. package/sdk/dist/query-raw-output-projection.d.ts.map +1 -0
  979. package/sdk/dist/query-raw-output-projection.js +67 -0
  980. package/sdk/dist/query-raw-output-projection.js.map +1 -0
  981. package/sdk/dist/query-runtime-bridge.d.ts +61 -0
  982. package/sdk/dist/query-runtime-bridge.d.ts.map +1 -0
  983. package/sdk/dist/query-runtime-bridge.js +144 -0
  984. package/sdk/dist/query-runtime-bridge.js.map +1 -0
  985. package/sdk/dist/query-sdd-tools-path.d.ts +2 -0
  986. package/sdk/dist/query-sdd-tools-path.d.ts.map +1 -0
  987. package/sdk/dist/query-sdd-tools-path.js +2 -0
  988. package/sdk/dist/query-sdd-tools-path.js.map +1 -0
  989. package/sdk/dist/query-sdd-tools-runtime.d.ts +20 -0
  990. package/sdk/dist/query-sdd-tools-runtime.d.ts.map +1 -0
  991. package/sdk/dist/query-sdd-tools-runtime.js +47 -0
  992. package/sdk/dist/query-sdd-tools-runtime.js.map +1 -0
  993. package/sdk/dist/query-subprocess-adapter.d.ts +18 -0
  994. package/sdk/dist/query-subprocess-adapter.d.ts.map +1 -0
  995. package/sdk/dist/query-subprocess-adapter.js +92 -0
  996. package/sdk/dist/query-subprocess-adapter.js.map +1 -0
  997. package/sdk/dist/query-tools-error-factory.d.ts +16 -0
  998. package/sdk/dist/query-tools-error-factory.d.ts.map +1 -0
  999. package/sdk/dist/query-tools-error-factory.js +33 -0
  1000. package/sdk/dist/query-tools-error-factory.js.map +1 -0
  1001. package/sdk/dist/research-gate.d.ts +24 -0
  1002. package/sdk/dist/research-gate.d.ts.map +1 -0
  1003. package/sdk/dist/research-gate.js +70 -0
  1004. package/sdk/dist/research-gate.js.map +1 -0
  1005. package/sdk/dist/runtime-gate.d.ts +14 -0
  1006. package/sdk/dist/runtime-gate.d.ts.map +1 -0
  1007. package/sdk/dist/runtime-gate.js +48 -0
  1008. package/sdk/dist/runtime-gate.js.map +1 -0
  1009. package/sdk/dist/sdd-tools-error.d.ts +23 -0
  1010. package/sdk/dist/sdd-tools-error.d.ts.map +1 -0
  1011. package/sdk/dist/sdd-tools-error.js +29 -0
  1012. package/sdk/dist/sdd-tools-error.js.map +1 -0
  1013. package/sdk/dist/sdd-tools.d.ts +97 -0
  1014. package/sdk/dist/sdd-tools.d.ts.map +1 -0
  1015. package/sdk/dist/sdd-tools.js +168 -0
  1016. package/sdk/dist/sdd-tools.js.map +1 -0
  1017. package/sdk/dist/sdd-transport-policy.d.ts +10 -0
  1018. package/sdk/dist/sdd-transport-policy.d.ts.map +1 -0
  1019. package/sdk/dist/sdd-transport-policy.js +32 -0
  1020. package/sdk/dist/sdd-transport-policy.js.map +1 -0
  1021. package/sdk/dist/sdd-transport.d.ts +39 -0
  1022. package/sdk/dist/sdd-transport.d.ts.map +1 -0
  1023. package/sdk/dist/sdd-transport.js +78 -0
  1024. package/sdk/dist/sdd-transport.js.map +1 -0
  1025. package/sdk/dist/sdk-package-compatibility.d.ts +38 -0
  1026. package/sdk/dist/sdk-package-compatibility.d.ts.map +1 -0
  1027. package/sdk/dist/sdk-package-compatibility.js +90 -0
  1028. package/sdk/dist/sdk-package-compatibility.js.map +1 -0
  1029. package/sdk/dist/session-runner.d.ts +40 -0
  1030. package/sdk/dist/session-runner.d.ts.map +1 -0
  1031. package/sdk/dist/session-runner.js +274 -0
  1032. package/sdk/dist/session-runner.js.map +1 -0
  1033. package/sdk/dist/tool-scoping.d.ts +31 -0
  1034. package/sdk/dist/tool-scoping.d.ts.map +1 -0
  1035. package/sdk/dist/tool-scoping.js +54 -0
  1036. package/sdk/dist/tool-scoping.js.map +1 -0
  1037. package/sdk/dist/types.d.ts +794 -0
  1038. package/sdk/dist/types.d.ts.map +1 -0
  1039. package/sdk/dist/types.js +77 -0
  1040. package/sdk/dist/types.js.map +1 -0
  1041. package/sdk/dist/workstream-name-policy.d.ts +13 -0
  1042. package/sdk/dist/workstream-name-policy.d.ts.map +1 -0
  1043. package/sdk/dist/workstream-name-policy.js +24 -0
  1044. package/sdk/dist/workstream-name-policy.js.map +1 -0
  1045. package/sdk/dist/workstream-utils.d.ts +15 -0
  1046. package/sdk/dist/workstream-utils.d.ts.map +1 -0
  1047. package/sdk/dist/workstream-utils.js +21 -0
  1048. package/sdk/dist/workstream-utils.js.map +1 -0
  1049. package/sdk/dist/ws-transport.d.ts +32 -0
  1050. package/sdk/dist/ws-transport.d.ts.map +1 -0
  1051. package/sdk/dist/ws-transport.js +84 -0
  1052. package/sdk/dist/ws-transport.js.map +1 -0
  1053. package/sdk/package-lock.json +2502 -0
  1054. package/sdk/package.json +57 -0
  1055. package/sdk/prompts/templates/project.md +186 -0
  1056. package/sdk/prompts/templates/requirements.md +231 -0
  1057. package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
  1058. package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
  1059. package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
  1060. package/sdk/prompts/templates/research-project/STACK.md +120 -0
  1061. package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
  1062. package/sdk/prompts/templates/roadmap.md +202 -0
  1063. package/sdk/prompts/templates/state.md +175 -0
  1064. package/sdk/shared/model-catalog.json +122 -0
  1065. package/sdk/src/assembled-prompts.test.ts +349 -0
  1066. package/sdk/src/bug-3591-sddtools-runtime-workstream.test.ts +179 -0
  1067. package/sdk/src/cli-transport.test.ts +388 -0
  1068. package/sdk/src/cli-transport.ts +130 -0
  1069. package/sdk/src/cli.test.ts +426 -0
  1070. package/sdk/src/cli.ts +589 -0
  1071. package/sdk/src/config.test.ts +271 -0
  1072. package/sdk/src/config.ts +218 -0
  1073. package/sdk/src/context-engine.test.ts +295 -0
  1074. package/sdk/src/context-engine.ts +170 -0
  1075. package/sdk/src/context-truncation.test.ts +163 -0
  1076. package/sdk/src/context-truncation.ts +233 -0
  1077. package/sdk/src/e2e.integration.test.ts +181 -0
  1078. package/sdk/src/errors.ts +72 -0
  1079. package/sdk/src/event-stream.test.ts +661 -0
  1080. package/sdk/src/event-stream.ts +441 -0
  1081. package/sdk/src/golden/capture.ts +95 -0
  1082. package/sdk/src/golden/fixtures/generate-slug.golden.json +1 -0
  1083. package/sdk/src/golden/fixtures/profile-sample-sessions/demo-project/sample.jsonl +3 -0
  1084. package/sdk/src/golden/fixtures/summary-extract-sample.md +26 -0
  1085. package/sdk/src/golden/fixtures/uat-render-checkpoint-sample.md +15 -0
  1086. package/sdk/src/golden/golden-integration-covered.ts +30 -0
  1087. package/sdk/src/golden/golden-mutation-covered.ts +17 -0
  1088. package/sdk/src/golden/golden-policy.test.ts +8 -0
  1089. package/sdk/src/golden/golden-policy.ts +120 -0
  1090. package/sdk/src/golden/golden.integration.test.ts +677 -0
  1091. package/sdk/src/golden/init-golden-normalize.ts +15 -0
  1092. package/sdk/src/golden/read-only-golden-rows.ts +77 -0
  1093. package/sdk/src/golden/read-only-parity.integration.test.ts +133 -0
  1094. package/sdk/src/golden/registry-canonical-commands.ts +31 -0
  1095. package/sdk/src/index.ts +352 -0
  1096. package/sdk/src/init-e2e.integration.test.ts +138 -0
  1097. package/sdk/src/init-runner.test.ts +740 -0
  1098. package/sdk/src/init-runner.ts +734 -0
  1099. package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
  1100. package/sdk/src/logger.test.ts +149 -0
  1101. package/sdk/src/logger.ts +113 -0
  1102. package/sdk/src/milestone-runner.test.ts +421 -0
  1103. package/sdk/src/model-catalog.ts +70 -0
  1104. package/sdk/src/phase-prompt.test.ts +535 -0
  1105. package/sdk/src/phase-prompt.ts +259 -0
  1106. package/sdk/src/phase-runner-types.test.ts +421 -0
  1107. package/sdk/src/phase-runner.integration.test.ts +377 -0
  1108. package/sdk/src/phase-runner.test.ts +2720 -0
  1109. package/sdk/src/phase-runner.ts +1442 -0
  1110. package/sdk/src/plan-parser.test.ts +579 -0
  1111. package/sdk/src/plan-parser.ts +431 -0
  1112. package/sdk/src/planning-journal.test.ts +70 -0
  1113. package/sdk/src/planning-journal.ts +153 -0
  1114. package/sdk/src/planning-runtime.test.ts +29 -0
  1115. package/sdk/src/planning-runtime.ts +100 -0
  1116. package/sdk/src/prompt-builder.test.ts +318 -0
  1117. package/sdk/src/prompt-builder.ts +218 -0
  1118. package/sdk/src/prompt-sanitizer.test.ts +260 -0
  1119. package/sdk/src/prompt-sanitizer.ts +116 -0
  1120. package/sdk/src/query/QUERY-HANDLERS.md +349 -0
  1121. package/sdk/src/query/active-workstream-store.ts +50 -0
  1122. package/sdk/src/query/agent-failure-classifier.test.ts +157 -0
  1123. package/sdk/src/query/agent-failure-classifier.ts +105 -0
  1124. package/sdk/src/query/audit-open.ts +722 -0
  1125. package/sdk/src/query/check-auto-mode.test.ts +77 -0
  1126. package/sdk/src/query/check-auto-mode.ts +49 -0
  1127. package/sdk/src/query/check-completion.test.ts +113 -0
  1128. package/sdk/src/query/check-completion.ts +182 -0
  1129. package/sdk/src/query/check-decision-coverage.test.ts +519 -0
  1130. package/sdk/src/query/check-decision-coverage.ts +554 -0
  1131. package/sdk/src/query/check-gates.test.ts +103 -0
  1132. package/sdk/src/query/check-gates.ts +112 -0
  1133. package/sdk/src/query/check-ship-ready.test.ts +111 -0
  1134. package/sdk/src/query/check-ship-ready.ts +104 -0
  1135. package/sdk/src/query/check-verification-status.test.ts +143 -0
  1136. package/sdk/src/query/check-verification-status.ts +160 -0
  1137. package/sdk/src/query/command-aliases.generated.ts +156 -0
  1138. package/sdk/src/query/command-catalog.ts +31 -0
  1139. package/sdk/src/query/command-definition.test.ts +47 -0
  1140. package/sdk/src/query/command-definition.ts +70 -0
  1141. package/sdk/src/query/command-family-handlers.ts +117 -0
  1142. package/sdk/src/query/command-manifest.init.ts +24 -0
  1143. package/sdk/src/query/command-manifest.non-family.ts +85 -0
  1144. package/sdk/src/query/command-manifest.phase.ts +16 -0
  1145. package/sdk/src/query/command-manifest.phases.ts +11 -0
  1146. package/sdk/src/query/command-manifest.roadmap.ts +11 -0
  1147. package/sdk/src/query/command-manifest.state.ts +31 -0
  1148. package/sdk/src/query/command-manifest.ts +17 -0
  1149. package/sdk/src/query/command-manifest.types.ts +13 -0
  1150. package/sdk/src/query/command-manifest.validate.ts +11 -0
  1151. package/sdk/src/query/command-manifest.verify.ts +15 -0
  1152. package/sdk/src/query/command-resolution.test.ts +70 -0
  1153. package/sdk/src/query/command-seam-coverage.test.ts +118 -0
  1154. package/sdk/src/query/command-static-catalog-domain.ts +117 -0
  1155. package/sdk/src/query/command-static-catalog-foundation.ts +103 -0
  1156. package/sdk/src/query/command-topology.test.ts +28 -0
  1157. package/sdk/src/query/command-topology.ts +114 -0
  1158. package/sdk/src/query/commands-list.test.ts +36 -0
  1159. package/sdk/src/query/commands-list.ts +19 -0
  1160. package/sdk/src/query/commit.test.ts +485 -0
  1161. package/sdk/src/query/commit.ts +383 -0
  1162. package/sdk/src/query/config-gates.test.ts +89 -0
  1163. package/sdk/src/query/config-gates.ts +69 -0
  1164. package/sdk/src/query/config-mutation.test.ts +598 -0
  1165. package/sdk/src/query/config-mutation.ts +575 -0
  1166. package/sdk/src/query/config-query.test.ts +367 -0
  1167. package/sdk/src/query/config-query.ts +244 -0
  1168. package/sdk/src/query/config-schema.ts +159 -0
  1169. package/sdk/src/query/decisions.test.ts +215 -0
  1170. package/sdk/src/query/decisions.ts +192 -0
  1171. package/sdk/src/query/decomposed-handlers.test.ts +431 -0
  1172. package/sdk/src/query/detect-custom-files.test.ts +115 -0
  1173. package/sdk/src/query/detect-custom-files.ts +96 -0
  1174. package/sdk/src/query/detect-phase-type.test.ts +105 -0
  1175. package/sdk/src/query/detect-phase-type.ts +141 -0
  1176. package/sdk/src/query/docs-init.ts +258 -0
  1177. package/sdk/src/query/fallow-audit.ts +88 -0
  1178. package/sdk/src/query/frontmatter-array.test.ts +14 -0
  1179. package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
  1180. package/sdk/src/query/frontmatter-mutation.ts +343 -0
  1181. package/sdk/src/query/frontmatter.test.ts +326 -0
  1182. package/sdk/src/query/frontmatter.ts +395 -0
  1183. package/sdk/src/query/helpers.test.ts +615 -0
  1184. package/sdk/src/query/helpers.ts +646 -0
  1185. package/sdk/src/query/index-thin-seam.test.ts +16 -0
  1186. package/sdk/src/query/index.ts +9 -0
  1187. package/sdk/src/query/init-complex.test.ts +616 -0
  1188. package/sdk/src/query/init-complex.ts +799 -0
  1189. package/sdk/src/query/init-progress-precedence.test.ts +177 -0
  1190. package/sdk/src/query/init-workstream-milestone-op.test.ts +321 -0
  1191. package/sdk/src/query/init.test.ts +792 -0
  1192. package/sdk/src/query/init.ts +1262 -0
  1193. package/sdk/src/query/intel.test.ts +90 -0
  1194. package/sdk/src/query/intel.ts +404 -0
  1195. package/sdk/src/query/mutation-event-decorator.test.ts +45 -0
  1196. package/sdk/src/query/mutation-event-decorator.ts +37 -0
  1197. package/sdk/src/query/mutation-event-mapper.test.ts +33 -0
  1198. package/sdk/src/query/mutation-event-mapper.ts +102 -0
  1199. package/sdk/src/query/mvp.test.ts +335 -0
  1200. package/sdk/src/query/mvp.ts +292 -0
  1201. package/sdk/src/query/normalize-query-command.test.ts +102 -0
  1202. package/sdk/src/query/phase-filesystem-adapter.ts +35 -0
  1203. package/sdk/src/query/phase-lifecycle-policy.ts +171 -0
  1204. package/sdk/src/query/phase-lifecycle.test.ts +1750 -0
  1205. package/sdk/src/query/phase-lifecycle.ts +1833 -0
  1206. package/sdk/src/query/phase-list-queries.test.ts +88 -0
  1207. package/sdk/src/query/phase-list-queries.ts +152 -0
  1208. package/sdk/src/query/phase-ready.test.ts +65 -0
  1209. package/sdk/src/query/phase-ready.ts +159 -0
  1210. package/sdk/src/query/phase-roadmap-mutation.ts +77 -0
  1211. package/sdk/src/query/phase.test.ts +651 -0
  1212. package/sdk/src/query/phase.ts +550 -0
  1213. package/sdk/src/query/pipeline.test.ts +169 -0
  1214. package/sdk/src/query/pipeline.ts +243 -0
  1215. package/sdk/src/query/plan-scan.test.ts +35 -0
  1216. package/sdk/src/query/plan-scan.ts +82 -0
  1217. package/sdk/src/query/plan-task-structure.test.ts +65 -0
  1218. package/sdk/src/query/plan-task-structure.ts +63 -0
  1219. package/sdk/src/query/policy-convergence.test.ts +28 -0
  1220. package/sdk/src/query/profile-extract-messages.ts +247 -0
  1221. package/sdk/src/query/profile-output.ts +929 -0
  1222. package/sdk/src/query/profile-questionnaire-data.ts +181 -0
  1223. package/sdk/src/query/profile-sample.ts +184 -0
  1224. package/sdk/src/query/profile-scan-sessions.ts +174 -0
  1225. package/sdk/src/query/profile.test.ts +136 -0
  1226. package/sdk/src/query/profile.ts +337 -0
  1227. package/sdk/src/query/progress.test.ts +156 -0
  1228. package/sdk/src/query/progress.ts +566 -0
  1229. package/sdk/src/query/query-cli-adapter.test.ts +79 -0
  1230. package/sdk/src/query/query-cli-adapter.ts +39 -0
  1231. package/sdk/src/query/query-cli-output.test.ts +33 -0
  1232. package/sdk/src/query/query-cli-output.ts +35 -0
  1233. package/sdk/src/query/query-command-diagnosis.test.ts +22 -0
  1234. package/sdk/src/query/query-command-diagnosis.ts +5 -0
  1235. package/sdk/src/query/query-command-resolution-strategy.test.ts +34 -0
  1236. package/sdk/src/query/query-command-resolution-strategy.ts +121 -0
  1237. package/sdk/src/query/query-command-semantics.test.ts +22 -0
  1238. package/sdk/src/query/query-command-semantics.ts +22 -0
  1239. package/sdk/src/query/query-dispatch-contract.ts +30 -0
  1240. package/sdk/src/query/query-dispatch-error-mapper.test.ts +62 -0
  1241. package/sdk/src/query/query-dispatch-error-mapper.ts +5 -0
  1242. package/sdk/src/query/query-dispatch-formatting.test.ts +28 -0
  1243. package/sdk/src/query/query-dispatch-formatting.ts +5 -0
  1244. package/sdk/src/query/query-dispatch-input-validation.test.ts +23 -0
  1245. package/sdk/src/query/query-dispatch-input-validation.ts +5 -0
  1246. package/sdk/src/query/query-dispatch-observability.test.ts +10 -0
  1247. package/sdk/src/query/query-dispatch-observability.ts +6 -0
  1248. package/sdk/src/query/query-dispatch-plan.test.ts +25 -0
  1249. package/sdk/src/query/query-dispatch-plan.ts +5 -0
  1250. package/sdk/src/query/query-dispatch-result-builder.test.ts +16 -0
  1251. package/sdk/src/query/query-dispatch-result-builder.ts +5 -0
  1252. package/sdk/src/query/query-dispatch.test.ts +399 -0
  1253. package/sdk/src/query/query-dispatch.ts +243 -0
  1254. package/sdk/src/query/query-error-details-schema.ts +29 -0
  1255. package/sdk/src/query/query-error-taxonomy.test.ts +39 -0
  1256. package/sdk/src/query/query-error-taxonomy.ts +117 -0
  1257. package/sdk/src/query/query-fallback-bridge-adapter.test.ts +32 -0
  1258. package/sdk/src/query/query-fallback-bridge-adapter.ts +54 -0
  1259. package/sdk/src/query/query-fallback-executor.test.ts +82 -0
  1260. package/sdk/src/query/query-fallback-executor.ts +44 -0
  1261. package/sdk/src/query/query-fallback-output-classifier.test.ts +36 -0
  1262. package/sdk/src/query/query-fallback-output-classifier.ts +31 -0
  1263. package/sdk/src/query/query-fallback-policy.test.ts +13 -0
  1264. package/sdk/src/query/query-fallback-policy.ts +11 -0
  1265. package/sdk/src/query/query-native-dispatch-adapter.ts +16 -0
  1266. package/sdk/src/query/query-policy-capability.test.ts +10 -0
  1267. package/sdk/src/query/query-policy-capability.ts +26 -0
  1268. package/sdk/src/query/query-policy-snapshot.test.ts +9 -0
  1269. package/sdk/src/query/query-registry-capability.test.ts +14 -0
  1270. package/sdk/src/query/query-runtime-context.ts +44 -0
  1271. package/sdk/src/query/query-unknown-command-hints.test.ts +9 -0
  1272. package/sdk/src/query/query-unknown-command-hints.ts +5 -0
  1273. package/sdk/src/query/registry-assembly-descriptor.ts +87 -0
  1274. package/sdk/src/query/registry-assembly-invariants.ts +127 -0
  1275. package/sdk/src/query/registry-assembly.test.ts +138 -0
  1276. package/sdk/src/query/registry-assembly.ts +78 -0
  1277. package/sdk/src/query/registry.test.ts +208 -0
  1278. package/sdk/src/query/registry.ts +142 -0
  1279. package/sdk/src/query/requirements-extract-from-plans.test.ts +58 -0
  1280. package/sdk/src/query/requirements-extract-from-plans.ts +86 -0
  1281. package/sdk/src/query/roadmap-update-plan-progress.test.ts +233 -0
  1282. package/sdk/src/query/roadmap-update-plan-progress.ts +159 -0
  1283. package/sdk/src/query/roadmap.test.ts +1181 -0
  1284. package/sdk/src/query/roadmap.ts +894 -0
  1285. package/sdk/src/query/route-next-action.test.ts +61 -0
  1286. package/sdk/src/query/route-next-action.ts +345 -0
  1287. package/sdk/src/query/schema-detect.ts +189 -0
  1288. package/sdk/src/query/secrets.test.ts +66 -0
  1289. package/sdk/src/query/secrets.ts +43 -0
  1290. package/sdk/src/query/skill-manifest.test.ts +62 -0
  1291. package/sdk/src/query/skill-manifest.ts +216 -0
  1292. package/sdk/src/query/skills.test.ts +234 -0
  1293. package/sdk/src/query/skills.ts +143 -0
  1294. package/sdk/src/query/state-document.test.ts +197 -0
  1295. package/sdk/src/query/state-document.ts +129 -0
  1296. package/sdk/src/query/state-mutation.test.ts +1198 -0
  1297. package/sdk/src/query/state-mutation.ts +1718 -0
  1298. package/sdk/src/query/state-project-load.ts +80 -0
  1299. package/sdk/src/query/state.test.ts +616 -0
  1300. package/sdk/src/query/state.ts +463 -0
  1301. package/sdk/src/query/sub-repos-root.integration.test.ts +79 -0
  1302. package/sdk/src/query/summary.test.ts +95 -0
  1303. package/sdk/src/query/summary.ts +296 -0
  1304. package/sdk/src/query/template.test.ts +180 -0
  1305. package/sdk/src/query/template.ts +242 -0
  1306. package/sdk/src/query/uat.test.ts +77 -0
  1307. package/sdk/src/query/uat.ts +365 -0
  1308. package/sdk/src/query/utils.test.ts +82 -0
  1309. package/sdk/src/query/utils.ts +106 -0
  1310. package/sdk/src/query/validate.test.ts +831 -0
  1311. package/sdk/src/query/validate.ts +952 -0
  1312. package/sdk/src/query/verify.test.ts +414 -0
  1313. package/sdk/src/query/verify.ts +692 -0
  1314. package/sdk/src/query/websearch.test.ts +31 -0
  1315. package/sdk/src/query/websearch.ts +82 -0
  1316. package/sdk/src/query/workspace.test.ts +120 -0
  1317. package/sdk/src/query/workspace.ts +145 -0
  1318. package/sdk/src/query/workstream-inventory.ts +195 -0
  1319. package/sdk/src/query/workstream.test.ts +153 -0
  1320. package/sdk/src/query/workstream.ts +324 -0
  1321. package/sdk/src/query/worktree.ts +39 -0
  1322. package/sdk/src/query-command-executor.ts +31 -0
  1323. package/sdk/src/query-execution-policy.test.ts +52 -0
  1324. package/sdk/src/query-execution-policy.ts +46 -0
  1325. package/sdk/src/query-failure-classification.test.ts +23 -0
  1326. package/sdk/src/query-failure-classification.ts +42 -0
  1327. package/sdk/src/query-hotpath-methods.ts +48 -0
  1328. package/sdk/src/query-native-direct-adapter.test.ts +35 -0
  1329. package/sdk/src/query-native-direct-adapter.ts +70 -0
  1330. package/sdk/src/query-native-hotpath-adapter.test.ts +43 -0
  1331. package/sdk/src/query-native-hotpath-adapter.ts +45 -0
  1332. package/sdk/src/query-raw-output-projection.test.ts +39 -0
  1333. package/sdk/src/query-raw-output-projection.ts +74 -0
  1334. package/sdk/src/query-runtime-bridge.test.ts +150 -0
  1335. package/sdk/src/query-runtime-bridge.ts +215 -0
  1336. package/sdk/src/query-runtime-seam-coverage.test.ts +20 -0
  1337. package/sdk/src/query-sdd-tools-path.ts +1 -0
  1338. package/sdk/src/query-sdd-tools-runtime.ts +89 -0
  1339. package/sdk/src/query-subprocess-adapter.test.ts +84 -0
  1340. package/sdk/src/query-subprocess-adapter.ts +146 -0
  1341. package/sdk/src/query-tools-error-factory.test.ts +35 -0
  1342. package/sdk/src/query-tools-error-factory.ts +76 -0
  1343. package/sdk/src/research-gate.test.ts +190 -0
  1344. package/sdk/src/research-gate.ts +94 -0
  1345. package/sdk/src/runtime-bridge-options.test.ts +33 -0
  1346. package/sdk/src/runtime-gate.test.ts +84 -0
  1347. package/sdk/src/runtime-gate.ts +52 -0
  1348. package/sdk/src/sdd-tools-error.test.ts +21 -0
  1349. package/sdk/src/sdd-tools-error.ts +65 -0
  1350. package/sdk/src/sdd-tools.test.ts +472 -0
  1351. package/sdk/src/sdd-tools.ts +237 -0
  1352. package/sdk/src/sdd-transport-policy.test.ts +34 -0
  1353. package/sdk/src/sdd-transport-policy.ts +48 -0
  1354. package/sdk/src/sdd-transport.test.ts +292 -0
  1355. package/sdk/src/sdd-transport.ts +117 -0
  1356. package/sdk/src/sdk-package-compatibility.test.ts +97 -0
  1357. package/sdk/src/sdk-package-compatibility.ts +141 -0
  1358. package/sdk/src/session-runner.test.ts +164 -0
  1359. package/sdk/src/session-runner.ts +327 -0
  1360. package/sdk/src/tool-scoping.test.ts +160 -0
  1361. package/sdk/src/tool-scoping.ts +61 -0
  1362. package/sdk/src/types.ts +927 -0
  1363. package/sdk/src/workflow-agent-skills-consistency.test.ts +98 -0
  1364. package/sdk/src/workstream-name-policy.ts +24 -0
  1365. package/sdk/src/workstream-utils.ts +21 -0
  1366. package/sdk/src/ws-flag.test.ts +285 -0
  1367. package/sdk/src/ws-transport.test.ts +161 -0
  1368. package/sdk/src/ws-transport.ts +93 -0
  1369. package/sdk/tsconfig.json +20 -0
  1370. package/commands/sdd/add-backlog.md +0 -76
  1371. package/commands/sdd/add-phase.md +0 -43
  1372. package/commands/sdd/add-todo.md +0 -47
  1373. package/commands/sdd/check-todos.md +0 -45
  1374. package/commands/sdd/do.md +0 -30
  1375. package/commands/sdd/insert-phase.md +0 -32
  1376. package/commands/sdd/join-discord.md +0 -18
  1377. package/commands/sdd/list-phase-assumptions.md +0 -46
  1378. package/commands/sdd/list-workspaces.md +0 -19
  1379. package/commands/sdd/new-workspace.md +0 -44
  1380. package/commands/sdd/next.md +0 -24
  1381. package/commands/sdd/note.md +0 -34
  1382. package/commands/sdd/plan-milestone-gaps.md +0 -34
  1383. package/commands/sdd/plant-seed.md +0 -28
  1384. package/commands/sdd/reapply-patches.md +0 -123
  1385. package/commands/sdd/remove-phase.md +0 -31
  1386. package/commands/sdd/remove-workspace.md +0 -26
  1387. package/commands/sdd/research-phase.md +0 -195
  1388. package/commands/sdd/session-report.md +0 -19
  1389. package/commands/sdd/set-profile.md +0 -12
  1390. package/scripts/sync-upstream.sh +0 -56
  1391. package/sdd/commands/sdd/workstreams.md +0 -63
  1392. package/sdd/workflows/research-phase.md +0 -82
@@ -4,9 +4,63 @@
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
- const { escapeRegex, loadConfig, normalizePhaseName, comparePhaseNum, findPhaseInternal, getArchivedPhaseDirs, generateSlugInternal, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, toPosixPath, planningDir, output, error, readSubdirectories } = require('./core.cjs');
7
+ const { escapeRegex, loadConfig, normalizePhaseName, phaseMarkdownRegexSource, comparePhaseNum, findPhaseInternal, getArchivedPhaseDirs, generateSlugInternal, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, toPosixPath, output, error, readSubdirectories, phaseTokenMatches, ERROR_REASON } = require('./core.cjs');
8
+ const { platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
9
+ const { planningDir, withPlanningLock } = require('./planning-workspace.cjs');
8
10
  const { extractFrontmatter } = require('./frontmatter.cjs');
9
- const { writeStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback } = require('./state.cjs');
11
+ const { writeStateMd, readModifyWriteStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, updatePerformanceMetricsSection } = require('./state.cjs');
12
+
13
+ // #2893 — strict canonical filter: `{padded_phase}-{NN}-PLAN.md` or `PLAN.md`.
14
+ // Documented in agents/sdd-planner.md (write_phase_prompt step). The wider
15
+ // "looks like a plan but isn't canonical" probe below is used to surface a
16
+ // loud warning instead of silently returning zero plans.
17
+ const isCanonicalPlanFile = (f) => f.endsWith('-PLAN.md') || f === 'PLAN.md';
18
+
19
+ // Any .md file with PLAN anywhere in the basename — the diagnostic net for
20
+ // catching agent deviations like `01-PLAN-01-foundation.md` (#2893).
21
+ // Excludes derivative files (`-PLAN-OUTLINE.md`, `*.pre-bounce.md`, etc.) that
22
+ // the planner legitimately produces alongside canonical plans.
23
+ const PLAN_OUTLINE_RE = /-PLAN-OUTLINE\.md$/i;
24
+ const PLAN_PRE_BOUNCE_RE = /-PLAN.*\.pre-bounce\.md$/i;
25
+ const looksLikePlanFile = (f) =>
26
+ /\.md$/i.test(f)
27
+ && /PLAN/i.test(f)
28
+ && !PLAN_OUTLINE_RE.test(f)
29
+ && !PLAN_PRE_BOUNCE_RE.test(f);
30
+
31
+ /**
32
+ * Detect plan-shaped files that the canonical filter would reject. Returns
33
+ * a warning string when offenders exist, else null. Centralised so every
34
+ * read site (phase-plan-index, phases list --type plans, find-phase) emits
35
+ * the same message.
36
+ *
37
+ * @param {string[]} dirFiles — readdirSync output for one phase directory
38
+ * @param {string[]} matchedFiles — what the canonical filter accepted
39
+ * @returns {string|null}
40
+ */
41
+ function describeNonCanonicalPlans(dirFiles, matchedFiles) {
42
+ const matched = new Set(matchedFiles);
43
+ const offenders = dirFiles.filter((f) => looksLikePlanFile(f) && !matched.has(f));
44
+ if (offenders.length === 0) return null;
45
+ return (
46
+ `Found ${offenders.length} plan-shaped file(s) in this phase that don't match the canonical ` +
47
+ `naming convention "{padded_phase}-{NN}-PLAN.md" (or bare "PLAN.md") and were skipped: ` +
48
+ offenders.map((f) => `"${f}"`).join(', ') +
49
+ `. Rename to the canonical form (e.g. "01-01-PLAN.md") so the executor can detect them. ` +
50
+ `See agents/sdd-planner.md write_phase_prompt step for the full contract.`
51
+ );
52
+ }
53
+
54
+ function extractCanonicalPlanId(filename) {
55
+ const base = filename.replace(/-PLAN\.md$/i, '').replace(/-SUMMARY\.md$/i, '').replace(/\.md$/i, '');
56
+ const parts = base.split('-').filter(Boolean);
57
+ const tokenRe = /^\d+[A-Z]?(?:\.\d+)*$/i;
58
+ const phaseIdx = parts.findIndex(p => tokenRe.test(p));
59
+ if (phaseIdx >= 0 && phaseIdx + 1 < parts.length && tokenRe.test(parts[phaseIdx + 1])) {
60
+ return `${parts[phaseIdx]}-${parts[phaseIdx + 1]}`;
61
+ }
62
+ return base;
63
+ }
10
64
 
11
65
  function cmdPhasesList(cwd, options, raw) {
12
66
  const phasesDir = path.join(planningDir(cwd), 'phases');
@@ -41,7 +95,7 @@ function cmdPhasesList(cwd, options, raw) {
41
95
  // If filtering by phase number
42
96
  if (phase) {
43
97
  const normalized = normalizePhaseName(phase);
44
- const match = dirs.find(d => d.startsWith(normalized));
98
+ const match = dirs.find(d => phaseTokenMatches(d, normalized));
45
99
  if (!match) {
46
100
  output({ files: [], count: 0, phase_dir: null, error: 'Phase not found' }, raw, '');
47
101
  return;
@@ -52,13 +106,18 @@ function cmdPhasesList(cwd, options, raw) {
52
106
  // If listing files of a specific type
53
107
  if (type) {
54
108
  const files = [];
109
+ const warnings = [];
55
110
  for (const dir of dirs) {
56
111
  const dirPath = path.join(phasesDir, dir);
57
112
  const dirFiles = fs.readdirSync(dirPath);
58
113
 
59
114
  let filtered;
60
115
  if (type === 'plans') {
61
- filtered = dirFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
116
+ filtered = dirFiles.filter(isCanonicalPlanFile);
117
+ // #2893 — surface plan-shaped files the canonical filter rejected
118
+ // so callers (executor init, etc.) don't silently see zero plans.
119
+ const w = describeNonCanonicalPlans(dirFiles, filtered);
120
+ if (w) warnings.push(`${dir}: ${w}`);
62
121
  } else if (type === 'summaries') {
63
122
  filtered = dirFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
64
123
  } else {
@@ -73,6 +132,7 @@ function cmdPhasesList(cwd, options, raw) {
73
132
  count: files.length,
74
133
  phase_dir: phase ? dirs[0].replace(/^\d+(?:\.\d+)*-?/, '') : null,
75
134
  };
135
+ if (warnings.length) result.warning = warnings.join(' | ');
76
136
  output(result, raw, files.join('\n'));
77
137
  return;
78
138
  }
@@ -88,50 +148,51 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
88
148
  const phasesDir = path.join(planningDir(cwd), 'phases');
89
149
  const normalized = normalizePhaseName(basePhase);
90
150
 
91
- // Check if phases directory exists
92
- if (!fs.existsSync(phasesDir)) {
93
- output(
94
- {
95
- found: false,
96
- base_phase: normalized,
97
- next: `${normalized}.1`,
98
- existing: [],
99
- },
100
- raw,
101
- `${normalized}.1`
102
- );
103
- return;
104
- }
105
-
106
151
  try {
107
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
108
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
109
-
110
- // Check if base phase exists
111
- const baseExists = dirs.some(d => d.startsWith(normalized + '-') || d === normalized);
152
+ let baseExists = false;
153
+ const decimalSet = new Set();
112
154
 
113
- // Find existing decimal phases for this base
114
- const decimalPattern = new RegExp(`^${normalized}\\.(\\d+)`);
115
- const existingDecimals = [];
155
+ // Scan directory names for existing decimal phases
156
+ if (fs.existsSync(phasesDir)) {
157
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
158
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
159
+ baseExists = dirs.some(d => phaseTokenMatches(d, normalized));
116
160
 
117
- for (const dir of dirs) {
118
- const match = dir.match(decimalPattern);
119
- if (match) {
120
- existingDecimals.push(`${normalized}.${match[1]}`);
161
+ const dirPattern = new RegExp(`^(?:[A-Z]{1,6}-)?${escapeRegex(normalized)}\\.(\\d+)`);
162
+ for (const dir of dirs) {
163
+ const match = dir.match(dirPattern);
164
+ if (match) decimalSet.add(parseInt(match[1], 10));
121
165
  }
122
166
  }
123
167
 
124
- // Sort numerically
125
- existingDecimals.sort((a, b) => comparePhaseNum(a, b));
168
+ // Also scan ROADMAP.md for phase entries that may not have directories yet
169
+ const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
170
+ if (fs.existsSync(roadmapPath)) {
171
+ try {
172
+ const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
173
+ // #3537: padding-tolerant on both sides — `0*${escapeRegex(...)}`
174
+ // tolerated extra padding but not missing.
175
+ const phasePattern = new RegExp(
176
+ `#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(normalized)}\\.(\\d+)\\s*:`, 'gi'
177
+ );
178
+ let pm;
179
+ while ((pm = phasePattern.exec(roadmapContent)) !== null) {
180
+ decimalSet.add(parseInt(pm[1], 10));
181
+ }
182
+ } catch { /* ROADMAP.md read failure is non-fatal */ }
183
+ }
184
+
185
+ // Build sorted list of existing decimals
186
+ const existingDecimals = Array.from(decimalSet)
187
+ .sort((a, b) => a - b)
188
+ .map(n => `${normalized}.${n}`);
126
189
 
127
190
  // Calculate next decimal
128
191
  let nextDecimal;
129
- if (existingDecimals.length === 0) {
192
+ if (decimalSet.size === 0) {
130
193
  nextDecimal = `${normalized}.1`;
131
194
  } else {
132
- const lastDecimal = existingDecimals[existingDecimals.length - 1];
133
- const lastNum = parseInt(lastDecimal.split('.')[1], 10);
134
- nextDecimal = `${normalized}.${lastNum + 1}`;
195
+ nextDecimal = `${normalized}.${Math.max(...decimalSet) + 1}`;
135
196
  }
136
197
 
137
198
  output(
@@ -149,48 +210,128 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
149
210
  }
150
211
  }
151
212
 
213
+ function getRoadmapModeForPhase(cwd, phaseNum) {
214
+ const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
215
+ if (!fs.existsSync(roadmapPath)) return null;
216
+
217
+ const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
218
+ const milestoneContent = extractCurrentMilestone(rawContent, cwd);
219
+ const fullContent = stripShippedMilestones(rawContent);
220
+ const escapedPhase = phaseMarkdownRegexSource(phaseNum);
221
+ const phaseHeader = new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}\\s*:`, 'i');
222
+
223
+ for (const content of [milestoneContent, fullContent]) {
224
+ const headerMatch = content.match(phaseHeader);
225
+ if (!headerMatch || headerMatch.index === undefined) continue;
226
+
227
+ const sectionStart = headerMatch.index;
228
+ const rest = content.slice(sectionStart);
229
+ const nextHeader = rest.slice(headerMatch[0].length).match(/\n#{2,4}\s+Phase\s+\S/i);
230
+ const sectionEnd = nextHeader ? sectionStart + headerMatch[0].length + nextHeader.index : content.length;
231
+ const section = content.slice(sectionStart, sectionEnd);
232
+ const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
233
+ if (modeMatch) return modeMatch[1].trim().toLowerCase();
234
+ }
235
+
236
+ return null;
237
+ }
238
+
239
+ function cmdPhaseMvpMode(cwd, args, raw) {
240
+ const phaseNum = args[0];
241
+ if (!phaseNum) {
242
+ error('Usage: phase.mvp-mode <phase-number> [--cli-flag]', ERROR_REASON.USAGE);
243
+ }
244
+
245
+ const cliFlagPresent = args.includes('--cli-flag');
246
+ const roadmapMode = getRoadmapModeForPhase(cwd, phaseNum);
247
+ const config = loadConfig(cwd);
248
+ const configMvpMode = Boolean(config.mvp_mode);
249
+
250
+ let active = false;
251
+ let source = 'none';
252
+ if (cliFlagPresent) {
253
+ active = true;
254
+ source = 'cli_flag';
255
+ } else if (roadmapMode === 'mvp') {
256
+ active = true;
257
+ source = 'roadmap';
258
+ } else if (configMvpMode) {
259
+ active = true;
260
+ source = 'config';
261
+ }
262
+
263
+ output({
264
+ active,
265
+ source,
266
+ roadmap_mode: roadmapMode,
267
+ config_mvp_mode: configMvpMode,
268
+ cli_flag_present: cliFlagPresent,
269
+ }, raw);
270
+ }
271
+
152
272
  function cmdFindPhase(cwd, phase, raw) {
153
273
  if (!phase) {
154
274
  error('phase identifier required');
155
275
  }
156
276
 
157
- const phasesDir = path.join(planningDir(cwd), 'phases');
277
+ const planBase = planningDir(cwd);
158
278
  const normalized = normalizePhaseName(phase);
279
+ const notFound = { found: false, directory: null, phase_number: null, phase_name: null, plans: [], summaries: [], searched_directories: [] };
159
280
 
160
- const notFound = { found: false, directory: null, phase_number: null, phase_name: null, plans: [], summaries: [] };
161
-
281
+ // Build candidate search dirs: flat layout first, then milestone-archive layout.
282
+ const searchDirs = [];
283
+ const flatPhasesDir = path.join(planBase, 'phases');
284
+ if (fs.existsSync(flatPhasesDir)) searchDirs.push(flatPhasesDir);
162
285
  try {
163
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
164
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
165
-
166
- const match = dirs.find(d => d.startsWith(normalized));
167
- if (!match) {
168
- output(notFound, raw, '');
169
- return;
286
+ const milestonesDir = path.join(planBase, 'milestones');
287
+ const entries = fs.readdirSync(milestonesDir, { withFileTypes: true })
288
+ .filter(e => e.isDirectory() && /^v\d+.*-phases$/.test(e.name))
289
+ .sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
290
+ for (const e of entries) {
291
+ searchDirs.push(path.join(milestonesDir, e.name));
170
292
  }
293
+ } catch { /* no milestones dir */ }
171
294
 
172
- const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);
173
- const phaseNumber = dirMatch ? dirMatch[1] : normalized;
174
- const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
295
+ notFound.searched_directories = searchDirs.map((searchDir) =>
296
+ toPosixPath(path.join(path.relative(cwd, planBase), path.relative(planBase, searchDir))));
175
297
 
176
- const phaseDir = path.join(phasesDir, match);
177
- const phaseFiles = fs.readdirSync(phaseDir);
178
- const plans = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
179
- const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').sort();
298
+ for (const searchDir of searchDirs) {
299
+ try {
300
+ const entries = fs.readdirSync(searchDir, { withFileTypes: true });
301
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
180
302
 
181
- const result = {
182
- found: true,
183
- directory: toPosixPath(path.join(path.relative(cwd, planningDir(cwd)), 'phases', match)),
184
- phase_number: phaseNumber,
185
- phase_name: phaseName,
186
- plans,
187
- summaries,
188
- };
303
+ const match = dirs.find(d => phaseTokenMatches(d, normalized));
304
+ if (!match) continue;
189
305
 
190
- output(result, raw, result.directory);
191
- } catch {
192
- output(notFound, raw, '');
306
+ // Extract phase number — supports project-code-prefixed (CK-01-name), numeric (01-name), and custom IDs
307
+ const dirMatch = match.match(/^(?:[A-Z]{1,6}-)(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i)
308
+ || match.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);
309
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
310
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
311
+
312
+ const phaseDir = path.join(searchDir, match);
313
+ const phaseFiles = fs.readdirSync(phaseDir);
314
+ const plans = phaseFiles.filter(isCanonicalPlanFile).sort();
315
+ const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').sort();
316
+ // #2893 — same diagnostic as phase-plan-index for consistency.
317
+ const planNamingWarning = describeNonCanonicalPlans(phaseFiles, plans);
318
+
319
+ const result = {
320
+ found: true,
321
+ directory: toPosixPath(path.join(path.relative(cwd, planBase), path.relative(planBase, searchDir), match)),
322
+ phase_number: phaseNumber,
323
+ phase_name: phaseName,
324
+ plans,
325
+ summaries,
326
+ };
327
+ if (planNamingWarning) result.warning = planNamingWarning;
328
+
329
+ output(result, raw, result.directory);
330
+ return;
331
+ } catch { continue; }
193
332
  }
333
+
334
+ output(notFound, raw, '');
194
335
  }
195
336
 
196
337
  function extractObjective(content) {
@@ -212,7 +353,7 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
212
353
  try {
213
354
  const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
214
355
  const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
215
- const match = dirs.find(d => d.startsWith(normalized));
356
+ const match = dirs.find(d => phaseTokenMatches(d, normalized));
216
357
  if (match) {
217
358
  phaseDir = path.join(phasesDir, match);
218
359
  phaseDirName = match;
@@ -228,18 +369,24 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
228
369
 
229
370
  // Get all files in phase directory
230
371
  const phaseFiles = fs.readdirSync(phaseDir);
231
- const planFiles = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
372
+ const planFiles = phaseFiles.filter(isCanonicalPlanFile).sort();
232
373
  const summaryFiles = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
374
+ // #2893 — surface plan-shaped files the canonical filter rejected so a
375
+ // misnamed plan never silently produces plan_count: 0 at executor init.
376
+ const planNamingWarning = describeNonCanonicalPlans(phaseFiles, planFiles);
233
377
 
234
378
  // Build set of plan IDs with summaries
235
379
  const completedPlanIds = new Set(
236
- summaryFiles.map(s => s.replace('-SUMMARY.md', '').replace('SUMMARY.md', ''))
380
+ summaryFiles.flatMap(s => {
381
+ const exact = s.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
382
+ const canonical = extractCanonicalPlanId(s);
383
+ return canonical === exact ? [exact] : [exact, canonical];
384
+ })
237
385
  );
238
386
 
239
- const plans = [];
240
- const waves = {};
241
- const incomplete = [];
242
- let hasCheckpoints = false;
387
+ // ── Pass 1: parse each plan file ─────────────────────────────────────────
388
+
389
+ const rawPlans = [];
243
390
 
244
391
  for (const planFile of planFiles) {
245
392
  const planId = planFile.replace('-PLAN.md', '').replace('PLAN.md', '');
@@ -252,8 +399,20 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
252
399
  const mdTasks = content.match(/##\s*Task\s*\d+/gi) || [];
253
400
  const taskCount = xmlTasks.length || mdTasks.length;
254
401
 
255
- // Parse wave as integer
256
- const wave = parseInt(fm.wave, 10) || 1;
402
+ // Parse wave as integer — use nullish handling so wave: 0 is preserved.
403
+ // parseInt returns NaN for missing/non-numeric values; fall back to null
404
+ // (meaning "no declared wave") so downstream can apply the topo default.
405
+ const parsedWave = parseInt(fm.wave, 10);
406
+ const declaredWave = Number.isNaN(parsedWave) ? null : parsedWave;
407
+
408
+ // Parse depends_on — normalise to string[]
409
+ let dependsOn = [];
410
+ const fmDeps = fm['depends_on'];
411
+ if (Array.isArray(fmDeps)) {
412
+ dependsOn = fmDeps.map(String);
413
+ } else if (typeof fmDeps === 'string' && fmDeps.trim() !== '') {
414
+ dependsOn = [fmDeps];
415
+ }
257
416
 
258
417
  // Parse autonomous (default true if not specified)
259
418
  let autonomous = true;
@@ -261,10 +420,6 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
261
420
  autonomous = fm.autonomous === 'true' || fm.autonomous === true;
262
421
  }
263
422
 
264
- if (!autonomous) {
265
- hasCheckpoints = true;
266
- }
267
-
268
423
  // Parse files_modified (underscore is canonical; also accept hyphenated for compat)
269
424
  let filesModified = [];
270
425
  const fmFiles = fm['files_modified'] || fm['files-modified'];
@@ -272,29 +427,130 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
272
427
  filesModified = Array.isArray(fmFiles) ? fmFiles : [fmFiles];
273
428
  }
274
429
 
275
- const hasSummary = completedPlanIds.has(planId);
276
- if (!hasSummary) {
277
- incomplete.push(planId);
278
- }
430
+ const hasSummary = completedPlanIds.has(planId) || completedPlanIds.has(extractCanonicalPlanId(planFile));
279
431
 
280
- const plan = {
432
+ rawPlans.push({
281
433
  id: planId,
282
- wave,
434
+ declaredWave,
435
+ dependsOn,
283
436
  autonomous,
284
437
  objective: extractObjective(content) || fm.objective || null,
285
- files_modified: filesModified,
286
- task_count: taskCount,
287
- has_summary: hasSummary,
438
+ filesModified,
439
+ taskCount,
440
+ hasSummary,
441
+ });
442
+ }
443
+
444
+ // ── Pass 2: topological level assignment via depends_on DAG ──────────────
445
+
446
+ // Build a map from plan ID → raw plan for fast lookup.
447
+ // Deps that reference plans outside this phase are treated as external and ignored.
448
+ const planMap = new Map(rawPlans.map(p => [p.id, p]));
449
+ // Secondary index: canonical prefix → full plan ID, so depends_on: ['03-01'] resolves
450
+ // to '03-01-auth-hardening-PLAN.md'-derived ID '03-01-auth-hardening' (k015).
451
+ const canonicalToId = new Map(rawPlans.map(p => [extractCanonicalPlanId(p.id), p.id]));
452
+
453
+ // Kahn's algorithm — compute in-degree and adjacency for in-phase deps only.
454
+ const level = new Map();
455
+ const inDeg = new Map();
456
+ const adj = new Map();
457
+
458
+ for (const p of rawPlans) {
459
+ if (!inDeg.has(p.id)) inDeg.set(p.id, 0);
460
+ if (!adj.has(p.id)) adj.set(p.id, []);
461
+ for (const dep of p.dependsOn) {
462
+ // Accept both full-stem ('03-01-auth-hardening') and canonical-prefix ('03-01') forms.
463
+ const resolvedDep = planMap.has(dep) ? dep : canonicalToId.get(dep);
464
+ if (!resolvedDep) continue; // external dep — ignore
465
+ if (!adj.has(resolvedDep)) adj.set(resolvedDep, []);
466
+ adj.get(resolvedDep).push(p.id);
467
+ inDeg.set(p.id, (inDeg.get(p.id) ?? 0) + 1);
468
+ }
469
+ }
470
+
471
+ // Start with nodes that have no in-phase dependencies.
472
+ const queue = [];
473
+ for (const p of rawPlans) {
474
+ if ((inDeg.get(p.id) ?? 0) === 0) {
475
+ queue.push(p.id);
476
+ level.set(p.id, 0);
477
+ }
478
+ }
479
+
480
+ let visited = 0;
481
+ while (queue.length > 0) {
482
+ const cur = queue.shift();
483
+ visited++;
484
+ const curLevel = level.get(cur);
485
+ for (const dep of (adj.get(cur) ?? [])) {
486
+ const newLevel = curLevel + 1;
487
+ if (newLevel > (level.get(dep) ?? -1)) {
488
+ level.set(dep, newLevel);
489
+ }
490
+ inDeg.set(dep, inDeg.get(dep) - 1);
491
+ if (inDeg.get(dep) === 0) {
492
+ queue.push(dep);
493
+ }
494
+ }
495
+ }
496
+
497
+ // Cycle detection — any node not visited has a cycle.
498
+ if (visited < rawPlans.length) {
499
+ const cycleNodes = rawPlans.filter(p => !level.has(p.id)).map(p => p.id);
500
+ error(`depends_on cycle detected in phase ${normalized} — cycle involves: ${cycleNodes.join(', ')}`);
501
+ return;
502
+ }
503
+
504
+ // ── Pass 3: determine lowest bucket key and build output ─────────────────
505
+
506
+ // If any plan has declared wave: 0, the lowest level maps to "0"; otherwise "1".
507
+ const anyWaveZero = rawPlans.some(p => p.declaredWave === 0);
508
+ const levelOffset = anyWaveZero ? 0 : 1;
509
+
510
+ const plans = [];
511
+ const waves = {};
512
+ const incomplete = [];
513
+ let hasCheckpoints = false;
514
+ const warnings = [];
515
+
516
+ for (const raw of rawPlans) {
517
+ if (!raw.autonomous) {
518
+ hasCheckpoints = true;
519
+ }
520
+ if (!raw.hasSummary) {
521
+ incomplete.push(raw.id);
522
+ }
523
+
524
+ // Computed wave = topological level + offset (so lowest level → 0 or 1).
525
+ const computedWave = (level.get(raw.id) ?? 0) + levelOffset;
526
+
527
+ // The effective wave used for bucketing is always the computed topo level.
528
+ // If the plan declared a wave that disagrees, emit a non-fatal warning.
529
+ const effectiveWave = computedWave;
530
+ if (raw.declaredWave !== null && raw.declaredWave !== computedWave) {
531
+ warnings.push(
532
+ `Plan ${raw.id}: declared wave: ${raw.declaredWave} but depends_on DAG places it in wave ${computedWave}`,
533
+ );
534
+ }
535
+
536
+ const plan = {
537
+ id: raw.id,
538
+ wave: effectiveWave,
539
+ depends_on: raw.dependsOn,
540
+ autonomous: raw.autonomous,
541
+ objective: raw.objective,
542
+ files_modified: raw.filesModified,
543
+ task_count: raw.taskCount,
544
+ has_summary: raw.hasSummary,
288
545
  };
289
546
 
290
547
  plans.push(plan);
291
548
 
292
- // Group by wave
293
- const waveKey = String(wave);
549
+ const waveKey = String(effectiveWave);
294
550
  if (!waves[waveKey]) {
295
551
  waves[waveKey] = [];
296
552
  }
297
- waves[waveKey].push(planId);
553
+ waves[waveKey].push(raw.id);
298
554
  }
299
555
 
300
556
  const result = {
@@ -304,6 +560,8 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
304
560
  incomplete,
305
561
  has_checkpoints: hasCheckpoints,
306
562
  };
563
+ if (planNamingWarning) result.warning = planNamingWarning;
564
+ if (warnings.length > 0) result.warnings = warnings;
307
565
 
308
566
  output(result, raw);
309
567
  }
@@ -319,53 +577,81 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
319
577
  error('ROADMAP.md not found');
320
578
  }
321
579
 
322
- const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
323
- const content = extractCurrentMilestone(rawContent, cwd);
324
580
  const slug = generateSlugInternal(description);
325
581
 
326
- let newPhaseId;
327
- let dirName;
328
-
329
- if (customId || config.phase_naming === 'custom') {
330
- // Custom phase naming: use provided ID or generate from description
331
- newPhaseId = customId || slug.toUpperCase().replace(/-/g, '-');
332
- if (!newPhaseId) error('--id required when phase_naming is "custom"');
333
- dirName = `${newPhaseId}-${slug}`;
334
- } else {
335
- // Sequential mode: find highest integer phase number (in current milestone only)
336
- const phasePattern = /#{2,4}\s*Phase\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
337
- let maxPhase = 0;
338
- let m;
339
- while ((m = phasePattern.exec(content)) !== null) {
340
- const num = parseInt(m[1], 10);
341
- if (num > maxPhase) maxPhase = num;
342
- }
582
+ // Wrap entire read-modify-write in lock to prevent concurrent corruption
583
+ const { newPhaseId, dirName } = withPlanningLock(cwd, () => {
584
+ const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
585
+ const content = extractCurrentMilestone(rawContent, cwd);
343
586
 
344
- newPhaseId = maxPhase + 1;
345
- const paddedNum = String(newPhaseId).padStart(2, '0');
346
- dirName = `${paddedNum}-${slug}`;
347
- }
587
+ // Optional project code prefix (e.g., 'CK' → 'CK-01-foundation')
588
+ const projectCode = config.project_code || '';
589
+ const prefix = projectCode ? `${projectCode}-` : '';
590
+
591
+ let _newPhaseId;
592
+ let _dirName;
593
+
594
+ if (customId || config.phase_naming === 'custom') {
595
+ // Custom phase naming: use provided ID or generate from description
596
+ _newPhaseId = customId || slug.toUpperCase().replace(/-/g, '-');
597
+ if (!_newPhaseId) error('--id required when phase_naming is "custom"');
598
+ _dirName = `${prefix}${_newPhaseId}-${slug}`;
599
+ } else {
600
+ // Sequential mode: find highest integer phase number from two sources:
601
+ // 1. ROADMAP.md (current milestone only)
602
+ // 2. .planning/phases/ on disk (orphan directories not tracked in roadmap)
603
+ // Skip 999.x backlog phases — they live outside the active sequence
604
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
605
+ let maxPhase = 0;
606
+ let m;
607
+ while ((m = phasePattern.exec(content)) !== null) {
608
+ const num = parseInt(m[1], 10);
609
+ if (num >= 999) continue; // backlog phases use 999.x numbering
610
+ if (num > maxPhase) maxPhase = num;
611
+ }
348
612
 
349
- const dirPath = path.join(planningDir(cwd), 'phases', dirName);
613
+ // Also scan .planning/phases/ for orphan directories not tracked in ROADMAP.
614
+ // Directory names follow: [PREFIX-]NN-slug (e.g. 03-api or CK-05-old-feature).
615
+ // Strip the optional project_code prefix before extracting the leading integer.
616
+ const phasesOnDisk = path.join(planningDir(cwd), 'phases');
617
+ if (fs.existsSync(phasesOnDisk)) {
618
+ const dirNumPattern = /^(?:[A-Z][A-Z0-9]*-)?(\d+)-/;
619
+ for (const entry of fs.readdirSync(phasesOnDisk)) {
620
+ const match = entry.match(dirNumPattern);
621
+ if (!match) continue;
622
+ const num = parseInt(match[1], 10);
623
+ if (num >= 999) continue; // skip backlog orphans
624
+ if (num > maxPhase) maxPhase = num;
625
+ }
626
+ }
350
627
 
351
- // Create directory with .gitkeep so git tracks empty folders
352
- fs.mkdirSync(dirPath, { recursive: true });
353
- fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
628
+ _newPhaseId = maxPhase + 1;
629
+ const paddedNum = String(_newPhaseId).padStart(2, '0');
630
+ _dirName = `${prefix}${paddedNum}-${slug}`;
631
+ }
354
632
 
355
- // Build phase entry
356
- const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof newPhaseId === 'number' ? newPhaseId - 1 : 'TBD'}`;
357
- const phaseEntry = `\n### Phase ${newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /sdd:plan-phase ${newPhaseId} to break down)\n`;
633
+ const dirPath = path.join(planningDir(cwd), 'phases', _dirName);
358
634
 
359
- // Find insertion point: before last "---" or at end
360
- let updatedContent;
361
- const lastSeparator = rawContent.lastIndexOf('\n---');
362
- if (lastSeparator > 0) {
363
- updatedContent = rawContent.slice(0, lastSeparator) + phaseEntry + rawContent.slice(lastSeparator);
364
- } else {
365
- updatedContent = rawContent + phaseEntry;
366
- }
635
+ // Create directory with .gitkeep so git tracks empty folders
636
+ platformEnsureDir(dirPath);
637
+ platformWriteSync(path.join(dirPath, '.gitkeep'), '');
367
638
 
368
- fs.writeFileSync(roadmapPath, updatedContent, 'utf-8');
639
+ // Build phase entry
640
+ const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof _newPhaseId === 'number' ? _newPhaseId - 1 : 'TBD'}`;
641
+ const phaseEntry = `\n### Phase ${_newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /sdd:plan-phase ${_newPhaseId} to break down)\n`;
642
+
643
+ // Find insertion point: before last "---" or at end
644
+ let updatedContent;
645
+ const lastSeparator = rawContent.lastIndexOf('\n---');
646
+ if (lastSeparator > 0) {
647
+ updatedContent = rawContent.slice(0, lastSeparator) + phaseEntry + rawContent.slice(lastSeparator);
648
+ } else {
649
+ updatedContent = rawContent + phaseEntry;
650
+ }
651
+
652
+ platformWriteSync(roadmapPath, updatedContent);
653
+ return { newPhaseId: _newPhaseId, dirName: _dirName };
654
+ });
369
655
 
370
656
  const result = {
371
657
  phase_number: typeof newPhaseId === 'number' ? newPhaseId : String(newPhaseId),
@@ -379,6 +665,76 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
379
665
  output(result, raw, result.padded);
380
666
  }
381
667
 
668
+ function cmdPhaseAddBatch(cwd, descriptions, raw) {
669
+ if (!Array.isArray(descriptions) || descriptions.length === 0) {
670
+ error('descriptions array required for phase add-batch');
671
+ }
672
+ const config = loadConfig(cwd);
673
+ const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
674
+ if (!fs.existsSync(roadmapPath)) { error('ROADMAP.md not found'); }
675
+ const projectCode = config.project_code || '';
676
+ const prefix = projectCode ? `${projectCode}-` : '';
677
+
678
+ const results = withPlanningLock(cwd, () => {
679
+ let rawContent = fs.readFileSync(roadmapPath, 'utf-8');
680
+ const content = extractCurrentMilestone(rawContent, cwd);
681
+ let maxPhase = 0;
682
+ if (config.phase_naming !== 'custom') {
683
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
684
+ let m;
685
+ while ((m = phasePattern.exec(content)) !== null) {
686
+ const num = parseInt(m[1], 10);
687
+ if (num >= 999) continue;
688
+ if (num > maxPhase) maxPhase = num;
689
+ }
690
+ const phasesOnDisk = path.join(planningDir(cwd), 'phases');
691
+ if (fs.existsSync(phasesOnDisk)) {
692
+ const dirNumPattern = /^(?:[A-Z][A-Z0-9]*-)?(\d+)-/;
693
+ for (const entry of fs.readdirSync(phasesOnDisk)) {
694
+ const match = entry.match(dirNumPattern);
695
+ if (!match) continue;
696
+ const num = parseInt(match[1], 10);
697
+ if (num >= 999) continue;
698
+ if (num > maxPhase) maxPhase = num;
699
+ }
700
+ }
701
+ }
702
+ const added = [];
703
+ for (const description of descriptions) {
704
+ const slug = generateSlugInternal(description);
705
+ let newPhaseId, dirName;
706
+ if (config.phase_naming === 'custom') {
707
+ newPhaseId = slug.toUpperCase().replace(/-/g, '-');
708
+ dirName = `${prefix}${newPhaseId}-${slug}`;
709
+ } else {
710
+ maxPhase += 1;
711
+ newPhaseId = maxPhase;
712
+ dirName = `${prefix}${String(newPhaseId).padStart(2, '0')}-${slug}`;
713
+ }
714
+ const dirPath = path.join(planningDir(cwd), 'phases', dirName);
715
+ platformEnsureDir(dirPath);
716
+ platformWriteSync(path.join(dirPath, '.gitkeep'), '');
717
+ const dependsOn = config.phase_naming === 'custom' ? '' : `\n**Depends on:** Phase ${typeof newPhaseId === 'number' ? newPhaseId - 1 : 'TBD'}`;
718
+ const phaseEntry = `\n### Phase ${newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /sdd:plan-phase ${newPhaseId} to break down)\n`;
719
+ const lastSeparator = rawContent.lastIndexOf('\n---');
720
+ rawContent = lastSeparator > 0
721
+ ? rawContent.slice(0, lastSeparator) + phaseEntry + rawContent.slice(lastSeparator)
722
+ : rawContent + phaseEntry;
723
+ added.push({
724
+ phase_number: typeof newPhaseId === 'number' ? newPhaseId : String(newPhaseId),
725
+ padded: typeof newPhaseId === 'number' ? String(newPhaseId).padStart(2, '0') : String(newPhaseId),
726
+ name: description,
727
+ slug,
728
+ directory: toPosixPath(path.join(path.relative(cwd, planningDir(cwd)), 'phases', dirName)),
729
+ naming_mode: config.phase_naming,
730
+ });
731
+ }
732
+ platformWriteSync(roadmapPath, rawContent);
733
+ return added;
734
+ });
735
+ output({ phases: results, count: results.length }, raw);
736
+ }
737
+
382
738
  function cmdPhaseInsert(cwd, afterPhase, description, raw) {
383
739
  if (!afterPhase || !description) {
384
740
  error('after-phase and description required for phase insert');
@@ -389,66 +745,91 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
389
745
  error('ROADMAP.md not found');
390
746
  }
391
747
 
392
- const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
393
- const content = extractCurrentMilestone(rawContent, cwd);
394
748
  const slug = generateSlugInternal(description);
395
749
 
396
- // Normalize input then strip leading zeros for flexible matching
397
- const normalizedAfter = normalizePhaseName(afterPhase);
398
- const unpadded = normalizedAfter.replace(/^0+/, '');
399
- const afterPhaseEscaped = unpadded.replace(/\./g, '\\.');
400
- const targetPattern = new RegExp(`#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:`, 'i');
401
- if (!targetPattern.test(content)) {
402
- error(`Phase ${afterPhase} not found in ROADMAP.md`);
403
- }
404
-
405
- // Calculate next decimal using existing logic
406
- const phasesDir = path.join(planningDir(cwd), 'phases');
407
- const normalizedBase = normalizePhaseName(afterPhase);
408
- let existingDecimals = [];
409
-
410
- try {
411
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
412
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
413
- const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
414
- for (const dir of dirs) {
415
- const dm = dir.match(decimalPattern);
416
- if (dm) existingDecimals.push(parseInt(dm[1], 10));
750
+ // Wrap entire read-modify-write in lock to prevent concurrent corruption
751
+ const { decimalPhase, dirName } = withPlanningLock(cwd, () => {
752
+ const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
753
+ const content = extractCurrentMilestone(rawContent, cwd);
754
+
755
+ // Normalize input then route through canonical padding-tolerant fragment
756
+ // (#3537). The prior hand-rolled `0*${unpadded}` worked for the integer
757
+ // base but duplicated logic — funnel it through the shared helper.
758
+ const normalizedAfter = normalizePhaseName(afterPhase);
759
+ const afterPhaseEscaped = phaseMarkdownRegexSource(normalizedAfter);
760
+ const targetPattern = new RegExp(`#{2,4}\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
761
+ if (!targetPattern.test(content)) {
762
+ const checklistPattern = new RegExp(`-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${afterPhaseEscaped}:`, 'i');
763
+ if (checklistPattern.test(content)) {
764
+ error(`Phase ${afterPhase} exists in roadmap summary but is missing a detail section (### Phase ${afterPhase}: ...).`);
765
+ }
766
+ error(`Phase ${afterPhase} not found in ROADMAP.md`);
417
767
  }
418
- } catch { /* intentionally empty */ }
419
768
 
420
- const nextDecimal = existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1;
421
- const decimalPhase = `${normalizedBase}.${nextDecimal}`;
422
- const dirName = `${decimalPhase}-${slug}`;
423
- const dirPath = path.join(planningDir(cwd), 'phases', dirName);
769
+ // Calculate next decimal by scanning both directories AND ROADMAP.md entries
770
+ const phasesDir = path.join(planningDir(cwd), 'phases');
771
+ const normalizedBase = normalizePhaseName(afterPhase);
772
+ const decimalSet = new Set();
424
773
 
425
- // Create directory with .gitkeep so git tracks empty folders
426
- fs.mkdirSync(dirPath, { recursive: true });
427
- fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
774
+ try {
775
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
776
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
777
+ const decimalPattern = new RegExp(`^(?:[A-Z]{1,6}-)?${escapeRegex(normalizedBase)}\\.(\\d+)`);
778
+ for (const dir of dirs) {
779
+ const dm = dir.match(decimalPattern);
780
+ if (dm) decimalSet.add(parseInt(dm[1], 10));
781
+ }
782
+ } catch { /* intentionally empty */ }
428
783
 
429
- // Build phase entry
430
- const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /sdd:plan-phase ${decimalPhase} to break down)\n`;
784
+ // Also scan ROADMAP.md content (already loaded) for decimal entries.
785
+ // #3537: padding-tolerant fragment so un-padded `Phase 2.7:` is found
786
+ // when caller passes the padded base `02`.
787
+ const rmPhasePattern = new RegExp(
788
+ `#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(normalizedBase)}\\.(\\d+)\\s*:`, 'gi'
789
+ );
790
+ let rmMatch;
791
+ while ((rmMatch = rmPhasePattern.exec(rawContent)) !== null) {
792
+ decimalSet.add(parseInt(rmMatch[1], 10));
793
+ }
431
794
 
432
- // Insert after the target phase section
433
- const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
434
- const headerMatch = rawContent.match(headerPattern);
435
- if (!headerMatch) {
436
- error(`Could not find Phase ${afterPhase} header`);
437
- }
795
+ const nextDecimal = decimalSet.size === 0 ? 1 : Math.max(...decimalSet) + 1;
796
+ const _decimalPhase = `${normalizedBase}.${nextDecimal}`;
797
+ // Optional project code prefix
798
+ const insertConfig = loadConfig(cwd);
799
+ const projectCode = insertConfig.project_code || '';
800
+ const pfx = projectCode ? `${projectCode}-` : '';
801
+ const _dirName = `${pfx}${_decimalPhase}-${slug}`;
802
+ const dirPath = path.join(planningDir(cwd), 'phases', _dirName);
803
+
804
+ // Create directory with .gitkeep so git tracks empty folders
805
+ platformEnsureDir(dirPath);
806
+ platformWriteSync(path.join(dirPath, '.gitkeep'), '');
807
+
808
+ // Build phase entry
809
+ const phaseEntry = `\n### Phase ${_decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /sdd:plan-phase ${_decimalPhase} to break down)\n`;
810
+
811
+ // Insert after the target phase section
812
+ const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
813
+ const headerMatch = rawContent.match(headerPattern);
814
+ if (!headerMatch) {
815
+ error(`Could not find Phase ${afterPhase} header`);
816
+ }
438
817
 
439
- const headerIdx = rawContent.indexOf(headerMatch[0]);
440
- const afterHeader = rawContent.slice(headerIdx + headerMatch[0].length);
441
- const nextPhaseMatch = afterHeader.match(/\n#{2,4}\s+Phase\s+\d/i);
818
+ const headerIdx = rawContent.indexOf(headerMatch[0]);
819
+ const afterHeader = rawContent.slice(headerIdx + headerMatch[0].length);
820
+ const nextPhaseMatch = afterHeader.match(/\n#{2,4}\s+Phase\s+\d/i);
442
821
 
443
- let insertIdx;
444
- if (nextPhaseMatch) {
445
- insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
446
- } else {
447
- insertIdx = rawContent.length;
448
- }
822
+ let insertIdx;
823
+ if (nextPhaseMatch) {
824
+ insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
825
+ } else {
826
+ insertIdx = rawContent.length;
827
+ }
449
828
 
450
- const updatedContent = rawContent.slice(0, insertIdx) + phaseEntry + rawContent.slice(insertIdx);
451
- fs.writeFileSync(roadmapPath, updatedContent, 'utf-8');
829
+ const updatedContent = rawContent.slice(0, insertIdx) + phaseEntry + rawContent.slice(insertIdx);
830
+ platformWriteSync(roadmapPath, updatedContent);
831
+ return { decimalPhase: _decimalPhase, dirName: _dirName };
832
+ });
452
833
 
453
834
  const result = {
454
835
  phase_number: decimalPhase,
@@ -468,10 +849,12 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
468
849
  */
469
850
  function renameDecimalPhases(phasesDir, baseInt, removedDecimal) {
470
851
  const renamedDirs = [], renamedFiles = [];
471
- const decPattern = new RegExp(`^${baseInt}\\.(\\d+)-(.+)$`);
852
+ // Capture the zero-padded prefix (e.g. "06" from "06.3-slug") so the renamed
853
+ // directory preserves the original padding format.
854
+ const decPattern = new RegExp(`^(0*${baseInt})\\.(\\d+)-(.+)$`);
472
855
  const dirs = readSubdirectories(phasesDir, true);
473
856
  const toRename = dirs
474
- .map(dir => { const m = dir.match(decPattern); return m ? { dir, oldDecimal: parseInt(m[1], 10), slug: m[2] } : null; })
857
+ .map(dir => { const m = dir.match(decPattern); return m ? { dir, prefix: m[1], oldDecimal: parseInt(m[2], 10), slug: m[3] } : null; })
475
858
  .filter(item => item && item.oldDecimal > removedDecimal)
476
859
  .sort((a, b) => b.oldDecimal - a.oldDecimal); // descending to avoid conflicts
477
860
 
@@ -479,7 +862,7 @@ function renameDecimalPhases(phasesDir, baseInt, removedDecimal) {
479
862
  const newDecimal = item.oldDecimal - 1;
480
863
  const oldPhaseId = `${baseInt}.${item.oldDecimal}`;
481
864
  const newPhaseId = `${baseInt}.${newDecimal}`;
482
- const newDirName = `${baseInt}.${newDecimal}-${item.slug}`;
865
+ const newDirName = `${item.prefix}.${newDecimal}-${item.slug}`;
483
866
  fs.renameSync(path.join(phasesDir, item.dir), path.join(phasesDir, newDirName));
484
867
  renamedDirs.push({ from: item.dir, to: newDirName });
485
868
  for (const f of fs.readdirSync(path.join(phasesDir, newDirName))) {
@@ -506,7 +889,7 @@ function renameIntegerPhases(phasesDir, removedInt) {
506
889
  const m = dir.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);
507
890
  if (!m) return null;
508
891
  const dirInt = parseInt(m[1], 10);
509
- return dirInt > removedInt ? { dir, oldInt: dirInt, letter: m[2] ? m[2].toUpperCase() : '', decimal: m[3] ? parseInt(m[3], 10) : null, slug: m[4] } : null;
892
+ return (dirInt > removedInt && dirInt < 999) ? { dir, oldInt: dirInt, letter: m[2] ? m[2].toUpperCase() : '', decimal: m[3] ? parseInt(m[3], 10) : null, slug: m[4] } : null;
510
893
  })
511
894
  .filter(Boolean)
512
895
  .sort((a, b) => a.oldInt !== b.oldInt ? b.oldInt - a.oldInt : (b.decimal || 0) - (a.decimal || 0));
@@ -533,32 +916,94 @@ function renameIntegerPhases(phasesDir, removedInt) {
533
916
  return { renamedDirs, renamedFiles };
534
917
  }
535
918
 
919
+ function decrementRoadmapPhaseNumber(raw, removedInt) {
920
+ const num = parseInt(raw, 10);
921
+ if (!Number.isInteger(num) || num <= removedInt || num >= 999) return raw;
922
+ return String(num - 1);
923
+ }
924
+
925
+ function decrementRoadmapPhaseToken(raw, removedInt) {
926
+ const match = String(raw).match(/^(\d+)(\.\d+)?$/);
927
+ if (!match) return raw;
928
+ const num = parseInt(match[1], 10);
929
+ if (!Number.isInteger(num) || num <= removedInt || num >= 999) return raw;
930
+ return `${num - 1}${match[2] || ''}`;
931
+ }
932
+
933
+ function decrementRoadmapPaddedPhaseNumber(raw, removedInt) {
934
+ const num = parseInt(raw, 10);
935
+ if (!Number.isInteger(num) || num <= removedInt || num >= 999) return raw;
936
+ return String(num - 1).padStart(raw.length, '0');
937
+ }
938
+
536
939
  /**
537
940
  * Remove a phase section from ROADMAP.md and renumber all subsequent integer phases.
538
941
  */
539
- function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, removedInt) {
540
- let content = fs.readFileSync(roadmapPath, 'utf-8');
541
- const escaped = escapeRegex(targetPhase);
542
-
543
- content = content.replace(new RegExp(`\\n?#{2,4}\\s*Phase\\s+${escaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`, 'i'), '');
544
- content = content.replace(new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${escaped}[:\\s][^\\n]*`, 'gi'), '');
545
- content = content.replace(new RegExp(`\\n?\\|\\s*${escaped}\\.?\\s[^|]*\\|[^\\n]*`, 'gi'), '');
546
-
547
- if (!isDecimal) {
548
- const MAX_PHASE = 99;
549
- for (let oldNum = MAX_PHASE; oldNum > removedInt; oldNum--) {
550
- const newNum = oldNum - 1;
551
- const oldStr = String(oldNum), newStr = String(newNum);
552
- const oldPad = oldStr.padStart(2, '0'), newPad = newStr.padStart(2, '0');
553
- content = content.replace(new RegExp(`(#{2,4}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'), `$1${newStr}$2`);
554
- content = content.replace(new RegExp(`(Phase\\s+)${oldStr}([:\\s])`, 'g'), `$1${newStr}$2`);
555
- content = content.replace(new RegExp(`${oldPad}-(\\d{2})`, 'g'), `${newPad}-$1`);
556
- content = content.replace(new RegExp(`(\\|\\s*)${oldStr}\\.\\s`, 'g'), `$1${newStr}. `);
557
- content = content.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, 'gi'), `$1${newStr}`);
942
+ function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, removedInt, cwd) {
943
+ // Wrap entire read-modify-write in lock to prevent concurrent corruption
944
+ withPlanningLock(cwd, () => {
945
+ let content = fs.readFileSync(roadmapPath, 'utf-8');
946
+ const escaped = escapeRegex(targetPhase);
947
+
948
+ // #3601: the end-of-section lookahead is depth-aware. It captures the
949
+ // hash count of the header being removed and stops only at a subsequent
950
+ // header of the SAME depth, whether integer or decimal. This preserves
951
+ // two existing contracts:
952
+ //
953
+ // (#3601 case) Remove `### Phase 2:` and stop at `### Phase 2.1:` —
954
+ // `Phase 2.1` is a peer-level decimal phase (depth 3) and must be
955
+ // preserved.
956
+ //
957
+ // (#3355 case) Remove `### Phase 27:` and continue past
958
+ // `#### Phase 27.1:` (depth 4 — a child of Phase 27) until the next
959
+ // depth-3 header. The child decimal is part of the integer phase
960
+ // being removed.
961
+ //
962
+ // The `(?!#)` negative lookahead after the backreference prevents the
963
+ // depth-3 match from being satisfied by a depth-4+ header that starts
964
+ // with the same three hashes.
965
+ content = content.replace(new RegExp(`\\n?(?<h>#{2,4})\\s*Phase\\s+${escaped}\\s*:[\\s\\S]*?(?=\\n\\k<h>(?!#)\\s+Phase\\s+[^\\n:]+\\s*:|$)`, 'i'), '');
966
+ content = content.replace(new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${escaped}[:\\s][^\\n]*`, 'gi'), '');
967
+ content = content.replace(new RegExp(`\\n?\\|\\s*${escaped}\\.?\\s[^|]*\\|[^\\n]*`, 'gi'), '');
968
+
969
+ if (!isDecimal) {
970
+ content = content.replace(
971
+ /(#{2,4}\s*Phase\s+)(\d+(?:\.\d+)?)(\s*:)/gi,
972
+ (_match, prefix, num, suffix) => `${prefix}${decrementRoadmapPhaseToken(num, removedInt)}${suffix}`
973
+ );
974
+ content = content.replace(
975
+ /(-\s*\[[ x]\]\s*.*?Phase\s+)(\d+)(\s*:|\s+)/gi,
976
+ (_match, prefix, num, suffix) => `${prefix}${decrementRoadmapPhaseNumber(num, removedInt)}${suffix}`
977
+ );
978
+ content = content.replace(
979
+ /(\|\s*)(\d+)(\.\s)/g,
980
+ (_match, prefix, num, suffix) => `${prefix}${decrementRoadmapPhaseNumber(num, removedInt)}${suffix}`
981
+ );
982
+ // #3602: extend the suffix lookahead so slugged plan filenames like
983
+ // `07-01-cherry-pick-foundation-PLAN.md` match too. The previous
984
+ // pattern only allowed a compact `-(PLAN|SUMMARY).md` immediately
985
+ // after the plan number (or no suffix at all); a slug between the
986
+ // number and the `-PLAN.md` / `-SUMMARY.md` suffix made the
987
+ // lookahead fail and left the stale `07-01-` prefix in ROADMAP
988
+ // text while the on-disk file was already renamed to `06-01-…`.
989
+ // The slug segment `(?:-[A-Za-z][A-Za-z0-9-]*)*` allows any number
990
+ // of kebab-case tokens before the canonical PLAN/SUMMARY suffix.
991
+ content = content.replace(
992
+ /(?<![0-9-])(\d{2})-(\d{2})(?=(?:(?:-[A-Za-z][A-Za-z0-9-]*)*-(?:PLAN|SUMMARY)\.md)|(?![0-9-]))/g,
993
+ (_match, phaseNum, planNum) => `${decrementRoadmapPaddedPhaseNumber(phaseNum, removedInt)}-${planNum}`
994
+ );
995
+ content = content.replace(
996
+ /(\*\*Depends on\*\*\s*:\s*Phase\s+)(\d+(?:\.\d+)?)\b/gi,
997
+ (_match, prefix, num) => `${prefix}${decrementRoadmapPhaseToken(num, removedInt)}`
998
+ );
999
+ content = content.replace(
1000
+ /(Depends on:\*\*\s*Phase\s+)(\d+(?:\.\d+)?)\b/gi,
1001
+ (_match, prefix, num) => `${prefix}${decrementRoadmapPhaseToken(num, removedInt)}`
1002
+ );
558
1003
  }
559
- }
560
1004
 
561
- fs.writeFileSync(roadmapPath, content, 'utf-8');
1005
+ platformWriteSync(roadmapPath, content);
1006
+ });
562
1007
  }
563
1008
 
564
1009
  function cmdPhaseRemove(cwd, targetPhase, options, raw) {
@@ -575,7 +1020,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
575
1020
 
576
1021
  // Find target directory
577
1022
  const targetDir = readSubdirectories(phasesDir, true)
578
- .find(d => d.startsWith(normalized + '-') || d === normalized) || null;
1023
+ .find(d => phaseTokenMatches(d, normalized)) || null;
579
1024
 
580
1025
  // Guard against removing executed work
581
1026
  if (targetDir && !force) {
@@ -592,28 +1037,29 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
592
1037
  let renamedDirs = [], renamedFiles = [];
593
1038
  try {
594
1039
  const renamed = isDecimal
595
- ? renameDecimalPhases(phasesDir, normalized.split('.')[0], parseInt(normalized.split('.')[1], 10))
1040
+ ? renameDecimalPhases(phasesDir, parseInt(normalized.split('.')[0], 10), parseInt(normalized.split('.')[1], 10))
596
1041
  : renameIntegerPhases(phasesDir, parseInt(normalized, 10));
597
1042
  renamedDirs = renamed.renamedDirs;
598
1043
  renamedFiles = renamed.renamedFiles;
599
1044
  } catch { /* intentionally empty */ }
600
1045
 
601
1046
  // Update ROADMAP.md
602
- updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, parseInt(normalized, 10));
1047
+ updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, parseInt(normalized, 10), cwd);
603
1048
 
604
- // Update STATE.md phase count
1049
+ // Update STATE.md phase count atomically (#P4.4)
605
1050
  const statePath = path.join(planningDir(cwd), 'STATE.md');
606
1051
  if (fs.existsSync(statePath)) {
607
- let stateContent = fs.readFileSync(statePath, 'utf-8');
608
- const totalRaw = stateExtractField(stateContent, 'Total Phases');
609
- if (totalRaw) {
610
- stateContent = stateReplaceField(stateContent, 'Total Phases', String(parseInt(totalRaw, 10) - 1)) || stateContent;
611
- }
612
- const ofMatch = stateContent.match(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i);
613
- if (ofMatch) {
614
- stateContent = stateContent.replace(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i, `$1${parseInt(ofMatch[2], 10) - 1}$3`);
615
- }
616
- writeStateMd(statePath, stateContent, cwd);
1052
+ readModifyWriteStateMd(statePath, (stateContent) => {
1053
+ const totalRaw = stateExtractField(stateContent, 'Total Phases');
1054
+ if (totalRaw) {
1055
+ stateContent = stateReplaceField(stateContent, 'Total Phases', String(parseInt(totalRaw, 10) - 1)) || stateContent;
1056
+ }
1057
+ const ofMatch = stateContent.match(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i);
1058
+ if (ofMatch) {
1059
+ stateContent = stateContent.replace(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i, `$1${parseInt(ofMatch[2], 10) - 1}$3`);
1060
+ }
1061
+ return stateContent;
1062
+ }, cwd);
617
1063
  }
618
1064
 
619
1065
  output({
@@ -668,84 +1114,147 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
668
1114
  }
669
1115
  } catch {}
670
1116
 
671
- // Update ROADMAP.md: mark phase complete
1117
+ // Update ROADMAP.md and REQUIREMENTS.md atomically under lock
672
1118
  if (fs.existsSync(roadmapPath)) {
673
- let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
1119
+ withPlanningLock(cwd, () => {
1120
+ let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
1121
+
1122
+ // Checkbox: - [ ] Phase N: → - [x] Phase N: (...completed DATE)
1123
+ // #3537: padding-tolerant fragment so the caller-resolved padded id
1124
+ // matches un-padded ROADMAP prose.
1125
+ const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
1126
+ const checkboxPattern = new RegExp(
1127
+ `(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
1128
+ 'i'
1129
+ );
1130
+ roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
674
1131
 
675
- // Checkbox: - [ ] Phase N: - [x] Phase N: (...completed DATE)
676
- const checkboxPattern = new RegExp(
677
- `(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${escapeRegex(phaseNum)}[:\\s][^\\n]*)`,
678
- 'i'
679
- );
680
- roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
1132
+ // Progress table: update Status to Complete, add date (handles 4 or 5 column tables)
1133
+ const tableRowPattern = new RegExp(
1134
+ `^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
1135
+ 'im'
1136
+ );
1137
+ roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
1138
+ const cells = fullRow.split('|').slice(1, -1);
1139
+ if (cells.length === 5) {
1140
+ // 5-col: Phase | Milestone | Plans | Status | Completed
1141
+ cells[2] = ` ${summaryCount}/${planCount} `;
1142
+ cells[3] = ' Complete ';
1143
+ cells[4] = ` ${today} `;
1144
+ } else if (cells.length === 4) {
1145
+ // 4-col: Phase | Plans | Status | Completed
1146
+ cells[1] = ` ${summaryCount}/${planCount} `;
1147
+ cells[2] = ' Complete ';
1148
+ cells[3] = ` ${today} `;
1149
+ }
1150
+ return '|' + cells.join('|') + '|';
1151
+ });
1152
+
1153
+ // Update plan count in phase section.
1154
+ // Use direct .replace() rather than replaceInCurrentMilestone() so this
1155
+ // works when the current milestone section is itself inside a <details>
1156
+ // block (the standard /sdd:new-project layout). replaceInCurrentMilestone
1157
+ // scopes to content after the last </details>, which misses content inside
1158
+ // the current milestone's own <details> wrapper (#2005).
1159
+ // The phase-scoped heading pattern is specific enough to avoid matching
1160
+ // archived phases (which belong to different milestones).
1161
+ const planCountPattern = new RegExp(
1162
+ `(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
1163
+ 'i'
1164
+ );
1165
+ roadmapContent = roadmapContent.replace(
1166
+ planCountPattern,
1167
+ `$1${summaryCount}/${planCount} plans complete`
1168
+ );
681
1169
 
682
- // Progress table: update Status to Complete, add date (handles 4 or 5 column tables)
683
- const phaseEscaped = escapeRegex(phaseNum);
684
- const tableRowPattern = new RegExp(
685
- `^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
686
- 'im'
687
- );
688
- roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
689
- const cells = fullRow.split('|').slice(1, -1);
690
- if (cells.length === 5) {
691
- // 5-col: Phase | Milestone | Plans | Status | Completed
692
- cells[3] = ' Complete ';
693
- cells[4] = ` ${today} `;
694
- } else if (cells.length === 4) {
695
- // 4-col: Phase | Plans | Status | Completed
696
- cells[2] = ' Complete ';
697
- cells[3] = ` ${today} `;
1170
+ // Mark completed plan checkboxes (safety net for missed per-plan updates)
1171
+ // Handles both plain IDs ("- [ ] 01-01-PLAN.md") and bold-wrapped IDs ("- [ ] **01-01**")
1172
+ for (const summaryFile of phaseInfo.summaries) {
1173
+ const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
1174
+ if (!planId) continue;
1175
+ const planEscaped = escapeRegex(planId);
1176
+ const planCheckboxPattern = new RegExp(
1177
+ `(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`,
1178
+ 'i'
1179
+ );
1180
+ roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
698
1181
  }
699
- return '|' + cells.join('|') + '|';
700
- });
701
1182
 
702
- // Update plan count in phase section
703
- const planCountPattern = new RegExp(
704
- `(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
705
- 'i'
706
- );
707
- roadmapContent = replaceInCurrentMilestone(
708
- roadmapContent, planCountPattern,
709
- `$1${summaryCount}/${planCount} plans complete`
710
- );
1183
+ platformWriteSync(roadmapPath, roadmapContent);
1184
+
1185
+ // Update REQUIREMENTS.md traceability for this phase's requirements
1186
+ const reqPath = path.join(planningDir(cwd), 'REQUIREMENTS.md');
1187
+ if (fs.existsSync(reqPath)) {
1188
+ // Extract the current phase section from roadmap (scoped to avoid cross-phase matching).
1189
+ // #3537: padding-tolerant fragment so an un-padded `Phase 2.7:` heading
1190
+ // is found when caller resolved to padded `02.7`.
1191
+ const phaseEsc = phaseMarkdownRegexSource(phaseNum);
1192
+ const currentMilestoneRoadmap = extractCurrentMilestone(roadmapContent, cwd);
1193
+ const phaseSectionMatch = currentMilestoneRoadmap.match(
1194
+ new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEsc}[:\\s][\\s\\S]*?)(?=#{2,4}\\s*Phase\\s+|$)`, 'i')
1195
+ );
1196
+
1197
+ const sectionText = phaseSectionMatch ? phaseSectionMatch[1] : '';
1198
+ // Accept all bold/colon variants (#2769) — the previous pattern only
1199
+ // matched **Requirements:** (colon inside bold) and silently skipped
1200
+ // **Requirements**: (colon outside), preventing the matching REQ-IDs
1201
+ // from being ticked off in REQUIREMENTS.md on phase completion.
1202
+ const reqMatch = sectionText.match(/\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]+)/i);
711
1203
 
712
- fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
1204
+ let reqContent = fs.readFileSync(reqPath, 'utf-8');
713
1205
 
714
- // Update REQUIREMENTS.md traceability for this phase's requirements
715
- const reqPath = path.join(planningDir(cwd), 'REQUIREMENTS.md');
716
- if (fs.existsSync(reqPath)) {
717
- // Extract the current phase section from roadmap (scoped to avoid cross-phase matching)
718
- const phaseEsc = escapeRegex(phaseNum);
719
- const currentMilestoneRoadmap = extractCurrentMilestone(roadmapContent, cwd);
720
- const phaseSectionMatch = currentMilestoneRoadmap.match(
721
- new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEsc}[:\\s][\\s\\S]*?)(?=#{2,4}\\s*Phase\\s+|$)`, 'i')
722
- );
1206
+ if (reqMatch) {
1207
+ const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
1208
+
1209
+ for (const reqId of reqIds) {
1210
+ const reqEscaped = escapeRegex(reqId);
1211
+ // Update checkbox: - [ ] **REQ-ID** → - [x] **REQ-ID**
1212
+ reqContent = reqContent.replace(
1213
+ new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqEscaped}\\*\\*)`, 'gi'),
1214
+ '$1x$2'
1215
+ );
1216
+ // Update traceability table: | REQ-ID | Phase N | Pending/In Progress | → | REQ-ID | Phase N | Complete |
1217
+ reqContent = reqContent.replace(
1218
+ new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*(?:Pending|In Progress)\\s*(\\|)`, 'gi'),
1219
+ '$1 Complete $2'
1220
+ );
1221
+ }
1222
+ }
723
1223
 
724
- const sectionText = phaseSectionMatch ? phaseSectionMatch[1] : '';
725
- const reqMatch = sectionText.match(/\*\*Requirements:\*\*\s*([^\n]+)/i);
1224
+ // Scan body for all **REQ-ID** patterns, warn about any missing from the Traceability table.
1225
+ // Always runs regardless of whether the roadmap has a Requirements: line.
1226
+ const bodyReqIds = [];
1227
+ const bodyReqPattern = /\*\*([A-Z][A-Z0-9]*-\d+)\*\*/g;
1228
+ let bodyMatch;
1229
+ while ((bodyMatch = bodyReqPattern.exec(reqContent)) !== null) {
1230
+ const id = bodyMatch[1];
1231
+ if (!bodyReqIds.includes(id)) bodyReqIds.push(id);
1232
+ }
726
1233
 
727
- if (reqMatch) {
728
- const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
729
- let reqContent = fs.readFileSync(reqPath, 'utf-8');
1234
+ // Collect REQ-IDs present in the Traceability section only, to avoid
1235
+ // picking up IDs from other tables in the document.
1236
+ const traceabilityHeadingMatch = reqContent.match(/^#{1,6}\s+Traceability\b/im);
1237
+ const traceabilitySection = traceabilityHeadingMatch
1238
+ ? reqContent.slice(traceabilityHeadingMatch.index)
1239
+ : '';
1240
+ const tableReqIds = new Set();
1241
+ const tableRowPattern = /^\|\s*([A-Z][A-Z0-9]*-\d+)\s*\|/gm;
1242
+ let tableMatch;
1243
+ while ((tableMatch = tableRowPattern.exec(traceabilitySection)) !== null) {
1244
+ tableReqIds.add(tableMatch[1]);
1245
+ }
730
1246
 
731
- for (const reqId of reqIds) {
732
- const reqEscaped = escapeRegex(reqId);
733
- // Update checkbox: - [ ] **REQ-ID** → - [x] **REQ-ID**
734
- reqContent = reqContent.replace(
735
- new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqEscaped}\\*\\*)`, 'gi'),
736
- '$1x$2'
737
- );
738
- // Update traceability table: | REQ-ID | Phase N | Pending/In Progress | → | REQ-ID | Phase N | Complete |
739
- reqContent = reqContent.replace(
740
- new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*(?:Pending|In Progress)\\s*(\\|)`, 'gi'),
741
- '$1 Complete $2'
1247
+ const unregistered = bodyReqIds.filter(id => !tableReqIds.has(id));
1248
+ if (unregistered.length > 0) {
1249
+ warnings.push(
1250
+ `REQUIREMENTS.md: ${unregistered.length} REQ-ID(s) found in body but missing from Traceability table: ${unregistered.join(', ')} — add them manually to keep traceability in sync`
742
1251
  );
743
1252
  }
744
1253
 
745
- fs.writeFileSync(reqPath, reqContent, 'utf-8');
1254
+ platformWriteSync(reqPath, reqContent);
746
1255
  requirementsUpdated = true;
747
1256
  }
748
- }
1257
+ });
749
1258
  }
750
1259
 
751
1260
  // Find next phase — check both filesystem AND roadmap
@@ -763,9 +1272,11 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
763
1272
  .sort((a, b) => comparePhaseNum(a, b));
764
1273
 
765
1274
  // Find the next phase directory after current
1275
+ // Skip backlog phases (999.x) — they are parked ideas, not sequential work (#2129)
766
1276
  for (const dir of dirs) {
767
1277
  const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);
768
1278
  if (dm) {
1279
+ if (/^999(?:\.|$)/.test(dm[1])) continue;
769
1280
  if (comparePhaseNum(dm[1], phaseNum) > 0) {
770
1281
  nextPhaseNum = dm[1];
771
1282
  nextPhaseName = dm[2] || null;
@@ -794,69 +1305,88 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
794
1305
  } catch { /* intentionally empty */ }
795
1306
  }
796
1307
 
797
- // Update STATE.md — use shared helpers that handle both **bold:** and plain Field: formats
1308
+ // Update STATE.md atomically hold lock across read-modify-write (#P4.4).
1309
+ // Previously read outside the lock; a crash between the ROADMAP update
1310
+ // (locked above) and this write left ROADMAP/STATE inconsistent.
798
1311
  if (fs.existsSync(statePath)) {
799
- let stateContent = fs.readFileSync(statePath, 'utf-8');
800
-
801
- // Update Current Phase preserve "X of Y (Name)" compound format
802
- const phaseValue = nextPhaseNum || phaseNum;
803
- const existingPhaseField = stateExtractField(stateContent, 'Current Phase')
804
- || stateExtractField(stateContent, 'Phase');
805
- let newPhaseValue = String(phaseValue);
806
- if (existingPhaseField) {
807
- const totalMatch = existingPhaseField.match(/of\s+(\d+)/);
808
- const nameMatch = existingPhaseField.match(/\(([^)]+)\)/);
809
- if (totalMatch) {
810
- const total = totalMatch[1];
811
- const nameStr = nextPhaseName ? ` (${nextPhaseName.replace(/-/g, ' ')})` : (nameMatch ? ` (${nameMatch[1]})` : '');
812
- newPhaseValue = `${phaseValue} of ${total}${nameStr}`;
1312
+ readModifyWriteStateMd(statePath, (stateContent) => {
1313
+ // Update Current Phase — preserve "X of Y (Name)" compound format
1314
+ const phaseValue = nextPhaseNum || phaseNum;
1315
+ const existingPhaseField = stateExtractField(stateContent, 'Current Phase')
1316
+ || stateExtractField(stateContent, 'Phase');
1317
+ let newPhaseValue = String(phaseValue);
1318
+ if (existingPhaseField) {
1319
+ const totalMatch = existingPhaseField.match(/of\s+(\d+)/);
1320
+ const nameMatch = existingPhaseField.match(/\(([^)]+)\)/);
1321
+ if (totalMatch) {
1322
+ const total = totalMatch[1];
1323
+ const nameStr = nextPhaseName ? ` (${nextPhaseName.replace(/-/g, ' ')})` : (nameMatch ? ` (${nameMatch[1]})` : '');
1324
+ newPhaseValue = `${phaseValue} of ${total}${nameStr}`;
1325
+ }
813
1326
  }
814
- }
815
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Phase', 'Phase', newPhaseValue);
816
-
817
- // Update Current Phase Name
818
- if (nextPhaseName) {
819
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Phase Name', null, nextPhaseName.replace(/-/g, ' '));
820
- }
821
-
822
- // Update Status
823
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Status', null,
824
- isLastPhase ? 'Milestone complete' : 'Ready to plan');
1327
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Phase', 'Phase', newPhaseValue);
825
1328
 
826
- // Update Current Plan
827
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Plan', 'Plan', 'Not started');
1329
+ // Update Current Phase Name
1330
+ if (nextPhaseName) {
1331
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Phase Name', null, nextPhaseName.replace(/-/g, ' '));
1332
+ }
828
1333
 
829
- // Update Last Activity
830
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity', 'Last activity', today);
1334
+ // Update Status
1335
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Status', null,
1336
+ isLastPhase ? 'Milestone complete' : 'Ready to plan');
1337
+
1338
+ // Update Current Plan
1339
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Current Plan', 'Plan', 'Not started');
1340
+
1341
+ // Update Last Activity
1342
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity', 'Last activity', today);
1343
+
1344
+ // Update Last Activity Description
1345
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity Description', null,
1346
+ `Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ''}`);
1347
+
1348
+ // Increment Completed Phases counter (#956)
1349
+ const completedRaw = stateExtractField(stateContent, 'Completed Phases');
1350
+ if (completedRaw) {
1351
+ const newCompleted = parseInt(completedRaw, 10) + 1;
1352
+ stateContent = stateReplaceField(stateContent, 'Completed Phases', String(newCompleted)) || stateContent;
1353
+
1354
+ // Recalculate percent based on completed / total (#956)
1355
+ const totalRaw = stateExtractField(stateContent, 'Total Phases');
1356
+ if (totalRaw) {
1357
+ const totalPhases = parseInt(totalRaw, 10);
1358
+ if (totalPhases > 0) {
1359
+ const newPercent = Math.round((newCompleted / totalPhases) * 100);
1360
+ stateContent = stateReplaceField(stateContent, 'Progress', `${newPercent}%`) || stateContent;
1361
+ stateContent = stateContent.replace(
1362
+ /(percent:\s*)\d+/,
1363
+ `$1${newPercent}`
1364
+ );
1365
+ }
1366
+ }
1367
+ }
831
1368
 
832
- // Update Last Activity Description
833
- stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity Description', null,
834
- `Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ''}`);
1369
+ // Gate 4: Update Performance Metrics section (#1627)
1370
+ stateContent = updatePerformanceMetricsSection(stateContent, cwd, phaseNum, planCount, summaryCount);
835
1371
 
836
- // Increment Completed Phases counter (#956)
837
- const completedRaw = stateExtractField(stateContent, 'Completed Phases');
838
- if (completedRaw) {
839
- const newCompleted = parseInt(completedRaw, 10) + 1;
840
- stateContent = stateReplaceField(stateContent, 'Completed Phases', String(newCompleted)) || stateContent;
1372
+ return stateContent;
1373
+ }, cwd);
1374
+ }
841
1375
 
842
- // Recalculate percent based on completed / total (#956)
843
- const totalRaw = stateExtractField(stateContent, 'Total Phases');
844
- if (totalRaw) {
845
- const totalPhases = parseInt(totalRaw, 10);
846
- if (totalPhases > 0) {
847
- const newPercent = Math.round((newCompleted / totalPhases) * 100);
848
- stateContent = stateReplaceField(stateContent, 'Progress', `${newPercent}%`) || stateContent;
849
- // Also update percent field if it exists separately
850
- stateContent = stateContent.replace(
851
- /(percent:\s*)\d+/,
852
- `$1${newPercent}`
853
- );
854
- }
1376
+ // Auto-prune STATE.md on phase boundary when configured (#2087)
1377
+ let autoPruned = false;
1378
+ try {
1379
+ const configPath = path.join(planningDir(cwd), 'config.json');
1380
+ if (fs.existsSync(configPath)) {
1381
+ const rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1382
+ const autoPruneEnabled = rawConfig.workflow && rawConfig.workflow.auto_prune_state === true;
1383
+ if (autoPruneEnabled && fs.existsSync(statePath)) {
1384
+ const { cmdStatePrune } = require('./state.cjs');
1385
+ cmdStatePrune(cwd, { keepRecent: '3', dryRun: false, silent: true }, true);
1386
+ autoPruned = true;
855
1387
  }
856
1388
  }
857
-
858
- writeStateMd(statePath, stateContent, cwd);
859
- }
1389
+ } catch { /* intentionally empty — auto-prune is best-effort */ }
860
1390
 
861
1391
  const result = {
862
1392
  completed_phase: phaseNum,
@@ -869,6 +1399,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
869
1399
  roadmap_updated: fs.existsSync(roadmapPath),
870
1400
  state_updated: fs.existsSync(statePath),
871
1401
  requirements_updated: requirementsUpdated,
1402
+ auto_pruned: autoPruned,
872
1403
  warnings,
873
1404
  has_warnings: warnings.length > 0,
874
1405
  };
@@ -882,6 +1413,8 @@ module.exports = {
882
1413
  cmdFindPhase,
883
1414
  cmdPhasePlanIndex,
884
1415
  cmdPhaseAdd,
1416
+ cmdPhaseAddBatch,
1417
+ cmdPhaseMvpMode,
885
1418
  cmdPhaseInsert,
886
1419
  cmdPhaseRemove,
887
1420
  cmdPhaseComplete,