@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.160
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 +959 -0
- package/README.md +139 -10
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +417 -0
- package/dist/cli/command-metadata.js +303 -0
- package/dist/cli/command-option-descriptions.js +192 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/delivery.js +108 -0
- package/dist/cli/diagnostic-invocation.js +62 -0
- package/dist/cli/diagnostic.js +131 -0
- package/dist/cli/error-output.js +62 -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 +521 -19
- package/dist/cli/hook-ingress.js +89 -0
- package/dist/cli/input-preparation.js +193 -0
- package/dist/cli/interactive-invocation.js +33 -0
- package/dist/cli/interactive.js +114 -0
- package/dist/cli/memory-fix-invocation.js +52 -0
- package/dist/cli/memory-fix.js +100 -0
- package/dist/cli/memory-flow-admission.js +54 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +250 -0
- package/dist/cli/memory-storage-admission.js +54 -0
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +94 -0
- package/dist/cli/native-hook-preparation.js +82 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +72 -0
- package/dist/cli/run-cli.js +2020 -555
- 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 +150 -0
- package/dist/cli.js +103 -2
- package/dist/domain/delivery-flows.js +8 -0
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/distill-flow.js +20 -0
- package/dist/domain/interactive-flow.js +1 -0
- package/dist/domain/memory-fix-flow.js +2 -0
- package/dist/domain/review-fix-flow.js +2 -0
- package/dist/domain/source-snapshot-flows.js +4 -0
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +28 -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 +71 -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 +50 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +95 -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-error-boundary.mjs +87 -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/agy-native-inspection.mjs +225 -0
- package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
- package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
- package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -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 +11 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +113 -0
- package/dist/harness-runtime/lib/native-children.d.mts +34 -0
- package/dist/harness-runtime/lib/native-children.mjs +265 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +306 -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 +38 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -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 +140 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +200 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
- package/dist/harness-runtime/lib/runner-events.mjs +202 -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 +53 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
- package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -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-decision.schema.json +9 -0
- 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/code-work-result.schema.json +2 -1
- package/dist/schemas/delivery-stage-result.schema.json +286 -0
- package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/distill-stage-result.schema.json +667 -0
- package/dist/schemas/distill-work-result.schema.json +428 -0
- package/dist/schemas/execution-bundle.schema.json +215 -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 +29 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/interactive-stage-result.schema.json +47 -0
- package/dist/schemas/interactive-work-result.schema.json +63 -0
- package/dist/schemas/mb-fix-stage-result.schema.json +93 -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/mb-upgrade-stage-result.schema.json +863 -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 +56 -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 +12 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/review-fix-stage-result.schema.json +210 -0
- package/dist/schemas/run-control-receipt.schema.json +119 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/assigned-lifecycle.js +382 -0
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +54 -15
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +344 -0
- package/dist/services/check-runner-proof.js +373 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +32 -64
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1426 -121
- package/dist/services/codex-capacity.js +367 -0
- package/dist/services/codex-hook-delivery.js +41 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +16 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +25 -0
- package/dist/services/controller-child-capacity.js +71 -0
- package/dist/services/controller-child-observer.js +121 -0
- package/dist/services/controller-child-repair.js +117 -0
- package/dist/services/controller-execution.js +97 -0
- package/dist/services/controller-fanout.js +277 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +51 -0
- package/dist/services/controller-result-authority.js +127 -0
- package/dist/services/controller-work-failure.js +198 -0
- package/dist/services/dashboard.js +145 -58
- package/dist/services/delivery-access.js +123 -0
- package/dist/services/delivery-approval.js +68 -0
- package/dist/services/delivery-artifact.js +86 -0
- package/dist/services/delivery-checks.js +178 -0
- package/dist/services/delivery-execution.js +181 -0
- package/dist/services/delivery-intents.js +212 -0
- package/dist/services/delivery-operation.js +81 -0
- package/dist/services/delivery-policy.js +91 -0
- package/dist/services/delivery-procedures.js +43 -0
- package/dist/services/delivery-request.js +99 -0
- package/dist/services/delivery-result.js +144 -0
- package/dist/services/delivery-stage-commands.js +26 -0
- package/dist/services/delivery-stage-entry.js +56 -0
- package/dist/services/delivery-stage-handlers.js +135 -0
- package/dist/services/delivery-stage-results.js +94 -0
- package/dist/services/descriptor-coordinator-session.js +37 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +107 -0
- package/dist/services/descriptor-run-start.js +495 -0
- package/dist/services/descriptor-stage-coordinator.js +318 -0
- package/dist/services/descriptor-stage-settlement.js +268 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-repair-intake.js +123 -0
- package/dist/services/diagnostic-repair-source-intake.js +102 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +97 -0
- package/dist/services/diagnostic-run-bundle.js +113 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +109 -0
- package/dist/services/diagnostic-stage-results.js +158 -0
- package/dist/services/diagnostic-start.js +134 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -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/distill-document-publication.js +280 -0
- package/dist/services/distill-promotion.js +197 -0
- package/dist/services/distill-publication-custody.js +162 -0
- package/dist/services/distill-stage-handlers.js +249 -0
- package/dist/services/distill-stage-results.js +105 -0
- package/dist/services/distill-user-inputs.js +4 -0
- package/dist/services/distill-work-batch.js +42 -0
- package/dist/services/distill-work-collection.js +74 -0
- package/dist/services/distill-work-registration.js +93 -0
- package/dist/services/distill-work-results.js +98 -0
- package/dist/services/engines.js +354 -163
- package/dist/services/eval-snapshots.js +1326 -150
- package/dist/services/execution-bundles.js +275 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +316 -0
- package/dist/services/flow-catalog.js +54 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +404 -0
- package/dist/services/generic-execution.js +165 -0
- package/dist/services/harness-adapter.js +342 -35
- 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/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +813 -327
- package/dist/services/hybrid-contracts.js +479 -0
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +261 -0
- package/dist/services/interactive-attached-session.js +215 -0
- package/dist/services/interactive-baseline-custody.js +54 -0
- package/dist/services/interactive-before-versions.js +148 -0
- package/dist/services/interactive-bundle-data.js +85 -0
- package/dist/services/interactive-checks.js +179 -0
- package/dist/services/interactive-execution.js +33 -0
- package/dist/services/interactive-finish-integration.js +236 -0
- package/dist/services/interactive-git-baseline.js +230 -0
- package/dist/services/interactive-git-dispatch.js +278 -0
- package/dist/services/interactive-input-delivery.js +82 -0
- package/dist/services/interactive-mutation-scope.js +231 -0
- package/dist/services/interactive-request.js +287 -0
- package/dist/services/interactive-run-bundle.js +109 -0
- package/dist/services/interactive-stage-entry.js +41 -0
- package/dist/services/interactive-stage-handlers.js +289 -0
- package/dist/services/interactive-stage-observation.js +136 -0
- package/dist/services/interactive-stage-results.js +111 -0
- package/dist/services/interactive-user-inputs.js +142 -0
- package/dist/services/interactive-work-batch.js +43 -0
- package/dist/services/interactive-work-collection.js +119 -0
- package/dist/services/interactive-work-completion.js +180 -0
- package/dist/services/interactive-work-context.js +249 -0
- package/dist/services/interactive-work-history.js +112 -0
- package/dist/services/interactive-work-registration.js +124 -0
- package/dist/services/interactive-work-results.js +115 -0
- package/dist/services/interactive-work-start.js +155 -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 +2549 -0
- package/dist/services/managed-daemon-binding.js +129 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +449 -69
- package/dist/services/memory-accepted-composition.js +78 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +328 -0
- package/dist/services/memory-compose-handler.js +380 -0
- package/dist/services/memory-composition-acceptance.js +375 -0
- package/dist/services/memory-composition-graph.js +165 -0
- package/dist/services/memory-composition-registration.js +161 -0
- package/dist/services/memory-composition-work-custody.js +156 -0
- package/dist/services/memory-composition-work-results.js +108 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +37 -0
- package/dist/services/memory-corrected-review-findings.js +108 -0
- package/dist/services/memory-corrected-review-inputs.js +215 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +178 -0
- package/dist/services/memory-corrected-review-registration.js +192 -0
- package/dist/services/memory-correction-check-receipt.js +235 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +457 -0
- package/dist/services/memory-finish-handler.js +229 -0
- package/dist/services/memory-finish-integration.js +491 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-fix-finish-integration.js +129 -0
- package/dist/services/memory-fix-git-dispatch.js +139 -0
- package/dist/services/memory-fix-intake-custody.js +11 -0
- package/dist/services/memory-fix-intake-settlement.js +127 -0
- package/dist/services/memory-fix-publication.js +196 -0
- package/dist/services/memory-fix-repair-admission.js +116 -0
- package/dist/services/memory-fix-request.js +141 -0
- package/dist/services/memory-fix-run-bundle.js +83 -0
- package/dist/services/memory-fix-scope.js +78 -0
- package/dist/services/memory-fix-sources.js +85 -0
- package/dist/services/memory-fix-stage-entry.js +24 -0
- package/dist/services/memory-fix-stage-handlers.js +282 -0
- package/dist/services/memory-fix-stage-results.js +106 -0
- package/dist/services/memory-fix-workspace.js +127 -0
- package/dist/services/memory-flow-user-inputs.js +88 -0
- package/dist/services/memory-git-integration.js +220 -0
- package/dist/services/memory-historical-corrected-review.js +212 -0
- package/dist/services/memory-historical-original-review.js +143 -0
- package/dist/services/memory-historical-repair-registration.js +105 -0
- package/dist/services/memory-historical-second-review.js +147 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +181 -0
- package/dist/services/memory-init-controller-start.js +119 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +165 -0
- package/dist/services/memory-init-lint-capability.js +203 -0
- package/dist/services/memory-init-native-capacity.js +309 -0
- package/dist/services/memory-init-preflight.js +334 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +213 -0
- package/dist/services/memory-init-request-input.js +82 -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 +135 -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 +91 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +174 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +225 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +127 -0
- package/dist/services/memory-normal-flow-entry.js +87 -0
- package/dist/services/memory-ordinary-defaults.js +129 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +274 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +489 -0
- package/dist/services/memory-repair-history.js +496 -0
- package/dist/services/memory-repair-physical-binding.js +194 -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 +162 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +795 -0
- package/dist/services/memory-repair-registration.js +240 -0
- package/dist/services/memory-repair-work-baseline.js +415 -0
- package/dist/services/memory-repair-work-completion.js +117 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +108 -0
- package/dist/services/memory-repair-workspace.js +261 -0
- package/dist/services/memory-repair-writer-copy.js +736 -0
- package/dist/services/memory-result-binding.js +239 -0
- package/dist/services/memory-retained-review-authority.js +138 -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 +162 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +206 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +246 -0
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +117 -0
- package/dist/services/memory-review-work-custody.js +126 -0
- package/dist/services/memory-review-work-input.js +91 -0
- package/dist/services/memory-review-work-results.js +95 -0
- package/dist/services/memory-second-corrected-review-inputs.js +167 -0
- package/dist/services/memory-second-correction-plan.js +77 -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 +65 -0
- package/dist/services/memory-stage-results.js +290 -0
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +156 -0
- package/dist/services/memory-verification-checks.js +317 -0
- package/dist/services/memory-verification-receipt.js +160 -0
- package/dist/services/memory-verify-handler.js +137 -0
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +64 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +163 -35
- package/dist/services/migrations.js +164 -114
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +50 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/project-flow-source.js +195 -0
- package/dist/services/project-git-integration.js +92 -0
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +17 -87
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +463 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +588 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +150 -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 +29 -0
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/retained-native-hook.js +274 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/review-fix-handoff.js +82 -0
- package/dist/services/review-fix-intake-custody.js +17 -0
- package/dist/services/review-fix-stage-entry.js +43 -0
- package/dist/services/review-fix-stage-handlers.js +176 -0
- package/dist/services/review-fix-stage-results.js +86 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +469 -0
- package/dist/services/run-control.js +1109 -0
- package/dist/services/run-controller-adapter.js +434 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +226 -0
- package/dist/services/run-controller-recovery.js +335 -0
- package/dist/services/run-controller-state.js +106 -0
- package/dist/services/run-controller.js +1717 -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 +107 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +485 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/run-timeline.js +59 -0
- package/dist/services/runs.js +996 -271
- package/dist/services/runtime-budget.js +446 -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 +605 -0
- package/dist/services/runtime-scope-stop.js +211 -0
- package/dist/services/runtime-scope-worker.js +314 -0
- package/dist/services/runtime-service.js +19 -14
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +37 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +240 -18
- package/dist/services/stage-handlers.js +141 -0
- package/dist/services/stage-lifecycle.js +156 -119
- package/dist/services/stage-pause.js +222 -94
- package/dist/services/stage-report-renderer.js +230 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/technical-notes.js +31 -0
- package/dist/services/tooling.js +111 -44
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +393 -0
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +302 -91
- package/dist/services/vnext-code.js +325 -251
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +399 -20
- package/dist/services/vnext-merge.js +336 -114
- package/dist/services/vnext-plan-review.js +288 -133
- package/dist/services/vnext-plan.js +230 -106
- package/dist/services/vnext-protocolize.js +534 -96
- package/dist/services/vnext-specify.js +131 -84
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1950 -215
- package/dist/services/work-retry-context.js +55 -0
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +108 -48
- package/dist/shared/entity-references.js +1 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +931 -40
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +159 -0
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paired-writer-migration.js +424 -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 +96 -0
- package/dist/storage/writer-migration.js +346 -0
- package/dist/storage/writer-restore.js +217 -0
- package/dist/verification-components.json +384 -0
- package/package.json +52 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/probe-codex-history.mjs +37 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
- package/tools/verification-jobs.mjs +342 -0
package/dist/storage/database.js
CHANGED
|
@@ -1,12 +1,224 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
|
+
import { channel } from "node:diagnostics_channel";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { ensureDir } from "./paths.js";
|
|
6
7
|
import { AppError } from "../shared/errors.js";
|
|
8
|
+
import { executeStoreSchema, readStoreWriterContract, registerStoreWriter, registerReconciliationWriter, bindStoreWriterAdmission, STORE_WRITER_VERSION } from "./writer-contract.js";
|
|
9
|
+
import { assertImportAdmission } from "./import-guard.js";
|
|
10
|
+
import { withStoreCleanup, rollbackStoreTransaction } from "./native-cleanup.js";
|
|
11
|
+
const workContinuationStorageSql = `
|
|
12
|
+
CREATE TABLE IF NOT EXISTS stage_work_memberships (
|
|
13
|
+
project_id TEXT NOT NULL, run_id TEXT NOT NULL, work_id TEXT NOT NULL REFERENCES works(work_id),
|
|
14
|
+
stage TEXT NOT NULL, attempt TEXT NOT NULL, cycle TEXT NOT NULL DEFAULT '',
|
|
15
|
+
PRIMARY KEY(project_id, run_id, work_id, stage, attempt, cycle)
|
|
16
|
+
);
|
|
17
|
+
CREATE TABLE IF NOT EXISTS repair_intents (
|
|
18
|
+
identity TEXT PRIMARY KEY, project_id TEXT NOT NULL, run_id TEXT NOT NULL,
|
|
19
|
+
stage TEXT NOT NULL, attempt TEXT NOT NULL, payload_hash TEXT NOT NULL,
|
|
20
|
+
payload_json TEXT NOT NULL, work_id TEXT NOT NULL REFERENCES works(work_id), created_at TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
`;
|
|
23
|
+
function closeAfterOpenFailure(db, error) {
|
|
24
|
+
return withStoreCleanup(() => { throw error; }, () => { if (db.isOpen)
|
|
25
|
+
db.close(); });
|
|
26
|
+
}
|
|
7
27
|
const require = createRequire(import.meta.url);
|
|
8
|
-
const { DatabaseSync } = require("node:sqlite");
|
|
28
|
+
const { DatabaseSync, constants } = require("node:sqlite");
|
|
9
29
|
const resourceDatabases = new Map();
|
|
30
|
+
const resourceDatabaseIdentities = new WeakMap();
|
|
31
|
+
function assertResourceDatabaseIdentity(database) {
|
|
32
|
+
const expected = resourceDatabaseIdentities.get(database);
|
|
33
|
+
let actual;
|
|
34
|
+
try {
|
|
35
|
+
actual = fs.lstatSync(database.path);
|
|
36
|
+
}
|
|
37
|
+
catch { /* Refuse missing targets without reopening or initializing. */ }
|
|
38
|
+
if (!expected || !actual?.isFile() || actual.dev !== expected.dev || actual.ino !== expected.ino) {
|
|
39
|
+
throw new AppError("resource_storage_changed", "The cached resource registry no longer matches its physical database; close the owned connection before retrying", 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const publicationScopes = [];
|
|
43
|
+
/** Defer committed cross-store projections until the surrounding writer exits. */
|
|
44
|
+
export function withDeferredPublications(operation) {
|
|
45
|
+
const pending = [];
|
|
46
|
+
publicationScopes.push(pending);
|
|
47
|
+
let value;
|
|
48
|
+
let primary;
|
|
49
|
+
let failed = false;
|
|
50
|
+
try {
|
|
51
|
+
value = operation();
|
|
52
|
+
if (value && typeof value.then === "function")
|
|
53
|
+
throw new AppError("write_transaction_async", "Deferred publication scope must be synchronous", 1);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
primary = error;
|
|
57
|
+
failed = true;
|
|
58
|
+
}
|
|
59
|
+
publicationScopes.pop();
|
|
60
|
+
const parent = publicationScopes.at(-1);
|
|
61
|
+
if (parent)
|
|
62
|
+
parent.push(...pending);
|
|
63
|
+
else {
|
|
64
|
+
try {
|
|
65
|
+
executePublications(pending);
|
|
66
|
+
}
|
|
67
|
+
catch (publicationError) {
|
|
68
|
+
if (failed && primary instanceof AppError)
|
|
69
|
+
throw new AppError(primary.code, primary.message, primary.exitCode, { ...primary.details, effect: "partial", committed_publications: pending.length, publication_pending: true, publication_error: errorDetails(publicationError) });
|
|
70
|
+
if (failed)
|
|
71
|
+
throw new AppError("cross_store_failed", "Cross-store mutation failed and committed projections need reconciliation", 1, { effect: "partial", cause: errorDetails(primary), committed_publications: pending.length, publication_pending: true, publication_error: errorDetails(publicationError) });
|
|
72
|
+
throw publicationError;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (failed && pending.length && primary instanceof AppError)
|
|
76
|
+
throw new AppError(primary.code, primary.message, primary.exitCode, { ...primary.details, effect: "partial", committed_publications: pending.length });
|
|
77
|
+
if (failed && pending.length)
|
|
78
|
+
throw new AppError("cross_store_failed", "Cross-store mutation failed after a committed inner write", 1, { effect: "partial", cause: errorDetails(primary), committed_publications: pending.length });
|
|
79
|
+
if (failed)
|
|
80
|
+
throw primary;
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function executePublications(pending) {
|
|
84
|
+
let firstError;
|
|
85
|
+
let failed = false;
|
|
86
|
+
for (const operation of pending) {
|
|
87
|
+
try {
|
|
88
|
+
operation();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (!failed)
|
|
92
|
+
firstError = error;
|
|
93
|
+
failed = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (failed) {
|
|
97
|
+
if (firstError instanceof AppError && firstError.details.effect === "committed")
|
|
98
|
+
throw firstError;
|
|
99
|
+
throw new AppError("publication_pending", "SQL committed but a required projection needs reconciliation", 1, { effect: "committed", publication_pending: true, cause: errorDetails(firstError) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Invocation authority is prepared once with the runtime schema. Native
|
|
103
|
+
* hooks must only verify this table; they never create it on the hot path. */
|
|
104
|
+
export function ensureLifecycleInvocationStorage(db) {
|
|
105
|
+
db.exec(`
|
|
106
|
+
CREATE TABLE IF NOT EXISTS lifecycle_invocations (
|
|
107
|
+
id TEXT PRIMARY KEY, scope_json TEXT NOT NULL, command TEXT NOT NULL,
|
|
108
|
+
fingerprint TEXT NOT NULL, status TEXT NOT NULL,
|
|
109
|
+
event_key TEXT UNIQUE, identity_json TEXT, admission_json TEXT, deadline INTEGER, outcome_json TEXT,
|
|
110
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
111
|
+
);
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_lifecycle_invocations_scope
|
|
113
|
+
ON lifecycle_invocations(scope_json, fingerprint, created_at);
|
|
114
|
+
CREATE TABLE IF NOT EXISTS lifecycle_packet_assignments (
|
|
115
|
+
packet_sha256 TEXT NOT NULL,
|
|
116
|
+
invocation_id TEXT NOT NULL REFERENCES lifecycle_invocations(id),
|
|
117
|
+
PRIMARY KEY(packet_sha256, invocation_id)
|
|
118
|
+
);
|
|
119
|
+
`);
|
|
120
|
+
ensureColumn(db, "lifecycle_invocations", "admission_json", "ALTER TABLE lifecycle_invocations ADD COLUMN admission_json TEXT");
|
|
121
|
+
}
|
|
122
|
+
/** RUN authority stays in its existing store; process leases stay in runtime.sqlite. */
|
|
123
|
+
export function ensureRunControlStorage(db) {
|
|
124
|
+
db.execSchema(`
|
|
125
|
+
CREATE TABLE IF NOT EXISTS run_controls (
|
|
126
|
+
control_id TEXT PRIMARY KEY,
|
|
127
|
+
project_id TEXT NOT NULL, run_id TEXT NOT NULL,
|
|
128
|
+
request_id TEXT NOT NULL, request_hash TEXT NOT NULL, request_json TEXT NOT NULL,
|
|
129
|
+
recovery_id TEXT NOT NULL REFERENCES run_recovery_guards(recovery_id),
|
|
130
|
+
action TEXT NOT NULL, status TEXT NOT NULL,
|
|
131
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL,
|
|
132
|
+
UNIQUE(project_id, run_id, request_id),
|
|
133
|
+
FOREIGN KEY(project_id, run_id) REFERENCES runs(project_id, id)
|
|
134
|
+
);
|
|
135
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_run_control_active
|
|
136
|
+
ON run_controls(project_id, run_id) WHERE status IN ('requested','settled','resuming');
|
|
137
|
+
CREATE TABLE IF NOT EXISTS run_control_admissions (
|
|
138
|
+
control_id TEXT NOT NULL REFERENCES run_controls(control_id),
|
|
139
|
+
work_session_id TEXT NOT NULL REFERENCES work_sessions(id),
|
|
140
|
+
work_id TEXT NOT NULL REFERENCES works(work_id),
|
|
141
|
+
session_id TEXT NOT NULL, admitted_at TEXT NOT NULL,
|
|
142
|
+
PRIMARY KEY(control_id, work_session_id)
|
|
143
|
+
);
|
|
144
|
+
CREATE TABLE IF NOT EXISTS run_control_stage_admissions (
|
|
145
|
+
control_id TEXT NOT NULL REFERENCES run_controls(control_id),
|
|
146
|
+
stage TEXT NOT NULL, attempt TEXT NOT NULL,
|
|
147
|
+
work_session_id TEXT NOT NULL REFERENCES work_sessions(id),
|
|
148
|
+
PRIMARY KEY(control_id, stage)
|
|
149
|
+
);
|
|
150
|
+
CREATE TABLE IF NOT EXISTS run_control_operations (
|
|
151
|
+
operation_id TEXT PRIMARY KEY, control_id TEXT NOT NULL REFERENCES run_controls(control_id),
|
|
152
|
+
target_json TEXT NOT NULL, status TEXT NOT NULL, receipt_json TEXT, error_json TEXT,
|
|
153
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
154
|
+
);
|
|
155
|
+
CREATE TABLE IF NOT EXISTS run_control_workers (
|
|
156
|
+
control_id TEXT PRIMARY KEY REFERENCES run_controls(control_id),
|
|
157
|
+
owner_token TEXT NOT NULL, owner_pid INTEGER,
|
|
158
|
+
status TEXT NOT NULL, process_id TEXT, process_lease_token TEXT,
|
|
159
|
+
resource_home TEXT NOT NULL, state_dir TEXT NOT NULL,
|
|
160
|
+
snapshot_json TEXT, error_json TEXT,
|
|
161
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
162
|
+
);
|
|
163
|
+
`);
|
|
164
|
+
}
|
|
165
|
+
export function ensureRunControllerStorage(db) {
|
|
166
|
+
db.execSchema(`
|
|
167
|
+
CREATE TABLE IF NOT EXISTS run_controllers (
|
|
168
|
+
controller_id TEXT PRIMARY KEY,
|
|
169
|
+
project_id TEXT NOT NULL, run_id TEXT NOT NULL,
|
|
170
|
+
request_id TEXT NOT NULL, request_hash TEXT NOT NULL,
|
|
171
|
+
generation INTEGER NOT NULL, status TEXT NOT NULL,
|
|
172
|
+
owner_token TEXT NOT NULL, process_id TEXT, process_lease_token TEXT,
|
|
173
|
+
owner_pid INTEGER, manifest_json TEXT NOT NULL, state_json TEXT NOT NULL,
|
|
174
|
+
state_dir TEXT NOT NULL, last_error_json TEXT,
|
|
175
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL,
|
|
176
|
+
FOREIGN KEY(project_id, run_id) REFERENCES runs(project_id, id),
|
|
177
|
+
UNIQUE(project_id, run_id, request_id)
|
|
178
|
+
);
|
|
179
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_run_controller_owner
|
|
180
|
+
ON run_controllers(project_id, run_id)
|
|
181
|
+
WHERE status NOT IN ('completed', 'cancelled', 'superseded');
|
|
182
|
+
CREATE TABLE IF NOT EXISTS run_controller_events (
|
|
183
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
184
|
+
controller_id TEXT NOT NULL REFERENCES run_controllers(controller_id),
|
|
185
|
+
event_type TEXT NOT NULL, data_json TEXT NOT NULL, created_at TEXT NOT NULL
|
|
186
|
+
);
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_run_controller_events
|
|
188
|
+
ON run_controller_events(controller_id, sequence);
|
|
189
|
+
CREATE TABLE IF NOT EXISTS run_controller_operations (
|
|
190
|
+
operation_id TEXT PRIMARY KEY,
|
|
191
|
+
controller_id TEXT NOT NULL REFERENCES run_controllers(controller_id),
|
|
192
|
+
generation INTEGER NOT NULL, input_hash TEXT NOT NULL,
|
|
193
|
+
input_json TEXT NOT NULL, status TEXT NOT NULL,
|
|
194
|
+
receipt_json TEXT, error_json TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
195
|
+
);
|
|
196
|
+
`);
|
|
197
|
+
}
|
|
198
|
+
/** Recovery guards apply to every RUN kind, including custom eval fixtures. */
|
|
199
|
+
export function ensureRecoveryStorage(db) {
|
|
200
|
+
db.exec(`
|
|
201
|
+
CREATE TABLE IF NOT EXISTS run_recovery_guards (
|
|
202
|
+
recovery_id TEXT PRIMARY KEY, project_id TEXT NOT NULL, run_id TEXT NOT NULL, generation INTEGER NOT NULL, status TEXT NOT NULL, interruption_json TEXT NOT NULL, settlement_json TEXT, capture_path TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, sealed_at TEXT, resumed_at TEXT, UNIQUE(project_id, run_id, generation), FOREIGN KEY(project_id, run_id) REFERENCES runs(project_id, id)
|
|
203
|
+
);
|
|
204
|
+
CREATE INDEX IF NOT EXISTS idx_run_recovery_guards_run ON run_recovery_guards(project_id, run_id, generation DESC);
|
|
205
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_run_recovery_guards_active ON run_recovery_guards(project_id, run_id) WHERE status IN ('draining', 'sealed', 'resuming');
|
|
206
|
+
CREATE TABLE IF NOT EXISTS work_recovery_segments (
|
|
207
|
+
recovery_id TEXT NOT NULL, work_id TEXT NOT NULL, generation INTEGER NOT NULL, prior_started_at TEXT, evidence_path TEXT NOT NULL, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY(recovery_id, work_id), FOREIGN KEY(recovery_id) REFERENCES run_recovery_guards(recovery_id), FOREIGN KEY(work_id) REFERENCES works(work_id)
|
|
208
|
+
);
|
|
209
|
+
CREATE INDEX IF NOT EXISTS idx_work_recovery_segments_work ON work_recovery_segments(work_id, generation DESC);
|
|
210
|
+
CREATE TABLE IF NOT EXISTS run_recovery_bindings (
|
|
211
|
+
recovery_id TEXT PRIMARY KEY REFERENCES run_recovery_guards(recovery_id),
|
|
212
|
+
work_id TEXT NOT NULL REFERENCES works(work_id), prior_work_session_id TEXT NOT NULL,
|
|
213
|
+
session_id TEXT NOT NULL, harness TEXT NOT NULL, provider_session_id TEXT NOT NULL,
|
|
214
|
+
daemon_id TEXT NOT NULL, status TEXT NOT NULL, hook_event_key TEXT,
|
|
215
|
+
created_at TEXT NOT NULL, accepted_at TEXT
|
|
216
|
+
);
|
|
217
|
+
`);
|
|
218
|
+
ensureColumn(db, "run_recovery_bindings", "packet_json", "ALTER TABLE run_recovery_bindings ADD COLUMN packet_json TEXT");
|
|
219
|
+
ensureColumn(db, "run_recovery_bindings", "packet_sha256", "ALTER TABLE run_recovery_bindings ADD COLUMN packet_sha256 TEXT");
|
|
220
|
+
ensureColumn(db, "run_recovery_bindings", "runtime_dispatch_json", "ALTER TABLE run_recovery_bindings ADD COLUMN runtime_dispatch_json TEXT");
|
|
221
|
+
}
|
|
10
222
|
/** Creates the single Work/Session authority used by a fresh vNext beta runtime. */
|
|
11
223
|
export function ensureVnextWorkStorage(db) {
|
|
12
224
|
const legacy = db.get("SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('vnext_works', 'vnext_agent_turns', 'flow_agent_turns') LIMIT 1");
|
|
@@ -16,7 +228,7 @@ export function ensureVnextWorkStorage(db) {
|
|
|
16
228
|
if (existing && (!/task TEXT NOT NULL/.test(existing.sql) || /launch_token|adapter_session_id/.test(existing.sql))) {
|
|
17
229
|
throw new AppError("beta_storage_recreate_required", "This beta requires a fresh DD_FLOW_HOME; the existing works schema is not compatible", 1);
|
|
18
230
|
}
|
|
19
|
-
db.
|
|
231
|
+
db.execSchema(`
|
|
20
232
|
CREATE TABLE IF NOT EXISTS works (
|
|
21
233
|
work_id TEXT PRIMARY KEY,
|
|
22
234
|
project_id TEXT NOT NULL,
|
|
@@ -46,6 +258,9 @@ export function ensureVnextWorkStorage(db) {
|
|
|
46
258
|
status TEXT NOT NULL,
|
|
47
259
|
prompt_path TEXT NOT NULL,
|
|
48
260
|
result_path TEXT,
|
|
261
|
+
start_receipt_json TEXT,
|
|
262
|
+
start_publication_status TEXT,
|
|
263
|
+
result_receipt_json TEXT,
|
|
49
264
|
created_at TEXT NOT NULL,
|
|
50
265
|
updated_at TEXT NOT NULL,
|
|
51
266
|
completed_at TEXT,
|
|
@@ -53,6 +268,16 @@ export function ensureVnextWorkStorage(db) {
|
|
|
53
268
|
);
|
|
54
269
|
CREATE INDEX IF NOT EXISTS idx_work_sessions_work ON work_sessions(work_id, created_at DESC);
|
|
55
270
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_work_sessions_open ON work_sessions(work_id) WHERE status = 'running';
|
|
271
|
+
|
|
272
|
+
CREATE TABLE IF NOT EXISTS run_recovery_guards (
|
|
273
|
+
recovery_id TEXT PRIMARY KEY, project_id TEXT NOT NULL, run_id TEXT NOT NULL, generation INTEGER NOT NULL, status TEXT NOT NULL, interruption_json TEXT NOT NULL, settlement_json TEXT, capture_path TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, sealed_at TEXT, resumed_at TEXT, UNIQUE(project_id, run_id, generation), FOREIGN KEY(project_id, run_id) REFERENCES runs(project_id, id)
|
|
274
|
+
);
|
|
275
|
+
CREATE INDEX IF NOT EXISTS idx_run_recovery_guards_run ON run_recovery_guards(project_id, run_id, generation DESC);
|
|
276
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_run_recovery_guards_active ON run_recovery_guards(project_id, run_id) WHERE status IN ('draining', 'sealed', 'resuming');
|
|
277
|
+
CREATE TABLE IF NOT EXISTS work_recovery_segments (
|
|
278
|
+
recovery_id TEXT NOT NULL, work_id TEXT NOT NULL, generation INTEGER NOT NULL, prior_started_at TEXT, evidence_path TEXT NOT NULL, status TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY(recovery_id, work_id), FOREIGN KEY(recovery_id) REFERENCES run_recovery_guards(recovery_id), FOREIGN KEY(work_id) REFERENCES works(work_id)
|
|
279
|
+
);
|
|
280
|
+
CREATE INDEX IF NOT EXISTS idx_work_recovery_segments_work ON work_recovery_segments(work_id, generation DESC);
|
|
56
281
|
CREATE TABLE IF NOT EXISTS check_receipts (
|
|
57
282
|
id TEXT PRIMARY KEY,
|
|
58
283
|
project_id TEXT NOT NULL,
|
|
@@ -65,6 +290,9 @@ export function ensureVnextWorkStorage(db) {
|
|
|
65
290
|
command TEXT NOT NULL,
|
|
66
291
|
input_hash TEXT NOT NULL DEFAULT '',
|
|
67
292
|
verification_epoch TEXT NOT NULL DEFAULT '',
|
|
293
|
+
reservation_sequence INTEGER NOT NULL DEFAULT 0,
|
|
294
|
+
retry_of_receipt_id TEXT,
|
|
295
|
+
retry_reason TEXT,
|
|
68
296
|
status TEXT NOT NULL,
|
|
69
297
|
exit_code INTEGER,
|
|
70
298
|
stdout_path TEXT NOT NULL,
|
|
@@ -81,50 +309,316 @@ export function ensureVnextWorkStorage(db) {
|
|
|
81
309
|
FOREIGN KEY(work_id) REFERENCES works(work_id)
|
|
82
310
|
);
|
|
83
311
|
CREATE INDEX IF NOT EXISTS idx_check_receipts_run ON check_receipts(project_id, run_id, started_at);
|
|
312
|
+
${workContinuationStorageSql}
|
|
84
313
|
`);
|
|
314
|
+
// A committed Work start is authoritative even if publishing its prompt
|
|
315
|
+
// files is interrupted after commit. Keep the exact bytes for repair.
|
|
316
|
+
db.writeTransaction(() => {
|
|
317
|
+
const workColumns = db.all("PRAGMA table_info(works)");
|
|
318
|
+
if (!workColumns.some(column => column.name === "current_start_invocation_id"))
|
|
319
|
+
db.execSchema("ALTER TABLE works ADD COLUMN current_start_invocation_id TEXT");
|
|
320
|
+
const columns = db.all("PRAGMA table_info(work_sessions)");
|
|
321
|
+
if (!columns.some((column) => column.name === "start_receipt_json"))
|
|
322
|
+
db.execSchema("ALTER TABLE work_sessions ADD COLUMN start_receipt_json TEXT");
|
|
323
|
+
if (!columns.some((column) => column.name === "start_publication_status"))
|
|
324
|
+
db.execSchema("ALTER TABLE work_sessions ADD COLUMN start_publication_status TEXT");
|
|
325
|
+
if (!columns.some((column) => column.name === "result_receipt_json"))
|
|
326
|
+
db.execSchema("ALTER TABLE work_sessions ADD COLUMN result_receipt_json TEXT");
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/** Durable intent for a provider-root external Work. It is deliberately
|
|
330
|
+
* separate from `work_sessions`: only a trusted hook may bind that Session. */
|
|
331
|
+
export function ensureWorkLaunchStorage(db) {
|
|
332
|
+
db.execSchema(`
|
|
333
|
+
CREATE TABLE IF NOT EXISTS work_launches (
|
|
334
|
+
operation_id TEXT PRIMARY KEY,
|
|
335
|
+
work_id TEXT NOT NULL UNIQUE,
|
|
336
|
+
profile_json TEXT NOT NULL,
|
|
337
|
+
profile_hash TEXT NOT NULL,
|
|
338
|
+
status TEXT NOT NULL,
|
|
339
|
+
state_dir TEXT NOT NULL,
|
|
340
|
+
journal_path TEXT NOT NULL,
|
|
341
|
+
provider_session_id TEXT,
|
|
342
|
+
adapter_receipt_json TEXT,
|
|
343
|
+
created_at TEXT NOT NULL,
|
|
344
|
+
updated_at TEXT NOT NULL,
|
|
345
|
+
FOREIGN KEY(work_id) REFERENCES works(work_id)
|
|
346
|
+
);
|
|
347
|
+
CREATE INDEX IF NOT EXISTS idx_work_launches_status ON work_launches(status, updated_at DESC);
|
|
348
|
+
`);
|
|
349
|
+
}
|
|
350
|
+
/** Exclusive fresh creation; never reinterpret an empty/old database as fresh. */
|
|
351
|
+
function claimNewStore(file) {
|
|
352
|
+
for (const candidate of [file, `${file}-wal`, `${file}-shm`, `${file}-journal`]) {
|
|
353
|
+
try {
|
|
354
|
+
fs.lstatSync(candidate);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (error.code === "ENOENT")
|
|
358
|
+
continue;
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
throw new AppError("store_already_exists", "Fresh store creation refuses existing database or sidecar files", 1, { effect: "no_effect", path: candidate });
|
|
362
|
+
}
|
|
363
|
+
ensureDir(path.dirname(file));
|
|
364
|
+
let fd;
|
|
365
|
+
try {
|
|
366
|
+
fd = fs.openSync(file, "wx", 0o600);
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
if (error.code === "EEXIST")
|
|
370
|
+
throw new AppError("store_already_exists", "Another owner already created this store", 1, { effect: "no_effect", path: file });
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
fs.closeSync(fd);
|
|
85
374
|
}
|
|
86
|
-
export function getDatabase(ddFlowHome, mode = "initialize") {
|
|
375
|
+
export function getDatabase(ddFlowHome, mode = "initialize", importPermitId, hookDeadlineAt) {
|
|
376
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_open", store_kind: "run" });
|
|
377
|
+
if (hookDeadlineAt !== undefined && !Number.isSafeInteger(hookDeadlineAt))
|
|
378
|
+
throw new AppError("hook_deadline_invalid", "Deadline must be a finite absolute millisecond deadline", 1, { effect: "unknown" });
|
|
379
|
+
const deadline = hookDeadlineAt === undefined ? undefined : (phase) => {
|
|
380
|
+
const remaining = hookDeadlineAt - Date.now();
|
|
381
|
+
if (remaining <= 0)
|
|
382
|
+
throw new AppError("native_hook_timeout", "Native hook deadline expired before durable admission", 1, { phase, deadline_at: hookDeadlineAt, effect: "unknown" });
|
|
383
|
+
return Math.max(1, Math.min(4000, remaining));
|
|
384
|
+
};
|
|
385
|
+
deadline?.("store_open");
|
|
386
|
+
if (mode !== "read_existing")
|
|
387
|
+
assertImportAdmission(ddFlowHome, importPermitId);
|
|
87
388
|
const dbPath = path.join(ddFlowHome, "db.sqlite");
|
|
389
|
+
if (mode === "create_new")
|
|
390
|
+
claimNewStore(dbPath);
|
|
88
391
|
const exists = fs.existsSync(dbPath);
|
|
392
|
+
if (mode === "prepared_existing" && (!exists || !fs.lstatSync(dbPath).isFile()))
|
|
393
|
+
throw new AppError("run_storage_unprepared", "Prepare a regular existing RUN store before execution admission", 1);
|
|
89
394
|
if (mode === "read_existing" && !exists) {
|
|
90
395
|
return emptyReadOnlyDatabase(dbPath);
|
|
91
396
|
}
|
|
397
|
+
if (mode === "hook" && !exists)
|
|
398
|
+
throw new AppError("hook_storage_unprepared", "Prepare the runtime store before starting native hooks", 1);
|
|
399
|
+
if (mode === "reconcile" && !exists)
|
|
400
|
+
throw new AppError("reconciliation_storage_unprepared", "Reconciliation cannot create a runtime store", 1);
|
|
92
401
|
if (!exists || mode === "migrate")
|
|
93
402
|
ensureDir(ddFlowHome);
|
|
94
403
|
const db = mode === "read_existing" ? new DatabaseSync(dbPath, { readOnly: true }) : new DatabaseSync(dbPath);
|
|
95
|
-
|
|
404
|
+
if (mode !== "read_existing")
|
|
405
|
+
installWriterAdmission(db, () => assertImportAdmission(ddFlowHome, importPermitId), () => db.isTransaction, true);
|
|
406
|
+
try {
|
|
407
|
+
db.exec(`PRAGMA busy_timeout = ${deadline?.("store_open") ?? 4000}`);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
return closeAfterOpenFailure(db, error);
|
|
411
|
+
}
|
|
412
|
+
if (mode !== "read_existing") {
|
|
413
|
+
try {
|
|
414
|
+
const admission = () => { if (!db.isTransaction)
|
|
415
|
+
assertImportAdmission(ddFlowHome, importPermitId); };
|
|
416
|
+
if (mode === "reconcile")
|
|
417
|
+
registerReconciliationWriter(db, admission);
|
|
418
|
+
else
|
|
419
|
+
registerStoreWriter(db, admission);
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
return closeAfterOpenFailure(db, error);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
configureDatabase(db, mode, deadline);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
return closeAfterOpenFailure(db, error);
|
|
430
|
+
}
|
|
96
431
|
// Internal additive schema changes must be available to every write command.
|
|
97
432
|
// Higher-level Memory Bank migrations remain explicit in services/migrations.
|
|
98
|
-
if (mode !== "read_existing")
|
|
99
|
-
|
|
433
|
+
if (mode !== "read_existing" && mode !== "reconcile") {
|
|
434
|
+
try {
|
|
435
|
+
const initialize = () => { migrate(db); ensureRecoveryStorage(db); ensureLifecycleInvocationStorage(db); };
|
|
436
|
+
if (mode === "prepared_existing") {
|
|
437
|
+
const required = readStoreWriterContract(db);
|
|
438
|
+
if (required !== STORE_WRITER_VERSION)
|
|
439
|
+
throw new AppError("store_writer_incompatible", "Existing RUN store has no compatible writer contract", 1, { required, supported: STORE_WRITER_VERSION });
|
|
440
|
+
}
|
|
441
|
+
else if (mode !== "hook") {
|
|
442
|
+
const fresh = !db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1").get();
|
|
443
|
+
if (fresh || readStoreWriterContract(db) !== null)
|
|
444
|
+
executeStoreSchema(db, initialize, fresh);
|
|
445
|
+
else
|
|
446
|
+
initialize();
|
|
447
|
+
// This migration owns a VACUUM backup and a separate data transaction.
|
|
448
|
+
migrateSessionStorageKeys(db, dbPath);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
deadline?.("store_validation");
|
|
452
|
+
assertHookStoreReady(db);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
return closeAfterOpenFailure(db, error);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
100
459
|
if (mode === "read_existing")
|
|
101
460
|
db.exec("PRAGMA query_only = ON");
|
|
461
|
+
if (mode === "reconcile") {
|
|
462
|
+
try {
|
|
463
|
+
db.prepare("SELECT id, project_id, status, verdict, next_action, index_json, updated_at, completed_at FROM runs LIMIT 0");
|
|
464
|
+
db.prepare("SELECT id, root FROM projects LIMIT 0");
|
|
465
|
+
db.prepare("SELECT id, event_type, payload_json FROM audit_events LIMIT 0");
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
return closeAfterOpenFailure(db, error);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
102
471
|
db.exec("PRAGMA foreign_keys = ON");
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
472
|
+
return databaseFacade(dbPath, mode !== "read_existing", db, undefined, deadline, mode === "read_existing" ? undefined : () => assertImportAdmission(ddFlowHome, importPermitId));
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Hook ingress is a hot, synchronous path. It may write receipts, but it
|
|
476
|
+
* must never perform schema migration or VACUUM while the native host waits.
|
|
477
|
+
* Startup/migration owns this contract; a missing table is an explicit
|
|
478
|
+
* preparation error instead of an implicit migration race.
|
|
479
|
+
*/
|
|
480
|
+
function assertHookStoreReady(db) {
|
|
481
|
+
const required = ["projects", "hook_events", "codex_session_bindings", "sessions", "flow_session_segments", "lifecycle_invocations", "lifecycle_packet_assignments"];
|
|
482
|
+
const missing = required.filter((table) => !db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name = ?").get(table));
|
|
483
|
+
if (missing.length)
|
|
484
|
+
throw new AppError("hook_storage_unprepared", "Managed store is not prepared for synchronous hook admission", 1, { missing_tables: missing, next_action: "Open the runtime with a normal dd-flow command and complete migration before starting the native Session." });
|
|
485
|
+
// Table presence alone does not establish compatibility with an older home.
|
|
486
|
+
const receiptColumns = {
|
|
487
|
+
hook_events: "id, project_id, harness, provider_session_id, parent_session_id, daemon_id, session_id, agent_id, turn_id, event_name, tool_name, event_key, cwd, match_key, transcript_path, status, sanitized_summary, outcome_json, created_at",
|
|
488
|
+
lifecycle_invocations: "id, scope_json, command, fingerprint, status, event_key, identity_json, deadline, outcome_json, created_at, updated_at",
|
|
489
|
+
codex_session_bindings: "session_id, project_id, protocol_id, handshake_id, cwd, transcript_path, status, created_at, updated_at"
|
|
111
490
|
};
|
|
491
|
+
for (const [table, columns] of Object.entries(receiptColumns)) {
|
|
492
|
+
try {
|
|
493
|
+
db.prepare(`SELECT ${columns} FROM ${table} LIMIT 0`);
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
throw new AppError("hook_storage_unprepared", "Managed receipt schema needs preparation before native hooks start", 1, { table });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
112
499
|
}
|
|
113
500
|
/**
|
|
114
501
|
* Opens the small host-wide resource registry. It is intentionally separate
|
|
115
502
|
* from a RUN database: a process or a port can outlive the CLI client that
|
|
116
503
|
* created it, while a RUN database belongs to one flow home.
|
|
117
504
|
*/
|
|
118
|
-
export function
|
|
119
|
-
ensureDir(resourceHome);
|
|
505
|
+
export function withResourceReadSnapshot(resourceHome, read) {
|
|
120
506
|
const dbPath = path.join(resourceHome, "runtime.sqlite");
|
|
121
507
|
const cached = resourceDatabases.get(dbPath);
|
|
508
|
+
if (!cached && !fs.existsSync(dbPath))
|
|
509
|
+
return read(emptyReadOnlyDatabase(dbPath));
|
|
510
|
+
const db = cached ?? new DatabaseSync(dbPath, { readOnly: true });
|
|
511
|
+
const operation = () => {
|
|
512
|
+
if (!cached)
|
|
513
|
+
db.exec("PRAGMA busy_timeout = 4000; PRAGMA query_only = ON");
|
|
514
|
+
// A second process can observe SQLite's file before the initializing
|
|
515
|
+
// transaction publishes its schema. Treat that pre-commit view exactly
|
|
516
|
+
// like an absent registry; the writer path remains the serialization
|
|
517
|
+
// boundary and will reopen the committed schema.
|
|
518
|
+
if (!cached && !db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1").get()) {
|
|
519
|
+
return read(emptyReadOnlyDatabase(dbPath));
|
|
520
|
+
}
|
|
521
|
+
const reader = cached
|
|
522
|
+
? { ...emptyReadOnlyDatabase(dbPath), get: cached.get, all: cached.all }
|
|
523
|
+
: databaseFacade(dbPath, false, db);
|
|
524
|
+
// The facade owns savepoint cleanup and poisons a connection when rollback
|
|
525
|
+
// fails; the callback only receives the read-only view.
|
|
526
|
+
return (cached ?? reader).readSnapshot(() => read(reader));
|
|
527
|
+
};
|
|
122
528
|
if (cached)
|
|
123
|
-
return
|
|
529
|
+
return operation();
|
|
530
|
+
const native = db;
|
|
531
|
+
return withStoreCleanup(operation, () => { if (native.isOpen)
|
|
532
|
+
native.close(); });
|
|
533
|
+
}
|
|
534
|
+
export function getResourceDatabase(resourceHome, mode = "initialize", deadlineAt, importPermitId) {
|
|
535
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_open", store_kind: "resource" });
|
|
536
|
+
const deadline = requestDeadline(deadlineAt);
|
|
537
|
+
deadline?.("resource_open");
|
|
538
|
+
const dbPath = path.join(resourceHome, "runtime.sqlite");
|
|
539
|
+
if (mode !== "read_existing" && mode !== "read_isolated")
|
|
540
|
+
assertImportAdmission(resourceHome, importPermitId);
|
|
541
|
+
if (mode === "retirement" && !fs.existsSync(dbPath))
|
|
542
|
+
return getResourceDatabase(resourceHome, "initialize", deadlineAt, importPermitId);
|
|
543
|
+
const cached = resourceDatabases.get(dbPath);
|
|
544
|
+
if (mode === "create_new") {
|
|
545
|
+
if (cached)
|
|
546
|
+
throw new AppError("store_already_exists", "Fresh store creation refuses a cached resource owner", 1, { effect: "no_effect", path: dbPath });
|
|
547
|
+
claimNewStore(dbPath);
|
|
548
|
+
}
|
|
549
|
+
if (mode === "read_existing" || mode === "read_isolated") {
|
|
550
|
+
// Reuse a current transaction's view without exposing its write capability.
|
|
551
|
+
if (cached && mode === "read_existing") {
|
|
552
|
+
const bounded = deadlineAt === undefined ? cached : cached.withDeadline(deadlineAt);
|
|
553
|
+
return { ...emptyReadOnlyDatabase(dbPath), get: bounded.get, all: bounded.all };
|
|
554
|
+
}
|
|
555
|
+
if (!fs.existsSync(dbPath))
|
|
556
|
+
return emptyReadOnlyDatabase(dbPath);
|
|
557
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_connection", store_kind: "resource" });
|
|
558
|
+
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
559
|
+
try {
|
|
560
|
+
db.exec(`PRAGMA busy_timeout = ${deadline?.("resource_open") ?? 4000}; PRAGMA query_only = ON`);
|
|
561
|
+
if (!db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1").get()) {
|
|
562
|
+
db.close();
|
|
563
|
+
return emptyReadOnlyDatabase(dbPath);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
return closeAfterOpenFailure(db, error);
|
|
568
|
+
}
|
|
569
|
+
return databaseFacade(dbPath, false, db, undefined, deadline);
|
|
570
|
+
}
|
|
571
|
+
if (mode === "prepared_existing" && !fs.existsSync(dbPath))
|
|
572
|
+
throw new AppError("resource_storage_unprepared", "Prepare the resource registry before maintenance", 1);
|
|
573
|
+
if (cached) {
|
|
574
|
+
if (importPermitId && resourceDatabaseIdentities.get(cached)?.importPermitId !== importPermitId)
|
|
575
|
+
throw new AppError("resource_writer_reopen_required", "Close the owned cached resource writer before opening a privileged maintenance connection", 1);
|
|
576
|
+
if (mode === "prepared_existing" || mode === "retirement")
|
|
577
|
+
assertResourceDatabaseIdentity(cached);
|
|
578
|
+
const bounded = deadlineAt === undefined ? cached : cached.withDeadline(deadlineAt);
|
|
579
|
+
const reader = { prepare: (sql) => ({ get: (...params) => bounded.get(sql, params), all: (...params) => bounded.all(sql, params), run: (...params) => bounded.run(sql, params) }) };
|
|
580
|
+
assertResourceStoreReady(reader, mode === "retirement");
|
|
581
|
+
return bounded;
|
|
582
|
+
}
|
|
583
|
+
if (mode !== "prepared_existing" && mode !== "retirement")
|
|
584
|
+
ensureDir(resourceHome);
|
|
585
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_connection", store_kind: "resource" });
|
|
124
586
|
const db = new DatabaseSync(dbPath);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
587
|
+
installWriterAdmission(db, () => assertImportAdmission(resourceHome, importPermitId), () => db.isTransaction, true);
|
|
588
|
+
try {
|
|
589
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_writer_registration", store_kind: "resource" });
|
|
590
|
+
db.exec(`PRAGMA busy_timeout = ${deadline?.("resource_open") ?? 4000}`);
|
|
591
|
+
const admission = () => { if (!db.isTransaction)
|
|
592
|
+
assertImportAdmission(resourceHome, importPermitId); };
|
|
593
|
+
if (mode === "retirement")
|
|
594
|
+
registerReconciliationWriter(db, admission);
|
|
595
|
+
else
|
|
596
|
+
registerStoreWriter(db, admission);
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
return closeAfterOpenFailure(db, error);
|
|
600
|
+
}
|
|
601
|
+
channel("dd-flow.sqlite.ingress").publish({ phase: "store_validation", store_kind: "resource" });
|
|
602
|
+
try {
|
|
603
|
+
if (mode === "prepared_existing" || mode === "retirement")
|
|
604
|
+
assertResourceStoreReady(db, mode === "retirement");
|
|
605
|
+
else
|
|
606
|
+
configureDatabase(db, "initialize", deadline);
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
return closeAfterOpenFailure(db, error);
|
|
610
|
+
}
|
|
611
|
+
let fresh;
|
|
612
|
+
try {
|
|
613
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
614
|
+
fresh = !db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1").get();
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
return closeAfterOpenFailure(db, error);
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
if (mode !== "prepared_existing" && mode !== "retirement")
|
|
621
|
+
executeStoreSchema(db, `
|
|
128
622
|
CREATE TABLE IF NOT EXISTS managed_processes (
|
|
129
623
|
id TEXT PRIMARY KEY,
|
|
130
624
|
kind TEXT NOT NULL,
|
|
@@ -163,37 +657,401 @@ export function getResourceDatabase(resourceHome) {
|
|
|
163
657
|
);
|
|
164
658
|
CREATE INDEX IF NOT EXISTS idx_managed_resources_lease
|
|
165
659
|
ON managed_resources(lease_expires_at, updated_at);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
660
|
+
`, fresh);
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
return closeAfterOpenFailure(db, error);
|
|
664
|
+
}
|
|
665
|
+
const database = databaseFacade(dbPath, true, db, () => {
|
|
666
|
+
resourceDatabases.delete(dbPath);
|
|
667
|
+
db.close?.();
|
|
668
|
+
}, undefined, () => assertImportAdmission(resourceHome, importPermitId));
|
|
669
|
+
try {
|
|
670
|
+
const identity = fs.lstatSync(dbPath);
|
|
671
|
+
if (!identity.isFile())
|
|
672
|
+
throw new AppError("resource_storage_changed", "Resource registry target is not a regular file", 1);
|
|
673
|
+
resourceDatabaseIdentities.set(database, { dev: identity.dev, ino: identity.ino, ...(importPermitId ? { importPermitId } : {}) });
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
return closeAfterOpenFailure(db, error);
|
|
677
|
+
}
|
|
176
678
|
resourceDatabases.set(dbPath, database);
|
|
177
|
-
return database;
|
|
679
|
+
return deadlineAt === undefined ? database : database.withDeadline(deadlineAt);
|
|
680
|
+
}
|
|
681
|
+
function requestDeadline(at) {
|
|
682
|
+
if (at === undefined)
|
|
683
|
+
return undefined;
|
|
684
|
+
if (!Number.isSafeInteger(at))
|
|
685
|
+
throw new AppError("maintenance_deadline_invalid", "Maintenance deadline must be an absolute millisecond timestamp", 1);
|
|
686
|
+
return phase => { const remaining = at - Date.now(); if (remaining <= 0)
|
|
687
|
+
throw new AppError("process_heartbeat_timeout", "Maintenance storage deadline expired", 1, { phase, deadline_at: at, effect: "unknown" }); return Math.max(1, Math.min(4000, remaining)); };
|
|
688
|
+
}
|
|
689
|
+
function assertResourceStoreReady(db, retirement = false) {
|
|
690
|
+
const version = readStoreWriterContract(db);
|
|
691
|
+
if (version === null && !retirement)
|
|
692
|
+
throw new AppError("resource_storage_unprepared", "Prepare the resource registry writer contract before maintenance", 1);
|
|
693
|
+
if (version !== null && version !== STORE_WRITER_VERSION && !(retirement && version === 1))
|
|
694
|
+
throw new AppError("store_writer_incompatible", "This CLI cannot maintain the resource registry's required contract", 1, { required: version, supported: STORE_WRITER_VERSION });
|
|
695
|
+
for (const [table, columns] of Object.entries({ managed_processes: "id,kind,pid,pid_started_at,owner_id,lease_token,lease_expires_at,project_id,run_id,work_id,check_id,operation_id,stdout_path,stderr_path,state,started_at,updated_at,finished_at,termination_reason,metadata_json", managed_resources: "resource_kind,resource_key,owner_id,lease_token,lease_expires_at,process_id,metadata_json,created_at,updated_at" })) {
|
|
696
|
+
try {
|
|
697
|
+
db.prepare(`SELECT ${columns} FROM ${table} LIMIT 0`);
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
throw new AppError("resource_storage_unprepared", "Prepare the resource registry schema before maintenance", 1, { table });
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
for (const name of ["idx_managed_processes_lease", "idx_managed_resources_lease"])
|
|
704
|
+
if (!db.prepare("SELECT 1 FROM sqlite_schema WHERE type='index' AND name=?").get(name))
|
|
705
|
+
throw new AppError("resource_storage_unprepared", "Prepare resource registry indexes before maintenance", 1, { index: name });
|
|
178
706
|
}
|
|
179
707
|
function emptyReadOnlyDatabase(dbPath) {
|
|
180
708
|
return {
|
|
181
709
|
path: dbPath,
|
|
182
710
|
writable: false,
|
|
183
711
|
exec: () => { throw new AppError("read_only_storage", "Read-only access cannot write absent dd-flow storage", 1, { path: dbPath }); },
|
|
712
|
+
execSchema: () => { throw new AppError("read_only_storage", "Read-only access cannot write absent dd-flow storage", 1, { path: dbPath }); },
|
|
713
|
+
beginWriteTransaction: () => { throw new AppError("read_only_storage", "Read-only access cannot start a write transaction", 1, { path: dbPath }); },
|
|
714
|
+
writeTransaction: () => { throw new AppError("read_only_storage", "Read-only access cannot start a write transaction", 1, { path: dbPath }); },
|
|
715
|
+
readSnapshot: (operation) => operation(),
|
|
716
|
+
afterCommit: () => { throw new AppError("read_only_storage", "Read-only access cannot publish absent dd-flow storage", 1, { path: dbPath }); },
|
|
184
717
|
run: () => { throw new AppError("read_only_storage", "Read-only access cannot write absent dd-flow storage", 1, { path: dbPath }); },
|
|
185
718
|
get: () => undefined,
|
|
186
719
|
all: () => []
|
|
187
720
|
};
|
|
188
721
|
}
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
722
|
+
function installWriterAdmission(db, assertAdmission, reserved, initializing = false) {
|
|
723
|
+
const writes = new Set([constants.SQLITE_INSERT, constants.SQLITE_UPDATE, constants.SQLITE_DELETE,
|
|
724
|
+
constants.SQLITE_CREATE_INDEX, constants.SQLITE_CREATE_TABLE, constants.SQLITE_CREATE_TEMP_INDEX, constants.SQLITE_CREATE_TEMP_TABLE,
|
|
725
|
+
constants.SQLITE_CREATE_TEMP_TRIGGER, constants.SQLITE_CREATE_TEMP_VIEW, constants.SQLITE_CREATE_TRIGGER, constants.SQLITE_CREATE_VIEW,
|
|
726
|
+
constants.SQLITE_DROP_INDEX, constants.SQLITE_DROP_TABLE, constants.SQLITE_DROP_TEMP_INDEX, constants.SQLITE_DROP_TEMP_TABLE,
|
|
727
|
+
constants.SQLITE_DROP_TEMP_TRIGGER, constants.SQLITE_DROP_TEMP_VIEW, constants.SQLITE_DROP_TRIGGER, constants.SQLITE_DROP_VIEW,
|
|
728
|
+
constants.SQLITE_ATTACH, constants.SQLITE_DETACH, constants.SQLITE_ALTER_TABLE, constants.SQLITE_REINDEX, constants.SQLITE_ANALYZE,
|
|
729
|
+
constants.SQLITE_CREATE_VTABLE, constants.SQLITE_DROP_VTABLE]);
|
|
730
|
+
const filePragmas = new Set(["journal_mode", "wal_checkpoint", "optimize", "incremental_vacuum", "user_version", "application_id", "writable_schema", "auto_vacuum", "page_size", "max_page_count", "encoding", "locking_mode"]);
|
|
731
|
+
db.setAuthorizer((action, first, second) => {
|
|
732
|
+
if (initializing && action === constants.SQLITE_TRANSACTION && first === "BEGIN")
|
|
733
|
+
assertAdmission();
|
|
734
|
+
if (!reserved() && (writes.has(action) || action === constants.SQLITE_PRAGMA
|
|
735
|
+
&& filePragmas.has(first?.toLowerCase() ?? "") && (second !== null || ["wal_checkpoint", "optimize", "incremental_vacuum"].includes(first?.toLowerCase() ?? ""))))
|
|
736
|
+
assertAdmission();
|
|
737
|
+
return constants.SQLITE_OK;
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
function databaseFacade(dbPath, writable, db, close = () => db.close?.(), deadline, assertAdmission) {
|
|
741
|
+
let scopedDeadline = deadline;
|
|
742
|
+
let reserved = false;
|
|
743
|
+
let poisoned = false;
|
|
744
|
+
let closed = false;
|
|
745
|
+
const closeOnce = () => { if (closed || !db.isOpen) {
|
|
746
|
+
closed = true;
|
|
747
|
+
return;
|
|
748
|
+
} closed = true; close(); };
|
|
749
|
+
let savepoint = 0;
|
|
750
|
+
let afterCommit = [];
|
|
751
|
+
let manualFrames = [];
|
|
752
|
+
let readDepth = 0;
|
|
753
|
+
let ownedWriterToken = null;
|
|
754
|
+
if (writable && assertAdmission) {
|
|
755
|
+
// SQLite identifies DML/DDL even behind comments, CTEs or RETURNING.
|
|
756
|
+
// An already reserved writer may finish; maintenance must acquire its
|
|
757
|
+
// own barriers and recheck drain before mutating either store.
|
|
758
|
+
installWriterAdmission(db, assertAdmission, () => reserved && db.isTransaction);
|
|
759
|
+
// Existing writer triggers also check at execution time. This closes the
|
|
760
|
+
// prepare-before-fence / step-after-fence window for protected DML.
|
|
761
|
+
bindStoreWriterAdmission(db, () => { if (!(reserved && db.isTransaction))
|
|
762
|
+
assertAdmission(); });
|
|
763
|
+
}
|
|
764
|
+
const writerTiming = channel("dd-flow.sqlite.writer");
|
|
765
|
+
const ingressPhase = channel("dd-flow.sqlite.ingress");
|
|
766
|
+
const storeKind = path.basename(dbPath) === "runtime.sqlite" ? "resource" : "run";
|
|
767
|
+
const bound = (phase) => {
|
|
768
|
+
const remaining = scopedDeadline?.(phase);
|
|
769
|
+
if (remaining !== undefined)
|
|
770
|
+
db.exec(`PRAGMA busy_timeout = ${remaining}`);
|
|
771
|
+
if (ingressPhase.hasSubscribers)
|
|
772
|
+
ingressPhase.publish({ phase, store_kind: storeKind, ...(remaining !== undefined ? { remaining_ms: remaining } : {}) });
|
|
773
|
+
};
|
|
774
|
+
const publishCommitted = () => {
|
|
775
|
+
const pending = afterCommit;
|
|
776
|
+
afterCommit = [];
|
|
777
|
+
const scope = publicationScopes.at(-1);
|
|
778
|
+
if (scope)
|
|
779
|
+
scope.push(...pending);
|
|
780
|
+
else
|
|
781
|
+
executePublications(pending);
|
|
782
|
+
};
|
|
783
|
+
const assertUsable = () => { if (poisoned || closed || !db.isOpen)
|
|
784
|
+
throw new AppError("write_transaction_unresolved", "Transaction cleanup failed or the connection is closed; reopen the connection before continuing", 1); };
|
|
785
|
+
const beginWriteTransaction = () => {
|
|
786
|
+
assertUsable();
|
|
787
|
+
if (readDepth)
|
|
788
|
+
throw new AppError("read_snapshot_write", "Cannot reserve a writer inside a read snapshot", 1);
|
|
789
|
+
if (!writable)
|
|
790
|
+
throw new AppError("read_only_storage", "Read-only access cannot start a write transaction", 1, { path: dbPath });
|
|
791
|
+
assertAdmission?.();
|
|
792
|
+
bound("writer_reservation");
|
|
793
|
+
db.exec("BEGIN IMMEDIATE");
|
|
794
|
+
reserved = true;
|
|
795
|
+
ownedWriterToken = Object.freeze({});
|
|
796
|
+
if (ingressPhase.hasSubscribers)
|
|
797
|
+
ingressPhase.publish({ phase: "writer_reserved", store_kind: storeKind });
|
|
798
|
+
};
|
|
799
|
+
const rollback = (sql, original) => {
|
|
800
|
+
try {
|
|
801
|
+
db.exec(sql);
|
|
802
|
+
}
|
|
803
|
+
catch (cleanup) {
|
|
804
|
+
poisoned = true;
|
|
805
|
+
let closeError;
|
|
806
|
+
try {
|
|
807
|
+
closeOnce();
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
closeError = errorDetails(error);
|
|
811
|
+
}
|
|
812
|
+
const secondary = { transaction_cleanup_error: errorDetails(cleanup), ...(closeError ? { transaction_close_error: closeError } : {}) };
|
|
813
|
+
if (original instanceof AppError)
|
|
814
|
+
throw new AppError(original.code, original.message, original.exitCode, { ...original.details, ...secondary });
|
|
815
|
+
throw new AppError("write_transaction_failed", "Write transaction failed and SQLite cleanup also failed", 1, { cause: errorDetails(original), ...secondary });
|
|
816
|
+
}
|
|
817
|
+
throw original;
|
|
818
|
+
};
|
|
819
|
+
const writeTransaction = (operation, operationName = "writeTransaction") => {
|
|
820
|
+
assertUsable();
|
|
821
|
+
if (readDepth)
|
|
822
|
+
throw new AppError("read_snapshot_write", "Cannot reserve a writer inside a read snapshot", 1);
|
|
823
|
+
if (!writable)
|
|
824
|
+
throw new AppError("read_only_storage", "Read-only access cannot start a write transaction", 1, { path: dbPath });
|
|
825
|
+
if (db.isTransaction && !reserved)
|
|
826
|
+
throw new AppError("write_transaction_unowned", "Cannot write inside an unowned transaction or read snapshot", 1);
|
|
827
|
+
const nested = db.isTransaction;
|
|
828
|
+
const callbackLength = afterCommit.length;
|
|
829
|
+
const marker = `dd_flow_write_${++savepoint}`;
|
|
830
|
+
const startedAt = performance.now();
|
|
831
|
+
try {
|
|
832
|
+
if (nested)
|
|
833
|
+
db.exec(`SAVEPOINT ${marker}`);
|
|
834
|
+
else
|
|
835
|
+
beginWriteTransaction();
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
if (error instanceof AppError)
|
|
839
|
+
throw error;
|
|
840
|
+
throw new AppError("storage_write_failed", "Cannot reserve SQLite writer", 1, { cause: errorDetails(error), transaction_phase: "begin", transaction_rolled_back: true });
|
|
841
|
+
}
|
|
842
|
+
let phase = "body";
|
|
843
|
+
try {
|
|
844
|
+
const value = operation();
|
|
845
|
+
if (value && typeof value.then === "function") {
|
|
846
|
+
void Promise.resolve(value).catch(() => { });
|
|
847
|
+
throw new AppError("write_transaction_async", "Write transaction callback must be synchronous", 1);
|
|
848
|
+
}
|
|
849
|
+
phase = "commit";
|
|
850
|
+
bound("commit");
|
|
851
|
+
if (nested)
|
|
852
|
+
db.exec(`RELEASE ${marker}`);
|
|
853
|
+
else
|
|
854
|
+
db.exec("COMMIT");
|
|
855
|
+
phase = "committed";
|
|
856
|
+
if (!nested && ingressPhase.hasSubscribers)
|
|
857
|
+
ingressPhase.publish({ phase: "committed", store_kind: storeKind });
|
|
858
|
+
if (!nested && writerTiming.hasSubscribers)
|
|
859
|
+
writerTiming.publish({ operation: operationName, wall_time_ms: performance.now() - startedAt, path: dbPath });
|
|
860
|
+
if (!nested)
|
|
861
|
+
publishCommitted();
|
|
862
|
+
return value;
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
if (phase === "committed")
|
|
866
|
+
throw error;
|
|
867
|
+
afterCommit.length = callbackLength;
|
|
868
|
+
try {
|
|
869
|
+
return rollback(nested ? `ROLLBACK TO ${marker}; RELEASE ${marker}` : "ROLLBACK", error);
|
|
870
|
+
}
|
|
871
|
+
catch (failure) {
|
|
872
|
+
if (failure === error && !(error instanceof AppError) && typeof error === "object" && error !== null && "code" in error && String(error.code).startsWith("ERR_SQLITE"))
|
|
873
|
+
throw new AppError("storage_write_failed", "SQLite write failed", 1, { cause: errorDetails(error), transaction_phase: phase, transaction_rolled_back: true });
|
|
874
|
+
throw failure;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
finally {
|
|
878
|
+
if (closed || !db.isOpen || !db.isTransaction) {
|
|
879
|
+
reserved = false;
|
|
880
|
+
if (phase !== "committed") {
|
|
881
|
+
afterCommit = [];
|
|
882
|
+
manualFrames = [];
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
const readSnapshot = (operation) => {
|
|
888
|
+
assertUsable();
|
|
889
|
+
const nested = db.isTransaction;
|
|
890
|
+
const marker = `dd_flow_read_${++savepoint}`;
|
|
891
|
+
const callbackLength = afterCommit.length;
|
|
892
|
+
try {
|
|
893
|
+
db.exec(nested ? `SAVEPOINT ${marker}` : "BEGIN");
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
throw new AppError("read_snapshot_failed", "Cannot start SQLite read snapshot", 1, { cause: errorDetails(error) });
|
|
897
|
+
}
|
|
898
|
+
readDepth += 1;
|
|
899
|
+
let phase = "body";
|
|
900
|
+
try {
|
|
901
|
+
const value = operation();
|
|
902
|
+
if (value && typeof value.then === "function") {
|
|
903
|
+
void Promise.resolve(value).catch(() => { });
|
|
904
|
+
throw new AppError("read_snapshot_async", "Read snapshot callback must be synchronous", 1);
|
|
905
|
+
}
|
|
906
|
+
phase = "commit";
|
|
907
|
+
db.exec(nested ? `RELEASE ${marker}` : "COMMIT");
|
|
908
|
+
phase = "committed";
|
|
909
|
+
return value;
|
|
910
|
+
}
|
|
911
|
+
catch (error) {
|
|
912
|
+
if (phase === "committed")
|
|
913
|
+
throw error;
|
|
914
|
+
afterCommit.length = callbackLength;
|
|
915
|
+
return rollback(nested ? `ROLLBACK TO ${marker}; RELEASE ${marker}` : "ROLLBACK", error);
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
readDepth -= 1;
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
const applyManualBoundary = (sql) => {
|
|
922
|
+
for (const statement of sql.split(";")) {
|
|
923
|
+
const save = /^\s*SAVEPOINT\s+([\w-]+)/i.exec(statement);
|
|
924
|
+
if (save) {
|
|
925
|
+
manualFrames.push({ name: save[1], length: afterCommit.length });
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
const rollbackTo = /^\s*ROLLBACK\s+TO(?:\s+SAVEPOINT)?\s+([\w-]+)/i.exec(statement);
|
|
929
|
+
if (rollbackTo) {
|
|
930
|
+
const frame = manualFrames.findLastIndex((item) => item.name === rollbackTo[1]);
|
|
931
|
+
if (frame >= 0) {
|
|
932
|
+
afterCommit.length = manualFrames[frame].length;
|
|
933
|
+
manualFrames.length = frame + 1;
|
|
934
|
+
}
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
const release = /^\s*RELEASE(?:\s+SAVEPOINT)?\s+([\w-]+)/i.exec(statement);
|
|
938
|
+
if (release) {
|
|
939
|
+
const frame = manualFrames.findLastIndex((item) => item.name === release[1]);
|
|
940
|
+
if (frame >= 0)
|
|
941
|
+
manualFrames.length = frame;
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
if (/^\s*ROLLBACK\b/i.test(statement)) {
|
|
945
|
+
afterCommit = [];
|
|
946
|
+
manualFrames = [];
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
if (/^\s*COMMIT\b/i.test(statement)) {
|
|
950
|
+
manualFrames = [];
|
|
951
|
+
publishCommitted();
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
const facade = {
|
|
956
|
+
path: dbPath,
|
|
957
|
+
writable,
|
|
958
|
+
close: closeOnce,
|
|
959
|
+
exec: (sql) => {
|
|
960
|
+
assertUsable();
|
|
961
|
+
if (readDepth)
|
|
962
|
+
throw new AppError("read_snapshot_write", "Cannot execute SQL inside a read snapshot", 1);
|
|
963
|
+
// Raw SQL may replace the transaction (including behind comments).
|
|
964
|
+
// Do not infer continuity by parsing SQL or its final isTransaction.
|
|
965
|
+
ownedWriterToken = null;
|
|
966
|
+
try {
|
|
967
|
+
db.exec(sql);
|
|
968
|
+
applyManualBoundary(sql);
|
|
969
|
+
}
|
|
970
|
+
catch (error) {
|
|
971
|
+
if (/^\s*ROLLBACK\b/i.test(sql)) {
|
|
972
|
+
poisoned = true;
|
|
973
|
+
try {
|
|
974
|
+
closeOnce();
|
|
975
|
+
}
|
|
976
|
+
catch { /* Keep the primary SQLite failure. */ }
|
|
977
|
+
}
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
finally {
|
|
981
|
+
if (closed || !db.isOpen || !db.isTransaction) {
|
|
982
|
+
reserved = false;
|
|
983
|
+
if (!/^\s*COMMIT\b/i.test(sql)) {
|
|
984
|
+
afterCommit = [];
|
|
985
|
+
manualFrames = [];
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
execSchema: (sql) => { assertUsable(); if (db.isTransaction && !reserved)
|
|
991
|
+
throw new AppError("write_transaction_unowned", "Cannot change schema inside an unowned transaction", 1); ownedWriterToken = null; executeStoreSchema(db, sql); },
|
|
992
|
+
beginWriteTransaction,
|
|
993
|
+
writeTransaction,
|
|
994
|
+
isOwnedWriteTransaction: () => { assertUsable(); return writable && reserved && ownedWriterToken !== null && db.isTransaction && readDepth === 0; },
|
|
995
|
+
ownedWriteTransactionToken: () => { assertUsable(); return writable && reserved && db.isTransaction && readDepth === 0 ? ownedWriterToken : null; },
|
|
996
|
+
readSnapshot,
|
|
997
|
+
afterCommit: (operation) => { assertUsable(); if (readDepth)
|
|
998
|
+
throw new AppError("read_snapshot_write", "Cannot queue publication inside a read snapshot", 1); if (db.isTransaction)
|
|
999
|
+
afterCommit.push(operation);
|
|
1000
|
+
else {
|
|
1001
|
+
const scope = publicationScopes.at(-1);
|
|
1002
|
+
if (scope)
|
|
1003
|
+
scope.push(operation);
|
|
1004
|
+
else
|
|
1005
|
+
executePublications([operation]);
|
|
1006
|
+
} },
|
|
1007
|
+
run: (sql, params = []) => { assertUsable(); if (readDepth)
|
|
1008
|
+
throw new AppError("read_snapshot_write", "Cannot write inside a read snapshot", 1); bound("write"); return db.prepare(sql).run(...params); },
|
|
1009
|
+
get: (sql, params = []) => { assertUsable(); bound("lookup"); return db.prepare(sql).get(...params); },
|
|
1010
|
+
all: (sql, params = []) => { assertUsable(); bound("lookup"); return db.prepare(sql).all(...params); }
|
|
1011
|
+
};
|
|
1012
|
+
facade.withDeadline = (at) => {
|
|
1013
|
+
const bounded = requestDeadline(at);
|
|
1014
|
+
const scoped = (operation) => { const previous = scopedDeadline; scopedDeadline = phase => Math.min(bounded(phase), previous?.(phase) ?? 4000); try {
|
|
1015
|
+
return operation();
|
|
1016
|
+
}
|
|
1017
|
+
finally {
|
|
1018
|
+
scopedDeadline = previous;
|
|
1019
|
+
if (db.isOpen)
|
|
1020
|
+
db.exec("PRAGMA busy_timeout = 4000");
|
|
1021
|
+
} };
|
|
1022
|
+
return { ...facade, exec: sql => scoped(() => facade.exec(sql)), execSchema: sql => scoped(() => facade.execSchema(sql)), beginWriteTransaction: () => scoped(() => facade.beginWriteTransaction()), writeTransaction: (operation, name) => scoped(() => facade.writeTransaction(operation, name)), readSnapshot: operation => scoped(() => facade.readSnapshot(operation)), get: (sql, params) => scoped(() => facade.get(sql, params)), all: (sql, params) => scoped(() => facade.all(sql, params)), run: (sql, params) => scoped(() => facade.run(sql, params)) };
|
|
1023
|
+
};
|
|
1024
|
+
return facade;
|
|
1025
|
+
}
|
|
1026
|
+
function errorDetails(error) {
|
|
1027
|
+
const value = error;
|
|
1028
|
+
return { message: String(value?.message ?? error), ...(typeof value?.code === "string" ? { code: value.code } : {}), ...(typeof value?.errcode === "number" ? { sqlite_extended_code: value.errcode } : {}) };
|
|
1029
|
+
}
|
|
1030
|
+
function configureDatabase(db, mode, ingressDeadline) {
|
|
1031
|
+
if (mode !== "read_existing" && mode !== "prepared_existing") {
|
|
1032
|
+
// Concurrent first openers can collide while changing journal mode, even
|
|
1033
|
+
// with busy_timeout set. Retrying this configuration does not replay writes.
|
|
1034
|
+
const deadline = performance.now() + 4000, pause = new Int32Array(new SharedArrayBuffer(4));
|
|
1035
|
+
for (;;) {
|
|
1036
|
+
try {
|
|
1037
|
+
const remaining = ingressDeadline?.("store_configuration");
|
|
1038
|
+
if (remaining !== undefined)
|
|
1039
|
+
db.exec(`PRAGMA busy_timeout = ${remaining}`);
|
|
1040
|
+
const current = db.prepare("PRAGMA journal_mode").get();
|
|
1041
|
+
if (current.journal_mode !== "wal")
|
|
1042
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
if ((error.errcode ?? 0) % 256 !== 5 || performance.now() >= deadline)
|
|
1047
|
+
throw error;
|
|
1048
|
+
Atomics.wait(pause, 0, 0, Math.min(25, Math.max(1, deadline - performance.now())));
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
193
1051
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
194
1052
|
}
|
|
195
1053
|
}
|
|
196
|
-
function migrate(db
|
|
1054
|
+
function migrate(db) {
|
|
197
1055
|
db.exec(`
|
|
198
1056
|
CREATE TABLE IF NOT EXISTS projects (
|
|
199
1057
|
id TEXT PRIMARY KEY,
|
|
@@ -229,6 +1087,7 @@ function migrate(db, dbPath) {
|
|
|
229
1087
|
CREATE TABLE IF NOT EXISTS plan_bindings (
|
|
230
1088
|
project_id TEXT NOT NULL,
|
|
231
1089
|
protocol_id TEXT NOT NULL,
|
|
1090
|
+
plan_id TEXT,
|
|
232
1091
|
plan_revision INTEGER NOT NULL,
|
|
233
1092
|
plan_sha256 TEXT NOT NULL,
|
|
234
1093
|
plan_path TEXT NOT NULL,
|
|
@@ -551,6 +1410,8 @@ function migrate(db, dbPath) {
|
|
|
551
1410
|
CREATE INDEX IF NOT EXISTS idx_check_receipts_run
|
|
552
1411
|
ON check_receipts(project_id, run_id, started_at);
|
|
553
1412
|
|
|
1413
|
+
${workContinuationStorageSql}
|
|
1414
|
+
|
|
554
1415
|
CREATE TABLE IF NOT EXISTS merge_requests (
|
|
555
1416
|
merge_request_id TEXT PRIMARY KEY,
|
|
556
1417
|
project_id TEXT NOT NULL,
|
|
@@ -799,7 +1660,6 @@ function migrate(db, dbPath) {
|
|
|
799
1660
|
ensureColumn(db, "sessions", "plan_item_id", "ALTER TABLE sessions ADD COLUMN plan_item_id TEXT");
|
|
800
1661
|
ensureColumn(db, "sessions", "session_kind", "ALTER TABLE sessions ADD COLUMN session_kind TEXT");
|
|
801
1662
|
ensureColumn(db, "sessions", "coverage_units_json", "ALTER TABLE sessions ADD COLUMN coverage_units_json TEXT NOT NULL DEFAULT '[]'");
|
|
802
|
-
migrateSessionStorageKeys(db, dbPath);
|
|
803
1663
|
ensureColumn(db, "usage", "cache_read_input_tokens", "ALTER TABLE usage ADD COLUMN cache_read_input_tokens INTEGER");
|
|
804
1664
|
ensureColumn(db, "usage", "cache_write_input_tokens", "ALTER TABLE usage ADD COLUMN cache_write_input_tokens INTEGER");
|
|
805
1665
|
ensureColumn(db, "usage", "uncached_input_tokens", "ALTER TABLE usage ADD COLUMN uncached_input_tokens INTEGER");
|
|
@@ -809,17 +1669,41 @@ function migrate(db, dbPath) {
|
|
|
809
1669
|
ensureColumn(db, "usage", "source_mtime_ms", "ALTER TABLE usage ADD COLUMN source_mtime_ms INTEGER");
|
|
810
1670
|
db.prepare("UPDATE usage SET cache_read_input_tokens = cached_input_tokens WHERE cache_read_input_tokens IS NULL AND cached_input_tokens IS NOT NULL").run();
|
|
811
1671
|
ensureColumn(db, "runs", "run_home_path", "ALTER TABLE runs ADD COLUMN run_home_path TEXT");
|
|
1672
|
+
ensureColumn(db, "plan_bindings", "plan_id", "ALTER TABLE plan_bindings ADD COLUMN plan_id TEXT");
|
|
1673
|
+
// Historical RUN projections already retain the canonical identifier. Use
|
|
1674
|
+
// those SQL bytes to backfill without opening plan files under the writer.
|
|
1675
|
+
for (const row of db.prepare("SELECT project_id, protocol_id FROM plan_bindings WHERE plan_id IS NULL").all()) {
|
|
1676
|
+
const runs = db.prepare("SELECT index_json FROM runs WHERE project_id = ? AND subject_type = 'protocol' AND subject_id = ? ORDER BY updated_at DESC").all(row.project_id, row.protocol_id);
|
|
1677
|
+
for (const run of runs) {
|
|
1678
|
+
try {
|
|
1679
|
+
const planId = JSON.parse(run.index_json).plan_ref?.plan_id;
|
|
1680
|
+
if (typeof planId !== "string" || !planId || planId === "unknown-plan")
|
|
1681
|
+
continue;
|
|
1682
|
+
db.prepare("UPDATE plan_bindings SET plan_id = ? WHERE project_id = ? AND protocol_id = ? AND plan_id IS NULL").run(planId, row.project_id, row.protocol_id);
|
|
1683
|
+
break;
|
|
1684
|
+
}
|
|
1685
|
+
catch { /* A malformed historical RUN cannot establish plan identity. */ }
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
812
1688
|
ensureColumn(db, "runs", "run_root", "ALTER TABLE runs ADD COLUMN run_root TEXT");
|
|
813
1689
|
db.prepare("UPDATE runs SET run_root = COALESCE(run_root, run_home_path, run_dir) WHERE run_root IS NULL OR run_root = ''").run();
|
|
814
1690
|
ensureColumn(db, "merge_requests", "accepted_tree", "ALTER TABLE merge_requests ADD COLUMN accepted_tree TEXT");
|
|
815
1691
|
ensureColumn(db, "merge_requests", "replacement_of_merge_request_id", "ALTER TABLE merge_requests ADD COLUMN replacement_of_merge_request_id TEXT");
|
|
816
1692
|
// A failed integration gate may create a new immutable source candidate in
|
|
817
1693
|
// the same RUN. The historical unique index made that repair path impossible.
|
|
818
|
-
|
|
1694
|
+
// Rebuilding an already-current index changes schema/data_version on every
|
|
1695
|
+
// CLI observation and prevents a controller's writer barrier from settling.
|
|
1696
|
+
const mergeRunIndex = db.prepare("PRAGMA index_list(merge_requests)").all().find(index => index.name === "idx_merge_requests_run");
|
|
1697
|
+
if (mergeRunIndex?.unique)
|
|
1698
|
+
db.exec("DROP INDEX idx_merge_requests_run");
|
|
819
1699
|
db.exec("CREATE INDEX IF NOT EXISTS idx_merge_requests_run ON merge_requests(project_id, run_id, created_at)");
|
|
820
1700
|
ensureColumn(db, "runs", "layout_version", "ALTER TABLE runs ADD COLUMN layout_version TEXT");
|
|
821
1701
|
ensureColumn(db, "runs", "artifact_root_kind", "ALTER TABLE runs ADD COLUMN artifact_root_kind TEXT");
|
|
822
1702
|
ensureColumn(db, "works", "payload_json", "ALTER TABLE works ADD COLUMN payload_json TEXT");
|
|
1703
|
+
ensureColumn(db, "works", "current_start_invocation_id", "ALTER TABLE works ADD COLUMN current_start_invocation_id TEXT");
|
|
1704
|
+
ensureColumn(db, "work_sessions", "start_receipt_json", "ALTER TABLE work_sessions ADD COLUMN start_receipt_json TEXT");
|
|
1705
|
+
ensureColumn(db, "work_sessions", "start_publication_status", "ALTER TABLE work_sessions ADD COLUMN start_publication_status TEXT");
|
|
1706
|
+
ensureColumn(db, "work_sessions", "result_receipt_json", "ALTER TABLE work_sessions ADD COLUMN result_receipt_json TEXT");
|
|
823
1707
|
ensureColumn(db, "check_receipts", "declaration_id", "ALTER TABLE check_receipts ADD COLUMN declaration_id TEXT NOT NULL DEFAULT 'CHK-LEGACY'");
|
|
824
1708
|
ensureColumn(db, "check_receipts", "workspace_fingerprint", "ALTER TABLE check_receipts ADD COLUMN workspace_fingerprint TEXT NOT NULL DEFAULT ''");
|
|
825
1709
|
ensureColumn(db, "check_receipts", "artifacts_json", "ALTER TABLE check_receipts ADD COLUMN artifacts_json TEXT NOT NULL DEFAULT '[]'");
|
|
@@ -827,10 +1711,17 @@ function migrate(db, dbPath) {
|
|
|
827
1711
|
ensureColumn(db, "check_receipts", "gate", "ALTER TABLE check_receipts ADD COLUMN gate TEXT NOT NULL DEFAULT 'work'");
|
|
828
1712
|
ensureColumn(db, "check_receipts", "input_hash", "ALTER TABLE check_receipts ADD COLUMN input_hash TEXT NOT NULL DEFAULT ''");
|
|
829
1713
|
ensureColumn(db, "check_receipts", "verification_epoch", "ALTER TABLE check_receipts ADD COLUMN verification_epoch TEXT NOT NULL DEFAULT ''");
|
|
1714
|
+
ensureColumn(db, "check_receipts", "reservation_sequence", "ALTER TABLE check_receipts ADD COLUMN reservation_sequence INTEGER NOT NULL DEFAULT 0");
|
|
1715
|
+
ensureColumn(db, "check_receipts", "retry_of_receipt_id", "ALTER TABLE check_receipts ADD COLUMN retry_of_receipt_id TEXT");
|
|
1716
|
+
ensureColumn(db, "check_receipts", "retry_reason", "ALTER TABLE check_receipts ADD COLUMN retry_reason TEXT");
|
|
830
1717
|
ensureColumn(db, "check_receipts", "before_fingerprint", "ALTER TABLE check_receipts ADD COLUMN before_fingerprint TEXT NOT NULL DEFAULT ''");
|
|
831
1718
|
ensureColumn(db, "check_receipts", "after_fingerprint", "ALTER TABLE check_receipts ADD COLUMN after_fingerprint TEXT NOT NULL DEFAULT ''");
|
|
832
1719
|
ensureColumn(db, "check_receipts", "profile_hash", "ALTER TABLE check_receipts ADD COLUMN profile_hash TEXT");
|
|
833
1720
|
ensureColumn(db, "check_receipts", "mutation_paths_json", "ALTER TABLE check_receipts ADD COLUMN mutation_paths_json TEXT NOT NULL DEFAULT '[]'");
|
|
1721
|
+
ensureColumn(db, "check_receipts", "fingerprint_scope", "ALTER TABLE check_receipts ADD COLUMN fingerprint_scope TEXT");
|
|
1722
|
+
ensureColumn(db, "check_receipts", "input_manifest_json", "ALTER TABLE check_receipts ADD COLUMN input_manifest_json TEXT");
|
|
1723
|
+
ensureColumn(db, "check_receipts", "execution_failures_json", "ALTER TABLE check_receipts ADD COLUMN execution_failures_json TEXT");
|
|
1724
|
+
ensureColumn(db, "check_receipts", "execution_contract_hash", "ALTER TABLE check_receipts ADD COLUMN execution_contract_hash TEXT");
|
|
834
1725
|
ensureColumn(db, "hook_events", "event_key", "ALTER TABLE hook_events ADD COLUMN event_key TEXT");
|
|
835
1726
|
ensureColumn(db, "hook_events", "match_key", "ALTER TABLE hook_events ADD COLUMN match_key TEXT");
|
|
836
1727
|
ensureColumn(db, "hook_events", "claimed_at", "ALTER TABLE hook_events ADD COLUMN claimed_at TEXT");
|
|
@@ -846,6 +1737,7 @@ function migrate(db, dbPath) {
|
|
|
846
1737
|
ensureColumn(db, "hook_events", "parent_session_id", "ALTER TABLE hook_events ADD COLUMN parent_session_id TEXT");
|
|
847
1738
|
ensureColumn(db, "hook_events", "daemon_id", "ALTER TABLE hook_events ADD COLUMN daemon_id TEXT");
|
|
848
1739
|
ensureColumn(db, "hook_events", "cwd", "ALTER TABLE hook_events ADD COLUMN cwd TEXT");
|
|
1740
|
+
ensureColumn(db, "hook_events", "outcome_json", "ALTER TABLE hook_events ADD COLUMN outcome_json TEXT");
|
|
849
1741
|
ensureColumn(db, "harness_usage_snapshots", "tool_calls", "ALTER TABLE harness_usage_snapshots ADD COLUMN tool_calls INTEGER");
|
|
850
1742
|
ensureColumn(db, "harness_usage_snapshots", "tool_failures", "ALTER TABLE harness_usage_snapshots ADD COLUMN tool_failures INTEGER");
|
|
851
1743
|
ensureColumn(db, "harness_usage_snapshots", "tool_by_name_json", "ALTER TABLE harness_usage_snapshots ADD COLUMN tool_by_name_json TEXT");
|
|
@@ -908,8 +1800,7 @@ function migrateSessionStorageKeys(db, dbPath) {
|
|
|
908
1800
|
db.exec("COMMIT");
|
|
909
1801
|
}
|
|
910
1802
|
catch (error) {
|
|
911
|
-
db
|
|
912
|
-
throw error;
|
|
1803
|
+
return rollbackStoreTransaction(db, "ROLLBACK", error);
|
|
913
1804
|
}
|
|
914
1805
|
}
|
|
915
1806
|
function storageSessionKey(harness, native) {
|