@deksden-com/dd-flow-cli 0.9.0-beta.13 → 0.9.0-beta.133

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 (204) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/README.md +101 -1
  3. package/dist/build-info.json +10 -10
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +306 -0
  6. package/dist/cli/help.js +88 -12
  7. package/dist/cli/hook-ingress.js +78 -0
  8. package/dist/cli/input-preparation.js +116 -0
  9. package/dist/cli/native-hook-ingress.js +80 -0
  10. package/dist/cli/run-cli.js +1465 -450
  11. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  12. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  13. package/dist/cli/runtime-process.js +153 -0
  14. package/dist/cli.js +17 -2
  15. package/dist/domain/stage-catalog.js +1 -1
  16. package/dist/harness-runtime/DD-ZCODE.md +99 -0
  17. package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
  18. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  19. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  20. package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
  21. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  22. package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
  23. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  24. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  25. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  26. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  27. package/dist/harness-runtime/lib/codex-capacity-policy.d.mts +10 -0
  28. package/dist/harness-runtime/lib/codex-capacity-policy.mjs +89 -0
  29. package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
  30. package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
  31. package/dist/harness-runtime/lib/daemon-operations.mjs +370 -0
  32. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
  33. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +693 -0
  34. package/dist/harness-runtime/lib/dd-agy.mjs +59 -0
  35. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  36. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +190 -0
  37. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  38. package/dist/harness-runtime/lib/dd-codex.mjs +805 -0
  39. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +128 -0
  40. package/dist/harness-runtime/lib/dd-droid.mjs +561 -0
  41. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +490 -0
  42. package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
  43. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +255 -0
  44. package/dist/harness-runtime/lib/dd-opencode.mjs +128 -0
  45. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +703 -0
  46. package/dist/harness-runtime/lib/dd-zcode.mjs +1108 -0
  47. package/dist/harness-runtime/lib/delegation-instructions.d.mts +13 -0
  48. package/dist/harness-runtime/lib/delegation-instructions.mjs +144 -0
  49. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  50. package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
  51. package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
  52. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  53. package/dist/harness-runtime/lib/lease-renewal.mjs +36 -0
  54. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  55. package/dist/harness-runtime/lib/lifecycle-command.mjs +272 -0
  56. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
  57. package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
  58. package/dist/harness-runtime/lib/managed-daemon.d.mts +12 -0
  59. package/dist/harness-runtime/lib/managed-daemon.mjs +619 -0
  60. package/dist/harness-runtime/lib/model-observations.mjs +101 -0
  61. package/dist/harness-runtime/lib/native-children.d.mts +13 -0
  62. package/dist/harness-runtime/lib/native-children.mjs +78 -0
  63. package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
  64. package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
  65. package/dist/harness-runtime/lib/native-streams.mjs +45 -0
  66. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  67. package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
  68. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  69. package/dist/harness-runtime/lib/operation-errors.d.mts +14 -0
  70. package/dist/harness-runtime/lib/operation-errors.mjs +116 -0
  71. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  72. package/dist/harness-runtime/lib/operation-progress.mjs +112 -0
  73. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  74. package/dist/harness-runtime/lib/process-json.mjs +201 -0
  75. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  76. package/dist/harness-runtime/lib/runner-events.mjs +200 -0
  77. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  78. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  79. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  80. package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
  81. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  82. package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
  83. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  84. package/dist/protocol/local-files.js +18 -9
  85. package/dist/runtime/context.js +8 -4
  86. package/dist/schemas/agent-profile.schema.json +1 -1
  87. package/dist/schemas/code-review-result.schema.json +1 -1
  88. package/dist/schemas/code-verification.schema.json +7 -5
  89. package/dist/schemas/code-work-batch.schema.json +2 -2
  90. package/dist/schemas/flow-run.schema.json +2 -0
  91. package/dist/schemas/plan-review-decision.schema.json +3 -3
  92. package/dist/schemas/plan-review-result.schema.json +2 -2
  93. package/dist/schemas/run-control-receipt.schema.json +118 -0
  94. package/dist/schemas/run-control-request.schema.json +36 -0
  95. package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
  96. package/dist/services/branch-context.js +3 -3
  97. package/dist/services/canon.js +11 -8
  98. package/dist/services/check-runner-evidence.js +121 -0
  99. package/dist/services/cleanup.js +133 -40
  100. package/dist/services/cli-operation-classifier.js +22 -24
  101. package/dist/services/code-checks.js +851 -109
  102. package/dist/services/codex-capacity.js +367 -0
  103. package/dist/services/codex-hook-delivery.js +32 -0
  104. package/dist/services/command-context.js +80 -0
  105. package/dist/services/compatibility-preflight.js +13 -3
  106. package/dist/services/config.js +11 -8
  107. package/dist/services/continuation-outcome.js +20 -0
  108. package/dist/services/controller-fanout.js +119 -0
  109. package/dist/services/dashboard.js +135 -56
  110. package/dist/services/engines.js +245 -119
  111. package/dist/services/eval-snapshots.js +1146 -105
  112. package/dist/services/execution-policy.js +181 -0
  113. package/dist/services/external-work-launch.js +230 -0
  114. package/dist/services/harness-adapter.js +329 -36
  115. package/dist/services/harness-config.js +2 -0
  116. package/dist/services/hooks.js +747 -325
  117. package/dist/services/lanes.js +78 -65
  118. package/dist/services/lifecycle-command.js +1 -288
  119. package/dist/services/lifecycle-contract.js +1 -0
  120. package/dist/services/lifecycle-invocations.js +1774 -0
  121. package/dist/services/managed-daemon-binding.js +38 -0
  122. package/dist/services/managed-lease-monitor.js +133 -0
  123. package/dist/services/managed-processes.js +409 -51
  124. package/dist/services/merge-queue.js +228 -149
  125. package/dist/services/merge-server.js +163 -35
  126. package/dist/services/migrations.js +37 -16
  127. package/dist/services/native-daemon-history.js +49 -0
  128. package/dist/services/native-session-control.js +39 -0
  129. package/dist/services/plan-runtime.js +26 -11
  130. package/dist/services/plans.js +28 -25
  131. package/dist/services/projects.js +20 -15
  132. package/dist/services/prompts.js +28 -17
  133. package/dist/services/protocols.js +163 -87
  134. package/dist/services/recovery-observation-budget.js +121 -0
  135. package/dist/services/recovery-snapshot-database.js +145 -0
  136. package/dist/services/repair-intents.js +42 -0
  137. package/dist/services/review-copy.js +157 -0
  138. package/dist/services/review-deferral.js +23 -0
  139. package/dist/services/run-control-receipt.js +56 -0
  140. package/dist/services/run-control-worker.js +411 -0
  141. package/dist/services/run-control.js +985 -0
  142. package/dist/services/run-controller-adapter.js +399 -0
  143. package/dist/services/run-controller-capture.js +179 -0
  144. package/dist/services/run-controller-process.js +187 -0
  145. package/dist/services/run-controller-recovery.js +289 -0
  146. package/dist/services/run-controller-state.js +97 -0
  147. package/dist/services/run-controller.js +1035 -0
  148. package/dist/services/run-engine-bindings.js +1 -1
  149. package/dist/services/run-fork.js +147 -0
  150. package/dist/services/run-observations.js +152 -0
  151. package/dist/services/run-projection.js +22 -64
  152. package/dist/services/run-recovery-guard.js +75 -0
  153. package/dist/services/run-recovery-runtime.js +112 -0
  154. package/dist/services/run-recovery.js +349 -0
  155. package/dist/services/runs.js +558 -217
  156. package/dist/services/runtime-budget.js +430 -0
  157. package/dist/services/runtime-command.js +43 -0
  158. package/dist/services/runtime-scope-capture.js +52 -0
  159. package/dist/services/runtime-scope-control.js +468 -0
  160. package/dist/services/runtime-scope-resume.js +599 -0
  161. package/dist/services/runtime-scope-stop.js +180 -0
  162. package/dist/services/runtime-scope-worker.js +287 -0
  163. package/dist/services/runtime-service.js +96 -0
  164. package/dist/services/schema-validation.js +10 -8
  165. package/dist/services/scope-writer-cleanup.js +27 -0
  166. package/dist/services/sessions.js +35 -69
  167. package/dist/services/stage-blocker.js +17 -7
  168. package/dist/services/stage-context.js +185 -19
  169. package/dist/services/stage-lifecycle.js +98 -107
  170. package/dist/services/stage-pause.js +89 -75
  171. package/dist/services/stage-report-renderer.js +158 -6
  172. package/dist/services/stage-work-graph.js +35 -0
  173. package/dist/services/tooling.js +32 -23
  174. package/dist/services/usage.js +49 -101
  175. package/dist/services/verification-matrix.js +374 -0
  176. package/dist/services/vnext-code-review.js +222 -87
  177. package/dist/services/vnext-code.js +324 -251
  178. package/dist/services/vnext-contracts.js +10 -1
  179. package/dist/services/vnext-execution-profile.js +79 -4
  180. package/dist/services/vnext-fanout.js +177 -17
  181. package/dist/services/vnext-merge.js +335 -113
  182. package/dist/services/vnext-plan-review.js +276 -126
  183. package/dist/services/vnext-plan.js +227 -102
  184. package/dist/services/vnext-protocolize.js +335 -82
  185. package/dist/services/vnext-specify.js +119 -81
  186. package/dist/services/work-registry.js +1038 -192
  187. package/dist/services/workspace-bootstrap.js +87 -0
  188. package/dist/services/workspace-files.js +85 -0
  189. package/dist/services/worktrees.js +98 -44
  190. package/dist/shared/entity-references.js +8 -0
  191. package/dist/shared/errors.js +17 -0
  192. package/dist/storage/agent-output.js +26 -0
  193. package/dist/storage/database.js +781 -37
  194. package/dist/storage/foreign-key-transaction.js +39 -0
  195. package/dist/storage/immutable-input.js +45 -0
  196. package/dist/storage/import-guard.js +102 -0
  197. package/dist/storage/native-cleanup.js +52 -0
  198. package/dist/storage/paths.js +17 -4
  199. package/dist/storage/projection-lock.js +73 -0
  200. package/dist/storage/work-references.js +23 -0
  201. package/dist/storage/writer-contract.js +77 -0
  202. package/dist/storage/writer-migration.js +85 -0
  203. package/package.json +30 -13
  204. package/tools/repair-paused-run-status.mjs +84 -0
package/dist/cli/help.js CHANGED
@@ -1,4 +1,37 @@
1
1
  const helpPages = new Map([
2
+ ["runtime scope", `Usage:
3
+ dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID --json
4
+ dd-flow runtime scope status --scope-id EVAL_ID --json
5
+ dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID --json
6
+ dd-flow runtime scope control --scope-id EVAL_ID --request-id ID --mode pause|stop [--manifest-json JSON] --json
7
+ dd-flow runtime scope resume --scope-id EVAL_ID --request-id ID --generation N --capture-key SHA256 --json
8
+ dd-flow runtime scope reconcile --scope-id EVAL_ID --generation N --request-id ID --json
9
+
10
+ Use the EVAL manifest's retained DD_FLOW_RESOURCE_HOME. Commands select exactly
11
+ one scope across its registered runtime homes; neighboring scopes are untouched.
12
+ fence durably blocks new provider turns and owned baseline checks. It does not
13
+ stop existing work. status reports retained ownership, not proof of settlement.
14
+ stop fences first and requests owned daemon-tree and baseline-process cleanup.
15
+ Inspect settled and each node's evidence: ok alone does not prove cleanup.
16
+ Unproven ownership or cleanup remains pending; repeat stop to observe settlement.
17
+ The fence/stop route is terminal cancellation and has no resume command.
18
+ control records recoverable scope intent and starts a detached drain owner.
19
+ The EVAL resolver supplies a runtime-scope-manifest@1 declaration with the source
20
+ manifest path, SHA-256 and execution IDs, including queued executions. Declaration
21
+ changes are rejected. resume accepts the exact captured generation and starts
22
+ detached recovery preparation. It does not yet release scope admission: inspect
23
+ dispatch_blocked and pending reasons; accepted preparation is not resumed work.
24
+ RUN pause/stop/resume uses dd-flow run control.`],
25
+ ["storage", `Usage:
26
+ dd-flow storage migrate-writer --database ABSOLUTE_DB --backup NEW_ABSOLUTE_FILE --offline --json
27
+
28
+ Drain all local and external runtime owners before using --offline. This command
29
+ migrates one existing database, not a whole runtime or retained provider Session.
30
+ It refuses unresolved ownership rows, preserves a verified WAL-safe backup, and
31
+ fences unaware old writers. It does not initialize the default DD_FLOW_HOME.
32
+ The backup path must be new; existing backups are never overwritten. An exact
33
+ retry of a committed migration returns its receipt after verifying the backup.
34
+ If no committed receipt exists, retain the failed backup and choose a new path.`],
2
35
  [
3
36
  "",
4
37
  `dd-flow - mechanical control layer for dd-flow workflows
@@ -33,11 +66,14 @@ Core commands:
33
66
  session register/status/stop Register flow sessions and stop workers.
34
67
  dashboard data/render/open/refresh Render, refresh, open, and inspect project/global dashboards.
35
68
  schema validate Validate canonical dd-flow JSON data contracts.
69
+ storage migrate-writer Explicitly back up and fence a drained SQLite store.
70
+ runtime scope fence/status/stop Fence, inspect, or cancel one retained EVAL scope.
36
71
  memory permissions preflight Check Memory Bank write/read permissions.
37
72
  integration cmux status Inspect optional cmux integration.
38
73
  codex home/hooks/hook Manage Codex homes, hooks, and hook events.
39
74
  zcode event/usage Ingest trusted zcode-acp lifecycle and usage evidence.
40
75
  grok event/usage Ingest trusted Grok Build lifecycle and usage evidence.
76
+ droid event/usage Ingest trusted Factory Droid lifecycle and physical usage evidence.
41
77
  opencode event/usage Ingest trusted OpenCode lifecycle and usage evidence.
42
78
  agy event/usage Ingest trusted Antigravity CLI lifecycle and usage evidence.
43
79
  worktree plan/create/status Record Worktrunk-backed feature worktrees.
@@ -152,12 +188,18 @@ Usage:
152
188
  dd-flow run start --project-root <root> [--workspace-root <checkout>] --flow-kind <kind> --subject-type <type> --subject-id <id> --slug <slug> [--preset compact|normal|full] [--task-profile-file <file>] [--protocol-override-file <file>] [--flag key=value] [--next-action <text>] --json
153
189
  dd-flow id next --type protocol|run --project-root <root> --slug <slug> --json
154
190
  dd-flow run status <RUN-ID|RUN-short-id> --project-root <root> --json
191
+ dd-flow run control status --run <RUN-ID> --project-root <root> --json
192
+ dd-flow run control reconcile --run <RUN-ID> --project-root <root> --control-id <ID> --request-id <ID> --json
193
+ dd-flow run drive launch --run <RUN-ID> --project-root <root> --request-id <id> --context-file <file> --context-sha256 <sha256> [--stop-after <stage>] [--capture-root <directory>] --json
194
+ dd-flow run drive status --run <RUN-ID> --project-root <root> [--after <sequence>] --json
155
195
  dd-flow run list --project-root <root> --json
156
- dd-flow run prepare-vnext-specify --project-root <root> --slug <slug> --json
157
- dd-flow run snapshot create <RUN-ID|RUN-short-id> (--stage-entry <stage>|--candidate) --project-root <root> --output <directory> --json
158
- dd-flow run snapshot restore --snapshot <directory> --project-root <prepared-root> --json
196
+ dd-flow run prepare-vnext-specify --project-root <root> --slug <slug> [--execution-routing-file <file>] --json
197
+ dd-flow run recovery inspect|begin|seal|capture|resume <RUN-ID|RUN-short-id> --project-root <root> --json
198
+ dd-flow run snapshot create <RUN-ID|RUN-short-id> (--stage-entry <stage>|--candidate|--incomplete|--recovery-id <RCV-ID>) --project-root <root> --output <directory> --json
199
+ dd-flow run snapshot restore --snapshot <directory> --project-root <prepared-root> [--recovery-id <RCV-ID>] --json
159
200
  dd-flow run snapshot bootstrap create --project-root <root> --output <directory> --json
160
201
  dd-flow run snapshot bootstrap restore --snapshot <directory> --project-root <prepared-root> --json
202
+ dd-flow run fork --from <sealed-stage-entry-snapshot> --output <empty-directory> --engine-version <exact-version> --request-id <id> [--integrity-checksum <sha256>] --json
161
203
  dd-flow run timeline <RUN-ID|RUN-short-id> --project-root <root> --json
162
204
  dd-flow stat usage --run <RUN-ID|RUN-short-id> --project-root <root> [--stage <stage[,stage...]>] [--session <session-id>] --json
163
205
  dd-flow stat run sessions ls --run <RUN-ID|RUN-short-id> --project-root <root> --json
@@ -169,7 +211,13 @@ Usage:
169
211
 
170
212
  RUN-* is the execution envelope for one concrete flow launch. Semantic truth remains in protocol, experiment, DEF, scenario, evidence, and Memory Bank documents. New run artifacts are stored under DD_FLOW_HOME/projects/<PRJ-ID-slug>/runs/<RUN-ID-slug>/ with one run.json projection and an append-only timeline.jsonl. Timeline is the compact full run report: summary is always present, while --hide may suppress bulky sections. Use \`dd-flow stat usage --run …\` to reread registered local Codex transcript counters and tool-call facts and refresh the current usage projection; unavailable data is explicit rather than zero.
171
213
 
172
- run prepare-vnext-specify allocates an otherwise unstarted vNext RUN for the canonical SPECIFY entry checkpoint. run snapshot is the eval-only checkpoint boundary: it snapshots one quiescent RUN, its dedicated DD_FLOW_HOME, and the exact project tree including its Git repository, then restores them into a fresh dedicated runtime. It is not a general runtime backup command and fails closed for a shared runtime.
214
+ run start and prepare-vnext-specify accept --execution-routing-file for vNext RUNs: dd-flow/execution-routing@1 with an execution object selects the complete routing policy from existing agent-profiles. RUN creation freezes the input and selected profiles without changing project settings.
215
+ run snapshot restore also accepts --execution-routing-file for a new stage-entry experiment. Its fresh DD_FLOW_HOME may contain only agent-profiles and the empty database; selected profiles are read before import, previous RUN policy is archived, and existing Work overrides must resolve against the selected frozen profiles. Recovery imports reject this option.
216
+ run snapshot restore and run fork accept --operational-decision-file for an explicitly accepted decision bound to the new stage-entry experiment. Store the input outside fork output; its bytes are bound to the idempotent request. A snapshot with a scoped decision cannot grant it to another execution implicitly. Recovery retains the original decision and rejects replacement.
217
+ run fork restores one sealed stage-entry snapshot to an isolated project and DD_FLOW_HOME, archives source authority, then binds an exact engine only in that copy. It is idempotent by request ID and output receipt; it never resumes a provider session or changes its source RUN.
218
+ run prepare-vnext-specify allocates an otherwise unstarted vNext RUN for the canonical SPECIFY entry checkpoint. run recovery makes an interrupted RUN read-only, requires an explicit settled-tree receipt, captures immutable recovery evidence, then explicitly returns delegated Work to the ready graph. run snapshot is the eval-only checkpoint boundary: it snapshots one quiescent RUN, its dedicated DD_FLOW_HOME, and the exact project tree including its Git repository, then restores them into a fresh dedicated runtime. It is not a general runtime backup command and fails closed for a shared runtime.
219
+
220
+ Managed drive is not yet native-provider qualified. With --capture-root, its detached controller fences RUN writers and captures each completed Stage before dispatching its successor. The capture root must be outside the runtime, project and workspace. Status events expose boundary_captured receipts; capturing means writers are still fenced, and capture_failed retains the fence for operator reconciliation. External snapshot creation is rejected while a managed controller owns a live RUN.
173
221
 
174
222
  Examples:
175
223
  dd-flow run start --project-root "$PWD" --flow-kind mb_sdlc --subject-type protocol --subject-id PRT-001-demo --slug demo --json
@@ -184,16 +232,22 @@ Examples:
184
232
  Usage:
185
233
  dd-flow stage start <RUN-ID|RUN-short-id> --project-root <root> --stage <name> [--dir <NN-stage-slug>] [--require-session-binding] --json
186
234
  dd-flow stage start --bootstrap --project-root <root> --stage specify --subject <safe-subject> (--intake-file <path>|--intake-stdin) [--require-session-binding] --json
187
- dd-flow stage pause <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> --question-stdin --json
188
- dd-flow stage resume <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> --answer-stdin --json
189
- dd-flow stage block <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> --kind engine|harness|environment --code <code> --summary-stdin [--retryable] --json
235
+ dd-flow stage pause <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> (--question-file <path>|--question-stdin) --json
236
+ dd-flow stage resume <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> (--answer-file <path>|--answer-stdin) --json
237
+
238
+ Managed resume commands restore the accepted answer file from runtime state. Execute the returned command without adding an answer path or recreating the answer. The explicit answer flags above are for unmanaged calls.
239
+ Path aliases @project, @workspace and @run are supported only in declared CLI path parameters and require a uniquely bound context. File-writing tools use physical paths. Both --option value and --option=value are accepted; quoted or escaped aliases are normalized identically. Pre-RUN bootstrap paths and external schema directories require physical paths.
240
+ An aggregate CODE/CODE-REVIEW failure may return continuation.kind=repair_required: its repair Work is already registered. End the coordinator turn and let the controller dispatch the fresh child; do not recreate the repair. A recovery retry uses --retry-check <receipt> --reason-file <recovery.json>, containing only {"reason":"actual environment recovery evidence"}. Legacy --reason remains accepted, but cannot be combined with --reason-file. A needs_input continuation is not yet executable: write its requested file first, then use command_after_input.
241
+ dd-flow stage block <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> --kind engine|harness|environment --code <code> (--summary-file <path>|--summary-stdin) [--retryable] --json
190
242
  dd-flow stage unblock <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --work <WORK-ID> --json
191
243
  dd-flow stage fanout status <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --json
192
244
  dd-flow stage fanout dispatch <RUN-ID|RUN-short-id> --project-root <root> --stage <name> --json
245
+ dd-flow stage fanout reconcile <RUN-ID> --project-root <root> --stage <name> --observations-file <native-observations.json> --json
246
+ dd-flow stage native observe <RUN-ID> --project-root <root> --stage <name> --observations-file <native-observations.json> --json
193
247
  dd-flow stage finish <RUN-ID|RUN-short-id> --project-root <root> --stage <name> [--semantic-file <RUN-local-json>] --json
194
248
  dd-flow stage finish <RUN-ID|RUN-short-id> --project-root <root> --stage specify (--result-file <RUN-local-specify.json>|--result-stdin) --outcome <specified|failed|cancelled> --json
195
249
 
196
- stage start is the worker's first practical flow command. It performs deterministic setup and returns a ready-to-use, stage-specific work packet with trusted runtime facts, compatibility and permission preflight, aliases, write boundary and exact lifecycle commands. A material user question uses stage pause, not stage finish: pause leaves the same stage, Work, Session and attempt open and returns the exact resume command. Engine, harness and environment failures use stage block and never create HITL. On the next Turn, pass a complete user answer to stage resume, or repair an external blocker and call its returned stage unblock command. stage fanout is read-only orchestration state plus the stage's deterministic Work materialization action for an external runner; it never makes product decisions. stage finish is reserved for actual stage completion and generates the final deterministic reports. Generated lifecycle commands include DD_FLOW_HOME when the RUN uses a non-default runtime.`
250
+ stage start is the worker's first practical flow command. It performs deterministic setup and returns a ready-to-use, stage-specific work packet with trusted runtime facts, compatibility and permission preflight, aliases, write boundary and exact lifecycle commands. Managed commands use scoped RUN/Work aliases and @project; their internal invocation UUID is bound by the native hook and is intentionally not model-facing. @ aliases apply only to declared dd-flow path arguments, not to shell tools. A material user question uses stage pause, not stage finish: pause leaves the same stage, Work, Session and attempt open and returns the exact resume command. Engine, harness and environment failures use stage block and never create HITL. On the next Turn, pass a complete user answer to stage resume, or repair an external blocker and call its returned stage unblock command. stage fanout is read-only orchestration state plus the stage's deterministic Work materialization action for an external runner; it never makes product decisions. stage finish is reserved for actual stage completion and generates the final deterministic reports. Unmanaged operator calls retain explicit project and runtime selection.`
197
251
  ],
198
252
  [
199
253
  "work",
@@ -203,8 +257,9 @@ Usage:
203
257
  dd-flow work ls (--run <RUN-ID>|--parent <WORK-ID>) [--ready] --json
204
258
  dd-flow work show <WORK-ID> --json
205
259
  dd-flow work start <WORK-ID> --project-root <root> --json
260
+ dd-flow work launch <WORK-ID> [--stage <name>] --json
206
261
  dd-flow work finish <WORK-ID> (--result-file <path>|--result-stdin) --project-root <root> --json
207
- dd-flow work repair add --run <RUN-ID> (--from-check <CHECK-ID>|--from-unresolved <gap> --verification-file <file>) --origin-work <WORK-ID>... --task-stdin --project-root <root> --json
262
+ dd-flow work repair add --run <RUN-ID> (--from-check <CHECK-ID>|--from-unresolved <gap> --verification-file <file>) --origin-work <WORK-ID>... (--task-file <path>|--task-stdin) --project-root <root> --json
208
263
 
209
264
  CODE workers receive their full accepted packet from work start. work finish validates the result and executes the packet's focused checks before completion. A failed aggregate CODE gate or unresolved semantic verification creates a repair Work from the selected completed origin context and retained evidence. Its planned write areas are coordination hints only; the repair may change any necessary project file inside its RUN workspace.`
210
265
  ],
@@ -537,17 +592,38 @@ handler resolves the registered project from cwd. The option is only a manual
537
592
  override.
538
593
 
539
594
  Managed homes isolate dd-flow hooks while sharing selected Codex state from the source home.`
595
+ ],
596
+ [
597
+ "zcode hook",
598
+ `dd-flow zcode hook - handle a synchronous native ZCode PreToolUse callback
599
+
600
+ Usage:
601
+ dd-flow zcode hook handle --project-root <root> --json
602
+
603
+ The managed ZCode native hook supplies the callback on stdin. It records the
604
+ receipt before returning a rewritten tool input with --hook-event-id.`
540
605
  ],
541
606
  [
542
607
  "zcode event",
543
- `dd-flow zcode event - ingest a zcode-acp session/update notification
608
+ `dd-flow zcode event - ingest a zcode-acp session/update notification as evidence
544
609
 
545
610
  Usage:
546
611
  dd-flow zcode event handle --project-root <root> --json
547
612
  dd-flow zcode usage ingest --project-root <root> --json
548
613
 
549
- Both payloads are read from stdin. The event adapter retains lifecycle identity;
550
- the usage adapter retains cumulative provider counters for RUN accounting.`
614
+ Both payloads are read from stdin. Native hooks own lifecycle admission; ACP
615
+ events and usage retain secondary evidence for RUN accounting.`
616
+ ],
617
+ [
618
+ "droid event",
619
+ `dd-flow droid event - ingest a controlled Factory Droid Execute hook
620
+
621
+ Usage:
622
+ dd-flow droid event handle --project-root <root> --json
623
+ dd-flow droid usage ingest --project-root <root> --json
624
+
625
+ Reads trusted adapter JSON from stdin. Usage requires physical_session scope and explicit completeness.
626
+ `
551
627
  ],
552
628
  [
553
629
  "opencode event",
@@ -0,0 +1,78 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { performance } from "node:perf_hooks";
3
+ import { parseCommandArgs, parseLifecycleCommand, lifecycleCommandFromPayload, continuationCommand } from "../services/lifecycle-command.js";
4
+ import { errorRecord, AppError } from "../shared/errors.js";
5
+ import { parseJsonObject } from "../shared/json.js";
6
+ const workerFile = fileURLToPath(import.meta.url);
7
+ const ingressBudgetMs = 20_000; // Native host timeout is 30s; this includes cold module/DB open.
8
+ const { hookBudget, readNativeHookInput } = await import("../harness-runtime/lib/native-hook-command.mjs");
9
+ /** Lightweight entry: unrelated hooks never load the router or open storage. */
10
+ export async function runHookIngress(args) {
11
+ try {
12
+ const parsed = parseCommandArgs(args.slice(3));
13
+ for (const [key, values] of parsed.options)
14
+ if (!["event", "project-root", "json"].includes(key) || values.length !== 1)
15
+ throw new AppError("usage", `Invalid hook option: ${key}`, 2);
16
+ const event = parsed.options.get("event")?.[0];
17
+ if (!event?.trim() || event === "__dd_flow_flag__" || parsed.positional.length)
18
+ throw new AppError("usage", "Hook ingress requires --event with a value", 2);
19
+ const root = parsed.options.get("project-root")?.[0];
20
+ if (root !== undefined && (!root.trim() || root === "__dd_flow_flag__"))
21
+ throw new AppError("usage", "--project-root requires a value", 2);
22
+ if (parsed.options.has("json") && parsed.options.get("json")?.[0] !== "__dd_flow_flag__")
23
+ throw new AppError("usage", "--json does not take a value", 2);
24
+ const budget = hookBudget(ingressBudgetMs, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null);
25
+ const stdin = await readNativeHookInput(process.stdin, budget);
26
+ const payload = parseJsonObject(stdin || "{}", "native hook stdin");
27
+ const command = lifecycleCommandFromPayload(payload);
28
+ const analysis = command ? parseLifecycleCommand(command) : { kind: "none", unresolved: false };
29
+ if (event !== "PreToolUse" || !command || (analysis.kind === "none" && (!("participation" in analysis) || !["lifecycle", "auxiliary"].includes(analysis.participation)) && !continuationCommand(command))) {
30
+ process.stdout.write("{}\n");
31
+ return 0;
32
+ }
33
+ const started = performance.now();
34
+ const { invokeNativeHook } = await import("../harness-runtime/lib/native-hook-command.mjs");
35
+ const result = await invokeNativeHook({ bin: workerFile, args, payload, home: process.env.DD_FLOW_HOME, timeoutMs: budget.remaining("admission") });
36
+ const { timing, ...output } = result;
37
+ process.stderr.write(`${JSON.stringify({ kind: "hook_ingress", total_ms: Math.round(performance.now() - started), ...timing })}\n`);
38
+ process.stdout.write(`${JSON.stringify(output)}\n`);
39
+ return 0;
40
+ }
41
+ catch (error) {
42
+ const record = errorRecord(error);
43
+ process.stderr.write(`${JSON.stringify({ ok: false, error: record })}\n`);
44
+ return record.exitCode;
45
+ }
46
+ }
47
+ // A bounded CLI subprocess, not a detached writer: its successful reply follows COMMIT.
48
+ if (process.argv[1] === workerFile) {
49
+ try {
50
+ const stdin = await readNativeHookInput(process.stdin, hookBudget(ingressBudgetMs, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null));
51
+ const payload = parseJsonObject(stdin, "native hook stdin");
52
+ const started = performance.now();
53
+ const { verifyCodexHookDelivery } = await import("../services/codex-hook-delivery.js");
54
+ const proof = await verifyCodexHookDelivery(process.env, payload);
55
+ const verified = performance.now();
56
+ const [{ createContext }, { handleCodexHook }] = await Promise.all([import("../runtime/context.js"), import("../services/hooks.js")]);
57
+ const loaded = performance.now();
58
+ const context = createContext(process.env, "hook");
59
+ const opened = performance.now();
60
+ try {
61
+ if (proof)
62
+ context.codexHookProof = proof;
63
+ const parsed = parseCommandArgs(process.argv.slice(5));
64
+ const projectRoot = parsed.options.get("project-root")?.[0];
65
+ const result = handleCodexHook(context, { event: "PreToolUse", ...(projectRoot ? { projectRoot } : {}), stdin });
66
+ process.stdout.write(`${JSON.stringify({ ...(result.hookSpecificOutput ? { hookSpecificOutput: result.hookSpecificOutput } : {}), timing: {
67
+ verify_ms: Math.round(verified - started), load_ms: Math.round(loaded - verified), open_ms: Math.round(opened - loaded), receipt_ms: Math.round(performance.now() - opened)
68
+ } })}\n`);
69
+ }
70
+ finally {
71
+ context.db.close?.();
72
+ }
73
+ }
74
+ catch (error) {
75
+ process.stderr.write(`${JSON.stringify({ ok: false, error: errorRecord(error) })}\n`);
76
+ process.exitCode = 1;
77
+ }
78
+ }
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import crypto from "node:crypto";
4
+ import { AppError, isAppError } from "../shared/errors.js";
5
+ import { validateSchema } from "../services/schema-validation.js";
6
+ export function prepareError(error, input = {}) {
7
+ if (isAppError(error)) {
8
+ // A malformed user input is safely correctable. A broken schema registry,
9
+ // binding, or storage is not; do not disguise infrastructure damage as a
10
+ // retryable command typo.
11
+ const inputErrorCodes = new Set([
12
+ "usage", "validation", "schema_validation", "invalid_json", "not_found",
13
+ "path_escape", "input_file_missing", "input_file_not_regular", "input_file_unreadable",
14
+ "response_file_parent_invalid", "response_file_invalid", "context_checksum_mismatch", "invalid_run_config_state", "invalid_work_state", "invalid_stage_state", "runtime_budget_invalid", "runtime_scope_manifest_invalid", "runtime_scope_resume_invalid",
15
+ "fork_path_conflict", "fork_output_not_empty", "fork_checkpoint_invalid", "fork_engine_unavailable", "snapshot_missing",
16
+ "repair_check_declaration_missing", "review_repair_check_required", "review_check_reference_unknown", "semantic_status_inconsistent", "migration_report_invalid", "controller_context_missing", "snapshot_output_inside_runtime"
17
+ ]);
18
+ if (!inputErrorCodes.has(error.code) && error.details.phase !== "prepare")
19
+ throw error;
20
+ throw new AppError(error.code, error.message, 2, {
21
+ ...error.details,
22
+ phase: "prepare",
23
+ effect: error.details.effect ?? "no_effect",
24
+ recoverable: error.details.recoverable ?? true,
25
+ ...input
26
+ });
27
+ }
28
+ const code = error?.code;
29
+ if (typeof input.file !== "string" || !["ENOENT", "EACCES", "EPERM", "EISDIR", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(code ?? ""))
30
+ throw error;
31
+ throw new AppError("input_file_unreadable", `Cannot read input file${typeof input.file === "string" ? `: ${input.file}` : ""}`, 2, {
32
+ phase: "prepare", effect: "no_effect", recoverable: true, ...input, cause: code ?? String(error)
33
+ });
34
+ }
35
+ export function prepareTextFile(file, label = "input file") {
36
+ const prepared = prepareFileBytes(file, label);
37
+ return { file: prepared.file, text: prepared.bytes.toString("utf8") };
38
+ }
39
+ export function prepareFileBytes(file, label = "input file") {
40
+ const absolute = path.resolve(file);
41
+ let descriptor;
42
+ try {
43
+ descriptor = fs.openSync(absolute, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK);
44
+ const stat = fs.fstatSync(descriptor);
45
+ if (!stat.isFile()) {
46
+ throw new AppError("input_file_not_regular", `${label} must be a readable regular file: ${absolute}`, 2, { file: absolute, label });
47
+ }
48
+ return { file: absolute, bytes: fs.readFileSync(descriptor) };
49
+ }
50
+ catch (error) {
51
+ if (error?.code === "ENOENT") {
52
+ throw new AppError("input_file_missing", `${label} does not exist: ${absolute}`, 2, {
53
+ phase: "prepare", effect: "no_effect", recoverable: true, file: absolute, label
54
+ });
55
+ }
56
+ return prepareError(error, { file: absolute, label });
57
+ }
58
+ finally {
59
+ if (descriptor !== undefined)
60
+ fs.closeSync(descriptor);
61
+ }
62
+ }
63
+ export function prepareJsonText(text, input) {
64
+ try {
65
+ return { file: input.file, text, value: JSON.parse(text) };
66
+ }
67
+ catch {
68
+ // Keep the established public code so agents can recognise this as a
69
+ // safe correction-and-retry condition.
70
+ throw new AppError("invalid_json", `Invalid JSON in ${input.label ?? "input"}: ${input.file}`, 2, {
71
+ phase: "prepare", effect: "no_effect", recoverable: true, file: input.file, label: input.label ?? "input",
72
+ cause: "Invalid JSON syntax", input_sha256: crypto.createHash("sha256").update(text).digest("hex")
73
+ });
74
+ }
75
+ }
76
+ export function prepareJsonFile(file, label = "input JSON") {
77
+ const prepared = prepareTextFile(file, label);
78
+ return prepareJsonText(prepared.text, { file: prepared.file, label });
79
+ }
80
+ export function prepareJsonSchema(file, label, schema) {
81
+ const prepared = prepareJsonFile(file, label);
82
+ try {
83
+ validateSchema({ ...schema, file: prepared.file, data: prepared.value });
84
+ }
85
+ catch (error) {
86
+ return prepareError(error, { file: prepared.file, label });
87
+ }
88
+ return prepared;
89
+ }
90
+ /** Read-only destination check; execution still handles filesystem races. */
91
+ export function prepareOutputFile(value) { return prepareOutput(value, false); }
92
+ export function prepareOutputDirectory(value) { return prepareOutput(value, true); }
93
+ function prepareOutput(value, directory) {
94
+ const file = path.resolve(value);
95
+ try {
96
+ let ancestor = directory ? file : path.dirname(file);
97
+ while (!fs.existsSync(ancestor) && path.dirname(ancestor) !== ancestor)
98
+ ancestor = path.dirname(ancestor);
99
+ if (!fs.statSync(ancestor).isDirectory())
100
+ throw new AppError("validation", "Output parent must be a directory", 2);
101
+ fs.accessSync(ancestor, fs.constants.W_OK | fs.constants.X_OK);
102
+ if (fs.existsSync(file)) {
103
+ const stat = fs.statSync(file);
104
+ if (directory ? !stat.isDirectory() : !stat.isFile())
105
+ throw new AppError("validation", directory ? "Output must name a directory" : "Output must name a regular file", 2);
106
+ fs.accessSync(file, fs.constants.W_OK | (directory ? fs.constants.X_OK : 0));
107
+ }
108
+ return file;
109
+ }
110
+ catch (error) {
111
+ const code = error?.code;
112
+ if (["ENOENT", "EACCES", "EPERM", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(code ?? ""))
113
+ return prepareError(new AppError("validation", "Output destination is not writable or has an invalid parent", 2, { cause: code }), { file, parameter: "output" });
114
+ return prepareError(error, { file, parameter: "output" });
115
+ }
116
+ }
@@ -0,0 +1,80 @@
1
+ import { AppError, errorRecord } from "../shared/errors.js";
2
+ import { parseCommandArgs } from "../services/lifecycle-command.js";
3
+ import { channel } from "node:diagnostics_channel";
4
+ /** These ingress commands consume a prepared store; they never prepare it. */
5
+ export function isNativeHookIngress(args) {
6
+ return args[2] === "handle" && ((args[1] === "event" && ["agy", "grok", "droid", "opencode", "zcode"].includes(args[0] ?? ""))
7
+ || args[0] === "zcode" && args[1] === "hook");
8
+ }
9
+ export async function dispatchNativeHook(args, stdin, env) {
10
+ const started = performance.now();
11
+ const deadline = env.DD_FLOW_HOOK_DEADLINE_AT === undefined ? undefined : Number(env.DD_FLOW_HOOK_DEADLINE_AT);
12
+ if (deadline !== undefined && !Number.isSafeInteger(deadline))
13
+ throw new AppError("hook_deadline_invalid", "Native hook deadline must be an absolute millisecond timestamp", 1, { effect: "unknown" });
14
+ const phase = (name) => {
15
+ if (deadline !== undefined && deadline <= Date.now())
16
+ throw new AppError("native_hook_timeout", "Native hook deadline expired", 1, { phase: name, effect: "unknown" });
17
+ process.stderr.write(`${JSON.stringify({ kind: "native_hook_phase", phase: name, elapsed_ms: Math.round(performance.now() - started), ...(deadline !== undefined ? { remaining_ms: Math.max(0, deadline - Date.now()) } : {}) })}\n`);
18
+ };
19
+ phase("import");
20
+ const parsed = parseCommandArgs(args.slice(3));
21
+ const projectRoot = parsed.options.get("project-root")?.[0];
22
+ if (!isNativeHookIngress(args) || parsed.positional.length || !projectRoot || projectRoot === "__dd_flow_flag__")
23
+ throw new AppError("usage", "Native hook requires --project-root", 2);
24
+ for (const [name, values] of parsed.options)
25
+ if (!["project-root", "json"].includes(name) || values.length !== 1 || name === "json" && values[0] !== "__dd_flow_flag__")
26
+ throw new AppError("usage", `Invalid native hook option: ${name}`, 2);
27
+ const [hooks, { createContext }, { resolveDdFlowHome }] = await Promise.all([
28
+ import("../services/hooks.js"), import("../runtime/context.js"), import("../storage/paths.js")
29
+ ]);
30
+ phase("imported");
31
+ let opened;
32
+ // Reuse the actual envelope validators and shell parser. They return for
33
+ // unrelated tools before their first DB access, without duplicating policy.
34
+ const context = {
35
+ env, ddFlowHome: resolveDdFlowHome(env), now: () => new Date().toISOString(),
36
+ get db() {
37
+ if (!opened) {
38
+ phase("store_open");
39
+ opened = createContext(env, "hook");
40
+ phase("store_opened");
41
+ }
42
+ return opened.db;
43
+ }
44
+ };
45
+ const handler = args[0] === "agy" ? hooks.handleAgyEvent : args[0] === "grok" ? hooks.handleGrokEvent
46
+ : args[0] === "droid" ? hooks.handleDroidEvent : args[0] === "opencode" ? hooks.handleOpenCodeEvent
47
+ : args[1] === "hook" ? hooks.handleZcodeHook : hooks.handleZcodeEvent;
48
+ const timing = channel("dd-flow.sqlite.ingress");
49
+ // This command owns one synchronous store; there is no concurrent handler here.
50
+ let previousPhase;
51
+ const log = (value) => {
52
+ const record = value;
53
+ if (record.phase === previousPhase)
54
+ return;
55
+ previousPhase = record.phase;
56
+ process.stderr.write(`${JSON.stringify({ kind: "native_hook_phase", phase: record.phase, elapsed_ms: Math.round(performance.now() - started), ...(record.remaining_ms !== undefined ? { remaining_ms: record.remaining_ms } : {}) })}\n`);
57
+ };
58
+ timing.subscribe(log);
59
+ try {
60
+ phase("receipt");
61
+ return await handler(context, { projectRoot, stdin });
62
+ }
63
+ finally {
64
+ timing.unsubscribe(log);
65
+ opened?.db.close?.();
66
+ }
67
+ }
68
+ export async function runNativeHookIngress(args) {
69
+ try {
70
+ const { readNativeHookInput, hookBudget } = await import("../harness-runtime/lib/native-hook-command.mjs");
71
+ const stdin = await readNativeHookInput(process.stdin, hookBudget(25_000, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null));
72
+ process.stdout.write(`${JSON.stringify(await dispatchNativeHook(args, stdin, process.env))}\n`);
73
+ return 0;
74
+ }
75
+ catch (error) {
76
+ const record = errorRecord(error);
77
+ process.stderr.write(`${JSON.stringify({ ok: false, error: record })}\n`);
78
+ return record.exitCode;
79
+ }
80
+ }