sdd-agent-platform 0.4.0 → 0.4.2

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 (801) hide show
  1. package/README.md +36 -39
  2. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +72 -71
  3. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js.map +1 -1
  4. package/node_modules/@sdd-agent-platform/core/dist/artifacts/ingestion.js +64 -9
  5. package/node_modules/@sdd-agent-platform/core/dist/artifacts/ingestion.js.map +1 -1
  6. package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-evidence.js +0 -1
  7. package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-evidence.js.map +1 -1
  8. package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-result.js +26 -17
  9. package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-result.js.map +1 -1
  10. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.d.ts +3 -0
  11. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js +13 -9
  12. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js.map +1 -1
  13. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.d.ts +3 -1
  14. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js +7 -3
  15. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js.map +1 -1
  16. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.d.ts +4 -4
  17. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js +16 -20
  18. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js.map +1 -1
  19. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.d.ts +1 -1
  20. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js +1 -7
  21. package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js.map +1 -1
  22. package/node_modules/@sdd-agent-platform/core/dist/context/evidence-summary.js +26 -8
  23. package/node_modules/@sdd-agent-platform/core/dist/context/evidence-summary.js.map +1 -1
  24. package/node_modules/@sdd-agent-platform/core/dist/context/log-worker.js +2 -2
  25. package/node_modules/@sdd-agent-platform/core/dist/context/log-worker.js.map +1 -1
  26. package/node_modules/@sdd-agent-platform/core/dist/context-offload/contracts.d.ts +1 -1
  27. package/node_modules/@sdd-agent-platform/core/dist/contracts.d.ts +4 -1
  28. package/node_modules/@sdd-agent-platform/core/dist/contracts.js +3 -0
  29. package/node_modules/@sdd-agent-platform/core/dist/contracts.js.map +1 -1
  30. package/node_modules/@sdd-agent-platform/core/dist/delegation/model.d.ts +3 -0
  31. package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.d.ts +3 -0
  32. package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.js +7 -4
  33. package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.js.map +1 -1
  34. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js +1 -1
  35. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js.map +1 -1
  36. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js +8 -8
  37. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js.map +1 -1
  38. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js +0 -1
  39. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js.map +1 -1
  40. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js +7 -7
  41. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js.map +1 -1
  42. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js +0 -24
  43. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js.map +1 -1
  44. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js +1 -1
  45. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  46. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +277 -3
  47. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
  48. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +23 -0
  49. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +61 -0
  50. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -0
  51. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/contracts.d.ts +11 -1
  52. package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js +15 -8
  53. package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js.map +1 -1
  54. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js +4 -4
  55. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js.map +1 -1
  56. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js +3 -3
  57. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js.map +1 -1
  58. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +5 -4
  59. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js.map +1 -1
  60. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +16 -7
  61. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
  62. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.d.ts +112 -0
  63. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +145 -0
  64. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -0
  65. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.d.ts +1 -1
  66. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js +1 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js.map +1 -1
  68. package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +59 -66
  70. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  71. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js +1 -1
  72. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js.map +1 -1
  73. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +3 -0
  74. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +55 -19
  75. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  76. package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
  77. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.d.ts +12 -2
  78. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +62 -21
  79. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
  80. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +20 -2
  81. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +218 -18
  82. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
  83. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +17 -17
  84. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  85. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.d.ts +10 -0
  86. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +32 -2
  87. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
  88. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.d.ts +2 -17
  89. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +222 -10
  90. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js.map +1 -1
  91. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
  92. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +9 -9
  93. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
  94. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js +4 -4
  95. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js.map +1 -1
  96. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js +2 -2
  97. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js.map +1 -1
  98. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +3 -3
  99. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
  100. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js +2 -2
  101. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js.map +1 -1
  102. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js +11 -11
  103. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js.map +1 -1
  104. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +12 -12
  105. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  106. package/node_modules/@sdd-agent-platform/core/dist/risk/contracts.d.ts +2 -2
  107. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +4 -4
  108. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
  109. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +4 -7
  110. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
  111. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.d.ts +2 -2
  112. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +19 -17
  113. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
  114. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js +28 -13
  115. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js.map +1 -1
  116. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +61 -1
  117. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.d.ts +3 -1
  118. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +192 -1
  119. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
  120. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +73 -17
  121. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  122. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.d.ts +28 -0
  123. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +373 -0
  124. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -0
  125. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js +11 -4
  126. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js.map +1 -1
  127. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +31 -3
  128. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
  129. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +37 -0
  130. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +235 -0
  131. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -0
  132. package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +2 -0
  133. package/node_modules/@sdd-agent-platform/core/dist/router.js +2 -0
  134. package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
  135. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +16 -0
  136. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +168 -18
  137. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  138. package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js +2 -2
  139. package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js.map +1 -1
  140. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +3 -3
  141. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +22 -54
  142. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
  143. package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js +2 -2
  144. package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js.map +1 -1
  145. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +53 -9
  146. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.d.ts +0 -2
  147. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js +1 -3
  148. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js.map +1 -1
  149. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +51 -34
  150. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  151. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +65 -0
  152. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +169 -0
  153. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -0
  154. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.d.ts +8 -0
  155. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js +131 -0
  156. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js.map +1 -0
  157. package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +2 -0
  158. package/node_modules/@sdd-agent-platform/core/dist/run-state.js +2 -0
  159. package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
  160. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +0 -3
  161. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
  162. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +5 -44
  163. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
  164. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/model.d.ts +1 -17
  165. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +20 -0
  166. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +109 -14
  167. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  168. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +64 -0
  169. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +200 -0
  170. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -0
  171. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js +1 -1
  172. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js.map +1 -1
  173. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +6 -0
  174. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +276 -0
  175. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -0
  176. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.d.ts +1 -1
  177. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +15 -4
  178. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
  179. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +21 -0
  180. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +139 -38
  181. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  182. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +55 -0
  183. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +322 -0
  184. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -0
  185. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +55 -0
  186. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +241 -0
  187. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -0
  188. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +888 -0
  189. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +3870 -0
  190. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -0
  191. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +8 -1
  192. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
  193. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +105 -1
  194. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +343 -8
  195. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  196. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.d.ts +348 -3
  197. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +1017 -8
  198. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js.map +1 -1
  199. package/node_modules/@sdd-agent-platform/core/dist/subagents/contracts.d.ts +1 -1
  200. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js +7 -7
  201. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js.map +1 -1
  202. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +21 -0
  203. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
  204. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -1
  205. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +19 -20
  206. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  207. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.d.ts +44 -0
  208. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +138 -0
  209. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -0
  210. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  211. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -1
  212. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +44 -37
  213. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  214. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +0 -2
  215. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +19 -49
  216. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  217. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.d.ts +22 -0
  218. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js +53 -0
  219. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js.map +1 -0
  220. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.d.ts +0 -1
  221. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +213 -111
  222. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  223. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.d.ts +28 -3
  224. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +546 -125
  225. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  226. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.d.ts +26 -0
  227. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js +73 -0
  228. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js.map +1 -0
  229. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +76 -0
  230. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +450 -0
  231. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -0
  232. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.d.ts +3 -1
  233. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +105 -30
  234. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  235. package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +2 -0
  236. package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -0
  237. package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
  238. package/node_modules/@sdd-agent-platform/core/dist/work-units/contracts.d.ts +1 -1
  239. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
  240. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +395 -0
  241. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -0
  242. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.d.ts +4 -0
  243. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +164 -0
  244. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -0
  245. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.d.ts +4 -0
  246. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +182 -0
  247. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -0
  248. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +88 -0
  249. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js +2 -0
  250. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js.map +1 -0
  251. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -1
  252. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +17 -3
  253. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  254. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.d.ts +8 -4
  255. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +25 -11
  256. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js.map +1 -1
  257. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +37 -0
  258. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +188 -0
  259. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
  260. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  261. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js +110 -0
  262. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js.map +1 -0
  263. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.d.ts +12 -0
  264. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js +63 -0
  265. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js.map +1 -0
  266. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  267. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js +95 -0
  268. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  269. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +80 -3
  270. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +674 -41
  271. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  272. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  273. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js +452 -0
  274. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js.map +1 -0
  275. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.d.ts +1 -0
  276. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js +1 -0
  277. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js.map +1 -1
  278. package/node_modules/@sdd-agent-platform/core/package.json +3 -3
  279. package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +49 -1
  280. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +72 -71
  281. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +38 -0
  282. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +65 -9
  283. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +0 -1
  284. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +52 -6
  285. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +26 -17
  286. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +44 -29
  287. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +15 -11
  288. package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +10 -4
  289. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +17 -20
  290. package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +2 -8
  291. package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +3 -2
  292. package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +27 -8
  293. package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +2 -2
  294. package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +1 -1
  295. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +4 -1
  296. package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +3 -0
  297. package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +8 -5
  298. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +1 -1
  299. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +8 -8
  300. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +0 -1
  301. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +7 -7
  302. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +0 -21
  303. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +1 -1
  304. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +217 -7
  305. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +301 -3
  306. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +88 -0
  307. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +12 -1
  308. package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +16 -11
  309. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +57 -2
  310. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +4 -4
  311. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +11 -2
  312. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +3 -3
  313. package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +5 -4
  314. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +17 -1
  315. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +16 -7
  316. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -0
  317. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -0
  318. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +14 -0
  319. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +10 -0
  320. package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +2 -2
  321. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +34 -13
  322. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +60 -67
  323. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +1 -1
  324. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +1 -1
  325. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -0
  326. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +58 -19
  327. package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +1 -1
  328. package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +74 -22
  329. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +3 -3
  330. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +8 -3
  331. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +2 -0
  332. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +3 -0
  333. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +319 -20
  334. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +17 -17
  335. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +42 -2
  336. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +238 -15
  337. package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +10 -10
  338. package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +4 -4
  339. package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +2 -2
  340. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +45 -4
  341. package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +3 -3
  342. package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +2 -2
  343. package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +11 -11
  344. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +12 -12
  345. package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +2 -2
  346. package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +4 -4
  347. package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +4 -7
  348. package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +20 -18
  349. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +32 -13
  350. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +68 -1
  351. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +212 -1
  352. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +391 -6
  353. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +78 -17
  354. package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +453 -0
  355. package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +11 -4
  356. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +32 -3
  357. package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +279 -0
  358. package/node_modules/@sdd-agent-platform/core/src/router.ts +2 -0
  359. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +173 -18
  360. package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +2 -2
  361. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +24 -59
  362. package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +2 -2
  363. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +59 -9
  364. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +1 -5
  365. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +53 -2
  366. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +55 -41
  367. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +252 -0
  368. package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -0
  369. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +2 -0
  370. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +0 -3
  371. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +6 -46
  372. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +1 -13
  373. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +0 -2
  374. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +131 -14
  375. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +96 -0
  376. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +292 -0
  377. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +1 -1
  378. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +306 -0
  379. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +15 -4
  380. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +261 -0
  381. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +169 -41
  382. package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +450 -0
  383. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +322 -0
  384. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2903 -0
  385. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5831 -0
  386. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +1 -1
  387. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +9 -1
  388. package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +239 -16
  389. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +497 -8
  390. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +560 -4
  391. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +1510 -9
  392. package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +1 -1
  393. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +3 -3
  394. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +7 -7
  395. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +21 -0
  396. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +20 -20
  397. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -0
  398. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +177 -0
  399. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.test.ts +13 -87
  400. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.ts +46 -42
  401. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +18 -52
  402. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +84 -0
  403. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -0
  404. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +138 -64
  405. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +226 -116
  406. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +148 -48
  407. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +619 -136
  408. package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -0
  409. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +383 -0
  410. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +556 -0
  411. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +131 -8
  412. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +117 -30
  413. package/node_modules/@sdd-agent-platform/core/src/verification.ts +2 -0
  414. package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +1 -1
  415. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +425 -0
  416. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +507 -0
  417. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +182 -0
  418. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +174 -0
  419. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +194 -0
  420. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +115 -0
  421. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +19 -4
  422. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +1 -1
  423. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +33 -11
  424. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +224 -0
  425. package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -0
  426. package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -0
  427. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -0
  428. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +851 -9
  429. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +862 -45
  430. package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -0
  431. package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +1 -0
  432. package/package.json +1 -1
  433. package/packages/cli/dist/args.js +2 -2
  434. package/packages/cli/dist/args.js.map +1 -1
  435. package/packages/cli/dist/commands/ai-tools.js +13 -2
  436. package/packages/cli/dist/commands/ai-tools.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/execution.js +49 -1
  440. package/packages/cli/dist/commands/execution.js.map +1 -1
  441. package/packages/cli/dist/commands/governance.js +1 -1
  442. package/packages/cli/dist/commands/governance.js.map +1 -1
  443. package/packages/cli/dist/commands/init.js +6 -1
  444. package/packages/cli/dist/commands/init.js.map +1 -1
  445. package/packages/cli/dist/commands/lifecycle.js +15 -2
  446. package/packages/cli/dist/commands/lifecycle.js.map +1 -1
  447. package/packages/cli/dist/commands/registry/runtime.js +48 -2
  448. package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
  449. package/packages/cli/dist/commands/run.js +52 -2
  450. package/packages/cli/dist/commands/run.js.map +1 -1
  451. package/packages/cli/dist/commands/stage-close.d.ts +6 -0
  452. package/packages/cli/dist/commands/stage-close.js +295 -0
  453. package/packages/cli/dist/commands/stage-close.js.map +1 -0
  454. package/packages/cli/dist/commands/status.js +70 -4
  455. package/packages/cli/dist/commands/status.js.map +1 -1
  456. package/packages/cli/dist/commands/tasks.js +4 -4
  457. package/packages/cli/dist/commands/tasks.js.map +1 -1
  458. package/packages/cli/dist/commands/test.js +272 -5
  459. package/packages/cli/dist/commands/test.js.map +1 -1
  460. package/packages/cli/dist/commands/verifies.js +9 -5
  461. package/packages/cli/dist/commands/verifies.js.map +1 -1
  462. package/packages/cli/dist/commands/verify.js +257 -20
  463. package/packages/cli/dist/commands/verify.js.map +1 -1
  464. package/packages/cli/dist/dispatch.js +4 -9
  465. package/packages/cli/dist/dispatch.js.map +1 -1
  466. package/packages/cli/dist/help.js +42 -27
  467. package/packages/cli/dist/help.js.map +1 -1
  468. package/packages/cli/dist/renderers/doctor.js +1 -1
  469. package/packages/cli/dist/renderers/doctor.js.map +1 -1
  470. package/packages/cli/dist/renderers/execution.js +1 -1
  471. package/packages/cli/dist/renderers/execution.js.map +1 -1
  472. package/packages/cli/dist/renderers/json.d.ts +1 -0
  473. package/packages/cli/dist/renderers/json.js +3 -0
  474. package/packages/cli/dist/renderers/json.js.map +1 -1
  475. package/packages/cli/dist/renderers/registry-runtime.d.ts +2 -1
  476. package/packages/cli/dist/renderers/registry-runtime.js +27 -2
  477. package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
  478. package/packages/cli/dist/renderers/router.js +5 -3
  479. package/packages/cli/dist/renderers/router.js.map +1 -1
  480. package/packages/cli/dist/renderers/workflow.d.ts +0 -4
  481. package/packages/cli/dist/renderers/workflow.js +46 -84
  482. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  483. package/packages/cli/dist/skill-import-args.d.ts +10 -0
  484. package/packages/cli/dist/skill-import-args.js +47 -0
  485. package/packages/cli/dist/skill-import-args.js.map +1 -0
  486. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  487. package/packages/cli/package.json +2 -2
  488. package/packages/core/dist/ai-tools.js +72 -71
  489. package/packages/core/dist/ai-tools.js.map +1 -1
  490. package/packages/core/dist/artifacts/ingestion.js +64 -9
  491. package/packages/core/dist/artifacts/ingestion.js.map +1 -1
  492. package/packages/core/dist/artifacts/sdd-evidence.js +0 -1
  493. package/packages/core/dist/artifacts/sdd-evidence.js.map +1 -1
  494. package/packages/core/dist/artifacts/sdd-result.js +26 -17
  495. package/packages/core/dist/artifacts/sdd-result.js.map +1 -1
  496. package/packages/core/dist/config/init-project.d.ts +3 -0
  497. package/packages/core/dist/config/init-project.js +13 -9
  498. package/packages/core/dist/config/init-project.js.map +1 -1
  499. package/packages/core/dist/config/project-config.d.ts +3 -1
  500. package/packages/core/dist/config/project-config.js +7 -3
  501. package/packages/core/dist/config/project-config.js.map +1 -1
  502. package/packages/core/dist/config/starter-documents.d.ts +4 -4
  503. package/packages/core/dist/config/starter-documents.js +16 -20
  504. package/packages/core/dist/config/starter-documents.js.map +1 -1
  505. package/packages/core/dist/context/build-package.d.ts +1 -1
  506. package/packages/core/dist/context/build-package.js +1 -7
  507. package/packages/core/dist/context/build-package.js.map +1 -1
  508. package/packages/core/dist/context/evidence-summary.js +26 -8
  509. package/packages/core/dist/context/evidence-summary.js.map +1 -1
  510. package/packages/core/dist/context/log-worker.js +2 -2
  511. package/packages/core/dist/context/log-worker.js.map +1 -1
  512. package/packages/core/dist/context-offload/contracts.d.ts +1 -1
  513. package/packages/core/dist/contracts.d.ts +4 -1
  514. package/packages/core/dist/contracts.js +3 -0
  515. package/packages/core/dist/contracts.js.map +1 -1
  516. package/packages/core/dist/delegation/model.d.ts +3 -0
  517. package/packages/core/dist/delegation/validation.d.ts +3 -0
  518. package/packages/core/dist/delegation/validation.js +7 -4
  519. package/packages/core/dist/delegation/validation.js.map +1 -1
  520. package/packages/core/dist/doctor/checks/document-chain.js +1 -1
  521. package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
  522. package/packages/core/dist/doctor/checks/project.js +8 -8
  523. package/packages/core/dist/doctor/checks/project.js.map +1 -1
  524. package/packages/core/dist/doctor/checks/registries.js +0 -1
  525. package/packages/core/dist/doctor/checks/registries.js.map +1 -1
  526. package/packages/core/dist/doctor/checks/run-evidence.js +7 -7
  527. package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
  528. package/packages/core/dist/doctor/checks/run-trust.js +0 -24
  529. package/packages/core/dist/doctor/checks/run-trust.js.map +1 -1
  530. package/packages/core/dist/doctor/checks/runtime-contracts.js +1 -1
  531. package/packages/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  532. package/packages/core/dist/doctor/doctor.js +277 -3
  533. package/packages/core/dist/doctor/doctor.js.map +1 -1
  534. package/packages/core/dist/evidence/lookup.d.ts +23 -0
  535. package/packages/core/dist/evidence/lookup.js +61 -0
  536. package/packages/core/dist/evidence/lookup.js.map +1 -0
  537. package/packages/core/dist/evidence-runtime/contracts.d.ts +11 -1
  538. package/packages/core/dist/execution/agent-execution-records.js +15 -8
  539. package/packages/core/dist/execution/agent-execution-records.js.map +1 -1
  540. package/packages/core/dist/execution/background-executor.js +4 -4
  541. package/packages/core/dist/execution/background-executor.js.map +1 -1
  542. package/packages/core/dist/execution/foreground-subagents.js +3 -3
  543. package/packages/core/dist/execution/foreground-subagents.js.map +1 -1
  544. package/packages/core/dist/execution/host-invocation.js +5 -4
  545. package/packages/core/dist/execution/host-invocation.js.map +1 -1
  546. package/packages/core/dist/execution/resident-worker.js +16 -7
  547. package/packages/core/dist/execution/resident-worker.js.map +1 -1
  548. package/packages/core/dist/execution/stage-team-runtime.d.ts +112 -0
  549. package/packages/core/dist/execution/stage-team-runtime.js +145 -0
  550. package/packages/core/dist/execution/stage-team-runtime.js.map +1 -0
  551. package/packages/core/dist/governance/policy.d.ts +1 -1
  552. package/packages/core/dist/governance/policy.js +1 -1
  553. package/packages/core/dist/governance/policy.js.map +1 -1
  554. package/packages/core/dist/instructions.d.ts +1 -1
  555. package/packages/core/dist/instructions.js +59 -66
  556. package/packages/core/dist/instructions.js.map +1 -1
  557. package/packages/core/dist/lifecycle/decision-gate.js +1 -1
  558. package/packages/core/dist/lifecycle/decision-gate.js.map +1 -1
  559. package/packages/core/dist/lifecycle/ship.d.ts +3 -0
  560. package/packages/core/dist/lifecycle/ship.js +55 -19
  561. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  562. package/packages/core/dist/orchestration/contracts.d.ts +1 -1
  563. package/packages/core/dist/orchestration/runtime.d.ts +12 -2
  564. package/packages/core/dist/orchestration/runtime.js +62 -21
  565. package/packages/core/dist/orchestration/runtime.js.map +1 -1
  566. package/packages/core/dist/registries/agent-capability-catalog.d.ts +20 -2
  567. package/packages/core/dist/registries/agent-capability-catalog.js +218 -18
  568. package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
  569. package/packages/core/dist/registries/agent-registry.js +17 -17
  570. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  571. package/packages/core/dist/registries/agent-runtime-static.d.ts +10 -0
  572. package/packages/core/dist/registries/agent-runtime-static.js +32 -2
  573. package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
  574. package/packages/core/dist/registries/capability-sources.d.ts +2 -17
  575. package/packages/core/dist/registries/capability-sources.js +222 -10
  576. package/packages/core/dist/registries/capability-sources.js.map +1 -1
  577. package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
  578. package/packages/core/dist/registries/command-team-runtime.js +9 -9
  579. package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
  580. package/packages/core/dist/registries/eval-learning-context.js +4 -4
  581. package/packages/core/dist/registries/eval-learning-context.js.map +1 -1
  582. package/packages/core/dist/registries/query-status.js +2 -2
  583. package/packages/core/dist/registries/query-status.js.map +1 -1
  584. package/packages/core/dist/registries/tool-capabilities.js +3 -3
  585. package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
  586. package/packages/core/dist/registries/tool-plugins.js +2 -2
  587. package/packages/core/dist/registries/tool-plugins.js.map +1 -1
  588. package/packages/core/dist/registries/worker-adapters.js +11 -11
  589. package/packages/core/dist/registries/worker-adapters.js.map +1 -1
  590. package/packages/core/dist/registries/workflow-gates.js +12 -12
  591. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  592. package/packages/core/dist/risk/contracts.d.ts +2 -2
  593. package/packages/core/dist/risk/kernel.js +4 -4
  594. package/packages/core/dist/risk/kernel.js.map +1 -1
  595. package/packages/core/dist/risk/legacy-adapters.js +4 -7
  596. package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
  597. package/packages/core/dist/risk/workflow-gates.d.ts +2 -2
  598. package/packages/core/dist/risk/workflow-gates.js +19 -17
  599. package/packages/core/dist/risk/workflow-gates.js.map +1 -1
  600. package/packages/core/dist/router/agent-runtime-config.js +28 -13
  601. package/packages/core/dist/router/agent-runtime-config.js.map +1 -1
  602. package/packages/core/dist/router/agent-runtime.d.ts +61 -1
  603. package/packages/core/dist/router/route-projection.d.ts +3 -1
  604. package/packages/core/dist/router/route-projection.js +192 -1
  605. package/packages/core/dist/router/route-projection.js.map +1 -1
  606. package/packages/core/dist/router/routing.js +73 -17
  607. package/packages/core/dist/router/routing.js.map +1 -1
  608. package/packages/core/dist/router/runtime-import.d.ts +28 -0
  609. package/packages/core/dist/router/runtime-import.js +373 -0
  610. package/packages/core/dist/router/runtime-import.js.map +1 -0
  611. package/packages/core/dist/router/runtime-inspection.js +11 -4
  612. package/packages/core/dist/router/runtime-inspection.js.map +1 -1
  613. package/packages/core/dist/router/runtime-validation.js +31 -3
  614. package/packages/core/dist/router/runtime-validation.js.map +1 -1
  615. package/packages/core/dist/router/stage-route-binding.d.ts +37 -0
  616. package/packages/core/dist/router/stage-route-binding.js +235 -0
  617. package/packages/core/dist/router/stage-route-binding.js.map +1 -0
  618. package/packages/core/dist/router.d.ts +2 -0
  619. package/packages/core/dist/router.js +2 -0
  620. package/packages/core/dist/router.js.map +1 -1
  621. package/packages/core/dist/run-state/artifacts.d.ts +16 -0
  622. package/packages/core/dist/run-state/artifacts.js +168 -18
  623. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  624. package/packages/core/dist/run-state/events.js +2 -2
  625. package/packages/core/dist/run-state/events.js.map +1 -1
  626. package/packages/core/dist/run-state/inspect-run.d.ts +3 -3
  627. package/packages/core/dist/run-state/inspect-run.js +22 -54
  628. package/packages/core/dist/run-state/inspect-run.js.map +1 -1
  629. package/packages/core/dist/run-state/invocation-ledger.js +2 -2
  630. package/packages/core/dist/run-state/invocation-ledger.js.map +1 -1
  631. package/packages/core/dist/run-state/model.d.ts +53 -9
  632. package/packages/core/dist/run-state/run-index.d.ts +0 -2
  633. package/packages/core/dist/run-state/run-index.js +1 -3
  634. package/packages/core/dist/run-state/run-index.js.map +1 -1
  635. package/packages/core/dist/run-state/run-state.js +51 -34
  636. package/packages/core/dist/run-state/run-state.js.map +1 -1
  637. package/packages/core/dist/run-state/task-evidence.d.ts +65 -0
  638. package/packages/core/dist/run-state/task-evidence.js +169 -0
  639. package/packages/core/dist/run-state/task-evidence.js.map +1 -0
  640. package/packages/core/dist/run-state/timing.d.ts +8 -0
  641. package/packages/core/dist/run-state/timing.js +131 -0
  642. package/packages/core/dist/run-state/timing.js.map +1 -0
  643. package/packages/core/dist/run-state.d.ts +2 -0
  644. package/packages/core/dist/run-state.js +2 -0
  645. package/packages/core/dist/run-state.js.map +1 -1
  646. package/packages/core/dist/runtime-analysis/build.js +0 -3
  647. package/packages/core/dist/runtime-analysis/build.js.map +1 -1
  648. package/packages/core/dist/runtime-analysis/findings.js +5 -44
  649. package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
  650. package/packages/core/dist/runtime-analysis/model.d.ts +1 -17
  651. package/packages/core/dist/runtime-paths.d.ts +20 -0
  652. package/packages/core/dist/runtime-paths.js +109 -14
  653. package/packages/core/dist/runtime-paths.js.map +1 -1
  654. package/packages/core/dist/runtime-projection-p0.d.ts +64 -0
  655. package/packages/core/dist/runtime-projection-p0.js +200 -0
  656. package/packages/core/dist/runtime-projection-p0.js.map +1 -0
  657. package/packages/core/dist/sdd-docs/context.js +1 -1
  658. package/packages/core/dist/sdd-docs/context.js.map +1 -1
  659. package/packages/core/dist/sdd-docs/document-hashes.d.ts +6 -0
  660. package/packages/core/dist/sdd-docs/document-hashes.js +276 -0
  661. package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -0
  662. package/packages/core/dist/sdd-docs/run-binding.d.ts +1 -1
  663. package/packages/core/dist/sdd-docs/run-binding.js +15 -4
  664. package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
  665. package/packages/core/dist/sdd-docs/task-parser.d.ts +21 -0
  666. package/packages/core/dist/sdd-docs/task-parser.js +139 -38
  667. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  668. package/packages/core/dist/stage-artifacts.d.ts +55 -0
  669. package/packages/core/dist/stage-artifacts.js +322 -0
  670. package/packages/core/dist/stage-artifacts.js.map +1 -0
  671. package/packages/core/dist/stage-collaboration-contracts.d.ts +55 -0
  672. package/packages/core/dist/stage-collaboration-contracts.js +241 -0
  673. package/packages/core/dist/stage-collaboration-contracts.js.map +1 -0
  674. package/packages/core/dist/stage-collaboration.d.ts +888 -0
  675. package/packages/core/dist/stage-collaboration.js +3870 -0
  676. package/packages/core/dist/stage-collaboration.js.map +1 -0
  677. package/packages/core/dist/stage-runtime/runtime.js +8 -1
  678. package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
  679. package/packages/core/dist/status/project-status.d.ts +105 -1
  680. package/packages/core/dist/status/project-status.js +343 -8
  681. package/packages/core/dist/status/project-status.js.map +1 -1
  682. package/packages/core/dist/storage/runtime-store.d.ts +348 -3
  683. package/packages/core/dist/storage/runtime-store.js +1017 -8
  684. package/packages/core/dist/storage/runtime-store.js.map +1 -1
  685. package/packages/core/dist/subagents/contracts.d.ts +1 -1
  686. package/packages/core/dist/subagents/runtime.js +7 -7
  687. package/packages/core/dist/subagents/runtime.js.map +1 -1
  688. package/packages/core/dist/test-support/fixtures.js +21 -0
  689. package/packages/core/dist/test-support/fixtures.js.map +1 -1
  690. package/packages/core/dist/test-support/run-state.d.ts +1 -1
  691. package/packages/core/dist/test-support/run-state.js +19 -20
  692. package/packages/core/dist/test-support/run-state.js.map +1 -1
  693. package/packages/core/dist/truth-reconciliation.d.ts +44 -0
  694. package/packages/core/dist/truth-reconciliation.js +138 -0
  695. package/packages/core/dist/truth-reconciliation.js.map +1 -0
  696. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  697. package/packages/core/dist/verification/goal-verify.d.ts +0 -1
  698. package/packages/core/dist/verification/goal-verify.js +44 -37
  699. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  700. package/packages/core/dist/verification/rendering.d.ts +0 -2
  701. package/packages/core/dist/verification/rendering.js +19 -49
  702. package/packages/core/dist/verification/rendering.js.map +1 -1
  703. package/packages/core/dist/verification/review-gate.d.ts +22 -0
  704. package/packages/core/dist/verification/review-gate.js +53 -0
  705. package/packages/core/dist/verification/review-gate.js.map +1 -0
  706. package/packages/core/dist/verification/single-task-loop.d.ts +0 -1
  707. package/packages/core/dist/verification/single-task-loop.js +213 -111
  708. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  709. package/packages/core/dist/verification/test-runtime.d.ts +28 -3
  710. package/packages/core/dist/verification/test-runtime.js +546 -125
  711. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  712. package/packages/core/dist/verification/validation-cache.d.ts +26 -0
  713. package/packages/core/dist/verification/validation-cache.js +73 -0
  714. package/packages/core/dist/verification/validation-cache.js.map +1 -0
  715. package/packages/core/dist/verification/validation-wave.d.ts +76 -0
  716. package/packages/core/dist/verification/validation-wave.js +450 -0
  717. package/packages/core/dist/verification/validation-wave.js.map +1 -0
  718. package/packages/core/dist/verification/verify-contract.d.ts +3 -1
  719. package/packages/core/dist/verification/verify-contract.js +105 -30
  720. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  721. package/packages/core/dist/verification.d.ts +2 -0
  722. package/packages/core/dist/verification.js +2 -0
  723. package/packages/core/dist/verification.js.map +1 -1
  724. package/packages/core/dist/work-units/contracts.d.ts +1 -1
  725. package/packages/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
  726. package/packages/core/dist/workflow-gate/evidence-packet.js +395 -0
  727. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -0
  728. package/packages/core/dist/workflow-gate/hard-checks.d.ts +4 -0
  729. package/packages/core/dist/workflow-gate/hard-checks.js +164 -0
  730. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -0
  731. package/packages/core/dist/workflow-gate/policy.d.ts +4 -0
  732. package/packages/core/dist/workflow-gate/policy.js +182 -0
  733. package/packages/core/dist/workflow-gate/policy.js.map +1 -0
  734. package/packages/core/dist/workflow-gate/types.d.ts +88 -0
  735. package/packages/core/dist/workflow-gate/types.js +2 -0
  736. package/packages/core/dist/workflow-gate/types.js.map +1 -0
  737. package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -1
  738. package/packages/core/dist/workflow-state/affected-file-conflicts.js +17 -3
  739. package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  740. package/packages/core/dist/workflow-state/dependencies.d.ts +8 -4
  741. package/packages/core/dist/workflow-state/dependencies.js +25 -11
  742. package/packages/core/dist/workflow-state/dependencies.js.map +1 -1
  743. package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +37 -0
  744. package/packages/core/dist/workflow-state/latest-eligible-run.js +188 -0
  745. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
  746. package/packages/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  747. package/packages/core/dist/workflow-state/migration-recovery.js +110 -0
  748. package/packages/core/dist/workflow-state/migration-recovery.js.map +1 -0
  749. package/packages/core/dist/workflow-state/repair-contract.d.ts +12 -0
  750. package/packages/core/dist/workflow-state/repair-contract.js +63 -0
  751. package/packages/core/dist/workflow-state/repair-contract.js.map +1 -0
  752. package/packages/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  753. package/packages/core/dist/workflow-state/resolve-task-run.js +95 -0
  754. package/packages/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  755. package/packages/core/dist/workflow-state/resolve.d.ts +80 -3
  756. package/packages/core/dist/workflow-state/resolve.js +674 -41
  757. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  758. package/packages/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  759. package/packages/core/dist/workflow-state/runtime-projections.js +452 -0
  760. package/packages/core/dist/workflow-state/runtime-projections.js.map +1 -0
  761. package/packages/core/dist/workflow-state.d.ts +1 -0
  762. package/packages/core/dist/workflow-state.js +1 -0
  763. package/packages/core/dist/workflow-state.js.map +1 -1
  764. package/packages/core/package.json +3 -3
  765. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.d.ts +0 -2
  766. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js +0 -44
  767. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js.map +0 -1
  768. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +0 -17
  769. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +0 -221
  770. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +0 -1
  771. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +0 -91
  772. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +0 -395
  773. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +0 -1
  774. package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +0 -2
  775. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +0 -3
  776. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +0 -1
  777. package/node_modules/@sdd-agent-platform/core/src/sync-back/apply.ts +0 -248
  778. package/node_modules/@sdd-agent-platform/core/src/sync-back/inspect.ts +0 -522
  779. package/node_modules/@sdd-agent-platform/core/src/sync-back/sync-back.test.ts +0 -446
  780. package/node_modules/@sdd-agent-platform/core/src/sync-back.ts +0 -2
  781. package/packages/cli/dist/commands/artifact.d.ts +0 -6
  782. package/packages/cli/dist/commands/artifact.js +0 -168
  783. package/packages/cli/dist/commands/artifact.js.map +0 -1
  784. package/packages/cli/dist/commands/sync-back.d.ts +0 -6
  785. package/packages/cli/dist/commands/sync-back.js +0 -82
  786. package/packages/cli/dist/commands/sync-back.js.map +0 -1
  787. package/packages/cli/dist/renderers/artifacts.d.ts +0 -5
  788. package/packages/cli/dist/renderers/artifacts.js +0 -43
  789. package/packages/cli/dist/renderers/artifacts.js.map +0 -1
  790. package/packages/core/dist/doctor/render.d.ts +0 -2
  791. package/packages/core/dist/doctor/render.js +0 -44
  792. package/packages/core/dist/doctor/render.js.map +0 -1
  793. package/packages/core/dist/sync-back/apply.d.ts +0 -17
  794. package/packages/core/dist/sync-back/apply.js +0 -221
  795. package/packages/core/dist/sync-back/apply.js.map +0 -1
  796. package/packages/core/dist/sync-back/inspect.d.ts +0 -91
  797. package/packages/core/dist/sync-back/inspect.js +0 -395
  798. package/packages/core/dist/sync-back/inspect.js.map +0 -1
  799. package/packages/core/dist/sync-back.d.ts +0 -2
  800. package/packages/core/dist/sync-back.js +0 -3
  801. package/packages/core/dist/sync-back.js.map +0 -1
@@ -0,0 +1,2903 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import path from 'node:path';
6
+
7
+ import { LIFECYCLE_RISK_DECISION_CONTRACT_VERSION, STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION, VERIFY_DOCUMENT_CONTRACT_VERSION, WORKFLOW_HANDOFF_CONTRACT_VERSION, type RuntimeRef, type RuntimeRefKind, type SddStage } from './contracts.js';
8
+ import type { LifecycleRiskDecision } from './risk/contracts.js';
9
+ import { initProject } from './config/init-project.js';
10
+ import {
11
+ adjudicateSpecStageClosureRequest,
12
+ buildDoStageWorkOrder,
13
+ buildGoalVerifyStageWorkOrder,
14
+ buildPlanStageWorkOrder,
15
+ buildShipStageWorkOrder,
16
+ buildSpecStageWorkOrder,
17
+ buildTasksStageWorkOrder,
18
+ buildTestStageWorkOrder,
19
+ buildVerifiesStageWorkOrder,
20
+ deriveSpecCollaborationProfile,
21
+ inspectFullStageChain,
22
+ inspectSpecCollaborationHealth,
23
+ reconcileDoCollaborationClosure,
24
+ reconcileGoalVerifyCollaborationClosure,
25
+ reconcileShipCollaborationClosure,
26
+ reconcilePlanCollaborationClosure,
27
+ reconcileTasksCollaborationClosure,
28
+ reconcileTestCollaborationClosure,
29
+ reconcileVerifiesCollaborationClosure,
30
+ reconcileSpecCollaborationClosure,
31
+ readTruthAlignmentProjection,
32
+ recordSpecCollaborationAdjudicationProjection,
33
+ type CapabilityFinding,
34
+ type SpecDocumentCandidate,
35
+ type SpecManagerCoordinationRecord,
36
+ type SpecProposalItemKind,
37
+ type SpecReviewResult,
38
+ type StageClosureRequest,
39
+ type StageCollaborationProfile
40
+ } from './stage-collaboration.js';
41
+ import { inspectWorkflowStageHandoff, readStageRunProjection, readWorkflowHandoffProjection, recordStageRunProjection, recordWorkflowHandoffProjection } from './stage-runtime/runtime.js';
42
+ import { hashDocumentContent } from './sdd-docs/document-hashes.js';
43
+ import { listRuntimeStageArtifacts, listRuntimeStageCollaborationContracts, recordRuntimeStageArtifact } from './storage/runtime-store.js';
44
+ import { expectedStageArtifactContracts, readMarkdownArtifact, validateStageArtifactFrontmatter } from './stage-artifacts.js';
45
+
46
+ const generatedAt = '2026-01-01T00:00:00.000Z';
47
+
48
+ test('direct lifecycle keeps spec collaboration as runtime no-op', () => {
49
+ const profile = deriveSpecCollaborationProfile(decision('direct', ['do', 'test']), generatedAt);
50
+ const result = adjudicateSpecStageClosureRequest({ profile, generatedAt });
51
+
52
+ assert.equal(profile.contract, STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION);
53
+ assert.equal(profile.intensity, 'noop');
54
+ assert.equal(profile.required, false);
55
+ assert.equal(profile.requiresStageClosure, false);
56
+ assert.equal(result.health, 'no-op');
57
+ assert.equal(result.stageDecision?.status, 'skipped');
58
+ assert.equal(result.handoffPacket, null);
59
+ });
60
+
61
+ test('full lifecycle creates spec-manager work order with agent-team proposal-only authority', () => {
62
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
63
+ const workOrder = buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt);
64
+
65
+ assert.equal(profile.intensity, 'team-required');
66
+ assert.equal(profile.required, true);
67
+ assert.equal(profile.requiresStageClosure, true);
68
+ assert.equal(workOrder.authorityCeiling, 'proposal_input');
69
+ assert.deepEqual(workOrder.forbiddenActions, ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved']);
70
+ assert.equal(workOrder.stageManager, 'spec-manager');
71
+ assert.deepEqual(workOrder.agentTeam, ['scout', 'spec-drafter', 'spec-reviewer']);
72
+ assert.deepEqual(workOrder.requiredCapabilities, ['norm_discovery', 'uncertainty_resolution']);
73
+ assert.equal(workOrder.collaborationPlan.topology, 'team-required');
74
+ assert.equal(workOrder.collaborationPlan.fanIn, 'runtime_adjudication');
75
+ assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'agent' && participant.id === 'spec-manager'), true);
76
+ assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'subagent' && participant.id === 'spec-reviewer' && participant.role === 'spec-document-reviewer'), true);
77
+ assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'subagent' && participant.id === 'scout' && participant.parallelGroup === 'discovery'), true);
78
+ assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'skill' && participant.id === 'cap.norm_discovery'), true);
79
+ assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'material-pack' && participant.id === 'project-norms'), true);
80
+ });
81
+
82
+ test('stage run projection keeps completed stage from being downgraded by later rejected diagnostics', async () => {
83
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-run-terminal-'));
84
+ try {
85
+ await initProject(root);
86
+ const scope = { branch: 'feature' };
87
+ const completed = testStageRun('do-completed', scope, 'completed', '2026-01-01T00:00:00.000Z');
88
+ const rejected = testStageRun('do-rejected', scope, 'failed', '2026-01-01T00:01:00.000Z');
89
+
90
+ await recordStageRunProjection(root, completed);
91
+ const result = await recordStageRunProjection(root, rejected);
92
+ const stored = await readStageRunProjection(root, scope, 'do');
93
+
94
+ assert.equal(result.status, 'unchanged');
95
+ assert.equal(stored?.payload.id, completed.id);
96
+ assert.equal(stored?.payload.status, 'completed');
97
+ } finally {
98
+ await rm(root, { recursive: true, force: true });
99
+ }
100
+ });
101
+
102
+ test('blocking ambiguity produces clarification gate instead of handoff', () => {
103
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
104
+ const team = stageClosure(profile, { items: [candidateItem('amb-1', 'blocking_ambiguity')] });
105
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
106
+
107
+ assert.equal(result.health, 'needs_clarification');
108
+ assert.equal(result.clarificationGate?.status, 'needs_clarification');
109
+ assert.deepEqual(result.clarificationGate?.blockingItemIds, ['amb-1']);
110
+ assert.equal(result.stageDecision, null);
111
+ assert.equal(result.handoffPacket, null);
112
+ assert.equal(profile.collaborationPlan.topology, 'team-required');
113
+ });
114
+
115
+ test('answered clarification becomes spec decision and spec to plan handoff', () => {
116
+ const profile = deriveSpecCollaborationProfile(decision('research', ['spec', 'plan', 'verifies']), generatedAt);
117
+ const team = stageClosure(profile, { items: [candidateItem('amb-1', 'blocking_ambiguity'), candidateItem('find-1', 'advisory_finding')] });
118
+ const result = adjudicateSpecStageClosureRequest({
119
+ profile,
120
+ closureRequest: team.closureRequest,
121
+ answerRefs: [ref('external', 'answer/spec-ambiguity')],
122
+ generatedAt
123
+ });
124
+
125
+ assert.equal(result.health, 'ready_for_plan');
126
+ assert.equal(result.specDecisionRecord?.source, 'clarification-answer');
127
+ assert.deepEqual(result.specDecisionRecord?.acceptedItemIds, ['amb-1', 'find-1']);
128
+ assert.equal(result.stageDecision?.status, 'completed');
129
+ assert.equal(result.handoffPacket?.fromStage, 'spec');
130
+ assert.equal(result.handoffPacket?.toStage, 'plan');
131
+ assert.deepEqual(result.handoffPacket?.recommendedNextStageRoles, ['role.norm-scout', 'role.uncertainty-reviewer']);
132
+ assert.equal(profile.collaborationPlan.topology, 'parallel-research');
133
+ });
134
+
135
+ test('valid stage closure is accepted into runtime-owned stage decision and handoff', () => {
136
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
137
+ const team = stageClosure(profile);
138
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
139
+
140
+ assert.equal(result.health, 'ready_for_plan');
141
+ assert.deepEqual(result.acceptedItemIds, ['find-1', 'cap-1', 'handoff-1']);
142
+ assert.equal(result.specDecisionRecord?.source, 'stage-closure-request');
143
+ assert.equal(result.stageDecision?.health, 'ready_for_plan');
144
+ assert.equal(result.handoffPacket?.status, 'proposed');
145
+ assert.deepEqual(result.handoffPacket?.recommendedNextStageRoles, ['role.norm-scout', 'role.performance-planner', 'role.verification-designer']);
146
+ });
147
+
148
+ test('authority-violating closure request is rejected with actionable metadata', () => {
149
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
150
+ const team = stageClosure(profile, { authorityAttempts: ['stage_pass'] });
151
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt, retryBudgetRemaining: 2 });
152
+
153
+ assert.equal(result.health, 'rejected');
154
+ assert.equal(result.rejection?.reasonCode, 'authority_violation');
155
+ assert.equal(result.rejection?.retryAllowed, true);
156
+ assert.equal(result.rejection?.retryBudgetRemaining, 2);
157
+ assert.equal(result.rejection?.fallbackRoute, 'revise-proposal');
158
+ assert.match(result.rejection?.requiredNextAction ?? '', /Remove workflow-authority/);
159
+ });
160
+
161
+ test('candidate items must carry refs', () => {
162
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
163
+ const team = stageClosure(profile, { items: [candidateItem('find-1', 'advisory_finding', [])] });
164
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
165
+
166
+ assert.equal(result.health, 'rejected');
167
+ assert.equal(result.rejection?.reasonCode, 'missing_refs');
168
+ });
169
+
170
+ test('missing spec-reviewer result returns prioritized stage-manager next action', () => {
171
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
172
+ const team = stageClosure(profile);
173
+ const closureRequest = { ...team.closureRequest, reviewRefs: [], reviewResults: [] };
174
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest, generatedAt });
175
+
176
+ assert.equal(result.health, 'rejected');
177
+ assert.equal(result.rejection?.reasonCode, 'missing_required_review');
178
+ assert.equal(result.nextActions[0]?.reasonCode, 'missing_required_review');
179
+ assert.equal(result.nextActions[0]?.owner, 'stage-manager');
180
+ assert.equal(result.nextActions[0]?.kind, 'collect_required_review');
181
+ });
182
+
183
+ test('spec collaboration adjudication projection exposes compact health', async () => {
184
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-collaboration-'));
185
+ try {
186
+ await initProject(root);
187
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
188
+ const team = stageClosure(profile);
189
+ const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
190
+
191
+ await recordSpecCollaborationAdjudicationProjection(root, result);
192
+ const health = await inspectSpecCollaborationHealth(root, 'master');
193
+
194
+ assert.equal(health.status, 'ready_for_plan');
195
+ assert.equal(health.projectionCount, 1);
196
+ assert.equal(health.latestHandoffId, result.handoffPacket?.handoffId);
197
+ assert.equal(health.latestClarificationGateId, null);
198
+ assert.equal(health.latestRejectionReason, null);
199
+ assert.match(health.reasons.join(' '), /ready for plan handoff/);
200
+ } finally {
201
+ await rm(root, { recursive: true, force: true });
202
+ }
203
+ });
204
+
205
+ test('full-stage chain diagnostic reports missing projections before stage closure', async () => {
206
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-missing-'));
207
+ try {
208
+ await initProject(root);
209
+ const chain = await inspectFullStageChain(root, 'master');
210
+
211
+ assert.equal(chain.contract, 'sdd-full-stage-chain-diagnostic-v1');
212
+ assert.equal(chain.status, 'missing');
213
+ assert.equal(chain.stages.length, 8);
214
+ assert.equal(chain.projectionCounts.adjudications, 0);
215
+ assert.equal(chain.projectionCounts.completedStages, 0);
216
+ assert.equal(chain.projectionCounts.validatedCollaborationContracts, 0);
217
+ assert.equal(chain.finalHealth, null);
218
+ assert.equal(chain.stages.every((stage) => stage.projectionRef === null), true);
219
+ } finally {
220
+ await rm(root, { recursive: true, force: true });
221
+ }
222
+ });
223
+
224
+ test('ready closure accepts reviewed spec document, agent-authored evidence, and sqlite projections', async () => {
225
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-ready-'));
226
+ try {
227
+ await initProject(root);
228
+ const reviewedSpecContent = reviewedSpec();
229
+ const specHash = hashDocumentContent(reviewedSpecContent);
230
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
231
+ await writeSpecStageArtifacts(root, { specHash });
232
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
233
+
234
+ const closure = await reconcileSpecCollaborationClosure(root, {
235
+ decision: riskDecision,
236
+ generatedAt,
237
+ runId: 'run-ready-closure'
238
+ });
239
+ const specContent = await readFile(path.join(root, 'specs', 'master', 'spec.md'), 'utf8');
240
+ const health = await inspectSpecCollaborationHealth(root, 'master');
241
+ const handoff = await inspectWorkflowStageHandoff(root, 'master');
242
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'spec' });
243
+ const registeredContracts = await listRuntimeStageCollaborationContracts(root, { branchSlug: 'master', stage: 'spec' });
244
+
245
+ assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'accepted');
246
+ assert.equal(closure.acceptedSpecRef?.ref, 'specs/master/spec.md');
247
+ assert.equal(closure.acceptedSpecRef?.hash, specHash);
248
+ assert.equal(specContent, reviewedSpecContent);
249
+ assert.equal(closure.artifactRefs.every((artifact) => artifact.ref.startsWith('.sdd/runs/master/spec/')), true);
250
+ assert.equal(closure.artifactRefs.some((artifact) => artifact.ref.startsWith(['artifacts', 'phase9.1'].join('/'))), false);
251
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'scout_context', 'spec_review']);
252
+ assert.equal(registeredContracts.length, 1);
253
+ assert.equal(registeredContracts[0]?.status, 'validated');
254
+ assert.equal(closure.registeredCollaborationContracts[0]?.ref, '.sdd/runs/master/spec/spec-collaboration-contract-v1.md');
255
+ assert.equal(closure.adjudication.closureRefs?.collaborationContractRef?.ref, '.sdd/runs/master/spec/spec-collaboration-contract-v1.md');
256
+ assert.equal(health.status, 'ready_for_plan');
257
+ assert.equal(health.latest?.closureRefs?.runRef.ref, 'run-ready-closure');
258
+ assert.equal(health.latest?.closureRefs?.acceptedSpecRef?.ref, 'specs/master/spec.md');
259
+ assert.equal(handoff.status, 'fresh');
260
+ assert.equal(handoff.latestStageRun?.status, 'completed');
261
+ assert.equal(handoff.latestHandoff?.requiredInputRefs[0]?.ref, 'specs/master/spec.md');
262
+ assert.equal(handoff.latestHandoff?.requiredInputRefs[0]?.hash, specHash);
263
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.1')));
264
+ } finally {
265
+ await rm(root, { recursive: true, force: true });
266
+ }
267
+ });
268
+
269
+ test('ready structural closure rejects missing collaboration contract without handoff', async () => {
270
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-missing-contract-'));
271
+ try {
272
+ await initProject(root);
273
+ const reviewedSpecContent = reviewedSpec();
274
+ const specHash = hashDocumentContent(reviewedSpecContent);
275
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
276
+ await writeSpecStageArtifacts(root, { specHash, omitContract: true });
277
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
278
+
279
+ const closure = await reconcileSpecCollaborationClosure(root, {
280
+ decision: riskDecision,
281
+ generatedAt,
282
+ runId: 'run-missing-contract-closure'
283
+ });
284
+ const handoff = await inspectWorkflowStageHandoff(root, 'master');
285
+ const registeredContracts = await listRuntimeStageCollaborationContracts(root, { branchSlug: 'master', stage: 'spec' });
286
+
287
+ assert.equal(closure.adjudication.health, 'rejected');
288
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
289
+ assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_wrong_ref');
290
+ assert.equal(closure.adjudication.closureRefs?.collaborationContractRef, null);
291
+ assert.equal(closure.acceptedSpecRef, null);
292
+ assert.equal(registeredContracts.length, 0);
293
+ assert.equal(handoff.status, 'missing');
294
+ } finally {
295
+ await rm(root, { recursive: true, force: true });
296
+ }
297
+ });
298
+
299
+ test('stage artifact registration supports plan-stage review and manager artifacts', async () => {
300
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-artifact-registration-'));
301
+ try {
302
+ await initProject(root);
303
+ const planContent = reviewedPlan();
304
+ const planHash = hashDocumentContent(planContent);
305
+ await mkdir(path.join(root, 'specs', 'master'), { recursive: true });
306
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
307
+ await writePlanStageArtifacts(root, { planHash });
308
+
309
+ const reviewArtifact = await readMarkdownArtifact(root, '.sdd/runs/master/plan/plan-review-v1.md');
310
+ const reviewRecord = validateStageArtifactFrontmatter({
311
+ branch: 'master',
312
+ stage: 'plan',
313
+ ref: reviewArtifact.ref,
314
+ hash: reviewArtifact.hash,
315
+ frontmatter: reviewArtifact.frontmatter,
316
+ registeredAt: generatedAt
317
+ });
318
+ const managerArtifact = await readMarkdownArtifact(root, '.sdd/runs/master/plan/plan-manager-v1.md');
319
+ const managerRecord = validateStageArtifactFrontmatter({
320
+ branch: 'master',
321
+ stage: 'plan',
322
+ ref: managerArtifact.ref,
323
+ hash: managerArtifact.hash,
324
+ frontmatter: managerArtifact.frontmatter,
325
+ registeredAt: generatedAt
326
+ });
327
+ await recordRuntimeStageArtifact(root, reviewRecord);
328
+ await recordRuntimeStageArtifact(root, managerRecord);
329
+
330
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'plan' });
331
+
332
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'plan_review']);
333
+ assert.equal(reviewRecord.targetRef?.ref, 'specs/master/plan.md');
334
+ assert.equal(reviewRecord.targetHash, planHash);
335
+ assert.equal(managerRecord.reviewRef?.ref, '.sdd/runs/master/plan/plan-review-v1.md');
336
+ assert.equal(managerRecord.reviewHash, reviewArtifact.hash);
337
+ assert.throws(() => validateStageArtifactFrontmatter({
338
+ branch: 'master',
339
+ stage: 'plan',
340
+ ref: reviewArtifact.ref,
341
+ hash: reviewArtifact.hash,
342
+ frontmatter: { ...reviewArtifact.frontmatter, producer: 'spec-reviewer' },
343
+ registeredAt: generatedAt
344
+ }), /must be produced by plan-reviewer/);
345
+ } finally {
346
+ await rm(root, { recursive: true, force: true });
347
+ }
348
+ });
349
+
350
+ test('stage artifact registration supports review and manager pairs through ship', async () => {
351
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-artifact-registration-'));
352
+ try {
353
+ await initProject(root);
354
+ for (const stageCase of stageArtifactPairCases) {
355
+ await writeStageArtifactPair(root, stageCase);
356
+ const reviewArtifact = await readMarkdownArtifact(root, `.sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}`);
357
+ const reviewRecord = validateStageArtifactFrontmatter({
358
+ branch: 'master',
359
+ stage: stageCase.stage,
360
+ ref: reviewArtifact.ref,
361
+ hash: reviewArtifact.hash,
362
+ frontmatter: reviewArtifact.frontmatter,
363
+ registeredAt: generatedAt
364
+ });
365
+ const managerArtifact = await readMarkdownArtifact(root, `.sdd/runs/master/${stageCase.stage}/${stageCase.managerFile}`);
366
+ const managerRecord = validateStageArtifactFrontmatter({
367
+ branch: 'master',
368
+ stage: stageCase.stage,
369
+ ref: managerArtifact.ref,
370
+ hash: managerArtifact.hash,
371
+ frontmatter: managerArtifact.frontmatter,
372
+ registeredAt: generatedAt
373
+ });
374
+ await recordRuntimeStageArtifact(root, reviewRecord);
375
+ await recordRuntimeStageArtifact(root, managerRecord);
376
+
377
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: stageCase.stage });
378
+
379
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', stageCase.reviewKind].sort());
380
+ assert.equal(reviewRecord.producer, stageCase.reviewProducer);
381
+ assert.equal(reviewRecord.targetRef?.ref, stageCase.targetRef);
382
+ assert.equal(managerRecord.producer, stageCase.managerProducer);
383
+ assert.equal(managerRecord.reviewRef?.ref, `.sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}`);
384
+ assert.equal(managerRecord.reviewHash, reviewArtifact.hash);
385
+ }
386
+ } finally {
387
+ await rm(root, { recursive: true, force: true });
388
+ }
389
+ });
390
+
391
+ test('clarification closure records agent-authored evidence without accepting spec or handoff', async () => {
392
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-clarification-'));
393
+ try {
394
+ await initProject(root);
395
+ const reviewedSpecContent = reviewedSpec();
396
+ const specHash = hashDocumentContent(reviewedSpecContent);
397
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
398
+ await writeSpecStageArtifacts(root, { specHash, verdict: 'blocked', blockingCount: 1, recommendation: 'request_clarification' });
399
+ const riskDecision = decision('research', ['spec', 'plan', 'verifies']);
400
+
401
+ const closure = await reconcileSpecCollaborationClosure(root, {
402
+ decision: riskDecision,
403
+ generatedAt,
404
+ runId: 'run-clarification-closure'
405
+ });
406
+ const health = await inspectSpecCollaborationHealth(root, 'master');
407
+ const handoff = await inspectWorkflowStageHandoff(root, 'master');
408
+
409
+ assert.equal(closure.adjudication.health, 'needs_clarification');
410
+ assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_needs_clarification');
411
+ assert.equal(closure.acceptedSpecRef, null);
412
+ assert.equal(closure.artifactRefs.every((artifact) => artifact.ref.startsWith('.sdd/runs/master/spec/')), true);
413
+ assert.equal(health.status, 'needs_clarification');
414
+ assert.equal(handoff.status, 'missing');
415
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.1')));
416
+ } finally {
417
+ await rm(root, { recursive: true, force: true });
418
+ }
419
+ });
420
+
421
+ test('ready structural closure rejects mismatched canonical spec hash without handoff', async () => {
422
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-mismatch-'));
423
+ try {
424
+ await initProject(root);
425
+ const reviewedSpecContent = reviewedSpec();
426
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
427
+ await writeSpecStageArtifacts(root, { specHash: 'stale-hash' });
428
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
429
+
430
+ const closure = await reconcileSpecCollaborationClosure(root, {
431
+ decision: riskDecision,
432
+ generatedAt,
433
+ runId: 'run-hash-mismatch-closure'
434
+ });
435
+ const handoff = await inspectWorkflowStageHandoff(root, 'master');
436
+
437
+ assert.equal(closure.adjudication.health, 'rejected');
438
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
439
+ assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_hash_mismatch');
440
+ assert.equal(closure.acceptedSpecRef, null);
441
+ assert.equal(handoff.status, 'missing');
442
+ } finally {
443
+ await rm(root, { recursive: true, force: true });
444
+ }
445
+ });
446
+
447
+ test('ready plan closure accepts reviewed plan document and records plan to tasks handoff', async () => {
448
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-ready-'));
449
+ try {
450
+ await initProject(root);
451
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
452
+ await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan');
453
+ const planContent = reviewedPlan();
454
+ const planHash = hashDocumentContent(planContent);
455
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
456
+ await writePlanStageArtifacts(root, { planHash });
457
+
458
+ const closure = await reconcilePlanCollaborationClosure(root, {
459
+ decision: riskDecision,
460
+ generatedAt,
461
+ runId: 'run-plan-ready-closure'
462
+ });
463
+ const planContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'plan.md'), 'utf8');
464
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'plan' });
465
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
466
+
467
+ assert.equal(closure.adjudication.health, 'ready_for_tasks');
468
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'accepted');
469
+ assert.equal(closure.acceptedPlanRef?.ref, 'specs/master/plan.md');
470
+ assert.equal(closure.acceptedPlanRef?.hash, planHash);
471
+ assert.equal(planContentAfterClosure, planContent);
472
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'plan_review']);
473
+ assert.equal(handoff?.payload.fromStage, 'plan');
474
+ assert.equal(handoff?.payload.toStage, 'tasks');
475
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/plan.md');
476
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, planHash);
477
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.2')));
478
+ } finally {
479
+ await rm(root, { recursive: true, force: true });
480
+ }
481
+ });
482
+
483
+ test('plan closure rejects missing plan review without recording handoff', async () => {
484
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-missing-review-'));
485
+ try {
486
+ await initProject(root);
487
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
488
+ await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-missing-review');
489
+ const planContent = reviewedPlan();
490
+ const planHash = hashDocumentContent(planContent);
491
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
492
+ await writePlanStageArtifacts(root, { planHash, omitReview: true });
493
+
494
+ const closure = await reconcilePlanCollaborationClosure(root, {
495
+ decision: riskDecision,
496
+ generatedAt,
497
+ runId: 'run-plan-missing-review-closure'
498
+ });
499
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
500
+
501
+ assert.equal(closure.adjudication.health, 'rejected');
502
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
503
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_review');
504
+ assert.equal(closure.acceptedPlanRef, null);
505
+ assert.equal(handoff, null);
506
+ } finally {
507
+ await rm(root, { recursive: true, force: true });
508
+ }
509
+ });
510
+
511
+ test('plan closure rejects missing accepted spec handoff without recording handoff', async () => {
512
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-missing-upstream-'));
513
+ try {
514
+ await initProject(root);
515
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
516
+ const planContent = reviewedPlan();
517
+ const planHash = hashDocumentContent(planContent);
518
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpec(), 'utf8');
519
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
520
+ await writePlanStageArtifacts(root, { planHash });
521
+
522
+ const closure = await reconcilePlanCollaborationClosure(root, {
523
+ decision: riskDecision,
524
+ generatedAt,
525
+ runId: 'run-plan-missing-upstream-closure'
526
+ });
527
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
528
+
529
+ assert.equal(closure.adjudication.health, 'rejected');
530
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_upstream');
531
+ assert.equal(closure.acceptedPlanRef, null);
532
+ assert.equal(handoff, null);
533
+ } finally {
534
+ await rm(root, { recursive: true, force: true });
535
+ }
536
+ });
537
+
538
+ test('plan closure rejects stale accepted spec handoff without recording handoff', async () => {
539
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-stale-upstream-'));
540
+ try {
541
+ await initProject(root);
542
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
543
+ await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-stale');
544
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), `${reviewedSpec()}\nStale after handoff.\n`, 'utf8');
545
+ const planContent = reviewedPlan();
546
+ const planHash = hashDocumentContent(planContent);
547
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
548
+ await writePlanStageArtifacts(root, { planHash });
549
+
550
+ const closure = await reconcilePlanCollaborationClosure(root, {
551
+ decision: riskDecision,
552
+ generatedAt,
553
+ runId: 'run-plan-stale-upstream-closure'
554
+ });
555
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
556
+
557
+ assert.equal(closure.adjudication.health, 'rejected');
558
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_upstream');
559
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
560
+ assert.equal(closure.acceptedPlanRef, null);
561
+ assert.equal(handoff, null);
562
+ } finally {
563
+ await rm(root, { recursive: true, force: true });
564
+ }
565
+ });
566
+
567
+ test('plan closure rejects mismatched canonical plan hash without recording handoff', async () => {
568
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-mismatch-'));
569
+ try {
570
+ await initProject(root);
571
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
572
+ await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-hash-mismatch');
573
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), reviewedPlan(), 'utf8');
574
+ await writePlanStageArtifacts(root, { planHash: 'stale-plan-hash' });
575
+
576
+ const closure = await reconcilePlanCollaborationClosure(root, {
577
+ decision: riskDecision,
578
+ generatedAt,
579
+ runId: 'run-plan-hash-mismatch-closure'
580
+ });
581
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
582
+
583
+ assert.equal(closure.adjudication.health, 'rejected');
584
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
585
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_hash_mismatch');
586
+ assert.equal(closure.acceptedPlanRef, null);
587
+ assert.equal(handoff, null);
588
+ } finally {
589
+ await rm(root, { recursive: true, force: true });
590
+ }
591
+ });
592
+
593
+ test('ready tasks closure accepts reviewed tasks document and records tasks to verifies handoff', async () => {
594
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-ready-'));
595
+ try {
596
+ await initProject(root);
597
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
598
+ await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks');
599
+ const tasksContent = reviewedTasks();
600
+ const tasksHash = hashDocumentContent(tasksContent);
601
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
602
+ await writeTasksStageArtifacts(root, { tasksHash });
603
+
604
+ const closure = await reconcileTasksCollaborationClosure(root, {
605
+ decision: riskDecision,
606
+ generatedAt,
607
+ runId: 'run-tasks-ready-closure'
608
+ });
609
+ const tasksContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'tasks.md'), 'utf8');
610
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'tasks' });
611
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
612
+
613
+ assert.equal(closure.adjudication.health, 'ready_for_verifies');
614
+ assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'accepted');
615
+ assert.equal(closure.acceptedTasksRef?.ref, 'specs/master/tasks.md');
616
+ assert.equal(closure.acceptedTasksRef?.hash, tasksHash);
617
+ assert.equal(tasksContentAfterClosure, tasksContent);
618
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'tasks_review']);
619
+ assert.equal(handoff?.payload.fromStage, 'tasks');
620
+ assert.equal(handoff?.payload.toStage, 'verifies');
621
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/tasks.md');
622
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, tasksHash);
623
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.3')));
624
+ } finally {
625
+ await rm(root, { recursive: true, force: true });
626
+ }
627
+ });
628
+
629
+ test('tasks closure rejects missing tasks review without recording handoff', async () => {
630
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-missing-review-'));
631
+ try {
632
+ await initProject(root);
633
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
634
+ await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-missing-review');
635
+ const tasksContent = reviewedTasks();
636
+ const tasksHash = hashDocumentContent(tasksContent);
637
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
638
+ await writeTasksStageArtifacts(root, { tasksHash, omitReview: true });
639
+
640
+ const closure = await reconcileTasksCollaborationClosure(root, {
641
+ decision: riskDecision,
642
+ generatedAt,
643
+ runId: 'run-tasks-missing-review-closure'
644
+ });
645
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
646
+
647
+ assert.equal(closure.adjudication.health, 'rejected');
648
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
649
+ assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_missing_review');
650
+ assert.equal(closure.acceptedTasksRef, null);
651
+ assert.equal(handoff, null);
652
+ } finally {
653
+ await rm(root, { recursive: true, force: true });
654
+ }
655
+ });
656
+
657
+ test('tasks closure rejects stale accepted plan handoff without recording handoff', async () => {
658
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-stale-upstream-'));
659
+ try {
660
+ await initProject(root);
661
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
662
+ await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-stale');
663
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), `${reviewedPlan()}\nStale after handoff.\n`, 'utf8');
664
+ const tasksContent = reviewedTasks();
665
+ const tasksHash = hashDocumentContent(tasksContent);
666
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
667
+ await writeTasksStageArtifacts(root, { tasksHash });
668
+
669
+ const closure = await reconcileTasksCollaborationClosure(root, {
670
+ decision: riskDecision,
671
+ generatedAt,
672
+ runId: 'run-tasks-stale-upstream-closure'
673
+ });
674
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
675
+
676
+ assert.equal(closure.adjudication.health, 'rejected');
677
+ assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_missing_upstream');
678
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
679
+ assert.equal(closure.acceptedTasksRef, null);
680
+ assert.equal(handoff, null);
681
+ } finally {
682
+ await rm(root, { recursive: true, force: true });
683
+ }
684
+ });
685
+
686
+ test('tasks closure rejects duplicate task ids without recording handoff', async () => {
687
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-duplicate-'));
688
+ try {
689
+ await initProject(root);
690
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
691
+ await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-duplicate');
692
+ const tasksContent = reviewedTasks({ duplicate: true });
693
+ const tasksHash = hashDocumentContent(tasksContent);
694
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
695
+ await writeTasksStageArtifacts(root, { tasksHash });
696
+
697
+ const closure = await reconcileTasksCollaborationClosure(root, {
698
+ decision: riskDecision,
699
+ generatedAt,
700
+ runId: 'run-tasks-duplicate-closure'
701
+ });
702
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
703
+
704
+ assert.equal(closure.adjudication.health, 'rejected');
705
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
706
+ assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_duplicate_task_ids');
707
+ assert.equal(closure.acceptedTasksRef, null);
708
+ assert.equal(handoff, null);
709
+ } finally {
710
+ await rm(root, { recursive: true, force: true });
711
+ }
712
+ });
713
+
714
+ test('ready verifies closure accepts reviewed verify contract and records verifies to do handoff', async () => {
715
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-ready-'));
716
+ try {
717
+ await initProject(root);
718
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
719
+ await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies');
720
+ const verifyContent = reviewedVerify(reviewedTasks());
721
+ const verifyHash = hashDocumentContent(verifyContent);
722
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
723
+ await writeVerifiesStageArtifacts(root, { verifyHash });
724
+
725
+ const closure = await reconcileVerifiesCollaborationClosure(root, {
726
+ decision: riskDecision,
727
+ generatedAt,
728
+ runId: 'run-verifies-ready-closure'
729
+ });
730
+ const verifyContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'verify.md'), 'utf8');
731
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'verifies' });
732
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
733
+
734
+ assert.equal(closure.adjudication.health, 'ready_for_do');
735
+ assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'accepted');
736
+ assert.equal(closure.acceptedVerifyRef?.ref, 'specs/master/verify.md');
737
+ assert.equal(closure.acceptedVerifyRef?.hash, verifyHash);
738
+ assert.equal(verifyContentAfterClosure, verifyContent);
739
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'verifies_review']);
740
+ assert.equal(handoff?.payload.fromStage, 'verifies');
741
+ assert.equal(handoff?.payload.toStage, 'do');
742
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/verify.md');
743
+ assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, verifyHash);
744
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.4')));
745
+ } finally {
746
+ await rm(root, { recursive: true, force: true });
747
+ }
748
+ });
749
+
750
+ test('verifies closure rejects missing verifies review without recording handoff', async () => {
751
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-missing-review-'));
752
+ try {
753
+ await initProject(root);
754
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
755
+ await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-missing-review');
756
+ const verifyContent = reviewedVerify(reviewedTasks());
757
+ const verifyHash = hashDocumentContent(verifyContent);
758
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
759
+ await writeVerifiesStageArtifacts(root, { verifyHash, omitReview: true });
760
+
761
+ const closure = await reconcileVerifiesCollaborationClosure(root, {
762
+ decision: riskDecision,
763
+ generatedAt,
764
+ runId: 'run-verifies-missing-review-closure'
765
+ });
766
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
767
+
768
+ assert.equal(closure.adjudication.health, 'rejected');
769
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
770
+ assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_missing_review');
771
+ assert.equal(closure.acceptedVerifyRef, null);
772
+ assert.equal(handoff, null);
773
+ } finally {
774
+ await rm(root, { recursive: true, force: true });
775
+ }
776
+ });
777
+
778
+ test('verifies closure rejects stale accepted tasks handoff without recording handoff', async () => {
779
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-stale-upstream-'));
780
+ try {
781
+ await initProject(root);
782
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
783
+ await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-stale');
784
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), `${reviewedTasks()}\nStale after handoff.\n`, 'utf8');
785
+ const verifyContent = reviewedVerify(reviewedTasks());
786
+ const verifyHash = hashDocumentContent(verifyContent);
787
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
788
+ await writeVerifiesStageArtifacts(root, { verifyHash });
789
+
790
+ const closure = await reconcileVerifiesCollaborationClosure(root, {
791
+ decision: riskDecision,
792
+ generatedAt,
793
+ runId: 'run-verifies-stale-upstream-closure'
794
+ });
795
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
796
+
797
+ assert.equal(closure.adjudication.health, 'rejected');
798
+ assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_missing_upstream');
799
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
800
+ assert.equal(closure.acceptedVerifyRef, null);
801
+ assert.equal(handoff, null);
802
+ } finally {
803
+ await rm(root, { recursive: true, force: true });
804
+ }
805
+ });
806
+
807
+ test('verifies closure rejects incomplete coverage without recording handoff', async () => {
808
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-incomplete-coverage-'));
809
+ try {
810
+ await initProject(root);
811
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
812
+ await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-incomplete');
813
+ const verifyContent = reviewedVerify(reviewedTasks(), { omitTaskId: 'T2' });
814
+ const verifyHash = hashDocumentContent(verifyContent);
815
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
816
+ await writeVerifiesStageArtifacts(root, { verifyHash });
817
+
818
+ const closure = await reconcileVerifiesCollaborationClosure(root, {
819
+ decision: riskDecision,
820
+ generatedAt,
821
+ runId: 'run-verifies-incomplete-coverage-closure'
822
+ });
823
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
824
+
825
+ assert.equal(closure.adjudication.health, 'rejected');
826
+ assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_incomplete_coverage');
827
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /T2/);
828
+ assert.equal(closure.acceptedVerifyRef, null);
829
+ assert.equal(handoff, null);
830
+ } finally {
831
+ await rm(root, { recursive: true, force: true });
832
+ }
833
+ });
834
+
835
+ test('ready do closure accepts reviewed implementation state and records do to test handoff', async () => {
836
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-ready-'));
837
+ try {
838
+ await initProject(root);
839
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
840
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do');
841
+ const changedFileRef = 'packages/app/login.ts';
842
+ const changedFileContent = 'export const login = () => true;\n';
843
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
844
+ const changedFileHash = hashDocumentContent(changedFileContent);
845
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
846
+ await writeFile(path.join(root, '.sdd', 'runs', 'master', 'do', 't1-schema-review-v1.md'), '# Human note\n\nThis note is not a stage artifact.\n', 'utf8');
847
+
848
+ const closure = await reconcileDoCollaborationClosure(root, {
849
+ decision: riskDecision,
850
+ allowedChangedFileRefs: [changedFileRef],
851
+ generatedAt,
852
+ runId: 'run-do-ready-closure'
853
+ });
854
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
855
+ const doFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'do'))).sort();
856
+ const changedFileAfterClosure = await readFile(path.join(root, changedFileRef), 'utf8');
857
+
858
+ assert.equal(closure.adjudication.health, 'ready_for_test');
859
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'accepted');
860
+ assert.equal(closure.acceptedImplementationRef?.ref, '.sdd/runs/master/do/implementation-v1.md');
861
+ assert.deepEqual(closure.changedFileRefs, [{ kind: 'evidence', ref: changedFileRef, hash: changedFileHash }]);
862
+ assert.equal(handoff?.payload.fromStage, 'do');
863
+ assert.equal(handoff?.payload.toStage, 'test');
864
+ assert.equal(handoff?.payload.requiredInputRefs.some((inputRef) => inputRef.ref === changedFileRef && inputRef.hash === changedFileHash), true);
865
+ assert.deepEqual(doFiles, ['code-review-v1.md', 'do-collaboration-contract-v1.md', 'do-manager-v1.md', 'implementation-v1.md', 't1-schema-review-v1.md']);
866
+ assert.equal(changedFileAfterClosure, changedFileContent);
867
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.5')));
868
+ } finally {
869
+ await rm(root, { recursive: true, force: true });
870
+ }
871
+ });
872
+
873
+ test('ready do closure accepts branch-scoped SQL implementation assets matched by task glob', async () => {
874
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-spec-sql-'));
875
+ try {
876
+ await initProject(root);
877
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
878
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-spec-sql');
879
+ const changedFileRef = 'specs/master/add_table.sql';
880
+ const changedFileContent = 'create table add_table (id int primary key);\n';
881
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
882
+ const changedFileHash = hashDocumentContent(changedFileContent);
883
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
884
+
885
+ const closure = await reconcileDoCollaborationClosure(root, {
886
+ decision: riskDecision,
887
+ allowedChangedFileRefs: ['specs/master/*.sql'],
888
+ generatedAt,
889
+ runId: 'run-do-spec-sql-closure'
890
+ });
891
+
892
+ assert.equal(closure.adjudication.health, 'ready_for_test');
893
+ assert.deepEqual(closure.changedFileRefs, [{ kind: 'evidence', ref: changedFileRef, hash: changedFileHash }]);
894
+ } finally {
895
+ await rm(root, { recursive: true, force: true });
896
+ }
897
+ });
898
+
899
+ test('do closure rejects missing code review without recording handoff', async () => {
900
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-missing-review-'));
901
+ try {
902
+ await initProject(root);
903
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
904
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-missing-review');
905
+ const changedFileRef = 'packages/app/login.ts';
906
+ const changedFileContent = 'export const login = () => true;\n';
907
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
908
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], omitReview: true });
909
+
910
+ const closure = await reconcileDoCollaborationClosure(root, {
911
+ decision: riskDecision,
912
+ allowedChangedFileRefs: [changedFileRef],
913
+ generatedAt,
914
+ runId: 'run-do-missing-review-closure'
915
+ });
916
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
917
+
918
+ assert.equal(closure.adjudication.health, 'rejected');
919
+ assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
920
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_review');
921
+ assert.equal(closure.acceptedImplementationRef, null);
922
+ assert.equal(handoff, null);
923
+ } finally {
924
+ await rm(root, { recursive: true, force: true });
925
+ }
926
+ });
927
+
928
+ test('do closure rejects stale accepted verifies handoff without recording handoff', async () => {
929
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-stale-upstream-'));
930
+ try {
931
+ await initProject(root);
932
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
933
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-stale');
934
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), `${reviewedVerify(reviewedTasks())}\nStale after handoff.\n`, 'utf8');
935
+ const changedFileRef = 'packages/app/login.ts';
936
+ const changedFileContent = 'export const login = () => true;\n';
937
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
938
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
939
+
940
+ const closure = await reconcileDoCollaborationClosure(root, {
941
+ decision: riskDecision,
942
+ allowedChangedFileRefs: [changedFileRef],
943
+ generatedAt,
944
+ runId: 'run-do-stale-upstream-closure'
945
+ });
946
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
947
+
948
+ assert.equal(closure.adjudication.health, 'rejected');
949
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_upstream');
950
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
951
+ assert.equal(closure.acceptedImplementationRef, null);
952
+ assert.equal(handoff, null);
953
+ } finally {
954
+ await rm(root, { recursive: true, force: true });
955
+ }
956
+ });
957
+
958
+ test('do closure rejects changed-file hash drift without recording handoff', async () => {
959
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-hash-drift-'));
960
+ try {
961
+ await initProject(root);
962
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
963
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-hash-drift');
964
+ const changedFileRef = 'packages/app/login.ts';
965
+ const changedFileContent = 'export const login = () => true;\n';
966
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
967
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
968
+ await writeWorkspaceFile(root, changedFileRef, 'export const login = () => false;\n');
969
+
970
+ const closure = await reconcileDoCollaborationClosure(root, {
971
+ decision: riskDecision,
972
+ allowedChangedFileRefs: [changedFileRef],
973
+ generatedAt,
974
+ runId: 'run-do-hash-drift-closure'
975
+ });
976
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
977
+
978
+ assert.equal(closure.adjudication.health, 'rejected');
979
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_hash_mismatch');
980
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
981
+ assert.equal(closure.acceptedImplementationRef, null);
982
+ assert.equal(handoff, null);
983
+ } finally {
984
+ await rm(root, { recursive: true, force: true });
985
+ }
986
+ });
987
+
988
+ test('do closure rejects changed files outside task boundary without recording handoff', async () => {
989
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-boundary-'));
990
+ try {
991
+ await initProject(root);
992
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
993
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-boundary');
994
+ const changedFileRef = 'packages/app/login.ts';
995
+ const changedFileContent = 'export const login = () => true;\n';
996
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
997
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
998
+
999
+ const closure = await reconcileDoCollaborationClosure(root, {
1000
+ decision: riskDecision,
1001
+ allowedChangedFileRefs: ['packages/app/profile.ts'],
1002
+ generatedAt,
1003
+ runId: 'run-do-boundary-closure'
1004
+ });
1005
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
1006
+
1007
+ assert.equal(closure.adjudication.health, 'rejected');
1008
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_boundary_violation');
1009
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /outside/);
1010
+ assert.equal(closure.acceptedImplementationRef, null);
1011
+ assert.equal(handoff, null);
1012
+ } finally {
1013
+ await rm(root, { recursive: true, force: true });
1014
+ }
1015
+ });
1016
+
1017
+ test('do closure rejects blocking code review without recording handoff', async () => {
1018
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-blocking-review-'));
1019
+ try {
1020
+ await initProject(root);
1021
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1022
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-blocking-review');
1023
+ const changedFileRef = 'packages/app/login.ts';
1024
+ const changedFileContent = 'export const login = () => true;\n';
1025
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
1026
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], verdict: 'changes_requested', blockingCount: 1 });
1027
+
1028
+ const closure = await reconcileDoCollaborationClosure(root, {
1029
+ decision: riskDecision,
1030
+ allowedChangedFileRefs: [changedFileRef],
1031
+ generatedAt,
1032
+ runId: 'run-do-blocking-review-closure'
1033
+ });
1034
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
1035
+
1036
+ assert.equal(closure.adjudication.health, 'rejected');
1037
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_review');
1038
+ assert.equal(closure.acceptedImplementationRef, null);
1039
+ assert.equal(handoff, null);
1040
+ } finally {
1041
+ await rm(root, { recursive: true, force: true });
1042
+ }
1043
+ });
1044
+
1045
+ test('do closure rejects do-manager rework recommendation without recording handoff', async () => {
1046
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-manager-rework-'));
1047
+ try {
1048
+ await initProject(root);
1049
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1050
+ await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-manager-rework');
1051
+ const changedFileRef = 'packages/app/login.ts';
1052
+ const changedFileContent = 'export const login = () => true;\n';
1053
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
1054
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], recommendation: 'request_clarification' });
1055
+
1056
+ const closure = await reconcileDoCollaborationClosure(root, {
1057
+ decision: riskDecision,
1058
+ allowedChangedFileRefs: [changedFileRef],
1059
+ generatedAt,
1060
+ runId: 'run-do-manager-rework-closure'
1061
+ });
1062
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
1063
+
1064
+ assert.equal(closure.adjudication.health, 'rejected');
1065
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_rejected');
1066
+ assert.equal(closure.acceptedImplementationRef, null);
1067
+ assert.equal(handoff, null);
1068
+ } finally {
1069
+ await rm(root, { recursive: true, force: true });
1070
+ }
1071
+ });
1072
+
1073
+ test('ready test closure accepts mapped validation evidence and records test to goal-verify handoff', async () => {
1074
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-ready-'));
1075
+ try {
1076
+ await initProject(root);
1077
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1078
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test');
1079
+ await writeTestStageArtifacts(root, {});
1080
+
1081
+ const closure = await reconcileTestCollaborationClosure(root, {
1082
+ decision: riskDecision,
1083
+ generatedAt,
1084
+ runId: 'run-test-ready-closure'
1085
+ });
1086
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1087
+ const testFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'test'))).sort();
1088
+
1089
+ assert.equal(closure.adjudication.health, 'ready_for_goal_verify');
1090
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'accepted');
1091
+ assert.equal(closure.acceptedTestEvidenceRef?.ref, '.sdd/runs/master/test/validation-v1.md');
1092
+ assert.equal(handoff?.payload.fromStage, 'test');
1093
+ assert.equal(handoff?.payload.toStage, 'goal-verify');
1094
+ assert.equal(handoff?.payload.requiredInputRefs.some((inputRef) => inputRef.ref === '.sdd/runs/master/test/validation-v1.md'), true);
1095
+ assert.deepEqual(testFiles, ['test-collaboration-contract-v1.md', 'test-execution-v1.md', 'test-manager-v1.md', 'test-review-v1.md', 'validation-v1.md']);
1096
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.6')));
1097
+ } finally {
1098
+ await rm(root, { recursive: true, force: true });
1099
+ }
1100
+ });
1101
+
1102
+ test('test closure rejects missing command evidence without recording handoff', async () => {
1103
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-missing-execution-'));
1104
+ try {
1105
+ await initProject(root);
1106
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1107
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-missing-execution');
1108
+ await writeTestStageArtifacts(root, { omitExecution: true });
1109
+
1110
+ const closure = await reconcileTestCollaborationClosure(root, {
1111
+ decision: riskDecision,
1112
+ generatedAt,
1113
+ runId: 'run-test-missing-execution-closure'
1114
+ });
1115
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1116
+
1117
+ assert.equal(closure.adjudication.health, 'rejected');
1118
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_execution');
1119
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1120
+ assert.equal(handoff, null);
1121
+ } finally {
1122
+ await rm(root, { recursive: true, force: true });
1123
+ }
1124
+ });
1125
+
1126
+ test('test closure rejects stale accepted do handoff without recording handoff', async () => {
1127
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-stale-upstream-'));
1128
+ try {
1129
+ await initProject(root);
1130
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1131
+ const doState = await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-stale');
1132
+ await writeWorkspaceFile(root, doState.changedFileRef, 'export const login = () => false;\n');
1133
+ await writeTestStageArtifacts(root, {});
1134
+
1135
+ const closure = await reconcileTestCollaborationClosure(root, {
1136
+ decision: riskDecision,
1137
+ generatedAt,
1138
+ runId: 'run-test-stale-upstream-closure'
1139
+ });
1140
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1141
+
1142
+ assert.equal(closure.adjudication.health, 'rejected');
1143
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_upstream');
1144
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
1145
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1146
+ assert.equal(handoff, null);
1147
+ } finally {
1148
+ await rm(root, { recursive: true, force: true });
1149
+ }
1150
+ });
1151
+
1152
+ test('test closure rejects failing command evidence without recording handoff', async () => {
1153
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-failing-execution-'));
1154
+ try {
1155
+ await initProject(root);
1156
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1157
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-failing');
1158
+ await writeTestStageArtifacts(root, { executionStatus: 'failed', exitCode: 1 });
1159
+
1160
+ const closure = await reconcileTestCollaborationClosure(root, {
1161
+ decision: riskDecision,
1162
+ generatedAt,
1163
+ runId: 'run-test-failing-execution-closure'
1164
+ });
1165
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1166
+
1167
+ assert.equal(closure.adjudication.health, 'rejected');
1168
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_failed_tests');
1169
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1170
+ assert.equal(handoff, null);
1171
+ } finally {
1172
+ await rm(root, { recursive: true, force: true });
1173
+ }
1174
+ });
1175
+
1176
+ test('test closure rejects unmapped acceptance evidence without recording handoff', async () => {
1177
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-unmapped-acceptance-'));
1178
+ try {
1179
+ await initProject(root);
1180
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1181
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-unmapped');
1182
+ await writeTestStageArtifacts(root, { acceptanceMapped: false });
1183
+
1184
+ const closure = await reconcileTestCollaborationClosure(root, {
1185
+ decision: riskDecision,
1186
+ generatedAt,
1187
+ runId: 'run-test-unmapped-acceptance-closure'
1188
+ });
1189
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1190
+
1191
+ assert.equal(closure.adjudication.health, 'rejected');
1192
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_unmapped_acceptance');
1193
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1194
+ assert.equal(handoff, null);
1195
+ } finally {
1196
+ await rm(root, { recursive: true, force: true });
1197
+ }
1198
+ });
1199
+
1200
+ test('test closure rejects blocking test review without recording handoff', async () => {
1201
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-blocking-review-'));
1202
+ try {
1203
+ await initProject(root);
1204
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1205
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-blocking-review');
1206
+ await writeTestStageArtifacts(root, { verdict: 'changes_requested', blockingCount: 1 });
1207
+
1208
+ const closure = await reconcileTestCollaborationClosure(root, {
1209
+ decision: riskDecision,
1210
+ generatedAt,
1211
+ runId: 'run-test-blocking-review-closure'
1212
+ });
1213
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1214
+
1215
+ assert.equal(closure.adjudication.health, 'rejected');
1216
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_review');
1217
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1218
+ assert.equal(handoff, null);
1219
+ } finally {
1220
+ await rm(root, { recursive: true, force: true });
1221
+ }
1222
+ });
1223
+
1224
+ test('test closure rejects test-manager rework recommendation without recording handoff', async () => {
1225
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-manager-rework-'));
1226
+ try {
1227
+ await initProject(root);
1228
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1229
+ await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-manager-rework');
1230
+ await writeTestStageArtifacts(root, { recommendation: 'request_clarification' });
1231
+
1232
+ const closure = await reconcileTestCollaborationClosure(root, {
1233
+ decision: riskDecision,
1234
+ generatedAt,
1235
+ runId: 'run-test-manager-rework-closure'
1236
+ });
1237
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
1238
+
1239
+ assert.equal(closure.adjudication.health, 'rejected');
1240
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_rejected');
1241
+ assert.equal(closure.acceptedTestEvidenceRef, null);
1242
+ assert.equal(handoff, null);
1243
+ } finally {
1244
+ await rm(root, { recursive: true, force: true });
1245
+ }
1246
+ });
1247
+
1248
+ test('ready goal-verify closure accepts reviewed goal evidence and records truthAlignment for ship', async () => {
1249
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-ready-'));
1250
+ try {
1251
+ await initProject(root);
1252
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1253
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify');
1254
+ await writeGoalVerifyStageArtifacts(root, {});
1255
+
1256
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1257
+ decision: riskDecision,
1258
+ generatedAt,
1259
+ runId: 'run-goal-verify-ready-closure'
1260
+ });
1261
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1262
+ const truthAlignment = await readTruthAlignmentProjection(root, riskDecision.scope);
1263
+ const chain = await inspectFullStageChain(root, 'master');
1264
+ const goalVerifyFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'goal-verify'))).sort();
1265
+
1266
+ assert.equal(closure.adjudication.health, 'ready_for_ship');
1267
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'accepted');
1268
+ assert.equal(closure.acceptedGoalVerificationRef?.ref, '.sdd/runs/master/goal-verify/goal-verification-v1.md');
1269
+ assert.equal(handoff, null);
1270
+ assert.equal(closure.adjudication.closureRefs.truthAlignmentProjectionRef?.kind, 'projection');
1271
+ assert.equal(truthAlignment?.payload.status, 'aligned');
1272
+ assert.equal(chain.status, 'partial');
1273
+ assert.equal(chain.finalHealth, 'ready_for_ship');
1274
+ assert.equal(chain.projectionCounts.completedStages, 7);
1275
+ assert.equal(truthAlignment?.payload.sourceStage, 'goal-verify');
1276
+ assert.equal(truthAlignment?.payload.acceptedRealityRefs.some((inputRef) => inputRef.ref === '.sdd/runs/master/goal-verify/goal-verification-v1.md'), true);
1277
+ assert.deepEqual(goalVerifyFiles, ['goal-review-v1.md', 'goal-verification-v1.md', 'goal-verify-collaboration-contract-v1.md', 'goal-verify-manager-v1.md']);
1278
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.7')));
1279
+ } finally {
1280
+ await rm(root, { recursive: true, force: true });
1281
+ }
1282
+ });
1283
+
1284
+ test('goal-verify closure rejects incomplete acceptance coverage without recording handoff', async () => {
1285
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-incomplete-coverage-'));
1286
+ try {
1287
+ await initProject(root);
1288
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1289
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-incomplete-coverage');
1290
+ await writeGoalVerifyStageArtifacts(root, { coverageComplete: false });
1291
+
1292
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1293
+ decision: riskDecision,
1294
+ generatedAt,
1295
+ runId: 'run-goal-verify-incomplete-coverage-closure'
1296
+ });
1297
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1298
+
1299
+ assert.equal(closure.adjudication.health, 'rejected');
1300
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_incomplete_coverage');
1301
+ assert.equal(closure.acceptedGoalVerificationRef, null);
1302
+ assert.equal(handoff, null);
1303
+ } finally {
1304
+ await rm(root, { recursive: true, force: true });
1305
+ }
1306
+ });
1307
+
1308
+ test('goal-verify closure rejects stale accepted test handoff without recording handoff', async () => {
1309
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-stale-test-'));
1310
+ try {
1311
+ await initProject(root);
1312
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1313
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-stale');
1314
+ await writeFile(path.join(root, '.sdd', 'runs', 'master', 'test', 'validation-v1.md'), validationEvidenceArtifact('stale-execution-hash', 'passed', true), 'utf8');
1315
+ await writeGoalVerifyStageArtifacts(root, {});
1316
+
1317
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1318
+ decision: riskDecision,
1319
+ generatedAt,
1320
+ runId: 'run-goal-verify-stale-test-closure'
1321
+ });
1322
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1323
+
1324
+ assert.equal(closure.adjudication.health, 'rejected');
1325
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_missing_upstream');
1326
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
1327
+ assert.equal(closure.acceptedGoalVerificationRef, null);
1328
+ assert.equal(handoff, null);
1329
+ } finally {
1330
+ await rm(root, { recursive: true, force: true });
1331
+ }
1332
+ });
1333
+
1334
+ test('goal-verify closure rejects open durable gaps without recording handoff', async () => {
1335
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-open-gap-'));
1336
+ try {
1337
+ await initProject(root);
1338
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1339
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-open-gap');
1340
+ await writeGoalVerifyStageArtifacts(root, { durableGapCount: 1 });
1341
+
1342
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1343
+ decision: riskDecision,
1344
+ generatedAt,
1345
+ runId: 'run-goal-verify-open-gap-closure'
1346
+ });
1347
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1348
+
1349
+ assert.equal(closure.adjudication.health, 'rejected');
1350
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_open_gap');
1351
+ assert.equal(closure.acceptedGoalVerificationRef, null);
1352
+ assert.equal(handoff, null);
1353
+ } finally {
1354
+ await rm(root, { recursive: true, force: true });
1355
+ }
1356
+ });
1357
+
1358
+ test('goal-verify closure rejects blocking goal review without recording handoff', async () => {
1359
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-blocking-review-'));
1360
+ try {
1361
+ await initProject(root);
1362
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1363
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-blocking-review');
1364
+ await writeGoalVerifyStageArtifacts(root, { verdict: 'changes_requested', blockingCount: 1 });
1365
+
1366
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1367
+ decision: riskDecision,
1368
+ generatedAt,
1369
+ runId: 'run-goal-verify-blocking-review-closure'
1370
+ });
1371
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1372
+
1373
+ assert.equal(closure.adjudication.health, 'rejected');
1374
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_missing_review');
1375
+ assert.equal(closure.acceptedGoalVerificationRef, null);
1376
+ assert.equal(handoff, null);
1377
+ } finally {
1378
+ await rm(root, { recursive: true, force: true });
1379
+ }
1380
+ });
1381
+
1382
+ test('goal-verify closure rejects goal-verify-manager rework recommendation without recording handoff', async () => {
1383
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-manager-rework-'));
1384
+ try {
1385
+ await initProject(root);
1386
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1387
+ await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-manager-rework');
1388
+ await writeGoalVerifyStageArtifacts(root, { recommendation: 'request_clarification' });
1389
+
1390
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1391
+ decision: riskDecision,
1392
+ generatedAt,
1393
+ runId: 'run-goal-verify-manager-rework-closure'
1394
+ });
1395
+ const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
1396
+
1397
+ assert.equal(closure.adjudication.health, 'rejected');
1398
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_rejected');
1399
+ assert.equal(closure.acceptedGoalVerificationRef, null);
1400
+ assert.equal(handoff, null);
1401
+ } finally {
1402
+ await rm(root, { recursive: true, force: true });
1403
+ }
1404
+ });
1405
+
1406
+
1407
+ test('ready ship closure accepts reviewed readiness evidence and records final ship-ready projection', async () => {
1408
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-ready-'));
1409
+ try {
1410
+ await initProject(root);
1411
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1412
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship');
1413
+ await writeShipStageArtifacts(root, {});
1414
+
1415
+ const closure = await reconcileShipCollaborationClosure(root, {
1416
+ decision: riskDecision,
1417
+ generatedAt,
1418
+ runId: 'run-ship-ready-closure'
1419
+ });
1420
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'ship' });
1421
+ const shipFiles = await readdir(path.join(root, '.sdd', 'runs', 'master', 'ship'));
1422
+
1423
+ assert.equal(closure.adjudication.health, 'ship_ready');
1424
+ assert.equal(closure.adjudication.stageDecision.status, 'completed');
1425
+ assert.equal(closure.adjudication.handoffPacket, null);
1426
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'accepted');
1427
+ assert.equal(closure.acceptedShipReadinessRef?.ref, '.sdd/runs/master/ship/ship-readiness-v1.md');
1428
+ assert.equal(closure.releaseDocumentRef, null);
1429
+ assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'release_review', 'ship_readiness']);
1430
+ assert.deepEqual(shipFiles, ['release-review-v1.md', 'ship-collaboration-contract-v1.md', 'ship-manager-v1.md', 'ship-readiness-v1.md']);
1431
+ await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.9')));
1432
+ } finally {
1433
+ await rm(root, { recursive: true, force: true });
1434
+ }
1435
+ });
1436
+
1437
+ test('ship required release review is satisfied by registered release-review artifact', async () => {
1438
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-required-review-'));
1439
+ try {
1440
+ await initProject(root);
1441
+ const riskDecision: LifecycleRiskDecision = {
1442
+ ...decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']),
1443
+ requiredEvidence: [{
1444
+ id: 'phase8-risk:command-evidence',
1445
+ acceptanceRef: 'phase8-risk-decision',
1446
+ kind: 'command',
1447
+ requiredBefore: 'ship',
1448
+ refs: [ref('task', 'T1'), ref('task', 'T2')],
1449
+ reasons: ['Task declares high-risk tags: evidence-semantics.']
1450
+ }],
1451
+ requiredReviews: [{
1452
+ id: 'phase8-risk:release-review',
1453
+ kind: 'release',
1454
+ requiredBefore: 'ship',
1455
+ reasons: ['Phase 8 risk signal requires release review.']
1456
+ }]
1457
+ };
1458
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-required-review');
1459
+ await writeShipStageArtifacts(root, {});
1460
+
1461
+ const closure = await reconcileShipCollaborationClosure(root, {
1462
+ decision: riskDecision,
1463
+ generatedAt,
1464
+ runId: 'run-ship-required-review-closure'
1465
+ });
1466
+ const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'ship' });
1467
+
1468
+ assert.equal(closure.adjudication.health, 'ship_ready');
1469
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'accepted');
1470
+ assert.equal(closure.acceptedShipReadinessRef?.ref, '.sdd/runs/master/ship/ship-readiness-v1.md');
1471
+ assert.equal(registered.some((artifact) => artifact.kind === 'release_review'), true);
1472
+ } finally {
1473
+ await rm(root, { recursive: true, force: true });
1474
+ }
1475
+ });
1476
+
1477
+ test('full-stage chain diagnostic summarizes ready chain from runtime projections', async () => {
1478
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-ready-'));
1479
+ try {
1480
+ await initProject(root);
1481
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1482
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-full-chain-ready-for-ship');
1483
+ await writeShipStageArtifacts(root, {});
1484
+ const shipClosure = await reconcileShipCollaborationClosure(root, {
1485
+ decision: riskDecision,
1486
+ generatedAt,
1487
+ runId: 'run-full-chain-ship-ready'
1488
+ });
1489
+ const chain = await inspectFullStageChain(root, 'master');
1490
+
1491
+ assert.equal(shipClosure.adjudication.health, 'ship_ready');
1492
+ assert.equal(chain.status, 'ready');
1493
+ assert.equal(chain.finalHealth, 'ship_ready');
1494
+ assert.equal(chain.projectionCounts.adjudications, 8);
1495
+ assert.equal(chain.projectionCounts.completedStages, 8);
1496
+ assert.equal(chain.projectionCounts.validatedCollaborationContracts, 8);
1497
+ assert.equal(chain.projectionCounts.handoffs, 6);
1498
+ assert.deepEqual(chain.stages.map((stage) => stage.stage), ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1499
+ assert.equal(chain.stages.every((stage) => !stage.acceptedRef?.ref.startsWith('runs/')), true);
1500
+ assert.equal(chain.stages.every((stage) => stage.acceptedRef?.ref.includes('evidence/artifacts') === false), true);
1501
+ assert.equal(chain.stages.every((stage) => stage.acceptedRef?.ref.startsWith('artifacts/') === false), true);
1502
+ } finally {
1503
+ await rm(root, { recursive: true, force: true });
1504
+ }
1505
+ });
1506
+
1507
+ test('runtime close replay does not create or mutate Markdown files', async () => {
1508
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-markdown-immutable-'));
1509
+ try {
1510
+ await initProject(root);
1511
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1512
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-markdown-immutable-ready-for-ship');
1513
+ await writeShipStageArtifacts(root, {});
1514
+ await reconcileShipCollaborationClosure(root, {
1515
+ decision: riskDecision,
1516
+ generatedAt,
1517
+ runId: 'run-markdown-immutable-ship-ready'
1518
+ });
1519
+ const before = await markdownSnapshot(root);
1520
+
1521
+ await reconcileSpecCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-spec' });
1522
+ await reconcilePlanCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-plan' });
1523
+ await reconcileTasksCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-tasks' });
1524
+ await reconcileVerifiesCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-verifies' });
1525
+ await reconcileDoCollaborationClosure(root, {
1526
+ decision: riskDecision,
1527
+ allowedChangedFileRefs: ['packages/app/login.ts'],
1528
+ generatedAt,
1529
+ runId: 'run-markdown-immutable-replay-do'
1530
+ });
1531
+ await reconcileTestCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-test' });
1532
+ await reconcileGoalVerifyCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-goal-verify' });
1533
+ await reconcileShipCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-ship' });
1534
+ const after = await markdownSnapshot(root);
1535
+
1536
+ assert.deepEqual(after, before);
1537
+ assert.equal([...after.keys()].some((file) => file.startsWith('runs/')), false);
1538
+ } finally {
1539
+ await rm(root, { recursive: true, force: true });
1540
+ }
1541
+ });
1542
+
1543
+ test('ship closure rejects missing release review without final readiness', async () => {
1544
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-missing-review-'));
1545
+ try {
1546
+ await initProject(root);
1547
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1548
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-missing-review');
1549
+ await writeShipStageArtifacts(root, { omitReview: true });
1550
+
1551
+ const closure = await reconcileShipCollaborationClosure(root, {
1552
+ decision: riskDecision,
1553
+ generatedAt,
1554
+ runId: 'run-ship-missing-review-closure'
1555
+ });
1556
+
1557
+ assert.equal(closure.adjudication.health, 'rejected');
1558
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_missing_review');
1559
+ assert.equal(closure.acceptedShipReadinessRef, null);
1560
+ } finally {
1561
+ await rm(root, { recursive: true, force: true });
1562
+ }
1563
+ });
1564
+
1565
+ test('ship closure rejects stale truthAlignment reality refs without final readiness', async () => {
1566
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-stale-truth-alignment-'));
1567
+ try {
1568
+ await initProject(root);
1569
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1570
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-stale');
1571
+ await writeFile(path.join(root, '.sdd', 'runs', 'master', 'goal-verify', 'goal-verification-v1.md'), `${goalVerificationArtifact('passed', true, 0, 0)}\nDrift after truthAlignment.\n`, 'utf8');
1572
+ await writeShipStageArtifacts(root, {});
1573
+
1574
+ const closure = await reconcileShipCollaborationClosure(root, {
1575
+ decision: riskDecision,
1576
+ generatedAt,
1577
+ runId: 'run-ship-stale-truth-alignment-closure'
1578
+ });
1579
+
1580
+ assert.equal(closure.adjudication.health, 'rejected');
1581
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_missing_upstream');
1582
+ assert.match(closure.adjudication.closureRefs.reasons.join(' '), /truthAlignment|stale/);
1583
+ assert.equal(closure.acceptedShipReadinessRef, null);
1584
+ } finally {
1585
+ await rm(root, { recursive: true, force: true });
1586
+ }
1587
+ });
1588
+
1589
+ test('ship closure rejects open readiness blockers without final readiness', async () => {
1590
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-open-blocker-'));
1591
+ try {
1592
+ await initProject(root);
1593
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1594
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-open-blocker');
1595
+ await writeShipStageArtifacts(root, { doctorBlockerCount: 1 });
1596
+
1597
+ const closure = await reconcileShipCollaborationClosure(root, {
1598
+ decision: riskDecision,
1599
+ generatedAt,
1600
+ runId: 'run-ship-open-blocker-closure'
1601
+ });
1602
+
1603
+ assert.equal(closure.adjudication.health, 'rejected');
1604
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_open_blocker');
1605
+ assert.equal(closure.acceptedShipReadinessRef, null);
1606
+ } finally {
1607
+ await rm(root, { recursive: true, force: true });
1608
+ }
1609
+ });
1610
+
1611
+ test('ship closure rejects release document hash drift when release doc is declared', async () => {
1612
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-release-drift-'));
1613
+ try {
1614
+ await initProject(root);
1615
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1616
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-release-drift');
1617
+ const releaseContent = '# Release Notes\n\nReady to ship.\n';
1618
+ await writeFile(path.join(root, 'specs', 'master', 'release.md'), releaseContent, 'utf8');
1619
+ await writeShipStageArtifacts(root, { release: { ref: 'specs/master/release.md', hash: hashDocumentContent(releaseContent) } });
1620
+ await writeFile(path.join(root, 'specs', 'master', 'release.md'), `${releaseContent}\nDrift after readiness.\n`, 'utf8');
1621
+
1622
+ const closure = await reconcileShipCollaborationClosure(root, {
1623
+ decision: riskDecision,
1624
+ generatedAt,
1625
+ runId: 'run-ship-release-drift-closure'
1626
+ });
1627
+
1628
+ assert.equal(closure.adjudication.health, 'rejected');
1629
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_release_drift');
1630
+ assert.equal(closure.acceptedShipReadinessRef, null);
1631
+ assert.equal(closure.releaseDocumentRef, null);
1632
+ } finally {
1633
+ await rm(root, { recursive: true, force: true });
1634
+ }
1635
+ });
1636
+
1637
+ test('ship closure rejects authority-violating readiness evidence', async () => {
1638
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-authority-'));
1639
+ try {
1640
+ await initProject(root);
1641
+ const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
1642
+ await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-authority');
1643
+ await writeShipStageArtifacts(root, { authorityAttempts: ['ship_ready'] });
1644
+
1645
+ const closure = await reconcileShipCollaborationClosure(root, {
1646
+ decision: riskDecision,
1647
+ generatedAt,
1648
+ runId: 'run-ship-authority-closure'
1649
+ });
1650
+
1651
+ assert.equal(closure.adjudication.health, 'rejected');
1652
+ assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_authority_violation');
1653
+ assert.equal(closure.acceptedShipReadinessRef, null);
1654
+ } finally {
1655
+ await rm(root, { recursive: true, force: true });
1656
+ }
1657
+ });
1658
+
1659
+ function decision(profile: LifecycleRiskDecision['profile'], requiredStages: LifecycleRiskDecision['requiredStages']): LifecycleRiskDecision {
1660
+ return {
1661
+ contract: LIFECYCLE_RISK_DECISION_CONTRACT_VERSION,
1662
+ scope: { branch: 'master' },
1663
+ profile,
1664
+ requiredStages,
1665
+ skippedStages: [],
1666
+ blockedStages: profile === 'blocked' ? ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship'] : [],
1667
+ requiredEvidence: [],
1668
+ requiredReviews: [],
1669
+ humanCheckpointRequired: profile === 'blocked',
1670
+ approvalPolicy: profile === 'blocked' ? 'blocked' : profile === 'direct' ? 'auto-allow' : 'review-required',
1671
+ inputRefs: [ref('document', 'specs/master/phase9.1-spec.md')],
1672
+ signalRefs: [],
1673
+ policyVersion: 'test-policy',
1674
+ inputHash: 'input-hash',
1675
+ confidence: 'high',
1676
+ reasons: [`${profile} test decision`],
1677
+ generatedAt
1678
+ };
1679
+ }
1680
+
1681
+ function stageClosure(profile: StageCollaborationProfile, overrides: { items?: CandidateItem[]; authorityAttempts?: StageClosureRequest['authorityAttempts']; canonicalSpecRef?: ReturnType<typeof ref> } = {}): { coordination: SpecManagerCoordinationRecord; closureRequest: StageClosureRequest } {
1682
+ const workOrder = buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt);
1683
+ const workOrderRef = ref('projection', `spec-work-order/${profile.scope.branch}`);
1684
+ const candidateRef = overrides.canonicalSpecRef ?? ref('document', 'specs/master/spec.md');
1685
+ const reviewRef = ref('artifact', '.sdd/runs/master/spec/spec-review-v1.md');
1686
+ const items = overrides.items ?? [candidateItem('find-1', 'advisory_finding'), candidateItem('cap-1', 'capability_suggestion'), candidateItem('handoff-1', 'handoff_proposal')];
1687
+ const unresolvedAmbiguityIds = items.filter((item) => item.kind === 'blocking_ambiguity').map((item) => item.id);
1688
+ const candidate: SpecDocumentCandidate = {
1689
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1690
+ candidateId: `candidate-${profile.scope.branch}`,
1691
+ stage: 'spec',
1692
+ scope: profile.scope,
1693
+ producedBy: 'spec-drafter',
1694
+ inputRefs: workOrder.inputRefs,
1695
+ evidenceRefs: items.flatMap((item) => item.refs),
1696
+ acceptedItems: items,
1697
+ generatedAt
1698
+ };
1699
+ const capabilityFindings = items
1700
+ .filter((item) => item.kind === 'capability_suggestion')
1701
+ .map((item): CapabilityFinding => ({
1702
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1703
+ findingId: `finding-${item.id}`,
1704
+ stage: 'spec',
1705
+ scope: profile.scope,
1706
+ capabilityDomain: 'solution-design',
1707
+ summary: item.summary,
1708
+ refs: item.refs,
1709
+ generatedAt
1710
+ }));
1711
+ const capabilityFindingRefs = capabilityFindings.map((finding) => ref('artifact', `.sdd/runs/master/spec/spec-capability-finding-${finding.findingId}.md`));
1712
+ const review: SpecReviewResult = {
1713
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1714
+ reviewId: `review-${profile.scope.branch}`,
1715
+ stage: 'spec',
1716
+ scope: profile.scope,
1717
+ reviewer: 'spec-reviewer',
1718
+ candidateRef,
1719
+ verdict: unresolvedAmbiguityIds.length > 0 ? 'blocked' : 'approved',
1720
+ findings: items.map((item) => item.summary),
1721
+ evidenceRefs: items.flatMap((item) => item.refs),
1722
+ generatedAt
1723
+ };
1724
+ const agentTeamRefs = [candidateRef, reviewRef, ...capabilityFindingRefs];
1725
+ const coordination: SpecManagerCoordinationRecord = {
1726
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1727
+ coordinationId: `coordination-${profile.scope.branch}`,
1728
+ stage: 'spec',
1729
+ scope: profile.scope,
1730
+ stageManager: 'spec-manager',
1731
+ workOrderRef,
1732
+ agentTeamRefs,
1733
+ recommendation: unresolvedAmbiguityIds.length > 0 ? 'request_clarification' : 'close_stage',
1734
+ generatedAt
1735
+ };
1736
+ const closureRequest: StageClosureRequest = {
1737
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1738
+ closureRequestId: `closure-${profile.scope.branch}`,
1739
+ stage: 'spec',
1740
+ scope: profile.scope,
1741
+ submittedBy: 'spec-manager',
1742
+ workOrderRef,
1743
+ coordinationRef: ref('artifact', '.sdd/runs/master/spec/spec-manager-v1.md'),
1744
+ candidateRef,
1745
+ reviewRefs: [reviewRef],
1746
+ capabilityFindingRefs,
1747
+ evidenceRefs: [workOrderRef, ...agentTeamRefs, ...items.flatMap((item) => item.refs)],
1748
+ candidate,
1749
+ reviewResults: [review],
1750
+ capabilityFindings,
1751
+ unresolvedAmbiguityIds,
1752
+ authorityAttempts: overrides.authorityAttempts ?? [],
1753
+ managerRecommendation: unresolvedAmbiguityIds.length > 0 ? 'request_clarification' : 'close_stage',
1754
+ generatedAt
1755
+ };
1756
+ return { coordination, closureRequest };
1757
+ }
1758
+
1759
+ type CandidateItem = { id: string; kind: SpecProposalItemKind; summary: string; refs: ReturnType<typeof ref>[] };
1760
+
1761
+ function candidateItem(id: string, kind: SpecProposalItemKind, refs = [ref('document', `specs/master/${id}.md`)]): CandidateItem {
1762
+ return { id, kind, summary: `${kind} ${id}`, refs };
1763
+ }
1764
+
1765
+ async function writeAcceptedSpecClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
1766
+ const specContent = reviewedSpec();
1767
+ const specHash = hashDocumentContent(specContent);
1768
+ await writeFile(path.join(root, 'specs', 'master', 'spec.md'), specContent, 'utf8');
1769
+ await writeSpecStageArtifacts(root, { specHash });
1770
+ const closure = await reconcileSpecCollaborationClosure(root, {
1771
+ decision: riskDecision,
1772
+ generatedAt,
1773
+ runId
1774
+ });
1775
+ assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'accepted');
1776
+ assert.equal(closure.acceptedSpecRef?.hash, specHash);
1777
+ }
1778
+
1779
+ async function writeAcceptedPlanClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
1780
+ await writeAcceptedSpecClosure(root, riskDecision, `${runId}-spec`);
1781
+ const planContent = reviewedPlan();
1782
+ const planHash = hashDocumentContent(planContent);
1783
+ await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
1784
+ await writePlanStageArtifacts(root, { planHash });
1785
+ const closure = await reconcilePlanCollaborationClosure(root, {
1786
+ decision: riskDecision,
1787
+ generatedAt,
1788
+ runId
1789
+ });
1790
+ assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'accepted');
1791
+ assert.equal(closure.acceptedPlanRef?.hash, planHash);
1792
+ }
1793
+
1794
+ async function writeAcceptedTasksClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
1795
+ await writeAcceptedPlanClosure(root, riskDecision, `${runId}-plan`);
1796
+ const tasksContent = reviewedTasks();
1797
+ const tasksHash = hashDocumentContent(tasksContent);
1798
+ await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
1799
+ await writeTasksStageArtifacts(root, { tasksHash });
1800
+ const closure = await reconcileTasksCollaborationClosure(root, {
1801
+ decision: riskDecision,
1802
+ generatedAt,
1803
+ runId
1804
+ });
1805
+ assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'accepted');
1806
+ assert.equal(closure.acceptedTasksRef?.hash, tasksHash);
1807
+ }
1808
+
1809
+ async function writeAcceptedVerifiesClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
1810
+ await writeAcceptedTasksClosure(root, riskDecision, `${runId}-tasks`);
1811
+ const tasksContent = reviewedTasks();
1812
+ const verifyContent = reviewedVerify(tasksContent);
1813
+ const verifyHash = hashDocumentContent(verifyContent);
1814
+ await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
1815
+ await writeVerifiesStageArtifacts(root, { verifyHash });
1816
+ const closure = await reconcileVerifiesCollaborationClosure(root, {
1817
+ decision: riskDecision,
1818
+ generatedAt,
1819
+ runId
1820
+ });
1821
+ assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'accepted');
1822
+ assert.equal(closure.acceptedVerifyRef?.hash, verifyHash);
1823
+ }
1824
+
1825
+ async function writeAcceptedDoClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<{ changedFileRef: string; changedFileHash: string }> {
1826
+ await writeAcceptedVerifiesClosure(root, riskDecision, `${runId}-verifies`);
1827
+ const changedFileRef = 'packages/app/login.ts';
1828
+ const changedFileContent = 'export const login = () => true;\n';
1829
+ await writeWorkspaceFile(root, changedFileRef, changedFileContent);
1830
+ const changedFileHash = hashDocumentContent(changedFileContent);
1831
+ await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
1832
+ const closure = await reconcileDoCollaborationClosure(root, {
1833
+ decision: riskDecision,
1834
+ allowedChangedFileRefs: [changedFileRef],
1835
+ generatedAt,
1836
+ runId
1837
+ });
1838
+ assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'accepted');
1839
+ assert.equal(closure.acceptedImplementationRef?.ref, '.sdd/runs/master/do/implementation-v1.md');
1840
+ return { changedFileRef, changedFileHash };
1841
+ }
1842
+
1843
+ async function writeAcceptedTestClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
1844
+ await writeAcceptedDoClosure(root, riskDecision, `${runId}-do`);
1845
+ await writeTestStageArtifacts(root, {});
1846
+ const closure = await reconcileTestCollaborationClosure(root, {
1847
+ decision: riskDecision,
1848
+ generatedAt,
1849
+ runId
1850
+ });
1851
+ assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'accepted');
1852
+ assert.equal(closure.acceptedTestEvidenceRef?.ref, '.sdd/runs/master/test/validation-v1.md');
1853
+ }
1854
+
1855
+ async function writeAcceptedGoalVerifyClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<{ acceptedGoalVerificationRef: RuntimeRef }> {
1856
+ await writeAcceptedTestClosure(root, riskDecision, `${runId}-test`);
1857
+ await writeGoalVerifyStageArtifacts(root, {});
1858
+ const closure = await reconcileGoalVerifyCollaborationClosure(root, {
1859
+ decision: riskDecision,
1860
+ generatedAt,
1861
+ runId
1862
+ });
1863
+ const acceptedGoalVerificationRef = closure.acceptedGoalVerificationRef;
1864
+ assert.equal(closure.adjudication.health, 'ready_for_ship');
1865
+ assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'accepted');
1866
+ assert.ok(acceptedGoalVerificationRef);
1867
+ assert.equal(acceptedGoalVerificationRef.ref, '.sdd/runs/master/goal-verify/goal-verification-v1.md');
1868
+ assert.ok(acceptedGoalVerificationRef.hash);
1869
+ assert.equal(closure.adjudication.closureRefs.truthAlignmentProjectionRef?.kind, 'projection');
1870
+ return { acceptedGoalVerificationRef };
1871
+ }
1872
+
1873
+
1874
+ async function writeWorkspaceFile(root: string, fileRef: string, content: string): Promise<void> {
1875
+ await mkdir(path.dirname(path.join(root, fileRef)), { recursive: true });
1876
+ await writeFile(path.join(root, fileRef), content, 'utf8');
1877
+ }
1878
+
1879
+ async function writeSpecStageArtifacts(root: string, options: { specHash: string; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation']; omitContract?: boolean }): Promise<void> {
1880
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'spec');
1881
+ await mkdir(dir, { recursive: true });
1882
+ if (!options.omitContract) {
1883
+ await writeFile(path.join(dir, 'spec-collaboration-contract-v1.md'), specCollaborationContractArtifact(specWorkOrderId()), 'utf8');
1884
+ }
1885
+ await writeFile(path.join(dir, 'scout.md'), specScoutArtifact(), 'utf8');
1886
+ const review = specReviewArtifact(options.specHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
1887
+ const reviewHash = hashDocumentContent(review);
1888
+ await writeFile(path.join(dir, 'spec-review-v1.md'), review, 'utf8');
1889
+ await writeFile(path.join(dir, 'spec-manager-v1.md'), specManagerArtifact(options.specHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
1890
+ }
1891
+
1892
+ function specWorkOrderId(): string {
1893
+ const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
1894
+ return buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt).workOrderId;
1895
+ }
1896
+
1897
+ function specCollaborationContractArtifact(workOrderId: string): string {
1898
+ return `---
1899
+ contract: sdd-stage-collaboration-contract-v1
1900
+ branch: master
1901
+ stage: spec
1902
+ kind: stage_collaboration_contract
1903
+ producer: spec-manager
1904
+ workOrderId: ${workOrderId}
1905
+ selectedAgents:
1906
+ - scout
1907
+ - spec-drafter
1908
+ - spec-reviewer
1909
+ selectedSkills:
1910
+ - cap.norm_discovery
1911
+ - cap.uncertainty_resolution
1912
+ selectedMaterialPacks:
1913
+ - project-norms
1914
+ - uncertainty-map
1915
+ evidenceContracts:
1916
+ - sdd-spec-scout-artifact-v1
1917
+ - sdd-spec-review-artifact-v1
1918
+ - sdd-spec-manager-artifact-v1
1919
+ checkpointPlan:
1920
+ - scout_context
1921
+ - review_before_closure
1922
+ writableRefs:
1923
+ - specs/master/spec.md
1924
+ - .sdd/runs/master/spec/scout.md
1925
+ - .sdd/runs/master/spec/spec-review-v1.md
1926
+ - .sdd/runs/master/spec/spec-manager-v1.md
1927
+ authorityAttempts: []
1928
+ maxReworkAttempts: 2
1929
+ allowAutoWithinStage: true
1930
+ allowAutoNextStage: false
1931
+ authorityCeiling: proposal_input
1932
+ ---
1933
+
1934
+ # Spec Collaboration Contract
1935
+
1936
+ Spec-manager selected the spec-stage team and evidence strategy within runtime constraints.
1937
+ `;
1938
+ }
1939
+
1940
+ interface StageContractFixtureWorkOrder {
1941
+ workOrderId: string;
1942
+ stage: SddStage;
1943
+ stageManager: string;
1944
+ agentTeam: readonly string[];
1945
+ requiredCapabilities: readonly string[];
1946
+ materialPackIds: readonly string[];
1947
+ requiredOutputKinds: readonly string[];
1948
+ authorityCeiling: string;
1949
+ }
1950
+
1951
+ async function writeStageCollaborationContract(root: string, stage: SddStage, writableRefs: readonly string[]): Promise<void> {
1952
+ const dir = path.join(root, '.sdd', 'runs', 'master', stage);
1953
+ const workOrder = stageContractFixtureWorkOrder(stage);
1954
+ await writeFile(path.join(dir, `${stage}-collaboration-contract-v1.md`), stageCollaborationContractArtifact(workOrder, writableRefs), 'utf8');
1955
+ }
1956
+
1957
+ function stageContractFixtureWorkOrder(stage: SddStage): StageContractFixtureWorkOrder {
1958
+ const scope = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']).scope;
1959
+ const profileRef = ref('projection', `profile/${stage}`);
1960
+ const inputRefs = [ref('document', 'specs/master/phase9.1-spec.md')];
1961
+ if (stage === 'plan') return buildPlanStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1962
+ if (stage === 'tasks') return buildTasksStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1963
+ if (stage === 'verifies') return buildVerifiesStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1964
+ if (stage === 'do') return buildDoStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1965
+ if (stage === 'test') return buildTestStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1966
+ if (stage === 'goal-verify') return buildGoalVerifyStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1967
+ if (stage === 'ship') return buildShipStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
1968
+ throw new Error(`Unsupported stage contract fixture ${stage}`);
1969
+ }
1970
+
1971
+ function stageCollaborationContractArtifact(workOrder: StageContractFixtureWorkOrder, writableRefs: readonly string[]): string {
1972
+ return `---
1973
+ contract: sdd-stage-collaboration-contract-v1
1974
+ branch: master
1975
+ stage: ${workOrder.stage}
1976
+ kind: stage_collaboration_contract
1977
+ producer: ${workOrder.stageManager}
1978
+ workOrderId: ${workOrder.workOrderId}
1979
+ selectedAgents:
1980
+ ${yamlList(workOrder.agentTeam)}
1981
+ selectedSkills:
1982
+ ${yamlList(workOrder.requiredCapabilities.map((capability) => `cap.${capability}`))}
1983
+ selectedMaterialPacks:
1984
+ ${yamlList(workOrder.materialPackIds)}
1985
+ evidenceContracts:
1986
+ ${yamlList(expectedStageArtifactContracts(workOrder.stage, workOrder.requiredOutputKinds))}
1987
+ checkpointPlan:
1988
+ ${yamlList(workOrder.requiredOutputKinds)}
1989
+ writableRefs:
1990
+ ${yamlList(writableRefs)}
1991
+ authorityAttempts: []
1992
+ maxReworkAttempts: 2
1993
+ allowAutoWithinStage: true
1994
+ allowAutoNextStage: false
1995
+ authorityCeiling: ${workOrder.authorityCeiling}
1996
+ ---
1997
+
1998
+ # ${workOrder.stage} Collaboration Contract
1999
+
2000
+ ${workOrder.stageManager} selected the stage team and evidence strategy within runtime constraints.
2001
+ `;
2002
+ }
2003
+
2004
+ function yamlList(values: readonly string[]): string {
2005
+ return values.map((value) => ` - ${value}`).join('\n');
2006
+ }
2007
+
2008
+ function specScoutArtifact(): string {
2009
+ return `---
2010
+ contract: sdd-spec-scout-artifact-v1
2011
+ branch: master
2012
+ stage: spec
2013
+ kind: scout_context
2014
+ producer: scout
2015
+ ---
2016
+
2017
+ # Scout Context
2018
+
2019
+ The scout captured project norms and constraints for the spec drafter and reviewer.
2020
+ `;
2021
+ }
2022
+
2023
+ function specReviewArtifact(specHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2024
+ return `---
2025
+ contract: sdd-spec-review-artifact-v1
2026
+ branch: master
2027
+ stage: spec
2028
+ kind: spec_review
2029
+ producer: spec-reviewer
2030
+ targetRef: specs/master/spec.md
2031
+ targetHash: ${specHash}
2032
+ verdict: ${verdict}
2033
+ findingCount: ${blockingCount}
2034
+ blockingCount: ${blockingCount}
2035
+ ---
2036
+
2037
+ # Spec Review
2038
+
2039
+ Verdict: ${verdict}.
2040
+ `;
2041
+ }
2042
+
2043
+ function specManagerArtifact(specHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2044
+ return `---
2045
+ contract: sdd-spec-manager-artifact-v1
2046
+ branch: master
2047
+ stage: spec
2048
+ kind: manager_closure_request
2049
+ producer: spec-manager
2050
+ targetRef: specs/master/spec.md
2051
+ targetHash: ${specHash}
2052
+ reviewRef: .sdd/runs/master/spec/spec-review-v1.md
2053
+ reviewHash: ${reviewHash}
2054
+ recommendation: ${recommendation}
2055
+ ---
2056
+
2057
+ # Spec Manager Coordination
2058
+
2059
+ Recommendation: ${recommendation}.
2060
+ `;
2061
+ }
2062
+
2063
+ async function writePlanStageArtifacts(root: string, options: { planHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2064
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'plan');
2065
+ await mkdir(dir, { recursive: true });
2066
+ if (!options.omitContract) {
2067
+ await writeStageCollaborationContract(root, 'plan', ['specs/master/plan.md', '.sdd/runs/master/plan/plan-review-v1.md', '.sdd/runs/master/plan/plan-manager-v1.md']);
2068
+ }
2069
+ const review = planReviewArtifact(options.planHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2070
+ const reviewHash = hashDocumentContent(review);
2071
+ if (!options.omitReview) {
2072
+ await writeFile(path.join(dir, 'plan-review-v1.md'), review, 'utf8');
2073
+ }
2074
+ await writeFile(path.join(dir, 'plan-manager-v1.md'), planManagerArtifact(options.planHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2075
+ }
2076
+
2077
+ function planReviewArtifact(planHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2078
+ return `---
2079
+ contract: sdd-plan-review-artifact-v1
2080
+ branch: master
2081
+ stage: plan
2082
+ kind: plan_review
2083
+ producer: plan-reviewer
2084
+ targetRef: specs/master/plan.md
2085
+ targetHash: ${planHash}
2086
+ verdict: ${verdict}
2087
+ findingCount: ${blockingCount}
2088
+ blockingCount: ${blockingCount}
2089
+ ---
2090
+
2091
+ # Plan Review
2092
+
2093
+ Verdict: ${verdict}.
2094
+ `;
2095
+ }
2096
+
2097
+ function planManagerArtifact(planHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2098
+ return `---
2099
+ contract: sdd-plan-manager-artifact-v1
2100
+ branch: master
2101
+ stage: plan
2102
+ kind: manager_closure_request
2103
+ producer: plan-manager
2104
+ targetRef: specs/master/plan.md
2105
+ targetHash: ${planHash}
2106
+ reviewRef: .sdd/runs/master/plan/plan-review-v1.md
2107
+ reviewHash: ${reviewHash}
2108
+ recommendation: ${recommendation}
2109
+ ---
2110
+
2111
+ # Plan Manager Coordination
2112
+
2113
+ Recommendation: ${recommendation}.
2114
+ `;
2115
+ }
2116
+
2117
+ async function writeTasksStageArtifacts(root: string, options: { tasksHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2118
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'tasks');
2119
+ await mkdir(dir, { recursive: true });
2120
+ if (!options.omitContract) {
2121
+ await writeStageCollaborationContract(root, 'tasks', ['specs/master/tasks.md', '.sdd/runs/master/tasks/tasks-review-v1.md', '.sdd/runs/master/tasks/tasks-manager-v1.md']);
2122
+ }
2123
+ const review = tasksReviewArtifact(options.tasksHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2124
+ const reviewHash = hashDocumentContent(review);
2125
+ if (!options.omitReview) {
2126
+ await writeFile(path.join(dir, 'tasks-review-v1.md'), review, 'utf8');
2127
+ }
2128
+ await writeFile(path.join(dir, 'tasks-manager-v1.md'), tasksManagerArtifact(options.tasksHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2129
+ }
2130
+
2131
+ function tasksReviewArtifact(tasksHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2132
+ return `---
2133
+ contract: sdd-tasks-review-artifact-v1
2134
+ branch: master
2135
+ stage: tasks
2136
+ kind: tasks_review
2137
+ producer: tasks-reviewer
2138
+ targetRef: specs/master/tasks.md
2139
+ targetHash: ${tasksHash}
2140
+ verdict: ${verdict}
2141
+ findingCount: ${blockingCount}
2142
+ blockingCount: ${blockingCount}
2143
+ ---
2144
+
2145
+ # Tasks Review
2146
+
2147
+ Verdict: ${verdict}.
2148
+ `;
2149
+ }
2150
+
2151
+ function tasksManagerArtifact(tasksHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2152
+ return `---
2153
+ contract: sdd-tasks-manager-artifact-v1
2154
+ branch: master
2155
+ stage: tasks
2156
+ kind: manager_closure_request
2157
+ producer: tasks-manager
2158
+ targetRef: specs/master/tasks.md
2159
+ targetHash: ${tasksHash}
2160
+ reviewRef: .sdd/runs/master/tasks/tasks-review-v1.md
2161
+ reviewHash: ${reviewHash}
2162
+ recommendation: ${recommendation}
2163
+ ---
2164
+
2165
+ # Tasks Manager Coordination
2166
+
2167
+ Recommendation: ${recommendation}.
2168
+ `;
2169
+ }
2170
+
2171
+ async function writeVerifiesStageArtifacts(root: string, options: { verifyHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2172
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'verifies');
2173
+ await mkdir(dir, { recursive: true });
2174
+ if (!options.omitContract) {
2175
+ await writeStageCollaborationContract(root, 'verifies', ['specs/master/verify.md', '.sdd/runs/master/verifies/verifies-review-v1.md', '.sdd/runs/master/verifies/verifies-manager-v1.md']);
2176
+ }
2177
+ const review = verifiesReviewArtifact(options.verifyHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2178
+ const reviewHash = hashDocumentContent(review);
2179
+ if (!options.omitReview) {
2180
+ await writeFile(path.join(dir, 'verifies-review-v1.md'), review, 'utf8');
2181
+ }
2182
+ await writeFile(path.join(dir, 'verifies-manager-v1.md'), verifiesManagerArtifact(options.verifyHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2183
+ }
2184
+
2185
+ function verifiesReviewArtifact(verifyHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2186
+ return `---
2187
+ contract: sdd-verifies-review-artifact-v1
2188
+ branch: master
2189
+ stage: verifies
2190
+ kind: verifies_review
2191
+ producer: verifies-reviewer
2192
+ targetRef: specs/master/verify.md
2193
+ targetHash: ${verifyHash}
2194
+ verdict: ${verdict}
2195
+ findingCount: ${blockingCount}
2196
+ blockingCount: ${blockingCount}
2197
+ ---
2198
+
2199
+ # Verifies Review
2200
+
2201
+ Verdict: ${verdict}.
2202
+ `;
2203
+ }
2204
+
2205
+ function verifiesManagerArtifact(verifyHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2206
+ return `---
2207
+ contract: sdd-verifies-manager-artifact-v1
2208
+ branch: master
2209
+ stage: verifies
2210
+ kind: manager_closure_request
2211
+ producer: verifies-manager
2212
+ targetRef: specs/master/verify.md
2213
+ targetHash: ${verifyHash}
2214
+ reviewRef: .sdd/runs/master/verifies/verifies-review-v1.md
2215
+ reviewHash: ${reviewHash}
2216
+ recommendation: ${recommendation}
2217
+ ---
2218
+
2219
+ # Verifies Manager Coordination
2220
+
2221
+ Recommendation: ${recommendation}.
2222
+ `;
2223
+ }
2224
+
2225
+ interface ChangedFileFixture {
2226
+ ref: string;
2227
+ hash: string;
2228
+ }
2229
+
2230
+ async function writeDoStageArtifacts(root: string, options: { changedFiles: ChangedFileFixture[]; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2231
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'do');
2232
+ await mkdir(dir, { recursive: true });
2233
+ if (!options.omitContract) {
2234
+ await writeStageCollaborationContract(root, 'do', ['.sdd/runs/master/do/implementation-v1.md', '.sdd/runs/master/do/code-review-v1.md', '.sdd/runs/master/do/do-manager-v1.md']);
2235
+ }
2236
+ const implementation = implementationArtifact(options.changedFiles);
2237
+ const implementationHash = hashDocumentContent(implementation);
2238
+ await writeFile(path.join(dir, 'implementation-v1.md'), implementation, 'utf8');
2239
+ const review = codeReviewArtifact(implementationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2240
+ const reviewHash = hashDocumentContent(review);
2241
+ if (!options.omitReview) {
2242
+ await writeFile(path.join(dir, 'code-review-v1.md'), review, 'utf8');
2243
+ }
2244
+ await writeFile(path.join(dir, 'do-manager-v1.md'), doManagerArtifact(implementationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2245
+ }
2246
+
2247
+ function implementationArtifact(changedFiles: ChangedFileFixture[]): string {
2248
+ const changedFileEntries = changedFiles
2249
+ .map((changedFile) => ` - ref: ${changedFile.ref}\n hash: ${changedFile.hash}`)
2250
+ .join('\n');
2251
+ return `---
2252
+ contract: sdd-do-implementation-artifact-v1
2253
+ branch: master
2254
+ stage: do
2255
+ kind: implementation_evidence
2256
+ producer: implementer
2257
+ changedFiles:
2258
+ ${changedFileEntries}
2259
+ ---
2260
+
2261
+ # Implementation Evidence
2262
+
2263
+ Changed files were produced by the implementer.
2264
+ `;
2265
+ }
2266
+
2267
+ function codeReviewArtifact(implementationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2268
+ return `---
2269
+ contract: sdd-do-code-review-artifact-v1
2270
+ branch: master
2271
+ stage: do
2272
+ kind: code_review
2273
+ producer: code-reviewer
2274
+ targetRef: .sdd/runs/master/do/implementation-v1.md
2275
+ targetHash: ${implementationHash}
2276
+ verdict: ${verdict}
2277
+ findingCount: ${blockingCount}
2278
+ blockingCount: ${blockingCount}
2279
+ ---
2280
+
2281
+ # Code Review
2282
+
2283
+ Verdict: ${verdict}.
2284
+ `;
2285
+ }
2286
+
2287
+ function doManagerArtifact(implementationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2288
+ return `---
2289
+ contract: sdd-do-manager-artifact-v1
2290
+ branch: master
2291
+ stage: do
2292
+ kind: manager_closure_request
2293
+ producer: do-manager
2294
+ targetRef: .sdd/runs/master/do/implementation-v1.md
2295
+ targetHash: ${implementationHash}
2296
+ reviewRef: .sdd/runs/master/do/code-review-v1.md
2297
+ reviewHash: ${reviewHash}
2298
+ recommendation: ${recommendation}
2299
+ ---
2300
+
2301
+ # Do Manager Coordination
2302
+
2303
+ Recommendation: ${recommendation}.
2304
+ `;
2305
+ }
2306
+
2307
+ async function writeTestStageArtifacts(root: string, options: { omitContract?: boolean; omitExecution?: boolean; omitValidation?: boolean; executionStatus?: 'passed' | 'failed'; validationStatus?: 'passed' | 'failed'; exitCode?: number; acceptanceMapped?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2308
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'test');
2309
+ await mkdir(dir, { recursive: true });
2310
+ if (!options.omitContract) {
2311
+ await writeStageCollaborationContract(root, 'test', ['.sdd/runs/master/test/test-execution-v1.md', '.sdd/runs/master/test/validation-v1.md', '.sdd/runs/master/test/test-review-v1.md', '.sdd/runs/master/test/test-manager-v1.md']);
2312
+ }
2313
+ const execution = testExecutionArtifact(options.executionStatus ?? 'passed', options.exitCode ?? 0);
2314
+ const executionHash = hashDocumentContent(execution);
2315
+ if (!options.omitExecution) {
2316
+ await writeFile(path.join(dir, 'test-execution-v1.md'), execution, 'utf8');
2317
+ }
2318
+ const validation = validationEvidenceArtifact(executionHash, options.validationStatus ?? 'passed', options.acceptanceMapped ?? true);
2319
+ const validationHash = hashDocumentContent(validation);
2320
+ if (!options.omitValidation) {
2321
+ await writeFile(path.join(dir, 'validation-v1.md'), validation, 'utf8');
2322
+ }
2323
+ const review = testReviewArtifact(validationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2324
+ const reviewHash = hashDocumentContent(review);
2325
+ if (!options.omitReview) {
2326
+ await writeFile(path.join(dir, 'test-review-v1.md'), review, 'utf8');
2327
+ }
2328
+ await writeFile(path.join(dir, 'test-manager-v1.md'), testManagerArtifact(validationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2329
+ }
2330
+
2331
+ function testExecutionArtifact(status: 'passed' | 'failed', exitCode: number): string {
2332
+ return `---
2333
+ contract: sdd-test-execution-artifact-v1
2334
+ branch: master
2335
+ stage: test
2336
+ kind: test_execution
2337
+ producer: test-runner
2338
+ status: ${status}
2339
+ exitCode: ${exitCode}
2340
+ ---
2341
+
2342
+ # Test Execution
2343
+
2344
+ Status: ${status}.
2345
+ `;
2346
+ }
2347
+
2348
+ function validationEvidenceArtifact(executionHash: string, status: 'passed' | 'failed', acceptanceMapped: boolean): string {
2349
+ return `---
2350
+ contract: sdd-test-validation-artifact-v1
2351
+ branch: master
2352
+ stage: test
2353
+ kind: validation_evidence
2354
+ producer: validator
2355
+ executionRef: .sdd/runs/master/test/test-execution-v1.md
2356
+ executionHash: ${executionHash}
2357
+ status: ${status}
2358
+ acceptanceMapped: ${acceptanceMapped}
2359
+ ---
2360
+
2361
+ # Validation Evidence
2362
+
2363
+ Acceptance mapped: ${acceptanceMapped}.
2364
+ `;
2365
+ }
2366
+
2367
+ function testReviewArtifact(validationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2368
+ return `---
2369
+ contract: sdd-test-review-artifact-v1
2370
+ branch: master
2371
+ stage: test
2372
+ kind: test_review
2373
+ producer: test-reviewer
2374
+ targetRef: .sdd/runs/master/test/validation-v1.md
2375
+ targetHash: ${validationHash}
2376
+ verdict: ${verdict}
2377
+ findingCount: ${blockingCount}
2378
+ blockingCount: ${blockingCount}
2379
+ ---
2380
+
2381
+ # Test Review
2382
+
2383
+ Verdict: ${verdict}.
2384
+ `;
2385
+ }
2386
+
2387
+ function testManagerArtifact(validationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2388
+ return `---
2389
+ contract: sdd-test-manager-artifact-v1
2390
+ branch: master
2391
+ stage: test
2392
+ kind: manager_closure_request
2393
+ producer: test-manager
2394
+ targetRef: .sdd/runs/master/test/validation-v1.md
2395
+ targetHash: ${validationHash}
2396
+ reviewRef: .sdd/runs/master/test/test-review-v1.md
2397
+ reviewHash: ${reviewHash}
2398
+ recommendation: ${recommendation}
2399
+ ---
2400
+
2401
+ # Test Manager Coordination
2402
+
2403
+ Recommendation: ${recommendation}.
2404
+ `;
2405
+ }
2406
+
2407
+ async function writeGoalVerifyStageArtifacts(root: string, options: { omitContract?: boolean; omitVerification?: boolean; status?: 'passed' | 'failed'; coverageComplete?: boolean; durableGapCount?: number; openGapCount?: number; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2408
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'goal-verify');
2409
+ await mkdir(dir, { recursive: true });
2410
+ if (!options.omitContract) {
2411
+ await writeStageCollaborationContract(root, 'goal-verify', ['.sdd/runs/master/goal-verify/goal-verification-v1.md', '.sdd/runs/master/goal-verify/goal-review-v1.md', '.sdd/runs/master/goal-verify/goal-verify-manager-v1.md']);
2412
+ }
2413
+ const verification = goalVerificationArtifact(options.status ?? 'passed', options.coverageComplete ?? true, options.durableGapCount ?? 0, options.openGapCount ?? 0);
2414
+ const verificationHash = hashDocumentContent(verification);
2415
+ if (!options.omitVerification) {
2416
+ await writeFile(path.join(dir, 'goal-verification-v1.md'), verification, 'utf8');
2417
+ }
2418
+ const review = goalReviewArtifact(verificationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2419
+ const reviewHash = hashDocumentContent(review);
2420
+ if (!options.omitReview) {
2421
+ await writeFile(path.join(dir, 'goal-review-v1.md'), review, 'utf8');
2422
+ }
2423
+ await writeFile(path.join(dir, 'goal-verify-manager-v1.md'), goalVerifyManagerArtifact(verificationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2424
+ }
2425
+
2426
+ function goalVerificationArtifact(status: 'passed' | 'failed', coverageComplete: boolean, durableGapCount: number, openGapCount: number): string {
2427
+ return `---
2428
+ contract: sdd-goal-verify-verification-artifact-v1
2429
+ branch: master
2430
+ stage: goal-verify
2431
+ kind: goal_verification
2432
+ producer: goal-validator
2433
+ status: ${status}
2434
+ coverageComplete: ${coverageComplete}
2435
+ durableGapCount: ${durableGapCount}
2436
+ openGapCount: ${openGapCount}
2437
+ ---
2438
+
2439
+ # Goal Verification
2440
+
2441
+ Coverage complete: ${coverageComplete}.
2442
+ `;
2443
+ }
2444
+
2445
+ function goalReviewArtifact(verificationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2446
+ return `---
2447
+ contract: sdd-goal-verify-review-artifact-v1
2448
+ branch: master
2449
+ stage: goal-verify
2450
+ kind: goal_review
2451
+ producer: goal-reviewer
2452
+ targetRef: .sdd/runs/master/goal-verify/goal-verification-v1.md
2453
+ targetHash: ${verificationHash}
2454
+ verdict: ${verdict}
2455
+ findingCount: ${blockingCount}
2456
+ blockingCount: ${blockingCount}
2457
+ ---
2458
+
2459
+ # Goal Review
2460
+
2461
+ Verdict: ${verdict}.
2462
+ `;
2463
+ }
2464
+
2465
+ function goalVerifyManagerArtifact(verificationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2466
+ return `---
2467
+ contract: sdd-goal-verify-manager-artifact-v1
2468
+ branch: master
2469
+ stage: goal-verify
2470
+ kind: manager_closure_request
2471
+ producer: goal-verify-manager
2472
+ targetRef: .sdd/runs/master/goal-verify/goal-verification-v1.md
2473
+ targetHash: ${verificationHash}
2474
+ reviewRef: .sdd/runs/master/goal-verify/goal-review-v1.md
2475
+ reviewHash: ${reviewHash}
2476
+ recommendation: ${recommendation}
2477
+ ---
2478
+
2479
+ # Goal Verify Manager Coordination
2480
+
2481
+ Recommendation: ${recommendation}.
2482
+ `;
2483
+ }
2484
+
2485
+
2486
+ async function writeShipStageArtifacts(root: string, options: { omitContract?: boolean; omitReadiness?: boolean; omitReview?: boolean; status?: 'ready' | 'blocked'; doctorBlockerCount?: number; capabilityBlockerCount?: number; repairBlockerCount?: number; gapBlockerCount?: number; migrationBlockerCount?: number; release?: { ref: string; hash: string }; authorityAttempts?: string[]; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
2487
+ const dir = path.join(root, '.sdd', 'runs', 'master', 'ship');
2488
+ await mkdir(dir, { recursive: true });
2489
+ if (!options.omitContract) {
2490
+ await writeStageCollaborationContract(root, 'ship', ['.sdd/runs/master/ship/ship-readiness-v1.md', '.sdd/runs/master/ship/release-review-v1.md', '.sdd/runs/master/ship/ship-manager-v1.md', 'specs/master/release.md']);
2491
+ }
2492
+ const readiness = shipReadinessArtifact({
2493
+ status: options.status ?? 'ready',
2494
+ doctorBlockerCount: options.doctorBlockerCount ?? 0,
2495
+ capabilityBlockerCount: options.capabilityBlockerCount ?? 0,
2496
+ repairBlockerCount: options.repairBlockerCount ?? 0,
2497
+ gapBlockerCount: options.gapBlockerCount ?? 0,
2498
+ migrationBlockerCount: options.migrationBlockerCount ?? 0,
2499
+ release: options.release,
2500
+ authorityAttempts: options.authorityAttempts ?? []
2501
+ });
2502
+ const readinessHash = hashDocumentContent(readiness);
2503
+ if (!options.omitReadiness) {
2504
+ await writeFile(path.join(dir, 'ship-readiness-v1.md'), readiness, 'utf8');
2505
+ }
2506
+ const review = shipReleaseReviewArtifact(readinessHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
2507
+ const reviewHash = hashDocumentContent(review);
2508
+ if (!options.omitReview) {
2509
+ await writeFile(path.join(dir, 'release-review-v1.md'), review, 'utf8');
2510
+ }
2511
+ await writeFile(path.join(dir, 'ship-manager-v1.md'), shipManagerArtifact(readinessHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
2512
+ }
2513
+
2514
+ function shipReadinessArtifact(options: { status: 'ready' | 'blocked'; doctorBlockerCount: number; capabilityBlockerCount: number; repairBlockerCount: number; gapBlockerCount: number; migrationBlockerCount: number; release?: { ref: string; hash: string }; authorityAttempts: string[] }): string {
2515
+ const releaseLines = options.release ? `releaseRef: ${options.release.ref}\nreleaseHash: ${options.release.hash}\n` : '';
2516
+ const authorityLines = options.authorityAttempts.length > 0
2517
+ ? `authorityAttempts:\n${options.authorityAttempts.map((attempt) => ` - ${attempt}`).join('\n')}\n`
2518
+ : '';
2519
+ return `---
2520
+ contract: sdd-ship-readiness-artifact-v1
2521
+ branch: master
2522
+ stage: ship
2523
+ kind: ship_readiness
2524
+ producer: ship-validator
2525
+ status: ${options.status}
2526
+ doctorBlockerCount: ${options.doctorBlockerCount}
2527
+ capabilityBlockerCount: ${options.capabilityBlockerCount}
2528
+ repairBlockerCount: ${options.repairBlockerCount}
2529
+ gapBlockerCount: ${options.gapBlockerCount}
2530
+ migrationBlockerCount: ${options.migrationBlockerCount}
2531
+ ${releaseLines}${authorityLines}---
2532
+
2533
+ # Ship Readiness
2534
+
2535
+ Status: ${options.status}.
2536
+ `;
2537
+ }
2538
+
2539
+ function shipReleaseReviewArtifact(readinessHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
2540
+ return `---
2541
+ contract: sdd-ship-release-review-artifact-v1
2542
+ branch: master
2543
+ stage: ship
2544
+ kind: release_review
2545
+ producer: release-reviewer
2546
+ targetRef: .sdd/runs/master/ship/ship-readiness-v1.md
2547
+ targetHash: ${readinessHash}
2548
+ verdict: ${verdict}
2549
+ findingCount: ${blockingCount}
2550
+ blockingCount: ${blockingCount}
2551
+ ---
2552
+
2553
+ # Release Review
2554
+
2555
+ Verdict: ${verdict}.
2556
+ `;
2557
+ }
2558
+
2559
+ function shipManagerArtifact(readinessHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
2560
+ return `---
2561
+ contract: sdd-ship-manager-artifact-v1
2562
+ branch: master
2563
+ stage: ship
2564
+ kind: manager_closure_request
2565
+ producer: ship-manager
2566
+ targetRef: .sdd/runs/master/ship/ship-readiness-v1.md
2567
+ targetHash: ${readinessHash}
2568
+ reviewRef: .sdd/runs/master/ship/release-review-v1.md
2569
+ reviewHash: ${reviewHash}
2570
+ recommendation: ${recommendation}
2571
+ ---
2572
+
2573
+ # Ship Manager Coordination
2574
+
2575
+ Recommendation: ${recommendation}.
2576
+ `;
2577
+ }
2578
+
2579
+ function reviewedSpec(): string {
2580
+ return `---
2581
+ contract: sdd-spec-doc-v1
2582
+ title: Reviewed test spec
2583
+ ---
2584
+
2585
+ # Reviewed Test Spec
2586
+
2587
+ This spec is produced by the spec-stage test fixture and must be preserved by runtime.
2588
+ `;
2589
+ }
2590
+
2591
+ function reviewedPlan(): string {
2592
+ return `---
2593
+ contract: sdd-plan-doc-v1
2594
+ title: Reviewed test plan
2595
+ ---
2596
+
2597
+ # Reviewed Test Plan
2598
+
2599
+ This plan is produced by the plan-stage test fixture and must be preserved by runtime.
2600
+ `;
2601
+ }
2602
+
2603
+ function reviewedTasks(options: { duplicate?: boolean } = {}): string {
2604
+ const secondId = options.duplicate ? 'T1' : 'T2';
2605
+ return `---
2606
+ contract: sdd-tasks-doc-v1
2607
+ title: Reviewed test tasks
2608
+ ---
2609
+
2610
+ # Reviewed Test Tasks
2611
+
2612
+ ## T1 — Implement first reviewed task
2613
+
2614
+ \`\`\`sdd-task
2615
+ id: T1
2616
+ status: pending
2617
+ wave: 1
2618
+ implementation_wave: core
2619
+ validation_batch: core
2620
+ validation_timing: task_end
2621
+ requires_verify_before_next: true
2622
+ change_surface: backend_only
2623
+ depends_on: []
2624
+ affected_files:
2625
+ - packages/core/src/example.ts
2626
+ validation:
2627
+ - npm test
2628
+ risk: []
2629
+ acceptance_refs:
2630
+ - AC-1
2631
+ plan_refs:
2632
+ - PLAN-1
2633
+ file_ownership: []
2634
+ agent_fit: []
2635
+ verification_availability: []
2636
+ autonomy: supervised
2637
+ allowed_agents: []
2638
+ required_artifacts: []
2639
+ \`\`\`
2640
+
2641
+ #### Boundary
2642
+
2643
+ Implement only the reviewed T1 boundary.
2644
+
2645
+ #### Acceptance
2646
+
2647
+ - AC-1 is satisfied.
2648
+
2649
+ ## ${secondId} — Implement second reviewed task
2650
+
2651
+ \`\`\`sdd-task
2652
+ id: ${secondId}
2653
+ status: pending
2654
+ wave: 1
2655
+ implementation_wave: core
2656
+ validation_batch: core
2657
+ validation_timing: task_end
2658
+ requires_verify_before_next: true
2659
+ change_surface: backend_only
2660
+ depends_on: []
2661
+ affected_files:
2662
+ - packages/core/src/example-2.ts
2663
+ validation:
2664
+ - npm test
2665
+ risk: []
2666
+ acceptance_refs:
2667
+ - AC-2
2668
+ plan_refs:
2669
+ - PLAN-2
2670
+ file_ownership: []
2671
+ agent_fit: []
2672
+ verification_availability: []
2673
+ autonomy: supervised
2674
+ allowed_agents: []
2675
+ required_artifacts: []
2676
+ \`\`\`
2677
+
2678
+ #### Boundary
2679
+
2680
+ Implement only the reviewed ${secondId} boundary.
2681
+
2682
+ #### Acceptance
2683
+
2684
+ - AC-2 is satisfied.
2685
+ `;
2686
+ }
2687
+
2688
+ function reviewedVerify(tasksContent: string, options: { omitTaskId?: string } = {}): string {
2689
+ const taskRows = ['T1', 'T2']
2690
+ .filter((taskId) => taskId !== options.omitTaskId)
2691
+ .map((taskId) => `| ${taskId} | AC-${taskId.slice(1)} | npm test | validation-report.md | available |`)
2692
+ .join('\n');
2693
+ const batches = ['T1', 'T2']
2694
+ .filter((taskId) => taskId !== options.omitTaskId)
2695
+ .map((taskId) => ` - task: ${taskId}`)
2696
+ .join('\n');
2697
+ return `---
2698
+ contract: ${VERIFY_DOCUMENT_CONTRACT_VERSION}
2699
+ version: 1.0.0
2700
+ branch: master
2701
+ author_role: verification-designer
2702
+ independent_from_roles:
2703
+ - task-planner
2704
+ - implementer
2705
+ ---
2706
+
2707
+ # Verify Contract: master
2708
+
2709
+ ## 1. Purpose
2710
+
2711
+ Reviewed verification contract produced by the verifies-stage fixture.
2712
+
2713
+ ## 2. Verification Batches
2714
+
2715
+ verification_batches:
2716
+ ${batches}
2717
+
2718
+ ## 3. Task Verification Matrix
2719
+
2720
+ | Task | Acceptance refs | Validation commands | Required artifacts | Verification availability |
2721
+ |---|---|---|---|---|
2722
+ ${taskRows}
2723
+
2724
+ ## 4. Verification Rules
2725
+
2726
+ - PASS requires policy-backed acceptance evidence.
2727
+
2728
+ ## 5. Out of Scope
2729
+
2730
+ - This document does not authorize publish, push, tag, or release actions.
2731
+ `;
2732
+ }
2733
+
2734
+ interface StageArtifactPairCase {
2735
+ stage: SddStage;
2736
+ reviewFile: string;
2737
+ managerFile: string;
2738
+ reviewKind: string;
2739
+ reviewProducer: string;
2740
+ managerProducer: string;
2741
+ reviewContract: string;
2742
+ managerContract: string;
2743
+ targetRef: string;
2744
+ }
2745
+
2746
+ const stageArtifactPairCases: StageArtifactPairCase[] = [
2747
+ {
2748
+ stage: 'tasks',
2749
+ reviewFile: 'tasks-review-v1.md',
2750
+ managerFile: 'tasks-manager-v1.md',
2751
+ reviewKind: 'tasks_review',
2752
+ reviewProducer: 'tasks-reviewer',
2753
+ managerProducer: 'tasks-manager',
2754
+ reviewContract: 'sdd-tasks-review-artifact-v1',
2755
+ managerContract: 'sdd-tasks-manager-artifact-v1',
2756
+ targetRef: 'specs/master/tasks.md'
2757
+ },
2758
+ {
2759
+ stage: 'verifies',
2760
+ reviewFile: 'verifies-review-v1.md',
2761
+ managerFile: 'verifies-manager-v1.md',
2762
+ reviewKind: 'verifies_review',
2763
+ reviewProducer: 'verifies-reviewer',
2764
+ managerProducer: 'verifies-manager',
2765
+ reviewContract: 'sdd-verifies-review-artifact-v1',
2766
+ managerContract: 'sdd-verifies-manager-artifact-v1',
2767
+ targetRef: 'specs/master/verify.md'
2768
+ },
2769
+ {
2770
+ stage: 'do',
2771
+ reviewFile: 'code-review-v1.md',
2772
+ managerFile: 'do-manager-v1.md',
2773
+ reviewKind: 'code_review',
2774
+ reviewProducer: 'code-reviewer',
2775
+ managerProducer: 'do-manager',
2776
+ reviewContract: 'sdd-do-code-review-artifact-v1',
2777
+ managerContract: 'sdd-do-manager-artifact-v1',
2778
+ targetRef: '.sdd/runs/master/do/implementation-v1.md'
2779
+ },
2780
+ {
2781
+ stage: 'test',
2782
+ reviewFile: 'test-review-v1.md',
2783
+ managerFile: 'test-manager-v1.md',
2784
+ reviewKind: 'test_review',
2785
+ reviewProducer: 'test-reviewer',
2786
+ managerProducer: 'test-manager',
2787
+ reviewContract: 'sdd-test-review-artifact-v1',
2788
+ managerContract: 'sdd-test-manager-artifact-v1',
2789
+ targetRef: '.sdd/runs/master/test/test-execution-v1.md'
2790
+ },
2791
+ {
2792
+ stage: 'goal-verify',
2793
+ reviewFile: 'goal-review-v1.md',
2794
+ managerFile: 'goal-verify-manager-v1.md',
2795
+ reviewKind: 'goal_review',
2796
+ reviewProducer: 'goal-reviewer',
2797
+ managerProducer: 'goal-verify-manager',
2798
+ reviewContract: 'sdd-goal-verify-review-artifact-v1',
2799
+ managerContract: 'sdd-goal-verify-manager-artifact-v1',
2800
+ targetRef: '.sdd/runs/master/goal-verify/goal-verification-v1.md'
2801
+ },
2802
+ {
2803
+ stage: 'ship',
2804
+ reviewFile: 'release-review-v1.md',
2805
+ managerFile: 'ship-manager-v1.md',
2806
+ reviewKind: 'release_review',
2807
+ reviewProducer: 'release-reviewer',
2808
+ managerProducer: 'ship-manager',
2809
+ reviewContract: 'sdd-ship-release-review-artifact-v1',
2810
+ managerContract: 'sdd-ship-manager-artifact-v1',
2811
+ targetRef: '.sdd/runs/master/ship/ship-readiness-v1.md'
2812
+ }
2813
+ ];
2814
+
2815
+ async function writeStageArtifactPair(root: string, stageCase: StageArtifactPairCase): Promise<void> {
2816
+ const dir = path.join(root, '.sdd', 'runs', 'master', stageCase.stage);
2817
+ await mkdir(dir, { recursive: true });
2818
+ const targetHash = `target-hash-${stageCase.stage}`;
2819
+ const review = stageReviewArtifact(stageCase, targetHash);
2820
+ const reviewHash = hashDocumentContent(review);
2821
+ await writeFile(path.join(dir, stageCase.reviewFile), review, 'utf8');
2822
+ await writeFile(path.join(dir, stageCase.managerFile), stageManagerArtifact(stageCase, targetHash, reviewHash), 'utf8');
2823
+ }
2824
+
2825
+ function stageReviewArtifact(stageCase: StageArtifactPairCase, targetHash: string): string {
2826
+ return `---
2827
+ contract: ${stageCase.reviewContract}
2828
+ branch: master
2829
+ stage: ${stageCase.stage}
2830
+ kind: ${stageCase.reviewKind}
2831
+ producer: ${stageCase.reviewProducer}
2832
+ targetRef: ${stageCase.targetRef}
2833
+ targetHash: ${targetHash}
2834
+ verdict: approved
2835
+ findingCount: 0
2836
+ blockingCount: 0
2837
+ ---
2838
+
2839
+ # ${stageCase.stage} Review
2840
+
2841
+ Verdict: approved.
2842
+ `;
2843
+ }
2844
+
2845
+ function stageManagerArtifact(stageCase: StageArtifactPairCase, targetHash: string, reviewHash: string): string {
2846
+ return `---
2847
+ contract: ${stageCase.managerContract}
2848
+ branch: master
2849
+ stage: ${stageCase.stage}
2850
+ kind: manager_closure_request
2851
+ producer: ${stageCase.managerProducer}
2852
+ targetRef: ${stageCase.targetRef}
2853
+ targetHash: ${targetHash}
2854
+ reviewRef: .sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}
2855
+ reviewHash: ${reviewHash}
2856
+ recommendation: close_stage
2857
+ ---
2858
+
2859
+ # ${stageCase.stage} Manager Coordination
2860
+
2861
+ Recommendation: close_stage.
2862
+ `;
2863
+ }
2864
+
2865
+ function testStageRun(id: string, scope: { branch: string }, status: 'completed' | 'failed', timestamp: string) {
2866
+ return {
2867
+ contract: 'sdd-stage-run-v1' as const,
2868
+ id,
2869
+ scope,
2870
+ stage: 'do' as const,
2871
+ ownerAgent: 'do-manager',
2872
+ coMainAgents: ['implementer', 'code-reviewer'],
2873
+ status,
2874
+ inputRefs: [],
2875
+ outputRefs: status === 'completed' ? [ref('artifact', '.sdd/runs/feature/do/implementation-v1.md', 'hash')] : [],
2876
+ decisionRefs: [],
2877
+ blockingReasons: status === 'failed' ? ['Later diagnostic rejection must not downgrade completed stage.'] : [],
2878
+ createdAt: timestamp,
2879
+ updatedAt: timestamp
2880
+ };
2881
+ }
2882
+
2883
+ function ref(kind: RuntimeRefKind, value: string, hash?: string) {
2884
+ return hash ? { kind, ref: value, hash } : { kind, ref: value };
2885
+ }
2886
+
2887
+ async function markdownSnapshot(root: string): Promise<Map<string, string>> {
2888
+ const entries = new Map<string, string>();
2889
+ await collectMarkdownSnapshot(root, root, entries);
2890
+ return entries;
2891
+ }
2892
+
2893
+ async function collectMarkdownSnapshot(root: string, dir: string, entries: Map<string, string>): Promise<void> {
2894
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
2895
+ const absolute = path.join(dir, entry.name);
2896
+ const relative = path.relative(root, absolute).replace(/\\/g, '/');
2897
+ if (entry.isDirectory()) {
2898
+ await collectMarkdownSnapshot(root, absolute, entries);
2899
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
2900
+ entries.set(relative, hashDocumentContent(await readFile(absolute, 'utf8')));
2901
+ }
2902
+ }
2903
+ }