sdd-agent-platform 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (826) hide show
  1. package/README.md +33 -39
  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 -0
  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 +7 -13
  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/project.js +8 -8
  37. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js.map +1 -1
  38. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js +1 -0
  39. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js.map +1 -1
  40. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js +4 -4
  41. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js.map +1 -1
  42. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js +24 -0
  43. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js.map +1 -1
  44. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js +1 -1
  45. package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  46. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +43 -180
  47. package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
  48. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +1 -1
  49. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +7 -14
  50. package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -1
  51. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js +110 -0
  52. package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/coordination.js.map +1 -0
  53. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js +4 -4
  54. package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js.map +1 -1
  55. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js +3 -3
  56. package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js.map +1 -1
  57. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +85 -86
  58. package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js.map +1 -1
  59. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +2 -3
  60. package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
  61. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +2 -2
  62. package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -1
  63. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.d.ts +1 -1
  64. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js +1 -1
  65. package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js.map +1 -1
  66. package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
  67. package/node_modules/@sdd-agent-platform/core/dist/instructions.js +31 -67
  68. package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
  69. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js +1 -1
  70. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js.map +1 -1
  71. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +0 -1
  72. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +59 -85
  73. package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
  74. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  75. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js +7 -0
  76. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/contracts.js.map +1 -0
  77. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  78. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js +461 -0
  79. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph/kernel.js.map +1 -0
  80. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.d.ts +2 -0
  81. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js +3 -0
  82. package/node_modules/@sdd-agent-platform/core/dist/lifecycle-graph.js.map +1 -0
  83. package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
  84. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.d.ts +2 -12
  85. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +32 -80
  86. package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
  87. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +2 -5
  88. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +27 -69
  89. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
  90. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +118 -34
  91. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
  92. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +1 -1
  93. package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
  94. package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +1 -1
  95. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
  96. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +8 -15
  97. package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
  98. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js +4 -4
  99. package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js.map +1 -1
  100. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.d.ts +13 -0
  101. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js +76 -0
  102. package/node_modules/@sdd-agent-platform/core/dist/registries/plan-scout-domains.js.map +1 -0
  103. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js +2 -2
  104. package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js.map +1 -1
  105. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js +7 -7
  106. package/node_modules/@sdd-agent-platform/core/dist/registries/skill-capabilities.js.map +1 -1
  107. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +4 -4
  108. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
  109. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js +2 -2
  110. package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js.map +1 -1
  111. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js +11 -11
  112. package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js.map +1 -1
  113. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.d.ts +1 -1
  114. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +21 -21
  115. package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
  116. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js +2 -1
  117. package/node_modules/@sdd-agent-platform/core/dist/risk/consumer-diagnostics.js.map +1 -1
  118. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +6 -6
  119. package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
  120. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +11 -23
  121. package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
  122. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.d.ts +2 -2
  123. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +18 -20
  124. package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
  125. package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +0 -2
  126. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +1 -1
  127. package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
  128. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +16 -48
  129. package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
  130. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +11 -1
  131. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -1
  132. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +2 -2
  133. package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
  134. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +2 -2
  135. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +20 -28
  136. package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -1
  137. package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +0 -1
  138. package/node_modules/@sdd-agent-platform/core/dist/router.js +0 -1
  139. package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
  140. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +6 -6
  141. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +13 -124
  142. package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
  143. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +2 -0
  144. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +5 -7
  145. package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
  146. package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +28 -28
  147. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.d.ts +2 -0
  148. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js +3 -1
  149. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js.map +1 -1
  150. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +26 -36
  151. package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
  152. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +0 -4
  153. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +5 -51
  154. package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -1
  155. package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +0 -1
  156. package/node_modules/@sdd-agent-platform/core/dist/run-state.js +0 -1
  157. package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
  158. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +1 -1
  159. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
  160. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +5 -5
  161. package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
  162. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +1 -1
  163. package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
  164. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +2 -2
  165. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +11 -0
  166. package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -1
  167. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  168. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js +179 -0
  169. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  170. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +0 -2
  171. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +10 -97
  172. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -1
  173. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.d.ts +1 -1
  174. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +6 -8
  175. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
  176. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +5 -2
  177. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +85 -68
  178. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
  179. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js +2 -2
  180. package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-rendering.js.map +1 -1
  181. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js +40 -0
  182. package/node_modules/@sdd-agent-platform/core/dist/spec-entry.js.map +1 -0
  183. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.d.ts +12 -0
  184. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js +2 -0
  185. package/node_modules/@sdd-agent-platform/core/dist/spec-manager-contracts.js.map +1 -0
  186. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +2 -2
  187. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +19 -26
  188. package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -1
  189. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +1 -1
  190. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +3 -6
  191. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -1
  192. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +111 -263
  193. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +1272 -1124
  194. package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -1
  195. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +5 -5
  196. package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
  197. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +1 -44
  198. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +47 -170
  199. package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
  200. package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +73 -73
  201. package/node_modules/@sdd-agent-platform/core/dist/subagents/contracts.d.ts +1 -1
  202. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js +7 -7
  203. package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js.map +1 -1
  204. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +1 -0
  205. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +2 -0
  206. package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +1 -0
  207. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +1 -0
  208. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +2 -0
  209. package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +1 -0
  210. package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +1 -0
  211. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +2 -0
  212. package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +1 -0
  213. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.d.ts +167 -0
  214. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js +377 -0
  215. package/node_modules/@sdd-agent-platform/core/dist/task-execution-contract.js.map +1 -0
  216. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +329 -314
  217. package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
  218. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -0
  219. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +53 -7
  220. package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
  221. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +9 -12
  222. package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -1
  223. package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
  224. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -48
  225. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +1 -520
  226. package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
  227. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +5 -5
  228. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +14 -14
  229. package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
  230. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.d.ts +1 -0
  231. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +111 -159
  232. package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
  233. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  234. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js +521 -0
  235. package/node_modules/@sdd-agent-platform/core/dist/verification/task-evidence-judgment.js.map +1 -0
  236. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +21 -21
  237. package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
  238. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +0 -18
  239. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +5 -27
  240. package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -1
  241. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +45 -45
  242. package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
  243. package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +3 -3
  244. package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -2
  245. package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
  246. package/node_modules/@sdd-agent-platform/core/dist/work-units/contracts.d.ts +1 -1
  247. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +9 -227
  248. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  249. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +9 -50
  250. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -1
  251. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +4 -42
  252. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -1
  253. package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +2 -3
  254. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  255. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +2 -1
  256. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  257. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +1 -1
  258. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +1 -0
  259. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +23 -63
  260. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  261. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +2 -2
  262. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +43 -65
  263. package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
  264. package/node_modules/@sdd-agent-platform/core/package.json +5 -2
  265. package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +238 -185
  266. package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +56 -73
  267. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +189 -227
  268. package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +222 -278
  269. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.test.ts +28 -28
  270. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +302 -301
  271. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +181 -181
  272. package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +231 -240
  273. package/node_modules/@sdd-agent-platform/core/src/artifacts/templates.ts +99 -99
  274. package/node_modules/@sdd-agent-platform/core/src/artifacts.ts +4 -4
  275. package/node_modules/@sdd-agent-platform/core/src/coding-facts/contracts.ts +79 -79
  276. package/node_modules/@sdd-agent-platform/core/src/coding-facts.ts +1 -1
  277. package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +314 -318
  278. package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +128 -123
  279. package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +265 -265
  280. package/node_modules/@sdd-agent-platform/core/src/config/project-detection.ts +147 -147
  281. package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +400 -432
  282. package/node_modules/@sdd-agent-platform/core/src/context/budget.ts +30 -30
  283. package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +305 -311
  284. package/node_modules/@sdd-agent-platform/core/src/context/command-summary.ts +45 -45
  285. package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +188 -189
  286. package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +144 -163
  287. package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +48 -48
  288. package/node_modules/@sdd-agent-platform/core/src/context/source-refs.ts +41 -41
  289. package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +47 -47
  290. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.test.ts +71 -71
  291. package/node_modules/@sdd-agent-platform/core/src/context-offload/runtime.ts +178 -178
  292. package/node_modules/@sdd-agent-platform/core/src/context-offload.ts +2 -2
  293. package/node_modules/@sdd-agent-platform/core/src/context.ts +6 -6
  294. package/node_modules/@sdd-agent-platform/core/src/contracts/issues.ts +13 -13
  295. package/node_modules/@sdd-agent-platform/core/src/contracts.test.ts +9 -9
  296. package/node_modules/@sdd-agent-platform/core/src/contracts.ts +121 -116
  297. package/node_modules/@sdd-agent-platform/core/src/delegation/delegation.test.ts +183 -183
  298. package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +23 -26
  299. package/node_modules/@sdd-agent-platform/core/src/delegation/queue.ts +58 -58
  300. package/node_modules/@sdd-agent-platform/core/src/delegation/run-state.ts +14 -14
  301. package/node_modules/@sdd-agent-platform/core/src/delegation/state-machine.ts +90 -90
  302. package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +124 -127
  303. package/node_modules/@sdd-agent-platform/core/src/delegation.ts +26 -26
  304. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/ai-entries.ts +28 -28
  305. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +104 -112
  306. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/local-run-index.ts +27 -27
  307. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +84 -84
  308. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +252 -251
  309. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +330 -330
  310. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-records.ts +79 -79
  311. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +128 -107
  312. package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +300 -300
  313. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +627 -755
  314. package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +301 -453
  315. package/node_modules/@sdd-agent-platform/core/src/doctor/model.ts +13 -13
  316. package/node_modules/@sdd-agent-platform/core/src/doctor/summary.ts +11 -11
  317. package/node_modules/@sdd-agent-platform/core/src/doctor.ts +2 -2
  318. package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +80 -88
  319. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +48 -48
  320. package/node_modules/@sdd-agent-platform/core/src/evidence-runtime.ts +1 -1
  321. package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +195 -195
  322. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +187 -235
  323. package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +305 -305
  324. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +97 -106
  325. package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +453 -453
  326. package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +225 -226
  327. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +132 -143
  328. package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +436 -437
  329. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -102
  330. package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -271
  331. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +111 -121
  332. package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.ts +231 -231
  333. package/node_modules/@sdd-agent-platform/core/src/execution.ts +5 -5
  334. package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +57 -65
  335. package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +175 -175
  336. package/node_modules/@sdd-agent-platform/core/src/governance.ts +1 -1
  337. package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +80 -64
  338. package/node_modules/@sdd-agent-platform/core/src/instructions.ts +32 -68
  339. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +174 -174
  340. package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +373 -373
  341. package/node_modules/@sdd-agent-platform/core/src/lifecycle/rendering.ts +29 -29
  342. package/node_modules/@sdd-agent-platform/core/src/lifecycle/risk-signals.ts +146 -146
  343. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -47
  344. package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +255 -280
  345. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/contracts.ts +179 -0
  346. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph/kernel.ts +522 -0
  347. package/node_modules/@sdd-agent-platform/core/src/lifecycle-graph.ts +2 -0
  348. package/node_modules/@sdd-agent-platform/core/src/lifecycle.ts +4 -4
  349. package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +50 -50
  350. package/node_modules/@sdd-agent-platform/core/src/orchestration/index.ts +2 -2
  351. package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +331 -394
  352. package/node_modules/@sdd-agent-platform/core/src/path-safety.test.ts +22 -22
  353. package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +243 -242
  354. package/node_modules/@sdd-agent-platform/core/src/phase8-projection-compat.test.ts +152 -153
  355. package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +277 -277
  356. package/node_modules/@sdd-agent-platform/core/src/phase9-lifecycle-graph.test.ts +103 -0
  357. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +88 -88
  358. package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.ts +222 -222
  359. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +79 -79
  360. package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.ts +160 -160
  361. package/node_modules/@sdd-agent-platform/core/src/planning.ts +2 -2
  362. package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +426 -473
  363. package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +230 -146
  364. package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +142 -142
  365. package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +253 -253
  366. package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +302 -309
  367. package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +246 -246
  368. package/node_modules/@sdd-agent-platform/core/src/registries/plan-scout-domains.ts +89 -0
  369. package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +119 -119
  370. package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +454 -445
  371. package/node_modules/@sdd-agent-platform/core/src/registries/skill-capabilities.ts +37 -37
  372. package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +135 -135
  373. package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +132 -132
  374. package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +144 -144
  375. package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +111 -111
  376. package/node_modules/@sdd-agent-platform/core/src/registries.ts +42 -42
  377. package/node_modules/@sdd-agent-platform/core/src/risk/consumer-diagnostics.ts +98 -97
  378. package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +63 -63
  379. package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +233 -233
  380. package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +251 -263
  381. package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +203 -205
  382. package/node_modules/@sdd-agent-platform/core/src/risk.ts +5 -5
  383. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +327 -327
  384. package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +388 -390
  385. package/node_modules/@sdd-agent-platform/core/src/router/profile-resolution.ts +154 -154
  386. package/node_modules/@sdd-agent-platform/core/src/router/risk-policy.ts +33 -33
  387. package/node_modules/@sdd-agent-platform/core/src/router/route-cache.ts +100 -100
  388. package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +356 -356
  389. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +428 -665
  390. package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.ts +2 -2
  391. package/node_modules/@sdd-agent-platform/core/src/router/routing-rules.ts +73 -73
  392. package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +189 -223
  393. package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +464 -453
  394. package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +124 -124
  395. package/node_modules/@sdd-agent-platform/core/src/router/runtime-registry.ts +123 -123
  396. package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +277 -277
  397. package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +273 -279
  398. package/node_modules/@sdd-agent-platform/core/src/router/team-mode.ts +170 -170
  399. package/node_modules/@sdd-agent-platform/core/src/router.ts +5 -6
  400. package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +126 -240
  401. package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +27 -27
  402. package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +172 -172
  403. package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +109 -109
  404. package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +252 -253
  405. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.test.ts +52 -52
  406. package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +356 -352
  407. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +70 -118
  408. package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +406 -416
  409. package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +198 -252
  410. package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -146
  411. package/node_modules/@sdd-agent-platform/core/src/run-state.ts +8 -9
  412. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +60 -60
  413. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +257 -256
  414. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +140 -140
  415. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +66 -66
  416. package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.ts +2 -2
  417. package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +253 -253
  418. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +101 -96
  419. package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +314 -292
  420. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.test.ts +380 -0
  421. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/artifact-depth.ts +207 -0
  422. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +111 -111
  423. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +207 -306
  424. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +95 -97
  425. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-inspection.ts +39 -39
  426. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +467 -523
  427. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +738 -709
  428. package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-rendering.ts +81 -81
  429. package/node_modules/@sdd-agent-platform/core/src/sdd-docs.ts +5 -5
  430. package/node_modules/@sdd-agent-platform/core/src/spec-manager-contracts.ts +13 -0
  431. package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +435 -450
  432. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +316 -322
  433. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2963 -2902
  434. package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5856 -5831
  435. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/contracts.ts +40 -40
  436. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +209 -209
  437. package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +360 -360
  438. package/node_modules/@sdd-agent-platform/core/src/stage-runtime.ts +2 -2
  439. package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +288 -511
  440. package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +651 -851
  441. package/node_modules/@sdd-agent-platform/core/src/status.ts +2 -2
  442. package/node_modules/@sdd-agent-platform/core/src/storage/json-io.ts +10 -10
  443. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +489 -681
  444. package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +1981 -1981
  445. package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +45 -45
  446. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +232 -232
  447. package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +307 -307
  448. package/node_modules/@sdd-agent-platform/core/src/subagents.ts +2 -2
  449. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.test.ts +141 -0
  450. package/node_modules/@sdd-agent-platform/core/src/task-execution-contract.ts +566 -0
  451. package/node_modules/@sdd-agent-platform/core/src/task-risk-profile.ts +193 -193
  452. package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +413 -398
  453. package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +102 -56
  454. package/node_modules/@sdd-agent-platform/core/src/test-support.ts +2 -2
  455. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -72
  456. package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +9 -12
  457. package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +137 -137
  458. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +77 -84
  459. package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -77
  460. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +455 -506
  461. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.test.ts → task-evidence-judgment.test.ts} +261 -261
  462. package/node_modules/@sdd-agent-platform/core/src/verification/{goal-verify.ts → task-evidence-judgment.ts} +619 -619
  463. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +1190 -1190
  464. package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -106
  465. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +513 -556
  466. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +334 -334
  467. package/node_modules/@sdd-agent-platform/core/src/verification.ts +8 -8
  468. package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +26 -26
  469. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.test.ts +88 -88
  470. package/node_modules/@sdd-agent-platform/core/src/work-units/runtime.ts +112 -112
  471. package/node_modules/@sdd-agent-platform/core/src/work-units.ts +2 -2
  472. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +190 -425
  473. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +169 -507
  474. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +136 -182
  475. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +135 -174
  476. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +153 -194
  477. package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +111 -115
  478. package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +95 -93
  479. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +32 -32
  480. package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +114 -114
  481. package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +184 -224
  482. package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -158
  483. package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -77
  484. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -114
  485. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +969 -956
  486. package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +967 -992
  487. package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -712
  488. package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +2 -2
  489. package/node_modules/@sdd-agent-platform/core/src/worktree/isolation.ts +130 -130
  490. package/node_modules/@sdd-agent-platform/core/src/worktree/lifecycle.ts +269 -269
  491. package/node_modules/@sdd-agent-platform/core/src/worktree/worktree.test.ts +150 -150
  492. package/node_modules/@sdd-agent-platform/core/src/worktree.ts +2 -2
  493. package/node_modules/@sdd-agent-platform/core/tsconfig.json +15 -15
  494. package/package.json +2 -2
  495. package/packages/cli/dist/args.js +2 -2
  496. package/packages/cli/dist/args.js.map +1 -1
  497. package/packages/cli/dist/commands/ai-tools.js +2 -13
  498. package/packages/cli/dist/commands/ai-tools.js.map +1 -1
  499. package/packages/cli/dist/commands/{verifies.d.ts → artifact.d.ts} +1 -1
  500. package/packages/cli/dist/commands/artifact.js +168 -0
  501. package/packages/cli/dist/commands/artifact.js.map +1 -0
  502. package/packages/cli/dist/commands/context.js +1 -1
  503. package/packages/cli/dist/commands/context.js.map +1 -1
  504. package/packages/cli/dist/commands/evidence.js.map +1 -0
  505. package/packages/cli/dist/commands/execution.js +127 -49
  506. package/packages/cli/dist/commands/execution.js.map +1 -1
  507. package/packages/cli/dist/commands/governance.js +1 -1
  508. package/packages/cli/dist/commands/governance.js.map +1 -1
  509. package/packages/cli/dist/commands/init.js +1 -6
  510. package/packages/cli/dist/commands/init.js.map +1 -1
  511. package/packages/cli/dist/commands/instructions.d.ts +1 -1
  512. package/packages/cli/dist/commands/instructions.js +15 -1
  513. package/packages/cli/dist/commands/instructions.js.map +1 -1
  514. package/packages/cli/dist/commands/registry/runtime.js +63 -40
  515. package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
  516. package/packages/cli/dist/commands/run.js +13 -52
  517. package/packages/cli/dist/commands/run.js.map +1 -1
  518. package/packages/cli/dist/commands/stage-close.d.ts +60 -0
  519. package/packages/cli/dist/commands/stage-close.js +270 -41
  520. package/packages/cli/dist/commands/stage-close.js.map +1 -1
  521. package/packages/cli/dist/commands/status.js +9 -68
  522. package/packages/cli/dist/commands/status.js.map +1 -1
  523. package/packages/cli/dist/commands/tasks.js.map +1 -1
  524. package/packages/cli/dist/dispatch.js +6 -26
  525. package/packages/cli/dist/dispatch.js.map +1 -1
  526. package/packages/cli/dist/help.js +153 -159
  527. package/packages/cli/dist/help.js.map +1 -1
  528. package/packages/cli/dist/renderers/artifacts.d.ts +5 -0
  529. package/packages/cli/dist/renderers/artifacts.js +43 -0
  530. package/packages/cli/dist/renderers/artifacts.js.map +1 -0
  531. package/packages/cli/dist/renderers/doctor.js +1 -1
  532. package/packages/cli/dist/renderers/doctor.js.map +1 -1
  533. package/packages/cli/dist/renderers/execution.js +1 -1
  534. package/packages/cli/dist/renderers/execution.js.map +1 -1
  535. package/packages/cli/dist/renderers/json.d.ts +0 -1
  536. package/packages/cli/dist/renderers/json.js +0 -3
  537. package/packages/cli/dist/renderers/json.js.map +1 -1
  538. package/packages/cli/dist/renderers/registry-runtime.d.ts +1 -2
  539. package/packages/cli/dist/renderers/registry-runtime.js +0 -20
  540. package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
  541. package/packages/cli/dist/renderers/router.js +1 -1
  542. package/packages/cli/dist/renderers/router.js.map +1 -1
  543. package/packages/cli/dist/renderers/workflow.d.ts +53 -0
  544. package/packages/cli/dist/renderers/workflow.js +89 -30
  545. package/packages/cli/dist/renderers/workflow.js.map +1 -1
  546. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  547. package/packages/cli/package.json +2 -2
  548. package/packages/core/dist/ai-tools.js +56 -73
  549. package/packages/core/dist/ai-tools.js.map +1 -1
  550. package/packages/core/dist/artifacts/ingestion.js +9 -64
  551. package/packages/core/dist/artifacts/ingestion.js.map +1 -1
  552. package/packages/core/dist/artifacts/sdd-evidence.js +1 -0
  553. package/packages/core/dist/artifacts/sdd-evidence.js.map +1 -1
  554. package/packages/core/dist/artifacts/sdd-result.js +17 -26
  555. package/packages/core/dist/artifacts/sdd-result.js.map +1 -1
  556. package/packages/core/dist/config/init-project.d.ts +8 -7
  557. package/packages/core/dist/config/init-project.js +8 -12
  558. package/packages/core/dist/config/init-project.js.map +1 -1
  559. package/packages/core/dist/config/project-config.d.ts +1 -1
  560. package/packages/core/dist/config/project-config.js +1 -1
  561. package/packages/core/dist/config/project-config.js.map +1 -1
  562. package/packages/core/dist/config/starter-documents.d.ts +3 -4
  563. package/packages/core/dist/config/starter-documents.js +377 -411
  564. package/packages/core/dist/config/starter-documents.js.map +1 -1
  565. package/packages/core/dist/context/build-package.d.ts +1 -1
  566. package/packages/core/dist/context/build-package.js +7 -13
  567. package/packages/core/dist/context/build-package.js.map +1 -1
  568. package/packages/core/dist/context/evidence-summary.js +8 -26
  569. package/packages/core/dist/context/evidence-summary.js.map +1 -1
  570. package/packages/core/dist/context/log-worker.js +2 -2
  571. package/packages/core/dist/context/log-worker.js.map +1 -1
  572. package/packages/core/dist/context-offload/contracts.d.ts +1 -1
  573. package/packages/core/dist/contracts.d.ts +6 -1
  574. package/packages/core/dist/contracts.js +5 -0
  575. package/packages/core/dist/contracts.js.map +1 -1
  576. package/packages/core/dist/delegation/model.d.ts +0 -3
  577. package/packages/core/dist/delegation/validation.d.ts +0 -3
  578. package/packages/core/dist/delegation/validation.js +4 -7
  579. package/packages/core/dist/delegation/validation.js.map +1 -1
  580. package/packages/core/dist/doctor/checks/document-chain.js +3 -13
  581. package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
  582. package/packages/core/dist/doctor/checks/project.js +8 -8
  583. package/packages/core/dist/doctor/checks/project.js.map +1 -1
  584. package/packages/core/dist/doctor/checks/registries.js +1 -0
  585. package/packages/core/dist/doctor/checks/registries.js.map +1 -1
  586. package/packages/core/dist/doctor/checks/run-evidence.js +4 -4
  587. package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
  588. package/packages/core/dist/doctor/checks/run-trust.js +24 -0
  589. package/packages/core/dist/doctor/checks/run-trust.js.map +1 -1
  590. package/packages/core/dist/doctor/checks/runtime-contracts.js +1 -1
  591. package/packages/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
  592. package/packages/core/dist/doctor/doctor.js +43 -180
  593. package/packages/core/dist/doctor/doctor.js.map +1 -1
  594. package/packages/core/dist/evidence/lookup.d.ts +1 -1
  595. package/packages/core/dist/evidence/lookup.js +7 -14
  596. package/packages/core/dist/evidence/lookup.js.map +1 -1
  597. package/packages/core/dist/evidence-runtime/coordination.js +110 -0
  598. package/packages/core/dist/evidence-runtime/coordination.js.map +1 -0
  599. package/packages/core/dist/execution/background-executor.js +4 -4
  600. package/packages/core/dist/execution/background-executor.js.map +1 -1
  601. package/packages/core/dist/execution/foreground-subagents.js +3 -3
  602. package/packages/core/dist/execution/foreground-subagents.js.map +1 -1
  603. package/packages/core/dist/execution/host-invocation.js +85 -86
  604. package/packages/core/dist/execution/host-invocation.js.map +1 -1
  605. package/packages/core/dist/execution/resident-worker.js +2 -3
  606. package/packages/core/dist/execution/resident-worker.js.map +1 -1
  607. package/packages/core/dist/execution/stage-team-runtime.js +2 -2
  608. package/packages/core/dist/execution/stage-team-runtime.js.map +1 -1
  609. package/packages/core/dist/governance/policy.d.ts +1 -1
  610. package/packages/core/dist/governance/policy.js +1 -1
  611. package/packages/core/dist/governance/policy.js.map +1 -1
  612. package/packages/core/dist/instructions.d.ts +1 -1
  613. package/packages/core/dist/instructions.js +31 -67
  614. package/packages/core/dist/instructions.js.map +1 -1
  615. package/packages/core/dist/lifecycle/decision-gate.js +1 -1
  616. package/packages/core/dist/lifecycle/decision-gate.js.map +1 -1
  617. package/packages/core/dist/lifecycle/ship.d.ts +0 -1
  618. package/packages/core/dist/lifecycle/ship.js +59 -85
  619. package/packages/core/dist/lifecycle/ship.js.map +1 -1
  620. package/packages/core/dist/lifecycle-graph/contracts.d.ts +159 -0
  621. package/packages/core/dist/lifecycle-graph/contracts.js +7 -0
  622. package/packages/core/dist/lifecycle-graph/contracts.js.map +1 -0
  623. package/packages/core/dist/lifecycle-graph/kernel.d.ts +16 -0
  624. package/packages/core/dist/lifecycle-graph/kernel.js +461 -0
  625. package/packages/core/dist/lifecycle-graph/kernel.js.map +1 -0
  626. package/packages/core/dist/lifecycle-graph.d.ts +2 -0
  627. package/packages/core/dist/lifecycle-graph.js +3 -0
  628. package/packages/core/dist/lifecycle-graph.js.map +1 -0
  629. package/packages/core/dist/orchestration/contracts.d.ts +1 -1
  630. package/packages/core/dist/orchestration/runtime.d.ts +2 -12
  631. package/packages/core/dist/orchestration/runtime.js +32 -80
  632. package/packages/core/dist/orchestration/runtime.js.map +1 -1
  633. package/packages/core/dist/registries/agent-capability-catalog.d.ts +2 -5
  634. package/packages/core/dist/registries/agent-capability-catalog.js +27 -69
  635. package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
  636. package/packages/core/dist/registries/agent-registry.js +118 -34
  637. package/packages/core/dist/registries/agent-registry.js.map +1 -1
  638. package/packages/core/dist/registries/agent-runtime-static.js +1 -1
  639. package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
  640. package/packages/core/dist/registries/capability-sources.js +1 -1
  641. package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
  642. package/packages/core/dist/registries/command-team-runtime.js +8 -15
  643. package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
  644. package/packages/core/dist/registries/eval-learning-context.js +4 -4
  645. package/packages/core/dist/registries/eval-learning-context.js.map +1 -1
  646. package/packages/core/dist/registries/plan-scout-domains.d.ts +13 -0
  647. package/packages/core/dist/registries/plan-scout-domains.js +76 -0
  648. package/packages/core/dist/registries/plan-scout-domains.js.map +1 -0
  649. package/packages/core/dist/registries/query-status.js +2 -2
  650. package/packages/core/dist/registries/query-status.js.map +1 -1
  651. package/packages/core/dist/registries/skill-capabilities.js +7 -7
  652. package/packages/core/dist/registries/skill-capabilities.js.map +1 -1
  653. package/packages/core/dist/registries/tool-capabilities.js +4 -4
  654. package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
  655. package/packages/core/dist/registries/tool-plugins.js +2 -2
  656. package/packages/core/dist/registries/tool-plugins.js.map +1 -1
  657. package/packages/core/dist/registries/worker-adapters.js +11 -11
  658. package/packages/core/dist/registries/worker-adapters.js.map +1 -1
  659. package/packages/core/dist/registries/workflow-gates.d.ts +1 -1
  660. package/packages/core/dist/registries/workflow-gates.js +21 -21
  661. package/packages/core/dist/registries/workflow-gates.js.map +1 -1
  662. package/packages/core/dist/risk/consumer-diagnostics.js +2 -1
  663. package/packages/core/dist/risk/consumer-diagnostics.js.map +1 -1
  664. package/packages/core/dist/risk/kernel.js +6 -6
  665. package/packages/core/dist/risk/kernel.js.map +1 -1
  666. package/packages/core/dist/risk/legacy-adapters.js +11 -23
  667. package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
  668. package/packages/core/dist/risk/workflow-gates.d.ts +2 -2
  669. package/packages/core/dist/risk/workflow-gates.js +18 -20
  670. package/packages/core/dist/risk/workflow-gates.js.map +1 -1
  671. package/packages/core/dist/router/agent-runtime.d.ts +0 -2
  672. package/packages/core/dist/router/route-projection.js +1 -1
  673. package/packages/core/dist/router/route-projection.js.map +1 -1
  674. package/packages/core/dist/router/routing.js +16 -48
  675. package/packages/core/dist/router/routing.js.map +1 -1
  676. package/packages/core/dist/router/runtime-import.js +11 -1
  677. package/packages/core/dist/router/runtime-import.js.map +1 -1
  678. package/packages/core/dist/router/runtime-validation.js +2 -2
  679. package/packages/core/dist/router/runtime-validation.js.map +1 -1
  680. package/packages/core/dist/router/stage-route-binding.d.ts +2 -2
  681. package/packages/core/dist/router/stage-route-binding.js +20 -28
  682. package/packages/core/dist/router/stage-route-binding.js.map +1 -1
  683. package/packages/core/dist/router.d.ts +0 -1
  684. package/packages/core/dist/router.js +0 -1
  685. package/packages/core/dist/router.js.map +1 -1
  686. package/packages/core/dist/run-state/artifacts.d.ts +6 -6
  687. package/packages/core/dist/run-state/artifacts.js +13 -124
  688. package/packages/core/dist/run-state/artifacts.js.map +1 -1
  689. package/packages/core/dist/run-state/inspect-run.d.ts +2 -0
  690. package/packages/core/dist/run-state/inspect-run.js +5 -7
  691. package/packages/core/dist/run-state/inspect-run.js.map +1 -1
  692. package/packages/core/dist/run-state/model.d.ts +28 -28
  693. package/packages/core/dist/run-state/run-index.d.ts +2 -0
  694. package/packages/core/dist/run-state/run-index.js +3 -1
  695. package/packages/core/dist/run-state/run-index.js.map +1 -1
  696. package/packages/core/dist/run-state/run-state.js +26 -36
  697. package/packages/core/dist/run-state/run-state.js.map +1 -1
  698. package/packages/core/dist/run-state/task-evidence.d.ts +0 -4
  699. package/packages/core/dist/run-state/task-evidence.js +5 -51
  700. package/packages/core/dist/run-state/task-evidence.js.map +1 -1
  701. package/packages/core/dist/run-state.d.ts +0 -1
  702. package/packages/core/dist/run-state.js +0 -1
  703. package/packages/core/dist/run-state.js.map +1 -1
  704. package/packages/core/dist/runtime-analysis/build.js +1 -1
  705. package/packages/core/dist/runtime-analysis/build.js.map +1 -1
  706. package/packages/core/dist/runtime-analysis/findings.js +5 -5
  707. package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
  708. package/packages/core/dist/runtime-paths.js +1 -1
  709. package/packages/core/dist/runtime-paths.js.map +1 -1
  710. package/packages/core/dist/runtime-projection-p0.d.ts +2 -2
  711. package/packages/core/dist/runtime-projection-p0.js +11 -0
  712. package/packages/core/dist/runtime-projection-p0.js.map +1 -1
  713. package/packages/core/dist/sdd-docs/artifact-depth.d.ts +14 -0
  714. package/packages/core/dist/sdd-docs/artifact-depth.js +179 -0
  715. package/packages/core/dist/sdd-docs/artifact-depth.js.map +1 -0
  716. package/packages/core/dist/sdd-docs/document-hashes.d.ts +0 -2
  717. package/packages/core/dist/sdd-docs/document-hashes.js +10 -97
  718. package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -1
  719. package/packages/core/dist/sdd-docs/run-binding.d.ts +1 -1
  720. package/packages/core/dist/sdd-docs/run-binding.js +6 -8
  721. package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
  722. package/packages/core/dist/sdd-docs/task-parser.d.ts +5 -2
  723. package/packages/core/dist/sdd-docs/task-parser.js +85 -68
  724. package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
  725. package/packages/core/dist/sdd-docs/task-rendering.js +2 -2
  726. package/packages/core/dist/sdd-docs/task-rendering.js.map +1 -1
  727. package/packages/core/dist/spec-entry.js +40 -0
  728. package/packages/core/dist/spec-entry.js.map +1 -0
  729. package/packages/core/dist/spec-manager-contracts.d.ts +12 -0
  730. package/packages/core/dist/spec-manager-contracts.js +2 -0
  731. package/packages/core/dist/spec-manager-contracts.js.map +1 -0
  732. package/packages/core/dist/stage-artifacts.d.ts +2 -2
  733. package/packages/core/dist/stage-artifacts.js +19 -26
  734. package/packages/core/dist/stage-artifacts.js.map +1 -1
  735. package/packages/core/dist/stage-collaboration-contracts.d.ts +1 -1
  736. package/packages/core/dist/stage-collaboration-contracts.js +3 -6
  737. package/packages/core/dist/stage-collaboration-contracts.js.map +1 -1
  738. package/packages/core/dist/stage-collaboration.d.ts +111 -263
  739. package/packages/core/dist/stage-collaboration.js +1272 -1124
  740. package/packages/core/dist/stage-collaboration.js.map +1 -1
  741. package/packages/core/dist/stage-runtime/runtime.js +5 -5
  742. package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
  743. package/packages/core/dist/status/project-status.d.ts +1 -44
  744. package/packages/core/dist/status/project-status.js +47 -170
  745. package/packages/core/dist/status/project-status.js.map +1 -1
  746. package/packages/core/dist/storage/runtime-store.js +73 -73
  747. package/packages/core/dist/subagents/contracts.d.ts +1 -1
  748. package/packages/core/dist/subagents/runtime.js +7 -7
  749. package/packages/core/dist/subagents/runtime.js.map +1 -1
  750. package/packages/core/dist/sync-back/apply.d.ts +1 -0
  751. package/packages/core/dist/sync-back/apply.js +2 -0
  752. package/packages/core/dist/sync-back/apply.js.map +1 -0
  753. package/packages/core/dist/sync-back/inspect.d.ts +1 -0
  754. package/packages/core/dist/sync-back/inspect.js +2 -0
  755. package/packages/core/dist/sync-back/inspect.js.map +1 -0
  756. package/packages/core/dist/sync-back.d.ts +1 -0
  757. package/packages/core/dist/sync-back.js +2 -0
  758. package/packages/core/dist/sync-back.js.map +1 -0
  759. package/packages/core/dist/task-execution-contract.d.ts +167 -0
  760. package/packages/core/dist/task-execution-contract.js +377 -0
  761. package/packages/core/dist/task-execution-contract.js.map +1 -0
  762. package/packages/core/dist/test-support/fixtures.js +329 -314
  763. package/packages/core/dist/test-support/fixtures.js.map +1 -1
  764. package/packages/core/dist/test-support/run-state.d.ts +1 -0
  765. package/packages/core/dist/test-support/run-state.js +53 -7
  766. package/packages/core/dist/test-support/run-state.js.map +1 -1
  767. package/packages/core/dist/truth-reconciliation.js +9 -12
  768. package/packages/core/dist/truth-reconciliation.js.map +1 -1
  769. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  770. package/packages/core/dist/verification/goal-verify.d.ts +0 -48
  771. package/packages/core/dist/verification/goal-verify.js +1 -520
  772. package/packages/core/dist/verification/goal-verify.js.map +1 -1
  773. package/packages/core/dist/verification/rendering.d.ts +5 -5
  774. package/packages/core/dist/verification/rendering.js +14 -14
  775. package/packages/core/dist/verification/rendering.js.map +1 -1
  776. package/packages/core/dist/verification/single-task-loop.d.ts +1 -0
  777. package/packages/core/dist/verification/single-task-loop.js +111 -159
  778. package/packages/core/dist/verification/single-task-loop.js.map +1 -1
  779. package/packages/core/dist/verification/task-evidence-judgment.d.ts +49 -0
  780. package/packages/core/dist/verification/task-evidence-judgment.js +521 -0
  781. package/packages/core/dist/verification/task-evidence-judgment.js.map +1 -0
  782. package/packages/core/dist/verification/test-runtime.js +21 -21
  783. package/packages/core/dist/verification/test-runtime.js.map +1 -1
  784. package/packages/core/dist/verification/validation-wave.d.ts +0 -18
  785. package/packages/core/dist/verification/validation-wave.js +5 -27
  786. package/packages/core/dist/verification/validation-wave.js.map +1 -1
  787. package/packages/core/dist/verification/verify-contract.js +45 -45
  788. package/packages/core/dist/verification/verify-contract.js.map +1 -1
  789. package/packages/core/dist/verification.d.ts +3 -3
  790. package/packages/core/dist/verification.js +2 -2
  791. package/packages/core/dist/verification.js.map +1 -1
  792. package/packages/core/dist/work-units/contracts.d.ts +1 -1
  793. package/packages/core/dist/workflow-gate/evidence-packet.js +9 -227
  794. package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -1
  795. package/packages/core/dist/workflow-gate/hard-checks.js +9 -50
  796. package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -1
  797. package/packages/core/dist/workflow-gate/policy.js +4 -42
  798. package/packages/core/dist/workflow-gate/policy.js.map +1 -1
  799. package/packages/core/dist/workflow-gate/types.d.ts +2 -3
  800. package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -0
  801. package/packages/core/dist/workflow-state/affected-file-conflicts.js +2 -1
  802. package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
  803. package/packages/core/dist/workflow-state/dependencies.js +1 -1
  804. package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +1 -0
  805. package/packages/core/dist/workflow-state/latest-eligible-run.js +23 -63
  806. package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -1
  807. package/packages/core/dist/workflow-state/resolve.d.ts +2 -2
  808. package/packages/core/dist/workflow-state/resolve.js +43 -65
  809. package/packages/core/dist/workflow-state/resolve.js.map +1 -1
  810. package/packages/core/package.json +5 -2
  811. package/tsconfig.build.json +6 -7
  812. package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +0 -269
  813. package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +0 -492
  814. package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +0 -383
  815. package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +0 -188
  816. package/packages/cli/dist/commands/lifecycle.d.ts +0 -6
  817. package/packages/cli/dist/commands/lifecycle.js +0 -125
  818. package/packages/cli/dist/commands/lifecycle.js.map +0 -1
  819. package/packages/cli/dist/commands/test.d.ts +0 -6
  820. package/packages/cli/dist/commands/test.js +0 -373
  821. package/packages/cli/dist/commands/test.js.map +0 -1
  822. package/packages/cli/dist/commands/verifies.js +0 -87
  823. package/packages/cli/dist/commands/verifies.js.map +0 -1
  824. package/packages/cli/dist/commands/verify.d.ts +0 -6
  825. package/packages/cli/dist/commands/verify.js +0 -330
  826. 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
+ }