@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.160

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 (670) hide show
  1. package/CHANGELOG.md +959 -0
  2. package/README.md +139 -10
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +417 -0
  6. package/dist/cli/command-metadata.js +303 -0
  7. package/dist/cli/command-option-descriptions.js +192 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/delivery.js +108 -0
  11. package/dist/cli/diagnostic-invocation.js +62 -0
  12. package/dist/cli/diagnostic.js +131 -0
  13. package/dist/cli/error-output.js +62 -0
  14. package/dist/cli/executor-onboarding.js +76 -0
  15. package/dist/cli/flows.js +29 -0
  16. package/dist/cli/harnesses.js +84 -0
  17. package/dist/cli/help.js +521 -19
  18. package/dist/cli/hook-ingress.js +89 -0
  19. package/dist/cli/input-preparation.js +193 -0
  20. package/dist/cli/interactive-invocation.js +33 -0
  21. package/dist/cli/interactive.js +114 -0
  22. package/dist/cli/memory-fix-invocation.js +52 -0
  23. package/dist/cli/memory-fix.js +100 -0
  24. package/dist/cli/memory-flow-admission.js +54 -0
  25. package/dist/cli/memory-init-invocation.js +85 -0
  26. package/dist/cli/memory-init.js +250 -0
  27. package/dist/cli/memory-storage-admission.js +54 -0
  28. package/dist/cli/memory-storage-qualification.js +105 -0
  29. package/dist/cli/memory-storage-transition.js +123 -0
  30. package/dist/cli/memory-upgrade.js +189 -0
  31. package/dist/cli/models.js +39 -0
  32. package/dist/cli/native-hook-ingress.js +94 -0
  33. package/dist/cli/native-hook-preparation.js +82 -0
  34. package/dist/cli/operator-profiles.js +61 -0
  35. package/dist/cli/output-options.js +35 -0
  36. package/dist/cli/response-file.js +72 -0
  37. package/dist/cli/run-cli.js +2020 -555
  38. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  39. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  40. package/dist/cli/runtime-process.js +153 -0
  41. package/dist/cli/setup.js +150 -0
  42. package/dist/cli.js +103 -2
  43. package/dist/domain/delivery-flows.js +8 -0
  44. package/dist/domain/diagnostic-aspects.js +103 -0
  45. package/dist/domain/diagnostic-flows.js +8 -0
  46. package/dist/domain/distill-flow.js +20 -0
  47. package/dist/domain/interactive-flow.js +1 -0
  48. package/dist/domain/memory-fix-flow.js +2 -0
  49. package/dist/domain/review-fix-flow.js +2 -0
  50. package/dist/domain/source-snapshot-flows.js +4 -0
  51. package/dist/domain/stage-catalog.js +1 -1
  52. package/dist/domain/stage-handler-identities.js +28 -0
  53. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  54. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  55. package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
  56. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  57. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  58. package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
  59. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  60. package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
  61. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  62. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  63. package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
  64. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  65. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  66. package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
  67. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  68. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  69. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
  70. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  71. package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
  72. package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
  73. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  74. package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
  75. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
  76. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
  77. package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
  78. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  79. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
  80. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  81. package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
  82. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
  83. package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
  84. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  85. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
  86. package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
  87. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
  88. package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
  89. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  90. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
  91. package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
  92. package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
  93. package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
  94. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  95. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  96. package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
  97. package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
  98. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  99. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  100. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  101. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  102. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  103. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  104. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
  105. package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
  106. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  107. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  108. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  109. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  110. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  111. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  112. package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
  113. package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
  114. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  115. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  116. package/dist/harness-runtime/lib/native-streams.mjs +38 -0
  117. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  118. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  119. package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
  120. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  121. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  122. package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
  123. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  124. package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
  125. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  126. package/dist/harness-runtime/lib/process-json.mjs +200 -0
  127. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  128. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  129. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  130. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  131. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  132. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  133. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  134. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  135. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  136. package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
  137. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  138. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  147. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  148. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  149. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  150. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  151. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  152. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  154. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  155. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  156. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  157. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  158. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  159. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  160. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  161. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  162. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  163. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  164. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  165. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  167. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  168. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  169. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  171. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  172. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  173. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  175. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  176. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  177. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  178. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  179. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  180. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  181. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  182. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  183. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  184. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  186. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  187. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  188. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  189. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  190. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  191. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  192. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  193. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  194. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  195. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  196. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  197. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  199. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  200. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  201. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  202. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  203. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  204. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  205. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  206. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  207. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  208. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  209. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  210. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  211. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  212. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  213. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  214. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  215. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  216. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  217. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  218. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  219. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  220. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  221. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  222. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  223. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  224. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  225. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  226. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  227. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  228. package/dist/protocol/local-files.js +18 -9
  229. package/dist/runtime/context.js +8 -4
  230. package/dist/schemas/agent-profile.schema.json +1 -1
  231. package/dist/schemas/code-review-decision.schema.json +9 -0
  232. package/dist/schemas/code-review-result.schema.json +1 -1
  233. package/dist/schemas/code-verification.schema.json +7 -5
  234. package/dist/schemas/code-work-batch.schema.json +2 -2
  235. package/dist/schemas/code-work-result.schema.json +2 -1
  236. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  237. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  238. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  239. package/dist/schemas/discovery-result.schema.json +184 -0
  240. package/dist/schemas/distill-stage-result.schema.json +667 -0
  241. package/dist/schemas/distill-work-result.schema.json +428 -0
  242. package/dist/schemas/execution-bundle.schema.json +215 -0
  243. package/dist/schemas/execution-settings.schema.json +162 -0
  244. package/dist/schemas/flow-descriptor.schema.json +54 -0
  245. package/dist/schemas/flow-run.schema.json +29 -0
  246. package/dist/schemas/harness-catalog.schema.json +80 -0
  247. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  248. package/dist/schemas/interactive-work-result.schema.json +63 -0
  249. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  250. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  251. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  252. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  253. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  254. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  255. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  256. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  257. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  258. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  259. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  260. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  261. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  262. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  263. package/dist/schemas/model-catalog.schema.json +153 -0
  264. package/dist/schemas/plan-review-decision.schema.json +12 -3
  265. package/dist/schemas/plan-review-result.schema.json +2 -2
  266. package/dist/schemas/profile-settings.schema.json +122 -0
  267. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  268. package/dist/schemas/release-impact.schema.json +3 -3
  269. package/dist/schemas/release-manifest.schema.json +291 -0
  270. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  271. package/dist/schemas/run-control-receipt.schema.json +119 -0
  272. package/dist/schemas/run-control-request.schema.json +36 -0
  273. package/dist/schemas/run-engine-binding.schema.json +1 -1
  274. package/dist/schemas/stage-start-response.schema.json +4 -0
  275. package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
  276. package/dist/services/accepted-intent.js +139 -0
  277. package/dist/services/assigned-lifecycle.js +382 -0
  278. package/dist/services/branch-context.js +3 -3
  279. package/dist/services/canon.js +54 -15
  280. package/dist/services/check-environment.js +21 -0
  281. package/dist/services/check-runner-evidence.js +344 -0
  282. package/dist/services/check-runner-proof.js +373 -0
  283. package/dist/services/cleanup.js +133 -40
  284. package/dist/services/cli-operation-classifier.js +32 -64
  285. package/dist/services/cli-upgrade-allowlist.js +27 -0
  286. package/dist/services/code-checks.js +1426 -121
  287. package/dist/services/codex-capacity.js +367 -0
  288. package/dist/services/codex-hook-delivery.js +41 -0
  289. package/dist/services/command-context.js +80 -0
  290. package/dist/services/compatibility-preflight.js +16 -3
  291. package/dist/services/config.js +11 -8
  292. package/dist/services/continuation-outcome.js +25 -0
  293. package/dist/services/controller-child-capacity.js +71 -0
  294. package/dist/services/controller-child-observer.js +121 -0
  295. package/dist/services/controller-child-repair.js +117 -0
  296. package/dist/services/controller-execution.js +97 -0
  297. package/dist/services/controller-fanout.js +277 -0
  298. package/dist/services/controller-memory-task.js +45 -0
  299. package/dist/services/controller-memory-worker.js +51 -0
  300. package/dist/services/controller-result-authority.js +127 -0
  301. package/dist/services/controller-work-failure.js +198 -0
  302. package/dist/services/dashboard.js +145 -58
  303. package/dist/services/delivery-access.js +123 -0
  304. package/dist/services/delivery-approval.js +68 -0
  305. package/dist/services/delivery-artifact.js +86 -0
  306. package/dist/services/delivery-checks.js +178 -0
  307. package/dist/services/delivery-execution.js +181 -0
  308. package/dist/services/delivery-intents.js +212 -0
  309. package/dist/services/delivery-operation.js +81 -0
  310. package/dist/services/delivery-policy.js +91 -0
  311. package/dist/services/delivery-procedures.js +43 -0
  312. package/dist/services/delivery-request.js +99 -0
  313. package/dist/services/delivery-result.js +144 -0
  314. package/dist/services/delivery-stage-commands.js +26 -0
  315. package/dist/services/delivery-stage-entry.js +56 -0
  316. package/dist/services/delivery-stage-handlers.js +135 -0
  317. package/dist/services/delivery-stage-results.js +94 -0
  318. package/dist/services/descriptor-coordinator-session.js +37 -0
  319. package/dist/services/descriptor-root-work.js +53 -0
  320. package/dist/services/descriptor-run-settlement.js +107 -0
  321. package/dist/services/descriptor-run-start.js +495 -0
  322. package/dist/services/descriptor-stage-coordinator.js +318 -0
  323. package/dist/services/descriptor-stage-settlement.js +268 -0
  324. package/dist/services/descriptor-stage-start.js +175 -0
  325. package/dist/services/diagnostic-evidence.js +73 -0
  326. package/dist/services/diagnostic-repair-intake.js +123 -0
  327. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  328. package/dist/services/diagnostic-report-publication.js +72 -0
  329. package/dist/services/diagnostic-report.js +97 -0
  330. package/dist/services/diagnostic-run-bundle.js +113 -0
  331. package/dist/services/diagnostic-snapshot.js +159 -0
  332. package/dist/services/diagnostic-stage-handlers.js +109 -0
  333. package/dist/services/diagnostic-stage-results.js +158 -0
  334. package/dist/services/diagnostic-start.js +134 -0
  335. package/dist/services/diagnostic-work-batch.js +34 -0
  336. package/dist/services/diagnostic-work-collection.js +97 -0
  337. package/dist/services/diagnostic-work-registration.js +101 -0
  338. package/dist/services/diagnostic-work-results.js +93 -0
  339. package/dist/services/discovery/agy-discovery.js +2 -0
  340. package/dist/services/discovery/codex-discovery.js +2 -0
  341. package/dist/services/discovery/droid-discovery.js +2 -0
  342. package/dist/services/discovery/grok-discovery.js +2 -0
  343. package/dist/services/discovery/opencode-discovery.js +2 -0
  344. package/dist/services/discovery/static.js +31 -0
  345. package/dist/services/discovery/zcode-discovery.js +2 -0
  346. package/dist/services/distill-document-publication.js +280 -0
  347. package/dist/services/distill-promotion.js +197 -0
  348. package/dist/services/distill-publication-custody.js +162 -0
  349. package/dist/services/distill-stage-handlers.js +249 -0
  350. package/dist/services/distill-stage-results.js +105 -0
  351. package/dist/services/distill-user-inputs.js +4 -0
  352. package/dist/services/distill-work-batch.js +42 -0
  353. package/dist/services/distill-work-collection.js +74 -0
  354. package/dist/services/distill-work-registration.js +93 -0
  355. package/dist/services/distill-work-results.js +98 -0
  356. package/dist/services/engines.js +354 -163
  357. package/dist/services/eval-snapshots.js +1326 -150
  358. package/dist/services/execution-bundles.js +275 -0
  359. package/dist/services/execution-policy.js +185 -0
  360. package/dist/services/executor-onboarding.js +56 -0
  361. package/dist/services/external-work-launch.js +316 -0
  362. package/dist/services/flow-catalog.js +54 -0
  363. package/dist/services/flow-descriptors.js +82 -0
  364. package/dist/services/frozen-engine.js +404 -0
  365. package/dist/services/generic-execution.js +165 -0
  366. package/dist/services/harness-adapter.js +342 -35
  367. package/dist/services/harness-config.js +2 -0
  368. package/dist/services/harness-discovery.js +29 -0
  369. package/dist/services/harness-doctor.js +75 -0
  370. package/dist/services/harness-probe-effects.js +51 -0
  371. package/dist/services/harness-version-probe.js +100 -0
  372. package/dist/services/hook-memory-routing.js +188 -0
  373. package/dist/services/hook-recovery-guard.js +14 -0
  374. package/dist/services/hook-session-observation.js +24 -0
  375. package/dist/services/hooks.js +813 -327
  376. package/dist/services/hybrid-contracts.js +479 -0
  377. package/dist/services/hybrid-digest.js +14 -0
  378. package/dist/services/ids.js +29 -1
  379. package/dist/services/installation-lock.js +95 -0
  380. package/dist/services/installed-flow-source.js +261 -0
  381. package/dist/services/interactive-attached-session.js +215 -0
  382. package/dist/services/interactive-baseline-custody.js +54 -0
  383. package/dist/services/interactive-before-versions.js +148 -0
  384. package/dist/services/interactive-bundle-data.js +85 -0
  385. package/dist/services/interactive-checks.js +179 -0
  386. package/dist/services/interactive-execution.js +33 -0
  387. package/dist/services/interactive-finish-integration.js +236 -0
  388. package/dist/services/interactive-git-baseline.js +230 -0
  389. package/dist/services/interactive-git-dispatch.js +278 -0
  390. package/dist/services/interactive-input-delivery.js +82 -0
  391. package/dist/services/interactive-mutation-scope.js +231 -0
  392. package/dist/services/interactive-request.js +287 -0
  393. package/dist/services/interactive-run-bundle.js +109 -0
  394. package/dist/services/interactive-stage-entry.js +41 -0
  395. package/dist/services/interactive-stage-handlers.js +289 -0
  396. package/dist/services/interactive-stage-observation.js +136 -0
  397. package/dist/services/interactive-stage-results.js +111 -0
  398. package/dist/services/interactive-user-inputs.js +142 -0
  399. package/dist/services/interactive-work-batch.js +43 -0
  400. package/dist/services/interactive-work-collection.js +119 -0
  401. package/dist/services/interactive-work-completion.js +180 -0
  402. package/dist/services/interactive-work-context.js +249 -0
  403. package/dist/services/interactive-work-history.js +112 -0
  404. package/dist/services/interactive-work-registration.js +124 -0
  405. package/dist/services/interactive-work-results.js +115 -0
  406. package/dist/services/interactive-work-start.js +155 -0
  407. package/dist/services/lanes.js +78 -65
  408. package/dist/services/lifecycle-command.js +1 -288
  409. package/dist/services/lifecycle-contract.js +1 -0
  410. package/dist/services/lifecycle-invocations.js +2549 -0
  411. package/dist/services/managed-daemon-binding.js +129 -0
  412. package/dist/services/managed-lease-monitor.js +133 -0
  413. package/dist/services/managed-processes.js +449 -69
  414. package/dist/services/memory-accepted-composition.js +78 -0
  415. package/dist/services/memory-baseline-preservation.js +65 -0
  416. package/dist/services/memory-completed-correction-wave.js +328 -0
  417. package/dist/services/memory-compose-handler.js +380 -0
  418. package/dist/services/memory-composition-acceptance.js +375 -0
  419. package/dist/services/memory-composition-graph.js +165 -0
  420. package/dist/services/memory-composition-registration.js +161 -0
  421. package/dist/services/memory-composition-work-custody.js +156 -0
  422. package/dist/services/memory-composition-work-results.js +108 -0
  423. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  424. package/dist/services/memory-corrected-review-batch.js +37 -0
  425. package/dist/services/memory-corrected-review-findings.js +108 -0
  426. package/dist/services/memory-corrected-review-inputs.js +215 -0
  427. package/dist/services/memory-corrected-review-projection.js +78 -0
  428. package/dist/services/memory-corrected-review-receipt.js +178 -0
  429. package/dist/services/memory-corrected-review-registration.js +192 -0
  430. package/dist/services/memory-correction-check-receipt.js +235 -0
  431. package/dist/services/memory-correction-input-owner.js +6 -0
  432. package/dist/services/memory-custody-snapshot.js +36 -0
  433. package/dist/services/memory-discover-handler.js +104 -0
  434. package/dist/services/memory-finish-evidence.js +457 -0
  435. package/dist/services/memory-finish-handler.js +229 -0
  436. package/dist/services/memory-finish-integration.js +491 -0
  437. package/dist/services/memory-finish-readiness.js +169 -0
  438. package/dist/services/memory-fix-finish-integration.js +129 -0
  439. package/dist/services/memory-fix-git-dispatch.js +139 -0
  440. package/dist/services/memory-fix-intake-custody.js +11 -0
  441. package/dist/services/memory-fix-intake-settlement.js +127 -0
  442. package/dist/services/memory-fix-publication.js +196 -0
  443. package/dist/services/memory-fix-repair-admission.js +116 -0
  444. package/dist/services/memory-fix-request.js +141 -0
  445. package/dist/services/memory-fix-run-bundle.js +83 -0
  446. package/dist/services/memory-fix-scope.js +78 -0
  447. package/dist/services/memory-fix-sources.js +85 -0
  448. package/dist/services/memory-fix-stage-entry.js +24 -0
  449. package/dist/services/memory-fix-stage-handlers.js +282 -0
  450. package/dist/services/memory-fix-stage-results.js +106 -0
  451. package/dist/services/memory-fix-workspace.js +127 -0
  452. package/dist/services/memory-flow-user-inputs.js +88 -0
  453. package/dist/services/memory-git-integration.js +220 -0
  454. package/dist/services/memory-historical-corrected-review.js +212 -0
  455. package/dist/services/memory-historical-original-review.js +143 -0
  456. package/dist/services/memory-historical-repair-registration.js +105 -0
  457. package/dist/services/memory-historical-second-review.js +147 -0
  458. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  459. package/dist/services/memory-init-contexts.js +181 -0
  460. package/dist/services/memory-init-controller-start.js +119 -0
  461. package/dist/services/memory-init-engine-release.js +223 -0
  462. package/dist/services/memory-init-git-policy.js +165 -0
  463. package/dist/services/memory-init-lint-capability.js +203 -0
  464. package/dist/services/memory-init-native-capacity.js +309 -0
  465. package/dist/services/memory-init-preflight.js +334 -0
  466. package/dist/services/memory-init-project-bootstrap.js +599 -0
  467. package/dist/services/memory-init-request-candidate.js +213 -0
  468. package/dist/services/memory-init-request-input.js +82 -0
  469. package/dist/services/memory-init-resource-storage.js +125 -0
  470. package/dist/services/memory-init-run-allocation.js +81 -0
  471. package/dist/services/memory-init-run-bundle.js +135 -0
  472. package/dist/services/memory-init-start.js +181 -0
  473. package/dist/services/memory-init-startup-custody.js +80 -0
  474. package/dist/services/memory-init-startup-input.js +240 -0
  475. package/dist/services/memory-init-storage.js +91 -0
  476. package/dist/services/memory-init-stores.js +359 -0
  477. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  478. package/dist/services/memory-layer-publication.js +611 -0
  479. package/dist/services/memory-layer.js +225 -0
  480. package/dist/services/memory-lint-config.js +194 -0
  481. package/dist/services/memory-native-review-copy.js +127 -0
  482. package/dist/services/memory-normal-flow-entry.js +87 -0
  483. package/dist/services/memory-ordinary-defaults.js +129 -0
  484. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  485. package/dist/services/memory-preflight-handler.js +274 -0
  486. package/dist/services/memory-repair-current-view.js +299 -0
  487. package/dist/services/memory-repair-document-delta.js +489 -0
  488. package/dist/services/memory-repair-history.js +496 -0
  489. package/dist/services/memory-repair-physical-binding.js +194 -0
  490. package/dist/services/memory-repair-publication-files.js +152 -0
  491. package/dist/services/memory-repair-publication-intent.js +27 -0
  492. package/dist/services/memory-repair-publication-plan.js +162 -0
  493. package/dist/services/memory-repair-publication-source.js +225 -0
  494. package/dist/services/memory-repair-publication.js +795 -0
  495. package/dist/services/memory-repair-registration.js +240 -0
  496. package/dist/services/memory-repair-work-baseline.js +415 -0
  497. package/dist/services/memory-repair-work-completion.js +117 -0
  498. package/dist/services/memory-repair-work-custody.js +73 -0
  499. package/dist/services/memory-repair-work-results.js +108 -0
  500. package/dist/services/memory-repair-workspace.js +261 -0
  501. package/dist/services/memory-repair-writer-copy.js +736 -0
  502. package/dist/services/memory-result-binding.js +239 -0
  503. package/dist/services/memory-retained-review-authority.js +138 -0
  504. package/dist/services/memory-retained-review-results.js +70 -0
  505. package/dist/services/memory-retained-second-baseline.js +102 -0
  506. package/dist/services/memory-retained-second-repairs.js +162 -0
  507. package/dist/services/memory-review-acceptance.js +214 -0
  508. package/dist/services/memory-review-correction-plan.js +206 -0
  509. package/dist/services/memory-review-handler.js +585 -0
  510. package/dist/services/memory-review-inputs.js +246 -0
  511. package/dist/services/memory-review-limits.js +10 -0
  512. package/dist/services/memory-review-receipt.js +180 -0
  513. package/dist/services/memory-review-registration.js +117 -0
  514. package/dist/services/memory-review-work-custody.js +126 -0
  515. package/dist/services/memory-review-work-input.js +91 -0
  516. package/dist/services/memory-review-work-results.js +95 -0
  517. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  518. package/dist/services/memory-second-correction-plan.js +77 -0
  519. package/dist/services/memory-second-repair-baseline.js +91 -0
  520. package/dist/services/memory-second-repair-launch.js +614 -0
  521. package/dist/services/memory-second-repair-registration.js +88 -0
  522. package/dist/services/memory-second-repair-source.js +187 -0
  523. package/dist/services/memory-second-review-acceptance.js +115 -0
  524. package/dist/services/memory-source-observations.js +117 -0
  525. package/dist/services/memory-stage-dispatch.js +65 -0
  526. package/dist/services/memory-stage-results.js +290 -0
  527. package/dist/services/memory-tool-probe.js +25 -0
  528. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  529. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  530. package/dist/services/memory-upgrade-evidence.js +100 -0
  531. package/dist/services/memory-upgrade-inventory.js +442 -0
  532. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  533. package/dist/services/memory-upgrade-publication.js +413 -0
  534. package/dist/services/memory-upgrade-selection.js +430 -0
  535. package/dist/services/memory-upgrade-start.js +60 -0
  536. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  537. package/dist/services/memory-verification-acceptance.js +156 -0
  538. package/dist/services/memory-verification-checks.js +317 -0
  539. package/dist/services/memory-verification-receipt.js +160 -0
  540. package/dist/services/memory-verify-handler.js +137 -0
  541. package/dist/services/memory-work-result-json.js +12 -0
  542. package/dist/services/memory-work-result-preparation.js +64 -0
  543. package/dist/services/merge-queue.js +228 -149
  544. package/dist/services/merge-server.js +163 -35
  545. package/dist/services/migrations.js +164 -114
  546. package/dist/services/model-catalog.js +109 -0
  547. package/dist/services/model-discovery.js +46 -0
  548. package/dist/services/native-daemon-history.js +50 -0
  549. package/dist/services/native-session-control.js +39 -0
  550. package/dist/services/operator-profiles.js +236 -0
  551. package/dist/services/physical-startup.js +45 -0
  552. package/dist/services/plan-runtime.js +26 -11
  553. package/dist/services/plans.js +28 -25
  554. package/dist/services/project-flow-source.js +195 -0
  555. package/dist/services/project-git-integration.js +92 -0
  556. package/dist/services/project-registry.js +97 -0
  557. package/dist/services/project-summary.js +1 -1
  558. package/dist/services/projects.js +17 -87
  559. package/dist/services/prompt-delivery.js +71 -0
  560. package/dist/services/prompts.js +28 -17
  561. package/dist/services/protocols.js +163 -87
  562. package/dist/services/provider-capacity.js +463 -0
  563. package/dist/services/recovery-observation-budget.js +121 -0
  564. package/dist/services/recovery-snapshot-database.js +588 -0
  565. package/dist/services/release-archive-producer.js +52 -0
  566. package/dist/services/release-archive.js +143 -0
  567. package/dist/services/release-cache.js +150 -0
  568. package/dist/services/release-compatibility.js +18 -0
  569. package/dist/services/release-download.js +67 -0
  570. package/dist/services/release-envelope.js +104 -0
  571. package/dist/services/release-manifest.js +45 -0
  572. package/dist/services/release-payload.js +114 -0
  573. package/dist/services/release-producer.js +63 -0
  574. package/dist/services/release-provenance.js +160 -0
  575. package/dist/services/release-registry.js +66 -0
  576. package/dist/services/release-source.js +194 -0
  577. package/dist/services/repair-intents.js +29 -0
  578. package/dist/services/repair-receipt.js +13 -0
  579. package/dist/services/retained-native-hook.js +274 -0
  580. package/dist/services/review-copy.js +165 -0
  581. package/dist/services/review-deferral.js +23 -0
  582. package/dist/services/review-fix-handoff.js +82 -0
  583. package/dist/services/review-fix-intake-custody.js +17 -0
  584. package/dist/services/review-fix-stage-entry.js +43 -0
  585. package/dist/services/review-fix-stage-handlers.js +176 -0
  586. package/dist/services/review-fix-stage-results.js +86 -0
  587. package/dist/services/run-control-receipt.js +56 -0
  588. package/dist/services/run-control-worker.js +469 -0
  589. package/dist/services/run-control.js +1109 -0
  590. package/dist/services/run-controller-adapter.js +434 -0
  591. package/dist/services/run-controller-capture.js +186 -0
  592. package/dist/services/run-controller-process.js +226 -0
  593. package/dist/services/run-controller-recovery.js +335 -0
  594. package/dist/services/run-controller-state.js +106 -0
  595. package/dist/services/run-controller.js +1717 -0
  596. package/dist/services/run-engine-bindings.js +1 -1
  597. package/dist/services/run-fork.js +147 -0
  598. package/dist/services/run-health.js +149 -0
  599. package/dist/services/run-observations.js +152 -0
  600. package/dist/services/run-projection.js +22 -64
  601. package/dist/services/run-recovery-guard.js +107 -0
  602. package/dist/services/run-recovery-runtime.js +112 -0
  603. package/dist/services/run-recovery.js +485 -0
  604. package/dist/services/run-start-custody.js +189 -0
  605. package/dist/services/run-timeline.js +59 -0
  606. package/dist/services/runs.js +996 -271
  607. package/dist/services/runtime-budget.js +446 -0
  608. package/dist/services/runtime-command.js +43 -0
  609. package/dist/services/runtime-scope-capture.js +52 -0
  610. package/dist/services/runtime-scope-control.js +468 -0
  611. package/dist/services/runtime-scope-resume.js +605 -0
  612. package/dist/services/runtime-scope-stop.js +211 -0
  613. package/dist/services/runtime-scope-worker.js +314 -0
  614. package/dist/services/runtime-service.js +19 -14
  615. package/dist/services/schema-validation.js +10 -8
  616. package/dist/services/scope-writer-cleanup.js +27 -0
  617. package/dist/services/sessions.js +37 -91
  618. package/dist/services/setup.js +427 -0
  619. package/dist/services/stage-blocker.js +17 -7
  620. package/dist/services/stage-context.js +240 -18
  621. package/dist/services/stage-handlers.js +141 -0
  622. package/dist/services/stage-lifecycle.js +156 -119
  623. package/dist/services/stage-pause.js +222 -94
  624. package/dist/services/stage-report-renderer.js +230 -6
  625. package/dist/services/stage-work-graph.js +52 -0
  626. package/dist/services/technical-notes.js +31 -0
  627. package/dist/services/tooling.js +111 -44
  628. package/dist/services/usage.js +49 -101
  629. package/dist/services/verification-matrix.js +393 -0
  630. package/dist/services/version-status.js +1 -1
  631. package/dist/services/vnext-code-review.js +302 -91
  632. package/dist/services/vnext-code.js +325 -251
  633. package/dist/services/vnext-contracts.js +10 -1
  634. package/dist/services/vnext-execution-profile.js +80 -5
  635. package/dist/services/vnext-fanout.js +399 -20
  636. package/dist/services/vnext-merge.js +336 -114
  637. package/dist/services/vnext-plan-review.js +288 -133
  638. package/dist/services/vnext-plan.js +230 -106
  639. package/dist/services/vnext-protocolize.js +534 -96
  640. package/dist/services/vnext-specify.js +131 -84
  641. package/dist/services/vnext-workspace-policy.js +7 -2
  642. package/dist/services/work-registry.js +1950 -215
  643. package/dist/services/work-retry-context.js +55 -0
  644. package/dist/services/workspace-bootstrap.js +53 -15
  645. package/dist/services/workspace-files.js +85 -0
  646. package/dist/services/worktrees.js +108 -48
  647. package/dist/shared/entity-references.js +1 -0
  648. package/dist/shared/errors.js +27 -0
  649. package/dist/storage/agent-output.js +26 -0
  650. package/dist/storage/data-preservation.js +65 -0
  651. package/dist/storage/database.js +931 -40
  652. package/dist/storage/foreign-key-transaction.js +39 -0
  653. package/dist/storage/immutable-input.js +45 -0
  654. package/dist/storage/import-guard.js +159 -0
  655. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  656. package/dist/storage/native-cleanup.js +52 -0
  657. package/dist/storage/paired-writer-migration.js +424 -0
  658. package/dist/storage/paths.js +17 -4
  659. package/dist/storage/projection-lock.js +73 -0
  660. package/dist/storage/work-references.js +23 -0
  661. package/dist/storage/writer-contract.js +96 -0
  662. package/dist/storage/writer-migration.js +346 -0
  663. package/dist/storage/writer-restore.js +217 -0
  664. package/dist/verification-components.json +384 -0
  665. package/package.json +52 -13
  666. package/tools/create-canon-release.mjs +33 -0
  667. package/tools/probe-codex-history.mjs +37 -0
  668. package/tools/produce-hybrid-release.mjs +41 -0
  669. package/tools/repair-paused-run-status.mjs +84 -0
  670. package/tools/verification-jobs.mjs +342 -0
@@ -1,22 +1,32 @@
1
+ import { managedLifecycleCommand } from "./lifecycle-invocations.js";
1
2
  import crypto from "node:crypto";
3
+ import { withStageSettlement, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, readFlowRunExecutionBundle } from "./runs.js";
2
4
  import fs from "node:fs";
3
5
  import path from "node:path";
4
6
  import { AppError } from "../shared/errors.js";
5
7
  import { resolveProjectRoot } from "../storage/paths.js";
6
8
  import { requireProjectByRoot } from "./projects.js";
7
9
  import { advanceFlowRun, appendFlowRunTimelineEvent, attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunVariables, gitFacts, setRuntimeRunVariable } from "./runs.js";
8
- import { addWorkBatch, bindStageCoordinatorWork, ensureWorkRegistry, refreshRunWorkProjection, workStartCommand } from "./work-registry.js";
10
+ import { addWorkBatch, prepareWorkBatch, bindStageCoordinatorWork, ensureWorkRegistry, refreshRunWorkProjection, workStartCommand } from "./work-registry.js";
9
11
  import { validateSchema } from "./schema-validation.js";
10
12
  import { flowCommand, stagePauseCommand, stagePauseCommandTemplate } from "./stage-pause.js";
11
- import { assertStageStartHookEvent } from "./hooks.js";
12
- import { validateVnextPlanArtifacts } from "./vnext-plan.js";
13
+ import { assertAssignedStageStart as assertStageStartHookEvent } from "./assigned-lifecycle.js";
14
+ import { planCheckInstruction, prepareVnextPlanArtifacts, validateVnextPlanArtifacts } from "./vnext-plan.js";
13
15
  import { requireVnextWorkspaceRoute } from "./vnext-workspace-policy.js";
14
16
  import { nextWorkId } from "./ids.js";
15
17
  import { vnextStageDirectory } from "../domain/stage-catalog.js";
16
- import { writeStageReport } from "./stage-report-renderer.js";
18
+ import { acceptStagePacket, acceptStageReport, prepareStagePacket, prepareStageReport } from "./stage-report-renderer.js";
17
19
  import { assertPortableArtifactRef } from "./portable-refs.js";
18
- import { applyExternalStageContext } from "./stage-context.js";
19
- import { readFanoutDescriptor, subagentCapacityKey, writeFanoutDescriptor } from "./vnext-fanout.js";
20
+ import { currentStagePrompt, applyExternalStageContext, prepareExternalStageContext } from "./stage-context.js";
21
+ import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.js";
22
+ import { getStageFanoutCapacity, readFanoutDescriptor, subagentCapacityKey, writeFanoutDescriptor } from "./vnext-fanout.js";
23
+ import { stageGraphScope } from "./stage-work-graph.js";
24
+ import { stageLifecycleInstruction, workStopConditionInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
25
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, latestRunVerificationMatrix } from "./verification-matrix.js";
26
+ import { reviewerSourceInputs, reviewerSourceInstruction } from "./review-copy.js";
27
+ import { reviewDefEvidence } from "./review-deferral.js";
28
+ import { acceptedIntentReviewFingerprint, validateAcceptedIntentReview, assertAcceptedIntentReviewCurrent } from "./accepted-intent.js";
29
+ import { stageSuccessor } from "./vnext-contracts.js";
20
30
  const stage = "plan-review";
21
31
  const stageDir = vnextStageDirectory(stage);
22
32
  const reviewTask = "Independently review the accepted PLAN and decide whether CODE may open.";
@@ -24,68 +34,128 @@ export function isVnextPlanReviewRun(context, input) {
24
34
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
25
35
  return Boolean(context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ? AND flow_kind = 'vnext_protocolize'", [project.id, input.runId]));
26
36
  }
27
- export function startVnextPlanReview(context, input) {
28
- ensureWorkRegistry(context);
37
+ export function prepareVnextPlanReviewStart(context, input) {
29
38
  const projectRoot = resolveProjectRoot(input.projectRoot);
30
39
  const project = requireProjectByRoot(context, projectRoot);
31
40
  const run = requireRun(context, projectRoot, input.runId);
32
41
  const home = requireHome(run);
33
- assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
34
- const workspaceRoute = requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan-review" });
35
42
  const planRoot = path.join(home, "03-plan");
36
43
  const root = path.join(home, stageDir);
37
44
  const batch = path.join(planRoot, "code-work-batch.json");
45
+ const common = { projectRoot, project, run, home, planRoot, root, batch };
46
+ const priorStage = readIndex(run).stage_runs?.find((entry) => entry.stage === stage);
47
+ if (priorStage?.status === "done") {
48
+ const report = readJson(path.join(root, "stage-report.json"));
49
+ const outcome = report.semantic?.plan_review?.outcome;
50
+ if (outcome === "off" || outcome === "accepted")
51
+ return { ...common, state: "terminal", report, outcome };
52
+ throw new AppError("invalid_work_state", "PLAN-REVIEW is already terminal; corrections are applied in the same review Work and are not re-reviewed automatically", 2, { run_id: run.id, outcome });
53
+ }
54
+ const workspaceRoute = requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan-review" });
38
55
  if (!fs.existsSync(path.join(planRoot, "stage-report.json")) || !fs.existsSync(batch))
39
- throw new AppError("not_found", "PLAN-REVIEW requires the accepted PLAN report and proposed CODE batch", 1);
56
+ throw new AppError("runtime_artifact_missing", "PLAN-REVIEW requires the accepted PLAN report and proposed CODE batch", 1);
40
57
  const requested = requestedMode(run);
41
58
  const protocolIds = protocolIdsForReview(home);
42
59
  assertReviewWorkspace(run.workspace_root, protocolIds);
43
60
  const groups = reviewGroups(home, run.workspace_root);
44
61
  const effective = resolveMode(requested, groups, home, run.workspace_root);
62
+ const reviewerInputs = effective === "off" ? [] : reviewerSourceInputs(run.workspace_root, groups.flatMap(group => group.aspect_ids));
63
+ if (effective !== "off")
64
+ for (const group of groups)
65
+ group.task += `\n${reviewerSourceInstruction(run.workspace_root, group.aspect_ids)}`;
45
66
  const existing = context.db.get("SELECT work_id, status FROM works WHERE project_id = ? AND run_id = ? AND task = ? ORDER BY created_at DESC LIMIT 1", [project.id, run.id, reviewTask]);
67
+ const batchChecksum = checksum(batch), planChecksum = planSetChecksum(home, run.workspace_root);
68
+ if (input.externalContext)
69
+ prepareExternalStageContext({ stageRoot: root, loaded: input.externalContext });
70
+ prepareOutputFile(path.join(root, "stage-prompt.md"));
71
+ const attachment = existing?.status === "running" ? null : prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@4" });
72
+ const matrix = existing?.status === "running" ? latestRunVerificationMatrix(context, project.id, run.id, stage, "input") : prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
73
+ const active = { ...common, requested, protocolIds, groups, effective, workspaceRoute, batchChecksum, planChecksum, matrix, reviewerInputs };
46
74
  if (existing?.status === "running") {
47
- const promptPath = path.join(root, "stage-prompt.md");
48
- const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
49
- return { ...preparedExisting(context, { projectRoot, projectId: project.id, run, root, workId: existing.work_id, hookEventId: input.hookEventId, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}), requested, effective, groups, batchChecksum: checksum(batch), planChecksum: planSetChecksum(home, run.workspace_root) }), ...(externalContext ? { external_context: externalContext } : {}) };
75
+ const orchestration = readFanoutDescriptor(root);
76
+ if (!orchestration)
77
+ prepareOutputFile(path.join(root, "fanout.json"));
78
+ return { ...active, state: "resumed", workId: existing.work_id, prompt: fs.readFileSync(path.join(root, "stage-prompt.md"), "utf8"), orchestration };
50
79
  }
51
- const priorStage = readIndex(run).stage_runs?.find((entry) => entry.stage === stage);
52
- if (priorStage?.status === "done") {
53
- const report = readJson(path.join(root, "stage-report.json"));
54
- const outcome = report.semantic?.plan_review?.outcome;
55
- if (outcome === "off" || outcome === "accepted")
56
- return { ok: true, run_id: run.id, stage, outcome: outcome === "off" ? "review_off" : "accepted", ...(report.semantic?.code ? { code: report.semantic.code } : {}), next: { kind: "start_stage", stage: "code", command: codeCommand(context, run.id, projectRoot) }, idempotent: true };
57
- throw new AppError("invalid_work_state", "PLAN-REVIEW is already terminal; corrections are applied in the same review Work and are not re-reviewed automatically", 2, { run_id: run.id, outcome });
58
- }
59
- fs.mkdirSync(root, { recursive: true });
60
- const now = context.now();
61
- const batchChecksum = checksum(batch);
62
- const planChecksum = planSetChecksum(home, run.workspace_root);
80
+ const rootWork = rootWorkId(context, project.id, run.id);
63
81
  if (effective === "off") {
64
82
  const failures = validateVnextPlanArtifacts(context, { projectRoot, workspaceRoot: run.workspace_root, runId: run.id, home, protocols: protocolIds });
65
83
  if (failures.length)
66
- throw new AppError("validation", "PLAN artifacts have validation errors", 2, { errors: failures });
67
- const rootWork = rootWorkId(context, project.id, run.id);
84
+ throw new AppError("runtime_artifact_invalid", "Accepted PLAN artifacts have validation errors", 1, { errors: failures });
85
+ // This is retained runtime data, not a newly supplied --file argument.
86
+ const codeBatch = readJson(batch);
87
+ let codeItems;
88
+ try {
89
+ codeItems = prepareWorkBatch(context, { parentWorkId: rootWork, value: codeBatch });
90
+ }
91
+ catch (error) {
92
+ if (error instanceof AppError && error.exitCode === 2)
93
+ throw new AppError("runtime_artifact_invalid", "Accepted CODE batch cannot be registered", 1, { file: batch, cause_code: error.code });
94
+ throw error;
95
+ }
96
+ for (const name of ["stage-report.json", "stage-report.md", "stage-report.html"])
97
+ prepareOutputFile(path.join(root, name));
98
+ const outputMatrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
99
+ return { ...active, state: "off", rootWork, codeItems, outputMatrix };
100
+ }
101
+ const template = read(path.join(projectRoot, ".memory-bank", "dd-flow", "vnext", "plan-review.md"));
102
+ const revision = currentPlanRevision(home, run.workspace_root);
103
+ const capacity = getStageFanoutCapacity(context, { projectRoot, runId: run.id, stage });
104
+ for (const name of ["work-context.json", "fanout.json"])
105
+ prepareOutputFile(path.join(root, name));
106
+ return { ...active, state: "fresh", rootWork, template, revision, capacity };
107
+ }
108
+ export function startVnextPlanReview(context, input, prepared = prepareVnextPlanReviewStart(context, input)) {
109
+ const { projectRoot, project, run, home, root, planRoot } = prepared;
110
+ assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
111
+ if (prepared.state === "terminal") {
112
+ const { report, outcome } = prepared;
113
+ return { ok: true, run_id: run.id, stage, outcome: outcome === "off" ? "review_off" : "accepted", ...(report.semantic?.code ? { code: report.semantic.code } : {}), next: codeSuccessor(context, run.id, projectRoot), idempotent: true };
114
+ }
115
+ const { requested, effective, groups, batchChecksum, planChecksum, workspaceRoute } = prepared;
116
+ if (prepared.state === "resumed") {
117
+ resumeVerificationMatrix(context, project.id, run.id, stage);
118
+ const promptPath = path.join(root, "stage-prompt.md");
119
+ const externalContext = applyExternalStageContext({ context, stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
120
+ return { ...preparedExisting(context, { projectRoot, projectId: project.id, run, root, workId: prepared.workId, hookEventId: input.hookEventId, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}), requested, effective, groups, batchChecksum, planChecksum, orchestration: prepared.orchestration }), ...(externalContext ? { external_context: externalContext } : {}) };
121
+ }
122
+ ensureWorkRegistry(context);
123
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@4" }, true);
124
+ fs.mkdirSync(root, { recursive: true });
125
+ const now = context.now(), rootWork = prepared.rootWork;
126
+ if (prepared.state === "off") {
68
127
  const promptPath = path.join(root, "stage-prompt.md");
69
128
  fs.writeFileSync(promptPath, "PLAN-REVIEW is disabled by the frozen RUN configuration.\n");
70
- const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
129
+ const externalContext = applyExternalStageContext({ context, stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
71
130
  bindStageCoordinatorWork(context, { workId: rootWork, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
72
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
73
- const registered = registerCode(context, project.id, run.id, batch);
74
- const report = reportFor({ run, mode: effective, requested, outcome: "off", groups: [], batchChecksum, planChecksum, code: registered, now, projectRoot, flow: flowCommand(context) });
75
- writeReport(root, report);
76
- completeFlowRunStage(context, { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html" });
77
- advanceFlowRun(context, { projectRoot, runId: run.id, status: "running", verdict: "plan_review_off", nextAction: "start_code" });
78
- appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_opened", mode: effective, outcome: "off" });
79
- return { ok: true, run_id: run.id, stage, outcome: "review_off", ...(externalContext ? { external_context: externalContext } : {}), plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), reason: "No applicable independent review group." }, next: { kind: "start_stage", stage: "code", command: codeCommand(context, run.id, projectRoot) } };
131
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@4" };
132
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
133
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
134
+ const reportGit = gitFacts(run.workspace_root);
135
+ const next = codeSuccessor(context, run.id, projectRoot);
136
+ const completionBundle = readFlowRunExecutionBundle(context, { projectRoot, runId: run.id });
137
+ return withStageSettlement(context, () => {
138
+ const registered = addWorkBatch(context, { parentWorkId: rootWork, prepared: prepared.codeItems, stage: "code" });
139
+ const report = reportFor(context, { run, mode: effective, requested, outcome: "off", groups: [], batchChecksum, planChecksum, code: registered, now, projectRoot, flow: flowCommand(context), git: reportGit, ...(next.command ? { nextCommand: next.command } : {}) });
140
+ acceptVerificationMatrix(context, prepared.outputMatrix);
141
+ if (prepared.outputMatrix)
142
+ Object.assign(report.semantic, { verification_matrix: prepared.outputMatrix.metadata });
143
+ const preparedReport = prepareStageReport(report);
144
+ acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
145
+ const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
146
+ completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput, completionBundle));
147
+ advanceFlowRun(context, { settlementStage: "plan-review", projectRoot, runId: run.id, status: "running", verdict: "plan_review_off", nextAction: "start_code" });
148
+ appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_opened", mode: effective, outcome: "off" });
149
+ return { ok: true, run_id: run.id, stage, outcome: "review_off", ...(externalContext ? { external_context: externalContext } : {}), plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), reason: "No applicable independent review group." }, next };
150
+ });
80
151
  }
81
- const rootWork = rootWorkId(context, project.id, run.id);
82
152
  const workId = nextWorkId(context, project.id, "plan-review");
83
153
  context.db.run("INSERT INTO works (work_id, project_id, run_id, parent_work_id, task, launch_policy, result_schema, depends_on_json, status, result, started_at, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, 'reuse_allowed', NULL, '[]', 'running', NULL, ?, ?, ?, NULL)", [workId, project.id, run.id, rootWork, reviewTask, now, now, now]);
84
154
  const pauseCommand = stagePauseCommand(context, { runId: run.id, stage, workId, projectRoot });
85
- const prompt = orchestratorPrompt(context, { projectRoot, run, home, root, workId, requested, effective, groups, batchChecksum, planChecksum, workspaceRoute, pauseCommand, pauseCommandTemplate: stagePauseCommandTemplate(pauseCommand) });
155
+ const prompt = `${orchestratorPrompt(context, { projectRoot, run, home, root, workId, requested, effective, groups, batchChecksum, planChecksum, workspaceRoute, pauseCommand, pauseCommandTemplate: stagePauseCommandTemplate(pauseCommand), template: prepared.template, revision: prepared.revision, capacity: prepared.capacity })}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, home, "reviewing PLAN coverage, fixtures and proof limits")}\n`;
86
156
  const promptPath = path.join(root, "stage-prompt.md");
87
157
  fs.writeFileSync(promptPath, prompt);
88
- const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
158
+ const externalContext = applyExternalStageContext({ context, stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
89
159
  let binding;
90
160
  try {
91
161
  binding = bindStageCoordinatorWork(context, { workId, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
@@ -95,15 +165,17 @@ export function startVnextPlanReview(context, input) {
95
165
  fs.rmSync(promptPath, { force: true });
96
166
  throw error;
97
167
  }
98
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
168
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@4" };
169
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
170
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
99
171
  markReviewPending(home, groups);
100
172
  const workSessionId = String(binding.work_session_id);
101
173
  const sessionId = String(binding.session_id);
102
- const orchestration = writeFanoutDescriptor(root, { stage, parent_work_id: workId, dispatch: "plan-review", capacity_required: true });
103
- writeJson(path.join(root, "work-context.json"), { schema_id: "dd-flow/plan-review-context@1", system: { run_id: run.id, work_id: workId, stage, requested_mode: requested, effective_mode: effective, plan_revision: currentPlanRevision(home, run.workspace_root), plan_checksum: planChecksum, batch_checksum: batchChecksum }, groups, workspace: { project_root: projectRoot, workspace_root: run.workspace_root, stage_root: root, plan_root: planRoot } });
174
+ const orchestration = writeFanoutDescriptor(root, { stage, parent_work_id: workId, dispatch: "plan-review", capacity_required: true, scope: stageGraphScope(context, project.id, run.id, stage) });
175
+ writeJson(path.join(root, "work-context.json"), { schema_id: "dd-flow/plan-review-context@1", accepted_intent_review_fingerprint: acceptedIntentReviewFingerprint(prepared.matrix?.metadata.accepted_sources), reviewer_inputs: prepared.reviewerInputs, read_only_input: { parent_work_id: workId, version: 3 }, system: { run_id: run.id, work_id: workId, stage, requested_mode: requested, effective_mode: effective, plan_sources: readJson(prepared.batch).sources.map(source => ({ ...source, content_sha256: planContentHash(readJson(path.join(run.workspace_root, ".memory-bank", "protocol", source.protocol_id, "plan.json"))) })), plan_revision: prepared.revision, plan_checksum: planChecksum, batch_checksum: batchChecksum }, groups, workspace: { project_root: projectRoot, workspace_root: run.workspace_root, stage_root: root, plan_root: planRoot } });
104
176
  refreshRunWorkProjection(context, project.id, run.id);
105
177
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "work_session_started", stage, work_id: workId, id: workSessionId, session_id: sessionId });
106
- return { ok: true, run_id: run.id, stage, outcome: "review_required", work_id: workId, id: workSessionId, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), ...(externalContext ? { external_context: externalContext } : {}), orchestration, plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), groups }, next: { dispatch_command: dispatchCommand(context, run.id, projectRoot), finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
178
+ return { ok: true, run_id: run.id, stage, outcome: "review_required", work_id: workId, id: workSessionId, ...currentStagePrompt(context, promptPath), ...(externalContext ? { external_context: externalContext } : {}), orchestration, plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), groups }, next: { dispatch_command: dispatchCommand(context, run.id, projectRoot), finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
107
179
  }
108
180
  export function dispatchVnextPlanReview(context, input) {
109
181
  ensureWorkRegistry(context);
@@ -131,7 +203,7 @@ export function dispatchVnextPlanReview(context, input) {
131
203
  const file = path.join(root, ".dispatch.json");
132
204
  writeJson(file, { works: pending.map((group) => ({ key: group.key, task: group.task, depends_on: group.depends_on, launch_policy: "fresh_agent_required", result_schema: "dd-flow/plan-review-result@1", payload: { kind: "plan-review", read_only: true, group: { key: group.key, aspect_ids: group.aspect_ids } } })) });
133
205
  try {
134
- addWorkBatch(context, { parentWorkId: parent.work_id, file });
206
+ addWorkBatch(context, { parentWorkId: parent.work_id, file, stage });
135
207
  }
136
208
  finally {
137
209
  fs.rmSync(file, { force: true });
@@ -139,14 +211,12 @@ export function dispatchVnextPlanReview(context, input) {
139
211
  }
140
212
  const created = context.db.all("SELECT work_id, task, status, launch_policy, created_at FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ?", [project.id, run.id, parent.work_id]);
141
213
  const reviews = groups.map((group) => { const work = created.find((item) => item.task === group.task); if (!work)
142
- throw new AppError("runtime_missing", "Reviewer Work was not created", 1, { group: group.key }); const ready = work.status === "created" && isReviewWorkReady(context, work.work_id); return { group: group.key, work_id: work.work_id, status: work.status, depends_on: group.depends_on, ready, ...(ready ? { start_command: workStartCommand(context, { ...work, project_id: project.id, run_id: run.id }) } : {}) }; });
214
+ throw new AppError("runtime_missing", "Reviewer Work was not created", 1, { group: group.key }); const ready = work.status === "created" && isReviewWorkReady(context, work.work_id); return { group: group.key, work_id: work.work_id, status: work.status, depends_on: group.depends_on, ready, ...(ready ? { start_command: workStartCommand(context, { ...work, project_id: project.id, run_id: run.id }, { preview: true }) } : {}) }; });
143
215
  refreshRunWorkProjection(context, project.id, run.id);
144
216
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_dispatched", work_id: parent.work_id, groups: reviews.map((item) => ({ group: item.group, work_id: item.work_id })) });
145
217
  const waves = plannedReviewWaves(groups, capacity.availableSlots);
146
- return { ok: true, run_id: run.id, stage, capacity: { available_slots: capacity.availableSlots }, routing: { group_count: groups.length, planned_waves: waves.length, waves, preference: "Use one wave when semantic compatibility permits; hard dependencies create later waves." }, review_works: reviews, next_action: "launch_ready_reviews" };
218
+ return { ok: true, run_id: run.id, stage, capacity: { available_slots: capacity.availableSlots, source: capacity.source }, routing: { group_count: groups.length, planned_waves: waves.length, waves, preference: "Use one wave when semantic compatibility permits; hard dependencies create later waves." }, review_works: reviews, next_action: "launch_ready_reviews" };
147
219
  }
148
- // Capacity is a RUN fact shared by every fan-out stage.
149
- const reviewCapacityKey = subagentCapacityKey;
150
220
  /** Capacity is observed immediately before this stage dispatches reviewers. */
151
221
  export function recordVnextPlanReviewCapacity(context, input) {
152
222
  if (!Number.isInteger(input.availableSlots) || input.availableSlots < 0 || input.availableSlots > 15) {
@@ -156,19 +226,22 @@ export function recordVnextPlanReviewCapacity(context, input) {
156
226
  const project = requireProjectByRoot(context, projectRoot);
157
227
  const run = requireRun(context, projectRoot, input.runId);
158
228
  const variables = getFlowRunVariables(context, { projectRoot, runId: run.id });
159
- const prior = variables.variables[reviewCapacityKey];
229
+ const prior = variables.variables[subagentCapacityKey];
160
230
  if (typeof prior === "number") {
161
231
  if (prior === input.availableSlots)
162
232
  return { ok: true, run_id: run.id, capacity: { available_slots: prior, source: "harness_qualification", reused: true } };
163
- setRuntimeRunVariable(context, { projectRoot, runId: run.id, key: reviewCapacityKey, value: input.availableSlots });
233
+ setRuntimeRunVariable(context, { projectRoot, runId: run.id, key: subagentCapacityKey, value: input.availableSlots });
164
234
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "subagent_capacity_refreshed", previous_available_slots: prior, available_slots: input.availableSlots, source: "harness_qualification" });
165
235
  return { ok: true, run_id: run.id, capacity: { available_slots: input.availableSlots, previous_available_slots: prior, source: "harness_qualification", refreshed: true } };
166
236
  }
167
- setRuntimeRunVariable(context, { projectRoot, runId: run.id, key: reviewCapacityKey, value: input.availableSlots });
237
+ setRuntimeRunVariable(context, { projectRoot, runId: run.id, key: subagentCapacityKey, value: input.availableSlots });
168
238
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "subagent_capacity_observed", available_slots: input.availableSlots, source: "harness_qualification" });
169
239
  return { ok: true, run_id: run.id, capacity: { available_slots: input.availableSlots, source: "harness_qualification" } };
170
240
  }
171
241
  export function finishVnextPlanReview(context, input) {
242
+ return finishPlanReviewOwned(context, input);
243
+ }
244
+ function finishPlanReviewOwned(context, input) {
172
245
  const projectRoot = resolveProjectRoot(input.projectRoot);
173
246
  const project = requireProjectByRoot(context, projectRoot);
174
247
  const run = requireRun(context, projectRoot, input.runId);
@@ -180,7 +253,8 @@ export function finishVnextPlanReview(context, input) {
180
253
  if (!parent || parent.status !== "running")
181
254
  throw new AppError("invalid_work_state", "PLAN-REVIEW has no running parent Work", 2);
182
255
  const decisionPath = path.resolve(input.decisionFile ?? path.join(root, "decision.json"));
183
- const decision = readDecision(context, projectRoot, run.id, home, decisionPath);
256
+ const decision = input.decision ?? readDecision(context, projectRoot, run.id, home, decisionPath);
257
+ validateSchema({ schemaName: "plan-review-decision", file: decisionPath, data: decision, projectRoot, runId: run.id, runRoot: home, ddFlowHome: context.ddFlowHome });
184
258
  const children = context.db.all("SELECT work_id, task, status, result, created_at FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ?", [project.id, run.id, parent.work_id]);
185
259
  const contextFile = readJson(path.join(root, "work-context.json"));
186
260
  const groups = contextFile.groups ?? [];
@@ -188,27 +262,33 @@ export function finishVnextPlanReview(context, input) {
188
262
  if (!Number.isInteger(reviewedPlanRevision) || reviewedPlanRevision < 1)
189
263
  throw new AppError("runtime_missing", "PLAN-REVIEW starting revision is unavailable", 1);
190
264
  const planRevision = currentPlanRevision(home, run.workspace_root);
191
- const currentPlanChecksum = planSetChecksum(home, run.workspace_root);
192
265
  if (decision.outcome === "blocked") {
193
266
  throw new AppError("stage_pause_required", "A user or decision blocker must pause the current PLAN-REVIEW Work with `dd-flow stage pause`; do not finish the stage as blocked", 2, { run_id: run.id, stage, work_id: parent.work_id });
194
267
  }
268
+ if (children.some((child) => !["completed", "failed", "cancelled"].includes(child.status)))
269
+ throw new AppError("worker_jobs_incomplete", "PLAN-REVIEW finish requires all reviewer Works to settle", 1, { works: children.map((child) => ({ work_id: child.work_id, status: child.status })) });
195
270
  if (["failed", "cancelled"].includes(decision.outcome)) {
196
- preserveDecisionReceipt(root, decisionPath);
197
- return finishTerminalReview(context, { projectRoot, projectId: project.id, run, root, planRoot, batch, parentWorkId: parent.work_id, children, contextFile, decision });
271
+ const decisionReceipt = prepareDecisionReceipt(root, decision, input.decisionText);
272
+ const now = context.now();
273
+ const reportGit = gitFacts(run.workspace_root);
274
+ const report = reportFor(context, { run, mode: contextFile.system?.effective_mode ?? "standard", requested: contextFile.system?.requested_mode ?? "auto", outcome: decision.outcome, groups: contextFile.groups ?? [], batchChecksum: contextFile.system?.batch_checksum ?? checksum(batch), planChecksum: contextFile.system?.plan_checksum ?? planSetChecksum(home, run.workspace_root), code: {}, now, projectRoot, decision, children: latestReviewChildren(children), flow: flowCommand(context), git: reportGit });
275
+ const preparedReport = prepareStageReport(report);
276
+ const stageStatus = decision.outcome === "cancelled" ? "skipped" : "failed";
277
+ const completionInput = { projectRoot, runId: run.id, stage, status: stageStatus, data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
278
+ const preparedCompletion = prepareFlowRunStageCompletion(context, completionInput);
279
+ return withStageSettlement(context, () => finishTerminalReview(context, { projectRoot, projectId: project.id, run, root, parentWorkId: parent.work_id, decision, now, decisionReceipt, preparedReport, completionInput, preparedCompletion }));
198
280
  }
199
- if (children.some((child) => child.status === "created" || child.status === "running"))
200
- throw new AppError("worker_jobs_incomplete", "PLAN-REVIEW finish requires all reviewer Works to settle", 1, { works: children.map((child) => ({ work_id: child.work_id, status: child.status })) });
201
281
  const latestChildren = latestReviewChildren(children);
282
+ const intentEvidence = validateAcceptedIntentReview(context, { projectId: project.id, runId: run.id, home, stage }, latestRunVerificationMatrix(context, project.id, run.id, stage, "input")?.metadata.accepted_sources, contextFile.accepted_intent_review_fingerprint, decision.accepted_intent_acknowledgement);
202
283
  const evidenceFailures = reviewerEvidenceFailures(context, { projectId: project.id, parentWorkId: parent.work_id, children: latestChildren, groups, planRevision: reviewedPlanRevision, workspaceRoot: run.workspace_root, runHome: home, runId: run.id });
203
284
  if (evidenceFailures.length)
204
285
  throw new AppError("review_evidence_invalid", "PLAN-REVIEW reviewer evidence is incomplete, stale, or not isolated", 2, { errors: evidenceFailures });
205
286
  if (decision.outcome !== "accepted")
206
- throw new AppError("validation", "Unsupported PLAN-REVIEW decision outcome", 2, { outcome: decision.outcome });
287
+ throw new AppError("validation", "Unsupported PLAN-REVIEW decision outcome", 2, { outcome: decision.outcome, phase: "prepare", effect: "no_effect", recoverable: true });
207
288
  // A completed reviewer can legitimately report `blocked` when an upstream
208
289
  // plan uncertainty prevents it from approving its aspect. That is evidence
209
290
  // for the coordinator to classify and fix in this one review pass, not an
210
291
  // infrastructure failure or a reason to start another review cycle.
211
- const needsCorrection = latestChildren.some((child) => ["needs_changes", "blocked"].includes(reviewerVerdict(child.result)));
212
292
  if (latestChildren.some((child) => child.status !== "completed"))
213
293
  throw new AppError("blocked", "PLAN-REVIEW cannot accept incomplete reviewer evidence", 2);
214
294
  const canonicalFindings = canonicalReviewerFindings(latestChildren);
@@ -219,52 +299,80 @@ export function finishVnextPlanReview(context, input) {
219
299
  const unknownDecisions = decisionFindingIds.filter((id) => !allFindingIds.includes(id));
220
300
  if (new Set(allFindingIds).size !== allFindingIds.length || decidedFindingIds.size !== decisionFindingIds.length || unknownDecisions.length || materialFindingIds.some((id) => !decidedFindingIds.has(id)))
221
301
  throw new AppError("review_evidence_invalid", "Reviewer findings and coordinator decisions must use unique known canonical references, with every material finding decided", 2, { material_finding_ids: materialFindingIds, decided_finding_ids: decisionFindingIds, unknown_decisions: unknownDecisions });
222
- if (needsCorrection) {
223
- if (decision.correction.status !== "applied")
224
- throw new AppError("validation", "Reviewer findings require an in-place correction receipt", 2);
225
- if (decision.correction.previous_plan_revision !== reviewedPlanRevision || planRevision <= reviewedPlanRevision || decision.correction.changed_paths.length === 0)
226
- throw new AppError("validation", "Applied PLAN correction must advance revision and list changed paths", 2, { reviewed_plan_revision: reviewedPlanRevision, final_plan_revision: planRevision });
227
- }
228
- else if (decision.correction.status !== "not_required" || planRevision !== reviewedPlanRevision || currentPlanChecksum !== contextFile.system?.plan_checksum) {
229
- throw new AppError("validation", "A clean PLAN review must not claim an unverified correction", 2);
230
- }
231
- const planFailures = validateVnextPlanArtifacts(context, { projectRoot, workspaceRoot: run.workspace_root, runId: run.id, home, protocols: protocolIdsForReview(home) });
232
- if (planFailures.length)
233
- throw new AppError("validation", "Corrected PLAN artifacts have validation errors", 2, { errors: planFailures });
234
- if (needsCorrection) {
235
- const initialBatchChecksum = contextFile.system?.batch_checksum;
236
- if (!initialBatchChecksum || checksum(batch) === initialBatchChecksum)
237
- throw new AppError("stale_code_work_batch", "Applied PLAN correction must regenerate the proposed CODE batch", 2, { batch, initial_batch_checksum: initialBatchChecksum });
302
+ const defEvidence = validatePlanFindingDecisions(decision.finding_decisions, allFindingIds, run.workspace_root);
303
+ const acceptedMatrix = latestRunVerificationMatrix(context, project.id, run.id, "plan", "output");
304
+ const planSources = acceptedMatrix ? acceptedMatrix.matrix.sources.filter(source => source.role.startsWith("plan:")).map(source => {
305
+ const packet = acceptedMatrix.packets.find(packet => packet.file === path.join(source.root === "run" ? home : run.workspace_root, source.path) && packet.sha256 === source.sha256);
306
+ if (!packet)
307
+ throw new AppError("review_input_contract_missing", "Accepted PLAN source lacks its immutable publication bytes", 1);
308
+ const value = JSON.parse(packet.bytes.toString("utf8"));
309
+ return { plan_id: String(value.plan_id), protocol_id: String(value.protocol_id), revision: Number(value.revision), sha256: source.sha256, content_sha256: planContentHash(value) };
310
+ }) : contextFile.system?.plan_sources ?? [];
311
+ if (checksum(path.join(planRoot, "stage-report.json")) !== readIndex(run).stage_runs?.find(entry => entry.stage === "plan")?.data_sha256)
312
+ throw new AppError("read_only_input_changed", "Accepted PLAN report changed during review", 2);
313
+ const acceptedPlan = readJson(path.join(planRoot, "stage-report.json"));
314
+ validatePlanCorrection({ decision, reviewedPlanRevision: reviewedPlanRevision, sources: planSources, maps: acceptedPlan.semantic.aspect_map_checksums, workspaceRoot: run.workspace_root, home });
315
+ const prepared = prepareVnextPlanArtifacts(context, { projectRoot, workspaceRoot: run.workspace_root, runId: run.id, home, protocols: protocolIdsForReview(home) });
316
+ if (prepared.failures.length)
317
+ throw new AppError("validation", "Corrected PLAN artifacts have validation errors", 2, { errors: prepared.failures, phase: "prepare", effect: "no_effect", recoverable: true });
318
+ if (decision.correction.status === "applied") {
319
+ // Regeneration above is authoritative. A valid PLAN correction need not
320
+ // change the derived CODE tasks (for example, a clarified explanation).
238
321
  if (decision.correction.changed_paths.some((item) => item.endsWith("code-work-batch.json")))
239
- throw new AppError("validation", "Generated CODE batch must not be listed as an agent-authored correction", 2, { changed_paths: decision.correction.changed_paths });
322
+ throw new AppError("validation", "Generated CODE batch must not be listed as an agent-authored correction", 2, { changed_paths: decision.correction.changed_paths, phase: "prepare", effect: "no_effect", recoverable: true });
240
323
  }
241
- preserveDecisionReceipt(root, decisionPath);
242
324
  const finalPlanChecksum = planSetChecksum(home, run.workspace_root);
243
- const finalBatchChecksum = checksum(batch);
244
- const registered = registerCode(context, project.id, run.id, batch);
245
- const now = context.now();
246
- settleReviewParent(context, { projectRoot, projectId: project.id, runId: run.id, parentWorkId: parent.work_id, status: "completed", result: decision.summary, reason: "plan_review_completed", resultPath: path.join(root, "stage-report.json") });
247
- const report = reportFor({ run, mode: contextFile.system?.effective_mode ?? "standard", requested: contextFile.system?.requested_mode ?? "auto", outcome: "accepted", groups, batchChecksum: contextFile.system?.batch_checksum ?? finalBatchChecksum, planChecksum: contextFile.system?.plan_checksum ?? finalPlanChecksum, finalBatchChecksum, finalPlanChecksum, initialPlanRevision: reviewedPlanRevision, finalPlanRevision: planRevision, code: registered, now, projectRoot, decision, children: latestChildren, flow: flowCommand(context) });
248
- applyReviewResults(home, latestChildren, decision);
249
- writeReport(root, report);
250
- refreshRunWorkProjection(context, project.id, run.id);
251
- completeFlowRunStage(context, { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html" });
252
- advanceFlowRun(context, { projectRoot, runId: run.id, status: "running", verdict: "plan_review_accepted", nextAction: "start_code" });
253
- appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_accepted", work_id: parent.work_id, code: registered });
254
- return { ok: true, run_id: run.id, stage, outcome: "accepted", code: registered, next_action: "start_code", next: { kind: "start_stage", stage: "code", command: codeCommand(context, run.id, projectRoot) } };
325
+ const finalBatchChecksum = prepared.batchChecksum;
326
+ prepared.publish();
327
+ const decisionReceipt = prepareDecisionReceipt(root, decision, input.decisionText);
328
+ const codeParentId = rootWorkId(context, project.id, run.id);
329
+ const codeItems = prepareWorkBatch(context, { parentWorkId: codeParentId, file: batch, stage: "code" });
330
+ const reportGit = gitFacts(run.workspace_root);
331
+ const next = codeSuccessor(context, run.id, projectRoot);
332
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", semanticFile: decisionPath });
333
+ const completionBundle = readFlowRunExecutionBundle(context, { projectRoot, runId: run.id });
334
+ return withStageSettlement(context, () => {
335
+ assertAcceptedIntentReviewCurrent(context, { projectId: project.id, runId: run.id }, intentEvidence);
336
+ const registered = addWorkBatch(context, { parentWorkId: codeParentId, prepared: codeItems, stage: "code" });
337
+ const now = context.now();
338
+ settleReviewParent(context, { projectRoot, projectId: project.id, runId: run.id, parentWorkId: parent.work_id, status: "completed", result: decision.summary, reason: "plan_review_completed", resultPath: path.join(root, "stage-report.json") });
339
+ const report = reportFor(context, { run, mode: contextFile.system?.effective_mode ?? "standard", requested: contextFile.system?.requested_mode ?? "auto", outcome: "accepted", groups, batchChecksum: contextFile.system?.batch_checksum ?? finalBatchChecksum, planChecksum: contextFile.system?.plan_checksum ?? finalPlanChecksum, finalBatchChecksum, finalPlanChecksum, initialPlanRevision: reviewedPlanRevision, finalPlanRevision: planRevision, code: registered, now, projectRoot, decision, children: latestChildren, flow: flowCommand(context), git: reportGit, ...(next.command ? { nextCommand: next.command } : {}) });
340
+ Object.assign(report.semantic, { def_evidence: defEvidence });
341
+ if (intentEvidence)
342
+ Object.assign(report.semantic, { accepted_intent_review: intentEvidence });
343
+ applyReviewResults(home, latestChildren, decision);
344
+ if (matrix)
345
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
346
+ const preparedReport = prepareStageReport(report);
347
+ acceptVerificationMatrix(context, matrix);
348
+ acceptStagePacket(context, { projectId: project.id, runId: run.id, stage, prepared: decisionReceipt });
349
+ acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
350
+ const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
351
+ completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput, completionBundle));
352
+ refreshRunWorkProjection(context, project.id, run.id);
353
+ advanceFlowRun(context, { settlementStage: "plan-review", projectRoot, runId: run.id, status: "running", verdict: "plan_review_accepted", nextAction: "start_code" });
354
+ appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_accepted", work_id: parent.work_id, code: registered });
355
+ return { ok: true, run_id: run.id, stage, outcome: "accepted", code: registered, next_action: "start_code", next };
356
+ });
255
357
  }
256
358
  function preparedExisting(context, input) {
257
359
  const promptPath = path.join(input.root, "stage-prompt.md");
258
360
  const binding = bindStageCoordinatorWork(context, { workId: input.workId, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(input.root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
259
- const orchestration = readFanoutDescriptor(input.root) ?? writeFanoutDescriptor(input.root, { stage, parent_work_id: input.workId, dispatch: "plan-review", capacity_required: true });
260
- return { ok: true, resumed: true, run_id: input.run.id, stage, outcome: "review_required", work_id: input.workId, id: binding.work_session_id, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), orchestration, plan_review: { requested_mode: input.requested, effective_mode: input.effective, policy_source: policySource(input.run), groups: input.groups }, next: { dispatch_command: dispatchCommand(context, input.run.id, input.projectRoot), finish_command: finishCommand(context, input.run.id, input.projectRoot, path.join(input.root, "decision.json")) } };
361
+ const orchestration = input.orchestration ?? writeFanoutDescriptor(input.root, { stage, parent_work_id: input.workId, dispatch: "plan-review", capacity_required: true, scope: stageGraphScope(context, input.run.project_id, input.run.id, stage) });
362
+ return { ok: true, resumed: true, run_id: input.run.id, stage, outcome: "review_required", work_id: input.workId, id: binding.work_session_id, ...currentStagePrompt(context, promptPath), orchestration, plan_review: { requested_mode: input.requested, effective_mode: input.effective, policy_source: policySource(input.run), groups: input.groups }, next: { dispatch_command: dispatchCommand(context, input.run.id, input.projectRoot), finish_command: finishCommand(context, input.run.id, input.projectRoot, path.join(input.root, "decision.json")) } };
261
363
  }
262
364
  function orchestratorPrompt(context, input) {
263
- const template = read(path.join(input.projectRoot, ".memory-bank", "dd-flow", "vnext", "plan-review.md"));
365
+ const { revision, capacity } = input;
264
366
  const decision = path.join(input.root, "decision.json");
265
- const revision = currentPlanRevision(input.home, input.run.workspace_root);
266
367
  const workspaceContract = ["<workspace_contract>", `- route: ${input.workspaceRoute.route}`, `- feature branch: ${input.workspaceRoute.feature_branch ?? "not applicable"}`, `- base commit: ${input.workspaceRoute.base_ref ?? "not applicable"}`, `- read/write workspace: ${input.run.workspace_root}`, "The CLI verified this frozen route. All plan and correction writes belong in the named workspace; project root remains only the stable lifecycle identity. Do not create, switch, merge or delete branches/worktrees.", "</workspace_contract>"].join("\n");
267
- return ["<stage_identity>", `- RUN: ${input.run.id}`, `- Work: ${input.workId}`, "- Stage: plan-review", `- Mode: ${input.effective}`, "</stage_identity>", "", "<trusted_runtime_context>", "These facts were collected by dd-flow. Trust them; do not repeat CLI, Git, compatibility, permission or schema discovery.", `- Project root: ${input.projectRoot}`, `- Stage workspace: ${input.root}`, `- PLAN revision: ${revision}`, `- PLAN report checksum: ${input.planChecksum}`, `- Generated CODE batch checksum: ${input.batchChecksum}`, "</trusted_runtime_context>", "", workspaceContract, "", "<review_groups>", ...input.groups.map((group) => `- ${group.key}: ${group.aspect_ids.join(", ")}`), "</review_groups>", "", "<execution_commands>", `Dispatch fresh reviewers: ${dispatchCommand(context, input.run.id, input.projectRoot)}`, `If dispatch reports qualified_capacity_required, stop. The external harness controller qualifies the selected profile outside this RUN and records the resulting integer with ${capacityRecordCommand(context, input.run.id, input.projectRoot, "<qualified-native-child-count>")}. PLAN-REVIEW never launches a capacity probe.`, "After dispatch, launch at most the qualified capacity at once. If more independent reviewer Works remain, wait for the current wave to settle, then start the unchanged queued Works in the next wave. A reviewer launch rejected before it starts is not review evidence: do not create a replacement; wait for a running wave to settle and start that same queued Work. Each reviewer must be a genuinely fresh harness child Session. The lifecycle adapter binds that observed Session; do not bind or supply a Session ID manually. Reviewers are read-only and must not create children. As soon as a reviewer result is accepted, release that reviewer Session when the harness permits; do not let finished disposable workers occupy slots before the next wave.", "Review the execution environment of every selected check as part of its proof: a reset/fixture process, service process and client process must share the intended data and configuration world. A runtime entrypoint that can break that invariant must be explicit in one Work's task and verification and ordered before its consumer. planned_write_areas may advertise likely overlap, but do not treat them as ownership; required_read alone is not a delivery plan.", "If the final decision needs user input with no reasonable default, run this exact one-command heredoc, replacing only its placeholder body. The heredoc is the permitted stdin form; do not use cat, a pipe, a temporary file or a second shell command:", "```sh", input.pauseCommandTemplate, "```", "Ask the returned user_message, stop, then resume this same PLAN-REVIEW Work. Do not write decision.json or finish first.", `When all reviewer results are complete and every user question is resolved, classify every material finding, fix accepted findings in this same PLAN-REVIEW Work, then write ${decision} and finish: ${finishCommand(context, input.run.id, input.projectRoot, decision)}`, "Reviewer findings use local FIND-NNN ids. dd-flow exposes each finding to this coordinator as WRK-.../FIND-NNN; use that canonical finding_ref in the decision.", "A completed reviewer result with needs_changes or blocked is evidence, not the stage outcome. Classify its material findings and apply accepted fixes in this one review pass; do not start a second review automatically. Only a missing, malformed or unfinished reviewer result blocks the stage. For an accepted correction, increment PLAN revision and update only plan.json and the relevant aspect map. Do not edit or list code-work-batch.json: the CLI validates final PLAN and regenerates it. If no material correction is needed, set correction.status=not_required. The CLI checks mechanical handoff coherence; it does not prove semantic correctness.", "```json", JSON.stringify({ schema_id: "dd-flow/plan-review-decision@3", outcome: "accepted | failed | cancelled", summary: "Concise evidence-backed final decision.", finding_decisions: [{ finding_ref: "WRK-001-review/FIND-001", decision: "accepted_fix | rejected | deferred_as_DEF | requires_user | duplicate", reason: "Why." }], correction: { status: "not_required | applied", previous_plan_revision: revision, changed_paths: [], summary: "No material correction was needed, or summarize the applied correction." } }, null, 2), "```", "</execution_commands>", "", "<stage_instructions>", template, "</stage_instructions>", ""].join("\n");
368
+ const acceptanceOwnership = "For every deferred acceptance or document update, identify the named CODE Work owner, its causal checks, and when it can truthfully close the obligation. Aggregate checks run only after implementation Work completes: assign a CODE owner to pending documents and use CODE needs_checks → verification_required → repair after those receipts. A promise that MERGE or an unnamed readiness owner will finish acceptance is a material PLAN finding; bootstrap workspace readiness is not proof of the new feature.";
369
+ const template = [acceptanceOwnership, planCheckInstruction, workStopConditionInstruction, "Review stop_conditions semantically: reject conditions that veto the requested change merely because the baseline differs, turn planned paths into ownership, or expand one unsupported evidence operation into a prohibition on all implementation. Preserve real safety, accepted-decision and required-evidence boundaries.",
370
+ `The accepted check profile is ${path.join(input.run.workspace_root, ".memory-bank", "spec", "engineering", "code-check-profile.json")}. A new check required by a correction is planned, not available: name its provider Work and order consumers after that Work.`,
371
+ input.template].join("\n\n");
372
+ const reviewerLaunch = capacity.source === "external_policy"
373
+ ? `After dispatch, return at the Work-graph boundary. The shared runtime launches the queued reviewer Works as separate external Sessions, at most ${capacity.available_slots} in parallel under the RUN's frozen profiles. Do not launch native children, create provider roots, run a capacity probe, or record this external limit as native capacity. Reviewers are read-only leaf workers. Continue the semantic decision only after their Work receipts settle; do not substitute a missing result or relaunch a settled reviewer.`
374
+ : "After dispatch, stop at the Work-graph boundary. The shared controller supplies the selected adapter's native tool, exact parameters, child task, and wait contract; do not choose a provider tool from this stage prompt or substitute a shell command. It launches at most the qualified capacity at once and starts unchanged queued Works only after the current wave settles. A launch rejected before it starts is not review evidence: do not create a replacement. Each reviewer must be a genuinely fresh harness child Session; the lifecycle adapter binds that observed Session, so do not bind or supply a Session ID manually. Reviewers are read-only and must not create children. As soon as a reviewer result is accepted, release that reviewer Session when the harness permits.";
375
+ return ["<stage_identity>", `- RUN: ${input.run.id}`, `- Work: ${input.workId}`, "- Stage: plan-review", `- Mode: ${input.effective}`, "</stage_identity>", "", "<trusted_runtime_context>", "These facts were collected by dd-flow. Trust them; do not repeat CLI, Git, compatibility, permission or schema discovery.", `- Project root: ${input.projectRoot}`, `- Stage workspace: ${input.root}`, `- PLAN revision: ${revision}`, `- PLAN report checksum: ${input.planChecksum}`, `- Generated CODE batch checksum: ${input.batchChecksum}`, "</trusted_runtime_context>", "", workspaceContract, "", "<review_groups>", ...input.groups.map((group) => `- ${group.key}: ${group.aspect_ids.join(", ")}`), "</review_groups>", "", "<execution_commands>", `Dispatch fresh reviewers: ${dispatchCommand(context, input.run.id, input.projectRoot)}`, `If dispatch reports qualified_capacity_required, stop. The external harness controller qualifies the selected profile outside this RUN and records the resulting integer with ${capacityRecordCommand(context, input.run.id, input.projectRoot, "<qualified-native-child-count>")}. PLAN-REVIEW never launches a capacity probe.`, reviewerLaunch, "Review the execution environment of every selected check as part of its proof: a reset/fixture process, service process and client process must share the intended data and configuration world. A runtime entrypoint that can break that invariant must be explicit in one Work's task and verification and ordered before its consumer. planned_write_areas may advertise likely overlap, but do not treat them as ownership; required_read alone is not a delivery plan.", "If the final decision needs user input with no reasonable default, write the question packet to the named text file, then run the separate standalone lifecycle command below. Do not combine file creation and dd-flow in one shell command:", "```sh", input.pauseCommandTemplate, "```", "Ask the returned user_message, stop, then resume this same PLAN-REVIEW Work. Do not write decision.json or finish first.", `When all reviewer results are complete and every user question is resolved, classify every material finding, fix accepted findings in this same PLAN-REVIEW Work, then write ${decision} and finish: ${finishCommand(context, input.run.id, input.projectRoot, decision)}`, "Reviewer findings use local FIND-NNN ids. dd-flow exposes each finding to this coordinator as WRK-.../FIND-NNN; use that canonical finding_ref in the decision.", "A completed reviewer result with needs_changes or blocked is evidence, not the stage outcome. Classify its material findings and apply accepted fixes in this one review pass; do not start a second review automatically. Only a missing, malformed or unfinished reviewer result blocks the stage. Accepted fixes need an actual authored delta. Rejected/duplicate findings need no artificial correction. Coordinator-only correction is allowed. Increment only the changed protocol PLAN's own revision; an aspect-map-only correction needs no fake PLAN revision. changed_paths must list exactly the changed workspace-relative PLAN paths and RUN/<aspect-map-path>. Do not edit or list code-work-batch.json: the CLI validates final PLAN and regenerates it. If no material correction is needed, set correction.status=not_required. The CLI checks mechanical handoff coherence; it does not prove semantic correctness.", "```json", JSON.stringify({ schema_id: "dd-flow/plan-review-decision@4", outcome: "accepted | failed | cancelled", summary: "Concise evidence-backed final decision.", finding_decisions: [{ finding_ref: "WRK-001-review/FIND-001", decision: "accepted_fix | rejected | deferred_as_DEF | requires_user | duplicate", reason: "Why.", duplicate_of: "A distinct canonical finding_ref only for duplicate", def_id: "DEF-0001 only for deferred_as_DEF" }], correction: { status: "not_required | applied", previous_plan_revision: revision, changed_paths: [], summary: "No material correction was needed, or summarize the applied correction." } }, null, 2), "```", "</execution_commands>", "", "<stage_instructions>", template, "</stage_instructions>", ""].join("\n");
268
376
  }
269
377
  function reviewGroups(home, workspaceRoot) {
270
378
  const root = path.join(home, "03-plan");
@@ -285,7 +393,7 @@ function reviewGroups(home, workspaceRoot) {
285
393
  if (dependencyGroup && dependencyGroup !== key)
286
394
  dependencies.add(dependencyGroup);
287
395
  }
288
- groups.push({ key, protocol_id: protocolId, aspect_ids: group.aspect_ids, depends_on: [...dependencies].sort(), task: `Review PLAN group ${key}: ${group.aspect_ids.join(", ")}. Read run://${path.basename(home)}/03-plan/${protocolId}/aspect-map.json and the referenced plan. Try to falsify ambiguous behavior and missing verification. For every selected check that starts fixture/reset, service, or client processes, verify that they share the intended data/configuration world and that any runtime entrypoint requiring change is explicit in a Work task and verification and ordered before its consumer. planned_write_areas are coordination hints, not ownership. Do not edit plan or product files. Return one aspect entry for every assigned aspect, with exact evidence references and concise findings. Finish with the JSON contract supplied in your work prompt.` });
396
+ groups.push({ key, protocol_id: protocolId, aspect_ids: group.aspect_ids, depends_on: [...dependencies].sort(), task: `Review PLAN group ${key}: ${group.aspect_ids.join(", ")}. Read run://${path.basename(home)}/03-plan/${protocolId}/aspect-map.json and .memory-bank/protocol/${protocolId}/plan.json under workspace_root. Cite the PLAN using that workspace-relative path, not a run:// ref. Try to falsify ambiguous behavior and missing verification. For every selected check that starts fixture/reset, service, or client processes, verify that they share the intended data/configuration world and that any runtime entrypoint requiring change is explicit in a Work task and verification and ordered before its consumer. planned_write_areas are coordination hints, not ownership. Do not edit plan or product files. Return one aspect entry for every assigned aspect, with exact evidence references and concise findings. Finish with the JSON contract supplied in your work prompt.` });
289
397
  }
290
398
  }
291
399
  return groups;
@@ -328,10 +436,9 @@ export function capacityQualificationResponse(context, input) {
328
436
  return { ok: true, run_id: input.runId, stage, outcome: "qualified_capacity_required", next_action: "qualify_harness_profile_then_record_capacity", next: { record_command: capacityRecordCommand(context, input.runId, input.projectRoot, "<qualified-native-child-count>"), retry_command: dispatchCommand(context, input.runId, input.projectRoot) } };
329
437
  }
330
438
  function ensureReviewCapacity(context, input) {
331
- const variables = getFlowRunVariables(context, { projectRoot: input.projectRoot, runId: input.runId });
332
- const known = variables.variables[reviewCapacityKey];
333
- if (typeof known === "number" && Number.isInteger(known) && known >= 0)
334
- return { pending: false, availableSlots: known };
439
+ const capacity = getStageFanoutCapacity(context, { ...input, stage });
440
+ if (capacity.available_slots !== null)
441
+ return { pending: false, availableSlots: capacity.available_slots, source: capacity.source };
335
442
  return { pending: true, response: capacityQualificationResponse(context, { projectRoot: input.projectRoot, runId: input.runId }) };
336
443
  }
337
444
  function protocolIdsForReview(home) {
@@ -343,6 +450,65 @@ function markReviewPending(_home, _groups) { }
343
450
  /** Review evidence is immutable child Work output plus the stage decision. */
344
451
  function applyReviewResults(_home, _children, _decision) { }
345
452
  function canonicalReviewerFindings(children) { return children.flatMap((child) => (reviewerResult(child.result)?.aspects ?? []).flatMap((aspect) => aspect.findings.map((finding) => ({ finding_ref: `${child.work_id}/${finding.finding_id}`, finding })))); }
453
+ export function validatePlanFindingDecisions(items, knownRefs, workspaceRoot) {
454
+ const known = new Set(knownRefs), decisions = new Map(items.map(item => [item.finding_ref, item]));
455
+ if (decisions.size !== items.length || items.some(item => !item.finding_ref || !known.has(item.finding_ref)))
456
+ throw new AppError("review_evidence_invalid", "PLAN decisions require unique known finding references", 2);
457
+ if (items.some(item => item.decision === "requires_user"))
458
+ throw new AppError("stage_pause_required", "Resolve the user question with stage pause/resume before accepting PLAN-REVIEW", 2);
459
+ for (const item of items.filter(item => item.decision === "duplicate")) {
460
+ let cursor = item;
461
+ const seen = new Set();
462
+ while (cursor?.decision === "duplicate") {
463
+ if (!cursor.finding_ref || seen.has(cursor.finding_ref) || !cursor.duplicate_of || !known.has(cursor.duplicate_of) || cursor.duplicate_of === cursor.finding_ref)
464
+ throw new AppError("review_evidence_invalid", "PLAN duplicates must form an acyclic chain of distinct known findings", 2);
465
+ seen.add(cursor.finding_ref);
466
+ cursor = decisions.get(cursor.duplicate_of);
467
+ }
468
+ if (!cursor)
469
+ throw new AppError("review_evidence_invalid", "A PLAN duplicate must end at a final decided finding", 2);
470
+ }
471
+ return items.filter(item => item.decision === "deferred_as_DEF").map(item => reviewDefEvidence(workspaceRoot, item.def_id));
472
+ }
473
+ function planContentHash(plan) {
474
+ const content = { ...plan };
475
+ delete content.revision;
476
+ return crypto.createHash("sha256").update(JSON.stringify(content, (_key, value) => value && typeof value === "object" && !Array.isArray(value) ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b))) : value)).digest("hex");
477
+ }
478
+ export function validatePlanCorrection(input) {
479
+ const changed = [];
480
+ if (!input.sources.length || !input.maps?.length)
481
+ throw new AppError("review_input_contract_missing", "PLAN correction needs its accepted per-protocol sources", 1);
482
+ const invalid = (message) => { throw new AppError("validation", message, 2, { phase: "prepare", effect: "no_effect", recoverable: true }); };
483
+ for (const source of input.sources) {
484
+ const relative = `.memory-bank/protocol/${source.protocol_id}/plan.json`;
485
+ const file = assertPortableArtifactRef(relative, { workspaceRoot: input.workspaceRoot, runHome: input.home, runId: path.basename(input.home) });
486
+ const current = readJson(file);
487
+ if (current.plan_id !== source.plan_id || current.protocol_id !== source.protocol_id)
488
+ invalid("PLAN correction cannot change protocol identity");
489
+ if (checksum(file) !== source.sha256) {
490
+ if (!Number.isSafeInteger(current.revision) || Number(current.revision) <= source.revision || planContentHash(current) === source.content_sha256)
491
+ invalid("A changed PLAN must advance its own revision and contain a real authored change");
492
+ changed.push(relative);
493
+ }
494
+ }
495
+ for (const map of input.maps) {
496
+ const file = assertPortableArtifactRef(`run://${path.basename(input.home)}/${map.path}`, { workspaceRoot: input.workspaceRoot, runHome: input.home, runId: path.basename(input.home) });
497
+ if (checksum(file) !== map.sha256)
498
+ changed.push(`RUN/${map.path}`);
499
+ }
500
+ const correction = input.decision.correction;
501
+ const actual = [...new Set(changed)].sort();
502
+ const named = correction.changed_paths.map(ref => ref.startsWith(`run://${path.basename(input.home)}/`) ? `RUN/${ref.slice(`run://${path.basename(input.home)}/`.length)}` : ref).sort();
503
+ if (new Set(named).size !== named.length || JSON.stringify(actual) !== JSON.stringify(named))
504
+ invalid("Correction changed_paths must exactly name the changed PLAN/aspect-map artifacts");
505
+ if (actual.length) {
506
+ if (correction.status !== "applied" || correction.previous_plan_revision !== input.reviewedPlanRevision)
507
+ invalid("An authored correction requires an applied receipt bound to the reviewed revision");
508
+ }
509
+ else if (correction.status !== "not_required" || input.decision.finding_decisions.some(item => item.decision === "accepted_fix"))
510
+ invalid("An accepted fix needs a real authored correction; unchanged rejected findings need not_required");
511
+ }
346
512
  function resolveMode(requested, groups, home, projectRoot) {
347
513
  if (requested !== "auto")
348
514
  return requested;
@@ -381,17 +547,18 @@ catch {
381
547
  } }
382
548
  function rootWorkId(context, projectId, runId) { const root = context.db.get("SELECT work_id FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id IS NULL ORDER BY created_at LIMIT 1", [projectId, runId]); if (!root)
383
549
  throw new AppError("runtime_missing", "vNext RUN has no root Work", 1); return root.work_id; }
384
- function registerCode(context, projectId, runId, batch) { return addWorkBatch(context, { parentWorkId: rootWorkId(context, projectId, runId), file: batch }); }
385
- function readDecision(context, projectRoot, runId, runRoot, file) { validateSchema({ schemaName: "plan-review-decision", file, projectRoot, runId, runRoot, ddFlowHome: context.ddFlowHome }); return readJson(file); }
386
- function preserveDecisionReceipt(root, source) {
550
+ function readDecision(context, projectRoot, runId, runRoot, file) { return prepareJsonSchema(file, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, runId, runRoot, ddFlowHome: context.ddFlowHome }).value; }
551
+ function prepareDecisionReceipt(root, decision, preparedText) {
387
552
  const receipt = path.join(root, "decision.receipt.json");
388
- const bytes = fs.readFileSync(source);
553
+ // The CLI has already parsed and validated this decision during prepare.
554
+ // Persist that immutable value rather than re-opening a caller-controlled
555
+ // filename after lifecycle admission.
556
+ const bytes = Buffer.from(preparedText ?? `${JSON.stringify(decision, null, 2)}\n`);
389
557
  if (fs.existsSync(receipt)) {
390
558
  if (!bytes.equals(fs.readFileSync(receipt)))
391
559
  throw new AppError("decision_receipt_conflict", "The immutable PLAN-REVIEW decision receipt already contains a different payload", 2, { receipt });
392
- return;
393
560
  }
394
- fs.writeFileSync(receipt, bytes);
561
+ return prepareStagePacket(receipt, bytes);
395
562
  }
396
563
  function reviewerResult(result) { try {
397
564
  const value = JSON.parse(result ?? "");
@@ -423,8 +590,11 @@ function reviewerEvidenceFailures(context, input) {
423
590
  continue;
424
591
  }
425
592
  const actual = result.aspects.map((item) => item.aspect_id);
426
- if (actual.length !== group.aspect_ids.length || new Set(actual).size !== actual.length || group.aspect_ids.some((id) => !actual.includes(id)) || result.aspects.some((item) => item.evidence_refs.length === 0))
593
+ if (actual.length !== group.aspect_ids.length || new Set(actual).size !== actual.length || group.aspect_ids.some((id) => !actual.includes(id)))
427
594
  failures.push({ group: group.key, work_id: child.work_id, error: "reviewer_aspect_coverage_invalid", expected_aspects: group.aspect_ids, actual_aspects: actual });
595
+ const missingEvidence = result.aspects.filter((item) => item.evidence_refs.length === 0).map((item) => item.aspect_id);
596
+ if (missingEvidence.length)
597
+ failures.push({ group: group.key, work_id: child.work_id, error: "reviewer_aspect_evidence_missing", missing_aspects: missingEvidence });
428
598
  for (const ref of result.aspects.flatMap((item) => [...item.evidence_refs, ...item.findings.flatMap((finding) => finding.evidence_refs)])) {
429
599
  try {
430
600
  assertPortableArtifactRef(ref, { workspaceRoot: input.workspaceRoot, runHome: input.runHome, runId: input.runId });
@@ -448,37 +618,24 @@ function settleReviewParent(context, input) {
448
618
  context.db.run("UPDATE work_sessions SET status = ?, result_path = COALESCE(?, result_path), completed_at = ?, updated_at = ? WHERE id = ?", [input.status, input.resultPath ?? null, now, now, workSession.id]);
449
619
  }
450
620
  function finishTerminalReview(context, input) {
451
- const now = context.now();
621
+ const now = input.now;
452
622
  const childStatus = input.decision.outcome === "cancelled" ? "cancelled" : "failed";
453
- // A terminal decision is structured cancellation of the review subtree. It
454
- // must settle semantic reviewers before its parent Work can settle; otherwise
455
- // the RUN contains abandoned active Work/Session links.
456
- settleReviewChildren(context, { projectId: input.projectId, runId: input.run.id, parentWorkId: input.parentWorkId, status: childStatus, reason: `PLAN-REVIEW terminal decision: ${input.decision.summary}`, now });
457
623
  settleReviewParent(context, { projectRoot: input.projectRoot, projectId: input.projectId, runId: input.run.id, parentWorkId: input.parentWorkId, status: childStatus, result: input.decision.summary, reason: `plan_review_${input.decision.outcome}`, resultPath: path.join(input.root, "stage-report.json") });
458
624
  const rootWork = rootWorkId(context, input.projectId, input.run.id);
459
625
  context.db.run("UPDATE works SET status = ?, result = ?, completed_at = ?, updated_at = ? WHERE work_id = ? AND status = 'running'", [childStatus, input.decision.summary, now, now, rootWork]);
460
626
  closeRunningWorkSession(context, rootWork, childStatus, now);
461
- const settledChildren = context.db.all("SELECT work_id, task, status, result, created_at FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ?", [input.projectId, input.run.id, input.parentWorkId]);
462
- const report = reportFor({ run: input.run, mode: input.contextFile.system?.effective_mode ?? "standard", requested: input.contextFile.system?.requested_mode ?? "auto", outcome: input.decision.outcome, groups: input.contextFile.groups ?? [], batchChecksum: input.contextFile.system?.batch_checksum ?? checksum(input.batch), planChecksum: input.contextFile.system?.plan_checksum ?? planSetChecksum(requireHome(input.run), input.run.workspace_root), code: {}, now, projectRoot: input.projectRoot, decision: input.decision, children: latestReviewChildren(settledChildren), flow: flowCommand(context) });
463
- writeReport(input.root, report);
627
+ acceptStagePacket(context, { projectId: input.projectId, runId: input.run.id, stage, prepared: input.decisionReceipt });
628
+ acceptStageReport(context, { projectId: input.projectId, runId: input.run.id, stage, root: input.root, prepared: input.preparedReport });
629
+ completeFlowRunStage(context, input.completionInput, input.preparedCompletion);
464
630
  refreshRunWorkProjection(context, input.projectId, input.run.id);
465
- const stageStatus = input.decision.outcome === "cancelled" ? "skipped" : input.decision.outcome === "failed" ? "failed" : "blocked";
466
- completeFlowRunStage(context, { projectRoot: input.projectRoot, runId: input.run.id, stage, status: stageStatus, data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html" });
467
631
  completeFlowRun(context, { projectRoot: input.projectRoot, runId: input.run.id, status: input.decision.outcome === "cancelled" ? "cancelled" : input.decision.outcome === "failed" ? "failed" : "blocked", verdict: `plan_review_${input.decision.outcome}`, nextAction: "resolve_plan_review_blocker" });
468
632
  appendFlowRunTimelineEvent(context, input.projectId, input.run.id, { type: "plan_review_terminal", outcome: input.decision.outcome, work_id: input.parentWorkId });
469
633
  return { ok: true, run_id: input.run.id, stage, outcome: input.decision.outcome, next_action: "resolve_plan_review_blocker" };
470
634
  }
471
- function settleReviewChildren(context, input) {
472
- const active = context.db.all("SELECT work_id FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ? AND status IN ('created', 'running')", [input.projectId, input.runId, input.parentWorkId]);
473
- for (const child of active) {
474
- context.db.run("UPDATE works SET status = ?, result = ?, completed_at = ?, updated_at = ? WHERE work_id = ?", [input.status, input.reason, input.now, input.now, child.work_id]);
475
- closeRunningWorkSession(context, child.work_id, input.status, input.now);
476
- }
477
- }
478
635
  function closeRunningWorkSession(context, workId, status, now) {
479
636
  context.db.run("UPDATE work_sessions SET status = ?, completed_at = ?, updated_at = ? WHERE work_id = ? AND status = 'running'", [status, now, now, workId]);
480
637
  }
481
- function reportFor(input) {
638
+ function reportFor(context, input) {
482
639
  const accepted = input.outcome === "accepted" || input.outcome === "off";
483
640
  const result = input.outcome === "off" ? "Independent PLAN review was explicitly or automatically skipped." : accepted ? "Independent PLAN review accepted the plan." : `PLAN-REVIEW stopped with outcome: ${input.outcome}.`;
484
641
  const decision = input.decision;
@@ -487,27 +644,25 @@ function reportFor(input) {
487
644
  const sessionCommand = `${input.flow} stat run sessions ls --run ${input.run.id} --project-root ${JSON.stringify(input.projectRoot)} --json`;
488
645
  const usageCommand = `${input.flow} stat usage --run ${input.run.id} --project-root ${JSON.stringify(input.projectRoot)} --json`;
489
646
  const changedFiles = correction?.status === "applied" ? correction.changed_paths : [];
490
- return { schema_id: "dd-flow/stage-report@1", run_id: input.run.id, stage, generated_at: input.now, verdict: accepted ? "done" : input.outcome, semantic: { result, acceptance: [input.outcome], changed_files: changedFiles, checks: ["accepted PLAN report", "generated CODE batch", "reviewer evidence", ...(correction ? ["mechanical correction receipt"] : [])], evidence: [], next_action: accepted ? "start_code" : "resolve_plan_review_blocker", plan_review: { requested_mode: input.requested, effective_mode: input.mode, outcome: input.outcome, groups: input.groups.map((group) => group.key), initial_plan_revision: input.initialPlanRevision ?? null, final_plan_revision: input.finalPlanRevision ?? input.initialPlanRevision ?? null, initial_plan_checksum: input.planChecksum, final_plan_checksum: input.finalPlanChecksum ?? input.planChecksum, initial_batch_checksum: input.batchChecksum, final_batch_checksum: input.finalBatchChecksum ?? input.batchChecksum, correction, decision: input.decision ?? null, reviewer_works: input.children?.map((child) => ({ work_id: child.work_id, status: child.status, verdict: reviewerVerdict(child.result) })) ?? [] }, plan_checksum: input.finalPlanChecksum ?? input.planChecksum, batch_checksum: input.finalBatchChecksum ?? input.batchChecksum, code: input.code }, mechanical: { started_at: timing.started_at, finished_at: input.now, wall_clock_ms: timing.wall_clock_ms, git: gitFacts(input.run.workspace_root), session_stats_command: sessionCommand, usage_stats_command: usageCommand, ...(accepted ? { next_command: `${input.flow} stage start ${input.run.id} --stage code --project-root ${JSON.stringify(input.projectRoot)} --json` } : {}) }, artifacts: { json: "stage-report.json", markdown: "stage-report.md", html: "stage-report.html", summary: "stage-report.md" }, validation: { permission_scope: "known_targets_only", memory_bank_scope: "changed_files_and_links_only", status: "passed" } };
647
+ const nextCommand = accepted ? input.nextCommand : undefined;
648
+ return { schema_id: "dd-flow/stage-report@1", run_id: input.run.id, stage, generated_at: input.now, verdict: accepted ? "done" : input.outcome, semantic: { result, acceptance: [input.outcome], changed_files: changedFiles, checks: ["accepted PLAN report", "generated CODE batch", "reviewer evidence", ...(correction ? ["mechanical correction receipt"] : [])], evidence: [], next_action: accepted ? "start_code" : "resolve_plan_review_blocker", plan_review: { requested_mode: input.requested, effective_mode: input.mode, outcome: input.outcome, groups: input.groups.map((group) => group.key), initial_plan_revision: input.initialPlanRevision ?? null, final_plan_revision: input.finalPlanRevision ?? input.initialPlanRevision ?? null, initial_plan_checksum: input.planChecksum, final_plan_checksum: input.finalPlanChecksum ?? input.planChecksum, initial_batch_checksum: input.batchChecksum, final_batch_checksum: input.finalBatchChecksum ?? input.batchChecksum, correction, decision: input.decision ?? null, reviewer_works: input.children?.map((child) => ({ work_id: child.work_id, status: child.status, verdict: reviewerVerdict(child.result) })) ?? [] }, plan_checksum: input.finalPlanChecksum ?? input.planChecksum, batch_checksum: input.finalBatchChecksum ?? input.batchChecksum, code: input.code }, mechanical: { started_at: timing.started_at, finished_at: input.now, wall_clock_ms: timing.wall_clock_ms, git: input.git ?? gitFacts(input.run.workspace_root), session_stats_command: sessionCommand, usage_stats_command: usageCommand, ...(nextCommand ? { next_command: nextCommand } : {}) }, artifacts: { json: "stage-report.json", markdown: "stage-report.md", html: "stage-report.html", summary: "stage-report.md" }, validation: { permission_scope: "known_targets_only", memory_bank_scope: "changed_files_and_links_only", status: "passed" } };
491
649
  }
492
650
  function stageTiming(run, now) { const started = readIndex(run).stage_runs?.find((item) => item.stage === stage && typeof item.started_at === "string"); const startedAt = started?.started_at ?? now; const milliseconds = Date.parse(now) - Date.parse(startedAt); return { started_at: startedAt, wall_clock_ms: Number.isFinite(milliseconds) && milliseconds >= 0 ? milliseconds : null }; }
493
- function writeReport(root, report) {
494
- writeStageReport(root, report);
495
- }
496
- function codeCommand(context, runId, projectRoot) { return `${flowCommand(context)} stage start ${runId} --stage code --project-root ${JSON.stringify(projectRoot)} --json`; }
651
+ function codeSuccessor(context, runId, projectRoot) { return stageSuccessor(context, { projectRoot, runId, stage: "code", command: `${flowCommand(context)} stage start ${runId} --stage code --project-root ${JSON.stringify(projectRoot)} --json` }); }
497
652
  function dispatchCommand(context, runId, projectRoot) { return `${flowCommand(context)} plan-review dispatch ${runId} --project-root ${JSON.stringify(projectRoot)} --json`; }
498
- function finishCommand(context, runId, projectRoot, decision) { return `${flowCommand(context)} stage finish ${runId} --stage plan-review --project-root ${JSON.stringify(projectRoot)} --decision-file ${JSON.stringify(decision)} --json`; }
653
+ function finishCommand(context, runId, projectRoot, decision) { return managedLifecycleCommand(context, `${flowCommand(context)} stage finish ${runId} --stage plan-review --project-root ${JSON.stringify(projectRoot)} --decision-file ${JSON.stringify(decision)} --json`); }
499
654
  function capacityRecordCommand(context, runId, projectRoot, availableSlots) { return `${flowCommand(context)} run capacity record ${runId} --available-slots ${availableSlots} --project-root ${JSON.stringify(projectRoot)} --json`; }
500
655
  function requireRun(context, projectRoot, runId) { const project = requireProjectByRoot(context, projectRoot); const run = context.db.get("SELECT id, project_id, workspace_root, run_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]); if (!run)
501
656
  throw new AppError("not_found", "RUN is not registered", 1); return run; }
502
657
  function requireHome(run) { if (!run.run_root)
503
658
  throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1); return run.run_root; }
504
659
  function read(file) { if (!fs.existsSync(file))
505
- throw new AppError("not_found", "Required vNext prompt is missing", 1, { file }); return fs.readFileSync(file, "utf8"); }
660
+ throw new AppError("runtime_artifact_missing", "Required vNext prompt is missing", 1, { file }); return fs.readFileSync(file, "utf8"); }
506
661
  function readJson(file) { try {
507
662
  return JSON.parse(fs.readFileSync(file, "utf8"));
508
663
  }
509
664
  catch {
510
- throw new AppError("validation", "Expected valid JSON artifact", 2, { file });
665
+ throw new AppError("runtime_artifact_invalid", "Expected valid retained JSON artifact", 1, { file });
511
666
  } }
512
667
  function writeJson(file, value) { const temporary = `${file}.${crypto.randomUUID()}.tmp`; fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`); fs.renameSync(temporary, file); }
513
668
  function checksum(file) { return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); }