@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.140
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +733 -0
- package/README.md +143 -1
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +376 -0
- package/dist/cli/command-metadata.js +197 -0
- package/dist/cli/command-option-descriptions.js +191 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +328 -18
- package/dist/cli/hook-ingress.js +78 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +44 -0
- package/dist/cli/memory-init-invocation.js +78 -0
- package/dist/cli/memory-init.js +240 -0
- package/dist/cli/memory-storage-admission.js +53 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +80 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +71 -0
- package/dist/cli/run-cli.js +1689 -545
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +71 -2
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +724 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +129 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +507 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +354 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +750 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +13 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +654 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +101 -0
- package/dist/harness-runtime/lib/native-children.d.mts +16 -0
- package/dist/harness-runtime/lib/native-children.mjs +113 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +14 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +116 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +205 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +28 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +49 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +3 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +11 -8
- package/dist/services/check-runner-evidence.js +168 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +25 -62
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1156 -107
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +13 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +115 -0
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +202 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +105 -0
- package/dist/services/descriptor-run-start.js +449 -0
- package/dist/services/descriptor-stage-coordinator.js +197 -0
- package/dist/services/descriptor-stage-settlement.js +222 -0
- package/dist/services/descriptor-stage-start.js +139 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +294 -153
- package/dist/services/eval-snapshots.js +1215 -116
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +308 -0
- package/dist/services/flow-catalog.js +21 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +250 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hooks.js +771 -325
- package/dist/services/hybrid-contracts.js +483 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +183 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2007 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +475 -55
- package/dist/services/memory-accepted-composition.js +77 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +320 -0
- package/dist/services/memory-compose-handler.js +258 -0
- package/dist/services/memory-composition-acceptance.js +199 -0
- package/dist/services/memory-composition-graph.js +134 -0
- package/dist/services/memory-composition-registration.js +146 -0
- package/dist/services/memory-composition-work-custody.js +151 -0
- package/dist/services/memory-composition-work-results.js +96 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +30 -0
- package/dist/services/memory-corrected-review-findings.js +106 -0
- package/dist/services/memory-corrected-review-inputs.js +210 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +170 -0
- package/dist/services/memory-corrected-review-registration.js +191 -0
- package/dist/services/memory-correction-check-receipt.js +219 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +435 -0
- package/dist/services/memory-finish-handler.js +215 -0
- package/dist/services/memory-finish-integration.js +280 -0
- package/dist/services/memory-finish-readiness.js +122 -0
- package/dist/services/memory-git-integration.js +151 -0
- package/dist/services/memory-historical-corrected-review.js +206 -0
- package/dist/services/memory-historical-original-review.js +130 -0
- package/dist/services/memory-historical-repair-registration.js +99 -0
- package/dist/services/memory-historical-second-review.js +128 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +89 -0
- package/dist/services/memory-init-controller-start.js +84 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +136 -0
- package/dist/services/memory-init-lint-capability.js +185 -0
- package/dist/services/memory-init-native-capacity.js +296 -0
- package/dist/services/memory-init-preflight.js +330 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +157 -0
- package/dist/services/memory-init-request-input.js +66 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +98 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +81 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +178 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +156 -0
- package/dist/services/memory-lint-config.js +85 -0
- package/dist/services/memory-native-review-copy.js +165 -0
- package/dist/services/memory-normal-flow-entry.js +75 -0
- package/dist/services/memory-ordinary-defaults.js +112 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +259 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +451 -0
- package/dist/services/memory-repair-history.js +491 -0
- package/dist/services/memory-repair-physical-binding.js +312 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +160 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +794 -0
- package/dist/services/memory-repair-registration.js +239 -0
- package/dist/services/memory-repair-work-baseline.js +413 -0
- package/dist/services/memory-repair-work-completion.js +97 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +89 -0
- package/dist/services/memory-repair-workspace.js +252 -0
- package/dist/services/memory-repair-writer-copy.js +735 -0
- package/dist/services/memory-result-binding.js +173 -0
- package/dist/services/memory-retained-review-authority.js +129 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +156 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +171 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +200 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +108 -0
- package/dist/services/memory-review-work-custody.js +124 -0
- package/dist/services/memory-review-work-input.js +72 -0
- package/dist/services/memory-review-work-results.js +73 -0
- package/dist/services/memory-second-corrected-review-inputs.js +165 -0
- package/dist/services/memory-second-correction-plan.js +76 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +28 -0
- package/dist/services/memory-stage-results.js +199 -0
- package/dist/services/memory-verification-acceptance.js +135 -0
- package/dist/services/memory-verification-checks.js +252 -0
- package/dist/services/memory-verification-receipt.js +159 -0
- package/dist/services/memory-verify-handler.js +135 -0
- package/dist/services/memory-work-result-preparation.js +63 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +161 -35
- package/dist/services/migrations.js +37 -16
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +37 -17
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +385 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +560 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +142 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +42 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +411 -0
- package/dist/services/run-control.js +1056 -0
- package/dist/services/run-controller-adapter.js +399 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +208 -0
- package/dist/services/run-controller-recovery.js +289 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1205 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +77 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +489 -0
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +1019 -247
- package/dist/services/runtime-budget.js +430 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +599 -0
- package/dist/services/runtime-scope-stop.js +210 -0
- package/dist/services/runtime-scope-worker.js +287 -0
- package/dist/services/runtime-service.js +96 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +35 -69
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-lifecycle.js +100 -108
- package/dist/services/stage-pause.js +89 -75
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +32 -23
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +374 -0
- package/dist/services/vnext-code-review.js +223 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +311 -19
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +279 -127
- package/dist/services/vnext-plan.js +229 -104
- package/dist/services/vnext-protocolize.js +411 -86
- package/dist/services/vnext-specify.js +119 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1627 -199
- package/dist/services/workspace-bootstrap.js +52 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +98 -44
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +17 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/database.js +891 -39
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +89 -0
- package/dist/storage/writer-migration.js +85 -0
- package/package.json +43 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -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 { assertSourceRepairEligible, checkRepairDisposition } 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 {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
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,
|
|
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 { assertStageStartHookEvent } from "./hooks.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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
99
|
-
requestId
|
|
100
|
-
context
|
|
101
|
-
context
|
|
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
|
|
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
|
|
119
|
-
|
|
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 =
|
|
134
|
-
if (
|
|
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
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,54 +254,81 @@ 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
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
-
|
|
219
|
-
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
301
|
+
const gate = prepared.gate, checks = gate.checks;
|
|
302
|
+
if (input.retryCheckId)
|
|
303
|
+
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "check_retry_requested", check_receipt_id: input.retryCheckId, stage, reason: input.retryReason ?? "operator retry" });
|
|
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);
|
|
233
316
|
const failed = receipts.filter((receipt) => receipt.status !== "passed");
|
|
234
317
|
if (failed.length) {
|
|
235
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]);
|
|
236
|
-
|
|
319
|
+
const sourceRepair = failed.every(receipt => checkRepairDisposition(receipt) === "source_repair");
|
|
320
|
+
throw new AppError("merge_gate_failed", "Integrated target checks failed. Only candidate-source-backed failures permit source repair; tooling or unknown evidence requires diagnosis/settlement first.", 2, { failures: failed, ...(sourceRepair ? { repair_command: repairCommand(context, request, projectRoot), continuation: { ...recoveryEvidenceInput(run.run_root, failed[0].id), command_after_input: finishCommand(context, run.id, request, projectRoot, failed[0].id) } } : { outcome: "blocked", recoverable: false }) });
|
|
237
321
|
}
|
|
238
|
-
const requiredRefs = mergeAcceptanceRefs(run.workspace_root, JSON.parse(request.protocol_ids_json));
|
|
239
322
|
const passedRefs = new Set(receipts.flatMap((receipt) => receipt.check_refs));
|
|
240
|
-
const missingRefs =
|
|
323
|
+
const missingRefs = frozenGateRefs(gate).filter((ref) => !passedRefs.has(ref));
|
|
241
324
|
if (missingRefs.length)
|
|
242
|
-
throw new AppError("merge_acceptance_unproven", "Current MERGE receipts do not cover every merge
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
325
|
+
throw new AppError("merge_acceptance_unproven", "Current MERGE receipts do not cover every frozen merge-gate reference", 2, { missing_check_refs: missingRefs });
|
|
326
|
+
if (!request.integration_commit) {
|
|
327
|
+
const acceptedTree = gitValue(request.target_workspace, ["write-tree"]);
|
|
328
|
+
writeMergeGateAcceptance(context, acceptancePath, request, gate, acceptedTree, receipts);
|
|
329
|
+
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]);
|
|
330
|
+
request = requireRequest(context, request.merge_request_id);
|
|
331
|
+
}
|
|
246
332
|
if (!request.integration_commit) {
|
|
247
333
|
commitIntegration(request.target_workspace, run.id, ignoredGitPaths(context, project.id));
|
|
248
334
|
const commit = gitValue(request.target_workspace, ["rev-parse", "HEAD"]);
|
|
@@ -253,17 +339,35 @@ export async function finishVnextMerge(context, input) {
|
|
|
253
339
|
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]);
|
|
254
340
|
request = requireRequest(context, request.merge_request_id);
|
|
255
341
|
}
|
|
342
|
+
if (request.integration_commit && request.accepted_tree && gitValue(request.target_workspace, ["rev-parse", `${request.integration_commit}^{tree}`]) !== request.accepted_tree)
|
|
343
|
+
return recovery(context, request, "merge_committed_tree_drift", { accepted_tree: request.accepted_tree, integration_commit: request.integration_commit });
|
|
256
344
|
verifyLocalDelivery(request);
|
|
257
345
|
context.db.run("UPDATE merge_requests SET checkpoint = 'delivered', updated_at = ? WHERE merge_request_id = ?", [context.now(), request.merge_request_id]);
|
|
258
346
|
request = requireRequest(context, request.merge_request_id);
|
|
347
|
+
const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", receipts, semanticFile: prepared.resultPath });
|
|
348
|
+
// Retain exact bounded sources before deleting the feature checkout. A
|
|
349
|
+
// crash here recovers presentation from packets, never repeats the checks.
|
|
350
|
+
context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
|
|
351
|
+
publishVerificationMatrix(context, matrix);
|
|
259
352
|
performConfiguredCleanup(run, request);
|
|
260
|
-
|
|
353
|
+
const acceptedResult = prepared.result.text;
|
|
354
|
+
const executor = context.db.get("SELECT status, result FROM works WHERE work_id = ?", [request.executor_work_id]);
|
|
355
|
+
if (!executor)
|
|
356
|
+
return recovery(context, request, "merge_executor_missing", { work_id: request.executor_work_id });
|
|
357
|
+
if (executor.status === "running")
|
|
358
|
+
await finishWork(context, request.executor_work_id, acceptedResult);
|
|
359
|
+
else if (executor.status !== "completed" || executor.result !== acceptedResult)
|
|
360
|
+
return recovery(context, request, "merge_executor_settlement_mismatch", { work_id: request.executor_work_id, status: executor.status });
|
|
361
|
+
assertRunMutationAllowed(context, project.id, run.id, generation);
|
|
261
362
|
const now = context.now();
|
|
262
363
|
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]);
|
|
263
|
-
const report = mergeReport(context, run, requireRequest(context, request.merge_request_id), semantic, receipts);
|
|
264
364
|
const root = path.join(requireHome(run), stageDir);
|
|
265
|
-
|
|
266
|
-
|
|
365
|
+
const retainedReport = completedMergeReport(context, run, root);
|
|
366
|
+
const report = retainedReport ?? mergeReport(context, run, requireRequest(context, request.merge_request_id), semantic, receipts);
|
|
367
|
+
if (matrix)
|
|
368
|
+
Object.assign(report.semantic, { verification_matrix: matrix.metadata });
|
|
369
|
+
if (!retainedReport)
|
|
370
|
+
commitMergeStageReport(context, run, projectRoot, root, report);
|
|
267
371
|
const rootWork = requireRootWork(context, project.id, run.id);
|
|
268
372
|
finishFanInWork(context, rootWork.work_id, JSON.stringify(report));
|
|
269
373
|
completeFlowRun(context, { projectRoot, runId: run.id, status: "done", verdict: "merge_completed", nextAction: undefined });
|
|
@@ -282,37 +386,100 @@ function ensureMergeStageAttached(context, run, projectRoot) {
|
|
|
282
386
|
function recoverCompletedMergeRun(context, run, projectRoot, request) {
|
|
283
387
|
const root = path.join(requireHome(run), stageDir);
|
|
284
388
|
const reportPath = path.join(root, "stage-report.json");
|
|
285
|
-
|
|
286
|
-
|
|
389
|
+
const retainedReport = completedMergeReport(context, run, root);
|
|
390
|
+
let report;
|
|
391
|
+
if (retainedReport)
|
|
392
|
+
report = retainedReport;
|
|
393
|
+
else if (!fs.existsSync(reportPath)) {
|
|
394
|
+
const semantic = request.result_json ? JSON.parse(request.result_json) : null;
|
|
395
|
+
const gate = readFrozenMergeGate(path.join(root, "merge-gate.json"), request.merge_request_id);
|
|
396
|
+
const accepted = readMergeGateAcceptance(context, path.join(root, "merge-gate-acceptance.json"), request, gate);
|
|
397
|
+
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)
|
|
398
|
+
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 });
|
|
399
|
+
verifyLocalDelivery(request);
|
|
400
|
+
performConfiguredCleanup(run, request);
|
|
401
|
+
report = mergeReport(context, run, request, semantic, accepted.receipts);
|
|
402
|
+
}
|
|
403
|
+
else
|
|
404
|
+
report = JSON.parse(fs.readFileSync(reportPath, "utf8"));
|
|
287
405
|
ensureMergeStageAttached(context, run, projectRoot);
|
|
288
|
-
|
|
406
|
+
if (!retainedReport)
|
|
407
|
+
commitMergeStageReport(context, run, projectRoot, root, report);
|
|
289
408
|
const rootWork = findRootWork(context, run.project_id, run.id);
|
|
290
409
|
const rootStatus = rootWork.status;
|
|
291
410
|
if (rootStatus === "running")
|
|
292
411
|
finishFanInWork(context, rootWork.work_id, fs.readFileSync(reportPath, "utf8"));
|
|
412
|
+
else if (rootStatus !== "completed")
|
|
413
|
+
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 });
|
|
293
414
|
const runStatus = context.db.get("SELECT status FROM runs WHERE project_id = ? AND id = ?", [run.project_id, run.id])?.status;
|
|
294
415
|
if (runStatus !== "done")
|
|
295
416
|
completeFlowRun(context, { projectRoot, runId: run.id, status: "done", verdict: "merge_completed", nextAction: undefined });
|
|
296
417
|
refreshRunWorkProjection(context, run.project_id, run.id);
|
|
297
418
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
419
|
+
function completedMergeReport(context, run, root) {
|
|
420
|
+
const current = requireRun(context, run.project_id, run.id);
|
|
421
|
+
const index = JSON.parse(current.index_json);
|
|
422
|
+
const entry = index.stage_runs?.find(item => item.stage === stage);
|
|
423
|
+
if (entry?.status !== "done")
|
|
424
|
+
return null;
|
|
425
|
+
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]);
|
|
426
|
+
const receipt = rows.map(row => JSON.parse(row.payload_json))
|
|
427
|
+
.find(item => item.run_id === run.id && item.stage === stage && item.attempt === entry.attempt && item.root === root);
|
|
428
|
+
if (receipt) {
|
|
429
|
+
if (crypto.createHash("sha256").update(receipt.bytes.json).digest("hex") !== receipt.sha256)
|
|
430
|
+
throw new AppError("stage_report_invalid", "Accepted MERGE report receipt has invalid bytes", 1, { run_id: run.id });
|
|
431
|
+
publishStageReport(context, run.project_id, run.id, stage, root);
|
|
432
|
+
return JSON.parse(receipt.bytes.json);
|
|
433
|
+
}
|
|
434
|
+
const file = path.join(root, "stage-report.json");
|
|
435
|
+
if (!fs.existsSync(file))
|
|
436
|
+
throw new AppError("merge_report_missing", "Completed MERGE has no accepted report receipt or published report", 1, { run_id: run.id, report_path: file });
|
|
437
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
438
|
+
}
|
|
439
|
+
function commitMergeStageReport(context, run, projectRoot, root, report) {
|
|
440
|
+
const matrix = latestRunVerificationMatrix(context, run.project_id, run.id, stage, "output");
|
|
441
|
+
if (matrix)
|
|
442
|
+
Object.assign(report.semantic, { verification_matrix: matrix.metadata });
|
|
443
|
+
if (JSON.parse(run.index_json).verification_matrix_contract && !matrix)
|
|
444
|
+
throw new AppError("verification_matrix_packet_missing", "Completed marked MERGE lacks its retained final matrix", 1, { run_id: run.id });
|
|
445
|
+
const preparedReport = prepareStageReport(report);
|
|
446
|
+
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 };
|
|
447
|
+
const preparedCompletion = prepareFlowRunStageCompletion(context, completion);
|
|
448
|
+
withStageSettlement(context, () => {
|
|
449
|
+
acceptVerificationMatrix(context, matrix);
|
|
450
|
+
acceptStageReport(context, { projectId: run.project_id, runId: run.id, stage, root, prepared: preparedReport });
|
|
451
|
+
completeFlowRunStage(context, completion, preparedCompletion);
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
export function routeVnextMergeRequest(context, input) {
|
|
455
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
456
|
+
const request = context.db.writeTransaction(() => {
|
|
457
|
+
const request = requireRequest(context, input.requestId);
|
|
458
|
+
if (request.project_id !== project.id || request.status !== "queued")
|
|
459
|
+
throw new AppError("invalid_merge_state", "Only a queued request without an active dispatch may change route", 2, { status: request.status });
|
|
460
|
+
assertRunMutationAllowed(context, request.project_id, request.run_id);
|
|
461
|
+
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]);
|
|
462
|
+
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]);
|
|
463
|
+
return request;
|
|
464
|
+
});
|
|
465
|
+
refreshRunWorkProjection(context, request.project_id, request.run_id);
|
|
466
|
+
return requestView(context, requireRequest(context, request.merge_request_id));
|
|
303
467
|
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
context.db.exec("ROLLBACK");
|
|
306
|
-
throw error;
|
|
307
|
-
} refreshRunWorkProjection(context, request.project_id, request.run_id); return requestView(context, requireRequest(context, request.merge_request_id)); }
|
|
308
468
|
/** Move a failed integration gate back to the source CODE/CODE-REVIEW loop. */
|
|
309
469
|
export async function repairVnextMerge(context, input) {
|
|
310
470
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
311
471
|
const project = requireProjectByRoot(context, projectRoot);
|
|
312
472
|
const request = requireRequest(context, input.requestId);
|
|
473
|
+
const generation = assertRunMutationAllowed(context, project.id, request.run_id);
|
|
474
|
+
if (request.project_id === project.id && request.status === "superseded" && errorCode(request) === "merge_source_repair") {
|
|
475
|
+
const saved = JSON.parse(request.last_error_json);
|
|
476
|
+
releaseMergeLane(context, projectRoot, request, `MERGE ${request.merge_request_id} moved to source repair`);
|
|
477
|
+
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"]) }) };
|
|
478
|
+
}
|
|
313
479
|
if (request.project_id !== project.id || request.status !== "action_required" || errorCode(request) !== "merge_gate_failed") {
|
|
314
480
|
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) });
|
|
315
481
|
}
|
|
482
|
+
assertSourceRepairEligible(checkReceipts(context, { projectId: project.id, runId: request.run_id }).filter(receipt => failedReceiptIds(request).includes(receipt.id)));
|
|
316
483
|
abortIntegration(request);
|
|
317
484
|
const originWorkIds = sourceCodeWorkIds(context, request.project_id, request.run_id);
|
|
318
485
|
if (!originWorkIds.length)
|
|
@@ -322,37 +489,77 @@ export async function repairVnextMerge(context, input) {
|
|
|
322
489
|
throw new AppError("runtime_missing", "MERGE failure has no durable failed receipt", 1, { merge_request_id: request.merge_request_id });
|
|
323
490
|
// Settle the child while its canonical result path still exists; the next
|
|
324
491
|
// step archives the entire MERGE attempt as immutable evidence.
|
|
325
|
-
|
|
492
|
+
const executor = context.db.get("SELECT status FROM works WHERE work_id = ?", [request.executor_work_id]);
|
|
493
|
+
if (executor?.status !== "failed")
|
|
494
|
+
await failWork(context, request.executor_work_id, `Superseded by source repair after failed integration gate ${failed[0]}.`);
|
|
495
|
+
assertRunMutationAllowed(context, project.id, request.run_id, generation);
|
|
326
496
|
const prepared = prepareVnextMergeSourceRepairAttempt(context, { projectRoot, runId: request.run_id, mergeRequestId: request.merge_request_id });
|
|
327
|
-
const repair = addVnextCodeRepair(context, { projectRoot, runId: request.run_id, checkReceiptId: failed[0], originWorkIds, objective: `Repair source behavior evidenced by failed integration check ${failed[0]}.` });
|
|
497
|
+
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]}.` });
|
|
328
498
|
const now = context.now();
|
|
329
499
|
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]);
|
|
330
500
|
releaseMergeLane(context, projectRoot, request, `MERGE ${request.merge_request_id} moved to source repair`);
|
|
331
501
|
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 });
|
|
332
|
-
return { ok: true, run_id: request.run_id, merge_request_id: request.merge_request_id, status: "superseded", source_repair: repair, next: {
|
|
502
|
+
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"]) }) };
|
|
503
|
+
}
|
|
504
|
+
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"); }
|
|
505
|
+
function mergeReport(context, run, request, semantic, receipts) {
|
|
506
|
+
const now = context.now();
|
|
507
|
+
const startedAt = request.lock_acquired_at ?? request.created_at;
|
|
508
|
+
const cleanup = cleanupReceiptPath(run);
|
|
509
|
+
return { schema_id: "dd-flow/stage-report@2", run_id: run.id, stage, generated_at: now, verdict: "done", summary: semantic.summary,
|
|
510
|
+
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 } },
|
|
511
|
+
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) },
|
|
512
|
+
artifacts: { json: "stage-report.json", markdown: "stage-report.md", html: "stage-report.html" }, validation: { status: "passed" } };
|
|
333
513
|
}
|
|
334
|
-
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"); }
|
|
335
|
-
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" } }; }
|
|
336
514
|
function effectiveMergeChecks(run, request) { return readFrozenMergeGate(path.join(requireHome(run), stageDir, "merge-gate.json"), request.merge_request_id).checks; }
|
|
337
515
|
function planChecks(workspace, protocols) { return protocols.flatMap((protocol) => { const file = path.join(workspace, ".memory-bank", "protocol", protocol, "plan.json"); if (!fs.existsSync(file))
|
|
338
|
-
|
|
516
|
+
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}` })); }); }
|
|
339
517
|
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)
|
|
340
518
|
throw new AppError("merge_acceptance_invalid", "A merge acceptance criterion references a missing check", 2, { check_refs: missing }); }
|
|
341
|
-
function freezeMergeGate(file, input) { const canonical = JSON.stringify(input.checks); const hash = cryptoHash(canonical); if (fs.existsSync(file)) {
|
|
519
|
+
function freezeMergeGate(file, input) { const canonical = JSON.stringify({ checks: input.checks, acceptance_refs: [...input.acceptanceRefs].sort() }); const hash = cryptoHash(canonical); if (fs.existsSync(file)) {
|
|
342
520
|
const existing = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
343
|
-
if (existing.run_id !== input.runId || existing.
|
|
521
|
+
if (existing.run_id !== input.runId || existing.gate_hash !== hash)
|
|
344
522
|
throw new AppError("merge_gate_freeze_conflict", "MERGE gate is already frozen with different checks", 2, { file });
|
|
345
523
|
return;
|
|
346
|
-
} fs.writeFileSync(file, `${JSON.stringify({ schema_id: "dd-flow/merge-gate@
|
|
524
|
+
} 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`); }
|
|
347
525
|
function readFrozenMergeGate(file, requestId) { try {
|
|
348
526
|
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
349
|
-
if (value.schema_id !== "dd-flow/merge-gate@
|
|
527
|
+
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() })))
|
|
350
528
|
throw new Error("invalid gate");
|
|
351
|
-
return { checks: value.checks };
|
|
529
|
+
return { checks: value.checks, acceptanceRefs: [...value.acceptance_refs].sort(), profileHash: value.profile_hash ?? null, gateHash: value.gate_hash };
|
|
352
530
|
}
|
|
353
531
|
catch (cause) {
|
|
354
532
|
throw new AppError("merge_gate_missing", "MERGE gate is missing or invalid", 1, { merge_request_id: requestId, file, cause: String(cause) });
|
|
355
533
|
} }
|
|
534
|
+
function retainMergeProfile(root, role, workspace) {
|
|
535
|
+
const { profile, hash, file: profileFile } = readCodeCheckProfile(workspace);
|
|
536
|
+
const file = path.join(root, `check-profile-${role}.json`);
|
|
537
|
+
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`;
|
|
538
|
+
if (fs.existsSync(file)) {
|
|
539
|
+
if (fs.readFileSync(file, "utf8") !== bytes)
|
|
540
|
+
throw new AppError("check_profile_drift", "Retained MERGE profile changed", 2, { role, file });
|
|
541
|
+
}
|
|
542
|
+
else
|
|
543
|
+
fs.writeFileSync(file, bytes, { flag: "wx" });
|
|
544
|
+
}
|
|
545
|
+
function frozenGateRefs(gate) { return [...new Set([...gate.checks.map(check => check.canonical_ref ?? check.id), ...gate.acceptanceRefs])].sort(); }
|
|
546
|
+
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); }
|
|
547
|
+
function readMergeGateAcceptance(context, file, request, gate) { try {
|
|
548
|
+
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
549
|
+
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")))
|
|
550
|
+
return { valid: false, receipts: [] };
|
|
551
|
+
const all = checkReceipts(context, { projectId: request.project_id, runId: request.run_id });
|
|
552
|
+
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));
|
|
553
|
+
const bindingRefs = value.receipts.flatMap(receipt => receipt.binding_refs);
|
|
554
|
+
const bindings = new Set(bindingRefs);
|
|
555
|
+
const expected = frozenGateRefs(gate);
|
|
556
|
+
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)
|
|
557
|
+
return { valid: false, receipts: [] };
|
|
558
|
+
return { valid: true, receipts: receipts.map((receipt, index) => ({ ...receipt, check_refs: value.receipts[index].binding_refs })) };
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
return { valid: false, receipts: [] };
|
|
562
|
+
} }
|
|
356
563
|
function cryptoHash(value) { return crypto.createHash("sha256").update(value).digest("hex"); }
|
|
357
564
|
function mergeAcceptanceRefs(workspace, protocols) { return protocols.flatMap((protocol) => { const file = path.join(workspace, ".memory-bank", "protocol", protocol, "plan.json"); if (!fs.existsSync(file))
|
|
358
565
|
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}`)); }); }
|
|
@@ -361,10 +568,14 @@ function protocolIds(home) { const root = path.join(home, "03-plan"); if (!fs.ex
|
|
|
361
568
|
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)
|
|
362
569
|
throw new AppError("workspace_route_missing", "MERGE requires the frozen integration branch", 1, { file }); return { integration_branch: value.policy.integration_branch }; }
|
|
363
570
|
function executionSettings(run) { return JSON.parse(run.index_json).execution_profile?.settings ?? {}; }
|
|
364
|
-
|
|
571
|
+
function requireBootstrapCommand(run) {
|
|
365
572
|
const command = executionSettings(run).code_bootstrap?.command;
|
|
366
573
|
if (!command)
|
|
367
|
-
throw new AppError("execution_profile_invalid", "MERGE requires
|
|
574
|
+
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);
|
|
575
|
+
return command;
|
|
576
|
+
}
|
|
577
|
+
async function runBootstrap(context, run, cwd, mergeId, progress) {
|
|
578
|
+
const command = requireBootstrapCommand(run);
|
|
368
579
|
const result = await runWorkspaceBootstrap(context, { projectId: run.project_id, runId: run.id, runHome: requireHome(run), workspaceRoot: cwd, command, artifactDir: `${stageDir}/${mergeId}/readiness`, progress });
|
|
369
580
|
if (result.exit_code !== 0 || result.error)
|
|
370
581
|
throw new AppError("merge_bootstrap_failed", "Integrated target bootstrap failed; inspect its retained logs", 2, { command, ...result });
|
|
@@ -396,7 +607,12 @@ function releaseMergeLane(context, projectRoot, request, reason) {
|
|
|
396
607
|
try {
|
|
397
608
|
releaseLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, reason });
|
|
398
609
|
}
|
|
399
|
-
catch {
|
|
610
|
+
catch (error) {
|
|
611
|
+
// An absent/replaced lease is already no longer ours. Storage and other
|
|
612
|
+
// failures must remain visible so a repeated operation can release it.
|
|
613
|
+
if (!(error instanceof AppError) || !["lane_lock_not_active", "lane_lock_owned_by_other_worker"].includes(error.code))
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
400
616
|
}
|
|
401
617
|
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; }
|
|
402
618
|
function queueStatus(context, request) { return { position: queueAhead(context, request) + 1, requests_ahead: queueAhead(context, request), status: request.status, route: request.execution_route }; }
|
|
@@ -405,15 +621,17 @@ function requireOwnedActiveRequest(context, input) { const project = requireProj
|
|
|
405
621
|
function assertExecutionBaseline(request) { const head = gitValue(request.target_workspace, ["rev-parse", "HEAD"], true); if (request.checkpoint === "baseline_locked" && head !== request.execution_target_head)
|
|
406
622
|
throw new AppError("merge_target_changed", "Integration target changed after the lane baseline was locked", 1, { expected: request.execution_target_head, actual: head }); }
|
|
407
623
|
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 }); }
|
|
408
|
-
function commitPendingSource(cwd, runId, ignored) { const paths = meaningfulStatus(cwd, ignored)
|
|
624
|
+
function commitPendingSource(cwd, runId, ignored) { const paths = meaningfulStatus(cwd, ignored); if (!paths.length)
|
|
409
625
|
return; git(cwd, ["add", "-A", "--", ...paths]); git(cwd, ["commit", "-m", `chore(${runId.toLowerCase()}): freeze accepted source`]); }
|
|
410
|
-
function commitIntegration(cwd, runId, ignored) { const mergeHead = fs.existsSync(path.join(gitDirectory(cwd), "MERGE_HEAD")); const paths = meaningfulStatus(cwd, ignored)
|
|
626
|
+
function commitIntegration(cwd, runId, ignored) { const mergeHead = fs.existsSync(path.join(gitDirectory(cwd), "MERGE_HEAD")); const paths = meaningfulStatus(cwd, ignored); if (!mergeHead && !paths.length)
|
|
411
627
|
return; if (paths.length)
|
|
412
628
|
git(cwd, ["add", "-A", "--", ...paths]); git(cwd, ["commit", "-m", `merge(${runId.toLowerCase()}): integrate accepted source`]); }
|
|
413
|
-
function ignoredGitPaths(context, projectId) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
629
|
+
function ignoredGitPaths(context, projectId) {
|
|
630
|
+
// Shared workspace classification omits only the exact untracked qualified
|
|
631
|
+
// hook. Tracked or mixed user hooks remain ordinary product changes.
|
|
632
|
+
return [readProjectConfig(context, projectId).dashboard.markdown_path.replaceAll("\\", "/")];
|
|
633
|
+
}
|
|
634
|
+
function meaningfulStatus(cwd, ignored) { return workspaceChangedPaths(cwd).filter((item) => !ignored.includes(item)); }
|
|
417
635
|
function unmerged(cwd) { const value = gitValue(cwd, ["diff", "--name-only", "--diff-filter=U"], true); return value.split(/\r?\n/).filter(Boolean); }
|
|
418
636
|
function gitDirectory(cwd) { const value = gitValue(cwd, ["rev-parse", "--git-dir"]); return path.resolve(cwd, value); }
|
|
419
637
|
function git(cwd, args) { try {
|
|
@@ -469,5 +687,8 @@ function findRootWork(context, projectId, runId) { const work = context.db.get("
|
|
|
469
687
|
throw new AppError("runtime_missing", "vNext RUN has no root Work", 1); return work; }
|
|
470
688
|
function requireRootWork(context, projectId, runId) { const work = findRootWork(context, projectId, runId); if (work.status !== "running")
|
|
471
689
|
throw new AppError("runtime_missing", "vNext RUN has no running root Work", 1); return work; }
|
|
472
|
-
function applyCommand(context, request, projectRoot) { return
|
|
473
|
-
function
|
|
690
|
+
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"])); }
|
|
691
|
+
function repairCommand(context, request, projectRoot) { return managedLifecycleCommand(context, runtimeCommand(context, ["merge", "repair", request.merge_request_id, "--project-root", projectRoot, "--json"])); }
|
|
692
|
+
export function finishCommand(context, runId, request, projectRoot, retryCheckId) {
|
|
693
|
+
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)]));
|
|
694
|
+
}
|