@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.161

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 (670) hide show
  1. package/CHANGELOG.md +965 -0
  2. package/README.md +139 -10
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +417 -0
  6. package/dist/cli/command-metadata.js +303 -0
  7. package/dist/cli/command-option-descriptions.js +192 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/delivery.js +108 -0
  11. package/dist/cli/diagnostic-invocation.js +62 -0
  12. package/dist/cli/diagnostic.js +131 -0
  13. package/dist/cli/error-output.js +62 -0
  14. package/dist/cli/executor-onboarding.js +76 -0
  15. package/dist/cli/flows.js +29 -0
  16. package/dist/cli/harnesses.js +84 -0
  17. package/dist/cli/help.js +521 -19
  18. package/dist/cli/hook-ingress.js +89 -0
  19. package/dist/cli/input-preparation.js +193 -0
  20. package/dist/cli/interactive-invocation.js +33 -0
  21. package/dist/cli/interactive.js +114 -0
  22. package/dist/cli/memory-fix-invocation.js +52 -0
  23. package/dist/cli/memory-fix.js +100 -0
  24. package/dist/cli/memory-flow-admission.js +54 -0
  25. package/dist/cli/memory-init-invocation.js +85 -0
  26. package/dist/cli/memory-init.js +250 -0
  27. package/dist/cli/memory-storage-admission.js +54 -0
  28. package/dist/cli/memory-storage-qualification.js +105 -0
  29. package/dist/cli/memory-storage-transition.js +123 -0
  30. package/dist/cli/memory-upgrade.js +189 -0
  31. package/dist/cli/models.js +39 -0
  32. package/dist/cli/native-hook-ingress.js +94 -0
  33. package/dist/cli/native-hook-preparation.js +82 -0
  34. package/dist/cli/operator-profiles.js +61 -0
  35. package/dist/cli/output-options.js +35 -0
  36. package/dist/cli/response-file.js +72 -0
  37. package/dist/cli/run-cli.js +2020 -555
  38. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  39. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  40. package/dist/cli/runtime-process.js +153 -0
  41. package/dist/cli/setup.js +150 -0
  42. package/dist/cli.js +103 -2
  43. package/dist/domain/delivery-flows.js +8 -0
  44. package/dist/domain/diagnostic-aspects.js +103 -0
  45. package/dist/domain/diagnostic-flows.js +8 -0
  46. package/dist/domain/distill-flow.js +20 -0
  47. package/dist/domain/interactive-flow.js +1 -0
  48. package/dist/domain/memory-fix-flow.js +2 -0
  49. package/dist/domain/review-fix-flow.js +2 -0
  50. package/dist/domain/source-snapshot-flows.js +4 -0
  51. package/dist/domain/stage-catalog.js +1 -1
  52. package/dist/domain/stage-handler-identities.js +28 -0
  53. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  54. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  55. package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
  56. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  57. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  58. package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
  59. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  60. package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
  61. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  62. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  63. package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
  64. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  65. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  66. package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
  67. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  68. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  69. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
  70. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  71. package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
  72. package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
  73. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  74. package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
  75. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
  76. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
  77. package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
  78. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  79. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
  80. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  81. package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
  82. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
  83. package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
  84. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  85. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
  86. package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
  87. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
  88. package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
  89. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  90. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
  91. package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
  92. package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
  93. package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
  94. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  95. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  96. package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
  97. package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
  98. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  99. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  100. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  101. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  102. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  103. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  104. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
  105. package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
  106. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  107. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  108. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  109. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  110. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  111. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  112. package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
  113. package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
  114. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  115. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  116. package/dist/harness-runtime/lib/native-streams.mjs +38 -0
  117. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  118. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  119. package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
  120. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  121. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  122. package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
  123. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  124. package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
  125. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  126. package/dist/harness-runtime/lib/process-json.mjs +200 -0
  127. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  128. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  129. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  130. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  131. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  132. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  133. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  134. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  135. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  136. package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
  137. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  138. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  147. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  148. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  149. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  150. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  151. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  152. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  154. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  155. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  156. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  157. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  158. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  159. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  160. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  161. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  162. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  163. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  164. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  165. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  167. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  168. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  169. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  171. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  172. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  173. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  175. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  176. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  177. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  178. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  179. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  180. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  181. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  182. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  183. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  184. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  186. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  187. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  188. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  189. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  190. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  191. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  192. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  193. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  194. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  195. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  196. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  197. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  199. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  200. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  201. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  202. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  203. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  204. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  205. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  206. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  207. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  208. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  209. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  210. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  211. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  212. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  213. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  214. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  215. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  216. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  217. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  218. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  219. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  220. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  221. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  222. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  223. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  224. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  225. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  226. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  227. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  228. package/dist/protocol/local-files.js +18 -9
  229. package/dist/runtime/context.js +8 -4
  230. package/dist/schemas/agent-profile.schema.json +1 -1
  231. package/dist/schemas/code-review-decision.schema.json +9 -0
  232. package/dist/schemas/code-review-result.schema.json +1 -1
  233. package/dist/schemas/code-verification.schema.json +7 -5
  234. package/dist/schemas/code-work-batch.schema.json +2 -2
  235. package/dist/schemas/code-work-result.schema.json +2 -1
  236. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  237. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  238. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  239. package/dist/schemas/discovery-result.schema.json +184 -0
  240. package/dist/schemas/distill-stage-result.schema.json +667 -0
  241. package/dist/schemas/distill-work-result.schema.json +428 -0
  242. package/dist/schemas/execution-bundle.schema.json +215 -0
  243. package/dist/schemas/execution-settings.schema.json +162 -0
  244. package/dist/schemas/flow-descriptor.schema.json +54 -0
  245. package/dist/schemas/flow-run.schema.json +29 -0
  246. package/dist/schemas/harness-catalog.schema.json +80 -0
  247. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  248. package/dist/schemas/interactive-work-result.schema.json +63 -0
  249. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  250. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  251. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  252. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  253. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  254. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  255. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  256. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  257. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  258. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  259. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  260. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  261. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  262. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  263. package/dist/schemas/model-catalog.schema.json +153 -0
  264. package/dist/schemas/plan-review-decision.schema.json +12 -3
  265. package/dist/schemas/plan-review-result.schema.json +2 -2
  266. package/dist/schemas/profile-settings.schema.json +122 -0
  267. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  268. package/dist/schemas/release-impact.schema.json +3 -3
  269. package/dist/schemas/release-manifest.schema.json +291 -0
  270. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  271. package/dist/schemas/run-control-receipt.schema.json +119 -0
  272. package/dist/schemas/run-control-request.schema.json +36 -0
  273. package/dist/schemas/run-engine-binding.schema.json +1 -1
  274. package/dist/schemas/stage-start-response.schema.json +4 -0
  275. package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
  276. package/dist/services/accepted-intent.js +139 -0
  277. package/dist/services/assigned-lifecycle.js +382 -0
  278. package/dist/services/branch-context.js +3 -3
  279. package/dist/services/canon.js +54 -15
  280. package/dist/services/check-environment.js +21 -0
  281. package/dist/services/check-runner-evidence.js +344 -0
  282. package/dist/services/check-runner-proof.js +373 -0
  283. package/dist/services/cleanup.js +133 -40
  284. package/dist/services/cli-operation-classifier.js +32 -64
  285. package/dist/services/cli-upgrade-allowlist.js +27 -0
  286. package/dist/services/code-checks.js +1426 -121
  287. package/dist/services/codex-capacity.js +367 -0
  288. package/dist/services/codex-hook-delivery.js +41 -0
  289. package/dist/services/command-context.js +80 -0
  290. package/dist/services/compatibility-preflight.js +16 -3
  291. package/dist/services/config.js +11 -8
  292. package/dist/services/continuation-outcome.js +25 -0
  293. package/dist/services/controller-child-capacity.js +71 -0
  294. package/dist/services/controller-child-observer.js +121 -0
  295. package/dist/services/controller-child-repair.js +117 -0
  296. package/dist/services/controller-execution.js +97 -0
  297. package/dist/services/controller-fanout.js +277 -0
  298. package/dist/services/controller-memory-task.js +45 -0
  299. package/dist/services/controller-memory-worker.js +51 -0
  300. package/dist/services/controller-result-authority.js +127 -0
  301. package/dist/services/controller-work-failure.js +198 -0
  302. package/dist/services/dashboard.js +145 -58
  303. package/dist/services/delivery-access.js +123 -0
  304. package/dist/services/delivery-approval.js +68 -0
  305. package/dist/services/delivery-artifact.js +86 -0
  306. package/dist/services/delivery-checks.js +178 -0
  307. package/dist/services/delivery-execution.js +181 -0
  308. package/dist/services/delivery-intents.js +212 -0
  309. package/dist/services/delivery-operation.js +81 -0
  310. package/dist/services/delivery-policy.js +91 -0
  311. package/dist/services/delivery-procedures.js +43 -0
  312. package/dist/services/delivery-request.js +99 -0
  313. package/dist/services/delivery-result.js +144 -0
  314. package/dist/services/delivery-stage-commands.js +26 -0
  315. package/dist/services/delivery-stage-entry.js +56 -0
  316. package/dist/services/delivery-stage-handlers.js +135 -0
  317. package/dist/services/delivery-stage-results.js +94 -0
  318. package/dist/services/descriptor-coordinator-session.js +37 -0
  319. package/dist/services/descriptor-root-work.js +53 -0
  320. package/dist/services/descriptor-run-settlement.js +107 -0
  321. package/dist/services/descriptor-run-start.js +495 -0
  322. package/dist/services/descriptor-stage-coordinator.js +318 -0
  323. package/dist/services/descriptor-stage-settlement.js +268 -0
  324. package/dist/services/descriptor-stage-start.js +175 -0
  325. package/dist/services/diagnostic-evidence.js +73 -0
  326. package/dist/services/diagnostic-repair-intake.js +123 -0
  327. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  328. package/dist/services/diagnostic-report-publication.js +72 -0
  329. package/dist/services/diagnostic-report.js +97 -0
  330. package/dist/services/diagnostic-run-bundle.js +113 -0
  331. package/dist/services/diagnostic-snapshot.js +159 -0
  332. package/dist/services/diagnostic-stage-handlers.js +109 -0
  333. package/dist/services/diagnostic-stage-results.js +158 -0
  334. package/dist/services/diagnostic-start.js +134 -0
  335. package/dist/services/diagnostic-work-batch.js +34 -0
  336. package/dist/services/diagnostic-work-collection.js +97 -0
  337. package/dist/services/diagnostic-work-registration.js +101 -0
  338. package/dist/services/diagnostic-work-results.js +93 -0
  339. package/dist/services/discovery/agy-discovery.js +2 -0
  340. package/dist/services/discovery/codex-discovery.js +2 -0
  341. package/dist/services/discovery/droid-discovery.js +2 -0
  342. package/dist/services/discovery/grok-discovery.js +2 -0
  343. package/dist/services/discovery/opencode-discovery.js +2 -0
  344. package/dist/services/discovery/static.js +31 -0
  345. package/dist/services/discovery/zcode-discovery.js +2 -0
  346. package/dist/services/distill-document-publication.js +280 -0
  347. package/dist/services/distill-promotion.js +197 -0
  348. package/dist/services/distill-publication-custody.js +162 -0
  349. package/dist/services/distill-stage-handlers.js +249 -0
  350. package/dist/services/distill-stage-results.js +105 -0
  351. package/dist/services/distill-user-inputs.js +4 -0
  352. package/dist/services/distill-work-batch.js +42 -0
  353. package/dist/services/distill-work-collection.js +74 -0
  354. package/dist/services/distill-work-registration.js +93 -0
  355. package/dist/services/distill-work-results.js +98 -0
  356. package/dist/services/engines.js +354 -163
  357. package/dist/services/eval-snapshots.js +1326 -150
  358. package/dist/services/execution-bundles.js +275 -0
  359. package/dist/services/execution-policy.js +185 -0
  360. package/dist/services/executor-onboarding.js +56 -0
  361. package/dist/services/external-work-launch.js +316 -0
  362. package/dist/services/flow-catalog.js +54 -0
  363. package/dist/services/flow-descriptors.js +82 -0
  364. package/dist/services/frozen-engine.js +404 -0
  365. package/dist/services/generic-execution.js +165 -0
  366. package/dist/services/harness-adapter.js +342 -35
  367. package/dist/services/harness-config.js +2 -0
  368. package/dist/services/harness-discovery.js +29 -0
  369. package/dist/services/harness-doctor.js +75 -0
  370. package/dist/services/harness-probe-effects.js +51 -0
  371. package/dist/services/harness-version-probe.js +100 -0
  372. package/dist/services/hook-memory-routing.js +188 -0
  373. package/dist/services/hook-recovery-guard.js +14 -0
  374. package/dist/services/hook-session-observation.js +24 -0
  375. package/dist/services/hooks.js +813 -327
  376. package/dist/services/hybrid-contracts.js +479 -0
  377. package/dist/services/hybrid-digest.js +14 -0
  378. package/dist/services/ids.js +29 -1
  379. package/dist/services/installation-lock.js +95 -0
  380. package/dist/services/installed-flow-source.js +261 -0
  381. package/dist/services/interactive-attached-session.js +215 -0
  382. package/dist/services/interactive-baseline-custody.js +54 -0
  383. package/dist/services/interactive-before-versions.js +148 -0
  384. package/dist/services/interactive-bundle-data.js +85 -0
  385. package/dist/services/interactive-checks.js +179 -0
  386. package/dist/services/interactive-execution.js +33 -0
  387. package/dist/services/interactive-finish-integration.js +236 -0
  388. package/dist/services/interactive-git-baseline.js +230 -0
  389. package/dist/services/interactive-git-dispatch.js +278 -0
  390. package/dist/services/interactive-input-delivery.js +82 -0
  391. package/dist/services/interactive-mutation-scope.js +231 -0
  392. package/dist/services/interactive-request.js +287 -0
  393. package/dist/services/interactive-run-bundle.js +109 -0
  394. package/dist/services/interactive-stage-entry.js +41 -0
  395. package/dist/services/interactive-stage-handlers.js +289 -0
  396. package/dist/services/interactive-stage-observation.js +136 -0
  397. package/dist/services/interactive-stage-results.js +111 -0
  398. package/dist/services/interactive-user-inputs.js +142 -0
  399. package/dist/services/interactive-work-batch.js +43 -0
  400. package/dist/services/interactive-work-collection.js +119 -0
  401. package/dist/services/interactive-work-completion.js +180 -0
  402. package/dist/services/interactive-work-context.js +249 -0
  403. package/dist/services/interactive-work-history.js +112 -0
  404. package/dist/services/interactive-work-registration.js +124 -0
  405. package/dist/services/interactive-work-results.js +115 -0
  406. package/dist/services/interactive-work-start.js +155 -0
  407. package/dist/services/lanes.js +78 -65
  408. package/dist/services/lifecycle-command.js +1 -288
  409. package/dist/services/lifecycle-contract.js +1 -0
  410. package/dist/services/lifecycle-invocations.js +2549 -0
  411. package/dist/services/managed-daemon-binding.js +129 -0
  412. package/dist/services/managed-lease-monitor.js +133 -0
  413. package/dist/services/managed-processes.js +449 -69
  414. package/dist/services/memory-accepted-composition.js +78 -0
  415. package/dist/services/memory-baseline-preservation.js +65 -0
  416. package/dist/services/memory-completed-correction-wave.js +328 -0
  417. package/dist/services/memory-compose-handler.js +380 -0
  418. package/dist/services/memory-composition-acceptance.js +375 -0
  419. package/dist/services/memory-composition-graph.js +165 -0
  420. package/dist/services/memory-composition-registration.js +161 -0
  421. package/dist/services/memory-composition-work-custody.js +156 -0
  422. package/dist/services/memory-composition-work-results.js +108 -0
  423. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  424. package/dist/services/memory-corrected-review-batch.js +37 -0
  425. package/dist/services/memory-corrected-review-findings.js +108 -0
  426. package/dist/services/memory-corrected-review-inputs.js +215 -0
  427. package/dist/services/memory-corrected-review-projection.js +78 -0
  428. package/dist/services/memory-corrected-review-receipt.js +178 -0
  429. package/dist/services/memory-corrected-review-registration.js +192 -0
  430. package/dist/services/memory-correction-check-receipt.js +235 -0
  431. package/dist/services/memory-correction-input-owner.js +6 -0
  432. package/dist/services/memory-custody-snapshot.js +36 -0
  433. package/dist/services/memory-discover-handler.js +104 -0
  434. package/dist/services/memory-finish-evidence.js +457 -0
  435. package/dist/services/memory-finish-handler.js +229 -0
  436. package/dist/services/memory-finish-integration.js +491 -0
  437. package/dist/services/memory-finish-readiness.js +169 -0
  438. package/dist/services/memory-fix-finish-integration.js +129 -0
  439. package/dist/services/memory-fix-git-dispatch.js +139 -0
  440. package/dist/services/memory-fix-intake-custody.js +11 -0
  441. package/dist/services/memory-fix-intake-settlement.js +127 -0
  442. package/dist/services/memory-fix-publication.js +196 -0
  443. package/dist/services/memory-fix-repair-admission.js +116 -0
  444. package/dist/services/memory-fix-request.js +141 -0
  445. package/dist/services/memory-fix-run-bundle.js +83 -0
  446. package/dist/services/memory-fix-scope.js +78 -0
  447. package/dist/services/memory-fix-sources.js +85 -0
  448. package/dist/services/memory-fix-stage-entry.js +24 -0
  449. package/dist/services/memory-fix-stage-handlers.js +282 -0
  450. package/dist/services/memory-fix-stage-results.js +106 -0
  451. package/dist/services/memory-fix-workspace.js +127 -0
  452. package/dist/services/memory-flow-user-inputs.js +88 -0
  453. package/dist/services/memory-git-integration.js +220 -0
  454. package/dist/services/memory-historical-corrected-review.js +212 -0
  455. package/dist/services/memory-historical-original-review.js +143 -0
  456. package/dist/services/memory-historical-repair-registration.js +105 -0
  457. package/dist/services/memory-historical-second-review.js +147 -0
  458. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  459. package/dist/services/memory-init-contexts.js +181 -0
  460. package/dist/services/memory-init-controller-start.js +119 -0
  461. package/dist/services/memory-init-engine-release.js +223 -0
  462. package/dist/services/memory-init-git-policy.js +165 -0
  463. package/dist/services/memory-init-lint-capability.js +203 -0
  464. package/dist/services/memory-init-native-capacity.js +309 -0
  465. package/dist/services/memory-init-preflight.js +334 -0
  466. package/dist/services/memory-init-project-bootstrap.js +599 -0
  467. package/dist/services/memory-init-request-candidate.js +213 -0
  468. package/dist/services/memory-init-request-input.js +82 -0
  469. package/dist/services/memory-init-resource-storage.js +125 -0
  470. package/dist/services/memory-init-run-allocation.js +81 -0
  471. package/dist/services/memory-init-run-bundle.js +135 -0
  472. package/dist/services/memory-init-start.js +181 -0
  473. package/dist/services/memory-init-startup-custody.js +80 -0
  474. package/dist/services/memory-init-startup-input.js +240 -0
  475. package/dist/services/memory-init-storage.js +91 -0
  476. package/dist/services/memory-init-stores.js +359 -0
  477. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  478. package/dist/services/memory-layer-publication.js +611 -0
  479. package/dist/services/memory-layer.js +225 -0
  480. package/dist/services/memory-lint-config.js +194 -0
  481. package/dist/services/memory-native-review-copy.js +127 -0
  482. package/dist/services/memory-normal-flow-entry.js +87 -0
  483. package/dist/services/memory-ordinary-defaults.js +129 -0
  484. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  485. package/dist/services/memory-preflight-handler.js +274 -0
  486. package/dist/services/memory-repair-current-view.js +299 -0
  487. package/dist/services/memory-repair-document-delta.js +489 -0
  488. package/dist/services/memory-repair-history.js +496 -0
  489. package/dist/services/memory-repair-physical-binding.js +194 -0
  490. package/dist/services/memory-repair-publication-files.js +152 -0
  491. package/dist/services/memory-repair-publication-intent.js +27 -0
  492. package/dist/services/memory-repair-publication-plan.js +162 -0
  493. package/dist/services/memory-repair-publication-source.js +225 -0
  494. package/dist/services/memory-repair-publication.js +795 -0
  495. package/dist/services/memory-repair-registration.js +240 -0
  496. package/dist/services/memory-repair-work-baseline.js +415 -0
  497. package/dist/services/memory-repair-work-completion.js +117 -0
  498. package/dist/services/memory-repair-work-custody.js +73 -0
  499. package/dist/services/memory-repair-work-results.js +108 -0
  500. package/dist/services/memory-repair-workspace.js +261 -0
  501. package/dist/services/memory-repair-writer-copy.js +736 -0
  502. package/dist/services/memory-result-binding.js +239 -0
  503. package/dist/services/memory-retained-review-authority.js +138 -0
  504. package/dist/services/memory-retained-review-results.js +70 -0
  505. package/dist/services/memory-retained-second-baseline.js +102 -0
  506. package/dist/services/memory-retained-second-repairs.js +162 -0
  507. package/dist/services/memory-review-acceptance.js +214 -0
  508. package/dist/services/memory-review-correction-plan.js +206 -0
  509. package/dist/services/memory-review-handler.js +585 -0
  510. package/dist/services/memory-review-inputs.js +246 -0
  511. package/dist/services/memory-review-limits.js +10 -0
  512. package/dist/services/memory-review-receipt.js +180 -0
  513. package/dist/services/memory-review-registration.js +117 -0
  514. package/dist/services/memory-review-work-custody.js +126 -0
  515. package/dist/services/memory-review-work-input.js +91 -0
  516. package/dist/services/memory-review-work-results.js +95 -0
  517. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  518. package/dist/services/memory-second-correction-plan.js +77 -0
  519. package/dist/services/memory-second-repair-baseline.js +91 -0
  520. package/dist/services/memory-second-repair-launch.js +614 -0
  521. package/dist/services/memory-second-repair-registration.js +88 -0
  522. package/dist/services/memory-second-repair-source.js +187 -0
  523. package/dist/services/memory-second-review-acceptance.js +115 -0
  524. package/dist/services/memory-source-observations.js +117 -0
  525. package/dist/services/memory-stage-dispatch.js +65 -0
  526. package/dist/services/memory-stage-results.js +290 -0
  527. package/dist/services/memory-tool-probe.js +25 -0
  528. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  529. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  530. package/dist/services/memory-upgrade-evidence.js +100 -0
  531. package/dist/services/memory-upgrade-inventory.js +442 -0
  532. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  533. package/dist/services/memory-upgrade-publication.js +413 -0
  534. package/dist/services/memory-upgrade-selection.js +430 -0
  535. package/dist/services/memory-upgrade-start.js +60 -0
  536. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  537. package/dist/services/memory-verification-acceptance.js +156 -0
  538. package/dist/services/memory-verification-checks.js +317 -0
  539. package/dist/services/memory-verification-receipt.js +160 -0
  540. package/dist/services/memory-verify-handler.js +137 -0
  541. package/dist/services/memory-work-result-json.js +12 -0
  542. package/dist/services/memory-work-result-preparation.js +64 -0
  543. package/dist/services/merge-queue.js +228 -149
  544. package/dist/services/merge-server.js +163 -35
  545. package/dist/services/migrations.js +164 -114
  546. package/dist/services/model-catalog.js +109 -0
  547. package/dist/services/model-discovery.js +46 -0
  548. package/dist/services/native-daemon-history.js +50 -0
  549. package/dist/services/native-session-control.js +39 -0
  550. package/dist/services/operator-profiles.js +236 -0
  551. package/dist/services/physical-startup.js +45 -0
  552. package/dist/services/plan-runtime.js +26 -11
  553. package/dist/services/plans.js +28 -25
  554. package/dist/services/project-flow-source.js +195 -0
  555. package/dist/services/project-git-integration.js +92 -0
  556. package/dist/services/project-registry.js +97 -0
  557. package/dist/services/project-summary.js +1 -1
  558. package/dist/services/projects.js +17 -87
  559. package/dist/services/prompt-delivery.js +71 -0
  560. package/dist/services/prompts.js +28 -17
  561. package/dist/services/protocols.js +163 -87
  562. package/dist/services/provider-capacity.js +463 -0
  563. package/dist/services/recovery-observation-budget.js +121 -0
  564. package/dist/services/recovery-snapshot-database.js +588 -0
  565. package/dist/services/release-archive-producer.js +52 -0
  566. package/dist/services/release-archive.js +143 -0
  567. package/dist/services/release-cache.js +150 -0
  568. package/dist/services/release-compatibility.js +18 -0
  569. package/dist/services/release-download.js +67 -0
  570. package/dist/services/release-envelope.js +104 -0
  571. package/dist/services/release-manifest.js +45 -0
  572. package/dist/services/release-payload.js +114 -0
  573. package/dist/services/release-producer.js +63 -0
  574. package/dist/services/release-provenance.js +160 -0
  575. package/dist/services/release-registry.js +66 -0
  576. package/dist/services/release-source.js +194 -0
  577. package/dist/services/repair-intents.js +29 -0
  578. package/dist/services/repair-receipt.js +13 -0
  579. package/dist/services/retained-native-hook.js +274 -0
  580. package/dist/services/review-copy.js +165 -0
  581. package/dist/services/review-deferral.js +23 -0
  582. package/dist/services/review-fix-handoff.js +82 -0
  583. package/dist/services/review-fix-intake-custody.js +17 -0
  584. package/dist/services/review-fix-stage-entry.js +43 -0
  585. package/dist/services/review-fix-stage-handlers.js +176 -0
  586. package/dist/services/review-fix-stage-results.js +86 -0
  587. package/dist/services/run-control-receipt.js +56 -0
  588. package/dist/services/run-control-worker.js +469 -0
  589. package/dist/services/run-control.js +1109 -0
  590. package/dist/services/run-controller-adapter.js +434 -0
  591. package/dist/services/run-controller-capture.js +186 -0
  592. package/dist/services/run-controller-process.js +226 -0
  593. package/dist/services/run-controller-recovery.js +335 -0
  594. package/dist/services/run-controller-state.js +106 -0
  595. package/dist/services/run-controller.js +1717 -0
  596. package/dist/services/run-engine-bindings.js +1 -1
  597. package/dist/services/run-fork.js +147 -0
  598. package/dist/services/run-health.js +149 -0
  599. package/dist/services/run-observations.js +152 -0
  600. package/dist/services/run-projection.js +22 -64
  601. package/dist/services/run-recovery-guard.js +107 -0
  602. package/dist/services/run-recovery-runtime.js +112 -0
  603. package/dist/services/run-recovery.js +485 -0
  604. package/dist/services/run-start-custody.js +189 -0
  605. package/dist/services/run-timeline.js +59 -0
  606. package/dist/services/runs.js +996 -271
  607. package/dist/services/runtime-budget.js +446 -0
  608. package/dist/services/runtime-command.js +43 -0
  609. package/dist/services/runtime-scope-capture.js +52 -0
  610. package/dist/services/runtime-scope-control.js +468 -0
  611. package/dist/services/runtime-scope-resume.js +605 -0
  612. package/dist/services/runtime-scope-stop.js +211 -0
  613. package/dist/services/runtime-scope-worker.js +314 -0
  614. package/dist/services/runtime-service.js +19 -14
  615. package/dist/services/schema-validation.js +10 -8
  616. package/dist/services/scope-writer-cleanup.js +27 -0
  617. package/dist/services/sessions.js +37 -91
  618. package/dist/services/setup.js +427 -0
  619. package/dist/services/stage-blocker.js +17 -7
  620. package/dist/services/stage-context.js +240 -18
  621. package/dist/services/stage-handlers.js +141 -0
  622. package/dist/services/stage-lifecycle.js +156 -119
  623. package/dist/services/stage-pause.js +222 -94
  624. package/dist/services/stage-report-renderer.js +230 -6
  625. package/dist/services/stage-work-graph.js +52 -0
  626. package/dist/services/technical-notes.js +31 -0
  627. package/dist/services/tooling.js +111 -44
  628. package/dist/services/usage.js +49 -101
  629. package/dist/services/verification-matrix.js +393 -0
  630. package/dist/services/version-status.js +1 -1
  631. package/dist/services/vnext-code-review.js +302 -91
  632. package/dist/services/vnext-code.js +325 -251
  633. package/dist/services/vnext-contracts.js +10 -1
  634. package/dist/services/vnext-execution-profile.js +80 -5
  635. package/dist/services/vnext-fanout.js +399 -20
  636. package/dist/services/vnext-merge.js +336 -114
  637. package/dist/services/vnext-plan-review.js +288 -133
  638. package/dist/services/vnext-plan.js +257 -110
  639. package/dist/services/vnext-protocolize.js +534 -96
  640. package/dist/services/vnext-specify.js +131 -84
  641. package/dist/services/vnext-workspace-policy.js +7 -2
  642. package/dist/services/work-registry.js +1950 -215
  643. package/dist/services/work-retry-context.js +55 -0
  644. package/dist/services/workspace-bootstrap.js +53 -15
  645. package/dist/services/workspace-files.js +85 -0
  646. package/dist/services/worktrees.js +108 -48
  647. package/dist/shared/entity-references.js +1 -0
  648. package/dist/shared/errors.js +27 -0
  649. package/dist/storage/agent-output.js +26 -0
  650. package/dist/storage/data-preservation.js +65 -0
  651. package/dist/storage/database.js +931 -40
  652. package/dist/storage/foreign-key-transaction.js +39 -0
  653. package/dist/storage/immutable-input.js +45 -0
  654. package/dist/storage/import-guard.js +159 -0
  655. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  656. package/dist/storage/native-cleanup.js +52 -0
  657. package/dist/storage/paired-writer-migration.js +424 -0
  658. package/dist/storage/paths.js +17 -4
  659. package/dist/storage/projection-lock.js +73 -0
  660. package/dist/storage/work-references.js +23 -0
  661. package/dist/storage/writer-contract.js +96 -0
  662. package/dist/storage/writer-migration.js +346 -0
  663. package/dist/storage/writer-restore.js +217 -0
  664. package/dist/verification-components.json +384 -0
  665. package/package.json +52 -13
  666. package/tools/create-canon-release.mjs +33 -0
  667. package/tools/probe-codex-history.mjs +37 -0
  668. package/tools/produce-hybrid-release.mjs +41 -0
  669. package/tools/repair-paused-run-status.mjs +84 -0
  670. package/tools/verification-jobs.mjs +342 -0
@@ -1,3 +1,5 @@
1
+ import { managedLifecycleCommand } from "./lifecycle-invocations.js";
2
+ import { stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
1
3
  import crypto from "node:crypto";
2
4
  import { runWorkspaceBootstrap } from "./workspace-bootstrap.js";
3
5
  import fs from "node:fs";
@@ -5,19 +7,25 @@ import path from "node:path";
5
7
  import { execFileSync, spawnSync } from "node:child_process";
6
8
  import { vnextStageDirectory } from "../domain/stage-catalog.js";
7
9
  import { AppError } from "../shared/errors.js";
10
+ import { assertCheckDiagnosisEligible, checkDiagnosisDisposition } from "./check-runner-evidence.js";
8
11
  import { resolveProjectRoot } from "../storage/paths.js";
9
12
  import { requireProjectByRoot } from "./projects.js";
10
13
  import { nextMergeRequestId } from "./ids.js";
11
14
  import { readProjectConfig } from "./config.js";
12
- import { appendFlowRunTimelineEvent, attachFlowRunStage, completeFlowRun, completeFlowRunStage, gitFacts, prepareVnextMergeSourceRepairAttempt } from "./runs.js";
13
- import { flowCommand, stagePauseCommandTemplate } from "./stage-pause.js";
14
- import { validateSchema } from "./schema-validation.js";
15
- import { writeStageReport } from "./stage-report-renderer.js";
15
+ import { appendFlowRunTimelineEvent, attachFlowRunStage, completeFlowRun, completeFlowRunStage, gitFacts, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, prepareVnextMergeSourceRepairAttempt, withStageSettlement } from "./runs.js";
16
+ import { stagePauseCommand, stagePauseCommandTemplate } from "./stage-pause.js";
17
+ import { runtimeCommand, checkRetryArgs, recoveryEvidenceInput } from "./runtime-command.js";
18
+ import { acceptStageReport, prepareStageReport, publishStageReport } from "./stage-report-renderer.js";
16
19
  import { bindStageCoordinatorWork, createChildWork, failWork, finishFanInWork, finishWork, refreshRunWorkProjection, startStageCoordinatorWork } from "./work-registry.js";
17
20
  import { addVnextCodeRepair } from "./vnext-code.js";
18
- import { checkReceipts, effectiveCheckDeclarations, readCodeCheckProfile, runCodeChecks, unchangedFinalGateFailures, validateFailedCheckRetry, workspaceFingerprint } from "./code-checks.js";
19
- import { applyExternalStageContext } from "./stage-context.js";
20
- import { ensureLaneWorkspace, heartbeatLaneLock, releaseLaneLock, waitAcquireLaneLock } from "./lanes.js";
21
+ import { checkReceiptEvidenceValid, checkReceipts, effectiveCheckDeclarations, mergeCheckProfileHash, readCodeCheckProfile, runCodeChecks, validateCheckDeclaration, validateFailedCheckRetry, workspaceChangedPaths, workspaceFingerprint } from "./code-checks.js";
22
+ import { currentStagePrompt, applyExternalStageContext, prepareExternalStageContext } from "./stage-context.js";
23
+ import { ensureLaneWorkspace, prepareLaneWorkspace, heartbeatLaneLock, releaseLaneLock, waitAcquireLaneLock } from "./lanes.js";
24
+ import { assertRunMutationAllowed } from "./run-recovery.js";
25
+ import { assertAssignedStageStart as assertStageStartHookEvent } from "./assigned-lifecycle.js";
26
+ import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.js";
27
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, latestRunVerificationMatrix, publishVerificationMatrix } from "./verification-matrix.js";
28
+ import { stageSuccessor } from "./vnext-contracts.js";
21
29
  const stage = "merge";
22
30
  const stageDir = vnextStageDirectory(stage);
23
31
  export function isVnextMergeRun(context, input) {
@@ -26,16 +34,24 @@ export function isVnextMergeRun(context, input) {
26
34
  }
27
35
  /** Terminal CODE/CODE-REVIEW calls this once; it freezes source and creates the child MERGE Work. */
28
36
  export function ensureVnextMergeRequest(context, input) {
37
+ return prepareVnextMergeRequest(context, input)();
38
+ }
39
+ /** Freeze filesystem evidence outside the stage writer lock; return only the SQL settlement. */
40
+ export function prepareVnextMergeRequest(context, input) {
29
41
  const projectRoot = resolveProjectRoot(input.projectRoot);
30
42
  const project = requireProjectByRoot(context, projectRoot);
31
43
  const run = requireRun(context, project.id, input.runId);
44
+ assertRunMutationAllowed(context, project.id, run.id);
32
45
  const existing = context.db.get("SELECT * FROM merge_requests WHERE project_id = ? AND run_id = ? AND status NOT IN ('completed','failed','cancelled','superseded') ORDER BY created_at DESC LIMIT 1", [project.id, run.id]);
33
46
  if (existing)
34
- return requestView(context, existing);
47
+ return () => requestView(context, requireRequest(context, existing.merge_request_id));
35
48
  const settings = executionSettings(run);
36
49
  const route = settings.merge_mode ?? "same_session";
37
50
  const home = requireHome(run);
38
51
  const protocols = protocolIds(home);
52
+ if (route === "same_session" && path.resolve(run.workspace_root) !== path.resolve(projectRoot)) {
53
+ throw new AppError("merge_session_workspace_conflict", "The existing coordinator Session is bound to the feature workspace, but MERGE executes in the integration checkout", 2, { effect: "no_effect", recoverable: false, source_workspace: run.workspace_root, target_workspace: projectRoot });
54
+ }
39
55
  const sourceBranch = gitValue(run.workspace_root, ["branch", "--show-current"]);
40
56
  if (!sourceBranch)
41
57
  throw new AppError("merge_source_invalid", "MERGE requires a named source branch", 1, { workspace_root: run.workspace_root });
@@ -48,7 +64,10 @@ export function ensureVnextMergeRequest(context, input) {
48
64
  throw new AppError("merge_source_invalid", "MERGE could not determine the target branch", 1, { target_branch: targetBranch });
49
65
  const semantic = planChecks(run.workspace_root, protocols);
50
66
  validateMergeAcceptance(run.workspace_root, protocols, semantic);
67
+ const acceptanceRefs = mergeAcceptanceRefs(run.workspace_root, protocols);
51
68
  const effective = effectiveCheckDeclarations(targetWorkspace, semantic, ["work", "code", "readiness", "merge"]);
69
+ for (const check of effective)
70
+ validateCheckDeclaration(run.workspace_root, check);
52
71
  if (run.workspace_root !== targetWorkspace && effective.length === 0)
53
72
  throw new AppError("merge_gate_missing", "A real source/target integration requires at least one semantic or policy merge check", 2, { run_id: run.id, protocol_ids: protocols });
54
73
  const ignored = ignoredGitPaths(context, project.id);
@@ -57,18 +76,23 @@ export function ensureVnextMergeRequest(context, input) {
57
76
  const freezeFile = path.join(freezeRoot, "source-freeze.json");
58
77
  const gateFile = path.join(freezeRoot, "merge-gate.json");
59
78
  fs.mkdirSync(freezeRoot, { recursive: true });
60
- freezeMergeGate(gateFile, { runId: run.id, protocols, checks: effective, profileHash: readCodeCheckProfile(targetWorkspace).hash, now: context.now() });
79
+ const profileHash = mergeCheckProfileHash(run.workspace_root, targetWorkspace, effective);
80
+ retainMergeProfile(freezeRoot, "baseline", targetWorkspace);
81
+ retainMergeProfile(freezeRoot, "source", run.workspace_root);
82
+ freezeMergeGate(gateFile, { runId: run.id, protocols, checks: effective, acceptanceRefs, profileHash, now: context.now() });
61
83
  let sourceCommit;
62
84
  let sourcePaths;
63
85
  if (fs.existsSync(freezeFile)) {
64
86
  const frozen = JSON.parse(fs.readFileSync(freezeFile, "utf8"));
65
87
  sourceCommit = frozen.run_id === run.id && typeof frozen.source_commit === "string" ? frozen.source_commit : "";
66
88
  sourcePaths = Array.isArray(frozen.paths) ? frozen.paths : [];
67
- if (!sourceCommit || gitValue(run.workspace_root, ["rev-parse", "HEAD"]) !== sourceCommit || (accepted && JSON.stringify(accepted) !== JSON.stringify(sourcePaths)))
89
+ // A retry sees a clean tree: the original paths are already in sourceCommit.
90
+ // Reuse that receipt, but never accept new edits merely because names match.
91
+ if (!sourceCommit || frozen.source_workspace !== run.workspace_root || frozen.source_branch !== sourceBranch || gitValue(run.workspace_root, ["rev-parse", "HEAD"]) !== sourceCommit || meaningfulStatus(run.workspace_root, ignored).length)
68
92
  throw new AppError("merge_source_freeze_conflict", "Existing MERGE source freeze does not match the accepted source", 2, { freeze_file: freezeFile });
69
93
  }
70
94
  else {
71
- sourcePaths = [...new Set(meaningfulStatus(run.workspace_root, ignored).map(statusPath))].sort();
95
+ sourcePaths = meaningfulStatus(run.workspace_root, ignored);
72
96
  if (accepted) {
73
97
  const unexpected = sourcePaths.filter((item) => !accepted.includes(item));
74
98
  const absent = accepted.filter((item) => !sourcePaths.includes(item));
@@ -84,58 +108,81 @@ export function ensureVnextMergeRequest(context, input) {
84
108
  if (!sourceCommit)
85
109
  throw new AppError("merge_source_invalid", "MERGE could not freeze source commit", 1, { source_commit: sourceCommit, target_branch: targetBranch });
86
110
  const enqueueTarget = gitValue(targetWorkspace, ["rev-parse", targetBranch], true);
87
- const root = requireRootWork(context, project.id, run.id);
88
- let child;
89
- let requestId;
90
- const now = context.now();
91
- context.db.exec("BEGIN IMMEDIATE");
92
- try {
111
+ return () => context.db.writeTransaction(() => {
112
+ assertRunMutationAllowed(context, project.id, run.id);
113
+ const root = requireRootWork(context, project.id, run.id);
114
+ const now = context.now();
93
115
  const raced = context.db.get("SELECT * FROM merge_requests WHERE project_id = ? AND run_id = ? AND status NOT IN ('completed','failed','cancelled','superseded') ORDER BY created_at DESC LIMIT 1", [project.id, run.id]);
94
- if (raced) {
95
- context.db.exec("COMMIT");
116
+ if (raced)
96
117
  return requestView(context, raced);
97
- }
98
- child = createChildWork(context, { parentWorkId: root.work_id, slug: "merge", task: `Integrate frozen source ${sourceCommit} into ${targetBranch}, resolve material conflicts, and preserve accepted behavior.`, launchPolicy: route === "server" ? "fresh_agent_required" : "reuse_allowed", resultSchema: "dd-flow/merge-result@1", payload: { kind: "merge", checks: semantic, protocol_ids: protocols } });
99
- requestId = nextMergeRequestId(context);
100
- context.db.run("INSERT INTO merge_requests (merge_request_id, project_id, run_id, executor_work_id, protocol_ids_json, source_workspace, source_branch, source_commit, target_workspace, target_branch, enqueue_target_head, execution_target_head, integration_commit, execution_route, status, dispatch_owner, dispatch_lease_token, dispatch_lease_expires_at, lock_acquired_at, checkpoint, profile_hash, replacement_of_merge_request_id, adapter_receipt_json, result_json, last_error_json, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, 'queued', NULL, NULL, NULL, NULL, 'queued', ?, ?, NULL, NULL, NULL, ?, ?, NULL)", [requestId, project.id, run.id, child.work_id, JSON.stringify(protocols), run.workspace_root, sourceBranch, sourceCommit, targetWorkspace, targetBranch, enqueueTarget, route, readCodeCheckProfile(targetWorkspace).hash, replacementOf(run), now, now]);
101
- context.db.exec("COMMIT");
102
- }
103
- catch (error) {
104
- context.db.exec("ROLLBACK");
105
- throw error;
106
- }
107
- appendFlowRunTimelineEvent(context, project.id, run.id, { type: "merge_request_queued", merge_request_id: requestId, work_id: child.work_id, route, source_commit: sourceCommit, target_branch: targetBranch });
108
- return requestView(context, requireRequest(context, requestId));
118
+ const child = createChildWork(context, { parentWorkId: root.work_id, stage: "merge", slug: "merge", task: `Integrate frozen source ${sourceCommit} into ${targetBranch}, resolve material conflicts, and preserve accepted behavior.`, launchPolicy: route === "server" ? "fresh_agent_required" : "reuse_allowed", resultSchema: "dd-flow/merge-result@1", payload: { kind: "merge", checks: semantic, protocol_ids: protocols } });
119
+ const requestId = nextMergeRequestId(context);
120
+ context.db.run("INSERT INTO merge_requests (merge_request_id, project_id, run_id, executor_work_id, protocol_ids_json, source_workspace, source_branch, source_commit, target_workspace, target_branch, enqueue_target_head, execution_target_head, integration_commit, execution_route, status, dispatch_owner, dispatch_lease_token, dispatch_lease_expires_at, lock_acquired_at, checkpoint, profile_hash, replacement_of_merge_request_id, adapter_receipt_json, result_json, last_error_json, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, 'queued', NULL, NULL, NULL, NULL, 'queued', ?, ?, NULL, NULL, NULL, ?, ?, NULL)", [requestId, project.id, run.id, child.work_id, JSON.stringify(protocols), run.workspace_root, sourceBranch, sourceCommit, targetWorkspace, targetBranch, enqueueTarget, route, profileHash, replacementOf(run), now, now]);
121
+ appendFlowRunTimelineEvent(context, project.id, run.id, { type: "merge_request_queued", merge_request_id: requestId, work_id: child.work_id, route, source_commit: sourceCommit, target_branch: targetBranch });
122
+ return requestView(context, requireRequest(context, requestId));
123
+ });
109
124
  }
110
- export async function startVnextMerge(context, input) {
125
+ export function prepareVnextMergeStart(context, input) {
111
126
  const projectRoot = resolveProjectRoot(input.projectRoot);
112
127
  const project = requireProjectByRoot(context, projectRoot);
113
128
  const run = requireRun(context, project.id, input.runId);
114
129
  const home = requireHome(run);
115
130
  const request = requestForRun(context, project.id, run.id);
131
+ const generation = assertRunMutationAllowed(context, project.id, run.id);
132
+ const common = { projectRoot, project, run, request, generation };
116
133
  if (request.status === "completed")
134
+ return { ...common, terminal: true };
135
+ if (!["queued", "dispatching", "active", "action_required"].includes(request.status))
136
+ throw new AppError("merge_request_state_invalid", "MERGE request cannot be started in its current state", 1, { status: request.status, merge_request_id: request.merge_request_id });
137
+ const root = path.join(home, stageDir), workRoot = path.join(root, "works", request.executor_work_id);
138
+ const resultPath = path.join(workRoot, "result.json"), promptPath = path.join(workRoot, "prompt.md");
139
+ const work = context.db.get("SELECT status, project_id, run_id FROM works WHERE work_id = ?", [request.executor_work_id]);
140
+ if (!work || work.project_id !== project.id || work.run_id !== run.id || !["created", "running"].includes(work.status))
141
+ throw new AppError("runtime_artifact_invalid", "MERGE executor Work is missing or not startable", 1, { work_id: request.executor_work_id });
142
+ const index = JSON.parse(run.index_json);
143
+ const attachment = ["active", "action_required"].includes(request.status) && index.stage_runs?.some(entry => entry.stage === stage) ? null : prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" });
144
+ prepareLaneWorkspace(context, { projectRoot, lane: "merge", workspacePath: request.target_workspace, branch: request.target_branch });
145
+ const checks = effectiveMergeChecks(run, request), settings = executionSettings(run);
146
+ const existingPrompt = ["active", "action_required"].includes(request.status) && fs.existsSync(promptPath) ? fs.readFileSync(promptPath, "utf8") : null;
147
+ if (existingPrompt === null || input.externalContext)
148
+ prepareOutputFile(promptPath);
149
+ if (input.externalContext)
150
+ prepareExternalStageContext({ stageRoot: root, loaded: input.externalContext });
151
+ const matrix = existingPrompt !== null ? latestRunVerificationMatrix(context, project.id, run.id, stage, "input") : prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
152
+ return { ...common, terminal: false, root, workRoot, resultPath, promptPath, checks, settings, existingPrompt, matrix };
153
+ }
154
+ export async function startVnextMerge(context, input, prepared = prepareVnextMergeStart(context, input)) {
155
+ const { projectRoot, project, run, request, generation } = prepared;
156
+ if (prepared.terminal)
117
157
  return requestView(context, request);
118
- const root = path.join(home, stageDir);
119
- const workRoot = path.join(root, "works", request.executor_work_id);
158
+ const { root, workRoot, resultPath, promptPath, checks, settings, existingPrompt } = prepared;
159
+ if (existingPrompt !== null)
160
+ resumeVerificationMatrix(context, project.id, run.id, stage);
161
+ assertRunMutationAllowed(context, project.id, run.id, generation);
162
+ assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
163
+ const alreadyAttached = ["active", "action_required"].includes(request.status) && (JSON.parse(requireRun(context, project.id, run.id).index_json).stage_runs ?? []).some((item) => item.stage === stage);
164
+ if (!alreadyAttached)
165
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" }, true);
120
166
  fs.mkdirSync(workRoot, { recursive: true });
121
- const resultPath = path.join(workRoot, "result.json");
122
- const promptPath = path.join(workRoot, "prompt.md");
123
167
  ensureLaneWorkspace(context, { projectRoot, lane: "merge", workspacePath: request.target_workspace, branch: request.target_branch });
124
168
  const lane = await waitAcquireLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, timeoutSeconds: 3600, pollIntervalSeconds: 15, ttlSeconds: 7200, reason: `MERGE ${request.merge_request_id}`, ...(input.progress ? { progress: input.progress } : {}) });
125
169
  if (!lane.acquired)
126
170
  throw new AppError("merge_lane_wait_failed", "MERGE could not acquire the project integration lane", 1, { merge_request_id: request.merge_request_id, status: lane.status ?? "unknown" });
127
171
  try {
172
+ assertRunMutationAllowed(context, project.id, run.id, generation);
128
173
  if (["active", "action_required"].includes(request.status)) {
129
174
  const pendingWork = context.db.get("SELECT status FROM works WHERE work_id = ?", [request.executor_work_id]);
130
175
  const binding = pendingWork?.status === "created"
131
176
  ? startStageCoordinatorWork(context, { workId: request.executor_work_id, hookEventId: input.hookEventId, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) })
132
177
  : bindStageCoordinatorWork(context, { workId: request.executor_work_id, hookEventId: input.hookEventId, stage, promptPath, resultPath, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
133
- const prompt = fs.existsSync(promptPath) ? fs.readFileSync(promptPath, "utf8") : mergePrompt(context, { run, request, resultPath, projectRoot });
134
- if (!fs.existsSync(promptPath))
178
+ const prompt = existingPrompt ?? `${mergePrompt(context, { run, request, resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, run.run_root, "MERGE source/target verification and residual-risk assessment")}\n`;
179
+ if (existingPrompt === null)
135
180
  fs.writeFileSync(promptPath, prompt);
136
- const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
137
- ensureMergeStageAttached(context, run, projectRoot);
138
- return startPacket(context, run, request, binding, promptPath, resultPath, prompt, projectRoot, externalContext);
181
+ const externalContext = applyExternalStageContext({ context, stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
182
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" };
183
+ const attachment = alreadyAttached ? null : prepareFlowRunStageAttachment(context, attachmentInput);
184
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachment ? attachFlowRunStage(context, attachmentInput, attachment) : undefined);
185
+ return startPacket(context, run, request, binding, promptPath, resultPath, projectRoot, checks, externalContext);
139
186
  }
140
187
  const current = requireRequest(context, request.merge_request_id);
141
188
  const targetHead = gitValue(current.target_workspace, ["rev-parse", current.target_branch], true);
@@ -149,31 +196,43 @@ export async function startVnextMerge(context, input) {
149
196
  if (claimed.changes !== 1 && requireRequest(context, current.merge_request_id).status !== "active")
150
197
  throw new AppError("merge_claim_conflict", "MERGE request could not acquire the integration lane", 1, { merge_request_id: current.merge_request_id });
151
198
  const started = startStageCoordinatorWork(context, { workId: current.executor_work_id, hookEventId: input.hookEventId, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
152
- const prompt = mergePrompt(context, { run, request: requireRequest(context, current.merge_request_id), resultPath, projectRoot });
199
+ const prompt = `${mergePrompt(context, { run, request: requireRequest(context, current.merge_request_id), resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, run.run_root, "MERGE source/target verification and residual-risk assessment")}\n`;
153
200
  fs.writeFileSync(promptPath, prompt);
154
- const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
201
+ const externalContext = applyExternalStageContext({ context, stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
155
202
  context.db.run("UPDATE work_sessions SET prompt_path = ?, result_path = ?, updated_at = ? WHERE work_id = ? AND status = 'running'", [promptPath, resultPath, context.now(), current.executor_work_id]);
156
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" });
203
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" };
204
+ const attachment = alreadyAttached ? null : prepareFlowRunStageAttachment(context, attachmentInput);
205
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachment ? attachFlowRunStage(context, attachmentInput, attachment) : undefined);
157
206
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "merge_started", merge_request_id: current.merge_request_id, work_id: current.executor_work_id, target_head: targetHead });
158
- return startPacket(context, run, current, started, promptPath, resultPath, prompt, projectRoot, externalContext);
207
+ return startPacket(context, run, current, started, promptPath, resultPath, projectRoot, checks, externalContext);
159
208
  }
160
209
  catch (error) {
161
210
  const latest = requireRequest(context, request.merge_request_id);
211
+ if (error instanceof AppError && ["run_recovery_guarded", "run_recovery_generation_stale"].includes(error.code)) {
212
+ releaseMergeLane(context, projectRoot, latest, `MERGE ${latest.merge_request_id} interrupted`);
213
+ throw error;
214
+ }
162
215
  if (["active", "dispatching"].includes(latest.status))
163
216
  context.db.run("UPDATE merge_requests SET status = 'action_required', last_error_json = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ code: "merge_start_failed", error: error instanceof Error ? error.message : String(error) }), context.now(), latest.merge_request_id]);
164
217
  releaseMergeLane(context, projectRoot, latest, `MERGE ${latest.merge_request_id} start failed`);
165
218
  throw error;
166
219
  }
167
220
  }
168
- function startPacket(context, run, request, binding, promptPath, resultPath, prompt, projectRoot, externalContext) {
169
- return { ok: true, run_id: run.id, stage, merge_request_id: request.merge_request_id, work_id: request.executor_work_id, session_binding: binding.session_binding ?? binding, prompt_path: promptPath, result_path: resultPath, worker_prompt_markdown: prompt, ...(externalContext ? { external_context: externalContext } : {}), queue: queueStatus(context, request), effective_checks: effectiveMergeChecks(run, request), next: { apply_command: applyCommand(context, request, projectRoot), finish_command: finishCommand(context, run.id, request, projectRoot) } };
221
+ function startPacket(context, run, request, binding, promptPath, resultPath, projectRoot, checks, externalContext) {
222
+ return { ok: true, run_id: run.id, stage, merge_request_id: request.merge_request_id, work_id: request.executor_work_id, session_binding: binding.session_binding ?? binding, result_path: resultPath, ...currentStagePrompt(context, promptPath), ...(externalContext ? { external_context: externalContext } : {}), queue: queueStatus(context, request), effective_checks: checks, next: { apply_command: applyCommand(context, request, projectRoot), finish_command: finishCommand(context, run.id, request, projectRoot) } };
170
223
  }
171
224
  export function applyVnextMerge(context, input) {
172
225
  const existing = requireRequest(context, input.requestId);
173
226
  const receiptFile = applyReceiptPath(context, existing);
174
- if (existing.executor_work_id === input.workId && ["apply_recorded", "integration_committed", "bootstrap_ready", "checks_passed", "delivered", "finalized"].includes(existing.checkpoint) && fs.existsSync(receiptFile))
175
- return JSON.parse(fs.readFileSync(receiptFile, "utf8"));
176
227
  const request = requireOwnedActiveRequest(context, input);
228
+ assertRunMutationAllowed(context, request.project_id, request.run_id);
229
+ if (["apply_recorded", "integration_committed", "bootstrap_ready", "checks_passed", "delivered", "finalized"].includes(request.checkpoint) && fs.existsSync(receiptFile)) {
230
+ const receipt = JSON.parse(fs.readFileSync(receiptFile, "utf8"));
231
+ if (receipt.merge_request_id !== request.merge_request_id || receipt.work_id !== request.executor_work_id || receipt.source_commit !== request.source_commit)
232
+ throw new AppError("merge_apply_receipt_invalid", "Existing merge apply receipt does not match the active request", 1, { receipt_path: receiptFile });
233
+ return receipt;
234
+ }
235
+ requireBootstrapCommand(requireRun(context, request.project_id, request.run_id));
177
236
  heartbeatLaneLock(context, { projectRoot: input.projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, ttlSeconds: 7200 });
178
237
  assertExecutionBaseline(request);
179
238
  input.progress?.(`applying ${request.source_commit} to ${request.target_branch}`);
@@ -195,58 +254,80 @@ export function applyVnextMerge(context, input) {
195
254
  context.db.run("UPDATE merge_requests SET checkpoint = 'apply_recorded', result_json = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ apply_receipt: receiptFile }), context.now(), request.merge_request_id]);
196
255
  return { ok: true, ...receipt };
197
256
  }
198
- export async function finishVnextMerge(context, input) {
257
+ export function prepareVnextMergeFinish(context, input) {
199
258
  const projectRoot = resolveProjectRoot(input.projectRoot);
200
259
  const project = requireProjectByRoot(context, projectRoot);
201
260
  const run = requireRun(context, project.id, input.runId);
202
261
  const existing = requireRequest(context, input.requestId);
262
+ const generation = assertRunMutationAllowed(context, project.id, run.id);
203
263
  if (existing.project_id !== project.id || existing.run_id !== run.id || existing.executor_work_id !== input.workId)
204
264
  throw new AppError("merge_request_mismatch", "MRG/Work/RUN/project identity does not match", 2);
205
- if (existing.status === "completed") {
206
- recoverCompletedMergeRun(context, run, projectRoot, existing);
207
- return { ok: true, resumed: true, run_id: run.id, stage, outcome: "completed", merge_request_id: existing.merge_request_id, work_id: existing.executor_work_id, integration_commit: existing.integration_commit, report_path: path.join(requireHome(run), stageDir, "stage-report.json"), next_action: "merge_completed" };
208
- }
209
- let request = requireOwnedActiveRequest(context, input);
210
- heartbeatLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, ttlSeconds: 7200 });
265
+ if (existing.status === "completed")
266
+ return { projectRoot, project, run, existing, generation, terminal: true };
267
+ const request = requireOwnedActiveRequest(context, input);
211
268
  if (request.run_id !== run.id)
212
269
  throw new AppError("merge_request_mismatch", "MRG does not belong to RUN", 2);
213
270
  if (request.checkpoint === "baseline_locked" || !fs.existsSync(applyReceiptPath(context, request)))
214
271
  throw new AppError("merge_apply_required", "Run the exact merge apply command before MERGE finish", 2, { merge_request_id: request.merge_request_id, checkpoint: request.checkpoint });
215
- if (unmerged(request.target_workspace).length)
216
- throw new AppError("merge_conflicts_unresolved", "Resolve every unmerged path before finishing MERGE", 2, { paths: unmerged(request.target_workspace) });
272
+ const conflicts = unmerged(request.target_workspace);
273
+ if (conflicts.length)
274
+ throw new AppError("merge_conflicts_unresolved", "Resolve every unmerged path before finishing MERGE", 2, { paths: conflicts });
217
275
  const resultPath = path.join(requireHome(run), stageDir, "works", request.executor_work_id, "result.json");
218
- validateSchema({ schemaName: "merge-result", file: resultPath, projectRoot: request.target_workspace, ddFlowHome: context.ddFlowHome, runId: run.id, runRoot: requireHome(run) });
219
- const semantic = JSON.parse(fs.readFileSync(resultPath, "utf8"));
220
- if (semantic.outcome !== "completed")
276
+ const result = prepareJsonSchema(resultPath, "MERGE result", { schemaName: "merge-result", projectRoot: request.target_workspace, ddFlowHome: context.ddFlowHome, runId: run.id, runRoot: requireHome(run) });
277
+ if (result.value.outcome !== "completed")
221
278
  throw new AppError("merge_semantic_blocked", "A blocked semantic result cannot complete MERGE", 2, { result_path: resultPath });
279
+ const gate = readFrozenMergeGate(path.join(requireHome(run), stageDir, "merge-gate.json"), request.merge_request_id);
280
+ if (input.retryCheckId)
281
+ validateFailedCheckRetry(context, { projectId: project.id, runId: run.id, receiptId: input.retryCheckId, declarations: gate.checks, reason: input.retryReason });
282
+ return { projectRoot, project, run, existing, generation, terminal: false, request, resultPath, result, gate };
283
+ }
284
+ export async function finishVnextMerge(context, input, prepared = prepareVnextMergeFinish(context, input)) {
285
+ const { projectRoot, project, run, existing, generation } = prepared;
286
+ if (prepared.terminal) {
287
+ recoverCompletedMergeRun(context, run, projectRoot, existing);
288
+ releaseMergeLane(context, projectRoot, existing, `MERGE ${existing.merge_request_id} completion recovered`);
289
+ return { ok: true, resumed: true, run_id: run.id, stage, outcome: "completed", merge_request_id: existing.merge_request_id, work_id: existing.executor_work_id, integration_commit: existing.integration_commit, report_path: path.join(requireHome(run), stageDir, "stage-report.json"), next_action: "merge_completed" };
290
+ }
291
+ let request = prepared.request;
292
+ heartbeatLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, ttlSeconds: 7200 });
293
+ const semantic = prepared.result.value;
222
294
  if (request.checkpoint === "apply_recorded") {
223
295
  input.progress?.("bootstrapping integrated target");
224
296
  await runBootstrap(context, run, request.target_workspace, request.merge_request_id, input.progress);
297
+ assertRunMutationAllowed(context, project.id, run.id, generation);
225
298
  context.db.run("UPDATE merge_requests SET checkpoint = 'bootstrap_ready', updated_at = ? WHERE merge_request_id = ?", [context.now(), request.merge_request_id]);
226
299
  request = requireRequest(context, request.merge_request_id);
227
300
  }
228
- const checks = effectiveMergeChecks(run, request);
229
- if (input.retryCheckId) {
230
- validateFailedCheckRetry(context, { projectId: project.id, runId: run.id, receiptId: input.retryCheckId, declarations: checks });
301
+ const gate = prepared.gate, checks = gate.checks;
302
+ if (input.retryCheckId)
231
303
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "check_retry_requested", check_receipt_id: input.retryCheckId, stage, reason: input.retryReason ?? "operator retry" });
232
- }
233
- const unchanged = unchangedFinalGateFailures(context, { projectId: project.id, runId: run.id, workspaceRoot: request.target_workspace, declarations: checks, ...(input.retryCheckId ? { retryReceiptId: input.retryCheckId } : {}) });
234
- if (unchanged.length)
235
- throw new AppError("merge_check_repair_required", "MERGE checks already failed for the unchanged integrated tree; choose source repair or restore the environment and retry that receipt", 2, { failures: unchanged, retry_command: `${finishCommand(context, run.id, request, projectRoot)} --retry-check ${unchanged[0].id} --reason "<environment recovery evidence>"` });
236
- const receipts = request.checkpoint === "checks_passed" || request.checkpoint === "delivered" || request.checkpoint === "finalized" ? checkReceipts(context, { projectId: project.id, runId: run.id, workId: request.executor_work_id }).filter((receipt) => receipt.status === "passed" && receipt.before_fingerprint === workspaceFingerprint(request.target_workspace)) : await runCodeChecks(context, { projectId: project.id, runId: run.id, runHome: requireHome(run), workspaceRoot: request.target_workspace, workId: request.executor_work_id, artifactDir: path.join(stageDir, "works", request.executor_work_id), scope: "aggregate", checks, ...(input.progress ? { progress: input.progress } : {}) });
304
+ for (const check of checks)
305
+ validateCheckDeclaration(request.target_workspace, check);
306
+ retainMergeProfile(path.join(requireHome(run), stageDir), "target", request.target_workspace);
307
+ const acceptancePath = path.join(requireHome(run), stageDir, "merge-gate-acceptance.json");
308
+ const recovered = readMergeGateAcceptance(context, acceptancePath, request, gate);
309
+ const resumingGate = ["checks_passed", "integration_committed", "delivered"].includes(request.checkpoint);
310
+ if (resumingGate && !recovered.valid)
311
+ return recovery(context, request, "merge_evidence_missing", { checkpoint: request.checkpoint });
312
+ if (request.checkpoint === "checks_passed" && request.accepted_tree && gitValue(request.target_workspace, ["write-tree"]) !== request.accepted_tree)
313
+ return recovery(context, request, "merge_workspace_drift_after_checks", { accepted_tree: request.accepted_tree });
314
+ const receipts = resumingGate ? recovered.receipts : await runCodeChecks(context, { projectId: project.id, runId: run.id, runHome: requireHome(run), workspaceRoot: request.target_workspace, workId: request.executor_work_id, artifactDir: path.join(stageDir, "works", request.executor_work_id), scope: "aggregate", checks, expectedProfileHash: gate.profileHash, ...(input.retryCheckId ? { retryReceiptId: input.retryCheckId, retryReason: input.retryReason } : {}), ...(input.progress ? { progress: input.progress } : {}) });
315
+ assertRunMutationAllowed(context, project.id, run.id, generation);
237
316
  const failed = receipts.filter((receipt) => receipt.status !== "passed");
238
317
  if (failed.length) {
239
318
  context.db.run("UPDATE merge_requests SET status = 'action_required', last_error_json = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ code: "merge_gate_failed", failures: failed }), context.now(), request.merge_request_id]);
240
- throw new AppError("merge_gate_failed", "Integrated target checks failed. Classify retained evidence: source defects use source repair; restored environment uses retry.", 2, { failures: failed, repair_command: `${flowCommand(context)} merge repair ${request.merge_request_id} --project-root ${JSON.stringify(projectRoot)} --json`, retry_command: `${finishCommand(context, run.id, request, projectRoot)} --retry-check ${failed[0].id} --reason "<environment recovery evidence>"` });
319
+ throw new AppError("merge_gate_failed", "Integrated target checks failed. Investigate their cause in this MERGE Work after process settlement; keep acceptance requirements intact. Use source repair only after establishing that a product correction is needed.", 2, mergeGateFailureDetails(failed, () => ({ ...recoveryEvidenceInput(run.run_root, failed[0].id), command_after_input: finishCommand(context, run.id, request, projectRoot, failed[0].id) })));
241
320
  }
242
- const requiredRefs = mergeAcceptanceRefs(run.workspace_root, JSON.parse(request.protocol_ids_json));
243
321
  const passedRefs = new Set(receipts.flatMap((receipt) => receipt.check_refs));
244
- const missingRefs = requiredRefs.filter((ref) => !passedRefs.has(ref));
322
+ const missingRefs = frozenGateRefs(gate).filter((ref) => !passedRefs.has(ref));
245
323
  if (missingRefs.length)
246
- throw new AppError("merge_acceptance_unproven", "Current MERGE receipts do not cover every merge acceptance reference", 2, { missing_check_refs: missingRefs });
247
- const acceptedTree = gitValue(request.target_workspace, ["write-tree"]);
248
- context.db.run("UPDATE merge_requests SET checkpoint = 'checks_passed', accepted_tree = ?, status = 'active', updated_at = ? WHERE merge_request_id = ?", [acceptedTree, context.now(), request.merge_request_id]);
249
- request = requireRequest(context, request.merge_request_id);
324
+ throw new AppError("merge_acceptance_unproven", "Current MERGE receipts do not cover every frozen merge-gate reference", 2, { missing_check_refs: missingRefs });
325
+ if (!request.integration_commit) {
326
+ const acceptedTree = gitValue(request.target_workspace, ["write-tree"]);
327
+ writeMergeGateAcceptance(context, acceptancePath, request, gate, acceptedTree, receipts);
328
+ context.db.run("UPDATE merge_requests SET checkpoint = 'checks_passed', accepted_tree = ?, status = 'active', updated_at = ? WHERE merge_request_id = ?", [acceptedTree, context.now(), request.merge_request_id]);
329
+ request = requireRequest(context, request.merge_request_id);
330
+ }
250
331
  if (!request.integration_commit) {
251
332
  commitIntegration(request.target_workspace, run.id, ignoredGitPaths(context, project.id));
252
333
  const commit = gitValue(request.target_workspace, ["rev-parse", "HEAD"]);
@@ -257,17 +338,35 @@ export async function finishVnextMerge(context, input) {
257
338
  context.db.run("UPDATE merge_requests SET integration_commit = ?, checkpoint = 'integration_committed', updated_at = ? WHERE merge_request_id = ?", [commit, context.now(), request.merge_request_id]);
258
339
  request = requireRequest(context, request.merge_request_id);
259
340
  }
341
+ if (request.integration_commit && request.accepted_tree && gitValue(request.target_workspace, ["rev-parse", `${request.integration_commit}^{tree}`]) !== request.accepted_tree)
342
+ return recovery(context, request, "merge_committed_tree_drift", { accepted_tree: request.accepted_tree, integration_commit: request.integration_commit });
260
343
  verifyLocalDelivery(request);
261
344
  context.db.run("UPDATE merge_requests SET checkpoint = 'delivered', updated_at = ? WHERE merge_request_id = ?", [context.now(), request.merge_request_id]);
262
345
  request = requireRequest(context, request.merge_request_id);
346
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", receipts, semanticFile: prepared.resultPath });
347
+ // Retain exact bounded sources before deleting the feature checkout. A
348
+ // crash here recovers presentation from packets, never repeats the checks.
349
+ context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
350
+ publishVerificationMatrix(context, matrix);
263
351
  performConfiguredCleanup(run, request);
264
- await finishWork(context, request.executor_work_id, fs.readFileSync(resultPath, "utf8"));
352
+ const acceptedResult = prepared.result.text;
353
+ const executor = context.db.get("SELECT status, result FROM works WHERE work_id = ?", [request.executor_work_id]);
354
+ if (!executor)
355
+ return recovery(context, request, "merge_executor_missing", { work_id: request.executor_work_id });
356
+ if (executor.status === "running")
357
+ await finishWork(context, request.executor_work_id, acceptedResult);
358
+ else if (executor.status !== "completed" || executor.result !== acceptedResult)
359
+ return recovery(context, request, "merge_executor_settlement_mismatch", { work_id: request.executor_work_id, status: executor.status });
360
+ assertRunMutationAllowed(context, project.id, run.id, generation);
265
361
  const now = context.now();
266
362
  context.db.run("UPDATE merge_requests SET status = 'completed', checkpoint = 'finalized', result_json = ?, completed_at = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify(semantic), now, now, request.merge_request_id]);
267
- const report = mergeReport(context, run, requireRequest(context, request.merge_request_id), semantic, receipts);
268
363
  const root = path.join(requireHome(run), stageDir);
269
- writeStageReport(root, report);
270
- completeFlowRunStage(context, { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@2", report: "stage-report.md", stageReport: "stage-report.html" });
364
+ const retainedReport = completedMergeReport(context, run, root);
365
+ const report = retainedReport ?? mergeReport(context, run, requireRequest(context, request.merge_request_id), semantic, receipts);
366
+ if (matrix)
367
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
368
+ if (!retainedReport)
369
+ commitMergeStageReport(context, run, projectRoot, root, report);
271
370
  const rootWork = requireRootWork(context, project.id, run.id);
272
371
  finishFanInWork(context, rootWork.work_id, JSON.stringify(report));
273
372
  completeFlowRun(context, { projectRoot, runId: run.id, status: "done", verdict: "merge_completed", nextAction: undefined });
@@ -286,37 +385,100 @@ function ensureMergeStageAttached(context, run, projectRoot) {
286
385
  function recoverCompletedMergeRun(context, run, projectRoot, request) {
287
386
  const root = path.join(requireHome(run), stageDir);
288
387
  const reportPath = path.join(root, "stage-report.json");
289
- if (!fs.existsSync(reportPath))
290
- throw new AppError("merge_report_missing", "Completed MERGE request has no stage report", 1, { merge_request_id: request.merge_request_id, report_path: reportPath });
388
+ const retainedReport = completedMergeReport(context, run, root);
389
+ let report;
390
+ if (retainedReport)
391
+ report = retainedReport;
392
+ else if (!fs.existsSync(reportPath)) {
393
+ const semantic = request.result_json ? JSON.parse(request.result_json) : null;
394
+ const gate = readFrozenMergeGate(path.join(root, "merge-gate.json"), request.merge_request_id);
395
+ const accepted = readMergeGateAcceptance(context, path.join(root, "merge-gate-acceptance.json"), request, gate);
396
+ if (!semantic || typeof semantic.summary !== "string" || typeof semantic.verification_summary !== "string" || !Array.isArray(semantic.residual_risks) || !semantic.residual_risks.every(item => typeof item === "string") || !accepted.valid)
397
+ throw new AppError("merge_report_missing", "Completed MERGE has insufficient authoritative evidence to rebuild its report", 1, { merge_request_id: request.merge_request_id, report_path: reportPath });
398
+ verifyLocalDelivery(request);
399
+ performConfiguredCleanup(run, request);
400
+ report = mergeReport(context, run, request, semantic, accepted.receipts);
401
+ }
402
+ else
403
+ report = JSON.parse(fs.readFileSync(reportPath, "utf8"));
291
404
  ensureMergeStageAttached(context, run, projectRoot);
292
- completeFlowRunStage(context, { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@2", report: "stage-report.md", stageReport: "stage-report.html" });
405
+ if (!retainedReport)
406
+ commitMergeStageReport(context, run, projectRoot, root, report);
293
407
  const rootWork = findRootWork(context, run.project_id, run.id);
294
408
  const rootStatus = rootWork.status;
295
409
  if (rootStatus === "running")
296
410
  finishFanInWork(context, rootWork.work_id, fs.readFileSync(reportPath, "utf8"));
411
+ else if (rootStatus !== "completed")
412
+ throw new AppError("merge_root_settlement_mismatch", "Completed MERGE cannot recover a non-completed root Work", 1, { merge_request_id: request.merge_request_id, work_id: rootWork.work_id, status: rootStatus });
297
413
  const runStatus = context.db.get("SELECT status FROM runs WHERE project_id = ? AND id = ?", [run.project_id, run.id])?.status;
298
414
  if (runStatus !== "done")
299
415
  completeFlowRun(context, { projectRoot, runId: run.id, status: "done", verdict: "merge_completed", nextAction: undefined });
300
416
  refreshRunWorkProjection(context, run.project_id, run.id);
301
417
  }
302
- export function routeVnextMergeRequest(context, input) { const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)); const request = requireRequest(context, input.requestId); if (request.project_id !== project.id || request.status !== "queued")
303
- throw new AppError("invalid_merge_state", "Only a queued request without an active dispatch may change route", 2, { status: request.status }); context.db.exec("BEGIN IMMEDIATE"); try {
304
- context.db.run("UPDATE merge_requests SET execution_route = ?, last_error_json = ?, updated_at = ? WHERE merge_request_id = ? AND status = 'queued'", [input.mode, JSON.stringify({ route_reason: input.reason }), context.now(), request.merge_request_id]);
305
- context.db.run("UPDATE works SET launch_policy = ?, updated_at = ? WHERE work_id = ? AND status = 'created'", [input.mode === "server" ? "fresh_agent_required" : "reuse_allowed", context.now(), request.executor_work_id]);
306
- context.db.exec("COMMIT");
418
+ function completedMergeReport(context, run, root) {
419
+ const current = requireRun(context, run.project_id, run.id);
420
+ const index = JSON.parse(current.index_json);
421
+ const entry = index.stage_runs?.find(item => item.stage === stage);
422
+ if (entry?.status !== "done")
423
+ return null;
424
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.stage_report' ORDER BY id DESC", [run.project_id]);
425
+ const receipt = rows.map(row => JSON.parse(row.payload_json))
426
+ .find(item => item.run_id === run.id && item.stage === stage && item.attempt === entry.attempt && item.root === root);
427
+ if (receipt) {
428
+ if (crypto.createHash("sha256").update(receipt.bytes.json).digest("hex") !== receipt.sha256)
429
+ throw new AppError("stage_report_invalid", "Accepted MERGE report receipt has invalid bytes", 1, { run_id: run.id });
430
+ publishStageReport(context, run.project_id, run.id, stage, root);
431
+ return JSON.parse(receipt.bytes.json);
432
+ }
433
+ const file = path.join(root, "stage-report.json");
434
+ if (!fs.existsSync(file))
435
+ throw new AppError("merge_report_missing", "Completed MERGE has no accepted report receipt or published report", 1, { run_id: run.id, report_path: file });
436
+ return JSON.parse(fs.readFileSync(file, "utf8"));
437
+ }
438
+ function commitMergeStageReport(context, run, projectRoot, root, report) {
439
+ const matrix = latestRunVerificationMatrix(context, run.project_id, run.id, stage, "output");
440
+ if (matrix)
441
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
442
+ if (JSON.parse(run.index_json).verification_matrix_contract && !matrix)
443
+ throw new AppError("verification_matrix_packet_missing", "Completed marked MERGE lacks its retained final matrix", 1, { run_id: run.id });
444
+ const preparedReport = prepareStageReport(report);
445
+ const completion = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@2", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256 };
446
+ const preparedCompletion = prepareFlowRunStageCompletion(context, completion);
447
+ withStageSettlement(context, () => {
448
+ acceptVerificationMatrix(context, matrix);
449
+ acceptStageReport(context, { projectId: run.project_id, runId: run.id, stage, root, prepared: preparedReport });
450
+ completeFlowRunStage(context, completion, preparedCompletion);
451
+ });
452
+ }
453
+ export function routeVnextMergeRequest(context, input) {
454
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
455
+ const request = context.db.writeTransaction(() => {
456
+ const request = requireRequest(context, input.requestId);
457
+ if (request.project_id !== project.id || request.status !== "queued")
458
+ throw new AppError("invalid_merge_state", "Only a queued request without an active dispatch may change route", 2, { status: request.status });
459
+ assertRunMutationAllowed(context, request.project_id, request.run_id);
460
+ context.db.run("UPDATE merge_requests SET execution_route = ?, last_error_json = ?, updated_at = ? WHERE merge_request_id = ? AND status = 'queued'", [input.mode, JSON.stringify({ route_reason: input.reason }), context.now(), request.merge_request_id]);
461
+ context.db.run("UPDATE works SET launch_policy = ?, updated_at = ? WHERE work_id = ? AND status = 'created'", [input.mode === "server" ? "fresh_agent_required" : "reuse_allowed", context.now(), request.executor_work_id]);
462
+ return request;
463
+ });
464
+ refreshRunWorkProjection(context, request.project_id, request.run_id);
465
+ return requestView(context, requireRequest(context, request.merge_request_id));
307
466
  }
308
- catch (error) {
309
- context.db.exec("ROLLBACK");
310
- throw error;
311
- } refreshRunWorkProjection(context, request.project_id, request.run_id); return requestView(context, requireRequest(context, request.merge_request_id)); }
312
467
  /** Move a failed integration gate back to the source CODE/CODE-REVIEW loop. */
313
468
  export async function repairVnextMerge(context, input) {
314
469
  const projectRoot = resolveProjectRoot(input.projectRoot);
315
470
  const project = requireProjectByRoot(context, projectRoot);
316
471
  const request = requireRequest(context, input.requestId);
472
+ const generation = assertRunMutationAllowed(context, project.id, request.run_id);
473
+ if (request.project_id === project.id && request.status === "superseded" && errorCode(request) === "merge_source_repair") {
474
+ const saved = JSON.parse(request.last_error_json);
475
+ releaseMergeLane(context, projectRoot, request, `MERGE ${request.merge_request_id} moved to source repair`);
476
+ return { ok: true, run_id: request.run_id, merge_request_id: request.merge_request_id, status: "superseded", source_repair: { repair_work_id: saved.repair_work_id }, next: stageSuccessor(context, { projectRoot, runId: request.run_id, stage: "code", command: runtimeCommand(context, ["stage", "start", request.run_id, "--stage", "code", "--project-root", projectRoot, "--json", "--progress-jsonl"]) }) };
477
+ }
317
478
  if (request.project_id !== project.id || request.status !== "action_required" || errorCode(request) !== "merge_gate_failed") {
318
479
  throw new AppError("invalid_merge_repair_state", "MERGE source repair requires an action-required failed integration gate", 2, { merge_request_id: input.requestId, status: request.status, code: errorCode(request) });
319
480
  }
481
+ assertCheckDiagnosisEligible(checkReceipts(context, { projectId: project.id, runId: request.run_id }).filter(receipt => failedReceiptIds(request).includes(receipt.id)), request.target_workspace);
320
482
  abortIntegration(request);
321
483
  const originWorkIds = sourceCodeWorkIds(context, request.project_id, request.run_id);
322
484
  if (!originWorkIds.length)
@@ -326,37 +488,83 @@ export async function repairVnextMerge(context, input) {
326
488
  throw new AppError("runtime_missing", "MERGE failure has no durable failed receipt", 1, { merge_request_id: request.merge_request_id });
327
489
  // Settle the child while its canonical result path still exists; the next
328
490
  // step archives the entire MERGE attempt as immutable evidence.
329
- await failWork(context, request.executor_work_id, `Superseded by source repair after failed integration gate ${failed[0]}.`);
491
+ const executor = context.db.get("SELECT status FROM works WHERE work_id = ?", [request.executor_work_id]);
492
+ if (executor?.status !== "failed")
493
+ await failWork(context, request.executor_work_id, `Superseded by source repair after failed integration gate ${failed[0]}.`);
494
+ assertRunMutationAllowed(context, project.id, request.run_id, generation);
330
495
  const prepared = prepareVnextMergeSourceRepairAttempt(context, { projectRoot, runId: request.run_id, mergeRequestId: request.merge_request_id });
331
- const repair = addVnextCodeRepair(context, { projectRoot, runId: request.run_id, checkReceiptId: failed[0], originWorkIds, objective: `Repair source behavior evidenced by failed integration check ${failed[0]}.` });
496
+ const repair = addVnextCodeRepair(context, { projectRoot, runId: request.run_id, stage: "code", checkReceiptId: failed[0], originWorkIds, reviewCycle: `MRR-${request.merge_request_id}`, objective: `Repair source behavior evidenced by failed integration check ${failed[0]}.` });
332
497
  const now = context.now();
333
498
  context.db.run("UPDATE merge_requests SET status = 'superseded', checkpoint = 'source_repair_required', last_error_json = ?, completed_at = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ code: "merge_source_repair", replacement_pending: true, failed_receipt_ids: failed, repair_work_id: repair.repair_work_id }), now, now, request.merge_request_id]);
334
499
  releaseMergeLane(context, projectRoot, request, `MERGE ${request.merge_request_id} moved to source repair`);
335
500
  appendFlowRunTimelineEvent(context, project.id, request.run_id, { type: "merge_source_repair_created", merge_request_id: request.merge_request_id, repair_work_id: repair.repair_work_id, cycle: prepared.cycle, failed_receipt_ids: failed });
336
- return { ok: true, run_id: request.run_id, merge_request_id: request.merge_request_id, status: "superseded", source_repair: repair, next: { kind: "start_stage", stage: "code", command: `${flowCommand(context)} stage start ${request.run_id} --stage code --project-root ${JSON.stringify(projectRoot)} --json --progress-jsonl` } };
501
+ return { ok: true, run_id: request.run_id, merge_request_id: request.merge_request_id, status: "superseded", source_repair: repair, next: stageSuccessor(context, { projectRoot, runId: request.run_id, stage: "code", command: runtimeCommand(context, ["stage", "start", request.run_id, "--stage", "code", "--project-root", projectRoot, "--json", "--progress-jsonl"]) }) };
502
+ }
503
+ export function mergeGateFailureDetails(failures, continuation) {
504
+ // A failed check does not establish a source defect. Keep the current MERGE
505
+ // Work until agent diagnosis chooses the explicitly available source route.
506
+ return { failures, ...(failures.length && failures.every(receipt => checkDiagnosisDisposition(receipt) === "diagnosis_required")
507
+ ? { continuation: continuation() } : { outcome: "blocked", recoverable: false }) };
508
+ }
509
+ function mergePrompt(context, input, checks, settings) { const pause = stagePauseCommand(context, { runId: input.run.id, stage: "merge", workId: input.request.executor_work_id, projectRoot: input.projectRoot }); const repair = repairCommand(context, input.request, input.projectRoot); return ["<stage_identity>", `- RUN: ${input.run.id}`, `- MERGE request: ${input.request.merge_request_id}`, `- Work: ${input.request.executor_work_id}`, "- stage: merge", "</stage_identity>", "", "<trusted_runtime_context>", `- integration workspace: ${input.request.target_workspace}`, `- source workspace: ${input.request.source_workspace}`, `- frozen source commit: ${input.request.source_commit}`, `- target branch: ${input.request.target_branch}`, `- execution target baseline: ${input.request.execution_target_head}`, `- queue route: ${input.request.execution_route}`, `- delivery: ${JSON.stringify(settings.merge_delivery)}`, `- cleanup: ${JSON.stringify(settings.merge_cleanup)}`, "These facts and the acquired project integration lane were established by dd-flow. Do not repeat discovery and do not run git merge/rebase/squash yourself.", "</trusted_runtime_context>", "", "<effective_merge_gate>", ...checks.map((check) => `- ${check.canonical_ref ?? check.id}: ${check.command} — ${check.purpose}`), "</effective_merge_gate>", "", "<execution_contract>", `1. Run this exact standalone command first: ${applyCommand(context, input.request, input.projectRoot)}`, "2. If it reports conflicts, resolve only the actual unmerged paths in the integration workspace. Do not repeat merge apply and do not edit product code, tests, documentation or configuration merely to make a gate pass.", `3. Write the compact semantic result to ${input.resultPath}:`, "```json", JSON.stringify({ schema_id: "dd-flow/merge-result@1", outcome: "completed", summary: "What was integrated.", conflict_resolution: "How material conflicts were resolved, or empty when none.", verification_summary: "Why the integrated result is ready for deterministic checks.", residual_risks: [] }, null, 2), "```", `4. Finish with this exact standalone command and wait for all progress: ${finishCommand(context, input.run.id, input.request, input.projectRoot)}`, `If a gate fails, read its receipt and logs and determine the cause. For a product defect use ${repair}; it restores the target baseline and opens CODE → independent CODE-REVIEW → replacement MRG. For an environment failure restore the declared environment in this MERGE and write the returned recovery JSON file and use continuation.command_after_input. The CLI reruns the real gate and retains the old receipt. Never edit product code in the integration target to make a gate pass.`, "If a material conflict has no reasonable answer in accepted evidence, pause this same Work by writing the question file and then running the separate standalone command below, ask the returned user_message, then use the exact resume command returned by CLI:", "```sh", stagePauseCommandTemplate(pause), "```", "</execution_contract>", ""].join("\n"); }
510
+ function mergeReport(context, run, request, semantic, receipts) {
511
+ const now = context.now();
512
+ const startedAt = request.lock_acquired_at ?? request.created_at;
513
+ const cleanup = cleanupReceiptPath(run);
514
+ return { schema_id: "dd-flow/stage-report@2", run_id: run.id, stage, generated_at: now, verdict: "done", summary: semantic.summary,
515
+ semantic: { result: semantic.summary, acceptance: ["source_commit_frozen", "integration_commit_created", "merge_gate_passed", "delivery_confirmed"], changed_files: [], checks: receipts.map((item) => item.command), evidence: [applyReceiptPath(context, request), ...receipts.map((item) => item.receipt_path), ...(fs.existsSync(cleanup) ? [cleanup] : [])], next_action: "merge_completed", merge: { merge_request_id: request.merge_request_id, work_id: request.executor_work_id, protocols: JSON.parse(request.protocol_ids_json), source_commit: request.source_commit, execution_target_head: request.execution_target_head, accepted_tree: request.accepted_tree, integration_commit: request.integration_commit, route: request.execution_route, delivery: executionSettings(run).merge_delivery, cleanup: executionSettings(run).merge_cleanup, verification_summary: semantic.verification_summary, residual_risks: semantic.residual_risks } },
516
+ mechanical: { started_at: startedAt, finished_at: now, wall_clock_ms: Math.max(0, Date.parse(now) - Date.parse(startedAt)), git: gitFacts(request.target_workspace), queue: queueStatus(context, request) },
517
+ artifacts: { json: "stage-report.json", markdown: "stage-report.md", html: "stage-report.html" }, validation: { status: "passed" } };
337
518
  }
338
- function mergePrompt(context, input) { const pause = `${flowCommand(context)} stage pause ${input.run.id} --stage merge --work ${input.request.executor_work_id} --project-root ${JSON.stringify(input.projectRoot)} --question-stdin --json`; const repair = `${flowCommand(context)} merge repair ${input.request.merge_request_id} --project-root ${JSON.stringify(input.projectRoot)} --json`; return ["<stage_identity>", `- RUN: ${input.run.id}`, `- MERGE request: ${input.request.merge_request_id}`, `- Work: ${input.request.executor_work_id}`, "- stage: merge", "</stage_identity>", "", "<trusted_runtime_context>", `- integration workspace: ${input.request.target_workspace}`, `- source workspace: ${input.request.source_workspace}`, `- frozen source commit: ${input.request.source_commit}`, `- target branch: ${input.request.target_branch}`, `- execution target baseline: ${input.request.execution_target_head}`, `- queue route: ${input.request.execution_route}`, `- delivery: ${JSON.stringify(executionSettings(input.run).merge_delivery)}`, `- cleanup: ${JSON.stringify(executionSettings(input.run).merge_cleanup)}`, "These facts and the acquired project integration lane were established by dd-flow. Do not repeat discovery and do not run git merge/rebase/squash yourself.", "</trusted_runtime_context>", "", "<effective_merge_gate>", ...effectiveMergeChecks(input.run, input.request).map((check) => `- ${check.canonical_ref ?? check.id}: ${check.command} — ${check.purpose}`), "</effective_merge_gate>", "", "<execution_contract>", `1. Run this exact standalone command first: ${applyCommand(context, input.request, input.projectRoot)}`, "2. If it reports conflicts, resolve only the actual unmerged paths in the integration workspace. Do not repeat merge apply and do not edit product code, tests, documentation or configuration merely to make a gate pass.", `3. Write the compact semantic result to ${input.resultPath}:`, "```json", JSON.stringify({ schema_id: "dd-flow/merge-result@1", outcome: "completed", summary: "What was integrated.", conflict_resolution: "How material conflicts were resolved, or empty when none.", verification_summary: "Why the integrated result is ready for deterministic checks.", residual_risks: [] }, null, 2), "```", `4. Finish with this exact standalone command and wait for all progress: ${finishCommand(context, input.run.id, input.request, input.projectRoot)}`, `If a gate fails, inspect only the returned receipt/logs, then run this exact source-repair command: ${repair}. It restores the integration target to baseline and creates a CODE → independent CODE-REVIEW → replacement MRG cycle. Do not repair the integration target and do not retry this MRG.`, "If a material conflict has no reasonable answer in accepted evidence, pause this same Work with the exact heredoc below, ask the returned user_message, then use the exact resume command returned by CLI:", "```sh", stagePauseCommandTemplate(pause), "```", "</execution_contract>", ""].join("\n"); }
339
- function mergeReport(context, run, request, semantic, receipts) { const now = context.now(); const cleanup = cleanupReceiptPath(run); return { schema_id: "dd-flow/stage-report@2", run_id: run.id, stage, generated_at: now, verdict: "done", summary: semantic.summary, semantic: { result: semantic.summary, acceptance: ["source_commit_frozen", "integration_commit_created", "merge_gate_passed", "delivery_confirmed"], changed_files: [], checks: receipts.map((item) => item.command), evidence: [applyReceiptPath(context, request), ...receipts.map((item) => item.receipt_path), ...(fs.existsSync(cleanup) ? [cleanup] : [])], next_action: "merge_completed", merge: { merge_request_id: request.merge_request_id, work_id: request.executor_work_id, protocols: JSON.parse(request.protocol_ids_json), source_commit: request.source_commit, execution_target_head: request.execution_target_head, accepted_tree: request.accepted_tree, integration_commit: request.integration_commit, route: request.execution_route, delivery: executionSettings(run).merge_delivery, cleanup: executionSettings(run).merge_cleanup, verification_summary: semantic.verification_summary, residual_risks: semantic.residual_risks } }, mechanical: { started_at: request.lock_acquired_at, finished_at: now, git: gitFacts(request.target_workspace), queue: queueStatus(context, request) }, artifacts: { json: "stage-report.json", markdown: "stage-report.md", html: "stage-report.html" }, validation: { status: "passed" } }; }
340
519
  function effectiveMergeChecks(run, request) { return readFrozenMergeGate(path.join(requireHome(run), stageDir, "merge-gate.json"), request.merge_request_id).checks; }
341
520
  function planChecks(workspace, protocols) { return protocols.flatMap((protocol) => { const file = path.join(workspace, ".memory-bank", "protocol", protocol, "plan.json"); if (!fs.existsSync(file))
342
- return []; const plan = JSON.parse(fs.readFileSync(file, "utf8")); return (plan.checks ?? []).filter((check) => check.availability === "available").map((check) => ({ ...check, canonical_ref: `${protocol}/${check.id}` })); }); }
521
+ throw new AppError("merge_plan_missing", "MERGE requires every accepted PLAN", 2, { protocol_id: protocol }); const plan = JSON.parse(fs.readFileSync(file, "utf8")); return (plan.checks ?? []).map((check) => ({ ...check, canonical_ref: `${protocol}/${check.id}` })); }); }
343
522
  function validateMergeAcceptance(workspace, protocols, checks) { const known = new Set(checks.map((check) => check.canonical_ref)); const missing = mergeAcceptanceRefs(workspace, protocols).filter((ref) => !known.has(ref)); if (missing.length)
344
523
  throw new AppError("merge_acceptance_invalid", "A merge acceptance criterion references a missing check", 2, { check_refs: missing }); }
345
- function freezeMergeGate(file, input) { const canonical = JSON.stringify(input.checks); const hash = cryptoHash(canonical); if (fs.existsSync(file)) {
524
+ function freezeMergeGate(file, input) { const canonical = JSON.stringify({ checks: input.checks, acceptance_refs: [...input.acceptanceRefs].sort() }); const hash = cryptoHash(canonical); if (fs.existsSync(file)) {
346
525
  const existing = JSON.parse(fs.readFileSync(file, "utf8"));
347
- if (existing.run_id !== input.runId || existing.checks_hash !== hash)
526
+ if (existing.run_id !== input.runId || existing.gate_hash !== hash)
348
527
  throw new AppError("merge_gate_freeze_conflict", "MERGE gate is already frozen with different checks", 2, { file });
349
528
  return;
350
- } fs.writeFileSync(file, `${JSON.stringify({ schema_id: "dd-flow/merge-gate@1", run_id: input.runId, protocols: input.protocols, checks: input.checks, checks_hash: hash, profile_hash: input.profileHash, frozen_at: input.now }, null, 2)}\n`); }
529
+ } fs.writeFileSync(file, `${JSON.stringify({ schema_id: "dd-flow/merge-gate@2", run_id: input.runId, protocols: input.protocols, checks: input.checks, acceptance_refs: [...input.acceptanceRefs].sort(), gate_hash: hash, profile_hash: input.profileHash, frozen_at: input.now }, null, 2)}\n`); }
351
530
  function readFrozenMergeGate(file, requestId) { try {
352
531
  const value = JSON.parse(fs.readFileSync(file, "utf8"));
353
- if (value.schema_id !== "dd-flow/merge-gate@1" || !Array.isArray(value.checks) || value.checks_hash !== cryptoHash(JSON.stringify(value.checks)))
532
+ if (value.schema_id !== "dd-flow/merge-gate@2" || !Array.isArray(value.checks) || !Array.isArray(value.acceptance_refs) || !value.acceptance_refs.every(ref => typeof ref === "string") || typeof value.gate_hash !== "string" || value.gate_hash !== cryptoHash(JSON.stringify({ checks: value.checks, acceptance_refs: [...value.acceptance_refs].sort() })))
354
533
  throw new Error("invalid gate");
355
- return { checks: value.checks };
534
+ return { checks: value.checks, acceptanceRefs: [...value.acceptance_refs].sort(), profileHash: value.profile_hash ?? null, gateHash: value.gate_hash };
356
535
  }
357
536
  catch (cause) {
358
537
  throw new AppError("merge_gate_missing", "MERGE gate is missing or invalid", 1, { merge_request_id: requestId, file, cause: String(cause) });
359
538
  } }
539
+ function retainMergeProfile(root, role, workspace) {
540
+ const { profile, hash, file: profileFile } = readCodeCheckProfile(workspace);
541
+ const file = path.join(root, `check-profile-${role}.json`);
542
+ const bytes = `${JSON.stringify({ schema_id: "dd-flow/retained-check-profile@1", profile, profile_sha256: hash, profile_bytes: profile ? fs.readFileSync(profileFile, "utf8") : null }, null, 2)}\n`;
543
+ if (fs.existsSync(file)) {
544
+ if (fs.readFileSync(file, "utf8") !== bytes)
545
+ throw new AppError("check_profile_drift", "Retained MERGE profile changed", 2, { role, file });
546
+ }
547
+ else
548
+ fs.writeFileSync(file, bytes, { flag: "wx" });
549
+ }
550
+ function frozenGateRefs(gate) { return [...new Set([...gate.checks.map(check => check.canonical_ref ?? check.id), ...gate.acceptanceRefs])].sort(); }
551
+ function writeMergeGateAcceptance(context, file, request, gate, acceptedTree, receipts) { const persisted = new Map(checkReceipts(context, { projectId: request.project_id, runId: request.run_id }).map(receipt => [receipt.id, receipt])); const value = { schema_id: "dd-flow/merge-gate-acceptance@2", merge_request_id: request.merge_request_id, run_id: request.run_id, work_id: request.executor_work_id, gate_hash: gate.gateHash, profile_hash: gate.profileHash, accepted_tree: acceptedTree, receipts: receipts.map(receipt => ({ id: receipt.id, execution_refs: [...(persisted.get(receipt.id)?.check_refs ?? receipt.check_refs)].sort(), binding_refs: [...receipt.check_refs].sort(), input_hash: receipt.input_hash })) }; const temporary = `${file}.tmp-${process.pid}-${crypto.randomUUID()}`; fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`); fs.renameSync(temporary, file); }
552
+ function readMergeGateAcceptance(context, file, request, gate) { try {
553
+ const value = JSON.parse(fs.readFileSync(file, "utf8"));
554
+ if (value.schema_id !== "dd-flow/merge-gate-acceptance@2" || value.merge_request_id !== request.merge_request_id || value.run_id !== request.run_id || value.work_id !== request.executor_work_id || value.gate_hash !== gate.gateHash || value.profile_hash !== gate.profileHash || value.accepted_tree !== request.accepted_tree || !Array.isArray(value.receipts) || !value.receipts.every(receipt => receipt && typeof receipt.id === "string" && typeof receipt.input_hash === "string" && Array.isArray(receipt.execution_refs) && receipt.execution_refs.every(ref => typeof ref === "string") && Array.isArray(receipt.binding_refs) && receipt.binding_refs.every(ref => typeof ref === "string")))
555
+ return { valid: false, receipts: [] };
556
+ const all = checkReceipts(context, { projectId: request.project_id, runId: request.run_id });
557
+ const receipts = value.receipts.map(saved => all.find(receipt => receipt.id === saved.id && receipt.input_hash === saved.input_hash && JSON.stringify([...receipt.check_refs].sort()) === JSON.stringify(saved.execution_refs))).filter((receipt) => Boolean(receipt));
558
+ const bindingRefs = value.receipts.flatMap(receipt => receipt.binding_refs);
559
+ const bindings = new Set(bindingRefs);
560
+ const expected = frozenGateRefs(gate);
561
+ if (receipts.length !== value.receipts.length || bindingRefs.length !== bindings.size || !receipts.every(checkReceiptEvidenceValid) || JSON.stringify([...bindings].sort()) !== JSON.stringify(expected) || gitValue(request.target_workspace, ["write-tree"]) !== value.accepted_tree)
562
+ return { valid: false, receipts: [] };
563
+ return { valid: true, receipts: receipts.map((receipt, index) => ({ ...receipt, check_refs: value.receipts[index].binding_refs })) };
564
+ }
565
+ catch {
566
+ return { valid: false, receipts: [] };
567
+ } }
360
568
  function cryptoHash(value) { return crypto.createHash("sha256").update(value).digest("hex"); }
361
569
  function mergeAcceptanceRefs(workspace, protocols) { return protocols.flatMap((protocol) => { const file = path.join(workspace, ".memory-bank", "protocol", protocol, "plan.json"); if (!fs.existsSync(file))
362
570
  return []; const plan = JSON.parse(fs.readFileSync(file, "utf8")); return (plan.acceptance ?? []).filter((entry) => entry.gate === "merge").flatMap((entry) => (entry.check_refs ?? []).map((ref) => `${protocol}/${ref}`)); }); }
@@ -365,10 +573,14 @@ function protocolIds(home) { const root = path.join(home, "03-plan"); if (!fs.ex
365
573
  function workspaceRoute(home) { const file = path.join(home, "02-protocolize", "workspace-route.json"); const value = JSON.parse(fs.readFileSync(file, "utf8")); if (!value.policy?.integration_branch)
366
574
  throw new AppError("workspace_route_missing", "MERGE requires the frozen integration branch", 1, { file }); return { integration_branch: value.policy.integration_branch }; }
367
575
  function executionSettings(run) { return JSON.parse(run.index_json).execution_profile?.settings ?? {}; }
368
- async function runBootstrap(context, run, cwd, mergeId, progress) {
576
+ function requireBootstrapCommand(run) {
369
577
  const command = executionSettings(run).code_bootstrap?.command;
370
578
  if (!command)
371
- throw new AppError("execution_profile_invalid", "MERGE requires a frozen workspace bootstrap command", 2);
579
+ throw new AppError("execution_profile_invalid", "MERGE requires an evidenced bootstrap command in the frozen RUN profile; configure code_bootstrap before starting a code-ready RUN", 2);
580
+ return command;
581
+ }
582
+ async function runBootstrap(context, run, cwd, mergeId, progress) {
583
+ const command = requireBootstrapCommand(run);
372
584
  const result = await runWorkspaceBootstrap(context, { projectId: run.project_id, runId: run.id, runHome: requireHome(run), workspaceRoot: cwd, command, artifactDir: `${stageDir}/${mergeId}/readiness`, progress });
373
585
  if (result.exit_code !== 0 || result.error)
374
586
  throw new AppError("merge_bootstrap_failed", "Integrated target bootstrap failed; inspect its retained logs", 2, { command, ...result });
@@ -400,7 +612,12 @@ function releaseMergeLane(context, projectRoot, request, reason) {
400
612
  try {
401
613
  releaseLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, reason });
402
614
  }
403
- catch { /* An expired lease cannot invalidate an already completed MERGE. */ }
615
+ catch (error) {
616
+ // An absent/replaced lease is already no longer ours. Storage and other
617
+ // failures must remain visible so a repeated operation can release it.
618
+ if (!(error instanceof AppError) || !["lane_lock_not_active", "lane_lock_owned_by_other_worker"].includes(error.code))
619
+ throw error;
620
+ }
404
621
  }
405
622
  function queueAhead(context, request) { return context.db.get("SELECT COUNT(*) AS count FROM merge_requests WHERE project_id = ? AND status NOT IN ('completed','failed','cancelled','superseded') AND (created_at < ? OR (created_at = ? AND merge_request_id < ?))", [request.project_id, request.created_at, request.created_at, request.merge_request_id])?.count ?? 0; }
406
623
  function queueStatus(context, request) { return { position: queueAhead(context, request) + 1, requests_ahead: queueAhead(context, request), status: request.status, route: request.execution_route }; }
@@ -409,15 +626,17 @@ function requireOwnedActiveRequest(context, input) { const project = requireProj
409
626
  function assertExecutionBaseline(request) { const head = gitValue(request.target_workspace, ["rev-parse", "HEAD"], true); if (request.checkpoint === "baseline_locked" && head !== request.execution_target_head)
410
627
  throw new AppError("merge_target_changed", "Integration target changed after the lane baseline was locked", 1, { expected: request.execution_target_head, actual: head }); }
411
628
  function recovery(context, request, code, detail) { context.db.run("UPDATE merge_requests SET status = 'recovery_required', last_error_json = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ code, detail }), context.now(), request.merge_request_id]); throw new AppError("merge_recovery_required", "MERGE mutation could not be reconciled automatically", 1, { merge_request_id: request.merge_request_id, code, detail }); }
412
- function commitPendingSource(cwd, runId, ignored) { const paths = meaningfulStatus(cwd, ignored).map(statusPath); if (!paths.length)
629
+ function commitPendingSource(cwd, runId, ignored) { const paths = meaningfulStatus(cwd, ignored); if (!paths.length)
413
630
  return; git(cwd, ["add", "-A", "--", ...paths]); git(cwd, ["commit", "-m", `chore(${runId.toLowerCase()}): freeze accepted source`]); }
414
- function commitIntegration(cwd, runId, ignored) { const mergeHead = fs.existsSync(path.join(gitDirectory(cwd), "MERGE_HEAD")); const paths = meaningfulStatus(cwd, ignored).map(statusPath); if (!mergeHead && !paths.length)
631
+ function commitIntegration(cwd, runId, ignored) { const mergeHead = fs.existsSync(path.join(gitDirectory(cwd), "MERGE_HEAD")); const paths = meaningfulStatus(cwd, ignored); if (!mergeHead && !paths.length)
415
632
  return; if (paths.length)
416
633
  git(cwd, ["add", "-A", "--", ...paths]); git(cwd, ["commit", "-m", `merge(${runId.toLowerCase()}): integrate accepted source`]); }
417
- function ignoredGitPaths(context, projectId) { return [readProjectConfig(context, projectId).dashboard.markdown_path.replaceAll("\\", "/")]; }
418
- function meaningfulStatus(cwd, ignored) { const result = spawnSync("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd, encoding: "utf8" }); if (result.status !== 0)
419
- return []; return result.stdout.split(/\r?\n/).filter(Boolean).filter((line) => !ignored.includes(statusPath(line))); }
420
- function statusPath(line) { const value = line.slice(3); const renamed = value.includes(" -> ") ? value.slice(value.lastIndexOf(" -> ") + 4) : value; return renamed.replace(/^"|"$/g, ""); }
634
+ function ignoredGitPaths(context, projectId) {
635
+ // Shared workspace classification omits only the exact untracked qualified
636
+ // hook. Tracked or mixed user hooks remain ordinary product changes.
637
+ return [readProjectConfig(context, projectId).dashboard.markdown_path.replaceAll("\\", "/")];
638
+ }
639
+ function meaningfulStatus(cwd, ignored) { return workspaceChangedPaths(cwd).filter((item) => !ignored.includes(item)); }
421
640
  function unmerged(cwd) { const value = gitValue(cwd, ["diff", "--name-only", "--diff-filter=U"], true); return value.split(/\r?\n/).filter(Boolean); }
422
641
  function gitDirectory(cwd) { const value = gitValue(cwd, ["rev-parse", "--git-dir"]); return path.resolve(cwd, value); }
423
642
  function git(cwd, args) { try {
@@ -473,5 +692,8 @@ function findRootWork(context, projectId, runId) { const work = context.db.get("
473
692
  throw new AppError("runtime_missing", "vNext RUN has no root Work", 1); return work; }
474
693
  function requireRootWork(context, projectId, runId) { const work = findRootWork(context, projectId, runId); if (work.status !== "running")
475
694
  throw new AppError("runtime_missing", "vNext RUN has no running root Work", 1); return work; }
476
- function applyCommand(context, request, projectRoot) { return `${flowCommand(context)} merge apply ${request.merge_request_id} --work ${request.executor_work_id} --project-root ${JSON.stringify(projectRoot)} --json --progress-jsonl`; }
477
- function finishCommand(context, runId, request, projectRoot) { return `${flowCommand(context)} stage finish ${runId} --stage merge --request ${request.merge_request_id} --work ${request.executor_work_id} --project-root ${JSON.stringify(projectRoot)} --json --progress-jsonl`; }
695
+ function applyCommand(context, request, projectRoot) { return managedLifecycleCommand(context, runtimeCommand(context, ["merge", "apply", request.merge_request_id, "--work", request.executor_work_id, "--project-root", projectRoot, "--json", "--progress-jsonl"])); }
696
+ function repairCommand(context, request, projectRoot) { return managedLifecycleCommand(context, runtimeCommand(context, ["merge", "repair", request.merge_request_id, "--project-root", projectRoot, "--json"])); }
697
+ export function finishCommand(context, runId, request, projectRoot, retryCheckId) {
698
+ return managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId, "--stage", "merge", "--request", request.merge_request_id, "--work", request.executor_work_id, "--project-root", projectRoot, "--json", "--progress-jsonl", ...checkRetryArgs(context, projectRoot, runId, retryCheckId)]));
699
+ }