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

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 +740 -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 +728 -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 +140 -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 +557 -0
  62. package/dist/harness-runtime/lib/dd-opencode.mjs +363 -0
  63. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +751 -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 +16 -0
  77. package/dist/harness-runtime/lib/managed-daemon.mjs +683 -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 +16 -0
  92. package/dist/harness-runtime/lib/operation-errors.mjs +127 -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 +250 -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 +1168 -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 +152 -116
  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 +41 -30
  460. package/dist/services/usage.js +49 -101
  461. package/dist/services/verification-matrix.js +375 -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 +228 -104
  470. package/dist/services/vnext-protocolize.js +531 -95
  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 +105 -48
  477. package/dist/shared/entity-references.js +8 -0
  478. package/dist/shared/errors.js +27 -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
@@ -1 +1,10 @@
1
- export {};
1
+ import { isControllerManagedInvocation, managedLifecycleCommand } from "./lifecycle-invocations.js";
2
+ /** Successor authority belongs to the controller, not the finishing Session.
3
+ * Manual flows still need an executable next step. Current-stage finish and
4
+ * paused/child Work commands do not pass through this boundary. */
5
+ export function stageSuccessor(context, input) {
6
+ const next = { ...input.metadata, kind: "start_stage", stage: input.stage };
7
+ return isControllerManagedInvocation(context, input.projectRoot, input.runId)
8
+ ? { ...next, controller_owned: true, stop_turn: true, instruction: "This Stage is complete. End this Turn now and await the controller's next Stage assignment. Do not start the successor Stage or prepare its command." }
9
+ : { ...next, command: managedLifecycleCommand(context, input.command) };
10
+ }
@@ -1,19 +1,91 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import crypto from "node:crypto";
4
+ import { isDeepStrictEqual } from "node:util";
3
5
  import { AppError } from "../shared/errors.js";
6
+ import { canonicalPath } from "../storage/paths.js";
7
+ import { isExecutionRouting } from "./execution-policy.js";
8
+ export const RUN_OPERATIONAL_DECISION_CONTRACT = "run-operational-decision@1";
9
+ const object = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
10
+ const text = (value) => typeof value === "string" && Boolean(value.trim());
11
+ const keys = (value, allowed) => object(value) && Object.keys(value).every(key => allowed.includes(key));
12
+ /** Structural validation only: acceptance is supplied by the caller, never inferred from settings. */
13
+ export function validateRunOperationalDecision(value, settings) {
14
+ if (!object(value) || value.schema_id !== RUN_OPERATIONAL_DECISION_CONTRACT || !text(value.id)
15
+ || !object(value.scope) || !text(value.scope.project_root) || !path.isAbsolute(value.scope.project_root)
16
+ || [value.scope.eval_id, value.scope.execution_id].some(item => item !== undefined && !text(item))
17
+ || !object(value.settings) || !object(value.settings.merge_delivery) || value.settings.merge_delivery.strategy !== settings.merge_delivery.strategy
18
+ || !object(value.settings.merge_cleanup) || value.settings.merge_cleanup.source !== settings.merge_cleanup.source
19
+ || !object(value.policy) || !text(value.policy.path) || !text(value.policy.sha256) || !/^[a-f0-9]{64}$/.test(value.policy.sha256)
20
+ || !Array.isArray(value.policy.clauses) || !value.policy.clauses.length || !value.policy.clauses.every(text)
21
+ || !object(value.acceptance) || !text(value.acceptance.source) || !text(value.acceptance.reference)
22
+ || value.exception !== undefined && (!object(value.exception) || !text(value.exception.reason) || !text(value.exception.owner)
23
+ || !text(value.exception.review_at) || !Number.isFinite(Date.parse(value.exception.review_at))
24
+ || value.exception.expires_at !== undefined && (!text(value.exception.expires_at) || !Number.isFinite(Date.parse(value.exception.expires_at))))) {
25
+ throw new AppError("operational_decision_invalid", "Operational decision requires explicit acceptance, scoped authority and matching settings", 2);
26
+ }
27
+ if (!keys(value, ["schema_id", "id", "scope", "settings", "policy", "acceptance", "exception", "declaration"])
28
+ || !keys(value.scope, ["project_root", "eval_id", "execution_id"])
29
+ || !keys(value.settings, ["merge_delivery", "merge_cleanup"])
30
+ || !keys(value.settings.merge_delivery, ["strategy"]) || !keys(value.settings.merge_cleanup, ["source"])
31
+ || !keys(value.policy, ["path", "sha256", "clauses"]) || !keys(value.acceptance, ["source", "reference"])
32
+ || value.exception !== undefined && !keys(value.exception, ["reason", "owner", "review_at", "expires_at"])
33
+ || value.declaration !== undefined && !keys(value.declaration, ["bytes", "sha256"]))
34
+ throw new AppError("operational_decision_invalid", "Operational decision contains unsupported authority fields", 2);
35
+ if (value.declaration !== undefined) {
36
+ if (!object(value.declaration) || !text(value.declaration.bytes) || !text(value.declaration.sha256)
37
+ || crypto.createHash("sha256").update(value.declaration.bytes).digest("hex") !== value.declaration.sha256)
38
+ throw new AppError("operational_decision_invalid", "Original operational declaration checksum does not match", 2);
39
+ let declaration;
40
+ try {
41
+ declaration = JSON.parse(value.declaration.bytes);
42
+ }
43
+ catch {
44
+ throw new AppError("operational_decision_invalid", "Original operational declaration must be JSON", 2);
45
+ }
46
+ if (!object(declaration) || !keys(declaration, ["schema_id", "id", "scope", "settings", "policy", "acceptance", "exception"])
47
+ || declaration.scope !== undefined && !keys(declaration.scope, ["project_root", "eval_id", "execution_id"])
48
+ || ["schema_id", "id", "settings", "policy", "acceptance", "exception"].some(key => !isDeepStrictEqual(declaration[key], value[key])))
49
+ throw new AppError("operational_decision_invalid", "Materialized decision changed the accepted declaration", 2);
50
+ }
51
+ return value;
52
+ }
53
+ /** Machine validation of declared policy bytes, not an NLP interpretation of Markdown. */
54
+ export function assertOperationalDecisionSource(decision, projectRoot, now) {
55
+ if (canonicalPath(decision.scope.project_root, false) !== canonicalPath(projectRoot, false))
56
+ throw new AppError("operational_decision_scope_mismatch", "Operational decision belongs to another project", 2);
57
+ const file = path.resolve(projectRoot, decision.policy.path);
58
+ const relative = path.relative(fs.realpathSync(projectRoot), fs.existsSync(file) ? fs.realpathSync(file) : file);
59
+ if (relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) || relative === ".." || !fs.existsSync(file) || crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex") !== decision.policy.sha256)
60
+ throw new AppError("operational_decision_policy_changed", "Operational decision policy source checksum does not match an owned file", 2, { file });
61
+ if (decision.exception?.expires_at && Date.parse(decision.exception.expires_at) <= Date.parse(now))
62
+ throw new AppError("operational_decision_expired", "Explicit operational decision expiration has passed", 2);
63
+ }
4
64
  const relativePath = path.join(".memory-bank", "dd-flow", "project-execution.json");
5
65
  /** The project owns defaults; a RUN snapshots them once and never re-infers them. */
6
66
  export function loadVnextExecutionProfile(projectRoot) {
67
+ return loadVnextExecutionProfileSnapshot(projectRoot).profile;
68
+ }
69
+ /** Parse and identify the same source bytes; a second read cannot bind another declaration. */
70
+ export function loadVnextExecutionProfileSnapshot(projectRoot) {
7
71
  const file = path.join(projectRoot, relativePath);
72
+ let bytes;
8
73
  let value;
9
74
  try {
10
- value = JSON.parse(fs.readFileSync(file, "utf8"));
75
+ bytes = fs.readFileSync(file);
76
+ value = JSON.parse(bytes.toString("utf8"));
11
77
  }
12
78
  catch (error) {
13
79
  throw new AppError("execution_profile_missing", "vNext flow requires .memory-bank/dd-flow/project-execution.json", 1, { file, cause: String(error) });
14
80
  }
81
+ return { profile: validateVnextExecutionProfile(value), bytes };
82
+ }
83
+ /** Pure shared admission contract for project-execution@2 and @3. */
84
+ export function validateVnextExecutionProfile(value) {
85
+ if (!object(value))
86
+ throw new AppError("execution_profile_invalid", "Project execution profile must be an object", 1);
15
87
  const profile = value;
16
- if (profile.schema_id !== "dd-flow/project-execution@2"
88
+ if ((profile.schema_id !== "dd-flow/project-execution@2" && profile.schema_id !== "dd-flow/project-execution@3")
17
89
  || (profile.stage_session_mode !== undefined && profile.stage_session_mode !== "same_session" && profile.stage_session_mode !== "new_session")
18
90
  || !["auto", "off", "standard", "deep"].includes(String(profile.plan_review_mode))
19
91
  || (profile.code_review_mode !== undefined && !["auto", "off", "standard", "deep"].includes(String(profile.code_review_mode)))
@@ -21,10 +93,13 @@ export function loadVnextExecutionProfile(projectRoot) {
21
93
  || profile.merge_delivery?.strategy !== "local"
22
94
  || !["retain", "delete_after_success"].includes(String(profile.merge_cleanup?.source))
23
95
  || !["code_completed", "code_review_completed", "merge_completed"].includes(String(profile.stop_target))
24
- || !profile.code_bootstrap || typeof profile.code_bootstrap.command !== "string" || !profile.code_bootstrap.command.trim()
25
- || typeof profile.code_bootstrap.policy_ref !== "string" || !profile.code_bootstrap.policy_ref.trim()) {
26
- throw new AppError("execution_profile_invalid", "Project execution profile does not match dd-flow/project-execution@2", 1, { file });
96
+ || (profile.code_bootstrap !== undefined && (!profile.code_bootstrap || typeof profile.code_bootstrap.command !== "string" || !profile.code_bootstrap.command.trim()
97
+ || typeof profile.code_bootstrap.policy_ref !== "string" || !profile.code_bootstrap.policy_ref.trim()))
98
+ || (profile.execution === undefined ? profile.schema_id === "dd-flow/project-execution@3" : !isExecutionRouting(profile.execution))) {
99
+ throw new AppError("execution_profile_invalid", "Project execution profile does not match its supported dd-flow schema", 1);
27
100
  }
101
+ if (profile.operational_decision !== undefined)
102
+ validateRunOperationalDecision(profile.operational_decision, profile);
28
103
  return { ...profile, stage_session_mode: profile.stage_session_mode ?? "same_session", code_review_mode: profile.code_review_mode ?? "off" };
29
104
  }
30
105
  export function executionProfilePath(projectRoot) { return path.join(projectRoot, relativePath); }
@@ -3,10 +3,106 @@ import path from "node:path";
3
3
  import { AppError } from "../shared/errors.js";
4
4
  import { resolveProjectRoot } from "../storage/paths.js";
5
5
  import { requireProjectByRoot } from "./projects.js";
6
- import { getFlowRunVariables } from "./runs.js";
7
- import { listWorks } from "./work-registry.js";
6
+ import { getFlowRunVariables, appendFlowRunTimelineEvent, readFlowRunExecutionBundle } from "./runs.js";
7
+ import { resolveExecutionPolicy } from "./execution-policy.js";
8
+ import { listWorks, failWork, cancelWork, readWorkBatchCustody } from "./work-registry.js";
9
+ import { storageSessionId } from "./session-identity.js";
10
+ import { reconcileUnfinishedChecks } from "./code-checks.js";
8
11
  import { vnextStageDirectory } from "../domain/stage-catalog.js";
12
+ import { stageWorkIds, stageGraphScope } from "./stage-work-graph.js";
13
+ import { eligibleProviderChildOverload } from "./provider-capacity.js";
14
+ import { renderNativeChildFollowupInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
15
+ import { readDescriptorRootWork } from "./descriptor-root-work.js";
16
+ import { selectMemoryCompositionRegistration } from "./memory-composition-registration.js";
17
+ import { safeNativeFailure } from "../harness-runtime/lib/native-children.mjs";
9
18
  export const subagentCapacityKey = "runtime.subagent.available_slots";
19
+ /** External concurrency is policy, never evidence of native child qualification. */
20
+ export function getStageFanoutCapacity(context, input) {
21
+ const projectRoot = resolveProjectRoot(input.projectRoot);
22
+ const project = requireProjectByRoot(context, projectRoot);
23
+ const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
24
+ if (!run)
25
+ throw new AppError("not_found", "RUN is unavailable", 1);
26
+ const index = JSON.parse(run.index_json);
27
+ const bundle = index.execution_bundle ? readFlowRunExecutionBundle(context, { projectRoot, runId: input.runId }) : null;
28
+ const generic = bundle?.execution.work(input.stage).delegation;
29
+ if (generic?.mode === "external")
30
+ return input.nativeOnly
31
+ ? { run_key: null, available_slots: null, source: "harness_qualification" }
32
+ : { run_key: null, available_slots: generic.max_parallel, source: "external_policy" };
33
+ // Generic native admission already requires a scoped capacity receipt whose
34
+ // observed count covers this frozen limit. Mutable legacy variables cannot
35
+ // replace that receipt or silently prevent its productive dispatch.
36
+ if (generic?.mode === "native")
37
+ return { run_key: null, available_slots: generic.max_parallel, source: "harness_qualification" };
38
+ const frozen = index.execution_profile;
39
+ if (!bundle && frozen?.settings.execution) {
40
+ if (!frozen.agent_profiles)
41
+ throw new AppError("execution_profile_not_frozen", "Fan-out needs the RUN's frozen profiles", 1);
42
+ const resolved = resolveExecutionPolicy({ ddFlowHome: context.ddFlowHome, policy: frozen.settings.execution, profiles: frozen.agent_profiles, stage: input.stage });
43
+ if (resolved.delegation.mode === "external" && !input.nativeOnly)
44
+ return { run_key: null, available_slots: resolved.delegation.max_parallel, source: "external_policy" };
45
+ }
46
+ const variables = getFlowRunVariables(context, { projectRoot, runId: input.runId });
47
+ const available = variables.variables[subagentCapacityKey];
48
+ return { run_key: subagentCapacityKey, available_slots: typeof available === "number" && Number.isInteger(available) && available >= 0 ? Math.min(available, generic?.max_parallel ?? available) : null, source: "harness_qualification" };
49
+ }
50
+ function nativeFailureController(context, projectId, runId, stage, parent) {
51
+ if (!context.db.get("SELECT 1 FROM sqlite_master WHERE type='table' AND name='run_controllers'"))
52
+ return null;
53
+ const rows = context.db.all("SELECT controller_id,generation,state_json FROM run_controllers WHERE project_id=? AND run_id=? AND status NOT IN ('completed','cancelled','superseded')", [projectId, runId]);
54
+ const matching = rows.filter(row => {
55
+ try {
56
+ const state = JSON.parse(row.state_json);
57
+ return state.stage === stage && state.sessions?.[state.current_session]?.id === parent;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ });
63
+ return matching.length === 1 ? matching[0] : null;
64
+ }
65
+ /** Failure metadata authored by a Work is not native evidence. Require the
66
+ * independently recorded reconciliation and its exact current registry links. */
67
+ export function verifiedNativeWorkFailure(context, input) {
68
+ const b = input.receipt.native_binding;
69
+ if (!b || b.schema_id !== "dd-flow/native-work-failure@1")
70
+ return;
71
+ const project = requireProjectByRoot(context, input.projectRoot);
72
+ const scope = stageGraphScope(context, project.id, input.runId, input.stage);
73
+ const controller = nativeFailureController(context, project.id, input.runId, input.stage, input.parentSessionId);
74
+ if (!controller || b.controller_id !== controller.controller_id || b.generation !== controller.generation
75
+ || b.project_id !== project.id || b.run_id !== input.runId || b.stage !== input.stage
76
+ || b.stage_attempt_id !== scope.attempt || b.stage_cycle !== scope.cycle
77
+ || b.work_id !== input.workId || b.parent_work_id !== input.parentWorkId
78
+ || b.parent_provider_session_id !== input.parentSessionId || b.terminal_turn_id !== input.receipt.terminal_turn_id
79
+ || input.receipt.source !== "native_child_observation" || input.receipt.outcome !== "failed")
80
+ return;
81
+ const link = context.db.get(`SELECT ws.id FROM work_sessions ws JOIN works w ON w.work_id=ws.work_id JOIN sessions s ON s.project_id=w.project_id AND s.session_id=ws.session_id
82
+ WHERE ws.id=? AND w.project_id=? AND w.run_id=? AND w.work_id=? AND w.parent_work_id=? AND ws.status='failed' AND w.status='failed' AND s.parent_session_id=? AND s.provider_session_id=? AND s.harness=?`, [b.work_session_id, project.id, input.runId, input.workId, input.parentWorkId, storageSessionId({ harness_id: b.harness_id, session_id: input.parentSessionId }), b.provider_session_id, b.harness_id]);
83
+ if (!link)
84
+ return;
85
+ const run = context.db.get("SELECT run_root FROM runs WHERE project_id=? AND id=?", [project.id, input.runId]);
86
+ try {
87
+ const events = fs.readFileSync(path.join(run.run_root, "timeline.jsonl"), "utf8").trim().split("\n");
88
+ const proof = events.some(line => {
89
+ const event = JSON.parse(line);
90
+ if (event.type !== "native_child_reconciliation" || event.stage !== input.stage)
91
+ return false;
92
+ const terminal = event.observations?.children?.find((child) => child.session_id === b.provider_session_id && child.terminal_turn_id === b.terminal_turn_id);
93
+ if (event.observations?.parent_session_id !== b.parent_provider_session_id || event.observations?.harness_id !== b.harness_id
94
+ || terminal?.status !== "failed" || terminal.terminal_observed_at !== input.receipt.terminal_observed_at
95
+ || JSON.stringify(safeNativeFailure(terminal.provider_error)) !== JSON.stringify(safeNativeFailure(input.receipt.provider_error)))
96
+ return false;
97
+ return event.reconciled?.some((entry) => entry.outcome === "failed"
98
+ && entry.native_binding && Object.keys(b).every(key => b[key] === entry.native_binding[key]));
99
+ });
100
+ return proof ? b : undefined;
101
+ }
102
+ catch {
103
+ return;
104
+ }
105
+ }
10
106
  export function fanoutState(input) {
11
107
  if (!input.hasWork)
12
108
  return input.dispatch === "none" ? "coordinator_required" : "dispatch_required";
@@ -18,7 +114,7 @@ export function fanoutState(input) {
18
114
  }
19
115
  export function fanoutFile(stageRoot) { return path.join(stageRoot, "fanout.json"); }
20
116
  export function writeFanoutDescriptor(stageRoot, descriptor) {
21
- const value = { schema_id: "dd-flow/work-fanout@1", kind: "work_fanout", ...descriptor };
117
+ const value = { schema_id: descriptor.scope ? "dd-flow/work-fanout@2" : "dd-flow/work-fanout@1", kind: "work_fanout", ...descriptor };
22
118
  fs.writeFileSync(fanoutFile(stageRoot), `${JSON.stringify(value, null, 2)}\n`);
23
119
  return value;
24
120
  }
@@ -27,31 +123,95 @@ export function readFanoutDescriptor(stageRoot) {
27
123
  if (!fs.existsSync(file))
28
124
  return null;
29
125
  const value = JSON.parse(fs.readFileSync(file, "utf8"));
30
- if (value.schema_id !== "dd-flow/work-fanout@1" || value.kind !== "work_fanout" || typeof value.stage !== "string" || typeof value.parent_work_id !== "string" || !["none", "plan-review"].includes(String(value.dispatch)) || typeof value.capacity_required !== "boolean") {
126
+ if (!["dd-flow/work-fanout@1", "dd-flow/work-fanout@2"].includes(String(value.schema_id)) || value.kind !== "work_fanout" || typeof value.stage !== "string" || typeof value.parent_work_id !== "string" || !["none", "plan-review"].includes(String(value.dispatch)) || typeof value.capacity_required !== "boolean" || value.schema_id === "dd-flow/work-fanout@2" && (!value.scope || [value.scope.project_id, value.scope.run_id, value.scope.attempt, value.scope.cycle].some(item => typeof item !== "string"))) {
31
127
  throw new AppError("runtime_missing", "Stage fan-out descriptor is invalid", 1, { file });
32
128
  }
33
129
  return value;
34
130
  }
131
+ /** Memory graphs are SQL-owned; a mutable/missing fanout.json is not their
132
+ * dispatch authority. Domain registrations and Work launch owners still fence
133
+ * productive assignments, including reviewer/correction waves.
134
+ */
135
+ export function memoryStageFanoutDescriptor(context, input) {
136
+ if (!["compose", "review"].includes(input.stage))
137
+ return null;
138
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
139
+ const run = context.db.get("SELECT flow_kind,index_json FROM runs WHERE project_id=? AND id=?", [project.id, input.runId]);
140
+ if (run?.flow_kind !== "mb-init")
141
+ return null;
142
+ const bundle = readFlowRunExecutionBundle(context, input);
143
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
144
+ throw new AppError("stage_graph_compatibility_required", "Memory graph requires its admitted RUN bundle", 1);
145
+ const index = JSON.parse(run.index_json);
146
+ if (index.current_stage !== input.stage || !index.stage_runs.some(stage => stage.stage === input.stage && stage.status === "running"))
147
+ return null;
148
+ const root = readDescriptorRootWork(context, { projectId: project.id, runId: input.runId }, bundle);
149
+ const scope = stageGraphScope(context, project.id, input.runId, input.stage), members = stageWorkIds(context, project.id, input.runId, input.stage);
150
+ if (root.status !== "running" || !members.has(root.workId))
151
+ throw new AppError("stage_graph_conflict", "Memory graph lost its active coordinator", 1);
152
+ for (const id of members) {
153
+ if (id === root.workId)
154
+ continue;
155
+ const work = context.db.get("SELECT parent_work_id,result_schema FROM works WHERE project_id=? AND run_id=? AND work_id=?", [project.id, input.runId, id]);
156
+ const schemas = input.stage === "compose" ? ["dd-flow/memory-composition-work-result@1"] : ["dd-flow/memory-review-work-result@1", "dd-flow/memory-repair-work-result@1"];
157
+ if (!work || work.parent_work_id !== root.workId || !schemas.includes(work.result_schema ?? ""))
158
+ throw new AppError("stage_graph_conflict", "Memory graph contains an unassigned Work", 1);
159
+ const custody = readWorkBatchCustody(context, id);
160
+ if (custody.scope.project_id !== scope.project_id || custody.scope.run_id !== scope.run_id || custody.scope.attempt !== scope.attempt
161
+ || custody.scope.cycle !== scope.cycle || custody.scope.stage !== input.stage || custody.scope.parent_work_id !== root.workId)
162
+ throw new AppError("stage_graph_conflict", "Memory graph contains foreign allocation custody", 1);
163
+ }
164
+ if (input.stage === "compose") {
165
+ const children = context.db.all("SELECT work_id FROM works WHERE project_id=? AND run_id=? AND parent_work_id=?", [project.id, input.runId, root.workId]).filter(work => members.has(work.work_id));
166
+ const registered = context.db.get("SELECT 1 FROM audit_events WHERE project_id=? AND event_type='memory.composition_registration' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.scope.attempt')=? AND json_extract(payload_json,'$.scope.cycle')=? LIMIT 1", [project.id, input.runId, scope.attempt, scope.cycle]);
167
+ if (children.length || registered) {
168
+ const registration = selectMemoryCompositionRegistration(context, { ...scope, stage: "compose", parent_work_id: root.workId });
169
+ const ids = new Set(Object.values(registration.work_ids));
170
+ if (ids.size !== children.length || children.some(work => !ids.has(work.work_id)))
171
+ throw new AppError("stage_graph_conflict", "Memory composition differs from its registered graph", 1);
172
+ }
173
+ }
174
+ return { schema_id: "dd-flow/work-fanout@2", kind: "work_fanout", scope, stage: input.stage, parent_work_id: root.workId, dispatch: "none", capacity_required: true };
175
+ }
35
176
  /** Read-only projection for an external orchestrator. dd-flow keeps the graph authority. */
36
177
  export function getVnextFanoutStatus(context, input) {
37
178
  const projectRoot = resolveProjectRoot(input.projectRoot);
38
179
  const project = requireProjectByRoot(context, projectRoot);
39
- const run = context.db.get("SELECT run_root FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
40
- if (!run?.run_root)
41
- throw new AppError("not_found", "RUN artifact root is unavailable", 1, { run_id: input.runId });
42
- const stageRoot = path.join(run.run_root, vnextStageDirectory(input.stage));
43
- const descriptor = readFanoutDescriptor(stageRoot);
180
+ const run = context.db.get("SELECT run_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
181
+ if (!run)
182
+ throw new AppError("not_found", "RUN is not registered", 1, { run_id: input.runId });
183
+ if (!run.run_root)
184
+ throw new AppError("runtime_artifact_missing", "RUN artifact root is unavailable", 1, { run_id: input.runId });
185
+ const index = JSON.parse(run.index_json);
186
+ const bundle = index.execution_bundle ? readFlowRunExecutionBundle(context, { projectRoot, runId: input.runId }) : null;
187
+ const stageRoot = path.join(run.run_root, bundle ? bundle.descriptor.stage(input.stage).directory : vnextStageDirectory(input.stage));
188
+ let descriptor = memoryStageFanoutDescriptor(context, input) ?? readFanoutDescriptor(stageRoot);
189
+ // Registration is authoritative even if projection publication was interrupted,
190
+ // or an otherwise review-off stage acquired an aggregate repair graph.
191
+ if (!descriptor && context.db.get("SELECT 1 FROM sqlite_master WHERE name = 'repair_intents'")) {
192
+ const members = stageWorkIds(context, project.id, input.runId, input.stage);
193
+ const parents = context.db.all("SELECT w.work_id, w.parent_work_id FROM repair_intents i JOIN works w ON w.work_id = i.work_id WHERE i.project_id = ? AND i.run_id = ? AND i.stage = ?", [project.id, input.runId, input.stage]).filter(work => members.has(work.work_id));
194
+ const roots = [...new Set(parents.map(work => work.parent_work_id))];
195
+ if (roots.length > 1)
196
+ throw new AppError("stage_graph_conflict", "Registered repairs disagree on their stage coordinator", 1);
197
+ if (roots.length === 1)
198
+ descriptor = { schema_id: "dd-flow/work-fanout@2", scope: stageGraphScope(context, project.id, input.runId, input.stage), kind: "work_fanout", stage: input.stage, parent_work_id: roots[0], dispatch: "none", capacity_required: true };
199
+ }
44
200
  if (!descriptor)
45
201
  return { ok: true, run_id: input.runId, stage: input.stage, orchestration: null };
46
202
  if (descriptor.stage !== input.stage)
47
203
  throw new AppError("runtime_missing", "Stage fan-out descriptor belongs to another stage", 1, { requested_stage: input.stage, descriptor_stage: descriptor.stage });
48
- const variables = getFlowRunVariables(context, { projectRoot, runId: input.runId });
49
- const available = variables.variables[subagentCapacityKey];
50
- const all = listWorks(context, { parentWorkId: descriptor.parent_work_id, includeResults: false });
51
- const ready = listWorks(context, { parentWorkId: descriptor.parent_work_id, ready: true, includeResults: false });
204
+ const capacity = getStageFanoutCapacity(context, { projectRoot, runId: input.runId, stage: input.stage });
205
+ const members = stageWorkIds(context, project.id, input.runId, input.stage);
206
+ const currentScope = stageGraphScope(context, project.id, input.runId, input.stage);
207
+ const compatible = descriptor.scope ? Object.entries(currentScope).every(([key, value]) => descriptor.scope[key] === value) : members.size > 0;
208
+ if (!compatible)
209
+ return { ok: true, run_id: input.runId, stage: input.stage, orchestration: { ...descriptor, state: "compatibility_required", compatibility: { code: "stage_graph_compatibility_required", message: "Legacy or stale stage graph has no authoritative current membership; use the pinned engine", expected_scope: currentScope } } };
210
+ const all = listWorks(context, { parentWorkId: descriptor.parent_work_id, includeResults: false, workIds: members });
211
+ const ready = { works: all.works.filter(work => work.ready) };
52
212
  const counts = Object.fromEntries(["created", "running", "completed", "failed", "cancelled"].map((status) => [status, all.works.filter((work) => work.status === status).length]));
53
213
  const hasWork = all.works.length > 0;
54
- const capacityKnown = typeof available === "number" && Number.isInteger(available) && available >= 0;
214
+ const capacityKnown = capacity.available_slots !== null;
55
215
  return {
56
216
  ok: true,
57
217
  run_id: input.runId,
@@ -62,11 +222,143 @@ export function getVnextFanoutStatus(context, input) {
62
222
  // not: their coordinator first materializes the Work graph from its
63
223
  // authoritative prompt. An empty graph is therefore not a success.
64
224
  state: fanoutState({ dispatch: descriptor.dispatch, hasWork, capacityRequired: descriptor.capacity_required, capacityKnown, created: counts.created ?? 0, running: counts.running ?? 0 }),
65
- capacity: {
66
- run_key: subagentCapacityKey,
67
- available_slots: capacityKnown ? available : null
68
- },
69
- works: { ...counts, ready: ready.works.map((work) => ({ work_id: work.work_id, task: work.task, start_command: work.start_command })) }
225
+ capacity,
226
+ graph_revision: all.works.map(work => ({ work_id: work.work_id, status: work.status, depends_on: work.depends_on })),
227
+ works: { ...counts, ready: ready.works.map((work) => ({ work_id: work.work_id, task: work.task, launch_policy: work.launch_policy, start_command: work.start_command })) }
70
228
  }
71
229
  };
72
230
  }
231
+ /** Explicit controller mutation, separate from read-only fanout status.
232
+ * A native child may be mapped only by its registered identity. Reused native
233
+ * Sessions need a provider execution identity: without one we report ambiguity
234
+ * instead of applying a late event to the newest Work attempt. */
235
+ export async function reconcileVnextFanout(context, input) {
236
+ const value = prepareNativeObservations(input.observations);
237
+ const status = getVnextFanoutStatus(context, input);
238
+ if (!status.orchestration)
239
+ return { ok: true, issues: [], reconciled: [] };
240
+ if (status.orchestration.state === "compatibility_required")
241
+ throw new AppError("stage_graph_compatibility_required", "Cannot reconcile a legacy or stale graph", 1);
242
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
243
+ const members = stageWorkIds(context, project.id, input.runId, input.stage);
244
+ const parent = storageSessionId({ harness_id: value.harness_id, session_id: value.parent_session_id });
245
+ const issues = [], reconciled = [];
246
+ for (const child of value.children) {
247
+ if (child.provenance === "parent_mismatch" || child.parent_session_id !== undefined && child.parent_session_id !== value.parent_session_id) {
248
+ issues.push({ code: "native_child_parent_mismatch", session_id: child.session_id, observed_parent_session_id: child.parent_session_id ?? null, expected_parent_session_id: value.parent_session_id, outcome: child.status });
249
+ continue;
250
+ }
251
+ if (child.status === "running" || child.status === "unknown")
252
+ continue;
253
+ const sessionId = storageSessionId({ harness_id: value.harness_id, session_id: String(child.session_id) });
254
+ const links = context.db.all(`SELECT ws.id, ws.work_id, ws.status, w.status AS work_status, w.parent_work_id FROM work_sessions ws JOIN works w ON w.work_id = ws.work_id JOIN sessions s ON s.project_id = w.project_id AND s.session_id = ws.session_id WHERE w.project_id = ? AND w.run_id = ? AND ws.session_id = ? AND s.parent_session_id = ? ORDER BY ws.created_at`, [project.id, input.runId, sessionId, parent]);
255
+ const active = links.filter(link => link.status === "running" && link.work_status === "running" && link.parent_work_id === status.orchestration.parent_work_id && members.has(link.work_id));
256
+ if (!active.length) {
257
+ if (!links.length)
258
+ issues.push({ code: "child_launch_association_missing", session_id: child.session_id, outcome: child.status });
259
+ continue;
260
+ }
261
+ if (links.length !== 1 || active.length !== 1) {
262
+ issues.push({ code: "child_execution_ambiguous", session_id: child.session_id, work_ids: active.map(link => link.work_id) });
263
+ continue;
264
+ }
265
+ const link = active[0];
266
+ if (renderNativeChildFollowupInstruction(String(value.harness_id), String(child.session_id)) && eligibleProviderChildOverload(child, value.parent_session_id)) {
267
+ issues.push({ code: "child_capacity_pending", work_id: link.work_id, session_id: child.session_id });
268
+ continue;
269
+ }
270
+ const pendingCheck = () => context.db.get("SELECT 1 FROM check_receipts WHERE project_id = ? AND run_id = ? AND work_id = ? AND status = 'running' LIMIT 1", [project.id, input.runId, link.work_id]);
271
+ if (pendingCheck()) {
272
+ const run = context.db.get("SELECT workspace_root, run_root FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
273
+ try {
274
+ reconcileUnfinishedChecks(context, { projectId: project.id, runId: input.runId, workspaceRoot: run.workspace_root, runHome: run.run_root, workId: link.work_id });
275
+ }
276
+ catch (error) {
277
+ if (!(error instanceof AppError) || error.code !== "check_in_progress")
278
+ throw error;
279
+ }
280
+ if (pendingCheck()) {
281
+ issues.push({ code: "child_check_in_progress", work_id: link.work_id });
282
+ continue;
283
+ }
284
+ }
285
+ if (context.db.get("SELECT 1 FROM works WHERE parent_work_id = ? AND status IN ('created','running','paused') LIMIT 1", [link.work_id])) {
286
+ issues.push({ code: "child_descendants_unsettled", work_id: link.work_id });
287
+ continue;
288
+ }
289
+ if (child.status === "settled_by_root") {
290
+ issues.push({ code: "child_execution_outcome_unconfirmed", work_id: link.work_id, session_id: child.session_id });
291
+ continue;
292
+ }
293
+ if (child.status === "completed") {
294
+ issues.push({ code: "execution_ended_without_work_result", work_id: link.work_id, session_id: child.session_id });
295
+ continue;
296
+ }
297
+ const providerError = safeNativeFailure(child.provider_error);
298
+ const controller = nativeFailureController(context, project.id, input.runId, input.stage, value.parent_session_id);
299
+ const scope = stageGraphScope(context, project.id, input.runId, input.stage);
300
+ const terminalTime = typeof child.terminal_observed_at === "string" ? Date.parse(child.terminal_observed_at) : NaN;
301
+ const nativeBinding = child.status === "failed" && controller && typeof child.terminal_turn_id === "string" && child.terminal_turn_id.length > 0 && child.terminal_turn_id.length <= 256 && Number.isFinite(terminalTime)
302
+ ? { schema_id: "dd-flow/native-work-failure@1", project_id: project.id, run_id: input.runId, stage: input.stage, stage_attempt_id: scope.attempt, stage_cycle: scope.cycle,
303
+ parent_work_id: status.orchestration.parent_work_id, work_id: link.work_id, work_session_id: link.id, harness_id: value.harness_id,
304
+ parent_provider_session_id: value.parent_session_id, provider_session_id: String(child.session_id), terminal_turn_id: child.terminal_turn_id,
305
+ controller_id: controller.controller_id, generation: controller.generation } : undefined;
306
+ const reason = JSON.stringify({ source: "native_child_observation", harness_id: value.harness_id, session_id: child.session_id, work_session_id: link.id, outcome: child.status,
307
+ ...(nativeBinding ? { native_binding: nativeBinding } : {}),
308
+ ...(typeof child.terminal_turn_id === "string" ? { terminal_turn_id: child.terminal_turn_id } : {}),
309
+ ...(typeof child.terminal_observed_at === "string" ? { terminal_observed_at: child.terminal_observed_at } : {}),
310
+ ...(providerError ? { provider_error: providerError } : {}) });
311
+ const result = await (child.status === "failed" ? failWork : cancelWork)(context, link.work_id, reason);
312
+ reconciled.push({ work_id: link.work_id, work_session_id: link.id, outcome: child.status, ...(nativeBinding ? { native_binding: nativeBinding } : {}), result });
313
+ }
314
+ appendFlowRunTimelineEvent(context, project.id, input.runId, { type: "native_child_reconciliation", stage: input.stage, observations: value, issues, reconciled });
315
+ return { ok: true, issues, reconciled };
316
+ }
317
+ /**
318
+ * Records every terminal native child at the Stage where it is first observed.
319
+ * This is separate from fan-out settlement: a historical child can be valid
320
+ * evidence for an earlier Work, while an unbound terminal child is retained
321
+ * as an inconsistency instead of being rediscovered in a later review wave.
322
+ */
323
+ export function prepareNativeObservations(observations) {
324
+ const value = observations;
325
+ if (!value || typeof value.harness_id !== "string" || !value.harness_id.trim() || typeof value.parent_session_id !== "string" || !value.parent_session_id.trim() || !Array.isArray(value.children)
326
+ || value.children.some(child => !child || typeof child.session_id !== "string" || !child.session_id.trim() || typeof child.status !== "string" || !["running", "unknown", "completed", "failed", "cancelled", "settled_by_root"].includes(child.status) || child.parent_session_id !== undefined && typeof child.parent_session_id !== "string" || child.provenance !== undefined && !["root_child", "parent_mismatch"].includes(String(child.provenance))))
327
+ throw new AppError("usage", "Expected native observations: harness_id, parent_session_id and children[{session_id,status}]", 2);
328
+ if (new Set(value.children.map(child => child.session_id)).size !== value.children.length)
329
+ throw new AppError("usage", "Native observations must contain each child Session exactly once", 2);
330
+ return { harness_id: value.harness_id, parent_session_id: value.parent_session_id, children: value.children };
331
+ }
332
+ export function observeVnextNativeChildren(context, input) {
333
+ const value = prepareNativeObservations(input.observations);
334
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
335
+ const parent = storageSessionId({ harness_id: value.harness_id, session_id: value.parent_session_id });
336
+ const issues = [], history = [], pending = [];
337
+ for (const child of value.children) {
338
+ if (child.provenance === "parent_mismatch" || child.parent_session_id !== undefined && child.parent_session_id !== value.parent_session_id) {
339
+ issues.push({ code: "native_child_parent_mismatch", session_id: child.session_id, observed_parent_session_id: child.parent_session_id ?? null, expected_parent_session_id: value.parent_session_id, outcome: child.status, detected_at_stage: input.stage });
340
+ continue;
341
+ }
342
+ const sessionId = storageSessionId({ harness_id: value.harness_id, session_id: String(child.session_id) });
343
+ const links = context.db.all(`SELECT ws.id, ws.work_id, ws.status, w.status AS work_status, w.parent_work_id, s.current_stage
344
+ FROM work_sessions ws JOIN works w ON w.work_id = ws.work_id
345
+ JOIN sessions s ON s.project_id = w.project_id AND s.session_id = ws.session_id
346
+ WHERE w.project_id = ? AND w.run_id = ? AND ws.session_id = ? AND s.parent_session_id = ? ORDER BY ws.created_at`, [project.id, input.runId, sessionId, parent]);
347
+ if (child.status === "running" || child.status === "unknown") {
348
+ pending.push({ session_id: child.session_id, outcome: child.status, links: links.map(link => ({ work_id: link.work_id, work_session_id: link.id })) });
349
+ continue;
350
+ }
351
+ if (!links.length) {
352
+ issues.push({ code: "unbound_native_delegation_detected", session_id: child.session_id, outcome: child.status, detected_at_stage: input.stage, origin_stage: "unknown" });
353
+ continue;
354
+ }
355
+ if (links.length !== 1) {
356
+ issues.push({ code: "child_execution_ambiguous", session_id: child.session_id, outcome: child.status, detected_at_stage: input.stage, work_ids: links.map(link => link.work_id) });
357
+ continue;
358
+ }
359
+ const link = links[0];
360
+ history.push({ session_id: child.session_id, outcome: child.status, work_id: link.work_id, work_session_id: link.id, work_status: link.work_status, origin_stage: link.current_stage ?? "unknown", detected_at_stage: input.stage });
361
+ }
362
+ appendFlowRunTimelineEvent(context, project.id, input.runId, { type: "native_child_observation", stage: input.stage, observations: value, issues, history, pending });
363
+ return { ok: true, issues, history, pending };
364
+ }