@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
@@ -13,7 +13,8 @@ import { PreparedMemoryInitLintCapability } from "../services/memory-init-lint-c
13
13
  import { PreparedMemoryInitWorktrunkCapability } from "../services/memory-init-worktrunk-capability.js";
14
14
  import { produceMemoryInitNativeCapacity } from "../services/memory-init-native-capacity.js";
15
15
  import { openMemoryInitStores, assertMemoryInitStorePrerequisites } from "../services/memory-init-stores.js";
16
- import { prepareMemoryInitControllerStart, prepareMemoryUpgradeControllerStart } from "../services/memory-init-controller-start.js";
16
+ import { prepareMemoryInitControllerStart, prepareMemoryUpgradeControllerStart, prepareInteractiveControllerStart, prepareDeliveryControllerStart } from "../services/memory-init-controller-start.js";
17
+ import { isDeliveryFlow } from "../domain/delivery-flows.js";
17
18
  import { runControllerStatus } from "../services/run-controller.js";
18
19
  import { requireRun } from "../services/run-controller-state.js";
19
20
  import { requireProjectByRoot } from "../services/projects.js";
@@ -28,8 +29,8 @@ import { onboardExecutor } from "../services/executor-onboarding.js";
28
29
  import { readOperatorProfiles, readOperatorProfileInput } from "../services/operator-profiles.js";
29
30
  import { resolveDdFlowHome } from "../storage/paths.js";
30
31
  import { askExecutorChoice } from "./executor-onboarding.js";
31
- import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
32
- import { prepareDiagnosticControllerStart } from "../services/memory-init-controller-start.js";
32
+ import { isSourceSnapshotFlow } from "../domain/source-snapshot-flows.js";
33
+ import { prepareDiagnosticControllerStart, prepareMemoryFixControllerStart } from "../services/memory-init-controller-start.js";
33
34
  /** Backpressure bounds the observing client's output; closing it never controls the RUN. */
34
35
  export async function writeMemoryInitObservation(stdout, text) {
35
36
  const disconnected = () => Boolean(stdout.destroyed);
@@ -186,7 +187,7 @@ export async function launchAndObserveMemoryFlow(context, invocation, started, f
186
187
  await writeMemoryInitObservation(io.stdout, `${JSON.stringify(writeResponseFile(response, output), null, output.json || output.progressJsonl ? undefined : 2)}\n`);
187
188
  return 0;
188
189
  }
189
- const launch = isDiagnosticFlow(flowId) ? prepareDiagnosticControllerStart(context, invocation.scope, flowId)
190
+ const launch = isDeliveryFlow(flowId) ? prepareDeliveryControllerStart(context, invocation.scope, flowId) : flowId === "interactive" ? prepareInteractiveControllerStart(context, invocation.scope) : flowId === "mb-fix" ? prepareMemoryFixControllerStart(context, invocation.scope) : isSourceSnapshotFlow(flowId) ? prepareDiagnosticControllerStart(context, invocation.scope, flowId)
190
191
  : (flowId === "mb-init" ? prepareMemoryInitControllerStart : prepareMemoryUpgradeControllerStart)(context, invocation.scope);
191
192
  // Use the existing RUN-bound router: a global CLI must not launch its own
192
193
  // controller code in place of the independently frozen RUN executable.
@@ -1,7 +1,7 @@
1
1
  import { AppError, errorRecord } from "../shared/errors.js";
2
2
  import { parseCommandArgs } from "../services/lifecycle-command.js";
3
3
  import { channel } from "node:diagnostics_channel";
4
- import { nativeHookNonParticipation } from "./native-hook-preparation.js";
4
+ import { nativeHookAssignedSettlement, nativeHookNonParticipation } from "./native-hook-preparation.js";
5
5
  /** These ingress commands consume a prepared store; they never prepare it. */
6
6
  export function isNativeHookIngress(args) {
7
7
  return args[2] === "handle" && ((args[1] === "event" && ["agy", "grok", "droid", "opencode", "zcode"].includes(args[0] ?? ""))
@@ -31,6 +31,10 @@ export async function dispatchNativeHook(args, stdin, env) {
31
31
  phase("ack");
32
32
  return unrelated;
33
33
  }
34
+ if (nativeHookAssignedSettlement(args, stdin, env)) {
35
+ phase("ack");
36
+ return { ok: true, observed: false, reason: "cli_invocation_owns_admission" };
37
+ }
34
38
  const [hooks, { createContext }, { resolveDdFlowHome }] = await Promise.all([
35
39
  import("../services/hooks.js"), import("../runtime/context.js"), import("../storage/paths.js")
36
40
  ]);
@@ -1,12 +1,12 @@
1
1
  import path from "node:path";
2
2
  import { parseJsonObject } from "../shared/json.js";
3
- import { continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
3
+ import { commandOption, continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
4
4
  const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
5
5
  const text = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
6
6
  /** Only a validated, ordinary before-tool command can skip the stateful handler.
7
7
  * In particular after-tool, Stop, malformed and ambiguous events remain owned
8
8
  * by their existing handler; this gate does not invent an admission policy. */
9
- export function nativeHookNonParticipation(args, stdin) {
9
+ function beforeCommand(args, stdin) {
10
10
  const payload = parseJsonObject(stdin || "{}", "native hook stdin");
11
11
  let command;
12
12
  if (args[0] === "zcode" && args[1] === "event") {
@@ -47,9 +47,36 @@ export function nativeHookNonParticipation(args, stdin) {
47
47
  const input = object(payload.input);
48
48
  command = text(input.command) ?? text(input.cmd) ?? text(input.CommandLine);
49
49
  }
50
- if (!command)
50
+ return command ? { command, reason: args[0] === "zcode" && args[1] === "event" ? "acp_evidence_only" : "event_not_participating" } : null;
51
+ }
52
+ export function nativeHookNonParticipation(args, stdin) {
53
+ const before = beforeCommand(args, stdin);
54
+ if (!before)
51
55
  return null;
56
+ const { command, reason } = before;
52
57
  const parsed = parseLifecycleCommand(command);
53
58
  return parsed.participation === "none" && !continuationCommand(command)
54
- ? { ok: true, observed: false, reason: args[0] === "zcode" && args[1] === "event" ? "acp_evidence_only" : "event_not_participating" } : null;
59
+ ? { ok: true, observed: false, reason } : null;
60
+ }
61
+ /** A supplied ID is not a grant. The executing CLI must prove its retained
62
+ * assignment; the synchronous native hook has no second admission role. */
63
+ export function assignedLifecycleHookCommand(command, env) {
64
+ if (!env.DD_FLOW_DAEMON_ID)
65
+ return false;
66
+ const parsed = parseLifecycleCommand(command);
67
+ return parsed.kind === "standalone" && Boolean(commandOption(parsed.invocation, "invocation-id"))
68
+ && ["stage_start", "stage_finish", "stage_pause", "stage_resume", "work_finish", "work_fail"].includes(parsed.invocation.operation);
69
+ }
70
+ /** The native daemon has already retained this exact delivery; the CLI will
71
+ * verify and materialize it before admitting the issued Work invocation. */
72
+ export function retainedWorkStartHookCommand(command, binding) {
73
+ const retained = object(binding), parsed = parseLifecycleCommand(command);
74
+ return parsed.kind === "standalone" && parsed.invocation.operation === "work_start"
75
+ && Boolean(commandOption(parsed.invocation, "invocation-id"))
76
+ && retained.schema_id === "dd-flow/hook-request@1" && retained.source_harness === "codex-desktop"
77
+ && retained.native_command === command && typeof retained.request_id === "string" && retained.request_id.length > 0;
78
+ }
79
+ export function nativeHookAssignedSettlement(args, stdin, env) {
80
+ const before = beforeCommand(args, stdin);
81
+ return Boolean(before && assignedLifecycleHookCommand(before.command, env));
55
82
  }
@@ -3,13 +3,14 @@ import { prepareResponseDestination, writeResponseFile } from "./response-file.j
3
3
  import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
4
4
  import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
5
5
  import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
6
- import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget } from "../services/lifecycle-invocations.js";
6
+ import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget, currentAssignedLifecycle } from "../services/lifecycle-invocations.js";
7
7
  import path from "node:path";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { quote } from "shell-quote";
10
10
  import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
11
11
  import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
12
12
  import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
13
+ import { readAcceptedAssignedWork } from "../services/assigned-lifecycle.js";
13
14
  import { createContext, createRouterContext } from "../runtime/context.js";
14
15
  import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
15
16
  import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
@@ -66,9 +67,43 @@ import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMer
66
67
  import { sdlcStageHandlers } from "../services/stage-handlers.js";
67
68
  import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
68
69
  import { startDiagnosticStage, finishDiagnosticStage } from "../services/diagnostic-stage-handlers.js";
70
+ import { startDistillStage, finishDistillStage } from "../services/distill-stage-handlers.js";
71
+ import { startMemoryFixStage } from "../services/memory-fix-stage-entry.js";
72
+ import { finishMemoryFixStage } from "../services/memory-fix-stage-handlers.js";
73
+ import { startReviewFixStage } from "../services/review-fix-stage-entry.js";
74
+ import { finishReviewFixStage } from "../services/review-fix-stage-handlers.js";
75
+ import { startInteractiveStage } from "../services/interactive-stage-entry.js";
76
+ import { isDeliveryFlow } from "../domain/delivery-flows.js";
77
+ import { startDeliveryStage, readDeliveryStageScope, assertDeliveryCommandOwner } from "../services/delivery-stage-entry.js";
78
+ import { finishDeliveryStage } from "../services/delivery-stage-handlers.js";
79
+ import { prepareDeliveryChecks } from "../services/delivery-checks.js";
80
+ import { executeDeliveryOperation } from "../services/delivery-operation.js";
81
+ import { observeDeliveryResult } from "../services/delivery-result.js";
82
+ import { prepareDeliveryProjectBinding } from "../services/delivery-policy.js";
83
+ import { deliveryApprovalScope } from "../services/delivery-approval.js";
84
+ import { issueDeliveryStageCommands } from "../services/delivery-stage-commands.js";
85
+ import { readDescriptorStageInput } from "../services/descriptor-stage-coordinator.js";
86
+ import { readAcceptedStageData } from "../services/stage-report-renderer.js";
87
+ import { finishInteractiveStage, registerInteractiveReviewCorrection, registerInteractiveCorrectionReview } from "../services/interactive-stage-handlers.js";
88
+ import { observeInteractiveOriginalGitInputs } from "../services/interactive-finish-integration.js";
89
+ import { readInteractiveUserInputs } from "../services/interactive-user-inputs.js";
90
+ import { deliverInteractiveUserInput } from "../services/interactive-input-delivery.js";
91
+ import { PreparedInteractiveMutationScope } from "../services/interactive-mutation-scope.js";
92
+ import { PreparedInteractiveChecks } from "../services/interactive-checks.js";
93
+ import { registerInteractiveStageGraph } from "../services/interactive-work-registration.js";
94
+ import { readInteractiveWorkGit } from "../services/interactive-work-start.js";
95
+ import { integrateInteractiveFinish } from "../services/interactive-git-dispatch.js";
96
+ import { readMemoryFixSourceIntake } from "../services/memory-fix-intake-custody.js";
97
+ import { readMemoryFixUserInputs } from "../services/diagnostic-repair-intake.js";
98
+ import { prepareMemoryFixScopeEvidence } from "../services/memory-fix-scope.js";
99
+ import { prepareMemoryFixPublication } from "../services/memory-fix-publication.js";
100
+ import { integrateMemoryFixFinish } from "../services/memory-fix-git-dispatch.js";
69
101
  import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
70
102
  import { bindStageCoordinatorWork } from "../services/work-registry.js";
71
103
  import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
104
+ import { publishDistillFromStage } from "../services/distill-promotion.js";
105
+ import { readDistillUserInputs } from "../services/distill-user-inputs.js";
106
+ import { hybridDigest } from "../services/hybrid-contracts.js";
72
107
  import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
73
108
  import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
74
109
  import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
@@ -77,7 +112,7 @@ import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFrom
77
112
  import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
78
113
  import { registerMemoryRepairBaselineFromStage, registerMemorySecondRepairBaselineFromStage, reserveMemoryRepairWriterCopyFromStage, reserveMemorySecondRepairCopyFromStage, materializeMemoryRepairWriterCopyFromStage, materializeMemorySecondRepairCopyFromStage, prepareMemoryRepairDispatchFromStage, prepareMemorySecondRepairDispatchFromStage, registerMemoryRepairCurrentViewFromStage, registerMemorySecondRepairCurrentViewFromStage, registerMemoryRepairPublicationSourceFromStage, reserveMemoryRepairPublicationAgainstSourceFromStage, reserveMemorySecondRepairPublicationFromStage, applyMemoryRepairPublicationFromStage, applyMemorySecondRepairPublicationFromStage, finishMemoryRepairPublicationFromStage, finishMemorySecondRepairPublicationFromStage } from "../services/memory-review-handler.js";
79
114
  import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
80
- import { getVnextFanoutStatus, memoryStageFanoutDescriptor, diagnosticStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
115
+ import { getVnextFanoutStatus, registeredStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
81
116
  import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
82
117
  import { runtimeCommand } from "../services/runtime-command.js";
83
118
  import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
@@ -132,6 +167,10 @@ async function runCliRequest(args, io, env) {
132
167
  let output;
133
168
  try {
134
169
  output = parseOutputOptions(args);
170
+ // Expand before routing/context selection, after stripping transport flags.
171
+ // The alias keeps the existing RUN's engine and prepared-store rules.
172
+ if (output.args[0] === "finish")
173
+ output.args = ["interactive", "finish", ...output.args.slice(1)];
135
174
  }
136
175
  catch (error) {
137
176
  const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
@@ -349,6 +388,7 @@ async function runCliRequest(args, io, env) {
349
388
  // after admission; retained outcomes must not need a fresh hook receipt.
350
389
  if (invocationId) {
351
390
  const command = quote(["dd-flow", ...invocationArgs]);
391
+ let nextNativeObservation = 0;
352
392
  const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
353
393
  validateArgs: () => {
354
394
  try {
@@ -362,7 +402,15 @@ async function runCliRequest(args, io, env) {
362
402
  }
363
403
  },
364
404
  prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
365
- assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output) });
405
+ assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output),
406
+ ...((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) => {
407
+ if (Date.now() < nextNativeObservation)
408
+ return;
409
+ nextNativeObservation = Date.now() + 250;
410
+ const { reconcileRetainedNativeHook } = await import("../services/retained-native-hook.js");
411
+ await reconcileRetainedNativeHook(dispatchContext, { scope, invocationId, onProgress,
412
+ onDiagnostic: value => io.stderr.write(`${JSON.stringify({ kind: "native_hook_diagnostic", ...value })}\n`) });
413
+ } } : {}) });
366
414
  if (admission.replay) {
367
415
  retainedOutcome = admission.outcome;
368
416
  settleHook = undefined;
@@ -372,8 +420,10 @@ async function runCliRequest(args, io, env) {
372
420
  invocationSettlement = { context: dispatchContext, id: invocationId };
373
421
  if (observedInvocation)
374
422
  retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
375
- dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
376
- preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
423
+ if (admission.kind === "native_hook") {
424
+ dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
425
+ preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
426
+ }
377
427
  dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
378
428
  dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
379
429
  }
@@ -498,6 +548,23 @@ async function runCliRequest(args, io, env) {
498
548
  io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
499
549
  }
500
550
  }
551
+ if (invocationSettlement && error.details.work_result_accepted === true) {
552
+ const accepted = readAcceptedAssignedWork(invocationSettlement.context, invocationSettlement.id);
553
+ if (accepted) {
554
+ // The Work/WorkSession commit is authoritative. Report its retained
555
+ // answer even if a derivative publication failed after that commit.
556
+ const payload = addWarnings(accepted, [{ code: "work_post_commit_pending", message: "Work result accepted; derivative publication requires reconciliation" }]);
557
+ try {
558
+ writeResponseFile(payload, output);
559
+ }
560
+ catch { /* Accepted SQL reply remains available on stdout/replay. */ }
561
+ if (output.json)
562
+ writeJson(io.stdout, payload);
563
+ else
564
+ io.stdout.write(renderHumanResult(output.args, payload));
565
+ return completedCommandExitCode(payload);
566
+ }
567
+ }
501
568
  if (settleHook) {
502
569
  try {
503
570
  settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
@@ -560,6 +627,9 @@ function databaseModeForCommand(args, classification) {
560
627
  if (isRuntimeMaintenance(args))
561
628
  return "read_existing";
562
629
  const [family, command] = args;
630
+ // Bound interactive operations never initialize or migrate a RUN store.
631
+ if (family === "interactive" && classification.mode !== "read_only_diagnostics")
632
+ return "prepared_existing";
563
633
  if (family === "migration" && command === "readiness")
564
634
  return "prepared_existing";
565
635
  // Managed attempts are issued only from a prepared store. Opening that
@@ -872,6 +942,57 @@ async function prepareCliInput(...args) {
872
942
  async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
873
943
  const { family, command, parsed } = route;
874
944
  const prepared = { route };
945
+ if (family === "delivery" && ["checks", "execute"].includes(command ?? "")) {
946
+ const file = requiredOption(parsed, "result-file"), text = prepareTextFile(file, "Delivery input", 1024 * 1024);
947
+ const value = prepareJsonText(text.text, { file, label: "Delivery input" }).value;
948
+ const keys = command === "checks" ? "path,sha256,subject_ref,version" : "basis,expires_at,reason,scope_sha256,user_inputs_sha256";
949
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== keys)
950
+ throw new AppError("usage", "Delivery input must match the selected command's exact JSON contract", 2, { phase: "prepare", effect: "no_effect" });
951
+ if (command === "checks")
952
+ prepared.deliveryArtifact = value;
953
+ else
954
+ prepared.deliveryApproval = value;
955
+ }
956
+ if (family === "interactive" && command === "integrate" && hasOption(parsed, "result-file")) {
957
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive Git agreement", 1024 * 1024);
958
+ const value = prepareJsonText(input.text, { file, label: "Interactive Git agreement" }).value;
959
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== "basis,include_existing_paths,reason")
960
+ throw new AppError("usage", "Interactive Git agreement requires basis, include_existing_paths and reason", 2, { phase: "prepare", effect: "no_effect" });
961
+ prepared.interactiveGitAgreement = value;
962
+ }
963
+ if (family === "interactive" && command === "checks") {
964
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive check selection", 1024 * 1024);
965
+ const value = prepareJsonText(input.text, { file, label: "Interactive check selection" }).value;
966
+ if (!value || typeof value !== "object" || Array.isArray(value)
967
+ || !["additionalChecks", "additionalChecks,noChecksReason"].includes(Object.keys(value).sort().join()))
968
+ throw new AppError("usage", "Interactive checks require additionalChecks and optional noChecksReason", 2, { phase: "prepare", effect: "no_effect" });
969
+ prepared.interactiveChecksProposal = value;
970
+ }
971
+ if (family === "interactive" && command === "scope") {
972
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive mutation scope", 1024 * 1024);
973
+ const value = prepareJsonText(input.text, { file, label: "Interactive mutation scope" }).value;
974
+ if (!value || typeof value !== "object" || Array.isArray(value)
975
+ || Object.keys(value).sort().join() !== "basis,expectedInputsSha256,paths,reason,requestId")
976
+ throw new AppError("usage", "Interactive scope requires requestId, paths, basis, reason and expectedInputsSha256", 2, { phase: "prepare", effect: "no_effect" });
977
+ const basis = recordObject(recordObject(value).basis);
978
+ if (!basis || Object.keys(basis).sort().join() !== "event_id,role,sha256"
979
+ || !["request", "turn", "answer", "finish"].includes(String(basis.role))
980
+ || typeof basis.event_id !== "number" || !Number.isSafeInteger(basis.event_id) || basis.event_id < 0
981
+ || typeof basis.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(basis.sha256))
982
+ throw new AppError("usage", "Interactive scope basis requires role request|turn|answer|finish, a nonnegative integer event_id and a SHA-256", 2, {
983
+ reason: "invalid_scope_basis_shape", phase: "prepare", effect: "no_effect", recoverable: true,
984
+ retry_command: runtimeCommand(context, ["interactive", "scope", "--project-root", requiredOption(parsed, "project-root"),
985
+ "--run", requiredOption(parsed, "run"), "--result-file", file, "--json", "--progress-jsonl"]),
986
+ 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."
987
+ });
988
+ prepared.interactiveScopeProposal = value;
989
+ }
990
+ if (family === "interactive" && (command === "turn" || command === "finish")) {
991
+ const input = prepareTextFile(requiredOption(parsed, "intake-file"), "Interactive user input", 1024 * 1024);
992
+ prepared.interactiveInputBytes = Buffer.from(input.text);
993
+ if (!prepared.interactiveInputBytes.length || !/^[a-f0-9]{64}$/.test(requiredOption(parsed, "expected-inputs-sha256")))
994
+ throw new AppError("usage", "Interactive delivery requires nonempty UTF-8 input and its current input digest", 2, { phase: "prepare", effect: "no_effect" });
995
+ }
875
996
  if (optionalOption(parsed, "reason-file")) {
876
997
  const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
877
998
  if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
@@ -1232,7 +1353,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1232
1353
  const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
1233
1354
  if (bundle)
1234
1355
  prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
1235
- if (bundle && (["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id))) {
1356
+ 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))) {
1236
1357
  bundle.descriptor.stage(stage);
1237
1358
  if (hasOption(parsed, "dir"))
1238
1359
  throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
@@ -1240,7 +1361,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1240
1361
  if (loaded)
1241
1362
  prepared.stageStart = { externalContext: loaded };
1242
1363
  prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
1243
- flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
1364
+ 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" };
1244
1365
  return prepared;
1245
1366
  }
1246
1367
  }
@@ -1345,7 +1466,8 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1345
1466
  prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
1346
1467
  }
1347
1468
  if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
1348
- || command === "repair" && parsed.positional[0] === "publication-reserve")) {
1469
+ || command === "repair" && parsed.positional[0] === "publication-reserve"
1470
+ || command === "distill" && parsed.positional[0] === "publish")) {
1349
1471
  try {
1350
1472
  const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
1351
1473
  if (!bytes.length || bytes.length > 1024 * 1024)
@@ -1359,6 +1481,12 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1359
1481
  throw error;
1360
1482
  }
1361
1483
  }
1484
+ if (family === "memory" && command === "fix" && ["scope", "publish", "integrate"].includes(parsed.positional[0] ?? "") && hasOption(parsed, "result-file")) {
1485
+ const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Memory repair proposal", 1024 * 1024);
1486
+ prepared.memoryFixProposal = prepareJsonText(input.text, { file, label: "Memory repair proposal" }).value;
1487
+ if (!prepared.memoryFixProposal || typeof prepared.memoryFixProposal !== "object" || Array.isArray(prepared.memoryFixProposal))
1488
+ throw new AppError("usage", "Memory repair proposal must be a JSON object", 2, { phase: "prepare", effect: "no_effect" });
1489
+ }
1362
1490
  if (family !== "stage" || command !== "finish")
1363
1491
  return prepared;
1364
1492
  const projectRoot = requiredOption(parsed, "project-root");
@@ -1375,9 +1503,9 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1375
1503
  const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
1376
1504
  const runtime = run(projectRoot, runId);
1377
1505
  const retainedFlowId = JSON.parse(runtime.indexJson).execution_bundle?.flow_id;
1378
- if (["mb-init", "mb-upgrade"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId)) {
1506
+ if (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId) || isDeliveryFlow(retainedFlowId)) {
1379
1507
  const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
1380
- if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id))
1508
+ 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))
1381
1509
  throw new AppError("cli_input_unprepared", "Registered RUN bundle is unavailable", 1);
1382
1510
  bundle.descriptor.stage(stage);
1383
1511
  if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
@@ -1387,7 +1515,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
1387
1515
  const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
1388
1516
  if (!bytes.length || bytes.length > 1024 * 1024)
1389
1517
  throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
1390
- prepared.memoryStage = { operation: "finish", bytes, flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
1518
+ 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" };
1391
1519
  return prepared;
1392
1520
  }
1393
1521
  const terminalReplay = (name) => {
@@ -1776,12 +1904,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1776
1904
  const externalContext = prepared.stageStart?.externalContext;
1777
1905
  if (prepared.memoryStage?.operation === "start") {
1778
1906
  const runId = requiredPosition(parsed, 0, "run-id");
1779
- const event = hookEventId ?? findRecentMatchingHookEvent(context, {
1907
+ const event = hookEventId ?? (currentAssignedLifecycle(context) ? undefined : findRecentMatchingHookEvent(context, {
1780
1908
  projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
1781
1909
  matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
1782
1910
  errorCode: "trusted_session_binding_required", operation: "Memory stage start"
1783
- }).eventKey;
1784
- const work = (isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
1911
+ }).eventKey);
1912
+ 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 });
1785
1913
  try {
1786
1914
  const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
1787
1915
  promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
@@ -1791,9 +1919,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1791
1919
  const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
1792
1920
  "--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
1793
1921
  const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
1794
- const orchestration = memoryStageFanoutDescriptor(context, { projectRoot, runId, stage }) ?? diagnosticStageFanoutDescriptor(context, { projectRoot, runId, stage });
1922
+ // These assignments belong to this native coordinator delivery, not to
1923
+ // the immutable semantic packet restored by auxiliary owners.
1924
+ const deliveryInstructions = isDeliveryFlow(prepared.memoryStage.flowId) ? issueDeliveryStageCommands(context, { flowId: prepared.memoryStage.flowId, projectRoot, runId, stage, stageRoot: path.dirname(work.result_path) }).instructions : "";
1925
+ const orchestration = registeredStageFanoutDescriptor(context, { projectRoot, runId, stage });
1795
1926
  const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
1796
- worker_prompt_markdown: `${work.worker_prompt_markdown}\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` };
1927
+ 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` };
1797
1928
  bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
1798
1929
  return controlLifecycleReply(context, { projectRoot, runId }, delivery);
1799
1930
  }
@@ -1803,6 +1934,8 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1803
1934
  }
1804
1935
  }
1805
1936
  const claimVnextStageStart = (runId) => {
1937
+ if (currentAssignedLifecycle(context)?.operation === "stage_start")
1938
+ return undefined;
1806
1939
  registerProject(context, { root: projectRoot });
1807
1940
  return findRecentMatchingHookEvent(context, {
1808
1941
  projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
@@ -1900,12 +2033,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
1900
2033
  // Descriptor settlement validates exact coordinator custody and claims
1901
2034
  // the fresh hook atomically with result acceptance. Do not consume it
1902
2035
  // through the SDLC lifecycle owner before that settlement.
1903
- return controlledReply((isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
1904
- hookEventId: hookEventId ?? findRecentMatchingHookEvent(context, {
2036
+ 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,
2037
+ hookEventId: hookEventId ?? (currentAssignedLifecycle(context) ? "" : findRecentMatchingHookEvent(context, {
1905
2038
  projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
1906
2039
  matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
1907
2040
  errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
1908
- }).eventKey }));
2041
+ }).eventKey) }));
1909
2042
  }
1910
2043
  const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
1911
2044
  || (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
@@ -2112,6 +2245,102 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
2112
2245
  if (family === "schema") {
2113
2246
  return prepared.schemaValidation;
2114
2247
  }
2248
+ if (family === "delivery") {
2249
+ const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
2250
+ const runId = resolveRun(context, project.id, requiredPosition(parsed, 0, "run-id")).id, stage = requiredOption(parsed, "stage");
2251
+ const input = { projectRoot, runId, stage, command: quote(["dd-flow", ...args]) };
2252
+ const selected = command === "inputs" ? readDeliveryStageScope(context, input) : assertDeliveryCommandOwner(context, input);
2253
+ if (command === "checks") {
2254
+ if (!prepared.deliveryArtifact)
2255
+ throw new AppError("cli_input_unprepared", "Delivery artifact was not prepared", 1);
2256
+ const checks = prepareDeliveryChecks(context, { ...input, artifact: prepared.deliveryArtifact });
2257
+ await checks.run();
2258
+ return { ok: true, verification: checks.evidence().receipt };
2259
+ }
2260
+ if (command === "execute") {
2261
+ if (!prepared.deliveryApproval)
2262
+ throw new AppError("cli_input_unprepared", "Delivery approval was not prepared", 1);
2263
+ return executeDeliveryOperation(context, { ...input, approval: prepared.deliveryApproval });
2264
+ }
2265
+ const packet = readDescriptorStageInput(context, selected.scope, selected.bundle);
2266
+ const previous = packet.input.predecessor ? readAcceptedStageData(context, { ...selected.scope, stage: packet.input.predecessor.stage }) : null;
2267
+ const details = previous ? JSON.parse(previous.bytes.toString("utf8")).details : null;
2268
+ const operation = details?.decision && details?.artifact ? deliveryApprovalScope(selected.scope, selected.bundle, { operationId: details.decision.operation_id, binding: details.decision.binding,
2269
+ artifact: { subject_ref: details.artifact.subject_ref, version: details.artifact.version, sha256: details.artifact.sha256 } }) : null;
2270
+ if (command === "inputs")
2271
+ return { flow_id: selected.flowId, run_id: runId, stage, user_inputs: selected.users,
2272
+ user_inputs_sha256: selected.userInputsSha256, operation_scope: operation, scope_sha256: operation ? hybridDigest(operation) : null };
2273
+ if (command === "reconcile") {
2274
+ if (!details?.decision)
2275
+ throw new AppError("delivery_stage_invalid", "Original delivery decision is required", 2, { effect: "no_effect" });
2276
+ const policy = prepareDeliveryProjectBinding(selected.bundle.execution.snapshot().scope.workspace, details.decision.policy, details.decision.binding.selector);
2277
+ if (hybridDigest(policy.binding) !== hybridDigest(details.decision.binding))
2278
+ throw new AppError("delivery_stage_invalid", "Original delivery policy changed", 2, { effect: "no_effect" });
2279
+ return observeDeliveryResult(context, { ...selected.scope, binding: policy.binding,
2280
+ assertCurrent() { assertDeliveryCommandOwner(context, input); policy.assertCurrent(); } });
2281
+ }
2282
+ throw new AppError("usage", "Unknown delivery command", 2);
2283
+ }
2284
+ if (family === "interactive") {
2285
+ const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
2286
+ const runId = resolveRun(context, project.id, requiredOption(parsed, "run")).id;
2287
+ if (command === "integrate")
2288
+ return integrateInteractiveFinish(context, { projectRoot, runId,
2289
+ ...(prepared.interactiveGitAgreement ? { agreement: prepared.interactiveGitAgreement } : {}) });
2290
+ if (command === "correction")
2291
+ return registerInteractiveReviewCorrection(context, { projectRoot, runId, reviewWorkId: requiredOption(parsed, "work") });
2292
+ if (command === "re-review")
2293
+ return registerInteractiveCorrectionReview(context, { projectRoot, runId, correctionWorkId: requiredOption(parsed, "work") });
2294
+ if (command === "checks") {
2295
+ if (!prepared.interactiveChecksProposal)
2296
+ throw new AppError("cli_input_unprepared", "Interactive check selection was not prepared", 1);
2297
+ let checks;
2298
+ try {
2299
+ checks = PreparedInteractiveChecks.prepare(context, { projectRoot, runId,
2300
+ stage: requiredOption(parsed, "stage"), ...prepared.interactiveChecksProposal });
2301
+ }
2302
+ catch (error) {
2303
+ if (isAppError(error) && error.code === "interactive_checks_invalid"
2304
+ && error.details.reason === "invalid_check_selection" && error.details.effect === "no_effect") {
2305
+ Object.assign(error.details, { phase: "prepare", recoverable: true,
2306
+ retry_command: runtimeCommand(context, ["interactive", "checks", "--project-root", projectRoot, "--run", runId,
2307
+ "--stage", requiredOption(parsed, "stage"), "--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
2308
+ 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." });
2309
+ }
2310
+ throw error;
2311
+ }
2312
+ return checks.execute(context).then(receipts => ({ plan_sha256: checks.sha256, plan: checks.value(), receipts }));
2313
+ }
2314
+ if (command === "register") {
2315
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
2316
+ if (!bundle || bundle.manifest.flow_id !== "interactive")
2317
+ throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
2318
+ return registerInteractiveStageGraph(context, { projectRoot, runId, stage: requiredOption(parsed, "stage"),
2319
+ gitExecutable: readInteractiveWorkGit(bundle).git_executable });
2320
+ }
2321
+ if (command === "git-inputs")
2322
+ return observeInteractiveOriginalGitInputs(context, { projectRoot, runId, stage: requiredOption(parsed, "stage") });
2323
+ if (command === "inputs" || command === "turn" || command === "finish" || command === "scope") {
2324
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
2325
+ if (!bundle || bundle.manifest.flow_id !== "interactive")
2326
+ throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
2327
+ const scope = { projectId: project.id, runId };
2328
+ if (command === "inputs")
2329
+ return readInteractiveUserInputs(context, scope, bundle);
2330
+ if (command === "scope") {
2331
+ if (!prepared.interactiveScopeProposal)
2332
+ throw new AppError("cli_input_unprepared", "Interactive scope proposal was not prepared", 1);
2333
+ const result = PreparedInteractiveMutationScope.prepare(context, scope, bundle, prepared.interactiveScopeProposal).retain(context);
2334
+ return { changed: result.changed, scope_revision: result.scope.revision, user_inputs_sha256: result.scope.inputs.sha256,
2335
+ baseline_sha256: result.scope.baseline.sha256, paths: result.scope.active_paths, needs_scope_refresh: result.scope.needs_scope_refresh };
2336
+ }
2337
+ if (!prepared.interactiveInputBytes)
2338
+ throw new AppError("cli_input_unprepared", "Interactive user bytes were not prepared", 1);
2339
+ return deliverInteractiveUserInput(context, scope, bundle, { projectRoot, intakeFile: requiredOption(parsed, "intake-file"),
2340
+ requestId: requiredOption(parsed, "request-id"), kind: command, bytes: prepared.interactiveInputBytes,
2341
+ expectedInputsSha256: requiredOption(parsed, "expected-inputs-sha256") });
2342
+ }
2343
+ }
2115
2344
  if (family === "memory") {
2116
2345
  return dispatchMemory(context, command, parsed, prepared);
2117
2346
  }
@@ -2502,6 +2731,32 @@ function dispatchMemory(context, command, parsed, prepared) {
2502
2731
  }
2503
2732
  if (command === "upgrade" && subcommand === "publish")
2504
2733
  return publishMemoryUpgradeFromStage(context, scope());
2734
+ if (command === "distill" && subcommand === "inputs") {
2735
+ const inputs = readDistillUserInputs(context, scope());
2736
+ return { ...inputs, user_inputs_sha256: hybridDigest(inputs) };
2737
+ }
2738
+ if (command === "fix" && subcommand === "context") {
2739
+ const intake = readMemoryFixSourceIntake(context, scope()), users = readMemoryFixUserInputs(context, scope());
2740
+ return { ...intake, user_inputs: users, user_inputs_sha256: hybridDigest(users) };
2741
+ }
2742
+ if (command === "fix" && subcommand === "scope") {
2743
+ const proposal = prepared.memoryFixProposal;
2744
+ if (!proposal || Object.keys(proposal).sort().join() !== "routing,write_scope")
2745
+ throw new AppError("usage", "Repair scope proposal requires routing and write_scope", 2, { effect: "no_effect" });
2746
+ return prepareMemoryFixScopeEvidence(context, { ...scope(), routing: proposal.routing, writeScope: proposal.write_scope }).snapshot();
2747
+ }
2748
+ if (command === "fix" && subcommand === "publish") {
2749
+ if (!prepared.memoryFixProposal)
2750
+ throw new AppError("cli_input_unprepared", "Repair proposal was not prepared", 1);
2751
+ return prepareMemoryFixPublication(context, { ...scope(), proposal: prepared.memoryFixProposal }).apply();
2752
+ }
2753
+ if (command === "fix" && subcommand === "integrate")
2754
+ return integrateMemoryFixFinish(context, { ...scope(), ...(prepared.memoryFixProposal ? { agreement: prepared.memoryFixProposal } : {}) });
2755
+ if (command === "distill" && subcommand === "publish") {
2756
+ if (!prepared.memoryOperationBytes)
2757
+ throw new AppError("cli_input_unprepared", "Distillation proposal bytes were not prepared", 1);
2758
+ return publishDistillFromStage(context, { ...scope(), bytes: prepared.memoryOperationBytes });
2759
+ }
2505
2760
  if (command === "verify" && subcommand === "checks")
2506
2761
  return runMemoryVerificationChecks(context, scope());
2507
2762
  if (command === "review" && subcommand === "register")