@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.140
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +733 -0
- package/README.md +143 -1
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +376 -0
- package/dist/cli/command-metadata.js +197 -0
- package/dist/cli/command-option-descriptions.js +191 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +328 -18
- package/dist/cli/hook-ingress.js +78 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +44 -0
- package/dist/cli/memory-init-invocation.js +78 -0
- package/dist/cli/memory-init.js +240 -0
- package/dist/cli/memory-storage-admission.js +53 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +80 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +71 -0
- package/dist/cli/run-cli.js +1689 -545
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +71 -2
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +724 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +129 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +507 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +354 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +750 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +13 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +654 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +101 -0
- package/dist/harness-runtime/lib/native-children.d.mts +16 -0
- package/dist/harness-runtime/lib/native-children.mjs +113 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +14 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +116 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +205 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +28 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +49 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +3 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +11 -8
- package/dist/services/check-runner-evidence.js +168 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +25 -62
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1156 -107
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +13 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +115 -0
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +202 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +105 -0
- package/dist/services/descriptor-run-start.js +449 -0
- package/dist/services/descriptor-stage-coordinator.js +197 -0
- package/dist/services/descriptor-stage-settlement.js +222 -0
- package/dist/services/descriptor-stage-start.js +139 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +294 -153
- package/dist/services/eval-snapshots.js +1215 -116
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +308 -0
- package/dist/services/flow-catalog.js +21 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +250 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hooks.js +771 -325
- package/dist/services/hybrid-contracts.js +483 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +183 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2007 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +475 -55
- package/dist/services/memory-accepted-composition.js +77 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +320 -0
- package/dist/services/memory-compose-handler.js +258 -0
- package/dist/services/memory-composition-acceptance.js +199 -0
- package/dist/services/memory-composition-graph.js +134 -0
- package/dist/services/memory-composition-registration.js +146 -0
- package/dist/services/memory-composition-work-custody.js +151 -0
- package/dist/services/memory-composition-work-results.js +96 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +30 -0
- package/dist/services/memory-corrected-review-findings.js +106 -0
- package/dist/services/memory-corrected-review-inputs.js +210 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +170 -0
- package/dist/services/memory-corrected-review-registration.js +191 -0
- package/dist/services/memory-correction-check-receipt.js +219 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +435 -0
- package/dist/services/memory-finish-handler.js +215 -0
- package/dist/services/memory-finish-integration.js +280 -0
- package/dist/services/memory-finish-readiness.js +122 -0
- package/dist/services/memory-git-integration.js +151 -0
- package/dist/services/memory-historical-corrected-review.js +206 -0
- package/dist/services/memory-historical-original-review.js +130 -0
- package/dist/services/memory-historical-repair-registration.js +99 -0
- package/dist/services/memory-historical-second-review.js +128 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +89 -0
- package/dist/services/memory-init-controller-start.js +84 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +136 -0
- package/dist/services/memory-init-lint-capability.js +185 -0
- package/dist/services/memory-init-native-capacity.js +296 -0
- package/dist/services/memory-init-preflight.js +330 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +157 -0
- package/dist/services/memory-init-request-input.js +66 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +98 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +81 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +178 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +156 -0
- package/dist/services/memory-lint-config.js +85 -0
- package/dist/services/memory-native-review-copy.js +165 -0
- package/dist/services/memory-normal-flow-entry.js +75 -0
- package/dist/services/memory-ordinary-defaults.js +112 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +259 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +451 -0
- package/dist/services/memory-repair-history.js +491 -0
- package/dist/services/memory-repair-physical-binding.js +312 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +160 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +794 -0
- package/dist/services/memory-repair-registration.js +239 -0
- package/dist/services/memory-repair-work-baseline.js +413 -0
- package/dist/services/memory-repair-work-completion.js +97 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +89 -0
- package/dist/services/memory-repair-workspace.js +252 -0
- package/dist/services/memory-repair-writer-copy.js +735 -0
- package/dist/services/memory-result-binding.js +173 -0
- package/dist/services/memory-retained-review-authority.js +129 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +156 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +171 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +200 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +108 -0
- package/dist/services/memory-review-work-custody.js +124 -0
- package/dist/services/memory-review-work-input.js +72 -0
- package/dist/services/memory-review-work-results.js +73 -0
- package/dist/services/memory-second-corrected-review-inputs.js +165 -0
- package/dist/services/memory-second-correction-plan.js +76 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +28 -0
- package/dist/services/memory-stage-results.js +199 -0
- package/dist/services/memory-verification-acceptance.js +135 -0
- package/dist/services/memory-verification-checks.js +252 -0
- package/dist/services/memory-verification-receipt.js +159 -0
- package/dist/services/memory-verify-handler.js +135 -0
- package/dist/services/memory-work-result-preparation.js +63 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +161 -35
- package/dist/services/migrations.js +37 -16
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +37 -17
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +385 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +560 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +142 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +42 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +411 -0
- package/dist/services/run-control.js +1056 -0
- package/dist/services/run-controller-adapter.js +399 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +208 -0
- package/dist/services/run-controller-recovery.js +289 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1205 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +77 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +489 -0
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +1019 -247
- package/dist/services/runtime-budget.js +430 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +599 -0
- package/dist/services/runtime-scope-stop.js +210 -0
- package/dist/services/runtime-scope-worker.js +287 -0
- package/dist/services/runtime-service.js +96 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +35 -69
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-lifecycle.js +100 -108
- package/dist/services/stage-pause.js +89 -75
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +32 -23
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +374 -0
- package/dist/services/vnext-code-review.js +223 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +311 -19
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +279 -127
- package/dist/services/vnext-plan.js +229 -104
- package/dist/services/vnext-protocolize.js +411 -86
- package/dist/services/vnext-specify.js +119 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1627 -199
- package/dist/services/workspace-bootstrap.js +52 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +98 -44
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +17 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/database.js +891 -39
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +89 -0
- package/dist/storage/writer-migration.js +85 -0
- package/package.json +43 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { quote } from "shell-quote";
|
|
5
|
+
import { lifecycleRetryCommands, managedLifecycleCommand } from "./lifecycle-invocations.js";
|
|
5
6
|
import { AppError } from "../shared/errors.js";
|
|
7
|
+
import { entityReferenceVariants, shortEntityReference } from "../shared/entity-references.js";
|
|
6
8
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
9
|
+
import { publishImmutableInput } from "../storage/immutable-input.js";
|
|
10
|
+
import { resolveWorkReference } from "../storage/work-references.js";
|
|
7
11
|
import { findRecentMatchingHookEvent, claimStageResumeHookEvent, stageResumeMatchKey } from "./hooks.js";
|
|
8
12
|
import { requireProjectByRoot } from "./projects.js";
|
|
9
13
|
import { appendFlowRunTimelineEvent, pauseFlowRunStage, resumeFlowRunStage } from "./runs.js";
|
|
10
14
|
import { bindSessionForResume, refreshRunWorkProjection } from "./work-registry.js";
|
|
11
|
-
|
|
15
|
+
import { assertRunMutationAllowed } from "./run-recovery.js";
|
|
16
|
+
import { runtimeCommand } from "./runtime-command.js";
|
|
17
|
+
import { prepareAgentOutputFile } from "../storage/agent-output.js";
|
|
18
|
+
export { flowCommand } from "./runtime-command.js";
|
|
19
|
+
export function prepareStagePause(context, input) {
|
|
20
|
+
if (typeof input.question !== "string" || !input.question.trim())
|
|
21
|
+
throw new AppError("validation", "stage pause requires a non-empty user question", 2);
|
|
22
|
+
safeStage(input.stage);
|
|
12
23
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
13
24
|
const project = requireProjectByRoot(context, projectRoot);
|
|
14
25
|
const run = requireRun(context, project.id, input.runId);
|
|
26
|
+
assertRunMutationAllowed(context, project.id, run.id);
|
|
15
27
|
const work = requireWork(context, project.id, run.id, input.workId);
|
|
16
28
|
if (work.status !== "running")
|
|
17
29
|
throw new AppError("invalid_work_state", "Only a running Work can pause", 1, { work_id: work.work_id, status: work.status });
|
|
18
30
|
const question = input.question;
|
|
19
|
-
if (!question.trim())
|
|
20
|
-
throw new AppError("validation", "stage pause requires a non-empty user question", 2);
|
|
21
31
|
const stage = requireStage(run, input.stage, "running");
|
|
32
|
+
requireRunHome(run);
|
|
33
|
+
return { projectRoot, project, run, work, question, stage };
|
|
34
|
+
}
|
|
35
|
+
export function pauseStageForUser(context, input) {
|
|
36
|
+
const { projectRoot, project, run, work, question, stage } = prepareStagePause(context, input);
|
|
22
37
|
const pauseId = nextPauseId(requireRunHome(run));
|
|
23
38
|
const pauseRoot = path.join(requireRunHome(run), "intake", "hitl", `${pauseId}-${safeStage(input.stage)}`);
|
|
24
39
|
const questionPath = path.join(pauseRoot, "question.md");
|
|
25
40
|
fs.mkdirSync(pauseRoot, { recursive: true });
|
|
26
|
-
|
|
41
|
+
publishImmutableInput(questionPath, Buffer.from(question), "hitl_question_conflict");
|
|
42
|
+
const answerInputPath = prepareAgentOutputFile(path.join(pauseRoot, "answer-input.md"));
|
|
27
43
|
const now = context.now();
|
|
28
|
-
context.db.
|
|
29
|
-
try {
|
|
44
|
+
context.db.writeTransaction(() => {
|
|
30
45
|
const paused = context.db.run("UPDATE works SET status = 'paused', updated_at = ? WHERE work_id = ? AND status = 'running'", [now, work.work_id]);
|
|
31
46
|
if (paused.changes !== 1)
|
|
32
47
|
throw new AppError("invalid_work_state", "Work changed before pause could be accepted", 1, { work_id: work.work_id });
|
|
33
48
|
pauseFlowRunStage(context, { projectRoot, runId: run.id, stage: stage.stage, workId: work.work_id, pauseId, questionPath });
|
|
34
|
-
context.db.
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
refreshRunWorkProjection(context, project.id, run.id);
|
|
41
|
-
const resume = stageResumeCommand(context, { runId: run.id, stage: stage.stage, workId: work.work_id, projectRoot });
|
|
42
|
-
const resumeTemplate = `${resume} <<'USER_ANSWER'\n<paste the complete user answer exactly>\nUSER_ANSWER`;
|
|
49
|
+
context.db.afterCommit(() => refreshRunWorkProjection(context, project.id, run.id));
|
|
50
|
+
});
|
|
51
|
+
const resume = stageResumeCommand(context, { runId: run.id, stage: stage.stage, workId: work.work_id, projectRoot, answerInputPath });
|
|
52
|
+
const resumeTemplate = `Write the complete user answer exactly to ${quote([answerInputPath])}, then run:\n${resume}`;
|
|
43
53
|
return {
|
|
44
54
|
ok: true,
|
|
45
55
|
outcome: "paused",
|
|
@@ -48,39 +58,56 @@ export function pauseStageForUser(context, input) {
|
|
|
48
58
|
stage: stage.stage,
|
|
49
59
|
pause: { id: pauseId, reason: "waiting_for_user", question_path: questionPath, user_message: question },
|
|
50
60
|
next_action: "ask_user_then_resume_same_stage",
|
|
51
|
-
agent_instruction:
|
|
61
|
+
agent_instruction: `Send user_message to the user and stop this Turn. When the user answers, write the complete raw answer without interpretation to ${answerInputPath}, then make resume_command the first flow command and follow the returned continuation prompt. Do not create a RUN, Work, attempt, or stage start.`,
|
|
52
62
|
resume_command: resume,
|
|
53
63
|
resume_command_template: resumeTemplate
|
|
54
64
|
};
|
|
55
65
|
}
|
|
56
|
-
export function
|
|
66
|
+
export function prepareStageResume(context, input) {
|
|
67
|
+
if (typeof input.answer !== "string" || !input.answer.trim())
|
|
68
|
+
throw new AppError("validation", "stage resume requires a non-empty user answer", 2);
|
|
69
|
+
safeStage(input.stage);
|
|
57
70
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
58
71
|
const project = requireProjectByRoot(context, projectRoot);
|
|
59
72
|
const run = requireRun(context, project.id, input.runId);
|
|
73
|
+
assertRunMutationAllowed(context, project.id, run.id);
|
|
60
74
|
const work = requireWork(context, project.id, run.id, input.workId);
|
|
61
75
|
if (work.status !== "paused")
|
|
62
76
|
throw new AppError("invalid_work_state", "stage resume requires its paused Work", 1, { work_id: work.work_id, status: work.status });
|
|
63
77
|
const stage = requireStage(run, input.stage, "paused");
|
|
64
78
|
if (!stage.pause || stage.pause.work_id !== work.work_id || stage.pause.answer_path)
|
|
65
79
|
throw new AppError("invalid_stage_state", "Stage has no unanswered pause for this Work", 1, { run_id: run.id, stage: stage.stage, work_id: work.work_id });
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
const workSession = context.db.get("SELECT id, session_id, prompt_path, result_path, status FROM work_sessions WHERE work_id = ? AND status = 'running' ORDER BY created_at DESC LIMIT 1", [work.work_id]);
|
|
81
|
+
if (!workSession)
|
|
82
|
+
throw new AppError("runtime_missing", "Paused Work has no open Session link", 1, { work_id: work.work_id });
|
|
83
|
+
let evidence = input.evidence;
|
|
84
|
+
if (evidence && (evidence.pauseId !== stage.pause.id || evidence.promptPath !== workSession.prompt_path || evidence.questionPath !== stage.pause.question_path))
|
|
85
|
+
throw new AppError("runtime_state_changed", "Paused Work changed after resume preparation", 1, { work_id: work.work_id });
|
|
86
|
+
if (!evidence) {
|
|
87
|
+
try {
|
|
88
|
+
evidence = { pauseId: stage.pause.id, promptPath: workSession.prompt_path, questionPath: stage.pause.question_path, prompt: fs.readFileSync(workSession.prompt_path, "utf8"), question: fs.readFileSync(stage.pause.question_path, "utf8") };
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
throw new AppError("runtime_missing", "Paused Work continuation artifacts cannot be read", 1, { work_id: work.work_id, cause: String(error) });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { projectRoot, project, run, work, stage, pause: stage.pause, workSession, evidence };
|
|
95
|
+
}
|
|
96
|
+
export function resumeStageAfterUser(context, input) {
|
|
97
|
+
const { projectRoot, project, run, work, stage, pause, workSession, evidence } = prepareStageResume(context, input);
|
|
68
98
|
const hookEventId = input.hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
69
99
|
projectId: project.id,
|
|
70
|
-
matchKey:
|
|
100
|
+
matchKey: entityReferenceVariants(run.id).flatMap((runReference) => entityReferenceVariants(work.work_id).map((workReference) => stageResumeMatchKey(runReference, stage.stage, workReference, projectRoot))),
|
|
71
101
|
errorCode: "trusted_stage_resume_required",
|
|
72
102
|
operation: "stage resume"
|
|
73
103
|
}).eventKey;
|
|
74
|
-
const answerPath = path.join(path.dirname(stage.pause.question_path), "answer.md");
|
|
75
|
-
fs.writeFileSync(answerPath, input.answer);
|
|
76
104
|
const now = context.now();
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
identity = claimStageResumeHookEvent(context, { projectId: project.id, runId: run.id, stage: stage.stage, workId: work.work_id, projectRoot, eventKey: hookEventId });
|
|
105
|
+
const answerPath = path.join(path.dirname(pause.question_path), `answer-${crypto.createHash("sha256").update(`${pause.id}\0${input.answer}`).digest("hex")}.md`);
|
|
106
|
+
// The answer is an immutable, content-addressed input. Prepare it before
|
|
107
|
+
// reserving SQLite's writer; an unreferenced candidate has no flow authority.
|
|
108
|
+
publishImmutableInput(answerPath, Buffer.from(input.answer), "hitl_answer_conflict");
|
|
109
|
+
context.db.writeTransaction(() => {
|
|
110
|
+
const identity = claimStageResumeHookEvent(context, { projectId: project.id, runId: run.id, stage: stage.stage, workId: work.work_id, projectRoot, eventKey: hookEventId });
|
|
84
111
|
if (workSession.session_id !== identity.sessionId) {
|
|
85
112
|
bindSessionForResume(context, work.work_id, identity, now);
|
|
86
113
|
context.db.run("UPDATE work_sessions SET status = 'completed', updated_at = ?, completed_at = ? WHERE id = ?", [now, now, workSession.id]);
|
|
@@ -90,26 +117,23 @@ export function resumeStageAfterUser(context, input) {
|
|
|
90
117
|
if (resumed.changes !== 1)
|
|
91
118
|
throw new AppError("invalid_work_state", "Work changed before resume could be accepted", 1, { work_id: work.work_id });
|
|
92
119
|
resumeFlowRunStage(context, { projectRoot, runId: run.id, stage: stage.stage, workId: work.work_id, answerPath });
|
|
93
|
-
context.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
refreshRunWorkProjection(context, project.id, run.id);
|
|
100
|
-
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "stage_resume_session_bound", stage: stage.stage, work_id: work.work_id, session_id: identity.sessionId });
|
|
101
|
-
const prompt = fs.readFileSync(workSession.prompt_path, "utf8");
|
|
120
|
+
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "stage_resume_session_bound", stage: stage.stage, work_id: work.work_id, session_id: identity.sessionId });
|
|
121
|
+
context.db.afterCommit(() => refreshRunWorkProjection(context, project.id, run.id));
|
|
122
|
+
});
|
|
123
|
+
const prompt = evidence.prompt;
|
|
124
|
+
const retryCommands = lifecycleRetryCommands(context, stage.stage);
|
|
102
125
|
const continuation = [
|
|
103
126
|
prompt.trimEnd(),
|
|
104
127
|
"",
|
|
105
128
|
"<hitl_resume>",
|
|
106
|
-
`- Pause: ${
|
|
107
|
-
`- Question source: ${
|
|
129
|
+
`- Pause: ${pause.id}`,
|
|
130
|
+
`- Question source: ${pause.question_path}`,
|
|
108
131
|
`- Answer source: ${answerPath}`,
|
|
109
132
|
"- Continue the same stage, Work and attempt. Do not call stage start or repeat completed preparation.",
|
|
133
|
+
...(retryCommands.length ? ["- A prior no-effect lifecycle rejection now has these authoritative replacement commands; correct the rejected input and use only these replacements:", ...retryCommands.map(command => ` ${command}`)] : []),
|
|
110
134
|
"",
|
|
111
135
|
"<user_question>",
|
|
112
|
-
|
|
136
|
+
evidence.question,
|
|
113
137
|
"</user_question>",
|
|
114
138
|
"",
|
|
115
139
|
"<user_answer>",
|
|
@@ -118,18 +142,18 @@ export function resumeStageAfterUser(context, input) {
|
|
|
118
142
|
"</hitl_resume>",
|
|
119
143
|
""
|
|
120
144
|
].join("\n");
|
|
121
|
-
return { ok: true, outcome: "resumed", run_id: run.id, work_id: work.work_id, stage: stage.stage, pause_id:
|
|
145
|
+
return { ok: true, outcome: "resumed", run_id: run.id, work_id: work.work_id, stage: stage.stage, pause_id: pause.id, question_path: pause.question_path, answer_path: answerPath, prompt_path: workSession.prompt_path, worker_prompt_markdown: continuation, next_action: `continue_${stage.stage}` };
|
|
122
146
|
}
|
|
123
147
|
export function stagePauseCommand(context, input) {
|
|
124
|
-
|
|
148
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
149
|
+
const run = requireRun(context, project.id, input.runId);
|
|
150
|
+
const questionInputPath = prepareAgentOutputFile(path.join(requireRunHome(run), "works", input.workId, "question-input.md"));
|
|
151
|
+
return managedLifecycleCommand(context, runtimeCommand(context, ["stage", "pause", shortEntityReference(input.runId), "--stage", input.stage, "--work", shortEntityReference(input.workId), "--question-file", questionInputPath, "--project-root", input.projectRoot, "--json"]));
|
|
125
152
|
}
|
|
126
|
-
/**
|
|
127
|
-
* The only shell form allowed for an agent-owned HITL pause. Supplying a
|
|
128
|
-
* complete heredoc makes stdin explicit without asking an agent to invent a
|
|
129
|
-
* pipe, a temporary file or a second command.
|
|
130
|
-
*/
|
|
153
|
+
/** Render separate file-write and lifecycle-call instructions for HITL. */
|
|
131
154
|
export function stagePauseCommandTemplate(command) {
|
|
132
|
-
|
|
155
|
+
const questionFile = command.match(/--question-file\s+(?:'([^']*)'|"([^"]*)"|(\S+))/)?.slice(1).find(Boolean) ?? "<question-file-from-command>";
|
|
156
|
+
return `Write this text to ${questionFile}:
|
|
133
157
|
## Q-001 — <short decision title>
|
|
134
158
|
|
|
135
159
|
Why this decision is required:
|
|
@@ -144,27 +168,12 @@ Recommendation:
|
|
|
144
168
|
|
|
145
169
|
Effect on scope or acceptance:
|
|
146
170
|
- <one sentence>
|
|
147
|
-
|
|
171
|
+
|
|
172
|
+
Then run this standalone command:
|
|
173
|
+
${command}`;
|
|
148
174
|
}
|
|
149
175
|
function stageResumeCommand(context, input) {
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
export function flowCommand(context) {
|
|
153
|
-
const defaultHome = path.resolve(path.join(os.homedir(), ".dd-flow"));
|
|
154
|
-
// Desktop/ACP tool processes are not required to inherit the environment of
|
|
155
|
-
// the adapter that created their Session. In an isolated runtime, a bare
|
|
156
|
-
// `dd-flow` can therefore select the host-global router despite a correct
|
|
157
|
-
// DD_FLOW_HOME. The runner supplies an absolute executable for that case;
|
|
158
|
-
// make every generated lifecycle command self-contained.
|
|
159
|
-
const executable = context.env.DD_FLOW_BIN;
|
|
160
|
-
if (executable && path.isAbsolute(executable)) {
|
|
161
|
-
return `DD_FLOW_HOME=${JSON.stringify(context.ddFlowHome)} ${JSON.stringify(executable)}`;
|
|
162
|
-
}
|
|
163
|
-
const isolatedExecutable = path.join(context.ddFlowHome, "bin", "dd-flow");
|
|
164
|
-
if (context.ddFlowHome !== defaultHome && fs.existsSync(isolatedExecutable)) {
|
|
165
|
-
return `DD_FLOW_HOME=${JSON.stringify(context.ddFlowHome)} ${JSON.stringify(isolatedExecutable)}`;
|
|
166
|
-
}
|
|
167
|
-
return context.ddFlowHome === defaultHome ? "dd-flow" : `DD_FLOW_HOME=${JSON.stringify(context.ddFlowHome)} dd-flow`;
|
|
176
|
+
return managedLifecycleCommand(context, runtimeCommand(context, ["stage", "resume", shortEntityReference(input.runId), "--stage", input.stage, "--work", shortEntityReference(input.workId), "--answer-file", input.answerInputPath, "--project-root", input.projectRoot, "--json"]));
|
|
168
177
|
}
|
|
169
178
|
function requireRun(context, projectId, id) {
|
|
170
179
|
const rows = context.db.all("SELECT id, short_id, project_id, project_root, run_root, index_json FROM runs WHERE project_id = ? AND (id = ? OR short_id = ?)", [projectId, id, id]);
|
|
@@ -173,14 +182,19 @@ function requireRun(context, projectId, id) {
|
|
|
173
182
|
return rows[0];
|
|
174
183
|
}
|
|
175
184
|
function requireWork(context, projectId, runId, workId) {
|
|
176
|
-
|
|
177
|
-
if (!work)
|
|
178
|
-
throw new AppError("not_found", "Work does not belong to this RUN", 1, { run_id: runId, work_id: workId });
|
|
179
|
-
return work;
|
|
185
|
+
return resolveWorkReference(context, workId, { projectId, runId });
|
|
180
186
|
}
|
|
181
187
|
function requireStage(run, stage, status) {
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
let index;
|
|
189
|
+
try {
|
|
190
|
+
index = JSON.parse(run.index_json);
|
|
191
|
+
if (!index || !Array.isArray(index.stage_runs))
|
|
192
|
+
throw new Error("RUN index must contain stage_runs");
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
throw new AppError("runtime_state_invalid", "RUN mechanical state in SQLite is missing or invalid", 1, { run_id: run.id, cause: String(error) });
|
|
196
|
+
}
|
|
197
|
+
const found = index.stage_runs.find((item) => item.stage === stage);
|
|
184
198
|
if (!found || found.status !== status)
|
|
185
199
|
throw new AppError("invalid_stage_state", `Stage must be ${status}`, 1, { run_id: run.id, stage, status: found?.status ?? "missing" });
|
|
186
200
|
return found;
|
|
@@ -1,14 +1,247 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { AppError } from "../shared/errors.js";
|
|
5
|
+
import { withProjectionLock } from "../storage/projection-lock.js";
|
|
3
6
|
import { writeJsonAtomic } from "../storage/paths.js";
|
|
7
|
+
import { assertRunBundleSqlAuthority } from "./runs.js";
|
|
8
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
9
|
+
/** Accepted bytes come from SQL receipts, never from the mutable publication.
|
|
10
|
+
* Stage completion and its independent committed checksum must agree. This is
|
|
11
|
+
* byte custody, not a substitute for the registered handler's semantic gates.
|
|
12
|
+
*/
|
|
13
|
+
export function readAcceptedStageData(context, scope) {
|
|
14
|
+
function refuse() { throw new AppError("stage_data_custody_invalid", "Accepted stage data lacks matching committed SQL byte custody", 1, { run_id: scope.runId, stage: scope.stage }); }
|
|
15
|
+
const run = context.db.get("SELECT index_json, run_root, run_dir FROM runs WHERE project_id = ? AND id = ?", [scope.projectId, scope.runId]);
|
|
16
|
+
if (!run)
|
|
17
|
+
refuse();
|
|
18
|
+
const index = JSON.parse(run.index_json);
|
|
19
|
+
assertRunBundleSqlAuthority(context, scope, index.execution_bundle);
|
|
20
|
+
const stage = index.stage_runs?.find((item) => item.stage === scope.stage);
|
|
21
|
+
const portable = (value) => typeof value === "string" && Boolean(value) && !path.posix.isAbsolute(value) && !path.win32.isAbsolute(value) && !value.includes("\\") && !value.includes("\0") && value.split("/").every(part => Boolean(part) && part !== "." && part !== "..");
|
|
22
|
+
if (!stage || stage.status !== "done" || !portable(stage.dir) || !portable(stage.data)
|
|
23
|
+
|| typeof stage.attempt !== "string" || !/^try-[0-9]{3,}$/.test(stage.attempt)
|
|
24
|
+
|| typeof stage.data_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(stage.data_sha256))
|
|
25
|
+
refuse();
|
|
26
|
+
const root = run.run_root ?? run.run_dir;
|
|
27
|
+
if (!path.isAbsolute(root))
|
|
28
|
+
refuse();
|
|
29
|
+
const relative = `${stage.dir}/${stage.data}`, destination = path.resolve(root, relative);
|
|
30
|
+
const rows = context.db.all("SELECT event_type, payload_json FROM audit_events WHERE project_id = ? AND event_type IN ('flow_run.stage_packet', 'flow_run.stage_report') AND json_extract(payload_json, '$.run_id') = ? AND json_extract(payload_json, '$.stage') = ? AND json_extract(payload_json, '$.attempt') = ? LIMIT 33", [scope.projectId, scope.runId, scope.stage, stage.attempt]);
|
|
31
|
+
if (rows.length > 32)
|
|
32
|
+
refuse();
|
|
33
|
+
let accepted;
|
|
34
|
+
for (const row of rows) {
|
|
35
|
+
if (Buffer.byteLength(row.payload_json) > 8 * 1024 * 1024)
|
|
36
|
+
refuse();
|
|
37
|
+
const receipt = JSON.parse(row.payload_json);
|
|
38
|
+
if (receipt.sha256 !== stage.data_sha256)
|
|
39
|
+
continue;
|
|
40
|
+
const locator = row.event_type === "flow_run.stage_packet" ? receipt.file : typeof receipt.root === "string" ? path.join(receipt.root, "stage-report.json") : null;
|
|
41
|
+
if (typeof locator !== "string" || resolveStagePublicationPath(context, scope.projectId, scope.runId, locator) !== destination)
|
|
42
|
+
continue;
|
|
43
|
+
let bytes;
|
|
44
|
+
if (row.event_type === "flow_run.stage_packet") {
|
|
45
|
+
if (typeof receipt.base64 !== "string")
|
|
46
|
+
refuse();
|
|
47
|
+
bytes = Buffer.from(receipt.base64, "base64");
|
|
48
|
+
if (bytes.toString("base64") !== receipt.base64)
|
|
49
|
+
refuse();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
if (typeof receipt.bytes?.json !== "string")
|
|
53
|
+
refuse();
|
|
54
|
+
bytes = Buffer.from(receipt.bytes.json);
|
|
55
|
+
}
|
|
56
|
+
if (!bytes.length || bytes.length > 1024 * 1024 || crypto.createHash("sha256").update(bytes).digest("hex") !== stage.data_sha256)
|
|
57
|
+
refuse();
|
|
58
|
+
if (accepted && !accepted.equals(bytes))
|
|
59
|
+
refuse();
|
|
60
|
+
accepted = bytes;
|
|
61
|
+
}
|
|
62
|
+
if (!accepted)
|
|
63
|
+
refuse();
|
|
64
|
+
return { bytes: Buffer.from(accepted), result: { path: relative, sha256: stage.data_sha256 },
|
|
65
|
+
input_sha256: stage.input_sha256, data_schema_id: stage.data_schema_id,
|
|
66
|
+
authority_sha256: hybridDigest({ scope, attempt: stage.attempt, directory: stage.dir, data: stage.data,
|
|
67
|
+
input_sha256: stage.input_sha256 ?? null, data_schema_id: stage.data_schema_id ?? null,
|
|
68
|
+
anchor: index.execution_bundle ?? null, data_sha256: stage.data_sha256 }) };
|
|
69
|
+
}
|
|
4
70
|
/** Deterministic JSON -> Markdown/HTML projection shared by every stage. */
|
|
5
71
|
export function writeStageReport(root, report) {
|
|
6
72
|
const json = path.join(root, "stage-report.json");
|
|
7
73
|
const markdown = path.join(root, "stage-report.md");
|
|
8
74
|
const html = path.join(root, "stage-report.html");
|
|
75
|
+
const normalized = normalizeStageReport(report);
|
|
76
|
+
writeJsonAtomic(json, normalized);
|
|
77
|
+
const text = renderStageReportMarkdown(normalized);
|
|
78
|
+
fs.writeFileSync(markdown, text);
|
|
79
|
+
fs.writeFileSync(html, renderStageReportHtml(normalized, text));
|
|
80
|
+
return { json, markdown, html };
|
|
81
|
+
}
|
|
82
|
+
export function prepareStageReport(report) {
|
|
83
|
+
const normalized = normalizeStageReport(report);
|
|
84
|
+
const json = `${JSON.stringify(normalized, null, 2)}\n`;
|
|
85
|
+
const markdown = renderStageReportMarkdown(normalized);
|
|
86
|
+
const html = renderStageReportHtml(normalized, markdown);
|
|
87
|
+
return { normalized, bytes: { json, markdown, html }, sha256: crypto.createHash("sha256").update(json).digest("hex") };
|
|
88
|
+
}
|
|
89
|
+
export function prepareStagePacket(file, bytes) {
|
|
90
|
+
if (fs.existsSync(file) && !bytes.equals(fs.readFileSync(file)))
|
|
91
|
+
throw new AppError("stage_packet_conflict", "An existing Stage packet differs from prepared bytes", 2, { file });
|
|
92
|
+
return { file, bytes, sha256: crypto.createHash("sha256").update(bytes).digest("hex") };
|
|
93
|
+
}
|
|
94
|
+
export function acceptStagePacket(context, input) {
|
|
95
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [input.projectId, input.runId]);
|
|
96
|
+
const index = run ? JSON.parse(run.index_json) : null;
|
|
97
|
+
const attempt = index?.stage_runs?.find((item) => item.stage === input.stage)?.attempt;
|
|
98
|
+
if (!attempt)
|
|
99
|
+
throw new AppError("stage_packet_owner_missing", "Stage packet has no owning attempt", 1, { run_id: input.runId, stage: input.stage });
|
|
100
|
+
const payload = { run_id: input.runId, stage: input.stage, attempt, file: input.prepared.file, sha256: input.prepared.sha256, base64: input.prepared.bytes.toString("base64") };
|
|
101
|
+
context.db.run("INSERT INTO audit_events (project_id, event_type, forced, payload_json, created_at) VALUES (?, 'flow_run.stage_packet', 0, ?, ?)", [input.projectId, JSON.stringify(payload), context.now()]);
|
|
102
|
+
context.db.afterCommit(() => publishStagePacket(context, input.projectId, input.runId, input.stage, input.prepared.file));
|
|
103
|
+
}
|
|
104
|
+
export function publishStagePacket(context, projectId, runId, stage, file) {
|
|
105
|
+
file = resolveStagePublicationPath(context, projectId, runId, file);
|
|
106
|
+
assertPublicationPath(context, projectId, runId, file);
|
|
107
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
108
|
+
withProjectionLock(`${file}.publish-lock`, () => {
|
|
109
|
+
assertPublicationPath(context, projectId, runId, file);
|
|
110
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
111
|
+
const index = run ? JSON.parse(run.index_json) : null;
|
|
112
|
+
const currentAttempt = index?.stage_runs?.find((item) => item.stage === stage)?.attempt;
|
|
113
|
+
const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.stage_packet' ORDER BY id DESC", [projectId]);
|
|
114
|
+
const packet = rows.map((row) => JSON.parse(row.payload_json)).find((item) => item.run_id === runId && item.stage === stage && item.attempt === currentAttempt && resolveStagePublicationPath(context, projectId, runId, item.file) === file);
|
|
115
|
+
if (!packet)
|
|
116
|
+
return;
|
|
117
|
+
const bytes = Buffer.from(packet.base64, "base64");
|
|
118
|
+
if (crypto.createHash("sha256").update(bytes).digest("hex") !== packet.sha256)
|
|
119
|
+
throw new AppError("stage_packet_invalid", "Stored Stage packet hash does not match its bytes", 1, { file });
|
|
120
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
121
|
+
try {
|
|
122
|
+
fs.writeFileSync(file, bytes, { flag: "wx" });
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error.code !== "EEXIST")
|
|
126
|
+
throw error;
|
|
127
|
+
if (!bytes.equals(fs.readFileSync(file)))
|
|
128
|
+
throw new AppError("stage_packet_conflict", "Accepted Stage packet differs from the published file", 1, { file, run_id: runId, stage, attempt: currentAttempt });
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** SQL receipt and exact bytes are committed with the owning Stage transition. */
|
|
133
|
+
export function acceptStageReport(context, input) {
|
|
134
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [input.projectId, input.runId]);
|
|
135
|
+
const index = run ? JSON.parse(run.index_json) : null;
|
|
136
|
+
const attempt = index?.stage_runs?.find((item) => item.stage === input.stage)?.attempt;
|
|
137
|
+
if (!attempt)
|
|
138
|
+
throw new AppError("stage_report_owner_missing", "Stage report has no owning attempt", 1, { run_id: input.runId, stage: input.stage });
|
|
139
|
+
const payload = { run_id: input.runId, stage: input.stage, attempt, root: input.root, sha256: input.prepared.sha256, bytes: input.prepared.bytes };
|
|
140
|
+
context.db.run("INSERT INTO audit_events (project_id, event_type, forced, payload_json, created_at) VALUES (?, 'flow_run.stage_report', 0, ?, ?)", [input.projectId, JSON.stringify(payload), context.now()]);
|
|
141
|
+
context.db.afterCommit(() => publishStageReport(context, input.projectId, input.runId, input.stage, input.root));
|
|
142
|
+
}
|
|
143
|
+
/** Replays the latest committed receipt, never an obsolete attempt. */
|
|
144
|
+
export function publishStageReport(context, projectId, runId, stage, root) {
|
|
145
|
+
root = resolveStagePublicationPath(context, projectId, runId, root);
|
|
146
|
+
assertPublicationPath(context, projectId, runId, root);
|
|
147
|
+
withProjectionLock(`${root}.report-publish-lock`, () => {
|
|
148
|
+
assertPublicationPath(context, projectId, runId, root);
|
|
149
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
150
|
+
const index = run ? JSON.parse(run.index_json) : null;
|
|
151
|
+
const currentAttempt = index?.stage_runs?.find((item) => item.stage === stage)?.attempt;
|
|
152
|
+
const events = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.stage_report' ORDER BY id DESC", [projectId]);
|
|
153
|
+
const receipt = events.map((row) => JSON.parse(row.payload_json)).find((item) => item.run_id === runId && item.stage === stage && item.attempt === currentAttempt && resolveStagePublicationPath(context, projectId, runId, item.root) === root);
|
|
154
|
+
if (!receipt)
|
|
155
|
+
return;
|
|
156
|
+
fs.mkdirSync(root, { recursive: true });
|
|
157
|
+
for (const [name, bytes] of Object.entries(receipt.bytes)) {
|
|
158
|
+
const file = path.join(root, `stage-report.${name === "json" ? "json" : name === "markdown" ? "md" : "html"}`);
|
|
159
|
+
assertPublicationPath(context, projectId, runId, file);
|
|
160
|
+
try {
|
|
161
|
+
fs.writeFileSync(file, bytes, { flag: "wx" });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error.code !== "EEXIST")
|
|
165
|
+
throw error;
|
|
166
|
+
if (fs.readFileSync(file, "utf8") !== bytes)
|
|
167
|
+
throw new AppError("stage_report_conflict", "Accepted Stage report differs from the published file", 1, { run_id: runId, stage, attempt: currentAttempt, file });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/** Operational destinations follow validated imports, while audit bytes retain
|
|
173
|
+
* their original locators. SQL-only so readers may also use it in settlement. */
|
|
174
|
+
export function resolveStagePublicationPath(context, projectId, runId, locator) {
|
|
175
|
+
const run = context.db.get("SELECT run_root FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
176
|
+
if (!run?.run_root || !path.isAbsolute(locator))
|
|
177
|
+
throw new AppError("stage_packet_invalid", "Stage publication has no absolute bound RUN locator", 1, { run_id: runId, locator });
|
|
178
|
+
let destination = path.resolve(locator);
|
|
179
|
+
const mappings = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.publication_relocation' ORDER BY id", [projectId]);
|
|
180
|
+
for (const row of mappings) {
|
|
181
|
+
const mapping = JSON.parse(row.payload_json);
|
|
182
|
+
if (mapping.run_id !== runId || !path.isAbsolute(mapping.from) || !path.isAbsolute(mapping.to))
|
|
183
|
+
continue;
|
|
184
|
+
const relative = path.relative(mapping.from, destination);
|
|
185
|
+
if (relative && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
|
|
186
|
+
destination = path.resolve(mapping.to, relative);
|
|
187
|
+
}
|
|
188
|
+
const relative = path.relative(run.run_root, destination);
|
|
189
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
190
|
+
throw new AppError("publication_pending", "Stage publication escaped its bound RUN lineage", 1, { effect: "committed", run_id: runId, locator });
|
|
191
|
+
return destination;
|
|
192
|
+
}
|
|
193
|
+
/** Recheck the actual destination after preparation: a missing leaf may have
|
|
194
|
+
* an existing symlink ancestor, and retained packet bytes never authorize it. */
|
|
195
|
+
function assertPublicationPath(context, projectId, runId, file) {
|
|
196
|
+
const run = context.db.get("SELECT run_root FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
197
|
+
const root = run?.run_root && path.resolve(run.run_root), destination = path.resolve(file);
|
|
198
|
+
if (!root || !destination.startsWith(`${root}${path.sep}`))
|
|
199
|
+
throw new AppError("publication_pending", "Stage publication escaped its RUN", 1, { effect: "committed", run_id: runId, file });
|
|
200
|
+
let ancestor = destination;
|
|
201
|
+
while (!fs.existsSync(ancestor) && ancestor !== root)
|
|
202
|
+
ancestor = path.dirname(ancestor);
|
|
203
|
+
const actual = fs.realpathSync(ancestor), realRoot = fs.realpathSync(root);
|
|
204
|
+
if (actual !== realRoot && !actual.startsWith(`${realRoot}${path.sep}`))
|
|
205
|
+
throw new AppError("publication_pending", "Stage publication escaped its RUN through a symlink", 1, { effect: "committed", run_id: runId, file });
|
|
206
|
+
}
|
|
207
|
+
/** Admission repair for predecessor reports; the stage being retried is archived separately. */
|
|
208
|
+
export function reconcileRunStageReports(context, projectId, runId, exceptStage) {
|
|
209
|
+
const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.stage_report' ORDER BY id DESC", [projectId]);
|
|
210
|
+
const seen = new Set();
|
|
211
|
+
for (const row of rows) {
|
|
212
|
+
const receipt = JSON.parse(row.payload_json);
|
|
213
|
+
if (receipt.run_id !== runId || typeof receipt.stage !== "string" || typeof receipt.root !== "string" || receipt.stage === exceptStage || seen.has(receipt.stage))
|
|
214
|
+
continue;
|
|
215
|
+
seen.add(receipt.stage);
|
|
216
|
+
publishStageReport(context, projectId, runId, receipt.stage, receipt.root);
|
|
217
|
+
}
|
|
218
|
+
const packets = context.db.all("SELECT payload_json FROM audit_events WHERE project_id = ? AND event_type = 'flow_run.stage_packet' ORDER BY id DESC", [projectId]);
|
|
219
|
+
const packetFiles = new Set();
|
|
220
|
+
for (const row of packets) {
|
|
221
|
+
const packet = JSON.parse(row.payload_json);
|
|
222
|
+
if (packet.run_id !== runId || typeof packet.stage !== "string" || typeof packet.file !== "string" || packet.stage === exceptStage || packetFiles.has(packet.file))
|
|
223
|
+
continue;
|
|
224
|
+
packetFiles.add(packet.file);
|
|
225
|
+
publishStagePacket(context, projectId, runId, packet.stage, packet.file);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function normalizeStageReport(report) {
|
|
9
229
|
const semantic = object(report.semantic);
|
|
230
|
+
if (semantic.materialization !== undefined) {
|
|
231
|
+
const value = object(semantic.materialization);
|
|
232
|
+
const authored = object(value.authored_result);
|
|
233
|
+
const relative = (item) => typeof item === "string" && item.length > 0 && !path.isAbsolute(item) && !item.split(/[\\/]/).includes("..");
|
|
234
|
+
if (value.schema_id !== "dd-flow/protocolize-materialization@1" || value.run_id !== report.run_id || report.stage !== "protocolize" || value.stage !== "protocolize"
|
|
235
|
+
|| !["create", "link", "update", "not_applicable"].includes(String(value.action)) || value.status !== "passed"
|
|
236
|
+
|| authored.role !== "authored_input" || !relative(authored.run_relative_path) || !/^[a-f0-9]{64}$/.test(String(authored.sha256))
|
|
237
|
+
|| !(value.feature_path === null || relative(value.feature_path)) || !Array.isArray(value.protocol_ids) || value.protocol_ids.some(id => typeof id !== "string")
|
|
238
|
+
|| !Array.isArray(value.documents) || value.documents.some(item => { const ref = object(item); return !relative(ref.path) || !/^[a-f0-9]{64}$/.test(String(ref.sha256)); })
|
|
239
|
+
|| !Array.isArray(value.owned_links) || value.owned_links.some(item => { const edge = object(item); return !relative(edge.feature_path) || !relative(edge.protocol_path) || !value.protocol_ids.includes(edge.protocol_id); })) {
|
|
240
|
+
throw new AppError("stage_report_materialization_invalid", "PROTOCOLIZE materialization receipt is malformed or out of scope", 1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
10
243
|
const sourceArtifacts = object(report.artifacts);
|
|
11
|
-
|
|
244
|
+
return {
|
|
12
245
|
...report,
|
|
13
246
|
schema_id: "dd-flow/stage-report@2",
|
|
14
247
|
summary: string(report.summary) || string(semantic.result) || "Stage completed.",
|
|
@@ -27,11 +260,6 @@ export function writeStageReport(root, report) {
|
|
|
27
260
|
html: string(sourceArtifacts.html) || "stage-report.html"
|
|
28
261
|
}
|
|
29
262
|
};
|
|
30
|
-
writeJsonAtomic(json, normalized);
|
|
31
|
-
const text = renderStageReportMarkdown(normalized);
|
|
32
|
-
fs.writeFileSync(markdown, text);
|
|
33
|
-
fs.writeFileSync(html, renderStageReportHtml(normalized, text));
|
|
34
|
-
return { json, markdown, html };
|
|
35
263
|
}
|
|
36
264
|
export function renderStageReportMarkdown(report) {
|
|
37
265
|
const semantic = object(report.semantic);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
export function stageAttempt(context, projectId, runId, stage) {
|
|
3
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
4
|
+
if (!run)
|
|
5
|
+
throw new AppError("not_found", "Stage graph RUN is unavailable", 1);
|
|
6
|
+
const stages = JSON.parse(run.index_json).stage_runs ?? [];
|
|
7
|
+
return [...stages].reverse().find(item => item.stage === stage)?.attempt ?? "try-001";
|
|
8
|
+
}
|
|
9
|
+
export function bindStageWork(context, input) {
|
|
10
|
+
context.db.writeTransaction(() => bindStageWorkOwned(context, input), "stage-work.bind");
|
|
11
|
+
}
|
|
12
|
+
function bindStageWorkOwned(context, input) {
|
|
13
|
+
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [input.projectId, input.runId]);
|
|
14
|
+
const index = run ? JSON.parse(run.index_json) : null;
|
|
15
|
+
if (index?.execution_bundle) {
|
|
16
|
+
// Stage attachment already admitted the frozen descriptor outside the writer.
|
|
17
|
+
// Membership checks here consume only authoritative SQLite facts.
|
|
18
|
+
const stage = index.stage_runs.find(item => item.stage === input.stage && item.status === "running");
|
|
19
|
+
const work = context.db.get("SELECT parent_work_id FROM works WHERE project_id = ? AND run_id = ? AND work_id = ?", [input.projectId, input.runId, input.workId]);
|
|
20
|
+
const parentOwned = !work?.parent_work_id || context.db.get("SELECT 1 FROM works WHERE project_id = ? AND run_id = ? AND work_id = ?", [input.projectId, input.runId, work.parent_work_id]);
|
|
21
|
+
const currentStage = index.current_stage ?? (index.execution_bundle.flow_id === "sdlc" ? index.stage_runs.at(-1)?.stage : undefined);
|
|
22
|
+
if (!stage || currentStage !== input.stage || !work || !parentOwned || input.attempt !== undefined && input.attempt !== (stage.attempt ?? "try-001")
|
|
23
|
+
|| input.cycle !== undefined && input.cycle !== stageCycle(context, input.projectId, input.runId, input.stage)) {
|
|
24
|
+
throw new AppError("stage_work_scope_invalid", "Work membership does not belong to the active frozen stage scope", 1, { run_id: input.runId, stage: input.stage, work_id: input.workId });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
context.db.run("INSERT OR IGNORE INTO stage_work_memberships (project_id, run_id, work_id, stage, attempt, cycle) VALUES (?, ?, ?, ?, ?, ?)", [input.projectId, input.runId, input.workId, input.stage, input.attempt ?? stageAttempt(context, input.projectId, input.runId, input.stage), input.cycle ?? stageCycle(context, input.projectId, input.runId, input.stage)]);
|
|
28
|
+
}
|
|
29
|
+
export function stageCycle(context, projectId, runId, stage) {
|
|
30
|
+
if (stage !== "code" && stage !== "code-review")
|
|
31
|
+
return "";
|
|
32
|
+
const row = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
33
|
+
const cycle = row ? JSON.parse(row.index_json).variables?.["merge.source_repair"]?.cycle : undefined;
|
|
34
|
+
return typeof cycle === "string" ? cycle : stage === "code-review" ? "initial" : "";
|
|
35
|
+
}
|
|
36
|
+
export function stageWorkIds(context, projectId, runId, stage) {
|
|
37
|
+
if (!context.db.get("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'stage_work_memberships'"))
|
|
38
|
+
throw new AppError("stage_graph_compatibility_required", "Stage Work membership is unavailable; use the RUN's pinned engine", 1);
|
|
39
|
+
return new Set(context.db.all("SELECT work_id FROM stage_work_memberships WHERE project_id = ? AND run_id = ? AND stage = ? AND attempt = ? AND cycle = ?", [projectId, runId, stage, stageAttempt(context, projectId, runId, stage), stageCycle(context, projectId, runId, stage)]).map(row => row.work_id));
|
|
40
|
+
}
|
|
41
|
+
/** Generic Work APIs also serve non-stage runs; null preserves that contract. */
|
|
42
|
+
export function currentStageWorkIds(context, projectId, runId) {
|
|
43
|
+
const row = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]);
|
|
44
|
+
if (!row)
|
|
45
|
+
return null;
|
|
46
|
+
const index = JSON.parse(row.index_json);
|
|
47
|
+
const stage = [...(index.stage_runs ?? [])].reverse().find(item => item.status === "running")?.stage;
|
|
48
|
+
return stage ? stageWorkIds(context, projectId, runId, stage) : null;
|
|
49
|
+
}
|
|
50
|
+
export function stageGraphScope(context, projectId, runId, stage) {
|
|
51
|
+
return { project_id: projectId, run_id: runId, attempt: stageAttempt(context, projectId, runId, stage), cycle: stageCycle(context, projectId, runId, stage) };
|
|
52
|
+
}
|