@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +740 -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 +728 -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 +140 -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 +557 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +363 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +751 -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 +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +683 -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 +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -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 +250 -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 +1168 -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 +152 -116
- 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 +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +375 -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 +228 -104
- package/dist/services/vnext-protocolize.js +531 -95
- 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 +105 -48
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +27 -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
|
@@ -166,11 +166,11 @@ function gitFacts(workspacePath) {
|
|
|
166
166
|
if (!fs.existsSync(workspacePath)) {
|
|
167
167
|
return { status: "missing_workspace", actual_branch: null, dirty: null, path: workspacePath };
|
|
168
168
|
}
|
|
169
|
-
const branch = spawnSync("git", ["-C", workspacePath, "rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" });
|
|
169
|
+
const branch = spawnSync("git", ["-C", workspacePath, "rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8", timeout: 5000 });
|
|
170
170
|
if (branch.status !== 0) {
|
|
171
|
-
return { status: "not_git_checkout", actual_branch: null, dirty: null, path: workspacePath };
|
|
171
|
+
return { status: branch.error || branch.signal ? "observation_unavailable" : "not_git_checkout", actual_branch: null, dirty: null, path: workspacePath };
|
|
172
172
|
}
|
|
173
|
-
const status = spawnSync("git", ["-C", workspacePath, "status", "--porcelain"], { encoding: "utf8" });
|
|
173
|
+
const status = spawnSync("git", ["-C", workspacePath, "status", "--porcelain"], { encoding: "utf8", timeout: 5000 });
|
|
174
174
|
return {
|
|
175
175
|
status: "ok",
|
|
176
176
|
actual_branch: branch.stdout.trim() || null,
|
package/dist/services/canon.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { AppError } from "../shared/errors.js";
|
|
5
|
+
import { prepareError } from "../cli/input-preparation.js";
|
|
4
6
|
const canonRootKey = "canon.root";
|
|
5
|
-
export function
|
|
7
|
+
export function prepareCanonRegistration(context, input) {
|
|
8
|
+
if (typeof input.root !== "string" || !input.root.trim())
|
|
9
|
+
return prepareError(new AppError("validation", "Canon root must be a non-empty path", 2));
|
|
6
10
|
const resolution = resolveCanonRoot(context, { explicitRoot: input.root, allowRegistered: false });
|
|
7
11
|
if (!resolution.ok || !resolution.canon) {
|
|
8
|
-
return {
|
|
9
|
-
...resolution,
|
|
10
|
-
action: "register",
|
|
11
|
-
exit_code: 1
|
|
12
|
-
};
|
|
12
|
+
return prepareError(new AppError("validation", "Canon root cannot be registered", 2, { root: input.root, blockers: resolution.blockers, candidates: resolution.candidates, bootstrap: resolution.bootstrap }));
|
|
13
13
|
}
|
|
14
|
+
return resolution.canon;
|
|
15
|
+
}
|
|
16
|
+
export function registerCanonRoot(context, input, canon = prepareCanonRegistration(context, input)) {
|
|
14
17
|
const now = context.now();
|
|
15
18
|
context.db.run(`INSERT INTO runtime_config (key, value_json, created_at, updated_at)
|
|
16
19
|
VALUES (?, ?, ?, ?)
|
|
17
20
|
ON CONFLICT(key) DO UPDATE SET
|
|
18
21
|
value_json = excluded.value_json,
|
|
19
|
-
updated_at = excluded.updated_at`, [canonRootKey, JSON.stringify({ root:
|
|
22
|
+
updated_at = excluded.updated_at`, [canonRootKey, JSON.stringify({ root: canon.root }), now, now]);
|
|
20
23
|
return {
|
|
21
24
|
ok: true,
|
|
22
25
|
action: "register",
|
|
23
26
|
canon: {
|
|
24
|
-
...
|
|
27
|
+
...canon,
|
|
25
28
|
source: "registered"
|
|
26
29
|
},
|
|
27
30
|
db_path: context.db.path
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import { stripVTControlCharacters } from "node:util";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { parse } from "shell-quote";
|
|
6
|
+
import { AppError } from "../shared/errors.js";
|
|
7
|
+
import { validateObservationDuration } from "../harness-runtime/lib/observation-clock.mjs";
|
|
8
|
+
function object(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
|
|
9
|
+
function boundedString(value, bytes) { return typeof value === "string" && Buffer.byteLength(value) <= bytes; }
|
|
10
|
+
const validatedBrowserProofs = new WeakSet();
|
|
11
|
+
function relativeProofPath(value) { return typeof value === "string" && value.length > 0 && Buffer.byteLength(value) <= 4096 && !path.isAbsolute(value) && !value.includes("\\") && value.split("/").every(part => part !== "" && part !== "." && part !== ".."); }
|
|
12
|
+
function proofArtifact(value) { return object(value) && relativeProofPath(value.relative_path) && typeof value.sha256 === "string" && /^[a-f0-9]{64}$/.test(value.sha256); }
|
|
13
|
+
function browserProofShape(proof) {
|
|
14
|
+
const attempt = proof.attempt, step = proof.step;
|
|
15
|
+
return proof.native_code === "PLAYWRIGHT_ACTION_INACTIVITY" && object(attempt) && boundedString(attempt.invocation_id, 4096) && boundedString(attempt.test_id, 4096) && Boolean(attempt.test_id)
|
|
16
|
+
&& [attempt.retry, attempt.repeat_each_index, attempt.worker_index].every(value => Number.isSafeInteger(value) && value >= 0)
|
|
17
|
+
&& object(step) && boundedString(step.id, 4096) && Boolean(step.id) && ["pw:api", "expect"].includes(String(step.category)) && step.file === proof.file && Number.isSafeInteger(step.line) && step.line > 0
|
|
18
|
+
&& proofArtifact(proof.worker_snapshot) && proofArtifact(proof.native_result);
|
|
19
|
+
}
|
|
20
|
+
export function runnerFailureValid(value) {
|
|
21
|
+
if (!object(value) || typeof value.category !== "string" || !["assertion", "tooling", "unclassified"].includes(value.category))
|
|
22
|
+
return false;
|
|
23
|
+
if (value.diagnostic_artifact !== undefined && !proofArtifact(value.diagnostic_artifact))
|
|
24
|
+
return false;
|
|
25
|
+
for (const key of ["code", "message", "file", "test_id", "native_code", "classification_reason"])
|
|
26
|
+
if (value[key] !== undefined && !boundedString(value[key], 4096))
|
|
27
|
+
return false;
|
|
28
|
+
if (value.stack !== undefined && !boundedString(value.stack, 16384) || value.origin !== undefined && (typeof value.origin !== "string" || !["candidate", "tooling", "unknown"].includes(value.origin)) || value.phase !== undefined && (typeof value.phase !== "string" || !["test", "setup", "teardown", "collection", "runner"].includes(value.phase)))
|
|
29
|
+
return false;
|
|
30
|
+
if (value.repair_evidence !== undefined) {
|
|
31
|
+
const proof = value.repair_evidence;
|
|
32
|
+
if (!object(proof) || !boundedString(proof.rule, 128) || !boundedString(proof.native_code, 128) || !boundedString(proof.file, 4096) || !object(proof.world))
|
|
33
|
+
return false;
|
|
34
|
+
const world = proof.world;
|
|
35
|
+
if (world.schema_id !== "dd-tasks/test-world-ready@1" || !boundedString(world.invocation_id, 4096) || typeof world.world_id !== "string" || !/^[a-f0-9]{32}$/.test(world.world_id) || typeof world.migrations_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(world.migrations_sha256) || world.ready !== true || world.setup_completed !== true || !boundedString(world.role, 128) || !world.role)
|
|
36
|
+
return false;
|
|
37
|
+
if (proof.rule === "candidate_browser_action_inactivity" && !browserProofShape(proof))
|
|
38
|
+
return false;
|
|
39
|
+
if (!["candidate_sql_integrity", "candidate_browser_action_inactivity"].includes(String(proof.rule)))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
function candidateFile(root, file) {
|
|
45
|
+
if (!root || !path.isAbsolute(file))
|
|
46
|
+
return false;
|
|
47
|
+
try {
|
|
48
|
+
const relative = path.relative(fs.realpathSync(root), fs.realpathSync(file));
|
|
49
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative) && fs.statSync(file).isFile();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function candidateFailureFiles(evidence, root) {
|
|
56
|
+
return [...new Set((evidence?.failures ?? []).flatMap(failure => failure.file && candidateFile(root, failure.file) ? [failure.file] : []))];
|
|
57
|
+
}
|
|
58
|
+
function candidateFailure(failure, evidence, root, capturedFiles) {
|
|
59
|
+
const owned = (file) => capturedFiles === undefined ? candidateFile(root, file) : capturedFiles.includes(file);
|
|
60
|
+
if (failure.origin === "tooling" || failure.origin === "unknown")
|
|
61
|
+
return false;
|
|
62
|
+
if (failure.category === "assertion")
|
|
63
|
+
return failure.origin === undefined || failure.origin === "candidate" && failure.file !== undefined && owned(failure.file);
|
|
64
|
+
const proof = failure.repair_evidence;
|
|
65
|
+
if (proof?.rule === "candidate_browser_action_inactivity")
|
|
66
|
+
return validatedBrowserProofs.has(proof) && failure.category === "unclassified" && failure.phase === "test" && failure.origin === "candidate" && failure.native_code === proof.native_code && failure.file === proof.file && failure.test_id === proof.attempt.test_id && proof.attempt.invocation_id === evidence.binding.invocation_id && owned(proof.file);
|
|
67
|
+
return failure.category === "unclassified" && failure.origin === "candidate" && ["test", "setup"].includes(failure.phase ?? "") && proof?.rule === "candidate_sql_integrity" && ["23502", "23503", "23505", "23514"].includes(proof.native_code) && failure.native_code === proof.native_code && failure.file === proof.file && proof.world.invocation_id === evidence.binding.invocation_id && owned(proof.file);
|
|
68
|
+
}
|
|
69
|
+
export function runnerObservationDurations(environment) {
|
|
70
|
+
// Both observers leave the launcher its bounded physical cleanup tail.
|
|
71
|
+
return { startupMs: validateObservationDuration(validateObservationDuration(Number(environment.DD_FLOW_TEST_STARTUP_SILENCE_MS ?? 120_000)) + 10_000),
|
|
72
|
+
executionMs: validateObservationDuration(validateObservationDuration(Number(environment.DD_FLOW_TEST_EXECUTION_SILENCE_MS ?? 600_000)) + 15_000) };
|
|
73
|
+
}
|
|
74
|
+
/** Baseline uses argv rather than a shell alias. Recognize only a literal,
|
|
75
|
+
* unique execution contract; never guess shell expansion or compound syntax. */
|
|
76
|
+
export function runnerContractForArgv(profile, command, args) {
|
|
77
|
+
if (profile.schema_id !== "dd-flow/code-check-profile@7" || !profile.runner_evidence_by_alias)
|
|
78
|
+
return null;
|
|
79
|
+
if (!runnerContractsValid(profile.runner_evidence_by_alias))
|
|
80
|
+
throw new AppError("invalid_code_check_profile", "Invalid baseline runner evidence contract", 2);
|
|
81
|
+
const matches = [];
|
|
82
|
+
for (const [alias, contract] of Object.entries(profile.runner_evidence_by_alias)) {
|
|
83
|
+
const source = profile.aliases?.[alias];
|
|
84
|
+
if (typeof source !== "string")
|
|
85
|
+
throw new AppError("invalid_code_check_profile", "Baseline runner evidence references an unknown alias", 2, { alias });
|
|
86
|
+
let expansion = false;
|
|
87
|
+
const tokens = parse(source, () => { expansion = true; return ""; });
|
|
88
|
+
if (!expansion && tokens.every(token => typeof token === "string") && JSON.stringify(tokens) === JSON.stringify([command, ...args]))
|
|
89
|
+
matches.push({ alias, command: source, contract });
|
|
90
|
+
}
|
|
91
|
+
if (matches.some(match => JSON.stringify(match.contract) !== JSON.stringify(matches[0].contract)))
|
|
92
|
+
throw new AppError("runner_evidence_ambiguous", "Baseline argv matches conflicting runner evidence contracts", 2, { aliases: matches.map(match => match.alias) });
|
|
93
|
+
return matches[0] ?? null;
|
|
94
|
+
}
|
|
95
|
+
/** Shared by CODE and EVAL baseline. The sole publisher owns this structured
|
|
96
|
+
* journal; logs belong on stdout/stderr. Partial final lines await append. */
|
|
97
|
+
export function readRunnerProgress(file, invocationId, lastSequence = 0, now = Date.now()) {
|
|
98
|
+
try {
|
|
99
|
+
const text = readBoundedProofFile(file, 4 * 1024 * 1024).toString("utf8");
|
|
100
|
+
const accepted = [];
|
|
101
|
+
for (const line of text.split("\n").slice(0, -1)) {
|
|
102
|
+
if (Buffer.byteLength(line) > 16 * 1024)
|
|
103
|
+
throw new Error("Progress record exceeds 16 KiB");
|
|
104
|
+
const value = JSON.parse(line);
|
|
105
|
+
if (value && typeof value === "object" && !Array.isArray(value) && typeof value.invocation_id === "string" && value.invocation_id !== invocationId)
|
|
106
|
+
continue;
|
|
107
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema_id !== "dd-flow/check-runner-progress@1" || value.invocation_id !== invocationId
|
|
108
|
+
|| !Number.isSafeInteger(value.sequence) || value.sequence <= 0 || typeof value.at !== "string" || !Number.isFinite(Date.parse(value.at))
|
|
109
|
+
|| !["startup", "execution"].includes(value.phase)
|
|
110
|
+
|| !["setup_step_completed", "run_started", "module_collected", "test_started", "test_completed", "step_completed", "run_completed"].includes(value.event))
|
|
111
|
+
throw new Error("Malformed official progress record");
|
|
112
|
+
if (value.sequence <= lastSequence || Date.parse(value.at) > now)
|
|
113
|
+
continue;
|
|
114
|
+
accepted.push(value);
|
|
115
|
+
lastSequence = value.sequence;
|
|
116
|
+
}
|
|
117
|
+
return accepted;
|
|
118
|
+
}
|
|
119
|
+
catch (cause) {
|
|
120
|
+
if (cause.code === "ENOENT")
|
|
121
|
+
return [];
|
|
122
|
+
throw new AppError("runner_progress_invalid", "Runner progress cannot be safely observed", 1, { cause: cause.code ?? cause.message });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function readBoundedProofFile(file, limit) {
|
|
126
|
+
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
127
|
+
try {
|
|
128
|
+
const stat = fs.fstatSync(fd);
|
|
129
|
+
if (!stat.isFile() || stat.size > limit)
|
|
130
|
+
throw new Error("Evidence is not a bounded regular file");
|
|
131
|
+
const buffer = Buffer.alloc(Math.min(limit + 1, stat.size + 1));
|
|
132
|
+
let length = 0;
|
|
133
|
+
while (length < buffer.length) {
|
|
134
|
+
const read = fs.readSync(fd, buffer, length, buffer.length - length, null);
|
|
135
|
+
if (!read)
|
|
136
|
+
break;
|
|
137
|
+
length += read;
|
|
138
|
+
}
|
|
139
|
+
if (length > limit || length > stat.size)
|
|
140
|
+
throw new Error("Evidence grew during observation");
|
|
141
|
+
return buffer.subarray(0, length);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
fs.closeSync(fd);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function readProofArtifact(root, ref, limit = 256 * 1024) {
|
|
148
|
+
let current = root;
|
|
149
|
+
if (fs.lstatSync(current).isSymbolicLink())
|
|
150
|
+
throw new Error("Symlink evidence root");
|
|
151
|
+
for (const part of ref.relative_path.split("/")) {
|
|
152
|
+
current = path.join(current, part);
|
|
153
|
+
if (fs.lstatSync(current).isSymbolicLink())
|
|
154
|
+
throw new Error("Symlink proof artifact");
|
|
155
|
+
}
|
|
156
|
+
const bytes = readBoundedProofFile(current, limit);
|
|
157
|
+
if (crypto.createHash("sha256").update(bytes).digest("hex") !== ref.sha256)
|
|
158
|
+
throw new Error("Browser proof hash differs");
|
|
159
|
+
const value = JSON.parse(bytes.toString("utf8"));
|
|
160
|
+
if (!object(value))
|
|
161
|
+
throw new Error("Invalid proof object");
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
function matches(actual, expected) { return object(actual) && Object.entries(expected).every(([key, value]) => actual[key] === value); }
|
|
165
|
+
function validateBrowserArtifacts(root, proof, binding) {
|
|
166
|
+
const snapshot = readProofArtifact(root, proof.worker_snapshot), native = readProofArtifact(root, proof.native_result);
|
|
167
|
+
if (!Array.isArray(native.errors) || native.errors.length < 1 || native.errors.length > 64 || native.errors.some(error => !boundedString(error, 4096) || !/^(?:Error: )?(?:[A-Za-z][\w.]*: )?Test timeout of [1-9]\d*ms exceeded\.(?:\nCall log:[\s\S]*)?$/.test(stripVTControlCharacters(String(error)))))
|
|
168
|
+
return false;
|
|
169
|
+
if (!matches(snapshot.binding, binding) || !matches(native.binding, binding) || !matches(snapshot.attempt, proof.attempt) || !matches(native.attempt, proof.attempt)
|
|
170
|
+
|| !matches(snapshot.step, proof.step) || !matches(native.step, proof.step) || !matches(snapshot.world, proof.world) || proof.world.invocation_id !== binding.invocation_id
|
|
171
|
+
|| native.status !== "timedOut" || native.phase !== "test" || native.file !== proof.file || snapshot.phase !== "body" || snapshot.observation_gap !== false || !matches(snapshot.browser, { connected: true, page_closed: false, crashed: false })
|
|
172
|
+
|| !matches(snapshot.world_health, { api: true, web: true, db: true }))
|
|
173
|
+
return false;
|
|
174
|
+
const quiet = snapshot.quiet_ms, last = Date.parse(String(snapshot.last_progress_at)), deadline = Date.parse(String(snapshot.deadline_at)), observed = Date.parse(String(snapshot.observed_at)), health = Date.parse(String(snapshot.health_observed_at)), ended = Date.parse(String(native.ended_at));
|
|
175
|
+
return Number.isSafeInteger(quiet) && quiet > 0 && [last, deadline, observed, health, ended].every(Number.isFinite) && Math.abs(deadline - last - quiet) <= 2 && observed >= last && observed <= deadline && health <= observed && health >= last && deadline - health <= Math.min(1000, quiet / 4) && ended >= deadline && ended <= Date.now() && ended - deadline <= 5000;
|
|
176
|
+
}
|
|
177
|
+
export function runnerProofArtifacts(evidence) {
|
|
178
|
+
return [...new Set((evidence?.failures ?? []).flatMap(failure => { const proof = failure.repair_evidence; return [...(failure.diagnostic_artifact ? [failure.diagnostic_artifact.relative_path] : []), ...(proof?.rule === "candidate_browser_action_inactivity" ? [proof.worker_snapshot.relative_path, proof.native_result.relative_path] : [])]; }))];
|
|
179
|
+
}
|
|
180
|
+
export function runnerContractsValid(value) {
|
|
181
|
+
if (value === undefined)
|
|
182
|
+
return true;
|
|
183
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
184
|
+
return false;
|
|
185
|
+
return Object.entries(value).every(([alias, raw]) => {
|
|
186
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
187
|
+
return false;
|
|
188
|
+
const contract = raw;
|
|
189
|
+
return alias.startsWith("@check/") && contract.schema_id === "dd-flow/check-runner-evidence@1" && typeof contract.path === "string" && contract.path.length > 0 && contract.path.split("/")[0] !== "engine-outcome.json" && !contract.path.includes("\\") && !path.isAbsolute(contract.path) && !contract.path.split("/").some(part => part === ".." || part === "" || part === ".");
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
export function readRunnerEvidence(root, contract, binding) {
|
|
193
|
+
try {
|
|
194
|
+
let current = root;
|
|
195
|
+
if (!fs.lstatSync(current).isDirectory())
|
|
196
|
+
return null;
|
|
197
|
+
for (const part of contract.path.split(/[\\/]/)) {
|
|
198
|
+
current = path.join(current, part);
|
|
199
|
+
if (fs.lstatSync(current).isSymbolicLink())
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const evidence = JSON.parse(readBoundedProofFile(current, 1024 * 1024).toString("utf8"));
|
|
203
|
+
if (evidence.schema_id !== contract.schema_id || !evidence.binding || Object.entries(binding).some(([key, value]) => evidence.binding[key] !== value)
|
|
204
|
+
|| typeof evidence.runner?.name !== "string" || !evidence.runner.name || typeof evidence.runner.version !== "string" || !evidence.runner.version || typeof evidence.completed !== "boolean" || !evidence.counts
|
|
205
|
+
|| ![evidence.counts.passed, evidence.counts.failed, evidence.counts.skipped].every(n => Number.isSafeInteger(n) && n >= 0)
|
|
206
|
+
|| !Array.isArray(evidence.failures) || evidence.failures.length > 64 || evidence.failures.some(f => !runnerFailureValid(f))
|
|
207
|
+
|| evidence.counts.failed > evidence.failures.length || evidence.observation_unknown !== undefined && typeof evidence.observation_unknown !== "boolean")
|
|
208
|
+
return null;
|
|
209
|
+
for (const failure of evidence.failures) {
|
|
210
|
+
if (failure.diagnostic_artifact && !matches(readProofArtifact(root, failure.diagnostic_artifact, 2 * 1024 * 1024).binding, binding))
|
|
211
|
+
return null;
|
|
212
|
+
const proof = failure.repair_evidence;
|
|
213
|
+
if (proof?.rule === "candidate_browser_action_inactivity") {
|
|
214
|
+
if (!validateBrowserArtifacts(root, proof, binding))
|
|
215
|
+
return null;
|
|
216
|
+
validatedBrowserProofs.add(proof);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return evidence;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
export function runnerEvidencePass(evidence) { return Boolean(evidence?.completed && !evidence.observation_unknown && evidence.counts.failed === 0 && evidence.failures.length === 0); }
|
|
226
|
+
export function checkRepairDisposition(receipt) {
|
|
227
|
+
if (receipt.status === "running" || receipt.status === "aborted" || receipt.runner_evidence?.observation_unknown)
|
|
228
|
+
return "settlement_required";
|
|
229
|
+
if (receipt.status === "passed")
|
|
230
|
+
return "passed";
|
|
231
|
+
if (receipt.execution_failures?.length || receipt.mutation_paths?.length || receipt.before_fingerprint !== receipt.after_fingerprint || receipt.diagnostic_evidence_valid === false)
|
|
232
|
+
return "tooling_blocker";
|
|
233
|
+
if (!Number.isSafeInteger(receipt.exit_code) || receipt.exit_code === 0)
|
|
234
|
+
return "unclassified";
|
|
235
|
+
const evidence = receipt.runner_evidence;
|
|
236
|
+
if (!evidence || !Array.isArray(evidence.failures) || !evidence.failures.length || receipt.exit_code === 0 || evidence.failures.some(f => !f || !["assertion", "tooling", "unclassified"].includes(f.category)))
|
|
237
|
+
return "unclassified";
|
|
238
|
+
if (evidence.failures.some(f => f.category === "tooling"))
|
|
239
|
+
return "tooling_blocker";
|
|
240
|
+
if (!evidence.completed)
|
|
241
|
+
return "unclassified";
|
|
242
|
+
if (evidence.failures.some(f => !runnerFailureValid(f) || !candidateFailure(f, evidence, receipt.candidate_root, receipt.candidate_files)))
|
|
243
|
+
return "unclassified";
|
|
244
|
+
return "source_repair";
|
|
245
|
+
}
|
|
246
|
+
export function assertSourceRepairEligible(receipts) {
|
|
247
|
+
const blockers = receipts.filter(receipt => checkRepairDisposition(receipt) !== "source_repair");
|
|
248
|
+
if (!receipts.length || blockers.length)
|
|
249
|
+
throw new AppError("check_gate_blocked", "Checks require tooling diagnosis or settlement before product repair; retained nonzero is not a candidate-source verdict", 2, { failures: receipts, dispositions: receipts.map(receipt => ({ check_receipt_id: receipt.id, disposition: checkRepairDisposition(receipt) })), outcome: "blocked", recoverable: false, instruction: "Inspect retained runner evidence. Do not change product code to repair tooling, unknown observation or unclassified failure. Retry only after establishing and correcting the cause." });
|
|
250
|
+
}
|
package/dist/services/cleanup.js
CHANGED
|
@@ -2,13 +2,13 @@ import fs from "node:fs";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { AppError } from "../shared/errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { projectCheckoutRoot, resolveProjectRoot } from "../storage/paths.js";
|
|
5
|
+
import { prepareJsonFile } from "../cli/input-preparation.js";
|
|
6
|
+
import { projectCheckoutRoot, resolveProjectRoot, runtimeStateJsonPath } from "../storage/paths.js";
|
|
8
7
|
import { appendAudit } from "./audit.js";
|
|
9
8
|
import { requireProjectByRoot } from "./projects.js";
|
|
10
|
-
import { protocolRunDiagnostics, readProtocolRuntimeState, requireProtocol } from "./protocols.js";
|
|
9
|
+
import { persistProtocolState, prepareProtocolRuntimeRepair, protocolRunDiagnostics, readProtocolRuntimeState, requireProtocol } from "./protocols.js";
|
|
11
10
|
import { publicSessionIdentity } from "./session-identity.js";
|
|
11
|
+
import { publishRunProjections } from "./runs.js";
|
|
12
12
|
const actionKinds = new Set([
|
|
13
13
|
"repair_protocol_state",
|
|
14
14
|
"cancel_queue_job",
|
|
@@ -25,7 +25,8 @@ export function cleanupScan(context, input) {
|
|
|
25
25
|
const protocols = context.db.all("SELECT id, status, stage, state_path FROM protocols WHERE project_id = ? ORDER BY id ASC", [project.id]);
|
|
26
26
|
for (const protocol of protocols) {
|
|
27
27
|
const terminal = ["closed", "cancelled"].includes(protocol.status) || ["closed", "cancelled"].includes(protocol.stage);
|
|
28
|
-
|
|
28
|
+
const requiresMigration = protocol.state_path !== runtimeStateJsonPath(context.ddFlowHome, project.id, protocol.id);
|
|
29
|
+
if (!fs.existsSync(protocol.state_path) || requiresMigration) {
|
|
29
30
|
pushFinding(findings, actions, "missing_protocol_runtime_state", "warning", {
|
|
30
31
|
protocol_id: protocol.id,
|
|
31
32
|
path: protocol.state_path,
|
|
@@ -43,6 +44,8 @@ export function cleanupScan(context, input) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
const fullProtocol = requireProtocol(context, protocol.id, project.id);
|
|
47
|
+
if (requiresMigration)
|
|
48
|
+
continue;
|
|
46
49
|
const state = readProtocolRuntimeState(context, fullProtocol).state;
|
|
47
50
|
const diagnostics = protocolRunDiagnostics(context, fullProtocol, state).diagnostics;
|
|
48
51
|
for (const diagnostic of diagnostics.filter((entry) => entry.code === "protocol_run_stage_mismatch")) {
|
|
@@ -170,36 +173,55 @@ export function cleanupScan(context, input) {
|
|
|
170
173
|
plan: { project_root: project.root, project_id: project.id, actions }
|
|
171
174
|
};
|
|
172
175
|
}
|
|
173
|
-
export function
|
|
174
|
-
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
176
|
+
export function prepareCleanupApply(context, input) {
|
|
175
177
|
const reason = input.reason.trim();
|
|
176
178
|
if (!reason) {
|
|
177
179
|
throw new AppError("validation", "cleanup apply requires --reason", 2);
|
|
178
180
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
const value = prepareJsonFile(input.planFile, "cleanup plan").value;
|
|
182
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
183
|
+
throw new AppError("validation", "cleanup plan must be an object", 2);
|
|
184
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
185
|
+
const plan = validateCleanupPlan(value, project);
|
|
181
186
|
validateNoDestructiveSurprises(context, plan, input.force);
|
|
187
|
+
return { project, reason, plan };
|
|
188
|
+
}
|
|
189
|
+
export function cleanupApply(context, input) {
|
|
190
|
+
const { project, reason, plan } = input.prepared ?? prepareCleanupApply(context, input);
|
|
191
|
+
// A supplied plan can outlive its initial inspection. Refresh external
|
|
192
|
+
// workspace evidence before reserving the writer, then recheck SQL below.
|
|
193
|
+
if (input.prepared)
|
|
194
|
+
validateNoDestructiveSurprises(context, plan, input.force);
|
|
195
|
+
const prepared = prepareCleanupEffects(context, plan);
|
|
182
196
|
const now = context.now();
|
|
183
197
|
const results = [];
|
|
184
|
-
|
|
198
|
+
let executing = false, committed = false;
|
|
185
199
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
context.db.writeTransaction(() => {
|
|
201
|
+
context.db.afterCommit(() => { committed = true; });
|
|
202
|
+
validateNoDestructiveSurprises(context, plan, input.force, false);
|
|
203
|
+
prepared.assertUnchanged();
|
|
204
|
+
for (const action of plan.actions) {
|
|
205
|
+
executing = true;
|
|
206
|
+
results.push(applyAction(context, action, reason, input.force, now, prepared));
|
|
207
|
+
}
|
|
208
|
+
const changed = results.filter((result) => result.changed).length;
|
|
209
|
+
if (changed > 0) {
|
|
210
|
+
appendAudit(context, {
|
|
211
|
+
projectId: project.id,
|
|
212
|
+
eventType: "cleanup.applied",
|
|
213
|
+
reason,
|
|
214
|
+
forced: input.force,
|
|
215
|
+
payload: { project_id: project.id, action_count: plan.actions.length, changed, results }
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
200
219
|
}
|
|
201
220
|
catch (error) {
|
|
202
|
-
|
|
221
|
+
if (committed || error instanceof AppError && error.details.effect === "committed")
|
|
222
|
+
throw new AppError(error instanceof AppError ? error.code : "cleanup_publication_failed", error instanceof Error ? error.message : "Committed cleanup publication failed", error instanceof AppError ? error.exitCode : 1, { ...(error instanceof AppError ? error.details : {}), effect: "committed", publication_pending: true, attempted_results: results, cause: String(error) });
|
|
223
|
+
if (executing)
|
|
224
|
+
throw new AppError("cleanup_apply_failed", "Cleanup failed after execution began; inspect retained artifacts before retrying", 1, { effect: "unknown", database_rolled_back: true, attempted_results: results, cause: error instanceof Error ? error.message : String(error), ...(error instanceof AppError ? { cause_code: error.code } : {}) });
|
|
203
225
|
throw error;
|
|
204
226
|
}
|
|
205
227
|
return {
|
|
@@ -248,18 +270,36 @@ function validateCleanupPlan(value, project) {
|
|
|
248
270
|
throw new AppError("validation", "close_flow_session requires a native session identity", 2);
|
|
249
271
|
}
|
|
250
272
|
}
|
|
273
|
+
const requiredField = action.kind === "discard_stale_run" ? "run_id"
|
|
274
|
+
: ["repair_protocol_state", "cancel_queue_job", "close_worktree_record"].includes(String(action.kind)) ? "protocol_id" : undefined;
|
|
275
|
+
if (requiredField && (typeof action[requiredField] !== "string" || !action[requiredField].trim()))
|
|
276
|
+
throw new AppError("validation", `cleanup ${String(action.kind)} requires ${requiredField}`, 2);
|
|
277
|
+
const numericField = action.kind === "expire_lane_lock" ? "lock_id" : action.kind === "expire_lane_waiter" ? "waiter_id" : undefined;
|
|
278
|
+
if (numericField && (!Number.isSafeInteger(action[numericField]) || action[numericField] < 1))
|
|
279
|
+
throw new AppError("validation", `cleanup ${String(action.kind)} requires a positive integer ${numericField}`, 2);
|
|
280
|
+
if (action.worktree_path !== undefined && (typeof action.worktree_path !== "string" || !path.isAbsolute(action.worktree_path)))
|
|
281
|
+
throw new AppError("validation", "cleanup worktree_path must be an absolute path", 2);
|
|
251
282
|
return action;
|
|
252
283
|
});
|
|
253
284
|
return { project_root: project.root, project_id: project.id, actions };
|
|
254
285
|
}
|
|
255
|
-
function validateNoDestructiveSurprises(context, plan, force) {
|
|
256
|
-
const
|
|
286
|
+
function validateNoDestructiveSurprises(context, plan, force, inspectFiles = true) {
|
|
287
|
+
for (const action of plan.actions) {
|
|
288
|
+
if (action.kind === "repair_protocol_state")
|
|
289
|
+
requireProtocol(context, action.protocol_id, action.project_id);
|
|
290
|
+
if (action.kind === "discard_stale_run") {
|
|
291
|
+
const run = context.db.get("SELECT index_json, status FROM runs WHERE project_id = ? AND id = ?", [action.project_id, action.run_id]);
|
|
292
|
+
if (run && ["running", "discarded"].includes(run.status))
|
|
293
|
+
cleanupRunIndex(run.index_json);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const destructive = inspectFiles && plan.actions.find((action) => action.kind === "close_worktree_record" && action.worktree_path && dirtyGit(action.worktree_path));
|
|
257
297
|
if (destructive && !force) {
|
|
258
298
|
throw new AppError("worktree_dirty", "cleanup apply refuses dirty worktree actions without --force --reason", 1, {
|
|
259
299
|
path: destructive.worktree_path
|
|
260
300
|
});
|
|
261
301
|
}
|
|
262
|
-
const unsafeWorktree = plan.actions.find((action) => action.kind === "close_worktree_record" && action.worktree_path && !safeCleanupWorktreePath(context, action.project_id, action.worktree_path));
|
|
302
|
+
const unsafeWorktree = inspectFiles && plan.actions.find((action) => action.kind === "close_worktree_record" && action.worktree_path && !safeCleanupWorktreePath(context, action.project_id, action.worktree_path));
|
|
263
303
|
if (unsafeWorktree) {
|
|
264
304
|
throw new AppError("worktree_cleanup_unsafe", "cleanup apply refuses unsafe worktree path", 1, {
|
|
265
305
|
path: unsafeWorktree.worktree_path
|
|
@@ -278,12 +318,53 @@ function validateNoDestructiveSurprises(context, plan, force) {
|
|
|
278
318
|
});
|
|
279
319
|
}
|
|
280
320
|
}
|
|
281
|
-
function
|
|
321
|
+
function prepareCleanupEffects(context, plan) {
|
|
322
|
+
const checks = [], files = new Map();
|
|
323
|
+
const repairs = new Map();
|
|
324
|
+
const read = (sql, params) => {
|
|
325
|
+
const value = context.db.get(sql, params), encoded = JSON.stringify(value);
|
|
326
|
+
checks.push(() => { if (JSON.stringify(context.db.get(sql, params)) !== encoded)
|
|
327
|
+
throw new AppError("cleanup_plan_changed", "Cleanup target changed during preparation", 1, { effect: "no_effect" }); });
|
|
328
|
+
return value;
|
|
329
|
+
};
|
|
330
|
+
const remember = (file) => { if (file !== undefined)
|
|
331
|
+
files.set(file, fs.existsSync(file)); };
|
|
332
|
+
for (const action of plan.actions) {
|
|
333
|
+
if (action.kind === "repair_protocol_state") {
|
|
334
|
+
read("SELECT * FROM protocols WHERE project_id = ? AND id = ?", [action.project_id, action.protocol_id]);
|
|
335
|
+
const protocol = requireProtocol(context, action.protocol_id, action.project_id);
|
|
336
|
+
remember(protocol.state_path);
|
|
337
|
+
repairs.set(action, prepareProtocolRuntimeRepair(context, protocol));
|
|
338
|
+
}
|
|
339
|
+
if (action.kind === "discard_stale_run") {
|
|
340
|
+
const run = read("SELECT * FROM runs WHERE project_id = ? AND id = ?", [action.project_id, action.run_id]);
|
|
341
|
+
if (run) {
|
|
342
|
+
remember(run.runtime_path);
|
|
343
|
+
remember(run.run_dir);
|
|
344
|
+
remember(run.workspace_root);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (action.kind === "close_worktree_record") {
|
|
348
|
+
const record = read("SELECT * FROM worktree_records WHERE project_id = ? AND protocol_id = ?", [action.project_id, action.protocol_id]);
|
|
349
|
+
remember(action.worktree_path ?? record?.worktree_path);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return { repairs, assertUnchanged: () => { for (const check of checks)
|
|
353
|
+
check(); }, exists: (file) => {
|
|
354
|
+
if (!files.has(file))
|
|
355
|
+
throw new AppError("cleanup_plan_changed", "Cleanup target has no prepared filesystem observation", 1, { path: file, effect: "no_effect" });
|
|
356
|
+
return files.get(file);
|
|
357
|
+
} };
|
|
358
|
+
}
|
|
359
|
+
function applyAction(context, action, reason, force, now, prepared) {
|
|
282
360
|
if (action.kind === "repair_protocol_state" && action.protocol_id) {
|
|
283
361
|
const protocol = requireProtocol(context, action.protocol_id, action.project_id);
|
|
284
362
|
const beforePath = protocol.state_path;
|
|
285
|
-
const existed =
|
|
286
|
-
|
|
363
|
+
const existed = prepared.exists(beforePath), repair = prepared.repairs.get(action);
|
|
364
|
+
context.db.run("UPDATE protocols SET state_path = ?, plan_path = ? WHERE project_id = ? AND id = ?", [repair.statePath, repair.planPath, action.project_id, action.protocol_id]);
|
|
365
|
+
persistProtocolState(context, { ...protocol, state_path: repair.statePath, plan_path: repair.planPath }, repair.state);
|
|
366
|
+
protocol.state_path = repair.statePath;
|
|
367
|
+
appendAudit(context, { protocolId: protocol.id, projectId: protocol.project_id, eventType: "protocol.runtime_state_repaired", payload: { protocol_id: protocol.id, path: repair.statePath, repaired_from: repair.previousStatePath, source: "sqlite" } });
|
|
287
368
|
return {
|
|
288
369
|
kind: action.kind,
|
|
289
370
|
changed: !existed || beforePath !== protocol.state_path,
|
|
@@ -338,21 +419,19 @@ function applyAction(context, action, reason, force, now) {
|
|
|
338
419
|
if (run.status === "discarded") {
|
|
339
420
|
const index = discardedRunIndex(run.index_json, reason, now);
|
|
340
421
|
context.db.run("UPDATE runs SET verdict = 'discarded', next_action = ?, index_json = ?, updated_at = ?, completed_at = COALESCE(completed_at, ?) WHERE project_id = ? AND id = ?", [reason, JSON.stringify(index), now, now, action.project_id, action.run_id]);
|
|
341
|
-
|
|
342
|
-
fs.writeFileSync(run.runtime_path, `${JSON.stringify(index, null, 2)}\n`);
|
|
422
|
+
context.db.afterCommit(() => publishRunProjections(context, action.project_id, action.run_id));
|
|
343
423
|
return { kind: action.kind, changed: true, run_id: action.run_id };
|
|
344
424
|
}
|
|
345
425
|
if (run.status !== "running") {
|
|
346
426
|
return { kind: action.kind, changed: false, skipped: true, reason: `already_${run.status}`, run_id: action.run_id };
|
|
347
427
|
}
|
|
348
|
-
if (
|
|
428
|
+
if (prepared.exists(run.runtime_path) || prepared.exists(run.run_dir) || prepared.exists(run.workspace_root)) {
|
|
349
429
|
return { kind: action.kind, changed: false, skipped: true, reason: "runtime_or_workspace_reappeared", run_id: action.run_id };
|
|
350
430
|
}
|
|
351
431
|
const index = discardedRunIndex(run.index_json, reason, now);
|
|
352
432
|
context.db.run(`UPDATE runs SET status = 'discarded', verdict = 'discarded', next_action = ?, index_json = ?, updated_at = ?, completed_at = ?
|
|
353
433
|
WHERE project_id = ? AND id = ? AND status = 'running'`, [reason, JSON.stringify(index), now, now, action.project_id, action.run_id]);
|
|
354
|
-
|
|
355
|
-
fs.writeFileSync(run.runtime_path, `${JSON.stringify(index, null, 2)}\n`);
|
|
434
|
+
context.db.afterCommit(() => publishRunProjections(context, action.project_id, action.run_id));
|
|
356
435
|
context.db.run(`UPDATE sessions SET status = 'stopped', stop_reason = ?, updated_at = ?, stopped_at = ?
|
|
357
436
|
WHERE project_id = ? AND run_id = ? AND status IN ('pending', 'active', 'waiting_user', 'blocked', 'stopping')`, [reason, now, now, action.project_id, action.run_id]);
|
|
358
437
|
context.db.run("UPDATE flow_session_segments SET ended_at = ? WHERE project_id = ? AND run_id = ? AND ended_at IS NULL", [now, action.project_id, action.run_id]);
|
|
@@ -394,7 +473,7 @@ function applyAction(context, action, reason, force, now) {
|
|
|
394
473
|
}
|
|
395
474
|
if (action.kind === "close_worktree_record" && action.protocol_id) {
|
|
396
475
|
const worktreePath = action.worktree_path ?? context.db.get("SELECT worktree_path FROM worktree_records WHERE project_id = ? AND protocol_id = ?", [action.project_id, action.protocol_id])?.worktree_path;
|
|
397
|
-
const status = worktreePath &&
|
|
476
|
+
const status = worktreePath && prepared.exists(worktreePath) ? "kept" : "removed";
|
|
398
477
|
const result = context.db.run(`UPDATE worktree_records SET status = ?, updated_at = ?, closed_at = ?
|
|
399
478
|
WHERE project_id = ? AND protocol_id = ? AND status = 'active'`, [status, now, now, action.project_id, action.protocol_id]);
|
|
400
479
|
return {
|
|
@@ -407,22 +486,36 @@ function applyAction(context, action, reason, force, now) {
|
|
|
407
486
|
return { kind: action.kind, changed: false, skipped: true, reason: "invalid_action" };
|
|
408
487
|
}
|
|
409
488
|
function discardedRunIndex(indexJson, reason, now) {
|
|
410
|
-
const index =
|
|
489
|
+
const index = cleanupRunIndex(indexJson);
|
|
411
490
|
index.schema_id = "dd-flow/flow-run@3";
|
|
412
491
|
index.status = "discarded";
|
|
413
492
|
index.verdict = "discarded";
|
|
414
493
|
index.next_action = reason;
|
|
415
494
|
index.updated_at = now;
|
|
495
|
+
index.runtime_revision = (typeof index.runtime_revision === "number" ? index.runtime_revision : 0) + 1;
|
|
416
496
|
index.completed_at ??= now;
|
|
417
497
|
index.finished_at ??= now;
|
|
418
498
|
return index;
|
|
419
499
|
}
|
|
500
|
+
function cleanupRunIndex(indexJson) {
|
|
501
|
+
try {
|
|
502
|
+
const index = JSON.parse(indexJson);
|
|
503
|
+
if (!index || typeof index !== "object" || Array.isArray(index))
|
|
504
|
+
throw new Error("RUN index must be an object");
|
|
505
|
+
return index;
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
throw new AppError("runtime_state_invalid", "Cleanup cannot read retained RUN state", 1, { cause: String(error) });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
420
511
|
function dirtyGit(worktreePath) {
|
|
421
512
|
if (!fs.existsSync(worktreePath)) {
|
|
422
513
|
return false;
|
|
423
514
|
}
|
|
424
|
-
const result = spawnSync("git", ["-C", worktreePath, "status", "--porcelain"], { encoding: "utf8" });
|
|
425
|
-
|
|
515
|
+
const result = spawnSync("git", ["-C", worktreePath, "status", "--porcelain"], { encoding: "utf8", timeout: 5000 });
|
|
516
|
+
if (result.error || result.status !== 0)
|
|
517
|
+
throw new AppError("workspace_status_failed", "Cannot establish worktree status before cleanup", 1, { path: worktreePath, cause: result.error?.message ?? result.stderr });
|
|
518
|
+
return result.stdout.trim().length > 0;
|
|
426
519
|
}
|
|
427
520
|
function safeCleanupWorktreePath(context, projectId, worktreePath) {
|
|
428
521
|
if (!fs.existsSync(worktreePath)) {
|