sdd-agent-platform 0.4.0 → 0.4.1

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 (417) hide show
  1. package/README.md +18 -23
  2. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +31 -28
  3. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js.map +1 -1
  4. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js +3 -2
  5. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js.map +1 -1
  6. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.d.ts +1 -1
  7. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js +14 -5
  8. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js.map +1 -1
  9. package/node_modules/@sdd-agent-platform/core/dist/contracts.d.ts +2 -0
  10. package/node_modules/@sdd-agent-platform/core/dist/contracts.js +2 -0
  11. package/node_modules/@sdd-agent-platform/core/dist/contracts.js.map +1 -1
  12. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js +3 -3
  13. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js.map +1 -1
  14. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +155 -1
  15. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
  16. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +23 -0
  17. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +54 -0
  18. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -0
  19. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/contracts.d.ts +11 -0
  20. package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js +15 -8
  21. package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js.map +1 -1
  22. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +14 -6
  23. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
  24. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.d.ts +112 -0
  25. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +145 -0
  26. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -0
  27. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +36 -36
  28. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  29. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +2 -0
  30. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +37 -17
  31. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  32. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +16 -1
  33. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +174 -16
  34. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
  35. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +2 -2
  36. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  37. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.d.ts +10 -0
  38. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +31 -1
  39. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
  40. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.d.ts +2 -17
  41. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +222 -10
  42. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js.map +1 -1
  43. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +5 -5
  44. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  45. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js +27 -12
  46. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js.map +1 -1
  47. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +59 -1
  48. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.d.ts +3 -1
  49. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +191 -0
  50. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
  51. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +32 -6
  52. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  53. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js +11 -4
  54. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js.map +1 -1
  55. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +31 -3
  56. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
  57. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +48 -15
  58. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  59. package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js +2 -2
  60. package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js.map +1 -1
  61. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +3 -1
  62. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +15 -49
  63. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
  64. package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js +2 -2
  65. package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js.map +1 -1
  66. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +25 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +21 -14
  68. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +62 -0
  70. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +130 -0
  71. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -0
  72. package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +1 -0
  73. package/node_modules/@sdd-agent-platform/core/dist/run-state.js +1 -0
  74. package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
  75. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +10 -0
  76. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +44 -14
  77. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  78. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js +1 -1
  79. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js.map +1 -1
  80. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +4 -0
  81. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +189 -0
  82. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -0
  83. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +12 -3
  84. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
  85. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +20 -0
  86. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +101 -21
  87. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  88. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +62 -1
  89. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +192 -4
  90. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  91. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.d.ts +195 -2
  92. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +499 -2
  93. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js.map +1 -1
  94. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +23 -1
  95. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +1 -1
  96. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +19 -0
  97. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +114 -12
  98. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +1 -1
  99. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +21 -0
  100. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
  101. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +16 -2
  102. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  103. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  104. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +34 -2
  105. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  106. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +15 -5
  107. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  108. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.d.ts +22 -0
  109. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js +53 -0
  110. package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js.map +1 -0
  111. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +102 -9
  112. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  113. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.d.ts +16 -1
  114. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +355 -69
  115. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  116. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +58 -0
  117. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +428 -0
  118. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -0
  119. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.d.ts +2 -0
  120. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +116 -18
  121. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  122. package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +2 -0
  123. package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -0
  124. package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
  125. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
  126. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +182 -0
  127. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -0
  128. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.d.ts +4 -0
  129. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +130 -0
  130. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -0
  131. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.d.ts +4 -0
  132. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +146 -0
  133. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -0
  134. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +89 -0
  135. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js +2 -0
  136. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js.map +1 -0
  137. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  138. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +16 -1
  139. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  140. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.d.ts +8 -4
  141. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +25 -11
  142. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js.map +1 -1
  143. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +38 -0
  144. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +122 -0
  145. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
  146. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +27 -0
  147. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +166 -37
  148. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  149. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.d.ts +1 -0
  150. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js +1 -0
  151. package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js.map +1 -1
  152. package/node_modules/@sdd-agent-platform/core/package.json +1 -1
  153. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +31 -28
  154. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +50 -4
  155. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +13 -10
  156. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +3 -2
  157. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +15 -5
  158. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +2 -0
  159. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +3 -3
  160. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +117 -5
  161. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +164 -1
  162. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +80 -0
  163. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +12 -0
  164. package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +16 -11
  165. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +7 -0
  166. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +5 -0
  167. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +14 -6
  168. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -0
  169. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -0
  170. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +4 -0
  171. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +2 -0
  172. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +11 -5
  173. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +36 -36
  174. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +39 -17
  175. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +3 -2
  176. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +5 -0
  177. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +2 -0
  178. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +3 -0
  179. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +269 -17
  180. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +2 -2
  181. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +41 -1
  182. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +238 -15
  183. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +27 -2
  184. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +5 -5
  185. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +31 -12
  186. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +66 -1
  187. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +211 -0
  188. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +151 -3
  189. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +35 -6
  190. package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +11 -4
  191. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +32 -3
  192. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +48 -15
  193. package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +2 -2
  194. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +17 -52
  195. package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +2 -2
  196. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +28 -1
  197. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +3 -0
  198. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +22 -18
  199. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +206 -0
  200. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +1 -0
  201. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +54 -14
  202. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +1 -1
  203. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +207 -0
  204. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +12 -3
  205. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +139 -0
  206. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +137 -24
  207. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +268 -5
  208. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +368 -4
  209. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +697 -2
  210. package/node_modules/@sdd-agent-platform/core/src/sync-back/apply.ts +23 -1
  211. package/node_modules/@sdd-agent-platform/core/src/sync-back/inspect.ts +145 -12
  212. package/node_modules/@sdd-agent-platform/core/src/sync-back/sync-back.test.ts +132 -9
  213. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +21 -0
  214. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +16 -2
  215. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.test.ts +1 -1
  216. package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.ts +38 -5
  217. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +15 -5
  218. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +77 -0
  219. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -0
  220. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +64 -4
  221. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +110 -12
  222. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +72 -25
  223. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +402 -77
  224. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +341 -0
  225. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +513 -0
  226. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +144 -5
  227. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +129 -18
  228. package/node_modules/@sdd-agent-platform/core/src/verification.ts +2 -0
  229. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +196 -0
  230. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +171 -0
  231. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +143 -0
  232. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +137 -0
  233. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +155 -0
  234. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +114 -0
  235. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +18 -1
  236. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +1 -1
  237. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +33 -11
  238. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +156 -0
  239. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +351 -2
  240. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +227 -39
  241. package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +1 -0
  242. package/package.json +1 -1
  243. package/packages/cli/dist/commands/status.js +2 -2
  244. package/packages/cli/dist/commands/status.js.map +1 -1
  245. package/packages/cli/dist/commands/sync-back.js +1 -1
  246. package/packages/cli/dist/commands/sync-back.js.map +1 -1
  247. package/packages/cli/dist/commands/tasks.js +4 -4
  248. package/packages/cli/dist/commands/tasks.js.map +1 -1
  249. package/packages/cli/dist/commands/test.js +94 -5
  250. package/packages/cli/dist/commands/test.js.map +1 -1
  251. package/packages/cli/dist/commands/verifies.js +5 -3
  252. package/packages/cli/dist/commands/verifies.js.map +1 -1
  253. package/packages/cli/dist/commands/verify.js +48 -7
  254. package/packages/cli/dist/commands/verify.js.map +1 -1
  255. package/packages/cli/dist/help.js +32 -18
  256. package/packages/cli/dist/help.js.map +1 -1
  257. package/packages/cli/dist/renderers/artifacts.js +1 -1
  258. package/packages/cli/dist/renderers/artifacts.js.map +1 -1
  259. package/packages/cli/dist/renderers/registry-runtime.js +7 -2
  260. package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
  261. package/packages/cli/dist/renderers/router.js +4 -2
  262. package/packages/cli/dist/renderers/router.js.map +1 -1
  263. package/packages/cli/dist/renderers/workflow.js +33 -12
  264. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  265. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  266. package/packages/cli/package.json +2 -2
  267. package/packages/core/dist/ai-tools.js +31 -28
  268. package/packages/core/dist/ai-tools.js.map +1 -1
  269. package/packages/core/dist/config/init-project.js +3 -2
  270. package/packages/core/dist/config/init-project.js.map +1 -1
  271. package/packages/core/dist/config/starter-documents.d.ts +1 -1
  272. package/packages/core/dist/config/starter-documents.js +14 -5
  273. package/packages/core/dist/config/starter-documents.js.map +1 -1
  274. package/packages/core/dist/contracts.d.ts +2 -0
  275. package/packages/core/dist/contracts.js +2 -0
  276. package/packages/core/dist/contracts.js.map +1 -1
  277. package/packages/core/dist/doctor/checks/run-evidence.js +3 -3
  278. package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
  279. package/packages/core/dist/doctor/doctor.js +155 -1
  280. package/packages/core/dist/doctor/doctor.js.map +1 -1
  281. package/packages/core/dist/evidence/lookup.d.ts +23 -0
  282. package/packages/core/dist/evidence/lookup.js +54 -0
  283. package/packages/core/dist/evidence/lookup.js.map +1 -0
  284. package/packages/core/dist/evidence-runtime/contracts.d.ts +11 -0
  285. package/packages/core/dist/execution/agent-execution-records.js +15 -8
  286. package/packages/core/dist/execution/agent-execution-records.js.map +1 -1
  287. package/packages/core/dist/execution/resident-worker.js +14 -6
  288. package/packages/core/dist/execution/resident-worker.js.map +1 -1
  289. package/packages/core/dist/execution/stage-team-runtime.d.ts +112 -0
  290. package/packages/core/dist/execution/stage-team-runtime.js +145 -0
  291. package/packages/core/dist/execution/stage-team-runtime.js.map +1 -0
  292. package/packages/core/dist/instructions.js +36 -36
  293. package/packages/core/dist/instructions.js.map +1 -1
  294. package/packages/core/dist/lifecycle/ship.d.ts +2 -0
  295. package/packages/core/dist/lifecycle/ship.js +37 -17
  296. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  297. package/packages/core/dist/registries/agent-capability-catalog.d.ts +16 -1
  298. package/packages/core/dist/registries/agent-capability-catalog.js +174 -16
  299. package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
  300. package/packages/core/dist/registries/agent-registry.js +2 -2
  301. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  302. package/packages/core/dist/registries/agent-runtime-static.d.ts +10 -0
  303. package/packages/core/dist/registries/agent-runtime-static.js +31 -1
  304. package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
  305. package/packages/core/dist/registries/capability-sources.d.ts +2 -17
  306. package/packages/core/dist/registries/capability-sources.js +222 -10
  307. package/packages/core/dist/registries/capability-sources.js.map +1 -1
  308. package/packages/core/dist/registries/workflow-gates.js +5 -5
  309. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  310. package/packages/core/dist/router/agent-runtime-config.js +27 -12
  311. package/packages/core/dist/router/agent-runtime-config.js.map +1 -1
  312. package/packages/core/dist/router/agent-runtime.d.ts +59 -1
  313. package/packages/core/dist/router/route-projection.d.ts +3 -1
  314. package/packages/core/dist/router/route-projection.js +191 -0
  315. package/packages/core/dist/router/route-projection.js.map +1 -1
  316. package/packages/core/dist/router/routing.js +32 -6
  317. package/packages/core/dist/router/routing.js.map +1 -1
  318. package/packages/core/dist/router/runtime-inspection.js +11 -4
  319. package/packages/core/dist/router/runtime-inspection.js.map +1 -1
  320. package/packages/core/dist/router/runtime-validation.js +31 -3
  321. package/packages/core/dist/router/runtime-validation.js.map +1 -1
  322. package/packages/core/dist/run-state/artifacts.js +48 -15
  323. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  324. package/packages/core/dist/run-state/events.js +2 -2
  325. package/packages/core/dist/run-state/events.js.map +1 -1
  326. package/packages/core/dist/run-state/inspect-run.d.ts +3 -1
  327. package/packages/core/dist/run-state/inspect-run.js +15 -49
  328. package/packages/core/dist/run-state/inspect-run.js.map +1 -1
  329. package/packages/core/dist/run-state/invocation-ledger.js +2 -2
  330. package/packages/core/dist/run-state/invocation-ledger.js.map +1 -1
  331. package/packages/core/dist/run-state/model.d.ts +25 -1
  332. package/packages/core/dist/run-state/run-state.js +21 -14
  333. package/packages/core/dist/run-state/run-state.js.map +1 -1
  334. package/packages/core/dist/run-state/task-evidence.d.ts +62 -0
  335. package/packages/core/dist/run-state/task-evidence.js +130 -0
  336. package/packages/core/dist/run-state/task-evidence.js.map +1 -0
  337. package/packages/core/dist/run-state.d.ts +1 -0
  338. package/packages/core/dist/run-state.js +1 -0
  339. package/packages/core/dist/run-state.js.map +1 -1
  340. package/packages/core/dist/runtime-paths.d.ts +10 -0
  341. package/packages/core/dist/runtime-paths.js +44 -14
  342. package/packages/core/dist/runtime-paths.js.map +1 -1
  343. package/packages/core/dist/sdd-docs/context.js +1 -1
  344. package/packages/core/dist/sdd-docs/context.js.map +1 -1
  345. package/packages/core/dist/sdd-docs/document-hashes.d.ts +4 -0
  346. package/packages/core/dist/sdd-docs/document-hashes.js +189 -0
  347. package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -0
  348. package/packages/core/dist/sdd-docs/run-binding.js +12 -3
  349. package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
  350. package/packages/core/dist/sdd-docs/task-parser.d.ts +20 -0
  351. package/packages/core/dist/sdd-docs/task-parser.js +101 -21
  352. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  353. package/packages/core/dist/status/project-status.d.ts +62 -1
  354. package/packages/core/dist/status/project-status.js +192 -4
  355. package/packages/core/dist/status/project-status.js.map +1 -1
  356. package/packages/core/dist/storage/runtime-store.d.ts +195 -2
  357. package/packages/core/dist/storage/runtime-store.js +499 -2
  358. package/packages/core/dist/storage/runtime-store.js.map +1 -1
  359. package/packages/core/dist/sync-back/apply.js +23 -1
  360. package/packages/core/dist/sync-back/apply.js.map +1 -1
  361. package/packages/core/dist/sync-back/inspect.d.ts +19 -0
  362. package/packages/core/dist/sync-back/inspect.js +114 -12
  363. package/packages/core/dist/sync-back/inspect.js.map +1 -1
  364. package/packages/core/dist/test-support/fixtures.js +21 -0
  365. package/packages/core/dist/test-support/fixtures.js.map +1 -1
  366. package/packages/core/dist/test-support/run-state.js +16 -2
  367. package/packages/core/dist/test-support/run-state.js.map +1 -1
  368. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  369. package/packages/core/dist/verification/goal-verify.js +34 -2
  370. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  371. package/packages/core/dist/verification/rendering.js +15 -5
  372. package/packages/core/dist/verification/rendering.js.map +1 -1
  373. package/packages/core/dist/verification/review-gate.d.ts +22 -0
  374. package/packages/core/dist/verification/review-gate.js +53 -0
  375. package/packages/core/dist/verification/review-gate.js.map +1 -0
  376. package/packages/core/dist/verification/single-task-loop.js +102 -9
  377. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  378. package/packages/core/dist/verification/test-runtime.d.ts +16 -1
  379. package/packages/core/dist/verification/test-runtime.js +355 -69
  380. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  381. package/packages/core/dist/verification/validation-wave.d.ts +58 -0
  382. package/packages/core/dist/verification/validation-wave.js +428 -0
  383. package/packages/core/dist/verification/validation-wave.js.map +1 -0
  384. package/packages/core/dist/verification/verify-contract.d.ts +2 -0
  385. package/packages/core/dist/verification/verify-contract.js +116 -18
  386. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  387. package/packages/core/dist/verification.d.ts +2 -0
  388. package/packages/core/dist/verification.js +2 -0
  389. package/packages/core/dist/verification.js.map +1 -1
  390. package/packages/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
  391. package/packages/core/dist/workflow-gate/evidence-packet.js +182 -0
  392. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -0
  393. package/packages/core/dist/workflow-gate/hard-checks.d.ts +4 -0
  394. package/packages/core/dist/workflow-gate/hard-checks.js +130 -0
  395. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -0
  396. package/packages/core/dist/workflow-gate/policy.d.ts +4 -0
  397. package/packages/core/dist/workflow-gate/policy.js +146 -0
  398. package/packages/core/dist/workflow-gate/policy.js.map +1 -0
  399. package/packages/core/dist/workflow-gate/types.d.ts +89 -0
  400. package/packages/core/dist/workflow-gate/types.js +2 -0
  401. package/packages/core/dist/workflow-gate/types.js.map +1 -0
  402. package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  403. package/packages/core/dist/workflow-state/affected-file-conflicts.js +16 -1
  404. package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  405. package/packages/core/dist/workflow-state/dependencies.d.ts +8 -4
  406. package/packages/core/dist/workflow-state/dependencies.js +25 -11
  407. package/packages/core/dist/workflow-state/dependencies.js.map +1 -1
  408. package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +38 -0
  409. package/packages/core/dist/workflow-state/latest-eligible-run.js +122 -0
  410. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
  411. package/packages/core/dist/workflow-state/resolve.d.ts +27 -0
  412. package/packages/core/dist/workflow-state/resolve.js +166 -37
  413. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  414. package/packages/core/dist/workflow-state.d.ts +1 -0
  415. package/packages/core/dist/workflow-state.js +1 -0
  416. package/packages/core/dist/workflow-state.js.map +1 -1
  417. package/packages/core/package.json +1 -1
@@ -9,7 +9,7 @@ import {
9
9
  import { artifactKind, readArtifact, writeArtifact } from '../run-state/artifacts.js';
10
10
  import { appendEvent } from '../run-state/events.js';
11
11
  import { appendArtifactHashLedgerEntry, appendDeclaredCommandLedgerEntries, appendInvocationLedgerEntry, listInvocationLedgerEntries } from '../run-state/invocation-ledger.js';
12
- import type { InvocationLedgerEntry, InvocationLedgerKind, RunState } from '../run-state/model.js';
12
+ import type { InvocationLedgerEntry, InvocationLedgerKind, RunState, RunStateTaskRuntime } from '../run-state/model.js';
13
13
  import { readRunState, writeRunState } from '../run-state/run-state.js';
14
14
  import { toArtifactRootRelativePath } from '../runtime-paths.js';
15
15
  import { hasRuntimeEvidenceScopeViolation, readRuntimeEvidenceClaims } from '../context/evidence-summary.js';
@@ -20,6 +20,7 @@ import { taskGap } from '../sdd-docs/task-inspection.js';
20
20
  import { inspectSddTask } from '../sdd-docs/task-inspection.js';
21
21
  import type { SddTask, SddTaskGap } from '../sdd-docs/task-parser.js';
22
22
  import { resolveTaskRun } from '../sync-back/inspect.js';
23
+ import { recordRuntimeSyncBackDecision, runtimeScopedId } from '../storage/runtime-store.js';
23
24
 
24
25
  export type GoalVerifyStatus = 'PASS' | 'PASS_WITH_GAPS' | 'FAIL' | 'BLOCKED';
25
26
  export type HarnessVerifyStatus = 'PASS' | 'GAPS' | 'BLOCKED' | 'HUMAN_NEEDED';
@@ -204,13 +205,28 @@ export async function runGoalVerify(projectRoot: string, options: GoalVerifyOpti
204
205
  await persistVerifyState(projectRoot, runId, {
205
206
  status,
206
207
  taskId: options.taskId,
207
- taskState: { status: status === 'PASS' ? 'verified' : 'blocked', verifyStatus: status, gaps, artifacts: allArtifacts, acceptanceCoverage },
208
+ taskState: buildGoalVerifyTaskState(inspected.task, status, gaps, allArtifacts, acceptanceCoverage),
208
209
  commands: plannedCommands,
209
210
  evidence: allArtifacts,
210
211
  syncBackProposalPath: proposal.runRelativePath,
211
212
  syncBackProposalDigest: proposal.digest,
212
213
  artifacts: allArtifacts.map((artifactPath) => ({ path: artifactPath, kind: artifactKind(artifactPath), task: options.taskId, agent: agentFromArtifactPath(artifactPath) }))
213
214
  });
215
+ const decisionTime = new Date().toISOString();
216
+ await recordRuntimeSyncBackDecision(projectRoot, {
217
+ decisionId: runtimeScopedId(runId, options.taskId, 'sync-back'),
218
+ runId,
219
+ branch,
220
+ taskId: options.taskId,
221
+ status: 'proposed',
222
+ proposalPath: proposal.runRelativePath,
223
+ proposalDigest: proposal.digest,
224
+ proposalPayloadId: proposal.payloadId,
225
+ reasons: gaps.map((gap) => `${gap.severity} ${gap.type} ${gap.field}: ${gap.message}`),
226
+ createdAt: decisionTime,
227
+ updatedAt: decisionTime,
228
+ payload: { sourceVerifyStatus: status, standardStatus, artifacts: allArtifacts }
229
+ });
214
230
  await appendEvent(projectRoot, runId, {
215
231
  event: status === 'PASS' ? 'validation_passed' : 'validation_failed',
216
232
  runId,
@@ -243,10 +259,26 @@ export async function runGoalVerify(projectRoot: string, options: GoalVerifyOpti
243
259
  };
244
260
  }
245
261
 
262
+ function buildGoalVerifyTaskState(task: SddTask | null, status: GoalVerifyStatus, gaps: SddTaskGap[], artifacts: string[], acceptanceCoverage: unknown[]): RunStateTaskRuntime {
263
+ const verificationStatus = status === 'PASS' ? 'pass' : status === 'PASS_WITH_GAPS' ? 'pass_with_gaps' : status === 'FAIL' ? 'failed' : 'blocked';
264
+ return {
265
+ status: status === 'PASS' ? 'implemented_verified' : verificationStatus === 'pass_with_gaps' ? 'validation_blocked' : verificationStatus === 'failed' ? 'validation_failed' : 'validation_blocked',
266
+ implementationStatus: 'implemented',
267
+ verificationStatus,
268
+ validationBatch: task?.validationBatch ?? null,
269
+ validationTiming: task?.validationTiming ?? 'task_end',
270
+ requiresVerifyBeforeNext: task?.requiresVerifyBeforeNext ?? true,
271
+ verifyStatus: status,
272
+ gaps,
273
+ artifacts,
274
+ acceptanceCoverage
275
+ };
276
+ }
277
+
246
278
  async function persistVerifyState(projectRoot: string, runId: string, input: {
247
279
  status: GoalVerifyStatus;
248
280
  taskId: string;
249
- taskState: unknown;
281
+ taskState: RunStateTaskRuntime;
250
282
  commands: string[];
251
283
  evidence: string[];
252
284
  syncBackProposalPath: string;
@@ -284,10 +316,11 @@ async function persistVerifyState(projectRoot: string, runId: string, input: {
284
316
  });
285
317
  }
286
318
 
287
- async function writeSyncBackProposal(projectRoot: string, runId: string, taskId: string, status: string, artifacts: string[], gaps: SddTaskGap[], summary: string): Promise<{ absolutePath: string; runRelativePath: string; digest: string }> {
319
+ async function writeSyncBackProposal(projectRoot: string, runId: string, taskId: string, status: string, artifacts: string[], gaps: SddTaskGap[], summary: string): Promise<{ absolutePath: string; runRelativePath: string; digest: string; payloadId: string }> {
288
320
  const content = `# Sync-back Proposal\n\n## ${taskId}\n\n- status: ${status}\n- summary: ${summary}\n- artifacts:\n${artifacts.length > 0 ? artifacts.map((artifact) => ` - ${artifact}`).join('\n') : ' - none'}\n- gaps:\n${gaps.length > 0 ? gaps.map((gap) => ` - [${gap.severity}] ${gap.type} ${gap.field}: ${gap.message}`).join('\n') : ' - none'}\n\n## Boundaries\n\n- Proposal only; tasks.md/spec.md/plan.md were not modified by runtime.\n- Runtime modeled agent/verify steps through supplied artifacts and contract validation; no external agent API was invoked.\n`;
321
+ const digest = hashDocumentContent(content);
289
322
  const written = await writeArtifact(projectRoot, runId, 'sync-back-proposal.md', content);
290
- return { ...written, digest: hashDocumentContent(content) };
323
+ return { ...written, digest, payloadId: runtimeScopedId(runId, written.runRelativePath, digest) };
291
324
  }
292
325
 
293
326
  function renderAcceptanceCoverageArtifact(taskId: string, status: GoalVerifyStatus, task: SddTask | null, reviewArtifact: string | null, validationArtifact: string | null, coverage: AcceptanceCoverageItem[], gaps: SddTaskGap[], executedCommands: string[]): string {
@@ -57,7 +57,7 @@ export interface SingleTaskLoopResultLike {
57
57
  export function renderGoalVerifyResult(result: GoalVerifyResultLike): string {
58
58
  const lines = ['SDD verify task result', 'changed'];
59
59
  lines.push(`- acceptance coverage written to ${result.coverageArtifactPath}`);
60
- lines.push(`- sync-back proposal written to ${result.syncBackProposalPath}`);
60
+ lines.push(`- compatibility diagnostic proposal=${result.syncBackProposalPath || 'none'}`);
61
61
  lines.push('decision');
62
62
  lines.push(`- status=${result.status}`);
63
63
  lines.push(`- standard_status=${result.standardStatus}`);
@@ -85,7 +85,8 @@ export function renderGoalVerifyResult(result: GoalVerifyResultLike): string {
85
85
  }
86
86
  lines.push('next');
87
87
  if (result.status === 'PASS') {
88
- lines.push(`- sdd sync-back inspect ${result.runId} --task ${result.taskId}`);
88
+ lines.push(`- sdd test task ${result.taskId} --run ${result.runId}`);
89
+ lines.push('- then follow the test gate result toward ship readiness; use sync-back only for explicit diagnostic/recovery/replay.');
89
90
  } else {
90
91
  lines.push(`- update review/validator artifacts and rerun sdd test task ${result.taskId} --run ${result.runId}`);
91
92
  }
@@ -108,8 +109,8 @@ export function renderSingleTaskLoopResult(result: SingleTaskLoopResultLike): st
108
109
  lines.push(`- required_artifacts=${result.requiredArtifacts.join(',') || 'none'}`);
109
110
  lines.push(`- accepted_artifacts=${result.acceptedArtifacts.join(',') || 'none'}`);
110
111
  lines.push(`- sync_back_proposal=${result.syncBackProposalPath || 'none'}`);
111
- lines.push(`- agent_execution_records=.sdd/runs/${result.runId}/agent-executions/`);
112
- lines.push(`- team_session_records=.sdd/runs/${result.runId}/team-sessions/`);
112
+ lines.push('- agent_execution_records=runtime.sqlite:agent_executions');
113
+ lines.push('- team_session_records=runtime.sqlite:team_sessions');
113
114
  lines.push('gaps');
114
115
  if (result.gaps.length === 0) {
115
116
  lines.push('- none');
@@ -118,7 +119,12 @@ export function renderSingleTaskLoopResult(result: SingleTaskLoopResultLike): st
118
119
  }
119
120
  lines.push('next');
120
121
  if (result.status === 'completed') {
121
- lines.push(`- sdd test task ${result.taskId} --run ${result.runId}`);
122
+ if (result.task && !requiresImmediateValidation(result.task) && result.task.validationBatch) {
123
+ lines.push(`- sdd status --branch <branch> to select the next safe do task or validation boundary`);
124
+ lines.push(`- after all tasks in validation_batch ${result.task.validationBatch} are implemented, run sdd test batch ${result.task.validationBatch}`);
125
+ } else {
126
+ lines.push(`- sdd test task ${result.taskId} --run ${result.runId}`);
127
+ }
122
128
  } else {
123
129
  const missingArtifacts = result.requiredArtifacts.filter((artifact) => !result.acceptedArtifacts.includes(artifact));
124
130
  if (missingArtifacts.length > 0) {
@@ -137,6 +143,10 @@ export function renderSingleTaskLoopResult(result: SingleTaskLoopResultLike): st
137
143
  return lines.join('\n');
138
144
  }
139
145
 
146
+ function requiresImmediateValidation(task: SddTask): boolean {
147
+ return task.requiresVerifyBeforeNext || task.validationTiming === 'task_end';
148
+ }
149
+
140
150
  function agentForLoopArtifact(artifactPath: string): LoopAgent | null {
141
151
  const filename = artifactPath.replace(/\\/g, '/').split('/').pop() ?? '';
142
152
  if (filename.startsWith('implement-')) {
@@ -0,0 +1,77 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, rm } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import path from 'node:path';
6
+
7
+ import { initProject } from '../config/init-project.js';
8
+ import { listRuntimeWorkflowGateDecisions } from '../storage/runtime-store.js';
9
+ import { writeBranchDocs } from '../test-support/fixtures.js';
10
+ import { writeVerifyContract } from './verify-contract.js';
11
+ import { runValidationWave } from './validation-wave.js';
12
+ import { renderReviewGateResult, runReviewGate } from './review-gate.js';
13
+
14
+ function taskMarkdown(taskId: string, command: string, wave: number): string {
15
+ return `### ${taskId}: Review gate task
16
+
17
+ \`\`\`sdd-task
18
+ id: ${taskId}
19
+ status: pending
20
+ wave: ${wave}
21
+ depends_on: []
22
+ acceptance_refs:
23
+ - AC-${taskId}
24
+ plan_refs:
25
+ - "§4 Target Design Overview"
26
+ affected_files:
27
+ - docs/${taskId.toLowerCase()}.md
28
+ validation:
29
+ - ${command} => AC-${taskId}
30
+ risk: []
31
+ \`\`\`
32
+
33
+ #### Boundary
34
+
35
+ Stay in review gate fixtures.
36
+
37
+ #### Acceptance
38
+
39
+ - AC-${taskId}: Review gate evidence is mapped.
40
+ `;
41
+ }
42
+
43
+ test('review gate records a workflow gate decision with whole-wave scope', async () => {
44
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-review-gate-'));
45
+ try {
46
+ await initProject(root);
47
+ await writeBranchDocs(root, 'feature', `# Tasks
48
+
49
+ ${taskMarkdown('T1', 'node -e "process.stdout.write(\'one\')"', 1)}
50
+ ${taskMarkdown('T2', 'node -e "process.stdout.write(\'two\')"', 1)}`);
51
+ await writeVerifyContract(root, { branch: 'feature', branchSource: 'cli_option' });
52
+
53
+ const wave = await runValidationWave(root, { branch: 'feature', wave: 1, approved: true });
54
+ const expectedEvidenceRefs = wave.taskResults.flatMap((taskResult) => [taskResult.validationArtifact, taskResult.indexArtifact].filter((ref): ref is string => Boolean(ref))).sort();
55
+ const result = await runReviewGate(root, {
56
+ branch: 'feature',
57
+ taskId: 'T1',
58
+ runId: wave.taskResults[0].runId,
59
+ waveRunId: wave.waveRunId,
60
+ taskIds: wave.plan.taskIds,
61
+ runIds: wave.taskResults.map((taskResult) => taskResult.runId),
62
+ evidenceRefs: expectedEvidenceRefs
63
+ });
64
+ const decisions = await listRuntimeWorkflowGateDecisions(root, { partition: 'feature', decisionKind: 'review_gate' });
65
+ const workflowGateScope = (result.workflowGateDecision.payload as { workflowScope?: { waveRunId: string | null; taskIds: string[]; runIds: string[]; evidenceRefs: string[] } }).workflowScope;
66
+
67
+ assert.equal(result.status, 'PASS');
68
+ assert.equal(decisions.some((decision) => decision.decisionId === result.workflowGateDecision.decisionId), true);
69
+ assert.equal(workflowGateScope?.waveRunId, wave.waveRunId);
70
+ assert.deepEqual(workflowGateScope?.taskIds, ['T1', 'T2']);
71
+ assert.deepEqual(workflowGateScope?.runIds, wave.taskResults.map((taskResult) => taskResult.runId).sort());
72
+ assert.deepEqual(workflowGateScope?.evidenceRefs, expectedEvidenceRefs);
73
+ assert.match(renderReviewGateResult(result), /workflow_gate=PASS:/);
74
+ } finally {
75
+ await rm(root, { recursive: true, force: true });
76
+ }
77
+ });
@@ -0,0 +1,77 @@
1
+ import { evaluateAndRecordWorkflowGateDecision } from '../workflow-gate/evidence-packet.js';
2
+ import type { WorkflowGateDecision, WorkflowGateStatus } from '../workflow-gate/types.js';
3
+
4
+ export const REVIEW_GATE_RUNTIME_CONTRACT_VERSION = 'phase-8.17-review-gate-runtime-v1';
5
+
6
+ export interface RunReviewGateOptions {
7
+ branch?: string | null;
8
+ taskId?: string | null;
9
+ runId?: string | null;
10
+ waveRunId?: string | null;
11
+ taskIds?: string[];
12
+ runIds?: string[];
13
+ evidenceRefs?: string[];
14
+ }
15
+
16
+ export interface ReviewGateRunResult {
17
+ contract: typeof REVIEW_GATE_RUNTIME_CONTRACT_VERSION;
18
+ branch: string;
19
+ taskId: string | null;
20
+ runId: string | null;
21
+ status: WorkflowGateStatus;
22
+ workflowGateDecision: WorkflowGateDecision;
23
+ next: string;
24
+ }
25
+
26
+ export async function runReviewGate(projectRoot: string, options: RunReviewGateOptions = {}): Promise<ReviewGateRunResult> {
27
+ const { decision } = await evaluateAndRecordWorkflowGateDecision(projectRoot, {
28
+ branch: options.branch,
29
+ taskId: options.taskId,
30
+ runId: options.runId,
31
+ decisionKind: 'review_gate',
32
+ waveRunId: options.waveRunId,
33
+ taskIds: options.taskIds,
34
+ runIds: options.runIds,
35
+ evidenceRefs: options.evidenceRefs
36
+ });
37
+ return {
38
+ contract: REVIEW_GATE_RUNTIME_CONTRACT_VERSION,
39
+ branch: decision.partition,
40
+ taskId: decision.taskId,
41
+ runId: decision.runId,
42
+ status: decision.status,
43
+ workflowGateDecision: decision,
44
+ next: nextForReviewGate(decision)
45
+ };
46
+ }
47
+
48
+ export function renderReviewGateResult(result: ReviewGateRunResult): string {
49
+ return [
50
+ 'SDD review gate',
51
+ '',
52
+ `status=${result.status}`,
53
+ `branch=${result.branch}`,
54
+ `task=${result.taskId ?? 'none'}`,
55
+ `run=${result.runId ?? 'none'}`,
56
+ `workflow_gate=${result.workflowGateDecision.status}:${result.workflowGateDecision.decisionId}`,
57
+ '',
58
+ 'Next:',
59
+ `- ${result.next}`
60
+ ].join('\n');
61
+ }
62
+
63
+ function nextForReviewGate(decision: WorkflowGateDecision): string {
64
+ if (decision.status === 'PASS') {
65
+ return `Continue workflow after review gate ${decision.decisionId}.`;
66
+ }
67
+ if (decision.status === 'WARN') {
68
+ return `Inspect review gate ${decision.decisionId} warnings before continuing.`;
69
+ }
70
+ if (decision.status === 'HUMAN_REQUIRED') {
71
+ return `Create a decision card for review gate ${decision.decisionId}.`;
72
+ }
73
+ if (decision.status === 'ADVISORY_ONLY') {
74
+ return `Inspect advisor assessments for review gate ${decision.decisionId}; do not treat advisory output as pass.`;
75
+ }
76
+ return `Resolve review gate ${decision.decisionId} blockers before continuing.`;
77
+ }
@@ -12,10 +12,13 @@ import { getProjectStatus } from '../status/project-status.js';
12
12
  import { doctor } from '../doctor/doctor.js';
13
13
  import { readArtifact, writeArtifact } from '../run-state/artifacts.js';
14
14
  import { inspectRun } from '../run-state/inspect-run.js';
15
+ import { readTaskEvidenceView } from '../run-state/task-evidence.js';
15
16
  import { rebuildLocalRunIndex } from '../run-state/run-index.js';
16
17
  import { readRunEvents } from '../run-state/events.js';
17
18
  import { createRun, readRunState } from '../run-state/run-state.js';
19
+ import { recordRuntimeArtifactPayload, runtimeScopedId } from '../storage/runtime-store.js';
18
20
  import { validResultArtifact, validTaskMarkdown, validTrustEvidence, writeBranchDocs } from '../test-support/fixtures.js';
21
+ import { writeVerifyContract } from './verify-contract.js';
19
22
  import { bindTestRunState } from '../test-support/run-state.js';
20
23
  import { renderSingleTaskLoopResult } from './rendering.js';
21
24
  import { runSingleTaskLoop } from './single-task-loop.js';
@@ -32,6 +35,7 @@ test('runSingleTaskLoop completes from supplied review and validation artifacts
32
35
  await initProject(root);
33
36
  await execFileAsync('git', ['init'], { cwd: root });
34
37
  await writeBranchDocs(root, 'feature', validTaskMarkdown('T1', []));
38
+ await writeVerifyContract(root, { branch: 'feature', branchSource: 'cli_option' });
35
39
  const state = await createRun(root, { runId: 'run-1' });
36
40
  await bindTestRunState(root, state.runId, 'feature', 'T1');
37
41
  await writeArtifact(root, state.runId, 'review-T1.md', validResultArtifact('reviewer', 'T1', 'PASS', 'artifacts/review-T1.md'));
@@ -50,6 +54,21 @@ test('runSingleTaskLoop completes from supplied review and validation artifacts
50
54
  const doctorReport = await doctor(root, { latestOnly: true });
51
55
  const events = stringifyEvents(await readRunEvents(root, state.runId));
52
56
  const tasksMarkdown = await readFile(path.join(root, 'specs', 'feature', 'tasks.md'), 'utf8');
57
+ await execFileAsync('git', ['checkout', '-b', 'feature'], { cwd: root });
58
+ await recordRuntimeArtifactPayload(root, {
59
+ payloadId: runtimeScopedId(state.runId, 'artifacts/validation-T2.md', 'foreign-task'),
60
+ runId: state.runId,
61
+ sourceRunId: state.runId,
62
+ branchSlug: 'feature',
63
+ taskId: 'T2',
64
+ logicalRef: 'artifacts/validation-T2.md',
65
+ physicalPayloadPath: 'artifacts/validation-T2-run-1-foreign-task.md',
66
+ artifactRole: 'validation',
67
+ digest: 'foreign-task',
68
+ status: 'active',
69
+ payload: { logicalRef: 'artifacts/validation-T2.md' }
70
+ });
71
+ const taskEvidence = await readTaskEvidenceView(root, { branch: 'feature', taskId: 'T1' });
53
72
 
54
73
  assert.equal(result.status, 'completed');
55
74
  assert.deepEqual(result.acceptedArtifacts, ['artifacts/review-T1.md', 'artifacts/validation-T1.md']);
@@ -59,7 +78,7 @@ test('runSingleTaskLoop completes from supplied review and validation artifacts
59
78
  assert.equal(restored.syncBack.proposalPath, 'artifacts/sync-back-proposal.md');
60
79
  assert.equal(ingestionInspection.valid, true);
61
80
  assert.deepEqual(ingestionInspection.records.map((record) => record.delegationId), ['B-T1-reviewer-001', 'B-T1-validator-001']);
62
- assert.equal(doctorReport.status, 'WARN');
81
+ assert.equal(doctorReport.status, 'FAIL');
63
82
  assert.match(events, /task_selected/);
64
83
  assert.match(events, /review_passed/);
65
84
  assert.match(events, /validation_passed/);
@@ -67,6 +86,45 @@ test('runSingleTaskLoop completes from supplied review and validation artifacts
67
86
  assert.match(events, /delegation_completed/);
68
87
  assert.doesNotMatch(events, /agent_completed/);
69
88
  assert.match(tasksMarkdown, /status: pending/);
89
+ assert.equal(taskEvidence.latestEligibleRun?.runId, state.runId);
90
+ assert.equal(taskEvidence.compatibility, 'compatible');
91
+ assert.equal(taskEvidence.payloads.active.some((payload) => payload.logicalRef === 'artifacts/sync-back-proposal.md'), true);
92
+ assert.equal(taskEvidence.payloads.all.some((payload) => payload.taskId === 'T2'), false);
93
+ assert.equal(taskEvidence.validation.runValidation?.status, 'pass');
94
+ assert.equal(taskEvidence.syncBackDecision?.status, 'proposed');
95
+ } finally {
96
+ await rm(root, { recursive: true, force: true });
97
+ }
98
+ });
99
+
100
+ test('runSingleTaskLoop blocks rejected validation ingestion without accepting the artifact', async () => {
101
+ const root = await mkdtemp(path.join(tmpdir(), 'sdd-loop-rejected-validation-'));
102
+ try {
103
+ await initProject(root);
104
+ await writeBranchDocs(root, 'feature', validTaskMarkdown('T1', []));
105
+ const state = await createRun(root, { runId: 'run-1' });
106
+ await bindTestRunState(root, state.runId, 'feature', 'T1');
107
+ await writeArtifact(root, state.runId, 'review-T1.md', validResultArtifact('reviewer', 'T1', 'PASS', 'artifacts/review-T1.md'));
108
+ await writeArtifact(root, state.runId, 'validation-T1.md', validResultArtifact('validator', 'T1', 'PASS', 'artifacts/validation-T1.md'));
109
+
110
+ const result = await runSingleTaskLoop(root, {
111
+ runId: state.runId,
112
+ branch: 'feature',
113
+ taskId: 'T1',
114
+ reviewArtifact: 'artifacts/review-T1.md',
115
+ validationArtifact: 'artifacts/validation-T1.md'
116
+ });
117
+ const restored = await readRunState(root, state.runId);
118
+ const ingestionInspection = await inspectArtifactResultIngestions(root, state.runId);
119
+
120
+ assert.equal(result.status, 'blocked');
121
+ assert.equal(restored.status, 'blocked');
122
+ assert.equal(restored.validation.status, 'blocked');
123
+ assert.deepEqual(result.acceptedArtifacts, ['artifacts/review-T1.md', 'artifacts/gap-report-T1.md']);
124
+ assert.equal(restored.artifacts.some((artifact) => artifact.path === 'artifacts/validation-T1.md'), false);
125
+ assert.equal(ingestionInspection.valid, true);
126
+ assert.equal(ingestionInspection.records.find((record) => record.artifactPath === 'artifacts/validation-T1.md')?.status, 'rejected');
127
+ assert.equal(result.gaps.some((gap) => /UNSOURCED_PASS/.test(gap.message)), true);
70
128
  } finally {
71
129
  await rm(root, { recursive: true, force: true });
72
130
  }
@@ -104,6 +162,7 @@ test('runSingleTaskLoop blocks on missing reviewer artifact and creates gap prop
104
162
  assert.match(rendered, /physical artifact files belong under branch evidence \.sdd\/runs\/<branchSlug>\/evidence\/artifacts\//);
105
163
  assert.match(rendered, /artifact_path_scope=CLI flags use run-relative artifacts\/<file>/);
106
164
  assert.equal(restored.status, 'blocked');
165
+ assert.match(restored.artifactRoot, /\.sdd[\\/]runs[\\/]feature[\\/]evidence/);
107
166
  assert.match(gapReport, /reviewer artifact was not supplied/);
108
167
  assert.match(proposal, /Proposal only/);
109
168
  const events = stringifyEvents(await readRunEvents(root, state.runId));
@@ -185,9 +244,10 @@ test('runSingleTaskLoop persists agent and team evidence records', async () => {
185
244
  assert.equal(teamSession.teamMode.costClass, 'low');
186
245
  assert.match(teamSession.teamMode.reason, /review|validation/i);
187
246
  assert.equal(inspection.taskRunEvidence.teamSessions.length, inspection.teamSessions.length);
188
- assert.equal(status.latestRunEvidence?.routePreflight, true);
189
- assert.equal(status.latestRunEvidence?.agentExecutions, 2);
190
- assert.equal(status.latestRunEvidence?.teamSessions, 1);
247
+ const rejectedRun = status.latestEligibleRunsByTask.flatMap((selection) => selection.rejected).find((candidate) => candidate.runId === result.runId);
248
+ assert.equal(rejectedRun?.reasons.includes('Run is blocked.'), true);
249
+ assert.equal(inspection.taskRunEvidence.agentExecutions.length, 2);
250
+ assert.equal(inspection.taskRunEvidence.teamSessions.length, 1);
191
251
  assert.equal(report.checks.some((check) => check.check === 'agent_team_execution_records' && /agent execution record/.test(check.message)), true);
192
252
  } finally {
193
253
  await rm(root, { recursive: true, force: true });
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { SDD_RESULT_CONTRACT, SDD_RESULT_VERSION } from '../contracts.js';
3
3
  import { artifactKind, writeArtifact } from '../run-state/artifacts.js';
4
4
  import { appendEvent } from '../run-state/events.js';
5
- import type { RunState } from '../run-state/model.js';
5
+ import type { RunState, RunStateTaskRuntime } from '../run-state/model.js';
6
6
  import { createRun, readRunState, writeRunState } from '../run-state/run-state.js';
7
7
  import { resolveSddContext } from '../sdd-docs/context.js';
8
8
  import { bindRunStateToTask } from '../sdd-docs/run-binding.js';
@@ -13,6 +13,7 @@ import type { TeamModeActivation } from '../router/route-cache.js';
13
13
  import { routeSddTask } from '../router/route-sdd-task.js';
14
14
  import { buildAgentExecutionRecord, buildTeamSessionRecord, writeAgentExecutionRecord, writeTeamSessionRecord } from '../execution/agent-execution-records.js';
15
15
  import { runBackgroundExecutor } from '../execution/background-executor.js';
16
+ import { recordRuntimeSyncBackDecision, runtimeScopedId } from '../storage/runtime-store.js';
16
17
 
17
18
  export type SingleTaskLoopStatus = 'completed' | 'blocked' | 'failed';
18
19
 
@@ -92,12 +93,23 @@ export async function runSingleTaskLoop(projectRoot: string, options: SingleTask
92
93
  status: 'blocked',
93
94
  phase: 'do',
94
95
  taskId: options.taskId,
95
- taskState: { status: 'blocked', gaps: allGaps, artifacts: [gapArtifact.runRelativePath] },
96
+ taskState: buildRuntimeTaskState(inspected.task, 'blocked', 'blocked', allGaps, [gapArtifact.runRelativePath]),
96
97
  validationStatus: 'blocked',
97
98
  syncBackProposalPath: proposal.runRelativePath,
98
99
  syncBackProposalDigest: proposal.digest,
99
100
  artifacts: [{ path: gapArtifact.runRelativePath, kind: 'gap-report', task: options.taskId, agent: 'runtime' }]
100
101
  });
102
+ await recordLoopSyncBackDecision(projectRoot, {
103
+ runId,
104
+ branch,
105
+ taskId: options.taskId,
106
+ status: 'proposed',
107
+ proposalPath: proposal.runRelativePath,
108
+ proposalDigest: proposal.digest,
109
+ proposalPayloadId: proposal.payloadId,
110
+ reasons: allGaps.map((gap) => `${gap.severity} ${gap.type} ${gap.field}: ${gap.message}`),
111
+ payload: { sourceVerifyStatus: 'blocked', artifacts: [gapArtifact.runRelativePath] }
112
+ });
101
113
  await appendEvent(projectRoot, runId, {
102
114
  event: 'gap_detected',
103
115
  runId,
@@ -135,11 +147,11 @@ export async function runSingleTaskLoop(projectRoot: string, options: SingleTask
135
147
  data: { task: options.taskId, title: inspected.task.title, source: inspected.task.source }
136
148
  });
137
149
 
138
- const steps = buildLoopSteps(options.taskId, options);
150
+ const steps = buildLoopSteps(options.taskId, options, inspected.task);
139
151
  const acceptedArtifacts: string[] = [];
140
152
  const gaps: SddTaskGap[] = [];
141
153
  let terminalStatus: SingleTaskLoopStatus = 'completed';
142
- let validationStatus: RunState['validation']['status'] = 'pass';
154
+ let validationStatus: RunState['validation']['status'] = 'not_run';
143
155
 
144
156
  for (const step of steps) {
145
157
  const stepRouteDecision = await routeSddTask(projectRoot, { taskId: options.taskId, branch, agent: step.agent, teamModeEnabled: options.teamModeEnabled, teamModeActivation: options.teamModeActivation, approved: options.approved });
@@ -194,7 +206,7 @@ export async function runSingleTaskLoop(projectRoot: string, options: SingleTask
194
206
  approved: options.approved
195
207
  });
196
208
 
197
- if (!result.ingestion || !result.ingestion.resultStatus) {
209
+ if (!result.ingestion || result.ingestion.status !== 'accepted' || !result.ingestion.resultStatus) {
198
210
  const issueText = result.issues.map((issue) => issue.message).join('; ') || result.message;
199
211
  const recommendation = issueText.includes('manual isolation gate') || issueText.includes('requires confirmation')
200
212
  ? 'Resolve the manual isolation or approval gate for this high-risk task before ingesting execution artifacts.'
@@ -250,17 +262,30 @@ export async function runSingleTaskLoop(projectRoot: string, options: SingleTask
250
262
  });
251
263
  }
252
264
 
253
- const proposal = await writeSyncBackProposal(projectRoot, runId, options.taskId, terminalStatus === 'completed' ? 'completed' : terminalStatus, acceptedArtifacts, gaps, terminalStatus === 'completed' ? 'Ingestion-aware task loop has accepted required artifacts through the Phase 3 executor.' : terminalStatus === 'blocked' && validationStatus === 'pass_with_gaps' ? 'Ingestion-aware task loop stopped because validator returned PASS_WITH_GAPS; sync-back is a blocked gap proposal, not task completion.' : 'Ingestion-aware task loop stopped with blocking/failing evidence.');
265
+ const taskState = buildRuntimeTaskState(inspected.task, terminalStatus, validationStatus, gaps, acceptedArtifacts);
266
+ const proposalStatus = taskState.status === 'implemented_pending_validation' ? taskState.status : terminalStatus === 'completed' ? 'completed' : terminalStatus;
267
+ const proposal = await writeSyncBackProposal(projectRoot, runId, options.taskId, proposalStatus, acceptedArtifacts, gaps, terminalStatus === 'completed' ? 'Ingestion-aware task loop accepted implementation evidence; validation may still be pending by task contract.' : terminalStatus === 'blocked' && validationStatus === 'pass_with_gaps' ? 'Ingestion-aware task loop stopped because validator returned PASS_WITH_GAPS; sync-back is a blocked gap proposal, not task completion.' : 'Ingestion-aware task loop stopped with blocking/failing evidence.');
254
268
  await persistLoopState(projectRoot, runId, {
255
269
  status: terminalStatus,
256
270
  phase: 'do',
257
271
  taskId: options.taskId,
258
- taskState: { status: terminalStatus, gaps, artifacts: acceptedArtifacts },
272
+ taskState,
259
273
  validationStatus,
260
274
  syncBackProposalPath: proposal.runRelativePath,
261
275
  syncBackProposalDigest: proposal.digest,
262
276
  artifacts: acceptedArtifacts.map((artifactPath) => ({ path: artifactPath, kind: artifactKind(artifactPath), task: options.taskId, agent: agentFromArtifactPath(artifactPath) }))
263
277
  });
278
+ await recordLoopSyncBackDecision(projectRoot, {
279
+ runId,
280
+ branch,
281
+ taskId: options.taskId,
282
+ status: 'proposed',
283
+ proposalPath: proposal.runRelativePath,
284
+ proposalDigest: proposal.digest,
285
+ proposalPayloadId: proposal.payloadId,
286
+ reasons: gaps.map((gap) => `${gap.severity} ${gap.type} ${gap.field}: ${gap.message}`),
287
+ payload: { sourceVerifyStatus: terminalStatus, artifacts: acceptedArtifacts }
288
+ });
264
289
  await appendEvent(projectRoot, runId, {
265
290
  event: 'sync_back_proposed',
266
291
  runId,
@@ -298,7 +323,7 @@ export async function runSingleTaskLoop(projectRoot: string, options: SingleTask
298
323
  };
299
324
  }
300
325
 
301
- function buildLoopSteps(taskId: string, options: SingleTaskLoopOptions): LoopAgentStep[] {
326
+ function buildLoopSteps(taskId: string, options: SingleTaskLoopOptions, task: SddTask | null): LoopAgentStep[] {
302
327
  const steps: LoopAgentStep[] = [
303
328
  { agent: 'implementer', suppliedArtifact: options.implementArtifact, expectedArtifact: `artifacts/implement-${taskId}.md`, required: false },
304
329
  { agent: 'reviewer', suppliedArtifact: options.reviewArtifact, expectedArtifact: `artifacts/review-${taskId}.md`, required: true }
@@ -306,15 +331,69 @@ function buildLoopSteps(taskId: string, options: SingleTaskLoopOptions): LoopAge
306
331
  if (options.debugArtifact) {
307
332
  steps.push({ agent: 'debugger', suppliedArtifact: options.debugArtifact, expectedArtifact: `artifacts/debug-${taskId}.md`, required: false });
308
333
  }
309
- steps.push({ agent: 'validator', suppliedArtifact: options.validationArtifact, expectedArtifact: `artifacts/validation-${taskId}.md`, required: true });
334
+ steps.push({ agent: 'validator', suppliedArtifact: options.validationArtifact, expectedArtifact: `artifacts/validation-${taskId}.md`, required: requiresImmediateValidation(task) });
310
335
  return steps;
311
336
  }
312
337
 
338
+ function requiresImmediateValidation(task: SddTask | null): boolean {
339
+ return !task || task.requiresVerifyBeforeNext || task.validationTiming === 'task_end';
340
+ }
341
+
342
+ function buildRuntimeTaskState(task: SddTask | null, status: SingleTaskLoopStatus, validationStatus: RunState['validation']['status'], gaps: SddTaskGap[], artifacts: string[]): RunStateTaskRuntime {
343
+ const implementationStatus: NonNullable<RunStateTaskRuntime['implementationStatus']> = status === 'completed' ? 'implemented' : status === 'blocked' ? 'blocked' : 'failed';
344
+ const verificationStatus: NonNullable<RunStateTaskRuntime['verificationStatus']> = verificationStatusForLoop(task, status, validationStatus);
345
+ return {
346
+ status: runtimeTaskStatus(implementationStatus, verificationStatus),
347
+ implementationStatus,
348
+ verificationStatus,
349
+ validationBatch: task?.validationBatch ?? null,
350
+ validationTiming: task?.validationTiming ?? 'task_end',
351
+ requiresVerifyBeforeNext: task?.requiresVerifyBeforeNext ?? true,
352
+ gaps,
353
+ artifacts
354
+ };
355
+ }
356
+
357
+ function verificationStatusForLoop(task: SddTask | null, status: SingleTaskLoopStatus, validationStatus: RunState['validation']['status']): NonNullable<RunStateTaskRuntime['verificationStatus']> {
358
+ if (validationStatus === 'pass') {
359
+ return 'pass';
360
+ }
361
+ if (validationStatus === 'pass_with_gaps') {
362
+ return 'pass_with_gaps';
363
+ }
364
+ if (validationStatus === 'blocked') {
365
+ return 'blocked';
366
+ }
367
+ if (validationStatus === 'fail') {
368
+ return 'failed';
369
+ }
370
+ if (status !== 'completed') {
371
+ return status === 'failed' ? 'failed' : 'blocked';
372
+ }
373
+ return task && !requiresImmediateValidation(task) ? 'pending_batch' : 'not_run';
374
+ }
375
+
376
+ function runtimeTaskStatus(implementationStatus: NonNullable<RunStateTaskRuntime['implementationStatus']>, verificationStatus: NonNullable<RunStateTaskRuntime['verificationStatus']>): string {
377
+ if (implementationStatus === 'implemented' && verificationStatus === 'pending_batch') {
378
+ return 'implemented_pending_validation';
379
+ }
380
+ if (implementationStatus === 'implemented' && verificationStatus === 'pass') {
381
+ return 'implemented_verified';
382
+ }
383
+ if (verificationStatus === 'pass_with_gaps' || verificationStatus === 'blocked') {
384
+ return 'validation_blocked';
385
+ }
386
+ if (implementationStatus === 'failed' || verificationStatus === 'failed') {
387
+ return 'failed';
388
+ }
389
+ return implementationStatus;
390
+ }
391
+
313
392
  async function persistLoopState(projectRoot: string, runId: string, input: {
314
393
  status: SingleTaskLoopStatus;
315
394
  phase: string;
316
395
  taskId: string;
317
- taskState: unknown;
396
+ taskState: RunStateTaskRuntime;
318
397
  validationStatus: RunState['validation']['status'];
319
398
  syncBackProposalPath: string;
320
399
  syncBackProposalDigest: string;
@@ -351,10 +430,29 @@ async function persistLoopState(projectRoot: string, runId: string, input: {
351
430
  });
352
431
  }
353
432
 
354
- async function writeSyncBackProposal(projectRoot: string, runId: string, taskId: string, status: string, artifacts: string[], gaps: SddTaskGap[], summary: string): Promise<{ absolutePath: string; runRelativePath: string; digest: string }> {
433
+ async function writeSyncBackProposal(projectRoot: string, runId: string, taskId: string, status: string, artifacts: string[], gaps: SddTaskGap[], summary: string): Promise<{ absolutePath: string; runRelativePath: string; digest: string; payloadId: string }> {
355
434
  const content = `# Sync-back Proposal\n\n## ${taskId}\n\n- status: ${status}\n- summary: ${summary}\n- artifacts:\n${artifacts.length > 0 ? artifacts.map((artifact) => ` - ${artifact}`).join('\n') : ' - none'}\n- gaps:\n${gaps.length > 0 ? gaps.map((gap) => ` - [${gap.severity}] ${gap.type} ${gap.field}: ${gap.message}`).join('\n') : ' - none'}\n\n## Boundaries\n\n- Proposal only; tasks.md/spec.md/plan.md were not modified by runtime.\n- Runtime modeled agent/verify steps through supplied artifacts and contract validation; no external agent API was invoked.\n`;
435
+ const digest = hashDocumentContent(content);
356
436
  const written = await writeArtifact(projectRoot, runId, 'sync-back-proposal.md', content);
357
- return { ...written, digest: hashDocumentContent(content) };
437
+ return { ...written, digest, payloadId: runtimeScopedId(runId, written.runRelativePath, digest) };
438
+ }
439
+
440
+ async function recordLoopSyncBackDecision(projectRoot: string, input: { runId: string; branch: string; taskId: string; status: string; proposalPath: string; proposalDigest: string; proposalPayloadId: string; reasons: string[]; payload: unknown }): Promise<void> {
441
+ const now = new Date().toISOString();
442
+ await recordRuntimeSyncBackDecision(projectRoot, {
443
+ decisionId: runtimeScopedId(input.runId, input.taskId, 'sync-back'),
444
+ runId: input.runId,
445
+ branch: input.branch,
446
+ taskId: input.taskId,
447
+ status: input.status,
448
+ proposalPath: input.proposalPath,
449
+ proposalDigest: input.proposalDigest,
450
+ proposalPayloadId: input.proposalPayloadId,
451
+ reasons: input.reasons,
452
+ createdAt: now,
453
+ updatedAt: now,
454
+ payload: input.payload
455
+ });
358
456
  }
359
457
 
360
458
  function renderLoopGapReport(taskId: string, gaps: SddTaskGap[]): string {