sdd-agent-platform 0.4.2 → 0.5.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 (834) hide show
  1. package/README.md +34 -41
  2. package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +56 -73
  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 +9 -64
  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 +1 -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 +17 -26
  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 +8 -7
  11. package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js +8 -12
  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 +1 -1
  14. package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js +1 -1
  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 +3 -4
  17. package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js +377 -411
  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 +18 -25
  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 +8 -26
  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 +6 -1
  28. package/node_modules/@sdd-agent-platform/core/dist/contracts.js +5 -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 +0 -3
  31. package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.d.ts +0 -3
  32. package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.js +4 -7
  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 +3 -13
  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/local-run-index.js +1 -9
  37. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/local-run-index.js.map +1 -1
  38. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js +9 -9
  39. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js.map +1 -1
  40. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js +1 -0
  41. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js.map +1 -1
  42. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js +4 -4
  43. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js.map +1 -1
  44. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js +24 -0
  45. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js.map +1 -1
  46. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js +2 -2
  47. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  48. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +43 -180
  49. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
  50. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +1 -1
  51. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +7 -14
  52. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -1
  53. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js +110 -0
  54. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js.map +1 -0
  55. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js +4 -4
  56. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js.map +1 -1
  57. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js +3 -3
  58. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js.map +1 -1
  59. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +85 -86
  60. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js.map +1 -1
  61. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +2 -3
  62. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
  63. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +2 -2
  64. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -1
  65. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.d.ts +1 -1
  66. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js +1 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js.map +1 -1
  68. package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +31 -67
  70. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  71. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js +1 -1
  72. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js.map +1 -1
  73. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +0 -1
  74. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +59 -85
  75. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  76. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  77. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js +7 -0
  78. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js.map +1 -0
  79. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  80. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js +461 -0
  81. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js.map +1 -0
  82. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.d.ts +2 -0
  83. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js +3 -0
  84. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js.map +1 -0
  85. package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
  86. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.d.ts +2 -12
  87. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +32 -80
  88. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
  89. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +2 -5
  90. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +27 -69
  91. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
  92. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +118 -34
  93. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  94. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +1 -1
  95. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
  96. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +1 -1
  97. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
  98. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +8 -15
  99. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
  100. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js +4 -4
  101. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js.map +1 -1
  102. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.d.ts +13 -0
  103. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js +76 -0
  104. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js.map +1 -0
  105. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js +2 -2
  106. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js.map +1 -1
  107. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js +7 -7
  108. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js.map +1 -1
  109. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +4 -4
  110. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
  111. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js +2 -2
  112. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js.map +1 -1
  113. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js +11 -11
  114. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js.map +1 -1
  115. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.d.ts +1 -1
  116. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +21 -21
  117. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  118. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js +2 -1
  119. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js.map +1 -1
  120. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +6 -6
  121. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
  122. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +11 -23
  123. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
  124. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.d.ts +2 -2
  125. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +18 -20
  126. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
  127. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +0 -2
  128. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +1 -1
  129. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
  130. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +16 -48
  131. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  132. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +11 -1
  133. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -1
  134. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +2 -2
  135. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
  136. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +2 -2
  137. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +20 -28
  138. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -1
  139. package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +0 -1
  140. package/node_modules/@sdd-agent-platform/core/dist/router.js +0 -1
  141. package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
  142. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +6 -6
  143. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +13 -124
  144. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  145. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +2 -0
  146. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +5 -7
  147. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
  148. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +28 -28
  149. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.d.ts +3 -2
  150. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js +15 -66
  151. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js.map +1 -1
  152. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +26 -36
  153. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  154. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +0 -4
  155. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +5 -51
  156. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -1
  157. package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +0 -1
  158. package/node_modules/@sdd-agent-platform/core/dist/run-state.js +0 -1
  159. package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
  160. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +1 -1
  161. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
  162. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +7 -16
  163. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
  164. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/model.d.ts +1 -2
  165. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +0 -1
  166. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +1 -4
  167. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  168. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +2 -2
  169. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +11 -0
  170. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -1
  171. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  172. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js +179 -0
  173. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  174. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +0 -2
  175. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +10 -97
  176. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -1
  177. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.d.ts +1 -1
  178. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +6 -8
  179. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
  180. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +5 -2
  181. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +85 -68
  182. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  183. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js +2 -2
  184. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js.map +1 -1
  185. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js +40 -0
  186. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js.map +1 -0
  187. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.d.ts +12 -0
  188. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js +2 -0
  189. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js.map +1 -0
  190. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +2 -2
  191. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +19 -26
  192. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -1
  193. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +1 -1
  194. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +3 -6
  195. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -1
  196. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +111 -263
  197. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +1272 -1124
  198. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -1
  199. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +5 -5
  200. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
  201. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +1 -44
  202. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +47 -170
  203. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  204. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +73 -73
  205. package/node_modules/@sdd-agent-platform/core/dist/subagents/contracts.d.ts +1 -1
  206. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js +7 -7
  207. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js.map +1 -1
  208. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +1 -0
  209. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +2 -0
  210. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +1 -0
  211. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +1 -0
  212. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +2 -0
  213. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +1 -0
  214. package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +1 -0
  215. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +2 -0
  216. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +1 -0
  217. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.d.ts +167 -0
  218. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js +377 -0
  219. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js.map +1 -0
  220. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +329 -314
  221. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
  222. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -0
  223. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +53 -7
  224. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  225. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +9 -12
  226. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -1
  227. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  228. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -48
  229. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +1 -520
  230. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  231. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +5 -5
  232. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +14 -14
  233. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  234. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.d.ts +1 -0
  235. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +111 -159
  236. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  237. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  238. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js +521 -0
  239. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js.map +1 -0
  240. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +21 -21
  241. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  242. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +0 -18
  243. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +5 -27
  244. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -1
  245. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +45 -45
  246. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  247. package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +3 -3
  248. package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -2
  249. package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
  250. package/node_modules/@sdd-agent-platform/core/dist/work-units/contracts.d.ts +1 -1
  251. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +9 -227
  252. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  253. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +9 -50
  254. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -1
  255. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +4 -42
  256. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -1
  257. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +2 -3
  258. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  259. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +2 -1
  260. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  261. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +1 -1
  262. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +1 -0
  263. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +23 -63
  264. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  265. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +2 -2
  266. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +43 -65
  267. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  268. package/node_modules/@sdd-agent-platform/core/package.json +5 -2
  269. package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +238 -185
  270. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +56 -73
  271. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +189 -227
  272. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +222 -278
  273. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.test.ts +28 -28
  274. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +301 -301
  275. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +181 -181
  276. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +231 -240
  277. package/node_modules/@sdd-agent-platform/core/src/artifacts/templates.ts +99 -99
  278. package/node_modules/@sdd-agent-platform/core/src/artifacts.ts +4 -4
  279. package/node_modules/@sdd-agent-platform/core/src/coding-facts/contracts.ts +79 -79
  280. package/node_modules/@sdd-agent-platform/core/src/coding-facts.ts +1 -1
  281. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +314 -318
  282. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +128 -123
  283. package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +265 -265
  284. package/node_modules/@sdd-agent-platform/core/src/config/project-detection.ts +147 -147
  285. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +400 -432
  286. package/node_modules/@sdd-agent-platform/core/src/context/budget.ts +30 -30
  287. package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +304 -311
  288. package/node_modules/@sdd-agent-platform/core/src/context/command-summary.ts +45 -45
  289. package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +188 -189
  290. package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +144 -163
  291. package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +48 -48
  292. package/node_modules/@sdd-agent-platform/core/src/context/source-refs.ts +41 -41
  293. package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +47 -47
  294. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.test.ts +71 -71
  295. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.ts +178 -178
  296. package/node_modules/@sdd-agent-platform/core/src/context-offload.ts +2 -2
  297. package/node_modules/@sdd-agent-platform/core/src/context.ts +6 -6
  298. package/node_modules/@sdd-agent-platform/core/src/contracts/issues.ts +13 -13
  299. package/node_modules/@sdd-agent-platform/core/src/contracts.test.ts +9 -9
  300. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +121 -116
  301. package/node_modules/@sdd-agent-platform/core/src/delegation/delegation.test.ts +183 -183
  302. package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +23 -26
  303. package/node_modules/@sdd-agent-platform/core/src/delegation/queue.ts +58 -58
  304. package/node_modules/@sdd-agent-platform/core/src/delegation/run-state.ts +14 -14
  305. package/node_modules/@sdd-agent-platform/core/src/delegation/state-machine.ts +90 -90
  306. package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +124 -127
  307. package/node_modules/@sdd-agent-platform/core/src/delegation.ts +26 -26
  308. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/ai-entries.ts +28 -28
  309. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +104 -112
  310. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/local-run-index.ts +19 -27
  311. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +84 -84
  312. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +252 -251
  313. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +330 -330
  314. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-records.ts +79 -79
  315. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +128 -107
  316. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +300 -300
  317. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +628 -755
  318. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +301 -453
  319. package/node_modules/@sdd-agent-platform/core/src/doctor/model.ts +13 -13
  320. package/node_modules/@sdd-agent-platform/core/src/doctor/summary.ts +11 -11
  321. package/node_modules/@sdd-agent-platform/core/src/doctor.ts +2 -2
  322. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +80 -88
  323. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +48 -48
  324. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime.ts +1 -1
  325. package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +195 -195
  326. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +187 -235
  327. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +305 -305
  328. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +97 -106
  329. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +453 -453
  330. package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +225 -226
  331. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +132 -143
  332. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +436 -437
  333. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -102
  334. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -271
  335. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +111 -121
  336. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.ts +231 -231
  337. package/node_modules/@sdd-agent-platform/core/src/execution.ts +5 -5
  338. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +57 -65
  339. package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +175 -175
  340. package/node_modules/@sdd-agent-platform/core/src/governance.ts +1 -1
  341. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +80 -64
  342. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +32 -68
  343. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +174 -174
  344. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +373 -373
  345. package/node_modules/@sdd-agent-platform/core/src/lifecycle/rendering.ts +29 -29
  346. package/node_modules/@sdd-agent-platform/core/src/lifecycle/risk-signals.ts +146 -146
  347. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -47
  348. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +255 -280
  349. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/contracts.ts +179 -0
  350. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/kernel.ts +522 -0
  351. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph.ts +2 -0
  352. package/node_modules/@sdd-agent-platform/core/src/lifecycle.ts +4 -4
  353. package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +50 -50
  354. package/node_modules/@sdd-agent-platform/core/src/orchestration/index.ts +2 -2
  355. package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +331 -394
  356. package/node_modules/@sdd-agent-platform/core/src/path-safety.test.ts +22 -22
  357. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +243 -242
  358. package/node_modules/@sdd-agent-platform/core/src/phase8-projection-compat.test.ts +152 -153
  359. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +277 -277
  360. package/node_modules/@sdd-agent-platform/core/src/phase9-lifecycle-graph.test.ts +103 -0
  361. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +88 -88
  362. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.ts +222 -222
  363. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +79 -79
  364. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.ts +160 -160
  365. package/node_modules/@sdd-agent-platform/core/src/planning.ts +2 -2
  366. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +426 -473
  367. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +230 -146
  368. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +142 -142
  369. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +253 -253
  370. package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +302 -309
  371. package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +246 -246
  372. package/node_modules/@sdd-agent-platform/core/src/registries/plan-scout-domains.ts +89 -0
  373. package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +119 -119
  374. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +454 -445
  375. package/node_modules/@sdd-agent-platform/core/src/registries/skill-capabilities.ts +37 -37
  376. package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +135 -135
  377. package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +132 -132
  378. package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +144 -144
  379. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +111 -111
  380. package/node_modules/@sdd-agent-platform/core/src/registries.ts +42 -42
  381. package/node_modules/@sdd-agent-platform/core/src/risk/consumer-diagnostics.ts +98 -97
  382. package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +63 -63
  383. package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +233 -233
  384. package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +251 -263
  385. package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +203 -205
  386. package/node_modules/@sdd-agent-platform/core/src/risk.ts +5 -5
  387. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +327 -327
  388. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +388 -390
  389. package/node_modules/@sdd-agent-platform/core/src/router/profile-resolution.ts +154 -154
  390. package/node_modules/@sdd-agent-platform/core/src/router/risk-policy.ts +33 -33
  391. package/node_modules/@sdd-agent-platform/core/src/router/route-cache.ts +100 -100
  392. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +356 -356
  393. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +428 -665
  394. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.ts +2 -2
  395. package/node_modules/@sdd-agent-platform/core/src/router/routing-rules.ts +73 -73
  396. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +189 -223
  397. package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +464 -453
  398. package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +124 -124
  399. package/node_modules/@sdd-agent-platform/core/src/router/runtime-registry.ts +123 -123
  400. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +277 -277
  401. package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +273 -279
  402. package/node_modules/@sdd-agent-platform/core/src/router/team-mode.ts +170 -170
  403. package/node_modules/@sdd-agent-platform/core/src/router.ts +5 -6
  404. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +126 -240
  405. package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +27 -27
  406. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +172 -172
  407. package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +109 -109
  408. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +252 -253
  409. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.test.ts +80 -52
  410. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +301 -352
  411. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +70 -118
  412. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +406 -416
  413. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +198 -252
  414. package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -146
  415. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +8 -9
  416. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +60 -60
  417. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +249 -256
  418. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +139 -140
  419. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +65 -66
  420. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.ts +2 -2
  421. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +249 -253
  422. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +101 -96
  423. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +314 -292
  424. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.test.ts +380 -0
  425. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.ts +207 -0
  426. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +111 -111
  427. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +207 -306
  428. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +95 -97
  429. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-inspection.ts +39 -39
  430. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +467 -523
  431. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +738 -709
  432. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-rendering.ts +81 -81
  433. package/node_modules/@sdd-agent-platform/core/src/sdd-docs.ts +5 -5
  434. package/node_modules/@sdd-agent-platform/core/src/spec-manager-contracts.ts +13 -0
  435. package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +435 -450
  436. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +316 -322
  437. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2963 -2902
  438. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5856 -5831
  439. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/contracts.ts +40 -40
  440. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +209 -209
  441. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +360 -360
  442. package/node_modules/@sdd-agent-platform/core/src/stage-runtime.ts +2 -2
  443. package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +288 -511
  444. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +651 -851
  445. package/node_modules/@sdd-agent-platform/core/src/status.ts +2 -2
  446. package/node_modules/@sdd-agent-platform/core/src/storage/json-io.ts +10 -10
  447. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +489 -681
  448. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +1981 -1981
  449. package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +45 -45
  450. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +232 -232
  451. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +307 -307
  452. package/node_modules/@sdd-agent-platform/core/src/subagents.ts +2 -2
  453. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.test.ts +141 -0
  454. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.ts +566 -0
  455. package/node_modules/@sdd-agent-platform/core/src/task-risk-profile.ts +193 -193
  456. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +413 -398
  457. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +102 -56
  458. package/node_modules/@sdd-agent-platform/core/src/test-support.ts +2 -2
  459. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -72
  460. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +9 -12
  461. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +137 -137
  462. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +77 -84
  463. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -77
  464. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +455 -506
  465. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.test.ts → task-evidence-judgment.test.ts} +261 -261
  466. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.ts → task-evidence-judgment.ts} +619 -619
  467. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +1190 -1190
  468. package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -106
  469. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +513 -556
  470. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +334 -334
  471. package/node_modules/@sdd-agent-platform/core/src/verification.ts +8 -8
  472. package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +26 -26
  473. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.test.ts +88 -88
  474. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.ts +112 -112
  475. package/node_modules/@sdd-agent-platform/core/src/work-units.ts +2 -2
  476. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +190 -425
  477. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +169 -507
  478. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +136 -182
  479. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +135 -174
  480. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +153 -194
  481. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +111 -115
  482. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +95 -93
  483. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +32 -32
  484. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +114 -114
  485. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +184 -224
  486. package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -158
  487. package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -77
  488. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -114
  489. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +969 -956
  490. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +967 -992
  491. package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -712
  492. package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +2 -2
  493. package/node_modules/@sdd-agent-platform/core/src/worktree/isolation.ts +130 -130
  494. package/node_modules/@sdd-agent-platform/core/src/worktree/lifecycle.ts +269 -269
  495. package/node_modules/@sdd-agent-platform/core/src/worktree/worktree.test.ts +150 -150
  496. package/node_modules/@sdd-agent-platform/core/src/worktree.ts +2 -2
  497. package/node_modules/@sdd-agent-platform/core/tsconfig.json +15 -15
  498. package/package.json +2 -2
  499. package/packages/cli/dist/args.js +2 -2
  500. package/packages/cli/dist/args.js.map +1 -1
  501. package/packages/cli/dist/commands/ai-tools.js +2 -13
  502. package/packages/cli/dist/commands/ai-tools.js.map +1 -1
  503. package/packages/cli/dist/commands/{verifies.d.ts → artifact.d.ts} +1 -1
  504. package/packages/cli/dist/commands/artifact.js +168 -0
  505. package/packages/cli/dist/commands/artifact.js.map +1 -0
  506. package/packages/cli/dist/commands/context.js +1 -1
  507. package/packages/cli/dist/commands/context.js.map +1 -1
  508. package/packages/cli/dist/commands/evidence.js.map +1 -0
  509. package/packages/cli/dist/commands/execution.js +127 -49
  510. package/packages/cli/dist/commands/execution.js.map +1 -1
  511. package/packages/cli/dist/commands/governance.js +1 -1
  512. package/packages/cli/dist/commands/governance.js.map +1 -1
  513. package/packages/cli/dist/commands/init.js +1 -6
  514. package/packages/cli/dist/commands/init.js.map +1 -1
  515. package/packages/cli/dist/commands/instructions.d.ts +1 -1
  516. package/packages/cli/dist/commands/instructions.js +15 -1
  517. package/packages/cli/dist/commands/instructions.js.map +1 -1
  518. package/packages/cli/dist/commands/registry/runtime.js +63 -40
  519. package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
  520. package/packages/cli/dist/commands/run.js +13 -52
  521. package/packages/cli/dist/commands/run.js.map +1 -1
  522. package/packages/cli/dist/commands/stage-close.d.ts +60 -0
  523. package/packages/cli/dist/commands/stage-close.js +270 -41
  524. package/packages/cli/dist/commands/stage-close.js.map +1 -1
  525. package/packages/cli/dist/commands/status.js +9 -68
  526. package/packages/cli/dist/commands/status.js.map +1 -1
  527. package/packages/cli/dist/commands/tasks.js.map +1 -1
  528. package/packages/cli/dist/dispatch.js +6 -26
  529. package/packages/cli/dist/dispatch.js.map +1 -1
  530. package/packages/cli/dist/help.js +153 -159
  531. package/packages/cli/dist/help.js.map +1 -1
  532. package/packages/cli/dist/renderers/artifacts.d.ts +5 -0
  533. package/packages/cli/dist/renderers/artifacts.js +43 -0
  534. package/packages/cli/dist/renderers/artifacts.js.map +1 -0
  535. package/packages/cli/dist/renderers/doctor.js +1 -2
  536. package/packages/cli/dist/renderers/doctor.js.map +1 -1
  537. package/packages/cli/dist/renderers/execution.js +1 -1
  538. package/packages/cli/dist/renderers/execution.js.map +1 -1
  539. package/packages/cli/dist/renderers/json.d.ts +0 -1
  540. package/packages/cli/dist/renderers/json.js +0 -3
  541. package/packages/cli/dist/renderers/json.js.map +1 -1
  542. package/packages/cli/dist/renderers/registry-runtime.d.ts +1 -2
  543. package/packages/cli/dist/renderers/registry-runtime.js +0 -20
  544. package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
  545. package/packages/cli/dist/renderers/router.js +1 -1
  546. package/packages/cli/dist/renderers/router.js.map +1 -1
  547. package/packages/cli/dist/renderers/workflow.d.ts +53 -0
  548. package/packages/cli/dist/renderers/workflow.js +93 -34
  549. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  550. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  551. package/packages/cli/package.json +2 -2
  552. package/packages/core/dist/ai-tools.js +56 -73
  553. package/packages/core/dist/ai-tools.js.map +1 -1
  554. package/packages/core/dist/artifacts/ingestion.js +9 -64
  555. package/packages/core/dist/artifacts/ingestion.js.map +1 -1
  556. package/packages/core/dist/artifacts/sdd-evidence.js +1 -1
  557. package/packages/core/dist/artifacts/sdd-evidence.js.map +1 -1
  558. package/packages/core/dist/artifacts/sdd-result.js +17 -26
  559. package/packages/core/dist/artifacts/sdd-result.js.map +1 -1
  560. package/packages/core/dist/config/init-project.d.ts +8 -7
  561. package/packages/core/dist/config/init-project.js +8 -12
  562. package/packages/core/dist/config/init-project.js.map +1 -1
  563. package/packages/core/dist/config/project-config.d.ts +1 -1
  564. package/packages/core/dist/config/project-config.js +1 -1
  565. package/packages/core/dist/config/project-config.js.map +1 -1
  566. package/packages/core/dist/config/starter-documents.d.ts +3 -4
  567. package/packages/core/dist/config/starter-documents.js +377 -411
  568. package/packages/core/dist/config/starter-documents.js.map +1 -1
  569. package/packages/core/dist/context/build-package.d.ts +1 -1
  570. package/packages/core/dist/context/build-package.js +18 -25
  571. package/packages/core/dist/context/build-package.js.map +1 -1
  572. package/packages/core/dist/context/evidence-summary.js +8 -26
  573. package/packages/core/dist/context/evidence-summary.js.map +1 -1
  574. package/packages/core/dist/context/log-worker.js +2 -2
  575. package/packages/core/dist/context/log-worker.js.map +1 -1
  576. package/packages/core/dist/context-offload/contracts.d.ts +1 -1
  577. package/packages/core/dist/contracts.d.ts +6 -1
  578. package/packages/core/dist/contracts.js +5 -0
  579. package/packages/core/dist/contracts.js.map +1 -1
  580. package/packages/core/dist/delegation/model.d.ts +0 -3
  581. package/packages/core/dist/delegation/validation.d.ts +0 -3
  582. package/packages/core/dist/delegation/validation.js +4 -7
  583. package/packages/core/dist/delegation/validation.js.map +1 -1
  584. package/packages/core/dist/doctor/checks/document-chain.js +3 -13
  585. package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
  586. package/packages/core/dist/doctor/checks/local-run-index.js +1 -9
  587. package/packages/core/dist/doctor/checks/local-run-index.js.map +1 -1
  588. package/packages/core/dist/doctor/checks/project.js +9 -9
  589. package/packages/core/dist/doctor/checks/project.js.map +1 -1
  590. package/packages/core/dist/doctor/checks/registries.js +1 -0
  591. package/packages/core/dist/doctor/checks/registries.js.map +1 -1
  592. package/packages/core/dist/doctor/checks/run-evidence.js +4 -4
  593. package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
  594. package/packages/core/dist/doctor/checks/run-trust.js +24 -0
  595. package/packages/core/dist/doctor/checks/run-trust.js.map +1 -1
  596. package/packages/core/dist/doctor/checks/runtime-contracts.js +2 -2
  597. package/packages/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  598. package/packages/core/dist/doctor/doctor.js +43 -180
  599. package/packages/core/dist/doctor/doctor.js.map +1 -1
  600. package/packages/core/dist/evidence/lookup.d.ts +1 -1
  601. package/packages/core/dist/evidence/lookup.js +7 -14
  602. package/packages/core/dist/evidence/lookup.js.map +1 -1
  603. package/packages/core/dist/evidence-runtime/coordination.js +110 -0
  604. package/packages/core/dist/evidence-runtime/coordination.js.map +1 -0
  605. package/packages/core/dist/execution/background-executor.js +4 -4
  606. package/packages/core/dist/execution/background-executor.js.map +1 -1
  607. package/packages/core/dist/execution/foreground-subagents.js +3 -3
  608. package/packages/core/dist/execution/foreground-subagents.js.map +1 -1
  609. package/packages/core/dist/execution/host-invocation.js +85 -86
  610. package/packages/core/dist/execution/host-invocation.js.map +1 -1
  611. package/packages/core/dist/execution/resident-worker.js +2 -3
  612. package/packages/core/dist/execution/resident-worker.js.map +1 -1
  613. package/packages/core/dist/execution/stage-team-runtime.js +2 -2
  614. package/packages/core/dist/execution/stage-team-runtime.js.map +1 -1
  615. package/packages/core/dist/governance/policy.d.ts +1 -1
  616. package/packages/core/dist/governance/policy.js +1 -1
  617. package/packages/core/dist/governance/policy.js.map +1 -1
  618. package/packages/core/dist/instructions.d.ts +1 -1
  619. package/packages/core/dist/instructions.js +31 -67
  620. package/packages/core/dist/instructions.js.map +1 -1
  621. package/packages/core/dist/lifecycle/decision-gate.js +1 -1
  622. package/packages/core/dist/lifecycle/decision-gate.js.map +1 -1
  623. package/packages/core/dist/lifecycle/ship.d.ts +0 -1
  624. package/packages/core/dist/lifecycle/ship.js +59 -85
  625. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  626. package/packages/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  627. package/packages/core/dist/lifecycle-graph/contracts.js +7 -0
  628. package/packages/core/dist/lifecycle-graph/contracts.js.map +1 -0
  629. package/packages/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  630. package/packages/core/dist/lifecycle-graph/kernel.js +461 -0
  631. package/packages/core/dist/lifecycle-graph/kernel.js.map +1 -0
  632. package/packages/core/dist/lifecycle-graph.d.ts +2 -0
  633. package/packages/core/dist/lifecycle-graph.js +3 -0
  634. package/packages/core/dist/lifecycle-graph.js.map +1 -0
  635. package/packages/core/dist/orchestration/contracts.d.ts +1 -1
  636. package/packages/core/dist/orchestration/runtime.d.ts +2 -12
  637. package/packages/core/dist/orchestration/runtime.js +32 -80
  638. package/packages/core/dist/orchestration/runtime.js.map +1 -1
  639. package/packages/core/dist/registries/agent-capability-catalog.d.ts +2 -5
  640. package/packages/core/dist/registries/agent-capability-catalog.js +27 -69
  641. package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
  642. package/packages/core/dist/registries/agent-registry.js +118 -34
  643. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  644. package/packages/core/dist/registries/agent-runtime-static.js +1 -1
  645. package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
  646. package/packages/core/dist/registries/capability-sources.js +1 -1
  647. package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
  648. package/packages/core/dist/registries/command-team-runtime.js +8 -15
  649. package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
  650. package/packages/core/dist/registries/eval-learning-context.js +4 -4
  651. package/packages/core/dist/registries/eval-learning-context.js.map +1 -1
  652. package/packages/core/dist/registries/plan-scout-domains.d.ts +13 -0
  653. package/packages/core/dist/registries/plan-scout-domains.js +76 -0
  654. package/packages/core/dist/registries/plan-scout-domains.js.map +1 -0
  655. package/packages/core/dist/registries/query-status.js +2 -2
  656. package/packages/core/dist/registries/query-status.js.map +1 -1
  657. package/packages/core/dist/registries/skill-capabilities.js +7 -7
  658. package/packages/core/dist/registries/skill-capabilities.js.map +1 -1
  659. package/packages/core/dist/registries/tool-capabilities.js +4 -4
  660. package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
  661. package/packages/core/dist/registries/tool-plugins.js +2 -2
  662. package/packages/core/dist/registries/tool-plugins.js.map +1 -1
  663. package/packages/core/dist/registries/worker-adapters.js +11 -11
  664. package/packages/core/dist/registries/worker-adapters.js.map +1 -1
  665. package/packages/core/dist/registries/workflow-gates.d.ts +1 -1
  666. package/packages/core/dist/registries/workflow-gates.js +21 -21
  667. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  668. package/packages/core/dist/risk/consumer-diagnostics.js +2 -1
  669. package/packages/core/dist/risk/consumer-diagnostics.js.map +1 -1
  670. package/packages/core/dist/risk/kernel.js +6 -6
  671. package/packages/core/dist/risk/kernel.js.map +1 -1
  672. package/packages/core/dist/risk/legacy-adapters.js +11 -23
  673. package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
  674. package/packages/core/dist/risk/workflow-gates.d.ts +2 -2
  675. package/packages/core/dist/risk/workflow-gates.js +18 -20
  676. package/packages/core/dist/risk/workflow-gates.js.map +1 -1
  677. package/packages/core/dist/router/agent-runtime.d.ts +0 -2
  678. package/packages/core/dist/router/route-projection.js +1 -1
  679. package/packages/core/dist/router/route-projection.js.map +1 -1
  680. package/packages/core/dist/router/routing.js +16 -48
  681. package/packages/core/dist/router/routing.js.map +1 -1
  682. package/packages/core/dist/router/runtime-import.js +11 -1
  683. package/packages/core/dist/router/runtime-import.js.map +1 -1
  684. package/packages/core/dist/router/runtime-validation.js +2 -2
  685. package/packages/core/dist/router/runtime-validation.js.map +1 -1
  686. package/packages/core/dist/router/stage-route-binding.d.ts +2 -2
  687. package/packages/core/dist/router/stage-route-binding.js +20 -28
  688. package/packages/core/dist/router/stage-route-binding.js.map +1 -1
  689. package/packages/core/dist/router.d.ts +0 -1
  690. package/packages/core/dist/router.js +0 -1
  691. package/packages/core/dist/router.js.map +1 -1
  692. package/packages/core/dist/run-state/artifacts.d.ts +6 -6
  693. package/packages/core/dist/run-state/artifacts.js +13 -124
  694. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  695. package/packages/core/dist/run-state/inspect-run.d.ts +2 -0
  696. package/packages/core/dist/run-state/inspect-run.js +5 -7
  697. package/packages/core/dist/run-state/inspect-run.js.map +1 -1
  698. package/packages/core/dist/run-state/model.d.ts +28 -28
  699. package/packages/core/dist/run-state/run-index.d.ts +3 -2
  700. package/packages/core/dist/run-state/run-index.js +15 -66
  701. package/packages/core/dist/run-state/run-index.js.map +1 -1
  702. package/packages/core/dist/run-state/run-state.js +26 -36
  703. package/packages/core/dist/run-state/run-state.js.map +1 -1
  704. package/packages/core/dist/run-state/task-evidence.d.ts +0 -4
  705. package/packages/core/dist/run-state/task-evidence.js +5 -51
  706. package/packages/core/dist/run-state/task-evidence.js.map +1 -1
  707. package/packages/core/dist/run-state.d.ts +0 -1
  708. package/packages/core/dist/run-state.js +0 -1
  709. package/packages/core/dist/run-state.js.map +1 -1
  710. package/packages/core/dist/runtime-analysis/build.js +1 -1
  711. package/packages/core/dist/runtime-analysis/build.js.map +1 -1
  712. package/packages/core/dist/runtime-analysis/findings.js +7 -16
  713. package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
  714. package/packages/core/dist/runtime-analysis/model.d.ts +1 -2
  715. package/packages/core/dist/runtime-paths.d.ts +0 -1
  716. package/packages/core/dist/runtime-paths.js +1 -4
  717. package/packages/core/dist/runtime-paths.js.map +1 -1
  718. package/packages/core/dist/runtime-projection-p0.d.ts +2 -2
  719. package/packages/core/dist/runtime-projection-p0.js +11 -0
  720. package/packages/core/dist/runtime-projection-p0.js.map +1 -1
  721. package/packages/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  722. package/packages/core/dist/sdd-docs/artifact-depth.js +179 -0
  723. package/packages/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  724. package/packages/core/dist/sdd-docs/document-hashes.d.ts +0 -2
  725. package/packages/core/dist/sdd-docs/document-hashes.js +10 -97
  726. package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -1
  727. package/packages/core/dist/sdd-docs/run-binding.d.ts +1 -1
  728. package/packages/core/dist/sdd-docs/run-binding.js +6 -8
  729. package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
  730. package/packages/core/dist/sdd-docs/task-parser.d.ts +5 -2
  731. package/packages/core/dist/sdd-docs/task-parser.js +85 -68
  732. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  733. package/packages/core/dist/sdd-docs/task-rendering.js +2 -2
  734. package/packages/core/dist/sdd-docs/task-rendering.js.map +1 -1
  735. package/packages/core/dist/spec-entry.js +40 -0
  736. package/packages/core/dist/spec-entry.js.map +1 -0
  737. package/packages/core/dist/spec-manager-contracts.d.ts +12 -0
  738. package/packages/core/dist/spec-manager-contracts.js +2 -0
  739. package/packages/core/dist/spec-manager-contracts.js.map +1 -0
  740. package/packages/core/dist/stage-artifacts.d.ts +2 -2
  741. package/packages/core/dist/stage-artifacts.js +19 -26
  742. package/packages/core/dist/stage-artifacts.js.map +1 -1
  743. package/packages/core/dist/stage-collaboration-contracts.d.ts +1 -1
  744. package/packages/core/dist/stage-collaboration-contracts.js +3 -6
  745. package/packages/core/dist/stage-collaboration-contracts.js.map +1 -1
  746. package/packages/core/dist/stage-collaboration.d.ts +111 -263
  747. package/packages/core/dist/stage-collaboration.js +1272 -1124
  748. package/packages/core/dist/stage-collaboration.js.map +1 -1
  749. package/packages/core/dist/stage-runtime/runtime.js +5 -5
  750. package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
  751. package/packages/core/dist/status/project-status.d.ts +1 -44
  752. package/packages/core/dist/status/project-status.js +47 -170
  753. package/packages/core/dist/status/project-status.js.map +1 -1
  754. package/packages/core/dist/storage/runtime-store.js +73 -73
  755. package/packages/core/dist/subagents/contracts.d.ts +1 -1
  756. package/packages/core/dist/subagents/runtime.js +7 -7
  757. package/packages/core/dist/subagents/runtime.js.map +1 -1
  758. package/packages/core/dist/sync-back/apply.d.ts +1 -0
  759. package/packages/core/dist/sync-back/apply.js +2 -0
  760. package/packages/core/dist/sync-back/apply.js.map +1 -0
  761. package/packages/core/dist/sync-back/inspect.d.ts +1 -0
  762. package/packages/core/dist/sync-back/inspect.js +2 -0
  763. package/packages/core/dist/sync-back/inspect.js.map +1 -0
  764. package/packages/core/dist/sync-back.d.ts +1 -0
  765. package/packages/core/dist/sync-back.js +2 -0
  766. package/packages/core/dist/sync-back.js.map +1 -0
  767. package/packages/core/dist/task-execution-contract.d.ts +167 -0
  768. package/packages/core/dist/task-execution-contract.js +377 -0
  769. package/packages/core/dist/task-execution-contract.js.map +1 -0
  770. package/packages/core/dist/test-support/fixtures.js +329 -314
  771. package/packages/core/dist/test-support/fixtures.js.map +1 -1
  772. package/packages/core/dist/test-support/run-state.d.ts +1 -0
  773. package/packages/core/dist/test-support/run-state.js +53 -7
  774. package/packages/core/dist/test-support/run-state.js.map +1 -1
  775. package/packages/core/dist/truth-reconciliation.js +9 -12
  776. package/packages/core/dist/truth-reconciliation.js.map +1 -1
  777. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  778. package/packages/core/dist/verification/goal-verify.d.ts +0 -48
  779. package/packages/core/dist/verification/goal-verify.js +1 -520
  780. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  781. package/packages/core/dist/verification/rendering.d.ts +5 -5
  782. package/packages/core/dist/verification/rendering.js +14 -14
  783. package/packages/core/dist/verification/rendering.js.map +1 -1
  784. package/packages/core/dist/verification/single-task-loop.d.ts +1 -0
  785. package/packages/core/dist/verification/single-task-loop.js +111 -159
  786. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  787. package/packages/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  788. package/packages/core/dist/verification/task-evidence-judgment.js +521 -0
  789. package/packages/core/dist/verification/task-evidence-judgment.js.map +1 -0
  790. package/packages/core/dist/verification/test-runtime.js +21 -21
  791. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  792. package/packages/core/dist/verification/validation-wave.d.ts +0 -18
  793. package/packages/core/dist/verification/validation-wave.js +5 -27
  794. package/packages/core/dist/verification/validation-wave.js.map +1 -1
  795. package/packages/core/dist/verification/verify-contract.js +45 -45
  796. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  797. package/packages/core/dist/verification.d.ts +3 -3
  798. package/packages/core/dist/verification.js +2 -2
  799. package/packages/core/dist/verification.js.map +1 -1
  800. package/packages/core/dist/work-units/contracts.d.ts +1 -1
  801. package/packages/core/dist/workflow-gate/evidence-packet.js +9 -227
  802. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  803. package/packages/core/dist/workflow-gate/hard-checks.js +9 -50
  804. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -1
  805. package/packages/core/dist/workflow-gate/policy.js +4 -42
  806. package/packages/core/dist/workflow-gate/policy.js.map +1 -1
  807. package/packages/core/dist/workflow-gate/types.d.ts +2 -3
  808. package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  809. package/packages/core/dist/workflow-state/affected-file-conflicts.js +2 -1
  810. package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  811. package/packages/core/dist/workflow-state/dependencies.js +1 -1
  812. package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +1 -0
  813. package/packages/core/dist/workflow-state/latest-eligible-run.js +23 -63
  814. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  815. package/packages/core/dist/workflow-state/resolve.d.ts +2 -2
  816. package/packages/core/dist/workflow-state/resolve.js +43 -65
  817. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  818. package/packages/core/package.json +5 -2
  819. package/tsconfig.build.json +6 -7
  820. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +0 -269
  821. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +0 -492
  822. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +0 -383
  823. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +0 -188
  824. package/packages/cli/dist/commands/lifecycle.d.ts +0 -6
  825. package/packages/cli/dist/commands/lifecycle.js +0 -125
  826. package/packages/cli/dist/commands/lifecycle.js.map +0 -1
  827. package/packages/cli/dist/commands/test.d.ts +0 -6
  828. package/packages/cli/dist/commands/test.js +0 -373
  829. package/packages/cli/dist/commands/test.js.map +0 -1
  830. package/packages/cli/dist/commands/verifies.js +0 -87
  831. package/packages/cli/dist/commands/verifies.js.map +0 -1
  832. package/packages/cli/dist/commands/verify.d.ts +0 -6
  833. package/packages/cli/dist/commands/verify.js +0 -330
  834. package/packages/cli/dist/commands/verify.js.map +0 -1
@@ -1,1190 +1,1190 @@
1
- import { createHash } from 'node:crypto';
2
- import { spawn } from 'node:child_process';
3
- import { appendEvent } from '../run-state/events.js';
4
- import { appendArtifactHashLedgerEntry, appendInvocationLedgerEntry } from '../run-state/invocation-ledger.js';
5
- import type { RunState, RunStateTaskRuntime } from '../run-state/model.js';
6
- import { createRun, readAllRunStates, readRunState, writeRunState } from '../run-state/run-state.js';
7
- import { recordRuntimeOnlyArtifact, recordStageEvidenceArtifact } from '../run-state/artifacts.js';
8
- import { toBranchStageEvidenceRef } from '../runtime-paths.js';
9
- import { resolveSddContext } from '../sdd-docs/context.js';
10
- import { bindRunStateToTask } from '../sdd-docs/run-binding.js';
11
- import { parseSddBranch, type SddTask, type SddTaskModel } from '../sdd-docs/task-parser.js';
12
- import { inspectSddTask } from '../sdd-docs/task-inspection.js';
13
- import { listRuntimeArtifactPayloads, readRuntimeValidationCacheEntry, recordRuntimeAcceptanceEvidenceMap, recordRuntimeDurableGap, recordRuntimeProjection, recordRuntimeTestRun, recordRuntimeTestStep, recordRuntimeValidationCacheEntry, recordRuntimeValidationCacheUse, recordRuntimeValidationEnvironmentSession, recordRuntimeValidationWaveRun, runtimeScopedId, updateRuntimeDurableGapStatus, type RuntimeValidationCacheEntryRecord } from '../storage/runtime-store.js';
14
- import { ACCEPTANCE_POLICY_RULESET_VERSION, SDD_EVIDENCE_CONTRACT, SDD_EVIDENCE_VERSION, SDD_RESULT_CONTRACT, SDD_RESULT_VERSION, TEST_EVIDENCE_RUN_CONTRACT_VERSION, WORKFLOW_HANDOFF_CONTRACT_VERSION } from '../contracts.js';
15
- import type { LifecycleRiskDecision } from '../risk/contracts.js';
16
- import { inspectVerifyContract, type VerifyContractInspection } from './verify-contract.js';
17
- import type { AcceptanceEvidenceCoverage, CapabilityEvidenceClassification, EvidenceCoverageStatus, TestEvidenceStatus, UnifiedTestEvidenceRun } from '../evidence-runtime.js';
18
- import { ensureTaskOrchestration, inspectOrchestrationGate } from '../orchestration/runtime.js';
19
- import { recordStageRunProjection, recordWorkflowHandoffProjection, validateWorkflowHandoff } from '../stage-runtime/runtime.js';
20
- import type { StageRun, WorkflowHandoff } from '../stage-runtime/contracts.js';
21
- import { evaluateTaskWorkflowGate, verifyContractBlockedGate, type ApprovalPolicy, type LifecycleRiskProfile, type LifecycleWorkflowGate } from '../risk.js';
22
- import { validateSddResultArtifact } from '../artifacts/sdd-result.js';
23
- import { dependencyBlockingReasonsForTask } from '../workflow-state/dependencies.js';
24
- import { selectLatestEligibleRunsByTask } from '../workflow-state/latest-eligible-run.js';
25
- import { latestRuntimeTaskStates } from '../workflow-state/resolve.js';
26
- import { routeSddTask } from '../router/route-sdd-task.js';
27
- import type { AgentCapabilityRouteDecision } from '../router/agent-runtime.js';
28
- import { evaluateAndRecordWorkflowGateDecision } from '../workflow-gate/evidence-packet.js';
29
- import type { WorkflowGateDecision, WorkflowGateStatus } from '../workflow-gate/types.js';
30
- import { buildValidationCachePlan, type ValidationCacheUnsafeReason } from './validation-cache.js';
31
-
32
- const DEFAULT_TEST_TIMEOUT_MS = 120_000;
33
- const MAX_CAPTURE_BYTES = 256 * 1024;
34
-
35
- export type SddTestStatus = 'PASS' | 'FAIL' | 'BLOCKED';
36
- export type SddTestStepStatus = 'pass' | 'fail' | 'blocked';
37
-
38
- export interface SddTestCommandInput {
39
- command?: string;
40
- argv?: string[];
41
- }
42
-
43
- export interface SddTestCommandStep {
44
- stepId: string;
45
- command: string;
46
- argv: string[] | null;
47
- shell: boolean;
48
- acceptanceRefs: string[];
49
- status: SddTestStepStatus;
50
- exitCode: number | null;
51
- signal: string | null;
52
- durationMs: number;
53
- outputArtifact: string | null;
54
- stdoutBytes: number;
55
- stderrBytes: number;
56
- truncated: boolean;
57
- startedAt: string;
58
- endedAt: string;
59
- cwd: string;
60
- stdoutDigest: string;
61
- stderrDigest: string;
62
- outputSummary: string;
63
- cacheStatus: 'hit' | 'miss' | 'unsafe';
64
- cacheKey: string | null;
65
- cacheSourceTestRunId: string | null;
66
- cacheUnsafeReasons: ValidationCacheUnsafeReason[];
67
- }
68
-
69
- interface NormalizedSddTestCommand {
70
- command: string;
71
- argv: string[] | null;
72
- shell: boolean;
73
- }
74
-
75
- type VerifyContractAction = 'none' | 'created' | 'refreshed' | 'blocked';
76
-
77
- type RuntimeTestJudgment = WorkflowGateStatus;
78
-
79
- export interface SddTestResult {
80
- contract: 'sdd-test-runtime-v1';
81
- runId: string;
82
- testRunId: string;
83
- validationWaveRunId: string;
84
- validationEnvironmentSessionId: string;
85
- branch: string;
86
- taskId: string;
87
- status: SddTestStatus;
88
- validationStatus: SddTestStatus;
89
- workflowGateStatus: WorkflowGateStatus;
90
- runtimeJudgment: RuntimeTestJudgment;
91
- workflowGateDecision: WorkflowGateDecision;
92
- verifyContractStatus: string;
93
- verifyContractAction: VerifyContractAction;
94
- lifecycleGate: LifecycleWorkflowGate;
95
- lifecycleProfile: LifecycleRiskProfile | null;
96
- approvalPolicy: ApprovalPolicy | null;
97
- requiredStages: string[];
98
- primaryReason: string;
99
- commandStatus: TestEvidenceStatus;
100
- evidenceCoverage: EvidenceCoverageStatus;
101
- policyJudgment: TestEvidenceStatus;
102
- acceptanceCoverage: AcceptanceEvidenceCoverage[];
103
- capabilityEvidence: CapabilityEvidenceClassification[];
104
- commands: string[];
105
- steps: SddTestCommandStep[];
106
- validationArtifact: string | null;
107
- indexArtifact: string | null;
108
- gaps: string[];
109
- next: string;
110
- }
111
-
112
- export interface RunSddTestOptions {
113
- taskId: string;
114
- branch?: string | null;
115
- runId?: string | null;
116
- commands?: string[];
117
- commandInputs?: SddTestCommandInput[];
118
- timeoutMs?: number;
119
- approved?: boolean;
120
- validationWave?: { waveRunId: string; environmentSessionId: string; taskIds: string[]; acceptanceRefsByTask?: Record<string, string[]> };
121
- }
122
-
123
- export async function runSddTest(projectRoot: string, options: RunSddTestOptions): Promise<SddTestResult> {
124
- const context = await resolveSddContext(projectRoot, { branch: options.branch ?? undefined, branchSource: options.branch ? 'cli_option' : undefined });
125
- const model = await parseSddBranch(projectRoot, context.partition);
126
- const inspected = inspectSddTask(model, options.taskId);
127
- const task = inspected.task;
128
- const verifyContract = await ensureVerifyContractForTest(projectRoot, context.partition);
129
- const verifyInspection = verifyContract.inspection;
130
- const initialState = options.runId ? await readRunState(projectRoot, options.runId) : await createRun(projectRoot);
131
- const state = await bindRunStateToTask(projectRoot, initialState, context, model, task, options.taskId);
132
- const testRunId = runtimeScopedId(state.runId, options.taskId, new Date().toISOString(), 'sdd-test');
133
- const commandInputs = normalizeTestCommandInputs(options.commandInputs, options.commands, task?.validation ?? []);
134
- const commands = commandInputs.map((input) => input.command);
135
- const gaps: string[] = [];
136
- const startedAt = new Date().toISOString();
137
- const ownsValidationWave = !options.validationWave;
138
- const validationWaveRunId = options.validationWave?.waveRunId ?? runtimeScopedId(context.partition, options.taskId, state.runId, testRunId, 'validation-wave');
139
- const validationEnvironmentSessionId = options.validationWave?.environmentSessionId ?? runtimeScopedId(context.partition, validationWaveRunId, 'validation-env');
140
- const validationWaveTaskIds = options.validationWave?.taskIds ?? [options.taskId];
141
- const validationWaveAcceptanceRefs = options.validationWave?.acceptanceRefsByTask?.[options.taskId];
142
- const validationWaveScopeAcceptanceRefs = [...new Set(Object.values(options.validationWave?.acceptanceRefsByTask ?? { [options.taskId]: task?.acceptanceRefs ?? [] }).flat())].sort();
143
- if (ownsValidationWave) {
144
- await recordRuntimeValidationEnvironmentSession(projectRoot, {
145
- sessionId: validationEnvironmentSessionId,
146
- partition: context.partition,
147
- runId: state.runId,
148
- waveRunId: validationWaveRunId,
149
- status: 'active',
150
- reuseKey: `${context.partition}:${options.taskId}`,
151
- createdAt: startedAt,
152
- updatedAt: startedAt,
153
- payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task' }
154
- });
155
- await recordRuntimeValidationWaveRun(projectRoot, {
156
- waveRunId: validationWaveRunId,
157
- partition: context.partition,
158
- runId: state.runId,
159
- taskIds: validationWaveTaskIds,
160
- status: 'RUNNING',
161
- environmentSessionId: validationEnvironmentSessionId,
162
- startedAt,
163
- completedAt: startedAt,
164
- payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId }
165
- });
166
- }
167
-
168
- await appendEvent(projectRoot, state.runId, {
169
- event: 'test_runtime_started',
170
- runId: state.runId,
171
- summary: `SDD test runtime started for ${options.taskId}`,
172
- data: { taskId: options.taskId, branch: context.partition, testRunId, commands }
173
- });
174
-
175
- const states = await readAllRunStates(projectRoot);
176
- const latestEligibleRunsByTask = selectLatestEligibleRunsByTask({ states, model, partition: context.partition, currentGitBranch: context.currentGitBranch });
177
- const runtimeByTask = latestRuntimeTaskStates(latestEligibleRunsByTask, states);
178
-
179
- if (!task) {
180
- gaps.push(`Task ${options.taskId} was not found in specs/${context.partition}/tasks.md.`);
181
- }
182
- if (task) {
183
- gaps.push(...inspected.gaps.filter((gap) => gap.severity === 'blocking').map((gap) => `${gap.field}: ${gap.message}`));
184
- gaps.push(...dependencyBlockingReasonsForTask(model, options.taskId, { runtimeByTask }));
185
- }
186
- if (verifyContract.action === 'blocked') {
187
- gaps.push(verifyContractBlocker(verifyInspection));
188
- }
189
- if (commands.length === 0) {
190
- gaps.push(`Task ${options.taskId} has no validation commands.`);
191
- }
192
- const orchestration = await ensureTaskOrchestration(projectRoot, model, task, {
193
- branch: context.partition,
194
- runId: state.runId,
195
- taskId: options.taskId,
196
- agent: 'validator',
197
- stage: 'test',
198
- status: 'active'
199
- });
200
- const orchestrationGate = await inspectOrchestrationGate(projectRoot, {
201
- branch: context.partition,
202
- runId: state.runId,
203
- taskId: options.taskId,
204
- target: 'test',
205
- riskDecision: orchestration.riskDecision,
206
- stageRun: orchestration.stageRun,
207
- contextLoadSignal: orchestration.contextLoadSignal,
208
- contextOffloadDecision: orchestration.contextOffloadDecision
209
- });
210
- const reviewerCheckpointSatisfied = await hasReviewerCheckpoint(projectRoot, state, options.taskId);
211
- const workflowGate = verifyContract.action === 'blocked'
212
- ? verifyContractBlockedGate(options.taskId)
213
- : evaluateTaskWorkflowGate({ task, taskId: options.taskId, riskDecision: orchestration.riskDecision, approved: options.approved, reviewerCheckpointSatisfied });
214
- if (workflowGate.blocksTest) {
215
- gaps.push(workflowGate.primaryReason);
216
- }
217
- gaps.push(...orchestrationGate.blockingReasons);
218
-
219
- await recordRuntimeTestRun(projectRoot, {
220
- testRunId,
221
- runId: state.runId,
222
- partition: context.partition,
223
- taskId: options.taskId,
224
- status: 'RUNNING',
225
- startedAt,
226
- completedAt: startedAt,
227
- payload: { verifyContractStatus: verifyInspection.status, verifyContractAction: verifyContract.action, lifecycleGate: workflowGate.lifecycleGate, lifecycleProfile: workflowGate.lifecycleProfile, approvalPolicy: workflowGate.approvalPolicy, requiredStages: workflowGate.requiredStages, primaryReason: workflowGate.primaryReason, commands, commandInputs, evidence: [], gaps }
228
- });
229
-
230
- const steps: SddTestCommandStep[] = [];
231
- if (gaps.length === 0) {
232
- for (const [index, commandInput] of commandInputs.entries()) {
233
- const step = await runCommandStep(projectRoot, state.runId, context.partition, options.taskId, testRunId, index + 1, commandInput, acceptanceRefsForCommand(task, commandInput.command, validationWaveAcceptanceRefs), options.timeoutMs ?? DEFAULT_TEST_TIMEOUT_MS, model, task!, validationWaveTaskIds, validationWaveScopeAcceptanceRefs);
234
- steps.push(step);
235
- await appendEvent(projectRoot, state.runId, {
236
- event: 'test_step_completed',
237
- runId: state.runId,
238
- summary: `SDD test step ${step.status}: ${step.command}`,
239
- data: { taskId: options.taskId, testRunId, step }
240
- });
241
- }
242
- }
243
-
244
- const commandStatus = deriveCommandStatus(gaps, steps);
245
- const acceptanceCoverage = buildAcceptanceCoverage(task, steps, commandStatus, validationWaveAcceptanceRefs);
246
- const evidenceCoverage = summarizeEvidenceCoverage(acceptanceCoverage);
247
- const policyJudgment = derivePolicyJudgment(commandStatus, evidenceCoverage);
248
- await recordAcceptanceEvidenceMaps(projectRoot, validationWaveRunId, testRunId, context.partition, state.runId, options.taskId, acceptanceCoverage);
249
- const validationStatus = policyJudgment;
250
- const capabilityRoute = task ? await routeSddTask(projectRoot, { taskId: options.taskId, branch: context.partition, approved: options.approved }) : null;
251
- const capabilityEvidence = buildCapabilityEvidenceClassification(capabilityRoute?.capabilityDecision ?? null, steps);
252
- const validationArtifact = task ? await writeValidationArtifact(projectRoot, state.runId, context.partition, task, validationStatus, steps, gaps, capabilityEvidence) : null;
253
- const evidenceBeforeGate = runtimeEvidenceRefs(validationArtifact?.runRelativePath ?? null, steps);
254
- await persistTestRunState(projectRoot, state, options.taskId, validationStatus, commands, evidenceBeforeGate, validationArtifact?.runRelativePath ?? null);
255
- await resolveTestRuntimeDurableGap(projectRoot, context.partition, state.runId, options.taskId, validationStatus, gaps);
256
-
257
- const gateDecision = (await evaluateAndRecordWorkflowGateDecision(projectRoot, {
258
- branch: context.partition,
259
- taskId: options.taskId,
260
- runId: state.runId,
261
- decisionKind: 'test'
262
- })).decision;
263
- const runtimeJudgment = gateDecision.status;
264
- const status = finalStatusForTest(validationStatus, runtimeJudgment);
265
- await recordTestRuntimeDurableGap(projectRoot, context.partition, state.runId, options.taskId, status, validationStatus, runtimeJudgment, gaps, evidenceBeforeGate);
266
- const unifiedEvidence = buildUnifiedTestEvidenceRun(testRunId, context.partition, state.runId, options.taskId, commandStatus, evidenceCoverage, policyJudgment, status, runtimeJudgment, steps, acceptanceCoverage, capabilityEvidence, gaps, workflowGate.nextAction, gateDecision);
267
- const completedAt = new Date().toISOString();
268
- const evidence = runtimeEvidenceRefs(validationArtifact?.runRelativePath ?? null, steps);
269
-
270
- await recordRuntimeTestRun(projectRoot, {
271
- testRunId,
272
- runId: state.runId,
273
- partition: context.partition,
274
- taskId: options.taskId,
275
- status,
276
- startedAt,
277
- completedAt,
278
- payload: { verifyContractStatus: verifyInspection.status, verifyContractAction: verifyContract.action, lifecycleGate: workflowGate.lifecycleGate, lifecycleProfile: workflowGate.lifecycleProfile, approvalPolicy: workflowGate.approvalPolicy, requiredStages: workflowGate.requiredStages, primaryReason: workflowGate.primaryReason, commandStatus, evidenceCoverage, policyJudgment, validationStatus, workflowGateStatus: gateDecision.status, runtimeJudgment, workflowGateDecision: gateDecision, acceptanceCoverage, capabilityEvidence, commands, commandInputs, evidence, gaps }
279
- });
280
- await recordRuntimeProjection(projectRoot, 'test_runtime', `${context.partition}:${options.taskId}:${state.runId}`, {
281
- contract: 'sdd-test-runtime-v1',
282
- testRunId,
283
- runId: state.runId,
284
- taskId: options.taskId,
285
- status,
286
- validationStatus,
287
- workflowGateStatus: gateDecision.status,
288
- runtimeJudgment,
289
- lifecycleGate: workflowGate.lifecycleGate,
290
- primaryReason: workflowGate.primaryReason,
291
- evidence,
292
- gaps
293
- });
294
- await recordRuntimeProjection(projectRoot, 'test_evidence_run', `${context.partition}:${options.taskId}:${state.runId}`, unifiedEvidence);
295
- await recordTestWorkflowProjection(projectRoot, {
296
- taskId: options.taskId,
297
- stageRun: orchestration.stageRun,
298
- status,
299
- completedAt,
300
- evidence,
301
- gaps,
302
- riskDecision: orchestration.riskDecision
303
- });
304
- await persistTestGateOutcome(projectRoot, state.runId, options.taskId, status, validationStatus, commands, evidence, validationArtifact?.runRelativePath ?? null, gateDecision);
305
- if (ownsValidationWave) {
306
- await recordRuntimeValidationWaveRun(projectRoot, {
307
- waveRunId: validationWaveRunId,
308
- partition: context.partition,
309
- runId: state.runId,
310
- taskIds: validationWaveTaskIds,
311
- status,
312
- environmentSessionId: validationEnvironmentSessionId,
313
- startedAt,
314
- completedAt,
315
- payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId, testRunId, evidence, gaps, workflowGateDecision: gateDecision }
316
- });
317
- await recordRuntimeValidationEnvironmentSession(projectRoot, {
318
- sessionId: validationEnvironmentSessionId,
319
- partition: context.partition,
320
- runId: state.runId,
321
- waveRunId: validationWaveRunId,
322
- status: status === 'PASS' ? 'completed' : status === 'FAIL' ? 'failed' : 'blocked',
323
- reuseKey: `${context.partition}:${options.taskId}`,
324
- createdAt: startedAt,
325
- updatedAt: completedAt,
326
- payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId, validationStatus, workflowGateStatus: gateDecision.status, status }
327
- });
328
- }
329
-
330
- await appendEvent(projectRoot, state.runId, {
331
- event: status === 'PASS' ? 'test_runtime_passed' : 'test_runtime_blocked',
332
- runId: state.runId,
333
- summary: `SDD test runtime ${status} for ${options.taskId}`,
334
- data: { taskId: options.taskId, testRunId, status, validationStatus, evidence, gaps, gateDecisionId: gateDecision.decisionId, gateStatus: gateDecision.status }
335
- });
336
-
337
- return {
338
- contract: 'sdd-test-runtime-v1',
339
- runId: state.runId,
340
- testRunId,
341
- validationWaveRunId,
342
- validationEnvironmentSessionId,
343
- branch: context.partition,
344
- taskId: options.taskId,
345
- status,
346
- validationStatus,
347
- workflowGateStatus: gateDecision.status,
348
- runtimeJudgment,
349
- workflowGateDecision: gateDecision,
350
- verifyContractStatus: verifyInspection.status,
351
- verifyContractAction: verifyContract.action,
352
- lifecycleGate: workflowGate.lifecycleGate,
353
- lifecycleProfile: workflowGate.lifecycleProfile,
354
- approvalPolicy: workflowGate.approvalPolicy,
355
- requiredStages: workflowGate.requiredStages,
356
- primaryReason: workflowGate.primaryReason,
357
- commandStatus,
358
- evidenceCoverage,
359
- policyJudgment,
360
- acceptanceCoverage,
361
- capabilityEvidence,
362
- commands,
363
- steps,
364
- validationArtifact: validationArtifact?.runRelativePath ?? null,
365
- indexArtifact: null,
366
- gaps,
367
- next: nextForTestResult(status, runtimeJudgment, context.partition, options.taskId, workflowGate.nextAction, gateDecision)
368
- };
369
- }
370
-
371
- async function recordAcceptanceEvidenceMaps(projectRoot: string, waveRunId: string, testRunId: string, partition: string, runId: string, taskId: string, acceptanceCoverage: AcceptanceEvidenceCoverage[]): Promise<void> {
372
- const createdAt = new Date().toISOString();
373
- for (const coverage of acceptanceCoverage) {
374
- await recordRuntimeAcceptanceEvidenceMap(projectRoot, {
375
- mapId: runtimeScopedId(waveRunId, testRunId, taskId, coverage.acceptanceRef),
376
- waveRunId,
377
- testRunId,
378
- partition,
379
- runId,
380
- taskId,
381
- acceptanceRef: coverage.acceptanceRef,
382
- status: coverage.status,
383
- evidenceRefs: coverage.evidenceRefs.map((ref) => ref.ref),
384
- gaps: coverage.gaps,
385
- createdAt,
386
- payload: coverage
387
- });
388
- }
389
- }
390
-
391
- async function recordTestWorkflowProjection(projectRoot: string, input: { taskId: string; stageRun: StageRun; status: SddTestStatus; completedAt: string; evidence: string[]; gaps: string[]; riskDecision: LifecycleRiskDecision }): Promise<void> {
392
- const outputRefs = input.evidence.map((ref) => ({ kind: 'artifact' as const, ref }));
393
- const completedStage: StageRun = {
394
- ...input.stageRun,
395
- status: input.status === 'PASS' ? 'completed' : input.status === 'FAIL' ? 'failed' : 'blocked',
396
- outputRefs,
397
- blockingReasons: input.status === 'PASS' ? [] : input.gaps.length > 0 ? input.gaps : [`SDD test ${input.status}.`],
398
- updatedAt: input.completedAt
399
- };
400
- await recordStageRunProjection(projectRoot, completedStage);
401
-
402
- const handoff: WorkflowHandoff = {
403
- contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
404
- id: `${completedStage.id}:handoff:goal-verify`,
405
- scope: completedStage.scope,
406
- fromStage: 'test',
407
- toStage: 'goal-verify',
408
- fromAgent: 'validator',
409
- toAgent: 'verifier',
410
- status: input.status === 'PASS' ? 'proposed' : 'blocked',
411
- outputRefs,
412
- requiredInputRefs: [{ kind: 'task', ref: input.taskId }],
413
- riskDecisionRef: input.stageRun.decisionRefs[0] ?? { kind: 'task', ref: input.taskId },
414
- evidenceRefs: outputRefs,
415
- openQuestions: [],
416
- blockingGaps: input.status === 'PASS' ? [] : completedStage.blockingReasons,
417
- createdAt: input.completedAt,
418
- decidedAt: input.completedAt
419
- };
420
- const validation = validateWorkflowHandoff({ handoff, sourceStageRun: completedStage, lifecycleRiskDecision: input.riskDecision });
421
- await recordWorkflowHandoffProjection(projectRoot, validation.valid ? handoff : { ...handoff, status: 'blocked', blockingGaps: validation.issues, decidedAt: input.completedAt });
422
- }
423
-
424
-
425
- async function ensureVerifyContractForTest(projectRoot: string, branch: string): Promise<{ inspection: VerifyContractInspection; action: VerifyContractAction }> {
426
- const inspection = await inspectVerifyContract(projectRoot, { branch, branchSource: 'cli_option' });
427
- return { inspection, action: inspection.status === 'PASS' ? 'none' : 'blocked' };
428
- }
429
-
430
- function verifyContractBlocker(inspection: VerifyContractInspection): string {
431
- const issueSummary = inspection.issues.map((issue) => `${issue.field}: ${issue.message}`).join(' ');
432
- return `verify.md contract is ${inspection.status}; ${issueSummary || 'inspect verify.md before executing tests.'}`;
433
- }
434
-
435
- async function hasReviewerCheckpoint(projectRoot: string, state: RunState, taskId: string): Promise<boolean> {
436
- const artifactPaths = new Set(state.artifacts
437
- .filter((artifact) => artifact.task === taskId && (artifact.agent === 'reviewer' || artifact.kind === 'review'))
438
- .map((artifact) => artifact.path));
439
- const branch = state.partition ?? state.gitBranch ?? 'unscoped';
440
- const payloads = await listRuntimeArtifactPayloads(projectRoot, { runId: state.runId, taskId });
441
- for (const payload of payloads) {
442
- const fileName = payload.logicalRef.replace(/\\/g, '/').split('/').filter(Boolean).pop();
443
- if (fileName && (payload.artifactRole === 'review' || /review/i.test(fileName))) {
444
- artifactPaths.add(toBranchStageEvidenceRef(branch, 'do', fileName));
445
- }
446
- }
447
-
448
- for (const artifactPath of artifactPaths) {
449
- const report = await validateSddResultArtifact(projectRoot, state.runId, artifactPath, { expectedTask: taskId, expectedAgent: 'reviewer' });
450
- if (report.valid && report.result?.status === 'PASS') {
451
- return true;
452
- }
453
- }
454
-
455
- return false;
456
- }
457
-
458
- export function renderSddTestResult(result: SddTestResult): string {
459
- return [
460
- `SDD test ${result.taskId}`,
461
- '',
462
- resultSentenceForTest(result),
463
- '',
464
- 'Decision:',
465
- `- validation_status=${result.validationStatus}`,
466
- `- workflow_gate_status=${result.workflowGateStatus}`,
467
- `- workflow_gate_decision=${result.workflowGateDecision.decisionId}`,
468
- '',
469
- 'Why:',
470
- `- ${result.primaryReason}`,
471
- `- capability_evidence=${capabilityEvidenceSummary(result.capabilityEvidence)}`,
472
- '',
473
- 'Next:',
474
- `- ${result.next}`
475
- ].join('\n');
476
- }
477
-
478
-
479
- async function runCommandStep(projectRoot: string, runId: string, branch: string, taskId: string, testRunId: string, sequence: number, commandInput: NormalizedSddTestCommand, acceptanceRefs: string[], timeoutMs: number, model: SddTaskModel, task: SddTask, validationScopeTaskIds: string[], validationScopeAcceptanceRefs: string[]): Promise<SddTestCommandStep> {
480
- const cachePlan = buildValidationCachePlan({ branch, model, task, command: commandInput.command, argv: commandInput.argv, shell: commandInput.shell, validationScopeTaskIds, acceptanceRefs: validationScopeAcceptanceRefs });
481
- const cached = cachePlan.eligible ? await readRuntimeValidationCacheEntry(projectRoot, { branchSlug: branch, cacheKey: cachePlan.cacheKey }) : null;
482
- if (cached) {
483
- return recordCachedCommandStep(projectRoot, runId, branch, taskId, testRunId, sequence, commandInput, acceptanceRefs, cachePlan.cacheKey, cached);
484
- }
485
-
486
- const started = Date.now();
487
- const startedAt = new Date(started).toISOString();
488
- const executed = await executeCommand(projectRoot, commandInput, timeoutMs);
489
- const ended = Date.now();
490
- const endedAt = new Date(ended).toISOString();
491
- const durationMs = ended - started;
492
- const status: SddTestStepStatus = executed.timedOut || executed.error ? 'blocked' : executed.exitCode === 0 ? 'pass' : 'fail';
493
- const stepId = `${testRunId}-${String(sequence).padStart(3, '0')}`;
494
- const cacheStatus = cachePlan.eligible ? 'miss' : 'unsafe';
495
- const shouldPersistOutputArtifact = shouldPersistCommandOutputArtifact(status, executed, cacheStatus, cachePlan.unsafeReasons);
496
- const outputFileName = shouldPersistOutputArtifact ? `test-${taskId}-${String(sequence).padStart(3, '0')}.log` : null;
497
- const outputRef = outputFileName ? toBranchStageEvidenceRef(branch, 'test', outputFileName) : null;
498
- const output = outputFileName ? renderCommandOutput(commandInput, status, executed, durationMs, cacheStatus, cachePlan.cacheKey, null, cachePlan.unsafeReasons) : null;
499
- if (outputFileName && outputRef && output) {
500
- await recordRuntimeOnlyArtifact(projectRoot, runId, outputFileName, output, { logicalRef: outputRef, branch, taskId, artifactRole: 'test-command-output' });
501
- await appendArtifactHashLedgerEntry(projectRoot, {
502
- runId,
503
- taskId,
504
- branch,
505
- artifactPath: outputRef,
506
- content: output,
507
- status: 'recorded'
508
- });
509
- }
510
- const stdoutDigest = hashDocumentContent(executed.stdout);
511
- const stderrDigest = hashDocumentContent(executed.stderr);
512
- const outputSummary = summarizeCommandOutput(executed);
513
- await appendInvocationLedgerEntry(projectRoot, {
514
- runId,
515
- taskId,
516
- branch,
517
- kind: 'command',
518
- ref: commandInput.command,
519
- status,
520
- artifactPath: outputRef,
521
- outputHash: output ? hashDocumentContent(output) : null,
522
- materialRefs: outputRef ? [outputRef] : [],
523
- metadata: {
524
- stepId,
525
- source: 'sdd-test',
526
- exitCode: executed.exitCode,
527
- durationMs,
528
- stdoutBytes: executed.stdoutBytes,
529
- stderrBytes: executed.stderrBytes,
530
- truncated: executed.truncated,
531
- acceptanceRefs: acceptanceRefs.join(','),
532
- shell: commandInput.shell,
533
- argv: commandInput.argv ? JSON.stringify(commandInput.argv) : null,
534
- stdoutDigest,
535
- stderrDigest,
536
- cacheKey: cachePlan.cacheKey,
537
- cacheStatus,
538
- cacheUnsafeReasons: cachePlan.unsafeReasons.join(',')
539
- }
540
- });
541
- const step: SddTestCommandStep = {
542
- stepId,
543
- command: commandInput.command,
544
- argv: commandInput.argv,
545
- shell: commandInput.shell,
546
- acceptanceRefs,
547
- status,
548
- exitCode: executed.exitCode,
549
- signal: executed.signal,
550
- durationMs,
551
- outputArtifact: outputRef,
552
- stdoutBytes: executed.stdoutBytes,
553
- stderrBytes: executed.stderrBytes,
554
- truncated: executed.truncated,
555
- startedAt,
556
- endedAt,
557
- cwd: projectRoot,
558
- stdoutDigest,
559
- stderrDigest,
560
- outputSummary,
561
- cacheStatus,
562
- cacheKey: cachePlan.cacheKey,
563
- cacheSourceTestRunId: null,
564
- cacheUnsafeReasons: cachePlan.unsafeReasons
565
- };
566
- await recordRuntimeTestStep(projectRoot, {
567
- stepId,
568
- testRunId,
569
- runId,
570
- taskId,
571
- command: commandInput.command,
572
- status,
573
- exitCode: executed.exitCode,
574
- durationMs,
575
- outputArtifact: outputRef,
576
- payload: step
577
- });
578
- if (status === 'pass' && cachePlan.eligible) {
579
- const now = new Date().toISOString();
580
- await recordRuntimeValidationCacheEntry(projectRoot, {
581
- cacheKey: cachePlan.cacheKey,
582
- branchSlug: branch,
583
- command: commandInput.command,
584
- status: 'valid',
585
- sourceTestRunId: testRunId,
586
- sourceRunId: runId,
587
- sourceEvidenceSetId: null,
588
- outputArtifact: outputRef,
589
- stdoutDigest,
590
- stderrDigest,
591
- createdAt: now,
592
- lastUsedAt: now,
593
- payload: { cachePlan, step }
594
- });
595
- }
596
- return step;
597
- }
598
-
599
- async function recordCachedCommandStep(projectRoot: string, runId: string, branch: string, taskId: string, testRunId: string, sequence: number, commandInput: NormalizedSddTestCommand, acceptanceRefs: string[], cacheKey: string, cached: RuntimeValidationCacheEntryRecord): Promise<SddTestCommandStep> {
600
- const now = new Date().toISOString();
601
- const stepId = `${testRunId}-${String(sequence).padStart(3, '0')}`;
602
- const step: SddTestCommandStep = {
603
- stepId,
604
- command: commandInput.command,
605
- argv: commandInput.argv,
606
- shell: commandInput.shell,
607
- acceptanceRefs,
608
- status: 'pass',
609
- exitCode: 0,
610
- signal: null,
611
- durationMs: 0,
612
- outputArtifact: null,
613
- stdoutBytes: 0,
614
- stderrBytes: 0,
615
- truncated: false,
616
- startedAt: now,
617
- endedAt: now,
618
- cwd: projectRoot,
619
- stdoutDigest: cached.stdoutDigest,
620
- stderrDigest: cached.stderrDigest,
621
- outputSummary: `cache_hit source_test_run=${cached.sourceTestRunId} source_artifact=${cached.outputArtifact ?? 'none'}`,
622
- cacheStatus: 'hit',
623
- cacheKey,
624
- cacheSourceTestRunId: cached.sourceTestRunId,
625
- cacheUnsafeReasons: []
626
- };
627
- await appendInvocationLedgerEntry(projectRoot, {
628
- runId,
629
- taskId,
630
- branch,
631
- kind: 'command',
632
- ref: commandInput.command,
633
- status: 'pass',
634
- artifactPath: null,
635
- outputHash: null,
636
- materialRefs: cached.outputArtifact ? [cached.outputArtifact] : [],
637
- metadata: { source: 'sdd-test-cache', stepId, cacheKey, sourceTestRunId: cached.sourceTestRunId, sourceEvidenceSetId: cached.sourceEvidenceSetId, acceptanceRefs: acceptanceRefs.join(',') }
638
- });
639
- await recordRuntimeTestStep(projectRoot, {
640
- stepId,
641
- testRunId,
642
- runId,
643
- taskId,
644
- command: commandInput.command,
645
- status: 'pass',
646
- exitCode: 0,
647
- durationMs: 0,
648
- outputArtifact: null,
649
- payload: step
650
- });
651
- await recordRuntimeValidationCacheUse(projectRoot, {
652
- useId: runtimeScopedId(cacheKey, testRunId, taskId, stepId),
653
- cacheKey,
654
- branchSlug: branch,
655
- testRunId,
656
- runId,
657
- taskId,
658
- sourceTestRunId: cached.sourceTestRunId,
659
- sourceEvidenceSetId: cached.sourceEvidenceSetId,
660
- reusedAt: now,
661
- mappedTaskIds: [taskId],
662
- reason: 'same validation cache key within compatible branch contract scope',
663
- payload: { sourceRunId: cached.sourceRunId, sourceArtifact: cached.outputArtifact }
664
- });
665
- return step;
666
- }
667
-
668
- function executeCommand(projectRoot: string, commandInput: NormalizedSddTestCommand, timeoutMs: number): Promise<{ exitCode: number | null; signal: string | null; stdout: string; stderr: string; stdoutBytes: number; stderrBytes: number; truncated: boolean; timedOut: boolean; error: string | null }> {
669
- return new Promise((resolve) => {
670
- const child = commandInput.argv
671
- ? spawn(commandInput.argv[0], commandInput.argv.slice(1), { cwd: projectRoot, shell: false, windowsHide: true, env: process.env })
672
- : spawn(commandInput.command, { cwd: projectRoot, shell: true, windowsHide: true, env: process.env });
673
- let stdout = '';
674
- let stderr = '';
675
- let stdoutBytes = 0;
676
- let stderrBytes = 0;
677
- let truncated = false;
678
- let settled = false;
679
- let timedOut = false;
680
- const timer = setTimeout(() => {
681
- timedOut = true;
682
- child.kill();
683
- }, timeoutMs);
684
- const finish = (result: { exitCode: number | null; signal: string | null; error: string | null }) => {
685
- if (settled) {
686
- return;
687
- }
688
- settled = true;
689
- clearTimeout(timer);
690
- resolve({ ...result, stdout, stderr, stdoutBytes, stderrBytes, truncated, timedOut });
691
- };
692
- child.stdout?.on('data', (chunk: Buffer) => {
693
- stdoutBytes += chunk.length;
694
- const next = chunk.toString('utf8');
695
- if (Buffer.byteLength(stdout, 'utf8') < MAX_CAPTURE_BYTES) {
696
- stdout += next;
697
- } else {
698
- truncated = true;
699
- }
700
- });
701
- child.stderr?.on('data', (chunk: Buffer) => {
702
- stderrBytes += chunk.length;
703
- const next = chunk.toString('utf8');
704
- if (Buffer.byteLength(stderr, 'utf8') < MAX_CAPTURE_BYTES) {
705
- stderr += next;
706
- } else {
707
- truncated = true;
708
- }
709
- });
710
- child.on('error', (error) => finish({ exitCode: null, signal: null, error: error.message }));
711
- child.on('close', (code, signal) => finish({ exitCode: code, signal, error: null }));
712
- });
713
- }
714
-
715
- async function writeValidationArtifact(projectRoot: string, runId: string, branch: string, task: SddTask, status: SddTestStatus, steps: SddTestCommandStep[], gaps: string[], _capabilityEvidence: CapabilityEvidenceClassification[]): Promise<{ absolutePath: string; runRelativePath: string }> {
716
- const artifactPath = validationArtifactPath(task);
717
- const stageEvidenceRef = toBranchStageEvidenceRef(branch, 'test', artifactPath);
718
- const resultStatus = status === 'PASS' ? 'PASS' : status === 'FAIL' ? 'FAIL' : 'BLOCKED';
719
- const content = `# Test Validation ${task.id}\n\n\`\`\`sdd-result\ncontract: ${SDD_RESULT_CONTRACT}\nversion: ${SDD_RESULT_VERSION}\nagent: validator\ntask: ${task.id}\nstatus: ${resultStatus}\nartifacts:\n - ${stageEvidenceRef}\n\`\`\`\n\n## Test Runtime\n\n- status: ${status}\n- commands:\n${steps.length > 0 ? steps.map((step) => ` - [${step.status}] ${step.command}`).join('\n') : ' - none'}\n- gaps:\n${gaps.length > 0 ? gaps.map((gap) => ` - ${gap}`).join('\n') : ' - none'}\n\n## Acceptance Evidence\n\n${renderEvidenceBlocks(task, status, stageEvidenceRef, steps)}\n`;
720
- const written = await recordStageEvidenceArtifact(projectRoot, runId, stageEvidenceRef, content, { taskId: task.id, artifactRole: 'test-validation' });
721
- return { ...written, runRelativePath: stageEvidenceRef };
722
- }
723
-
724
- function validationArtifactPath(task: SddTask): string {
725
- return `test-validation-${task.id}.md`;
726
- }
727
-
728
-
729
-
730
- async function persistTestRunState(projectRoot: string, state: RunState, taskId: string, validationStatus: SddTestStatus, commands: string[], evidence: string[], validationArtifact: string | null): Promise<void> {
731
- const latest = await readRunState(projectRoot, state.runId);
732
- const knownArtifacts = new Set(latest.artifacts.map((artifact) => artifact.path));
733
- const now = new Date().toISOString();
734
- const nextArtifacts = evidence
735
- .filter((artifactPath) => !knownArtifacts.has(artifactPath))
736
- .map((artifactPath) => ({ path: artifactPath, kind: testArtifactKind(artifactPath), task: taskId, agent: 'test-runtime', createdAt: now }));
737
- const existingTaskState = latest.tasks[taskId];
738
- await writeRunState(projectRoot, {
739
- ...latest,
740
- status: validationStatus === 'PASS' ? 'running' : validationStatus === 'FAIL' ? 'failed' : 'blocked',
741
- phase: 'test',
742
- currentTask: taskId,
743
- tasks: {
744
- ...latest.tasks,
745
- [taskId]: {
746
- ...baseRuntimeTaskState(existingTaskState),
747
- status: validationStatus === 'PASS' ? 'validation_passed_pending_gate' : validationStatus === 'FAIL' ? 'validation_failed' : 'validation_blocked',
748
- implementationStatus: existingTaskState?.implementationStatus ?? 'not_started',
749
- verificationStatus: verificationStatusFromTest(validationStatus),
750
- testStatus: validationStatus,
751
- evidence
752
- }
753
- },
754
- artifacts: [...latest.artifacts, ...nextArtifacts],
755
- validation: {
756
- status: validationStatus === 'PASS' ? 'pass' : validationStatus === 'FAIL' ? 'fail' : 'blocked',
757
- commands,
758
- evidence
759
- }
760
- });
761
- }
762
-
763
- async function persistTestGateOutcome(projectRoot: string, runId: string, taskId: string, status: SddTestStatus, validationStatus: SddTestStatus, commands: string[], evidence: string[], validationArtifact: string | null, gateDecision: WorkflowGateDecision): Promise<void> {
764
- const latest = await readRunState(projectRoot, runId);
765
- const knownArtifacts = new Set(latest.artifacts.map((artifact) => artifact.path));
766
- const now = new Date().toISOString();
767
- const nextArtifacts = evidence
768
- .filter((artifactPath) => !knownArtifacts.has(artifactPath))
769
- .map((artifactPath) => ({ path: artifactPath, kind: testArtifactKind(artifactPath), task: taskId, agent: 'test-runtime', createdAt: now }));
770
- const existingTaskState = latest.tasks[taskId];
771
- await writeRunState(projectRoot, {
772
- ...latest,
773
- status: status === 'PASS' ? 'completed' : status === 'FAIL' ? 'failed' : 'blocked',
774
- phase: 'test',
775
- currentTask: taskId,
776
- tasks: {
777
- ...latest.tasks,
778
- [taskId]: {
779
- ...baseRuntimeTaskState(existingTaskState),
780
- status: runtimeTaskStatusAfterGate(status, validationStatus),
781
- implementationStatus: existingTaskState?.implementationStatus ?? 'not_started',
782
- verificationStatus: verificationStatusFromTest(validationStatus),
783
- testStatus: status,
784
- validationStatus,
785
- workflowGateStatus: gateDecision.status,
786
- workflowGateDecisionId: gateDecision.decisionId,
787
- evidence
788
- }
789
- },
790
- artifacts: [...latest.artifacts, ...nextArtifacts],
791
- validation: {
792
- status: validationStatus === 'PASS' ? 'pass' : validationStatus === 'FAIL' ? 'fail' : 'blocked',
793
- commands,
794
- evidence
795
- }
796
- });
797
- }
798
-
799
- function testArtifactKind(artifactPath: string): string {
800
- const fileName = artifactPath.split('/').pop() ?? artifactPath;
801
- if (fileName.startsWith('test-validation-')) {
802
- return 'test-validation';
803
- }
804
- return 'test';
805
- }
806
-
807
- function baseRuntimeTaskState(existing: RunStateTaskRuntime | undefined): RunStateTaskRuntime {
808
- return {
809
- status: existing?.status ?? 'not_started',
810
- implementationStatus: existing?.implementationStatus ?? 'not_started',
811
- verificationStatus: existing?.verificationStatus ?? 'not_run',
812
- validationBatch: existing?.validationBatch ?? null,
813
- validationTiming: existing?.validationTiming ?? 'task_end',
814
- requiresVerifyBeforeNext: existing?.requiresVerifyBeforeNext ?? true,
815
- gaps: existing?.gaps,
816
- artifacts: existing?.artifacts,
817
- testStatus: existing?.testStatus,
818
- workflowGateStatus: existing?.workflowGateStatus,
819
- workflowGateDecisionId: existing?.workflowGateDecisionId,
820
- evidence: existing?.evidence
821
- };
822
- }
823
-
824
- function verificationStatusFromTest(validationStatus: SddTestStatus): RunStateTaskRuntime['verificationStatus'] {
825
- if (validationStatus === 'PASS') {
826
- return 'pass';
827
- }
828
- return validationStatus === 'FAIL' ? 'failed' : 'blocked';
829
- }
830
-
831
- function runtimeTaskStatusAfterGate(status: SddTestStatus, validationStatus: SddTestStatus): string {
832
- if (status === 'PASS') {
833
- return 'implemented_verified';
834
- }
835
- if (validationStatus === 'PASS') {
836
- return 'workflow_gate_blocked';
837
- }
838
- return validationStatus === 'FAIL' ? 'validation_failed' : 'validation_blocked';
839
- }
840
-
841
- function deriveCommandStatus(gaps: string[], steps: SddTestCommandStep[]): TestEvidenceStatus {
842
- if (gaps.length > 0 || steps.some((step) => step.status === 'blocked')) {
843
- return 'BLOCKED';
844
- }
845
- if (steps.some((step) => step.status === 'fail')) {
846
- return 'FAIL';
847
- }
848
- return 'PASS';
849
- }
850
-
851
- function buildAcceptanceCoverage(task: SddTask | null, steps: SddTestCommandStep[], commandStatus: TestEvidenceStatus, acceptanceRefsOverride: string[] | undefined): AcceptanceEvidenceCoverage[] {
852
- const acceptanceRefs = acceptanceRefsOverride && acceptanceRefsOverride.length > 0 ? [...new Set(acceptanceRefsOverride)] : task ? taskAcceptanceRefs(task) : [];
853
- return acceptanceRefs.map((acceptanceRef) => {
854
- const mappedSteps = steps.filter((step) => step.acceptanceRefs.includes(acceptanceRef));
855
- const hasPassingEvidence = mappedSteps.some((step) => step.status === 'pass');
856
- const hasFailingEvidence = mappedSteps.some((step) => step.status === 'fail' || step.status === 'blocked');
857
- const status: EvidenceCoverageStatus = hasPassingEvidence && !hasFailingEvidence && commandStatus === 'PASS'
858
- ? 'complete'
859
- : mappedSteps.length > 0
860
- ? 'partial'
861
- : 'missing';
862
- return {
863
- acceptanceRef,
864
- status,
865
- evidenceRefs: mappedSteps.map(commandStepRuntimeRef),
866
- gaps: status === 'complete' ? [] : [`Acceptance ${acceptanceRef} has no complete non-stale evidence from sdd test task.`]
867
- };
868
- });
869
- }
870
-
871
- function runtimeEvidenceRefs(validationArtifact: string | null, steps: SddTestCommandStep[]): string[] {
872
- return [validationArtifact, ...steps.map((step) => step.outputArtifact)].filter((item): item is string => Boolean(item));
873
- }
874
-
875
- function commandStepRuntimeRef(step: SddTestCommandStep): { kind: 'command'; ref: string } {
876
- return { kind: 'command', ref: step.stepId };
877
- }
878
-
879
- function artifactRuntimeRefs(ref: string | null): Array<{ kind: 'artifact'; ref: string }> {
880
- return ref ? [{ kind: 'artifact', ref }] : [];
881
- }
882
-
883
- function summarizeEvidenceCoverage(acceptanceCoverage: AcceptanceEvidenceCoverage[]): EvidenceCoverageStatus {
884
- if (acceptanceCoverage.length === 0) {
885
- return 'missing';
886
- }
887
- if (acceptanceCoverage.every((coverage) => coverage.status === 'complete')) {
888
- return 'complete';
889
- }
890
- if (acceptanceCoverage.some((coverage) => coverage.status === 'complete' || coverage.status === 'partial')) {
891
- return 'partial';
892
- }
893
- return 'missing';
894
- }
895
-
896
- function derivePolicyJudgment(commandStatus: TestEvidenceStatus, evidenceCoverage: EvidenceCoverageStatus): SddTestStatus {
897
- if (commandStatus === 'FAIL') {
898
- return 'FAIL';
899
- }
900
- if (commandStatus === 'BLOCKED' || evidenceCoverage !== 'complete') {
901
- return 'BLOCKED';
902
- }
903
- return 'PASS';
904
- }
905
-
906
- async function resolveTestRuntimeDurableGap(
907
- projectRoot: string,
908
- branch: string,
909
- runId: string,
910
- taskId: string,
911
- validationStatus: SddTestStatus,
912
- gaps: string[]
913
- ): Promise<void> {
914
- if (validationStatus !== 'PASS' || gaps.length > 0) {
915
- return;
916
- }
917
- await updateRuntimeDurableGapStatus(projectRoot, {
918
- gapId: testRuntimeGapId(branch, runId, taskId),
919
- status: 'resolved',
920
- source: 'gate_policy',
921
- payload: { validationStatus, gaps }
922
- });
923
- }
924
-
925
- async function recordTestRuntimeDurableGap(
926
- projectRoot: string,
927
- branch: string,
928
- runId: string,
929
- taskId: string,
930
- status: SddTestStatus,
931
- validationStatus: SddTestStatus,
932
- runtimeJudgment: WorkflowGateStatus,
933
- gaps: string[],
934
- evidenceRefs: string[]
935
- ): Promise<void> {
936
- if (status === 'PASS' && gaps.length === 0) {
937
- return;
938
- }
939
- const message = gaps[0] ?? (runtimeJudgment === 'PASS' ? `Validation status is ${validationStatus}.` : `Workflow gate status is ${runtimeJudgment}.`);
940
- await recordRuntimeDurableGap(projectRoot, {
941
- gapId: testRuntimeGapId(branch, runId, taskId),
942
- partition: branch,
943
- taskId,
944
- runId,
945
- stage: 'test',
946
- gate: 'test',
947
- source: 'runtime',
948
- category: runtimeJudgment === 'PASS' ? 'validation' : 'workflow_gate',
949
- severity: 'blocking',
950
- status: 'open',
951
- message,
952
- recommendation: `Resolve test runtime gaps for ${taskId}, then rerun ${testValidationUnitCommand(branch, taskId)}.`,
953
- evidenceRefs,
954
- proposalRefs: [],
955
- sourceRefs: [],
956
- payload: { status, validationStatus, runtimeJudgment, gaps }
957
- });
958
- }
959
-
960
- function testRuntimeGapId(branch: string, _runId: string, taskId: string): string {
961
- return runtimeScopedId(branch, taskId, 'test-runtime-gap');
962
- }
963
-
964
- function finalStatusForTest(validationStatus: SddTestStatus, runtimeJudgment: WorkflowGateStatus): SddTestStatus {
965
- if (validationStatus !== 'PASS') {
966
- return validationStatus;
967
- }
968
- return runtimeJudgment === 'PASS' || runtimeJudgment === 'WARN' ? 'PASS' : 'BLOCKED';
969
- }
970
-
971
- function buildUnifiedTestEvidenceRun(id: string, branch: string, runId: string, taskId: string, commandStatus: TestEvidenceStatus, evidenceCoverage: EvidenceCoverageStatus, policyJudgment: TestEvidenceStatus, status: SddTestStatus, runtimeJudgment: WorkflowGateStatus, steps: SddTestCommandStep[], acceptanceCoverage: AcceptanceEvidenceCoverage[], capabilityEvidence: CapabilityEvidenceClassification[], gaps: string[], gateNextAction: string | null, gateDecision: WorkflowGateDecision | null): UnifiedTestEvidenceRun {
972
- return {
973
- contract: TEST_EVIDENCE_RUN_CONTRACT_VERSION,
974
- id,
975
- scope: { branch, taskId, runId },
976
- commandStatus,
977
- evidenceCoverage,
978
- policyJudgment,
979
- commands: steps.map((step) => ({
980
- command: step.command,
981
- status: step.status === 'pass' ? 'PASS' : step.status === 'fail' ? 'FAIL' : 'BLOCKED',
982
- outputRef: step.outputArtifact ? { kind: 'artifact', ref: step.outputArtifact } : undefined,
983
- evidenceRefs: [commandStepRuntimeRef(step), ...artifactRuntimeRefs(step.outputArtifact)],
984
- acceptanceRefs: step.acceptanceRefs,
985
- startedAt: new Date(Date.now() - step.durationMs).toISOString(),
986
- completedAt: new Date().toISOString()
987
- })),
988
- acceptanceCoverage,
989
- capabilityEvidence,
990
- gaps: [...gaps, ...acceptanceCoverage.flatMap((coverage) => coverage.gaps)],
991
- next: nextForTestResult(status, runtimeJudgment, branch, taskId, gateNextAction, gateDecision),
992
- generatedAt: new Date().toISOString()
993
- };
994
- }
995
-
996
- function buildCapabilityEvidenceClassification(decision: AgentCapabilityRouteDecision | null, steps: SddTestCommandStep[]): CapabilityEvidenceClassification[] {
997
- if (!decision) {
998
- return [{
999
- class: 'diagnostic',
1000
- source: 'runtime_diagnostic',
1001
- domainOrSourceId: 'capability-routing',
1002
- evidenceRefs: [],
1003
- acceptanceRefs: [],
1004
- provenanceRefs: [],
1005
- reason: 'Capability routing did not run; no capability output is accepted as test evidence.'
1006
- }];
1007
- }
1008
- const acceptanceRefs = [...new Set(steps.flatMap((step) => step.acceptanceRefs))];
1009
- const professionalEvidence = decision.selectedDomains.map((domain) => ({
1010
- class: 'candidate' as const,
1011
- source: 'professional_capability' as const,
1012
- domainOrSourceId: domain.domain,
1013
- evidenceRefs: [],
1014
- acceptanceRefs,
1015
- provenanceRefs: [{ kind: 'projection' as const, ref: `capability:${domain.capabilityId}` }],
1016
- reason: `${domain.reason}; capability output is advisory candidate evidence until accepted by command evidence and policy refs.`
1017
- }));
1018
- const externalEvidence = decision.rejectedExternalSources.map((source) => ({
1019
- class: capabilityClassForRejectedSource(source.quarantineStatus) as CapabilityEvidenceClassification['class'],
1020
- source: 'external_source' as const,
1021
- domainOrSourceId: source.sourceId,
1022
- evidenceRefs: [],
1023
- acceptanceRefs: [],
1024
- provenanceRefs: [{ kind: 'external' as const, ref: source.sourceId }],
1025
- reason: source.reason
1026
- }));
1027
- return [...professionalEvidence, ...externalEvidence];
1028
- }
1029
-
1030
- function capabilityClassForRejectedSource(status: AgentCapabilityRouteDecision['rejectedExternalSources'][number]['quarantineStatus']): CapabilityEvidenceClassification['class'] {
1031
- if (status === 'denied') {
1032
- return 'blocked';
1033
- }
1034
- if (status === 'required' || status === 'quarantined') {
1035
- return 'quarantined';
1036
- }
1037
- return 'diagnostic';
1038
- }
1039
-
1040
-
1041
- function capabilityEvidenceSummary(items: CapabilityEvidenceClassification[]): string {
1042
- if (items.length === 0) {
1043
- return 'none';
1044
- }
1045
- const counts = new Map<CapabilityEvidenceClassification['class'], number>();
1046
- for (const item of items) {
1047
- counts.set(item.class, (counts.get(item.class) ?? 0) + 1);
1048
- }
1049
- return [...counts.entries()].map(([kind, count]) => `${kind}:${count}`).join(',');
1050
- }
1051
-
1052
- function nextForTestResult(status: SddTestStatus, runtimeJudgment: WorkflowGateStatus, branch: string, taskId: string, gateNextAction: string | null, gateDecision: WorkflowGateDecision | null): string {
1053
- const inspectHint = `Inspect runtime test read model for ${taskId}`;
1054
- if (status === 'PASS') {
1055
- return `sdd test close --branch ${branch} --target goal-verify --compact-json`;
1056
- }
1057
- if (gateNextAction) {
1058
- return gateNextAction;
1059
- }
1060
- if (runtimeJudgment === 'HUMAN_REQUIRED') {
1061
- return gateDecision ? `Create a decision card for workflow gate ${gateDecision.decisionId}, then rerun ${testValidationUnitCommand(branch, taskId)}.` : `Create a decision card, then rerun ${testValidationUnitCommand(branch, taskId)}.`;
1062
- }
1063
- if (runtimeJudgment === 'WARN') {
1064
- return gateDecision ? `Review workflow gate ${gateDecision.decisionId} warnings, then rerun ${testValidationUnitCommand(branch, taskId)} or proceed only with explicit review.` : `Review workflow gate warnings, then rerun ${testValidationUnitCommand(branch, taskId)}.`;
1065
- }
1066
- if (runtimeJudgment === 'ADVISORY_ONLY') {
1067
- return `Inspect advisor assessments for ${taskId}; advisory output cannot satisfy the test gate.`;
1068
- }
1069
- if (status === 'FAIL') {
1070
- return `${inspectHint}, fix failing validation commands, then rerun ${testValidationUnitCommand(branch, taskId)}.`;
1071
- }
1072
- return gateDecision ? `${inspectHint} and workflow gate ${gateDecision.decisionId}, resolve blockers, then rerun ${testValidationUnitCommand(branch, taskId)}.` : `${inspectHint}, fix command/evidence gaps, then rerun ${testValidationUnitCommand(branch, taskId)}.`;
1073
- }
1074
-
1075
-
1076
- function testValidationUnitCommand(branch: string, taskId: string): string {
1077
- return `sdd test task-${taskId} --branch ${branch}`;
1078
- }
1079
-
1080
- function resultSentenceForTest(result: SddTestResult): string {
1081
- if (result.status === 'PASS') {
1082
- return 'Validation and workflow gate passed; proceed to goal verification and truthAlignment before release readiness.';
1083
- }
1084
- if (result.validationStatus === 'PASS' && result.workflowGateStatus !== 'PASS') {
1085
- return `Validation passed, but workflow gate returned ${result.workflowGateStatus}.`;
1086
- }
1087
- if (result.commandStatus === 'BLOCKED') {
1088
- return 'Blocked before validation commands ran.';
1089
- }
1090
- return result.status === 'FAIL' ? 'Validation failed.' : 'Validation did not produce complete evidence.';
1091
- }
1092
-
1093
- function renderEvidenceBlocks(task: SddTask, status: SddTestStatus, sourceArtifact: string, steps: SddTestCommandStep[]): string {
1094
- const acceptances = task.acceptanceRefs.length > 0 ? task.acceptanceRefs : task.acceptance;
1095
- if (acceptances.length === 0) {
1096
- return 'No acceptance targets declared.';
1097
- }
1098
- const mappedEvidence = acceptances
1099
- .map((acceptance) => ({ acceptance, steps: steps.filter((step) => step.acceptanceRefs.includes(acceptance)) }))
1100
- .filter((item) => item.steps.length > 0);
1101
- if (mappedEvidence.length === 0) {
1102
- return 'No acceptance evidence emitted; validation commands are not explicitly mapped to acceptance refs.';
1103
- }
1104
- return mappedEvidence.map(({ acceptance, steps: mappedSteps }) => {
1105
- const evidenceStatus = evidenceStatusForMappedSteps(status, mappedSteps);
1106
- return `\`\`\`sdd-evidence\ncontract: ${SDD_EVIDENCE_CONTRACT}\nversion: ${SDD_EVIDENCE_VERSION}\ntask: ${task.id}\nacceptance: ${acceptance}\nstatus: ${evidenceStatus}\nclaim: Explicit validation mapping ${mappedSteps.map((step) => step.command).join(' && ')} produced ${evidenceStatus} for ${acceptance}.\nsource_artifact: ${sourceArtifact}\nevidence_refs:\n${mappedSteps.map(renderStepEvidenceRefs).join('\n')}\nprovenance_refs:\n - artifact:${sourceArtifact}\n${mappedSteps.map((step) => ` - command:${step.command}`).join('\n')}\npolicy_refs:\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-source-evidence\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-provenance\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-policy-rule\n\`\`\``;
1107
- }).join('\n\n');
1108
- }
1109
-
1110
- function normalizeTestCommandInputs(commandInputs: SddTestCommandInput[] | undefined, commands: string[] | undefined, taskValidation: string[]): NormalizedSddTestCommand[] {
1111
- if (commandInputs && commandInputs.length > 0) {
1112
- return commandInputs.map(normalizeTestCommandInput);
1113
- }
1114
- return (commands && commands.length > 0 ? commands : taskValidation).map((command) => ({ command, argv: null, shell: true }));
1115
- }
1116
-
1117
- function normalizeTestCommandInput(input: SddTestCommandInput): NormalizedSddTestCommand {
1118
- if (input.argv) {
1119
- const argv = input.argv.filter((item) => item.length > 0);
1120
- if (argv.length === 0) {
1121
- throw new Error('Command argv input must include an executable.');
1122
- }
1123
- return { command: argv.join(' '), argv, shell: false };
1124
- }
1125
- if (input.command) {
1126
- return { command: input.command, argv: null, shell: true };
1127
- }
1128
- throw new Error('Command input must include command or argv.');
1129
- }
1130
-
1131
- function acceptanceRefsForCommand(task: SddTask | null, command: string, acceptanceRefsOverride: string[] | undefined): string[] {
1132
- if (acceptanceRefsOverride && acceptanceRefsOverride.length > 0) {
1133
- return [...new Set(acceptanceRefsOverride)];
1134
- }
1135
- return [...new Set((task?.validationCommands ?? [])
1136
- .filter((entry) => entry.command === command)
1137
- .flatMap((entry) => entry.acceptanceRefs))];
1138
- }
1139
-
1140
- function taskAcceptanceRefs(task: SddTask): string[] {
1141
- const refs = task.acceptanceRefs.length > 0 ? task.acceptanceRefs : task.acceptance;
1142
- return [...new Set(refs)];
1143
- }
1144
-
1145
- function evidenceStatusForMappedSteps(status: SddTestStatus, steps: SddTestCommandStep[]): SddTestStatus {
1146
- if (steps.some((step) => step.status === 'fail')) {
1147
- return 'FAIL';
1148
- }
1149
- if (status === 'BLOCKED' || steps.some((step) => step.status === 'blocked')) {
1150
- return 'BLOCKED';
1151
- }
1152
- return 'PASS';
1153
- }
1154
-
1155
- function renderCommandOutput(commandInput: NormalizedSddTestCommand, status: SddTestStepStatus, executed: { exitCode: number | null; signal: string | null; stdout: string; stderr: string; truncated: boolean; timedOut: boolean; error: string | null }, durationMs: number, cacheStatus: 'hit' | 'miss' | 'unsafe' = 'unsafe', cacheKey: string | null = null, cacheSourceTestRunId: string | null = null, cacheUnsafeReasons: ValidationCacheUnsafeReason[] = []): string {
1156
- return `# Test Command Output\n\n- command: ${commandInput.command}\n- shell: ${commandInput.shell}\n- argv: ${commandInput.argv ? JSON.stringify(commandInput.argv) : 'none'}\n- status: ${status}\n- exit_code: ${executed.exitCode ?? 'none'}\n- signal: ${executed.signal ?? 'none'}\n- duration_ms: ${durationMs}\n- timed_out: ${executed.timedOut}\n- truncated: ${executed.truncated}\n- error: ${executed.error ?? 'none'}\n- cache_status: ${cacheStatus}\n- cache_key: ${cacheKey ?? 'none'}\n- cache_source_test_run: ${cacheSourceTestRunId ?? 'none'}\n- cache_unsafe_reasons: ${cacheUnsafeReasons.join(',') || 'none'}\n\n## stdout\n\n\`\`\`text\n${executed.stdout}\n\`\`\`\n\n## stderr\n\n\`\`\`text\n${executed.stderr}\n\`\`\`\n`;
1157
- }
1158
-
1159
- function shouldPersistCommandOutputArtifact(status: SddTestStepStatus, executed: { truncated: boolean; timedOut: boolean; error: string | null }, cacheStatus: 'hit' | 'miss' | 'unsafe', cacheUnsafeReasons: ValidationCacheUnsafeReason[]): boolean {
1160
- return status !== 'pass' || executed.truncated || executed.timedOut || Boolean(executed.error) || cacheStatus === 'unsafe' || cacheUnsafeReasons.length > 0;
1161
- }
1162
-
1163
- function renderStepEvidenceRefs(step: SddTestCommandStep): string {
1164
- const refs = [` - command:${step.stepId}`];
1165
- if (step.outputArtifact) {
1166
- refs.push(` - artifact:${step.outputArtifact}`);
1167
- }
1168
- return refs.join('\n');
1169
- }
1170
-
1171
-
1172
- function summarizeCommandOutput(executed: { exitCode: number | null; signal: string | null; stdout: string; stderr: string; stdoutBytes: number; stderrBytes: number; truncated: boolean; timedOut: boolean; error: string | null }): string {
1173
- const parts = [
1174
- `exit=${executed.exitCode ?? 'none'}`,
1175
- `signal=${executed.signal ?? 'none'}`,
1176
- `stdout_bytes=${executed.stdoutBytes}`,
1177
- `stderr_bytes=${executed.stderrBytes}`,
1178
- `truncated=${executed.truncated}`,
1179
- `timed_out=${executed.timedOut}`
1180
- ];
1181
- if (executed.error) {
1182
- parts.push(`error=${executed.error}`);
1183
- }
1184
- return parts.join(' ');
1185
- }
1186
-
1187
-
1188
- function hashDocumentContent(raw: string): string {
1189
- return createHash('sha256').update(raw.replace(/\r\n/g, '\n'), 'utf8').digest('hex');
1190
- }
1
+ import { createHash } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { appendEvent } from '../run-state/events.js';
4
+ import { appendArtifactHashLedgerEntry, appendInvocationLedgerEntry } from '../run-state/invocation-ledger.js';
5
+ import type { RunState, RunStateTaskRuntime } from '../run-state/model.js';
6
+ import { createRun, readAllRunStates, readRunState, writeRunState } from '../run-state/run-state.js';
7
+ import { recordRuntimeOnlyArtifact, recordStageEvidenceArtifact } from '../run-state/artifacts.js';
8
+ import { toBranchStageEvidenceRef } from '../runtime-paths.js';
9
+ import { resolveSddContext } from '../sdd-docs/context.js';
10
+ import { bindRunStateToTask } from '../sdd-docs/run-binding.js';
11
+ import { parseSddBranch, type SddTask, type SddTaskModel } from '../sdd-docs/task-parser.js';
12
+ import { inspectSddTask } from '../sdd-docs/task-inspection.js';
13
+ import { listRuntimeArtifactPayloads, readRuntimeValidationCacheEntry, recordRuntimeAcceptanceEvidenceMap, recordRuntimeDurableGap, recordRuntimeProjection, recordRuntimeTestRun, recordRuntimeTestStep, recordRuntimeValidationCacheEntry, recordRuntimeValidationCacheUse, recordRuntimeValidationEnvironmentSession, recordRuntimeValidationWaveRun, runtimeScopedId, updateRuntimeDurableGapStatus, type RuntimeValidationCacheEntryRecord } from '../storage/runtime-store.js';
14
+ import { ACCEPTANCE_POLICY_RULESET_VERSION, SDD_EVIDENCE_CONTRACT, SDD_EVIDENCE_VERSION, SDD_RESULT_CONTRACT, SDD_RESULT_VERSION, TEST_EVIDENCE_RUN_CONTRACT_VERSION, WORKFLOW_HANDOFF_CONTRACT_VERSION } from '../contracts.js';
15
+ import type { LifecycleRiskDecision } from '../risk/contracts.js';
16
+ import { inspectVerifyContract, type VerifyContractInspection } from './verify-contract.js';
17
+ import type { AcceptanceEvidenceCoverage, CapabilityEvidenceClassification, EvidenceCoverageStatus, TestEvidenceStatus, UnifiedTestEvidenceRun } from '../evidence-runtime.js';
18
+ import { ensureTaskOrchestration, inspectOrchestrationGate } from '../orchestration/runtime.js';
19
+ import { recordStageRunProjection, recordWorkflowHandoffProjection, validateWorkflowHandoff } from '../stage-runtime/runtime.js';
20
+ import type { StageRun, WorkflowHandoff } from '../stage-runtime/contracts.js';
21
+ import { evaluateTaskWorkflowGate, verifyContractBlockedGate, type ApprovalPolicy, type LifecycleRiskProfile, type LifecycleWorkflowGate } from '../risk.js';
22
+ import { validateSddResultArtifact } from '../artifacts/sdd-result.js';
23
+ import { dependencyBlockingReasonsForTask } from '../workflow-state/dependencies.js';
24
+ import { selectLatestEligibleRunsByTask } from '../workflow-state/latest-eligible-run.js';
25
+ import { latestRuntimeTaskStates } from '../workflow-state/resolve.js';
26
+ import { routeSddTask } from '../router/route-sdd-task.js';
27
+ import type { AgentCapabilityRouteDecision } from '../router/agent-runtime.js';
28
+ import { evaluateAndRecordWorkflowGateDecision } from '../workflow-gate/evidence-packet.js';
29
+ import type { WorkflowGateDecision, WorkflowGateStatus } from '../workflow-gate/types.js';
30
+ import { buildValidationCachePlan, type ValidationCacheUnsafeReason } from './validation-cache.js';
31
+
32
+ const DEFAULT_TEST_TIMEOUT_MS = 120_000;
33
+ const MAX_CAPTURE_BYTES = 256 * 1024;
34
+
35
+ export type SddTestStatus = 'PASS' | 'FAIL' | 'BLOCKED';
36
+ export type SddTestStepStatus = 'pass' | 'fail' | 'blocked';
37
+
38
+ export interface SddTestCommandInput {
39
+ command?: string;
40
+ argv?: string[];
41
+ }
42
+
43
+ export interface SddTestCommandStep {
44
+ stepId: string;
45
+ command: string;
46
+ argv: string[] | null;
47
+ shell: boolean;
48
+ acceptanceRefs: string[];
49
+ status: SddTestStepStatus;
50
+ exitCode: number | null;
51
+ signal: string | null;
52
+ durationMs: number;
53
+ outputArtifact: string | null;
54
+ stdoutBytes: number;
55
+ stderrBytes: number;
56
+ truncated: boolean;
57
+ startedAt: string;
58
+ endedAt: string;
59
+ cwd: string;
60
+ stdoutDigest: string;
61
+ stderrDigest: string;
62
+ outputSummary: string;
63
+ cacheStatus: 'hit' | 'miss' | 'unsafe';
64
+ cacheKey: string | null;
65
+ cacheSourceTestRunId: string | null;
66
+ cacheUnsafeReasons: ValidationCacheUnsafeReason[];
67
+ }
68
+
69
+ interface NormalizedSddTestCommand {
70
+ command: string;
71
+ argv: string[] | null;
72
+ shell: boolean;
73
+ }
74
+
75
+ type VerifyContractAction = 'none' | 'created' | 'refreshed' | 'blocked';
76
+
77
+ type RuntimeTestJudgment = WorkflowGateStatus;
78
+
79
+ export interface SddTestResult {
80
+ contract: 'sdd-test-runtime-v1';
81
+ runId: string;
82
+ testRunId: string;
83
+ validationWaveRunId: string;
84
+ validationEnvironmentSessionId: string;
85
+ branch: string;
86
+ taskId: string;
87
+ status: SddTestStatus;
88
+ validationStatus: SddTestStatus;
89
+ workflowGateStatus: WorkflowGateStatus;
90
+ runtimeJudgment: RuntimeTestJudgment;
91
+ workflowGateDecision: WorkflowGateDecision;
92
+ verifyContractStatus: string;
93
+ verifyContractAction: VerifyContractAction;
94
+ lifecycleGate: LifecycleWorkflowGate;
95
+ lifecycleProfile: LifecycleRiskProfile | null;
96
+ approvalPolicy: ApprovalPolicy | null;
97
+ requiredStages: string[];
98
+ primaryReason: string;
99
+ commandStatus: TestEvidenceStatus;
100
+ evidenceCoverage: EvidenceCoverageStatus;
101
+ policyJudgment: TestEvidenceStatus;
102
+ acceptanceCoverage: AcceptanceEvidenceCoverage[];
103
+ capabilityEvidence: CapabilityEvidenceClassification[];
104
+ commands: string[];
105
+ steps: SddTestCommandStep[];
106
+ validationArtifact: string | null;
107
+ indexArtifact: string | null;
108
+ gaps: string[];
109
+ next: string;
110
+ }
111
+
112
+ export interface RunSddTestOptions {
113
+ taskId: string;
114
+ branch?: string | null;
115
+ runId?: string | null;
116
+ commands?: string[];
117
+ commandInputs?: SddTestCommandInput[];
118
+ timeoutMs?: number;
119
+ approved?: boolean;
120
+ validationWave?: { waveRunId: string; environmentSessionId: string; taskIds: string[]; acceptanceRefsByTask?: Record<string, string[]> };
121
+ }
122
+
123
+ export async function runSddTest(projectRoot: string, options: RunSddTestOptions): Promise<SddTestResult> {
124
+ const context = await resolveSddContext(projectRoot, { branch: options.branch ?? undefined, branchSource: options.branch ? 'cli_option' : undefined });
125
+ const model = await parseSddBranch(projectRoot, context.partition);
126
+ const inspected = inspectSddTask(model, options.taskId);
127
+ const task = inspected.task;
128
+ const verifyContract = await ensureVerifyContractForTest(projectRoot, context.partition);
129
+ const verifyInspection = verifyContract.inspection;
130
+ const initialState = options.runId ? await readRunState(projectRoot, options.runId) : await createRun(projectRoot);
131
+ const state = await bindRunStateToTask(projectRoot, initialState, context, model, task, options.taskId);
132
+ const testRunId = runtimeScopedId(state.runId, options.taskId, new Date().toISOString(), 'sdd-test');
133
+ const commandInputs = normalizeTestCommandInputs(options.commandInputs, options.commands, task?.validation ?? []);
134
+ const commands = commandInputs.map((input) => input.command);
135
+ const gaps: string[] = [];
136
+ const startedAt = new Date().toISOString();
137
+ const ownsValidationWave = !options.validationWave;
138
+ const validationWaveRunId = options.validationWave?.waveRunId ?? runtimeScopedId(context.partition, options.taskId, state.runId, testRunId, 'validation-wave');
139
+ const validationEnvironmentSessionId = options.validationWave?.environmentSessionId ?? runtimeScopedId(context.partition, validationWaveRunId, 'validation-env');
140
+ const validationWaveTaskIds = options.validationWave?.taskIds ?? [options.taskId];
141
+ const validationWaveAcceptanceRefs = options.validationWave?.acceptanceRefsByTask?.[options.taskId];
142
+ const validationWaveScopeAcceptanceRefs = [...new Set(Object.values(options.validationWave?.acceptanceRefsByTask ?? { [options.taskId]: task?.acceptanceRefs ?? [] }).flat())].sort();
143
+ if (ownsValidationWave) {
144
+ await recordRuntimeValidationEnvironmentSession(projectRoot, {
145
+ sessionId: validationEnvironmentSessionId,
146
+ partition: context.partition,
147
+ runId: state.runId,
148
+ waveRunId: validationWaveRunId,
149
+ status: 'active',
150
+ reuseKey: `${context.partition}:${options.taskId}`,
151
+ createdAt: startedAt,
152
+ updatedAt: startedAt,
153
+ payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task' }
154
+ });
155
+ await recordRuntimeValidationWaveRun(projectRoot, {
156
+ waveRunId: validationWaveRunId,
157
+ partition: context.partition,
158
+ runId: state.runId,
159
+ taskIds: validationWaveTaskIds,
160
+ status: 'RUNNING',
161
+ environmentSessionId: validationEnvironmentSessionId,
162
+ startedAt,
163
+ completedAt: startedAt,
164
+ payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId }
165
+ });
166
+ }
167
+
168
+ await appendEvent(projectRoot, state.runId, {
169
+ event: 'test_runtime_started',
170
+ runId: state.runId,
171
+ summary: `SDD test runtime started for ${options.taskId}`,
172
+ data: { taskId: options.taskId, branch: context.partition, testRunId, commands }
173
+ });
174
+
175
+ const states = await readAllRunStates(projectRoot);
176
+ const latestEligibleRunsByTask = selectLatestEligibleRunsByTask({ states, model, partition: context.partition, currentGitBranch: context.currentGitBranch });
177
+ const runtimeByTask = latestRuntimeTaskStates(latestEligibleRunsByTask, states);
178
+
179
+ if (!task) {
180
+ gaps.push(`Task ${options.taskId} was not found in specs/${context.partition}/tasks.md.`);
181
+ }
182
+ if (task) {
183
+ gaps.push(...inspected.gaps.filter((gap) => gap.severity === 'blocking').map((gap) => `${gap.field}: ${gap.message}`));
184
+ gaps.push(...dependencyBlockingReasonsForTask(model, options.taskId, { runtimeByTask }));
185
+ }
186
+ if (verifyContract.action === 'blocked') {
187
+ gaps.push(verifyContractBlocker(verifyInspection));
188
+ }
189
+ if (commands.length === 0) {
190
+ gaps.push(`Task ${options.taskId} has no validation commands.`);
191
+ }
192
+ const orchestration = await ensureTaskOrchestration(projectRoot, model, task, {
193
+ branch: context.partition,
194
+ runId: state.runId,
195
+ taskId: options.taskId,
196
+ agent: 'validator',
197
+ stage: 'execute',
198
+ status: 'active'
199
+ });
200
+ const orchestrationGate = await inspectOrchestrationGate(projectRoot, {
201
+ branch: context.partition,
202
+ runId: state.runId,
203
+ taskId: options.taskId,
204
+ target: 'execution',
205
+ riskDecision: orchestration.riskDecision,
206
+ stageRun: orchestration.stageRun,
207
+ contextLoadSignal: orchestration.contextLoadSignal,
208
+ contextOffloadDecision: orchestration.contextOffloadDecision
209
+ });
210
+ const reviewerCheckpointSatisfied = await hasReviewerCheckpoint(projectRoot, state, options.taskId);
211
+ const workflowGate = verifyContract.action === 'blocked'
212
+ ? verifyContractBlockedGate(options.taskId)
213
+ : evaluateTaskWorkflowGate({ task, taskId: options.taskId, riskDecision: orchestration.riskDecision, approved: options.approved, reviewerCheckpointSatisfied });
214
+ if (workflowGate.blocksTest) {
215
+ gaps.push(workflowGate.primaryReason);
216
+ }
217
+ gaps.push(...orchestrationGate.blockingReasons);
218
+
219
+ await recordRuntimeTestRun(projectRoot, {
220
+ testRunId,
221
+ runId: state.runId,
222
+ partition: context.partition,
223
+ taskId: options.taskId,
224
+ status: 'RUNNING',
225
+ startedAt,
226
+ completedAt: startedAt,
227
+ payload: { verifyContractStatus: verifyInspection.status, verifyContractAction: verifyContract.action, lifecycleGate: workflowGate.lifecycleGate, lifecycleProfile: workflowGate.lifecycleProfile, approvalPolicy: workflowGate.approvalPolicy, requiredStages: workflowGate.requiredStages, primaryReason: workflowGate.primaryReason, commands, commandInputs, evidence: [], gaps }
228
+ });
229
+
230
+ const steps: SddTestCommandStep[] = [];
231
+ if (gaps.length === 0) {
232
+ for (const [index, commandInput] of commandInputs.entries()) {
233
+ const step = await runCommandStep(projectRoot, state.runId, context.partition, options.taskId, testRunId, index + 1, commandInput, acceptanceRefsForCommand(task, commandInput.command, validationWaveAcceptanceRefs), options.timeoutMs ?? DEFAULT_TEST_TIMEOUT_MS, model, task!, validationWaveTaskIds, validationWaveScopeAcceptanceRefs);
234
+ steps.push(step);
235
+ await appendEvent(projectRoot, state.runId, {
236
+ event: 'test_step_completed',
237
+ runId: state.runId,
238
+ summary: `SDD test step ${step.status}: ${step.command}`,
239
+ data: { taskId: options.taskId, testRunId, step }
240
+ });
241
+ }
242
+ }
243
+
244
+ const commandStatus = deriveCommandStatus(gaps, steps);
245
+ const acceptanceCoverage = buildAcceptanceCoverage(task, steps, commandStatus, validationWaveAcceptanceRefs);
246
+ const evidenceCoverage = summarizeEvidenceCoverage(acceptanceCoverage);
247
+ const policyJudgment = derivePolicyJudgment(commandStatus, evidenceCoverage);
248
+ await recordAcceptanceEvidenceMaps(projectRoot, validationWaveRunId, testRunId, context.partition, state.runId, options.taskId, acceptanceCoverage);
249
+ const validationStatus = policyJudgment;
250
+ const capabilityRoute = task ? await routeSddTask(projectRoot, { taskId: options.taskId, branch: context.partition, approved: options.approved }) : null;
251
+ const capabilityEvidence = buildCapabilityEvidenceClassification(capabilityRoute?.capabilityDecision ?? null, steps);
252
+ const validationArtifact = task ? await writeValidationArtifact(projectRoot, state.runId, context.partition, task, validationStatus, steps, gaps, capabilityEvidence) : null;
253
+ const evidenceBeforeGate = runtimeEvidenceRefs(validationArtifact?.runRelativePath ?? null, steps);
254
+ await persistTestRunState(projectRoot, state, options.taskId, validationStatus, commands, evidenceBeforeGate, validationArtifact?.runRelativePath ?? null);
255
+ await resolveTestRuntimeDurableGap(projectRoot, context.partition, state.runId, options.taskId, validationStatus, gaps);
256
+
257
+ const gateDecision = (await evaluateAndRecordWorkflowGateDecision(projectRoot, {
258
+ branch: context.partition,
259
+ taskId: options.taskId,
260
+ runId: state.runId,
261
+ decisionKind: 'test'
262
+ })).decision;
263
+ const runtimeJudgment = gateDecision.status;
264
+ const status = finalStatusForTest(validationStatus, runtimeJudgment);
265
+ await recordTestRuntimeDurableGap(projectRoot, context.partition, state.runId, options.taskId, status, validationStatus, runtimeJudgment, gaps, evidenceBeforeGate);
266
+ const unifiedEvidence = buildUnifiedTestEvidenceRun(testRunId, context.partition, state.runId, options.taskId, commandStatus, evidenceCoverage, policyJudgment, status, runtimeJudgment, steps, acceptanceCoverage, capabilityEvidence, gaps, workflowGate.nextAction, gateDecision);
267
+ const completedAt = new Date().toISOString();
268
+ const evidence = runtimeEvidenceRefs(validationArtifact?.runRelativePath ?? null, steps);
269
+
270
+ await recordRuntimeTestRun(projectRoot, {
271
+ testRunId,
272
+ runId: state.runId,
273
+ partition: context.partition,
274
+ taskId: options.taskId,
275
+ status,
276
+ startedAt,
277
+ completedAt,
278
+ payload: { verifyContractStatus: verifyInspection.status, verifyContractAction: verifyContract.action, lifecycleGate: workflowGate.lifecycleGate, lifecycleProfile: workflowGate.lifecycleProfile, approvalPolicy: workflowGate.approvalPolicy, requiredStages: workflowGate.requiredStages, primaryReason: workflowGate.primaryReason, commandStatus, evidenceCoverage, policyJudgment, validationStatus, workflowGateStatus: gateDecision.status, runtimeJudgment, workflowGateDecision: gateDecision, acceptanceCoverage, capabilityEvidence, commands, commandInputs, evidence, gaps }
279
+ });
280
+ await recordRuntimeProjection(projectRoot, 'test_runtime', `${context.partition}:${options.taskId}:${state.runId}`, {
281
+ contract: 'sdd-test-runtime-v1',
282
+ testRunId,
283
+ runId: state.runId,
284
+ taskId: options.taskId,
285
+ status,
286
+ validationStatus,
287
+ workflowGateStatus: gateDecision.status,
288
+ runtimeJudgment,
289
+ lifecycleGate: workflowGate.lifecycleGate,
290
+ primaryReason: workflowGate.primaryReason,
291
+ evidence,
292
+ gaps
293
+ });
294
+ await recordRuntimeProjection(projectRoot, 'test_evidence_run', `${context.partition}:${options.taskId}:${state.runId}`, unifiedEvidence);
295
+ await recordTestWorkflowProjection(projectRoot, {
296
+ taskId: options.taskId,
297
+ stageRun: orchestration.stageRun,
298
+ status,
299
+ completedAt,
300
+ evidence,
301
+ gaps,
302
+ riskDecision: orchestration.riskDecision
303
+ });
304
+ await persistTestGateOutcome(projectRoot, state.runId, options.taskId, status, validationStatus, commands, evidence, validationArtifact?.runRelativePath ?? null, gateDecision);
305
+ if (ownsValidationWave) {
306
+ await recordRuntimeValidationWaveRun(projectRoot, {
307
+ waveRunId: validationWaveRunId,
308
+ partition: context.partition,
309
+ runId: state.runId,
310
+ taskIds: validationWaveTaskIds,
311
+ status,
312
+ environmentSessionId: validationEnvironmentSessionId,
313
+ startedAt,
314
+ completedAt,
315
+ payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId, testRunId, evidence, gaps, workflowGateDecision: gateDecision }
316
+ });
317
+ await recordRuntimeValidationEnvironmentSession(projectRoot, {
318
+ sessionId: validationEnvironmentSessionId,
319
+ partition: context.partition,
320
+ runId: state.runId,
321
+ waveRunId: validationWaveRunId,
322
+ status: status === 'PASS' ? 'completed' : status === 'FAIL' ? 'failed' : 'blocked',
323
+ reuseKey: `${context.partition}:${options.taskId}`,
324
+ createdAt: startedAt,
325
+ updatedAt: completedAt,
326
+ payload: { contract: 'phase-8.17-validation-wave-runtime-v1', mode: 'single-task', taskId: options.taskId, validationStatus, workflowGateStatus: gateDecision.status, status }
327
+ });
328
+ }
329
+
330
+ await appendEvent(projectRoot, state.runId, {
331
+ event: status === 'PASS' ? 'test_runtime_passed' : 'test_runtime_blocked',
332
+ runId: state.runId,
333
+ summary: `SDD test runtime ${status} for ${options.taskId}`,
334
+ data: { taskId: options.taskId, testRunId, status, validationStatus, evidence, gaps, gateDecisionId: gateDecision.decisionId, gateStatus: gateDecision.status }
335
+ });
336
+
337
+ return {
338
+ contract: 'sdd-test-runtime-v1',
339
+ runId: state.runId,
340
+ testRunId,
341
+ validationWaveRunId,
342
+ validationEnvironmentSessionId,
343
+ branch: context.partition,
344
+ taskId: options.taskId,
345
+ status,
346
+ validationStatus,
347
+ workflowGateStatus: gateDecision.status,
348
+ runtimeJudgment,
349
+ workflowGateDecision: gateDecision,
350
+ verifyContractStatus: verifyInspection.status,
351
+ verifyContractAction: verifyContract.action,
352
+ lifecycleGate: workflowGate.lifecycleGate,
353
+ lifecycleProfile: workflowGate.lifecycleProfile,
354
+ approvalPolicy: workflowGate.approvalPolicy,
355
+ requiredStages: workflowGate.requiredStages,
356
+ primaryReason: workflowGate.primaryReason,
357
+ commandStatus,
358
+ evidenceCoverage,
359
+ policyJudgment,
360
+ acceptanceCoverage,
361
+ capabilityEvidence,
362
+ commands,
363
+ steps,
364
+ validationArtifact: validationArtifact?.runRelativePath ?? null,
365
+ indexArtifact: null,
366
+ gaps,
367
+ next: nextForTestResult(status, runtimeJudgment, context.partition, options.taskId, workflowGate.nextAction, gateDecision)
368
+ };
369
+ }
370
+
371
+ async function recordAcceptanceEvidenceMaps(projectRoot: string, waveRunId: string, testRunId: string, partition: string, runId: string, taskId: string, acceptanceCoverage: AcceptanceEvidenceCoverage[]): Promise<void> {
372
+ const createdAt = new Date().toISOString();
373
+ for (const coverage of acceptanceCoverage) {
374
+ await recordRuntimeAcceptanceEvidenceMap(projectRoot, {
375
+ mapId: runtimeScopedId(waveRunId, testRunId, taskId, coverage.acceptanceRef),
376
+ waveRunId,
377
+ testRunId,
378
+ partition,
379
+ runId,
380
+ taskId,
381
+ acceptanceRef: coverage.acceptanceRef,
382
+ status: coverage.status,
383
+ evidenceRefs: coverage.evidenceRefs.map((ref) => ref.ref),
384
+ gaps: coverage.gaps,
385
+ createdAt,
386
+ payload: coverage
387
+ });
388
+ }
389
+ }
390
+
391
+ async function recordTestWorkflowProjection(projectRoot: string, input: { taskId: string; stageRun: StageRun; status: SddTestStatus; completedAt: string; evidence: string[]; gaps: string[]; riskDecision: LifecycleRiskDecision }): Promise<void> {
392
+ const outputRefs = input.evidence.map((ref) => ({ kind: 'artifact' as const, ref }));
393
+ const completedStage: StageRun = {
394
+ ...input.stageRun,
395
+ status: input.status === 'PASS' ? 'completed' : input.status === 'FAIL' ? 'failed' : 'blocked',
396
+ outputRefs,
397
+ blockingReasons: input.status === 'PASS' ? [] : input.gaps.length > 0 ? input.gaps : [`SDD test ${input.status}.`],
398
+ updatedAt: input.completedAt
399
+ };
400
+ await recordStageRunProjection(projectRoot, completedStage);
401
+
402
+ const handoff: WorkflowHandoff = {
403
+ contract: WORKFLOW_HANDOFF_CONTRACT_VERSION,
404
+ id: `${completedStage.id}:handoff:ship`,
405
+ scope: completedStage.scope,
406
+ fromStage: 'execute',
407
+ toStage: 'ship',
408
+ fromAgent: 'validator',
409
+ toAgent: 'ship-manager',
410
+ status: input.status === 'PASS' ? 'proposed' : 'blocked',
411
+ outputRefs,
412
+ requiredInputRefs: [{ kind: 'task', ref: input.taskId }],
413
+ riskDecisionRef: input.stageRun.decisionRefs[0] ?? { kind: 'task', ref: input.taskId },
414
+ evidenceRefs: outputRefs,
415
+ openQuestions: [],
416
+ blockingGaps: input.status === 'PASS' ? [] : completedStage.blockingReasons,
417
+ createdAt: input.completedAt,
418
+ decidedAt: input.completedAt
419
+ };
420
+ const validation = validateWorkflowHandoff({ handoff, sourceStageRun: completedStage, lifecycleRiskDecision: input.riskDecision });
421
+ await recordWorkflowHandoffProjection(projectRoot, validation.valid ? handoff : { ...handoff, status: 'blocked', blockingGaps: validation.issues, decidedAt: input.completedAt });
422
+ }
423
+
424
+
425
+ async function ensureVerifyContractForTest(projectRoot: string, branch: string): Promise<{ inspection: VerifyContractInspection; action: VerifyContractAction }> {
426
+ const inspection = await inspectVerifyContract(projectRoot, { branch, branchSource: 'cli_option' });
427
+ return { inspection, action: inspection.status === 'PASS' ? 'none' : 'blocked' };
428
+ }
429
+
430
+ function verifyContractBlocker(inspection: VerifyContractInspection): string {
431
+ const issueSummary = inspection.issues.map((issue) => `${issue.field}: ${issue.message}`).join(' ');
432
+ return `verify.md contract is ${inspection.status}; ${issueSummary || 'inspect verify.md before executing tests.'}`;
433
+ }
434
+
435
+ async function hasReviewerCheckpoint(projectRoot: string, state: RunState, taskId: string): Promise<boolean> {
436
+ const artifactPaths = new Set(state.artifacts
437
+ .filter((artifact) => artifact.task === taskId && (artifact.agent === 'reviewer' || artifact.kind === 'review'))
438
+ .map((artifact) => artifact.path));
439
+ const branch = state.partition ?? state.gitBranch ?? 'unscoped';
440
+ const payloads = await listRuntimeArtifactPayloads(projectRoot, { runId: state.runId, taskId });
441
+ for (const payload of payloads) {
442
+ const fileName = payload.logicalRef.replace(/\\/g, '/').split('/').filter(Boolean).pop();
443
+ if (fileName && (payload.artifactRole === 'review' || /review/i.test(fileName))) {
444
+ artifactPaths.add(toBranchStageEvidenceRef(branch, 'execute', fileName));
445
+ }
446
+ }
447
+
448
+ for (const artifactPath of artifactPaths) {
449
+ const report = await validateSddResultArtifact(projectRoot, state.runId, artifactPath, { expectedTask: taskId, expectedAgent: 'reviewer' });
450
+ if (report.valid && report.result?.status === 'PASS') {
451
+ return true;
452
+ }
453
+ }
454
+
455
+ return false;
456
+ }
457
+
458
+ export function renderSddTestResult(result: SddTestResult): string {
459
+ return [
460
+ `SDD test ${result.taskId}`,
461
+ '',
462
+ resultSentenceForTest(result),
463
+ '',
464
+ 'Decision:',
465
+ `- validation_status=${result.validationStatus}`,
466
+ `- workflow_gate_status=${result.workflowGateStatus}`,
467
+ `- workflow_gate_decision=${result.workflowGateDecision.decisionId}`,
468
+ '',
469
+ 'Why:',
470
+ `- ${result.primaryReason}`,
471
+ `- capability_evidence=${capabilityEvidenceSummary(result.capabilityEvidence)}`,
472
+ '',
473
+ 'Next:',
474
+ `- ${result.next}`
475
+ ].join('\n');
476
+ }
477
+
478
+
479
+ async function runCommandStep(projectRoot: string, runId: string, branch: string, taskId: string, testRunId: string, sequence: number, commandInput: NormalizedSddTestCommand, acceptanceRefs: string[], timeoutMs: number, model: SddTaskModel, task: SddTask, validationScopeTaskIds: string[], validationScopeAcceptanceRefs: string[]): Promise<SddTestCommandStep> {
480
+ const cachePlan = buildValidationCachePlan({ branch, model, task, command: commandInput.command, argv: commandInput.argv, shell: commandInput.shell, validationScopeTaskIds, acceptanceRefs: validationScopeAcceptanceRefs });
481
+ const cached = cachePlan.eligible ? await readRuntimeValidationCacheEntry(projectRoot, { branchSlug: branch, cacheKey: cachePlan.cacheKey }) : null;
482
+ if (cached) {
483
+ return recordCachedCommandStep(projectRoot, runId, branch, taskId, testRunId, sequence, commandInput, acceptanceRefs, cachePlan.cacheKey, cached);
484
+ }
485
+
486
+ const started = Date.now();
487
+ const startedAt = new Date(started).toISOString();
488
+ const executed = await executeCommand(projectRoot, commandInput, timeoutMs);
489
+ const ended = Date.now();
490
+ const endedAt = new Date(ended).toISOString();
491
+ const durationMs = ended - started;
492
+ const status: SddTestStepStatus = executed.timedOut || executed.error ? 'blocked' : executed.exitCode === 0 ? 'pass' : 'fail';
493
+ const stepId = `${testRunId}-${String(sequence).padStart(3, '0')}`;
494
+ const cacheStatus = cachePlan.eligible ? 'miss' : 'unsafe';
495
+ const shouldPersistOutputArtifact = shouldPersistCommandOutputArtifact(status, executed, cacheStatus, cachePlan.unsafeReasons);
496
+ const outputFileName = shouldPersistOutputArtifact ? `test-${taskId}-${String(sequence).padStart(3, '0')}.log` : null;
497
+ const outputRef = outputFileName ? toBranchStageEvidenceRef(branch, 'execute', outputFileName) : null;
498
+ const output = outputFileName ? renderCommandOutput(commandInput, status, executed, durationMs, cacheStatus, cachePlan.cacheKey, null, cachePlan.unsafeReasons) : null;
499
+ if (outputFileName && outputRef && output) {
500
+ await recordRuntimeOnlyArtifact(projectRoot, runId, outputFileName, output, { logicalRef: outputRef, branch, taskId, artifactRole: 'test-command-output' });
501
+ await appendArtifactHashLedgerEntry(projectRoot, {
502
+ runId,
503
+ taskId,
504
+ branch,
505
+ artifactPath: outputRef,
506
+ content: output,
507
+ status: 'recorded'
508
+ });
509
+ }
510
+ const stdoutDigest = hashDocumentContent(executed.stdout);
511
+ const stderrDigest = hashDocumentContent(executed.stderr);
512
+ const outputSummary = summarizeCommandOutput(executed);
513
+ await appendInvocationLedgerEntry(projectRoot, {
514
+ runId,
515
+ taskId,
516
+ branch,
517
+ kind: 'command',
518
+ ref: commandInput.command,
519
+ status,
520
+ artifactPath: outputRef,
521
+ outputHash: output ? hashDocumentContent(output) : null,
522
+ materialRefs: outputRef ? [outputRef] : [],
523
+ metadata: {
524
+ stepId,
525
+ source: 'sdd-test',
526
+ exitCode: executed.exitCode,
527
+ durationMs,
528
+ stdoutBytes: executed.stdoutBytes,
529
+ stderrBytes: executed.stderrBytes,
530
+ truncated: executed.truncated,
531
+ acceptanceRefs: acceptanceRefs.join(','),
532
+ shell: commandInput.shell,
533
+ argv: commandInput.argv ? JSON.stringify(commandInput.argv) : null,
534
+ stdoutDigest,
535
+ stderrDigest,
536
+ cacheKey: cachePlan.cacheKey,
537
+ cacheStatus,
538
+ cacheUnsafeReasons: cachePlan.unsafeReasons.join(',')
539
+ }
540
+ });
541
+ const step: SddTestCommandStep = {
542
+ stepId,
543
+ command: commandInput.command,
544
+ argv: commandInput.argv,
545
+ shell: commandInput.shell,
546
+ acceptanceRefs,
547
+ status,
548
+ exitCode: executed.exitCode,
549
+ signal: executed.signal,
550
+ durationMs,
551
+ outputArtifact: outputRef,
552
+ stdoutBytes: executed.stdoutBytes,
553
+ stderrBytes: executed.stderrBytes,
554
+ truncated: executed.truncated,
555
+ startedAt,
556
+ endedAt,
557
+ cwd: projectRoot,
558
+ stdoutDigest,
559
+ stderrDigest,
560
+ outputSummary,
561
+ cacheStatus,
562
+ cacheKey: cachePlan.cacheKey,
563
+ cacheSourceTestRunId: null,
564
+ cacheUnsafeReasons: cachePlan.unsafeReasons
565
+ };
566
+ await recordRuntimeTestStep(projectRoot, {
567
+ stepId,
568
+ testRunId,
569
+ runId,
570
+ taskId,
571
+ command: commandInput.command,
572
+ status,
573
+ exitCode: executed.exitCode,
574
+ durationMs,
575
+ outputArtifact: outputRef,
576
+ payload: step
577
+ });
578
+ if (status === 'pass' && cachePlan.eligible) {
579
+ const now = new Date().toISOString();
580
+ await recordRuntimeValidationCacheEntry(projectRoot, {
581
+ cacheKey: cachePlan.cacheKey,
582
+ branchSlug: branch,
583
+ command: commandInput.command,
584
+ status: 'valid',
585
+ sourceTestRunId: testRunId,
586
+ sourceRunId: runId,
587
+ sourceEvidenceSetId: null,
588
+ outputArtifact: outputRef,
589
+ stdoutDigest,
590
+ stderrDigest,
591
+ createdAt: now,
592
+ lastUsedAt: now,
593
+ payload: { cachePlan, step }
594
+ });
595
+ }
596
+ return step;
597
+ }
598
+
599
+ async function recordCachedCommandStep(projectRoot: string, runId: string, branch: string, taskId: string, testRunId: string, sequence: number, commandInput: NormalizedSddTestCommand, acceptanceRefs: string[], cacheKey: string, cached: RuntimeValidationCacheEntryRecord): Promise<SddTestCommandStep> {
600
+ const now = new Date().toISOString();
601
+ const stepId = `${testRunId}-${String(sequence).padStart(3, '0')}`;
602
+ const step: SddTestCommandStep = {
603
+ stepId,
604
+ command: commandInput.command,
605
+ argv: commandInput.argv,
606
+ shell: commandInput.shell,
607
+ acceptanceRefs,
608
+ status: 'pass',
609
+ exitCode: 0,
610
+ signal: null,
611
+ durationMs: 0,
612
+ outputArtifact: null,
613
+ stdoutBytes: 0,
614
+ stderrBytes: 0,
615
+ truncated: false,
616
+ startedAt: now,
617
+ endedAt: now,
618
+ cwd: projectRoot,
619
+ stdoutDigest: cached.stdoutDigest,
620
+ stderrDigest: cached.stderrDigest,
621
+ outputSummary: `cache_hit source_test_run=${cached.sourceTestRunId} source_artifact=${cached.outputArtifact ?? 'none'}`,
622
+ cacheStatus: 'hit',
623
+ cacheKey,
624
+ cacheSourceTestRunId: cached.sourceTestRunId,
625
+ cacheUnsafeReasons: []
626
+ };
627
+ await appendInvocationLedgerEntry(projectRoot, {
628
+ runId,
629
+ taskId,
630
+ branch,
631
+ kind: 'command',
632
+ ref: commandInput.command,
633
+ status: 'pass',
634
+ artifactPath: null,
635
+ outputHash: null,
636
+ materialRefs: cached.outputArtifact ? [cached.outputArtifact] : [],
637
+ metadata: { source: 'sdd-test-cache', stepId, cacheKey, sourceTestRunId: cached.sourceTestRunId, sourceEvidenceSetId: cached.sourceEvidenceSetId, acceptanceRefs: acceptanceRefs.join(',') }
638
+ });
639
+ await recordRuntimeTestStep(projectRoot, {
640
+ stepId,
641
+ testRunId,
642
+ runId,
643
+ taskId,
644
+ command: commandInput.command,
645
+ status: 'pass',
646
+ exitCode: 0,
647
+ durationMs: 0,
648
+ outputArtifact: null,
649
+ payload: step
650
+ });
651
+ await recordRuntimeValidationCacheUse(projectRoot, {
652
+ useId: runtimeScopedId(cacheKey, testRunId, taskId, stepId),
653
+ cacheKey,
654
+ branchSlug: branch,
655
+ testRunId,
656
+ runId,
657
+ taskId,
658
+ sourceTestRunId: cached.sourceTestRunId,
659
+ sourceEvidenceSetId: cached.sourceEvidenceSetId,
660
+ reusedAt: now,
661
+ mappedTaskIds: [taskId],
662
+ reason: 'same validation cache key within compatible branch contract scope',
663
+ payload: { sourceRunId: cached.sourceRunId, sourceArtifact: cached.outputArtifact }
664
+ });
665
+ return step;
666
+ }
667
+
668
+ function executeCommand(projectRoot: string, commandInput: NormalizedSddTestCommand, timeoutMs: number): Promise<{ exitCode: number | null; signal: string | null; stdout: string; stderr: string; stdoutBytes: number; stderrBytes: number; truncated: boolean; timedOut: boolean; error: string | null }> {
669
+ return new Promise((resolve) => {
670
+ const child = commandInput.argv
671
+ ? spawn(commandInput.argv[0], commandInput.argv.slice(1), { cwd: projectRoot, shell: false, windowsHide: true, env: process.env })
672
+ : spawn(commandInput.command, { cwd: projectRoot, shell: true, windowsHide: true, env: process.env });
673
+ let stdout = '';
674
+ let stderr = '';
675
+ let stdoutBytes = 0;
676
+ let stderrBytes = 0;
677
+ let truncated = false;
678
+ let settled = false;
679
+ let timedOut = false;
680
+ const timer = setTimeout(() => {
681
+ timedOut = true;
682
+ child.kill();
683
+ }, timeoutMs);
684
+ const finish = (result: { exitCode: number | null; signal: string | null; error: string | null }) => {
685
+ if (settled) {
686
+ return;
687
+ }
688
+ settled = true;
689
+ clearTimeout(timer);
690
+ resolve({ ...result, stdout, stderr, stdoutBytes, stderrBytes, truncated, timedOut });
691
+ };
692
+ child.stdout?.on('data', (chunk: Buffer) => {
693
+ stdoutBytes += chunk.length;
694
+ const next = chunk.toString('utf8');
695
+ if (Buffer.byteLength(stdout, 'utf8') < MAX_CAPTURE_BYTES) {
696
+ stdout += next;
697
+ } else {
698
+ truncated = true;
699
+ }
700
+ });
701
+ child.stderr?.on('data', (chunk: Buffer) => {
702
+ stderrBytes += chunk.length;
703
+ const next = chunk.toString('utf8');
704
+ if (Buffer.byteLength(stderr, 'utf8') < MAX_CAPTURE_BYTES) {
705
+ stderr += next;
706
+ } else {
707
+ truncated = true;
708
+ }
709
+ });
710
+ child.on('error', (error) => finish({ exitCode: null, signal: null, error: error.message }));
711
+ child.on('close', (code, signal) => finish({ exitCode: code, signal, error: null }));
712
+ });
713
+ }
714
+
715
+ async function writeValidationArtifact(projectRoot: string, runId: string, branch: string, task: SddTask, status: SddTestStatus, steps: SddTestCommandStep[], gaps: string[], _capabilityEvidence: CapabilityEvidenceClassification[]): Promise<{ absolutePath: string; runRelativePath: string }> {
716
+ const artifactPath = validationArtifactPath(task);
717
+ const stageEvidenceRef = toBranchStageEvidenceRef(branch, 'execute', artifactPath);
718
+ const resultStatus = status === 'PASS' ? 'PASS' : status === 'FAIL' ? 'FAIL' : 'BLOCKED';
719
+ const content = `# Test Validation ${task.id}\n\n\`\`\`sdd-result\ncontract: ${SDD_RESULT_CONTRACT}\nversion: ${SDD_RESULT_VERSION}\nagent: validator\ntask: ${task.id}\nstatus: ${resultStatus}\nartifacts:\n - ${stageEvidenceRef}\n\`\`\`\n\n## Test Runtime\n\n- status: ${status}\n- commands:\n${steps.length > 0 ? steps.map((step) => ` - [${step.status}] ${step.command}`).join('\n') : ' - none'}\n- gaps:\n${gaps.length > 0 ? gaps.map((gap) => ` - ${gap}`).join('\n') : ' - none'}\n\n## Acceptance Evidence\n\n${renderEvidenceBlocks(task, status, stageEvidenceRef, steps)}\n`;
720
+ const written = await recordStageEvidenceArtifact(projectRoot, runId, stageEvidenceRef, content, { taskId: task.id, artifactRole: 'test-validation' });
721
+ return { ...written, runRelativePath: stageEvidenceRef };
722
+ }
723
+
724
+ function validationArtifactPath(task: SddTask): string {
725
+ return `test-validation-${task.id}.md`;
726
+ }
727
+
728
+
729
+
730
+ async function persistTestRunState(projectRoot: string, state: RunState, taskId: string, validationStatus: SddTestStatus, commands: string[], evidence: string[], validationArtifact: string | null): Promise<void> {
731
+ const latest = await readRunState(projectRoot, state.runId);
732
+ const knownArtifacts = new Set(latest.artifacts.map((artifact) => artifact.path));
733
+ const now = new Date().toISOString();
734
+ const nextArtifacts = evidence
735
+ .filter((artifactPath) => !knownArtifacts.has(artifactPath))
736
+ .map((artifactPath) => ({ path: artifactPath, kind: testArtifactKind(artifactPath), task: taskId, agent: 'test-runtime', createdAt: now }));
737
+ const existingTaskState = latest.tasks[taskId];
738
+ await writeRunState(projectRoot, {
739
+ ...latest,
740
+ status: validationStatus === 'PASS' ? 'running' : validationStatus === 'FAIL' ? 'failed' : 'blocked',
741
+ phase: 'test',
742
+ currentTask: taskId,
743
+ tasks: {
744
+ ...latest.tasks,
745
+ [taskId]: {
746
+ ...baseRuntimeTaskState(existingTaskState),
747
+ status: validationStatus === 'PASS' ? 'validation_passed_pending_gate' : validationStatus === 'FAIL' ? 'validation_failed' : 'validation_blocked',
748
+ implementationStatus: existingTaskState?.implementationStatus ?? 'not_started',
749
+ verificationStatus: verificationStatusFromTest(validationStatus),
750
+ testStatus: validationStatus,
751
+ evidence
752
+ }
753
+ },
754
+ artifacts: [...latest.artifacts, ...nextArtifacts],
755
+ validation: {
756
+ status: validationStatus === 'PASS' ? 'pass' : validationStatus === 'FAIL' ? 'fail' : 'blocked',
757
+ commands,
758
+ evidence
759
+ }
760
+ });
761
+ }
762
+
763
+ async function persistTestGateOutcome(projectRoot: string, runId: string, taskId: string, status: SddTestStatus, validationStatus: SddTestStatus, commands: string[], evidence: string[], validationArtifact: string | null, gateDecision: WorkflowGateDecision): Promise<void> {
764
+ const latest = await readRunState(projectRoot, runId);
765
+ const knownArtifacts = new Set(latest.artifacts.map((artifact) => artifact.path));
766
+ const now = new Date().toISOString();
767
+ const nextArtifacts = evidence
768
+ .filter((artifactPath) => !knownArtifacts.has(artifactPath))
769
+ .map((artifactPath) => ({ path: artifactPath, kind: testArtifactKind(artifactPath), task: taskId, agent: 'test-runtime', createdAt: now }));
770
+ const existingTaskState = latest.tasks[taskId];
771
+ await writeRunState(projectRoot, {
772
+ ...latest,
773
+ status: status === 'PASS' ? 'completed' : status === 'FAIL' ? 'failed' : 'blocked',
774
+ phase: 'test',
775
+ currentTask: taskId,
776
+ tasks: {
777
+ ...latest.tasks,
778
+ [taskId]: {
779
+ ...baseRuntimeTaskState(existingTaskState),
780
+ status: runtimeTaskStatusAfterGate(status, validationStatus),
781
+ implementationStatus: existingTaskState?.implementationStatus ?? 'not_started',
782
+ verificationStatus: verificationStatusFromTest(validationStatus),
783
+ testStatus: status,
784
+ validationStatus,
785
+ workflowGateStatus: gateDecision.status,
786
+ workflowGateDecisionId: gateDecision.decisionId,
787
+ evidence
788
+ }
789
+ },
790
+ artifacts: [...latest.artifacts, ...nextArtifacts],
791
+ validation: {
792
+ status: validationStatus === 'PASS' ? 'pass' : validationStatus === 'FAIL' ? 'fail' : 'blocked',
793
+ commands,
794
+ evidence
795
+ }
796
+ });
797
+ }
798
+
799
+ function testArtifactKind(artifactPath: string): string {
800
+ const fileName = artifactPath.split('/').pop() ?? artifactPath;
801
+ if (fileName.startsWith('test-validation-')) {
802
+ return 'test-validation';
803
+ }
804
+ return 'test';
805
+ }
806
+
807
+ function baseRuntimeTaskState(existing: RunStateTaskRuntime | undefined): RunStateTaskRuntime {
808
+ return {
809
+ status: existing?.status ?? 'not_started',
810
+ implementationStatus: existing?.implementationStatus ?? 'not_started',
811
+ verificationStatus: existing?.verificationStatus ?? 'not_run',
812
+ validationBatch: existing?.validationBatch ?? null,
813
+ validationTiming: existing?.validationTiming ?? 'task_end',
814
+ requiresVerifyBeforeNext: existing?.requiresVerifyBeforeNext ?? true,
815
+ gaps: existing?.gaps,
816
+ artifacts: existing?.artifacts,
817
+ testStatus: existing?.testStatus,
818
+ workflowGateStatus: existing?.workflowGateStatus,
819
+ workflowGateDecisionId: existing?.workflowGateDecisionId,
820
+ evidence: existing?.evidence
821
+ };
822
+ }
823
+
824
+ function verificationStatusFromTest(validationStatus: SddTestStatus): RunStateTaskRuntime['verificationStatus'] {
825
+ if (validationStatus === 'PASS') {
826
+ return 'pass';
827
+ }
828
+ return validationStatus === 'FAIL' ? 'failed' : 'blocked';
829
+ }
830
+
831
+ function runtimeTaskStatusAfterGate(status: SddTestStatus, validationStatus: SddTestStatus): string {
832
+ if (status === 'PASS') {
833
+ return 'implemented_verified';
834
+ }
835
+ if (validationStatus === 'PASS') {
836
+ return 'workflow_gate_blocked';
837
+ }
838
+ return validationStatus === 'FAIL' ? 'validation_failed' : 'validation_blocked';
839
+ }
840
+
841
+ function deriveCommandStatus(gaps: string[], steps: SddTestCommandStep[]): TestEvidenceStatus {
842
+ if (gaps.length > 0 || steps.some((step) => step.status === 'blocked')) {
843
+ return 'BLOCKED';
844
+ }
845
+ if (steps.some((step) => step.status === 'fail')) {
846
+ return 'FAIL';
847
+ }
848
+ return 'PASS';
849
+ }
850
+
851
+ function buildAcceptanceCoverage(task: SddTask | null, steps: SddTestCommandStep[], commandStatus: TestEvidenceStatus, acceptanceRefsOverride: string[] | undefined): AcceptanceEvidenceCoverage[] {
852
+ const acceptanceRefs = acceptanceRefsOverride && acceptanceRefsOverride.length > 0 ? [...new Set(acceptanceRefsOverride)] : task ? taskAcceptanceRefs(task) : [];
853
+ return acceptanceRefs.map((acceptanceRef) => {
854
+ const mappedSteps = steps.filter((step) => step.acceptanceRefs.includes(acceptanceRef));
855
+ const hasPassingEvidence = mappedSteps.some((step) => step.status === 'pass');
856
+ const hasFailingEvidence = mappedSteps.some((step) => step.status === 'fail' || step.status === 'blocked');
857
+ const status: EvidenceCoverageStatus = hasPassingEvidence && !hasFailingEvidence && commandStatus === 'PASS'
858
+ ? 'complete'
859
+ : mappedSteps.length > 0
860
+ ? 'partial'
861
+ : 'missing';
862
+ return {
863
+ acceptanceRef,
864
+ status,
865
+ evidenceRefs: mappedSteps.map(commandStepRuntimeRef),
866
+ gaps: status === 'complete' ? [] : [`Acceptance ${acceptanceRef} has no complete non-stale execute validation evidence.`]
867
+ };
868
+ });
869
+ }
870
+
871
+ function runtimeEvidenceRefs(validationArtifact: string | null, steps: SddTestCommandStep[]): string[] {
872
+ return [validationArtifact, ...steps.map((step) => step.outputArtifact)].filter((item): item is string => Boolean(item));
873
+ }
874
+
875
+ function commandStepRuntimeRef(step: SddTestCommandStep): { kind: 'command'; ref: string } {
876
+ return { kind: 'command', ref: step.stepId };
877
+ }
878
+
879
+ function artifactRuntimeRefs(ref: string | null): Array<{ kind: 'artifact'; ref: string }> {
880
+ return ref ? [{ kind: 'artifact', ref }] : [];
881
+ }
882
+
883
+ function summarizeEvidenceCoverage(acceptanceCoverage: AcceptanceEvidenceCoverage[]): EvidenceCoverageStatus {
884
+ if (acceptanceCoverage.length === 0) {
885
+ return 'missing';
886
+ }
887
+ if (acceptanceCoverage.every((coverage) => coverage.status === 'complete')) {
888
+ return 'complete';
889
+ }
890
+ if (acceptanceCoverage.some((coverage) => coverage.status === 'complete' || coverage.status === 'partial')) {
891
+ return 'partial';
892
+ }
893
+ return 'missing';
894
+ }
895
+
896
+ function derivePolicyJudgment(commandStatus: TestEvidenceStatus, evidenceCoverage: EvidenceCoverageStatus): SddTestStatus {
897
+ if (commandStatus === 'FAIL') {
898
+ return 'FAIL';
899
+ }
900
+ if (commandStatus === 'BLOCKED' || evidenceCoverage !== 'complete') {
901
+ return 'BLOCKED';
902
+ }
903
+ return 'PASS';
904
+ }
905
+
906
+ async function resolveTestRuntimeDurableGap(
907
+ projectRoot: string,
908
+ branch: string,
909
+ runId: string,
910
+ taskId: string,
911
+ validationStatus: SddTestStatus,
912
+ gaps: string[]
913
+ ): Promise<void> {
914
+ if (validationStatus !== 'PASS' || gaps.length > 0) {
915
+ return;
916
+ }
917
+ await updateRuntimeDurableGapStatus(projectRoot, {
918
+ gapId: testRuntimeGapId(branch, runId, taskId),
919
+ status: 'resolved',
920
+ source: 'gate_policy',
921
+ payload: { validationStatus, gaps }
922
+ });
923
+ }
924
+
925
+ async function recordTestRuntimeDurableGap(
926
+ projectRoot: string,
927
+ branch: string,
928
+ runId: string,
929
+ taskId: string,
930
+ status: SddTestStatus,
931
+ validationStatus: SddTestStatus,
932
+ runtimeJudgment: WorkflowGateStatus,
933
+ gaps: string[],
934
+ evidenceRefs: string[]
935
+ ): Promise<void> {
936
+ if (status === 'PASS' && gaps.length === 0) {
937
+ return;
938
+ }
939
+ const message = gaps[0] ?? (runtimeJudgment === 'PASS' ? `Validation status is ${validationStatus}.` : `Workflow gate status is ${runtimeJudgment}.`);
940
+ await recordRuntimeDurableGap(projectRoot, {
941
+ gapId: testRuntimeGapId(branch, runId, taskId),
942
+ partition: branch,
943
+ taskId,
944
+ runId,
945
+ stage: 'execute',
946
+ gate: 'execute',
947
+ source: 'runtime',
948
+ category: runtimeJudgment === 'PASS' ? 'validation' : 'workflow_gate',
949
+ severity: 'blocking',
950
+ status: 'open',
951
+ message,
952
+ recommendation: `Resolve execute runtime gaps for ${taskId}, then rerun ${testValidationUnitCommand(branch)}.`,
953
+ evidenceRefs,
954
+ proposalRefs: [],
955
+ sourceRefs: [],
956
+ payload: { status, validationStatus, runtimeJudgment, gaps }
957
+ });
958
+ }
959
+
960
+ function testRuntimeGapId(branch: string, _runId: string, taskId: string): string {
961
+ return runtimeScopedId(branch, taskId, 'test-runtime-gap');
962
+ }
963
+
964
+ function finalStatusForTest(validationStatus: SddTestStatus, runtimeJudgment: WorkflowGateStatus): SddTestStatus {
965
+ if (validationStatus !== 'PASS') {
966
+ return validationStatus;
967
+ }
968
+ return runtimeJudgment === 'PASS' || runtimeJudgment === 'WARN' ? 'PASS' : 'BLOCKED';
969
+ }
970
+
971
+ function buildUnifiedTestEvidenceRun(id: string, branch: string, runId: string, taskId: string, commandStatus: TestEvidenceStatus, evidenceCoverage: EvidenceCoverageStatus, policyJudgment: TestEvidenceStatus, status: SddTestStatus, runtimeJudgment: WorkflowGateStatus, steps: SddTestCommandStep[], acceptanceCoverage: AcceptanceEvidenceCoverage[], capabilityEvidence: CapabilityEvidenceClassification[], gaps: string[], gateNextAction: string | null, gateDecision: WorkflowGateDecision | null): UnifiedTestEvidenceRun {
972
+ return {
973
+ contract: TEST_EVIDENCE_RUN_CONTRACT_VERSION,
974
+ id,
975
+ scope: { branch, taskId, runId },
976
+ commandStatus,
977
+ evidenceCoverage,
978
+ policyJudgment,
979
+ commands: steps.map((step) => ({
980
+ command: step.command,
981
+ status: step.status === 'pass' ? 'PASS' : step.status === 'fail' ? 'FAIL' : 'BLOCKED',
982
+ outputRef: step.outputArtifact ? { kind: 'artifact', ref: step.outputArtifact } : undefined,
983
+ evidenceRefs: [commandStepRuntimeRef(step), ...artifactRuntimeRefs(step.outputArtifact)],
984
+ acceptanceRefs: step.acceptanceRefs,
985
+ startedAt: new Date(Date.now() - step.durationMs).toISOString(),
986
+ completedAt: new Date().toISOString()
987
+ })),
988
+ acceptanceCoverage,
989
+ capabilityEvidence,
990
+ gaps: [...gaps, ...acceptanceCoverage.flatMap((coverage) => coverage.gaps)],
991
+ next: nextForTestResult(status, runtimeJudgment, branch, taskId, gateNextAction, gateDecision),
992
+ generatedAt: new Date().toISOString()
993
+ };
994
+ }
995
+
996
+ function buildCapabilityEvidenceClassification(decision: AgentCapabilityRouteDecision | null, steps: SddTestCommandStep[]): CapabilityEvidenceClassification[] {
997
+ if (!decision) {
998
+ return [{
999
+ class: 'diagnostic',
1000
+ source: 'runtime_diagnostic',
1001
+ domainOrSourceId: 'capability-routing',
1002
+ evidenceRefs: [],
1003
+ acceptanceRefs: [],
1004
+ provenanceRefs: [],
1005
+ reason: 'Capability routing did not run; no capability output is accepted as test evidence.'
1006
+ }];
1007
+ }
1008
+ const acceptanceRefs = [...new Set(steps.flatMap((step) => step.acceptanceRefs))];
1009
+ const professionalEvidence = decision.selectedDomains.map((domain) => ({
1010
+ class: 'candidate' as const,
1011
+ source: 'professional_capability' as const,
1012
+ domainOrSourceId: domain.domain,
1013
+ evidenceRefs: [],
1014
+ acceptanceRefs,
1015
+ provenanceRefs: [{ kind: 'projection' as const, ref: `capability:${domain.capabilityId}` }],
1016
+ reason: `${domain.reason}; capability output is advisory candidate evidence until accepted by command evidence and policy refs.`
1017
+ }));
1018
+ const externalEvidence = decision.rejectedExternalSources.map((source) => ({
1019
+ class: capabilityClassForRejectedSource(source.quarantineStatus) as CapabilityEvidenceClassification['class'],
1020
+ source: 'external_source' as const,
1021
+ domainOrSourceId: source.sourceId,
1022
+ evidenceRefs: [],
1023
+ acceptanceRefs: [],
1024
+ provenanceRefs: [{ kind: 'external' as const, ref: source.sourceId }],
1025
+ reason: source.reason
1026
+ }));
1027
+ return [...professionalEvidence, ...externalEvidence];
1028
+ }
1029
+
1030
+ function capabilityClassForRejectedSource(status: AgentCapabilityRouteDecision['rejectedExternalSources'][number]['quarantineStatus']): CapabilityEvidenceClassification['class'] {
1031
+ if (status === 'denied') {
1032
+ return 'blocked';
1033
+ }
1034
+ if (status === 'required' || status === 'quarantined') {
1035
+ return 'quarantined';
1036
+ }
1037
+ return 'diagnostic';
1038
+ }
1039
+
1040
+
1041
+ function capabilityEvidenceSummary(items: CapabilityEvidenceClassification[]): string {
1042
+ if (items.length === 0) {
1043
+ return 'none';
1044
+ }
1045
+ const counts = new Map<CapabilityEvidenceClassification['class'], number>();
1046
+ for (const item of items) {
1047
+ counts.set(item.class, (counts.get(item.class) ?? 0) + 1);
1048
+ }
1049
+ return [...counts.entries()].map(([kind, count]) => `${kind}:${count}`).join(',');
1050
+ }
1051
+
1052
+ function nextForTestResult(status: SddTestStatus, runtimeJudgment: WorkflowGateStatus, branch: string, taskId: string, gateNextAction: string | null, gateDecision: WorkflowGateDecision | null): string {
1053
+ const inspectHint = `Inspect runtime test read model for ${taskId}`;
1054
+ if (status === 'PASS') {
1055
+ return `sdd execute close --branch ${branch} --compact-json`;
1056
+ }
1057
+ if (gateNextAction) {
1058
+ return gateNextAction;
1059
+ }
1060
+ if (runtimeJudgment === 'HUMAN_REQUIRED') {
1061
+ return gateDecision ? `Create a decision card for workflow gate ${gateDecision.decisionId}, then rerun ${testValidationUnitCommand(branch)}.` : `Create a decision card, then rerun ${testValidationUnitCommand(branch)}.`;
1062
+ }
1063
+ if (runtimeJudgment === 'WARN') {
1064
+ return gateDecision ? `Review workflow gate ${gateDecision.decisionId} warnings, then rerun ${testValidationUnitCommand(branch)} or proceed only with explicit review.` : `Review workflow gate warnings, then rerun ${testValidationUnitCommand(branch)}.`;
1065
+ }
1066
+ if (runtimeJudgment === 'ADVISORY_ONLY') {
1067
+ return `Inspect advisor assessments for ${taskId}; advisory output cannot satisfy the test gate.`;
1068
+ }
1069
+ if (status === 'FAIL') {
1070
+ return `${inspectHint}, fix failing validation commands, then rerun ${testValidationUnitCommand(branch)}.`;
1071
+ }
1072
+ return gateDecision ? `${inspectHint} and workflow gate ${gateDecision.decisionId}, resolve blockers, then rerun ${testValidationUnitCommand(branch)}.` : `${inspectHint}, fix command/evidence gaps, then rerun ${testValidationUnitCommand(branch)}.`;
1073
+ }
1074
+
1075
+
1076
+ function testValidationUnitCommand(branch: string): string {
1077
+ return `sdd execute --branch ${branch} --json`;
1078
+ }
1079
+
1080
+ function resultSentenceForTest(result: SddTestResult): string {
1081
+ if (result.status === 'PASS') {
1082
+ return 'Validation and workflow gate passed; proceed to execute evidence judgment and truthAlignment before release readiness.';
1083
+ }
1084
+ if (result.validationStatus === 'PASS' && result.workflowGateStatus !== 'PASS') {
1085
+ return `Validation passed, but workflow gate returned ${result.workflowGateStatus}.`;
1086
+ }
1087
+ if (result.commandStatus === 'BLOCKED') {
1088
+ return 'Blocked before validation commands ran.';
1089
+ }
1090
+ return result.status === 'FAIL' ? 'Validation failed.' : 'Validation did not produce complete evidence.';
1091
+ }
1092
+
1093
+ function renderEvidenceBlocks(task: SddTask, status: SddTestStatus, sourceArtifact: string, steps: SddTestCommandStep[]): string {
1094
+ const acceptances = task.acceptanceRefs.length > 0 ? task.acceptanceRefs : task.acceptance;
1095
+ if (acceptances.length === 0) {
1096
+ return 'No acceptance targets declared.';
1097
+ }
1098
+ const mappedEvidence = acceptances
1099
+ .map((acceptance) => ({ acceptance, steps: steps.filter((step) => step.acceptanceRefs.includes(acceptance)) }))
1100
+ .filter((item) => item.steps.length > 0);
1101
+ if (mappedEvidence.length === 0) {
1102
+ return 'No acceptance evidence emitted; validation commands are not explicitly mapped to acceptance refs.';
1103
+ }
1104
+ return mappedEvidence.map(({ acceptance, steps: mappedSteps }) => {
1105
+ const evidenceStatus = evidenceStatusForMappedSteps(status, mappedSteps);
1106
+ return `\`\`\`sdd-evidence\ncontract: ${SDD_EVIDENCE_CONTRACT}\nversion: ${SDD_EVIDENCE_VERSION}\ntask: ${task.id}\nacceptance: ${acceptance}\nstatus: ${evidenceStatus}\nclaim: Explicit validation mapping ${mappedSteps.map((step) => step.command).join(' && ')} produced ${evidenceStatus} for ${acceptance}.\nsource_artifact: ${sourceArtifact}\nevidence_refs:\n${mappedSteps.map(renderStepEvidenceRefs).join('\n')}\nprovenance_refs:\n - artifact:${sourceArtifact}\n${mappedSteps.map((step) => ` - command:${step.command}`).join('\n')}\npolicy_refs:\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-source-evidence\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-provenance\n - ${ACCEPTANCE_POLICY_RULESET_VERSION}:require-policy-rule\n\`\`\``;
1107
+ }).join('\n\n');
1108
+ }
1109
+
1110
+ function normalizeTestCommandInputs(commandInputs: SddTestCommandInput[] | undefined, commands: string[] | undefined, taskValidation: string[]): NormalizedSddTestCommand[] {
1111
+ if (commandInputs && commandInputs.length > 0) {
1112
+ return commandInputs.map(normalizeTestCommandInput);
1113
+ }
1114
+ return (commands && commands.length > 0 ? commands : taskValidation).map((command) => ({ command, argv: null, shell: true }));
1115
+ }
1116
+
1117
+ function normalizeTestCommandInput(input: SddTestCommandInput): NormalizedSddTestCommand {
1118
+ if (input.argv) {
1119
+ const argv = input.argv.filter((item) => item.length > 0);
1120
+ if (argv.length === 0) {
1121
+ throw new Error('Command argv input must include an executable.');
1122
+ }
1123
+ return { command: argv.join(' '), argv, shell: false };
1124
+ }
1125
+ if (input.command) {
1126
+ return { command: input.command, argv: null, shell: true };
1127
+ }
1128
+ throw new Error('Command input must include command or argv.');
1129
+ }
1130
+
1131
+ function acceptanceRefsForCommand(task: SddTask | null, command: string, acceptanceRefsOverride: string[] | undefined): string[] {
1132
+ if (acceptanceRefsOverride && acceptanceRefsOverride.length > 0) {
1133
+ return [...new Set(acceptanceRefsOverride)];
1134
+ }
1135
+ return [...new Set((task?.validationCommands ?? [])
1136
+ .filter((entry) => entry.command === command)
1137
+ .flatMap((entry) => entry.acceptanceRefs))];
1138
+ }
1139
+
1140
+ function taskAcceptanceRefs(task: SddTask): string[] {
1141
+ const refs = task.acceptanceRefs.length > 0 ? task.acceptanceRefs : task.acceptance;
1142
+ return [...new Set(refs)];
1143
+ }
1144
+
1145
+ function evidenceStatusForMappedSteps(status: SddTestStatus, steps: SddTestCommandStep[]): SddTestStatus {
1146
+ if (steps.some((step) => step.status === 'fail')) {
1147
+ return 'FAIL';
1148
+ }
1149
+ if (status === 'BLOCKED' || steps.some((step) => step.status === 'blocked')) {
1150
+ return 'BLOCKED';
1151
+ }
1152
+ return 'PASS';
1153
+ }
1154
+
1155
+ function renderCommandOutput(commandInput: NormalizedSddTestCommand, status: SddTestStepStatus, executed: { exitCode: number | null; signal: string | null; stdout: string; stderr: string; truncated: boolean; timedOut: boolean; error: string | null }, durationMs: number, cacheStatus: 'hit' | 'miss' | 'unsafe' = 'unsafe', cacheKey: string | null = null, cacheSourceTestRunId: string | null = null, cacheUnsafeReasons: ValidationCacheUnsafeReason[] = []): string {
1156
+ return `# Test Command Output\n\n- command: ${commandInput.command}\n- shell: ${commandInput.shell}\n- argv: ${commandInput.argv ? JSON.stringify(commandInput.argv) : 'none'}\n- status: ${status}\n- exit_code: ${executed.exitCode ?? 'none'}\n- signal: ${executed.signal ?? 'none'}\n- duration_ms: ${durationMs}\n- timed_out: ${executed.timedOut}\n- truncated: ${executed.truncated}\n- error: ${executed.error ?? 'none'}\n- cache_status: ${cacheStatus}\n- cache_key: ${cacheKey ?? 'none'}\n- cache_source_test_run: ${cacheSourceTestRunId ?? 'none'}\n- cache_unsafe_reasons: ${cacheUnsafeReasons.join(',') || 'none'}\n\n## stdout\n\n\`\`\`text\n${executed.stdout}\n\`\`\`\n\n## stderr\n\n\`\`\`text\n${executed.stderr}\n\`\`\`\n`;
1157
+ }
1158
+
1159
+ function shouldPersistCommandOutputArtifact(status: SddTestStepStatus, executed: { truncated: boolean; timedOut: boolean; error: string | null }, cacheStatus: 'hit' | 'miss' | 'unsafe', cacheUnsafeReasons: ValidationCacheUnsafeReason[]): boolean {
1160
+ return status !== 'pass' || executed.truncated || executed.timedOut || Boolean(executed.error) || cacheStatus === 'unsafe' || cacheUnsafeReasons.length > 0;
1161
+ }
1162
+
1163
+ function renderStepEvidenceRefs(step: SddTestCommandStep): string {
1164
+ const refs = [` - command:${step.stepId}`];
1165
+ if (step.outputArtifact) {
1166
+ refs.push(` - artifact:${step.outputArtifact}`);
1167
+ }
1168
+ return refs.join('\n');
1169
+ }
1170
+
1171
+
1172
+ function summarizeCommandOutput(executed: { exitCode: number | null; signal: string | null; stdout: string; stderr: string; stdoutBytes: number; stderrBytes: number; truncated: boolean; timedOut: boolean; error: string | null }): string {
1173
+ const parts = [
1174
+ `exit=${executed.exitCode ?? 'none'}`,
1175
+ `signal=${executed.signal ?? 'none'}`,
1176
+ `stdout_bytes=${executed.stdoutBytes}`,
1177
+ `stderr_bytes=${executed.stderrBytes}`,
1178
+ `truncated=${executed.truncated}`,
1179
+ `timed_out=${executed.timedOut}`
1180
+ ];
1181
+ if (executed.error) {
1182
+ parts.push(`error=${executed.error}`);
1183
+ }
1184
+ return parts.join(' ');
1185
+ }
1186
+
1187
+
1188
+ function hashDocumentContent(raw: string): string {
1189
+ return createHash('sha256').update(raw.replace(/\r\n/g, '\n'), 'utf8').digest('hex');
1190
+ }