sdd-agent-platform 0.4.1 → 0.5.0

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 (698) hide show
  1. package/README.md +24 -28
  2. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +84 -103
  3. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js.map +1 -1
  4. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.d.ts +10 -6
  5. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js +7 -8
  6. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js.map +1 -1
  7. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.d.ts +3 -1
  8. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js +7 -3
  9. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js.map +1 -1
  10. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.d.ts +0 -1
  11. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js +374 -421
  12. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js.map +1 -1
  13. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.d.ts +1 -1
  14. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js +7 -19
  15. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js.map +1 -1
  16. package/node_modules/@sdd-agent-platform/core/dist/contracts.d.ts +7 -1
  17. package/node_modules/@sdd-agent-platform/core/dist/contracts.js +6 -0
  18. package/node_modules/@sdd-agent-platform/core/dist/contracts.js.map +1 -1
  19. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js +2 -12
  20. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js.map +1 -1
  21. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +1 -18
  22. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
  23. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +1 -1
  24. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +1 -1
  25. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -1
  26. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/contracts.d.ts +0 -1
  27. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js +110 -0
  28. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js.map +1 -0
  29. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +83 -83
  30. package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
  31. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +37 -80
  32. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  33. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +58 -68
  34. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  35. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  36. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js +7 -0
  37. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js.map +1 -0
  38. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  39. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js +461 -0
  40. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js.map +1 -0
  41. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.d.ts +2 -0
  42. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js +3 -0
  43. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js.map +1 -0
  44. package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
  45. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +21 -28
  46. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
  47. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +124 -40
  48. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  49. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
  50. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +6 -13
  51. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
  52. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.d.ts +13 -0
  53. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js +76 -0
  54. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js.map +1 -0
  55. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js +7 -7
  56. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js.map +1 -1
  57. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +6 -6
  58. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
  59. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.d.ts +1 -1
  60. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +18 -18
  61. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  62. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js +2 -1
  63. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js.map +1 -1
  64. package/node_modules/@sdd-agent-platform/core/dist/risk/contracts.d.ts +2 -2
  65. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +7 -7
  66. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +12 -27
  68. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +6 -6
  70. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
  71. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js +1 -1
  72. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js.map +1 -1
  73. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +2 -4
  74. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  75. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.d.ts +28 -0
  76. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +383 -0
  77. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -0
  78. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +37 -0
  79. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +227 -0
  80. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -0
  81. package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +1 -0
  82. package/node_modules/@sdd-agent-platform/core/dist/router.js +1 -0
  83. package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
  84. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +16 -0
  85. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +6 -0
  86. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  87. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +20 -0
  88. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +7 -7
  89. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  90. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +1 -2
  91. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +2 -9
  92. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -1
  93. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.d.ts +8 -0
  94. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js +131 -0
  95. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js.map +1 -0
  96. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +1 -4
  97. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
  98. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +0 -39
  99. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
  100. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/model.d.ts +1 -17
  101. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +10 -0
  102. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +65 -0
  103. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  104. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +64 -0
  105. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +211 -0
  106. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -0
  107. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  108. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js +179 -0
  109. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  110. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +5 -1
  111. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +60 -22
  112. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  113. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js +2 -2
  114. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js.map +1 -1
  115. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js +40 -0
  116. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js.map +1 -0
  117. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.d.ts +12 -0
  118. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js +2 -0
  119. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js.map +1 -0
  120. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +55 -0
  121. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +315 -0
  122. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -0
  123. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +55 -0
  124. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +238 -0
  125. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -0
  126. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +736 -0
  127. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +4018 -0
  128. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -0
  129. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +8 -1
  130. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
  131. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +25 -1
  132. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  133. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.d.ts +170 -18
  134. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +597 -85
  135. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js.map +1 -1
  136. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +1 -17
  137. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +1 -242
  138. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +1 -1
  139. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +1 -110
  140. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +1 -496
  141. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +1 -1
  142. package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +1 -2
  143. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +1 -2
  144. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +1 -1
  145. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.d.ts +167 -0
  146. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js +377 -0
  147. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js.map +1 -0
  148. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +329 -314
  149. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
  150. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -0
  151. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +31 -0
  152. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  153. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.d.ts +44 -0
  154. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +135 -0
  155. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -0
  156. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  157. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -49
  158. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +1 -545
  159. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  160. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +5 -7
  161. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +15 -55
  162. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  163. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +1 -40
  164. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  165. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  166. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js +521 -0
  167. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js.map +1 -0
  168. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.d.ts +12 -2
  169. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +247 -112
  170. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  171. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.d.ts +26 -0
  172. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js +73 -0
  173. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js.map +1 -0
  174. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.d.ts +1 -1
  175. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +49 -72
  176. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  177. package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +3 -3
  178. package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -2
  179. package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
  180. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +2 -7
  181. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  182. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +0 -7
  183. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -1
  184. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +2 -4
  185. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -1
  186. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +3 -5
  187. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +30 -4
  188. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  189. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  190. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js +110 -0
  191. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js.map +1 -0
  192. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.d.ts +12 -0
  193. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js +63 -0
  194. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js.map +1 -0
  195. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  196. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js +95 -0
  197. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  198. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +55 -5
  199. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +518 -36
  200. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  201. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  202. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js +452 -0
  203. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js.map +1 -0
  204. package/node_modules/@sdd-agent-platform/core/package.json +6 -3
  205. package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +238 -137
  206. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +84 -103
  207. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +189 -189
  208. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +222 -222
  209. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.test.ts +28 -28
  210. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +302 -302
  211. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +181 -181
  212. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +231 -231
  213. package/node_modules/@sdd-agent-platform/core/src/artifacts/templates.ts +99 -99
  214. package/node_modules/@sdd-agent-platform/core/src/artifacts.ts +4 -4
  215. package/node_modules/@sdd-agent-platform/core/src/coding-facts/contracts.ts +79 -79
  216. package/node_modules/@sdd-agent-platform/core/src/coding-facts.ts +1 -1
  217. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +314 -306
  218. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +128 -120
  219. package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +265 -259
  220. package/node_modules/@sdd-agent-platform/core/src/config/project-detection.ts +147 -147
  221. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +400 -445
  222. package/node_modules/@sdd-agent-platform/core/src/context/budget.ts +30 -30
  223. package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +305 -317
  224. package/node_modules/@sdd-agent-platform/core/src/context/command-summary.ts +45 -45
  225. package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +188 -188
  226. package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +144 -144
  227. package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +48 -48
  228. package/node_modules/@sdd-agent-platform/core/src/context/source-refs.ts +41 -41
  229. package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +47 -47
  230. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.test.ts +71 -71
  231. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.ts +178 -178
  232. package/node_modules/@sdd-agent-platform/core/src/context-offload.ts +2 -2
  233. package/node_modules/@sdd-agent-platform/core/src/context.ts +6 -6
  234. package/node_modules/@sdd-agent-platform/core/src/contracts/issues.ts +13 -13
  235. package/node_modules/@sdd-agent-platform/core/src/contracts.test.ts +9 -9
  236. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +121 -115
  237. package/node_modules/@sdd-agent-platform/core/src/delegation/delegation.test.ts +183 -183
  238. package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +23 -23
  239. package/node_modules/@sdd-agent-platform/core/src/delegation/queue.ts +58 -58
  240. package/node_modules/@sdd-agent-platform/core/src/delegation/run-state.ts +14 -14
  241. package/node_modules/@sdd-agent-platform/core/src/delegation/state-machine.ts +90 -90
  242. package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +124 -124
  243. package/node_modules/@sdd-agent-platform/core/src/delegation.ts +26 -26
  244. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/ai-entries.ts +28 -28
  245. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +104 -112
  246. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/local-run-index.ts +27 -27
  247. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +84 -84
  248. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +252 -252
  249. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +330 -330
  250. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-records.ts +79 -79
  251. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +128 -128
  252. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +300 -300
  253. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +627 -657
  254. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +301 -318
  255. package/node_modules/@sdd-agent-platform/core/src/doctor/model.ts +13 -13
  256. package/node_modules/@sdd-agent-platform/core/src/doctor/summary.ts +11 -11
  257. package/node_modules/@sdd-agent-platform/core/src/doctor.ts +2 -2
  258. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +80 -80
  259. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +48 -49
  260. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime.ts +1 -1
  261. package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +195 -195
  262. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +187 -187
  263. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +305 -305
  264. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +97 -97
  265. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +453 -453
  266. package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +225 -225
  267. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +132 -132
  268. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +436 -436
  269. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -102
  270. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -271
  271. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +111 -111
  272. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.ts +231 -231
  273. package/node_modules/@sdd-agent-platform/core/src/execution.ts +5 -5
  274. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +57 -57
  275. package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +175 -175
  276. package/node_modules/@sdd-agent-platform/core/src/governance.ts +1 -1
  277. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +80 -49
  278. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +38 -81
  279. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +174 -174
  280. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +373 -373
  281. package/node_modules/@sdd-agent-platform/core/src/lifecycle/rendering.ts +29 -29
  282. package/node_modules/@sdd-agent-platform/core/src/lifecycle/risk-signals.ts +146 -146
  283. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -0
  284. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +255 -263
  285. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/contracts.ts +179 -0
  286. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/kernel.ts +522 -0
  287. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph.ts +2 -0
  288. package/node_modules/@sdd-agent-platform/core/src/lifecycle.ts +4 -4
  289. package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +50 -50
  290. package/node_modules/@sdd-agent-platform/core/src/orchestration/index.ts +2 -2
  291. package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +331 -342
  292. package/node_modules/@sdd-agent-platform/core/src/path-safety.test.ts +22 -22
  293. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +243 -243
  294. package/node_modules/@sdd-agent-platform/core/src/phase8-projection-compat.test.ts +152 -153
  295. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +277 -277
  296. package/node_modules/@sdd-agent-platform/core/src/phase9-lifecycle-graph.test.ts +103 -0
  297. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +88 -88
  298. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.ts +222 -222
  299. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +79 -79
  300. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.ts +160 -160
  301. package/node_modules/@sdd-agent-platform/core/src/planning.ts +2 -2
  302. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +426 -426
  303. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +230 -146
  304. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +142 -142
  305. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +253 -253
  306. package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +302 -309
  307. package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +246 -246
  308. package/node_modules/@sdd-agent-platform/core/src/registries/plan-scout-domains.ts +89 -0
  309. package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +119 -119
  310. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +454 -429
  311. package/node_modules/@sdd-agent-platform/core/src/registries/skill-capabilities.ts +37 -37
  312. package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +135 -135
  313. package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +132 -132
  314. package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +144 -144
  315. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +111 -111
  316. package/node_modules/@sdd-agent-platform/core/src/registries.ts +42 -42
  317. package/node_modules/@sdd-agent-platform/core/src/risk/consumer-diagnostics.ts +98 -97
  318. package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +63 -63
  319. package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +233 -233
  320. package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +251 -266
  321. package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +203 -203
  322. package/node_modules/@sdd-agent-platform/core/src/risk.ts +5 -5
  323. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +327 -327
  324. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +388 -388
  325. package/node_modules/@sdd-agent-platform/core/src/router/profile-resolution.ts +154 -154
  326. package/node_modules/@sdd-agent-platform/core/src/router/risk-policy.ts +33 -33
  327. package/node_modules/@sdd-agent-platform/core/src/router/route-cache.ts +100 -100
  328. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +356 -356
  329. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +428 -428
  330. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.ts +2 -2
  331. package/node_modules/@sdd-agent-platform/core/src/router/routing-rules.ts +73 -73
  332. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +189 -191
  333. package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +464 -0
  334. package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +124 -124
  335. package/node_modules/@sdd-agent-platform/core/src/router/runtime-registry.ts +123 -123
  336. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +277 -277
  337. package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +273 -0
  338. package/node_modules/@sdd-agent-platform/core/src/router/team-mode.ts +170 -170
  339. package/node_modules/@sdd-agent-platform/core/src/router.ts +5 -4
  340. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +126 -118
  341. package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +27 -27
  342. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +172 -172
  343. package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +109 -109
  344. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +252 -230
  345. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.test.ts +52 -52
  346. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +356 -356
  347. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +70 -70
  348. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +406 -406
  349. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +198 -206
  350. package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -0
  351. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +8 -8
  352. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +60 -63
  353. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +257 -296
  354. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +140 -152
  355. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +66 -68
  356. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.ts +2 -2
  357. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +253 -176
  358. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +101 -0
  359. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +314 -0
  360. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.test.ts +380 -0
  361. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.ts +207 -0
  362. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +111 -111
  363. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +207 -207
  364. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +95 -95
  365. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-inspection.ts +39 -39
  366. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +467 -401
  367. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +738 -694
  368. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-rendering.ts +81 -81
  369. package/node_modules/@sdd-agent-platform/core/src/sdd-docs.ts +5 -5
  370. package/node_modules/@sdd-agent-platform/core/src/spec-manager-contracts.ts +13 -0
  371. package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +435 -0
  372. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +316 -0
  373. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2964 -0
  374. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5856 -0
  375. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/contracts.ts +40 -40
  376. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +209 -209
  377. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +360 -352
  378. package/node_modules/@sdd-agent-platform/core/src/stage-runtime.ts +2 -2
  379. package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +288 -288
  380. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +651 -625
  381. package/node_modules/@sdd-agent-platform/core/src/status.ts +2 -2
  382. package/node_modules/@sdd-agent-platform/core/src/storage/json-io.ts +10 -10
  383. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +489 -489
  384. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +1981 -1175
  385. package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +45 -45
  386. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +232 -232
  387. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +307 -307
  388. package/node_modules/@sdd-agent-platform/core/src/subagents.ts +2 -2
  389. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.test.ts +141 -0
  390. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.ts +566 -0
  391. package/node_modules/@sdd-agent-platform/core/src/task-risk-profile.ts +193 -193
  392. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +413 -398
  393. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +102 -70
  394. package/node_modules/@sdd-agent-platform/core/src/test-support.ts +2 -2
  395. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -0
  396. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +174 -0
  397. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +137 -181
  398. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +77 -77
  399. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -77
  400. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +455 -494
  401. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.test.ts → task-evidence-judgment.test.ts} +261 -335
  402. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.ts → task-evidence-judgment.ts} +619 -648
  403. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +1190 -1032
  404. package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -0
  405. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +513 -513
  406. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +334 -358
  407. package/node_modules/@sdd-agent-platform/core/src/verification.ts +8 -8
  408. package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +26 -26
  409. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.test.ts +88 -88
  410. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.ts +112 -112
  411. package/node_modules/@sdd-agent-platform/core/src/work-units.ts +2 -2
  412. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +190 -196
  413. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +169 -171
  414. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +136 -143
  415. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +135 -137
  416. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +153 -155
  417. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +111 -114
  418. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +95 -95
  419. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +32 -32
  420. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +114 -114
  421. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +184 -156
  422. package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -0
  423. package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -0
  424. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -0
  425. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +970 -464
  426. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +967 -363
  427. package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -0
  428. package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +2 -2
  429. package/node_modules/@sdd-agent-platform/core/src/worktree/isolation.ts +130 -130
  430. package/node_modules/@sdd-agent-platform/core/src/worktree/lifecycle.ts +269 -269
  431. package/node_modules/@sdd-agent-platform/core/src/worktree/worktree.test.ts +150 -150
  432. package/node_modules/@sdd-agent-platform/core/src/worktree.ts +2 -2
  433. package/node_modules/@sdd-agent-platform/core/tsconfig.json +15 -15
  434. package/package.json +2 -2
  435. package/packages/cli/dist/args.js +1 -1
  436. package/packages/cli/dist/args.js.map +1 -1
  437. package/packages/cli/dist/commands/context.js +1 -1
  438. package/packages/cli/dist/commands/context.js.map +1 -1
  439. package/packages/cli/dist/commands/evidence.js.map +1 -0
  440. package/packages/cli/dist/commands/execution.js +126 -0
  441. package/packages/cli/dist/commands/execution.js.map +1 -1
  442. package/packages/cli/dist/commands/instructions.d.ts +1 -1
  443. package/packages/cli/dist/commands/instructions.js +15 -1
  444. package/packages/cli/dist/commands/instructions.js.map +1 -1
  445. package/packages/cli/dist/commands/registry/runtime.js +70 -1
  446. package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
  447. package/packages/cli/dist/commands/run.js +12 -1
  448. package/packages/cli/dist/commands/run.js.map +1 -1
  449. package/packages/cli/dist/commands/stage-close.d.ts +66 -0
  450. package/packages/cli/dist/commands/stage-close.js +524 -0
  451. package/packages/cli/dist/commands/stage-close.js.map +1 -0
  452. package/packages/cli/dist/commands/status.js +8 -1
  453. package/packages/cli/dist/commands/status.js.map +1 -1
  454. package/packages/cli/dist/commands/tasks.js.map +1 -1
  455. package/packages/cli/dist/dispatch.js +6 -31
  456. package/packages/cli/dist/dispatch.js.map +1 -1
  457. package/packages/cli/dist/help.js +153 -158
  458. package/packages/cli/dist/help.js.map +1 -1
  459. package/packages/cli/dist/renderers/workflow.d.ts +51 -2
  460. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  461. package/packages/cli/dist/skill-import-args.d.ts +10 -0
  462. package/packages/cli/dist/skill-import-args.js +47 -0
  463. package/packages/cli/dist/skill-import-args.js.map +1 -0
  464. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  465. package/packages/cli/package.json +2 -2
  466. package/packages/core/dist/ai-tools.js +84 -103
  467. package/packages/core/dist/ai-tools.js.map +1 -1
  468. package/packages/core/dist/config/init-project.d.ts +10 -6
  469. package/packages/core/dist/config/init-project.js +7 -8
  470. package/packages/core/dist/config/init-project.js.map +1 -1
  471. package/packages/core/dist/config/project-config.d.ts +3 -1
  472. package/packages/core/dist/config/project-config.js +7 -3
  473. package/packages/core/dist/config/project-config.js.map +1 -1
  474. package/packages/core/dist/config/starter-documents.d.ts +0 -1
  475. package/packages/core/dist/config/starter-documents.js +374 -421
  476. package/packages/core/dist/config/starter-documents.js.map +1 -1
  477. package/packages/core/dist/context/build-package.d.ts +1 -1
  478. package/packages/core/dist/context/build-package.js +7 -19
  479. package/packages/core/dist/context/build-package.js.map +1 -1
  480. package/packages/core/dist/contracts.d.ts +7 -1
  481. package/packages/core/dist/contracts.js +6 -0
  482. package/packages/core/dist/contracts.js.map +1 -1
  483. package/packages/core/dist/doctor/checks/document-chain.js +2 -12
  484. package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
  485. package/packages/core/dist/doctor/doctor.js +1 -18
  486. package/packages/core/dist/doctor/doctor.js.map +1 -1
  487. package/packages/core/dist/evidence/lookup.d.ts +1 -1
  488. package/packages/core/dist/evidence/lookup.js +1 -1
  489. package/packages/core/dist/evidence/lookup.js.map +1 -1
  490. package/packages/core/dist/evidence-runtime/contracts.d.ts +0 -1
  491. package/packages/core/dist/evidence-runtime/coordination.js +110 -0
  492. package/packages/core/dist/evidence-runtime/coordination.js.map +1 -0
  493. package/packages/core/dist/execution/host-invocation.js +83 -83
  494. package/packages/core/dist/instructions.d.ts +1 -1
  495. package/packages/core/dist/instructions.js +37 -80
  496. package/packages/core/dist/instructions.js.map +1 -1
  497. package/packages/core/dist/lifecycle/ship.js +58 -68
  498. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  499. package/packages/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  500. package/packages/core/dist/lifecycle-graph/contracts.js +7 -0
  501. package/packages/core/dist/lifecycle-graph/contracts.js.map +1 -0
  502. package/packages/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  503. package/packages/core/dist/lifecycle-graph/kernel.js +461 -0
  504. package/packages/core/dist/lifecycle-graph/kernel.js.map +1 -0
  505. package/packages/core/dist/lifecycle-graph.d.ts +2 -0
  506. package/packages/core/dist/lifecycle-graph.js +3 -0
  507. package/packages/core/dist/lifecycle-graph.js.map +1 -0
  508. package/packages/core/dist/orchestration/contracts.d.ts +1 -1
  509. package/packages/core/dist/orchestration/runtime.js +21 -28
  510. package/packages/core/dist/orchestration/runtime.js.map +1 -1
  511. package/packages/core/dist/registries/agent-registry.js +124 -40
  512. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  513. package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
  514. package/packages/core/dist/registries/command-team-runtime.js +6 -13
  515. package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
  516. package/packages/core/dist/registries/plan-scout-domains.d.ts +13 -0
  517. package/packages/core/dist/registries/plan-scout-domains.js +76 -0
  518. package/packages/core/dist/registries/plan-scout-domains.js.map +1 -0
  519. package/packages/core/dist/registries/skill-capabilities.js +7 -7
  520. package/packages/core/dist/registries/skill-capabilities.js.map +1 -1
  521. package/packages/core/dist/registries/tool-capabilities.js +6 -6
  522. package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
  523. package/packages/core/dist/registries/workflow-gates.d.ts +1 -1
  524. package/packages/core/dist/registries/workflow-gates.js +18 -18
  525. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  526. package/packages/core/dist/risk/consumer-diagnostics.js +2 -1
  527. package/packages/core/dist/risk/consumer-diagnostics.js.map +1 -1
  528. package/packages/core/dist/risk/contracts.d.ts +2 -2
  529. package/packages/core/dist/risk/kernel.js +7 -7
  530. package/packages/core/dist/risk/kernel.js.map +1 -1
  531. package/packages/core/dist/risk/legacy-adapters.js +12 -27
  532. package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
  533. package/packages/core/dist/risk/workflow-gates.js +6 -6
  534. package/packages/core/dist/risk/workflow-gates.js.map +1 -1
  535. package/packages/core/dist/router/agent-runtime-config.js +1 -1
  536. package/packages/core/dist/router/agent-runtime-config.js.map +1 -1
  537. package/packages/core/dist/router/routing.js +2 -4
  538. package/packages/core/dist/router/routing.js.map +1 -1
  539. package/packages/core/dist/router/runtime-import.d.ts +28 -0
  540. package/packages/core/dist/router/runtime-import.js +383 -0
  541. package/packages/core/dist/router/runtime-import.js.map +1 -0
  542. package/packages/core/dist/router/stage-route-binding.d.ts +37 -0
  543. package/packages/core/dist/router/stage-route-binding.js +227 -0
  544. package/packages/core/dist/router/stage-route-binding.js.map +1 -0
  545. package/packages/core/dist/router.d.ts +1 -0
  546. package/packages/core/dist/router.js +1 -0
  547. package/packages/core/dist/router.js.map +1 -1
  548. package/packages/core/dist/run-state/artifacts.d.ts +16 -0
  549. package/packages/core/dist/run-state/artifacts.js +6 -0
  550. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  551. package/packages/core/dist/run-state/model.d.ts +20 -0
  552. package/packages/core/dist/run-state/run-state.js +7 -7
  553. package/packages/core/dist/run-state/run-state.js.map +1 -1
  554. package/packages/core/dist/run-state/task-evidence.d.ts +1 -2
  555. package/packages/core/dist/run-state/task-evidence.js +2 -9
  556. package/packages/core/dist/run-state/task-evidence.js.map +1 -1
  557. package/packages/core/dist/run-state/timing.d.ts +8 -0
  558. package/packages/core/dist/run-state/timing.js +131 -0
  559. package/packages/core/dist/run-state/timing.js.map +1 -0
  560. package/packages/core/dist/runtime-analysis/build.js +1 -4
  561. package/packages/core/dist/runtime-analysis/build.js.map +1 -1
  562. package/packages/core/dist/runtime-analysis/findings.js +0 -39
  563. package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
  564. package/packages/core/dist/runtime-analysis/model.d.ts +1 -17
  565. package/packages/core/dist/runtime-paths.d.ts +10 -0
  566. package/packages/core/dist/runtime-paths.js +65 -0
  567. package/packages/core/dist/runtime-paths.js.map +1 -1
  568. package/packages/core/dist/runtime-projection-p0.d.ts +64 -0
  569. package/packages/core/dist/runtime-projection-p0.js +211 -0
  570. package/packages/core/dist/runtime-projection-p0.js.map +1 -0
  571. package/packages/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  572. package/packages/core/dist/sdd-docs/artifact-depth.js +179 -0
  573. package/packages/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  574. package/packages/core/dist/sdd-docs/task-parser.d.ts +5 -1
  575. package/packages/core/dist/sdd-docs/task-parser.js +60 -22
  576. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  577. package/packages/core/dist/sdd-docs/task-rendering.js +2 -2
  578. package/packages/core/dist/sdd-docs/task-rendering.js.map +1 -1
  579. package/packages/core/dist/spec-entry.js +40 -0
  580. package/packages/core/dist/spec-entry.js.map +1 -0
  581. package/packages/core/dist/spec-manager-contracts.d.ts +12 -0
  582. package/packages/core/dist/spec-manager-contracts.js +2 -0
  583. package/packages/core/dist/spec-manager-contracts.js.map +1 -0
  584. package/packages/core/dist/stage-artifacts.d.ts +55 -0
  585. package/packages/core/dist/stage-artifacts.js +315 -0
  586. package/packages/core/dist/stage-artifacts.js.map +1 -0
  587. package/packages/core/dist/stage-collaboration-contracts.d.ts +55 -0
  588. package/packages/core/dist/stage-collaboration-contracts.js +238 -0
  589. package/packages/core/dist/stage-collaboration-contracts.js.map +1 -0
  590. package/packages/core/dist/stage-collaboration.d.ts +736 -0
  591. package/packages/core/dist/stage-collaboration.js +4018 -0
  592. package/packages/core/dist/stage-collaboration.js.map +1 -0
  593. package/packages/core/dist/stage-runtime/runtime.js +8 -1
  594. package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
  595. package/packages/core/dist/status/project-status.js +25 -1
  596. package/packages/core/dist/status/project-status.js.map +1 -1
  597. package/packages/core/dist/storage/runtime-store.d.ts +170 -18
  598. package/packages/core/dist/storage/runtime-store.js +597 -85
  599. package/packages/core/dist/storage/runtime-store.js.map +1 -1
  600. package/packages/core/dist/sync-back/apply.d.ts +1 -17
  601. package/packages/core/dist/sync-back/apply.js +1 -242
  602. package/packages/core/dist/sync-back/apply.js.map +1 -1
  603. package/packages/core/dist/sync-back/inspect.d.ts +1 -110
  604. package/packages/core/dist/sync-back/inspect.js +1 -496
  605. package/packages/core/dist/sync-back/inspect.js.map +1 -1
  606. package/packages/core/dist/sync-back.d.ts +1 -2
  607. package/packages/core/dist/sync-back.js +1 -2
  608. package/packages/core/dist/sync-back.js.map +1 -1
  609. package/packages/core/dist/task-execution-contract.d.ts +167 -0
  610. package/packages/core/dist/task-execution-contract.js +377 -0
  611. package/packages/core/dist/task-execution-contract.js.map +1 -0
  612. package/packages/core/dist/test-support/fixtures.js +329 -314
  613. package/packages/core/dist/test-support/fixtures.js.map +1 -1
  614. package/packages/core/dist/test-support/run-state.d.ts +1 -0
  615. package/packages/core/dist/test-support/run-state.js +31 -0
  616. package/packages/core/dist/test-support/run-state.js.map +1 -1
  617. package/packages/core/dist/truth-reconciliation.d.ts +44 -0
  618. package/packages/core/dist/truth-reconciliation.js +135 -0
  619. package/packages/core/dist/truth-reconciliation.js.map +1 -0
  620. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  621. package/packages/core/dist/verification/goal-verify.d.ts +0 -49
  622. package/packages/core/dist/verification/goal-verify.js +1 -545
  623. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  624. package/packages/core/dist/verification/rendering.d.ts +5 -7
  625. package/packages/core/dist/verification/rendering.js +15 -55
  626. package/packages/core/dist/verification/rendering.js.map +1 -1
  627. package/packages/core/dist/verification/single-task-loop.js +1 -40
  628. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  629. package/packages/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  630. package/packages/core/dist/verification/task-evidence-judgment.js +521 -0
  631. package/packages/core/dist/verification/task-evidence-judgment.js.map +1 -0
  632. package/packages/core/dist/verification/test-runtime.d.ts +12 -2
  633. package/packages/core/dist/verification/test-runtime.js +247 -112
  634. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  635. package/packages/core/dist/verification/validation-cache.d.ts +26 -0
  636. package/packages/core/dist/verification/validation-cache.js +73 -0
  637. package/packages/core/dist/verification/validation-cache.js.map +1 -0
  638. package/packages/core/dist/verification/verify-contract.d.ts +1 -1
  639. package/packages/core/dist/verification/verify-contract.js +49 -72
  640. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  641. package/packages/core/dist/verification.d.ts +3 -3
  642. package/packages/core/dist/verification.js +2 -2
  643. package/packages/core/dist/verification.js.map +1 -1
  644. package/packages/core/dist/workflow-gate/evidence-packet.js +2 -7
  645. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  646. package/packages/core/dist/workflow-gate/hard-checks.js +0 -7
  647. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -1
  648. package/packages/core/dist/workflow-gate/policy.js +2 -4
  649. package/packages/core/dist/workflow-gate/policy.js.map +1 -1
  650. package/packages/core/dist/workflow-gate/types.d.ts +3 -5
  651. package/packages/core/dist/workflow-state/latest-eligible-run.js +30 -4
  652. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  653. package/packages/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  654. package/packages/core/dist/workflow-state/migration-recovery.js +110 -0
  655. package/packages/core/dist/workflow-state/migration-recovery.js.map +1 -0
  656. package/packages/core/dist/workflow-state/repair-contract.d.ts +12 -0
  657. package/packages/core/dist/workflow-state/repair-contract.js +63 -0
  658. package/packages/core/dist/workflow-state/repair-contract.js.map +1 -0
  659. package/packages/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  660. package/packages/core/dist/workflow-state/resolve-task-run.js +95 -0
  661. package/packages/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  662. package/packages/core/dist/workflow-state/resolve.d.ts +55 -5
  663. package/packages/core/dist/workflow-state/resolve.js +518 -36
  664. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  665. package/packages/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  666. package/packages/core/dist/workflow-state/runtime-projections.js +452 -0
  667. package/packages/core/dist/workflow-state/runtime-projections.js.map +1 -0
  668. package/packages/core/package.json +6 -3
  669. package/tsconfig.build.json +6 -7
  670. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.d.ts +0 -2
  671. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js +0 -44
  672. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js.map +0 -1
  673. package/node_modules/@sdd-agent-platform/core/src/sync-back/apply.ts +0 -270
  674. package/node_modules/@sdd-agent-platform/core/src/sync-back/inspect.ts +0 -655
  675. package/node_modules/@sdd-agent-platform/core/src/sync-back/sync-back.test.ts +0 -569
  676. package/node_modules/@sdd-agent-platform/core/src/sync-back.ts +0 -2
  677. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +0 -255
  678. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +0 -439
  679. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +0 -341
  680. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +0 -204
  681. package/packages/cli/dist/commands/lifecycle.d.ts +0 -6
  682. package/packages/cli/dist/commands/lifecycle.js +0 -112
  683. package/packages/cli/dist/commands/lifecycle.js.map +0 -1
  684. package/packages/cli/dist/commands/sync-back.d.ts +0 -6
  685. package/packages/cli/dist/commands/sync-back.js +0 -82
  686. package/packages/cli/dist/commands/sync-back.js.map +0 -1
  687. package/packages/cli/dist/commands/test.d.ts +0 -6
  688. package/packages/cli/dist/commands/test.js +0 -195
  689. package/packages/cli/dist/commands/test.js.map +0 -1
  690. package/packages/cli/dist/commands/verifies.d.ts +0 -6
  691. package/packages/cli/dist/commands/verifies.js +0 -85
  692. package/packages/cli/dist/commands/verifies.js.map +0 -1
  693. package/packages/cli/dist/commands/verify.d.ts +0 -6
  694. package/packages/cli/dist/commands/verify.js +0 -134
  695. package/packages/cli/dist/commands/verify.js.map +0 -1
  696. package/packages/core/dist/doctor/render.d.ts +0 -2
  697. package/packages/core/dist/doctor/render.js +0 -44
  698. package/packages/core/dist/doctor/render.js.map +0 -1
@@ -1,694 +1,738 @@
1
- import { hashDocumentContent, hashSemanticDocument, hashTasksContract, documentHashMatches } from './document-hashes.js';
2
- import { readdir, readFile } from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { assertSafePathSegment } from '../path-safety.js';
5
- import { exists } from '../storage/json-io.js';
6
-
7
- export type SddTaskStatus = 'pending' | 'in_progress' | 'completed' | 'blocked' | 'deferred' | 'unknown';
8
- export type SddChangeSurface = 'frontend_only' | 'backend_only' | 'full_stack' | 'docs_only' | 'config_only' | 'unknown';
9
- export type SddValidationTiming = 'task_end' | 'batch_end' | 'wave_end';
10
- export type SddGapSeverity = 'blocking' | 'warning';
11
- export type SddGapType = 'Document Gap' | 'Task Gap' | 'Dependency Gap';
12
-
13
- export interface SddTaskSourceLocation {
14
- filePath: string;
15
- heading: string | null;
16
- lineStart: number;
17
- lineEnd: number;
18
- }
19
-
20
- export interface SddValidationCommand {
21
- command: string;
22
- acceptanceRefs: string[];
23
- raw: string;
24
- }
25
-
26
- export interface SddTask {
27
- id: string;
28
- title: string | null;
29
- status: SddTaskStatus;
30
- wave: number | null;
31
- implementationWave: string | null;
32
- validationBatch: string | null;
33
- validationTiming: SddValidationTiming;
34
- requiresVerifyBeforeNext: boolean;
35
- changeSurface: SddChangeSurface;
36
- dependsOn: string[];
37
- affectedFiles: string[];
38
- validation: string[];
39
- validationCommands: SddValidationCommand[];
40
- risk: string[];
41
- acceptanceRefs: string[];
42
- planRefs: string[];
43
- fileOwnership: string[];
44
- agentFit: string[];
45
- verificationAvailability: string[];
46
- autonomy: string | null;
47
- allowedAgents: string[];
48
- requiredArtifacts: string[];
49
- gapState: string | null;
50
- boundary: string | null;
51
- acceptance: string[];
52
- implementationNotes: string | null;
53
- rawMetadata: Record<string, string | string[]>;
54
- source: SddTaskSourceLocation;
55
- }
56
-
57
- export interface SddTaskGap {
58
- type: SddGapType;
59
- severity: SddGapSeverity;
60
- taskId: string | null;
61
- field: string;
62
- message: string;
63
- recommendation: string;
64
- }
65
-
66
- export interface SddTaskModel {
67
- branch: string;
68
- specPath: string;
69
- planPath: string;
70
- tasksPath: string;
71
- verifyPath: string;
72
- documents: {
73
- specExists: boolean;
74
- planExists: boolean;
75
- tasksExists: boolean;
76
- verifyExists: boolean;
77
- specHash?: string | null;
78
- planHash?: string | null;
79
- tasksHash?: string | null;
80
- verifyHash?: string | null;
81
- specDocHash?: string | null;
82
- planDocHash?: string | null;
83
- tasksDocHash?: string | null;
84
- verifyDocHash?: string | null;
85
- specContractHash?: string | null;
86
- planContractHash?: string | null;
87
- tasksContractHash?: string | null;
88
- verifyContractHash?: string | null;
89
- planBasedOnSpecHash?: string | null;
90
- tasksBasedOnPlanHash?: string | null;
91
- verifyBasedOnTasksHash?: string | null;
92
- planBasedOnSpecContractHash?: string | null;
93
- tasksBasedOnPlanContractHash?: string | null;
94
- verifyBasedOnTasksContractHash?: string | null;
95
- planStale?: boolean;
96
- tasksStale?: boolean;
97
- verifyStale?: boolean;
98
- };
99
- tasks: SddTask[];
100
- gaps: SddTaskGap[];
101
- }
102
-
103
- export async function parseSddBranch(projectRoot: string, branch = 'master'): Promise<SddTaskModel> {
104
- assertSafePathSegment(branch, 'branch');
105
- const specPath = path.join(projectRoot, 'specs', branch, 'spec.md');
106
- const planPath = path.join(projectRoot, 'specs', branch, 'plan.md');
107
- const tasksPath = path.join(projectRoot, 'specs', branch, 'tasks.md');
108
- const verifyPath = path.join(projectRoot, 'specs', branch, 'verify.md');
109
- const [specExists, planExists, tasksExists, verifyExists] = await Promise.all([exists(specPath), exists(planPath), exists(tasksPath), exists(verifyPath)]);
110
- const [rawSpec, rawPlan, rawTasks, rawVerify] = await Promise.all([
111
- specExists ? readFile(specPath, 'utf8') : Promise.resolve(null),
112
- planExists ? readFile(planPath, 'utf8') : Promise.resolve(null),
113
- tasksExists ? readFile(tasksPath, 'utf8') : Promise.resolve(null),
114
- verifyExists ? readFile(verifyPath, 'utf8') : Promise.resolve(null)
115
- ]);
116
- const documents = buildDocumentChainState({ specExists, planExists, tasksExists, verifyExists, rawSpec, rawPlan, rawTasks, rawVerify });
117
- const gaps: SddTaskGap[] = [];
118
-
119
- if (documents.planStale) {
120
- gaps.push(documentGap('plan.md', `Plan document is stale because based_on_spec_hash ${documents.planBasedOnSpecHash} no longer matches current spec hash ${documents.specHash}.`, 'Refresh the plan stage for this partition before updating tasks or executing implementation.'));
121
- }
122
- if (documents.tasksStale) {
123
- gaps.push(documentGap('tasks.md', `Tasks document is stale because based_on_plan_hash ${documents.tasksBasedOnPlanHash} no longer matches current plan hash ${documents.planHash}.`, 'Refresh the tasks stage for this partition before executing implementation.'));
124
- }
125
- if (documents.verifyStale) {
126
- const staleBoundary = documents.verifyBasedOnTasksContractHash ? 'tasks contract hash' : 'tasks contract hash binding';
127
- const currentHash = documents.verifyBasedOnTasksContractHash ? documents.tasksContractHash : 'required';
128
- gaps.push(documentGap('verify.md', `Verify document is stale because based_on_${staleBoundary.replace(/ /g, '_')} ${documents.verifyBasedOnTasksContractHash ?? 'missing'} no longer matches current ${staleBoundary} ${currentHash}.`, 'Run /sdd:tasks or explicit sdd verifies write recovery before /sdd:test.'));
129
- }
130
- if (!specExists) {
131
- gaps.push(documentGap('spec.md', 'Spec document is missing.', 'Create or restore specs/<branch>/spec.md before full SDD execution.'));
132
- }
133
- if (!planExists) {
134
- gaps.push(documentGap('plan.md', 'Plan document is missing.', 'Create or restore specs/<branch>/plan.md before task execution.'));
135
- }
136
- if (!tasksExists || rawTasks === null) {
137
- gaps.push(documentGap('tasks.md', 'Tasks document is missing.', 'Create specs/<branch>/tasks.md with sdd-task fenced blocks.'));
138
- return {
139
- branch,
140
- specPath,
141
- planPath,
142
- tasksPath,
143
- verifyPath,
144
- documents,
145
- tasks: [],
146
- gaps
147
- };
148
- }
149
-
150
- const taskModel = parseSddTasksMarkdown(rawTasks, { tasksPath });
151
- if (taskModel.tasks.length === 0 && !path.basename(tasksPath).startsWith('phase')) {
152
- const retainedModel = await parseRetainedPhaseTasks(path.dirname(tasksPath));
153
- if (retainedModel.tasks.length > 0) {
154
- return {
155
- branch,
156
- specPath,
157
- planPath,
158
- tasksPath,
159
- verifyPath,
160
- documents,
161
- tasks: retainedModel.tasks,
162
- gaps: [...gaps, ...retainedModel.gaps]
163
- };
164
- }
165
- }
166
- return {
167
- branch,
168
- specPath,
169
- planPath,
170
- tasksPath,
171
- verifyPath,
172
- documents,
173
- tasks: taskModel.tasks,
174
- gaps: [...gaps, ...taskModel.gaps]
175
- };
176
- }
177
-
178
- export function parseSddTasksMarkdown(raw: string, options: { branch?: string; tasksPath?: string; validateDependencies?: boolean } = {}): Pick<SddTaskModel, 'tasks' | 'gaps'> {
179
- const tasksPath = options.tasksPath ?? 'tasks.md';
180
- const fencedBlocks = Array.from(raw.matchAll(/^\s*```sdd-task\s*\r?\n([\s\S]*?)\r?^\s*```\s*$/gm));
181
- const tasks: SddTask[] = [];
182
- const gaps: SddTaskGap[] = [];
183
-
184
- if (fencedBlocks.length === 0) {
185
- gaps.push({
186
- type: 'Task Gap',
187
- severity: 'blocking',
188
- taskId: null,
189
- field: 'sdd-task',
190
- message: 'No sdd-task fenced blocks found in tasks.md.',
191
- recommendation: 'Add one sdd-task fenced block per executable task.'
192
- });
193
- return { tasks, gaps };
194
- }
195
-
196
- const seenIds = new Map<string, SddTaskSourceLocation>();
197
- for (let blockIndex = 0; blockIndex < fencedBlocks.length; blockIndex += 1) {
198
- const blockMatch = fencedBlocks[blockIndex];
199
- const block = blockMatch[1] ?? '';
200
- const blockStart = blockMatch.index ?? 0;
201
- const blockEnd = blockStart + blockMatch[0].length;
202
- const nextBlockStart = fencedBlocks[blockIndex + 1]?.index ?? raw.length;
203
- const lineStart = lineNumberAt(raw, blockStart);
204
- const lineEnd = lineNumberAt(raw, blockEnd);
205
- const heading = nearestTaskHeading(raw.slice(0, blockStart));
206
- const metadata = parseSimpleYamlBlock(block);
207
- const id = scalarValue(metadata.id);
208
- const taskId = id || heading?.id || null;
209
- const section = raw.slice(blockEnd, nextTaskStart(raw, blockEnd, nextBlockStart));
210
- const parsedSections = parseTaskCompanionSections(section);
211
- if (!taskId) {
212
- gaps.push({
213
- type: 'Task Gap',
214
- severity: 'blocking',
215
- taskId: null,
216
- field: 'id',
217
- message: `sdd-task block starting at line ${lineStart} is missing id.`,
218
- recommendation: 'Add a stable id field such as id: T1.'
219
- });
220
- continue;
221
- }
222
-
223
- const source: SddTaskSourceLocation = {
224
- filePath: tasksPath,
225
- heading: heading?.raw ?? null,
226
- lineStart,
227
- lineEnd
228
- };
229
- const priorSource = seenIds.get(taskId);
230
- if (priorSource) {
231
- gaps.push({
232
- type: 'Task Gap',
233
- severity: 'blocking',
234
- taskId,
235
- field: 'id',
236
- message: `Duplicate task id ${taskId} in ${taskSourceEvidence({ id: taskId, source })} and ${sourceLocationEvidence(priorSource)}.`,
237
- recommendation: 'Keep task ids unique within a spec branch.'
238
- });
239
- }
240
- seenIds.set(taskId, source);
241
-
242
- const task: SddTask = {
243
- id: taskId,
244
- title: heading?.title ?? null,
245
- status: parseTaskStatus(scalarValue(metadata.status)),
246
- wave: parseWave(scalarValue(metadata.wave)),
247
- implementationWave: scalarValue(metadata.implementation_wave),
248
- validationBatch: scalarValue(metadata.validation_batch),
249
- validationTiming: parseValidationTiming(scalarValue(metadata.validation_timing)),
250
- requiresVerifyBeforeNext: parseRequiresVerifyBeforeNext(scalarValue(metadata.requires_verify_before_next)),
251
- changeSurface: parseChangeSurface(scalarValue(metadata.change_surface)),
252
- dependsOn: listValue(metadata.depends_on),
253
- affectedFiles: listValue(metadata.affected_files),
254
- validation: parseValidationCommands(metadata.validation).map((command) => command.command),
255
- validationCommands: parseValidationCommands(metadata.validation),
256
- risk: listValue(metadata.risk),
257
- acceptanceRefs: listValue(metadata.acceptance_refs),
258
- planRefs: listValue(metadata.plan_refs),
259
- fileOwnership: listValue(metadata.file_ownership),
260
- agentFit: listValue(metadata.agent_fit),
261
- verificationAvailability: listValue(metadata.verification_availability),
262
- autonomy: scalarValue(metadata.autonomy),
263
- allowedAgents: listValue(metadata.allowed_agents),
264
- requiredArtifacts: listValue(metadata.required_artifacts),
265
- gapState: scalarValue(metadata.gap_state),
266
- boundary: parsedSections.boundary,
267
- acceptance: parsedSections.acceptance,
268
- implementationNotes: parsedSections.implementationNotes,
269
- rawMetadata: metadata,
270
- source
271
- };
272
- tasks.push(task);
273
- gaps.push(...validateTask(task));
274
- }
275
-
276
- if (options.validateDependencies !== false) {
277
- gaps.push(...validateAggregateTaskSet(tasks));
278
- }
279
-
280
- return { tasks, gaps };
281
- }
282
-
283
- function buildDocumentChainState(input: { specExists: boolean; planExists: boolean; tasksExists: boolean; verifyExists: boolean; rawSpec: string | null; rawPlan: string | null; rawTasks: string | null; rawVerify: string | null }): SddTaskModel['documents'] {
284
- const specDocHash = input.rawSpec === null ? null : hashDocumentContent(input.rawSpec);
285
- const planDocHash = input.rawPlan === null ? null : hashDocumentContent(input.rawPlan);
286
- const tasksDocHash = input.rawTasks === null ? null : hashDocumentContent(input.rawTasks);
287
- const verifyDocHash = input.rawVerify === null ? null : hashDocumentContent(input.rawVerify);
288
- const specContractHash = hashSemanticDocument(input.rawSpec);
289
- const planContractHash = hashSemanticDocument(input.rawPlan);
290
- const tasksContractHash = hashTasksContract(input.rawTasks);
291
- const verifyContractHash = hashSemanticDocument(input.rawVerify);
292
- const planBasedOnSpecHash = input.rawPlan === null ? null : readDocumentScalar(input.rawPlan, 'based_on_spec_hash');
293
- const tasksBasedOnPlanHash = input.rawTasks === null ? null : readDocumentScalar(input.rawTasks, 'based_on_plan_hash');
294
- const verifyBasedOnTasksHash = input.rawVerify === null ? null : readDocumentScalar(input.rawVerify, 'based_on_tasks_hash');
295
- const planBasedOnSpecContractHash = input.rawPlan === null ? null : readDocumentScalar(input.rawPlan, 'based_on_spec_contract_hash');
296
- const tasksBasedOnPlanContractHash = input.rawTasks === null ? null : readDocumentScalar(input.rawTasks, 'based_on_plan_contract_hash');
297
- const verifyBasedOnTasksContractHash = input.rawVerify === null ? null : readDocumentScalar(input.rawVerify, 'based_on_tasks_contract_hash');
298
-
299
- const planStale = Boolean(
300
- (planBasedOnSpecContractHash && specContractHash && !documentHashMatches(planBasedOnSpecContractHash, specContractHash))
301
- || (!planBasedOnSpecContractHash && planBasedOnSpecHash && specDocHash && !documentHashMatches(planBasedOnSpecHash, specDocHash))
302
- );
303
- const tasksHashMismatch = Boolean(
304
- (tasksBasedOnPlanContractHash && planContractHash && !documentHashMatches(tasksBasedOnPlanContractHash, planContractHash))
305
- || (!tasksBasedOnPlanContractHash && tasksBasedOnPlanHash && planDocHash && !documentHashMatches(tasksBasedOnPlanHash, planDocHash))
306
- );
307
- const verifyHashMismatch = Boolean(
308
- input.rawVerify !== null && (
309
- !verifyBasedOnTasksContractHash
310
- || (verifyBasedOnTasksContractHash && tasksContractHash && !documentHashMatches(verifyBasedOnTasksContractHash, tasksContractHash))
311
- )
312
- );
313
-
314
- return {
315
- specExists: input.specExists,
316
- planExists: input.planExists,
317
- tasksExists: input.tasksExists,
318
- verifyExists: input.verifyExists,
319
- specHash: specDocHash,
320
- planHash: planDocHash,
321
- tasksHash: tasksDocHash,
322
- verifyHash: verifyDocHash,
323
- specDocHash,
324
- planDocHash,
325
- tasksDocHash,
326
- verifyDocHash,
327
- specContractHash,
328
- planContractHash,
329
- tasksContractHash,
330
- verifyContractHash,
331
- planBasedOnSpecHash,
332
- tasksBasedOnPlanHash,
333
- verifyBasedOnTasksHash,
334
- planBasedOnSpecContractHash,
335
- tasksBasedOnPlanContractHash,
336
- verifyBasedOnTasksContractHash,
337
- planStale,
338
- tasksStale: planStale || tasksHashMismatch,
339
- verifyStale: planStale || tasksHashMismatch || verifyHashMismatch
340
- };
341
- }
342
-
343
- function readDocumentScalar(raw: string, key: string): string | null {
344
- const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
345
- const match = raw.match(new RegExp(`^\\s*(?:-\\s*)?${escapedKey}:\\s*(.+?)\\s*$`, 'm'));
346
- return match?.[1]?.trim().replace(/^["'`]|["'`]$/g, '') ?? null;
347
- }
348
-
349
- async function parseRetainedPhaseTasks(specBranchDir: string): Promise<Pick<SddTaskModel, 'tasks' | 'gaps'>> {
350
- const entries = await readdir(specBranchDir, { withFileTypes: true });
351
- const taskFiles = entries
352
- .filter((entry) => entry.isFile() && /^phase\d+\.\d+-tasks\.md$/.test(entry.name))
353
- .map((entry) => path.join(specBranchDir, entry.name))
354
- .sort();
355
- const tasks: SddTask[] = [];
356
- const gaps: SddTaskGap[] = [];
357
- for (const taskFile of taskFiles) {
358
- const raw = await readFile(taskFile, 'utf8');
359
- const parsed = parseSddTasksMarkdown(raw, { tasksPath: taskFile, validateDependencies: false });
360
- tasks.push(...parsed.tasks);
361
- gaps.push(...parsed.gaps);
362
- }
363
- gaps.push(...validateAggregateTaskSet(tasks));
364
- return { tasks, gaps };
365
- }
366
-
367
- function documentGap(field: string, message: string, recommendation: string): SddTaskGap {
368
- return {
369
- type: 'Document Gap',
370
- severity: 'blocking',
371
- taskId: null,
372
- field,
373
- message,
374
- recommendation
375
- };
376
- }
377
-
378
- function parseTaskStatus(value: string | null): SddTaskStatus {
379
- if (value === 'pending' || value === 'in_progress' || value === 'completed' || value === 'blocked' || value === 'deferred') {
380
- return value;
381
- }
382
- return 'unknown';
383
- }
384
-
385
- function parseWave(value: string | null): number | null {
386
- if (!value) {
387
- return null;
388
- }
389
- const parsed = Number(value);
390
- return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
391
- }
392
-
393
- function parseChangeSurface(value: string | null): SddChangeSurface {
394
- if (value === 'frontend_only' || value === 'backend_only' || value === 'full_stack' || value === 'docs_only' || value === 'config_only' || value === 'unknown') {
395
- return value;
396
- }
397
- return 'unknown';
398
- }
399
-
400
- function parseValidationTiming(value: string | null): SddValidationTiming {
401
- if (value === 'batch_end' || value === 'wave_end') {
402
- return value;
403
- }
404
- return 'task_end';
405
- }
406
-
407
- function parseRequiresVerifyBeforeNext(value: string | null): boolean {
408
- return value === 'false' ? false : true;
409
- }
410
-
411
- function isValidChangeSurface(value: string | null): boolean {
412
- return value === null || value === 'frontend_only' || value === 'backend_only' || value === 'full_stack' || value === 'docs_only' || value === 'config_only' || value === 'unknown';
413
- }
414
-
415
- function isValidValidationTiming(value: string | null): boolean {
416
- return value === null || value === 'task_end' || value === 'batch_end' || value === 'wave_end';
417
- }
418
-
419
- function isValidBooleanScalar(value: string | null): boolean {
420
- return value === null || value === 'true' || value === 'false';
421
- }
422
-
423
- function parseSimpleYamlBlock(raw: string): Record<string, string | string[]> {
424
- const result: Record<string, string | string[]> = {};
425
- const lines = raw.split(/\r?\n/);
426
- let currentListKey: string | null = null;
427
-
428
- for (const line of lines) {
429
- const trimmed = line.trim();
430
- if (!trimmed || trimmed.startsWith('#')) {
431
- continue;
432
- }
433
- if (currentListKey && /^-\s+/.test(trimmed)) {
434
- const current = result[currentListKey];
435
- const items = Array.isArray(current) ? current : [];
436
- items.push(unquoteSimpleYamlValue(trimmed.slice(2).trim()));
437
- result[currentListKey] = items;
438
- continue;
439
- }
440
-
441
- const scalarMatch = trimmed.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
442
- if (!scalarMatch) {
443
- currentListKey = null;
444
- continue;
445
- }
446
- const key = scalarMatch[1];
447
- const value = scalarMatch[2].trim();
448
- if (value === '') {
449
- result[key] = [];
450
- currentListKey = key;
451
- } else if (value === '[]') {
452
- result[key] = [];
453
- currentListKey = null;
454
- } else if (value.startsWith('[') && value.endsWith(']')) {
455
- result[key] = value.slice(1, -1).split(',').map((item) => unquoteSimpleYamlValue(item.trim())).filter(Boolean);
456
- currentListKey = null;
457
- } else {
458
- result[key] = unquoteSimpleYamlValue(value);
459
- currentListKey = null;
460
- }
461
- }
462
-
463
- return result;
464
- }
465
-
466
- function unquoteSimpleYamlValue(value: string): string {
467
- if (value.length >= 2) {
468
- const first = value[0];
469
- const last = value[value.length - 1];
470
- if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
471
- return value.slice(1, -1);
472
- }
473
- }
474
- return value;
475
- }
476
-
477
- function scalarValue(value: string | string[] | undefined): string | null {
478
- return typeof value === 'string' && value.length > 0 ? value : null;
479
- }
480
-
481
- function listValue(value: string | string[] | undefined): string[] {
482
- if (Array.isArray(value)) {
483
- return value.filter(Boolean);
484
- }
485
- if (!value || value === '[]') {
486
- return [];
487
- }
488
- return [value];
489
- }
490
-
491
- function parseValidationCommands(value: string | string[] | undefined): SddValidationCommand[] {
492
- return listValue(value).map(parseValidationCommand);
493
- }
494
-
495
- function parseValidationCommand(raw: string): SddValidationCommand {
496
- const separator = raw.match(/\s=>\s/);
497
- if (!separator || separator.index === undefined) {
498
- return { command: raw, acceptanceRefs: [], raw };
499
- }
500
- const command = raw.slice(0, separator.index).trim();
501
- const refsRaw = raw.slice(separator.index + separator[0].length).trim();
502
- return {
503
- command: command || raw,
504
- acceptanceRefs: parseAcceptanceRefList(refsRaw),
505
- raw
506
- };
507
- }
508
-
509
- function parseAcceptanceRefList(raw: string): string[] {
510
- const trimmed = raw.replace(/^\[/, '').replace(/\]$/, '').trim();
511
- if (!trimmed) {
512
- return [];
513
- }
514
- return trimmed.split(',').map((item) => unquoteSimpleYamlValue(item.trim())).filter(Boolean);
515
- }
516
-
517
- function lineNumberAt(raw: string, offset: number): number {
518
- return raw.slice(0, offset).split(/\r?\n/).length;
519
- }
520
-
521
- function nearestTaskHeading(prefix: string): { raw: string; id: string | null; title: string | null } | null {
522
- const matches = Array.from(prefix.matchAll(/^\s*###\s+(.+)$/gm));
523
- const last = matches.at(-1);
524
- if (!last) {
525
- return null;
526
- }
527
- const raw = last[1].trim();
528
- const parsed = raw.match(/^([^::\s]+)\s*[::]\s*(.+)$/);
529
- return {
530
- raw,
531
- id: parsed?.[1]?.trim() ?? null,
532
- title: parsed?.[2]?.trim() ?? raw
533
- };
534
- }
535
-
536
- function nextTaskStart(raw: string, offset: number, limit = raw.length): number {
537
- const next = raw.slice(offset, limit).search(/^\s*###\s+/m);
538
- return next < 0 ? limit : offset + next;
539
- }
540
-
541
- function parseTaskCompanionSections(raw: string): { boundary: string | null; acceptance: string[]; implementationNotes: string | null } {
542
- return {
543
- boundary: sectionText(raw, 'Boundary'),
544
- acceptance: sectionBullets(raw, 'Acceptance'),
545
- implementationNotes: sectionText(raw, 'Implementation Notes')
546
- };
547
- }
548
-
549
- function sectionText(raw: string, title: string): string | null {
550
- const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
551
- const sectionPattern = `^\\s*####\\s+${escaped}\\s*$([\\s\\S]*?)(?=^\\s*####\\s+|^\\s*###\\s+|$(?![\\s\\S]))`;
552
- const match = raw.match(new RegExp(sectionPattern, 'im'));
553
- const text = match?.[1]?.trim() ?? '';
554
- return text.length > 0 ? text : null;
555
- }
556
-
557
- function sectionBullets(raw: string, title: string): string[] {
558
- const text = sectionText(raw, title);
559
- if (!text) {
560
- return [];
561
- }
562
- return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith('- ')).map((line) => line.slice(2).trim()).filter(Boolean);
563
- }
564
-
565
- function validateTask(task: SddTask): SddTaskGap[] {
566
- const gaps: SddTaskGap[] = [];
567
- const requiredLists: Array<[keyof SddTask, string]> = [
568
- ['affectedFiles', 'affected_files'],
569
- ['validation', 'validation']
570
- ];
571
-
572
- if (task.status === 'unknown') {
573
- gaps.push(taskGap(task.id, 'status', 'Task status is missing or unsupported.', 'Use one of pending, in_progress, completed, blocked, deferred.'));
574
- }
575
- if (task.wave === null) {
576
- gaps.push(taskGap(task.id, 'wave', 'Task wave is missing or invalid.', 'Add a positive integer wave value.'));
577
- }
578
- if (!isValidChangeSurface(scalarValue(task.rawMetadata.change_surface))) {
579
- gaps.push(taskGap(task.id, 'change_surface', `Task ${task.id} has invalid change_surface.`, 'Use one of frontend_only, backend_only, full_stack, docs_only, config_only, unknown.'));
580
- }
581
- if (!isValidValidationTiming(scalarValue(task.rawMetadata.validation_timing))) {
582
- gaps.push(taskGap(task.id, 'validation_timing', `Task ${task.id} has invalid validation_timing.`, 'Use one of task_end, batch_end, wave_end.'));
583
- }
584
- if (!isValidBooleanScalar(scalarValue(task.rawMetadata.requires_verify_before_next))) {
585
- gaps.push(taskGap(task.id, 'requires_verify_before_next', `Task ${task.id} has invalid requires_verify_before_next.`, 'Use true or false.'));
586
- }
587
- if ((task.validationTiming === 'batch_end' || task.validationTiming === 'wave_end') && !task.validationBatch) {
588
- gaps.push(taskGap(task.id, 'validation_batch', `Task ${task.id} uses ${task.validationTiming} but has no validation_batch.`, 'Declare validation_batch so batch/wave validation can group implementation evidence before /sdd:test.'));
589
- }
590
- if ((task.validationTiming === 'batch_end' || task.validationTiming === 'wave_end') && task.requiresVerifyBeforeNext) {
591
- gaps.push(taskGap(task.id, 'requires_verify_before_next', `Task ${task.id} uses ${task.validationTiming} but requires verify before next.`, 'Set requires_verify_before_next: false for batch/wave validation, or use validation_timing: task_end for strict validation.'));
592
- }
593
- for (const [property, field] of requiredLists) {
594
- if ((task[property] as unknown[]).length === 0) {
595
- gaps.push(taskGap(task.id, field, `Task ${task.id} has no ${field}.`, `Declare ${field} in the sdd-task block before implementation.`));
596
- }
597
- }
598
- if ((task.changeSurface === 'frontend_only' || isFrontendOnlyTask(task)) && task.validation.some(isBackendBuildValidationCommand)) {
599
- gaps.push(taskGap(task.id, 'validation', `Task ${task.id} is frontend-only but declares backend build validation.`, 'For JSP/static frontend-only changes, replace Maven/Gradle validation with frontend-appropriate checks, dev-server/manual page evidence, lint/typecheck, or an explicit unavailable reason.'));
600
- }
601
- if (!task.boundary) {
602
- gaps.push(taskGap(task.id, 'Boundary', `Task ${task.id} has no Boundary section.`, 'Add a #### Boundary section describing allowed and forbidden scope.'));
603
- }
604
- if (task.acceptance.length === 0) {
605
- gaps.push(taskGap(task.id, 'Acceptance', `Task ${task.id} has no acceptance items.`, 'Add verifiable bullets under #### Acceptance.'));
606
- }
607
- return gaps;
608
- }
609
-
610
- export function isFrontendOnlyTask(task: SddTask): boolean {
611
- return task.affectedFiles.length > 0 && task.affectedFiles.every(isFrontendOnlyPath);
612
- }
613
-
614
- function isFrontendOnlyPath(filePath: string): boolean {
615
- const normalized = filePath.toLowerCase().replace(/\\/g, '/');
616
- return /\.(jsp|html|htm|css|scss|sass|less|js|jsx|ts|tsx|vue|svelte|png|jpg|jpeg|gif|svg|webp|ico)$/.test(normalized)
617
- && !normalized.includes('/src/main/java/')
618
- && !normalized.includes('/src/test/java/')
619
- && !normalized.includes('/pom.xml')
620
- && !normalized.endsWith('pom.xml')
621
- && !normalized.endsWith('build.gradle')
622
- && !normalized.endsWith('build.gradle.kts');
623
- }
624
-
625
- export function isBackendBuildValidationCommand(command: string): boolean {
626
- return /(^|\s)(mvn|maven|mvnw|\.\/mvnw|gradle|gradlew|\.\/gradlew)(\s|$)|\b(maven_compile|gradle_build)\b/i.test(command);
627
- }
628
-
629
- function taskGap(taskId: string, field: string, message: string, recommendation: string): SddTaskGap {
630
- return {
631
- type: 'Task Gap',
632
- severity: 'blocking',
633
- taskId,
634
- field,
635
- message,
636
- recommendation
637
- };
638
- }
639
-
640
- function validateAggregateTaskSet(tasks: SddTask[]): SddTaskGap[] {
641
- const gaps: SddTaskGap[] = [];
642
- const tasksById = new Map<string, SddTask[]>();
643
- for (const task of tasks) {
644
- const matchingTasks = tasksById.get(task.id) ?? [];
645
- matchingTasks.push(task);
646
- tasksById.set(task.id, matchingTasks);
647
- }
648
-
649
- for (const [taskId, matchingTasks] of tasksById) {
650
- if (matchingTasks.length > 1) {
651
- gaps.push(taskGap(
652
- taskId,
653
- 'id',
654
- `Duplicate task id ${taskId} across parsed task files: ${matchingTasks.map(taskSourceEvidence).join('; ')}.`,
655
- 'Rename duplicate task ids or add deterministic source disambiguation before implementation.'
656
- ));
657
- }
658
- }
659
-
660
- for (const task of tasks) {
661
- for (const dependency of task.dependsOn) {
662
- const matchingDependencies = tasksById.get(dependency) ?? [];
663
- if (matchingDependencies.length === 0) {
664
- gaps.push({
665
- type: 'Dependency Gap',
666
- severity: 'blocking',
667
- taskId: task.id,
668
- field: 'depends_on',
669
- message: `Task ${task.id} depends on unknown task ${dependency}.`,
670
- recommendation: 'Fix depends_on to reference an existing task id, or add the missing task.'
671
- });
672
- } else if (matchingDependencies.length > 1) {
673
- gaps.push({
674
- type: 'Dependency Gap',
675
- severity: 'blocking',
676
- taskId: task.id,
677
- field: 'depends_on',
678
- message: `Task ${task.id} depends on ambiguous duplicate task id ${dependency}: ${matchingDependencies.map(taskSourceEvidence).join('; ')}.`,
679
- recommendation: 'Rename duplicate task ids so dependencies resolve to one task.'
680
- });
681
- }
682
- }
683
- }
684
-
685
- return gaps;
686
- }
687
-
688
- function taskSourceEvidence(task: Pick<SddTask, 'id' | 'source'>): string {
689
- return `${task.id} at ${sourceLocationEvidence(task.source)}`;
690
- }
691
-
692
- function sourceLocationEvidence(source: SddTaskSourceLocation): string {
693
- return `${source.filePath}:${source.lineStart}-${source.lineEnd}`;
694
- }
1
+ import { hashDocumentContent, hashSemanticDocument, hashTasksContract, documentHashMatches } from './document-hashes.js';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { assertSafePathSegment } from '../path-safety.js';
5
+ import { exists } from '../storage/json-io.js';
6
+
7
+ export type SddTaskStatus = 'pending' | 'in_progress' | 'completed' | 'blocked' | 'deferred' | 'unknown';
8
+ export type SddChangeSurface = 'frontend_only' | 'backend_only' | 'full_stack' | 'docs_only' | 'config_only' | 'cli_only' | 'unknown';
9
+ export type SddValidationTiming = 'task_end' | 'batch_end' | 'wave_end';
10
+ export type SddGapSeverity = 'blocking' | 'warning';
11
+ export type SddGapType = 'Document Gap' | 'Task Gap' | 'Dependency Gap';
12
+
13
+ export type SddTaskClass = 'implementation' | 'validation';
14
+ export interface SddTaskSourceLocation {
15
+ filePath: string;
16
+ heading: string | null;
17
+ lineStart: number;
18
+ lineEnd: number;
19
+ }
20
+
21
+ export interface SddValidationCommand {
22
+ command: string;
23
+ acceptanceRefs: string[];
24
+ raw: string;
25
+ }
26
+
27
+ export interface SddTask {
28
+ id: string;
29
+ title: string | null;
30
+ status: SddTaskStatus;
31
+ wave: number | null;
32
+ taskClass: SddTaskClass | null;
33
+ unitType: string | null;
34
+ implementationWave: string | null;
35
+ validationBatch: string | null;
36
+ validationTiming: SddValidationTiming;
37
+ requiresVerifyBeforeNext: boolean;
38
+ changeSurface: SddChangeSurface;
39
+ dependsOn: string[];
40
+ validationHandoff: string[];
41
+ affectedFiles: string[];
42
+ validation: string[];
43
+ validationCommands: SddValidationCommand[];
44
+ risk: string[];
45
+ acceptanceRefs: string[];
46
+ planRefs: string[];
47
+ fileOwnership: string[];
48
+ agentFit: string[];
49
+ verificationAvailability: string[];
50
+ autonomy: string | null;
51
+ allowedAgents: string[];
52
+ requiredArtifacts: string[];
53
+ gapState: string | null;
54
+ boundary: string | null;
55
+ acceptance: string[];
56
+ implementationNotes: string | null;
57
+ rawMetadata: Record<string, string | string[]>;
58
+ source: SddTaskSourceLocation;
59
+ }
60
+
61
+ export interface SddTaskGap {
62
+ type: SddGapType;
63
+ severity: SddGapSeverity;
64
+ taskId: string | null;
65
+ field: string;
66
+ message: string;
67
+ recommendation: string;
68
+ }
69
+
70
+ export interface SddTaskModel {
71
+ branch: string;
72
+ specPath: string;
73
+ planPath: string;
74
+ tasksPath: string;
75
+ verifyPath: string;
76
+ documents: {
77
+ specExists: boolean;
78
+ planExists: boolean;
79
+ tasksExists: boolean;
80
+ verifyExists: boolean;
81
+ specHash?: string | null;
82
+ planHash?: string | null;
83
+ tasksHash?: string | null;
84
+ verifyHash?: string | null;
85
+ specDocHash?: string | null;
86
+ planDocHash?: string | null;
87
+ tasksDocHash?: string | null;
88
+ verifyDocHash?: string | null;
89
+ specContractHash?: string | null;
90
+ planContractHash?: string | null;
91
+ tasksContractHash?: string | null;
92
+ verifyContractHash?: string | null;
93
+ planBasedOnSpecHash?: string | null;
94
+ tasksBasedOnPlanHash?: string | null;
95
+ verifyBasedOnTasksHash?: string | null;
96
+ planBasedOnSpecContractHash?: string | null;
97
+ tasksBasedOnPlanContractHash?: string | null;
98
+ verifyBasedOnTasksContractHash?: string | null;
99
+ planStale?: boolean;
100
+ tasksStale?: boolean;
101
+ verifyStale?: boolean;
102
+ };
103
+ tasks: SddTask[];
104
+ gaps: SddTaskGap[];
105
+ }
106
+
107
+ export async function parseSddBranch(projectRoot: string, branch = 'master'): Promise<SddTaskModel> {
108
+ assertSafePathSegment(branch, 'branch');
109
+ const specPath = path.join(projectRoot, 'specs', branch, 'spec.md');
110
+ const planPath = path.join(projectRoot, 'specs', branch, 'plan.md');
111
+ const tasksPath = path.join(projectRoot, 'specs', branch, 'tasks.md');
112
+ const verifyPath = path.join(projectRoot, 'specs', branch, 'verify.md');
113
+ const [specExists, planExists, tasksExists, verifyExists] = await Promise.all([exists(specPath), exists(planPath), exists(tasksPath), exists(verifyPath)]);
114
+ const [rawSpec, rawPlan, rawTasks, rawVerify] = await Promise.all([
115
+ specExists ? readFile(specPath, 'utf8') : Promise.resolve(null),
116
+ planExists ? readFile(planPath, 'utf8') : Promise.resolve(null),
117
+ tasksExists ? readFile(tasksPath, 'utf8') : Promise.resolve(null),
118
+ verifyExists ? readFile(verifyPath, 'utf8') : Promise.resolve(null)
119
+ ]);
120
+ const documents = buildDocumentChainState({ specExists, planExists, tasksExists, verifyExists, rawSpec, rawPlan, rawTasks, rawVerify });
121
+ const gaps: SddTaskGap[] = [];
122
+
123
+ if (documents.planStale) {
124
+ gaps.push(documentGap('plan.md', `Plan document is stale because based_on_spec_hash ${documents.planBasedOnSpecHash} no longer matches current spec hash ${documents.specHash}.`, 'Refresh the plan stage for this partition before updating tasks or executing implementation.'));
125
+ }
126
+ if (documents.tasksStale) {
127
+ gaps.push(documentGap('tasks.md', `Tasks document is stale because based_on_plan_hash ${documents.tasksBasedOnPlanHash} no longer matches current plan hash ${documents.planHash}.`, 'Refresh the tasks stage for this partition before executing implementation.'));
128
+ }
129
+
130
+ if (!specExists) {
131
+ gaps.push(documentGap('spec.md', 'Spec document is missing.', 'Create or restore specs/<branch>/spec.md before full SDD execution.'));
132
+ }
133
+ if (!planExists) {
134
+ gaps.push(documentGap('plan.md', 'Plan document is missing.', 'Create or restore specs/<branch>/plan.md before task execution.'));
135
+ }
136
+ if (!tasksExists || rawTasks === null) {
137
+ gaps.push(documentGap('tasks.md', 'Tasks document is missing.', 'Create specs/<branch>/tasks.md with sdd-task fenced blocks.'));
138
+ return {
139
+ branch,
140
+ specPath,
141
+ planPath,
142
+ tasksPath,
143
+ verifyPath,
144
+ documents,
145
+ tasks: [],
146
+ gaps
147
+ };
148
+ }
149
+
150
+ const taskModel = parseSddTasksMarkdown(rawTasks, { tasksPath });
151
+ if (taskModel.tasks.length === 0 && !path.basename(tasksPath).startsWith('phase')) {
152
+ const retainedModel = await parseRetainedPhaseTasks(path.dirname(tasksPath));
153
+ if (retainedModel.tasks.length > 0) {
154
+ return {
155
+ branch,
156
+ specPath,
157
+ planPath,
158
+ tasksPath,
159
+ verifyPath,
160
+ documents,
161
+ tasks: retainedModel.tasks,
162
+ gaps: [...gaps, ...retainedModel.gaps]
163
+ };
164
+ }
165
+ }
166
+ return {
167
+ branch,
168
+ specPath,
169
+ planPath,
170
+ tasksPath,
171
+ verifyPath,
172
+ documents,
173
+ tasks: taskModel.tasks,
174
+ gaps: [...gaps, ...taskModel.gaps]
175
+ };
176
+ }
177
+
178
+ export function parseSddTasksMarkdown(raw: string, options: { branch?: string; tasksPath?: string; validateDependencies?: boolean } = {}): Pick<SddTaskModel, 'tasks' | 'gaps'> {
179
+ const tasksPath = options.tasksPath ?? 'tasks.md';
180
+ const fencedBlocks = Array.from(raw.matchAll(/^\s*```sdd-task\s*\r?\n([\s\S]*?)\r?^\s*```\s*$/gm));
181
+ const tasks: SddTask[] = [];
182
+ const gaps: SddTaskGap[] = [];
183
+
184
+ if (fencedBlocks.length === 0) {
185
+ gaps.push({
186
+ type: 'Task Gap',
187
+ severity: 'blocking',
188
+ taskId: null,
189
+ field: 'sdd-task',
190
+ message: 'No sdd-task fenced blocks found in tasks.md.',
191
+ recommendation: 'Add one sdd-task fenced block per executable task.'
192
+ });
193
+ return { tasks, gaps };
194
+ }
195
+
196
+ const seenIds = new Map<string, SddTaskSourceLocation>();
197
+ for (let blockIndex = 0; blockIndex < fencedBlocks.length; blockIndex += 1) {
198
+ const blockMatch = fencedBlocks[blockIndex];
199
+ const block = blockMatch[1] ?? '';
200
+ const blockStart = blockMatch.index ?? 0;
201
+ const blockEnd = blockStart + blockMatch[0].length;
202
+ const nextBlockStart = fencedBlocks[blockIndex + 1]?.index ?? raw.length;
203
+ const lineStart = lineNumberAt(raw, blockStart);
204
+ const lineEnd = lineNumberAt(raw, blockEnd);
205
+ const heading = nearestTaskHeading(raw.slice(0, blockStart));
206
+ const metadata = parseSimpleYamlBlock(block);
207
+ const id = scalarValue(metadata.id);
208
+ const taskId = id || heading?.id || null;
209
+ const section = raw.slice(blockEnd, nextTaskStart(raw, blockEnd, nextBlockStart));
210
+ const parsedSections = parseTaskCompanionSections(section);
211
+ if (!taskId) {
212
+ gaps.push({
213
+ type: 'Task Gap',
214
+ severity: 'blocking',
215
+ taskId: null,
216
+ field: 'id',
217
+ message: `sdd-task block starting at line ${lineStart} is missing id.`,
218
+ recommendation: 'Add a stable id field such as id: T1.'
219
+ });
220
+ continue;
221
+ }
222
+
223
+ const source: SddTaskSourceLocation = {
224
+ filePath: tasksPath,
225
+ heading: heading?.raw ?? null,
226
+ lineStart,
227
+ lineEnd
228
+ };
229
+ const priorSource = seenIds.get(taskId);
230
+ if (priorSource) {
231
+ gaps.push({
232
+ type: 'Task Gap',
233
+ severity: 'blocking',
234
+ taskId,
235
+ field: 'id',
236
+ message: `Duplicate task id ${taskId} in ${taskSourceEvidence({ id: taskId, source })} and ${sourceLocationEvidence(priorSource)}.`,
237
+ recommendation: 'Keep task ids unique within a spec branch.'
238
+ });
239
+ }
240
+ seenIds.set(taskId, source);
241
+
242
+ const task: SddTask = {
243
+ id: taskId,
244
+ title: heading?.title ?? null,
245
+ status: parseTaskStatus(scalarValue(metadata.status)),
246
+ wave: parseWave(scalarValue(metadata.wave)),
247
+ taskClass: parseTaskClass(scalarByNames(metadata, ['taskClass', 'task_class'])),
248
+ unitType: scalarByNames(metadata, ['unitType', 'unit_type']),
249
+ implementationWave: scalarValue(metadata.implementation_wave),
250
+ validationBatch: scalarValue(metadata.validation_batch),
251
+ validationTiming: parseValidationTiming(scalarValue(metadata.validation_timing)),
252
+ requiresVerifyBeforeNext: parseRequiresVerifyBeforeNext(scalarValue(metadata.requires_verify_before_next)),
253
+ changeSurface: parseChangeSurface(scalarValue(metadata.change_surface)),
254
+ dependsOn: listByNames(metadata, ['depends_on', 'dependencies', 'dependsOn']),
255
+ validationHandoff: listByNames(metadata, ['validationHandoff', 'validation_handoff', 'validationTasks', 'validation_tasks']),
256
+ affectedFiles: listByNames(metadata, ['affected_files', 'touches', 'affectedFiles']),
257
+ validation: parseValidationCommands(metadata.validation ?? metadata.completionEvidence ?? metadata.completion_evidence).map((command) => command.command),
258
+ validationCommands: parseValidationCommands(metadata.validation ?? metadata.completionEvidence ?? metadata.completion_evidence),
259
+ risk: listValue(metadata.risk),
260
+ acceptanceRefs: listByNames(metadata, ['acceptance_refs', 'sourceAcceptanceCriteria', 'source_acceptance_criteria']),
261
+ planRefs: listByNames(metadata, ['plan_refs', 'planRefs']),
262
+ fileOwnership: listByNames(metadata, ['file_ownership', 'primaryWritableOwnership', 'primary_writable_ownership']),
263
+ agentFit: listValue(metadata.agent_fit),
264
+ verificationAvailability: listValue(metadata.verification_availability),
265
+ autonomy: scalarValue(metadata.autonomy),
266
+ allowedAgents: listValue(metadata.allowed_agents),
267
+ requiredArtifacts: listValue(metadata.required_artifacts),
268
+ gapState: scalarValue(metadata.gap_state),
269
+ boundary: parsedSections.boundary,
270
+ acceptance: parsedSections.acceptance,
271
+ implementationNotes: parsedSections.implementationNotes,
272
+ rawMetadata: metadata,
273
+ source
274
+ };
275
+ tasks.push(task);
276
+ gaps.push(...validateTask(task));
277
+ }
278
+
279
+ if (options.validateDependencies !== false) {
280
+ gaps.push(...validateAggregateTaskSet(tasks));
281
+ }
282
+
283
+ return { tasks, gaps };
284
+ }
285
+
286
+ function buildDocumentChainState(input: { specExists: boolean; planExists: boolean; tasksExists: boolean; verifyExists: boolean; rawSpec: string | null; rawPlan: string | null; rawTasks: string | null; rawVerify: string | null }): SddTaskModel['documents'] {
287
+ const specDocHash = input.rawSpec === null ? null : hashDocumentContent(input.rawSpec);
288
+ const planDocHash = input.rawPlan === null ? null : hashDocumentContent(input.rawPlan);
289
+ const tasksDocHash = input.rawTasks === null ? null : hashDocumentContent(input.rawTasks);
290
+ const verifyDocHash = input.rawVerify === null ? null : hashDocumentContent(input.rawVerify);
291
+ const specContractHash = hashSemanticDocument(input.rawSpec);
292
+ const planContractHash = hashSemanticDocument(input.rawPlan);
293
+ const tasksContractHash = hashTasksContract(input.rawTasks);
294
+ const verifyContractHash = hashSemanticDocument(input.rawVerify);
295
+ const planBasedOnSpecHash = input.rawPlan === null ? null : readDocumentScalar(input.rawPlan, 'based_on_spec_hash');
296
+ const tasksBasedOnPlanHash = input.rawTasks === null ? null : readDocumentScalar(input.rawTasks, 'based_on_plan_hash');
297
+ const verifyBasedOnTasksHash = input.rawVerify === null ? null : readDocumentScalar(input.rawVerify, 'based_on_tasks_hash');
298
+ const planBasedOnSpecContractHash = input.rawPlan === null ? null : readDocumentScalar(input.rawPlan, 'based_on_spec_contract_hash');
299
+ const tasksBasedOnPlanContractHash = input.rawTasks === null ? null : readDocumentScalar(input.rawTasks, 'based_on_plan_contract_hash');
300
+ const verifyBasedOnTasksContractHash = input.rawVerify === null ? null : readDocumentScalar(input.rawVerify, 'based_on_tasks_contract_hash');
301
+
302
+ const planStale = Boolean(
303
+ (planBasedOnSpecContractHash && specContractHash && !documentHashMatches(planBasedOnSpecContractHash, specContractHash))
304
+ || (!planBasedOnSpecContractHash && planBasedOnSpecHash && specDocHash && !documentHashMatches(planBasedOnSpecHash, specDocHash))
305
+ );
306
+ const tasksHashMismatch = Boolean(
307
+ (tasksBasedOnPlanContractHash && planContractHash && !documentHashMatches(tasksBasedOnPlanContractHash, planContractHash))
308
+ || (!tasksBasedOnPlanContractHash && tasksBasedOnPlanHash && planDocHash && !documentHashMatches(tasksBasedOnPlanHash, planDocHash))
309
+ );
310
+ const verifyHashMismatch = false;
311
+
312
+ return {
313
+ specExists: input.specExists,
314
+ planExists: input.planExists,
315
+ tasksExists: input.tasksExists,
316
+ verifyExists: input.verifyExists,
317
+ specHash: specDocHash,
318
+ planHash: planDocHash,
319
+ tasksHash: tasksDocHash,
320
+ verifyHash: verifyDocHash,
321
+ specDocHash,
322
+ planDocHash,
323
+ tasksDocHash,
324
+ verifyDocHash,
325
+ specContractHash,
326
+ planContractHash,
327
+ tasksContractHash,
328
+ verifyContractHash,
329
+ planBasedOnSpecHash,
330
+ tasksBasedOnPlanHash,
331
+ verifyBasedOnTasksHash,
332
+ planBasedOnSpecContractHash,
333
+ tasksBasedOnPlanContractHash,
334
+ verifyBasedOnTasksContractHash,
335
+ planStale,
336
+ tasksStale: planStale || tasksHashMismatch,
337
+ verifyStale: planStale || tasksHashMismatch || verifyHashMismatch
338
+ };
339
+ }
340
+
341
+ function readDocumentScalar(raw: string, key: string): string | null {
342
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
343
+ const match = raw.match(new RegExp(`^\\s*(?:-\\s*)?${escapedKey}:\\s*(.+?)\\s*$`, 'm'));
344
+ return match?.[1]?.trim().replace(/^["'`]|["'`]$/g, '') ?? null;
345
+ }
346
+
347
+ async function parseRetainedPhaseTasks(specBranchDir: string): Promise<Pick<SddTaskModel, 'tasks' | 'gaps'>> {
348
+ const entries = await readdir(specBranchDir, { withFileTypes: true });
349
+ const taskFiles = entries
350
+ .filter((entry) => entry.isFile() && /^phase\d+\.\d+-tasks\.md$/.test(entry.name))
351
+ .map((entry) => path.join(specBranchDir, entry.name))
352
+ .sort();
353
+ const tasks: SddTask[] = [];
354
+ const gaps: SddTaskGap[] = [];
355
+ for (const taskFile of taskFiles) {
356
+ const raw = await readFile(taskFile, 'utf8');
357
+ const parsed = parseSddTasksMarkdown(raw, { tasksPath: taskFile, validateDependencies: false });
358
+ tasks.push(...parsed.tasks);
359
+ gaps.push(...parsed.gaps);
360
+ }
361
+ gaps.push(...validateAggregateTaskSet(tasks));
362
+ return { tasks, gaps };
363
+ }
364
+
365
+ function documentGap(field: string, message: string, recommendation: string): SddTaskGap {
366
+ return {
367
+ type: 'Document Gap',
368
+ severity: 'blocking',
369
+ taskId: null,
370
+ field,
371
+ message,
372
+ recommendation
373
+ };
374
+ }
375
+
376
+ function parseTaskStatus(value: string | null): SddTaskStatus {
377
+ if (value === 'pending' || value === 'in_progress' || value === 'completed' || value === 'blocked' || value === 'deferred') {
378
+ return value;
379
+ }
380
+ return 'unknown';
381
+ }
382
+
383
+ function parseWave(value: string | null): number | null {
384
+ if (!value) {
385
+ return null;
386
+ }
387
+ const parsed = Number(value);
388
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
389
+ }
390
+
391
+ function parseChangeSurface(value: string | null): SddChangeSurface {
392
+ if (value === 'frontend_only' || value === 'backend_only' || value === 'full_stack' || value === 'docs_only' || value === 'config_only' || value === 'cli_only' || value === 'unknown') {
393
+ return value;
394
+ }
395
+ return 'unknown';
396
+ }
397
+ function parseTaskClass(value: string | null): SddTaskClass | null {
398
+ return value === 'implementation' || value === 'validation' ? value : null;
399
+ }
400
+
401
+ function parseValidationTiming(value: string | null): SddValidationTiming {
402
+ if (value === 'batch_end' || value === 'wave_end') {
403
+ return value;
404
+ }
405
+ return 'task_end';
406
+ }
407
+
408
+ function parseRequiresVerifyBeforeNext(value: string | null): boolean {
409
+ return value === 'false' ? false : true;
410
+ }
411
+
412
+ function isValidChangeSurface(value: string | null): boolean {
413
+ return value === null || value === 'frontend_only' || value === 'backend_only' || value === 'full_stack' || value === 'docs_only' || value === 'config_only' || value === 'cli_only' || value === 'unknown';
414
+ }
415
+
416
+ function isValidValidationTiming(value: string | null): boolean {
417
+ return value === null || value === 'task_end' || value === 'batch_end' || value === 'wave_end';
418
+ }
419
+
420
+ function isValidBooleanScalar(value: string | null): boolean {
421
+ return value === null || value === 'true' || value === 'false';
422
+ }
423
+
424
+ function parseSimpleYamlBlock(raw: string): Record<string, string | string[]> {
425
+ const result: Record<string, string | string[]> = {};
426
+ const lines = raw.split(/\r?\n/);
427
+ let currentListKey: string | null = null;
428
+
429
+ for (const line of lines) {
430
+ const trimmed = line.trim();
431
+ if (!trimmed || trimmed.startsWith('#')) {
432
+ continue;
433
+ }
434
+ if (currentListKey && /^-\s+/.test(trimmed)) {
435
+ const current = result[currentListKey];
436
+ const items = Array.isArray(current) ? current : [];
437
+ items.push(unquoteSimpleYamlValue(trimmed.slice(2).trim()));
438
+ result[currentListKey] = items;
439
+ continue;
440
+ }
441
+
442
+ const scalarMatch = trimmed.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
443
+ if (!scalarMatch) {
444
+ currentListKey = null;
445
+ continue;
446
+ }
447
+ const key = scalarMatch[1];
448
+ const value = scalarMatch[2].trim();
449
+ if (value === '') {
450
+ result[key] = [];
451
+ currentListKey = key;
452
+ } else if (value === '[]') {
453
+ result[key] = [];
454
+ currentListKey = null;
455
+ } else if (value.startsWith('[') && value.endsWith(']')) {
456
+ result[key] = value.slice(1, -1).split(',').map((item) => unquoteSimpleYamlValue(item.trim())).filter(Boolean);
457
+ currentListKey = null;
458
+ } else {
459
+ result[key] = unquoteSimpleYamlValue(value);
460
+ currentListKey = null;
461
+ }
462
+ }
463
+
464
+ return result;
465
+ }
466
+
467
+ function unquoteSimpleYamlValue(value: string): string {
468
+ if (value.length >= 2) {
469
+ const first = value[0];
470
+ const last = value[value.length - 1];
471
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
472
+ return value.slice(1, -1);
473
+ }
474
+ }
475
+ return value;
476
+ }
477
+
478
+ function scalarValue(value: string | string[] | undefined): string | null {
479
+ return typeof value === 'string' && value.length > 0 ? value : null;
480
+ }
481
+
482
+ function listValue(value: string | string[] | undefined): string[] {
483
+ if (Array.isArray(value)) {
484
+ return value.filter(Boolean);
485
+ }
486
+ if (!value || value === '[]') {
487
+ return [];
488
+ }
489
+ return [value];
490
+ }
491
+
492
+ function scalarByNames(metadata: Record<string, string | string[]>, names: string[]): string | null {
493
+ for (const name of names) {
494
+ const value = scalarValue(metadata[name]);
495
+ if (value) {
496
+ return value;
497
+ }
498
+ }
499
+ return null;
500
+ }
501
+
502
+ function listByNames(metadata: Record<string, string | string[]>, names: string[]): string[] {
503
+ for (const name of names) {
504
+ const value = listValue(metadata[name]);
505
+ if (value.length > 0) {
506
+ return value;
507
+ }
508
+ }
509
+ return [];
510
+ }
511
+
512
+ function parseValidationCommands(value: string | string[] | undefined): SddValidationCommand[] {
513
+ return listValue(value).map(parseValidationCommand);
514
+ }
515
+
516
+ function parseValidationCommand(raw: string): SddValidationCommand {
517
+ const separator = raw.match(/\s=>\s/);
518
+ if (!separator || separator.index === undefined) {
519
+ return { command: raw, acceptanceRefs: [], raw };
520
+ }
521
+ const command = raw.slice(0, separator.index).trim();
522
+ const refsRaw = raw.slice(separator.index + separator[0].length).trim();
523
+ return {
524
+ command: command || raw,
525
+ acceptanceRefs: parseAcceptanceRefList(refsRaw),
526
+ raw
527
+ };
528
+ }
529
+
530
+ function parseAcceptanceRefList(raw: string): string[] {
531
+ const trimmed = raw.replace(/^\[/, '').replace(/\]$/, '').trim();
532
+ if (!trimmed) {
533
+ return [];
534
+ }
535
+ return trimmed.split(',').map((item) => unquoteSimpleYamlValue(item.trim())).filter(Boolean);
536
+ }
537
+
538
+ function lineNumberAt(raw: string, offset: number): number {
539
+ return raw.slice(0, offset).split(/\r?\n/).length;
540
+ }
541
+
542
+ function nearestTaskHeading(prefix: string): { raw: string; id: string | null; title: string | null } | null {
543
+ const matches = Array.from(prefix.matchAll(/^\s*###\s+(.+)$/gm));
544
+ const last = matches.at(-1);
545
+ if (!last) {
546
+ return null;
547
+ }
548
+ const raw = last[1].trim();
549
+ const parsed = raw.match(/^([^::\s]+)\s*[::]\s*(.+)$/);
550
+ return {
551
+ raw,
552
+ id: parsed?.[1]?.trim() ?? null,
553
+ title: parsed?.[2]?.trim() ?? raw
554
+ };
555
+ }
556
+
557
+ function nextTaskStart(raw: string, offset: number, limit = raw.length): number {
558
+ const next = raw.slice(offset, limit).search(/^\s*###\s+/m);
559
+ return next < 0 ? limit : offset + next;
560
+ }
561
+
562
+ function parseTaskCompanionSections(raw: string): { boundary: string | null; acceptance: string[]; implementationNotes: string | null } {
563
+ return {
564
+ boundary: sectionText(raw, 'Boundary'),
565
+ acceptance: sectionBullets(raw, 'Acceptance'),
566
+ implementationNotes: sectionText(raw, 'Implementation Notes')
567
+ };
568
+ }
569
+
570
+ function sectionText(raw: string, title: string): string | null {
571
+ const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
572
+ const sectionPattern = `^\\s*####\\s+${escaped}\\s*$([\\s\\S]*?)(?=^\\s*####\\s+|^\\s*###\\s+|^\\s*##\\s+|$(?![\\s\\S]))`;
573
+ const match = raw.match(new RegExp(sectionPattern, 'im'));
574
+ const text = match?.[1]?.trim() ?? '';
575
+ return text.length > 0 ? text : null;
576
+ }
577
+
578
+ function sectionBullets(raw: string, title: string): string[] {
579
+ const text = sectionText(raw, title);
580
+ if (!text) {
581
+ return [];
582
+ }
583
+ return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith('- ')).map((line) => line.slice(2).trim()).filter(Boolean);
584
+ }
585
+
586
+ function validateTask(task: SddTask): SddTaskGap[] {
587
+ const gaps: SddTaskGap[] = [];
588
+ const isV3Task = Boolean(scalarValue(task.rawMetadata.workType) || scalarValue(task.rawMetadata.work_type));
589
+
590
+ if (task.status === 'unknown') {
591
+ gaps.push(taskGap(task.id, 'status', 'Task status is missing or unsupported.', 'Use one of pending, in_progress, completed, blocked, deferred.'));
592
+ }
593
+ if (task.wave === null) {
594
+ gaps.push(taskGap(task.id, 'wave', 'Task wave is missing or invalid.', 'Add a positive integer wave value.'));
595
+ }
596
+
597
+ if (isV3Task) {
598
+ const requiredV3Lists: Array<[string[], string]> = [
599
+ [['primaryWritableOwnership', 'primary_writable_ownership'], 'primaryWritableOwnership'],
600
+ [['touches', 'affectedFiles', 'affected_files'], 'touches'],
601
+ [['completionEvidence', 'completion_evidence'], 'completionEvidence']
602
+ ];
603
+ for (const [names, field] of requiredV3Lists) {
604
+ if (listByNames(task.rawMetadata, names).length === 0) {
605
+ gaps.push(taskGap(task.id, field, `Task ${task.id} has no ${field}.`, `Declare ${field} in the sdd-task block before execute.`));
606
+ }
607
+ }
608
+ if (!scalarByNames(task.rawMetadata, ['executionLane', 'execution_lane'])) {
609
+ gaps.push(taskGap(task.id, 'executionLane', `Task ${task.id} has no executionLane.`, 'Declare executionLane in the sdd-task block before execute.'));
610
+ }
611
+ if (!scalarByNames(task.rawMetadata, ['failureRoute', 'failure_route'])) {
612
+ gaps.push(taskGap(task.id, 'failureRoute', `Task ${task.id} has no failureRoute.`, 'Declare failureRoute in the sdd-task block before execute.'));
613
+ }
614
+ return gaps;
615
+ }
616
+
617
+ const requiredLists: Array<[keyof SddTask, string]> = [
618
+ ['affectedFiles', 'affected_files'],
619
+ ['validation', 'validation']
620
+ ];
621
+
622
+ if (!isValidChangeSurface(scalarValue(task.rawMetadata.change_surface))) {
623
+ gaps.push(taskGap(task.id, 'change_surface', `Task ${task.id} has invalid change_surface.`, 'Use one of frontend_only, backend_only, full_stack, docs_only, config_only, cli_only, unknown.'));
624
+ }
625
+ if (!isValidValidationTiming(scalarValue(task.rawMetadata.validation_timing))) {
626
+ gaps.push(taskGap(task.id, 'validation_timing', `Task ${task.id} has invalid validation_timing.`, 'Use one of task_end, batch_end, wave_end.'));
627
+ }
628
+ if (!isValidBooleanScalar(scalarValue(task.rawMetadata.requires_verify_before_next))) {
629
+ gaps.push(taskGap(task.id, 'requires_verify_before_next', `Task ${task.id} has invalid requires_verify_before_next.`, 'Use true or false.'));
630
+ }
631
+ if ((task.validationTiming === 'batch_end' || task.validationTiming === 'wave_end') && !task.validationBatch) {
632
+ gaps.push(taskGap(task.id, 'validation_batch', `Task ${task.id} uses ${task.validationTiming} but has no validation_batch.`, 'Declare validation_batch so batch/wave validation can group implementation evidence before /sdd:test.'));
633
+ }
634
+ if ((task.validationTiming === 'batch_end' || task.validationTiming === 'wave_end') && task.requiresVerifyBeforeNext) {
635
+ gaps.push(taskGap(task.id, 'requires_verify_before_next', `Task ${task.id} uses ${task.validationTiming} but requires verify before next.`, 'Set requires_verify_before_next: false for batch/wave validation, or use validation_timing: task_end for strict validation.'));
636
+ }
637
+ for (const [property, field] of requiredLists) {
638
+ if ((task[property] as unknown[]).length === 0) {
639
+ gaps.push(taskGap(task.id, field, `Task ${task.id} has no ${field}.`, `Declare ${field} in the sdd-task block before implementation.`));
640
+ }
641
+ }
642
+ if ((task.changeSurface === 'frontend_only' || isFrontendOnlyTask(task)) && task.validation.some(isBackendBuildValidationCommand)) {
643
+ gaps.push(taskGap(task.id, 'validation', `Task ${task.id} is frontend-only but declares backend build validation.`, 'For JSP/static frontend-only changes, replace Maven/Gradle validation with frontend-appropriate checks, dev-server/manual page evidence, lint/typecheck, or an explicit unavailable reason.'));
644
+ }
645
+ if (!task.boundary) {
646
+ gaps.push(taskGap(task.id, 'Boundary', `Task ${task.id} has no Boundary section.`, 'Add a #### Boundary section describing allowed and forbidden scope.'));
647
+ }
648
+ if (task.acceptance.length === 0) {
649
+ gaps.push(taskGap(task.id, 'Acceptance', `Task ${task.id} has no acceptance items.`, 'Add verifiable bullets under #### Acceptance.'));
650
+ }
651
+ return gaps;
652
+ }
653
+
654
+ export function isFrontendOnlyTask(task: SddTask): boolean {
655
+ return task.affectedFiles.length > 0 && task.affectedFiles.every(isFrontendOnlyPath);
656
+ }
657
+
658
+ function isFrontendOnlyPath(filePath: string): boolean {
659
+ const normalized = filePath.toLowerCase().replace(/\\/g, '/');
660
+ return /\.(jsp|html|htm|css|scss|sass|less|js|jsx|ts|tsx|vue|svelte|png|jpg|jpeg|gif|svg|webp|ico)$/.test(normalized)
661
+ && !normalized.includes('/src/main/java/')
662
+ && !normalized.includes('/src/test/java/')
663
+ && !normalized.includes('/pom.xml')
664
+ && !normalized.endsWith('pom.xml')
665
+ && !normalized.endsWith('build.gradle')
666
+ && !normalized.endsWith('build.gradle.kts');
667
+ }
668
+
669
+ export function isBackendBuildValidationCommand(command: string): boolean {
670
+ return /(^|\s)(mvn|maven|mvnw|\.\/mvnw|gradle|gradlew|\.\/gradlew)(\s|$)|\b(maven_compile|gradle_build)\b/i.test(command);
671
+ }
672
+
673
+ function taskGap(taskId: string, field: string, message: string, recommendation: string): SddTaskGap {
674
+ return {
675
+ type: 'Task Gap',
676
+ severity: 'blocking',
677
+ taskId,
678
+ field,
679
+ message,
680
+ recommendation
681
+ };
682
+ }
683
+
684
+ function validateAggregateTaskSet(tasks: SddTask[]): SddTaskGap[] {
685
+ const gaps: SddTaskGap[] = [];
686
+ const tasksById = new Map<string, SddTask[]>();
687
+ for (const task of tasks) {
688
+ const matchingTasks = tasksById.get(task.id) ?? [];
689
+ matchingTasks.push(task);
690
+ tasksById.set(task.id, matchingTasks);
691
+ }
692
+
693
+ for (const [taskId, matchingTasks] of tasksById) {
694
+ if (matchingTasks.length > 1) {
695
+ gaps.push(taskGap(
696
+ taskId,
697
+ 'id',
698
+ `Duplicate task id ${taskId} across parsed task files: ${matchingTasks.map(taskSourceEvidence).join('; ')}.`,
699
+ 'Rename duplicate task ids or add deterministic source disambiguation before implementation.'
700
+ ));
701
+ }
702
+ }
703
+
704
+ for (const task of tasks) {
705
+ for (const dependency of task.dependsOn) {
706
+ const matchingDependencies = tasksById.get(dependency) ?? [];
707
+ if (matchingDependencies.length === 0) {
708
+ gaps.push({
709
+ type: 'Dependency Gap',
710
+ severity: 'blocking',
711
+ taskId: task.id,
712
+ field: 'depends_on',
713
+ message: `Task ${task.id} depends on unknown task ${dependency}.`,
714
+ recommendation: 'Fix depends_on to reference an existing task id, or add the missing task.'
715
+ });
716
+ } else if (matchingDependencies.length > 1) {
717
+ gaps.push({
718
+ type: 'Dependency Gap',
719
+ severity: 'blocking',
720
+ taskId: task.id,
721
+ field: 'depends_on',
722
+ message: `Task ${task.id} depends on ambiguous duplicate task id ${dependency}: ${matchingDependencies.map(taskSourceEvidence).join('; ')}.`,
723
+ recommendation: 'Rename duplicate task ids so dependencies resolve to one task.'
724
+ });
725
+ }
726
+ }
727
+ }
728
+
729
+ return gaps;
730
+ }
731
+
732
+ function taskSourceEvidence(task: Pick<SddTask, 'id' | 'source'>): string {
733
+ return `${task.id} at ${sourceLocationEvidence(task.source)}`;
734
+ }
735
+
736
+ function sourceLocationEvidence(source: SddTaskSourceLocation): string {
737
+ return `${source.filePath}:${source.lineStart}-${source.lineEnd}`;
738
+ }