@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.140

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 (493) hide show
  1. package/CHANGELOG.md +733 -0
  2. package/README.md +143 -1
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +376 -0
  6. package/dist/cli/command-metadata.js +197 -0
  7. package/dist/cli/command-option-descriptions.js +191 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/error-output.js +52 -0
  11. package/dist/cli/executor-onboarding.js +76 -0
  12. package/dist/cli/flows.js +29 -0
  13. package/dist/cli/harnesses.js +84 -0
  14. package/dist/cli/help.js +328 -18
  15. package/dist/cli/hook-ingress.js +78 -0
  16. package/dist/cli/input-preparation.js +165 -0
  17. package/dist/cli/memory-flow-admission.js +44 -0
  18. package/dist/cli/memory-init-invocation.js +78 -0
  19. package/dist/cli/memory-init.js +240 -0
  20. package/dist/cli/memory-storage-admission.js +53 -0
  21. package/dist/cli/models.js +39 -0
  22. package/dist/cli/native-hook-ingress.js +80 -0
  23. package/dist/cli/operator-profiles.js +61 -0
  24. package/dist/cli/output-options.js +35 -0
  25. package/dist/cli/response-file.js +71 -0
  26. package/dist/cli/run-cli.js +1689 -545
  27. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  28. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  29. package/dist/cli/runtime-process.js +153 -0
  30. package/dist/cli/setup.js +149 -0
  31. package/dist/cli.js +71 -2
  32. package/dist/domain/stage-catalog.js +1 -1
  33. package/dist/domain/stage-handler-identities.js +20 -0
  34. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  35. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  36. package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
  37. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  38. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  39. package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
  40. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  41. package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
  42. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  43. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  44. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  45. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  46. package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
  47. package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
  48. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  49. package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
  50. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
  51. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +724 -0
  52. package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
  53. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  54. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
  55. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  56. package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
  57. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +129 -0
  58. package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
  59. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
  60. package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
  61. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +507 -0
  62. package/dist/harness-runtime/lib/dd-opencode.mjs +354 -0
  63. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +750 -0
  64. package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
  65. package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
  66. package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
  67. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  68. package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
  69. package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
  70. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  71. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  72. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  73. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  74. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
  75. package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
  76. package/dist/harness-runtime/lib/managed-daemon.d.mts +13 -0
  77. package/dist/harness-runtime/lib/managed-daemon.mjs +654 -0
  78. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  79. package/dist/harness-runtime/lib/model-observations.mjs +101 -0
  80. package/dist/harness-runtime/lib/native-children.d.mts +16 -0
  81. package/dist/harness-runtime/lib/native-children.mjs +113 -0
  82. package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
  83. package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
  84. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  85. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  86. package/dist/harness-runtime/lib/native-streams.mjs +45 -0
  87. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  88. package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
  89. package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
  90. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  91. package/dist/harness-runtime/lib/operation-errors.d.mts +14 -0
  92. package/dist/harness-runtime/lib/operation-errors.mjs +116 -0
  93. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  94. package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
  95. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  96. package/dist/harness-runtime/lib/process-json.mjs +201 -0
  97. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  98. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
  99. package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
  100. package/dist/harness-runtime/lib/runner-events.mjs +200 -0
  101. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  102. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  103. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  104. package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
  105. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  106. package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
  107. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  108. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  109. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  110. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  111. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  112. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  113. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  114. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  115. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  116. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  117. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  118. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  119. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  120. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  121. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  122. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  123. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  124. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  125. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  126. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  127. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  128. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  129. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  130. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  131. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  132. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  133. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  134. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  135. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  136. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  137. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  138. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  139. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  140. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  141. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  142. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  143. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  144. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  145. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  146. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  147. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  148. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  149. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  150. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  151. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  152. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  153. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  154. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  155. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  156. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  157. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  158. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  159. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  160. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  161. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  162. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  163. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  164. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  165. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  166. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  167. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  168. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  169. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  170. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  171. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  172. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  173. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  174. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  175. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  176. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  177. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  178. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  179. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  180. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  181. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  182. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  183. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  184. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  185. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  186. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  187. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  188. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  189. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  190. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  191. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  192. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  193. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  194. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  195. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  196. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  197. package/dist/protocol/local-files.js +18 -9
  198. package/dist/runtime/context.js +8 -4
  199. package/dist/schemas/agent-profile.schema.json +1 -1
  200. package/dist/schemas/code-review-result.schema.json +1 -1
  201. package/dist/schemas/code-verification.schema.json +7 -5
  202. package/dist/schemas/code-work-batch.schema.json +2 -2
  203. package/dist/schemas/discovery-result.schema.json +184 -0
  204. package/dist/schemas/execution-bundle.schema.json +205 -0
  205. package/dist/schemas/execution-settings.schema.json +162 -0
  206. package/dist/schemas/flow-descriptor.schema.json +54 -0
  207. package/dist/schemas/flow-run.schema.json +28 -0
  208. package/dist/schemas/harness-catalog.schema.json +80 -0
  209. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  210. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  211. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  212. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  213. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  214. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  215. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  216. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  217. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  218. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  219. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  220. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  221. package/dist/schemas/model-catalog.schema.json +153 -0
  222. package/dist/schemas/plan-review-decision.schema.json +3 -3
  223. package/dist/schemas/plan-review-result.schema.json +2 -2
  224. package/dist/schemas/profile-settings.schema.json +122 -0
  225. package/dist/schemas/release-manifest.schema.json +291 -0
  226. package/dist/schemas/run-control-receipt.schema.json +118 -0
  227. package/dist/schemas/run-control-request.schema.json +36 -0
  228. package/dist/schemas/run-engine-binding.schema.json +1 -1
  229. package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
  230. package/dist/services/branch-context.js +3 -3
  231. package/dist/services/canon.js +11 -8
  232. package/dist/services/check-runner-evidence.js +168 -0
  233. package/dist/services/cleanup.js +133 -40
  234. package/dist/services/cli-operation-classifier.js +25 -62
  235. package/dist/services/cli-upgrade-allowlist.js +27 -0
  236. package/dist/services/code-checks.js +1156 -107
  237. package/dist/services/codex-hook-delivery.js +32 -0
  238. package/dist/services/command-context.js +80 -0
  239. package/dist/services/compatibility-preflight.js +13 -3
  240. package/dist/services/config.js +11 -8
  241. package/dist/services/continuation-outcome.js +20 -0
  242. package/dist/services/controller-child-capacity.js +13 -0
  243. package/dist/services/controller-child-observer.js +115 -0
  244. package/dist/services/controller-execution.js +88 -0
  245. package/dist/services/controller-fanout.js +202 -0
  246. package/dist/services/dashboard.js +143 -58
  247. package/dist/services/descriptor-coordinator-session.js +35 -0
  248. package/dist/services/descriptor-root-work.js +53 -0
  249. package/dist/services/descriptor-run-settlement.js +105 -0
  250. package/dist/services/descriptor-run-start.js +449 -0
  251. package/dist/services/descriptor-stage-coordinator.js +197 -0
  252. package/dist/services/descriptor-stage-settlement.js +222 -0
  253. package/dist/services/descriptor-stage-start.js +139 -0
  254. package/dist/services/discovery/agy-discovery.js +2 -0
  255. package/dist/services/discovery/codex-discovery.js +2 -0
  256. package/dist/services/discovery/droid-discovery.js +2 -0
  257. package/dist/services/discovery/grok-discovery.js +2 -0
  258. package/dist/services/discovery/opencode-discovery.js +2 -0
  259. package/dist/services/discovery/static.js +31 -0
  260. package/dist/services/discovery/zcode-discovery.js +2 -0
  261. package/dist/services/engines.js +294 -153
  262. package/dist/services/eval-snapshots.js +1215 -116
  263. package/dist/services/execution-bundles.js +263 -0
  264. package/dist/services/execution-policy.js +185 -0
  265. package/dist/services/executor-onboarding.js +56 -0
  266. package/dist/services/external-work-launch.js +308 -0
  267. package/dist/services/flow-catalog.js +21 -0
  268. package/dist/services/flow-descriptors.js +82 -0
  269. package/dist/services/frozen-engine.js +250 -0
  270. package/dist/services/generic-execution.js +164 -0
  271. package/dist/services/harness-adapter.js +355 -36
  272. package/dist/services/harness-config.js +2 -0
  273. package/dist/services/harness-discovery.js +29 -0
  274. package/dist/services/harness-doctor.js +75 -0
  275. package/dist/services/harness-probe-effects.js +51 -0
  276. package/dist/services/harness-version-probe.js +100 -0
  277. package/dist/services/hooks.js +771 -325
  278. package/dist/services/hybrid-contracts.js +483 -0
  279. package/dist/services/ids.js +29 -1
  280. package/dist/services/installation-lock.js +95 -0
  281. package/dist/services/installed-flow-source.js +183 -0
  282. package/dist/services/lanes.js +78 -65
  283. package/dist/services/lifecycle-command.js +1 -288
  284. package/dist/services/lifecycle-contract.js +1 -0
  285. package/dist/services/lifecycle-invocations.js +2007 -0
  286. package/dist/services/managed-daemon-binding.js +38 -0
  287. package/dist/services/managed-lease-monitor.js +133 -0
  288. package/dist/services/managed-processes.js +475 -55
  289. package/dist/services/memory-accepted-composition.js +77 -0
  290. package/dist/services/memory-baseline-preservation.js +65 -0
  291. package/dist/services/memory-completed-correction-wave.js +320 -0
  292. package/dist/services/memory-compose-handler.js +258 -0
  293. package/dist/services/memory-composition-acceptance.js +199 -0
  294. package/dist/services/memory-composition-graph.js +134 -0
  295. package/dist/services/memory-composition-registration.js +146 -0
  296. package/dist/services/memory-composition-work-custody.js +151 -0
  297. package/dist/services/memory-composition-work-results.js +96 -0
  298. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  299. package/dist/services/memory-corrected-review-batch.js +30 -0
  300. package/dist/services/memory-corrected-review-findings.js +106 -0
  301. package/dist/services/memory-corrected-review-inputs.js +210 -0
  302. package/dist/services/memory-corrected-review-projection.js +78 -0
  303. package/dist/services/memory-corrected-review-receipt.js +170 -0
  304. package/dist/services/memory-corrected-review-registration.js +191 -0
  305. package/dist/services/memory-correction-check-receipt.js +219 -0
  306. package/dist/services/memory-correction-input-owner.js +6 -0
  307. package/dist/services/memory-discover-handler.js +104 -0
  308. package/dist/services/memory-finish-evidence.js +435 -0
  309. package/dist/services/memory-finish-handler.js +215 -0
  310. package/dist/services/memory-finish-integration.js +280 -0
  311. package/dist/services/memory-finish-readiness.js +122 -0
  312. package/dist/services/memory-git-integration.js +151 -0
  313. package/dist/services/memory-historical-corrected-review.js +206 -0
  314. package/dist/services/memory-historical-original-review.js +130 -0
  315. package/dist/services/memory-historical-repair-registration.js +99 -0
  316. package/dist/services/memory-historical-second-review.js +128 -0
  317. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  318. package/dist/services/memory-init-contexts.js +89 -0
  319. package/dist/services/memory-init-controller-start.js +84 -0
  320. package/dist/services/memory-init-engine-release.js +223 -0
  321. package/dist/services/memory-init-git-policy.js +136 -0
  322. package/dist/services/memory-init-lint-capability.js +185 -0
  323. package/dist/services/memory-init-native-capacity.js +296 -0
  324. package/dist/services/memory-init-preflight.js +330 -0
  325. package/dist/services/memory-init-project-bootstrap.js +599 -0
  326. package/dist/services/memory-init-request-candidate.js +157 -0
  327. package/dist/services/memory-init-request-input.js +66 -0
  328. package/dist/services/memory-init-resource-storage.js +125 -0
  329. package/dist/services/memory-init-run-allocation.js +81 -0
  330. package/dist/services/memory-init-run-bundle.js +98 -0
  331. package/dist/services/memory-init-start.js +181 -0
  332. package/dist/services/memory-init-startup-custody.js +80 -0
  333. package/dist/services/memory-init-startup-input.js +240 -0
  334. package/dist/services/memory-init-storage.js +81 -0
  335. package/dist/services/memory-init-stores.js +359 -0
  336. package/dist/services/memory-init-worktrunk-capability.js +178 -0
  337. package/dist/services/memory-layer-publication.js +611 -0
  338. package/dist/services/memory-layer.js +156 -0
  339. package/dist/services/memory-lint-config.js +85 -0
  340. package/dist/services/memory-native-review-copy.js +165 -0
  341. package/dist/services/memory-normal-flow-entry.js +75 -0
  342. package/dist/services/memory-ordinary-defaults.js +112 -0
  343. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  344. package/dist/services/memory-preflight-handler.js +259 -0
  345. package/dist/services/memory-repair-current-view.js +299 -0
  346. package/dist/services/memory-repair-document-delta.js +451 -0
  347. package/dist/services/memory-repair-history.js +491 -0
  348. package/dist/services/memory-repair-physical-binding.js +312 -0
  349. package/dist/services/memory-repair-publication-files.js +152 -0
  350. package/dist/services/memory-repair-publication-intent.js +27 -0
  351. package/dist/services/memory-repair-publication-plan.js +160 -0
  352. package/dist/services/memory-repair-publication-source.js +225 -0
  353. package/dist/services/memory-repair-publication.js +794 -0
  354. package/dist/services/memory-repair-registration.js +239 -0
  355. package/dist/services/memory-repair-work-baseline.js +413 -0
  356. package/dist/services/memory-repair-work-completion.js +97 -0
  357. package/dist/services/memory-repair-work-custody.js +73 -0
  358. package/dist/services/memory-repair-work-results.js +89 -0
  359. package/dist/services/memory-repair-workspace.js +252 -0
  360. package/dist/services/memory-repair-writer-copy.js +735 -0
  361. package/dist/services/memory-result-binding.js +173 -0
  362. package/dist/services/memory-retained-review-authority.js +129 -0
  363. package/dist/services/memory-retained-review-results.js +70 -0
  364. package/dist/services/memory-retained-second-baseline.js +102 -0
  365. package/dist/services/memory-retained-second-repairs.js +156 -0
  366. package/dist/services/memory-review-acceptance.js +214 -0
  367. package/dist/services/memory-review-correction-plan.js +171 -0
  368. package/dist/services/memory-review-handler.js +585 -0
  369. package/dist/services/memory-review-inputs.js +200 -0
  370. package/dist/services/memory-review-receipt.js +180 -0
  371. package/dist/services/memory-review-registration.js +108 -0
  372. package/dist/services/memory-review-work-custody.js +124 -0
  373. package/dist/services/memory-review-work-input.js +72 -0
  374. package/dist/services/memory-review-work-results.js +73 -0
  375. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  376. package/dist/services/memory-second-correction-plan.js +76 -0
  377. package/dist/services/memory-second-repair-baseline.js +91 -0
  378. package/dist/services/memory-second-repair-launch.js +614 -0
  379. package/dist/services/memory-second-repair-registration.js +88 -0
  380. package/dist/services/memory-second-repair-source.js +187 -0
  381. package/dist/services/memory-second-review-acceptance.js +115 -0
  382. package/dist/services/memory-source-observations.js +117 -0
  383. package/dist/services/memory-stage-dispatch.js +28 -0
  384. package/dist/services/memory-stage-results.js +199 -0
  385. package/dist/services/memory-verification-acceptance.js +135 -0
  386. package/dist/services/memory-verification-checks.js +252 -0
  387. package/dist/services/memory-verification-receipt.js +159 -0
  388. package/dist/services/memory-verify-handler.js +135 -0
  389. package/dist/services/memory-work-result-preparation.js +63 -0
  390. package/dist/services/merge-queue.js +228 -149
  391. package/dist/services/merge-server.js +161 -35
  392. package/dist/services/migrations.js +37 -16
  393. package/dist/services/model-catalog.js +109 -0
  394. package/dist/services/model-discovery.js +46 -0
  395. package/dist/services/native-daemon-history.js +49 -0
  396. package/dist/services/native-session-control.js +39 -0
  397. package/dist/services/operator-profiles.js +236 -0
  398. package/dist/services/plan-runtime.js +26 -11
  399. package/dist/services/plans.js +28 -25
  400. package/dist/services/projects.js +37 -17
  401. package/dist/services/prompts.js +28 -17
  402. package/dist/services/protocols.js +163 -87
  403. package/dist/services/provider-capacity.js +385 -0
  404. package/dist/services/recovery-observation-budget.js +121 -0
  405. package/dist/services/recovery-snapshot-database.js +560 -0
  406. package/dist/services/release-archive-producer.js +52 -0
  407. package/dist/services/release-archive.js +143 -0
  408. package/dist/services/release-cache.js +142 -0
  409. package/dist/services/release-compatibility.js +18 -0
  410. package/dist/services/release-download.js +67 -0
  411. package/dist/services/release-envelope.js +104 -0
  412. package/dist/services/release-manifest.js +45 -0
  413. package/dist/services/release-payload.js +114 -0
  414. package/dist/services/release-producer.js +63 -0
  415. package/dist/services/release-provenance.js +160 -0
  416. package/dist/services/release-registry.js +66 -0
  417. package/dist/services/release-source.js +194 -0
  418. package/dist/services/repair-intents.js +42 -0
  419. package/dist/services/review-copy.js +165 -0
  420. package/dist/services/review-deferral.js +23 -0
  421. package/dist/services/run-control-receipt.js +56 -0
  422. package/dist/services/run-control-worker.js +411 -0
  423. package/dist/services/run-control.js +1056 -0
  424. package/dist/services/run-controller-adapter.js +399 -0
  425. package/dist/services/run-controller-capture.js +186 -0
  426. package/dist/services/run-controller-process.js +208 -0
  427. package/dist/services/run-controller-recovery.js +289 -0
  428. package/dist/services/run-controller-state.js +97 -0
  429. package/dist/services/run-controller.js +1205 -0
  430. package/dist/services/run-engine-bindings.js +1 -1
  431. package/dist/services/run-fork.js +147 -0
  432. package/dist/services/run-health.js +149 -0
  433. package/dist/services/run-observations.js +152 -0
  434. package/dist/services/run-projection.js +22 -64
  435. package/dist/services/run-recovery-guard.js +77 -0
  436. package/dist/services/run-recovery-runtime.js +112 -0
  437. package/dist/services/run-recovery.js +489 -0
  438. package/dist/services/run-start-custody.js +177 -0
  439. package/dist/services/runs.js +1019 -247
  440. package/dist/services/runtime-budget.js +430 -0
  441. package/dist/services/runtime-command.js +43 -0
  442. package/dist/services/runtime-scope-capture.js +52 -0
  443. package/dist/services/runtime-scope-control.js +468 -0
  444. package/dist/services/runtime-scope-resume.js +599 -0
  445. package/dist/services/runtime-scope-stop.js +210 -0
  446. package/dist/services/runtime-scope-worker.js +287 -0
  447. package/dist/services/runtime-service.js +96 -0
  448. package/dist/services/schema-validation.js +10 -8
  449. package/dist/services/scope-writer-cleanup.js +27 -0
  450. package/dist/services/sessions.js +35 -69
  451. package/dist/services/setup.js +377 -0
  452. package/dist/services/stage-blocker.js +17 -7
  453. package/dist/services/stage-context.js +193 -19
  454. package/dist/services/stage-handlers.js +74 -0
  455. package/dist/services/stage-lifecycle.js +100 -108
  456. package/dist/services/stage-pause.js +89 -75
  457. package/dist/services/stage-report-renderer.js +234 -6
  458. package/dist/services/stage-work-graph.js +52 -0
  459. package/dist/services/tooling.js +32 -23
  460. package/dist/services/usage.js +49 -101
  461. package/dist/services/verification-matrix.js +374 -0
  462. package/dist/services/vnext-code-review.js +223 -88
  463. package/dist/services/vnext-code.js +327 -226
  464. package/dist/services/vnext-contracts.js +10 -1
  465. package/dist/services/vnext-execution-profile.js +80 -5
  466. package/dist/services/vnext-fanout.js +311 -19
  467. package/dist/services/vnext-merge.js +332 -111
  468. package/dist/services/vnext-plan-review.js +279 -127
  469. package/dist/services/vnext-plan.js +229 -104
  470. package/dist/services/vnext-protocolize.js +411 -86
  471. package/dist/services/vnext-specify.js +119 -81
  472. package/dist/services/vnext-workspace-policy.js +7 -2
  473. package/dist/services/work-registry.js +1627 -199
  474. package/dist/services/workspace-bootstrap.js +52 -15
  475. package/dist/services/workspace-files.js +85 -0
  476. package/dist/services/worktrees.js +98 -44
  477. package/dist/shared/entity-references.js +8 -0
  478. package/dist/shared/errors.js +17 -0
  479. package/dist/storage/agent-output.js +26 -0
  480. package/dist/storage/database.js +891 -39
  481. package/dist/storage/foreign-key-transaction.js +39 -0
  482. package/dist/storage/immutable-input.js +45 -0
  483. package/dist/storage/import-guard.js +102 -0
  484. package/dist/storage/native-cleanup.js +52 -0
  485. package/dist/storage/paths.js +17 -4
  486. package/dist/storage/projection-lock.js +73 -0
  487. package/dist/storage/work-references.js +23 -0
  488. package/dist/storage/writer-contract.js +89 -0
  489. package/dist/storage/writer-migration.js +85 -0
  490. package/package.json +43 -13
  491. package/tools/create-canon-release.mjs +33 -0
  492. package/tools/produce-hybrid-release.mjs +41 -0
  493. package/tools/repair-paused-run-status.mjs +84 -0
@@ -0,0 +1,202 @@
1
+ import { AppError, errorRecord } from "../shared/errors.js";
2
+ import { normalizeNativeChildren, safeNativeFailure } from "../harness-runtime/lib/native-children.mjs";
3
+ import { getVnextFanoutStatus, observeVnextNativeChildren, reconcileVnextFanout, getStageFanoutCapacity, verifiedNativeWorkFailure } from "./vnext-fanout.js";
4
+ import { dispatchVnextPlanReview } from "./vnext-plan-review.js";
5
+ import { resolveExecutionPolicy } from "./execution-policy.js";
6
+ import { lifecycleCommandCompletionInstruction, renderDelegationInstructions, stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
7
+ import { assertWorkLaunchReady, workStartCommand } from "./work-registry.js";
8
+ import { prepareMemoryNativeReviewCopy } from "./memory-native-review-copy.js";
9
+ import { registerMemoryRepairBaselineFromStage, reserveMemoryRepairWriterCopyFromStage, materializeMemoryRepairWriterCopyFromStage, prepareMemoryRepairDispatchFromStage, registerMemorySecondRepairBaselineFromStage, reserveMemorySecondRepairCopyFromStage, materializeMemorySecondRepairCopyFromStage, prepareMemorySecondRepairDispatchFromStage, registerMemoryRepairCurrentViewFromStage, registerMemorySecondRepairCurrentViewFromStage } from "./memory-review-handler.js";
10
+ import { requireProjectByRoot } from "./projects.js";
11
+ import { stageAttempt, stageWorkIds } from "./stage-work-graph.js";
12
+ import { runtimeCommand } from "./runtime-command.js";
13
+ function semanticCompletionPrompt(stage, completed) {
14
+ const task = stage === "plan-review"
15
+ ? "Classify the reviewer findings, apply every accepted correction in the current PLAN-REVIEW Work, and write the authoritative review decision."
16
+ : stage === "code-review"
17
+ ? "Classify the review findings and write or retain the authoritative review decision."
18
+ : "Integrate the accepted child results and update the Stage's authoritative verification evidence.";
19
+ return [
20
+ `All declared ${stage} child Work has settled (${completed} completed).`,
21
+ task,
22
+ "Then invoke the exact Stage finish command from the original authoritative packet.",
23
+ "If that command returns either outcome = repair_required with a repair Work ID, or error.details.continuation.kind = repair_required, the repair Work is already registered. End this Turn so the controller can dispatch it. Otherwise follow the command's actual result.",
24
+ "Do not create speculative repair Work or start a successor Stage."
25
+ ].join(" ");
26
+ }
27
+ /** Stage entry acknowledges the packet before the controller issues child commands. */
28
+ export function controllerStageEntryPrompt(stage, command) {
29
+ return [
30
+ `Start the assigned ${stage} Stage now. Your first tool call must be this exact standalone command:`,
31
+ command,
32
+ "Read the complete authoritative packet from the command's response_file result, including worker_prompt_markdown. If a read is truncated, continue until the entire packet has been read.",
33
+ "If the returned packet has orchestration.kind = work_fanout, this Turn is only Stage entry: acknowledge the packet and end your Turn immediately. The packet describes the whole Stage, but its graph/dispatch/finish instructions belong to later controller continuations. Do not list, start, launch or finish Work, create children, change files, run checks, or finish the Stage in this entry Turn. The controller will inspect the graph and send the next assignment with exact issued commands (or dispatch external workers itself).",
34
+ "Otherwise perform this Stage's semantic work and stop at its boundary or a declared HITL boundary. Do not start a successor Stage.",
35
+ stageLifecycleInstruction
36
+ ].join("\n\n");
37
+ }
38
+ export function controllerStageResumePrompt(pauseId, command) {
39
+ return [
40
+ `The user supplied the raw answer for pause ${pauseId}. Your first tool call must be this exact standalone command:`,
41
+ command,
42
+ "Do not alter the answer. Follow the returned packet, then stop at this Stage boundary.",
43
+ stageLifecycleInstruction
44
+ ].join("\n\n");
45
+ }
46
+ /** The shared controller owns this former eval-only Work-graph decision. */
47
+ export async function nextControllerFanout(context, input) {
48
+ // Tree settlement is not a Work result. Keep these children visible to the
49
+ // association check, especially when a hook rejected work start before CLI.
50
+ const children = input.children;
51
+ const observations = { harness_id: input.harness, parent_session_id: input.sessionId, children };
52
+ if (children.length) {
53
+ const observed = observeVnextNativeChildren(context, { ...input, observations });
54
+ const issues = observed.issues;
55
+ if (issues.length)
56
+ throw new AppError("fanout_reconciliation_required", "Native children have unresolved lifecycle ownership", 1, { issues });
57
+ }
58
+ let fanout = getVnextFanoutStatus(context, input).orchestration;
59
+ if (!fanout) {
60
+ if (input.dispatchFence)
61
+ throw new AppError("fanout_reconciliation_required", "Observed native failure has no current graph; dispatch stays fenced", 1, { stage: input.stage, run_id: input.runId });
62
+ return { kind: "none" };
63
+ }
64
+ if (fanout.state === "compatibility_required")
65
+ throw new AppError("stage_graph_compatibility_required", "The retained graph cannot be dispatched by this controller; use the RUN's pinned engine", 1, { stage: input.stage, run_id: input.runId, graph: fanout });
66
+ if (children.length) {
67
+ const reconciled = await reconcileVnextFanout(context, { ...input, observations });
68
+ const issues = reconciled.issues.filter(issue => !["child_check_in_progress", "child_capacity_pending"].includes(issue.code));
69
+ if (issues.length)
70
+ throw new AppError("fanout_reconciliation_required", "Native child outcome requires reconciliation, not replay", 1, { issues });
71
+ if (children.some(child => child.status === "running" || child.status === "unknown"))
72
+ return { kind: "wait", reason: "native_children_unsettled" };
73
+ fanout = getVnextFanoutStatus(context, input).orchestration;
74
+ }
75
+ if (input.dispatchFence && !fanout.works.failed)
76
+ throw new AppError("fanout_reconciliation_required", "Observed native failure has no proven failed Work; dispatch stays fenced", 1, { stage: input.stage, run_id: input.runId });
77
+ if (fanout.state === "dispatch_required") {
78
+ dispatchVnextPlanReview(context, input);
79
+ fanout = getVnextFanoutStatus(context, input).orchestration;
80
+ if (fanout.state === "dispatch_required" || fanout.state === "capacity_required")
81
+ return { kind: "wait", reason: "qualified_capacity_required" };
82
+ }
83
+ const prompt = (action, text) => ({ kind: "prompt", action, prompt: action === "dispatch_work" ? text : `${text}\n\n${lifecycleCommandCompletionInstruction}`, fingerprint: JSON.stringify({ stage: input.stage, parent: fanout.parent_work_id, state: fanout.state, graph: fanout.graph_revision }) });
84
+ if (fanout.state === "coordinator_required")
85
+ return prompt("materialize_graph", `Continue the current ${input.stage} Stage using its authoritative packet. Materialize only its required Work graph, declaring real dependencies at registration. If the packet's CLI command registers the graph, use its returned Work graph unchanged: do not add or alter dependencies after registration. Accepted predecessor Stages already fence their completed producers; do not add cross-stage edges merely to restate input provenance. Do not launch children, finish the Stage, or start a successor. Return after registering the graph; the runtime will dispatch its ready Work.`);
86
+ if (fanout.works.failed || fanout.works.cancelled) {
87
+ const projectId = requireProjectByRoot(context, input.projectRoot).id;
88
+ const members = stageWorkIds(context, projectId, input.runId, input.stage);
89
+ const failures = context.db.all("SELECT work_id,status,result FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ? AND status IN ('failed','cancelled')", [projectId, input.runId, fanout.parent_work_id]).filter(work => members.has(work.work_id)).map(work => {
90
+ let receipt = {};
91
+ try {
92
+ const value = work.result ? JSON.parse(work.result) : null;
93
+ if (value && typeof value === "object" && !Array.isArray(value))
94
+ receipt = value;
95
+ }
96
+ catch { /* Missing legacy metadata is unknown. */ }
97
+ const providerError = safeNativeFailure(receipt.provider_error);
98
+ const nativeBinding = verifiedNativeWorkFailure(context, { projectRoot: input.projectRoot, runId: input.runId, stage: input.stage, parentSessionId: input.sessionId, parentWorkId: fanout.parent_work_id, workId: work.work_id, receipt });
99
+ return { work_id: work.work_id, status: work.status, ...(providerError ? { provider_error: providerError } : {}),
100
+ ...(nativeBinding ? { native_binding: nativeBinding } : {}),
101
+ ...Object.fromEntries(["terminal_turn_id", "terminal_observed_at"].filter(key => typeof receipt[key] === "string" && receipt[key].length <= 256).map(key => [key, receipt[key]])) };
102
+ });
103
+ throw new AppError("fanout_child_failed", "A child Work failed or was cancelled; no retry authority has been issued", 1, { stage: input.stage, run_id: input.runId, parent_work_id: fanout.parent_work_id, failed: fanout.works.failed, cancelled: fanout.works.cancelled, failures, effect: "unknown" });
104
+ }
105
+ if (fanout.works.ready.length) {
106
+ const choices = fanout.works.ready.map(work => {
107
+ const payload = context.db.get("SELECT payload_json FROM works WHERE work_id = ?", [work.work_id])?.payload_json;
108
+ const override = payload ? JSON.parse(payload).execution_override : undefined;
109
+ return { work, resolved: resolveExecutionPolicy({ ddFlowHome: context.ddFlowHome, policy: input.policy, profiles: input.profiles, stage: input.stage, ...(override ? { work: override } : {}) }) };
110
+ });
111
+ const prepareRepair = (work) => {
112
+ const row = context.db.get("SELECT payload_json,result_schema FROM works WHERE work_id=?", [work.work_id]);
113
+ const payload = JSON.parse(row?.payload_json ?? "null");
114
+ if (payload?.kind !== "memory-repair" || row?.result_schema !== "dd-flow/memory-repair-work-result@1")
115
+ return null;
116
+ const scope = { projectRoot: input.projectRoot, runId: input.runId, workId: work.work_id };
117
+ const second = payload.memory_repair?.wave === 2;
118
+ if (!second && payload.memory_repair?.wave !== 1)
119
+ throw new AppError("fanout_contract_invalid", "Memory repair wave is invalid", 1);
120
+ const registerBaseline = second ? registerMemorySecondRepairBaselineFromStage : registerMemoryRepairBaselineFromStage;
121
+ try {
122
+ registerBaseline(context, scope);
123
+ }
124
+ catch (error) {
125
+ const missing = error instanceof AppError && (second
126
+ ? error.code === "memory_second_repair_source_invalid" && error.details.reason === "registered_second_current_view_required"
127
+ : error.code === "memory_review_handler_invalid" && error.details.reason === "current_view_registration_required");
128
+ if (!missing)
129
+ throw error;
130
+ (second ? registerMemorySecondRepairCurrentViewFromStage : registerMemoryRepairCurrentViewFromStage)(context, scope);
131
+ registerBaseline(context, scope);
132
+ }
133
+ (second ? reserveMemorySecondRepairCopyFromStage : reserveMemoryRepairWriterCopyFromStage)(context, scope);
134
+ (second ? materializeMemorySecondRepairCopyFromStage : materializeMemoryRepairWriterCopyFromStage)(context, scope);
135
+ return (second ? prepareMemorySecondRepairDispatchFromStage : prepareMemoryRepairDispatchFromStage)(context, scope);
136
+ };
137
+ const external = choices.filter(choice => choice.resolved.delegation.mode === "external");
138
+ if (external.length) {
139
+ const maximum = Math.min(...external.map(choice => choice.resolved.delegation.max_parallel ?? 1));
140
+ const selected = external.slice(0, maximum).map(choice => choice.work);
141
+ for (const work of selected)
142
+ prepareRepair(work);
143
+ return { kind: "external", works: selected };
144
+ }
145
+ const capacity = getStageFanoutCapacity(context, { ...input, nativeOnly: true }).available_slots;
146
+ if (typeof capacity !== "number" || !Number.isInteger(capacity) || capacity < 1)
147
+ return { kind: "wait", reason: "qualified_capacity_required" };
148
+ const wave = choices.slice(0, capacity).map(choice => {
149
+ const work = choice.work;
150
+ const row = context.db.get("SELECT payload_json,result_schema FROM works WHERE work_id=?", [work.work_id]);
151
+ const payload = JSON.parse(row?.payload_json ?? "null");
152
+ if (payload?.kind === "memory-review" && row?.result_schema === "dd-flow/memory-review-work-result@1") {
153
+ const copy = prepareMemoryNativeReviewCopy(context, work.work_id, true);
154
+ return { ...work, start_command: `cd '${copy.root.replaceAll("'", "'\\''")}' && ${workStartCommand(context, assertWorkLaunchReady(context, work.work_id))}` };
155
+ }
156
+ const packet = prepareRepair(work);
157
+ const command = workStartCommand(context, assertWorkLaunchReady(context, work.work_id));
158
+ if (!packet)
159
+ return { ...work, start_command: command };
160
+ // Ordinary literal shell grammar, not an invented native spawn CWD field.
161
+ const quotedRoot = "'" + packet.copy_root.replaceAll("'", "'\\''") + "'";
162
+ return { ...work, start_command: `cd ${quotedRoot} && ${command}` };
163
+ });
164
+ for (const work of wave)
165
+ if (!work.work_id || !work.start_command)
166
+ throw new AppError("fanout_contract_invalid", "Ready Work omits its trusted start command", 1);
167
+ const totalWorks = fanout.works.created + fanout.works.running + fanout.works.completed + fanout.works.failed + fanout.works.cancelled;
168
+ return prompt("dispatch_work", renderDelegationInstructions({ harness: input.harness, stage: input.stage, capacity, works: wave, totalWorks, remainingWorks: fanout.works.created - wave.length }));
169
+ }
170
+ if (fanout.works.running)
171
+ return { kind: "wait", reason: "work_receipts_pending" };
172
+ if (fanout.works.created)
173
+ throw new AppError("fanout_graph_stalled", "Created Work has no ready or running dependency path", 1);
174
+ if (input.stage === "review") {
175
+ const projectId = requireProjectByRoot(context, input.projectRoot).id;
176
+ const run = context.db.get("SELECT flow_kind FROM runs WHERE project_id=? AND id=?", [projectId, input.runId]);
177
+ if (run?.flow_kind === "mb-init") {
178
+ const attempt = stageAttempt(context, projectId, input.runId, "review");
179
+ const repair = context.db.get(`SELECT CASE event_type WHEN 'memory.second_repair_registration' THEN 2 ELSE 1 END AS wave
180
+ FROM audit_events WHERE project_id=? AND event_type IN ('memory.repair_registration','memory.second_repair_registration')
181
+ AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.scope.attempt')=?
182
+ AND json_extract(payload_json,'$.scope.parent_work_id')=? ORDER BY wave DESC LIMIT 1`, [projectId, input.runId, attempt, fanout.parent_work_id]);
183
+ if (repair && !context.db.get(`SELECT 1 FROM audit_events WHERE project_id=? AND event_type='memory.corrected_review_registration'
184
+ AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.scope.attempt')=?
185
+ AND json_extract(payload_json,'$.scope.parent_work_id')=? AND json_extract(payload_json,'$.wave')=?`, [projectId, input.runId, attempt, fanout.parent_work_id, repair.wave])) {
186
+ const args = ["--run", input.runId, "--project-root", input.projectRoot, "--wave", String(repair.wave), "--json"];
187
+ return prompt("semantic_completion", `Correction wave ${repair.wave} Work has settled. Before Stage finish, run ${runtimeCommand(context, ["memory", "corrections", "checks", ...args])}, then ${runtimeCommand(context, ["memory", "review", "selective-register", ...args])}. Read both complete responses, then end this Turn so the controller can dispatch the independent selective reviewers. Do not finish the Stage or treat repair Work completion as independent acceptance.`);
188
+ }
189
+ }
190
+ }
191
+ return prompt("semantic_completion", semanticCompletionPrompt(input.stage, fanout.works.completed));
192
+ }
193
+ /** Shared pinned runtime normalization; preserve structured errors at the TS boundary. */
194
+ export function controllerNativeChildren(receipt, rootSessionId) {
195
+ try {
196
+ return normalizeNativeChildren(receipt, rootSessionId);
197
+ }
198
+ catch (error) {
199
+ const record = errorRecord(error);
200
+ throw Object.assign(new AppError(record.code, record.message, record.exitCode, record.details), record);
201
+ }
202
+ }
@@ -1,10 +1,12 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { spawnSync } from "node:child_process";
3
+ import { commandResult } from '../harness-runtime/lib/process-json.mjs';
4
+ import { SETUP_STARTUP_MS } from '../harness-runtime/lib/setup-progress.mjs';
4
5
  import { isActiveProtocolStatus, isActiveQueueStatus, isQueueHistoryStatus } from "../domain/contracts.js";
5
6
  import { AppError } from "../shared/errors.js";
6
- import { ensureDir, resolveProjectRoot } from "../storage/paths.js";
7
- import { dashboardMarkdownPath, globalDashboardHtmlPath, globalDashboardJsonPath, globalDashboardMarkdownPath, projectDashboardHtmlPath, projectDashboardJsonPath, projectSummaryJsonPath, protocolDashboardHtmlPath, protocolDashboardJsonPath, readProjectConfig } from "./config.js";
7
+ import { prepareOutputFile } from "../cli/input-preparation.js";
8
+ import { ensureDir, resolveProjectRoot, resolveProtocolPlanPath } from "../storage/paths.js";
9
+ import { dashboardMarkdownPath, globalDashboardHtmlPath, globalDashboardJsonPath, globalDashboardMarkdownPath, projectDashboardHtmlPath, projectDashboardJsonPath, projectDashboardDir, projectSummaryJsonPath, protocolDashboardHtmlPath, protocolDashboardJsonPath, readProjectConfig } from "./config.js";
8
10
  import { queueForProject } from "./merge-queue.js";
9
11
  import { publishProjectSummary } from "./project-summary.js";
10
12
  import { requireProjectByRoot } from "./projects.js";
@@ -13,70 +15,82 @@ import { activeFlowSessionsForProject } from "./sessions.js";
13
15
  import { usageForRun } from "./usage.js";
14
16
  import { normalizeProtocolLifecycle } from "./protocol-lifecycle.js";
15
17
  import { planSummary, planWithProgress } from "./plan-runtime.js";
16
- export function getCmuxStatus(context, input) {
18
+ export async function getCmuxStatus(context, input) {
17
19
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
18
- return { ok: true, project_root: project.root, cmux: detectCmux(context) };
20
+ return { ok: true, project_root: project.root, cmux: await detectCmux(context) };
19
21
  }
20
- export function renderDashboard(context, input) {
22
+ export function prepareDashboardRender(context, input) {
21
23
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
24
+ if (projectRecoveryGuarded(context, project.id))
25
+ throw new AppError("run_recovery_guarded", "Project dashboard is frozen while its RUN is retained for recovery", 1);
22
26
  const format = parseDashboardFormat(input.format);
27
+ if (input.protocol)
28
+ requireProtocol(context, input.protocol, project.id);
29
+ const config = readProjectConfig(context, project.id);
30
+ const output = input.output
31
+ ? path.resolve(project.root, input.output)
32
+ : format === "html"
33
+ ? input.protocol ? protocolDashboardHtmlPath(context, project.id, input.protocol) : projectDashboardHtmlPath(context, project.id)
34
+ : dashboardMarkdownPath(project.root, config);
35
+ prepareOutputFile(output);
36
+ if (format === "html")
37
+ prepareOutputFile(input.protocol ? protocolDashboardJsonPath(context, project.id, input.protocol) : projectDashboardJsonPath(context, project.id));
38
+ return { project, format, output };
39
+ }
40
+ export function renderDashboard(context, input) {
41
+ const { project, format, output } = prepareDashboardRender(context, input);
23
42
  if (format === "html") {
24
43
  if (input.protocol) {
25
44
  return renderProtocolDashboardHtml(context, project, input.protocol, input.output);
26
45
  }
27
46
  return renderProjectDashboardHtml(context, project, input.output);
28
47
  }
29
- const config = readProjectConfig(context, project.id);
30
- const output = input.output
31
- ? path.resolve(path.isAbsolute(input.output) ? input.output : path.join(project.root, input.output))
32
- : dashboardMarkdownPath(project.root, config);
33
48
  const markdown = renderProjectDashboardMarkdown(context, project, output);
34
49
  writeMarkdown(output, markdown);
35
50
  return { ok: true, project_root: project.root, dashboard: { path: output, bytes: Buffer.byteLength(markdown) } };
36
51
  }
37
- export function renderGlobalDashboard(context, input = {}) {
52
+ export function prepareGlobalDashboardRender(context, input = {}) {
38
53
  const format = parseDashboardFormat(input.format);
54
+ const output = input.output ? path.resolve(input.output) : format === "html" ? globalDashboardHtmlPath(context) : globalDashboardMarkdownPath(context);
55
+ prepareOutputFile(output);
56
+ if (format === "html")
57
+ prepareOutputFile(globalDashboardJsonPath(context));
58
+ return { format, output };
59
+ }
60
+ export function renderGlobalDashboard(context, input = {}) {
61
+ const { format, output } = prepareGlobalDashboardRender(context, input);
39
62
  if (format === "html") {
40
63
  return renderGlobalDashboardHtml(context, input.output);
41
64
  }
42
- const output = input.output
43
- ? path.resolve(input.output)
44
- : globalDashboardMarkdownPath(context);
45
65
  const markdown = renderGlobalDashboardMarkdown(context, output);
46
66
  writeMarkdown(output, markdown);
47
67
  return { ok: true, dashboard: { path: output, bytes: Buffer.byteLength(markdown) } };
48
68
  }
49
- export function openDashboard(context, input) {
69
+ export async function openDashboard(context, input) {
70
+ prepareDashboardOptions(input);
50
71
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
51
72
  const config = readProjectConfig(context, project.id);
52
73
  const format = parseDashboardFormat(input.format, "html");
53
- if (input.viewer && input.viewer !== "cmux") {
54
- throw new AppError("validation", "--viewer must be cmux", 2);
55
- }
56
74
  const dashboardPath = format === "html" ? projectDashboardHtmlPath(context, project.id) : dashboardMarkdownPath(project.root, config);
57
75
  return {
58
76
  ok: true,
59
77
  project_root: project.root,
60
78
  dashboard: { path: dashboardPath },
61
- open: openCmuxDashboard(context, project.root, dashboardPath, format)
79
+ open: await openCmuxDashboard(context, project.root, dashboardPath, format)
62
80
  };
63
81
  }
64
- export function openGlobalDashboard(context, input = {}) {
65
- if (input.viewer && input.viewer !== "cmux") {
66
- throw new AppError("validation", "--viewer must be cmux", 2);
67
- }
82
+ export async function openGlobalDashboard(context, input = {}) {
83
+ prepareDashboardOptions(input);
68
84
  const format = parseDashboardFormat(input.format, "html");
69
85
  const rendered = renderGlobalDashboard(context, { format });
70
86
  return {
71
87
  ok: true,
72
88
  dashboard: rendered.dashboard,
73
- open: openDashboardPath(context, rendered.dashboard.path, format)
89
+ open: await openDashboardPath(context, rendered.dashboard.path, format)
74
90
  };
75
91
  }
76
- export function openProtocolDashboard(context, input) {
77
- if (input.viewer && input.viewer !== "cmux") {
78
- throw new AppError("validation", "--viewer must be cmux", 2);
79
- }
92
+ export async function openProtocolDashboard(context, input) {
93
+ prepareDashboardOptions(input);
80
94
  const format = parseDashboardFormat(input.format, "html");
81
95
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
82
96
  if (format !== "html") {
@@ -90,17 +104,43 @@ export function openProtocolDashboard(context, input) {
90
104
  project_root: project.root,
91
105
  protocol_id: input.protocol,
92
106
  dashboard: rendered.dashboard,
93
- open: openCmuxDashboard(context, project.root, rendered.dashboard.path, format)
107
+ open: await openCmuxDashboard(context, project.root, rendered.dashboard.path, format)
94
108
  };
95
109
  }
96
- export function refreshDashboard(context, input) {
110
+ export function prepareDashboardRefresh(context, input) {
111
+ prepareDashboardOptions(input);
97
112
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
98
113
  const config = readProjectConfig(context, project.id);
99
114
  const format = parseDashboardFormat(input.format);
100
115
  const openMode = input.open ?? "auto";
101
- if (!["auto", "true", "false"].includes(openMode)) {
102
- throw new AppError("validation", "--open must be auto, true, or false", 2);
116
+ if (config.dashboard.project) {
117
+ if (projectRecoveryGuarded(context, project.id))
118
+ throw new AppError("run_recovery_guarded", "Project dashboard is frozen while its RUN is retained for recovery", 1);
119
+ if (input.protocol)
120
+ requireProtocol(context, input.protocol, project.id);
121
+ prepareRefreshOutputs(context, project, config, format, input.protocol);
103
122
  }
123
+ return { project, config, format, openMode };
124
+ }
125
+ function prepareRefreshOutputs(context, project, config, format, protocol) {
126
+ if (config.dashboard.project) {
127
+ if (!protocol)
128
+ prepareOutputFile(projectSummaryJsonPath(context, project.id));
129
+ if (format === "html") {
130
+ prepareOutputFile(protocol ? protocolDashboardHtmlPath(context, project.id, protocol) : projectDashboardHtmlPath(context, project.id));
131
+ prepareOutputFile(protocol ? protocolDashboardJsonPath(context, project.id, protocol) : projectDashboardJsonPath(context, project.id));
132
+ }
133
+ else
134
+ prepareOutputFile(dashboardMarkdownPath(project.root, config));
135
+ }
136
+ if (config.dashboard.global) {
137
+ prepareOutputFile(format === "html" ? globalDashboardHtmlPath(context) : globalDashboardMarkdownPath(context, config));
138
+ if (format === "html")
139
+ prepareOutputFile(globalDashboardJsonPath(context));
140
+ }
141
+ }
142
+ export async function refreshDashboard(context, input) {
143
+ const { project, config, format, openMode } = prepareDashboardRefresh(context, input);
104
144
  if (!config.dashboard.project) {
105
145
  return {
106
146
  ok: true,
@@ -124,7 +164,7 @@ export function refreshDashboard(context, input) {
124
164
  dashboard: rendered.dashboard,
125
165
  project_summary: projectSummary,
126
166
  global_dashboard: global.dashboard,
127
- open: shouldOpen ? openCmuxDashboard(context, project.root, rendered.dashboard.path, format) : { ok: true, skipped: true, reason: "open_disabled" }
167
+ open: shouldOpen ? await openCmuxDashboard(context, project.root, rendered.dashboard.path, format) : { ok: true, skipped: true, reason: "open_disabled" }
128
168
  };
129
169
  }
130
170
  export function refreshGlobalDashboard(context, input = {}) {
@@ -143,8 +183,9 @@ export function refreshAllDashboards(context, input = {}) {
143
183
  return { ok: true, project_id: project.id, project_root: project.root, skipped: true, reason: "dashboard_project_disabled" };
144
184
  }
145
185
  try {
146
- const projectSummary = publishProjectSummary(context, { project });
186
+ prepareRefreshOutputs(context, project, { ...config, dashboard: { ...config.dashboard, global: false } }, format);
147
187
  const rendered = renderDashboard(context, { projectRoot: project.root, format });
188
+ const projectSummary = publishProjectSummary(context, { project });
148
189
  return { ok: true, project_id: project.id, project_root: project.root, dashboard: rendered.dashboard, project_summary: projectSummary };
149
190
  }
150
191
  catch (error) {
@@ -170,10 +211,21 @@ export function refreshAllDashboards(context, input = {}) {
170
211
  }
171
212
  export function autoRefreshDashboards(context, input) {
172
213
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
173
- const config = readProjectConfig(context, project.id);
214
+ // Hooks and usage ingestion continue to record receipts after capture.
215
+ // Their derived timestamp must not mutate the captured workspace.
216
+ if (projectRecoveryGuarded(context, project.id))
217
+ return { ok: true, skipped: true, reason: "run_recovery_guarded" };
218
+ const configured = readProjectConfig(context, project.id);
219
+ // Automatic observation must not change the source baseline owned by init.
220
+ // Keep projections in Home; do not hide runtime-created paths in Git or
221
+ // weaken source preservation by excluding user-owned .tasks directories.
222
+ const initializing = context.db.get("SELECT 1 FROM runs WHERE project_id=? AND flow_kind='mb-init' AND (id=? OR status IN ('created','running','paused','waiting_for_user','blocked')) LIMIT 1", [project.id, input.runId ?? null]);
223
+ const config = initializing ? { ...configured, dashboard: { ...configured.dashboard,
224
+ markdown_path: path.join(projectDashboardDir(context.ddFlowHome, project.id), "project-dashboard.md") } } : configured;
174
225
  if (!config.dashboard.auto_refresh) {
175
226
  return { ok: true, skipped: true, reason: "dashboard_auto_refresh_disabled" };
176
227
  }
228
+ prepareRefreshOutputs(context, project, config, "markdown");
177
229
  const result = { ok: true, project_root: project.root };
178
230
  if (config.dashboard.project) {
179
231
  publishProjectSummary(context, { project });
@@ -196,6 +248,9 @@ export function autoRefreshDashboards(context, input) {
196
248
  }
197
249
  return result;
198
250
  }
251
+ function projectRecoveryGuarded(context, projectId) {
252
+ return Boolean(context.db.get("SELECT 1 FROM run_recovery_guards WHERE project_id = ? AND status IN ('draining', 'sealed', 'resuming') LIMIT 1", [projectId]));
253
+ }
199
254
  export function dashboardSummary(context, projectId, projectRoot) {
200
255
  const config = readProjectConfig(context, projectId);
201
256
  return {
@@ -221,13 +276,13 @@ export function dashboardData(context, input) {
221
276
  }
222
277
  return { ok: true, dashboard: buildProjectDashboardData(context, project, projectDashboardHtmlPath(context, project.id)) };
223
278
  }
224
- function openCmuxDashboard(context, projectRoot, dashboardPath, format = "markdown") {
279
+ async function openCmuxDashboard(context, projectRoot, dashboardPath, format = "markdown") {
225
280
  const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
226
281
  const config = readProjectConfig(context, project.id);
227
282
  if (config.integrations.cmux.mode === "off") {
228
283
  return { ok: true, skipped: true, reason: "cmux_off" };
229
284
  }
230
- const cmux = detectCmux(context);
285
+ const cmux = await detectCmux(context);
231
286
  if (!cmux.available) {
232
287
  if (config.integrations.cmux.mode === "required") {
233
288
  throw new AppError("cmux_unavailable", "cmux is required by project config but is unavailable", 1, { cmux });
@@ -238,20 +293,20 @@ function openCmuxDashboard(context, projectRoot, dashboardPath, format = "markdo
238
293
  return { ok: true, opened: false, dry_run: true, command: cmuxOpenCommand(format, dashboardPath) };
239
294
  }
240
295
  const command = cmuxOpenCommand(format, dashboardPath);
241
- const result = spawnSync(command[0], command.slice(1), { encoding: "utf8" });
242
- if (result.status !== 0) {
296
+ const result = await commandResult(command[0], command.slice(1), { startupMs: SETUP_STARTUP_MS });
297
+ if (result.exit_code !== 0) {
243
298
  if (config.integrations.cmux.mode === "required") {
244
299
  throw new AppError("cmux_open_failed", "cmux markdown open failed", 1, {
245
- status: result.status,
300
+ status: result.exit_code,
246
301
  stderr: result.stderr.trim()
247
302
  });
248
303
  }
249
- return { ok: true, skipped: true, reason: "cmux_open_failed", status: result.status, stderr: result.stderr.trim() };
304
+ return { ok: true, skipped: true, reason: "cmux_open_failed", status: result.exit_code, stderr: result.stderr.trim() };
250
305
  }
251
306
  return { ok: true, opened: true, command };
252
307
  }
253
- function openDashboardPath(context, dashboardPath, format) {
254
- const cmux = detectCmux(context);
308
+ async function openDashboardPath(context, dashboardPath, format) {
309
+ const cmux = await detectCmux(context);
255
310
  if (!cmux.available) {
256
311
  return { ok: true, skipped: true, reason: "cmux_unavailable", cmux };
257
312
  }
@@ -259,9 +314,9 @@ function openDashboardPath(context, dashboardPath, format) {
259
314
  return { ok: true, opened: false, dry_run: true, command: cmuxOpenCommand(format, dashboardPath) };
260
315
  }
261
316
  const command = cmuxOpenCommand(format, dashboardPath);
262
- const result = spawnSync(command[0], command.slice(1), { encoding: "utf8" });
263
- if (result.status !== 0) {
264
- return { ok: true, skipped: true, reason: "cmux_open_failed", status: result.status, stderr: result.stderr.trim() };
317
+ const result = await commandResult(command[0], command.slice(1), { startupMs: SETUP_STARTUP_MS });
318
+ if (result.exit_code !== 0) {
319
+ return { ok: true, skipped: true, reason: "cmux_open_failed", status: result.exit_code, stderr: result.stderr.trim() };
265
320
  }
266
321
  return { ok: true, opened: true, command };
267
322
  }
@@ -270,17 +325,31 @@ function cmuxOpenCommand(format, dashboardPath) {
270
325
  ? ["cmux", "browser", "open", pathToFileUrl(dashboardPath)]
271
326
  : ["cmux", "markdown", "open", dashboardPath];
272
327
  }
273
- function detectCmux(context) {
328
+ async function detectCmux(context) {
274
329
  if (context.env.DD_FLOW_CMUX_AVAILABLE === "true") {
275
330
  return { available: true, source: "env" };
276
331
  }
277
332
  if (context.env.DD_FLOW_CMUX_AVAILABLE === "false") {
278
333
  return { available: false, source: "env", reason: "forced_unavailable" };
279
334
  }
280
- const result = spawnSync("cmux", ["--version"], { encoding: "utf8" });
281
- return result.status === 0
335
+ let result;
336
+ try {
337
+ result = await commandResult("cmux", ["--version"], { startupMs: SETUP_STARTUP_MS, phase: 'native-version' });
338
+ }
339
+ catch (error) {
340
+ return { available: false, source: 'path', reason: error instanceof Error ? error.message : String(error) };
341
+ }
342
+ return result.exit_code === 0
282
343
  ? { available: true, source: "path", version: result.stdout.trim() }
283
- : { available: false, source: "path", reason: result.error?.message ?? result.stderr.trim() ?? "cmux not found" };
344
+ : { available: false, source: "path", reason: result.stderr.trim() || "cmux not found" };
345
+ }
346
+ export function prepareDashboardOptions(input) {
347
+ if (input.format !== undefined)
348
+ parseDashboardFormat(input.format);
349
+ if (input.viewer !== undefined && input.viewer !== "cmux")
350
+ throw new AppError("validation", "--viewer must be cmux", 2);
351
+ if (input.open !== undefined && !["auto", "true", "false"].includes(input.open))
352
+ throw new AppError("validation", "--open must be auto, true, or false", 2);
284
353
  }
285
354
  function parseDashboardFormat(value, defaultFormat = "markdown") {
286
355
  const format = value ?? defaultFormat;
@@ -294,15 +363,18 @@ function renderProjectDashboardHtml(context, project, output) {
294
363
  ? path.resolve(path.isAbsolute(output) ? output : path.join(project.root, output))
295
364
  : projectDashboardHtmlPath(context, project.id);
296
365
  const jsonPath = projectDashboardJsonPath(context, project.id);
366
+ prepareOutputFile(htmlPath);
367
+ prepareOutputFile(jsonPath);
297
368
  const data = buildProjectDashboardData(context, project, htmlPath);
298
369
  const html = renderDashboardHtmlPage(data);
299
- writeJsonFile(jsonPath, data);
300
- writeTextFile(htmlPath, html);
301
370
  const protocolCards = Array.isArray(data.protocol_cards) ? data.protocol_cards : [];
302
- const protocolPages = protocolCards
371
+ const preparedPages = protocolCards
303
372
  .filter((card) => card.generate_page !== false)
304
373
  .slice(0, 24)
305
- .map((card) => renderProtocolDashboardHtml(context, project, String(card.id)))
374
+ .map((card) => prepareProtocolDashboardHtml(context, project, String(card.id)));
375
+ writeJsonFile(jsonPath, data);
376
+ writeTextFile(htmlPath, html);
377
+ const protocolPages = preparedPages.map(writeProtocolDashboardHtml)
306
378
  .map((result) => ({
307
379
  id: String(result.protocol_id),
308
380
  path: result.dashboard.path,
@@ -321,12 +393,23 @@ function renderProjectDashboardHtml(context, project, output) {
321
393
  };
322
394
  }
323
395
  function renderProtocolDashboardHtml(context, project, protocolId, output) {
396
+ return writeProtocolDashboardHtml(prepareProtocolDashboardHtml(context, project, protocolId, output));
397
+ }
398
+ function prepareProtocolDashboardHtml(context, project, protocolId, output) {
399
+ if (projectRecoveryGuarded(context, project.id))
400
+ throw new AppError("run_recovery_guarded", "Project dashboard is frozen while its RUN is retained for recovery", 1);
324
401
  const htmlPath = output
325
402
  ? path.resolve(path.isAbsolute(output) ? output : path.join(project.root, output))
326
403
  : protocolDashboardHtmlPath(context, project.id, protocolId);
327
404
  const jsonPath = protocolDashboardJsonPath(context, project.id, protocolId);
405
+ prepareOutputFile(htmlPath);
406
+ prepareOutputFile(jsonPath);
328
407
  const data = buildProtocolDashboardData(context, project, protocolId, htmlPath);
329
408
  const html = renderDashboardHtmlPage(data);
409
+ return { project, protocolId, htmlPath, jsonPath, data, html };
410
+ }
411
+ function writeProtocolDashboardHtml(prepared) {
412
+ const { project, protocolId, htmlPath, jsonPath, data, html } = prepared;
330
413
  writeJsonFile(jsonPath, data);
331
414
  writeTextFile(htmlPath, html);
332
415
  return {
@@ -344,6 +427,8 @@ function renderProtocolDashboardHtml(context, project, protocolId, output) {
344
427
  function renderGlobalDashboardHtml(context, output) {
345
428
  const htmlPath = output ? path.resolve(output) : globalDashboardHtmlPath(context);
346
429
  const jsonPath = globalDashboardJsonPath(context);
430
+ prepareOutputFile(htmlPath);
431
+ prepareOutputFile(jsonPath);
347
432
  const data = buildGlobalDashboardData(context, htmlPath);
348
433
  const html = renderDashboardHtmlPage(data);
349
434
  writeJsonFile(jsonPath, data);
@@ -888,12 +973,12 @@ function protocolsForProject(context, projectId) {
888
973
  FROM protocols WHERE project_id = ? ORDER BY updated_at DESC`, [projectId]);
889
974
  }
890
975
  function planSummaryForProtocol(context, projectId, protocolId) {
891
- const protocol = context.db.get("SELECT plan_path FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocolId]);
976
+ const protocol = context.db.get("SELECT plan_path, project_root FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocolId]);
892
977
  if (!protocol) {
893
978
  return { plan_id: "none", total: 0, done: 0, blocked: 0 };
894
979
  }
895
980
  try {
896
- const summary = planSummary(context, { projectId, protocolId, planPath: protocol.plan_path }, { bind: false });
981
+ const summary = planSummary(context, { projectId, protocolId, planPath: resolveProtocolPlanPath(protocol.project_root, protocolId, protocol.plan_path) }, { bind: false });
897
982
  return { plan_id: summary.plan_id, total: summary.total, done: summary.done, blocked: summary.blocked };
898
983
  }
899
984
  catch {
@@ -903,13 +988,13 @@ function planSummaryForProtocol(context, projectId, protocolId) {
903
988
  function activePlanItems(context, projectId, protocols) {
904
989
  const items = [];
905
990
  for (const protocol of protocols) {
906
- const record = context.db.get("SELECT plan_path FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocol.id]);
991
+ const record = context.db.get("SELECT plan_path, project_root FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocol.id]);
907
992
  if (!record) {
908
993
  continue;
909
994
  }
910
995
  let plan;
911
996
  try {
912
- plan = planWithProgress(context, { projectId, protocolId: protocol.id, planPath: record.plan_path }, { bind: false }).plan;
997
+ plan = planWithProgress(context, { projectId, protocolId: protocol.id, planPath: resolveProtocolPlanPath(record.project_root, protocol.id, record.plan_path) }, { bind: false }).plan;
913
998
  }
914
999
  catch {
915
1000
  continue;