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

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 +965 -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 +257 -110
  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,57 +1,145 @@
1
1
  import fs from "node:fs";
2
+ import { ensureManagedWorktrunk } from "../services/tooling.js";
3
+ import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
4
+ import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
5
+ import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
6
+ import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
7
+ import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget, currentAssignedLifecycle } from "../services/lifecycle-invocations.js";
2
8
  import path from "node:path";
9
+ import { createHash, randomUUID } from "node:crypto";
10
+ import { quote } from "shell-quote";
11
+ import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
12
+ import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
13
+ import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
14
+ import { readAcceptedAssignedWork } from "../services/assigned-lifecycle.js";
3
15
  import { createContext, createRouterContext } from "../runtime/context.js";
4
- import { helpForArgs } from "./help.js";
5
- import { AppError, isAppError } from "../shared/errors.js";
6
- import { writeJson } from "../shared/json.js";
7
- import { archiveProject, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
8
- import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, requireProtocol, syncProtocolFromRun, transitionProtocol } from "../services/protocols.js";
9
- import { blockPlanItem, completePlanItem, getPlanStatus, skipPlanItem, startPlanItem } from "../services/plans.js";
10
- import { cancelMergeQueueJob, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
16
+ import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
17
+ import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
18
+ import { migrateStoreWriter } from "../storage/writer-migration.js";
19
+ import { resolveHelp } from "./help.js";
20
+ import { renderHumanError, writePreparationError } from "./error-output.js";
21
+ import { runOperatorProfilesCli } from "./operator-profiles.js";
22
+ import { runSetupCli } from "./setup.js";
23
+ import { runCompletionCli } from "./completion-cli.js";
24
+ import { runHarnessesCli } from "./harnesses.js";
25
+ import { runModelsCli } from "./models.js";
26
+ import { parseOutputOptions } from "./output-options.js";
27
+ import { AppError, errorRecord, isAppError } from "../shared/errors.js";
28
+ import { entityReferenceVariants } from "../shared/entity-references.js";
29
+ import { writeJson, parseJsonObject } from "../shared/json.js";
30
+ import { archiveProject, prepareProjectArchive, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
31
+ import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, prepareProtocolRegistration, requireProtocol, syncProtocolFromRun, prepareProtocolTransition, transitionProtocol } from "../services/protocols.js";
32
+ import { getPlanStatus, preparePlanItemMutation, updatePlanItem } from "../services/plans.js";
33
+ import { cancelMergeQueueJob, prepareMergeQueueCancellation, prepareMergeQueueWait, prepareMergeClaim, prepareMergeBundleClaim, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, prepareMergeBundleCompletion, completeMergeJob, prepareMergeJobCompletion, failMergeBundle, prepareMergeBundleFailure, failMergeJob, prepareMergeJobFailure, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, prepareMergeJobNote, waitNextMergeJob } from "../services/merge-queue.js";
11
34
  import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
12
- import { cleanupApply, cleanupScan } from "../services/cleanup.js";
13
- import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
14
- import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
15
- import { acquireLaneLock, cancelLaneWaiter, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
16
- import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
17
- import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
35
+ import { cleanupApply, cleanupScan, prepareCleanupApply } from "../services/cleanup.js";
36
+ import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleDroidEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeHook, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, stageLifecycleMatchKey, initCodexHome, prepareCodexHome, prepareCodexHooks, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, prepareCodexHomeRemoval, removeCodexHooks } from "../services/hooks.js";
37
+ import { bootstrapWorktree, closeWorktree, createWorktreeRecord, prepareWorktreeCreate, prepareWorktreeBootstrap, prepareWorktreeClose, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
38
+ import { acquireLaneLock, cancelLaneWaiter, prepareLaneWaiterCancellation, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, prepareLaneWorkspace, prepareLaneLock, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
39
+ import { getProjectConfigStatus, prepareProjectConfigValue, setProjectConfigValue } from "../services/config.js";
40
+ import { getCanonStatus, registerCanonRoot, resolveCanonRoot, prepareCanonRegistration } from "../services/canon.js";
18
41
  import { getRuntimeStatus } from "../services/status.js";
19
42
  import { getCliVersionReport } from "../services/build-info.js";
20
43
  import { parseCommandArgs } from "../services/lifecycle-command.js";
21
- import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
44
+ import { prepareError, prepareJsonFile, prepareJsonSchema, prepareJsonText, prepareTextFile, prepareFileBytes, readBoundedUtf8Stream, recordConsumedInputDigest, withPreparedInputBinding } from "./input-preparation.js";
45
+ import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, prepareDashboardRefresh, prepareDashboardRender, prepareGlobalDashboardRender, prepareDashboardOptions, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
22
46
  import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
23
- import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
47
+ import { getFlowSessionStatus, prepareFlowSessionPayload, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
24
48
  import { getProtocolBranchContext } from "../services/branch-context.js";
25
- import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, setFlowRunVariable, startFlowRun } from "../services/runs.js";
49
+ import { attachFlowRunStage, readFlowRunExecutionBundle, prepareFlowRunStageAttachment, prepareRawFlowRunStageCompletion, completeFlowRun, prepareFlowRunCompletion, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, prepareFlowRunConfig, prepareFlowRunVariable, setFlowRunVariable, startFlowRun, resolveRun, prepareFlowRunStart } from "../services/runs.js";
26
50
  import { previewNextEntityId } from "../services/ids.js";
27
51
  import { validateSchema } from "../services/schema-validation.js";
28
52
  import { preflightMemoryPermissions } from "../services/memory-permissions.js";
29
- import { ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
30
- import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, verifyMigrationReport } from "../services/migrations.js";
53
+ import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
54
+ import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
55
+ import { PreparedUpgradeStorageReadiness } from "../services/memory-upgrade-storage-readiness.js";
31
56
  import { publishProjectSummary } from "../services/project-summary.js";
32
57
  import { requireProjectByRoot } from "../services/projects.js";
33
- import { resolveProjectRoot } from "../storage/paths.js";
34
- import { doctorEngines, bindRunEngine, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
58
+ import { resolveProjectRoot, resolveDdFlowHome } from "../storage/paths.js";
59
+ import { doctorEngines, bindRunEngine, prepareRunEngineBind, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
35
60
  import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
36
- import { finishVnextSpecifyStage, getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, launchVnextSpecify } from "../services/vnext-specify.js";
37
- import { finishVnextProtocolize, isVnextProtocolizeRun, startVnextProtocolize } from "../services/vnext-protocolize.js";
38
- import { finishVnextPlan, isVnextPlanRun, startVnextPlan } from "../services/vnext-plan.js";
39
- import { dispatchVnextPlanReview, finishVnextPlanReview, isVnextPlanReviewRun, recordVnextPlanReviewCapacity, startVnextPlanReview } from "../services/vnext-plan-review.js";
40
- import { addVnextCodeRepair, finishVnextCode, startVnextCode } from "../services/vnext-code.js";
41
- import { addVnextCodeReviewRepair, finishVnextCodeReview, isVnextCodeReviewRun, startVnextCodeReview } from "../services/vnext-code-review.js";
42
- import { applyVnextMerge, finishVnextMerge, getVnextMergeRequest, isVnextMergeRun, repairVnextMerge, routeVnextMergeRequest, startVnextMerge } from "../services/vnext-merge.js";
61
+ import { getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, prepareVnextSpecifyStart } from "../services/vnext-specify.js";
62
+ import { isVnextProtocolizeRun, prepareProtocolizeResultPath, prepareVnextProtocolizeStart } from "../services/vnext-protocolize.js";
63
+ import { isVnextPlanRun, prepareVnextPlanStart, prepareVnextPlanFinish } from "../services/vnext-plan.js";
64
+ import { dispatchVnextPlanReview, isVnextPlanReviewRun, prepareVnextPlanReviewStart, recordVnextPlanReviewCapacity } from "../services/vnext-plan-review.js";
65
+ import { addVnextCodeRepair, prepareVnextCodeRepair, prepareVnextCodeStart } from "../services/vnext-code.js";
66
+ import { isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart } from "../services/vnext-code-review.js";
67
+ import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest } from "../services/vnext-merge.js";
68
+ import { sdlcStageHandlers } from "../services/stage-handlers.js";
69
+ import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
70
+ import { startDiagnosticStage, finishDiagnosticStage } from "../services/diagnostic-stage-handlers.js";
71
+ import { startDistillStage, finishDistillStage } from "../services/distill-stage-handlers.js";
72
+ import { startMemoryFixStage } from "../services/memory-fix-stage-entry.js";
73
+ import { finishMemoryFixStage } from "../services/memory-fix-stage-handlers.js";
74
+ import { startReviewFixStage } from "../services/review-fix-stage-entry.js";
75
+ import { finishReviewFixStage } from "../services/review-fix-stage-handlers.js";
76
+ import { startInteractiveStage } from "../services/interactive-stage-entry.js";
77
+ import { isDeliveryFlow } from "../domain/delivery-flows.js";
78
+ import { startDeliveryStage, readDeliveryStageScope, assertDeliveryCommandOwner } from "../services/delivery-stage-entry.js";
79
+ import { finishDeliveryStage } from "../services/delivery-stage-handlers.js";
80
+ import { prepareDeliveryChecks } from "../services/delivery-checks.js";
81
+ import { executeDeliveryOperation } from "../services/delivery-operation.js";
82
+ import { observeDeliveryResult } from "../services/delivery-result.js";
83
+ import { prepareDeliveryProjectBinding } from "../services/delivery-policy.js";
84
+ import { deliveryApprovalScope } from "../services/delivery-approval.js";
85
+ import { issueDeliveryStageCommands } from "../services/delivery-stage-commands.js";
86
+ import { readDescriptorStageInput } from "../services/descriptor-stage-coordinator.js";
87
+ import { readAcceptedStageData } from "../services/stage-report-renderer.js";
88
+ import { finishInteractiveStage, registerInteractiveReviewCorrection, registerInteractiveCorrectionReview } from "../services/interactive-stage-handlers.js";
89
+ import { observeInteractiveOriginalGitInputs } from "../services/interactive-finish-integration.js";
90
+ import { readInteractiveUserInputs } from "../services/interactive-user-inputs.js";
91
+ import { deliverInteractiveUserInput } from "../services/interactive-input-delivery.js";
92
+ import { PreparedInteractiveMutationScope } from "../services/interactive-mutation-scope.js";
93
+ import { PreparedInteractiveChecks } from "../services/interactive-checks.js";
94
+ import { registerInteractiveStageGraph } from "../services/interactive-work-registration.js";
95
+ import { readInteractiveWorkGit } from "../services/interactive-work-start.js";
96
+ import { integrateInteractiveFinish } from "../services/interactive-git-dispatch.js";
97
+ import { readMemoryFixSourceIntake } from "../services/memory-fix-intake-custody.js";
98
+ import { readMemoryFixUserInputs } from "../services/diagnostic-repair-intake.js";
99
+ import { prepareMemoryFixScopeEvidence } from "../services/memory-fix-scope.js";
100
+ import { prepareMemoryFixPublication } from "../services/memory-fix-publication.js";
101
+ import { integrateMemoryFixFinish } from "../services/memory-fix-git-dispatch.js";
102
+ import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
103
+ import { bindStageCoordinatorWork } from "../services/work-registry.js";
104
+ import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
105
+ import { publishDistillFromStage } from "../services/distill-promotion.js";
106
+ import { readDistillUserInputs } from "../services/distill-user-inputs.js";
107
+ import { hybridDigest } from "../services/hybrid-contracts.js";
108
+ import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
109
+ import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
110
+ import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
111
+ import { PreparedRetainedMemoryReviewResults } from "../services/memory-retained-review-results.js";
112
+ import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFromStage, runMemoryCorrectionChecksFromStage, runMemorySecondCorrectionChecksFromStage, registerCorrectedMemoryReviewFromStage, registerSecondCorrectedMemoryReviewFromStage, collectCorrectedMemoryReviewFromStage, collectSecondCorrectedMemoryReviewFromStage } from "../services/memory-review-handler.js";
113
+ import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
114
+ import { registerMemoryRepairBaselineFromStage, registerMemorySecondRepairBaselineFromStage, reserveMemoryRepairWriterCopyFromStage, reserveMemorySecondRepairCopyFromStage, materializeMemoryRepairWriterCopyFromStage, materializeMemorySecondRepairCopyFromStage, prepareMemoryRepairDispatchFromStage, prepareMemorySecondRepairDispatchFromStage, registerMemoryRepairCurrentViewFromStage, registerMemorySecondRepairCurrentViewFromStage, registerMemoryRepairPublicationSourceFromStage, reserveMemoryRepairPublicationAgainstSourceFromStage, reserveMemorySecondRepairPublicationFromStage, applyMemoryRepairPublicationFromStage, applyMemorySecondRepairPublicationFromStage, finishMemoryRepairPublicationFromStage, finishMemorySecondRepairPublicationFromStage } from "../services/memory-review-handler.js";
43
115
  import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
44
- import { getVnextFanoutStatus } from "../services/vnext-fanout.js";
45
- import { pauseStageForUser, resumeStageAfterUser } from "../services/stage-pause.js";
46
- import { blockStageForRuntime, unblockStageAfterRuntimeRepair } from "../services/stage-blocker.js";
116
+ import { getVnextFanoutStatus, registeredStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
117
+ import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
118
+ import { runtimeCommand } from "../services/runtime-command.js";
119
+ import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
120
+ import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRuntimeBlock, prepareStageRuntimeUnblock } from "../services/stage-blocker.js";
47
121
  import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
48
- import { renderWorkerPrompt } from "../services/prompts.js";
49
- import { finishStage, startStage } from "../services/stage-lifecycle.js";
50
- import { addWorkBatch, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, retryWork, showWork, startWork } from "../services/work-registry.js";
51
- import { createEvalBootstrapSnapshot, createEvalRunSnapshot, prepareVnextSpecifyRun, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
52
- import { loadExternalStageContext } from "../services/stage-context.js";
53
- import { stopManagedProcess, confirmManagedProcess, finishManagedProcess, heartbeatManagedProcess, managedProcessStatus, reconcileExpiredManagedProcesses, registerManagedProcess } from "../services/managed-processes.js";
54
- import { startRuntimeService } from "../services/runtime-service.js";
122
+ import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
123
+ import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
124
+ import { addWorkBatch, assertGenericWorkBatchAllowed, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, workResultByteLimit, retryWork, showWork, startWork } from "../services/work-registry.js";
125
+ import { launchExternalWork } from "../services/external-work-launch.js";
126
+ import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
127
+ import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
128
+ import { acceptRunRecovery, beginRunRecovery, inspectRunRecovery, prepareRecoveryCapture, prepareRecoveryPayload, prepareRunRecoveryResume, recordRecoveryCapture, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
129
+ import { loadExternalStageContext, prepareExternalStageContext } from "../services/stage-context.js";
130
+ import { vnextStageDirectory } from "../domain/stage-catalog.js";
131
+ import { prepareCommandRoute } from "./command-inputs.js";
132
+ import { parseLifecycleCommand } from "../services/lifecycle-command.js";
133
+ import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
134
+ import { reconcileFlowRunHealth } from "../services/run-health.js";
135
+ import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
136
+ import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
137
+ import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
138
+ import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
139
+ import { launchRunController, prepareRunControllerLaunch, resumeRunController, serveRunController, runControllerStatus, answerRunController, prepareRunControllerAnswer, supplyRunControllerContext, prepareRunControllerContext } from "../services/run-controller.js";
140
+ import { controlLifecycleReply, runControlStatus } from "../services/run-control.js";
141
+ import { reconcileRunControl, requestRunControl, serveRunControl } from "../services/run-control-worker.js";
142
+ import { startRuntimeService, prepareRuntimeService } from "../services/runtime-service.js";
55
143
  const defaultIo = {
56
144
  stdout: process.stdout,
57
145
  stderr: process.stderr,
@@ -59,9 +147,62 @@ const defaultIo = {
59
147
  };
60
148
  const flagValue = "__dd_flow_flag__";
61
149
  export async function runCli(args, io = defaultIo, env = process.env) {
62
- const output = parseOutputOptions(args);
150
+ return withMaintenanceDiagnosticScope(() => runCliRequest(args, io, env));
151
+ }
152
+ async function runCliRequest(args, io, env) {
153
+ try {
154
+ const help = resolveHelp(args);
155
+ if (help) {
156
+ if (help.json)
157
+ writeJson(io.stdout, help.data);
158
+ else
159
+ io.stdout.write(`${help.text}\n`);
160
+ return 0;
161
+ }
162
+ }
163
+ catch (error) {
164
+ const record = errorRecord(error);
165
+ writePreparationError(io, args, record);
166
+ return record.exitCode;
167
+ }
168
+ let output;
169
+ try {
170
+ output = parseOutputOptions(args);
171
+ // Expand before routing/context selection, after stripping transport flags.
172
+ // The alias keeps the existing RUN's engine and prepared-store rules.
173
+ if (output.args[0] === "finish")
174
+ output.args = ["interactive", "finish", ...output.args.slice(1)];
175
+ }
176
+ catch (error) {
177
+ const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
178
+ Object.assign(rejection.details, { phase: "prepare", effect: "no_effect", recoverable: true, ...rejection.details });
179
+ try {
180
+ const command = quote(["dd-flow", ...args]);
181
+ const analysis = parseLifecycleCommand(command);
182
+ const event = analysis.kind !== "none" || analysis.auxiliary
183
+ ? observedLifecycleNativeEvent(createRouterContext(env), command, env.DD_FLOW_DAEMON_ID) : null;
184
+ if (event) {
185
+ const context = createContext({ ...env }, "hook");
186
+ try {
187
+ observeLifecycleCommand(context, command, event)({ error: rejection });
188
+ }
189
+ finally {
190
+ context.db.close?.();
191
+ }
192
+ }
193
+ }
194
+ catch (persistence) {
195
+ rejection.details.lifecycle_diagnostic_error = String(persistence);
196
+ }
197
+ writePreparationError(io, args, errorRecord(rejection));
198
+ return rejection.exitCode;
199
+ }
63
200
  const progress = progressForCommand(output.args);
64
201
  let heartbeat;
202
+ let invocationSettlement;
203
+ let settleHook;
204
+ let diagnosticHome;
205
+ let maintenanceDiagnostics;
65
206
  try {
66
207
  if (output.args.length === 1 && output.args[0] === "--version") {
67
208
  const payload = getCliVersionReport();
@@ -71,11 +212,72 @@ export async function runCli(args, io = defaultIo, env = process.env) {
71
212
  io.stdout.write(`dd-flow ${payload.cli.version}\n`);
72
213
  return 0;
73
214
  }
74
- const help = helpForArgs(output.args);
75
- if (help) {
76
- io.stdout.write(`${help}\n`);
215
+ if (output.args[0] === "completion")
216
+ return runCompletionCli(args, io);
217
+ if (output.args[0] === "setup")
218
+ return runSetupCli(args, env, io);
219
+ if (output.args[0] === "harnesses")
220
+ return runHarnessesCli(args, env, io);
221
+ if (output.args[0] === "models")
222
+ return runModelsCli(args, io);
223
+ if (output.args[0] === "profiles")
224
+ return runOperatorProfilesCli(args, env, io);
225
+ if (output.args[0] === "tool") {
226
+ prepareCommandRoute(output.args); // Reject unknown tools before filesystem/store effects.
227
+ if (output.responseFile)
228
+ prepareResponseDestination(output.responseFile);
229
+ const tool = await ensureManagedWorktrunk({ ddFlowHome: resolveDdFlowHome(env), env });
230
+ const payload = writeResponseFile({ ok: true, tool: "wt", ...tool, cache_root: path.dirname(tool.bin) }, output);
231
+ if (output.json)
232
+ writeJson(io.stdout, payload);
233
+ else
234
+ io.stdout.write(`${renderHumanResult(output.args, payload)}${tool.bin}\n`);
235
+ return 0;
236
+ }
237
+ const suppliedInvocationId = optionalOption(parseArgs(output.args.slice(2)), "invocation-id");
238
+ if (isNativeHookIngress(output.args)) {
239
+ writeJson(io.stdout, await dispatchNativeHook(output.args, await readStdin(io.stdin), env));
77
240
  return 0;
78
241
  }
242
+ if (output.args.slice(0, 3).join(" ") === "codex hook handle") {
243
+ const parsed = prepareCommandRoute(output.args).parsed;
244
+ const stdin = await readStdin(io.stdin);
245
+ const payload = parseJsonObject(stdin || "{}", "codex hook stdin");
246
+ const event = requiredOption(parsed, "event");
247
+ const isolatedHome = event === "PreToolUse" ? isolatedDdFlowHomeFromHookStdin(stdin) : null;
248
+ const hookEnv = isolatedHome ? { ...env, DD_FLOW_HOME: isolatedHome } : env;
249
+ const proof = await verifyCodexHookDelivery(hookEnv, payload);
250
+ let opened;
251
+ const mode = databaseModeForCommand(output.args, classifyCliOperation(output.args, hookEnv));
252
+ const context = {
253
+ env: hookEnv, ddFlowHome: resolveDdFlowHome(hookEnv), now: () => new Date().toISOString(),
254
+ get db() { return (opened ??= createContext(hookEnv, mode)).db; }
255
+ };
256
+ try {
257
+ // Retired events retain the explicit startup/migration path. PreToolUse
258
+ // only opens its selected prepared store when the handler needs it.
259
+ if (mode !== "hook")
260
+ opened = createContext(hookEnv, mode);
261
+ if (proof)
262
+ context.codexHookProof = proof;
263
+ const result = handleCodexHook(context, { event, projectRoot: optionalOption(parsed, "project-root"), stdin });
264
+ writeJson(io.stdout, result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result
265
+ ? { hookSpecificOutput: result.hookSpecificOutput } : {});
266
+ return 0;
267
+ }
268
+ finally {
269
+ opened?.db.close?.();
270
+ }
271
+ }
272
+ if (output.args[0] === "run" && ["drive", "control"].includes(output.args[1] ?? "")) {
273
+ const controlArgs = parseArgs(output.args.slice(2));
274
+ requiredOption(controlArgs, "run");
275
+ requiredOption(controlArgs, "project-root");
276
+ }
277
+ if (io.stdin?.isTTY && ["work", "stage"].includes(output.args[0] ?? "") && output.args[1] === "finish"
278
+ && hasOption(parseArgs(output.args.slice(2)), "result-stdin")) {
279
+ throw new AppError("usage", "--result-stdin requires non-interactive input. Pipe or redirect the complete JSON into this command, or use --result-file; interactive terminals can truncate long JSON and wait indefinitely for EOF.", 2);
280
+ }
79
281
  const classification = classifyCliOperation(output.args, env);
80
282
  if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
81
283
  throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
@@ -83,26 +285,205 @@ export async function runCli(args, io = defaultIo, env = process.env) {
83
285
  required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
84
286
  });
85
287
  }
86
- const routerContext = createRouterContext(env);
288
+ // Registration may bootstrap the resource registry, but never the unrelated
289
+ // RUN store. Its caller's absolute deadline also bounds that first open.
290
+ const maintenanceOptions = isRuntimeMaintenance(output.args) ? { resourceDatabaseMode: output.args[2] === "register" ? "initialize" : output.args[2] === "status" ? "read_existing" : "prepared_existing", ...(env.DD_FLOW_HOOK_DEADLINE_AT ? { databaseDeadlineAt: Number(env.DD_FLOW_HOOK_DEADLINE_AT) } : {}) } : {};
291
+ if (output.args[0] === "runtime" && output.args[1] === "scope" && ["fence", "stop"].includes(output.args[2] ?? ""))
292
+ maintenanceOptions.resourceDatabaseMode = "retirement";
293
+ // Keep ordinary CLI JSON/stderr compatibility; bounded maintenance callers
294
+ // provide this request's deadline and consume its structured phase evidence.
295
+ if (maintenanceOptions.databaseDeadlineAt !== undefined)
296
+ maintenanceDiagnostics = runtimeMaintenanceDiagnostics(output.args, maintenanceOptions.databaseDeadlineAt, line => io.stderr.write(line));
297
+ const routerContext = createRouterContext(env, maintenanceOptions.databaseDeadlineAt);
298
+ const publicCommand = quote(["dd-flow", ...output.args]);
299
+ const nativeCommand = quote(["dd-flow", ...args]);
300
+ const lifecycleCommand = parseLifecycleCommand(publicCommand);
301
+ if (lifecycleCommand.kind === "standalone")
302
+ validateLifecyclePathAliases(output.args, lifecycleCommand.invocation.operation);
303
+ const managedScope = lifecycleCommand.kind === "standalone" && env.DD_FLOW_DAEMON_ID ? cliManagedInvocationScope({ ...routerContext, env }) : null;
304
+ const nativeEvent = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
305
+ ? observedLifecycleNativeEvent(routerContext, nativeCommand, env.DD_FLOW_DAEMON_ID) : null;
306
+ const nativeEventKey = nativeEvent && routerContext.db.get("SELECT event_key FROM hook_events WHERE project_id = ? AND id = ?", [nativeEvent.project_id, nativeEvent.id])?.event_key;
307
+ const observedInvocation = managedScope ? observedLifecycleInvocation(routerContext, publicCommand, managedScope, nativeEventKey ? { eventKey: nativeEventKey } : undefined) : null;
308
+ if (managedScope && suppliedInvocationId
309
+ && routerContext.db.get("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'lifecycle_invocations'")
310
+ && routerContext.db.get("SELECT id FROM lifecycle_invocations WHERE id = ?", [suppliedInvocationId])) {
311
+ const suppliedScope = lifecycleInvocationScope({ ...routerContext, env }, suppliedInvocationId);
312
+ if (["projectRoot", "daemonId", "rootSessionId", "runId", "generation"].some(key => suppliedScope[key] !== managedScope[key])) {
313
+ throw new AppError("invocation_scope_mismatch", "Explicit lifecycle assignment differs from the current physical native owner", 1, { effect: "no_effect", recoverable: false });
314
+ }
315
+ }
316
+ const diagnostic = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
317
+ ? observedLifecycleDiagnostic(routerContext, publicCommand, env.DD_FLOW_DAEMON_ID, observedInvocation?.id) : null;
318
+ if (diagnostic && (!nativeEvent || Number(diagnostic.hook_event_id) > nativeEvent.id)) {
319
+ throw new AppError("lifecycle_shell_syntax_invalid", `Native observer rejected the shell form: ${String(diagnostic.reason ?? "unsupported shell composition")}. Write structured input to a file and run the returned dd-flow command separately.`, 2, { ...diagnostic, phase: "prepare", effect: "no_effect", recoverable: true });
320
+ }
321
+ let manualRecoveryHookEventKey;
322
+ if (!observedInvocation && !suppliedInvocationId && env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone") {
323
+ const receipt = nativeEvent && routerContext.db.get("SELECT event_key,harness,provider_session_id,parent_session_id,daemon_id,status FROM hook_events WHERE id = ? AND project_id = ?", [nativeEvent.id, nativeEvent.project_id]);
324
+ const manual = managedScope && receipt?.status === "observed" ? manualRecoveryInvocationScope({ ...routerContext, env }, managedScope.projectRoot, { daemonId: receipt.daemon_id, rootSessionId: receipt.provider_session_id, sessionId: receipt.provider_session_id, parentSessionId: receipt.parent_session_id, toolCallId: String(nativeEvent.id) }, receipt.harness) : null;
325
+ if (!manual || JSON.stringify(manual) !== JSON.stringify(managedScope))
326
+ throw new AppError(nativeEvent ? "invocation_assignment_missing" : "invocation_receipt_missing", nativeEvent ? "Native call was observed, but its arguments do not match an issued lifecycle assignment" : "Managed lifecycle command has no committed native receipt", 1, { effect: "no_effect", recoverable: false });
327
+ assertManualRecoveryInvocationTarget({ ...routerContext, env }, manual, publicCommand);
328
+ manualRecoveryHookEventKey = receipt.event_key;
329
+ }
330
+ const invocationId = observedInvocation?.id ?? suppliedInvocationId;
331
+ let boundInvocationArgs;
332
+ try {
333
+ boundInvocationArgs = observedInvocation
334
+ ? resolveObservedLifecycleArgs(routerContext, observedInvocation.id, output.args)
335
+ : invocationId && invocationId !== suppliedInvocationId
336
+ ? [...output.args.filter((_, index, values) => values[index - 1] !== "--invocation-id" && values[index] !== "--invocation-id"), "--invocation-id", invocationId]
337
+ : output.args;
338
+ }
339
+ catch (error) {
340
+ if (!(observedInvocation && error instanceof AppError))
341
+ throw error;
342
+ const rejectionContext = createContext({ ...env }, "hook");
343
+ try {
344
+ Object.assign(error.details, settleLifecyclePreparationRejection(rejectionContext, observedInvocation.id, error));
345
+ }
346
+ finally {
347
+ rejectionContext.db.close?.();
348
+ }
349
+ throw error;
350
+ }
351
+ const observedOperation = lifecycleCommand.kind === "standalone" ? lifecycleCommand.invocation.operation : null;
352
+ if (observedInvocation && !observedOperation)
353
+ throw new AppError("invocation_command_invalid", "Observed lifecycle command could not be parsed", 1);
354
+ const invocationScope = invocationId ? lifecycleInvocationScope({ ...routerContext, env }, invocationId) : undefined;
355
+ const expandedInvocationArgs = invocationScope && observedOperation
356
+ ? expandLifecycleInvocationArgs(routerContext, boundInvocationArgs, invocationScope, observedOperation)
357
+ : boundInvocationArgs;
358
+ const retainedOutput = observedInvocation ? parseOutputOptions(expandedInvocationArgs) : null;
359
+ if (retainedOutput?.json)
360
+ output.json = true;
361
+ if (retainedOutput?.progressJsonl)
362
+ output.progressJsonl = true;
363
+ if (retainedOutput?.responseFile)
364
+ output.responseFile = retainedOutput.responseFile;
365
+ let invocationArgs = retainedOutput?.args ?? expandedInvocationArgs;
87
366
  emitHumanProgress(io, output, progress, "start");
88
367
  if (output.progressJsonl) {
89
368
  writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
90
369
  heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
91
370
  }
92
- const scopeProjectRoot = resolveProjectRootBeforeRouting(output.args, env);
93
- const routedArgs = output.responseFile ? [...output.args, "--response-file", output.responseFile] : output.args;
371
+ // Route by retained authority, so a mistyped project path reaches the CLI
372
+ // validator instead of selecting a different engine or failing preflight.
373
+ const scopeProjectRoot = invocationScope?.projectRoot ?? resolveCommandProjectRoot(routerContext, output.args, resolveProjectRootBeforeRouting(output.args, env, maintenanceOptions.databaseDeadlineAt));
374
+ assertAuxiliaryCommandScope({ ...routerContext, env }, publicCommand, scopeProjectRoot);
375
+ invocationArgs = resolveCommandPaths(routerContext, invocationArgs, scopeProjectRoot);
376
+ const routedArgs = output.responseFile ? [...invocationArgs, "--response-file", output.responseFile] : invocationArgs;
94
377
  const routed = await routeArgsThroughEngine(routerContext, output.json ? [...routedArgs, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : routedArgs, io, io.stdin, env, scopeProjectRoot);
95
378
  if (routed?.routed && !routed.inProcess) {
96
379
  if (heartbeat)
97
380
  clearInterval(heartbeat);
98
- emitHumanProgress(io, output, progress, "done");
381
+ emitHumanProgress(io, output, progress, routed.exitCode === 0 ? "done" : "failed");
382
+ if (output.progressJsonl && routed.exitCode !== 0)
383
+ writeProgressJsonl(io, "failed", "Routed engine failed", { exit_code: routed.exitCode });
99
384
  return routed.exitCode;
100
385
  }
386
+ let preparedInput;
387
+ let preparedRoute;
388
+ if (!invocationId) {
389
+ const preparationContext = createContext({ ...env }, "read_existing", undefined, maintenanceOptions);
390
+ try {
391
+ preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
392
+ }
393
+ finally {
394
+ preparationContext.db.close?.();
395
+ }
396
+ }
101
397
  const dispatchContext = routed?.inProcess
102
- ? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(output.args, classification))
103
- : createContext(env, databaseModeForCommand(output.args, classification));
104
- preflightCliCompatibility(dispatchContext, output.args, env, scopeProjectRoot);
105
- const result = await dispatch(output.args, dispatchContext, io, scopeProjectRoot, classification, output);
398
+ ? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions)
399
+ : createContext({ ...env }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions);
400
+ diagnosticHome = dispatchContext.ddFlowHome;
401
+ dispatchContext.env.DD_FLOW_INVOCATION_READONLY = classification.mode === "read_only_diagnostics" ? "1" : undefined;
402
+ let dispatchArgs = invocationArgs;
403
+ let retainedOutcome;
404
+ // Issued transports may deliver their receipt asynchronously. Observe only
405
+ // after admission; retained outcomes must not need a fresh hook receipt.
406
+ if (invocationId) {
407
+ const command = quote(["dd-flow", ...invocationArgs]);
408
+ let nextNativeObservation = 0;
409
+ const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
410
+ validateArgs: () => {
411
+ try {
412
+ preparedRoute = prepareCommandRoute(invocationArgs);
413
+ }
414
+ catch (error) {
415
+ if (error instanceof AppError && error.code === "usage" && ["invocation-id", "hook-event-id"].includes(String(error.details.parameter ?? ""))) {
416
+ throw new AppError("lifecycle_contract_invalid", `The runtime generated arguments rejected by its own CLI contract: ${error.message}`, 1, { ...error.details, recoverable: false, operation: observedOperation });
417
+ }
418
+ throw error;
419
+ }
420
+ },
421
+ prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
422
+ assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output),
423
+ ...((env.DD_FLOW_RUNTIME_OWNER || env.DD_FLOW_CODEX_STATE_DIR || env.DD_AGY_STATE_DIR || env.DD_GROK_STATE_DIR || env.DD_ZCODE_STATE_DIR) ? { reconcileNativeObservation: async (scope, onProgress) => {
424
+ if (Date.now() < nextNativeObservation)
425
+ return;
426
+ nextNativeObservation = Date.now() + 250;
427
+ const { reconcileRetainedNativeHook } = await import("../services/retained-native-hook.js");
428
+ await reconcileRetainedNativeHook(dispatchContext, { scope, invocationId, onProgress,
429
+ onDiagnostic: value => io.stderr.write(`${JSON.stringify({ kind: "native_hook_diagnostic", ...value })}\n`) });
430
+ } } : {}) });
431
+ if (admission.replay) {
432
+ retainedOutcome = admission.outcome;
433
+ settleHook = undefined;
434
+ delete dispatchContext.observeLifecycleHook;
435
+ }
436
+ else {
437
+ invocationSettlement = { context: dispatchContext, id: invocationId };
438
+ if (observedInvocation)
439
+ retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
440
+ if (admission.kind === "native_hook") {
441
+ dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
442
+ preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
443
+ }
444
+ dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
445
+ dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
446
+ }
447
+ }
448
+ if (!invocationId && manualRecoveryHookEventKey) {
449
+ // Keep the physically scoped receipt selected by admission; a service must
450
+ // not rediscover a different daemon's matching command event.
451
+ if (!invocationArgs.includes("--hook-event-id"))
452
+ dispatchArgs = [...invocationArgs, "--hook-event-id", manualRecoveryHookEventKey];
453
+ preparedInput?.route?.parsed.options.set("hook-event-id", [manualRecoveryHookEventKey]);
454
+ }
455
+ if (retainedOutcome?.error) {
456
+ const error = retainedOutcome.error;
457
+ throw new AppError(error.code, error.message, error.exitCode, error.details);
458
+ }
459
+ if (!retainedOutcome && !preparedInput)
460
+ preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
461
+ if (!retainedOutcome)
462
+ await preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
463
+ if (!retainedOutcome) {
464
+ const command = !invocationId ? nativeCommand : quote(["dd-flow", ...dispatchArgs]);
465
+ const event = !invocationId ? nativeEvent : null;
466
+ settleHook = observeLifecycleCommand(dispatchContext, command, event ?? undefined);
467
+ }
468
+ const result = preparePromptDelivery(retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput));
469
+ if (!retainedOutcome && result && typeof result === "object" && "worker_prompt_markdown" in result && typeof result.worker_prompt_markdown === "string") {
470
+ try {
471
+ bindLifecyclePublishedPacket(dispatchContext, result.worker_prompt_markdown);
472
+ }
473
+ catch (cause) {
474
+ throw new AppError("invocation_packet_binding_failed", "Stage started but its published lifecycle assignments could not be retained", 1, { effect: "committed", cause: String(cause) });
475
+ }
476
+ }
477
+ const completedHook = settleHook;
478
+ settleHook = undefined;
479
+ completedHook?.({ result });
480
+ if (invocationSettlement) {
481
+ const completed = invocationSettlement;
482
+ invocationSettlement = undefined;
483
+ // A failure to persist an already completed result is an unknown reply,
484
+ // not a failed operation eligible for a new productive retry.
485
+ settleLifecycleInvocation(completed.context, completed.id, { result });
486
+ }
106
487
  const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
107
488
  const resultWithWarnings = addWarnings(result, refreshWarnings);
108
489
  if (heartbeat)
@@ -111,48 +492,179 @@ export async function runCli(args, io = defaultIo, env = process.env) {
111
492
  writeProgressJsonl(io, "done", "Command finished");
112
493
  emitHumanProgress(io, output, progress, "done");
113
494
  const payload = attachProgress(resultWithWarnings, progress, output);
495
+ publishPromptDelivery(payload);
114
496
  const visible = writeResponseFile(payload, output);
497
+ const maintenanceReceipt = payload;
498
+ const maintenanceExpiry = maintenanceReceipt?.lease_expires_at ?? maintenanceReceipt?.process?.lease_expires_at;
499
+ maintenanceDiagnostics?.phase("ack", { ...(typeof maintenanceExpiry === "string" && Number.isFinite(Date.parse(maintenanceExpiry)) ? { lease_expires_at: maintenanceExpiry } : {}) });
115
500
  if (output.json)
116
501
  writeJson(io.stdout, visible);
117
502
  else
118
503
  io.stdout.write(renderHumanResult(output.args, payload));
119
504
  return completedCommandExitCode(payload);
120
505
  }
121
- catch (error) {
506
+ catch (caught) {
507
+ const record = errorRecord(caught);
508
+ maintenanceDiagnostics?.failure(caught);
509
+ const error = isAppError(caught) ? caught : Object.assign(new AppError(record.code, record.message, record.exitCode, { effect: "unknown", ...record.details, ...(caught?.errcode !== undefined ? { sqlite_extended_code: caught.errcode } : {}) }), { ...(record.cause ? { cause: record.cause } : {}), ...(record.cleanup_error ? { cleanup_error: record.cleanup_error } : {}), ...(record.retryable !== undefined ? { retryable: record.retryable } : {}) });
510
+ if (!settleHook && !invocationSettlement && error.code !== "prompt_delivery_pending") {
511
+ let earlyContext;
512
+ try {
513
+ const command = quote(["dd-flow", ...args]);
514
+ const analysis = parseLifecycleCommand(command);
515
+ // Maintenance failures must not start an unrelated, unbounded hook
516
+ // lookup after their own request deadline has already expired.
517
+ const router = analysis.kind !== "none" || analysis.auxiliary ? createRouterContext(env) : null;
518
+ const event = router ? observedLifecycleNativeEvent(router, command, env.DD_FLOW_DAEMON_ID) : null;
519
+ // A correctable prepare rejection precedes the terminal command's
520
+ // single-use claim. Correlate its unique hook by the same lifecycle
521
+ // match key used at dispatch, including compound provider tool shells;
522
+ // otherwise a repaired retry sees two fresh, unclaimed events.
523
+ if (router && (event || (error.details.phase === "prepare" && error.details.effect === "no_effect"
524
+ && fs.existsSync(path.join(router.ddFlowHome, "db.sqlite"))))) {
525
+ earlyContext = createContext({ ...env }, "hook");
526
+ observeLifecycleCommand(earlyContext, command, event ?? undefined)({ error });
527
+ }
528
+ }
529
+ catch (persistence) {
530
+ error.details.lifecycle_diagnostic_error = String(persistence);
531
+ }
532
+ finally {
533
+ earlyContext?.db.close?.();
534
+ }
535
+ }
536
+ if (diagnosticHome && (!isAppError(caught) || error.code === "storage_write_failed")) {
537
+ try {
538
+ const directory = path.join(diagnosticHome, "diagnostics", "cli");
539
+ fs.mkdirSync(directory, { recursive: true });
540
+ const artifact = path.join(directory, `${randomUUID()}.json`);
541
+ fs.writeFileSync(artifact, JSON.stringify({ code: error.code, message: error.message, details: error.details, stack: caught instanceof Error ? caught.stack : null }), { flag: "wx", mode: 0o600 });
542
+ error.details.diagnostic_artifact = artifact;
543
+ }
544
+ catch (secondary) {
545
+ io.stderr.write(`CLI diagnostic persistence failed: ${String(secondary)}\n`);
546
+ }
547
+ }
122
548
  if (heartbeat)
123
549
  clearInterval(heartbeat);
124
- const payload = isAppError(error)
125
- ? { ok: false, error: { code: error.code, message: error.message, details: error.details } }
126
- : { ok: false, error: { code: "unexpected", message: String(error) } };
550
+ if (invocationSettlement && error.details.effect !== "committed") {
551
+ try {
552
+ if (isAppError(error)) {
553
+ delete error.details.retry_command;
554
+ delete error.details.retry_instruction;
555
+ // The common settlement classifies original validated proof before
556
+ // errorRecord clones diagnostic objects and loses capability identity.
557
+ settleLifecycleRejection(invocationSettlement.context, invocationSettlement.id, error);
558
+ }
559
+ else
560
+ settleLifecycleInvocation(invocationSettlement.context, invocationSettlement.id, { error: { code: "unexpected", message: String(error), exitCode: 1, details: {} } });
561
+ }
562
+ catch (persistenceError) {
563
+ if (isAppError(error))
564
+ error.details.recoverable = false;
565
+ io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
566
+ }
567
+ }
568
+ if (invocationSettlement && error.details.work_result_accepted === true) {
569
+ const accepted = readAcceptedAssignedWork(invocationSettlement.context, invocationSettlement.id);
570
+ if (accepted) {
571
+ // The Work/WorkSession commit is authoritative. Report its retained
572
+ // answer even if a derivative publication failed after that commit.
573
+ const payload = addWarnings(accepted, [{ code: "work_post_commit_pending", message: "Work result accepted; derivative publication requires reconciliation" }]);
574
+ try {
575
+ writeResponseFile(payload, output);
576
+ }
577
+ catch { /* Accepted SQL reply remains available on stdout/replay. */ }
578
+ if (output.json)
579
+ writeJson(io.stdout, payload);
580
+ else
581
+ io.stdout.write(renderHumanResult(output.args, payload));
582
+ return completedCommandExitCode(payload);
583
+ }
584
+ }
585
+ if (settleHook) {
586
+ try {
587
+ settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
588
+ }
589
+ catch (persistence) {
590
+ io.stderr.write(`${JSON.stringify({ primary: String(error), lifecycle_persistence_error: String(persistence) })}\n`);
591
+ }
592
+ }
593
+ const payload = { ok: false, error: errorRecord(error) };
594
+ progress.splice(0, progress.length, ...progress.filter(event => event.phase !== "done"));
595
+ if (progress.length)
596
+ progress.push({ phase: "failed", message: payload.error.message, at: new Date().toISOString() });
127
597
  emitHumanProgress(io, output, progress, "failed");
128
598
  if (output.progressJsonl)
129
599
  writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
130
600
  const errorPayload = attachProgress(payload, progress, output);
131
- const visible = writeResponseFile(errorPayload, output);
601
+ let visible = errorPayload;
602
+ try {
603
+ if (error.details.parameter !== "response-file")
604
+ visible = writeResponseFile(errorPayload, output);
605
+ }
606
+ catch (publication) {
607
+ error.details.response_publication_error = { code: publication?.code ?? "response_publication_failed", message: publication instanceof Error ? publication.message : String(publication) };
608
+ }
132
609
  if (output.json && output.progressJsonl)
133
610
  writeJson(io.stdout, errorPayload);
134
611
  else if (output.json)
135
612
  writeJson(io.stderr, visible);
136
613
  else
137
- io.stderr.write(renderHumanError(payload));
614
+ io.stderr.write(renderHumanError(payload, args));
138
615
  return isAppError(error) ? error.exitCode : 1;
139
616
  }
617
+ finally {
618
+ maintenanceDiagnostics?.close();
619
+ }
140
620
  }
141
- function resolveProjectRootBeforeRouting(args, env) {
621
+ function resolveProjectRootBeforeRouting(args, env, deadlineAt) {
142
622
  const explicit = resolveExplicitProjectRoot(args);
143
623
  if (explicit)
144
624
  return explicit;
145
625
  try {
146
626
  // Router identity lookup is read-only; it must not initialize or migrate runtime storage.
147
- return resolveOperationProjectRoot(createContext(env, "read_existing"), args);
627
+ const context = createContext(env, "read_existing", undefined, deadlineAt === undefined ? {} : { databaseDeadlineAt: deadlineAt });
628
+ try {
629
+ return resolveOperationProjectRoot(context, args);
630
+ }
631
+ finally {
632
+ context.db.close?.();
633
+ }
148
634
  }
149
635
  catch {
150
636
  return null;
151
637
  }
152
638
  }
153
639
  function databaseModeForCommand(args, classification) {
640
+ if (isNativeHookIngress(args))
641
+ return "hook";
642
+ if (args[0] === "run" && args[1] === "reconcile")
643
+ return args.includes("--apply") ? "reconcile" : "read_existing";
644
+ if (isRuntimeMaintenance(args))
645
+ return "read_existing";
154
646
  const [family, command] = args;
647
+ // Bound interactive operations never initialize or migrate a RUN store.
648
+ if (family === "interactive" && classification.mode !== "read_only_diagnostics")
649
+ return "prepared_existing";
650
+ if (family === "migration" && command === "readiness")
651
+ return "prepared_existing";
652
+ // Managed attempts are issued only from a prepared store. Opening that
653
+ // existing writer must not run migrations before their input is admitted.
654
+ if (parseArgs(args.slice(2)).options.has("invocation-id"))
655
+ return "hook";
656
+ // Native hook ingress must remain synchronous, but it must not run schema
657
+ // migration/VACUUM while the provider is waiting for its decision. Startup
658
+ // prepares the store; the hook mode only verifies the prepared contract and
659
+ // records the receipt.
660
+ if (family === "codex" && command === "hook" && args[2] === "handle") {
661
+ // Only the participating PreToolUse ingress needs the prepared hot-path
662
+ // contract. SessionStart/Stop/PostToolUse are observational no-ops here
663
+ // and retain the ordinary startup path for backwards compatibility.
664
+ return optionalOption(parseArgs(args.slice(3)), "event") === "PreToolUse" ? "hook" : "initialize";
665
+ }
155
666
  if (family === "engine" ||
667
+ family === "storage" && command === "migrate-writer" ||
156
668
  family === "version" ||
157
669
  family === "schema" ||
158
670
  family === "canon" && command === "resolve" ||
@@ -166,86 +678,6 @@ function databaseModeForCommand(args, classification) {
166
678
  return "read_existing";
167
679
  return "initialize";
168
680
  }
169
- function renderHumanError(payload) {
170
- const lines = [`dd-flow: ${payload.error.message}`];
171
- const details = recordObject(payload.error.details);
172
- const compatibility = recordObject(details?.compatibility);
173
- const remediation = recordObject(details?.remediation);
174
- if (compatibility) {
175
- lines.push(`Compatibility: ${String(compatibility.verdict ?? "unknown")} (${String(compatibility.engine_resolution ?? "unknown")})`);
176
- if (compatibility.blocked_operation)
177
- lines.push(`Blocked operation: ${String(compatibility.blocked_operation)}`);
178
- if (compatibility.install_hint)
179
- lines.push(`Install compatible engine: ${String(compatibility.install_hint)}`);
180
- }
181
- if (remediation?.mb_upgrade_mode)
182
- lines.push(`Upgrade mode: ${String(remediation.mb_upgrade_mode)}`);
183
- if (details?.preferred_command)
184
- lines.push(`Preferred command: ${String(details.preferred_command)}`);
185
- const related = Array.isArray(details?.related_commands) ? details.related_commands.map(String) : [];
186
- if (related.length > 0) {
187
- lines.push("", "Related commands:");
188
- for (const command of related)
189
- lines.push(` ${command}`);
190
- }
191
- return `${lines.join("\n")}\n`;
192
- }
193
- function parseOutputOptions(args) {
194
- const stripped = [];
195
- let json = false;
196
- let progressJsonl = false;
197
- let responseFile = null;
198
- for (let index = 0; index < args.length; index += 1) {
199
- const arg = args[index];
200
- if (arg === undefined)
201
- continue;
202
- if (arg === "--json") {
203
- json = true;
204
- continue;
205
- }
206
- if (arg === "--progress-jsonl") {
207
- progressJsonl = true;
208
- continue;
209
- }
210
- if (arg === "--response-file") {
211
- const file = args[++index];
212
- if (!file)
213
- throw new AppError("usage", "--response-file requires a path", 2);
214
- responseFile = file;
215
- continue;
216
- }
217
- stripped.push(arg);
218
- }
219
- if (responseFile && !json)
220
- throw new AppError("usage", "--response-file requires --json", 2);
221
- const hookJsonOnly = stripped[0] === "codex" && stripped[1] === "hook" && stripped[2] === "handle";
222
- return { json: json || hookJsonOnly, progressJsonl, args: stripped, hookJsonOnly, responseFile };
223
- }
224
- /** Persist the complete machine response for harnesses that cap tool output.
225
- * The visible response keeps routing fields but omits the potentially large
226
- * rendered prompt; an agent can safely read the named file without rerunning
227
- * the lifecycle command. */
228
- function writeResponseFile(payload, output) {
229
- if (!output.responseFile)
230
- return payload;
231
- const file = path.resolve(output.responseFile);
232
- try {
233
- fs.mkdirSync(path.dirname(file), { recursive: true });
234
- const temporary = `${file}.tmp-${process.pid}-${crypto.randomUUID()}`;
235
- fs.writeFileSync(temporary, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
236
- fs.renameSync(temporary, file);
237
- }
238
- catch (error) {
239
- // The lifecycle mutation may already be accepted. Do not retry it just
240
- // because its optional full-response file could not be written.
241
- return { ...(payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true, result: payload }), response_file: file, response_file_error: String(error) };
242
- }
243
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
244
- return { ok: true, response_file: file };
245
- const visible = { ...payload, response_file: file };
246
- delete visible.worker_prompt_markdown;
247
- return visible;
248
- }
249
681
  function progressForCommand(args) {
250
682
  const [family, command, subcommand] = args;
251
683
  if (family === "merge-queue" && command === "wait-next") {
@@ -294,7 +726,12 @@ function writeProgressJsonl(io, phase, message, detail) {
294
726
  io.stderr.write(`${JSON.stringify({ schema_id: "dd-flow/progress@1", phase, message, at: new Date().toISOString(), ...(detail ? { detail } : {}) })}\n`);
295
727
  }
296
728
  function commandProgress(io, output) {
297
- return (message) => output.progressJsonl ? writeProgressJsonl(io, "running", message) : io.stderr.write(`[running] ${message}\n`);
729
+ return (message) => {
730
+ if (output.progressJsonl)
731
+ writeProgressJsonl(io, "running", message);
732
+ else if (!output.json && !output.hookJsonOnly)
733
+ io.stderr.write(`[running] ${message}\n`);
734
+ };
298
735
  }
299
736
  function attachProgress(result, progress, output) {
300
737
  if (progress.length === 0 || output.hookJsonOnly)
@@ -308,7 +745,7 @@ function attachProgress(result, progress, output) {
308
745
  }
309
746
  return { ok: true, result, progress };
310
747
  }
311
- function renderHumanResult(args, result) {
748
+ export function renderHumanResult(args, result) {
312
749
  const [family, command] = args;
313
750
  const record = result && typeof result === "object" && !Array.isArray(result) ? result : {};
314
751
  const ok = record.ok === false ? "failed" : "ok";
@@ -376,6 +813,16 @@ function renderHumanResult(args, result) {
376
813
  if (run.run_index_path)
377
814
  lines.push(`run_index: ${String(run.run_index_path)}`);
378
815
  }
816
+ for (const key of ["outcome", "instruction", "agent_instruction", "next_action", "resume_command", "resume_command_template", "prompt_path"]) {
817
+ if (record[key] !== undefined && typeof record[key] !== "object")
818
+ lines.push(`${key}: ${String(record[key])}`);
819
+ }
820
+ for (const [name, keys] of [["next", ["finish_command"]], ["repair", ["work_id"]], ["prompt_delivery", ["path", "sha256"]], ["pause", ["user_message", "question_path", "resume_command", "resume_command_template"]]]) {
821
+ const value = recordObject(record[name]);
822
+ for (const key of keys)
823
+ if (value?.[key] !== undefined)
824
+ lines.push(`${name}.${key}: ${String(value[key])}`);
825
+ }
379
826
  appendFlowGuidanceHuman(lines, record);
380
827
  return `${lines.join("\n")}\n`;
381
828
  }
@@ -501,27 +948,759 @@ function stringValue(value) {
501
948
  function shortSha(value) {
502
949
  return value ? value.slice(0, 12) : "unknown";
503
950
  }
504
- async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args)) {
505
- const [family, command, ...rest] = args;
506
- const parsed = parseArgs(rest);
951
+ function compositionProposalRetry(context, parsed) {
952
+ return {
953
+ retry_command: runtimeCommand(context, ["memory", "compose", "register", "--run", requiredOption(parsed, "run"),
954
+ "--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
955
+ "--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
956
+ retry_instruction: "Correct only the rejected composition proposal, then execute retry_command in this same Session. No Work graph was registered by this rejected call; do not dispatch children or finish the Stage until registration succeeds."
957
+ };
958
+ }
959
+ /** All caller supplied file/stdin data is consumed here, before a managed
960
+ * invocation is allowed to claim execution or a stage claims its hook event. */
961
+ async function prepareCliInput(...args) {
962
+ try {
963
+ return await withPreparedInputBinding(() => prepareCliInputValue(...args));
964
+ }
965
+ catch (error) {
966
+ return prepareError(error);
967
+ }
968
+ }
969
+ async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
970
+ const { family, command, parsed } = route;
971
+ const prepared = { route };
972
+ if (family === "delivery" && ["checks", "execute"].includes(command ?? "")) {
973
+ const file = requiredOption(parsed, "result-file"), text = prepareTextFile(file, "Delivery input", 1024 * 1024);
974
+ const value = prepareJsonText(text.text, { file, label: "Delivery input" }).value;
975
+ const keys = command === "checks" ? "path,sha256,subject_ref,version" : "basis,expires_at,reason,scope_sha256,user_inputs_sha256";
976
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== keys)
977
+ throw new AppError("usage", "Delivery input must match the selected command's exact JSON contract", 2, { phase: "prepare", effect: "no_effect" });
978
+ if (command === "checks")
979
+ prepared.deliveryArtifact = value;
980
+ else
981
+ prepared.deliveryApproval = value;
982
+ }
983
+ if (family === "interactive" && command === "integrate" && hasOption(parsed, "result-file")) {
984
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive Git agreement", 1024 * 1024);
985
+ const value = prepareJsonText(input.text, { file, label: "Interactive Git agreement" }).value;
986
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== "basis,include_existing_paths,reason")
987
+ throw new AppError("usage", "Interactive Git agreement requires basis, include_existing_paths and reason", 2, { phase: "prepare", effect: "no_effect" });
988
+ prepared.interactiveGitAgreement = value;
989
+ }
990
+ if (family === "interactive" && command === "checks") {
991
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive check selection", 1024 * 1024);
992
+ const value = prepareJsonText(input.text, { file, label: "Interactive check selection" }).value;
993
+ if (!value || typeof value !== "object" || Array.isArray(value)
994
+ || !["additionalChecks", "additionalChecks,noChecksReason"].includes(Object.keys(value).sort().join()))
995
+ throw new AppError("usage", "Interactive checks require additionalChecks and optional noChecksReason", 2, { phase: "prepare", effect: "no_effect" });
996
+ prepared.interactiveChecksProposal = value;
997
+ }
998
+ if (family === "interactive" && command === "scope") {
999
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive mutation scope", 1024 * 1024);
1000
+ const value = prepareJsonText(input.text, { file, label: "Interactive mutation scope" }).value;
1001
+ if (!value || typeof value !== "object" || Array.isArray(value)
1002
+ || Object.keys(value).sort().join() !== "basis,expectedInputsSha256,paths,reason,requestId")
1003
+ throw new AppError("usage", "Interactive scope requires requestId, paths, basis, reason and expectedInputsSha256", 2, { phase: "prepare", effect: "no_effect" });
1004
+ const basis = recordObject(recordObject(value).basis);
1005
+ if (!basis || Object.keys(basis).sort().join() !== "event_id,role,sha256"
1006
+ || !["request", "turn", "answer", "finish"].includes(String(basis.role))
1007
+ || typeof basis.event_id !== "number" || !Number.isSafeInteger(basis.event_id) || basis.event_id < 0
1008
+ || typeof basis.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(basis.sha256))
1009
+ throw new AppError("usage", "Interactive scope basis requires role request|turn|answer|finish, a nonnegative integer event_id and a SHA-256", 2, {
1010
+ reason: "invalid_scope_basis_shape", phase: "prepare", effect: "no_effect", recoverable: true,
1011
+ retry_command: runtimeCommand(context, ["interactive", "scope", "--project-root", requiredOption(parsed, "project-root"),
1012
+ "--run", requiredOption(parsed, "run"), "--result-file", file, "--json", "--progress-jsonl"]),
1013
+ retry_instruction: "Correct only the rejected basis shape. For the original request use role=request, event_id=0 and initial_request.sha256 from interactive inputs. For a later user message copy its exact role, integer event_id and sha256 from messages; a question is not user approval. No scope was retained by this call. Reuse this exact retry_command; valid-shaped but unowned evidence or conflicting accepted scope identities remain refusals."
1014
+ });
1015
+ prepared.interactiveScopeProposal = value;
1016
+ }
1017
+ if (family === "interactive" && (command === "turn" || command === "finish")) {
1018
+ const input = prepareTextFile(requiredOption(parsed, "intake-file"), "Interactive user input", 1024 * 1024);
1019
+ prepared.interactiveInputBytes = Buffer.from(input.text);
1020
+ if (!prepared.interactiveInputBytes.length || !/^[a-f0-9]{64}$/.test(requiredOption(parsed, "expected-inputs-sha256")))
1021
+ throw new AppError("usage", "Interactive delivery requires nonempty UTF-8 input and its current input digest", 2, { phase: "prepare", effect: "no_effect" });
1022
+ }
1023
+ if (optionalOption(parsed, "reason-file")) {
1024
+ const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
1025
+ if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
1026
+ throw new AppError("validation", "Recovery evidence must be a JSON object with one nonempty reason", 2);
1027
+ prepared.retryReason = value.reason;
1028
+ }
1029
+ else if (optionalOption(parsed, "retry-check"))
1030
+ prepared.retryReason = optionalOption(parsed, "reason");
1031
+ if (route.key === "project register")
1032
+ resolveProjectRoot(requiredOption(parsed, "root"));
1033
+ if (route.key === "project archive")
1034
+ prepareProjectArchive(context, { idOrAlias: parsed.positional[0], root: optionalOption(parsed, "root"), reason: requiredOption(parsed, "reason") });
1035
+ if (route.key === "protocol register")
1036
+ prepareProtocolRegistration(context, { handshakeId: requiredPosition(parsed, 0, "handshake-id"), projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "workspace-path") });
1037
+ if (route.key === "canon register")
1038
+ prepared.canonRegistration = prepareCanonRegistration(context, { root: requiredOption(parsed, "root") });
1039
+ if (route.key === "run attach-stage")
1040
+ prepareFlowRunStageAttachment(context, stageAttachmentInput(parsed));
1041
+ if (route.key === "run complete-stage")
1042
+ prepareRawFlowRunStageCompletion(context, stageCompletionInput(parsed));
1043
+ if (route.key === "run override")
1044
+ prepareFlowRunCompletion(context, runOverrideInput(parsed));
1045
+ if (route.key === "engine bind-run")
1046
+ prepared.engineBind = await prepareRunEngineBind(context, engineBindInput(parsed));
1047
+ if (route.key === "worktree create")
1048
+ prepared.worktreeCreate = prepareWorktreeCreate(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree create"), branch: requiredOption(parsed, "branch"), base: requiredOption(parsed, "base"), path: requiredOption(parsed, "path") });
1049
+ if (route.key === "worktree bootstrap")
1050
+ prepared.worktreeBootstrap = prepareWorktreeBootstrap(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree bootstrap") });
1051
+ if (route.key === "worktree close")
1052
+ prepareWorktreeClose(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree close"), mode: requiredOption(parsed, "mode") });
1053
+ if (route.key.startsWith("plan item "))
1054
+ preparePlanItemMutation(context, planItemInput(parsed, requiredScopeProjectRoot(scopeProjectRoot, route.key)));
1055
+ if (route.key === "lane workspace set")
1056
+ prepareLaneWorkspace(context, laneWorkspaceInput(parsed));
1057
+ if (route.key === "lane waiter cancel")
1058
+ prepareLaneWaiterCancellation(context, laneWaiterCancelInput(parsed));
1059
+ if (route.key === "merge-queue complete")
1060
+ prepareMergeJobCompletion(context, { ...mergeJobTarget(parsed, scopeProjectRoot), summary: requiredOption(parsed, "summary") });
1061
+ if (route.key === "merge-queue cancel")
1062
+ prepareMergeQueueCancellation(context, mergeCancellationInput(parsed, scopeProjectRoot));
1063
+ if (route.key === "merge-queue wait-next")
1064
+ prepareMergeQueueWait(context, mergeWaitInput(parsed, scopeProjectRoot));
1065
+ if (route.key === "merge-queue next" || route.key === "merge-queue claim")
1066
+ prepareMergeClaim(context, { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed), ...(route.key === "merge-queue claim" ? { protocolId: requiredPosition(parsed, 0, "protocol-id") } : {}) });
1067
+ if (route.key === "merge-queue note")
1068
+ prepareMergeJobNote(context, { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue note"), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
1069
+ if (route.key === "merge bundle complete")
1070
+ prepareMergeBundleCompletion(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
1071
+ if (route.key === "merge bundle claim")
1072
+ prepareMergeBundleClaim(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed) });
1073
+ if (route.key === "merge bundle fail")
1074
+ prepareMergeBundleFailure(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
1075
+ if (route.key === "merge-queue fail")
1076
+ prepareMergeJobFailure(context, { ...mergeJobTarget(parsed, scopeProjectRoot), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
1077
+ if (route.key.startsWith("lane lock ") && route.key !== "lane lock status") {
1078
+ const action = requiredPosition(parsed, 0, "lane lock action");
1079
+ prepareLaneLock(context, {
1080
+ projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"),
1081
+ workspacePath: workspacePathOption(parsed), leaseToken: optionalOption(parsed, "lease-token"), reason: optionalOption(parsed, "reason"),
1082
+ ttlSeconds: optionalNumber(parsed, "ttl"), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
1083
+ }, action);
1084
+ }
1085
+ if (route.key === "run recovery begin")
1086
+ prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "interruption-json"), "interruption");
1087
+ if (route.key === "run recovery seal")
1088
+ prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "settlement-json"), "settlement");
1089
+ if (route.key === "run recovery capture")
1090
+ prepared.recoveryCapture = prepareRecoveryCapture(context, recoveryCaptureInput(parsed));
1091
+ if (route.key === "run recovery resume")
1092
+ prepared.recoveryResume = prepareRunRecoveryResume(context, recoveryResumeInput(parsed));
1093
+ if (route.key === "run drive answer")
1094
+ prepared.controllerAnswer = prepareRunControllerAnswer(context, controllerAnswerInput(parsed));
1095
+ if (route.key === "run drive launch")
1096
+ prepared.controllerLaunch = prepareRunControllerLaunch(context, controllerLaunchInput(parsed));
1097
+ if (route.key === "run drive context")
1098
+ prepared.controllerContext = prepareRunControllerContext(context, controllerContextInput(parsed));
1099
+ if (route.key === "protocol transition" || route.key === "transition")
1100
+ prepared.protocolTransition = prepareProtocolTransition(context, protocolTransitionInput(parsed, scopeProjectRoot ?? resolveOperationProjectRoot(context, args)));
1101
+ if (route.key === "codex home init")
1102
+ prepared.codexHome = prepareCodexHome(context, codexHomeInput(parsed));
1103
+ if (route.key === "codex home remove")
1104
+ prepareCodexHomeRemoval(context, { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), mode: requiredOption(parsed, "mode") });
1105
+ if (["codex hooks install", "codex hooks remove"].includes(route.key))
1106
+ prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
1107
+ if (route.key === "migration apply")
1108
+ prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
1109
+ if (route.key === "migration readiness")
1110
+ prepared.upgradeStorageScope = {
1111
+ projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: requiredOption(parsed, "workspace-root"), requestId: requiredOption(parsed, "request-id")
1112
+ };
1113
+ if (route.key === "migration verify")
1114
+ prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
1115
+ if (responseFile)
1116
+ prepareResponseDestination(responseFile);
1117
+ if (family === "dashboard") {
1118
+ const action = dashboardAction(command);
1119
+ prepareDashboardOptions({ format: optionalOption(parsed, "format"), viewer: optionalOption(parsed, "viewer"), open: optionalOption(parsed, "open") });
1120
+ const target = resolveDashboardTarget(context, { action, project: optionalOption(parsed, "project"), projectRoot: optionalOption(parsed, "project-root"), protocol: optionalOption(parsed, "protocol"), all: hasOption(parsed, "all"), global: hasOption(parsed, "global") || command?.endsWith("-global") });
1121
+ if (action === "refresh" && target.kind !== "global" && optionalOption(parsed, "output") !== undefined) {
1122
+ throw new AppError("validation", "--output is supported only for global dashboard refresh; use dashboard render for a custom project/protocol output", 2, { parameter: "output" });
1123
+ }
1124
+ if (target.kind === "protocol")
1125
+ requireProtocol(context, target.protocolId, target.project.id);
1126
+ if (action !== "data") {
1127
+ if (target.kind === "global" || target.kind === "all_projects") {
1128
+ prepareGlobalDashboardRender(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? (command?.endsWith("-global") ? undefined : "html") });
1129
+ }
1130
+ else {
1131
+ const format = optionalOption(parsed, "format") ?? (action === "open" || hasOption(parsed, "project") ? "html" : undefined);
1132
+ const input = { projectRoot: target.project.root, format, ...(target.kind === "protocol" ? { protocol: target.protocolId } : {}) };
1133
+ if (action === "refresh")
1134
+ prepareDashboardRefresh(context, { ...input, open: optionalOption(parsed, "open") });
1135
+ else if (action === "render")
1136
+ prepareDashboardRender(context, { ...input, output: optionalOption(parsed, "output") });
1137
+ else if (target.kind === "protocol") {
1138
+ if (format !== "html")
1139
+ throw new AppError("validation", "Protocol dashboard open supports --format html", 2, { protocol: target.protocolId });
1140
+ prepareDashboardRender(context, input);
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+ if (route.key === "prompt render") {
1146
+ const input = workerPromptInput(parsed);
1147
+ prepared.workerPrompt = { input, value: prepareWorkerPrompt(context, input) };
1148
+ }
1149
+ if (route.key === "run snapshot restore" || route.key === "run snapshot bootstrap restore") {
1150
+ try {
1151
+ const input = snapshotRestoreInput(parsed);
1152
+ if (route.key === "run snapshot restore")
1153
+ prepared.snapshotRestore = prepareEvalRunRestore(context, input);
1154
+ else
1155
+ prepared.bootstrapRestore = prepareEvalBootstrapRestore(input);
1156
+ }
1157
+ catch (error) {
1158
+ return prepareError(error, { parameter: "snapshot/project-root" });
1159
+ }
1160
+ }
1161
+ if (route.key === "run snapshot bootstrap create") {
1162
+ try {
1163
+ prepareEvalBootstrapSnapshot(context, { projectRoot: requiredOption(parsed, "project-root"), output: requiredOption(parsed, "output"), flow: optionalOption(parsed, "flow") });
1164
+ }
1165
+ catch (error) {
1166
+ return prepareError(error, { parameter: "project-root/output" });
1167
+ }
1168
+ }
1169
+ if (route.key === "run snapshot create") {
1170
+ prepareEvalRunSnapshotCreation(context, snapshotCreateInput(parsed));
1171
+ }
1172
+ if (route.key === "run start") {
1173
+ try {
1174
+ const input = runStartInput(parsed);
1175
+ prepared.runStart = { input, value: prepareFlowRunStart(context, input) };
1176
+ }
1177
+ catch (error) {
1178
+ return prepareError(error, { parameter: "run start" });
1179
+ }
1180
+ }
1181
+ if (route.key === "run fork") {
1182
+ try {
1183
+ prepared.runFork = prepareRunFork(runForkInput(parsed));
1184
+ }
1185
+ catch (error) {
1186
+ return prepareError(error, { parameter: "from/output" });
1187
+ }
1188
+ }
1189
+ if (route.key === "runtime scope status")
1190
+ prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
1191
+ if (["runtime scope fence", "runtime scope stop"].includes(route.key))
1192
+ prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
1193
+ if (route.key === "runtime scope resume")
1194
+ prepareRuntimeScopeResumeRequest(context, runtimeScopeResumeInput(parsed));
1195
+ if (route.key === "runtime scope reconcile")
1196
+ prepareRuntimeScopeReconciliation(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id"));
1197
+ if (route.key === "runtime scope serve")
1198
+ prepareRuntimeScopeServing(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")));
1199
+ if (route.key === "runtime scope control") {
1200
+ try {
1201
+ const raw = optionalOption(parsed, "manifest-json");
1202
+ prepared.runtimeManifest = prepareRuntimeScopeControl(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), requiredOption(parsed, "mode"), raw === undefined ? undefined : prepareJsonText(raw, { file: "<manifest-json>", label: "scope manifest" }).value, context);
1203
+ }
1204
+ catch (error) {
1205
+ return prepareError(error, { parameter: "scope-id/request-id/manifest-json" });
1206
+ }
1207
+ }
1208
+ if (family === "runtime" && command === "process")
1209
+ Object.assign(prepared, prepareRuntimeProcess(context, route.key, parsed));
1210
+ if (route.key === "runtime process start") {
1211
+ try {
1212
+ prepareRuntimeService(context, runtimeServiceInput(parsed));
1213
+ }
1214
+ catch (error) {
1215
+ return prepareError(error, { parameter: "runtime process start" });
1216
+ }
1217
+ }
1218
+ if (family === "work" && command === "retry") {
1219
+ try {
1220
+ prepareWorkRetry(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
1221
+ }
1222
+ catch (error) {
1223
+ return prepareError(error, { parameter: "work-id/reason" });
1224
+ }
1225
+ }
1226
+ if (family === "work" && (command === "fail" || command === "cancel")) {
1227
+ try {
1228
+ prepareWorkSettlement(context, requiredPosition(parsed, 0, "work-id"), command === "fail" ? "failed" : "cancelled", requiredOption(parsed, "reason"));
1229
+ }
1230
+ catch (error) {
1231
+ return prepareError(error, { parameter: "work-id/reason" });
1232
+ }
1233
+ }
1234
+ if (family === "work" && (command === "deps" || command === "delete")) {
1235
+ try {
1236
+ if (command === "delete")
1237
+ prepareWorkDelete(context, requiredPosition(parsed, 0, "work-id"));
1238
+ else
1239
+ prepareWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), on: parsed.options.get("on") ?? [] });
1240
+ }
1241
+ catch (error) {
1242
+ return prepareError(error, { parameter: "work-id/on" });
1243
+ }
1244
+ }
1245
+ if (route.key === "cleanup apply") {
1246
+ const planFile = requiredOption(parsed, "plan-file");
1247
+ try {
1248
+ prepared.cleanup = prepareCleanupApply(context, { projectRoot: requiredOption(parsed, "project-root"), planFile, reason: requiredOption(parsed, "reason"), force: hasOption(parsed, "force") });
1249
+ }
1250
+ catch (error) {
1251
+ return prepareError(error, { parameter: "plan-file", file: planFile });
1252
+ }
1253
+ }
1254
+ if (route.key === "stage block" || route.key === "stage unblock") {
1255
+ const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
1256
+ try {
1257
+ if (route.key === "stage block") {
1258
+ const summaryFile = optionalOption(parsed, "summary-file"), summaryStdin = hasOption(parsed, "summary-stdin");
1259
+ if (Boolean(summaryFile) === summaryStdin)
1260
+ throw new AppError("usage", "stage block requires exactly one of --summary-file or --summary-stdin", 2);
1261
+ prepared.interactionText = summaryFile ? prepareTextFile(summaryFile, "Runtime block summary").text : await readStdin(io.stdin);
1262
+ prepareStageRuntimeBlock(context, { ...input, kind: requiredOption(parsed, "kind"), code: requiredOption(parsed, "code"), summary: prepared.interactionText, retryable: hasOption(parsed, "retryable") });
1263
+ }
1264
+ else
1265
+ prepareStageRuntimeUnblock(context, input);
1266
+ }
1267
+ catch (error) {
1268
+ return prepareError(error, { parameter: "stage/work/summary" });
1269
+ }
1270
+ }
1271
+ if (route.key === "zcode invocation prepare") {
1272
+ const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label: "invocation issuance" }).value;
1273
+ const payload = value && typeof value === "object" ? value : {};
1274
+ try {
1275
+ prepared.lifecycleIssuance = prepareLifecycleIssuance(payload.command, payload.scope);
1276
+ }
1277
+ catch (error) {
1278
+ if (error instanceof AppError && ["invocation_command_invalid", "invocation_scope_invalid"].includes(error.code))
1279
+ return prepareError(new AppError("usage", error.message, 2, { cause_code: error.code }), { parameter: "stdin" });
1280
+ throw error;
1281
+ }
1282
+ assertLifecycleInvocationCurrent(context, prepared.lifecycleIssuance.scope, prepared.lifecycleIssuance.command);
1283
+ }
1284
+ if (route.key === "work add-batch") {
1285
+ const file = requiredOption(parsed, "file");
1286
+ try {
1287
+ assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
1288
+ prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file,
1289
+ ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
1290
+ ...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
1291
+ }
1292
+ catch (error) {
1293
+ return prepareError(error, { parameter: "file/parent", file });
1294
+ }
1295
+ }
1296
+ if (route.key === "work repair add") {
1297
+ const taskFile = optionalOption(parsed, "task-file"), taskStdin = hasOption(parsed, "task-stdin");
1298
+ if (Boolean(taskFile) === taskStdin)
1299
+ throw new AppError("usage", "work repair add requires exactly one of --task-file or --task-stdin", 2);
1300
+ if (taskStdin && io.stdin?.isTTY)
1301
+ throw new AppError("usage", "--task-stdin requires piped or redirected input", 2);
1302
+ const task = taskFile ? prepareTextFile(taskFile, "Repair task").text : await readStdin(io.stdin);
1303
+ prepared.workRepair = prepareCliWorkRepair(context, parsed, task, scopeProjectRoot);
1304
+ }
1305
+ if (route.key === "run config set" || route.key === "run vars set") {
1306
+ const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") };
1307
+ try {
1308
+ if (route.key === "run config set")
1309
+ prepareFlowRunConfig(context, { ...input, reason: requiredOption(parsed, "reason") });
1310
+ else
1311
+ prepareFlowRunVariable(context, input);
1312
+ }
1313
+ catch (error) {
1314
+ return prepareError(error, { parameter: "run config/vars" });
1315
+ }
1316
+ }
1317
+ if (route.key === "schema validate") {
1318
+ try {
1319
+ prepared.schemaValidation = dispatchSchema(context, command, parsed);
1320
+ }
1321
+ catch (error) {
1322
+ return prepareError(error, { parameter: "file", file: requiredOption(parsed, "file") });
1323
+ }
1324
+ }
1325
+ if (route.key === "project config set") {
1326
+ try {
1327
+ prepareProjectConfigValue(requiredOption(parsed, "key"), requiredOption(parsed, "value"));
1328
+ }
1329
+ catch (error) {
1330
+ return prepareError(error, { parameter: "key/value" });
1331
+ }
1332
+ }
1333
+ if (family === "session" && command === "register") {
1334
+ try {
1335
+ prepared.sessionPayload = prepareFlowSessionPayload({ payloadBase64: optionalOption(parsed, "payload-base64"), payloadFile: optionalOption(parsed, "payload-file"), payloadJson: optionalOption(parsed, "payload-json") });
1336
+ }
1337
+ catch (error) {
1338
+ return prepareError(error, { parameter: "session payload" });
1339
+ }
1340
+ }
1341
+ const run = (projectRoot, runId) => {
1342
+ try {
1343
+ const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
1344
+ const row = context.db.get("SELECT run_root, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]);
1345
+ if (!row)
1346
+ throw new AppError("not_found", "RUN is not registered", 2, { run_id: runId });
1347
+ if (!row.run_root)
1348
+ throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1, { run_id: runId });
1349
+ return { runRoot: row.run_root, workspaceRoot: row.workspace_root, indexJson: row.index_json };
1350
+ }
1351
+ catch (error) {
1352
+ return prepareError(error, { parameter: "run-id", run_id: runId });
1353
+ }
1354
+ };
1355
+ const json = (file, stdin, label, schema) => {
1356
+ if (stdin) {
1357
+ // The caller awaits this helper; the synchronous facade below is only
1358
+ // used for file inputs. Keep stdin handling in the stage branch.
1359
+ throw new AppError("usage", `${label} stdin preparation must be awaited`, 2);
1360
+ }
1361
+ if (!file)
1362
+ throw new AppError("usage", `${label} input is required`, 2);
1363
+ return prepareJsonSchema(file, label, schema);
1364
+ };
1365
+ if (family === "stage" && command === "start") {
1366
+ const stage = requiredOption(parsed, "stage");
1367
+ if (stage !== "specify" && ["bootstrap", "intake-file", "intake-stdin"].some(option => hasOption(parsed, option)))
1368
+ throw new AppError("usage", "--bootstrap and intake inputs are only supported for SPECIFY", 2);
1369
+ requiredOption(parsed, "project-root");
1370
+ if (!hasOption(parsed, "bootstrap"))
1371
+ requiredPosition(parsed, 0, "run-id");
1372
+ if (hasOption(parsed, "session-id"))
1373
+ throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
1374
+ const contextFile = optionalOption(parsed, "context-file"), contextSha256 = optionalOption(parsed, "context-sha256");
1375
+ if (Boolean(contextFile) !== Boolean(contextSha256))
1376
+ throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
1377
+ if ([hasOption(parsed, "intake-file"), hasOption(parsed, "intake-stdin"), Boolean(contextFile)].filter(Boolean).length > 1)
1378
+ throw new AppError("usage", "Use exactly one of --intake-file, --intake-stdin or --context-file", 2);
1379
+ if (!hasOption(parsed, "bootstrap")) {
1380
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
1381
+ if (bundle)
1382
+ prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
1383
+ if (bundle && (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id) || isDeliveryFlow(bundle.descriptor.descriptor.id))) {
1384
+ bundle.descriptor.stage(stage);
1385
+ if (hasOption(parsed, "dir"))
1386
+ throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
1387
+ const loaded = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
1388
+ if (loaded)
1389
+ prepared.stageStart = { externalContext: loaded };
1390
+ prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
1391
+ flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
1392
+ return prepared;
1393
+ }
1394
+ }
1395
+ if (contextFile && contextSha256) {
1396
+ try {
1397
+ prepared.stageStart = { externalContext: loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) };
1398
+ }
1399
+ catch (error) {
1400
+ return prepareError(error, { file: contextFile, parameter: "context-file" });
1401
+ }
1402
+ if (!hasOption(parsed, "bootstrap")) {
1403
+ const { runRoot } = run(requiredOption(parsed, "project-root"), requiredPosition(parsed, 0, "run-id"));
1404
+ prepareExternalStageContext({ stageRoot: path.join(runRoot, vnextStageDirectory(stage)), loaded: prepared.stageStart.externalContext });
1405
+ }
1406
+ }
1407
+ if (stage === "specify") {
1408
+ const intakeFile = optionalOption(parsed, "intake-file");
1409
+ if (intakeFile)
1410
+ prepared.stageStart = { intakeMarkdown: prepareTextFile(intakeFile, "SPECIFY intake").text };
1411
+ else if (hasOption(parsed, "intake-stdin"))
1412
+ prepared.stageStart = { intakeMarkdown: await readStdin(io.stdin) };
1413
+ else if (prepared.stageStart?.externalContext)
1414
+ prepared.stageStart.intakeMarkdown = runnerIntake(prepared.stageStart.externalContext);
1415
+ const projectRoot = requiredOption(parsed, "project-root");
1416
+ const bootstrap = hasOption(parsed, "bootstrap");
1417
+ if (isVnextSpecifyFlow(projectRoot) && (bootstrap || isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) {
1418
+ prepared.specifyStart = prepareVnextSpecifyStart(context, { projectRoot, ...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : {}), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}), slug: optionalOption(parsed, "subject"), ...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {}) });
1419
+ }
1420
+ else if (bootstrap) {
1421
+ if (hasOption(parsed, "intake-stdin") || contextFile)
1422
+ throw new AppError("usage", "Legacy SPECIFY bootstrap supports --intake-file, not stdin or runner context", 2);
1423
+ prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot, stage, bootstrap: { subject: requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) }, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) }, prepared.stageStart?.intakeMarkdown);
1424
+ }
1425
+ }
1426
+ if (stage === "merge" && isVnextMergeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1427
+ prepared.mergeStart = prepareVnextMergeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1428
+ }
1429
+ if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1430
+ prepared.protocolizeStart = prepareVnextProtocolizeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1431
+ }
1432
+ if (stage === "plan" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1433
+ prepared.planStart = prepareVnextPlanStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1434
+ }
1435
+ if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1436
+ prepared.planReviewStart = prepareVnextPlanReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1437
+ }
1438
+ if (stage === "code" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1439
+ prepared.codeStart = prepareVnextCodeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1440
+ }
1441
+ if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
1442
+ prepared.codeReviewStart = prepareVnextCodeReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
1443
+ }
1444
+ if (![prepared.specifyStart, prepared.protocolizeStart, prepared.planStart, prepared.planReviewStart, prepared.codeStart, prepared.codeReviewStart, prepared.mergeStart, prepared.legacyStageStart].some(Boolean)) {
1445
+ if (prepared.stageStart?.externalContext)
1446
+ throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
1447
+ prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) });
1448
+ }
1449
+ }
1450
+ if (family === "stage" && (command === "pause" || command === "resume")) {
1451
+ requiredPosition(parsed, 0, "run-id");
1452
+ requiredOption(parsed, "project-root");
1453
+ requiredOption(parsed, "stage");
1454
+ requiredOption(parsed, "work");
1455
+ const fileParameter = command === "pause" ? "question-file" : "answer-file";
1456
+ const stdinParameter = command === "pause" ? "question-stdin" : "answer-stdin";
1457
+ const inputFile = optionalOption(parsed, fileParameter);
1458
+ const fromStdin = hasOption(parsed, stdinParameter);
1459
+ if (Boolean(inputFile) === fromStdin)
1460
+ throw new AppError("usage", `stage ${command} requires exactly one of --${fileParameter} or --${stdinParameter}`, 2);
1461
+ if (fromStdin && io.stdin?.isTTY)
1462
+ throw new AppError("usage", `--${stdinParameter} requires piped or redirected input`, 2);
1463
+ prepared.interactionText = inputFile ? prepareTextFile(inputFile, command === "pause" ? "User question" : "User answer").text : await readStdin(io.stdin);
1464
+ if (!prepared.interactionText.trim())
1465
+ return prepareError(new AppError("validation", `stage ${command} requires non-empty input`, 2), { parameter: inputFile ? fileParameter : stdinParameter });
1466
+ const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
1467
+ try {
1468
+ if (command === "pause")
1469
+ prepareStagePause(context, { ...input, question: prepared.interactionText });
1470
+ else
1471
+ prepared.resumeEvidence = prepareStageResume(context, { ...input, answer: prepared.interactionText }).evidence;
1472
+ }
1473
+ catch (error) {
1474
+ return prepareError(error, { parameter: "stage/work" });
1475
+ }
1476
+ }
1477
+ if (family === "work" && command === "finish") {
1478
+ const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
1479
+ if (Boolean(retryReceiptId) !== Boolean(retryReason))
1480
+ throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
1481
+ const resultFile = optionalOption(parsed, "result-file");
1482
+ const resultStdin = hasOption(parsed, "result-stdin");
1483
+ if (Boolean(resultFile) === resultStdin)
1484
+ throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
1485
+ const maxBytes = workResultByteLimit(context, requiredPosition(parsed, 0, "work-id"));
1486
+ prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes) };
1487
+ prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result, progress);
1488
+ }
1489
+ if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
1490
+ prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
1491
+ }
1492
+ if (family === "stage" && command === "native" && requiredPosition(parsed, 0, "native action") === "observe") {
1493
+ prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
1494
+ }
1495
+ if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
1496
+ || command === "repair" && parsed.positional[0] === "publication-reserve"
1497
+ || command === "distill" && parsed.positional[0] === "publish")) {
1498
+ try {
1499
+ const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
1500
+ if (!bytes.length || bytes.length > 1024 * 1024)
1501
+ throw new AppError("usage", "Memory operation result exceeds its byte bound", 2, { phase: "prepare", effect: "no_effect", recoverable: true });
1502
+ prepared.memoryOperationBytes = bytes;
1503
+ }
1504
+ catch (error) {
1505
+ if (command === "compose" && isAppError(error) && error.details.phase === "prepare"
1506
+ && error.details.effect === "no_effect" && error.details.recoverable === true)
1507
+ Object.assign(error.details, compositionProposalRetry(context, parsed));
1508
+ throw error;
1509
+ }
1510
+ }
1511
+ if (family === "memory" && command === "fix" && ["scope", "publish", "integrate"].includes(parsed.positional[0] ?? "") && hasOption(parsed, "result-file")) {
1512
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Memory repair proposal", 1024 * 1024);
1513
+ prepared.memoryFixProposal = prepareJsonText(input.text, { file, label: "Memory repair proposal" }).value;
1514
+ if (!prepared.memoryFixProposal || typeof prepared.memoryFixProposal !== "object" || Array.isArray(prepared.memoryFixProposal))
1515
+ throw new AppError("usage", "Memory repair proposal must be a JSON object", 2, { phase: "prepare", effect: "no_effect" });
1516
+ }
1517
+ if (family !== "stage" || command !== "finish")
1518
+ return prepared;
1519
+ const projectRoot = requiredOption(parsed, "project-root");
1520
+ const runId = requiredPosition(parsed, 0, "run-id");
1521
+ const stage = requiredOption(parsed, "stage");
1522
+ if (hasOption(parsed, "status"))
1523
+ throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
1524
+ if (["result-file", "semantic-file", "data", "result-stdin"].filter(key => hasOption(parsed, key)).length > 1)
1525
+ throw new AppError("usage", "Use exactly one result input: --result-file, --semantic-file, --data or --result-stdin", 2);
1526
+ if (Boolean(optionalOption(parsed, "retry-check")) !== Boolean(prepared.retryReason))
1527
+ throw new AppError("usage", "--retry-check requires recovery evidence", 2);
1528
+ const resultStdin = hasOption(parsed, "result-stdin");
1529
+ const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
1530
+ const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
1531
+ const runtime = run(projectRoot, runId);
1532
+ const retainedFlowId = JSON.parse(runtime.indexJson).execution_bundle?.flow_id;
1533
+ if (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId) || isDeliveryFlow(retainedFlowId)) {
1534
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
1535
+ if (!bundle || !["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id) && !isDeliveryFlow(bundle.descriptor.descriptor.id))
1536
+ throw new AppError("cli_input_unprepared", "Registered RUN bundle is unavailable", 1);
1537
+ bundle.descriptor.stage(stage);
1538
+ if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
1539
+ throw new AppError("usage", "Memory stage finish requires its registered JSON result, not legacy result options", 2);
1540
+ if (Boolean(resultFile) === resultStdin)
1541
+ throw new AppError("usage", "Memory stage finish requires exactly one result input", 2);
1542
+ const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
1543
+ if (!bytes.length || bytes.length > 1024 * 1024)
1544
+ throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
1545
+ prepared.memoryStage = { operation: "finish", bytes, flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
1546
+ return prepared;
1547
+ }
1548
+ const terminalReplay = (name) => {
1549
+ let index;
1550
+ try {
1551
+ index = JSON.parse(runtime.indexJson);
1552
+ }
1553
+ catch (error) {
1554
+ throw new AppError("runtime_artifact_invalid", "RUN index is not valid retained JSON", 1, { run_id: runId, cause: error instanceof Error ? error.message : String(error) });
1555
+ }
1556
+ return index.stage_runs?.some(entry => entry.stage === name && entry.status === "done") === true
1557
+ && fs.existsSync(path.join(runtime.runRoot, vnextStageDirectory(name), "stage-report.json"));
1558
+ };
1559
+ const acceptStageInputs = (allowed) => {
1560
+ const options = allowed.split(" ");
1561
+ for (const name of ["dir", "semantic-file", "data", "result-file", "result-stdin", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"]) {
1562
+ if (hasOption(parsed, name) && !options.includes(name === "reason-file" ? "reason" : name))
1563
+ throw new AppError("usage", `stage finish ${stage} does not accept --${name} for this flow`, 2, { parameter: name });
1564
+ }
1565
+ };
1566
+ const fromStdin = async (label, schema) => {
1567
+ const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label });
1568
+ try {
1569
+ validateSchema({ ...schema, file: value.file, data: value.value });
1570
+ }
1571
+ catch (error) {
1572
+ return prepareError(error, { file: value.file, label });
1573
+ }
1574
+ return value;
1575
+ };
1576
+ if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
1577
+ acceptStageInputs("result-file semantic-file data result-stdin outcome");
1578
+ const outcome = optionalOption(parsed, "outcome");
1579
+ if (!outcome || !["specified", "failed", "cancelled"].includes(outcome))
1580
+ throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
1581
+ if (Boolean(resultFile) === resultStdin)
1582
+ throw new AppError("usage", "vNext SPECIFY finish requires exactly one result input", 2);
1583
+ const schema = { schemaName: "vnext-specify", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot };
1584
+ const input = resultStdin ? await fromStdin("SPECIFY result", schema) : json(resultFile, false, "SPECIFY result", schema);
1585
+ prepared.stageFinish = { value: input.value, text: input.text };
1586
+ }
1587
+ else if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
1588
+ acceptStageInputs("result-file semantic-file data");
1589
+ if (!resultFile || resultStdin || hasOption(parsed, "outcome"))
1590
+ throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
1591
+ let absolute;
1592
+ try {
1593
+ absolute = prepareProtocolizeResultPath(runtime.runRoot, resultFile);
1594
+ }
1595
+ catch (error) {
1596
+ return prepareError(error, { file: resultFile });
1597
+ }
1598
+ const input = json(absolute, false, "PROTOCOLIZE result", { schemaName: "vnext-protocolize-result", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
1599
+ prepared.stageFinish = { value: input.value, text: input.text };
1600
+ }
1601
+ else if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
1602
+ acceptStageInputs("");
1603
+ if (hasOption(parsed, "code-work-batch-file"))
1604
+ throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
1605
+ try {
1606
+ prepared.planFinish = prepareVnextPlanFinish(context, { projectRoot, runId });
1607
+ }
1608
+ catch (error) {
1609
+ return prepareError(error, { parameter: "PLAN artifacts" });
1610
+ }
1611
+ }
1612
+ else if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
1613
+ acceptStageInputs("request work retry-check reason");
1614
+ if (resultStdin || resultFile || hasOption(parsed, "outcome"))
1615
+ throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
1616
+ requiredOption(parsed, "request");
1617
+ requiredOption(parsed, "work");
1618
+ try {
1619
+ prepared.mergeFinish = prepareVnextMergeFinish(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(optionalOption(parsed, "retry-check") ? { retryCheckId: optionalOption(parsed, "retry-check"), retryReason: prepared.retryReason } : {}) });
1620
+ }
1621
+ catch (error) {
1622
+ return prepareError(error, { parameter: "MERGE finish" });
1623
+ }
1624
+ }
1625
+ else if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
1626
+ acceptStageInputs("decision-file");
1627
+ if (resultStdin || hasOption(parsed, "outcome"))
1628
+ throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
1629
+ const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "04-plan-review", "decision.json");
1630
+ const input = json(decision, false, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
1631
+ prepared.stageFinish = { value: input.value, text: input.text };
1632
+ }
1633
+ else if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
1634
+ acceptStageInputs("verification-file retry-check reason");
1635
+ if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome"))
1636
+ throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
1637
+ const verification = optionalOption(parsed, "verification-file");
1638
+ if (!verification)
1639
+ throw new AppError("usage", "vNext CODE finish requires --verification-file", 2);
1640
+ if (!terminalReplay(stage)) {
1641
+ const input = json(verification, false, "CODE verification", { schemaName: "code-verification", projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
1642
+ prepared.stageFinish = { value: input.value, text: input.text };
1643
+ }
1644
+ }
1645
+ else if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
1646
+ acceptStageInputs("decision-file retry-check reason");
1647
+ if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
1648
+ throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
1649
+ const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "06-code-review", "decision.json");
1650
+ if (!terminalReplay(stage)) {
1651
+ const input = prepareCodeReviewDecision(runtime.runRoot, optionalOption(parsed, "decision-file"));
1652
+ try {
1653
+ validateSchema({ schemaName: "code-review-decision", file: decision, data: input.value, projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
1654
+ }
1655
+ catch (error) {
1656
+ return prepareError(error, { file: decision, label: "CODE-REVIEW decision" });
1657
+ }
1658
+ prepared.stageFinish = input;
1659
+ }
1660
+ }
1661
+ else {
1662
+ acceptStageInputs("dir semantic-file data");
1663
+ if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin)
1664
+ throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
1665
+ prepared.legacyStageFinish = prepareStageFinish(context, { projectRoot, runId, stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}), ...(semanticFile ? { semanticFile } : {}) });
1666
+ }
1667
+ return prepared;
1668
+ }
1669
+ async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args), prepared = {}) {
1670
+ if (!prepared.route)
1671
+ throw new AppError("command_not_prepared", "Command dispatch requires prepared arguments", 1);
1672
+ const { family, command, parsed } = prepared.route;
1673
+ if (family === "zcode" && command === "invocation" && parsed.positional[0] === "prepare") {
1674
+ const payload = prepared.lifecycleIssuance;
1675
+ if (!payload)
1676
+ throw new AppError("command_not_prepared", "Invocation issuance requires prepared input", 1);
1677
+ assertLifecycleInvocationCurrent(context, payload.scope, payload.command);
1678
+ return { ok: true, ...issueLifecycleInvocation(context, payload.command, payload.scope) };
1679
+ }
1680
+ if (family === "storage" && command === "migrate-writer") {
1681
+ if (parsed.positional.length || [...parsed.options.keys()].some(key => !["database", "backup", "offline"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
1682
+ throw new AppError("usage", "Usage: dd-flow storage migrate-writer --database ABSOLUTE_DB --backup NEW_ABSOLUTE_FILE --offline", 2);
1683
+ if (parsed.options.get("offline")?.[0] !== flagValue)
1684
+ throw new AppError("usage", "Writer migration requires --offline after draining all local and external runtime owners", 2);
1685
+ return { ok: true, ...migrateStoreWriter(requiredOption(parsed, "database"), requiredOption(parsed, "backup")) };
1686
+ }
507
1687
  if (family === "engine") {
508
- return dispatchEngine(context, command, parsed, classification);
1688
+ return dispatchEngine(context, command, parsed, classification, prepared);
509
1689
  }
510
1690
  if (family === "status") {
511
- const rootParsed = parseArgs([command ?? "", ...rest]);
512
1691
  return getRuntimeStatus(context, {
513
- projectRoot: optionalOption(rootParsed, "project-root") ?? optionalOption(rootParsed, "root"),
514
- checkRegistry: hasOption(rootParsed, "check-registry")
1692
+ projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
1693
+ checkRegistry: hasOption(parsed, "check-registry")
515
1694
  });
516
1695
  }
517
1696
  if (family === "version") {
518
1697
  return getCliVersionReport();
519
1698
  }
520
1699
  if (family === "runtime") {
521
- return await dispatchRuntime(context, command, parsed, commandProgress(io, output));
1700
+ return await dispatchRuntime(context, command, parsed, commandProgress(io, output), prepared);
522
1701
  }
523
1702
  if (family === "canon") {
524
- return dispatchCanon(context, command, parsed);
1703
+ return dispatchCanon(context, command, parsed, prepared);
525
1704
  }
526
1705
  if (family === "project" && command === "register") {
527
1706
  return registerProject(context, { root: requiredOption(parsed, "root") });
@@ -598,16 +1777,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
598
1777
  return readyForMerge(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol ready-for-merge") });
599
1778
  }
600
1779
  if (family === "protocol" && command === "transition") {
601
- const protocolId = requiredPosition(parsed, 0, "protocol-id");
602
- const reason = optionalOption(parsed, "reason");
603
- return transitionProtocol(context, {
604
- protocolId,
605
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"),
606
- to: requiredOption(parsed, "to"),
607
- jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]),
608
- force: hasOption(parsed, "force"),
609
- ...(reason ? { reason } : {})
610
- });
1780
+ return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
611
1781
  }
612
1782
  if (family === "protocol" && command === "sync-from-run") {
613
1783
  const protocolId = requiredPosition(parsed, 0, "protocol-id");
@@ -632,17 +1802,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
632
1802
  });
633
1803
  }
634
1804
  if (family === "transition") {
635
- const transitionParsed = parseArgs([command ?? "", ...rest]);
636
- const protocolId = requiredPosition(transitionParsed, 0, "protocol-id");
637
- const reason = optionalOption(transitionParsed, "reason");
638
- return transitionProtocol(context, {
639
- protocolId,
640
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "transition"),
641
- to: requiredOption(transitionParsed, "to"),
642
- jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
643
- force: hasOption(transitionParsed, "force"),
644
- ...(reason ? { reason } : {})
645
- });
1805
+ return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
646
1806
  }
647
1807
  if (family === "plan" && command === "status") {
648
1808
  const protocolId = requiredPosition(parsed, 0, "protocol-id");
@@ -656,17 +1816,25 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
656
1816
  }
657
1817
  if (family === "stage" && command === "fanout") {
658
1818
  const action = requiredPosition(parsed, 0, "fanout action");
659
- const runId = requiredPosition(parsed, 1, "run-id");
660
1819
  const stage = requiredOption(parsed, "stage");
661
1820
  const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "stage fanout");
1821
+ const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
1822
+ const runId = resolveRun(context, project.id, requiredPosition(parsed, 1, "run-id")).id;
662
1823
  if (action === "status")
663
1824
  return getVnextFanoutStatus(context, { projectRoot, runId, stage });
1825
+ if (action === "reconcile")
1826
+ return reconcileVnextFanout(context, { projectRoot, runId, stage, observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
664
1827
  if (action === "dispatch") {
665
1828
  if (stage === "plan-review")
666
1829
  return dispatchVnextPlanReview(context, { projectRoot, runId });
667
1830
  return getVnextFanoutStatus(context, { projectRoot, runId, stage });
668
1831
  }
669
- throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch> <RUN> --stage <stage> --project-root <path>", 2);
1832
+ throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch|reconcile> <RUN> --stage <stage> --project-root <path>; reconcile requires --observations-file <native-observations.json>", 2);
1833
+ }
1834
+ if (family === "stage" && command === "native") {
1835
+ if (requiredPosition(parsed, 0, "native action") !== "observe")
1836
+ throw new AppError("usage", "Use: dd-flow stage native observe <RUN> --stage <stage> --observations-file <native-observations.json> --project-root <path>", 2);
1837
+ return observeVnextNativeChildren(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "stage native observe"), runId: requiredPosition(parsed, 1, "run-id"), stage: requiredOption(parsed, "stage"), observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
670
1838
  }
671
1839
  if (family === "run" && command === "capacity") {
672
1840
  if (requiredPosition(parsed, 0, "capacity action") !== "record")
@@ -678,16 +1846,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
678
1846
  return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
679
1847
  }
680
1848
  if (family === "prompt" && command === "render") {
681
- const workspaceRoot = optionalOption(parsed, "workspace-root");
682
- const taskFile = optionalOption(parsed, "task-file");
683
- return renderWorkerPrompt(context, {
684
- projectRoot: requiredOption(parsed, "project-root"),
685
- runId: requiredOption(parsed, "run"),
686
- stage: requiredOption(parsed, "stage"),
687
- profile: requiredOption(parsed, "profile"),
688
- ...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }),
689
- ...(workspaceRoot ? { workspaceRoot } : {})
690
- });
1849
+ return renderWorkerPrompt(context, prepared.workerPrompt?.input ?? workerPromptInput(parsed), prepared.workerPrompt?.value);
691
1850
  }
692
1851
  if (family === "work" && command === "status") {
693
1852
  return getVnextWorkStatus(context, {
@@ -696,7 +1855,10 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
696
1855
  });
697
1856
  }
698
1857
  if (family === "work" && command === "add-batch") {
699
- return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file") });
1858
+ assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
1859
+ return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch,
1860
+ ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
1861
+ ...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
700
1862
  }
701
1863
  if (family === "work" && command === "ls") {
702
1864
  return listWorks(context, { ...(optionalOption(parsed, "run") ? { runId: optionalOption(parsed, "run") } : {}), ...(optionalOption(parsed, "parent") ? { parentWorkId: optionalOption(parsed, "parent") } : {}), ...(optionalOption(parsed, "status") ? { status: optionalOption(parsed, "status") } : {}), ...(hasOption(parsed, "ready") ? { ready: true } : {}), ...(hasOption(parsed, "include-results") ? { includeResults: true } : {}), ...(optionalOption(parsed, "limit") ? { limit: Number(optionalOption(parsed, "limit")) } : {}) });
@@ -704,48 +1866,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
704
1866
  if (family === "work" && command === "show")
705
1867
  return showWork(context, requiredPosition(parsed, 0, "work-id"));
706
1868
  if (family === "work" && command === "repair" && requiredPosition(parsed, 0, "action") === "add") {
707
- if (!hasOption(parsed, "task-stdin"))
708
- throw new AppError("usage", "work repair add requires --task-stdin", 2);
709
- const origins = parsed.options.get("origin-work") ?? [];
710
- const findings = parsed.options.get("from-finding") ?? [];
711
- const unresolved = parsed.options.get("from-unresolved") ?? [];
712
- if (!origins.length && !findings.length)
713
- throw new AppError("usage", "work repair add requires --from-check with --origin-work, --from-finding, or --from-unresolved with --origin-work", 2);
714
- if (findings.length)
715
- return addVnextCodeReviewRepair(context, {
716
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
717
- runId: requiredOption(parsed, "run"),
718
- findingIds: findings,
719
- checkRefsByFinding: Object.fromEntries(findings.map((finding) => [finding, parsed.options.get("check-ref") ?? []])),
720
- objective: await readStdin(io.stdin)
721
- });
722
- if (unresolved.length) {
723
- if (!origins.length)
724
- throw new AppError("usage", "semantic repair requires at least one --origin-work", 2);
725
- const verificationPath = requiredOption(parsed, "verification-file");
726
- return addVnextCodeRepair(context, {
727
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
728
- runId: requiredOption(parsed, "run"),
729
- originWorkIds: origins,
730
- semanticUnresolved: unresolved,
731
- verificationPath,
732
- objective: await readStdin(io.stdin)
733
- });
734
- }
735
- if (!hasOption(parsed, "from-check"))
736
- throw new AppError("usage", "check repair requires --from-check", 2);
737
- if (!origins.length)
738
- throw new AppError("usage", "check repair requires at least one --origin-work", 2);
739
- const receipts = parsed.options.get("from-check") ?? [];
740
- if (receipts.length !== 1)
741
- throw new AppError("usage", "check repair accepts exactly one --from-check; create one causal repair Work per failed receipt", 2);
742
- return addVnextCodeRepair(context, {
743
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
744
- runId: requiredOption(parsed, "run"),
745
- checkReceiptId: receipts[0],
746
- originWorkIds: origins,
747
- objective: await readStdin(io.stdin)
748
- });
1869
+ if (!prepared.workRepair)
1870
+ throw new AppError("cli_input_unprepared", "Repair input was not prepared", 1);
1871
+ return addVnextCodeRepair(context, prepared.workRepair.input, prepared.workRepair.value);
749
1872
  }
750
1873
  if (family === "work" && command === "deps") {
751
1874
  const on = parsed.options.get("on") ?? [];
@@ -753,26 +1876,42 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
753
1876
  }
754
1877
  if (family === "work" && command === "delete")
755
1878
  return deleteWork(context, requiredPosition(parsed, 0, "work-id"));
1879
+ if (family === "work" && command === "launch")
1880
+ return await launchExternalWork(context, { workId: requiredPosition(parsed, 0, "work-id"), ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}), progress: commandProgress(io, output) });
756
1881
  if (family === "work" && command === "start") {
757
1882
  const hookEventId = optionalOption(parsed, "hook-event-id");
1883
+ const recoveryId = optionalOption(parsed, "recovery-id");
758
1884
  const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
759
- return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), projectRoot });
1885
+ return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot, progress: commandProgress(io, output) });
760
1886
  }
761
1887
  if (family === "work" && command === "finish") {
762
1888
  const resultFile = optionalOption(parsed, "result-file");
763
1889
  const resultStdin = hasOption(parsed, "result-stdin");
764
1890
  if (Boolean(resultFile) === resultStdin)
765
1891
  throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
766
- const result = resultFile ? fs.readFileSync(path.resolve(resultFile), "utf8") : await readStdin(io.stdin);
767
- return await finishWork(context, requiredPosition(parsed, 0, "work-id"), result, commandProgress(io, output));
1892
+ const workId = requiredPosition(parsed, 0, "work-id");
1893
+ const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work finish");
1894
+ assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_finish", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
1895
+ const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
1896
+ if (Boolean(retryReceiptId) !== Boolean(retryReason))
1897
+ throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
1898
+ const maxBytes = workResultByteLimit(context, workId);
1899
+ const result = prepared.workFinish?.result ?? (resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes));
1900
+ return await finishWork(context, workId, result, commandProgress(io, output), retryReceiptId ? { receiptId: retryReceiptId, reason: retryReason } : undefined);
1901
+ }
1902
+ if (family === "work" && command === "fail") {
1903
+ const workId = requiredPosition(parsed, 0, "work-id");
1904
+ const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work fail");
1905
+ assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_fail", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
1906
+ return failWork(context, workId, requiredOption(parsed, "reason"));
768
1907
  }
769
- if (family === "work" && command === "fail")
770
- return failWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
771
1908
  if (family === "work" && command === "cancel")
772
1909
  return cancelWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
773
1910
  if (family === "work" && command === "retry")
774
1911
  return retryWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
775
1912
  if (family === "stage" && command === "start") {
1913
+ if (prepared.stageBindingBundle)
1914
+ context = { ...context, stageBindingBundle: prepared.stageBindingBundle };
776
1915
  const dir = optionalOption(parsed, "dir");
777
1916
  const bootstrap = hasOption(parsed, "bootstrap");
778
1917
  const subject = optionalOption(parsed, "subject");
@@ -789,12 +1928,45 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
789
1928
  const requireSessionBinding = hasOption(parsed, "require-session-binding");
790
1929
  const projectRoot = requiredOption(parsed, "project-root");
791
1930
  const stage = requiredOption(parsed, "stage");
792
- const externalContext = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
1931
+ const externalContext = prepared.stageStart?.externalContext;
1932
+ if (prepared.memoryStage?.operation === "start") {
1933
+ const runId = requiredPosition(parsed, 0, "run-id");
1934
+ const event = hookEventId ?? (currentAssignedLifecycle(context) ? undefined : findRecentMatchingHookEvent(context, {
1935
+ projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
1936
+ matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
1937
+ errorCode: "trusted_session_binding_required", operation: "Memory stage start"
1938
+ }).eventKey);
1939
+ const work = (isDeliveryFlow(prepared.memoryStage.flowId) ? startDeliveryStage : prepared.memoryStage.flowId === "interactive" ? startInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? startReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? startMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? startDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
1940
+ try {
1941
+ const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
1942
+ promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
1943
+ // The frozen semantic prompt is also restored by auxiliary owners in
1944
+ // other CLI contexts. Keep native invocation authority in this current
1945
+ // delivery, not in those immutable instruction/custody bytes.
1946
+ const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
1947
+ "--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
1948
+ const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
1949
+ // These assignments belong to this native coordinator delivery, not to
1950
+ // the immutable semantic packet restored by auxiliary owners.
1951
+ const deliveryInstructions = isDeliveryFlow(prepared.memoryStage.flowId) ? issueDeliveryStageCommands(context, { flowId: prepared.memoryStage.flowId, projectRoot, runId, stage, stageRoot: path.dirname(work.result_path) }).instructions : "";
1952
+ const orchestration = registeredStageFanoutDescriptor(context, { projectRoot, runId, stage });
1953
+ const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
1954
+ worker_prompt_markdown: `${work.worker_prompt_markdown}${deliveryInstructions}\n## Current lifecycle commands\n\nWrite the semantic result to the assigned result path, then execute exactly:\n${finish}\n\nFor a required user choice, write the question file named by this command, then execute exactly:\n${pause}\n` };
1955
+ bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
1956
+ return controlLifecycleReply(context, { projectRoot, runId }, delivery);
1957
+ }
1958
+ catch (error) {
1959
+ throw new AppError("memory_stage_binding_pending", "Memory Stage allocated; trusted coordinator binding remains incomplete", 1, { effect: "committed", run_id: runId, stage, work_id: work.work_id, next_action: "reconcile_memory_stage_binding",
1960
+ cause: error instanceof AppError ? error.code : "coordinator_binding_failure" });
1961
+ }
1962
+ }
793
1963
  const claimVnextStageStart = (runId) => {
1964
+ if (currentAssignedLifecycle(context)?.operation === "stage_start")
1965
+ return undefined;
794
1966
  registerProject(context, { root: projectRoot });
795
1967
  return findRecentMatchingHookEvent(context, {
796
1968
  projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
797
- matchKey: stageStartMatchKey(runId, stage, projectRoot, contextSha256),
1969
+ matchKey: entityReferenceVariants(runId).map((reference) => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
798
1970
  errorCode: "trusted_session_binding_required",
799
1971
  operation: `vNext ${stage.toUpperCase()} start`
800
1972
  }).eventKey;
@@ -818,39 +1990,39 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
818
1990
  })
819
1991
  : claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
820
1992
  }
821
- const result = launchVnextSpecify(context, {
1993
+ const result = sdlcStageHandlers.specify.entry(context, {
822
1994
  projectRoot,
823
- ...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
1995
+ ...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
824
1996
  hookEventId,
825
1997
  ...(subject ? { slug: subject } : {}),
826
1998
  ...(externalContext ? { externalContext } : {}),
827
1999
  ...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
828
- });
2000
+ }, prepared.specifyStart);
829
2001
  return attachExternalStageContext(result, externalContext);
830
2002
  }
831
2003
  if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
832
2004
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
833
- return attachExternalStageContext(startVnextProtocolize(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2005
+ return attachExternalStageContext(await sdlcStageHandlers.protocolize.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.protocolizeStart), externalContext);
834
2006
  }
835
2007
  if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
836
2008
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
837
- return attachExternalStageContext(startVnextPlan(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2009
+ return attachExternalStageContext(sdlcStageHandlers.plan.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planStart), externalContext);
838
2010
  }
839
2011
  if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
840
2012
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
841
- return attachExternalStageContext(startVnextPlanReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2013
+ return attachExternalStageContext(sdlcStageHandlers["plan-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planReviewStart), externalContext);
842
2014
  }
843
2015
  if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
844
2016
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
845
- return attachExternalStageContext(await startVnextCode(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2017
+ return attachExternalStageContext(await sdlcStageHandlers.code.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeStart), externalContext);
846
2018
  }
847
2019
  if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
848
2020
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
849
- return attachExternalStageContext(startVnextCodeReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2021
+ return attachExternalStageContext(sdlcStageHandlers["code-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeReviewStart), externalContext);
850
2022
  }
851
2023
  if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
852
2024
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
853
- return attachExternalStageContext(await startVnextMerge(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
2025
+ return attachExternalStageContext(await sdlcStageHandlers.merge.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.mergeStart), externalContext);
854
2026
  }
855
2027
  if (intakeStdin) {
856
2028
  throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
@@ -865,7 +2037,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
865
2037
  ...(hookEventId ? { hookEventId } : {}),
866
2038
  ...(requireSessionBinding ? { requireSessionBinding: true } : {}),
867
2039
  ...(dir ? { dir } : {})
868
- });
2040
+ }, prepared.legacyStageStart);
869
2041
  }
870
2042
  if (family === "stage" && command === "finish") {
871
2043
  const dir = optionalOption(parsed, "dir");
@@ -875,13 +2047,34 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
875
2047
  const projectRoot = requiredOption(parsed, "project-root");
876
2048
  const runId = requiredPosition(parsed, 0, "run-id");
877
2049
  const stage = requiredOption(parsed, "stage");
2050
+ const controlledReply = (result) => controlLifecycleReply(context, { projectRoot, runId }, result);
878
2051
  const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
879
2052
  const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
880
2053
  const resultStdin = hasOption(parsed, "result-stdin");
881
2054
  const retryCheckId = optionalOption(parsed, "retry-check");
882
- const retryReason = optionalOption(parsed, "reason");
2055
+ const retryReason = prepared.retryReason;
2056
+ const hookEventId = optionalOption(parsed, "hook-event-id");
883
2057
  if (retryCheckId && !retryReason?.trim())
884
2058
  throw new AppError("usage", "--retry-check requires --reason explaining the recovered environment", 2);
2059
+ if (prepared.memoryStage?.operation === "finish") {
2060
+ // Descriptor settlement validates exact coordinator custody and claims
2061
+ // the fresh hook atomically with result acceptance. Do not consume it
2062
+ // through the SDLC lifecycle owner before that settlement.
2063
+ return controlledReply((isDeliveryFlow(prepared.memoryStage.flowId) ? finishDeliveryStage : prepared.memoryStage.flowId === "interactive" ? finishInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? finishReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? finishMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? finishDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
2064
+ hookEventId: hookEventId ?? (currentAssignedLifecycle(context) ? "" : findRecentMatchingHookEvent(context, {
2065
+ projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
2066
+ matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
2067
+ errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
2068
+ }).eventKey) }));
2069
+ }
2070
+ const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
2071
+ || (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
2072
+ || (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
2073
+ || (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
2074
+ || (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
2075
+ || (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
2076
+ if (protectedLifecycle)
2077
+ assertStageLifecycleOwner(context, { projectRoot, runId, stage, operation: "stage_finish", ...(hookEventId ? { hookEventId } : {}) });
885
2078
  if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
886
2079
  const outcome = optionalOption(parsed, "outcome");
887
2080
  if (!outcome || !["specified", "failed", "cancelled"].includes(outcome)) {
@@ -893,84 +2086,92 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
893
2086
  if (!resultFile && !resultStdin) {
894
2087
  throw new AppError("usage", "vNext SPECIFY finish requires --result-file or --result-stdin", 2);
895
2088
  }
896
- return finishVnextSpecifyStage(context, {
2089
+ return controlledReply(await sdlcStageHandlers.specify.finish(context, {
897
2090
  projectRoot,
898
2091
  runId,
899
- ...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
2092
+ ...(resultFile ? { resultFile } : prepared.stageFinish?.value ? {} : { resultJson: await readStdin(io.stdin) }),
2093
+ ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}),
900
2094
  outcome: outcome
901
- });
2095
+ }));
902
2096
  }
903
2097
  if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
904
2098
  if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
905
2099
  throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
906
2100
  }
907
- return finishVnextProtocolize(context, { projectRoot, runId, resultFile });
2101
+ return controlledReply(await sdlcStageHandlers.protocolize.finish(context, { projectRoot, runId, resultFile, ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}) }));
908
2102
  }
909
2103
  if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
910
2104
  if (optionalOption(parsed, "code-work-batch-file"))
911
2105
  throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
912
- return finishVnextPlan(context, { projectRoot, runId });
2106
+ return controlledReply(await sdlcStageHandlers.plan.finish(context, { projectRoot, runId, ...(prepared.planFinish ? { prepared: prepared.planFinish } : {}) }));
913
2107
  }
914
2108
  if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
915
2109
  if (resultStdin || hasOption(parsed, "outcome"))
916
2110
  throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
917
2111
  const decisionFile = optionalOption(parsed, "decision-file");
918
- return finishVnextPlanReview(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}) });
2112
+ return controlledReply(await sdlcStageHandlers["plan-review"].finish(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}) }));
919
2113
  }
920
2114
  if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
921
2115
  if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
922
2116
  throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
923
2117
  }
924
- return await finishVnextCode(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) });
2118
+ return controlledReply(await sdlcStageHandlers.code.finish(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), ...(prepared.stageFinish?.value ? { verification: prepared.stageFinish.value } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
925
2119
  }
926
2120
  if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
927
2121
  if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
928
2122
  throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
929
- return await finishVnextCodeReview(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) });
2123
+ return controlledReply(await sdlcStageHandlers["code-review"].finish(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
930
2124
  }
931
2125
  if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
932
2126
  if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
933
2127
  throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
934
- return await finishVnextMerge(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) });
2128
+ return controlledReply(await sdlcStageHandlers.merge.finish(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }, prepared.mergeFinish));
935
2129
  }
936
2130
  if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
937
2131
  throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
938
2132
  }
939
- return finishStage(context, {
2133
+ return controlledReply(await finishStage(context, {
940
2134
  projectRoot,
941
2135
  runId,
942
2136
  stage,
943
2137
  ...(dir ? { dir } : {}),
944
2138
  ...(semanticFile ? { semanticFile } : {})
945
- });
2139
+ }, prepared.legacyStageFinish));
946
2140
  }
947
2141
  if (family === "stage" && command === "pause") {
948
- if (!hasOption(parsed, "question-stdin"))
949
- throw new AppError("usage", "stage pause requires --question-stdin", 2);
2142
+ const projectRoot = requiredOption(parsed, "project-root");
2143
+ const runId = requiredPosition(parsed, 0, "run-id");
2144
+ const stage = requiredOption(parsed, "stage");
2145
+ const workId = requiredOption(parsed, "work");
2146
+ const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
2147
+ || (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
2148
+ || (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
2149
+ || (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
2150
+ || (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
2151
+ || (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
2152
+ if (protectedLifecycle)
2153
+ assertStageLifecycleOwner(context, { projectRoot, runId, stage, workId, operation: "stage_pause", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
950
2154
  return pauseStageForUser(context, {
951
- projectRoot: requiredOption(parsed, "project-root"),
952
- runId: requiredPosition(parsed, 0, "run-id"),
953
- stage: requiredOption(parsed, "stage"),
954
- workId: requiredOption(parsed, "work"),
955
- question: await readStdin(io.stdin)
2155
+ projectRoot,
2156
+ runId,
2157
+ stage,
2158
+ workId,
2159
+ question: prepared.interactionText ?? await readStdin(io.stdin)
956
2160
  });
957
2161
  }
958
2162
  if (family === "stage" && command === "resume") {
959
- if (!hasOption(parsed, "answer-stdin"))
960
- throw new AppError("usage", "stage resume requires --answer-stdin", 2);
961
2163
  const hookEventId = optionalOption(parsed, "hook-event-id");
962
2164
  return resumeStageAfterUser(context, {
963
2165
  projectRoot: requiredOption(parsed, "project-root"),
964
2166
  runId: requiredPosition(parsed, 0, "run-id"),
965
2167
  stage: requiredOption(parsed, "stage"),
966
2168
  workId: requiredOption(parsed, "work"),
967
- answer: await readStdin(io.stdin),
2169
+ answer: prepared.interactionText ?? await readStdin(io.stdin),
2170
+ evidence: prepared.resumeEvidence,
968
2171
  ...(hookEventId ? { hookEventId } : {})
969
2172
  });
970
2173
  }
971
2174
  if (family === "stage" && command === "block") {
972
- if (!hasOption(parsed, "summary-stdin"))
973
- throw new AppError("usage", "stage block requires --summary-stdin", 2);
974
2175
  const kind = requiredOption(parsed, "kind");
975
2176
  if (kind !== "engine" && kind !== "harness" && kind !== "environment")
976
2177
  throw new AppError("usage", "--kind must be engine, harness or environment", 2);
@@ -981,7 +2182,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
981
2182
  workId: requiredOption(parsed, "work"),
982
2183
  kind,
983
2184
  code: requiredOption(parsed, "code"),
984
- summary: await readStdin(io.stdin),
2185
+ summary: prepared.interactionText ?? await readStdin(io.stdin),
985
2186
  retryable: hasOption(parsed, "retryable")
986
2187
  });
987
2188
  }
@@ -1006,13 +2207,55 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1006
2207
  return dispatchMergeWorker(context, command, parsed);
1007
2208
  }
1008
2209
  if (family === "cleanup") {
1009
- return dispatchCleanup(context, command, parsed);
2210
+ return dispatchCleanup(context, command, parsed, prepared.cleanup);
1010
2211
  }
1011
2212
  if (family === "session") {
1012
- return dispatchSession(context, command, parsed);
2213
+ return dispatchSession(context, command, parsed, prepared.sessionPayload);
1013
2214
  }
1014
2215
  if (family === "run") {
1015
- return dispatchRun(context, command, parsed);
2216
+ if (command === "control") {
2217
+ const action = requiredPosition(parsed, 0, "control action");
2218
+ const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
2219
+ if (action === "status")
2220
+ return runControlStatus(context, scope);
2221
+ if (action === "reconcile") {
2222
+ if ([...parsed.options.keys()].some(key => !["run", "project-root", "control-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
2223
+ throw new AppError("usage", "Use run control reconcile --run ID --project-root PATH --control-id ID --request-id ID", 2);
2224
+ return await reconcileRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id"), requestId: requiredOption(parsed, "request-id") });
2225
+ }
2226
+ if (action === "resume") {
2227
+ if (["force", "grace-ms", "escalate"].some(option => hasOption(parsed, option)))
2228
+ throw new AppError("usage", "Resume does not accept stop or pause escalation options", 2);
2229
+ return await resumeRunController(context, { ...scope, requestId: requiredOption(parsed, "request-id"), controlId: requiredOption(parsed, "from"), ...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {}) });
2230
+ }
2231
+ if (action === "serve")
2232
+ return await serveRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id") });
2233
+ if (action === "pause" || action === "stop")
2234
+ return await requestRunControl(context, {
2235
+ ...scope, action, requestId: requiredOption(parsed, "request-id"),
2236
+ ...(hasOption(parsed, "force") ? { force: true } : {}),
2237
+ ...(hasOption(parsed, "grace-ms") ? { graceMs: Number(requiredOption(parsed, "grace-ms")) } : {}),
2238
+ ...(hasOption(parsed, "escalate") ? { escalate: requiredOption(parsed, "escalate") } : {}),
2239
+ ...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {})
2240
+ });
2241
+ throw new AppError("usage", "Use: dd-flow run control pause|stop|resume|reconcile|status", 2);
2242
+ }
2243
+ if (command === "drive") {
2244
+ const action = requiredPosition(parsed, 0, "drive action");
2245
+ const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
2246
+ if (action === "status")
2247
+ return runControllerStatus(context, { ...scope, ...(optionalOption(parsed, "after") ? { after: Number(optionalOption(parsed, "after")) } : {}) });
2248
+ if (action === "launch")
2249
+ return await launchRunController(context, controllerLaunchInput(parsed), prepared.controllerLaunch);
2250
+ if (action === "serve")
2251
+ return await serveRunController(context, { ...scope, controllerId: requiredOption(parsed, "controller-id") });
2252
+ if (action === "answer")
2253
+ return answerRunController(context, controllerAnswerInput(parsed), prepared.controllerAnswer);
2254
+ if (action === "context")
2255
+ return supplyRunControllerContext(context, controllerContextInput(parsed), prepared.controllerContext);
2256
+ throw new AppError("usage", "Use: dd-flow run drive launch|status|answer|context", 2);
2257
+ }
2258
+ return dispatchRun(context, command, parsed, prepared);
1016
2259
  }
1017
2260
  if (family === "stat") {
1018
2261
  return dispatchStat(context, command, parsed);
@@ -1027,22 +2270,122 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1027
2270
  return dispatchDashboard(context, command, parsed);
1028
2271
  }
1029
2272
  if (family === "schema") {
1030
- return dispatchSchema(context, command, parsed);
2273
+ return prepared.schemaValidation;
2274
+ }
2275
+ if (family === "delivery") {
2276
+ const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
2277
+ const runId = resolveRun(context, project.id, requiredPosition(parsed, 0, "run-id")).id, stage = requiredOption(parsed, "stage");
2278
+ const input = { projectRoot, runId, stage, command: quote(["dd-flow", ...args]) };
2279
+ const selected = command === "inputs" ? readDeliveryStageScope(context, input) : assertDeliveryCommandOwner(context, input);
2280
+ if (command === "checks") {
2281
+ if (!prepared.deliveryArtifact)
2282
+ throw new AppError("cli_input_unprepared", "Delivery artifact was not prepared", 1);
2283
+ const checks = prepareDeliveryChecks(context, { ...input, artifact: prepared.deliveryArtifact });
2284
+ await checks.run();
2285
+ return { ok: true, verification: checks.evidence().receipt };
2286
+ }
2287
+ if (command === "execute") {
2288
+ if (!prepared.deliveryApproval)
2289
+ throw new AppError("cli_input_unprepared", "Delivery approval was not prepared", 1);
2290
+ return executeDeliveryOperation(context, { ...input, approval: prepared.deliveryApproval });
2291
+ }
2292
+ const packet = readDescriptorStageInput(context, selected.scope, selected.bundle);
2293
+ const previous = packet.input.predecessor ? readAcceptedStageData(context, { ...selected.scope, stage: packet.input.predecessor.stage }) : null;
2294
+ const details = previous ? JSON.parse(previous.bytes.toString("utf8")).details : null;
2295
+ const operation = details?.decision && details?.artifact ? deliveryApprovalScope(selected.scope, selected.bundle, { operationId: details.decision.operation_id, binding: details.decision.binding,
2296
+ artifact: { subject_ref: details.artifact.subject_ref, version: details.artifact.version, sha256: details.artifact.sha256 } }) : null;
2297
+ if (command === "inputs")
2298
+ return { flow_id: selected.flowId, run_id: runId, stage, user_inputs: selected.users,
2299
+ user_inputs_sha256: selected.userInputsSha256, operation_scope: operation, scope_sha256: operation ? hybridDigest(operation) : null };
2300
+ if (command === "reconcile") {
2301
+ if (!details?.decision)
2302
+ throw new AppError("delivery_stage_invalid", "Original delivery decision is required", 2, { effect: "no_effect" });
2303
+ const policy = prepareDeliveryProjectBinding(selected.bundle.execution.snapshot().scope.workspace, details.decision.policy, details.decision.binding.selector);
2304
+ if (hybridDigest(policy.binding) !== hybridDigest(details.decision.binding))
2305
+ throw new AppError("delivery_stage_invalid", "Original delivery policy changed", 2, { effect: "no_effect" });
2306
+ return observeDeliveryResult(context, { ...selected.scope, binding: policy.binding,
2307
+ assertCurrent() { assertDeliveryCommandOwner(context, input); policy.assertCurrent(); } });
2308
+ }
2309
+ throw new AppError("usage", "Unknown delivery command", 2);
2310
+ }
2311
+ if (family === "interactive") {
2312
+ const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
2313
+ const runId = resolveRun(context, project.id, requiredOption(parsed, "run")).id;
2314
+ if (command === "integrate")
2315
+ return integrateInteractiveFinish(context, { projectRoot, runId,
2316
+ ...(prepared.interactiveGitAgreement ? { agreement: prepared.interactiveGitAgreement } : {}) });
2317
+ if (command === "correction")
2318
+ return registerInteractiveReviewCorrection(context, { projectRoot, runId, reviewWorkId: requiredOption(parsed, "work") });
2319
+ if (command === "re-review")
2320
+ return registerInteractiveCorrectionReview(context, { projectRoot, runId, correctionWorkId: requiredOption(parsed, "work") });
2321
+ if (command === "checks") {
2322
+ if (!prepared.interactiveChecksProposal)
2323
+ throw new AppError("cli_input_unprepared", "Interactive check selection was not prepared", 1);
2324
+ let checks;
2325
+ try {
2326
+ checks = PreparedInteractiveChecks.prepare(context, { projectRoot, runId,
2327
+ stage: requiredOption(parsed, "stage"), ...prepared.interactiveChecksProposal });
2328
+ }
2329
+ catch (error) {
2330
+ if (isAppError(error) && error.code === "interactive_checks_invalid"
2331
+ && error.details.reason === "invalid_check_selection" && error.details.effect === "no_effect") {
2332
+ Object.assign(error.details, { phase: "prepare", recoverable: true,
2333
+ retry_command: runtimeCommand(context, ["interactive", "checks", "--project-root", projectRoot, "--run", runId,
2334
+ "--stage", requiredOption(parsed, "stage"), "--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
2335
+ retry_instruction: "Correct only the rejected check selection JSON: additionalChecks contains unique id,command,purpose entries; IDs match ^CHK-[A-Za-z0-9-]{1,64}$ and must not collide with policy checks. No plan or check execution was retained by this call. Keep existing obligations and use this exact retry_command; this does not authorize replay of executed checks or bypass other refusals." });
2336
+ }
2337
+ throw error;
2338
+ }
2339
+ return checks.execute(context).then(receipts => ({ plan_sha256: checks.sha256, plan: checks.value(), receipts }));
2340
+ }
2341
+ if (command === "register") {
2342
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
2343
+ if (!bundle || bundle.manifest.flow_id !== "interactive")
2344
+ throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
2345
+ return registerInteractiveStageGraph(context, { projectRoot, runId, stage: requiredOption(parsed, "stage"),
2346
+ gitExecutable: readInteractiveWorkGit(bundle).git_executable });
2347
+ }
2348
+ if (command === "git-inputs")
2349
+ return observeInteractiveOriginalGitInputs(context, { projectRoot, runId, stage: requiredOption(parsed, "stage") });
2350
+ if (command === "inputs" || command === "turn" || command === "finish" || command === "scope") {
2351
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
2352
+ if (!bundle || bundle.manifest.flow_id !== "interactive")
2353
+ throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
2354
+ const scope = { projectId: project.id, runId };
2355
+ if (command === "inputs")
2356
+ return readInteractiveUserInputs(context, scope, bundle);
2357
+ if (command === "scope") {
2358
+ if (!prepared.interactiveScopeProposal)
2359
+ throw new AppError("cli_input_unprepared", "Interactive scope proposal was not prepared", 1);
2360
+ const result = PreparedInteractiveMutationScope.prepare(context, scope, bundle, prepared.interactiveScopeProposal).retain(context);
2361
+ return { changed: result.changed, scope_revision: result.scope.revision, user_inputs_sha256: result.scope.inputs.sha256,
2362
+ baseline_sha256: result.scope.baseline.sha256, paths: result.scope.active_paths, needs_scope_refresh: result.scope.needs_scope_refresh };
2363
+ }
2364
+ if (!prepared.interactiveInputBytes)
2365
+ throw new AppError("cli_input_unprepared", "Interactive user bytes were not prepared", 1);
2366
+ return deliverInteractiveUserInput(context, scope, bundle, { projectRoot, intakeFile: requiredOption(parsed, "intake-file"),
2367
+ requestId: requiredOption(parsed, "request-id"), kind: command, bytes: prepared.interactiveInputBytes,
2368
+ expectedInputsSha256: requiredOption(parsed, "expected-inputs-sha256") });
2369
+ }
1031
2370
  }
1032
2371
  if (family === "memory") {
1033
- return dispatchMemory(command, parsed);
2372
+ return dispatchMemory(context, command, parsed, prepared);
1034
2373
  }
1035
2374
  if (family === "migration") {
1036
- return dispatchMigration(context, command, parsed, classification);
2375
+ return dispatchMigration(context, command, parsed, classification, prepared);
1037
2376
  }
1038
2377
  if (family === "codex" && command === "hooks") {
1039
- return dispatchCodexHooks(context, parsed);
2378
+ return dispatchCodexHooks(context, parsed, prepared);
1040
2379
  }
1041
2380
  if (family === "codex" && command === "home") {
1042
- return dispatchCodexHome(context, parsed);
2381
+ return dispatchCodexHome(context, parsed, prepared);
1043
2382
  }
1044
2383
  if (family === "codex" && command === "hook") {
1045
- return dispatchCodexHook(context, parsed, await readStdin(io.stdin));
2384
+ // `handle` is owned by the pre-context ingress above, never generic dispatch.
2385
+ throw new AppError("usage", "Use: dd-flow codex hook handle --event EVENT", 2);
2386
+ }
2387
+ if (family === "zcode" && command === "hook") {
2388
+ return dispatchZcodeHook(context, parsed, await readStdin(io.stdin));
1046
2389
  }
1047
2390
  if (family === "zcode" && command === "event") {
1048
2391
  return dispatchZcodeEvent(context, parsed, await readStdin(io.stdin));
@@ -1068,6 +2411,16 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1068
2411
  throw new AppError("usage", "Unknown opencode usage action", 2);
1069
2412
  return ingestOpenCodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
1070
2413
  }
2414
+ if (family === "droid" && command === "event") {
2415
+ if (requiredPosition(parsed, 0, "droid event action") !== "handle")
2416
+ throw new AppError("usage", "Unknown droid event action", 2);
2417
+ return handleDroidEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
2418
+ }
2419
+ if (family === "droid" && command === "usage") {
2420
+ if (requiredPosition(parsed, 0, "droid usage action") !== "ingest")
2421
+ throw new AppError("usage", "Unknown droid usage action", 2);
2422
+ return ingestDroidUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
2423
+ }
1071
2424
  if (family === "agy" && command === "event")
1072
2425
  return dispatchAgyEvent(context, parsed, await readStdin(io.stdin));
1073
2426
  if (family === "agy" && command === "usage") {
@@ -1076,11 +2429,14 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1076
2429
  return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
1077
2430
  }
1078
2431
  if (family === "worktree") {
1079
- return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
2432
+ return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`), prepared);
1080
2433
  }
1081
2434
  throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
1082
2435
  }
1083
- function dispatchEngine(context, command, parsed, classification) {
2436
+ function engineBindInput(parsed) {
2437
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), packageName: optionalOption(parsed, "package"), engineVersion: requiredOption(parsed, "engine-version"), integrityChecksum: optionalOption(parsed, "integrity-checksum"), reason: requiredOption(parsed, "reason") };
2438
+ }
2439
+ function dispatchEngine(context, command, parsed, classification, prepared) {
1084
2440
  if (command === "install") {
1085
2441
  return installCurrentEngine(context, { force: hasOption(parsed, "force") });
1086
2442
  }
@@ -1105,20 +2461,20 @@ function dispatchEngine(context, command, parsed, classification) {
1105
2461
  });
1106
2462
  }
1107
2463
  if (command === "bind-run") {
1108
- return bindRunEngine(context, {
1109
- projectRoot: requiredOption(parsed, "project-root"),
1110
- runId: requiredPosition(parsed, 0, "run-id"),
1111
- packageName: optionalOption(parsed, "package"),
1112
- engineVersion: requiredOption(parsed, "engine-version"),
1113
- integrityChecksum: optionalOption(parsed, "integrity-checksum"),
1114
- reason: requiredOption(parsed, "reason")
1115
- });
2464
+ return bindRunEngine(context, engineBindInput(parsed), prepared.engineBind);
1116
2465
  }
1117
2466
  throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
1118
2467
  }
1119
- function dispatchMigration(context, command, parsed, classification) {
2468
+ function dispatchMigration(context, command, parsed, classification, prepared) {
1120
2469
  if (command === "impact")
1121
2470
  return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
2471
+ if (command === "readiness") {
2472
+ if (!prepared.upgradeStorageScope)
2473
+ throw new AppError("validation", "Upgrade storage scope must be prepared before publication", 2);
2474
+ // Unlike the initial readonly syntax pass, this connection is admitted
2475
+ // prepared-existing. Physical writer checks still precede receipt writes.
2476
+ return { ok: true, ...PreparedUpgradeStorageReadiness.prepare(context, prepared.upgradeStorageScope).apply(context) };
2477
+ }
1122
2478
  if (command === "plan" || command === "report") {
1123
2479
  return planMigration(context, {
1124
2480
  projectRoot: requiredOption(parsed, "project-root"),
@@ -1132,26 +2488,42 @@ function dispatchMigration(context, command, parsed, classification) {
1132
2488
  });
1133
2489
  }
1134
2490
  if (command === "verify") {
1135
- return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
2491
+ return prepared.migrationVerification ?? verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
1136
2492
  }
1137
2493
  if (command === "apply") {
1138
- return applyMigration(context, {
1139
- projectRoot: requiredOption(parsed, "project-root"),
1140
- sourceVersion: optionalOption(parsed, "source-version"),
1141
- targetVersion: optionalOption(parsed, "target-version"),
1142
- runId: optionalOption(parsed, "run"),
1143
- backupPath: optionalOption(parsed, "backup-path"),
1144
- backupCreatedAt: optionalOption(parsed, "backup-created-at"),
1145
- reportFile: optionalOption(parsed, "file"),
1146
- allowActive: false,
1147
- classification
1148
- });
2494
+ return applyMigration(context, migrationApplyInput(parsed, classification), prepared.migrationApply);
1149
2495
  }
1150
2496
  throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
1151
2497
  }
1152
- function dispatchCanon(context, command, parsed) {
2498
+ function protocolTransitionInput(parsed, scopeProjectRoot) {
2499
+ const reason = optionalOption(parsed, "reason");
2500
+ return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"), to: requiredOption(parsed, "to"), jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]), force: hasOption(parsed, "force"), ...(reason ? { reason } : {}) };
2501
+ }
2502
+ function controllerAnswerInput(parsed) {
2503
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), pauseId: requiredOption(parsed, "pause-id"), answerFile: requiredOption(parsed, "answer-file") };
2504
+ }
2505
+ function controllerLaunchInput(parsed) {
2506
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256"), ...(optionalOption(parsed, "stop-after") ? { stopAfter: optionalOption(parsed, "stop-after") } : {}), ...(optionalOption(parsed, "capture-root") ? { captureRoot: optionalOption(parsed, "capture-root") } : {}) };
2507
+ }
2508
+ function controllerContextInput(parsed) {
2509
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), controllerId: requiredOption(parsed, "controller-id"), requestId: requiredOption(parsed, "request-id"), stage: requiredOption(parsed, "stage"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256") };
2510
+ }
2511
+ function migrationApplyInput(parsed, classification) {
2512
+ return {
2513
+ projectRoot: requiredOption(parsed, "project-root"),
2514
+ sourceVersion: optionalOption(parsed, "source-version"),
2515
+ targetVersion: optionalOption(parsed, "target-version"),
2516
+ runId: optionalOption(parsed, "run"),
2517
+ backupPath: optionalOption(parsed, "backup-path"),
2518
+ backupCreatedAt: optionalOption(parsed, "backup-created-at"),
2519
+ reportFile: optionalOption(parsed, "file"),
2520
+ allowActive: false,
2521
+ classification
2522
+ };
2523
+ }
2524
+ function dispatchCanon(context, command, parsed, prepared) {
1153
2525
  if (command === "register") {
1154
- return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
2526
+ return registerCanonRoot(context, { root: requiredOption(parsed, "root") }, prepared.canonRegistration);
1155
2527
  }
1156
2528
  if (command === "status") {
1157
2529
  return getCanonStatus(context, { root: optionalOption(parsed, "root") });
@@ -1161,6 +2533,15 @@ function dispatchCanon(context, command, parsed) {
1161
2533
  }
1162
2534
  throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
1163
2535
  }
2536
+ function mergeJobTarget(parsed, scopeProjectRoot) {
2537
+ return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed) };
2538
+ }
2539
+ function mergeCancellationInput(parsed, scopeProjectRoot) {
2540
+ return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"), reason: requiredOption(parsed, "reason"), workerId: optionalOption(parsed, "worker-id"), workspacePath: optionalOption(parsed, "path"), force: hasOption(parsed, "force") };
2541
+ }
2542
+ function mergeWaitInput(parsed, scopeProjectRoot) {
2543
+ return { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval"), workspacePath: workspacePathOption(parsed), acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false };
2544
+ }
1164
2545
  function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
1165
2546
  if (hasOption(parsed, "session-id")) {
1166
2547
  throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
@@ -1184,32 +2565,17 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
1184
2565
  });
1185
2566
  }
1186
2567
  if (command === "wait-next") {
1187
- return waitNextMergeJob(context, {
1188
- projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
1189
- workerId: requiredWorkerId(parsed),
1190
- timeoutSeconds: optionalNumber(parsed, "timeout"),
1191
- pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
1192
- workspacePath: workspacePathOption(parsed),
1193
- acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false
1194
- });
2568
+ return waitNextMergeJob(context, mergeWaitInput(parsed, scopeProjectRoot));
1195
2569
  }
1196
2570
  if (command === "complete") {
1197
- const protocolId = requiredPosition(parsed, 0, "protocol-id");
1198
2571
  return completeMergeJob(context, {
1199
- protocolId,
1200
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue complete"),
1201
- workerId: requiredWorkerId(parsed),
1202
- workspacePath: workspacePathOption(parsed),
2572
+ ...mergeJobTarget(parsed, scopeProjectRoot),
1203
2573
  summary: requiredOption(parsed, "summary")
1204
2574
  });
1205
2575
  }
1206
2576
  if (command === "fail") {
1207
- const protocolId = requiredPosition(parsed, 0, "protocol-id");
1208
2577
  return failMergeJob(context, {
1209
- protocolId,
1210
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue fail"),
1211
- workerId: requiredWorkerId(parsed),
1212
- workspacePath: workspacePathOption(parsed),
2578
+ ...mergeJobTarget(parsed, scopeProjectRoot),
1213
2579
  reason: requiredOption(parsed, "reason"),
1214
2580
  requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue")
1215
2581
  });
@@ -1224,15 +2590,7 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
1224
2590
  });
1225
2591
  }
1226
2592
  if (command === "cancel") {
1227
- const protocolId = requiredPosition(parsed, 0, "protocol-id");
1228
- return cancelMergeQueueJob(context, {
1229
- protocolId,
1230
- projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"),
1231
- reason: requiredOption(parsed, "reason"),
1232
- workerId: optionalOption(parsed, "worker-id"),
1233
- workspacePath: optionalOption(parsed, "path"),
1234
- force: hasOption(parsed, "force")
1235
- });
2593
+ return cancelMergeQueueJob(context, mergeCancellationInput(parsed, scopeProjectRoot));
1236
2594
  }
1237
2595
  throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
1238
2596
  }
@@ -1331,7 +2689,7 @@ function dispatchMergeWorker(context, command, parsed) {
1331
2689
  }
1332
2690
  throw new AppError("usage", `Unknown merge-worker command: ${command ?? "<empty>"}`, 2);
1333
2691
  }
1334
- function dispatchMemory(command, parsed) {
2692
+ function dispatchMemory(context, command, parsed, prepared) {
1335
2693
  const subcommand = requiredPosition(parsed, 0, "memory subcommand");
1336
2694
  if (command === "permissions" && subcommand === "preflight") {
1337
2695
  return preflightMemoryPermissions({
@@ -1342,6 +2700,128 @@ function dispatchMemory(command, parsed) {
1342
2700
  mode: requiredOption(parsed, "mode")
1343
2701
  });
1344
2702
  }
2703
+ const scope = () => {
2704
+ const projectRoot = requiredOption(parsed, "project-root");
2705
+ const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
2706
+ return { projectRoot, runId: resolveRun(context, project.id, requiredOption(parsed, "run")).id };
2707
+ };
2708
+ if (command === "repair") {
2709
+ const wave = requiredOption(parsed, "wave");
2710
+ if (wave !== "1" && wave !== "2")
2711
+ throw new AppError("usage", "Memory repairs support only waves 1 and 2", 2);
2712
+ const work = { ...scope(), workId: requiredOption(parsed, "work") };
2713
+ if (subcommand === "baseline")
2714
+ return (wave === "1" ? registerMemoryRepairBaselineFromStage : registerMemorySecondRepairBaselineFromStage)(context, work);
2715
+ if (subcommand === "copy-reserve")
2716
+ return (wave === "1" ? reserveMemoryRepairWriterCopyFromStage : reserveMemorySecondRepairCopyFromStage)(context, work);
2717
+ if (subcommand === "copy-materialize")
2718
+ return (wave === "1" ? materializeMemoryRepairWriterCopyFromStage : materializeMemorySecondRepairCopyFromStage)(context, work);
2719
+ if (subcommand === "copy-dispatch")
2720
+ return (wave === "1" ? prepareMemoryRepairDispatchFromStage : prepareMemorySecondRepairDispatchFromStage)(context, work);
2721
+ const workspaceRoot = requiredOption(parsed, "workspace-root");
2722
+ if (wave === "2") {
2723
+ const bundle = readFlowRunExecutionBundle(context, work);
2724
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.execution.snapshot().scope.workspace !== workspaceRoot)
2725
+ throw new AppError("memory_repair_scope_invalid", "Repair workspace does not match its frozen memory RUN", 2, { phase: "prepare", effect: "no_effect" });
2726
+ }
2727
+ if (subcommand === "current-view")
2728
+ return wave === "1"
2729
+ ? registerMemoryRepairCurrentViewFromStage(context, { ...work, workspaceRoot })
2730
+ : registerMemorySecondRepairCurrentViewFromStage(context, work);
2731
+ const output = { ...work, workspaceRoot, workSessionId: requiredOption(parsed, "work-session") };
2732
+ if (subcommand === "publication-source" && wave === "1")
2733
+ return registerMemoryRepairPublicationSourceFromStage(context, output);
2734
+ if (subcommand === "publication-reserve") {
2735
+ if (!prepared.memoryOperationBytes)
2736
+ throw new AppError("cli_input_unprepared", "Memory repair output bytes were not prepared", 1);
2737
+ return (wave === "1" ? reserveMemoryRepairPublicationAgainstSourceFromStage : reserveMemorySecondRepairPublicationFromStage)(context, { ...output, bytes: prepared.memoryOperationBytes });
2738
+ }
2739
+ if (subcommand === "publication-apply")
2740
+ return (wave === "1" ? applyMemoryRepairPublicationFromStage : applyMemorySecondRepairPublicationFromStage)(context, output);
2741
+ if (subcommand === "publication-finish")
2742
+ return (wave === "1" ? finishMemoryRepairPublicationFromStage : finishMemorySecondRepairPublicationFromStage)(context, output);
2743
+ }
2744
+ if (command === "compose" && subcommand === "register") {
2745
+ if (!prepared.memoryOperationBytes)
2746
+ throw new AppError("cli_input_unprepared", "Memory composition bytes were not prepared", 1);
2747
+ try {
2748
+ return registerMemoryCompositionFromStage(context, { ...scope(), requestKey: requiredOption(parsed, "request-key"), bytes: prepared.memoryOperationBytes });
2749
+ }
2750
+ catch (error) {
2751
+ if (error instanceof AppError && (error.code === "memory_stage_result_invalid"
2752
+ || error.code === "memory_preflight_handler_invalid" && ["unselected_preflight_source", "proposed_source_sha256_mismatch"].includes(String(error.details.reason))) && error.details.phase === "prepare"
2753
+ && error.details.effect === "no_effect" && error.details.recoverable === true) {
2754
+ Object.assign(error.details, compositionProposalRetry(context, parsed));
2755
+ }
2756
+ throw error;
2757
+ }
2758
+ }
2759
+ if (command === "upgrade" && subcommand === "publish")
2760
+ return publishMemoryUpgradeFromStage(context, scope());
2761
+ if (command === "distill" && subcommand === "inputs") {
2762
+ const inputs = readDistillUserInputs(context, scope());
2763
+ return { ...inputs, user_inputs_sha256: hybridDigest(inputs) };
2764
+ }
2765
+ if (command === "fix" && subcommand === "context") {
2766
+ const intake = readMemoryFixSourceIntake(context, scope()), users = readMemoryFixUserInputs(context, scope());
2767
+ return { ...intake, user_inputs: users, user_inputs_sha256: hybridDigest(users) };
2768
+ }
2769
+ if (command === "fix" && subcommand === "scope") {
2770
+ const proposal = prepared.memoryFixProposal;
2771
+ if (!proposal || Object.keys(proposal).sort().join() !== "routing,write_scope")
2772
+ throw new AppError("usage", "Repair scope proposal requires routing and write_scope", 2, { effect: "no_effect" });
2773
+ return prepareMemoryFixScopeEvidence(context, { ...scope(), routing: proposal.routing, writeScope: proposal.write_scope }).snapshot();
2774
+ }
2775
+ if (command === "fix" && subcommand === "publish") {
2776
+ if (!prepared.memoryFixProposal)
2777
+ throw new AppError("cli_input_unprepared", "Repair proposal was not prepared", 1);
2778
+ return prepareMemoryFixPublication(context, { ...scope(), proposal: prepared.memoryFixProposal }).apply();
2779
+ }
2780
+ if (command === "fix" && subcommand === "integrate")
2781
+ return integrateMemoryFixFinish(context, { ...scope(), ...(prepared.memoryFixProposal ? { agreement: prepared.memoryFixProposal } : {}) });
2782
+ if (command === "distill" && subcommand === "publish") {
2783
+ if (!prepared.memoryOperationBytes)
2784
+ throw new AppError("cli_input_unprepared", "Distillation proposal bytes were not prepared", 1);
2785
+ return publishDistillFromStage(context, { ...scope(), bytes: prepared.memoryOperationBytes });
2786
+ }
2787
+ if (command === "verify" && subcommand === "checks")
2788
+ return runMemoryVerificationChecks(context, scope());
2789
+ if (command === "review" && subcommand === "register")
2790
+ return registerMemoryReviewFromStage(context, scope());
2791
+ if (command === "review" && subcommand === "collect") {
2792
+ const wave = collectMemoryInitialReviewFromStage(context, scope());
2793
+ const selected = scope(), bundle = readFlowRunExecutionBundle(context, selected);
2794
+ if (!bundle)
2795
+ throw new AppError("memory_review_handler_invalid", "Registered Memory bundle required", 2);
2796
+ const project = requireProjectByRoot(context, resolveProjectRoot(selected.projectRoot));
2797
+ const retained = PreparedRetainedMemoryReviewResults.prepare(context, PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: selected.runId }, bundle));
2798
+ return { references: wave.references(), projection: wave.resultProjection(),
2799
+ correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: selected.runId, wave: 1, review_sha256: retained.sha256 } };
2800
+ }
2801
+ if (command === "finish" && subcommand === "evidence")
2802
+ return registerMemoryFinishEvidenceFromStage(context, scope());
2803
+ if (command === "finish" && subcommand === "integrate")
2804
+ return integrateMemoryFinishFromStage(context, scope());
2805
+ if (command === "corrections" || command === "review" && ["selective-register", "selective-collect"].includes(subcommand)) {
2806
+ const wave = requiredOption(parsed, "wave");
2807
+ if (wave !== "1" && wave !== "2")
2808
+ throw new AppError("usage", "Memory corrections support only waves 1 and 2", 2);
2809
+ if (command === "corrections" && subcommand === "register") {
2810
+ if (!prepared.memoryOperationBytes)
2811
+ throw new AppError("cli_input_unprepared", "Memory correction plan bytes were not prepared", 1);
2812
+ const register = wave === "1" ? registerMemoryCorrectionsFromStage : registerMemorySecondCorrectionsFromStage;
2813
+ return register(context, { ...scope(), bytes: prepared.memoryOperationBytes });
2814
+ }
2815
+ if (command === "corrections" && subcommand === "checks")
2816
+ return (wave === "1" ? runMemoryCorrectionChecksFromStage : runMemorySecondCorrectionChecksFromStage)(context, scope());
2817
+ if (command === "review" && subcommand === "selective-register")
2818
+ return (wave === "1" ? registerCorrectedMemoryReviewFromStage : registerSecondCorrectedMemoryReviewFromStage)(context, scope());
2819
+ if (command === "review" && subcommand === "selective-collect") {
2820
+ const accepted = (wave === "1" ? collectCorrectedMemoryReviewFromStage : collectSecondCorrectedMemoryReviewFromStage)(context, scope());
2821
+ return { wave: Number(wave), references: accepted.references(), projection: accepted.resultProjection(),
2822
+ ...(wave === "1" ? { correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: scope().runId, wave: 2, review_sha256: accepted.sha256 } } : {}) };
2823
+ }
2824
+ }
1345
2825
  throw new AppError("usage", `Unknown memory command: ${["memory", command, subcommand].filter(Boolean).join(" ")}`, 2);
1346
2826
  }
1347
2827
  function completedCommandExitCode(payload) {
@@ -1350,7 +2830,7 @@ function completedCommandExitCode(payload) {
1350
2830
  const exitCode = payload.exit_code;
1351
2831
  return typeof exitCode === "number" && Number.isInteger(exitCode) ? exitCode : 0;
1352
2832
  }
1353
- function dispatchCleanup(context, command, parsed) {
2833
+ function dispatchCleanup(context, command, parsed, prepared) {
1354
2834
  if (command === "scan") {
1355
2835
  return cleanupScan(context, { projectRoot: requiredOption(parsed, "project-root") });
1356
2836
  }
@@ -1358,6 +2838,7 @@ function dispatchCleanup(context, command, parsed) {
1358
2838
  return cleanupApply(context, {
1359
2839
  projectRoot: requiredOption(parsed, "project-root"),
1360
2840
  planFile: requiredOption(parsed, "plan-file"),
2841
+ prepared,
1361
2842
  reason: requiredOption(parsed, "reason"),
1362
2843
  force: hasOption(parsed, "force")
1363
2844
  });
@@ -1378,12 +2859,14 @@ function dispatchProjectConfig(context, parsed) {
1378
2859
  }
1379
2860
  throw new AppError("usage", `Unknown project config action: ${action}`, 2);
1380
2861
  }
1381
- function dispatchSession(context, command, parsed) {
2862
+ function dispatchSession(context, command, parsed, preparedPayload) {
1382
2863
  if (command === "register") {
1383
2864
  return registerFlowSession(context, {
2865
+ ...(preparedPayload ? { preparedPayload } : {}),
1384
2866
  payloadBase64: optionalOption(parsed, "payload-base64"),
1385
2867
  payloadJson: optionalOption(parsed, "payload-json"),
1386
2868
  payloadFile: optionalOption(parsed, "payload-file"),
2869
+ hookEventId: optionalOption(parsed, "hook-event-id"),
1387
2870
  sessionId: optionalOption(parsed, "session-id")
1388
2871
  });
1389
2872
  }
@@ -1416,7 +2899,47 @@ function dispatchSession(context, command, parsed) {
1416
2899
  }
1417
2900
  throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
1418
2901
  }
1419
- function dispatchRun(context, command, parsed) {
2902
+ function snapshotCreateInput(parsed) {
2903
+ const recoveryId = optionalOption(parsed, "recovery-id"), stageEntry = optionalOption(parsed, "stage-entry");
2904
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), output: requiredOption(parsed, "output"), candidate: parsed.options.has("candidate"), incomplete: parsed.options.has("incomplete"), ...(recoveryId !== undefined ? { recoveryId } : {}), ...(stageEntry !== undefined ? { stageEntry } : {}) };
2905
+ }
2906
+ function snapshotRestoreInput(parsed) {
2907
+ return { snapshot: requiredOption(parsed, "snapshot"), projectRoot: requiredOption(parsed, "project-root"), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), operationalDecisionFile: optionalOption(parsed, "operational-decision-file"), ...(optionalOption(parsed, "recovery-id") ? { recoveryId: requiredOption(parsed, "recovery-id") } : {}) };
2908
+ }
2909
+ function runStartInput(parsed) {
2910
+ const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
2911
+ return { projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: optionalOption(parsed, "workspace-root"), flowKind: requiredOption(parsed, "flow-kind"), subjectType: requiredOption(parsed, "subject-type"), subjectId: requiredOption(parsed, "subject-id"), slug: requiredOption(parsed, "slug"), nextAction: optionalOption(parsed, "next-action"), preset: optionalOption(parsed, "preset"), taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), ...(protocolOverrides !== undefined ? { protocolOverrides } : {}), runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag")) };
2912
+ }
2913
+ function runForkInput(parsed) {
2914
+ return { snapshot: requiredOption(parsed, "from"), output: requiredOption(parsed, "output"), engineVersion: requiredOption(parsed, "engine-version"), requestId: requiredOption(parsed, "request-id"), ...(optionalOption(parsed, "integrity-checksum") ? { integrityChecksum: optionalOption(parsed, "integrity-checksum") } : {}), ...(optionalOption(parsed, "operational-decision-file") ? { operationalDecisionFile: optionalOption(parsed, "operational-decision-file") } : {}) };
2915
+ }
2916
+ function recoveryCaptureInput(parsed) {
2917
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), capturePath: requiredOption(parsed, "capture-path") };
2918
+ }
2919
+ function recoveryResumeInput(parsed) {
2920
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), daemonId: requiredOption(parsed, "daemon-id"), harness: requiredOption(parsed, "harness"), nativeSessionId: requiredOption(parsed, "native-session-id") };
2921
+ }
2922
+ function dispatchRun(context, command, parsed, prepared = {}) {
2923
+ if (command === "reconcile")
2924
+ return reconcileFlowRunHealth(context, { runId: parsed.positional[0], projectRoot: optionalOption(parsed, "project-root"), apply: hasOption(parsed, "apply"), noProgressMs: optionalNumber(parsed, "no-progress-ms") });
2925
+ if (command === "recovery") {
2926
+ const action = requiredPosition(parsed, 0, "recovery action");
2927
+ const projectRoot = requiredOption(parsed, "project-root");
2928
+ const runId = requiredPosition(parsed, 1, "run-id");
2929
+ if (action === "inspect")
2930
+ return inspectRunRecovery(context, { projectRoot, runId });
2931
+ if (action === "begin")
2932
+ return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") }, prepared.recoveryPayload);
2933
+ if (action === "seal")
2934
+ return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") }, prepared.recoveryPayload);
2935
+ if (action === "capture")
2936
+ return recordRecoveryCapture(context, recoveryCaptureInput(parsed), prepared.recoveryCapture);
2937
+ if (action === "resume")
2938
+ return resumeRunRecovery(context, recoveryResumeInput(parsed), prepared.recoveryResume);
2939
+ if (action === "accept")
2940
+ return acceptRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
2941
+ throw new AppError("usage", "Use: dd-flow run recovery inspect|begin|seal|capture|resume|accept", 2);
2942
+ }
1420
2943
  if (command === "snapshot") {
1421
2944
  const action = requiredPosition(parsed, 0, "snapshot action");
1422
2945
  if (action === "bootstrap") {
@@ -1424,59 +2947,31 @@ function dispatchRun(context, command, parsed) {
1424
2947
  if (bootstrapAction === "create")
1425
2948
  return createEvalBootstrapSnapshot(context, {
1426
2949
  projectRoot: requiredOption(parsed, "project-root"),
1427
- output: requiredOption(parsed, "output")
2950
+ output: requiredOption(parsed, "output"),
2951
+ flow: optionalOption(parsed, "flow")
1428
2952
  });
1429
2953
  if (bootstrapAction === "restore")
1430
- return restoreEvalBootstrapSnapshot(context, {
1431
- snapshot: requiredOption(parsed, "snapshot"),
1432
- projectRoot: requiredOption(parsed, "project-root")
1433
- });
2954
+ return restoreEvalBootstrapSnapshot(context, snapshotRestoreInput(parsed), prepared.bootstrapRestore);
1434
2955
  throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
1435
2956
  }
1436
2957
  if (action === "create") {
1437
- const candidate = parsed.options.has("candidate");
1438
- const stageEntry = optionalOption(parsed, "stage-entry");
1439
- if (candidate && stageEntry !== undefined)
1440
- throw new AppError("usage", "Use either --candidate or --stage-entry, not both", 2);
1441
- if (!candidate && stageEntry === undefined)
1442
- throw new AppError("usage", "Snapshot create requires --candidate or --stage-entry", 2);
1443
- const input = {
1444
- projectRoot: requiredOption(parsed, "project-root"),
1445
- runId: requiredPosition(parsed, 1, "run-id"),
1446
- output: requiredOption(parsed, "output")
1447
- };
1448
- return candidate
1449
- ? createEvalRunSnapshot(context, { ...input, candidate: true })
1450
- : createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
2958
+ return createEvalRunSnapshot(context, snapshotCreateInput(parsed));
1451
2959
  }
1452
2960
  if (action === "restore")
1453
- return restoreEvalRunSnapshot(context, {
1454
- snapshot: requiredOption(parsed, "snapshot"),
1455
- projectRoot: requiredOption(parsed, "project-root")
1456
- });
2961
+ return restoreEvalRunSnapshot(context, snapshotRestoreInput(parsed), prepared.snapshotRestore);
1457
2962
  throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore | dd-flow run snapshot create <RUN> (--stage-entry <stage>|--candidate) --project-root <path> --output <directory> | dd-flow run snapshot restore --snapshot <directory> --project-root <path>", 2);
1458
2963
  }
2964
+ if (command === "fork") {
2965
+ return forkRunFromSnapshot(context, runForkInput(parsed), prepared.runFork);
2966
+ }
1459
2967
  if (command === "prepare-vnext-specify") {
1460
- return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug") });
2968
+ return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug"), executionRoutingFile: optionalOption(parsed, "execution-routing-file") });
1461
2969
  }
1462
2970
  if (command === "start") {
1463
2971
  if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
1464
2972
  assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
1465
2973
  }
1466
- const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
1467
- return startFlowRun(context, {
1468
- projectRoot: requiredOption(parsed, "project-root"),
1469
- workspaceRoot: optionalOption(parsed, "workspace-root"),
1470
- flowKind: requiredOption(parsed, "flow-kind"),
1471
- subjectType: requiredOption(parsed, "subject-type"),
1472
- subjectId: requiredOption(parsed, "subject-id"),
1473
- slug: requiredOption(parsed, "slug"),
1474
- nextAction: optionalOption(parsed, "next-action"),
1475
- preset: optionalOption(parsed, "preset"),
1476
- taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")),
1477
- ...(protocolOverrides ? { protocolOverrides } : {}),
1478
- runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag"))
1479
- });
2974
+ return startFlowRun(context, prepared.runStart?.input ?? runStartInput(parsed), prepared.runStart?.value);
1480
2975
  }
1481
2976
  if (command === "status") {
1482
2977
  return getFlowRunStatus(context, {
@@ -1546,88 +3041,84 @@ function dispatchRun(context, command, parsed) {
1546
3041
  throw new AppError("usage", `Unknown run flags command: ${action}`, 2);
1547
3042
  }
1548
3043
  if (command === "attach-stage") {
1549
- return attachFlowRunStage(context, {
1550
- projectRoot: requiredOption(parsed, "project-root"),
1551
- runId: requiredPosition(parsed, 0, "run-id"),
1552
- stage: requiredOption(parsed, "stage"),
1553
- dir: requiredOption(parsed, "dir"),
1554
- status: requiredOption(parsed, "status"),
1555
- dataSchemaId: optionalOption(parsed, "data-schema-id")
1556
- });
3044
+ return attachFlowRunStage(context, stageAttachmentInput(parsed));
1557
3045
  }
1558
3046
  if (command === "complete-stage") {
1559
- const stageReport = optionalOption(parsed, "stage-report") ?? optionalOption(parsed, "dashboard");
1560
- return completeFlowRunStage(context, {
1561
- projectRoot: requiredOption(parsed, "project-root"),
1562
- runId: requiredPosition(parsed, 0, "run-id"),
1563
- stage: requiredOption(parsed, "stage"),
1564
- status: requiredOption(parsed, "status"),
1565
- stageReport,
1566
- data: optionalOption(parsed, "data"),
1567
- dataSchemaId: optionalOption(parsed, "data-schema-id"),
1568
- report: optionalOption(parsed, "report"),
1569
- aliases: allOptions(parsed, "alias")
1570
- });
3047
+ const input = stageCompletionInput(parsed);
3048
+ return completeFlowRunStage(context, input, prepareRawFlowRunStageCompletion(context, input));
1571
3049
  }
1572
3050
  if (command === "complete") {
1573
3051
  throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
1574
3052
  }
1575
3053
  if (command === "override") {
1576
- const status = requiredOption(parsed, "status");
1577
- if (!['cancelled', 'failed'].includes(status)) {
1578
- throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
1579
- }
1580
- return completeFlowRun(context, {
1581
- projectRoot: requiredOption(parsed, "project-root"),
1582
- runId: requiredPosition(parsed, 0, "run-id"),
1583
- status,
1584
- verdict: optionalOption(parsed, "verdict"),
1585
- nextAction: optionalOption(parsed, "next-action"),
1586
- manualOverrideReason: requiredOption(parsed, "reason")
1587
- });
3054
+ return completeFlowRun(context, runOverrideInput(parsed));
1588
3055
  }
1589
3056
  throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
1590
3057
  }
1591
- async function dispatchRuntime(context, command, parsed, progress) {
3058
+ function stageAttachmentInput(parsed) {
3059
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), dir: requiredOption(parsed, "dir"), status: requiredOption(parsed, "status"), dataSchemaId: optionalOption(parsed, "data-schema-id") };
3060
+ }
3061
+ function runOverrideInput(parsed) {
3062
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), status: requiredOption(parsed, "status"), verdict: optionalOption(parsed, "verdict"), nextAction: optionalOption(parsed, "next-action"), manualOverrideReason: requiredOption(parsed, "reason") };
3063
+ }
3064
+ function stageCompletionInput(parsed) {
3065
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), status: requiredOption(parsed, "status"), stageReport: optionalOption(parsed, "stage-report") ?? optionalOption(parsed, "dashboard"), data: optionalOption(parsed, "data"), dataSchemaId: optionalOption(parsed, "data-schema-id"), report: optionalOption(parsed, "report"), aliases: allOptions(parsed, "alias") };
3066
+ }
3067
+ function runtimeServiceInput(parsed) {
3068
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), command: requiredOption(parsed, "command"), ports: requiredOption(parsed, "ports").split(","), readyPort: requiredOption(parsed, "ready-port"), readyPath: optionalOption(parsed, "ready-path") ?? "/", timeoutMs: optionalPositiveNumber(parsed, "ready-timeout-ms", true) ?? 30_000 };
3069
+ }
3070
+ function runtimeScopeResumeInput(parsed) {
3071
+ return { scopeId: requiredOption(parsed, "scope-id"), requestId: requiredOption(parsed, "request-id"), generation: Number(requiredOption(parsed, "generation")), captureKey: requiredOption(parsed, "capture-key") };
3072
+ }
3073
+ async function dispatchRuntime(context, command, parsed, progress, prepared = {}) {
3074
+ if (command === "scope") {
3075
+ if (parsed.positional.length === 1 && parsed.positional[0] === "reconcile") {
3076
+ if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3077
+ throw new AppError("usage", "Use runtime scope reconcile --scope-id ID --generation N --request-id ID", 2);
3078
+ return { ok: true, ...await reconcileRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id")) };
3079
+ }
3080
+ if (parsed.positional.length === 1 && parsed.positional[0] === "resume") {
3081
+ if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation", "capture-key"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3082
+ throw new AppError("usage", "Use runtime scope resume --scope-id ID --request-id ID --generation N --capture-key SHA256", 2);
3083
+ return { ok: true, ...await launchRuntimeScopeResume(context, runtimeScopeResumeInput(parsed)) };
3084
+ }
3085
+ if (parsed.positional.length === 1 && parsed.positional[0] === "control") {
3086
+ if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "mode", "manifest-json"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3087
+ throw new AppError("usage", "Use runtime scope control --scope-id ID --request-id ID --mode pause|stop [--manifest-json JSON]", 2);
3088
+ const mode = requiredOption(parsed, "mode");
3089
+ if (mode !== "pause" && mode !== "stop")
3090
+ throw new AppError("usage", "Scope control mode must be pause or stop", 2);
3091
+ return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode, undefined, undefined, undefined, prepared.runtimeManifest) };
3092
+ }
3093
+ if (parsed.positional.length === 1 && parsed.positional[0] === "serve") {
3094
+ if ([...parsed.options.keys()].some(key => !["scope-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3095
+ throw new AppError("usage", "Invalid internal scope worker arguments", 2);
3096
+ return { ok: true, ...await serveRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation"))) };
3097
+ }
3098
+ if (parsed.positional.length === 1 && parsed.positional[0] === "stop") {
3099
+ if ([...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3100
+ throw new AppError("usage", "Usage: dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID", 2);
3101
+ return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
3102
+ }
3103
+ if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
3104
+ if ([...parsed.options.keys()].some(key => !["scope-id", "scope-ids-json"].includes(key)) || parsed.options.size !== 1 || [...parsed.options.values()].some(values => values.length !== 1))
3105
+ throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
3106
+ const ids = prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
3107
+ if (parsed.options.has("scope-ids-json")) {
3108
+ return { ok: true, scopes: ids.map(id => runtimeBudgetStatus(context, id)) };
3109
+ }
3110
+ return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
3111
+ }
3112
+ if (parsed.positional.length !== 1 || parsed.positional[0] !== "fence" || [...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
3113
+ throw new AppError("usage", "Usage: dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID", 2);
3114
+ return { ok: true, ...fenceRuntimeBudget(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
3115
+ }
1592
3116
  if (command !== "process")
1593
- throw new AppError("usage", "Usage: dd-flow runtime process <start|stop|register|confirm|heartbeat|finish|status|reconcile>", 2);
3117
+ throw new AppError("usage", "Usage: dd-flow runtime <scope|process>; see dd-flow runtime scope --help", 2);
1594
3118
  const action = requiredPosition(parsed, 0, "runtime process action");
1595
3119
  if (action === "start")
1596
- return await startRuntimeService(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), command: requiredOption(parsed, "command"), ports: requiredOption(parsed, "ports").split(","), readyPort: requiredOption(parsed, "ready-port"), readyPath: optionalOption(parsed, "ready-path") ?? "/", timeoutMs: optionalPositiveNumber(parsed, "ready-timeout-ms", true) ?? 30_000, progress });
1597
- if (action === "stop")
1598
- return { ok: true, process: await stopManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(optionalPositiveNumber(parsed, "grace-ms", true) ? { graceMs: optionalPositiveNumber(parsed, "grace-ms", true) } : {}) }) };
1599
- if (action === "register") {
1600
- const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
1601
- const process = registerManagedProcess(context, {
1602
- kind: requiredOption(parsed, "kind"), ownerId: requiredOption(parsed, "owner"),
1603
- ownerPid: optionalPositiveNumber(parsed, "owner-pid", true),
1604
- projectId: optionalOption(parsed, "project-id") ?? null, runId: optionalOption(parsed, "run") ?? null,
1605
- workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
1606
- operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
1607
- stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs ? { leaseMs } : {})
1608
- });
1609
- return { ok: true, process };
1610
- }
1611
- if (action === "confirm") {
1612
- const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
1613
- const processGroupId = optionalPositiveNumber(parsed, "process-group-id", true);
1614
- return { ok: true, process: confirmManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ownerPid: optionalPositiveNumber(parsed, "owner-pid", true), pid: optionalPositiveNumber(parsed, "pid", true) ?? 0, ...(processGroupId ? { processGroupId } : {}), ...(leaseMs ? { leaseMs } : {}) }) };
1615
- }
1616
- if (action === "heartbeat") {
1617
- const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
1618
- return { ok: heartbeatManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs ? { leaseMs } : {}) }) };
1619
- }
1620
- if (action === "finish") {
1621
- const state = requiredOption(parsed, "state");
1622
- if (state !== "stopped" && state !== "failed")
1623
- throw new AppError("validation", "--state must be stopped or failed", 2);
1624
- return { ok: finishManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state: state, reason: optionalOption(parsed, "reason") ?? null }) };
1625
- }
1626
- if (action === "status")
1627
- return { ok: true, processes: managedProcessStatus(context) };
1628
- if (action === "reconcile")
1629
- return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"), optionalPositiveNumber(parsed, "grace-ms", true)) };
1630
- throw new AppError("usage", "Usage: dd-flow runtime process <start|stop|register|confirm|heartbeat|finish|status|reconcile>", 2);
3120
+ return await startRuntimeService(context, { ...runtimeServiceInput(parsed), progress });
3121
+ return await dispatchRuntimeProcess(context, action, parsed, prepared);
1631
3122
  }
1632
3123
  function dispatchStat(context, command, parsed) {
1633
3124
  if (command === "usage") {
@@ -1661,7 +3152,7 @@ function dispatchCmux(context, parsed) {
1661
3152
  }
1662
3153
  throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
1663
3154
  }
1664
- function dispatchDashboard(context, command, parsed) {
3155
+ async function dispatchDashboard(context, command, parsed) {
1665
3156
  const action = dashboardAction(command);
1666
3157
  if (command === "data") {
1667
3158
  const target = resolveDashboardTarget(context, {
@@ -1696,7 +3187,7 @@ function dispatchDashboard(context, command, parsed) {
1696
3187
  projectRoot: optionalOption(parsed, "project-root"),
1697
3188
  protocol: optionalOption(parsed, "protocol")
1698
3189
  });
1699
- return withDashboardGuidance(action, target, openDashboardForResolvedTarget(context, target, parsed));
3190
+ return withDashboardGuidance(action, target, await openDashboardForResolvedTarget(context, target, parsed));
1700
3191
  }
1701
3192
  if (command === "refresh") {
1702
3193
  const open = optionalOption(parsed, "open");
@@ -1710,7 +3201,7 @@ function dispatchDashboard(context, command, parsed) {
1710
3201
  protocol: optionalOption(parsed, "protocol"),
1711
3202
  all: hasOption(parsed, "all")
1712
3203
  });
1713
- return withDashboardGuidance(action, target, refreshDashboardForResolvedTarget(context, target, parsed, open));
3204
+ return withDashboardGuidance(action, target, await refreshDashboardForResolvedTarget(context, target, parsed, open));
1714
3205
  }
1715
3206
  if (command === "refresh-global") {
1716
3207
  const target = resolveDashboardTarget(context, { action: "refresh", global: true });
@@ -1814,7 +3305,9 @@ function dispatchSchema(context, command, parsed) {
1814
3305
  // searching project directories: run_id is a structural field in the
1815
3306
  // submitted artifact and the database owns its materialized run_root.
1816
3307
  const explicitRunId = optionalOption(parsed, "run");
1817
- const declaredRunId = explicitRunId ?? readRunId(file);
3308
+ const data = prepareJsonFile(file, "schema input").value;
3309
+ const declared = data && typeof data === "object" && "run_id" in data ? data.run_id : undefined;
3310
+ const declaredRunId = explicitRunId ?? (typeof declared === "string" && declared.trim() ? declared : undefined);
1818
3311
  const fileRunRoot = path.basename(file) === "run.json" ? path.dirname(path.resolve(file)) : undefined;
1819
3312
  const bound = projectRoot
1820
3313
  ? context.db.get("SELECT r.id, r.run_root FROM runs r JOIN projects p ON p.id = r.project_id WHERE p.root = ? AND (r.id = ? OR r.run_root = ?) LIMIT 1", [fs.realpathSync(projectRoot), declaredRunId ?? "", fileRunRoot ?? ""])
@@ -1822,6 +3315,7 @@ function dispatchSchema(context, command, parsed) {
1822
3315
  return validateSchema({
1823
3316
  schemaName: requiredOption(parsed, "schema"),
1824
3317
  file,
3318
+ data,
1825
3319
  ...(schemaDir ? { schemaDir } : {}),
1826
3320
  ...(projectRoot ? { projectRoot } : {}),
1827
3321
  ...(declaredRunId || bound?.id ? { runId: declaredRunId ?? bound.id } : {}),
@@ -1831,14 +3325,11 @@ function dispatchSchema(context, command, parsed) {
1831
3325
  }
1832
3326
  throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
1833
3327
  }
1834
- function readRunId(file) {
1835
- try {
1836
- const value = JSON.parse(fs.readFileSync(file, "utf8"));
1837
- return typeof value.run_id === "string" && value.run_id.trim() ? value.run_id : undefined;
1838
- }
1839
- catch {
1840
- return undefined;
1841
- }
3328
+ function laneWorkspaceInput(parsed) {
3329
+ return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workspacePath: requiredOption(parsed, "path"), branch: optionalOption(parsed, "branch") };
3330
+ }
3331
+ function laneWaiterCancelInput(parsed) {
3332
+ return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"), reason: requiredOption(parsed, "reason") };
1842
3333
  }
1843
3334
  async function dispatchLane(context, command, parsed) {
1844
3335
  if (command === "status") {
@@ -1850,24 +3341,14 @@ async function dispatchLane(context, command, parsed) {
1850
3341
  if (command === "waiter") {
1851
3342
  const action = requiredPosition(parsed, 0, "lane waiter action");
1852
3343
  if (action === "cancel") {
1853
- return cancelLaneWaiter(context, {
1854
- projectRoot: requiredOption(parsed, "project-root"),
1855
- lane: requiredOption(parsed, "lane"),
1856
- workerId: requiredOption(parsed, "worker-id"),
1857
- reason: requiredOption(parsed, "reason")
1858
- });
3344
+ return cancelLaneWaiter(context, laneWaiterCancelInput(parsed));
1859
3345
  }
1860
3346
  throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
1861
3347
  }
1862
3348
  if (command === "workspace") {
1863
3349
  const action = requiredPosition(parsed, 0, "lane workspace action");
1864
3350
  if (action === "set") {
1865
- return setLaneWorkspace(context, {
1866
- projectRoot: requiredOption(parsed, "project-root"),
1867
- lane: requiredOption(parsed, "lane"),
1868
- workspacePath: requiredOption(parsed, "path"),
1869
- branch: optionalOption(parsed, "branch")
1870
- });
3351
+ return setLaneWorkspace(context, laneWorkspaceInput(parsed));
1871
3352
  }
1872
3353
  if (action === "check") {
1873
3354
  return checkLaneWorkspace(context, {
@@ -1939,7 +3420,7 @@ async function dispatchLane(context, command, parsed) {
1939
3420
  }
1940
3421
  throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
1941
3422
  }
1942
- function dispatchCodexHooks(context, parsed) {
3423
+ function dispatchCodexHooks(context, parsed, prepared) {
1943
3424
  const action = requiredPosition(parsed, 0, "codex hooks action");
1944
3425
  if (action === "print") {
1945
3426
  return printCodexHooks(context, {
@@ -1961,7 +3442,7 @@ function dispatchCodexHooks(context, parsed) {
1961
3442
  target: optionalOption(parsed, "target"),
1962
3443
  profile: optionalOption(parsed, "profile"),
1963
3444
  yes: hasOption(parsed, "yes")
1964
- });
3445
+ }, prepared.codexHooks);
1965
3446
  }
1966
3447
  if (action === "remove") {
1967
3448
  return removeCodexHooks(context, {
@@ -1969,11 +3450,14 @@ function dispatchCodexHooks(context, parsed) {
1969
3450
  target: optionalOption(parsed, "target"),
1970
3451
  profile: optionalOption(parsed, "profile"),
1971
3452
  yes: hasOption(parsed, "yes")
1972
- });
3453
+ }, prepared.codexHooks);
1973
3454
  }
1974
3455
  throw new AppError("usage", `Unknown codex hooks action: ${action}`, 2);
1975
3456
  }
1976
- function dispatchCodexHome(context, parsed) {
3457
+ function codexHomeInput(parsed) {
3458
+ return { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), sourceHome: optionalOption(parsed, "source-home"), targetHome: optionalOption(parsed, "target-home") };
3459
+ }
3460
+ function dispatchCodexHome(context, parsed, prepared) {
1977
3461
  const action = requiredPosition(parsed, 0, "codex home action");
1978
3462
  if (action === "plan") {
1979
3463
  return planCodexHome(context, {
@@ -1984,12 +3468,7 @@ function dispatchCodexHome(context, parsed) {
1984
3468
  });
1985
3469
  }
1986
3470
  if (action === "init") {
1987
- return initCodexHome(context, {
1988
- projectRoot: requiredOption(parsed, "project-root"),
1989
- profile: optionalOption(parsed, "profile"),
1990
- sourceHome: optionalOption(parsed, "source-home"),
1991
- targetHome: optionalOption(parsed, "target-home")
1992
- });
3471
+ return initCodexHome(context, codexHomeInput(parsed), prepared.codexHome);
1993
3472
  }
1994
3473
  if (action === "status") {
1995
3474
  return getCodexHomeStatus(context, {
@@ -2012,23 +3491,6 @@ function dispatchCodexHome(context, parsed) {
2012
3491
  }
2013
3492
  throw new AppError("usage", `Unknown codex home action: ${action}`, 2);
2014
3493
  }
2015
- function dispatchCodexHook(context, parsed, stdin) {
2016
- const action = requiredPosition(parsed, 0, "codex hook action");
2017
- if (action === "handle") {
2018
- const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
2019
- const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
2020
- const result = handleCodexHook(hookContext, {
2021
- event: requiredOption(parsed, "event"),
2022
- projectRoot: optionalOption(parsed, "project-root"),
2023
- stdin
2024
- });
2025
- if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
2026
- return { hookSpecificOutput: result.hookSpecificOutput };
2027
- }
2028
- return {};
2029
- }
2030
- throw new AppError("usage", `Unknown codex hook action: ${action}`, 2);
2031
- }
2032
3494
  function dispatchZcodeEvent(context, parsed, stdin) {
2033
3495
  const action = requiredPosition(parsed, 0, "zcode event action");
2034
3496
  if (action === "handle") {
@@ -2039,6 +3501,15 @@ function dispatchZcodeEvent(context, parsed, stdin) {
2039
3501
  }
2040
3502
  throw new AppError("usage", `Unknown zcode event action: ${action}`, 2);
2041
3503
  }
3504
+ function dispatchZcodeHook(context, parsed, stdin) {
3505
+ if (requiredPosition(parsed, 0, "zcode hook action") !== "handle")
3506
+ throw new AppError("usage", "Unknown zcode hook action", 2);
3507
+ const result = handleZcodeHook(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
3508
+ if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
3509
+ return { hookSpecificOutput: result.hookSpecificOutput };
3510
+ }
3511
+ return {};
3512
+ }
2042
3513
  function dispatchGrokEvent(context, parsed, stdin) {
2043
3514
  const action = requiredPosition(parsed, 0, "grok event action");
2044
3515
  if (action === "handle") {
@@ -2062,7 +3533,7 @@ function dispatchAgyEvent(context, parsed, stdin) {
2062
3533
  return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
2063
3534
  throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
2064
3535
  }
2065
- function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
3536
+ function dispatchWorktree(context, command, parsed, scopeProjectRoot, prepared) {
2066
3537
  if (command === "plan") {
2067
3538
  return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
2068
3539
  }
@@ -2073,13 +3544,13 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
2073
3544
  branch: requiredOption(parsed, "branch"),
2074
3545
  base: requiredOption(parsed, "base"),
2075
3546
  path: requiredOption(parsed, "path")
2076
- });
3547
+ }, prepared.worktreeCreate);
2077
3548
  }
2078
3549
  if (command === "status") {
2079
3550
  return getWorktreeStatus(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
2080
3551
  }
2081
3552
  if (command === "bootstrap") {
2082
- return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
3553
+ return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot }, prepared.worktreeBootstrap);
2083
3554
  }
2084
3555
  if (command === "close") {
2085
3556
  return closeWorktree(context, {
@@ -2091,52 +3562,50 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
2091
3562
  throw new AppError("usage", `Unknown worktree command: ${command ?? "<empty>"}`, 2);
2092
3563
  }
2093
3564
  function dispatchPlanItem(context, parsed, projectRoot) {
3565
+ return updatePlanItem(context, planItemInput(parsed, projectRoot));
3566
+ }
3567
+ function planItemInput(parsed, projectRoot) {
2094
3568
  const action = requiredPosition(parsed, 0, "plan item action");
2095
3569
  const protocolId = requiredPosition(parsed, 1, "protocol-id");
2096
3570
  const itemId = requiredPosition(parsed, 2, "item-id");
2097
3571
  if (action === "start") {
2098
- return startPlanItem(context, { protocolId, projectRoot, itemId });
3572
+ return { protocolId, projectRoot, itemId, action };
2099
3573
  }
2100
3574
  if (action === "done") {
2101
- return completePlanItem(context, {
3575
+ return {
3576
+ action,
2102
3577
  protocolId,
2103
3578
  projectRoot,
2104
3579
  itemId,
2105
3580
  summary: requiredOption(parsed, "summary"),
2106
3581
  evidence: parsed.options.get("evidence") ?? []
2107
- });
3582
+ };
2108
3583
  }
2109
3584
  if (action === "block") {
2110
- return blockPlanItem(context, {
3585
+ return {
3586
+ action,
2111
3587
  protocolId,
2112
3588
  projectRoot,
2113
3589
  itemId,
2114
3590
  reason: requiredOption(parsed, "reason"),
2115
3591
  userRequired: parseBoolean(requiredOption(parsed, "user-required"), "user-required")
2116
- });
3592
+ };
2117
3593
  }
2118
3594
  if (action === "skip") {
2119
- return skipPlanItem(context, {
3595
+ return {
3596
+ action,
2120
3597
  protocolId,
2121
3598
  projectRoot,
2122
3599
  itemId,
2123
3600
  reason: requiredOption(parsed, "reason")
2124
- });
3601
+ };
2125
3602
  }
2126
3603
  throw new AppError("usage", `Unknown plan item action: ${action}`, 2);
2127
3604
  }
2128
3605
  function readOptionalJsonFile(file) {
2129
3606
  if (!file)
2130
3607
  return undefined;
2131
- if (!fs.existsSync(file)) {
2132
- throw new AppError("not_found", `JSON file does not exist: ${file}`, 1);
2133
- }
2134
- try {
2135
- return JSON.parse(fs.readFileSync(file, "utf8"));
2136
- }
2137
- catch {
2138
- throw new AppError("validation", `JSON file is malformed: ${file}`, 2);
2139
- }
3608
+ return prepareJsonFile(file, "JSON input").value;
2140
3609
  }
2141
3610
  function parseFlowFlagOverrides(values) {
2142
3611
  const result = {};
@@ -2164,29 +3633,17 @@ function parseArgs(args) {
2164
3633
  function requiredPosition(parsed, index, label) {
2165
3634
  const value = parsed.positional[index];
2166
3635
  if (!value) {
2167
- throw new AppError("usage", `Missing required argument: ${label}`, 2);
3636
+ throw new AppError("usage", `Missing required argument: ${label}`, 2, { parameter: label, reason: "missing_required_argument" });
2168
3637
  }
2169
3638
  return value;
2170
3639
  }
2171
- function optionalOption(parsed, key) {
2172
- const values = parsed.options.get(key);
2173
- const value = values?.[values.length - 1];
2174
- return value === flagValue ? undefined : value;
2175
- }
2176
3640
  function allOptions(parsed, key) {
2177
3641
  return (parsed.options.get(key) ?? []).filter((value) => value !== flagValue);
2178
3642
  }
2179
- function requiredOption(parsed, key) {
2180
- const value = optionalOption(parsed, key);
2181
- if (!value) {
2182
- throw new AppError("usage", `Missing required option: --${key}`, 2);
2183
- }
2184
- return value;
2185
- }
2186
3643
  function requiredScopeProjectRoot(root, operation) {
2187
3644
  if (root)
2188
3645
  return root;
2189
- throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2);
3646
+ throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2, { parameter: "project-root", reason: "missing_required_option" });
2190
3647
  }
2191
3648
  function requiredOptionOrScope(parsed, root, key) {
2192
3649
  return root ?? requiredOption(parsed, key);
@@ -2205,7 +3662,7 @@ function requiredOptionAlias(parsed, keys) {
2205
3662
  if (value)
2206
3663
  return value;
2207
3664
  }
2208
- throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2);
3665
+ throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2, { parameter: [...keys].sort(), reason: "missing_required_option" });
2209
3666
  }
2210
3667
  function hasOption(parsed, key) {
2211
3668
  return parsed.options.has(key);
@@ -2222,26 +3679,6 @@ function parseBoolean(value, key) {
2222
3679
  function parseOptionalBoolean(value, key) {
2223
3680
  return typeof value === "string" ? parseBoolean(value, key) : undefined;
2224
3681
  }
2225
- function optionalNumber(parsed, key) {
2226
- const value = optionalOption(parsed, key);
2227
- if (typeof value === "undefined") {
2228
- return undefined;
2229
- }
2230
- const parsedNumber = Number(value);
2231
- if (!Number.isFinite(parsedNumber)) {
2232
- throw new AppError("validation", `--${key} must be a number`, 2);
2233
- }
2234
- return parsedNumber;
2235
- }
2236
- function optionalPositiveNumber(parsed, key, integer = false) {
2237
- const value = optionalNumber(parsed, key);
2238
- if (typeof value === "undefined")
2239
- return undefined;
2240
- if (value <= 0 || (integer && !Number.isInteger(value))) {
2241
- throw new AppError("validation", `--${key} must be a positive${integer ? " integer" : ""} number`, 2);
2242
- }
2243
- return value;
2244
- }
2245
3682
  function requiredWorkerId(parsed) {
2246
3683
  return requiredOption(parsed, "worker-id");
2247
3684
  }
@@ -2255,16 +3692,21 @@ function parseWorktreePolicy(value) {
2255
3692
  }
2256
3693
  throw new AppError("validation", "--worktree must be keep or remove", 2);
2257
3694
  }
2258
- async function readStdin(stream) {
3695
+ async function readStdin(stream, maxBytes) {
2259
3696
  if (!stream) {
2260
3697
  return "";
2261
3698
  }
2262
3699
  let data = "";
2263
- stream.setEncoding("utf8");
3700
+ if (maxBytes !== undefined)
3701
+ return readBoundedUtf8Stream(stream, maxBytes);
3702
+ const hash = createHash("sha256"), decoder = new TextDecoder("utf-8", { ignoreBOM: true });
2264
3703
  for await (const chunk of stream) {
2265
- data += chunk;
3704
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
3705
+ hash.update(bytes);
3706
+ data += decoder.decode(bytes, { stream: true });
2266
3707
  }
2267
- return data;
3708
+ recordConsumedInputDigest(hash.digest("hex"));
3709
+ return data + decoder.decode();
2268
3710
  }
2269
3711
  function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
2270
3712
  try {
@@ -2275,7 +3717,8 @@ function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
2275
3717
  const projectRoot = projectRootForMutation(context, args, result, resolvedScope);
2276
3718
  if (!projectRoot)
2277
3719
  return [];
2278
- autoRefreshDashboards(context, { projectRoot });
3720
+ const runId = result && typeof result === "object" && "run_id" in result && typeof result.run_id === "string" ? result.run_id : undefined;
3721
+ autoRefreshDashboards(context, { projectRoot, ...(runId ? { runId } : {}) });
2279
3722
  return [];
2280
3723
  }
2281
3724
  catch (error) {
@@ -2337,7 +3780,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
2337
3780
  return projectRootForMergeQueueMutation(context, command, parsed);
2338
3781
  }
2339
3782
  if (family === "merge" && ["one-shot", "apply", "repair", "request"].includes(command ?? "")) {
2340
- return requiredOption(parsed, "project-root");
3783
+ return resolvedScope ?? requiredOption(parsed, "project-root");
2341
3784
  }
2342
3785
  if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
2343
3786
  return requiredOption(parsed, "project-root");
@@ -2355,7 +3798,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
2355
3798
  return requiredOption(parsed, "project-root");
2356
3799
  }
2357
3800
  if (family === "stage" && ["start", "pause", "resume", "finish"].includes(command ?? "")) {
2358
- return requiredOption(parsed, "project-root");
3801
+ return resolvedScope ?? requiredOption(parsed, "project-root");
2359
3802
  }
2360
3803
  if (family === "worktree" && ["create", "bootstrap", "close"].includes(command ?? "")) {
2361
3804
  return protocolProjectRoot(context, requiredOption(parsed, "protocol-id"));
@@ -2369,7 +3812,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
2369
3812
  if (family === "opencode" && ((command === "event" && requiredPosition(parsed, 0, "opencode event action") === "handle") || command === "usage")) {
2370
3813
  return requiredOption(parsed, "project-root");
2371
3814
  }
2372
- if (family === "agy" && ((command === "event" && requiredPosition(parsed, 0, "agy event action") === "handle") || command === "usage"))
3815
+ if (["agy", "droid"].includes(family ?? "") && ((command === "event" && requiredPosition(parsed, 0, `${family} event action`) === "handle") || command === "usage"))
2373
3816
  return requiredOption(parsed, "project-root");
2374
3817
  if (family === "codex" && command === "hooks") {
2375
3818
  const action = requiredPosition(parsed, 0, "codex hooks action");
@@ -2406,14 +3849,36 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
2406
3849
  function protocolProjectRoot(context, protocolId) {
2407
3850
  return requireProtocol(context, protocolId).project_root;
2408
3851
  }
3852
+ function prepareCliWorkRepair(context, parsed, objective, scopeProjectRoot) {
3853
+ const origins = parsed.options.get("origin-work") ?? [], findings = parsed.options.get("from-finding") ?? [], unresolved = parsed.options.get("from-unresolved") ?? [], receipts = parsed.options.get("from-check") ?? [];
3854
+ if ([findings.length > 0, unresolved.length > 0, receipts.length > 0].filter(Boolean).length !== 1)
3855
+ throw new AppError("usage", "Repair requires exactly one source: --from-check, --from-finding or --from-unresolved", 2);
3856
+ const scope = { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"), runId: requiredOption(parsed, "run"), objective };
3857
+ if (findings.length) {
3858
+ if (origins.length || hasOption(parsed, "verification-file"))
3859
+ throw new AppError("usage", "Finding repair derives its origins and cannot accept --origin-work or --verification-file", 2);
3860
+ return prepareVnextCodeReviewRepair(context, { ...scope, findingIds: findings, checkRefsByFinding: Object.fromEntries(findings.map(finding => [finding, parsed.options.get("check-ref") ?? []])) });
3861
+ }
3862
+ if (!origins.length)
3863
+ throw new AppError("usage", "Repair requires at least one --origin-work", 2);
3864
+ if (hasOption(parsed, "check-ref"))
3865
+ throw new AppError("usage", "--check-ref is only supported with --from-finding", 2);
3866
+ if (receipts.length > 1)
3867
+ throw new AppError("usage", "Create one repair Work per --from-check receipt", 2);
3868
+ if (!unresolved.length && hasOption(parsed, "verification-file"))
3869
+ throw new AppError("usage", "--verification-file is only supported with --from-unresolved", 2);
3870
+ const input = { ...scope, originWorkIds: origins, ...(unresolved.length ? { semanticUnresolved: unresolved, verificationPath: requiredOption(parsed, "verification-file") } : { checkReceiptId: receipts[0] }) };
3871
+ return { input, value: prepareVnextCodeRepair(context, input) };
3872
+ }
3873
+ function workerPromptInput(parsed) {
3874
+ const workspaceRoot = optionalOption(parsed, "workspace-root"), taskFile = optionalOption(parsed, "task-file");
3875
+ return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), stage: requiredOption(parsed, "stage"), profile: requiredOption(parsed, "profile"), ...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }), ...(workspaceRoot ? { workspaceRoot } : {}) };
3876
+ }
2409
3877
  function runnerIntake(context) {
2410
3878
  const input = context.value.task_input?.[0];
2411
3879
  if (!input?.path)
2412
3880
  throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
2413
- const file = path.resolve(input.path);
2414
- if (!fs.existsSync(file) || !fs.statSync(file).isFile())
2415
- throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
2416
- return fs.readFileSync(file, "utf8");
3881
+ return prepareTextFile(input.path, "Runner SPECIFY task input").text;
2417
3882
  }
2418
3883
  /** Stage services install context before their lifecycle transition. Keeping
2419
3884
  * this check here makes an incomplete service migration fail closed instead of