@deksden-com/dd-flow-cli 0.9.0-beta.152 → 0.9.0-beta.156

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 (227) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/build-info.json +8 -8
  3. package/dist/cli/command-inputs.js +33 -3
  4. package/dist/cli/command-metadata.js +73 -2
  5. package/dist/cli/delivery.js +108 -0
  6. package/dist/cli/diagnostic-invocation.js +23 -7
  7. package/dist/cli/diagnostic.js +14 -3
  8. package/dist/cli/help.js +117 -1
  9. package/dist/cli/hook-ingress.js +36 -49
  10. package/dist/cli/interactive-invocation.js +33 -0
  11. package/dist/cli/interactive.js +114 -0
  12. package/dist/cli/memory-fix-invocation.js +52 -0
  13. package/dist/cli/memory-fix.js +100 -0
  14. package/dist/cli/memory-flow-admission.js +7 -3
  15. package/dist/cli/memory-init.js +5 -4
  16. package/dist/cli/native-hook-ingress.js +5 -1
  17. package/dist/cli/native-hook-preparation.js +31 -4
  18. package/dist/cli/run-cli.js +274 -19
  19. package/dist/cli.js +17 -3
  20. package/dist/domain/delivery-flows.js +8 -0
  21. package/dist/domain/distill-flow.js +20 -0
  22. package/dist/domain/interactive-flow.js +1 -0
  23. package/dist/domain/memory-fix-flow.js +2 -0
  24. package/dist/domain/review-fix-flow.js +2 -0
  25. package/dist/domain/source-snapshot-flows.js +4 -0
  26. package/dist/harness-runtime/bin/dd-agy.mjs +18 -3
  27. package/dist/harness-runtime/bin/dd-grok.mjs +15 -5
  28. package/dist/harness-runtime/bin/dd-zcode.mjs +15 -7
  29. package/dist/harness-runtime/lib/agy-error-boundary.mjs +18 -10
  30. package/dist/harness-runtime/lib/agy-native-inspection.mjs +12 -3
  31. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  32. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +13 -6
  33. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  34. package/dist/harness-runtime/lib/daemon-observation.d.mts +1 -0
  35. package/dist/harness-runtime/lib/daemon-observation.mjs +12 -4
  36. package/dist/harness-runtime/lib/daemon-operations.mjs +20 -5
  37. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +1 -0
  38. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +371 -114
  39. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +43 -3
  40. package/dist/harness-runtime/lib/dd-codex.mjs +36 -4
  41. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +25 -11
  42. package/dist/harness-runtime/lib/dd-droid.mjs +124 -11
  43. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  44. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +137 -25
  45. package/dist/harness-runtime/lib/dd-grok.mjs +4 -2
  46. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +115 -17
  47. package/dist/harness-runtime/lib/dd-opencode.mjs +10 -7
  48. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  49. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +115 -22
  50. package/dist/harness-runtime/lib/dd-zcode.mjs +75 -36
  51. package/dist/harness-runtime/lib/delegation-instructions.d.mts +2 -1
  52. package/dist/harness-runtime/lib/delegation-instructions.mjs +15 -5
  53. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  54. package/dist/harness-runtime/lib/driver-recovery.mjs +0 -19
  55. package/dist/harness-runtime/lib/droid-observation.mjs +9 -5
  56. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +1 -0
  57. package/dist/harness-runtime/lib/lifecycle-contract.mjs +2 -1
  58. package/dist/harness-runtime/lib/native-hook-command.d.mts +3 -2
  59. package/dist/harness-runtime/lib/native-hook-command.mjs +63 -8
  60. package/dist/harness-runtime/lib/native-streams.mjs +3 -10
  61. package/dist/harness-runtime/lib/opencode-plugin.mjs +36 -9
  62. package/dist/harness-runtime/lib/operation-errors.mjs +13 -0
  63. package/dist/harness-runtime/lib/operation-progress.mjs +6 -2
  64. package/dist/harness-runtime/lib/process-json.mjs +4 -5
  65. package/dist/harness-runtime/lib/tool-observations.mjs +20 -10
  66. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  67. package/dist/schemas/code-work-result.schema.json +2 -1
  68. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  69. package/dist/schemas/distill-stage-result.schema.json +667 -0
  70. package/dist/schemas/distill-work-result.schema.json +428 -0
  71. package/dist/schemas/execution-bundle.schema.json +6 -2
  72. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  73. package/dist/schemas/interactive-work-result.schema.json +63 -0
  74. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  75. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  76. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  77. package/dist/schemas/run-control-receipt.schema.json +1 -0
  78. package/dist/services/assigned-lifecycle.js +382 -0
  79. package/dist/services/check-runner-evidence.js +45 -8
  80. package/dist/services/cli-operation-classifier.js +6 -1
  81. package/dist/services/code-checks.js +46 -29
  82. package/dist/services/codex-hook-delivery.js +11 -2
  83. package/dist/services/controller-child-repair.js +95 -0
  84. package/dist/services/controller-execution.js +4 -1
  85. package/dist/services/controller-fanout.js +19 -9
  86. package/dist/services/controller-memory-worker.js +6 -4
  87. package/dist/services/controller-result-authority.js +122 -0
  88. package/dist/services/dashboard.js +4 -2
  89. package/dist/services/delivery-access.js +123 -0
  90. package/dist/services/delivery-approval.js +68 -0
  91. package/dist/services/delivery-artifact.js +86 -0
  92. package/dist/services/delivery-checks.js +178 -0
  93. package/dist/services/delivery-execution.js +181 -0
  94. package/dist/services/delivery-intents.js +212 -0
  95. package/dist/services/delivery-operation.js +81 -0
  96. package/dist/services/delivery-policy.js +91 -0
  97. package/dist/services/delivery-procedures.js +43 -0
  98. package/dist/services/delivery-request.js +99 -0
  99. package/dist/services/delivery-result.js +144 -0
  100. package/dist/services/delivery-stage-commands.js +26 -0
  101. package/dist/services/delivery-stage-entry.js +56 -0
  102. package/dist/services/delivery-stage-handlers.js +135 -0
  103. package/dist/services/delivery-stage-results.js +94 -0
  104. package/dist/services/descriptor-coordinator-session.js +3 -1
  105. package/dist/services/descriptor-run-start.js +24 -4
  106. package/dist/services/descriptor-stage-coordinator.js +78 -10
  107. package/dist/services/descriptor-stage-settlement.js +59 -14
  108. package/dist/services/diagnostic-repair-intake.js +123 -0
  109. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  110. package/dist/services/diagnostic-report.js +30 -0
  111. package/dist/services/diagnostic-run-bundle.js +48 -9
  112. package/dist/services/diagnostic-snapshot.js +2 -2
  113. package/dist/services/diagnostic-stage-handlers.js +3 -2
  114. package/dist/services/diagnostic-stage-results.js +26 -0
  115. package/dist/services/diagnostic-start.js +56 -6
  116. package/dist/services/diagnostic-work-collection.js +3 -3
  117. package/dist/services/distill-document-publication.js +280 -0
  118. package/dist/services/distill-promotion.js +197 -0
  119. package/dist/services/distill-publication-custody.js +162 -0
  120. package/dist/services/distill-stage-handlers.js +249 -0
  121. package/dist/services/distill-stage-results.js +105 -0
  122. package/dist/services/distill-user-inputs.js +4 -0
  123. package/dist/services/distill-work-batch.js +42 -0
  124. package/dist/services/distill-work-collection.js +74 -0
  125. package/dist/services/distill-work-registration.js +93 -0
  126. package/dist/services/distill-work-results.js +98 -0
  127. package/dist/services/eval-snapshots.js +17 -7
  128. package/dist/services/external-work-launch.js +8 -2
  129. package/dist/services/flow-catalog.js +24 -0
  130. package/dist/services/frozen-engine.js +19 -2
  131. package/dist/services/generic-execution.js +4 -3
  132. package/dist/services/harness-adapter.js +2 -14
  133. package/dist/services/hook-memory-routing.js +1 -1
  134. package/dist/services/hooks.js +31 -6
  135. package/dist/services/hybrid-contracts.js +6 -1
  136. package/dist/services/installed-flow-source.js +37 -2
  137. package/dist/services/interactive-attached-session.js +215 -0
  138. package/dist/services/interactive-baseline-custody.js +54 -0
  139. package/dist/services/interactive-before-versions.js +148 -0
  140. package/dist/services/interactive-bundle-data.js +85 -0
  141. package/dist/services/interactive-checks.js +179 -0
  142. package/dist/services/interactive-execution.js +33 -0
  143. package/dist/services/interactive-finish-integration.js +236 -0
  144. package/dist/services/interactive-git-baseline.js +230 -0
  145. package/dist/services/interactive-git-dispatch.js +278 -0
  146. package/dist/services/interactive-input-delivery.js +82 -0
  147. package/dist/services/interactive-mutation-scope.js +231 -0
  148. package/dist/services/interactive-request.js +287 -0
  149. package/dist/services/interactive-run-bundle.js +109 -0
  150. package/dist/services/interactive-stage-entry.js +41 -0
  151. package/dist/services/interactive-stage-handlers.js +289 -0
  152. package/dist/services/interactive-stage-observation.js +136 -0
  153. package/dist/services/interactive-stage-results.js +111 -0
  154. package/dist/services/interactive-user-inputs.js +142 -0
  155. package/dist/services/interactive-work-batch.js +43 -0
  156. package/dist/services/interactive-work-collection.js +119 -0
  157. package/dist/services/interactive-work-completion.js +180 -0
  158. package/dist/services/interactive-work-context.js +249 -0
  159. package/dist/services/interactive-work-history.js +112 -0
  160. package/dist/services/interactive-work-registration.js +124 -0
  161. package/dist/services/interactive-work-results.js +115 -0
  162. package/dist/services/interactive-work-start.js +155 -0
  163. package/dist/services/lifecycle-invocations.js +250 -33
  164. package/dist/services/managed-daemon-binding.js +52 -1
  165. package/dist/services/memory-compose-handler.js +2 -22
  166. package/dist/services/memory-finish-integration.js +58 -17
  167. package/dist/services/memory-fix-finish-integration.js +129 -0
  168. package/dist/services/memory-fix-git-dispatch.js +139 -0
  169. package/dist/services/memory-fix-intake-custody.js +11 -0
  170. package/dist/services/memory-fix-intake-settlement.js +127 -0
  171. package/dist/services/memory-fix-publication.js +196 -0
  172. package/dist/services/memory-fix-repair-admission.js +116 -0
  173. package/dist/services/memory-fix-request.js +141 -0
  174. package/dist/services/memory-fix-run-bundle.js +83 -0
  175. package/dist/services/memory-fix-scope.js +78 -0
  176. package/dist/services/memory-fix-sources.js +85 -0
  177. package/dist/services/memory-fix-stage-entry.js +24 -0
  178. package/dist/services/memory-fix-stage-handlers.js +282 -0
  179. package/dist/services/memory-fix-stage-results.js +106 -0
  180. package/dist/services/memory-fix-workspace.js +127 -0
  181. package/dist/services/memory-flow-user-inputs.js +88 -0
  182. package/dist/services/memory-init-contexts.js +55 -5
  183. package/dist/services/memory-init-controller-start.js +20 -4
  184. package/dist/services/memory-layer.js +7 -3
  185. package/dist/services/memory-native-review-copy.js +27 -3
  186. package/dist/services/memory-repair-document-delta.js +1 -0
  187. package/dist/services/project-flow-source.js +195 -0
  188. package/dist/services/project-git-integration.js +92 -0
  189. package/dist/services/repair-intents.js +2 -7
  190. package/dist/services/retained-native-hook.js +274 -0
  191. package/dist/services/review-fix-handoff.js +82 -0
  192. package/dist/services/review-fix-intake-custody.js +17 -0
  193. package/dist/services/review-fix-stage-entry.js +43 -0
  194. package/dist/services/review-fix-stage-handlers.js +176 -0
  195. package/dist/services/review-fix-stage-results.js +86 -0
  196. package/dist/services/run-control-worker.js +42 -1
  197. package/dist/services/run-control.js +40 -5
  198. package/dist/services/run-controller-adapter.js +37 -3
  199. package/dist/services/run-controller-process.js +25 -4
  200. package/dist/services/run-controller-recovery.js +46 -6
  201. package/dist/services/run-controller-state.js +3 -2
  202. package/dist/services/run-controller.js +257 -44
  203. package/dist/services/run-recovery.js +31 -2
  204. package/dist/services/run-timeline.js +59 -0
  205. package/dist/services/runs.js +18 -31
  206. package/dist/services/sessions.js +2 -1
  207. package/dist/services/stage-handlers.js +55 -3
  208. package/dist/services/stage-lifecycle.js +4 -3
  209. package/dist/services/stage-pause.js +26 -9
  210. package/dist/services/stage-report-renderer.js +2 -6
  211. package/dist/services/technical-notes.js +31 -0
  212. package/dist/services/verification-matrix.js +1 -1
  213. package/dist/services/version-status.js +1 -1
  214. package/dist/services/vnext-code-review.js +74 -5
  215. package/dist/services/vnext-code.js +8 -32
  216. package/dist/services/vnext-fanout.js +53 -8
  217. package/dist/services/vnext-merge.js +10 -5
  218. package/dist/services/vnext-plan-review.js +1 -1
  219. package/dist/services/vnext-plan.js +1 -1
  220. package/dist/services/vnext-protocolize.js +1 -1
  221. package/dist/services/vnext-specify.js +10 -5
  222. package/dist/services/work-registry.js +255 -69
  223. package/dist/services/worktrees.js +7 -4
  224. package/dist/storage/database.js +2 -1
  225. package/dist/verification-components.json +82 -0
  226. package/package.json +3 -2
  227. package/tools/verification-jobs.mjs +35 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @deksden-com/dd-flow-cli
2
2
 
3
+ ## 0.9.0-beta.156
4
+
5
+ ### Patch Changes
6
+
7
+ - b59c32b: Preserve current lifecycle successors, restore OpenCode retained child admission, and separate Work correction from technical pause and native resource cleanup. Continue an eligible completed child once per Work without replaying unknown effects or treating descriptive deviations as unfinished obligations.
8
+
9
+ Observe a concurrent controller-launch winner instead of rejecting an already accepted duplicate resume; retain physical ownership and launch CAS fences.
10
+
11
+ ## 0.9.0-beta.155
12
+
13
+ Versions beta.153 and beta.154 were used by unpublished preparation candidates; this release uses a new immutable identity. Older pending Changesets below reconcile release accounting rather than claim their functionality is new.
14
+
15
+ Scoped workers validate only their own consumed inputs; the coordinator retains whole-selection checks. Include integrated E5 flow owners and Changesets prerelease metadata in the reviewed impact map.
16
+
17
+ Connect the selected E5 and engine-storage Node contracts to their explicit runner groups without changing historical release-candidate@1 requirements.
18
+
19
+ ### Patch Changes
20
+
21
+ - 025a576: Accept managed lifecycle results through their retained assignments, without a
22
+ fresh native tool receipt. Preserve accepted work when native retirement fails,
23
+ report technical notes separately and retain conflicting-writer safeguards.
24
+ - f06d07f: Replace mandatory full release gates with change-impact verification and reusable scoped evidence. Publish component identities for technical EVAL proof reuse without coupling unrelated native adapter changes.
25
+ - fa9d145: Preserve AGY task completion and native profile provenance; validate recursive runner proofs through a portable shared core. Add bounded, effect-fenced transient provider recovery and managed ZCode continuation contracts, and expose immutable browser capability facts to checks.
26
+ - 623b866: Keep native hooks lightweight and admit first child Work through the existing CLI assignment and CAS. Preserve original late-tool identities and failures, extend native verification only on real progress, and prevent historical public callbacks from starving a fresh successor.
27
+
28
+ Separate accepted execution from resource retirement, refuse graceful cancellation of newly active required work, and retain shutdown notices as technical evidence. Preserve E5 coordinator and historical result custody with assigned lifecycle execution.
29
+
30
+ - a2ec242: Scope verification to meaningful per-contract inputs and reuse immutable evidence
31
+ across release selections and squash-equivalent sources. Isolate bounded jobs,
32
+ retain exact attempts and prevent orphaned children from freeing verification slots.
33
+
3
34
  ## 0.9.0-beta.151
4
35
 
5
36
  ### Patch Changes
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.9.0-beta.152",
4
- "cli_commit": "70ed6da341845d1b7f262b74f6731ac4787aff73",
5
- "built_at": "2026-10-26T03:31:35.940Z",
3
+ "cli_version": "0.9.0-beta.156",
4
+ "cli_commit": "1a2bd0ea6d022b22e94f552f1cde1fb8e8b6689e",
5
+ "built_at": "2026-10-28T07:26:47.345Z",
6
6
  "built_with_canon": {
7
- "version": "4.1.2",
8
- "commit": "925ef10ecc0b683f7feba6f74913e15560842500",
7
+ "version": "4.1.3",
8
+ "commit": "052ffdf0fff642a46485d2decc79bf78b1970d5e",
9
9
  "flow_contract": "dd-flow-canonical-2026-08",
10
- "repo_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-memorybank-cp253",
11
- "memorybank_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-memorybank-cp253/.memory-bank",
12
- "flow_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-memorybank-cp253/.memory-bank/dd-flow",
10
+ "repo_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-cp260-manifest",
11
+ "memorybank_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-cp260-manifest/.memory-bank",
12
+ "flow_root": "/Users/deksden/Documents/_Projects/_worktrees/dd-canon-cp260-manifest/.memory-bank/dd-flow",
13
13
  "layout": "dot_memory_bank"
14
14
  }
15
15
  }
@@ -16,7 +16,8 @@ const protocolTransitionInput = route(1, "to", `${scoped} payload-file json-file
16
16
  const setupInput = route(0, "request-id trust-file", "canon-source canon-version release-registry channel profile profile-file save-profile-intent", "non-interactive skip-agent-setup", "", { channel: ["stable", "preview", "development"] });
17
17
  const memoryInitInput = route(0, "project-root workspace-root request-id", "intake-file intake-sha256 profile profile-file stage-profile work-profile max-parallel delegation native-config-home git-route integration-branch", "detach non-interactive prepare-only allow-live-probe", "stage-profile work-profile", { "git-route": ["integration_branch_direct", "feature_worktree"], delegation: ["external", "native"] });
18
18
  const diagnosticInput = route(0, "project-root request-id", "intake-file profile profile-file stage-profile work-profile max-parallel", "detach non-interactive prepare-only", "stage-profile work-profile");
19
- export const commandAliases = { transition: "protocol transition", init: "flow mb-init", upgrade: "flow mb-upgrade", review: "flow project-review" };
19
+ const interactiveFinishInput = route(0, "project-root run request-id expected-inputs-sha256 intake-file");
20
+ export const commandAliases = { transition: "protocol transition", init: "flow mb-init", upgrade: "flow mb-upgrade", review: "flow project-review", finish: "interactive finish" };
20
21
  /** Path-shaped arguments default to physical paths unless the route explicitly
21
22
  * opts into contextualPaths. This inventory does not expand arbitrary strings. */
22
23
  export const physicalPathOptions = new Set(words("canon-source trust-file root project-root workspace-root workspace-path workspace-path path dir file schema-dir output result-file semantic-file data decision-file verification-file reason-file task-file intake-file context-file observations-file observation-file summary-file question-file answer-file payload-file json-file task-profile-file execution-routing-file protocol-override-file manifest-file routing-file config-file input-file report-file snapshot-file output-dir state-dir capture-root capture-path destination source archive snapshot plan-file backup-path database backup source-home target-home stdout stderr adapter-executable memory-bank flow stage-report dashboard report"));
@@ -28,21 +29,44 @@ physicalPathOptions.add("seed-file");
28
29
  physicalPathOptions.add("native-executable");
29
30
  physicalPathOptions.add("native-config-home");
30
31
  physicalPathOptions.add("operational-decision-file");
32
+ physicalPathOptions.add("workspace");
31
33
  export const commandInputs = {
34
+ "delivery inputs": route(1, "project-root stage"),
35
+ "delivery checks": route(1, "project-root stage result-file", lifecycle, "", "", { stage: ["prepare", "verify"] }),
36
+ "delivery execute": route(1, "project-root stage result-file", lifecycle, "", "", { stage: ["publish", "deploy"] }),
37
+ "delivery reconcile": route(1, "project-root stage", lifecycle, "", "", { stage: ["publish", "deploy", "verify", "finish"] }),
32
38
  "version": route(0),
33
39
  "flow list": route(0),
40
+ "flow release": route(0, "project-root request-id", "workspace intake-file profile profile-file stage-profile work-profile max-parallel", "detach prepare-only non-interactive", "stage-profile work-profile"),
41
+ "flow deploy": route(0, "project-root request-id", "workspace intake-file profile profile-file stage-profile work-profile max-parallel", "detach prepare-only non-interactive", "stage-profile work-profile"),
42
+ "flow publish": route(0, "project-root request-id", "workspace intake-file profile profile-file stage-profile work-profile max-parallel", "detach prepare-only non-interactive", "stage-profile work-profile"),
43
+ "flow interactive": route(0, "project-root request-id", "workspace intake-file profile profile-file stage-profile work-profile max-parallel execution git-route integration-branch", "detach prepare-only non-interactive", "stage-profile work-profile", { execution: ["attached", "managed"], "git-route": ["integration_branch_direct", "feature_worktree"] }),
44
+ "interactive inputs": route(0, "project-root run"),
45
+ "interactive turn": route(0, "project-root run request-id expected-inputs-sha256 intake-file"),
46
+ "interactive finish": interactiveFinishInput,
47
+ "finish": interactiveFinishInput,
48
+ "interactive scope": route(0, "project-root run result-file"),
49
+ "interactive checks": route(0, "project-root run stage result-file", "", "", "", { stage: ["hardening", "review"] }),
50
+ "interactive register": route(0, "project-root run stage", "", "", "", { stage: ["hardening", "review"] }),
51
+ "interactive correction": route(0, "project-root run work"),
52
+ "interactive re-review": route(0, "project-root run work"),
53
+ "interactive integrate": route(0, "project-root run", "result-file"),
54
+ "interactive git-inputs": route(0, "project-root run stage", "", "", "", { stage: ["preflight", "interactive", "consolidation", "hardening", "review", "integration"] }),
34
55
  "flow show": route(1),
35
56
  "flow mb-init": memoryInitInput,
36
57
  "init": memoryInitInput,
37
58
  "flow mb-upgrade": memoryInitInput,
38
59
  "upgrade": memoryInitInput,
39
60
  "flow mb-audit": diagnosticInput,
61
+ "flow mb-distill": diagnosticInput,
62
+ "flow review-fix": route(0, "project-root request-id", "source-run finding intake-file profile profile-file stage-profile work-profile max-parallel", "detach non-interactive prepare-only", "finding stage-profile work-profile"),
63
+ "flow mb-fix": route(0, "project-root request-id", "workspace-root source-run finding intake-file profile profile-file stage-profile work-profile max-parallel git-route integration-branch", "detach non-interactive prepare-only", "finding stage-profile work-profile", { "git-route": ["integration_branch_direct", "feature_worktree"] }),
40
64
  "flow project-review": diagnosticInput,
41
65
  "review": diagnosticInput,
42
66
  "runtime storage admit": route(0, "operation", "", "", "", { operation: ["memory-init", "memory-upgrade"] }),
43
67
  "runtime storage qualify": route(0, "operation", "", "", "", { operation: ["memory-upgrade"] }),
44
68
  "runtime storage transition": route(0, "action project-root workspace-root request-id selection-sha256", "expected-sha256 recovery-id", "offline", "", { action: ["plan", "apply", "status", "recovery-plan", "recover", "recovery-status"] }),
45
- "runtime flow admit": route(0, "operation", "", "", "", { operation: ["memory-init", "memory-upgrade", "mb-audit", "project-review"] }),
69
+ "runtime flow admit": route(0, "operation", "", "", "", { operation: ["memory-init", "memory-upgrade", "mb-audit", "project-review", "mb-distill", "mb-fix", "review-fix", "interactive", "release", "deploy", "publish"] }),
46
70
  "completion bash": route(0),
47
71
  "completion zsh": route(0),
48
72
  "completion fish": route(0),
@@ -147,7 +171,7 @@ export const commandInputs = {
147
171
  "run recovery capture": route(1, `${project} recovery-id capture-path`),
148
172
  "run recovery resume": route(1, `${project} recovery-id daemon-id harness native-session-id`),
149
173
  "run recovery accept": { ...route(1, `${project} recovery-id`, lifecycle), contextualPaths: ["project-root"] },
150
- "run snapshot bootstrap create": route(0, `${project} output flow`),
174
+ "run snapshot bootstrap create": route(0, `${project} output`, "flow"),
151
175
  "run snapshot bootstrap restore": route(0, `${project} snapshot`),
152
176
  "run snapshot create": route(1, `${project} output`, "recovery-id stage-entry", "candidate incomplete"),
153
177
  "run snapshot restore": route(0, `${project} snapshot`, "execution-routing-file operational-decision-file recovery-id"),
@@ -275,6 +299,12 @@ export const commandInputs = {
275
299
  "memory permissions preflight": route(0, "root memory-bank flow mode", "tasks"),
276
300
  "memory compose register": route(0, "project-root run request-key result-file"),
277
301
  "memory upgrade publish": route(0, "project-root run"),
302
+ "memory distill inputs": route(0, "project-root run"),
303
+ "memory distill publish": route(0, "project-root run result-file"),
304
+ "memory fix context": route(0, "project-root run"),
305
+ "memory fix scope": route(0, "project-root run result-file"),
306
+ "memory fix publish": route(0, "project-root run result-file"),
307
+ "memory fix integrate": route(0, "project-root run", "result-file"),
278
308
  "memory verify checks": route(0, "project-root run"),
279
309
  "memory review register": route(0, "project-root run"),
280
310
  "memory review collect": route(0, "project-root run"),
@@ -60,7 +60,60 @@ const memoryInitOptions = {
60
60
  "profile-file": "Absolute regular JSON file with one complete validated operation-only executor profile. Exclusive with --profile; never saves settings or changes defaults. Omit on replay to reuse frozen bytes; supplied profile content must match the original selection.",
61
61
  "non-interactive": "Never open the first-flow executor dialog, even on a terminal. Without an explicit profile/file or configured default, refuse before project effects; init cannot skip its executor."
62
62
  };
63
+ const deliveryLaunchOptions = {
64
+ "intake-file": "Original bounded UTF-8 user request. Required fresh; supplied replay text must equal the retained original. Starting delivery is not publication or deployment approval.",
65
+ workspace: "Existing canonical assigned Git checkout. Fresh defaults to project root; omitted replay preserves the original workspace. Never uses a diagnostic clone.",
66
+ "stage-profile": "Repeatable known stage=profile assignment for this delivery descriptor. Frozen before RUN creation; independent verify uses a separate physical Session.",
67
+ "profile-file": "Absolute JSON operation-only executor profile, exclusive with --profile. Does not save settings; omitted replay uses the retained selection.",
68
+ "prepare-only": "Retain or replay one request-bound delivery RUN without launching its controller. Publishes runtime state, not a dry run or permission to mutate the delivery target. Exclusive with --detach."
69
+ };
63
70
  const commandOptionDescriptions = {
71
+ "flow release": deliveryLaunchOptions,
72
+ "flow deploy": deliveryLaunchOptions,
73
+ "flow publish": deliveryLaunchOptions,
74
+ "delivery inputs": { stage: "Bound delivery Stage. Read original request/answers and the accepted operation scope when available; never grants approval." },
75
+ "delivery checks": { stage: "Current prepare or verify Stage; issued coordinator command only. Uses project CODE/readiness checks and actual artifact bytes.", "result-file": "Exact artifact JSON: path (workspace-relative), subject_ref, version and sha256. No commands or invented check receipts. Omit neither required checks nor artifact selection." },
76
+ "delivery execute": { stage: "Current publish or deploy Stage, bound to the physically admitted coordinator. Does not finish the Stage or RUN.", "result-file": "Exact scoped approval JSON: scope_sha256, user_inputs_sha256, basis (role=request|answer,event_id,sha256), expires_at and reason. Copy scope/input references from delivery inputs; startup, a question or authentication is not approval. Unknown effects require reconciliation, not retry." },
77
+ "delivery reconcile": { stage: "Current owned delivery Stage. Run only the original read-only result procedure and retain observation after physical command settlement. Never republishes, rolls back, switches account or grants retry." },
78
+ "flow interactive": {
79
+ "non-interactive": "Never open executor selection. A fresh request needs an explicit profile/file or configured default; the executor cannot be skipped and no profile is saved.",
80
+ "profile-file": "Absolute regular JSON file with a complete validated operation-only executor profile; exclusive with --profile. Does not save settings. Omitted replay uses retained selection; supplied content must match.",
81
+ workspace: "Optional existing canonical assigned checkout. Defaults to project root for a fresh request; omitted replay uses the original workspace. Does not create a worktree or widen Git scope.",
82
+ execution: "Defaults to attached: requires a unique current trusted hook observation of this exact argv, workspace and profile. Managed must be selected explicitly; no automatic fallback.",
83
+ "intake-file": "Original bounded nonempty UTF-8 request, up to 64 KiB. Required fresh; supplied replay bytes must match exactly. Current turns and answers remain separate retained inputs.",
84
+ "prepare-only": "Retain the original request and allocate its RUN without starting controller/provider execution. Attached also publishes immutable context candidates. Not a dry run; exclusive with --detach.",
85
+ detach: "Managed execution only. Return the existing controller receipt without foreground observation; disconnect never stops its owner.",
86
+ "stage-profile": "Repeatable preflight/interactive/consolidation/hardening/review/integration=profile selection. Frozen before dispatch; attached same-session constraints still apply.",
87
+ "work-profile": "Repeatable allowed author/critic executor profile. Work still uses existing physical Session and independence admission.",
88
+ "git-route": "Explicit local integration route, paired with --integration-branch; otherwise use project workspace policy. No push permission. Feature route requires an already assigned --workspace.",
89
+ "max-parallel": "External Work concurrency from 1 to 64, default 1; does not combine author and critic identities or qualify native capacity."
90
+ },
91
+ "flow review-fix": {
92
+ "project-root": "Canonical absolute source project. Review-fix uses a private read-only snapshot; it does not repair code or integrate Git changes.",
93
+ "source-run": "Exact completed mb-audit or project-review RUN. Required with --finding for a fresh request; omitted replay uses the retained original source, never the latest report.",
94
+ finding: "Repeat for each explicitly selected accepted finding ID, in retained order. No implicit all-findings scope; selecting a finding is not approval to implement it.",
95
+ "intake-file": "Original bounded UTF-8 user request and constraints. Required fresh; supplied replay bytes must match. Actual delivered answers remain part of scope assessment, not an agent-authored replacement specification.",
96
+ "stage-profile": "Repeatable intake/scope-review/handoff=profile assignment, frozen before dispatch. Scope-review requires a physically independent new Session; each stage may use a different supported profile.",
97
+ "prepare-only": "Retain the request, private source snapshot and review-fix RUN without launching controller/provider Sessions. Creates runtime state, not a dry run or downstream SDLC task. Exclusive with --detach.",
98
+ "max-parallel": "Frozen external Work concurrency limit from 1 to 64, default 1. The three root stages remain sequential; this does not combine intake and independent scope-review Sessions."
99
+ },
100
+ "flow mb-fix": {
101
+ "source-run": "Completed mb-audit or project-review RUN containing accepted findings. Required with explicit findings for a fresh request; omitted replay reuses the original source, never the latest audit.",
102
+ finding: "Repeat for each selected finding ID from --source-run. Order and exact IDs are retained; no implicit all-findings selection. Selection alone is not write approval.",
103
+ "workspace-root": "Optional canonical absolute existing repair workspace. Otherwise the launcher prepares the policy-selected workspace; replay uses its retained workspace, not the current shell directory.",
104
+ "intake-file": "Bounded UTF-8 original repair request and constraints. Required fresh; supplied replay bytes must match. Scope and consent are assessed at intake before document publication.",
105
+ "stage-profile": "Repeatable intake/repair/verify/review/finish=profile override. Profiles are frozen; verify and review require independent physical Sessions.",
106
+ "prepare-only": "Prepare or replay the retained repair RUN without launching controller/provider Sessions. Creates runtime/workspace state, not a dry run or semantic repair acceptance. Exclusive with --detach.",
107
+ "git-route": "Explicit feature_worktree or integration_branch_direct paired with --integration-branch; otherwise use project workspace policy. Never authorizes push or unrelated commits.",
108
+ "max-parallel": "External Work concurrency from 1 to 64, default 1. Does not combine author, verifier and reviewer Sessions or advertise native qualification."
109
+ },
110
+ "flow mb-distill": {
111
+ "project-root": "Canonical absolute source project identity. Extraction uses an isolated read-only snapshot; promotion requires a separately accepted destination and exact write scope.",
112
+ "intake-file": "Bounded UTF-8 original distillation request, intended reusable practices and constraints. Required fresh; supplied replay bytes must match the retained request. Does not grant destination write authority.",
113
+ "stage-profile": "Repeatable preflight/extract/review/promote/verify/finish=profile override. All profiles are frozen before dispatch; verification requires an independent physical Session.",
114
+ "prepare-only": "Retain the request, source snapshot and RUN without launching a controller or provider Sessions. Creates runtime state, not destination changes; not a dry run.",
115
+ "max-parallel": "External extraction and critic Work concurrency from 1 to 64; default 1. Changes scheduling, not required aspect coverage."
116
+ },
64
117
  "flow mb-audit": {
65
118
  "project-root": "Canonical absolute project identity. The runtime creates an isolated read-only source snapshot; neither the project nor its Memory Bank is a write destination.",
66
119
  "intake-file": "Bounded UTF-8 original diagnostic request, including audit mode/focus and constraints. Required for a fresh request; optional on exact replay, where supplied bytes must match.",
@@ -81,7 +134,7 @@ const commandOptionDescriptions = {
81
134
  upgrade: { ...memoryInitOptions, "stage-profile": "Repeatable stage=profile assignment for preflight, diff-analysis, upgrade, verify, review or finish. The original selection is frozen before dispatch.", "prepare-only": "Prepare or replay the upgrade RUN and accepted storage/bootstrap selection without launching its controller or a productive Session. This is not a dry run; it does not publish the upgraded canonical layer or accept semantic preservation." },
82
135
  "runtime storage admit": { operation: "Operation-specific writable-connection admission: memory-init or memory-upgrade. Upgrade also checks retained request storage. Requires explicit canonical DD_FLOW_HOME and an existing compatible resource registry. Never initializes, migrates, repairs or starts agents." },
83
136
  "runtime storage qualify": { operation: "memory-upgrade only. Qualify the selected engine before a native probe. Incompatible drained stores are copied privately with committed WAL; the actual transition and operation admission run on those copies. Requires both explicit canonical Homes. Never migrates real stores, grants productive admission, creates a RUN or starts agents." },
84
- "runtime flow admit": { operation: "Handler admission for memory-init or memory-upgrade in the executing engine. Reads bounded UTF-8 JSON stdin with source and source_sha256; memory-init additionally accepts startup_input_schema to require its startup reader. Validates the matching six-stage descriptor using actual registered handlers. Does not establish release trust, storage/provider readiness or semantic completion, and never creates Home, RUN or agents." },
137
+ "runtime flow admit": { operation: "Handler admission for the selected memory, source-snapshot or project-local interactive flow in the executing engine. Reads bounded UTF-8 JSON stdin with source and source_sha256; interactive requires its retained project pack, not installed canon. memory-init additionally accepts startup_input_schema to require its startup reader. Validates the exact descriptor using actual registered handlers and refuses unsupported handlers. Does not establish release trust, storage/provider readiness or semantic completion, and never creates Home, RUN or agents." },
85
138
  "profiles create": { file: "Regular UTF-8 JSON file containing one complete validated agent profile. The ID must be unused. Credentials and unknown fields are refused; creation uses --expected-revision and never starts a model Turn." },
86
139
  "profiles edit": { file: "Regular UTF-8 JSON file containing the complete replacement agent profile, not a patch. Its ID must already exist. Revision conflicts refuse the edit; retained RUN snapshots are unchanged." },
87
140
  "profiles alias": {
@@ -124,6 +177,7 @@ const commandOptionDescriptions = {
124
177
  "no-progress-ms": "Override the health-assessment progress-age threshold in milliseconds; integer at least 1000. Defaults are 120000 for starting, 300000 for capturing and 1800000 otherwise. This is not a productive command timeout or permission to stop a live owner."
125
178
  },
126
179
  "run fork": { from: "Existing retained snapshot path to fork into a new RUN with the explicitly selected engine; not a control request identity." },
180
+ "run snapshot bootstrap create": { flow: "Pre-RUN boundary: sdlc starts at specify; mb-init, mb-upgrade and source workflows start at preflight from installed canon. interactive starts at preflight from the selected project's curated pack, never global fallback. Creates no RUN or provider Session.", output: "New snapshot directory outside the project and runtime Home. Captures original project files and Git state; refuses an existing output." },
127
181
  "run control resume": { from: "Exact retained pause/stop control ID whose captured state authorizes this resume request; not a snapshot path." },
128
182
  "stage block": { kind: "Blocker category: engine, harness or environment; code and summary identify the actionable cause." },
129
183
  "migration impact": { mode: "Optional full_migration request forces the strict full-migration assessment; otherwise impact evidence determines patch, targeted_minor or full_migration." },
@@ -140,6 +194,22 @@ const commandOptionDescriptions = {
140
194
  "memory permissions preflight": { mode: "Permission contour to check: read, write, repair or report_only; write checks may create temporary permission probes.", flow: "Workflow permission contour: mb-init, mb-upgrade, mb-audit, mb-fix, mb-upgrade-review or custom. Not a filesystem path." },
141
195
  "memory compose register": { "run": "Active mb-init or mb-upgrade RUN whose accepted discovery/diff-analysis and immutable execution bind the composition graph.", "request-key": "Stable composition registration identity; exact replay retains the original Work graph.", "result-file": "Bounded proposed composition JSON. Bytes are prepared before registration; agent fields do not grant Work or write authority." },
142
196
  "memory upgrade publish": { run: "Active mb-upgrade authoring Stage with a registered blueprint and settled Work. Apply only its retained canonical publication plan and recovery archive; this does not accept the Stage or complete the RUN." },
197
+ "memory distill inputs": { run: "Bound mb-distill RUN. Read its original request and every delivered question/answer from retained custody; this grants no write scope and allocates no RUN or Session." },
198
+ "interactive inputs": { run: "Bound interactive RUN. Read the original request, its CLI-issued initial_request_basis, ordered turns, actual HITL answers and current input digest. Does not create a RUN, finish intake or approve changes." },
199
+ "interactive integrate": { run: "Bound interactive integration Stage with accepted independent review. Perform or reconcile only its retained scoped local Git operation; never push, reset or autostash. This does not accept the terminal Stage or complete the RUN.", "result-file": "Optional bounded JSON inclusion agreement: basis (role,event_id,sha256), reason and include_existing_paths. Required for pre-existing selected changes; cite genuine user input resolved before review. Exact retained replay needs no fresh agreement; unknown effects are reconciled, not repeated." },
200
+ "interactive checks": { run: "Bound interactive RUN. Retain current check obligations and execute them through the common aggregate runner; returned receipts are not Stage or review acceptance.", stage: "Current hardening or review Stage. Checks bind the complete task, scope and observed patch; corrections cannot reuse stale evidence.", "result-file": "Bounded JSON with additionalChecks (array of id,command,purpose) and optional noChecksReason. Each id must match ^CHK-[A-Za-z0-9-]{1,64}$, be unique and not collide with project policy checks; command and purpose must be nonempty strings. Example: {\"additionalChecks\":[{\"id\":\"CHK-project-test\",\"command\":\"npm test\",\"purpose\":\"Run the existing project tests\"}]}. Project CODE/readiness checks are always included. Keep accepted check IDs and commands unchanged in later rounds; do not drop retained obligations. A substantive no-check reason is required only when no checks apply." },
201
+ "interactive register": { run: "Bound interactive RUN. Register or replay the initial author/reviewer Work graph without starting a provider or accepting its results.", stage: "Current hardening or review Stage; each uses its own retained packet and role. Corrections use interactive correction and re-review, not arbitrary registration identities." },
202
+ "interactive correction": { run: "Bound interactive review RUN. Register or replay a scoped correction author within the same Stage and attempt; no Stage rewind.", work: "Exact latest completed negative reviewer Work. A ready reviewer or foreign Work cannot authorize correction." },
203
+ "interactive re-review": { run: "Bound interactive review RUN. Register or replay a fresh independent critic after a ready correction and current checks; author claims never become review acceptance.", work: "Exact completed correction-author Work in this review Stage. New registration requires latest current output and retained check evidence." },
204
+ "interactive scope": { run: "Bound interactive RUN. Retain the coordinator's selected mutation scope and original before-images. New user input requires explicit scope reassessment; no files are edited or Git effects performed.", "result-file": "Bounded JSON with exactly requestId, paths (sorted unique relative files), basis (role,event_id,sha256), reason and expectedInputsSha256. Basis for the original request uses role=request, event_id=0 (a JSON integer, not a request ID), and initial_request.sha256 from interactive inputs. For later input copy role (turn,answer,finish), integer event_id and sha256 from its exact messages entry; question is not user approval. expectedInputsSha256 is the complete interactive inputs response sha256. requestId identifies the scope operation, not the RUN: use a fresh scope requestId when the input digest, paths, basis or reason changes, including after finish; reuse only for an identical proposal retry. Basis must name genuine delivered user input; semantic applicability remains agent-owned. A finish message alone cannot widen scope." },
205
+ "interactive turn": { run: "Existing interactive RUN accepting turns. Retain these exact user bytes without allocating another RUN or Session. An unanswered managed pause also receives an ordinary controller answer; accepted means queued, not physically delivered.", "request-id": "Stable delivery identity. Exact retry reconciles the original turn and original pause only; different bytes or revision under the same identity refuse.", "expected-inputs-sha256": "Current SHA-256 from interactive inputs, including actual HITL answers. A concurrent delivery refuses instead of overwriting it.", "intake-file": "Bounded nonempty UTF-8 user message, preserved byte-for-byte, up to 1 MiB. Its text is never interpreted as a finish command or inclusion approval." },
206
+ "interactive finish": { run: "Existing interactive RUN. Explicitly close free-turn intake within this RUN and queue an ordinary answer to its unanswered managed pause, if present. This does not accept review, commit changes or complete the RUN; attached execution retains input for its current coordinator.", "request-id": "Stable finish delivery identity. Exact retry reconciles the original retained finish and original pause without duplicating either.", "expected-inputs-sha256": "Current SHA-256 from interactive inputs. Finish includes every delivered turn and HITL answer; stale revisions refuse.", "intake-file": "Bounded nonempty UTF-8 final user message and constraints, preserved byte-for-byte, up to 1 MiB. Choosing this command, not text inside the file, requests finish." },
207
+ "interactive git-inputs": { run: "Bound interactive RUN. Inspect original selected workspace, index and HEAD versions without Git writes or inclusion approval. Resolve existing-change decisions before accepting review.", stage: "Descriptor Stage whose current input and mutation scope must match. Available before finish and accepted review; stale scope or moved original Git inputs refuse." },
208
+ "memory fix context": { run: "Bound mb-fix RUN. Read its original selected findings, source intake and all delivered user inputs. Read-only; does not approve repair or close findings." },
209
+ "memory fix scope": { run: "Bound mb-fix RUN. Prepare current drift and selected-target observations without writing documents or accepting the intake Stage.", "result-file": "Bounded JSON object with routing and write_scope. Scope cites an actual delivered request/answer and the complete current user-input digest; semantic consent remains agent-owned." },
210
+ "memory fix publish": { run: "Active mb-fix repair Stage with accepted intake. Apply only the original admitted document publication; does not accept verification, review or finish.", "result-file": "Bounded JSON object with outputs: path, content (string or null for selected removal), finding_ids. Paths and findings must match the accepted repair scope. Retained intent governs recovery." },
211
+ "memory fix integrate": { run: "Active mb-fix finish Stage with independently verified and accepted review. Scoped local commit/merge under the frozen Git policy; never push, reset or autostash.", "result-file": "Optional current agreement object: user_inputs_sha256 and approval_source. Required when later delivered answers make the original agreement stale; cannot widen the accepted repair scope. Unknown original Git effects require reconciliation, not redispatch." },
212
+ "memory distill publish": { run: "Active bound mb-distill promote Stage with accepted review and independently settled research Work. Apply the original scoped publication and retain its observed result; this does not accept the Stage or RUN.", "result-file": "Bounded JSON proposal: write_scope, user_inputs_sha256 and outputs (path, content, candidate_ids). The agent interprets agreement; CLI checks delivered sources, accepted candidate IDs, exact paths and original-intent recovery. Missing agreement requires stage pause, not invented approval." },
143
213
  "memory verify checks": { "run": "Active mb-init or mb-upgrade RUN. Execute only its independently selected declared verification checks." },
144
214
  "memory review register": { "run": "Active mb-init or mb-upgrade RUN with accepted verification. Register initial independent reviewers; this does not launch providers." },
145
215
  "memory review collect": { "run": "Active mb-init or mb-upgrade RUN. Collect genuine registered initial-wave results without accepting the Stage or granting correction authority." },
@@ -197,7 +267,8 @@ export function optionsForCommandTree(command) {
197
267
  export function routerNativeRoute(key) {
198
268
  return key === "version" || key.startsWith("engine ") || key === "schema validate"
199
269
  || key === "flow list" || key === "flow show" || key === "flow mb-init" || key === "init" || key === "flow mb-upgrade" || key === "upgrade"
200
- || key === "flow mb-audit" || key === "flow project-review" || key === "review"
270
+ || key === "flow mb-audit" || key === "flow project-review" || key === "flow mb-distill" || key === "flow mb-fix" || key === "flow review-fix" || key === "flow interactive" || key === "review"
271
+ || key === "flow release" || key === "flow deploy" || key === "flow publish"
201
272
  || key === "run reconcile" || key === "runtime storage admit" || key === "runtime storage qualify" || key === "runtime storage transition"
202
273
  || key === "runtime flow admit"
203
274
  || key.startsWith("profiles ") || key.startsWith("models ") || key.startsWith("harnesses ") || key.startsWith("completion ") || key === "setup" || key.startsWith("setup ")
@@ -0,0 +1,108 @@
1
+ import path from "node:path";
2
+ import { createContext } from "../runtime/context.js";
3
+ import { AppError, errorRecord } from "../shared/errors.js";
4
+ import { deliveryFlowStages, isDeliveryFlow } from "../domain/delivery-flows.js";
5
+ import { prepareCommandRoute } from "./command-inputs.js";
6
+ import { prepareFlowExecutionSelectors } from "./diagnostic-invocation.js";
7
+ import { parseOutputOptions } from "./output-options.js";
8
+ import { prepareResponseDestination } from "./response-file.js";
9
+ import { prepareTextFile } from "./input-preparation.js";
10
+ import { writePreparationError } from "./error-output.js";
11
+ import { resolveDdFlowHome, resolveProjectRoot } from "../storage/paths.js";
12
+ import { readOperatorProfiles, readOperatorProfileInput } from "../services/operator-profiles.js";
13
+ import { onboardExecutor } from "../services/executor-onboarding.js";
14
+ import { askExecutorChoice } from "./executor-onboarding.js";
15
+ import { PreparedInstalledFlowSource } from "../services/installed-flow-source.js";
16
+ import { readSetupStatus } from "../services/setup.js";
17
+ import { selectMemoryInitInstalledEngine } from "../services/memory-init-engine-release.js";
18
+ import { FrozenGenericExecution } from "../services/generic-execution.js";
19
+ import { readDeliveryRequest, retainDeliveryRequest, startDeliveryRun } from "../services/delivery-request.js";
20
+ import { hybridDigest } from "../services/hybrid-contracts.js";
21
+ import { registerProject } from "../services/projects.js";
22
+ import { readFlowRunExecutionBundle } from "../services/runs.js";
23
+ import { launchAndObserveMemoryFlow } from "./memory-init.js";
24
+ function refuse(message) { throw new AppError("usage", message, 2, { phase: "prepare", effect: "no_effect" }); }
25
+ export function prepareDeliveryInvocation(args) {
26
+ const route = prepareCommandRoute(args), flowId = route.command;
27
+ if (route.family !== "flow" || !isDeliveryFlow(flowId))
28
+ refuse("Use flow release, flow deploy or flow publish");
29
+ const options = route.parsed.options, value = (key) => options.get(key)?.[0];
30
+ const projectRoot = value("project-root"), requestId = value("request-id"), workspace = value("workspace");
31
+ if (!path.isAbsolute(projectRoot) || path.resolve(projectRoot) !== projectRoot
32
+ || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId)
33
+ || workspace !== undefined && (!path.isAbsolute(workspace) || path.resolve(workspace) !== workspace))
34
+ refuse("Use canonical absolute roots and a bounded request ID");
35
+ if (options.has("detach") && options.has("prepare-only"))
36
+ refuse("--detach and --prepare-only are exclusive");
37
+ const selected = prepareFlowExecutionSelectors(options, deliveryFlowStages[flowId]);
38
+ if (selected.profileFile && !path.isAbsolute(selected.profileFile))
39
+ refuse("--profile-file must be absolute");
40
+ return { scope: { projectRoot, requestId, flowId }, workspace, ...selected, intakeFile: value("intake-file"),
41
+ detach: options.has("detach"), prepareOnly: options.has("prepare-only"), nonInteractive: options.has("non-interactive") };
42
+ }
43
+ /** Managed physical Sessions use the ordinary RUN controller. Launch is not
44
+ * publication permission; each productive operation still consumes approval. */
45
+ export async function runDeliveryCli(args, env = process.env, io = process) {
46
+ let context, effects = false;
47
+ try {
48
+ const output = parseOutputOptions(args), invocation = prepareDeliveryInvocation(output.args), home = resolveDdFlowHome(env);
49
+ if (output.responseFile)
50
+ prepareResponseDestination(output.responseFile);
51
+ const { scope } = invocation;
52
+ if (resolveProjectRoot(scope.projectRoot) !== scope.projectRoot || invocation.workspace !== undefined
53
+ && resolveProjectRoot(invocation.workspace) !== invocation.workspace)
54
+ refuse("Roots must be canonical existing checkouts");
55
+ if (invocation.profileFile)
56
+ invocation.execution.rootProfile = readOperatorProfileInput(invocation.profileFile);
57
+ const text = invocation.intakeFile ? prepareTextFile(invocation.intakeFile, "delivery request", 64 * 1024).text : undefined;
58
+ context = createContext(env, "prepared_existing", undefined, { resourceDatabaseMode: "prepared_existing" });
59
+ const project = context.db.get("SELECT status FROM projects WHERE root=?", [scope.projectRoot]);
60
+ if (project && project.status !== "active")
61
+ refuse("Activate the selected project before delivery");
62
+ const retained = project ? readDeliveryRequest(context, scope) : null;
63
+ if (retained) {
64
+ if (Object.entries(invocation.execution).some(([key, value]) => !Object.hasOwn(retained.selection, key)
65
+ || hybridDigest(value) !== hybridDigest(retained.selection[key]))
66
+ || text !== undefined && text !== retained.objective
67
+ || invocation.workspace !== undefined && invocation.workspace !== retained.execution.scope.workspace)
68
+ refuse("Replay must preserve the original request, workspace and selectors");
69
+ }
70
+ else {
71
+ if (!text?.trim())
72
+ refuse("Fresh delivery requires --intake-file with the original request");
73
+ const setup = readSetupStatus(home);
74
+ if (!setup.installation || setup.installation_readiness !== "installed")
75
+ refuse("Install the desired canon and CLI using setup first");
76
+ const source = PreparedInstalledFlowSource.fromInstalledRelease(home, setup.installation.manifest_sha256, scope.flowId);
77
+ const engine = selectMemoryInitInstalledEngine(home, setup.installation.manifest_sha256);
78
+ engine.admitDeliveryFlow(source, scope.flowId);
79
+ const profiles = readOperatorProfiles(home), root = invocation.execution.rootProfile, stdin = io.stdin ?? process.stdin;
80
+ const selected = await onboardExecutor(typeof root === "string" ? { profileId: root } : root ? { profile: root } : {}, () => profiles, { allowSkip: false, nonInteractive: invocation.nonInteractive || output.json || output.progressJsonl
81
+ || !stdin.isTTY || !io.stderr.isTTY }, settings => askExecutorChoice(settings, { stdin, stderr: io.stderr }, { allowSkip: false, allowSave: false }));
82
+ if (!selected.profile)
83
+ refuse("Select a delivery executor profile");
84
+ const execution = FrozenGenericExecution.prepare(source.resolveRegisteredFlow(), profiles, { ...invocation.execution,
85
+ rootProfile: root ?? selected.profile, execution: "managed", stageSessionMode: "new_session",
86
+ delegation: invocation.execution.delegation ?? { mode: "external", max_parallel: 1 } }, { projectRoot: scope.projectRoot, requestId: scope.requestId, workspace: invocation.workspace ?? scope.projectRoot });
87
+ effects = true;
88
+ registerProject(context, { root: scope.projectRoot });
89
+ retainDeliveryRequest(context, { scope, source, execution, engine, selection: invocation.execution, objective: text });
90
+ }
91
+ effects = true;
92
+ const started = startDeliveryRun(context, scope), bundle = readFlowRunExecutionBundle(context, { projectRoot: scope.projectRoot, runId: started.runId });
93
+ if (!bundle || bundle.manifest.flow_id !== scope.flowId)
94
+ throw new AppError("delivery_bundle_missing", "Restore the original delivery bundle", 1, { effect: "committed" });
95
+ return await launchAndObserveMemoryFlow(context, { ...invocation,
96
+ scope: { projectRoot: scope.projectRoot, requestId: scope.requestId, workspaceRoot: bundle.execution.snapshot().scope.workspace } }, started, scope.flowId, output, env, io);
97
+ }
98
+ catch (error) {
99
+ const record = errorRecord(error);
100
+ if (effects)
101
+ record.details = { ...record.details, effect: ["unknown", "partial", "committed"].includes(String(record.details.effect)) ? record.details.effect : "partial", next_action: "reconcile_exact_request" };
102
+ writePreparationError(io, args, record);
103
+ return record.exitCode;
104
+ }
105
+ finally {
106
+ context?.db.close?.();
107
+ }
108
+ }
@@ -1,29 +1,46 @@
1
1
  import path from "node:path";
2
2
  import { AppError } from "../shared/errors.js";
3
3
  import { diagnosticFlowStages } from "../domain/diagnostic-flows.js";
4
+ import { distillStages } from "../domain/distill-flow.js";
5
+ import { reviewFixStages } from "../domain/review-fix-flow.js";
4
6
  import { prepareCommandRoute } from "./command-inputs.js";
5
7
  function refuse(message) { throw new AppError("usage", message, 2, { effect: "no_effect" }); }
6
8
  /** Parser only: no files, operator settings, project mutation or runtime state. */
7
9
  export function prepareDiagnosticInvocation(args) {
8
10
  const route = prepareCommandRoute(args), options = route.parsed.options;
9
- const flowId = route.key === "flow mb-audit" ? "mb-audit"
10
- : ["review", "flow project-review"].includes(route.key) ? "project-review" : refuse("Use flow mb-audit or review / flow project-review");
11
+ const flowId = route.key === "flow review-fix" ? "review-fix" : route.key === "flow mb-distill" ? "mb-distill" : route.key === "flow mb-audit" ? "mb-audit"
12
+ : ["review", "flow project-review"].includes(route.key) ? "project-review" : refuse("Use flow mb-distill, flow mb-audit or review / flow project-review");
13
+ const stages = flowId === "review-fix" ? reviewFixStages : flowId === "mb-distill" ? distillStages : diagnosticFlowStages[flowId];
11
14
  const value = (key) => options.get(key)?.[0];
12
15
  const projectRoot = value("project-root"), requestId = value("request-id");
13
16
  if (!path.isAbsolute(projectRoot) || path.resolve(projectRoot) !== projectRoot
14
17
  || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId))
15
18
  refuse("Use a canonical absolute project root and a bounded request ID");
16
- if (value("profile") && value("profile-file"))
17
- refuse("Use either --profile or --profile-file");
18
19
  if (options.has("detach") && options.has("prepare-only"))
19
20
  refuse("--detach and --prepare-only are exclusive");
21
+ const selected = prepareFlowExecutionSelectors(options, stages);
22
+ const sourceRunId = value("source-run"), findingIds = options.get("finding");
23
+ if (Boolean(sourceRunId) !== Boolean(findingIds))
24
+ refuse("Supply --source-run together with one or more --finding values");
25
+ if (sourceRunId && (!/^RUN-[0-9]+-[a-z0-9-]+$/.test(sourceRunId) || !findingIds?.length || findingIds.length > 4096
26
+ || findingIds.some(id => !id.trim() || id.includes("\0")) || new Set(findingIds).size !== findingIds.length))
27
+ refuse("Select one exact source RUN and unique ordered finding IDs");
28
+ return { scope: { projectRoot, requestId, flowId }, ...selected,
29
+ ...(sourceRunId && findingIds ? { repairSelection: { sourceRunId, findingIds: [...findingIds] } } : {}), intakeFile: value("intake-file"),
30
+ detach: options.has("detach"), prepareOnly: options.has("prepare-only"), nonInteractive: options.has("non-interactive") };
31
+ }
32
+ /** Shared pure selectors for registered diagnostic and bounded repair launchers. */
33
+ export function prepareFlowExecutionSelectors(options, stages) {
34
+ const value = (key) => options.get(key)?.[0];
35
+ if (value("profile") && value("profile-file"))
36
+ refuse("Use either --profile or --profile-file");
20
37
  const execution = {};
21
38
  if (value("profile"))
22
39
  execution.rootProfile = value("profile");
23
40
  const stageProfiles = {};
24
41
  for (const assignment of options.get("stage-profile") ?? []) {
25
42
  const match = /^([^=]+)=([^=]+)$/.exec(assignment);
26
- if (!match || !diagnosticFlowStages[flowId].includes(match[1]) || Object.hasOwn(stageProfiles, match[1]))
43
+ if (!match || !stages.includes(match[1]) || Object.hasOwn(stageProfiles, match[1]))
27
44
  refuse("--stage-profile requires one known stage=profile assignment per stage");
28
45
  stageProfiles[match[1]] = match[2];
29
46
  }
@@ -41,6 +58,5 @@ export function prepareDiagnosticInvocation(args) {
41
58
  refuse("--max-parallel must be an integer from 1 to 64");
42
59
  execution.delegation = { mode: "external", max_parallel: n };
43
60
  }
44
- return { scope: { projectRoot, requestId, flowId }, execution, profileFile: value("profile-file"), intakeFile: value("intake-file"),
45
- detach: options.has("detach"), prepareOnly: options.has("prepare-only"), nonInteractive: options.has("non-interactive") };
61
+ return { execution, profileFile: value("profile-file") };
46
62
  }
@@ -42,6 +42,8 @@ export async function runDiagnosticCli(args, env = process.env, io = process) {
42
42
  finally {
43
43
  reader.db.close?.();
44
44
  }
45
+ if (scope.flowId === "review-fix" && !retained && !invocation.repairSelection)
46
+ throw new AppError("usage", "Fresh review-fix requires --source-run and explicit --finding selections", 2, { effect: "no_effect" });
45
47
  if (invocation.profileFile)
46
48
  invocation.execution.rootProfile = readOperatorProfileInput(invocation.profileFile);
47
49
  const readIntake = () => {
@@ -60,6 +62,8 @@ export async function runDiagnosticCli(args, env = process.env, io = process) {
60
62
  return text;
61
63
  };
62
64
  if (retained) {
65
+ if (invocation.repairSelection && hybridDigest(invocation.repairSelection) !== hybridDigest(retained.repair_source?.selection))
66
+ throw new AppError("diagnostic_request_conflict", "Supplied finding selection differs from the retained review-fix request", 2, { effect: "no_effect" });
63
67
  if (Object.entries(invocation.execution).some(([key, value]) => !Object.hasOwn(retained.selection, key) || hybridDigest(value) !== hybridDigest(retained.selection[key]))
64
68
  || invocation.intakeFile && readIntake() !== retained.objective)
65
69
  throw new AppError("diagnostic_request_conflict", "Supplied selectors differ from the original diagnostic request", 2, { effect: "no_effect" });
@@ -76,7 +80,12 @@ export async function runDiagnosticCli(args, env = process.env, io = process) {
76
80
  publishSetup("dependency_check_completed", "diagnostic:canon");
77
81
  engine = selectMemoryInitInstalledEngine(home, installation.manifest_sha256);
78
82
  publishSetup("dependency_check_completed", "diagnostic:engine");
79
- engine.admitDiagnosticFlow(source, scope.flowId);
83
+ if (scope.flowId === "review-fix")
84
+ engine.admitReviewFixFlow(source);
85
+ else if (scope.flowId === "mb-distill")
86
+ engine.admitDistillFlow(source);
87
+ else
88
+ engine.admitDiagnosticFlow(source, scope.flowId);
80
89
  publishSetup("dependency_check_completed", "diagnostic:handlers");
81
90
  const stdin = io.stdin ?? process.stdin;
82
91
  const selected = await onboardExecutor({ ...(typeof rootProfile === "string" ? { profileId: rootProfile } : {}),
@@ -88,7 +97,8 @@ export async function runDiagnosticCli(args, env = process.env, io = process) {
88
97
  }
89
98
  effects = true;
90
99
  context = openMemoryInitStores(env, { scope: { projectRoot: scope.projectRoot, workspaceRoot: scope.projectRoot, requestId: scope.requestId },
91
- selectionSha256: hybridDigest({ scope, objective, selection: retained?.selection ?? invocation.execution }) }).context;
100
+ selectionSha256: hybridDigest({ scope, objective, selection: retained?.selection ?? invocation.execution,
101
+ ...(scope.flowId === "review-fix" ? { repair_selection: retained?.repair_source?.selection ?? invocation.repairSelection } : {}) }) }).context;
92
102
  publishSetup("dependency_check_completed", "diagnostic:stores");
93
103
  registerProject(context, { root: scope.projectRoot });
94
104
  if (!retained) {
@@ -99,7 +109,8 @@ export async function runDiagnosticCli(args, env = process.env, io = process) {
99
109
  ...invocation.execution, ...(rootProfile !== undefined ? { rootProfile } : {}), execution: "managed", stageSessionMode: "new_session",
100
110
  delegation: invocation.execution.delegation ?? { mode: "external", max_parallel: 1 }
101
111
  }, { requestId: scope.requestId, projectRoot: scope.projectRoot, workspace });
102
- retainDiagnosticRequest(context, { scope, source: source, execution, engine: engine, objective, selection: invocation.execution });
112
+ retainDiagnosticRequest(context, { scope, source: source, execution, engine: engine, objective, selection: invocation.execution,
113
+ ...(invocation.repairSelection ? { repairSelection: invocation.repairSelection } : {}) });
103
114
  }
104
115
  const started = startDiagnosticRun(context, scope), bundle = readFlowRunExecutionBundle(context, { projectRoot: scope.projectRoot, runId: started.runId });
105
116
  if (!bundle)