sdd-agent-platform 0.4.1 → 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 (722) hide show
  1. package/README.md +30 -28
  2. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +67 -69
  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 +12 -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 +12 -25
  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 +2 -1
  28. package/node_modules/@sdd-agent-platform/core/dist/contracts.js +1 -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 +4 -4
  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 +178 -58
  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.js +14 -7
  49. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -1
  50. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/contracts.d.ts +0 -1
  51. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js +4 -4
  52. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js.map +1 -1
  53. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js +3 -3
  54. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js.map +1 -1
  55. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +5 -4
  56. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js.map +1 -1
  57. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +3 -2
  58. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
  59. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +2 -2
  60. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -1
  61. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.d.ts +1 -1
  62. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js +1 -1
  63. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js.map +1 -1
  64. package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
  65. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +54 -61
  66. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js +1 -1
  68. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js.map +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +1 -0
  70. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +24 -8
  71. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  72. package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
  73. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.d.ts +12 -2
  74. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +62 -21
  75. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
  76. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +5 -2
  77. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +69 -27
  78. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
  79. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +15 -15
  80. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  81. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +1 -1
  82. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
  83. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +1 -1
  84. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
  85. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +9 -9
  86. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
  87. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js +4 -4
  88. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js.map +1 -1
  89. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js +2 -2
  90. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js.map +1 -1
  91. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +3 -3
  92. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
  93. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js +2 -2
  94. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js.map +1 -1
  95. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js +11 -11
  96. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js.map +1 -1
  97. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +12 -12
  98. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  99. package/node_modules/@sdd-agent-platform/core/dist/risk/contracts.d.ts +2 -2
  100. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +4 -4
  101. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
  102. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +4 -7
  103. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
  104. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.d.ts +2 -2
  105. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +19 -17
  106. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
  107. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js +1 -1
  108. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js.map +1 -1
  109. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +2 -0
  110. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +1 -1
  111. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
  112. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +45 -15
  113. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  114. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.d.ts +28 -0
  115. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +373 -0
  116. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -0
  117. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +2 -2
  118. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
  119. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +37 -0
  120. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +235 -0
  121. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -0
  122. package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +2 -0
  123. package/node_modules/@sdd-agent-platform/core/dist/router.js +2 -0
  124. package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
  125. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +16 -0
  126. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +126 -9
  127. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  128. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +0 -2
  129. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +7 -5
  130. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
  131. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +28 -8
  132. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.d.ts +0 -2
  133. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js +1 -3
  134. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js.map +1 -1
  135. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +37 -27
  136. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  137. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +5 -2
  138. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +53 -14
  139. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -1
  140. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.d.ts +8 -0
  141. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js +131 -0
  142. package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js.map +1 -0
  143. package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +1 -0
  144. package/node_modules/@sdd-agent-platform/core/dist/run-state.js +1 -0
  145. package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
  146. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +0 -3
  147. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
  148. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +5 -44
  149. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
  150. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/model.d.ts +1 -17
  151. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +10 -0
  152. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +65 -0
  153. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  154. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +64 -0
  155. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +200 -0
  156. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -0
  157. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +2 -0
  158. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +97 -10
  159. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -1
  160. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.d.ts +1 -1
  161. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +8 -6
  162. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
  163. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +1 -0
  164. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +55 -34
  165. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  166. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +55 -0
  167. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +322 -0
  168. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -0
  169. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +55 -0
  170. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +241 -0
  171. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -0
  172. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +888 -0
  173. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +3870 -0
  174. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -0
  175. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +8 -1
  176. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
  177. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +44 -1
  178. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +170 -23
  179. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  180. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.d.ts +170 -18
  181. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +544 -32
  182. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js.map +1 -1
  183. package/node_modules/@sdd-agent-platform/core/dist/subagents/contracts.d.ts +1 -1
  184. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js +7 -7
  185. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js.map +1 -1
  186. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +1 -1
  187. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -1
  188. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +8 -23
  189. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  190. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.d.ts +44 -0
  191. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +138 -0
  192. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -0
  193. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  194. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -1
  195. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +28 -53
  196. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  197. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +0 -2
  198. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +10 -50
  199. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  200. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.d.ts +0 -1
  201. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +159 -150
  202. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  203. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.d.ts +12 -2
  204. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +238 -103
  205. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  206. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.d.ts +26 -0
  207. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js +73 -0
  208. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js.map +1 -0
  209. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +18 -0
  210. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +27 -5
  211. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -1
  212. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.d.ts +1 -1
  213. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +9 -32
  214. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  215. package/node_modules/@sdd-agent-platform/core/dist/work-units/contracts.d.ts +1 -1
  216. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +228 -15
  217. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  218. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +49 -15
  219. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -1
  220. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +42 -6
  221. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -1
  222. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +4 -5
  223. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +0 -1
  224. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +1 -2
  225. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  226. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +1 -1
  227. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +0 -1
  228. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +72 -6
  229. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  230. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  231. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js +110 -0
  232. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js.map +1 -0
  233. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.d.ts +12 -0
  234. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js +63 -0
  235. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js.map +1 -0
  236. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  237. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js +95 -0
  238. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  239. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +55 -5
  240. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +538 -34
  241. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  242. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  243. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js +452 -0
  244. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js.map +1 -0
  245. package/node_modules/@sdd-agent-platform/core/package.json +3 -3
  246. package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +49 -1
  247. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +67 -69
  248. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +38 -0
  249. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +65 -9
  250. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +0 -1
  251. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +2 -2
  252. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +26 -17
  253. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +43 -31
  254. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +14 -11
  255. package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +10 -4
  256. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +12 -25
  257. package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +2 -8
  258. package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +3 -2
  259. package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +27 -8
  260. package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +2 -2
  261. package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +1 -1
  262. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +2 -1
  263. package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +3 -0
  264. package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +8 -5
  265. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +1 -1
  266. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +8 -8
  267. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +0 -1
  268. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +4 -4
  269. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +0 -21
  270. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +1 -1
  271. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +143 -45
  272. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +193 -58
  273. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +15 -7
  274. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +0 -1
  275. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +50 -2
  276. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +4 -4
  277. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +11 -2
  278. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +3 -3
  279. package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +5 -4
  280. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +12 -1
  281. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +3 -2
  282. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +1 -1
  283. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +2 -2
  284. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +10 -0
  285. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +8 -0
  286. package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +2 -2
  287. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +33 -18
  288. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +55 -62
  289. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +1 -1
  290. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +1 -1
  291. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -0
  292. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +25 -8
  293. package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +1 -1
  294. package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +74 -22
  295. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +2 -3
  296. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +3 -3
  297. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +82 -35
  298. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +15 -15
  299. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +1 -1
  300. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +1 -1
  301. package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +10 -10
  302. package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +4 -4
  303. package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +2 -2
  304. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +18 -2
  305. package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +3 -3
  306. package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +2 -2
  307. package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +11 -11
  308. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +12 -12
  309. package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +2 -2
  310. package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +4 -4
  311. package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +4 -7
  312. package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +20 -18
  313. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +1 -1
  314. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +2 -0
  315. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +1 -1
  316. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +241 -4
  317. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +47 -15
  318. package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +453 -0
  319. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +2 -2
  320. package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +279 -0
  321. package/node_modules/@sdd-agent-platform/core/src/router.ts +2 -0
  322. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +132 -10
  323. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +7 -7
  324. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +31 -8
  325. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +1 -5
  326. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +50 -2
  327. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +39 -29
  328. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +62 -16
  329. package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -0
  330. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +1 -0
  331. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +0 -3
  332. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +6 -46
  333. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +1 -13
  334. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +0 -2
  335. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +77 -0
  336. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +96 -0
  337. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +292 -0
  338. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +109 -10
  339. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +8 -6
  340. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +123 -1
  341. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +58 -43
  342. package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +450 -0
  343. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +322 -0
  344. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2903 -0
  345. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5831 -0
  346. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +1 -1
  347. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +9 -1
  348. package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +239 -16
  349. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +249 -23
  350. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +196 -4
  351. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +860 -54
  352. package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +1 -1
  353. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +3 -3
  354. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +7 -7
  355. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +1 -1
  356. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +9 -23
  357. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -0
  358. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +177 -0
  359. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.test.ts +13 -87
  360. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.ts +27 -56
  361. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +10 -54
  362. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +8 -1
  363. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +93 -79
  364. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +166 -154
  365. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +100 -47
  366. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +265 -107
  367. package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -0
  368. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +47 -5
  369. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +48 -5
  370. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +15 -31
  371. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +9 -33
  372. package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +1 -1
  373. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +246 -17
  374. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +339 -3
  375. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +53 -14
  376. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +45 -8
  377. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +45 -6
  378. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +6 -5
  379. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +1 -3
  380. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +1 -1
  381. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +75 -7
  382. package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -0
  383. package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -0
  384. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -0
  385. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +518 -25
  386. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +670 -41
  387. package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -0
  388. package/package.json +1 -1
  389. package/packages/cli/dist/args.js +2 -2
  390. package/packages/cli/dist/args.js.map +1 -1
  391. package/packages/cli/dist/commands/ai-tools.js +13 -2
  392. package/packages/cli/dist/commands/ai-tools.js.map +1 -1
  393. package/packages/cli/dist/commands/context.js +1 -1
  394. package/packages/cli/dist/commands/context.js.map +1 -1
  395. package/packages/cli/dist/commands/execution.js +49 -1
  396. package/packages/cli/dist/commands/execution.js.map +1 -1
  397. package/packages/cli/dist/commands/governance.js +1 -1
  398. package/packages/cli/dist/commands/governance.js.map +1 -1
  399. package/packages/cli/dist/commands/init.js +6 -1
  400. package/packages/cli/dist/commands/init.js.map +1 -1
  401. package/packages/cli/dist/commands/lifecycle.js +15 -2
  402. package/packages/cli/dist/commands/lifecycle.js.map +1 -1
  403. package/packages/cli/dist/commands/registry/runtime.js +48 -2
  404. package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
  405. package/packages/cli/dist/commands/run.js +52 -2
  406. package/packages/cli/dist/commands/run.js.map +1 -1
  407. package/packages/cli/dist/commands/stage-close.d.ts +6 -0
  408. package/packages/cli/dist/commands/stage-close.js +295 -0
  409. package/packages/cli/dist/commands/stage-close.js.map +1 -0
  410. package/packages/cli/dist/commands/status.js +68 -2
  411. package/packages/cli/dist/commands/status.js.map +1 -1
  412. package/packages/cli/dist/commands/test.js +180 -2
  413. package/packages/cli/dist/commands/test.js.map +1 -1
  414. package/packages/cli/dist/commands/verifies.js +7 -5
  415. package/packages/cli/dist/commands/verifies.js.map +1 -1
  416. package/packages/cli/dist/commands/verify.js +222 -26
  417. package/packages/cli/dist/commands/verify.js.map +1 -1
  418. package/packages/cli/dist/dispatch.js +4 -9
  419. package/packages/cli/dist/dispatch.js.map +1 -1
  420. package/packages/cli/dist/help.js +27 -26
  421. package/packages/cli/dist/help.js.map +1 -1
  422. package/packages/cli/dist/renderers/doctor.js +1 -1
  423. package/packages/cli/dist/renderers/doctor.js.map +1 -1
  424. package/packages/cli/dist/renderers/execution.js +1 -1
  425. package/packages/cli/dist/renderers/execution.js.map +1 -1
  426. package/packages/cli/dist/renderers/json.d.ts +1 -0
  427. package/packages/cli/dist/renderers/json.js +3 -0
  428. package/packages/cli/dist/renderers/json.js.map +1 -1
  429. package/packages/cli/dist/renderers/registry-runtime.d.ts +2 -1
  430. package/packages/cli/dist/renderers/registry-runtime.js +20 -0
  431. package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
  432. package/packages/cli/dist/renderers/router.js +1 -1
  433. package/packages/cli/dist/renderers/router.js.map +1 -1
  434. package/packages/cli/dist/renderers/workflow.d.ts +0 -4
  435. package/packages/cli/dist/renderers/workflow.js +30 -89
  436. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  437. package/packages/cli/dist/skill-import-args.d.ts +10 -0
  438. package/packages/cli/dist/skill-import-args.js +47 -0
  439. package/packages/cli/dist/skill-import-args.js.map +1 -0
  440. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  441. package/packages/cli/package.json +2 -2
  442. package/packages/core/dist/ai-tools.js +67 -69
  443. package/packages/core/dist/ai-tools.js.map +1 -1
  444. package/packages/core/dist/artifacts/ingestion.js +64 -9
  445. package/packages/core/dist/artifacts/ingestion.js.map +1 -1
  446. package/packages/core/dist/artifacts/sdd-evidence.js +0 -1
  447. package/packages/core/dist/artifacts/sdd-evidence.js.map +1 -1
  448. package/packages/core/dist/artifacts/sdd-result.js +26 -17
  449. package/packages/core/dist/artifacts/sdd-result.js.map +1 -1
  450. package/packages/core/dist/config/init-project.d.ts +3 -0
  451. package/packages/core/dist/config/init-project.js +12 -9
  452. package/packages/core/dist/config/init-project.js.map +1 -1
  453. package/packages/core/dist/config/project-config.d.ts +3 -1
  454. package/packages/core/dist/config/project-config.js +7 -3
  455. package/packages/core/dist/config/project-config.js.map +1 -1
  456. package/packages/core/dist/config/starter-documents.d.ts +4 -4
  457. package/packages/core/dist/config/starter-documents.js +12 -25
  458. package/packages/core/dist/config/starter-documents.js.map +1 -1
  459. package/packages/core/dist/context/build-package.d.ts +1 -1
  460. package/packages/core/dist/context/build-package.js +1 -7
  461. package/packages/core/dist/context/build-package.js.map +1 -1
  462. package/packages/core/dist/context/evidence-summary.js +26 -8
  463. package/packages/core/dist/context/evidence-summary.js.map +1 -1
  464. package/packages/core/dist/context/log-worker.js +2 -2
  465. package/packages/core/dist/context/log-worker.js.map +1 -1
  466. package/packages/core/dist/context-offload/contracts.d.ts +1 -1
  467. package/packages/core/dist/contracts.d.ts +2 -1
  468. package/packages/core/dist/contracts.js +1 -0
  469. package/packages/core/dist/contracts.js.map +1 -1
  470. package/packages/core/dist/delegation/model.d.ts +3 -0
  471. package/packages/core/dist/delegation/validation.d.ts +3 -0
  472. package/packages/core/dist/delegation/validation.js +7 -4
  473. package/packages/core/dist/delegation/validation.js.map +1 -1
  474. package/packages/core/dist/doctor/checks/document-chain.js +1 -1
  475. package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
  476. package/packages/core/dist/doctor/checks/project.js +8 -8
  477. package/packages/core/dist/doctor/checks/project.js.map +1 -1
  478. package/packages/core/dist/doctor/checks/registries.js +0 -1
  479. package/packages/core/dist/doctor/checks/registries.js.map +1 -1
  480. package/packages/core/dist/doctor/checks/run-evidence.js +4 -4
  481. package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
  482. package/packages/core/dist/doctor/checks/run-trust.js +0 -24
  483. package/packages/core/dist/doctor/checks/run-trust.js.map +1 -1
  484. package/packages/core/dist/doctor/checks/runtime-contracts.js +1 -1
  485. package/packages/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  486. package/packages/core/dist/doctor/doctor.js +178 -58
  487. package/packages/core/dist/doctor/doctor.js.map +1 -1
  488. package/packages/core/dist/evidence/lookup.js +14 -7
  489. package/packages/core/dist/evidence/lookup.js.map +1 -1
  490. package/packages/core/dist/evidence-runtime/contracts.d.ts +0 -1
  491. package/packages/core/dist/execution/background-executor.js +4 -4
  492. package/packages/core/dist/execution/background-executor.js.map +1 -1
  493. package/packages/core/dist/execution/foreground-subagents.js +3 -3
  494. package/packages/core/dist/execution/foreground-subagents.js.map +1 -1
  495. package/packages/core/dist/execution/host-invocation.js +5 -4
  496. package/packages/core/dist/execution/host-invocation.js.map +1 -1
  497. package/packages/core/dist/execution/resident-worker.js +3 -2
  498. package/packages/core/dist/execution/resident-worker.js.map +1 -1
  499. package/packages/core/dist/execution/stage-team-runtime.js +2 -2
  500. package/packages/core/dist/execution/stage-team-runtime.js.map +1 -1
  501. package/packages/core/dist/governance/policy.d.ts +1 -1
  502. package/packages/core/dist/governance/policy.js +1 -1
  503. package/packages/core/dist/governance/policy.js.map +1 -1
  504. package/packages/core/dist/instructions.d.ts +1 -1
  505. package/packages/core/dist/instructions.js +54 -61
  506. package/packages/core/dist/instructions.js.map +1 -1
  507. package/packages/core/dist/lifecycle/decision-gate.js +1 -1
  508. package/packages/core/dist/lifecycle/decision-gate.js.map +1 -1
  509. package/packages/core/dist/lifecycle/ship.d.ts +1 -0
  510. package/packages/core/dist/lifecycle/ship.js +24 -8
  511. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  512. package/packages/core/dist/orchestration/contracts.d.ts +1 -1
  513. package/packages/core/dist/orchestration/runtime.d.ts +12 -2
  514. package/packages/core/dist/orchestration/runtime.js +62 -21
  515. package/packages/core/dist/orchestration/runtime.js.map +1 -1
  516. package/packages/core/dist/registries/agent-capability-catalog.d.ts +5 -2
  517. package/packages/core/dist/registries/agent-capability-catalog.js +69 -27
  518. package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
  519. package/packages/core/dist/registries/agent-registry.js +15 -15
  520. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  521. package/packages/core/dist/registries/agent-runtime-static.js +1 -1
  522. package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
  523. package/packages/core/dist/registries/capability-sources.js +1 -1
  524. package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
  525. package/packages/core/dist/registries/command-team-runtime.js +9 -9
  526. package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
  527. package/packages/core/dist/registries/eval-learning-context.js +4 -4
  528. package/packages/core/dist/registries/eval-learning-context.js.map +1 -1
  529. package/packages/core/dist/registries/query-status.js +2 -2
  530. package/packages/core/dist/registries/query-status.js.map +1 -1
  531. package/packages/core/dist/registries/tool-capabilities.js +3 -3
  532. package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
  533. package/packages/core/dist/registries/tool-plugins.js +2 -2
  534. package/packages/core/dist/registries/tool-plugins.js.map +1 -1
  535. package/packages/core/dist/registries/worker-adapters.js +11 -11
  536. package/packages/core/dist/registries/worker-adapters.js.map +1 -1
  537. package/packages/core/dist/registries/workflow-gates.js +12 -12
  538. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  539. package/packages/core/dist/risk/contracts.d.ts +2 -2
  540. package/packages/core/dist/risk/kernel.js +4 -4
  541. package/packages/core/dist/risk/kernel.js.map +1 -1
  542. package/packages/core/dist/risk/legacy-adapters.js +4 -7
  543. package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
  544. package/packages/core/dist/risk/workflow-gates.d.ts +2 -2
  545. package/packages/core/dist/risk/workflow-gates.js +19 -17
  546. package/packages/core/dist/risk/workflow-gates.js.map +1 -1
  547. package/packages/core/dist/router/agent-runtime-config.js +1 -1
  548. package/packages/core/dist/router/agent-runtime-config.js.map +1 -1
  549. package/packages/core/dist/router/agent-runtime.d.ts +2 -0
  550. package/packages/core/dist/router/route-projection.js +1 -1
  551. package/packages/core/dist/router/route-projection.js.map +1 -1
  552. package/packages/core/dist/router/routing.js +45 -15
  553. package/packages/core/dist/router/routing.js.map +1 -1
  554. package/packages/core/dist/router/runtime-import.d.ts +28 -0
  555. package/packages/core/dist/router/runtime-import.js +373 -0
  556. package/packages/core/dist/router/runtime-import.js.map +1 -0
  557. package/packages/core/dist/router/runtime-validation.js +2 -2
  558. package/packages/core/dist/router/runtime-validation.js.map +1 -1
  559. package/packages/core/dist/router/stage-route-binding.d.ts +37 -0
  560. package/packages/core/dist/router/stage-route-binding.js +235 -0
  561. package/packages/core/dist/router/stage-route-binding.js.map +1 -0
  562. package/packages/core/dist/router.d.ts +2 -0
  563. package/packages/core/dist/router.js +2 -0
  564. package/packages/core/dist/router.js.map +1 -1
  565. package/packages/core/dist/run-state/artifacts.d.ts +16 -0
  566. package/packages/core/dist/run-state/artifacts.js +126 -9
  567. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  568. package/packages/core/dist/run-state/inspect-run.d.ts +0 -2
  569. package/packages/core/dist/run-state/inspect-run.js +7 -5
  570. package/packages/core/dist/run-state/inspect-run.js.map +1 -1
  571. package/packages/core/dist/run-state/model.d.ts +28 -8
  572. package/packages/core/dist/run-state/run-index.d.ts +0 -2
  573. package/packages/core/dist/run-state/run-index.js +1 -3
  574. package/packages/core/dist/run-state/run-index.js.map +1 -1
  575. package/packages/core/dist/run-state/run-state.js +37 -27
  576. package/packages/core/dist/run-state/run-state.js.map +1 -1
  577. package/packages/core/dist/run-state/task-evidence.d.ts +5 -2
  578. package/packages/core/dist/run-state/task-evidence.js +53 -14
  579. package/packages/core/dist/run-state/task-evidence.js.map +1 -1
  580. package/packages/core/dist/run-state/timing.d.ts +8 -0
  581. package/packages/core/dist/run-state/timing.js +131 -0
  582. package/packages/core/dist/run-state/timing.js.map +1 -0
  583. package/packages/core/dist/run-state.d.ts +1 -0
  584. package/packages/core/dist/run-state.js +1 -0
  585. package/packages/core/dist/run-state.js.map +1 -1
  586. package/packages/core/dist/runtime-analysis/build.js +0 -3
  587. package/packages/core/dist/runtime-analysis/build.js.map +1 -1
  588. package/packages/core/dist/runtime-analysis/findings.js +5 -44
  589. package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
  590. package/packages/core/dist/runtime-analysis/model.d.ts +1 -17
  591. package/packages/core/dist/runtime-paths.d.ts +10 -0
  592. package/packages/core/dist/runtime-paths.js +65 -0
  593. package/packages/core/dist/runtime-paths.js.map +1 -1
  594. package/packages/core/dist/runtime-projection-p0.d.ts +64 -0
  595. package/packages/core/dist/runtime-projection-p0.js +200 -0
  596. package/packages/core/dist/runtime-projection-p0.js.map +1 -0
  597. package/packages/core/dist/sdd-docs/document-hashes.d.ts +2 -0
  598. package/packages/core/dist/sdd-docs/document-hashes.js +97 -10
  599. package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -1
  600. package/packages/core/dist/sdd-docs/run-binding.d.ts +1 -1
  601. package/packages/core/dist/sdd-docs/run-binding.js +8 -6
  602. package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
  603. package/packages/core/dist/sdd-docs/task-parser.d.ts +1 -0
  604. package/packages/core/dist/sdd-docs/task-parser.js +55 -34
  605. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  606. package/packages/core/dist/stage-artifacts.d.ts +55 -0
  607. package/packages/core/dist/stage-artifacts.js +322 -0
  608. package/packages/core/dist/stage-artifacts.js.map +1 -0
  609. package/packages/core/dist/stage-collaboration-contracts.d.ts +55 -0
  610. package/packages/core/dist/stage-collaboration-contracts.js +241 -0
  611. package/packages/core/dist/stage-collaboration-contracts.js.map +1 -0
  612. package/packages/core/dist/stage-collaboration.d.ts +888 -0
  613. package/packages/core/dist/stage-collaboration.js +3870 -0
  614. package/packages/core/dist/stage-collaboration.js.map +1 -0
  615. package/packages/core/dist/stage-runtime/runtime.js +8 -1
  616. package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
  617. package/packages/core/dist/status/project-status.d.ts +44 -1
  618. package/packages/core/dist/status/project-status.js +170 -23
  619. package/packages/core/dist/status/project-status.js.map +1 -1
  620. package/packages/core/dist/storage/runtime-store.d.ts +170 -18
  621. package/packages/core/dist/storage/runtime-store.js +544 -32
  622. package/packages/core/dist/storage/runtime-store.js.map +1 -1
  623. package/packages/core/dist/subagents/contracts.d.ts +1 -1
  624. package/packages/core/dist/subagents/runtime.js +7 -7
  625. package/packages/core/dist/subagents/runtime.js.map +1 -1
  626. package/packages/core/dist/test-support/fixtures.js +1 -1
  627. package/packages/core/dist/test-support/run-state.d.ts +1 -1
  628. package/packages/core/dist/test-support/run-state.js +8 -23
  629. package/packages/core/dist/test-support/run-state.js.map +1 -1
  630. package/packages/core/dist/truth-reconciliation.d.ts +44 -0
  631. package/packages/core/dist/truth-reconciliation.js +138 -0
  632. package/packages/core/dist/truth-reconciliation.js.map +1 -0
  633. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  634. package/packages/core/dist/verification/goal-verify.d.ts +0 -1
  635. package/packages/core/dist/verification/goal-verify.js +28 -53
  636. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  637. package/packages/core/dist/verification/rendering.d.ts +0 -2
  638. package/packages/core/dist/verification/rendering.js +10 -50
  639. package/packages/core/dist/verification/rendering.js.map +1 -1
  640. package/packages/core/dist/verification/single-task-loop.d.ts +0 -1
  641. package/packages/core/dist/verification/single-task-loop.js +159 -150
  642. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  643. package/packages/core/dist/verification/test-runtime.d.ts +12 -2
  644. package/packages/core/dist/verification/test-runtime.js +238 -103
  645. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  646. package/packages/core/dist/verification/validation-cache.d.ts +26 -0
  647. package/packages/core/dist/verification/validation-cache.js +73 -0
  648. package/packages/core/dist/verification/validation-cache.js.map +1 -0
  649. package/packages/core/dist/verification/validation-wave.d.ts +18 -0
  650. package/packages/core/dist/verification/validation-wave.js +27 -5
  651. package/packages/core/dist/verification/validation-wave.js.map +1 -1
  652. package/packages/core/dist/verification/verify-contract.d.ts +1 -1
  653. package/packages/core/dist/verification/verify-contract.js +9 -32
  654. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  655. package/packages/core/dist/work-units/contracts.d.ts +1 -1
  656. package/packages/core/dist/workflow-gate/evidence-packet.js +228 -15
  657. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  658. package/packages/core/dist/workflow-gate/hard-checks.js +49 -15
  659. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -1
  660. package/packages/core/dist/workflow-gate/policy.js +42 -6
  661. package/packages/core/dist/workflow-gate/policy.js.map +1 -1
  662. package/packages/core/dist/workflow-gate/types.d.ts +4 -5
  663. package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +0 -1
  664. package/packages/core/dist/workflow-state/affected-file-conflicts.js +1 -2
  665. package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  666. package/packages/core/dist/workflow-state/dependencies.js +1 -1
  667. package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +0 -1
  668. package/packages/core/dist/workflow-state/latest-eligible-run.js +72 -6
  669. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  670. package/packages/core/dist/workflow-state/migration-recovery.d.ts +40 -0
  671. package/packages/core/dist/workflow-state/migration-recovery.js +110 -0
  672. package/packages/core/dist/workflow-state/migration-recovery.js.map +1 -0
  673. package/packages/core/dist/workflow-state/repair-contract.d.ts +12 -0
  674. package/packages/core/dist/workflow-state/repair-contract.js +63 -0
  675. package/packages/core/dist/workflow-state/repair-contract.js.map +1 -0
  676. package/packages/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
  677. package/packages/core/dist/workflow-state/resolve-task-run.js +95 -0
  678. package/packages/core/dist/workflow-state/resolve-task-run.js.map +1 -0
  679. package/packages/core/dist/workflow-state/resolve.d.ts +55 -5
  680. package/packages/core/dist/workflow-state/resolve.js +538 -34
  681. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  682. package/packages/core/dist/workflow-state/runtime-projections.d.ts +228 -0
  683. package/packages/core/dist/workflow-state/runtime-projections.js +452 -0
  684. package/packages/core/dist/workflow-state/runtime-projections.js.map +1 -0
  685. package/packages/core/package.json +3 -3
  686. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.d.ts +0 -2
  687. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js +0 -44
  688. package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js.map +0 -1
  689. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +0 -17
  690. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +0 -243
  691. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +0 -1
  692. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +0 -110
  693. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +0 -497
  694. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +0 -1
  695. package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +0 -2
  696. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +0 -3
  697. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +0 -1
  698. package/node_modules/@sdd-agent-platform/core/src/sync-back/apply.ts +0 -270
  699. package/node_modules/@sdd-agent-platform/core/src/sync-back/inspect.ts +0 -655
  700. package/node_modules/@sdd-agent-platform/core/src/sync-back/sync-back.test.ts +0 -569
  701. package/node_modules/@sdd-agent-platform/core/src/sync-back.ts +0 -2
  702. package/packages/cli/dist/commands/artifact.d.ts +0 -6
  703. package/packages/cli/dist/commands/artifact.js +0 -168
  704. package/packages/cli/dist/commands/artifact.js.map +0 -1
  705. package/packages/cli/dist/commands/sync-back.d.ts +0 -6
  706. package/packages/cli/dist/commands/sync-back.js +0 -82
  707. package/packages/cli/dist/commands/sync-back.js.map +0 -1
  708. package/packages/cli/dist/renderers/artifacts.d.ts +0 -5
  709. package/packages/cli/dist/renderers/artifacts.js +0 -43
  710. package/packages/cli/dist/renderers/artifacts.js.map +0 -1
  711. package/packages/core/dist/doctor/render.d.ts +0 -2
  712. package/packages/core/dist/doctor/render.js +0 -44
  713. package/packages/core/dist/doctor/render.js.map +0 -1
  714. package/packages/core/dist/sync-back/apply.d.ts +0 -17
  715. package/packages/core/dist/sync-back/apply.js +0 -243
  716. package/packages/core/dist/sync-back/apply.js.map +0 -1
  717. package/packages/core/dist/sync-back/inspect.d.ts +0 -110
  718. package/packages/core/dist/sync-back/inspect.js +0 -497
  719. package/packages/core/dist/sync-back/inspect.js.map +0 -1
  720. package/packages/core/dist/sync-back.d.ts +0 -2
  721. package/packages/core/dist/sync-back.js +0 -3
  722. package/packages/core/dist/sync-back.js.map +0 -1
@@ -0,0 +1,3870 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readdir, readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { LIFECYCLE_DECISION_CONTRACT, LIFECYCLE_DECISION_VERSION, STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION, STAGE_RUN_CONTRACT_VERSION, WORKFLOW_HANDOFF_CONTRACT_VERSION } from './contracts.js';
5
+ import { LIFECYCLE_RISK_DECISION_PROJECTION_TYPE, lifecycleRiskDecisionScopeKey, recordLifecycleRiskDecisionProjection } from './risk/kernel.js';
6
+ import { createRun } from './run-state/run-state.js';
7
+ import { normalizePortablePath } from './path-safety.js';
8
+ import { getBranchStageEvidenceDir, normalizeBranchStageEvidenceRef, toBranchStageEvidenceRef } from './runtime-paths.js';
9
+ import { resolveSddContext } from './sdd-docs/context.js';
10
+ import { hashDocumentContent, hashSemanticDocument, hashTasksContract } from './sdd-docs/document-hashes.js';
11
+ import { parseSddTasksMarkdown } from './sdd-docs/task-parser.js';
12
+ import { readWorkflowHandoffProjection, recordStageRunProjection, recordWorkflowHandoffProjection, STAGE_RUN_PROJECTION_TYPE, stageRunScopeKey, WORKFLOW_HANDOFF_PROJECTION_TYPE, workflowHandoffScopeKey } from './stage-runtime/runtime.js';
13
+ import { listRuntimeProjections, recordRuntimeArtifactPayload, recordRuntimeProjectionEnvelope, recordRuntimeStageArtifact, recordRuntimeStageCollaborationContract, runtimeScopedId } from './storage/runtime-store.js';
14
+ import { readMarkdownArtifact, validateStageArtifactFrontmatter } from './stage-artifacts.js';
15
+ import { readStageCollaborationContract, validateStageCollaborationContractFrontmatter } from './stage-collaboration-contracts.js';
16
+ import { inspectVerifyContract } from './verification/verify-contract.js';
17
+ export const SPEC_STAGE_MANAGER = 'spec-manager';
18
+ export const SPEC_STAGE_DRAFTER_AGENT = 'spec-drafter';
19
+ export const SPEC_STAGE_REVIEW_AGENT = 'spec-reviewer';
20
+ export const SPEC_STAGE_SCOUT_AGENT = 'scout';
21
+ export const SPEC_STAGE_AGENT_TEAM = [SPEC_STAGE_DRAFTER_AGENT, SPEC_STAGE_REVIEW_AGENT, SPEC_STAGE_SCOUT_AGENT];
22
+ export const SPEC_STAGE_REQUIRED_CAPABILITIES = ['norm_discovery', 'uncertainty_resolution'];
23
+ export const SPEC_STAGE_OPTIONAL_CAPABILITIES = ['context_curation', 'solution-design', 'frontend-engineering', 'security-engineering', 'ui-ux-product-design'];
24
+ export const SPEC_STAGE_MATERIAL_PACKS = ['project-norms', 'uncertainty-map', 'baseline-solution-design', 'baseline-frontend-engineering', 'baseline-security-engineering', 'baseline-ui-ux-product-design'];
25
+ export const STAGE_COLLABORATION_RUNTIME_PRODUCER_VERSION = 'phase9.1-stage-collaboration-runtime-v1';
26
+ export const SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_1_spec_collaboration_adjudication';
27
+ export const PLAN_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_2_plan_collaboration_adjudication';
28
+ export const PLAN_STAGE_MANAGER = 'plan-manager';
29
+ export const PLAN_STAGE_DRAFTER_AGENT = 'plan-drafter';
30
+ export const PLAN_STAGE_REVIEW_AGENT = 'plan-reviewer';
31
+ export const TASKS_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_3_tasks_collaboration_adjudication';
32
+ export const TASKS_STAGE_MANAGER = 'tasks-manager';
33
+ export const TASKS_STAGE_DRAFTER_AGENT = 'tasks-drafter';
34
+ export const TASKS_STAGE_REVIEW_AGENT = 'tasks-reviewer';
35
+ export const TASKS_STAGE_SLICER_AGENT = 'task-slicer';
36
+ export const VERIFIES_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_4_verifies_collaboration_adjudication';
37
+ export const VERIFIES_STAGE_MANAGER = 'verifies-manager';
38
+ export const VERIFIES_STAGE_DRAFTER_AGENT = 'verifies-drafter';
39
+ export const VERIFIES_STAGE_REVIEW_AGENT = 'verifies-reviewer';
40
+ export const VERIFIES_STAGE_VERIFICATION_AGENT = 'verification-agent';
41
+ export const DO_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_5_do_collaboration_adjudication';
42
+ export const DO_STAGE_MANAGER = 'do-manager';
43
+ export const DO_STAGE_IMPLEMENTER_AGENT = 'implementer';
44
+ export const DO_STAGE_CODE_REVIEW_AGENT = 'code-reviewer';
45
+ export const DO_STAGE_DEBUGGER_AGENT = 'debugger';
46
+ export const TEST_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_6_test_collaboration_adjudication';
47
+ export const TEST_STAGE_MANAGER = 'test-manager';
48
+ export const TEST_STAGE_RUNNER_AGENT = 'test-runner';
49
+ export const TEST_STAGE_VALIDATOR_AGENT = 'validator';
50
+ export const TEST_STAGE_REVIEW_AGENT = 'test-reviewer';
51
+ export const GOAL_VERIFY_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_7_goal_verify_collaboration_adjudication';
52
+ export const GOAL_VERIFY_STAGE_MANAGER = 'goal-verify-manager';
53
+ export const GOAL_VERIFY_STAGE_VALIDATOR_AGENT = 'goal-validator';
54
+ export const GOAL_VERIFY_STAGE_REVIEW_AGENT = 'goal-reviewer';
55
+ export const TRUTH_ALIGNMENT_PROJECTION_TYPE = 'phase9_12_truth_alignment';
56
+ export const TRUTH_ALIGNMENT_CONTRACT = 'sdd-truth-alignment-v1';
57
+ export const SHIP_COLLABORATION_ADJUDICATION_PROJECTION_TYPE = 'phase9_9_ship_collaboration_adjudication';
58
+ export const SHIP_STAGE_MANAGER = 'ship-manager';
59
+ export const SHIP_STAGE_VALIDATOR_AGENT = 'ship-validator';
60
+ export const SHIP_STAGE_REVIEW_AGENT = 'release-reviewer';
61
+ const TRUTH_ALIGNMENT_STATUSES = ['aligned', 'drift_detected', 'update_required', 'blocked'];
62
+ const TRUTH_ALIGNMENT_SEMANTIC_IMPACTS = ['none', 'bounded', 'material'];
63
+ const TRUTH_ALIGNMENT_OWNER_STAGES = ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship'];
64
+ export function deriveSpecCollaborationProfile(decision, generatedAt = new Date().toISOString()) {
65
+ const required = decision.requiredStages.includes('spec');
66
+ const intensity = specIntensity(decision, required);
67
+ return {
68
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
69
+ stage: 'spec',
70
+ scope: decision.scope,
71
+ lifecycleProfile: decision.profile,
72
+ intensity,
73
+ required,
74
+ requiresStageClosure: intensity !== 'noop' && intensity !== 'blocked',
75
+ allowedProposalKinds: intensity === 'noop' ? [] : ['advisory_finding', 'blocking_ambiguity', 'capability_suggestion', 'handoff_proposal'],
76
+ stageManager: intensity === 'noop' || intensity === 'blocked' ? null : SPEC_STAGE_MANAGER,
77
+ agentTeam: specMemberAgents(intensity),
78
+ requiredCapabilities: specRequiredCapabilities(intensity),
79
+ optionalCapabilities: specOptionalCapabilities(intensity),
80
+ materialPackIds: specMaterialPackIds(intensity),
81
+ collaborationPlan: specCollaborationPlan(intensity),
82
+ inputRefs: decision.inputRefs,
83
+ reasons: specProfileReasons(decision, required, intensity),
84
+ generatedAt
85
+ };
86
+ }
87
+ export function buildSpecStageWorkOrder(profile, profileRef, generatedAt = new Date().toISOString()) {
88
+ return {
89
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
90
+ workOrderId: stableId('spec-work-order', profile.scope),
91
+ stage: 'spec',
92
+ scope: profile.scope,
93
+ profileRef,
94
+ authorityCeiling: 'proposal_input',
95
+ requiredOutputKinds: profile.agentTeam.includes(SPEC_STAGE_SCOUT_AGENT)
96
+ ? ['scout_context', 'spec_review', 'manager_closure_request']
97
+ : ['spec_review', 'manager_closure_request'],
98
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
99
+ stageManager: SPEC_STAGE_MANAGER,
100
+ agentTeam: profile.agentTeam,
101
+ requiredCapabilities: profile.requiredCapabilities,
102
+ optionalCapabilities: profile.optionalCapabilities,
103
+ materialPackIds: profile.materialPackIds,
104
+ collaborationPlan: profile.collaborationPlan,
105
+ inputRefs: profile.inputRefs,
106
+ generatedAt
107
+ };
108
+ }
109
+ export function planCollaborationScopeKey(scope) {
110
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'plan'].join(':');
111
+ }
112
+ export function buildPlanStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
113
+ return {
114
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
115
+ workOrderId: stableId('plan-work-order', scope),
116
+ stage: 'plan',
117
+ scope,
118
+ profileRef,
119
+ authorityCeiling: 'proposal_input',
120
+ requiredOutputKinds: ['plan_context', 'plan_review', 'manager_closure_request'],
121
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
122
+ inputRefs,
123
+ stageManager: PLAN_STAGE_MANAGER,
124
+ agentTeam: ['plan-scout', PLAN_STAGE_DRAFTER_AGENT, PLAN_STAGE_REVIEW_AGENT],
125
+ requiredCapabilities: ['solution-design', 'verification-design'],
126
+ optionalCapabilities: ['performance-planning', 'security-engineering', 'frontend-engineering'],
127
+ materialPackIds: ['project-norms', 'baseline-solution-design', 'baseline-verification-design'],
128
+ generatedAt
129
+ };
130
+ }
131
+ export function tasksCollaborationScopeKey(scope) {
132
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'tasks'].join(':');
133
+ }
134
+ export function buildTasksStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
135
+ return {
136
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
137
+ workOrderId: stableId('tasks-work-order', scope),
138
+ stage: 'tasks',
139
+ scope,
140
+ profileRef,
141
+ authorityCeiling: 'proposal_input',
142
+ requiredOutputKinds: ['tasks_context', 'tasks_review', 'manager_closure_request'],
143
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
144
+ inputRefs,
145
+ stageManager: TASKS_STAGE_MANAGER,
146
+ agentTeam: [TASKS_STAGE_SLICER_AGENT, TASKS_STAGE_DRAFTER_AGENT, TASKS_STAGE_REVIEW_AGENT],
147
+ requiredCapabilities: ['task-decomposition', 'acceptance-mapping'],
148
+ optionalCapabilities: ['dependency-analysis', 'parallelization-planning'],
149
+ materialPackIds: ['project-norms', 'baseline-task-slicing', 'baseline-acceptance-mapping'],
150
+ generatedAt
151
+ };
152
+ }
153
+ export function verifiesCollaborationScopeKey(scope) {
154
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'verifies'].join(':');
155
+ }
156
+ export function buildVerifiesStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
157
+ return {
158
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
159
+ workOrderId: stableId('verifies-work-order', scope),
160
+ stage: 'verifies',
161
+ scope,
162
+ profileRef,
163
+ authorityCeiling: 'proposal_input',
164
+ requiredOutputKinds: ['verifies_context', 'verifies_review', 'manager_closure_request'],
165
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
166
+ inputRefs,
167
+ stageManager: VERIFIES_STAGE_MANAGER,
168
+ agentTeam: [VERIFIES_STAGE_VERIFICATION_AGENT, VERIFIES_STAGE_DRAFTER_AGENT, VERIFIES_STAGE_REVIEW_AGENT],
169
+ requiredCapabilities: ['verification-design', 'acceptance-coverage'],
170
+ optionalCapabilities: ['security-testing', 'performance-testing', 'frontend-validation'],
171
+ materialPackIds: ['project-norms', 'baseline-verification-design', 'baseline-acceptance-coverage'],
172
+ generatedAt
173
+ };
174
+ }
175
+ export function doCollaborationScopeKey(scope) {
176
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'do'].join(':');
177
+ }
178
+ export function buildDoStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
179
+ return {
180
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
181
+ workOrderId: stableId('do-work-order', scope),
182
+ stage: 'do',
183
+ scope,
184
+ profileRef,
185
+ authorityCeiling: 'proposal_input',
186
+ requiredOutputKinds: ['implementation_evidence', 'code_review', 'manager_closure_request'],
187
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
188
+ inputRefs,
189
+ stageManager: DO_STAGE_MANAGER,
190
+ agentTeam: [DO_STAGE_IMPLEMENTER_AGENT, DO_STAGE_CODE_REVIEW_AGENT, DO_STAGE_DEBUGGER_AGENT],
191
+ requiredCapabilities: ['implementation', 'code-review'],
192
+ optionalCapabilities: ['debugging', 'security-engineering', 'frontend-engineering'],
193
+ materialPackIds: ['project-norms', 'baseline-implementation', 'baseline-code-review'],
194
+ generatedAt
195
+ };
196
+ }
197
+ export function testCollaborationScopeKey(scope) {
198
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'test'].join(':');
199
+ }
200
+ export function buildTestStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
201
+ return {
202
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
203
+ workOrderId: stableId('test-work-order', scope),
204
+ stage: 'test',
205
+ scope,
206
+ profileRef,
207
+ authorityCeiling: 'proposal_input',
208
+ requiredOutputKinds: ['test_execution', 'validation_evidence', 'test_review', 'manager_closure_request'],
209
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
210
+ inputRefs,
211
+ stageManager: TEST_STAGE_MANAGER,
212
+ agentTeam: [TEST_STAGE_RUNNER_AGENT, TEST_STAGE_VALIDATOR_AGENT, TEST_STAGE_REVIEW_AGENT],
213
+ requiredCapabilities: ['test-execution', 'validation-evidence'],
214
+ optionalCapabilities: ['flaky-test-analysis', 'frontend-validation', 'security-testing'],
215
+ materialPackIds: ['project-norms', 'baseline-test-execution', 'baseline-validation-evidence'],
216
+ generatedAt
217
+ };
218
+ }
219
+ export function goalVerifyCollaborationScopeKey(scope) {
220
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'goal-verify'].join(':');
221
+ }
222
+ export function buildGoalVerifyStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
223
+ return {
224
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
225
+ workOrderId: stableId('goal-verify-work-order', scope),
226
+ stage: 'goal-verify',
227
+ scope,
228
+ profileRef,
229
+ authorityCeiling: 'proposal_input',
230
+ requiredOutputKinds: ['goal_verification', 'goal_review', 'manager_closure_request'],
231
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
232
+ inputRefs,
233
+ stageManager: GOAL_VERIFY_STAGE_MANAGER,
234
+ agentTeam: [GOAL_VERIFY_STAGE_VALIDATOR_AGENT, GOAL_VERIFY_STAGE_REVIEW_AGENT],
235
+ requiredCapabilities: ['goal-verification', 'acceptance-coverage'],
236
+ optionalCapabilities: ['gap-analysis', 'regression-risk-review'],
237
+ materialPackIds: ['project-norms', 'baseline-goal-verification', 'baseline-acceptance-coverage'],
238
+ generatedAt
239
+ };
240
+ }
241
+ export function truthAlignmentScopeKey(scope) {
242
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'truth-alignment'].join(':');
243
+ }
244
+ function buildTruthAlignmentProjection(scope, input) {
245
+ const acceptedRealityRefs = uniqueRuntimeRefs([input.acceptedGoalVerificationRef, ...input.artifactRefs].filter((ref) => ref !== null));
246
+ return {
247
+ contract: TRUTH_ALIGNMENT_CONTRACT,
248
+ branch: scope.branch,
249
+ sourceStage: 'goal-verify',
250
+ declaredTruthRefs: uniqueRuntimeRefs(input.declaredTruthRefs),
251
+ acceptedRealityRefs,
252
+ status: input.status,
253
+ ownerStage: input.ownerStage,
254
+ semanticImpact: input.semanticImpact,
255
+ staleRefs: uniqueRuntimeRefs(input.staleRefs),
256
+ invalidatesStages: [...new Set(input.invalidatesStages)],
257
+ reasons: input.reasons,
258
+ createdAt: input.generatedAt
259
+ };
260
+ }
261
+ export function shipCollaborationScopeKey(scope) {
262
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'ship'].join(':');
263
+ }
264
+ export function buildShipStageWorkOrder(scope, profileRef, inputRefs, generatedAt = new Date().toISOString()) {
265
+ return {
266
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
267
+ workOrderId: stableId('ship-work-order', scope),
268
+ stage: 'ship',
269
+ scope,
270
+ profileRef,
271
+ authorityCeiling: 'proposal_input',
272
+ requiredOutputKinds: ['ship_readiness', 'release_review', 'manager_closure_request'],
273
+ forbiddenActions: ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved'],
274
+ inputRefs,
275
+ stageManager: SHIP_STAGE_MANAGER,
276
+ agentTeam: [SHIP_STAGE_VALIDATOR_AGENT, SHIP_STAGE_REVIEW_AGENT],
277
+ requiredCapabilities: ['ship-readiness', 'release-readiness'],
278
+ optionalCapabilities: ['release-note-review', 'migration-readiness', 'operational-readiness'],
279
+ materialPackIds: ['project-norms', 'baseline-ship-readiness', 'baseline-release-review'],
280
+ generatedAt
281
+ };
282
+ }
283
+ export function specCollaborationScopeKey(scope) {
284
+ return [scope.branch, scope.taskId ?? 'all', scope.runId ?? 'none', scope.changeRef ?? 'none', 'spec'].join(':');
285
+ }
286
+ export async function recordSpecCollaborationAdjudicationProjection(projectRoot, result) {
287
+ return recordRuntimeProjectionEnvelope(projectRoot, {
288
+ projectionType: SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
289
+ scopeKey: specCollaborationScopeKey(result.scope),
290
+ inputHash: stableHash(JSON.stringify(result)),
291
+ producer: 'phase9.1-stage-collaboration-runtime',
292
+ producerVersion: STAGE_COLLABORATION_RUNTIME_PRODUCER_VERSION,
293
+ generatedAt: result.createdAt,
294
+ payload: result
295
+ });
296
+ }
297
+ export async function recordPlanCollaborationAdjudicationProjection(projectRoot, result) {
298
+ return recordRuntimeProjectionEnvelope(projectRoot, {
299
+ projectionType: PLAN_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
300
+ scopeKey: planCollaborationScopeKey(result.scope),
301
+ inputHash: stableHash(JSON.stringify(result)),
302
+ producer: 'phase9.2-plan-stage-collaboration-runtime',
303
+ producerVersion: 'phase9.2-plan-stage-collaboration-runtime-v1',
304
+ generatedAt: result.createdAt,
305
+ payload: result
306
+ });
307
+ }
308
+ export async function recordTasksCollaborationAdjudicationProjection(projectRoot, result) {
309
+ return recordRuntimeProjectionEnvelope(projectRoot, {
310
+ projectionType: TASKS_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
311
+ scopeKey: tasksCollaborationScopeKey(result.scope),
312
+ inputHash: stableHash(JSON.stringify(result)),
313
+ producer: 'phase9.3-tasks-stage-collaboration-runtime',
314
+ producerVersion: 'phase9.3-tasks-stage-collaboration-runtime-v1',
315
+ generatedAt: result.createdAt,
316
+ payload: result
317
+ });
318
+ }
319
+ export async function recordVerifiesCollaborationAdjudicationProjection(projectRoot, result) {
320
+ return recordRuntimeProjectionEnvelope(projectRoot, {
321
+ projectionType: VERIFIES_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
322
+ scopeKey: verifiesCollaborationScopeKey(result.scope),
323
+ inputHash: stableHash(JSON.stringify(result)),
324
+ producer: 'phase9.4-verifies-stage-collaboration-runtime',
325
+ producerVersion: 'phase9.4-verifies-stage-collaboration-runtime-v1',
326
+ generatedAt: result.createdAt,
327
+ payload: result
328
+ });
329
+ }
330
+ export async function recordDoCollaborationAdjudicationProjection(projectRoot, result) {
331
+ return recordRuntimeProjectionEnvelope(projectRoot, {
332
+ projectionType: DO_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
333
+ scopeKey: doCollaborationScopeKey(result.scope),
334
+ inputHash: stableHash(JSON.stringify(result)),
335
+ producer: 'phase9.5-do-stage-collaboration-runtime',
336
+ producerVersion: 'phase9.5-do-stage-collaboration-runtime-v1',
337
+ generatedAt: result.createdAt,
338
+ payload: result
339
+ });
340
+ }
341
+ export async function recordTestCollaborationAdjudicationProjection(projectRoot, result) {
342
+ return recordRuntimeProjectionEnvelope(projectRoot, {
343
+ projectionType: TEST_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
344
+ scopeKey: testCollaborationScopeKey(result.scope),
345
+ inputHash: stableHash(JSON.stringify(result)),
346
+ producer: 'phase9.6-test-stage-collaboration-runtime',
347
+ producerVersion: 'phase9.6-test-stage-collaboration-runtime-v1',
348
+ generatedAt: result.createdAt,
349
+ payload: result
350
+ });
351
+ }
352
+ export async function recordGoalVerifyCollaborationAdjudicationProjection(projectRoot, result) {
353
+ return recordRuntimeProjectionEnvelope(projectRoot, {
354
+ projectionType: GOAL_VERIFY_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
355
+ scopeKey: goalVerifyCollaborationScopeKey(result.scope),
356
+ inputHash: stableHash(JSON.stringify(result)),
357
+ producer: 'phase9.7-goal-verify-stage-collaboration-runtime',
358
+ producerVersion: 'phase9.7-goal-verify-stage-collaboration-runtime-v1',
359
+ generatedAt: result.createdAt,
360
+ payload: result
361
+ });
362
+ }
363
+ export async function recordTruthAlignmentProjection(projectRoot, result) {
364
+ return recordRuntimeProjectionEnvelope(projectRoot, {
365
+ projectionType: TRUTH_ALIGNMENT_PROJECTION_TYPE,
366
+ scopeKey: truthAlignmentScopeKey({ branch: result.branch }),
367
+ inputHash: stableHash(JSON.stringify(result)),
368
+ producer: 'phase9.12-truth-alignment-runtime-projection',
369
+ producerVersion: 'phase9.12-truth-alignment-runtime-projection-v1',
370
+ generatedAt: result.createdAt,
371
+ payload: result
372
+ });
373
+ }
374
+ export async function readTruthAlignmentProjection(projectRoot, scope) {
375
+ const scopeKey = truthAlignmentScopeKey({ branch: scope.branch });
376
+ const projections = await listRuntimeProjections(projectRoot, [TRUTH_ALIGNMENT_PROJECTION_TYPE]);
377
+ return projections
378
+ .map((projection) => projection.payload)
379
+ .filter((envelope) => envelope?.projectionType === TRUTH_ALIGNMENT_PROJECTION_TYPE && (envelope.scopeKey === scopeKey || envelope.payload?.branch === scope.branch))
380
+ .sort((left, right) => right.generatedAt.localeCompare(left.generatedAt))[0] ?? null;
381
+ }
382
+ export async function recordShipCollaborationAdjudicationProjection(projectRoot, result) {
383
+ return recordRuntimeProjectionEnvelope(projectRoot, {
384
+ projectionType: SHIP_COLLABORATION_ADJUDICATION_PROJECTION_TYPE,
385
+ scopeKey: shipCollaborationScopeKey(result.scope),
386
+ inputHash: stableHash(JSON.stringify(result)),
387
+ producer: 'phase9.9-ship-stage-collaboration-runtime',
388
+ producerVersion: 'phase9.9-ship-stage-collaboration-runtime-v1',
389
+ generatedAt: result.createdAt,
390
+ payload: result
391
+ });
392
+ }
393
+ export async function reconcileSpecCollaborationClosure(projectRoot, input) {
394
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
395
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
396
+ const decision = {
397
+ ...input.decision,
398
+ scope: { ...input.decision.scope, branch: context.rawBranch }
399
+ };
400
+ const run = await createRun(projectRoot, {
401
+ runId: input.runId,
402
+ branch: context.rawBranch,
403
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
404
+ });
405
+ const runRef = { kind: 'run', ref: run.runId };
406
+ const profile = deriveSpecCollaborationProfile(decision, generatedAt);
407
+ const profileRef = { kind: 'projection', ref: `${SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${specCollaborationScopeKey(profile.scope)}:profile` };
408
+ const workOrder = profile.stageManager ? buildSpecStageWorkOrder(profile, profileRef, generatedAt) : null;
409
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'spec', workOrder, input.collaborationContractRefs, generatedAt);
410
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
411
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
412
+ const registeredArtifacts = await registerSpecStageArtifacts(projectRoot, context.rawBranch, profile, input.artifactRefs, run.runId, generatedAt);
413
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
414
+ const derivedClosure = deriveRegisteredStageClosureRequest(profile, workOrder, registeredArtifacts, context.partition, generatedAt);
415
+ const closureRequest = input.closureRequest ?? derivedClosure;
416
+ const coordination = input.coordination ?? deriveRegisteredSpecManagerCoordination(profile, workOrder, registeredArtifacts, generatedAt);
417
+ const candidate = closureRequest?.candidate ?? null;
418
+ const reviews = closureRequest?.reviewResults ?? [];
419
+ const capabilityFindings = closureRequest?.capabilityFindings ?? [];
420
+ let adjudication = adjudicateSpecStageClosureRequest({
421
+ profile,
422
+ closureRequest,
423
+ answerRefs: input.answerRefs,
424
+ generatedAt
425
+ });
426
+ const specAcceptance = await validateAcceptedSpecArtifact(projectRoot, {
427
+ partition: context.partition,
428
+ adjudication,
429
+ closureRequest,
430
+ registeredArtifacts,
431
+ validatedCollaborationContract
432
+ });
433
+ if (specAcceptance.rejectionIssue) {
434
+ adjudication = rejected({ profile, closureRequest, answerRefs: input.answerRefs }, specAcceptance.rejectionIssue.reasonCode, specAcceptance.rejectionIssue.explanation, specAcceptance.rejectionIssue.requiredNextAction, specAcceptance.rejectionIssue.fallbackRoute, generatedAt, true);
435
+ }
436
+ const adjudicationProjectionRef = specAdjudicationProjectionRef(adjudication.scope);
437
+ const lifecycleRiskProjectionRef = {
438
+ kind: 'projection',
439
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
440
+ };
441
+ const outputRefs = [specAcceptance.acceptedSpecRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
442
+ const stageRun = buildClosureStageRun(profile, adjudication, {
443
+ runId: run.runId,
444
+ outputRefs,
445
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
446
+ generatedAt
447
+ });
448
+ const stageRunProjectionRef = {
449
+ kind: 'projection',
450
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
451
+ };
452
+ const handoff = specAcceptance.specAcceptanceStatus === 'accepted' && adjudication.health === 'ready_for_plan'
453
+ ? buildClosureWorkflowHandoff(profile, decision, adjudication, {
454
+ outputRefs,
455
+ requiredInputRefs: specAcceptance.acceptedSpecRef ? [specAcceptance.acceptedSpecRef] : [],
456
+ evidenceRefs: artifactRefs,
457
+ riskDecisionRef: lifecycleRiskProjectionRef,
458
+ generatedAt
459
+ })
460
+ : null;
461
+ const workflowHandoffProjectionRef = handoff
462
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
463
+ : undefined;
464
+ const closureRefs = {
465
+ runRef,
466
+ acceptedSpecRef: specAcceptance.acceptedSpecRef,
467
+ specAcceptanceStatus: specAcceptance.specAcceptanceStatus,
468
+ specHash: specAcceptance.specHash,
469
+ specContractHash: specAcceptance.specContractHash,
470
+ artifactRefs,
471
+ collaborationContractRef,
472
+ lifecycleRiskProjectionRef,
473
+ adjudicationProjectionRef,
474
+ stageRunProjectionRef,
475
+ workflowHandoffProjectionRef,
476
+ reasons: specAcceptance.reasons
477
+ };
478
+ const enrichedAdjudication = { ...adjudication, closureRefs };
479
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
480
+ await recordStageRunProjection(projectRoot, stageRun);
481
+ if (handoff) {
482
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
483
+ }
484
+ const projection = await recordSpecCollaborationAdjudicationProjection(projectRoot, enrichedAdjudication);
485
+ return {
486
+ runId: run.runId,
487
+ branch: context.rawBranch,
488
+ profile,
489
+ workOrder,
490
+ coordination,
491
+ candidate,
492
+ reviews,
493
+ capabilityFindings,
494
+ closureRequest,
495
+ adjudication: enrichedAdjudication,
496
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
497
+ artifactRefs,
498
+ registeredArtifacts,
499
+ registeredCollaborationContracts,
500
+ acceptedSpecRef: specAcceptance.acceptedSpecRef
501
+ };
502
+ }
503
+ export async function reconcilePlanCollaborationClosure(projectRoot, input) {
504
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
505
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
506
+ const decision = {
507
+ ...input.decision,
508
+ scope: { ...input.decision.scope, branch: context.rawBranch }
509
+ };
510
+ const run = await createRun(projectRoot, {
511
+ runId: input.runId,
512
+ branch: context.rawBranch,
513
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
514
+ });
515
+ const runRef = { kind: 'run', ref: run.runId };
516
+ const planRequired = decision.requiredStages.includes('plan');
517
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('plan');
518
+ const acceptedSpecHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'spec', 'plan');
519
+ const workOrderInputRefs = acceptedSpecHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
520
+ const profileRef = { kind: 'projection', ref: `${PLAN_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${planCollaborationScopeKey(decision.scope)}:profile` };
521
+ const workOrder = planRequired && !blocked ? buildPlanStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
522
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'plan', workOrder, input.collaborationContractRefs, generatedAt);
523
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
524
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
525
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'plan', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
526
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
527
+ const lifecycleRiskProjectionRef = {
528
+ kind: 'projection',
529
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
530
+ };
531
+ const adjudicationProjectionRef = planAdjudicationProjectionRef(decision.scope);
532
+ const planAcceptance = await validateAcceptedPlanArtifact(projectRoot, {
533
+ partition: context.partition,
534
+ required: planRequired,
535
+ blocked,
536
+ registeredArtifacts,
537
+ registeredCollaborationContracts,
538
+ acceptedSpecHandoff: acceptedSpecHandoffEnvelope?.payload ?? null
539
+ });
540
+ const health = planAcceptance.planAcceptanceStatus === 'accepted'
541
+ ? 'ready_for_tasks'
542
+ : !planRequired
543
+ ? 'no-op'
544
+ : blocked
545
+ ? 'blocked'
546
+ : 'rejected';
547
+ const outputRefs = [planAcceptance.acceptedPlanRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
548
+ const stageRun = buildPlanClosureStageRun(decision.scope, run.runId, workOrder, health, {
549
+ outputRefs,
550
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
551
+ inputRefs: acceptedSpecHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
552
+ generatedAt,
553
+ rejectionReason: planAcceptance.rejectionIssue?.explanation ?? null
554
+ });
555
+ const stageRunProjectionRef = {
556
+ kind: 'projection',
557
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
558
+ };
559
+ const handoff = planAcceptance.planAcceptanceStatus === 'accepted'
560
+ ? buildPlanWorkflowHandoff(decision.scope, decision, {
561
+ outputRefs,
562
+ requiredInputRefs: planAcceptance.acceptedPlanRef ? [planAcceptance.acceptedPlanRef] : [],
563
+ evidenceRefs: artifactRefs,
564
+ riskDecisionRef: lifecycleRiskProjectionRef,
565
+ generatedAt
566
+ })
567
+ : null;
568
+ const workflowHandoffProjectionRef = handoff
569
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
570
+ : undefined;
571
+ const rejection = planAcceptance.rejectionIssue
572
+ ? buildStageRejection('plan', decision.scope, null, planAcceptance.rejectionIssue.reasonCode, planAcceptance.rejectionIssue.explanation, planAcceptance.rejectionIssue.requiredNextAction, planAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
573
+ : null;
574
+ const closureRefs = {
575
+ runRef,
576
+ acceptedPlanRef: planAcceptance.acceptedPlanRef,
577
+ planAcceptanceStatus: planAcceptance.planAcceptanceStatus,
578
+ planHash: planAcceptance.planHash,
579
+ planContractHash: planAcceptance.planContractHash,
580
+ acceptedSpecHandoffRef: acceptedSpecHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'spec', 'plan')}` } : null,
581
+ artifactRefs,
582
+ collaborationContractRef,
583
+ lifecycleRiskProjectionRef,
584
+ adjudicationProjectionRef,
585
+ stageRunProjectionRef,
586
+ workflowHandoffProjectionRef,
587
+ reasons: planAcceptance.reasons
588
+ };
589
+ const adjudication = {
590
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
591
+ adjudicationId: stableId('plan-adjudication', decision.scope, health, generatedAt),
592
+ stage: 'plan',
593
+ scope: decision.scope,
594
+ health,
595
+ stageDecision: {
596
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
597
+ decisionId: stableId('plan-stage-decision', decision.scope, run.runId, generatedAt),
598
+ stage: 'plan',
599
+ scope: decision.scope,
600
+ status: health === 'ready_for_tasks' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
601
+ health,
602
+ acceptedDecisionRefs: outputRefs,
603
+ advisoryRefs: [],
604
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('context') || ref.ref.includes('capability')),
605
+ blockingReasons: health === 'ready_for_tasks' || health === 'no-op' ? [] : planAcceptance.reasons,
606
+ createdAt: generatedAt
607
+ },
608
+ handoffPacket: handoff,
609
+ rejection,
610
+ nextActions: buildStageRuntimeNextActions('plan', decision.scope, generatedAt, health === 'ready_for_tasks' ? ['stage_ready', 'handoff_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
611
+ closureRefs,
612
+ createdAt: generatedAt
613
+ };
614
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
615
+ await recordStageRunProjection(projectRoot, stageRun);
616
+ if (handoff) {
617
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
618
+ }
619
+ const projection = await recordPlanCollaborationAdjudicationProjection(projectRoot, adjudication);
620
+ return {
621
+ runId: run.runId,
622
+ branch: context.rawBranch,
623
+ workOrder,
624
+ adjudication,
625
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
626
+ artifactRefs,
627
+ registeredArtifacts,
628
+ registeredCollaborationContracts,
629
+ acceptedPlanRef: planAcceptance.acceptedPlanRef
630
+ };
631
+ }
632
+ export async function reconcileTasksCollaborationClosure(projectRoot, input) {
633
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
634
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
635
+ const decision = {
636
+ ...input.decision,
637
+ scope: { ...input.decision.scope, branch: context.rawBranch }
638
+ };
639
+ const run = await createRun(projectRoot, {
640
+ runId: input.runId,
641
+ branch: context.rawBranch,
642
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
643
+ });
644
+ const runRef = { kind: 'run', ref: run.runId };
645
+ const tasksRequired = decision.requiredStages.includes('tasks');
646
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('tasks');
647
+ const acceptedPlanHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'plan', 'tasks');
648
+ const workOrderInputRefs = acceptedPlanHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
649
+ const profileRef = { kind: 'projection', ref: `${TASKS_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${tasksCollaborationScopeKey(decision.scope)}:profile` };
650
+ const workOrder = tasksRequired && !blocked ? buildTasksStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
651
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'tasks', workOrder, input.collaborationContractRefs, generatedAt);
652
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
653
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
654
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'tasks', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
655
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
656
+ const lifecycleRiskProjectionRef = {
657
+ kind: 'projection',
658
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
659
+ };
660
+ const adjudicationProjectionRef = tasksAdjudicationProjectionRef(decision.scope);
661
+ const tasksAcceptance = await validateAcceptedTasksArtifact(projectRoot, {
662
+ partition: context.partition,
663
+ required: tasksRequired,
664
+ blocked,
665
+ registeredArtifacts,
666
+ registeredCollaborationContracts,
667
+ acceptedPlanHandoff: acceptedPlanHandoffEnvelope?.payload ?? null
668
+ });
669
+ const health = tasksAcceptance.tasksAcceptanceStatus === 'accepted'
670
+ ? 'ready_for_verifies'
671
+ : !tasksRequired
672
+ ? 'no-op'
673
+ : blocked
674
+ ? 'blocked'
675
+ : 'rejected';
676
+ const outputRefs = [tasksAcceptance.acceptedTasksRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
677
+ const stageRun = buildTasksClosureStageRun(decision.scope, run.runId, workOrder, health, {
678
+ outputRefs,
679
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
680
+ inputRefs: acceptedPlanHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
681
+ generatedAt,
682
+ rejectionReason: tasksAcceptance.rejectionIssue?.explanation ?? null
683
+ });
684
+ const stageRunProjectionRef = {
685
+ kind: 'projection',
686
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
687
+ };
688
+ const handoff = tasksAcceptance.tasksAcceptanceStatus === 'accepted'
689
+ ? buildTasksWorkflowHandoff(decision.scope, decision, {
690
+ outputRefs,
691
+ requiredInputRefs: tasksAcceptance.acceptedTasksRef ? [tasksAcceptance.acceptedTasksRef] : [],
692
+ evidenceRefs: artifactRefs,
693
+ riskDecisionRef: lifecycleRiskProjectionRef,
694
+ generatedAt
695
+ })
696
+ : null;
697
+ const workflowHandoffProjectionRef = handoff
698
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
699
+ : undefined;
700
+ const rejection = tasksAcceptance.rejectionIssue
701
+ ? buildStageRejection('tasks', decision.scope, null, tasksAcceptance.rejectionIssue.reasonCode, tasksAcceptance.rejectionIssue.explanation, tasksAcceptance.rejectionIssue.requiredNextAction, tasksAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
702
+ : null;
703
+ const closureRefs = {
704
+ runRef,
705
+ acceptedTasksRef: tasksAcceptance.acceptedTasksRef,
706
+ tasksAcceptanceStatus: tasksAcceptance.tasksAcceptanceStatus,
707
+ tasksHash: tasksAcceptance.tasksHash,
708
+ tasksContractHash: tasksAcceptance.tasksContractHash,
709
+ acceptedPlanHandoffRef: acceptedPlanHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'plan', 'tasks')}` } : null,
710
+ artifactRefs,
711
+ collaborationContractRef,
712
+ lifecycleRiskProjectionRef,
713
+ adjudicationProjectionRef,
714
+ stageRunProjectionRef,
715
+ workflowHandoffProjectionRef,
716
+ reasons: tasksAcceptance.reasons
717
+ };
718
+ const adjudication = {
719
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
720
+ adjudicationId: stableId('tasks-adjudication', decision.scope, health, generatedAt),
721
+ stage: 'tasks',
722
+ scope: decision.scope,
723
+ health,
724
+ stageDecision: {
725
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
726
+ decisionId: stableId('tasks-stage-decision', decision.scope, run.runId, generatedAt),
727
+ stage: 'tasks',
728
+ scope: decision.scope,
729
+ status: health === 'ready_for_verifies' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
730
+ health,
731
+ acceptedDecisionRefs: outputRefs,
732
+ advisoryRefs: [],
733
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('context') || ref.ref.includes('capability')),
734
+ blockingReasons: health === 'ready_for_verifies' || health === 'no-op' ? [] : tasksAcceptance.reasons,
735
+ createdAt: generatedAt
736
+ },
737
+ handoffPacket: handoff,
738
+ rejection,
739
+ nextActions: buildStageRuntimeNextActions('tasks', decision.scope, generatedAt, health === 'ready_for_verifies' ? ['stage_ready', 'handoff_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
740
+ closureRefs,
741
+ createdAt: generatedAt
742
+ };
743
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
744
+ await recordStageRunProjection(projectRoot, stageRun);
745
+ if (handoff) {
746
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
747
+ }
748
+ const projection = await recordTasksCollaborationAdjudicationProjection(projectRoot, adjudication);
749
+ return {
750
+ runId: run.runId,
751
+ branch: context.rawBranch,
752
+ workOrder,
753
+ adjudication,
754
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
755
+ artifactRefs,
756
+ registeredArtifacts,
757
+ registeredCollaborationContracts,
758
+ acceptedTasksRef: tasksAcceptance.acceptedTasksRef
759
+ };
760
+ }
761
+ export async function reconcileVerifiesCollaborationClosure(projectRoot, input) {
762
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
763
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
764
+ const decision = {
765
+ ...input.decision,
766
+ scope: { ...input.decision.scope, branch: context.rawBranch }
767
+ };
768
+ const run = await createRun(projectRoot, {
769
+ runId: input.runId,
770
+ branch: context.rawBranch,
771
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
772
+ });
773
+ const runRef = { kind: 'run', ref: run.runId };
774
+ const verifiesRequired = decision.requiredStages.includes('verifies');
775
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('verifies');
776
+ const acceptedTasksHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'tasks', 'verifies');
777
+ const workOrderInputRefs = acceptedTasksHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
778
+ const profileRef = { kind: 'projection', ref: `${VERIFIES_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${verifiesCollaborationScopeKey(decision.scope)}:profile` };
779
+ const workOrder = verifiesRequired && !blocked ? buildVerifiesStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
780
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'verifies', workOrder, input.collaborationContractRefs, generatedAt);
781
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
782
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
783
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'verifies', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
784
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
785
+ const lifecycleRiskProjectionRef = {
786
+ kind: 'projection',
787
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
788
+ };
789
+ const adjudicationProjectionRef = verifiesAdjudicationProjectionRef(decision.scope);
790
+ const verifyAcceptance = await validateAcceptedVerifyArtifact(projectRoot, {
791
+ partition: context.partition,
792
+ required: verifiesRequired,
793
+ blocked,
794
+ registeredArtifacts,
795
+ registeredCollaborationContracts,
796
+ acceptedTasksHandoff: acceptedTasksHandoffEnvelope?.payload ?? null
797
+ });
798
+ const health = verifyAcceptance.verifyAcceptanceStatus === 'accepted'
799
+ ? 'ready_for_do'
800
+ : !verifiesRequired
801
+ ? 'no-op'
802
+ : blocked
803
+ ? 'blocked'
804
+ : 'rejected';
805
+ const outputRefs = [verifyAcceptance.acceptedVerifyRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
806
+ const stageRun = buildVerifiesClosureStageRun(decision.scope, run.runId, workOrder, health, {
807
+ outputRefs,
808
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
809
+ inputRefs: acceptedTasksHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
810
+ generatedAt,
811
+ rejectionReason: verifyAcceptance.rejectionIssue?.explanation ?? null
812
+ });
813
+ const stageRunProjectionRef = {
814
+ kind: 'projection',
815
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
816
+ };
817
+ const handoff = verifyAcceptance.verifyAcceptanceStatus === 'accepted'
818
+ ? buildVerifiesWorkflowHandoff(decision.scope, decision, {
819
+ outputRefs,
820
+ requiredInputRefs: verifyAcceptance.acceptedVerifyRef ? [verifyAcceptance.acceptedVerifyRef] : [],
821
+ evidenceRefs: artifactRefs,
822
+ riskDecisionRef: lifecycleRiskProjectionRef,
823
+ generatedAt
824
+ })
825
+ : null;
826
+ const workflowHandoffProjectionRef = handoff
827
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
828
+ : undefined;
829
+ const rejection = verifyAcceptance.rejectionIssue
830
+ ? buildStageRejection('verifies', decision.scope, null, verifyAcceptance.rejectionIssue.reasonCode, verifyAcceptance.rejectionIssue.explanation, verifyAcceptance.rejectionIssue.requiredNextAction, verifyAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
831
+ : null;
832
+ const closureRefs = {
833
+ runRef,
834
+ acceptedVerifyRef: verifyAcceptance.acceptedVerifyRef,
835
+ verifyAcceptanceStatus: verifyAcceptance.verifyAcceptanceStatus,
836
+ verifyHash: verifyAcceptance.verifyHash,
837
+ verifyContractHash: verifyAcceptance.verifyContractHash,
838
+ acceptedTasksHandoffRef: acceptedTasksHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'tasks', 'verifies')}` } : null,
839
+ artifactRefs,
840
+ collaborationContractRef,
841
+ lifecycleRiskProjectionRef,
842
+ adjudicationProjectionRef,
843
+ stageRunProjectionRef,
844
+ workflowHandoffProjectionRef,
845
+ reasons: verifyAcceptance.reasons
846
+ };
847
+ const adjudication = {
848
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
849
+ adjudicationId: stableId('verifies-adjudication', decision.scope, health, generatedAt),
850
+ stage: 'verifies',
851
+ scope: decision.scope,
852
+ health,
853
+ stageDecision: {
854
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
855
+ decisionId: stableId('verifies-stage-decision', decision.scope, run.runId, generatedAt),
856
+ stage: 'verifies',
857
+ scope: decision.scope,
858
+ status: health === 'ready_for_do' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
859
+ health,
860
+ acceptedDecisionRefs: outputRefs,
861
+ advisoryRefs: [],
862
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('context') || ref.ref.includes('capability')),
863
+ blockingReasons: health === 'ready_for_do' || health === 'no-op' ? [] : verifyAcceptance.reasons,
864
+ createdAt: generatedAt
865
+ },
866
+ handoffPacket: handoff,
867
+ rejection,
868
+ nextActions: buildStageRuntimeNextActions('verifies', decision.scope, generatedAt, health === 'ready_for_do' ? ['stage_ready', 'handoff_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
869
+ closureRefs,
870
+ createdAt: generatedAt
871
+ };
872
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
873
+ await recordStageRunProjection(projectRoot, stageRun);
874
+ if (handoff) {
875
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
876
+ }
877
+ const projection = await recordVerifiesCollaborationAdjudicationProjection(projectRoot, adjudication);
878
+ return {
879
+ runId: run.runId,
880
+ branch: context.rawBranch,
881
+ workOrder,
882
+ adjudication,
883
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
884
+ artifactRefs,
885
+ registeredArtifacts,
886
+ registeredCollaborationContracts,
887
+ acceptedVerifyRef: verifyAcceptance.acceptedVerifyRef
888
+ };
889
+ }
890
+ export async function reconcileDoCollaborationClosure(projectRoot, input) {
891
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
892
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
893
+ const decision = {
894
+ ...input.decision,
895
+ scope: { ...input.decision.scope, branch: context.rawBranch }
896
+ };
897
+ const run = await createRun(projectRoot, {
898
+ runId: input.runId,
899
+ branch: context.rawBranch,
900
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
901
+ });
902
+ const runRef = { kind: 'run', ref: run.runId };
903
+ const doRequired = decision.requiredStages.includes('do');
904
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('do');
905
+ const acceptedVerifyHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'verifies', 'do');
906
+ const workOrderInputRefs = acceptedVerifyHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
907
+ const profileRef = { kind: 'projection', ref: `${DO_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${doCollaborationScopeKey(decision.scope)}:profile` };
908
+ const workOrder = doRequired && !blocked ? buildDoStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
909
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'do', workOrder, input.collaborationContractRefs, generatedAt);
910
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
911
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
912
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'do', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
913
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
914
+ const lifecycleRiskProjectionRef = {
915
+ kind: 'projection',
916
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
917
+ };
918
+ const adjudicationProjectionRef = doAdjudicationProjectionRef(decision.scope);
919
+ const implementationAcceptance = await validateAcceptedDoArtifact(projectRoot, {
920
+ partition: context.partition,
921
+ required: doRequired,
922
+ blocked,
923
+ registeredArtifacts,
924
+ registeredCollaborationContracts,
925
+ acceptedVerifyHandoff: acceptedVerifyHandoffEnvelope?.payload ?? null,
926
+ allowedChangedFileRefs: input.allowedChangedFileRefs
927
+ });
928
+ const health = implementationAcceptance.implementationAcceptanceStatus === 'accepted'
929
+ ? 'ready_for_test'
930
+ : !doRequired
931
+ ? 'no-op'
932
+ : blocked
933
+ ? 'blocked'
934
+ : 'rejected';
935
+ const outputRefs = [implementationAcceptance.acceptedImplementationRef, adjudicationProjectionRef, collaborationContractRef, ...implementationAcceptance.changedFileRefs, ...artifactRefs].filter((ref) => ref !== null);
936
+ const stageRun = buildDoClosureStageRun(decision.scope, run.runId, workOrder, health, {
937
+ outputRefs,
938
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
939
+ inputRefs: acceptedVerifyHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
940
+ generatedAt,
941
+ rejectionReason: implementationAcceptance.rejectionIssue?.explanation ?? null
942
+ });
943
+ const stageRunProjectionRef = {
944
+ kind: 'projection',
945
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
946
+ };
947
+ const handoff = implementationAcceptance.implementationAcceptanceStatus === 'accepted'
948
+ ? buildDoWorkflowHandoff(decision.scope, decision, {
949
+ outputRefs,
950
+ requiredInputRefs: [implementationAcceptance.acceptedImplementationRef, ...implementationAcceptance.changedFileRefs].filter((ref) => ref !== null),
951
+ evidenceRefs: artifactRefs,
952
+ riskDecisionRef: lifecycleRiskProjectionRef,
953
+ generatedAt
954
+ })
955
+ : null;
956
+ const workflowHandoffProjectionRef = handoff
957
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
958
+ : undefined;
959
+ const rejection = implementationAcceptance.rejectionIssue
960
+ ? buildStageRejection('do', decision.scope, null, implementationAcceptance.rejectionIssue.reasonCode, implementationAcceptance.rejectionIssue.explanation, implementationAcceptance.rejectionIssue.requiredNextAction, implementationAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
961
+ : null;
962
+ const closureRefs = {
963
+ runRef,
964
+ acceptedImplementationRef: implementationAcceptance.acceptedImplementationRef,
965
+ implementationAcceptanceStatus: implementationAcceptance.implementationAcceptanceStatus,
966
+ implementationHash: implementationAcceptance.implementationHash,
967
+ changedFileRefs: implementationAcceptance.changedFileRefs,
968
+ acceptedVerifyHandoffRef: acceptedVerifyHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'verifies', 'do')}` } : null,
969
+ artifactRefs,
970
+ collaborationContractRef,
971
+ lifecycleRiskProjectionRef,
972
+ adjudicationProjectionRef,
973
+ stageRunProjectionRef,
974
+ workflowHandoffProjectionRef,
975
+ reasons: implementationAcceptance.reasons
976
+ };
977
+ const adjudication = {
978
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
979
+ adjudicationId: stableId('do-adjudication', decision.scope, health, generatedAt),
980
+ stage: 'do',
981
+ scope: decision.scope,
982
+ health,
983
+ stageDecision: {
984
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
985
+ decisionId: stableId('do-stage-decision', decision.scope, run.runId, generatedAt),
986
+ stage: 'do',
987
+ scope: decision.scope,
988
+ status: health === 'ready_for_test' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
989
+ health,
990
+ acceptedDecisionRefs: outputRefs,
991
+ advisoryRefs: [],
992
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('debug') || ref.ref.includes('capability')),
993
+ blockingReasons: health === 'ready_for_test' || health === 'no-op' ? [] : implementationAcceptance.reasons,
994
+ createdAt: generatedAt
995
+ },
996
+ handoffPacket: handoff,
997
+ rejection,
998
+ nextActions: buildStageRuntimeNextActions('do', decision.scope, generatedAt, health === 'ready_for_test' ? ['stage_ready', 'handoff_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
999
+ closureRefs,
1000
+ createdAt: generatedAt
1001
+ };
1002
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
1003
+ await recordStageRunProjection(projectRoot, stageRun);
1004
+ if (handoff) {
1005
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
1006
+ }
1007
+ const projection = await recordDoCollaborationAdjudicationProjection(projectRoot, adjudication);
1008
+ return {
1009
+ runId: run.runId,
1010
+ branch: context.rawBranch,
1011
+ workOrder,
1012
+ adjudication,
1013
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
1014
+ artifactRefs,
1015
+ registeredArtifacts,
1016
+ registeredCollaborationContracts,
1017
+ acceptedImplementationRef: implementationAcceptance.acceptedImplementationRef,
1018
+ changedFileRefs: implementationAcceptance.changedFileRefs
1019
+ };
1020
+ }
1021
+ export async function reconcileTestCollaborationClosure(projectRoot, input) {
1022
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
1023
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
1024
+ const decision = {
1025
+ ...input.decision,
1026
+ scope: { ...input.decision.scope, branch: context.rawBranch }
1027
+ };
1028
+ const run = await createRun(projectRoot, {
1029
+ runId: input.runId,
1030
+ branch: context.rawBranch,
1031
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
1032
+ });
1033
+ const runRef = { kind: 'run', ref: run.runId };
1034
+ const testRequired = decision.requiredStages.includes('test');
1035
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('test');
1036
+ const acceptedDoHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'do', 'test');
1037
+ const workOrderInputRefs = acceptedDoHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
1038
+ const profileRef = { kind: 'projection', ref: `${TEST_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${testCollaborationScopeKey(decision.scope)}:profile` };
1039
+ const workOrder = testRequired && !blocked ? buildTestStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
1040
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'test', workOrder, input.collaborationContractRefs, generatedAt);
1041
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
1042
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
1043
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'test', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
1044
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
1045
+ const lifecycleRiskProjectionRef = {
1046
+ kind: 'projection',
1047
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
1048
+ };
1049
+ const adjudicationProjectionRef = testAdjudicationProjectionRef(decision.scope);
1050
+ const testAcceptance = await validateAcceptedTestArtifact(projectRoot, {
1051
+ partition: context.partition,
1052
+ required: testRequired,
1053
+ blocked,
1054
+ registeredArtifacts,
1055
+ registeredCollaborationContracts,
1056
+ acceptedDoHandoff: acceptedDoHandoffEnvelope?.payload ?? null
1057
+ });
1058
+ const health = testAcceptance.testAcceptanceStatus === 'accepted'
1059
+ ? 'ready_for_goal_verify'
1060
+ : !testRequired
1061
+ ? 'no-op'
1062
+ : blocked
1063
+ ? 'blocked'
1064
+ : 'rejected';
1065
+ const outputRefs = [testAcceptance.acceptedTestEvidenceRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
1066
+ const stageRun = buildTestClosureStageRun(decision.scope, run.runId, workOrder, health, {
1067
+ outputRefs,
1068
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
1069
+ inputRefs: acceptedDoHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
1070
+ generatedAt,
1071
+ rejectionReason: testAcceptance.rejectionIssue?.explanation ?? null
1072
+ });
1073
+ const stageRunProjectionRef = {
1074
+ kind: 'projection',
1075
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
1076
+ };
1077
+ const handoff = testAcceptance.testAcceptanceStatus === 'accepted'
1078
+ ? buildTestWorkflowHandoff(decision.scope, decision, {
1079
+ outputRefs,
1080
+ requiredInputRefs: testAcceptance.acceptedTestEvidenceRef ? [testAcceptance.acceptedTestEvidenceRef] : [],
1081
+ evidenceRefs: artifactRefs,
1082
+ riskDecisionRef: lifecycleRiskProjectionRef,
1083
+ generatedAt
1084
+ })
1085
+ : null;
1086
+ const workflowHandoffProjectionRef = handoff
1087
+ ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(handoff.scope, handoff.fromStage, handoff.toStage)}` }
1088
+ : undefined;
1089
+ const rejection = testAcceptance.rejectionIssue
1090
+ ? buildStageRejection('test', decision.scope, null, testAcceptance.rejectionIssue.reasonCode, testAcceptance.rejectionIssue.explanation, testAcceptance.rejectionIssue.requiredNextAction, testAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
1091
+ : null;
1092
+ const closureRefs = {
1093
+ runRef,
1094
+ acceptedTestEvidenceRef: testAcceptance.acceptedTestEvidenceRef,
1095
+ testAcceptanceStatus: testAcceptance.testAcceptanceStatus,
1096
+ testEvidenceHash: testAcceptance.testEvidenceHash,
1097
+ acceptedDoHandoffRef: acceptedDoHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'do', 'test')}` } : null,
1098
+ artifactRefs,
1099
+ collaborationContractRef,
1100
+ lifecycleRiskProjectionRef,
1101
+ adjudicationProjectionRef,
1102
+ stageRunProjectionRef,
1103
+ workflowHandoffProjectionRef,
1104
+ reasons: testAcceptance.reasons
1105
+ };
1106
+ const adjudication = {
1107
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1108
+ adjudicationId: stableId('test-adjudication', decision.scope, health, generatedAt),
1109
+ stage: 'test',
1110
+ scope: decision.scope,
1111
+ health,
1112
+ stageDecision: {
1113
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1114
+ decisionId: stableId('test-stage-decision', decision.scope, run.runId, generatedAt),
1115
+ stage: 'test',
1116
+ scope: decision.scope,
1117
+ status: health === 'ready_for_goal_verify' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
1118
+ health,
1119
+ acceptedDecisionRefs: outputRefs,
1120
+ advisoryRefs: [],
1121
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('validation') || ref.ref.includes('capability')),
1122
+ blockingReasons: health === 'ready_for_goal_verify' || health === 'no-op' ? [] : testAcceptance.reasons,
1123
+ createdAt: generatedAt
1124
+ },
1125
+ handoffPacket: handoff,
1126
+ rejection,
1127
+ nextActions: buildStageRuntimeNextActions('test', decision.scope, generatedAt, health === 'ready_for_goal_verify' ? ['stage_ready', 'handoff_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
1128
+ closureRefs,
1129
+ createdAt: generatedAt
1130
+ };
1131
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
1132
+ await recordStageRunProjection(projectRoot, stageRun);
1133
+ if (handoff) {
1134
+ await recordWorkflowHandoffProjection(projectRoot, handoff);
1135
+ }
1136
+ const projection = await recordTestCollaborationAdjudicationProjection(projectRoot, adjudication);
1137
+ return {
1138
+ runId: run.runId,
1139
+ branch: context.rawBranch,
1140
+ workOrder,
1141
+ adjudication,
1142
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
1143
+ artifactRefs,
1144
+ registeredArtifacts,
1145
+ registeredCollaborationContracts,
1146
+ acceptedTestEvidenceRef: testAcceptance.acceptedTestEvidenceRef
1147
+ };
1148
+ }
1149
+ export async function reconcileGoalVerifyCollaborationClosure(projectRoot, input) {
1150
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
1151
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
1152
+ const decision = {
1153
+ ...input.decision,
1154
+ scope: { ...input.decision.scope, branch: context.rawBranch }
1155
+ };
1156
+ const run = await createRun(projectRoot, {
1157
+ runId: input.runId,
1158
+ branch: context.rawBranch,
1159
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
1160
+ });
1161
+ const runRef = { kind: 'run', ref: run.runId };
1162
+ const goalVerifyRequired = decision.requiredStages.includes('goal-verify');
1163
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('goal-verify');
1164
+ const acceptedTestHandoffEnvelope = await readWorkflowHandoffProjection(projectRoot, decision.scope, 'test', 'goal-verify');
1165
+ const workOrderInputRefs = acceptedTestHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs;
1166
+ const profileRef = { kind: 'projection', ref: `${GOAL_VERIFY_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${goalVerifyCollaborationScopeKey(decision.scope)}:profile` };
1167
+ const workOrder = goalVerifyRequired && !blocked ? buildGoalVerifyStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
1168
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'goal-verify', workOrder, input.collaborationContractRefs, generatedAt);
1169
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
1170
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
1171
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'goal-verify', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
1172
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
1173
+ const lifecycleRiskProjectionRef = {
1174
+ kind: 'projection',
1175
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
1176
+ };
1177
+ const adjudicationProjectionRef = goalVerifyAdjudicationProjectionRef(decision.scope);
1178
+ const goalVerifyAcceptance = await validateAcceptedGoalVerifyArtifact(projectRoot, {
1179
+ partition: context.partition,
1180
+ required: goalVerifyRequired,
1181
+ blocked,
1182
+ registeredArtifacts,
1183
+ registeredCollaborationContracts,
1184
+ acceptedTestHandoff: acceptedTestHandoffEnvelope?.payload ?? null
1185
+ });
1186
+ const truthAlignmentObservation = goalVerifyAcceptance.truthAlignment;
1187
+ const truthAlignmentStatus = goalVerifyAcceptance.goalVerifyAcceptanceStatus === 'accepted'
1188
+ ? truthAlignmentObservation?.status ?? 'aligned'
1189
+ : 'blocked';
1190
+ const declaredTruthRefs = await collectTruthAlignmentDeclaredTruthRefs(projectRoot, context.rawBranch, acceptedTestHandoffEnvelope?.payload ?? null);
1191
+ const truthAlignmentProjection = goalVerifyRequired
1192
+ ? buildTruthAlignmentProjection(decision.scope, {
1193
+ acceptedGoalVerificationRef: goalVerifyAcceptance.acceptedGoalVerificationRef,
1194
+ declaredTruthRefs,
1195
+ artifactRefs,
1196
+ status: truthAlignmentStatus,
1197
+ ownerStage: truthAlignmentObservation?.ownerStage ?? null,
1198
+ semanticImpact: truthAlignmentObservation?.semanticImpact ?? (truthAlignmentStatus === 'aligned' ? 'none' : 'material'),
1199
+ staleRefs: truthAlignmentObservation?.staleRefs ?? [],
1200
+ invalidatesStages: truthAlignmentObservation?.invalidatesStages ?? (truthAlignmentStatus === 'aligned' ? [] : ['ship']),
1201
+ reasons: truthAlignmentObservation?.reasons.length ? truthAlignmentObservation.reasons : goalVerifyAcceptance.goalVerifyAcceptanceStatus === 'accepted'
1202
+ ? ['Accepted goal verification is structurally aligned with declared upstream truth refs.']
1203
+ : goalVerifyAcceptance.reasons,
1204
+ generatedAt
1205
+ })
1206
+ : null;
1207
+ const goalVerifyClosureReasons = truthAlignmentProjection?.status !== 'aligned' && truthAlignmentProjection?.reasons.length ? truthAlignmentProjection.reasons : goalVerifyAcceptance.reasons;
1208
+ const truthAlignmentProjectionRef = truthAlignmentProjection
1209
+ ? { kind: 'projection', ref: `${TRUTH_ALIGNMENT_PROJECTION_TYPE}:${truthAlignmentScopeKey({ branch: decision.scope.branch })}` }
1210
+ : undefined;
1211
+ const health = goalVerifyAcceptance.goalVerifyAcceptanceStatus === 'accepted'
1212
+ ? truthAlignmentStatus === 'aligned' ? 'ready_for_ship' : 'blocked'
1213
+ : !goalVerifyRequired
1214
+ ? 'no-op'
1215
+ : blocked
1216
+ ? 'blocked'
1217
+ : 'rejected';
1218
+ const outputRefs = [goalVerifyAcceptance.acceptedGoalVerificationRef, truthAlignmentProjectionRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null && ref !== undefined);
1219
+ const stageRun = buildGoalVerifyClosureStageRun(decision.scope, run.runId, workOrder, health, {
1220
+ outputRefs,
1221
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
1222
+ inputRefs: acceptedTestHandoffEnvelope?.payload.requiredInputRefs ?? decision.inputRefs,
1223
+ generatedAt,
1224
+ rejectionReason: goalVerifyAcceptance.rejectionIssue?.explanation ?? null
1225
+ });
1226
+ const stageRunProjectionRef = {
1227
+ kind: 'projection',
1228
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
1229
+ };
1230
+ const handoff = null;
1231
+ const workflowHandoffProjectionRef = undefined;
1232
+ const rejection = goalVerifyAcceptance.rejectionIssue
1233
+ ? buildStageRejection('goal-verify', decision.scope, null, goalVerifyAcceptance.rejectionIssue.reasonCode, goalVerifyAcceptance.rejectionIssue.explanation, goalVerifyAcceptance.rejectionIssue.requiredNextAction, goalVerifyAcceptance.rejectionIssue.fallbackRoute, generatedAt, true)
1234
+ : null;
1235
+ const closureRefs = {
1236
+ runRef,
1237
+ acceptedGoalVerificationRef: goalVerifyAcceptance.acceptedGoalVerificationRef,
1238
+ goalVerifyAcceptanceStatus: goalVerifyAcceptance.goalVerifyAcceptanceStatus,
1239
+ goalVerificationHash: goalVerifyAcceptance.goalVerificationHash,
1240
+ acceptedTestHandoffRef: acceptedTestHandoffEnvelope ? { kind: 'projection', ref: `${WORKFLOW_HANDOFF_PROJECTION_TYPE}:${workflowHandoffScopeKey(decision.scope, 'test', 'goal-verify')}` } : null,
1241
+ artifactRefs,
1242
+ collaborationContractRef,
1243
+ lifecycleRiskProjectionRef,
1244
+ adjudicationProjectionRef,
1245
+ stageRunProjectionRef,
1246
+ workflowHandoffProjectionRef,
1247
+ truthAlignmentProjectionRef,
1248
+ reasons: goalVerifyClosureReasons
1249
+ };
1250
+ const adjudication = {
1251
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1252
+ adjudicationId: stableId('goal-verify-adjudication', decision.scope, health, generatedAt),
1253
+ stage: 'goal-verify',
1254
+ scope: decision.scope,
1255
+ health,
1256
+ stageDecision: {
1257
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1258
+ decisionId: stableId('goal-verify-stage-decision', decision.scope, run.runId, generatedAt),
1259
+ stage: 'goal-verify',
1260
+ scope: decision.scope,
1261
+ status: health === 'ready_for_ship' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
1262
+ health,
1263
+ acceptedDecisionRefs: outputRefs,
1264
+ advisoryRefs: [],
1265
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('goal-verification') || ref.ref.includes('capability')),
1266
+ blockingReasons: health === 'ready_for_ship' || health === 'no-op' ? [] : goalVerifyClosureReasons,
1267
+ createdAt: generatedAt
1268
+ },
1269
+ handoffPacket: handoff,
1270
+ rejection,
1271
+ nextActions: buildStageRuntimeNextActions('goal-verify', decision.scope, generatedAt, health === 'ready_for_ship' ? ['stage_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
1272
+ closureRefs,
1273
+ createdAt: generatedAt
1274
+ };
1275
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
1276
+ await recordStageRunProjection(projectRoot, stageRun);
1277
+ if (truthAlignmentProjection) {
1278
+ await recordTruthAlignmentProjection(projectRoot, truthAlignmentProjection);
1279
+ }
1280
+ const projection = await recordGoalVerifyCollaborationAdjudicationProjection(projectRoot, adjudication);
1281
+ return {
1282
+ runId: run.runId,
1283
+ branch: context.rawBranch,
1284
+ workOrder,
1285
+ adjudication,
1286
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
1287
+ artifactRefs,
1288
+ registeredArtifacts,
1289
+ registeredCollaborationContracts,
1290
+ acceptedGoalVerificationRef: goalVerifyAcceptance.acceptedGoalVerificationRef
1291
+ };
1292
+ }
1293
+ export async function reconcileShipCollaborationClosure(projectRoot, input) {
1294
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
1295
+ const context = await resolveSddContext(projectRoot, { branch: input.decision.scope.branch, branchSource: 'cli_option' });
1296
+ const decision = {
1297
+ ...input.decision,
1298
+ scope: { ...input.decision.scope, branch: context.rawBranch }
1299
+ };
1300
+ const run = await createRun(projectRoot, {
1301
+ runId: input.runId,
1302
+ branch: context.rawBranch,
1303
+ lifecycleDecision: runLifecycleDecisionRecord(decision)
1304
+ });
1305
+ const runRef = { kind: 'run', ref: run.runId };
1306
+ const shipRequired = decision.requiredStages.includes('ship');
1307
+ const openShipBlockers = shipBlockerCount(decision);
1308
+ const blockedReason = openShipBlockers > 0
1309
+ ? 'Lifecycle risk decision has open ship blockers.'
1310
+ : 'Lifecycle risk decision blocks ship closure.';
1311
+ const blocked = decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' || decision.blockedStages.includes('ship') || openShipBlockers > 0;
1312
+ const truthAlignmentEnvelope = await readTruthAlignmentProjection(projectRoot, decision.scope);
1313
+ const workOrderInputRefs = truthAlignmentEnvelope?.payload.acceptedRealityRefs ?? decision.inputRefs;
1314
+ const profileRef = { kind: 'projection', ref: `${SHIP_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${shipCollaborationScopeKey(decision.scope)}:profile` };
1315
+ const workOrder = shipRequired && !blocked ? buildShipStageWorkOrder(decision.scope, profileRef, workOrderInputRefs, generatedAt) : null;
1316
+ const registeredCollaborationContracts = await registerStageCollaborationContracts(projectRoot, context.rawBranch, 'ship', workOrder, input.collaborationContractRefs, generatedAt);
1317
+ const validatedCollaborationContract = latestValidatedStageCollaborationContract(registeredCollaborationContracts);
1318
+ const collaborationContractRef = validatedCollaborationContract ? runtimeRefForStageCollaborationContract(validatedCollaborationContract) : null;
1319
+ const registeredArtifacts = await registerBranchStageArtifacts(projectRoot, context.rawBranch, 'ship', input.artifactRefs, Boolean(workOrder), run.runId, generatedAt);
1320
+ const artifactRefs = registeredArtifacts.map(runtimeRefForStageArtifact);
1321
+ const lifecycleRiskProjectionRef = {
1322
+ kind: 'projection',
1323
+ ref: `${LIFECYCLE_RISK_DECISION_PROJECTION_TYPE}:${lifecycleRiskDecisionScopeKey(decision.scope)}`
1324
+ };
1325
+ const adjudicationProjectionRef = shipAdjudicationProjectionRef(decision.scope);
1326
+ const shipReadiness = await validateAcceptedShipReadinessArtifact(projectRoot, {
1327
+ partition: context.partition,
1328
+ required: shipRequired,
1329
+ blocked,
1330
+ blockedReason,
1331
+ registeredArtifacts,
1332
+ registeredCollaborationContracts,
1333
+ truthAlignment: truthAlignmentEnvelope?.payload ?? null
1334
+ });
1335
+ const health = shipReadiness.shipReadinessStatus === 'accepted'
1336
+ ? 'ship_ready'
1337
+ : !shipRequired
1338
+ ? 'no-op'
1339
+ : blocked
1340
+ ? 'blocked'
1341
+ : 'rejected';
1342
+ const truthAlignmentProjectionRef = truthAlignmentEnvelope ? { kind: 'projection', ref: `${TRUTH_ALIGNMENT_PROJECTION_TYPE}:${truthAlignmentScopeKey({ branch: decision.scope.branch })}` } : null;
1343
+ const outputRefs = [shipReadiness.acceptedShipReadinessRef, shipReadiness.releaseDocumentRef, truthAlignmentProjectionRef, adjudicationProjectionRef, collaborationContractRef, ...artifactRefs].filter((ref) => ref !== null);
1344
+ const stageRun = buildShipClosureStageRun(decision.scope, run.runId, workOrder, health, {
1345
+ outputRefs,
1346
+ decisionRefs: [lifecycleRiskProjectionRef, adjudicationProjectionRef],
1347
+ inputRefs: truthAlignmentEnvelope?.payload.acceptedRealityRefs ?? decision.inputRefs,
1348
+ generatedAt,
1349
+ rejectionReason: shipReadiness.rejectionIssue?.explanation ?? null
1350
+ });
1351
+ const stageRunProjectionRef = {
1352
+ kind: 'projection',
1353
+ ref: `${STAGE_RUN_PROJECTION_TYPE}:${stageRunScopeKey(stageRun.scope, stageRun.stage)}`
1354
+ };
1355
+ const rejection = shipReadiness.rejectionIssue
1356
+ ? buildStageRejection('ship', decision.scope, null, shipReadiness.rejectionIssue.reasonCode, shipReadiness.rejectionIssue.explanation, shipReadiness.rejectionIssue.requiredNextAction, shipReadiness.rejectionIssue.fallbackRoute, generatedAt, true)
1357
+ : null;
1358
+ const closureRefs = {
1359
+ runRef,
1360
+ acceptedShipReadinessRef: shipReadiness.acceptedShipReadinessRef,
1361
+ shipReadinessStatus: shipReadiness.shipReadinessStatus,
1362
+ shipReadinessHash: shipReadiness.shipReadinessHash,
1363
+ releaseDocumentRef: shipReadiness.releaseDocumentRef,
1364
+ truthAlignmentProjectionRef,
1365
+ artifactRefs,
1366
+ collaborationContractRef,
1367
+ lifecycleRiskProjectionRef,
1368
+ adjudicationProjectionRef,
1369
+ stageRunProjectionRef,
1370
+ reasons: shipReadiness.reasons
1371
+ };
1372
+ const adjudication = {
1373
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1374
+ adjudicationId: stableId('ship-adjudication', decision.scope, health, generatedAt),
1375
+ stage: 'ship',
1376
+ scope: decision.scope,
1377
+ health,
1378
+ stageDecision: {
1379
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1380
+ decisionId: stableId('ship-stage-decision', decision.scope, run.runId, generatedAt),
1381
+ stage: 'ship',
1382
+ scope: decision.scope,
1383
+ status: health === 'ship_ready' ? 'completed' : health === 'no-op' ? 'skipped' : health === 'blocked' ? 'blocked' : 'rejected',
1384
+ health,
1385
+ acceptedDecisionRefs: outputRefs,
1386
+ advisoryRefs: [],
1387
+ capabilityRefs: artifactRefs.filter((ref) => ref.ref.includes('ship') || ref.ref.includes('release') || ref.ref.includes('capability')),
1388
+ blockingReasons: health === 'ship_ready' || health === 'no-op' ? [] : shipReadiness.reasons,
1389
+ createdAt: generatedAt
1390
+ },
1391
+ handoffPacket: null,
1392
+ rejection,
1393
+ nextActions: buildStageRuntimeNextActions('ship', decision.scope, generatedAt, health === 'ship_ready' ? ['stage_ready'] : rejection ? [rejectionReasonToNextActionReason(rejection.reasonCode)] : ['report_only']),
1394
+ closureRefs,
1395
+ createdAt: generatedAt
1396
+ };
1397
+ await recordLifecycleRiskDecisionProjection(projectRoot, decision);
1398
+ await recordStageRunProjection(projectRoot, stageRun);
1399
+ const projection = await recordShipCollaborationAdjudicationProjection(projectRoot, adjudication);
1400
+ return {
1401
+ runId: run.runId,
1402
+ branch: context.rawBranch,
1403
+ workOrder,
1404
+ adjudication,
1405
+ projectionRef: { kind: 'projection', ref: `${projection.envelope.projectionType}:${projection.envelope.scopeKey}` },
1406
+ artifactRefs,
1407
+ registeredArtifacts,
1408
+ registeredCollaborationContracts,
1409
+ acceptedShipReadinessRef: shipReadiness.acceptedShipReadinessRef,
1410
+ releaseDocumentRef: shipReadiness.releaseDocumentRef
1411
+ };
1412
+ }
1413
+ const FULL_STAGE_CHAIN = [
1414
+ { stage: 'spec', expectedHealth: 'ready_for_plan', projectionType: SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1415
+ { stage: 'plan', expectedHealth: 'ready_for_tasks', projectionType: PLAN_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1416
+ { stage: 'tasks', expectedHealth: 'ready_for_verifies', projectionType: TASKS_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1417
+ { stage: 'verifies', expectedHealth: 'ready_for_do', projectionType: VERIFIES_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1418
+ { stage: 'do', expectedHealth: 'ready_for_test', projectionType: DO_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1419
+ { stage: 'test', expectedHealth: 'ready_for_goal_verify', projectionType: TEST_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1420
+ { stage: 'goal-verify', expectedHealth: 'ready_for_ship', projectionType: GOAL_VERIFY_COLLABORATION_ADJUDICATION_PROJECTION_TYPE },
1421
+ { stage: 'ship', expectedHealth: 'ship_ready', projectionType: SHIP_COLLABORATION_ADJUDICATION_PROJECTION_TYPE }
1422
+ ];
1423
+ export async function inspectFullStageChain(projectRoot, branch) {
1424
+ const projections = await listRuntimeProjections(projectRoot, FULL_STAGE_CHAIN.map((item) => item.projectionType));
1425
+ const envelopes = projections
1426
+ .map((projection) => projection.payload)
1427
+ .filter((envelope) => envelope?.payload?.scope?.branch === branch);
1428
+ const stages = FULL_STAGE_CHAIN.map((item) => fullStageChainStage(item, latestStageChainEnvelope(envelopes, item.projectionType, item.stage)));
1429
+ const completedStages = stages.filter((stage) => stage.status === 'completed' && stage.health === stage.expectedHealth).length;
1430
+ const validatedCollaborationContracts = stages.filter((stage) => stage.collaborationContractStatus === 'validated').length;
1431
+ const handoffs = stages.filter((stage) => stage.handoffProjectionRef !== null).length;
1432
+ const status = fullStageChainStatus(stages);
1433
+ return {
1434
+ contract: 'sdd-full-stage-chain-diagnostic-v1',
1435
+ branch,
1436
+ status,
1437
+ stages,
1438
+ projectionCounts: {
1439
+ adjudications: stages.filter((stage) => stage.projectionRef !== null).length,
1440
+ completedStages,
1441
+ validatedCollaborationContracts,
1442
+ handoffs
1443
+ },
1444
+ finalHealth: fullStageChainFinalHealth(stages),
1445
+ reasons: fullStageChainReasons(status, stages)
1446
+ };
1447
+ }
1448
+ async function collectTruthAlignmentDeclaredTruthRefs(projectRoot, branch, testHandoff) {
1449
+ const projectionTypes = FULL_STAGE_CHAIN
1450
+ .filter((item) => item.stage !== 'goal-verify' && item.stage !== 'ship')
1451
+ .map((item) => item.projectionType);
1452
+ const projections = await listRuntimeProjections(projectRoot, projectionTypes);
1453
+ const envelopes = projections
1454
+ .map((projection) => projection.payload)
1455
+ .filter((envelope) => envelope?.payload?.scope?.branch === branch);
1456
+ const acceptedRefs = FULL_STAGE_CHAIN
1457
+ .filter((item) => item.stage !== 'goal-verify' && item.stage !== 'ship')
1458
+ .map((item) => latestStageChainEnvelope(envelopes, item.projectionType, item.stage))
1459
+ .map((envelope) => envelope ? acceptedRefFromAdjudication(envelope.payload.stage, envelope.payload) : null)
1460
+ .filter((ref) => ref !== null);
1461
+ return uniqueRuntimeRefs([...acceptedRefs, ...(testHandoff?.requiredInputRefs ?? [])]);
1462
+ }
1463
+ function latestStageChainEnvelope(envelopes, projectionType, stage) {
1464
+ return envelopes
1465
+ .filter((envelope) => envelope.projectionType === projectionType && envelope.payload.stage === stage)
1466
+ .sort((left, right) => right.generatedAt.localeCompare(left.generatedAt))[0] ?? null;
1467
+ }
1468
+ function fullStageChainStage(item, envelope) {
1469
+ const adjudication = envelope?.payload ?? null;
1470
+ const closureRefs = (adjudication?.closureRefs ?? {});
1471
+ const collaborationContractRef = runtimeRefValue(closureRefs.collaborationContractRef);
1472
+ const handoff = adjudication?.handoffPacket ? {
1473
+ fromStage: adjudication.handoffPacket.fromStage,
1474
+ toStage: adjudication.handoffPacket.toStage,
1475
+ status: adjudication.handoffPacket.status
1476
+ } : null;
1477
+ const acceptedRef = adjudication ? acceptedRefFromAdjudication(item.stage, adjudication) : null;
1478
+ return {
1479
+ stage: item.stage,
1480
+ expectedHealth: item.expectedHealth,
1481
+ projectionType: item.projectionType,
1482
+ projectionRef: envelope ? { kind: 'projection', ref: `${envelope.projectionType}:${envelope.scopeKey}` } : null,
1483
+ health: adjudication?.health ?? null,
1484
+ status: adjudication?.stageDecision?.status ?? null,
1485
+ acceptedRef,
1486
+ acceptedHash: acceptedRef?.hash ?? null,
1487
+ collaborationContractRef,
1488
+ collaborationContractStatus: collaborationContractRef ? 'validated' : 'missing',
1489
+ handoff,
1490
+ handoffProjectionRef: runtimeRefValue(closureRefs.workflowHandoffProjectionRef),
1491
+ reasons: Array.isArray(closureRefs.reasons) ? closureRefs.reasons.filter((reason) => typeof reason === 'string') : adjudication?.rejection ? [adjudication.rejection.explanation] : []
1492
+ };
1493
+ }
1494
+ function acceptedRefFromAdjudication(stage, adjudication) {
1495
+ const closureRefs = (adjudication.closureRefs ?? {});
1496
+ const keyByStage = {
1497
+ spec: 'acceptedSpecRef',
1498
+ plan: 'acceptedPlanRef',
1499
+ tasks: 'acceptedTasksRef',
1500
+ verifies: 'acceptedVerifyRef',
1501
+ do: 'acceptedImplementationRef',
1502
+ test: 'acceptedTestEvidenceRef',
1503
+ 'goal-verify': 'acceptedGoalVerificationRef',
1504
+ ship: 'acceptedShipReadinessRef'
1505
+ };
1506
+ return runtimeRefValue(closureRefs[keyByStage[stage]]) ?? adjudication.stageDecision?.acceptedDecisionRefs[0] ?? null;
1507
+ }
1508
+ function runtimeRefValue(value) {
1509
+ if (!value || typeof value !== 'object') {
1510
+ return null;
1511
+ }
1512
+ const candidate = value;
1513
+ if (typeof candidate.kind !== 'string' || typeof candidate.ref !== 'string') {
1514
+ return null;
1515
+ }
1516
+ return {
1517
+ kind: candidate.kind,
1518
+ ref: candidate.ref,
1519
+ hash: typeof candidate.hash === 'string' ? candidate.hash : undefined
1520
+ };
1521
+ }
1522
+ function fullStageChainFinalHealth(stages) {
1523
+ return [...stages]
1524
+ .reverse()
1525
+ .find((stage) => stage.status === 'completed' && stage.health === stage.expectedHealth)?.health ?? null;
1526
+ }
1527
+ function fullStageChainStatus(stages) {
1528
+ if (stages.every((stage) => stage.projectionRef === null)) {
1529
+ return 'missing';
1530
+ }
1531
+ if (stages.some((stage) => stage.status === 'rejected' || stage.health === 'rejected')) {
1532
+ return 'rejected';
1533
+ }
1534
+ if (stages.some((stage) => stage.status === 'blocked' || stage.health === 'blocked')) {
1535
+ return 'blocked';
1536
+ }
1537
+ if (stages.every((stage) => stage.status === 'completed' && stage.health === stage.expectedHealth && stage.collaborationContractStatus === 'validated')) {
1538
+ return 'ready';
1539
+ }
1540
+ return 'partial';
1541
+ }
1542
+ function fullStageChainReasons(status, stages) {
1543
+ if (status === 'missing') {
1544
+ return ['No Phase 9 stage collaboration adjudication projections found.'];
1545
+ }
1546
+ if (status === 'ready') {
1547
+ return ['Active Phase 9 stage chain is closed through ship_ready with validated collaboration contracts.'];
1548
+ }
1549
+ return stages
1550
+ .filter((stage) => stage.status !== 'completed' || stage.health !== stage.expectedHealth || stage.collaborationContractStatus !== 'validated')
1551
+ .map((stage) => `${stage.stage} is ${stage.health ?? 'missing'}; expected ${stage.expectedHealth}; collaboration_contract=${stage.collaborationContractStatus}.`);
1552
+ }
1553
+ export async function inspectSpecCollaborationHealth(projectRoot, branch) {
1554
+ const projections = await listRuntimeProjections(projectRoot, [SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE]);
1555
+ const envelopes = projections
1556
+ .map((projection) => projection.payload)
1557
+ .filter((envelope) => envelope?.payload?.scope?.branch === branch);
1558
+ const latest = envelopes[0]?.payload ?? null;
1559
+ if (!latest) {
1560
+ return {
1561
+ status: 'missing',
1562
+ branch,
1563
+ latest: null,
1564
+ projectionCount: envelopes.length,
1565
+ latestClarificationGateId: null,
1566
+ latestHandoffId: null,
1567
+ latestRejectionReason: null,
1568
+ reasons: ['No spec collaboration adjudication projection found.']
1569
+ };
1570
+ }
1571
+ if (latest.contract !== STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION) {
1572
+ return {
1573
+ status: 'incompatible',
1574
+ branch,
1575
+ latest,
1576
+ projectionCount: envelopes.length,
1577
+ latestClarificationGateId: latest.clarificationGate?.gateId ?? null,
1578
+ latestHandoffId: latest.handoffPacket?.handoffId ?? null,
1579
+ latestRejectionReason: latest.rejection?.reasonCode ?? null,
1580
+ reasons: ['Latest spec collaboration projection has an incompatible contract.']
1581
+ };
1582
+ }
1583
+ return {
1584
+ status: latest.health,
1585
+ branch,
1586
+ latest,
1587
+ projectionCount: envelopes.length,
1588
+ latestClarificationGateId: latest.clarificationGate?.gateId ?? null,
1589
+ latestHandoffId: latest.handoffPacket?.handoffId ?? null,
1590
+ latestRejectionReason: latest.rejection?.reasonCode ?? null,
1591
+ reasons: specCollaborationDiagnosticReasons(latest)
1592
+ };
1593
+ }
1594
+ export function adjudicateSpecStageClosureRequest(input) {
1595
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
1596
+ if (input.profile.intensity === 'blocked') {
1597
+ return rejected(input, 'blocked_lifecycle', 'Lifecycle risk decision blocks spec collaboration.', 'Resolve lifecycle blockers before retrying spec collaboration.', 'runtime-blocked', generatedAt, false);
1598
+ }
1599
+ if (!input.profile.required) {
1600
+ return noOpResult(input.profile, generatedAt);
1601
+ }
1602
+ const closureIssue = validateStageClosureRequest(input);
1603
+ if (closureIssue) {
1604
+ return rejected(input, closureIssue.reasonCode, closureIssue.explanation, closureIssue.requiredNextAction, closureIssue.fallbackRoute, generatedAt, closureIssue.retryAllowed);
1605
+ }
1606
+ const closureRequest = input.closureRequest;
1607
+ const acceptedItems = closureRequest.candidate?.acceptedItems ?? [];
1608
+ const blockingItems = acceptedItems.filter((item) => item.kind === 'blocking_ambiguity' || closureRequest.unresolvedAmbiguityIds.includes(item.id));
1609
+ if (blockingItems.length > 0 && (input.answerRefs ?? []).length === 0) {
1610
+ const clarificationGate = {
1611
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1612
+ gateId: stableId('spec-clarification', input.profile.scope, closureRequest.closureRequestId, generatedAt),
1613
+ stage: 'spec',
1614
+ scope: input.profile.scope,
1615
+ status: 'needs_clarification',
1616
+ questions: blockingItems.map((item) => item.summary),
1617
+ blockingItemIds: blockingItems.map((item) => item.id),
1618
+ requiredResponder: 'user',
1619
+ createdAt: generatedAt,
1620
+ answerRefs: []
1621
+ };
1622
+ return baseResult(input.profile, generatedAt, {
1623
+ health: 'needs_clarification',
1624
+ acceptedItemIds: acceptedItems.filter((item) => item.kind !== 'blocking_ambiguity').map((item) => item.id),
1625
+ clarificationGate,
1626
+ nextActions: buildRuntimeNextActions(input.profile, generatedAt, ['unresolved_ambiguity'])
1627
+ });
1628
+ }
1629
+ const acceptedItemIds = acceptedItems.map((item) => item.id);
1630
+ const decisionRecord = buildSpecDecisionRecord(input.profile, closureRequest, input.answerRefs ?? [], acceptedItemIds, generatedAt);
1631
+ const stageDecision = buildStageRuntimeDecision(input.profile, closureRequest, decisionRecord, generatedAt);
1632
+ const handoffPacket = buildStageHandoffPacket(input.profile, closureRequest, decisionRecord, generatedAt);
1633
+ return baseResult(input.profile, generatedAt, {
1634
+ health: 'ready_for_plan',
1635
+ acceptedItemIds,
1636
+ specDecisionRecord: decisionRecord,
1637
+ stageDecision,
1638
+ handoffPacket,
1639
+ nextActions: buildRuntimeNextActions(input.profile, generatedAt, ['stage_ready', 'handoff_ready'])
1640
+ });
1641
+ }
1642
+ function specIntensity(decision, required) {
1643
+ if (decision.profile === 'blocked' || decision.blockedStages.includes('spec')) {
1644
+ return 'blocked';
1645
+ }
1646
+ if (!required || decision.profile === 'direct') {
1647
+ return 'noop';
1648
+ }
1649
+ if (decision.profile === 'compact') {
1650
+ return 'lightweight';
1651
+ }
1652
+ if (decision.profile === 'research') {
1653
+ return 'scout-first';
1654
+ }
1655
+ return 'team-required';
1656
+ }
1657
+ function specProfileReasons(decision, required, intensity) {
1658
+ if (intensity === 'blocked') {
1659
+ return ['Lifecycle risk decision blocks spec collaboration.', ...decision.reasons];
1660
+ }
1661
+ if (!required) {
1662
+ return ['Spec stage is not required by lifecycle risk decision.', ...decision.reasons];
1663
+ }
1664
+ if (intensity === 'scout-first') {
1665
+ return ['Research profile requires spec-stage uncertainty discovery before plan handoff.', ...decision.reasons];
1666
+ }
1667
+ if (intensity === 'team-required') {
1668
+ return ['Full lifecycle requires runtime-adjudicated spec collaboration before plan handoff.', ...decision.reasons];
1669
+ }
1670
+ return ['Spec collaboration can remain lightweight for this lifecycle decision.', ...decision.reasons];
1671
+ }
1672
+ function specMemberAgents(intensity) {
1673
+ if (intensity === 'noop' || intensity === 'blocked') {
1674
+ return [];
1675
+ }
1676
+ if (intensity === 'lightweight') {
1677
+ return [SPEC_STAGE_DRAFTER_AGENT, SPEC_STAGE_REVIEW_AGENT];
1678
+ }
1679
+ return [SPEC_STAGE_SCOUT_AGENT, SPEC_STAGE_DRAFTER_AGENT, SPEC_STAGE_REVIEW_AGENT];
1680
+ }
1681
+ function specRequiredCapabilities(intensity) {
1682
+ if (intensity === 'noop' || intensity === 'blocked') {
1683
+ return [];
1684
+ }
1685
+ if (intensity === 'lightweight') {
1686
+ return ['norm_discovery'];
1687
+ }
1688
+ return SPEC_STAGE_REQUIRED_CAPABILITIES;
1689
+ }
1690
+ function specOptionalCapabilities(intensity) {
1691
+ if (intensity === 'noop' || intensity === 'blocked') {
1692
+ return [];
1693
+ }
1694
+ if (intensity === 'lightweight') {
1695
+ return ['uncertainty_resolution', 'context_curation'];
1696
+ }
1697
+ return SPEC_STAGE_OPTIONAL_CAPABILITIES;
1698
+ }
1699
+ function specMaterialPackIds(intensity) {
1700
+ if (intensity === 'noop' || intensity === 'blocked') {
1701
+ return [];
1702
+ }
1703
+ if (intensity === 'lightweight') {
1704
+ return ['project-norms', 'uncertainty-map'];
1705
+ }
1706
+ return [...SPEC_STAGE_MATERIAL_PACKS];
1707
+ }
1708
+ function specCollaborationPlan(intensity) {
1709
+ if (intensity === 'noop' || intensity === 'blocked') {
1710
+ return { topology: 'none', participants: [], maxParallelism: 0, fanIn: 'runtime_adjudication' };
1711
+ }
1712
+ const requiredCapabilities = specRequiredCapabilities(intensity);
1713
+ const optionalCapabilities = specOptionalCapabilities(intensity);
1714
+ const materialPackIds = specMaterialPackIds(intensity);
1715
+ const participants = [
1716
+ { id: SPEC_STAGE_MANAGER, kind: 'agent', role: 'spec-stage-manager', required: true, capabilityDomain: 'uncertainty_resolution', parallelGroup: null },
1717
+ ...specMemberAgents(intensity).map((agent) => ({
1718
+ id: agent,
1719
+ kind: 'subagent',
1720
+ role: agent === SPEC_STAGE_SCOUT_AGENT ? 'bounded-context-scout' : agent === SPEC_STAGE_DRAFTER_AGENT ? 'spec-document-candidate-author' : 'spec-document-reviewer',
1721
+ required: true,
1722
+ capabilityDomain: agent === SPEC_STAGE_SCOUT_AGENT ? 'context_curation' : agent === SPEC_STAGE_DRAFTER_AGENT ? 'norm_discovery' : 'uncertainty_resolution',
1723
+ parallelGroup: agent === SPEC_STAGE_SCOUT_AGENT ? 'discovery' : agent === SPEC_STAGE_DRAFTER_AGENT ? 'authoring' : 'review'
1724
+ })),
1725
+ ...requiredCapabilities.map((capability) => ({ id: `cap.${capability}`, kind: 'skill', role: 'required-capability-review', required: true, capabilityDomain: capability, parallelGroup: 'capability-review' })),
1726
+ ...optionalCapabilities.map((capability) => ({ id: `cap.${capability}`, kind: 'skill', role: 'optional-capability-review', required: false, capabilityDomain: capability, parallelGroup: 'capability-review' })),
1727
+ ...materialPackIds.map((packId) => ({ id: packId, kind: 'material-pack', role: 'capability-material', required: false, parallelGroup: 'material-pack' }))
1728
+ ];
1729
+ const topology = intensity === 'lightweight' ? 'team-lite' : intensity === 'scout-first' ? 'parallel-research' : 'team-required';
1730
+ return { topology, participants, maxParallelism: topology === 'team-lite' ? 2 : 4, fanIn: 'runtime_adjudication' };
1731
+ }
1732
+ function validateStageClosureRequest(input) {
1733
+ const request = input.closureRequest;
1734
+ if (!request) {
1735
+ return { reasonCode: 'invalid_proposal', explanation: 'Spec stage closure requires a StageClosureRequest from spec-manager.', requiredNextAction: 'Submit spec-manager closure request with candidate, review, and evidence refs.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1736
+ }
1737
+ if (request.stage !== 'spec') {
1738
+ return { reasonCode: 'unsupported_stage', explanation: `Spec runtime cannot close ${request.stage} closure requests.`, requiredNextAction: 'Route the closure request to the matching stage runtime.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1739
+ }
1740
+ if (request.authorityAttempts.length > 0) {
1741
+ return { reasonCode: 'authority_violation', explanation: `Spec stage closure attempted workflow authority: ${request.authorityAttempts.join(', ')}.`, requiredNextAction: 'Remove workflow-authority attempts from manager and agent-team outputs.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1742
+ }
1743
+ if (!request.candidate || !request.candidateRef) {
1744
+ return { reasonCode: 'invalid_proposal', explanation: 'Spec stage closure is missing SpecDocumentCandidate.', requiredNextAction: 'Ask spec-drafter to produce a candidate and submit its ref.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1745
+ }
1746
+ if (request.candidate.stage !== 'spec') {
1747
+ return { reasonCode: 'unsupported_stage', explanation: `Spec runtime cannot close ${request.candidate.stage} candidates.`, requiredNextAction: 'Route the candidate to the matching stage runtime.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1748
+ }
1749
+ if (request.candidate.inputRefs.length === 0) {
1750
+ return { reasonCode: 'missing_refs', explanation: 'SpecDocumentCandidate has no input refs.', requiredNextAction: 'Attach spec request, document, or runtime projection refs before closure.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1751
+ }
1752
+ const unsupported = request.candidate.acceptedItems.find((item) => !input.profile.allowedProposalKinds.includes(item.kind));
1753
+ if (unsupported) {
1754
+ return { reasonCode: 'invalid_proposal', explanation: `Spec candidate item ${unsupported.id} uses unsupported kind ${unsupported.kind}.`, requiredNextAction: 'Submit only allowed spec candidate item kinds for this profile.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1755
+ }
1756
+ const missingItemRef = request.candidate.acceptedItems.find((item) => item.refs.length === 0);
1757
+ if (missingItemRef) {
1758
+ return { reasonCode: 'missing_refs', explanation: `Spec candidate item ${missingItemRef.id} has no refs.`, requiredNextAction: 'Attach source refs to every candidate item.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1759
+ }
1760
+ if (request.reviewRefs.length === 0 || request.reviewResults.length === 0) {
1761
+ return { reasonCode: 'missing_required_review', explanation: 'Spec stage closure is missing spec-reviewer review evidence.', requiredNextAction: 'Ask spec-reviewer to review the candidate before closure.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1762
+ }
1763
+ const approvedReview = request.reviewResults.find((review) => review.reviewer === SPEC_STAGE_REVIEW_AGENT && review.verdict === 'approved');
1764
+ if (!approvedReview && request.unresolvedAmbiguityIds.length === 0) {
1765
+ return { reasonCode: 'missing_required_review', explanation: 'Spec stage closure has no approved spec-reviewer result.', requiredNextAction: 'Resolve review findings or submit approved spec-reviewer evidence.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1766
+ }
1767
+ if (request.evidenceRefs.length === 0) {
1768
+ return { reasonCode: 'missing_refs', explanation: 'Spec stage closure has no evidence refs.', requiredNextAction: 'Attach work order, candidate, review, and source evidence refs.', fallbackRoute: 'revise-proposal', retryAllowed: true };
1769
+ }
1770
+ return null;
1771
+ }
1772
+ function noOpResult(profile, generatedAt) {
1773
+ const decisionRecord = {
1774
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1775
+ decisionId: stableId('spec-decision', profile.scope, 'noop', generatedAt),
1776
+ stage: 'spec',
1777
+ scope: profile.scope,
1778
+ source: 'runtime-noop',
1779
+ acceptedItemIds: [],
1780
+ answerRefs: [],
1781
+ decisionRefs: profile.inputRefs,
1782
+ mustNotAssume: [],
1783
+ createdAt: generatedAt
1784
+ };
1785
+ const stageDecision = {
1786
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1787
+ decisionId: stableId('spec-stage-decision', profile.scope, 'noop', generatedAt),
1788
+ stage: 'spec',
1789
+ scope: profile.scope,
1790
+ status: 'skipped',
1791
+ health: 'no-op',
1792
+ acceptedDecisionRefs: decisionRecord.decisionRefs,
1793
+ advisoryRefs: [],
1794
+ capabilityRefs: [],
1795
+ blockingReasons: [],
1796
+ createdAt: generatedAt
1797
+ };
1798
+ return baseResult(profile, generatedAt, { health: 'no-op', specDecisionRecord: decisionRecord, stageDecision });
1799
+ }
1800
+ function buildSpecDecisionRecord(profile, closureRequest, answerRefs, acceptedItemIds, generatedAt) {
1801
+ return {
1802
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1803
+ decisionId: stableId('spec-decision', profile.scope, closureRequest.closureRequestId, generatedAt),
1804
+ stage: 'spec',
1805
+ scope: profile.scope,
1806
+ source: answerRefs.length > 0 ? 'clarification-answer' : 'stage-closure-request',
1807
+ acceptedItemIds,
1808
+ answerRefs,
1809
+ decisionRefs: closureDecisionRefs(closureRequest, answerRefs),
1810
+ mustNotAssume: closureRequest.candidate?.acceptedItems.filter((item) => item.kind === 'blocking_ambiguity').map((item) => item.summary) ?? [],
1811
+ createdAt: generatedAt
1812
+ };
1813
+ }
1814
+ function buildStageRuntimeDecision(profile, closureRequest, decisionRecord, generatedAt) {
1815
+ return {
1816
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1817
+ decisionId: stableId('spec-stage-decision', profile.scope, closureRequest.closureRequestId, generatedAt),
1818
+ stage: 'spec',
1819
+ scope: profile.scope,
1820
+ status: 'completed',
1821
+ health: 'ready_for_plan',
1822
+ acceptedDecisionRefs: decisionRecord.decisionRefs,
1823
+ advisoryRefs: refsByCandidateKind(closureRequest, 'advisory_finding'),
1824
+ capabilityRefs: refsByCandidateKind(closureRequest, 'capability_suggestion'),
1825
+ blockingReasons: [],
1826
+ createdAt: generatedAt
1827
+ };
1828
+ }
1829
+ function buildStageHandoffPacket(profile, closureRequest, decisionRecord, generatedAt) {
1830
+ return {
1831
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1832
+ handoffId: stableId('spec-plan-handoff', profile.scope, closureRequest.closureRequestId, generatedAt),
1833
+ scope: profile.scope,
1834
+ fromStage: 'spec',
1835
+ toStage: 'plan',
1836
+ status: 'proposed',
1837
+ acceptedSpecDecisionRefs: decisionRecord.decisionRefs,
1838
+ advisoryRefs: refsByCandidateKind(closureRequest, 'advisory_finding'),
1839
+ capabilityRefs: refsByCandidateKind(closureRequest, 'capability_suggestion'),
1840
+ unresolvedRisks: [],
1841
+ mustNotAssume: decisionRecord.mustNotAssume,
1842
+ recommendedNextStageRoles: recommendedPlanRoles(profile),
1843
+ createdAt: generatedAt
1844
+ };
1845
+ }
1846
+ function refsByCandidateKind(closureRequest, kind) {
1847
+ return closureRequest.candidate?.acceptedItems.filter((item) => item.kind === kind).flatMap((item) => item.refs) ?? [];
1848
+ }
1849
+ function closureDecisionRefs(closureRequest, answerRefs) {
1850
+ return [
1851
+ closureRequest.workOrderRef,
1852
+ closureRequest.coordinationRef,
1853
+ closureRequest.candidateRef,
1854
+ ...closureRequest.reviewRefs,
1855
+ ...closureRequest.capabilityFindingRefs,
1856
+ ...closureRequest.evidenceRefs,
1857
+ ...answerRefs
1858
+ ].filter((ref) => ref !== null);
1859
+ }
1860
+ function recommendedPlanRoles(profile) {
1861
+ if (profile.intensity === 'scout-first') {
1862
+ return ['role.norm-scout', 'role.uncertainty-reviewer'];
1863
+ }
1864
+ if (profile.intensity === 'team-required') {
1865
+ return ['role.norm-scout', 'role.performance-planner', 'role.verification-designer'];
1866
+ }
1867
+ return [];
1868
+ }
1869
+ function rejected(input, reasonCode, explanation, requiredNextAction, fallbackRoute, generatedAt, retryAllowed) {
1870
+ const closureRequestId = input.closureRequest?.closureRequestId ?? null;
1871
+ const rejection = {
1872
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1873
+ rejectionId: stableId('spec-rejection', input.profile.scope, closureRequestId ?? reasonCode, generatedAt),
1874
+ stage: 'spec',
1875
+ scope: input.profile.scope,
1876
+ closureRequestId,
1877
+ reasonCode,
1878
+ explanation,
1879
+ requiredNextAction,
1880
+ retryAllowed,
1881
+ retryBudgetRemaining: retryAllowed ? input.retryBudgetRemaining ?? 1 : 0,
1882
+ fallbackRoute,
1883
+ createdAt: generatedAt
1884
+ };
1885
+ return baseResult(input.profile, generatedAt, {
1886
+ health: reasonCode === 'blocked_lifecycle' ? 'blocked' : 'rejected',
1887
+ rejection,
1888
+ nextActions: buildRuntimeNextActions(input.profile, generatedAt, [rejectionReasonToNextActionReason(reasonCode)])
1889
+ });
1890
+ }
1891
+ function baseResult(profile, generatedAt, overrides) {
1892
+ return {
1893
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1894
+ adjudicationId: stableId('spec-adjudication', profile.scope, overrides.health ?? 'unknown', generatedAt),
1895
+ stage: 'spec',
1896
+ scope: profile.scope,
1897
+ health: overrides.health ?? 'rejected',
1898
+ acceptedItemIds: overrides.acceptedItemIds ?? [],
1899
+ clarificationGate: overrides.clarificationGate ?? null,
1900
+ specDecisionRecord: overrides.specDecisionRecord ?? null,
1901
+ stageDecision: overrides.stageDecision ?? null,
1902
+ handoffPacket: overrides.handoffPacket ?? null,
1903
+ rejection: overrides.rejection ?? null,
1904
+ nextActions: overrides.nextActions ?? [],
1905
+ createdAt: generatedAt
1906
+ };
1907
+ }
1908
+ function rejectionReasonToNextActionReason(reasonCode) {
1909
+ if (reasonCode === 'authority_violation') {
1910
+ return 'authority_violation';
1911
+ }
1912
+ if (reasonCode === 'missing_required_review') {
1913
+ return 'missing_required_review';
1914
+ }
1915
+ if (reasonCode === 'missing_refs') {
1916
+ return 'insufficient_evidence';
1917
+ }
1918
+ if (reasonCode === 'blocked_lifecycle') {
1919
+ return 'graph_illegal';
1920
+ }
1921
+ return 'manager_rework_required';
1922
+ }
1923
+ function buildRuntimeNextActions(profile, generatedAt, reasonCodes) {
1924
+ return reasonCodes
1925
+ .map((reasonCode, index) => ({
1926
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
1927
+ actionId: stableId('spec-next-action', profile.scope, reasonCode, generatedAt),
1928
+ stage: 'spec',
1929
+ scope: profile.scope,
1930
+ kind: nextActionKind(reasonCode),
1931
+ owner: nextActionOwner(reasonCode),
1932
+ actionScope: nextActionScope(reasonCode),
1933
+ reasonCode,
1934
+ priority: {
1935
+ reasonRank: reasonCodeRank(reasonCode),
1936
+ upstreamDistance: 0,
1937
+ actionRank: actionKindRank(nextActionKind(reasonCode)),
1938
+ stableOrder: index
1939
+ },
1940
+ requiredInputs: nextActionRequiredInputs(reasonCode),
1941
+ blockedBy: [],
1942
+ createdAt: generatedAt
1943
+ }))
1944
+ .sort(compareRuntimeNextAction);
1945
+ }
1946
+ function compareRuntimeNextAction(left, right) {
1947
+ return left.priority.reasonRank - right.priority.reasonRank
1948
+ || left.priority.upstreamDistance - right.priority.upstreamDistance
1949
+ || left.priority.actionRank - right.priority.actionRank
1950
+ || left.priority.stableOrder - right.priority.stableOrder;
1951
+ }
1952
+ function reasonCodeRank(reasonCode) {
1953
+ const order = ['authority_violation', 'graph_illegal', 'scope_unresolved', 'ambiguous_target', 'missing_canonical_artifact', 'stale_upstream', 'missing_handoff', 'missing_required_review', 'unresolved_conflict', 'unresolved_ambiguity', 'insufficient_evidence', 'manager_rework_required', 'human_required', 'stage_ready', 'handoff_ready', 'report_only'];
1954
+ return order.indexOf(reasonCode);
1955
+ }
1956
+ function nextActionKind(reasonCode) {
1957
+ if (reasonCode === 'authority_violation' || reasonCode === 'graph_illegal') {
1958
+ return 'reject_proposal';
1959
+ }
1960
+ if (reasonCode === 'missing_required_review') {
1961
+ return 'collect_required_review';
1962
+ }
1963
+ if (reasonCode === 'unresolved_ambiguity') {
1964
+ return 'ask_user_clarification';
1965
+ }
1966
+ if (reasonCode === 'handoff_ready') {
1967
+ return 'create_handoff';
1968
+ }
1969
+ if (reasonCode === 'stage_ready') {
1970
+ return 'commit_stage_closure';
1971
+ }
1972
+ if (reasonCode === 'stale_upstream') {
1973
+ return 'refresh_upstream_stage';
1974
+ }
1975
+ if (reasonCode === 'report_only') {
1976
+ return 'report_status';
1977
+ }
1978
+ return 'request_manager_rework';
1979
+ }
1980
+ function nextActionOwner(reasonCode) {
1981
+ if (reasonCode === 'unresolved_ambiguity' || reasonCode === 'human_required') {
1982
+ return 'user';
1983
+ }
1984
+ if (reasonCode === 'stage_ready' || reasonCode === 'handoff_ready' || reasonCode === 'report_only') {
1985
+ return 'runtime';
1986
+ }
1987
+ if (reasonCode === 'scope_unresolved' || reasonCode === 'ambiguous_target') {
1988
+ return 'orchestrator';
1989
+ }
1990
+ return 'stage-manager';
1991
+ }
1992
+ function nextActionScope(reasonCode) {
1993
+ if (reasonCode === 'unresolved_ambiguity' || reasonCode === 'human_required') {
1994
+ return 'human';
1995
+ }
1996
+ if (reasonCode === 'stage_ready' || reasonCode === 'handoff_ready') {
1997
+ return 'runtime';
1998
+ }
1999
+ if (reasonCode === 'scope_unresolved' || reasonCode === 'ambiguous_target') {
2000
+ return 'global';
2001
+ }
2002
+ return 'stage';
2003
+ }
2004
+ function actionKindRank(kind) {
2005
+ const order = ['reject_proposal', 'refresh_upstream_stage', 'collect_required_review', 'resolve_ambiguity', 'request_manager_rework', 'ask_user_clarification', 'commit_stage_closure', 'create_handoff', 'report_status'];
2006
+ return order.indexOf(kind);
2007
+ }
2008
+ function nextActionRequiredInputs(reasonCode) {
2009
+ if (reasonCode === 'missing_required_review') {
2010
+ return ['SpecReviewResult'];
2011
+ }
2012
+ if (reasonCode === 'unresolved_ambiguity') {
2013
+ return ['ClarificationGate answer'];
2014
+ }
2015
+ if (reasonCode === 'insufficient_evidence') {
2016
+ return ['runtime evidence refs'];
2017
+ }
2018
+ return [];
2019
+ }
2020
+ function specCollaborationDiagnosticReasons(result) {
2021
+ if (result.health === 'no-op') {
2022
+ return ['Spec collaboration is not required for this lifecycle decision.'];
2023
+ }
2024
+ if (result.health === 'needs_clarification') {
2025
+ return result.clarificationGate?.questions ?? ['Spec collaboration is waiting for clarification.'];
2026
+ }
2027
+ if (result.health === 'ready_for_plan') {
2028
+ return [`Spec collaboration is ready for plan handoff ${result.handoffPacket?.handoffId ?? 'none'}.`];
2029
+ }
2030
+ if (result.health === 'blocked') {
2031
+ return [result.rejection?.explanation ?? 'Spec collaboration is blocked.'];
2032
+ }
2033
+ return [result.rejection?.explanation ?? 'Spec collaboration proposal was rejected.'];
2034
+ }
2035
+ async function registerStageCollaborationContracts(projectRoot, branch, stage, workOrder, contractRefs, registeredAt) {
2036
+ const refs = await collectStageCollaborationContractRefs(projectRoot, branch, stage, contractRefs, Boolean(workOrder));
2037
+ const records = [];
2038
+ for (const ref of refs) {
2039
+ const artifact = await readStageCollaborationContract(projectRoot, ref);
2040
+ const record = validateStageCollaborationContractFrontmatter({
2041
+ branch,
2042
+ stage,
2043
+ ref: artifact.ref,
2044
+ hash: artifact.hash,
2045
+ frontmatter: artifact.frontmatter,
2046
+ workOrder,
2047
+ registeredAt
2048
+ });
2049
+ records.push(await recordRuntimeStageCollaborationContract(projectRoot, record));
2050
+ }
2051
+ return records;
2052
+ }
2053
+ async function collectStageCollaborationContractRefs(projectRoot, branch, stage, contractRefs, required) {
2054
+ if (contractRefs && contractRefs.length > 0) {
2055
+ return [...new Set(contractRefs.map(normalizeBranchStageEvidenceRef))];
2056
+ }
2057
+ if (!required) {
2058
+ return [];
2059
+ }
2060
+ const dir = getBranchStageEvidenceDir(projectRoot, branch, stage);
2061
+ let files;
2062
+ try {
2063
+ files = await readdir(dir);
2064
+ }
2065
+ catch (error) {
2066
+ if (error.code === 'ENOENT') {
2067
+ return [];
2068
+ }
2069
+ throw error;
2070
+ }
2071
+ return files
2072
+ .filter((fileName) => isStageCollaborationContractFile(stage, fileName))
2073
+ .sort(compareStageEvidenceFileNames)
2074
+ .map((fileName) => toBranchStageEvidenceRef(branch, stage, fileName));
2075
+ }
2076
+ async function registerSpecStageArtifacts(projectRoot, branch, profile, artifactRefs, runId, registeredAt) {
2077
+ const refs = await collectSpecStageEvidenceRefs(projectRoot, branch, profile, artifactRefs);
2078
+ const records = [];
2079
+ for (const ref of refs) {
2080
+ const artifact = await readMarkdownArtifact(projectRoot, ref);
2081
+ const record = validateStageArtifactFrontmatter({
2082
+ branch,
2083
+ stage: 'spec',
2084
+ ref: artifact.ref,
2085
+ hash: artifact.hash,
2086
+ frontmatter: artifact.frontmatter,
2087
+ registeredAt
2088
+ });
2089
+ records.push(await recordRuntimeStageArtifact(projectRoot, record));
2090
+ await recordRegisteredStageArtifactPayload(projectRoot, runId, artifact, record);
2091
+ }
2092
+ return records;
2093
+ }
2094
+ async function collectSpecStageEvidenceRefs(projectRoot, branch, profile, artifactRefs) {
2095
+ if (artifactRefs && artifactRefs.length > 0) {
2096
+ return [...new Set(artifactRefs.map(normalizeBranchStageEvidenceRef))];
2097
+ }
2098
+ if (!profile.requiresStageClosure) {
2099
+ return [];
2100
+ }
2101
+ const dir = getBranchStageEvidenceDir(projectRoot, branch, 'spec');
2102
+ let files;
2103
+ try {
2104
+ files = await readdir(dir);
2105
+ }
2106
+ catch (error) {
2107
+ if (error.code === 'ENOENT') {
2108
+ return [];
2109
+ }
2110
+ throw error;
2111
+ }
2112
+ return files
2113
+ .filter(isSpecStageEvidenceFile)
2114
+ .sort(compareStageEvidenceFileNames)
2115
+ .map((fileName) => toBranchStageEvidenceRef(branch, 'spec', fileName));
2116
+ }
2117
+ async function registerBranchStageArtifacts(projectRoot, branch, stage, artifactRefs, required, runId, registeredAt) {
2118
+ const refs = await collectBranchStageEvidenceRefs(projectRoot, branch, stage, artifactRefs, required);
2119
+ const records = [];
2120
+ for (const ref of refs) {
2121
+ const artifact = await readMarkdownArtifact(projectRoot, ref);
2122
+ const record = validateStageArtifactFrontmatter({
2123
+ branch,
2124
+ stage,
2125
+ ref: artifact.ref,
2126
+ hash: artifact.hash,
2127
+ frontmatter: artifact.frontmatter,
2128
+ registeredAt
2129
+ });
2130
+ records.push(await recordRuntimeStageArtifact(projectRoot, record));
2131
+ await recordRegisteredStageArtifactPayload(projectRoot, runId, artifact, record);
2132
+ }
2133
+ return records;
2134
+ }
2135
+ async function recordRegisteredStageArtifactPayload(projectRoot, runId, artifact, record) {
2136
+ const physicalPayloadPath = `runtime.sqlite:${runId}:${artifact.ref}`;
2137
+ await recordRuntimeArtifactPayload(projectRoot, {
2138
+ payloadId: runtimeScopedId(runId, artifact.ref, artifact.hash),
2139
+ runId,
2140
+ sourceRunId: runId,
2141
+ branchSlug: record.branch,
2142
+ taskId: null,
2143
+ logicalRef: artifact.ref,
2144
+ physicalPayloadPath,
2145
+ artifactRole: record.kind,
2146
+ digest: artifact.hash,
2147
+ status: 'active',
2148
+ payload: { logicalRef: artifact.ref, physicalPayloadPath, storage: 'runtime.sqlite', source: 'branch_stage_evidence', content: artifact.content }
2149
+ });
2150
+ }
2151
+ async function collectBranchStageEvidenceRefs(projectRoot, branch, stage, artifactRefs, required) {
2152
+ if (artifactRefs && artifactRefs.length > 0) {
2153
+ return [...new Set(artifactRefs.map(normalizeBranchStageEvidenceRef).filter((ref) => !isStageCollaborationContractFile(stage, ref.split('/').pop() ?? ref)))];
2154
+ }
2155
+ if (!required) {
2156
+ return [];
2157
+ }
2158
+ const dir = getBranchStageEvidenceDir(projectRoot, branch, stage);
2159
+ let files;
2160
+ try {
2161
+ files = await readdir(dir);
2162
+ }
2163
+ catch (error) {
2164
+ if (error.code === 'ENOENT') {
2165
+ return [];
2166
+ }
2167
+ throw error;
2168
+ }
2169
+ const candidateRefs = files
2170
+ .filter((fileName) => fileName.endsWith('.md') && !isStageCollaborationContractFile(stage, fileName))
2171
+ .sort(compareStageEvidenceFileNames)
2172
+ .map((fileName) => toBranchStageEvidenceRef(branch, stage, fileName));
2173
+ const checkedRefs = await Promise.all(candidateRefs.map(async (ref) => (await isMarkdownStageArtifactCandidate(projectRoot, ref)) ? ref : null));
2174
+ return checkedRefs.filter((ref) => ref !== null);
2175
+ }
2176
+ async function isMarkdownStageArtifactCandidate(projectRoot, ref) {
2177
+ const content = await readFile(path.join(projectRoot, normalizeBranchStageEvidenceRef(ref)), 'utf8');
2178
+ return /^---\r?\n/.test(content);
2179
+ }
2180
+ function isSpecStageEvidenceFile(fileName) {
2181
+ return fileName === 'scout.md' || /^spec-review-v\d+\.md$/.test(fileName) || /^spec-manager-v\d+\.md$/.test(fileName);
2182
+ }
2183
+ function isStageCollaborationContractFile(stage, fileName) {
2184
+ return fileName === `${stage}-collaboration-contract.md` || new RegExp(`^${escapeRegex(stage)}-collaboration-contract-v\\d+\\.md$`).test(fileName);
2185
+ }
2186
+ function escapeRegex(value) {
2187
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2188
+ }
2189
+ function compareStageEvidenceFileNames(left, right) {
2190
+ return stageEvidenceVersion(left) - stageEvidenceVersion(right) || left.localeCompare(right);
2191
+ }
2192
+ function stageEvidenceVersion(fileNameOrRef) {
2193
+ const fileName = fileNameOrRef.split('/').pop() ?? fileNameOrRef;
2194
+ const match = /-v(\d+)\.md$/.exec(fileName);
2195
+ return match ? Number(match[1]) : 0;
2196
+ }
2197
+ function runtimeRefForStageArtifact(record) {
2198
+ return { kind: 'artifact', ref: record.ref, hash: record.hash };
2199
+ }
2200
+ function runtimeRefForStageCollaborationContract(record) {
2201
+ return { kind: 'artifact', ref: record.ref, hash: record.hash };
2202
+ }
2203
+ function latestValidatedStageCollaborationContract(records) {
2204
+ return records
2205
+ .filter((record) => record.status === 'validated')
2206
+ .sort((left, right) => stageEvidenceVersion(right.ref) - stageEvidenceVersion(left.ref) || right.registeredAt.localeCompare(left.registeredAt))[0] ?? null;
2207
+ }
2208
+ function deriveRegisteredSpecManagerCoordination(profile, workOrder, records, generatedAt) {
2209
+ const manager = latestStageArtifact(records, 'manager_closure_request');
2210
+ if (!manager || !workOrder) {
2211
+ return null;
2212
+ }
2213
+ return {
2214
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
2215
+ coordinationId: stableId('spec-manager-coordination', profile.scope, manager.ref, manager.hash, generatedAt),
2216
+ stage: 'spec',
2217
+ scope: profile.scope,
2218
+ stageManager: SPEC_STAGE_MANAGER,
2219
+ workOrderRef: { kind: 'projection', ref: `spec-work-order:${workOrder.workOrderId}` },
2220
+ agentTeamRefs: records.filter((record) => record.ref !== manager.ref).map(runtimeRefForStageArtifact),
2221
+ recommendation: manager.recommendation ?? 'blocked',
2222
+ generatedAt
2223
+ };
2224
+ }
2225
+ function deriveRegisteredStageClosureRequest(profile, workOrder, records, branchPartition, generatedAt) {
2226
+ const manager = latestStageArtifact(records, 'manager_closure_request');
2227
+ if (!manager) {
2228
+ return null;
2229
+ }
2230
+ const review = findReviewForManager(records, manager);
2231
+ const expectedSpecRef = `specs/${branchPartition}/spec.md`;
2232
+ const candidateRef = manager.targetRef ?? review?.targetRef ?? { kind: 'document', ref: expectedSpecRef };
2233
+ const evidenceRefs = records.map(runtimeRefForStageArtifact);
2234
+ const blockingItem = manager.recommendation === 'request_clarification' || review?.verdict === 'blocked'
2235
+ ? [{ id: 'blocking-ambiguity', kind: 'blocking_ambiguity', summary: 'Spec closure needs clarification before plan handoff.', refs: evidenceRefs }]
2236
+ : [];
2237
+ const handoffItem = manager.recommendation === 'close_stage'
2238
+ ? [{ id: 'accepted-spec', kind: 'handoff_proposal', summary: 'Spec manager requested plan handoff for the reviewed canonical spec.', refs: [candidateRef, ...evidenceRefs] }]
2239
+ : [];
2240
+ const acceptedItems = [...blockingItem, ...handoffItem];
2241
+ const candidate = {
2242
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
2243
+ candidateId: stableId('spec-document-candidate', profile.scope, manager.ref, manager.hash, generatedAt),
2244
+ stage: 'spec',
2245
+ scope: profile.scope,
2246
+ producedBy: SPEC_STAGE_DRAFTER_AGENT,
2247
+ inputRefs: profile.inputRefs.length > 0 ? profile.inputRefs : evidenceRefs,
2248
+ evidenceRefs,
2249
+ acceptedItems,
2250
+ generatedAt
2251
+ };
2252
+ return {
2253
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
2254
+ closureRequestId: stableId('spec-closure-request', profile.scope, manager.ref, manager.hash, generatedAt),
2255
+ stage: 'spec',
2256
+ scope: profile.scope,
2257
+ submittedBy: SPEC_STAGE_MANAGER,
2258
+ workOrderRef: workOrder ? { kind: 'projection', ref: `spec-work-order:${workOrder.workOrderId}` } : { kind: 'projection', ref: `spec-work-order:${specCollaborationScopeKey(profile.scope)}` },
2259
+ coordinationRef: runtimeRefForStageArtifact(manager),
2260
+ candidateRef,
2261
+ reviewRefs: review ? [runtimeRefForStageArtifact(review)] : [],
2262
+ capabilityFindingRefs: [],
2263
+ evidenceRefs,
2264
+ candidate,
2265
+ reviewResults: review ? [registeredReviewResult(profile, review, generatedAt)] : [],
2266
+ capabilityFindings: [],
2267
+ unresolvedAmbiguityIds: blockingItem.map((item) => item.id),
2268
+ authorityAttempts: [],
2269
+ managerRecommendation: manager.recommendation ?? 'blocked',
2270
+ generatedAt
2271
+ };
2272
+ }
2273
+ function registeredReviewResult(profile, review, generatedAt) {
2274
+ return {
2275
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
2276
+ reviewId: review.artifactId,
2277
+ stage: 'spec',
2278
+ scope: profile.scope,
2279
+ reviewer: SPEC_STAGE_REVIEW_AGENT,
2280
+ candidateRef: review.targetRef,
2281
+ verdict: review.verdict ?? 'blocked',
2282
+ findings: [`findingCount=${review.findingCount ?? 0}`, `blockingCount=${review.blockingCount ?? 0}`],
2283
+ evidenceRefs: [runtimeRefForStageArtifact(review)],
2284
+ generatedAt
2285
+ };
2286
+ }
2287
+ function findReviewForManager(records, manager) {
2288
+ if (manager.reviewRef) {
2289
+ return records.find((record) => record.kind === 'spec_review' && record.ref === manager.reviewRef?.ref && record.hash === manager.reviewHash) ?? null;
2290
+ }
2291
+ return latestStageArtifact(records, 'spec_review');
2292
+ }
2293
+ function latestStageArtifact(records, kind) {
2294
+ return records
2295
+ .filter((record) => record.kind === kind)
2296
+ .sort((left, right) => stageEvidenceVersion(right.ref) - stageEvidenceVersion(left.ref) || right.registeredAt.localeCompare(left.registeredAt))[0] ?? null;
2297
+ }
2298
+ async function validateAcceptedSpecArtifact(projectRoot, input) {
2299
+ if (input.adjudication.health !== 'ready_for_plan') {
2300
+ return {
2301
+ acceptedSpecRef: null,
2302
+ specAcceptanceStatus: specArtifactStatusForHealth(input.adjudication.health),
2303
+ specHash: null,
2304
+ specContractHash: null,
2305
+ reasons: [`Spec artifact was not accepted because adjudication health is ${input.adjudication.health}.`]
2306
+ };
2307
+ }
2308
+ if (!input.validatedCollaborationContract) {
2309
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready spec closure must be backed by a validated spec StageCollaborationContract.', 'Ask spec-manager to write .sdd/runs/<branch>/spec/spec-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2310
+ }
2311
+ const expectedRef = `specs/${input.partition}/spec.md`;
2312
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2313
+ if (!manager) {
2314
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready spec closure must be backed by a registered spec-manager closure artifact.', 'Ask spec-manager to write .sdd/runs/<branch>/spec/spec-manager-vN.md and register it before closure.');
2315
+ }
2316
+ if (manager.recommendation !== 'close_stage') {
2317
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Spec-manager recommendation ${manager.recommendation ?? 'none'} cannot accept spec for plan handoff.`, 'Ask spec-manager to submit a close_stage recommendation after review blockers are resolved.');
2318
+ }
2319
+ const reviewByRef = manager.reviewRef
2320
+ ? input.registeredArtifacts.find((record) => record.kind === 'spec_review' && record.ref === manager.reviewRef?.ref) ?? null
2321
+ : null;
2322
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2323
+ return rejectedSpecArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Spec-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh spec-manager closure artifact so reviewRef/reviewHash point at the registered review artifact.');
2324
+ }
2325
+ const approvedReview = findReviewForManager(input.registeredArtifacts, manager);
2326
+ if (!approvedReview) {
2327
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'missing_required_review', 'Ready spec closure must include a registered spec-reviewer artifact referenced by spec-manager.', 'Ask spec-reviewer to write .sdd/runs/<branch>/spec/spec-review-vN.md and ask spec-manager to reference that review hash.');
2328
+ }
2329
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2330
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'missing_required_review', `Spec-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept spec.`, 'Resolve review blockers and register an approved spec-reviewer artifact before closure.');
2331
+ }
2332
+ if (manager.targetRef?.kind !== 'document' || manager.targetRef.ref !== expectedRef || approvedReview.targetRef?.kind !== 'document' || approvedReview.targetRef.ref !== expectedRef) {
2333
+ return rejectedSpecArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready spec closure must reference reviewed ${expectedRef} as the canonical spec document.`, 'Refresh review and manager artifacts so targetRef points at specs/<branch>/spec.md.');
2334
+ }
2335
+ const specPath = path.join(projectRoot, 'specs', input.partition, 'spec.md');
2336
+ const content = await readOptionalText(specPath);
2337
+ if (content === null) {
2338
+ return rejectedSpecArtifact('not_accepted_missing_spec', 'missing_refs', `Reviewed canonical spec artifact is missing at ${expectedRef}.`, 'Produce and review specs/<branch>/spec.md before requesting runtime closure.');
2339
+ }
2340
+ if (isManagedStarterSpec(content)) {
2341
+ return rejectedSpecArtifact('not_accepted_starter_spec', 'invalid_proposal', `Canonical spec artifact at ${expectedRef} is still the managed starter spec.`, 'Replace starter spec.md with reviewed semantic spec content before closure.');
2342
+ }
2343
+ const specHash = hashDocumentContent(content);
2344
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2345
+ const mismatchedHash = expectedHashes.find((hash) => hash !== specHash);
2346
+ if (mismatchedHash) {
2347
+ return rejectedSpecArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed spec hash mismatch for ${expectedRef}: expected ${mismatchedHash}, actual ${specHash}.`, 'Refresh spec-reviewer and spec-manager artifacts with the current spec.md hash before closure.');
2348
+ }
2349
+ return {
2350
+ acceptedSpecRef: { kind: 'document', ref: expectedRef, hash: specHash },
2351
+ specAcceptanceStatus: 'accepted',
2352
+ specHash,
2353
+ specContractHash: hashSemanticDocument(content),
2354
+ reasons: [`Runtime accepted reviewed canonical spec artifact ${expectedRef}.`]
2355
+ };
2356
+ }
2357
+ function rejectedSpecArtifact(specAcceptanceStatus, reasonCode, explanation, requiredNextAction) {
2358
+ return {
2359
+ acceptedSpecRef: null,
2360
+ specAcceptanceStatus,
2361
+ specHash: null,
2362
+ specContractHash: null,
2363
+ reasons: [explanation],
2364
+ rejectionIssue: {
2365
+ reasonCode,
2366
+ explanation,
2367
+ requiredNextAction,
2368
+ fallbackRoute: 'revise-proposal'
2369
+ }
2370
+ };
2371
+ }
2372
+ async function validateAcceptedPlanArtifact(projectRoot, input) {
2373
+ if (!input.required) {
2374
+ return {
2375
+ acceptedPlanRef: null,
2376
+ planAcceptanceStatus: 'not_required_noop',
2377
+ planHash: null,
2378
+ planContractHash: null,
2379
+ reasons: ['Plan artifact was not required by this lifecycle decision.']
2380
+ };
2381
+ }
2382
+ if (input.blocked) {
2383
+ return rejectedPlanArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks plan closure.', 'Resolve lifecycle blockers before requesting plan-stage closure.', 'runtime-blocked');
2384
+ }
2385
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2386
+ return rejectedPlanArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready plan closure must be backed by a validated plan StageCollaborationContract.', 'Ask plan-manager to write .sdd/runs/<branch>/plan/plan-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2387
+ }
2388
+ const upstreamIssue = await validateAcceptedSpecHandoff(projectRoot, input.partition, input.acceptedSpecHandoff);
2389
+ if (upstreamIssue) {
2390
+ return rejectedPlanArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh spec-stage closure and provide a fresh spec -> plan handoff before plan closure.');
2391
+ }
2392
+ const expectedRef = `specs/${input.partition}/plan.md`;
2393
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2394
+ if (!manager) {
2395
+ return rejectedPlanArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready plan closure must be backed by a registered plan-manager closure artifact.', 'Ask plan-manager to write .sdd/runs/<branch>/plan/plan-manager-vN.md and register it before closure.');
2396
+ }
2397
+ if (manager.recommendation !== 'close_stage') {
2398
+ return rejectedPlanArtifact('not_accepted_rejected', 'invalid_proposal', `Plan-manager recommendation ${manager.recommendation ?? 'none'} cannot accept plan for tasks handoff.`, 'Ask plan-manager to submit a close_stage recommendation after review blockers are resolved.');
2399
+ }
2400
+ const reviewByRef = manager.reviewRef
2401
+ ? input.registeredArtifacts.find((record) => record.kind === 'plan_review' && record.ref === manager.reviewRef?.ref) ?? null
2402
+ : null;
2403
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2404
+ return rejectedPlanArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Plan-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh plan-manager closure artifact so reviewRef/reviewHash point at the registered review artifact.');
2405
+ }
2406
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'plan_review');
2407
+ if (!approvedReview) {
2408
+ return rejectedPlanArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready plan closure must include a registered plan-reviewer artifact referenced by plan-manager.', 'Ask plan-reviewer to write .sdd/runs/<branch>/plan/plan-review-vN.md and ask plan-manager to reference that review hash.');
2409
+ }
2410
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2411
+ return rejectedPlanArtifact('not_accepted_missing_review', 'missing_required_review', `Plan-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept plan.`, 'Resolve review blockers and register an approved plan-reviewer artifact before closure.');
2412
+ }
2413
+ if (manager.targetRef?.kind !== 'document' || manager.targetRef.ref !== expectedRef || approvedReview.targetRef?.kind !== 'document' || approvedReview.targetRef.ref !== expectedRef) {
2414
+ return rejectedPlanArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready plan closure must reference reviewed ${expectedRef} as the canonical plan document.`, 'Refresh review and manager artifacts so targetRef points at specs/<branch>/plan.md.');
2415
+ }
2416
+ const planPath = path.join(projectRoot, 'specs', input.partition, 'plan.md');
2417
+ const content = await readOptionalText(planPath);
2418
+ if (content === null) {
2419
+ return rejectedPlanArtifact('not_accepted_missing_plan', 'missing_refs', `Reviewed canonical plan artifact is missing at ${expectedRef}.`, 'Produce and review specs/<branch>/plan.md before requesting runtime closure.');
2420
+ }
2421
+ if (isManagedStarterSpec(content)) {
2422
+ return rejectedPlanArtifact('not_accepted_starter_plan', 'invalid_proposal', `Canonical plan artifact at ${expectedRef} is still the managed starter document.`, 'Replace starter plan.md with reviewed semantic plan content before closure.');
2423
+ }
2424
+ const planHash = hashDocumentContent(content);
2425
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2426
+ const mismatchedHash = expectedHashes.find((hash) => hash !== planHash);
2427
+ if (mismatchedHash) {
2428
+ return rejectedPlanArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed plan hash mismatch for ${expectedRef}: expected ${mismatchedHash}, actual ${planHash}.`, 'Refresh plan-reviewer and plan-manager artifacts with the current plan.md hash before closure.');
2429
+ }
2430
+ return {
2431
+ acceptedPlanRef: { kind: 'document', ref: expectedRef, hash: planHash },
2432
+ planAcceptanceStatus: 'accepted',
2433
+ planHash,
2434
+ planContractHash: hashSemanticDocument(content),
2435
+ reasons: [`Runtime accepted reviewed canonical plan artifact ${expectedRef}.`]
2436
+ };
2437
+ }
2438
+ async function validateAcceptedTasksArtifact(projectRoot, input) {
2439
+ if (!input.required) {
2440
+ return {
2441
+ acceptedTasksRef: null,
2442
+ tasksAcceptanceStatus: 'not_required_noop',
2443
+ tasksHash: null,
2444
+ tasksContractHash: null,
2445
+ reasons: ['Tasks artifact was not required by this lifecycle decision.']
2446
+ };
2447
+ }
2448
+ if (input.blocked) {
2449
+ return rejectedTasksArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks tasks closure.', 'Resolve lifecycle blockers before requesting tasks-stage closure.', 'runtime-blocked');
2450
+ }
2451
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2452
+ return rejectedTasksArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready tasks closure must be backed by a validated tasks StageCollaborationContract.', 'Ask tasks-manager to write .sdd/runs/<branch>/tasks/tasks-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2453
+ }
2454
+ const upstreamIssue = await validateAcceptedPlanHandoff(projectRoot, input.partition, input.acceptedPlanHandoff);
2455
+ if (upstreamIssue) {
2456
+ return rejectedTasksArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh plan-stage closure and provide a fresh plan -> tasks handoff before tasks closure.');
2457
+ }
2458
+ const expectedRef = `specs/${input.partition}/tasks.md`;
2459
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2460
+ if (!manager) {
2461
+ return rejectedTasksArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready tasks closure must be backed by a registered tasks-manager closure artifact.', 'Ask tasks-manager to write .sdd/runs/<branch>/tasks/tasks-manager-vN.md and register it before closure.');
2462
+ }
2463
+ if (manager.recommendation !== 'close_stage') {
2464
+ return rejectedTasksArtifact('not_accepted_rejected', 'invalid_proposal', `Tasks-manager recommendation ${manager.recommendation ?? 'none'} cannot accept tasks for verifies handoff.`, 'Ask tasks-manager to submit a close_stage recommendation after review blockers are resolved.');
2465
+ }
2466
+ const reviewByRef = manager.reviewRef
2467
+ ? input.registeredArtifacts.find((record) => record.kind === 'tasks_review' && record.ref === manager.reviewRef?.ref) ?? null
2468
+ : null;
2469
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2470
+ return rejectedTasksArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Tasks-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh tasks-manager closure artifact so reviewRef/reviewHash point at the registered review artifact.');
2471
+ }
2472
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'tasks_review');
2473
+ if (!approvedReview) {
2474
+ return rejectedTasksArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready tasks closure must include a registered tasks-reviewer artifact referenced by tasks-manager.', 'Ask tasks-reviewer to write .sdd/runs/<branch>/tasks/tasks-review-vN.md and ask tasks-manager to reference that review hash.');
2475
+ }
2476
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2477
+ return rejectedTasksArtifact('not_accepted_missing_review', 'missing_required_review', `Tasks-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept tasks.`, 'Resolve review blockers and register an approved tasks-reviewer artifact before closure.');
2478
+ }
2479
+ if (manager.targetRef?.kind !== 'document' || manager.targetRef.ref !== expectedRef || approvedReview.targetRef?.kind !== 'document' || approvedReview.targetRef.ref !== expectedRef) {
2480
+ return rejectedTasksArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready tasks closure must reference reviewed ${expectedRef} as the canonical tasks document.`, 'Refresh review and manager artifacts so targetRef points at specs/<branch>/tasks.md.');
2481
+ }
2482
+ const tasksPath = path.join(projectRoot, 'specs', input.partition, 'tasks.md');
2483
+ const content = await readOptionalText(tasksPath);
2484
+ if (content === null) {
2485
+ return rejectedTasksArtifact('not_accepted_missing_tasks', 'missing_refs', `Reviewed canonical tasks artifact is missing at ${expectedRef}.`, 'Produce and review specs/<branch>/tasks.md before requesting runtime closure.');
2486
+ }
2487
+ if (isManagedStarterSpec(content)) {
2488
+ return rejectedTasksArtifact('not_accepted_starter_tasks', 'invalid_proposal', `Canonical tasks artifact at ${expectedRef} is still the managed starter document.`, 'Replace starter tasks.md with reviewed executable task contracts before closure.');
2489
+ }
2490
+ const taskModel = parseSddTasksMarkdown(content, { tasksPath: expectedRef });
2491
+ const duplicateIdGap = taskModel.gaps.find((gap) => gap.field === 'id' && /Duplicate task id/i.test(gap.message));
2492
+ if (duplicateIdGap) {
2493
+ return rejectedTasksArtifact('not_accepted_duplicate_task_ids', 'invalid_proposal', duplicateIdGap.message, duplicateIdGap.recommendation);
2494
+ }
2495
+ const blockingTaskGap = taskModel.tasks.length === 0
2496
+ ? { message: `Reviewed tasks artifact ${expectedRef} contains no executable sdd-task blocks.`, recommendation: 'Add at least one reviewed sdd-task block before closure.' }
2497
+ : taskModel.gaps.find((gap) => gap.severity === 'blocking') ?? null;
2498
+ if (blockingTaskGap) {
2499
+ return rejectedTasksArtifact('not_accepted_invalid_task_ids', 'invalid_proposal', blockingTaskGap.message, blockingTaskGap.recommendation);
2500
+ }
2501
+ const tasksHash = hashDocumentContent(content);
2502
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2503
+ const mismatchedHash = expectedHashes.find((hash) => hash !== tasksHash);
2504
+ if (mismatchedHash) {
2505
+ return rejectedTasksArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed tasks hash mismatch for ${expectedRef}: expected ${mismatchedHash}, actual ${tasksHash}.`, 'Refresh tasks-reviewer and tasks-manager artifacts with the current tasks.md hash before closure.');
2506
+ }
2507
+ const tasksContractHash = hashTasksContract(content);
2508
+ if (!tasksContractHash) {
2509
+ return rejectedTasksArtifact('not_accepted_invalid_task_ids', 'invalid_proposal', `Reviewed tasks artifact ${expectedRef} does not produce a task contract hash.`, 'Refresh tasks.md with reviewed executable task contracts before closure.');
2510
+ }
2511
+ return {
2512
+ acceptedTasksRef: { kind: 'document', ref: expectedRef, hash: tasksHash },
2513
+ tasksAcceptanceStatus: 'accepted',
2514
+ tasksHash,
2515
+ tasksContractHash,
2516
+ reasons: [`Runtime accepted reviewed canonical tasks artifact ${expectedRef}.`]
2517
+ };
2518
+ }
2519
+ async function validateAcceptedVerifyArtifact(projectRoot, input) {
2520
+ if (!input.required) {
2521
+ return {
2522
+ acceptedVerifyRef: null,
2523
+ verifyAcceptanceStatus: 'not_required_noop',
2524
+ verifyHash: null,
2525
+ verifyContractHash: null,
2526
+ reasons: ['Verify artifact was not required by this lifecycle decision.']
2527
+ };
2528
+ }
2529
+ if (input.blocked) {
2530
+ return rejectedVerifyArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks verifies closure.', 'Resolve lifecycle blockers before requesting verifies-stage closure.', 'runtime-blocked');
2531
+ }
2532
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2533
+ return rejectedVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready verifies closure must be backed by a validated verifies StageCollaborationContract.', 'Ask verifies-manager to write .sdd/runs/<branch>/verifies/verifies-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2534
+ }
2535
+ const upstreamIssue = await validateAcceptedTasksHandoff(projectRoot, input.partition, input.acceptedTasksHandoff);
2536
+ if (upstreamIssue) {
2537
+ return rejectedVerifyArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh tasks-stage closure and provide a fresh tasks -> verifies handoff before verifies closure.');
2538
+ }
2539
+ const expectedRef = `specs/${input.partition}/verify.md`;
2540
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2541
+ if (!manager) {
2542
+ return rejectedVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready verifies closure must be backed by a registered verifies-manager closure artifact.', 'Ask verifies-manager to write .sdd/runs/<branch>/verifies/verifies-manager-vN.md and register it before closure.');
2543
+ }
2544
+ if (manager.recommendation !== 'close_stage') {
2545
+ return rejectedVerifyArtifact('not_accepted_rejected', 'invalid_proposal', `Verifies-manager recommendation ${manager.recommendation ?? 'none'} cannot accept verify contract for do handoff.`, 'Ask verifies-manager to submit a close_stage recommendation after review blockers are resolved.');
2546
+ }
2547
+ const reviewByRef = manager.reviewRef
2548
+ ? input.registeredArtifacts.find((record) => record.kind === 'verifies_review' && record.ref === manager.reviewRef?.ref) ?? null
2549
+ : null;
2550
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2551
+ return rejectedVerifyArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Verifies-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh verifies-manager closure artifact so reviewRef/reviewHash point at the registered review artifact.');
2552
+ }
2553
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'verifies_review');
2554
+ if (!approvedReview) {
2555
+ return rejectedVerifyArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready verifies closure must include a registered verifies-reviewer artifact referenced by verifies-manager.', 'Ask verifies-reviewer to write .sdd/runs/<branch>/verifies/verifies-review-vN.md and ask verifies-manager to reference that review hash.');
2556
+ }
2557
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2558
+ return rejectedVerifyArtifact('not_accepted_missing_review', 'missing_required_review', `Verifies-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept verify contract.`, 'Resolve review blockers and register an approved verifies-reviewer artifact before closure.');
2559
+ }
2560
+ if (manager.targetRef?.kind !== 'document' || manager.targetRef.ref !== expectedRef || approvedReview.targetRef?.kind !== 'document' || approvedReview.targetRef.ref !== expectedRef) {
2561
+ return rejectedVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready verifies closure must reference reviewed ${expectedRef} as the canonical verify contract.`, 'Refresh review and manager artifacts so targetRef points at specs/<branch>/verify.md.');
2562
+ }
2563
+ const verifyPath = path.join(projectRoot, 'specs', input.partition, 'verify.md');
2564
+ const content = await readOptionalText(verifyPath);
2565
+ if (content === null) {
2566
+ return rejectedVerifyArtifact('not_accepted_missing_verify', 'missing_refs', `Reviewed canonical verify artifact is missing at ${expectedRef}.`, 'Produce and review specs/<branch>/verify.md before requesting runtime closure.');
2567
+ }
2568
+ if (isManagedStarterSpec(content)) {
2569
+ return rejectedVerifyArtifact('not_accepted_starter_verify', 'invalid_proposal', `Canonical verify artifact at ${expectedRef} is still the managed starter document.`, 'Replace starter verify.md with a reviewed verification contract before closure.');
2570
+ }
2571
+ const verifyHash = hashDocumentContent(content);
2572
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2573
+ const mismatchedHash = expectedHashes.find((hash) => hash !== verifyHash);
2574
+ if (mismatchedHash) {
2575
+ return rejectedVerifyArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed verify hash mismatch for ${expectedRef}: expected ${mismatchedHash}, actual ${verifyHash}.`, 'Refresh verifies-reviewer and verifies-manager artifacts with the current verify.md hash before closure.');
2576
+ }
2577
+ const inspection = await inspectVerifyContract(projectRoot, { branch: input.partition, branchSource: 'cli_option' });
2578
+ const blockingIssue = inspection.issues.find((issue) => issue.level === 'FAIL')
2579
+ ?? inspection.issues.find((issue) => issue.field === 'tasks');
2580
+ if (blockingIssue) {
2581
+ return rejectedVerifyArtifact('not_accepted_incomplete_coverage', 'invalid_proposal', blockingIssue.message, blockingIssue.action);
2582
+ }
2583
+ return {
2584
+ acceptedVerifyRef: { kind: 'document', ref: expectedRef, hash: verifyHash },
2585
+ verifyAcceptanceStatus: 'accepted',
2586
+ verifyHash,
2587
+ verifyContractHash: hashSemanticDocument(content),
2588
+ reasons: [`Runtime accepted reviewed canonical verify artifact ${expectedRef}.`]
2589
+ };
2590
+ }
2591
+ async function validateAcceptedDoArtifact(projectRoot, input) {
2592
+ if (!input.required) {
2593
+ return {
2594
+ acceptedImplementationRef: null,
2595
+ implementationAcceptanceStatus: 'not_required_noop',
2596
+ implementationHash: null,
2597
+ changedFileRefs: [],
2598
+ reasons: ['Implementation artifact was not required by this lifecycle decision.']
2599
+ };
2600
+ }
2601
+ if (input.blocked) {
2602
+ return rejectedDoArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks do closure.', 'Resolve lifecycle blockers before requesting do-stage closure.', 'runtime-blocked');
2603
+ }
2604
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2605
+ return rejectedDoArtifact('not_accepted_wrong_ref', 'missing_refs', 'Ready do closure must be backed by a validated do StageCollaborationContract.', 'Ask do-manager to write .sdd/runs/<branch>/do/do-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2606
+ }
2607
+ const upstreamIssue = await validateAcceptedVerifyHandoff(projectRoot, input.partition, input.acceptedVerifyHandoff);
2608
+ if (upstreamIssue) {
2609
+ return rejectedDoArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh verifies-stage closure and provide a fresh verifies -> do handoff before do closure.');
2610
+ }
2611
+ const implementation = latestStageArtifact(input.registeredArtifacts, 'implementation_evidence');
2612
+ if (!implementation) {
2613
+ return rejectedDoArtifact('not_accepted_missing_implementation', 'missing_refs', 'Ready do closure must be backed by a registered implementer evidence artifact.', 'Ask implementer to write .sdd/runs/<branch>/do/implementation-vN.md and register it before closure.');
2614
+ }
2615
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2616
+ if (!manager) {
2617
+ return rejectedDoArtifact('not_accepted_wrong_ref', 'missing_refs', 'Ready do closure must be backed by a registered do-manager closure artifact.', 'Ask do-manager to write .sdd/runs/<branch>/do/do-manager-vN.md and register it before closure.');
2618
+ }
2619
+ if (manager.recommendation !== 'close_stage') {
2620
+ return rejectedDoArtifact('not_accepted_rejected', 'invalid_proposal', `Do-manager recommendation ${manager.recommendation ?? 'none'} cannot accept implementation state for test handoff.`, 'Ask do-manager to submit a close_stage recommendation after review blockers are resolved.');
2621
+ }
2622
+ const reviewByRef = manager.reviewRef
2623
+ ? input.registeredArtifacts.find((record) => record.kind === 'code_review' && record.ref === manager.reviewRef?.ref) ?? null
2624
+ : null;
2625
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2626
+ return rejectedDoArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Do-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh do-manager closure artifact so reviewRef/reviewHash point at the registered code review artifact.');
2627
+ }
2628
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'code_review');
2629
+ if (!approvedReview) {
2630
+ return rejectedDoArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready do closure must include a registered code-reviewer artifact referenced by do-manager.', 'Ask code-reviewer to write .sdd/runs/<branch>/do/code-review-vN.md and ask do-manager to reference that review hash.');
2631
+ }
2632
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2633
+ return rejectedDoArtifact('not_accepted_missing_review', 'missing_required_review', `Code-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept implementation state.`, 'Resolve review blockers and register an approved code-reviewer artifact before closure.');
2634
+ }
2635
+ if (manager.targetRef?.kind !== 'artifact' || manager.targetRef.ref !== implementation.ref || approvedReview.targetRef?.kind !== 'artifact' || approvedReview.targetRef.ref !== implementation.ref) {
2636
+ return rejectedDoArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready do closure must reference reviewed ${implementation.ref} as the implementation evidence artifact.`, 'Refresh code-reviewer and do-manager artifacts so targetRef points at the implementation evidence artifact.');
2637
+ }
2638
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2639
+ const mismatchedHash = expectedHashes.find((hash) => hash !== implementation.hash);
2640
+ if (mismatchedHash) {
2641
+ return rejectedDoArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed implementation evidence hash mismatch for ${implementation.ref}: expected ${mismatchedHash}, actual ${implementation.hash}.`, 'Refresh code-reviewer and do-manager artifacts with the current implementation evidence hash before closure.');
2642
+ }
2643
+ const changedFileParse = parseImplementationChangedFileRefs(implementation);
2644
+ if (changedFileParse.issue) {
2645
+ return rejectedDoArtifact('not_accepted_boundary_violation', 'invalid_proposal', changedFileParse.issue, 'Refresh implementation evidence with changedFiles entries that include safe refs and current hashes.');
2646
+ }
2647
+ const changedFileRefs = changedFileParse.refs;
2648
+ const boundaryIssue = validateAllowedChangedFileRefs(changedFileRefs, input.allowedChangedFileRefs);
2649
+ if (boundaryIssue) {
2650
+ return rejectedDoArtifact('not_accepted_boundary_violation', 'invalid_proposal', boundaryIssue, 'Restrict do-stage implementation evidence to the task boundary or update the allowed changed-file refs.');
2651
+ }
2652
+ const changedFileHashIssue = await validateChangedFileHashes(projectRoot, changedFileRefs);
2653
+ if (changedFileHashIssue) {
2654
+ return rejectedDoArtifact('not_accepted_hash_mismatch', 'invalid_proposal', changedFileHashIssue, 'Refresh implementation, code-reviewer, and do-manager artifacts with current changed-file hashes before closure.');
2655
+ }
2656
+ return {
2657
+ acceptedImplementationRef: { kind: 'artifact', ref: implementation.ref, hash: implementation.hash },
2658
+ implementationAcceptanceStatus: 'accepted',
2659
+ implementationHash: implementation.hash,
2660
+ changedFileRefs,
2661
+ reasons: [`Runtime accepted reviewed implementation evidence ${implementation.ref}.`]
2662
+ };
2663
+ }
2664
+ async function validateAcceptedTestArtifact(projectRoot, input) {
2665
+ if (!input.required) {
2666
+ return {
2667
+ acceptedTestEvidenceRef: null,
2668
+ testAcceptanceStatus: 'not_required_noop',
2669
+ testEvidenceHash: null,
2670
+ reasons: ['Test evidence was not required by this lifecycle decision.']
2671
+ };
2672
+ }
2673
+ if (input.blocked) {
2674
+ return rejectedTestArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks test closure.', 'Resolve lifecycle blockers before requesting test-stage closure.', 'runtime-blocked');
2675
+ }
2676
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2677
+ return rejectedTestArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready test closure must be backed by a validated test StageCollaborationContract.', 'Ask test-manager to write .sdd/runs/<branch>/test/test-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2678
+ }
2679
+ const upstreamIssue = await validateAcceptedDoHandoff(projectRoot, input.partition, input.acceptedDoHandoff);
2680
+ if (upstreamIssue) {
2681
+ return rejectedTestArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh do-stage closure and provide a fresh do -> test handoff before test closure.');
2682
+ }
2683
+ const execution = latestStageArtifact(input.registeredArtifacts, 'test_execution');
2684
+ if (!execution) {
2685
+ return rejectedTestArtifact('not_accepted_missing_execution', 'missing_refs', 'Ready test closure must be backed by a registered test-runner execution artifact.', 'Ask test-runner to write .sdd/runs/<branch>/test/test-execution-vN.md and register it before closure.');
2686
+ }
2687
+ const validation = latestStageArtifact(input.registeredArtifacts, 'validation_evidence');
2688
+ if (!validation) {
2689
+ return rejectedTestArtifact('not_accepted_missing_validation', 'missing_refs', 'Ready test closure must be backed by a registered validator evidence artifact.', 'Ask validator to write .sdd/runs/<branch>/test/validation-vN.md and register it before closure.');
2690
+ }
2691
+ const executionStatus = execution.frontmatter.status;
2692
+ const executionExitCode = execution.frontmatter.exitCode;
2693
+ if (executionStatus !== 'passed' || executionExitCode !== 0) {
2694
+ return rejectedTestArtifact('not_accepted_failed_tests', 'invalid_proposal', `Test execution ${execution.ref} status ${String(executionStatus)} with exitCode ${String(executionExitCode)} cannot close test stage.`, 'Rerun failing test commands and register passing test execution evidence before closure.');
2695
+ }
2696
+ const validationExecutionRef = executionRefFromValidation(validation);
2697
+ if (validationExecutionRef !== execution.ref) {
2698
+ return rejectedTestArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Validation evidence ${validation.ref} must reference execution evidence ${execution.ref}.`, 'Refresh validation evidence so executionRef points at the registered test execution artifact.');
2699
+ }
2700
+ const validationExecutionHash = validation.frontmatter.executionHash;
2701
+ if (validationExecutionHash !== execution.hash) {
2702
+ return rejectedTestArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Validation evidence execution hash mismatch for ${execution.ref}: expected ${String(validationExecutionHash)}, actual ${execution.hash}.`, 'Refresh validation evidence with the current test execution hash before closure.');
2703
+ }
2704
+ if (validation.frontmatter.status !== 'passed') {
2705
+ return rejectedTestArtifact('not_accepted_failed_tests', 'invalid_proposal', `Validation evidence ${validation.ref} status ${String(validation.frontmatter.status)} cannot close test stage.`, 'Resolve validation failures and register passing validation evidence before closure.');
2706
+ }
2707
+ if (validation.frontmatter.acceptanceMapped !== true) {
2708
+ return rejectedTestArtifact('not_accepted_unmapped_acceptance', 'invalid_proposal', `Validation evidence ${validation.ref} must declare acceptanceMapped: true.`, 'Map validation evidence to accepted task/verify criteria before requesting test closure.');
2709
+ }
2710
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2711
+ if (!manager) {
2712
+ return rejectedTestArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready test closure must be backed by a registered test-manager closure artifact.', 'Ask test-manager to write .sdd/runs/<branch>/test/test-manager-vN.md and register it before closure.');
2713
+ }
2714
+ if (manager.recommendation !== 'close_stage') {
2715
+ return rejectedTestArtifact('not_accepted_rejected', 'invalid_proposal', `Test-manager recommendation ${manager.recommendation ?? 'none'} cannot accept test evidence for goal-verify handoff.`, 'Ask test-manager to submit a close_stage recommendation after test blockers are resolved.');
2716
+ }
2717
+ const reviewByRef = manager.reviewRef
2718
+ ? input.registeredArtifacts.find((record) => record.kind === 'test_review' && record.ref === manager.reviewRef?.ref) ?? null
2719
+ : null;
2720
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2721
+ return rejectedTestArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Test-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh test-manager closure artifact so reviewRef/reviewHash point at the registered test review artifact.');
2722
+ }
2723
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'test_review');
2724
+ if (!approvedReview) {
2725
+ return rejectedTestArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready test closure must include a registered test-reviewer artifact referenced by test-manager.', 'Ask test-reviewer to write .sdd/runs/<branch>/test/test-review-vN.md and ask test-manager to reference that review hash.');
2726
+ }
2727
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2728
+ return rejectedTestArtifact('not_accepted_missing_review', 'missing_required_review', `Test-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept test evidence.`, 'Resolve review blockers and register an approved test-reviewer artifact before closure.');
2729
+ }
2730
+ if (manager.targetRef?.kind !== 'artifact' || manager.targetRef.ref !== validation.ref || approvedReview.targetRef?.kind !== 'artifact' || approvedReview.targetRef.ref !== validation.ref) {
2731
+ return rejectedTestArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready test closure must reference reviewed ${validation.ref} as the validation evidence artifact.`, 'Refresh test-reviewer and test-manager artifacts so targetRef points at the validation evidence artifact.');
2732
+ }
2733
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2734
+ const mismatchedHash = expectedHashes.find((hash) => hash !== validation.hash);
2735
+ if (mismatchedHash) {
2736
+ return rejectedTestArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed validation evidence hash mismatch for ${validation.ref}: expected ${mismatchedHash}, actual ${validation.hash}.`, 'Refresh test-reviewer and test-manager artifacts with the current validation evidence hash before closure.');
2737
+ }
2738
+ return {
2739
+ acceptedTestEvidenceRef: { kind: 'artifact', ref: validation.ref, hash: validation.hash },
2740
+ testAcceptanceStatus: 'accepted',
2741
+ testEvidenceHash: validation.hash,
2742
+ reasons: [`Runtime accepted reviewed test validation evidence ${validation.ref}.`]
2743
+ };
2744
+ }
2745
+ async function validateAcceptedGoalVerifyArtifact(projectRoot, input) {
2746
+ if (!input.required) {
2747
+ return {
2748
+ acceptedGoalVerificationRef: null,
2749
+ goalVerifyAcceptanceStatus: 'not_required_noop',
2750
+ goalVerificationHash: null,
2751
+ reasons: ['Goal verification was not required by this lifecycle decision.'],
2752
+ truthAlignment: null
2753
+ };
2754
+ }
2755
+ if (input.blocked) {
2756
+ return rejectedGoalVerifyArtifact('not_accepted_blocked', 'blocked_lifecycle', 'Lifecycle risk decision blocks goal-verify closure.', 'Resolve lifecycle blockers before requesting goal-verify-stage closure.', 'runtime-blocked');
2757
+ }
2758
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2759
+ return rejectedGoalVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready goal-verify closure must be backed by a validated goal-verify StageCollaborationContract.', 'Ask goal-verify-manager to write .sdd/runs/<branch>/goal-verify/goal-verify-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2760
+ }
2761
+ const upstreamIssue = await validateAcceptedTestHandoff(projectRoot, input.partition, input.acceptedTestHandoff);
2762
+ if (upstreamIssue) {
2763
+ return rejectedGoalVerifyArtifact('not_accepted_missing_upstream', 'missing_refs', upstreamIssue, 'Refresh test-stage closure and provide a fresh test -> goal-verify handoff before goal-verify closure.');
2764
+ }
2765
+ const verification = latestStageArtifact(input.registeredArtifacts, 'goal_verification');
2766
+ if (!verification) {
2767
+ return rejectedGoalVerifyArtifact('not_accepted_missing_verification', 'missing_refs', 'Ready goal-verify closure must be backed by a registered goal-validator verification artifact.', 'Ask goal-validator to write .sdd/runs/<branch>/goal-verify/goal-verification-vN.md and register it before closure.');
2768
+ }
2769
+ if (verification.frontmatter.status !== 'passed') {
2770
+ return rejectedGoalVerifyArtifact('not_accepted_failed_verification', 'invalid_proposal', `Goal verification ${verification.ref} status ${String(verification.frontmatter.status)} cannot close goal-verify stage.`, 'Resolve goal verification failures and register passing goal verification evidence before closure.');
2771
+ }
2772
+ if (verification.frontmatter.coverageComplete !== true) {
2773
+ return rejectedGoalVerifyArtifact('not_accepted_incomplete_coverage', 'invalid_proposal', `Goal verification ${verification.ref} must declare coverageComplete: true.`, 'Complete acceptance coverage before requesting goal-verify closure.');
2774
+ }
2775
+ const durableGapCount = verification.frontmatter.durableGapCount;
2776
+ const openGapCount = verification.frontmatter.openGapCount;
2777
+ if ((typeof durableGapCount === 'number' && durableGapCount > 0) || (typeof openGapCount === 'number' && openGapCount > 0)) {
2778
+ return rejectedGoalVerifyArtifact('not_accepted_open_gap', 'invalid_proposal', `Goal verification ${verification.ref} still declares open durable gaps.`, 'Resolve durable gaps before requesting goal-verify closure.');
2779
+ }
2780
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2781
+ if (!manager) {
2782
+ return rejectedGoalVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready goal-verify closure must be backed by a registered goal-verify-manager closure artifact.', 'Ask goal-verify-manager to write .sdd/runs/<branch>/goal-verify/goal-verify-manager-vN.md and register it before closure.');
2783
+ }
2784
+ if (manager.recommendation !== 'close_stage') {
2785
+ return rejectedGoalVerifyArtifact('not_accepted_rejected', 'invalid_proposal', `Goal-verify-manager recommendation ${manager.recommendation ?? 'none'} cannot accept goal verification for truthAlignment and ship readiness.`, 'Ask goal-verify-manager to submit a close_stage recommendation after goal verification blockers are resolved.');
2786
+ }
2787
+ const reviewByRef = manager.reviewRef
2788
+ ? input.registeredArtifacts.find((record) => record.kind === 'goal_review' && record.ref === manager.reviewRef?.ref) ?? null
2789
+ : null;
2790
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2791
+ return rejectedGoalVerifyArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Goal-verify-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh goal-verify-manager closure artifact so reviewRef/reviewHash point at the registered goal review artifact.');
2792
+ }
2793
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'goal_review');
2794
+ if (!approvedReview) {
2795
+ return rejectedGoalVerifyArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready goal-verify closure must include a registered goal-reviewer artifact referenced by goal-verify-manager.', 'Ask goal-reviewer to write .sdd/runs/<branch>/goal-verify/goal-review-vN.md and ask goal-verify-manager to reference that review hash.');
2796
+ }
2797
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2798
+ return rejectedGoalVerifyArtifact('not_accepted_missing_review', 'missing_required_review', `Goal-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot accept goal verification.`, 'Resolve review blockers and register an approved goal-reviewer artifact before closure.');
2799
+ }
2800
+ if (manager.targetRef?.kind !== 'artifact' || manager.targetRef.ref !== verification.ref || approvedReview.targetRef?.kind !== 'artifact' || approvedReview.targetRef.ref !== verification.ref) {
2801
+ return rejectedGoalVerifyArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready goal-verify closure must reference reviewed ${verification.ref} as the goal verification artifact.`, 'Refresh goal-reviewer and goal-verify-manager artifacts so targetRef points at the goal verification artifact.');
2802
+ }
2803
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2804
+ const mismatchedHash = expectedHashes.find((hash) => hash !== verification.hash);
2805
+ if (mismatchedHash) {
2806
+ return rejectedGoalVerifyArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed goal verification hash mismatch for ${verification.ref}: expected ${mismatchedHash}, actual ${verification.hash}.`, 'Refresh goal-reviewer and goal-verify-manager artifacts with the current goal verification hash before closure.');
2807
+ }
2808
+ return {
2809
+ acceptedGoalVerificationRef: { kind: 'artifact', ref: verification.ref, hash: verification.hash },
2810
+ goalVerifyAcceptanceStatus: 'accepted',
2811
+ goalVerificationHash: verification.hash,
2812
+ reasons: [`Runtime accepted reviewed goal verification evidence ${verification.ref}.`],
2813
+ truthAlignment: parseGoalVerifyTruthAlignment(verification)
2814
+ };
2815
+ }
2816
+ async function validateAcceptedShipReadinessArtifact(projectRoot, input) {
2817
+ if (!input.required) {
2818
+ return {
2819
+ acceptedShipReadinessRef: null,
2820
+ shipReadinessStatus: 'not_required_noop',
2821
+ shipReadinessHash: null,
2822
+ releaseDocumentRef: null,
2823
+ reasons: ['Ship was not required by this lifecycle decision.']
2824
+ };
2825
+ }
2826
+ if (input.blocked) {
2827
+ return rejectedShipReadinessArtifact('not_accepted_blocked', 'blocked_lifecycle', input.blockedReason ?? 'Lifecycle risk decision blocks ship closure.', 'Resolve ship blockers before requesting ship-stage closure.', 'runtime-blocked');
2828
+ }
2829
+ if (!latestValidatedStageCollaborationContract(input.registeredCollaborationContracts)) {
2830
+ return rejectedShipReadinessArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready ship closure must be backed by a validated ship StageCollaborationContract.', 'Ask ship-manager to write .sdd/runs/<branch>/ship/ship-collaboration-contract-vN.md within StageWorkOrder constraints before closure.');
2831
+ }
2832
+ const truthAlignmentIssue = await validateAcceptedTruthAlignment(projectRoot, input.partition, input.truthAlignment);
2833
+ if (truthAlignmentIssue) {
2834
+ return rejectedShipReadinessArtifact('not_accepted_missing_upstream', 'missing_refs', truthAlignmentIssue, 'Refresh goal-verify closure so runtime records an aligned truthAlignment projection before ship closure.');
2835
+ }
2836
+ const authorityIssue = validateNoRuntimeAuthorityAttempts(input.registeredArtifacts);
2837
+ if (authorityIssue) {
2838
+ return rejectedShipReadinessArtifact('not_accepted_authority_violation', 'authority_violation', authorityIssue, 'Remove workflow-authority attempts from ship evidence before requesting runtime closure.');
2839
+ }
2840
+ const readiness = latestStageArtifact(input.registeredArtifacts, 'ship_readiness');
2841
+ if (!readiness) {
2842
+ return rejectedShipReadinessArtifact('not_accepted_missing_readiness', 'missing_refs', 'Ready ship closure must be backed by a registered ship-validator readiness artifact.', 'Ask ship-validator to write .sdd/runs/<branch>/ship/ship-readiness-vN.md and register it before closure.');
2843
+ }
2844
+ if (readiness.frontmatter.status !== 'ready') {
2845
+ return rejectedShipReadinessArtifact('not_accepted_rejected', 'invalid_proposal', `Ship readiness ${readiness.ref} status ${String(readiness.frontmatter.status)} cannot mark the project ship-ready.`, 'Resolve ship readiness failures and register ready ship evidence before closure.');
2846
+ }
2847
+ const openBlockerCount = readinessBlockerCount(readiness);
2848
+ if (openBlockerCount > 0) {
2849
+ return rejectedShipReadinessArtifact('not_accepted_open_blocker', 'invalid_proposal', `Ship readiness ${readiness.ref} still declares ${openBlockerCount} open blockers.`, 'Resolve doctor, capability, repair, gap, and migration blockers before requesting ship closure.');
2850
+ }
2851
+ const releaseDocument = await parseShipReleaseDocumentRef(projectRoot, input.partition, readiness);
2852
+ if (releaseDocument.issue) {
2853
+ return rejectedShipReadinessArtifact('not_accepted_release_drift', 'invalid_proposal', releaseDocument.issue, 'Refresh release documentation and ship readiness releaseRef/releaseHash before requesting ship closure.');
2854
+ }
2855
+ const manager = latestStageArtifact(input.registeredArtifacts, 'manager_closure_request');
2856
+ if (!manager) {
2857
+ return rejectedShipReadinessArtifact('not_accepted_wrong_ref', 'invalid_proposal', 'Ready ship closure must be backed by a registered ship-manager closure artifact.', 'Ask ship-manager to write .sdd/runs/<branch>/ship/ship-manager-vN.md and register it before closure.');
2858
+ }
2859
+ if (manager.recommendation !== 'close_stage') {
2860
+ return rejectedShipReadinessArtifact('not_accepted_rejected', 'invalid_proposal', `Ship-manager recommendation ${manager.recommendation ?? 'none'} cannot mark the project ship-ready.`, 'Ask ship-manager to submit a close_stage recommendation after ship blockers are resolved.');
2861
+ }
2862
+ const reviewByRef = manager.reviewRef
2863
+ ? input.registeredArtifacts.find((record) => record.kind === 'release_review' && record.ref === manager.reviewRef?.ref) ?? null
2864
+ : null;
2865
+ if (reviewByRef && manager.reviewHash !== reviewByRef.hash) {
2866
+ return rejectedShipReadinessArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Ship-manager review hash mismatch for ${reviewByRef.ref}: expected ${manager.reviewHash ?? 'none'}, actual ${reviewByRef.hash}.`, 'Refresh ship-manager closure artifact so reviewRef/reviewHash point at the registered release review artifact.');
2867
+ }
2868
+ const approvedReview = findStageReviewForManager(input.registeredArtifacts, manager, 'release_review');
2869
+ if (!approvedReview) {
2870
+ return rejectedShipReadinessArtifact('not_accepted_missing_review', 'missing_required_review', 'Ready ship closure must include a registered release-reviewer artifact referenced by ship-manager.', 'Ask release-reviewer to write .sdd/runs/<branch>/ship/release-review-vN.md and ask ship-manager to reference that review hash.');
2871
+ }
2872
+ if (approvedReview.verdict !== 'approved' || approvedReview.blockingCount !== 0) {
2873
+ return rejectedShipReadinessArtifact('not_accepted_missing_review', 'missing_required_review', `Release-reviewer verdict ${approvedReview.verdict ?? 'none'} with ${approvedReview.blockingCount ?? 0} blockers cannot mark the project ship-ready.`, 'Resolve release review blockers and register an approved release-reviewer artifact before closure.');
2874
+ }
2875
+ if (manager.targetRef?.kind !== 'artifact' || manager.targetRef.ref !== readiness.ref || approvedReview.targetRef?.kind !== 'artifact' || approvedReview.targetRef.ref !== readiness.ref) {
2876
+ return rejectedShipReadinessArtifact('not_accepted_wrong_ref', 'invalid_proposal', `Ready ship closure must reference reviewed ${readiness.ref} as the ship readiness artifact.`, 'Refresh release-reviewer and ship-manager artifacts so targetRef points at the ship readiness artifact.');
2877
+ }
2878
+ const expectedHashes = [manager.targetHash, approvedReview.targetHash, manager.targetRef.hash, approvedReview.targetRef.hash].filter((hash) => Boolean(hash));
2879
+ const mismatchedHash = expectedHashes.find((hash) => hash !== readiness.hash);
2880
+ if (mismatchedHash) {
2881
+ return rejectedShipReadinessArtifact('not_accepted_hash_mismatch', 'invalid_proposal', `Reviewed ship readiness hash mismatch for ${readiness.ref}: expected ${mismatchedHash}, actual ${readiness.hash}.`, 'Refresh release-reviewer and ship-manager artifacts with the current ship readiness hash before closure.');
2882
+ }
2883
+ return {
2884
+ acceptedShipReadinessRef: { kind: 'artifact', ref: readiness.ref, hash: readiness.hash },
2885
+ shipReadinessStatus: 'accepted',
2886
+ shipReadinessHash: readiness.hash,
2887
+ releaseDocumentRef: releaseDocument.ref,
2888
+ reasons: [`Runtime accepted reviewed ship readiness evidence ${readiness.ref}.`]
2889
+ };
2890
+ }
2891
+ async function validateAcceptedPlanHandoff(projectRoot, partition, handoff) {
2892
+ const expectedPlanRef = `specs/${partition}/plan.md`;
2893
+ if (!handoff) {
2894
+ return 'Tasks closure requires a recorded plan -> tasks handoff.';
2895
+ }
2896
+ if (handoff.fromStage !== 'plan' || handoff.toStage !== 'tasks' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2897
+ return 'Tasks closure requires a non-blocking plan -> tasks handoff.';
2898
+ }
2899
+ const acceptedPlanRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'document' && inputRef.ref === expectedPlanRef)
2900
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'document' && outputRef.ref === expectedPlanRef);
2901
+ if (!acceptedPlanRef?.hash) {
2902
+ return `Plan -> tasks handoff must carry accepted ${expectedPlanRef} with a content hash.`;
2903
+ }
2904
+ const planContent = await readOptionalText(path.join(projectRoot, 'specs', partition, 'plan.md'));
2905
+ if (planContent === null) {
2906
+ return `Accepted upstream plan artifact is missing at ${expectedPlanRef}.`;
2907
+ }
2908
+ const currentPlanHash = hashDocumentContent(planContent);
2909
+ if (acceptedPlanRef.hash !== currentPlanHash) {
2910
+ return `Accepted upstream plan hash is stale for ${expectedPlanRef}: expected ${acceptedPlanRef.hash}, actual ${currentPlanHash}.`;
2911
+ }
2912
+ return null;
2913
+ }
2914
+ async function validateAcceptedTasksHandoff(projectRoot, partition, handoff) {
2915
+ const expectedTasksRef = `specs/${partition}/tasks.md`;
2916
+ if (!handoff) {
2917
+ return 'Verifies closure requires a recorded tasks -> verifies handoff.';
2918
+ }
2919
+ if (handoff.fromStage !== 'tasks' || handoff.toStage !== 'verifies' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2920
+ return 'Verifies closure requires a non-blocking tasks -> verifies handoff.';
2921
+ }
2922
+ const acceptedTasksRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'document' && inputRef.ref === expectedTasksRef)
2923
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'document' && outputRef.ref === expectedTasksRef);
2924
+ if (!acceptedTasksRef?.hash) {
2925
+ return `Tasks -> verifies handoff must carry accepted ${expectedTasksRef} with a content hash.`;
2926
+ }
2927
+ const tasksContent = await readOptionalText(path.join(projectRoot, 'specs', partition, 'tasks.md'));
2928
+ if (tasksContent === null) {
2929
+ return `Accepted upstream tasks artifact is missing at ${expectedTasksRef}.`;
2930
+ }
2931
+ const currentTasksHash = hashDocumentContent(tasksContent);
2932
+ if (acceptedTasksRef.hash !== currentTasksHash) {
2933
+ return `Accepted upstream tasks hash is stale for ${expectedTasksRef}: expected ${acceptedTasksRef.hash}, actual ${currentTasksHash}.`;
2934
+ }
2935
+ return null;
2936
+ }
2937
+ async function validateAcceptedVerifyHandoff(projectRoot, partition, handoff) {
2938
+ const expectedVerifyRef = `specs/${partition}/verify.md`;
2939
+ if (!handoff) {
2940
+ return 'Do closure requires a recorded verifies -> do handoff.';
2941
+ }
2942
+ if (handoff.fromStage !== 'verifies' || handoff.toStage !== 'do' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2943
+ return 'Do closure requires a non-blocking verifies -> do handoff.';
2944
+ }
2945
+ const acceptedVerifyRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'document' && inputRef.ref === expectedVerifyRef)
2946
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'document' && outputRef.ref === expectedVerifyRef);
2947
+ if (!acceptedVerifyRef?.hash) {
2948
+ return `Verifies -> do handoff must carry accepted ${expectedVerifyRef} with a content hash.`;
2949
+ }
2950
+ const verifyContent = await readOptionalText(path.join(projectRoot, 'specs', partition, 'verify.md'));
2951
+ if (verifyContent === null) {
2952
+ return `Accepted upstream verify artifact is missing at ${expectedVerifyRef}.`;
2953
+ }
2954
+ const currentVerifyHash = hashDocumentContent(verifyContent);
2955
+ if (acceptedVerifyRef.hash !== currentVerifyHash) {
2956
+ return `Accepted upstream verify hash is stale for ${expectedVerifyRef}: expected ${acceptedVerifyRef.hash}, actual ${currentVerifyHash}.`;
2957
+ }
2958
+ return null;
2959
+ }
2960
+ async function validateAcceptedDoHandoff(projectRoot, partition, handoff) {
2961
+ if (!handoff) {
2962
+ return 'Test closure requires a recorded do -> test handoff.';
2963
+ }
2964
+ if (handoff.fromStage !== 'do' || handoff.toStage !== 'test' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2965
+ return 'Test closure requires a non-blocking do -> test handoff.';
2966
+ }
2967
+ const acceptedImplementationRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'artifact' && inputRef.ref.startsWith(`.sdd/runs/${partition}/do/implementation-`))
2968
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'artifact' && outputRef.ref.startsWith(`.sdd/runs/${partition}/do/implementation-`));
2969
+ if (!acceptedImplementationRef?.hash) {
2970
+ return `Do -> test handoff must carry accepted .sdd/runs/${partition}/do/implementation-vN.md with a content hash.`;
2971
+ }
2972
+ return validateHandoffInputHashes(projectRoot, handoff.requiredInputRefs);
2973
+ }
2974
+ async function validateAcceptedTestHandoff(projectRoot, partition, handoff) {
2975
+ if (!handoff) {
2976
+ return 'Goal-verify closure requires a recorded test -> goal-verify handoff.';
2977
+ }
2978
+ if (handoff.fromStage !== 'test' || handoff.toStage !== 'goal-verify' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2979
+ return 'Goal-verify closure requires a non-blocking test -> goal-verify handoff.';
2980
+ }
2981
+ const acceptedTestRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'artifact' && inputRef.ref.startsWith(`.sdd/runs/${partition}/test/validation-`))
2982
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'artifact' && outputRef.ref.startsWith(`.sdd/runs/${partition}/test/validation-`));
2983
+ if (!acceptedTestRef?.hash) {
2984
+ return `Test -> goal-verify handoff must carry accepted .sdd/runs/${partition}/test/validation-vN.md with a content hash.`;
2985
+ }
2986
+ return validateHandoffInputHashes(projectRoot, handoff.requiredInputRefs);
2987
+ }
2988
+ async function validateAcceptedSpecHandoff(projectRoot, partition, handoff) {
2989
+ const expectedSpecRef = `specs/${partition}/spec.md`;
2990
+ if (!handoff) {
2991
+ return 'Plan closure requires a recorded spec -> plan handoff.';
2992
+ }
2993
+ if (handoff.fromStage !== 'spec' || handoff.toStage !== 'plan' || handoff.status === 'blocked' || handoff.status === 'rejected') {
2994
+ return 'Plan closure requires a non-blocking spec -> plan handoff.';
2995
+ }
2996
+ const acceptedSpecRef = handoff.requiredInputRefs.find((inputRef) => inputRef.kind === 'document' && inputRef.ref === expectedSpecRef)
2997
+ ?? handoff.outputRefs.find((outputRef) => outputRef.kind === 'document' && outputRef.ref === expectedSpecRef);
2998
+ if (!acceptedSpecRef?.hash) {
2999
+ return `Spec -> plan handoff must carry accepted ${expectedSpecRef} with a content hash.`;
3000
+ }
3001
+ const specContent = await readOptionalText(path.join(projectRoot, 'specs', partition, 'spec.md'));
3002
+ if (specContent === null) {
3003
+ return `Accepted upstream spec artifact is missing at ${expectedSpecRef}.`;
3004
+ }
3005
+ const currentSpecHash = hashDocumentContent(specContent);
3006
+ if (acceptedSpecRef.hash !== currentSpecHash) {
3007
+ return `Accepted upstream spec hash is stale for ${expectedSpecRef}: expected ${acceptedSpecRef.hash}, actual ${currentSpecHash}.`;
3008
+ }
3009
+ return null;
3010
+ }
3011
+ function rejectedPlanArtifact(planAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3012
+ return {
3013
+ acceptedPlanRef: null,
3014
+ planAcceptanceStatus,
3015
+ planHash: null,
3016
+ planContractHash: null,
3017
+ reasons: [explanation],
3018
+ rejectionIssue: {
3019
+ reasonCode,
3020
+ explanation,
3021
+ requiredNextAction,
3022
+ fallbackRoute
3023
+ }
3024
+ };
3025
+ }
3026
+ function rejectedTasksArtifact(tasksAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3027
+ return {
3028
+ acceptedTasksRef: null,
3029
+ tasksAcceptanceStatus,
3030
+ tasksHash: null,
3031
+ tasksContractHash: null,
3032
+ reasons: [explanation],
3033
+ rejectionIssue: {
3034
+ reasonCode,
3035
+ explanation,
3036
+ requiredNextAction,
3037
+ fallbackRoute
3038
+ }
3039
+ };
3040
+ }
3041
+ function rejectedVerifyArtifact(verifyAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3042
+ return {
3043
+ acceptedVerifyRef: null,
3044
+ verifyAcceptanceStatus,
3045
+ verifyHash: null,
3046
+ verifyContractHash: null,
3047
+ reasons: [explanation],
3048
+ rejectionIssue: {
3049
+ reasonCode,
3050
+ explanation,
3051
+ requiredNextAction,
3052
+ fallbackRoute
3053
+ }
3054
+ };
3055
+ }
3056
+ function rejectedDoArtifact(implementationAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3057
+ return {
3058
+ acceptedImplementationRef: null,
3059
+ implementationAcceptanceStatus,
3060
+ implementationHash: null,
3061
+ changedFileRefs: [],
3062
+ reasons: [explanation],
3063
+ rejectionIssue: {
3064
+ reasonCode,
3065
+ explanation,
3066
+ requiredNextAction,
3067
+ fallbackRoute
3068
+ }
3069
+ };
3070
+ }
3071
+ function rejectedTestArtifact(testAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3072
+ return {
3073
+ acceptedTestEvidenceRef: null,
3074
+ testAcceptanceStatus,
3075
+ testEvidenceHash: null,
3076
+ reasons: [explanation],
3077
+ rejectionIssue: {
3078
+ reasonCode,
3079
+ explanation,
3080
+ requiredNextAction,
3081
+ fallbackRoute
3082
+ }
3083
+ };
3084
+ }
3085
+ function rejectedGoalVerifyArtifact(goalVerifyAcceptanceStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3086
+ return {
3087
+ acceptedGoalVerificationRef: null,
3088
+ goalVerifyAcceptanceStatus,
3089
+ goalVerificationHash: null,
3090
+ reasons: [explanation],
3091
+ truthAlignment: null,
3092
+ rejectionIssue: {
3093
+ reasonCode,
3094
+ explanation,
3095
+ requiredNextAction,
3096
+ fallbackRoute
3097
+ }
3098
+ };
3099
+ }
3100
+ function parseGoalVerifyTruthAlignment(verification) {
3101
+ const status = enumFrontmatter(verification.frontmatter, 'truthAlignmentStatus', TRUTH_ALIGNMENT_STATUSES);
3102
+ const semanticImpact = enumFrontmatter(verification.frontmatter, 'semanticImpact', TRUTH_ALIGNMENT_SEMANTIC_IMPACTS);
3103
+ const ownerStage = enumFrontmatter(verification.frontmatter, 'ownerStage', TRUTH_ALIGNMENT_OWNER_STAGES);
3104
+ const invalidatesStages = stageListFrontmatter(verification.frontmatter, 'invalidatesStages');
3105
+ const staleRefs = runtimeRefListFrontmatter(verification.frontmatter, 'staleRefs');
3106
+ const reasons = stringListFrontmatter(verification.frontmatter, 'truthAlignmentReasons');
3107
+ return {
3108
+ status,
3109
+ ownerStage,
3110
+ semanticImpact,
3111
+ staleRefs,
3112
+ invalidatesStages,
3113
+ reasons
3114
+ };
3115
+ }
3116
+ function enumFrontmatter(frontmatter, key, allowed) {
3117
+ const value = frontmatter[key];
3118
+ if (value === undefined || value === null || value === '') {
3119
+ return null;
3120
+ }
3121
+ if (typeof value !== 'string' || !allowed.includes(value)) {
3122
+ return null;
3123
+ }
3124
+ return value;
3125
+ }
3126
+ function stringListFrontmatter(frontmatter, key) {
3127
+ const value = frontmatter[key];
3128
+ if (!Array.isArray(value)) {
3129
+ return [];
3130
+ }
3131
+ return value.filter((item) => typeof item === 'string' && item.length > 0);
3132
+ }
3133
+ function stageListFrontmatter(frontmatter, key) {
3134
+ const values = stringListFrontmatter(frontmatter, key);
3135
+ if (values.length === 0) {
3136
+ return null;
3137
+ }
3138
+ const allowed = new Set(['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
3139
+ return values.filter((value) => allowed.has(value));
3140
+ }
3141
+ function runtimeRefListFrontmatter(frontmatter, key) {
3142
+ const value = frontmatter[key];
3143
+ if (!Array.isArray(value)) {
3144
+ return [];
3145
+ }
3146
+ const refs = [];
3147
+ for (const item of value) {
3148
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
3149
+ continue;
3150
+ }
3151
+ const entry = item;
3152
+ if (typeof entry.ref !== 'string' || entry.ref.length === 0) {
3153
+ continue;
3154
+ }
3155
+ const kind = typeof entry.kind === 'string' ? entry.kind : entry.ref.startsWith('.sdd/runs/') ? 'artifact' : entry.ref.startsWith('specs/') ? 'document' : 'external';
3156
+ refs.push({ kind, ref: entry.ref, hash: typeof entry.hash === 'string' && entry.hash.length > 0 ? entry.hash : undefined });
3157
+ }
3158
+ return refs;
3159
+ }
3160
+ function rejectedShipReadinessArtifact(shipReadinessStatus, reasonCode, explanation, requiredNextAction, fallbackRoute = 'revise-proposal') {
3161
+ return {
3162
+ acceptedShipReadinessRef: null,
3163
+ shipReadinessStatus,
3164
+ shipReadinessHash: null,
3165
+ releaseDocumentRef: null,
3166
+ reasons: [explanation],
3167
+ rejectionIssue: {
3168
+ reasonCode,
3169
+ explanation,
3170
+ requiredNextAction,
3171
+ fallbackRoute
3172
+ }
3173
+ };
3174
+ }
3175
+ async function parseShipReleaseDocumentRef(projectRoot, partition, readiness) {
3176
+ const rawRef = readiness.frontmatter.releaseRef;
3177
+ const hash = readiness.frontmatter.releaseHash;
3178
+ if (rawRef === undefined || rawRef === null || rawRef === '') {
3179
+ return { ref: null, issue: null };
3180
+ }
3181
+ if (typeof rawRef !== 'string' || typeof hash !== 'string' || hash.length === 0) {
3182
+ return { ref: null, issue: `Ship readiness ${readiness.ref} releaseRef requires a non-empty releaseHash.` };
3183
+ }
3184
+ const normalizedRef = normalizePortablePath(rawRef);
3185
+ if (!normalizedRef || normalizedRef === '.' || normalizedRef === '..' || normalizedRef.includes('..') || path.isAbsolute(rawRef) || !normalizedRef.startsWith(`specs/${partition}/`)) {
3186
+ return { ref: null, issue: `Ship readiness ${readiness.ref} release document ref is unsafe or outside the branch docs: ${rawRef}` };
3187
+ }
3188
+ const filePath = path.resolve(projectRoot, normalizedRef);
3189
+ const projectPath = path.resolve(projectRoot);
3190
+ if (!filePath.startsWith(`${projectPath}${path.sep}`)) {
3191
+ return { ref: null, issue: `Ship readiness ${readiness.ref} release document ref escapes project root: ${rawRef}` };
3192
+ }
3193
+ const content = await readOptionalText(filePath);
3194
+ if (content === null) {
3195
+ return { ref: null, issue: `Release document is missing at ${normalizedRef}.` };
3196
+ }
3197
+ const currentHash = hashDocumentContent(content);
3198
+ if (hash !== currentHash) {
3199
+ return { ref: null, issue: `Release document hash is stale for ${normalizedRef}: expected ${hash}, actual ${currentHash}.` };
3200
+ }
3201
+ return { ref: { kind: 'document', ref: normalizedRef, hash }, issue: null };
3202
+ }
3203
+ async function validateAcceptedTruthAlignment(projectRoot, partition, truthAlignment) {
3204
+ if (!truthAlignment) {
3205
+ return 'Ship closure requires a recorded aligned truthAlignment projection from goal-verify.';
3206
+ }
3207
+ if (truthAlignment.contract !== TRUTH_ALIGNMENT_CONTRACT || truthAlignment.sourceStage !== 'goal-verify') {
3208
+ return 'Ship closure requires a valid goal-verify truthAlignment projection.';
3209
+ }
3210
+ if (truthAlignment.status !== 'aligned') {
3211
+ const reasons = truthAlignment.reasons.length > 0 ? `: ${truthAlignment.reasons.join('; ')}` : '';
3212
+ return `Ship closure requires aligned truthAlignment; current status is ${truthAlignment.status}${reasons}.`;
3213
+ }
3214
+ if (truthAlignment.semanticImpact === 'material') {
3215
+ return 'Ship closure requires material truth drift to be reconciled before ship.';
3216
+ }
3217
+ if (truthAlignment.staleRefs.length > 0) {
3218
+ return `Ship closure requires fresh truthAlignment refs; stale refs: ${truthAlignment.staleRefs.map((ref) => ref.ref).join(', ')}.`;
3219
+ }
3220
+ if (truthAlignment.invalidatesStages.includes('ship')) {
3221
+ return 'Ship closure requires truthAlignment that does not invalidate ship.';
3222
+ }
3223
+ const acceptedGoalVerifyRef = truthAlignment.acceptedRealityRefs.find((ref) => ref.kind === 'artifact' && ref.ref.startsWith(`.sdd/runs/${partition}/goal-verify/goal-verification-`));
3224
+ if (!acceptedGoalVerifyRef?.hash) {
3225
+ return `TruthAlignment must carry accepted .sdd/runs/${partition}/goal-verify/goal-verification-vN.md with a content hash.`;
3226
+ }
3227
+ return validateTruthAlignmentRefHashes(projectRoot, [...truthAlignment.declaredTruthRefs, ...truthAlignment.acceptedRealityRefs]);
3228
+ }
3229
+ async function validateTruthAlignmentRefHashes(projectRoot, refs) {
3230
+ for (const ref of refs) {
3231
+ if (!ref.hash || ref.kind === 'projection' || ref.kind === 'run') {
3232
+ continue;
3233
+ }
3234
+ const normalized = normalizePortablePath(ref.ref);
3235
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.includes('..') || path.isAbsolute(ref.ref)) {
3236
+ return `TruthAlignment accepted reality ref is unsafe: ${ref.ref}`;
3237
+ }
3238
+ const filePath = path.resolve(projectRoot, normalized);
3239
+ const projectPath = path.resolve(projectRoot);
3240
+ if (!filePath.startsWith(`${projectPath}${path.sep}`)) {
3241
+ return `TruthAlignment accepted reality ref escapes project root: ${ref.ref}`;
3242
+ }
3243
+ const content = await readOptionalText(filePath);
3244
+ if (content === null) {
3245
+ return `TruthAlignment accepted reality ref is missing at ${ref.ref}.`;
3246
+ }
3247
+ const currentHash = hashDocumentContent(content);
3248
+ if (ref.hash !== currentHash) {
3249
+ return `TruthAlignment accepted reality ref hash is stale for ${ref.ref}: expected ${ref.hash}, actual ${currentHash}.`;
3250
+ }
3251
+ }
3252
+ return null;
3253
+ }
3254
+ function validateNoRuntimeAuthorityAttempts(records) {
3255
+ const forbidden = new Set(['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved']);
3256
+ for (const record of records) {
3257
+ const value = record.frontmatter.authorityAttempts;
3258
+ if (!Array.isArray(value)) {
3259
+ continue;
3260
+ }
3261
+ const attempts = value.filter((attempt) => typeof attempt === 'string' && forbidden.has(attempt));
3262
+ if (attempts.length > 0) {
3263
+ return `Ship artifact ${record.ref} attempted workflow authority: ${attempts.join(', ')}.`;
3264
+ }
3265
+ }
3266
+ return null;
3267
+ }
3268
+ function shipBlockerCount(decision) {
3269
+ return decision.requiredEvidence.filter((evidence) => evidence.requiredBefore === 'ship' && evidence.refs.length === 0).length;
3270
+ }
3271
+ function readinessBlockerCount(readiness) {
3272
+ return numericFrontmatter(readiness.frontmatter, 'doctorBlockerCount')
3273
+ + numericFrontmatter(readiness.frontmatter, 'capabilityBlockerCount')
3274
+ + numericFrontmatter(readiness.frontmatter, 'repairBlockerCount')
3275
+ + numericFrontmatter(readiness.frontmatter, 'gapBlockerCount')
3276
+ + numericFrontmatter(readiness.frontmatter, 'migrationBlockerCount')
3277
+ + numericFrontmatter(readiness.frontmatter, 'openBlockerCount');
3278
+ }
3279
+ function numericFrontmatter(frontmatter, key) {
3280
+ const value = frontmatter[key];
3281
+ return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : 0;
3282
+ }
3283
+ function executionRefFromValidation(validation) {
3284
+ const value = validation.frontmatter.executionRef;
3285
+ if (typeof value !== 'string' || value.length === 0) {
3286
+ return null;
3287
+ }
3288
+ try {
3289
+ return normalizeBranchStageEvidenceRef(value);
3290
+ }
3291
+ catch {
3292
+ return null;
3293
+ }
3294
+ }
3295
+ async function validateHandoffInputHashes(projectRoot, inputRefs) {
3296
+ for (const ref of inputRefs) {
3297
+ if (!ref.hash) {
3298
+ continue;
3299
+ }
3300
+ const normalized = normalizePortablePath(ref.ref);
3301
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.includes('..') || path.isAbsolute(ref.ref)) {
3302
+ return `Handoff input ref is unsafe: ${ref.ref}`;
3303
+ }
3304
+ const filePath = path.resolve(projectRoot, normalized);
3305
+ const projectPath = path.resolve(projectRoot);
3306
+ if (!filePath.startsWith(`${projectPath}${path.sep}`)) {
3307
+ return `Handoff input ref escapes project root: ${ref.ref}`;
3308
+ }
3309
+ const content = await readOptionalText(filePath);
3310
+ if (content === null) {
3311
+ return `Accepted do handoff input is missing at ${ref.ref}.`;
3312
+ }
3313
+ const currentHash = hashDocumentContent(content);
3314
+ if (ref.hash !== currentHash) {
3315
+ return `Accepted do handoff input hash is stale for ${ref.ref}: expected ${ref.hash}, actual ${currentHash}.`;
3316
+ }
3317
+ }
3318
+ return null;
3319
+ }
3320
+ function findStageReviewForManager(records, manager, reviewKind) {
3321
+ if (manager.reviewRef) {
3322
+ return records.find((record) => record.kind === reviewKind && record.ref === manager.reviewRef?.ref && record.hash === manager.reviewHash) ?? null;
3323
+ }
3324
+ return latestStageArtifact(records, reviewKind);
3325
+ }
3326
+ function parseImplementationChangedFileRefs(implementation) {
3327
+ const value = implementation.frontmatter.changedFiles;
3328
+ if (!Array.isArray(value) || value.length === 0) {
3329
+ return { refs: [], issue: `Implementation evidence ${implementation.ref} must declare non-empty changedFiles frontmatter.` };
3330
+ }
3331
+ const refs = [];
3332
+ for (const item of value) {
3333
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
3334
+ return { refs: [], issue: `Implementation evidence ${implementation.ref} has an invalid changedFiles entry.` };
3335
+ }
3336
+ const entry = item;
3337
+ const rawRef = entry.ref;
3338
+ const hash = entry.hash;
3339
+ if (typeof rawRef !== 'string' || rawRef.length === 0 || typeof hash !== 'string' || hash.length === 0) {
3340
+ return { refs: [], issue: `Implementation evidence ${implementation.ref} changedFiles entries must include ref and hash.` };
3341
+ }
3342
+ const normalizedRef = normalizeChangedFileRef(rawRef);
3343
+ if (!normalizedRef) {
3344
+ return { refs: [], issue: `Implementation evidence ${implementation.ref} changed file ref is unsafe: ${rawRef}` };
3345
+ }
3346
+ refs.push({ kind: 'evidence', ref: normalizedRef, hash });
3347
+ }
3348
+ return { refs, issue: null };
3349
+ }
3350
+ function normalizeChangedFileRef(value) {
3351
+ const normalized = normalizePortablePath(value);
3352
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.includes('..') || path.isAbsolute(value)) {
3353
+ return null;
3354
+ }
3355
+ if (normalized.startsWith('.sdd/runs/') || normalized.startsWith('runs/') || isCanonicalSddDocumentRef(normalized)) {
3356
+ return null;
3357
+ }
3358
+ return normalized;
3359
+ }
3360
+ function validateAllowedChangedFileRefs(changedFileRefs, allowedChangedFileRefs) {
3361
+ if (!allowedChangedFileRefs || allowedChangedFileRefs.length === 0) {
3362
+ return null;
3363
+ }
3364
+ const allowedExact = new Set();
3365
+ const allowedPatterns = [];
3366
+ for (const value of allowedChangedFileRefs) {
3367
+ const normalized = value.includes('*') ? normalizeChangedFilePattern(value) : normalizeChangedFileRef(value);
3368
+ if (!normalized) {
3369
+ return `Allowed changed-file ref is unsafe: ${value}`;
3370
+ }
3371
+ if (normalized.includes('*')) {
3372
+ allowedPatterns.push(changedFilePatternRegex(normalized));
3373
+ }
3374
+ else {
3375
+ allowedExact.add(normalized);
3376
+ }
3377
+ }
3378
+ const outsideBoundary = changedFileRefs.find((ref) => !allowedExact.has(ref.ref) && !allowedPatterns.some((pattern) => pattern.test(ref.ref)));
3379
+ return outsideBoundary ? `Changed file ${outsideBoundary.ref} is outside the allowed do-stage task boundary.` : null;
3380
+ }
3381
+ function normalizeChangedFilePattern(value) {
3382
+ const normalized = normalizePortablePath(value);
3383
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.includes('..') || path.isAbsolute(value)) {
3384
+ return null;
3385
+ }
3386
+ if (normalized.startsWith('.sdd/runs/') || normalized.startsWith('runs/') || isCanonicalSddDocumentRef(normalized)) {
3387
+ return null;
3388
+ }
3389
+ return normalized;
3390
+ }
3391
+ function isCanonicalSddDocumentRef(normalizedRef) {
3392
+ return /^specs\/[^/]+\/(spec|plan|tasks|verify)\.md$/.test(normalizedRef);
3393
+ }
3394
+ function changedFilePatternRegex(pattern) {
3395
+ return new RegExp(`^${escapeRegex(pattern).replace(/\\\*/g, '[^/]*')}$`);
3396
+ }
3397
+ async function validateChangedFileHashes(projectRoot, changedFileRefs) {
3398
+ for (const ref of changedFileRefs) {
3399
+ const filePath = path.resolve(projectRoot, ref.ref);
3400
+ const projectPath = path.resolve(projectRoot);
3401
+ if (!filePath.startsWith(`${projectPath}${path.sep}`)) {
3402
+ return `Changed file ref escapes project root: ${ref.ref}`;
3403
+ }
3404
+ const content = await readOptionalText(filePath);
3405
+ if (content === null) {
3406
+ return `Changed file ${ref.ref} is missing from the workspace.`;
3407
+ }
3408
+ const currentHash = hashDocumentContent(content);
3409
+ if (ref.hash !== currentHash) {
3410
+ return `Changed file hash is stale for ${ref.ref}: expected ${ref.hash}, actual ${currentHash}.`;
3411
+ }
3412
+ }
3413
+ return null;
3414
+ }
3415
+ function buildPlanClosureStageRun(scope, runId, workOrder, health, input) {
3416
+ const status = health === 'ready_for_tasks'
3417
+ ? 'completed'
3418
+ : health === 'no-op'
3419
+ ? 'skipped'
3420
+ : 'blocked';
3421
+ return {
3422
+ contract: STAGE_RUN_CONTRACT_VERSION,
3423
+ id: stableId('stage-run-plan', scope, runId, input.generatedAt),
3424
+ scope,
3425
+ stage: 'plan',
3426
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3427
+ coMainAgents: workOrder?.agentTeam ?? [],
3428
+ status,
3429
+ inputRefs: input.inputRefs,
3430
+ outputRefs: input.outputRefs,
3431
+ decisionRefs: input.decisionRefs,
3432
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Plan adjudication health is ${health}.`] : [],
3433
+ createdAt: input.generatedAt,
3434
+ updatedAt: input.generatedAt
3435
+ };
3436
+ }
3437
+ function buildPlanWorkflowHandoff(scope, decision, input) {
3438
+ return {
3439
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3440
+ id: stableId('plan-tasks-handoff', scope, 'closure', input.generatedAt),
3441
+ scope,
3442
+ fromStage: 'plan',
3443
+ toStage: 'tasks',
3444
+ fromAgent: PLAN_STAGE_MANAGER,
3445
+ toAgent: 'tasks-stage-runtime',
3446
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3447
+ outputRefs: input.outputRefs,
3448
+ requiredInputRefs: input.requiredInputRefs,
3449
+ riskDecisionRef: input.riskDecisionRef,
3450
+ evidenceRefs: input.evidenceRefs,
3451
+ openQuestions: [],
3452
+ blockingGaps: [],
3453
+ createdAt: input.generatedAt
3454
+ };
3455
+ }
3456
+ function buildTasksClosureStageRun(scope, runId, workOrder, health, input) {
3457
+ const status = health === 'ready_for_verifies'
3458
+ ? 'completed'
3459
+ : health === 'no-op'
3460
+ ? 'skipped'
3461
+ : 'blocked';
3462
+ return {
3463
+ contract: STAGE_RUN_CONTRACT_VERSION,
3464
+ id: stableId('stage-run-tasks', scope, runId, input.generatedAt),
3465
+ scope,
3466
+ stage: 'tasks',
3467
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3468
+ coMainAgents: workOrder?.agentTeam ?? [],
3469
+ status,
3470
+ inputRefs: input.inputRefs,
3471
+ outputRefs: input.outputRefs,
3472
+ decisionRefs: input.decisionRefs,
3473
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Tasks adjudication health is ${health}.`] : [],
3474
+ createdAt: input.generatedAt,
3475
+ updatedAt: input.generatedAt
3476
+ };
3477
+ }
3478
+ function buildTasksWorkflowHandoff(scope, decision, input) {
3479
+ return {
3480
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3481
+ id: stableId('tasks-verifies-handoff', scope, 'closure', input.generatedAt),
3482
+ scope,
3483
+ fromStage: 'tasks',
3484
+ toStage: 'verifies',
3485
+ fromAgent: TASKS_STAGE_MANAGER,
3486
+ toAgent: 'verifies-stage-runtime',
3487
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3488
+ outputRefs: input.outputRefs,
3489
+ requiredInputRefs: input.requiredInputRefs,
3490
+ riskDecisionRef: input.riskDecisionRef,
3491
+ evidenceRefs: input.evidenceRefs,
3492
+ openQuestions: [],
3493
+ blockingGaps: [],
3494
+ createdAt: input.generatedAt
3495
+ };
3496
+ }
3497
+ function buildVerifiesClosureStageRun(scope, runId, workOrder, health, input) {
3498
+ const status = health === 'ready_for_do'
3499
+ ? 'completed'
3500
+ : health === 'no-op'
3501
+ ? 'skipped'
3502
+ : 'blocked';
3503
+ return {
3504
+ contract: STAGE_RUN_CONTRACT_VERSION,
3505
+ id: stableId('stage-run-verifies', scope, runId, input.generatedAt),
3506
+ scope,
3507
+ stage: 'verifies',
3508
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3509
+ coMainAgents: workOrder?.agentTeam ?? [],
3510
+ status,
3511
+ inputRefs: input.inputRefs,
3512
+ outputRefs: input.outputRefs,
3513
+ decisionRefs: input.decisionRefs,
3514
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Verifies adjudication health is ${health}.`] : [],
3515
+ createdAt: input.generatedAt,
3516
+ updatedAt: input.generatedAt
3517
+ };
3518
+ }
3519
+ function buildVerifiesWorkflowHandoff(scope, decision, input) {
3520
+ return {
3521
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3522
+ id: stableId('verifies-do-handoff', scope, 'closure', input.generatedAt),
3523
+ scope,
3524
+ fromStage: 'verifies',
3525
+ toStage: 'do',
3526
+ fromAgent: VERIFIES_STAGE_MANAGER,
3527
+ toAgent: 'do-stage-runtime',
3528
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3529
+ outputRefs: input.outputRefs,
3530
+ requiredInputRefs: input.requiredInputRefs,
3531
+ riskDecisionRef: input.riskDecisionRef,
3532
+ evidenceRefs: input.evidenceRefs,
3533
+ openQuestions: [],
3534
+ blockingGaps: [],
3535
+ createdAt: input.generatedAt
3536
+ };
3537
+ }
3538
+ function buildDoClosureStageRun(scope, runId, workOrder, health, input) {
3539
+ const status = health === 'ready_for_test'
3540
+ ? 'completed'
3541
+ : health === 'no-op'
3542
+ ? 'skipped'
3543
+ : 'blocked';
3544
+ return {
3545
+ contract: STAGE_RUN_CONTRACT_VERSION,
3546
+ id: stableId('stage-run-do', scope, runId, input.generatedAt),
3547
+ scope,
3548
+ stage: 'do',
3549
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3550
+ coMainAgents: workOrder?.agentTeam ?? [],
3551
+ status,
3552
+ inputRefs: input.inputRefs,
3553
+ outputRefs: input.outputRefs,
3554
+ decisionRefs: input.decisionRefs,
3555
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Do adjudication health is ${health}.`] : [],
3556
+ createdAt: input.generatedAt,
3557
+ updatedAt: input.generatedAt
3558
+ };
3559
+ }
3560
+ function buildDoWorkflowHandoff(scope, decision, input) {
3561
+ return {
3562
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3563
+ id: stableId('do-test-handoff', scope, 'closure', input.generatedAt),
3564
+ scope,
3565
+ fromStage: 'do',
3566
+ toStage: 'test',
3567
+ fromAgent: DO_STAGE_MANAGER,
3568
+ toAgent: 'test-stage-runtime',
3569
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3570
+ outputRefs: input.outputRefs,
3571
+ requiredInputRefs: input.requiredInputRefs,
3572
+ riskDecisionRef: input.riskDecisionRef,
3573
+ evidenceRefs: input.evidenceRefs,
3574
+ openQuestions: [],
3575
+ blockingGaps: [],
3576
+ createdAt: input.generatedAt
3577
+ };
3578
+ }
3579
+ function buildTestClosureStageRun(scope, runId, workOrder, health, input) {
3580
+ const status = health === 'ready_for_goal_verify'
3581
+ ? 'completed'
3582
+ : health === 'no-op'
3583
+ ? 'skipped'
3584
+ : 'blocked';
3585
+ return {
3586
+ contract: STAGE_RUN_CONTRACT_VERSION,
3587
+ id: stableId('stage-run-test', scope, runId, input.generatedAt),
3588
+ scope,
3589
+ stage: 'test',
3590
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3591
+ coMainAgents: workOrder?.agentTeam ?? [],
3592
+ status,
3593
+ inputRefs: input.inputRefs,
3594
+ outputRefs: input.outputRefs,
3595
+ decisionRefs: input.decisionRefs,
3596
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Test adjudication health is ${health}.`] : [],
3597
+ createdAt: input.generatedAt,
3598
+ updatedAt: input.generatedAt
3599
+ };
3600
+ }
3601
+ function buildTestWorkflowHandoff(scope, decision, input) {
3602
+ return {
3603
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3604
+ id: stableId('test-goal-verify-handoff', scope, 'closure', input.generatedAt),
3605
+ scope,
3606
+ fromStage: 'test',
3607
+ toStage: 'goal-verify',
3608
+ fromAgent: TEST_STAGE_MANAGER,
3609
+ toAgent: 'goal-verify-stage-runtime',
3610
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3611
+ outputRefs: input.outputRefs,
3612
+ requiredInputRefs: input.requiredInputRefs,
3613
+ riskDecisionRef: input.riskDecisionRef,
3614
+ evidenceRefs: input.evidenceRefs,
3615
+ openQuestions: [],
3616
+ blockingGaps: [],
3617
+ createdAt: input.generatedAt
3618
+ };
3619
+ }
3620
+ function buildGoalVerifyClosureStageRun(scope, runId, workOrder, health, input) {
3621
+ const status = health === 'ready_for_ship'
3622
+ ? 'completed'
3623
+ : health === 'no-op'
3624
+ ? 'skipped'
3625
+ : 'blocked';
3626
+ return {
3627
+ contract: STAGE_RUN_CONTRACT_VERSION,
3628
+ id: stableId('stage-run-goal-verify', scope, runId, input.generatedAt),
3629
+ scope,
3630
+ stage: 'goal-verify',
3631
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3632
+ coMainAgents: workOrder?.agentTeam ?? [],
3633
+ status,
3634
+ inputRefs: input.inputRefs,
3635
+ outputRefs: input.outputRefs,
3636
+ decisionRefs: input.decisionRefs,
3637
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Goal-verify adjudication health is ${health}.`] : [],
3638
+ createdAt: input.generatedAt,
3639
+ updatedAt: input.generatedAt
3640
+ };
3641
+ }
3642
+ function buildShipClosureStageRun(scope, runId, workOrder, health, input) {
3643
+ const status = health === 'ship_ready'
3644
+ ? 'completed'
3645
+ : health === 'no-op'
3646
+ ? 'skipped'
3647
+ : 'blocked';
3648
+ return {
3649
+ contract: STAGE_RUN_CONTRACT_VERSION,
3650
+ id: stableId('stage-run-ship', scope, runId, input.generatedAt),
3651
+ scope,
3652
+ stage: 'ship',
3653
+ ownerAgent: workOrder?.stageManager ?? 'runtime',
3654
+ coMainAgents: workOrder?.agentTeam ?? [],
3655
+ status,
3656
+ inputRefs: input.inputRefs,
3657
+ outputRefs: input.outputRefs,
3658
+ decisionRefs: input.decisionRefs,
3659
+ blockingReasons: status === 'blocked' ? [input.rejectionReason ?? `Ship adjudication health is ${health}.`] : [],
3660
+ createdAt: input.generatedAt,
3661
+ updatedAt: input.generatedAt
3662
+ };
3663
+ }
3664
+ function buildStageRejection(stage, scope, closureRequestId, reasonCode, explanation, requiredNextAction, fallbackRoute, generatedAt, retryAllowed) {
3665
+ return {
3666
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
3667
+ rejectionId: stableId(`${stage}-rejection`, scope, closureRequestId ?? reasonCode, generatedAt),
3668
+ stage,
3669
+ scope,
3670
+ closureRequestId,
3671
+ reasonCode,
3672
+ explanation,
3673
+ requiredNextAction,
3674
+ retryAllowed,
3675
+ retryBudgetRemaining: retryAllowed ? 1 : 0,
3676
+ fallbackRoute,
3677
+ createdAt: generatedAt
3678
+ };
3679
+ }
3680
+ function buildStageRuntimeNextActions(stage, scope, generatedAt, reasonCodes) {
3681
+ return reasonCodes
3682
+ .map((reasonCode, index) => ({
3683
+ contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
3684
+ actionId: stableId(`${stage}-next-action`, scope, reasonCode, generatedAt),
3685
+ stage,
3686
+ scope,
3687
+ kind: nextActionKind(reasonCode),
3688
+ owner: nextActionOwner(reasonCode),
3689
+ actionScope: nextActionScope(reasonCode),
3690
+ reasonCode,
3691
+ priority: {
3692
+ reasonRank: reasonCodeRank(reasonCode),
3693
+ upstreamDistance: stage === 'plan' && (reasonCode === 'missing_handoff' || reasonCode === 'stale_upstream') ? 1 : 0,
3694
+ actionRank: actionKindRank(nextActionKind(reasonCode)),
3695
+ stableOrder: index
3696
+ },
3697
+ requiredInputs: nextActionRequiredInputs(reasonCode),
3698
+ blockedBy: [],
3699
+ createdAt: generatedAt
3700
+ }))
3701
+ .sort(compareRuntimeNextAction);
3702
+ }
3703
+ function buildClosureStageRun(profile, adjudication, input) {
3704
+ const status = adjudication.health === 'ready_for_plan'
3705
+ ? 'completed'
3706
+ : adjudication.health === 'no-op'
3707
+ ? 'skipped'
3708
+ : 'blocked';
3709
+ return {
3710
+ contract: STAGE_RUN_CONTRACT_VERSION,
3711
+ id: stableId('stage-run-spec', profile.scope, input.runId, input.generatedAt),
3712
+ scope: profile.scope,
3713
+ stage: 'spec',
3714
+ ownerAgent: profile.stageManager ?? 'runtime',
3715
+ coMainAgents: profile.agentTeam,
3716
+ status,
3717
+ inputRefs: profile.inputRefs,
3718
+ outputRefs: input.outputRefs,
3719
+ decisionRefs: input.decisionRefs,
3720
+ blockingReasons: status === 'blocked' ? stageBlockingReasons(adjudication) : [],
3721
+ createdAt: input.generatedAt,
3722
+ updatedAt: input.generatedAt
3723
+ };
3724
+ }
3725
+ function buildClosureWorkflowHandoff(profile, decision, adjudication, input) {
3726
+ return {
3727
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
3728
+ id: adjudication.handoffPacket?.handoffId ?? stableId('spec-plan-handoff', profile.scope, 'closure', input.generatedAt),
3729
+ scope: profile.scope,
3730
+ fromStage: 'spec',
3731
+ toStage: 'plan',
3732
+ fromAgent: profile.stageManager ?? 'runtime',
3733
+ toAgent: recommendedPlanRoles(profile)[0] ?? 'plan-stage-runtime',
3734
+ status: decision.profile === 'blocked' || decision.approvalPolicy === 'blocked' ? 'blocked' : 'proposed',
3735
+ outputRefs: input.outputRefs,
3736
+ requiredInputRefs: input.requiredInputRefs,
3737
+ riskDecisionRef: input.riskDecisionRef,
3738
+ evidenceRefs: input.evidenceRefs,
3739
+ openQuestions: [],
3740
+ blockingGaps: [],
3741
+ createdAt: input.generatedAt
3742
+ };
3743
+ }
3744
+ function runLifecycleDecisionRecord(decision) {
3745
+ return {
3746
+ contract: LIFECYCLE_DECISION_CONTRACT,
3747
+ version: LIFECYCLE_DECISION_VERSION,
3748
+ model_version: decision.policyVersion,
3749
+ input_summary: { scope: decision.scope, inputHash: decision.inputHash },
3750
+ decision: {
3751
+ profile: decision.profile,
3752
+ confidence: decision.confidence,
3753
+ hard_gate_hits: decision.blockedStages,
3754
+ required_stages: decision.requiredStages,
3755
+ skipped_stages: decision.skippedStages,
3756
+ human_checkpoint_required: decision.humanCheckpointRequired
3757
+ },
3758
+ reasons: decision.reasons,
3759
+ escalation_triggers: decision.requiredReviews.map((review) => review.id),
3760
+ downgrade_reason: null,
3761
+ audit: {
3762
+ decided_at: decision.generatedAt,
3763
+ decided_by: 'phase9.1-stage-collaboration-runtime',
3764
+ policy_version: decision.policyVersion,
3765
+ source_artifacts: decision.inputRefs.map((ref) => ref.ref)
3766
+ }
3767
+ };
3768
+ }
3769
+ function stageBlockingReasons(adjudication) {
3770
+ if (adjudication.clarificationGate) {
3771
+ return adjudication.clarificationGate.questions;
3772
+ }
3773
+ if (adjudication.rejection) {
3774
+ return [adjudication.rejection.explanation];
3775
+ }
3776
+ return [`Spec adjudication health is ${adjudication.health}.`];
3777
+ }
3778
+ function specArtifactStatusForHealth(health) {
3779
+ if (health === 'no-op') {
3780
+ return 'not_required_noop';
3781
+ }
3782
+ if (health === 'needs_clarification') {
3783
+ return 'not_accepted_needs_clarification';
3784
+ }
3785
+ if (health === 'blocked') {
3786
+ return 'not_accepted_blocked';
3787
+ }
3788
+ return 'not_accepted_rejected';
3789
+ }
3790
+ function isManagedStarterSpec(content) {
3791
+ return /^sdd_managed_starter:\s*true\s*$/m.test(content);
3792
+ }
3793
+ async function readOptionalText(filePath) {
3794
+ try {
3795
+ return await readFile(filePath, 'utf8');
3796
+ }
3797
+ catch (error) {
3798
+ if (error.code === 'ENOENT') {
3799
+ return null;
3800
+ }
3801
+ throw error;
3802
+ }
3803
+ }
3804
+ function specAdjudicationProjectionRef(scope) {
3805
+ return {
3806
+ kind: 'projection',
3807
+ ref: `${SPEC_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${specCollaborationScopeKey(scope)}`
3808
+ };
3809
+ }
3810
+ function planAdjudicationProjectionRef(scope) {
3811
+ return {
3812
+ kind: 'projection',
3813
+ ref: `${PLAN_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${planCollaborationScopeKey(scope)}`
3814
+ };
3815
+ }
3816
+ function tasksAdjudicationProjectionRef(scope) {
3817
+ return {
3818
+ kind: 'projection',
3819
+ ref: `${TASKS_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${tasksCollaborationScopeKey(scope)}`
3820
+ };
3821
+ }
3822
+ function verifiesAdjudicationProjectionRef(scope) {
3823
+ return {
3824
+ kind: 'projection',
3825
+ ref: `${VERIFIES_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${verifiesCollaborationScopeKey(scope)}`
3826
+ };
3827
+ }
3828
+ function doAdjudicationProjectionRef(scope) {
3829
+ return {
3830
+ kind: 'projection',
3831
+ ref: `${DO_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${doCollaborationScopeKey(scope)}`
3832
+ };
3833
+ }
3834
+ function testAdjudicationProjectionRef(scope) {
3835
+ return {
3836
+ kind: 'projection',
3837
+ ref: `${TEST_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${testCollaborationScopeKey(scope)}`
3838
+ };
3839
+ }
3840
+ function goalVerifyAdjudicationProjectionRef(scope) {
3841
+ return {
3842
+ kind: 'projection',
3843
+ ref: `${GOAL_VERIFY_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${goalVerifyCollaborationScopeKey(scope)}`
3844
+ };
3845
+ }
3846
+ function shipAdjudicationProjectionRef(scope) {
3847
+ return {
3848
+ kind: 'projection',
3849
+ ref: `${SHIP_COLLABORATION_ADJUDICATION_PROJECTION_TYPE}:${shipCollaborationScopeKey(scope)}`
3850
+ };
3851
+ }
3852
+ function stableHash(value) {
3853
+ return createHash('sha256').update(value, 'utf8').digest('hex');
3854
+ }
3855
+ function uniqueRuntimeRefs(refs) {
3856
+ const seen = new Set();
3857
+ return refs.filter((ref) => {
3858
+ const key = `${ref.kind}:${ref.ref}:${ref.hash ?? ''}`;
3859
+ if (seen.has(key)) {
3860
+ return false;
3861
+ }
3862
+ seen.add(key);
3863
+ return true;
3864
+ });
3865
+ }
3866
+ function stableId(prefix, scope, ...parts) {
3867
+ const hash = createHash('sha256').update(JSON.stringify([scope, parts]), 'utf8').digest('hex').slice(0, 16);
3868
+ return `${prefix}-${hash}`;
3869
+ }
3870
+ //# sourceMappingURL=stage-collaboration.js.map