@deksden-com/dd-flow-cli 0.9.0-beta.137 → 0.9.0-beta.139
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 +19 -0
- package/README.md +9 -0
- package/dist/build-info.json +6 -6
- package/dist/cli/argument-values.js +5 -5
- package/dist/cli/command-inputs.js +89 -20
- 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 +240 -6
- package/dist/cli/input-preparation.js +54 -5
- 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 +1 -1
- 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 +283 -160
- package/dist/cli/runtime-process.js +1 -1
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +56 -2
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +53 -22
- package/dist/harness-runtime/lib/dd-agy.mjs +16 -5
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +12 -6
- package/dist/harness-runtime/lib/dd-codex.mjs +24 -12
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
- package/dist/harness-runtime/lib/dd-droid.mjs +32 -9
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +11 -3
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +100 -15
- package/dist/harness-runtime/lib/dd-opencode.mjs +51 -20
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +22 -10
- package/dist/harness-runtime/lib/dd-zcode.mjs +17 -4
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +16 -3
- package/dist/harness-runtime/lib/lease-renewal.mjs +2 -1
- package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/native-children.d.mts +2 -0
- package/dist/harness-runtime/lib/native-children.mjs +20 -1
- package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
- 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/opencode-plugin.mjs +32 -9
- package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
- 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/runtime/context.js +1 -1
- 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 +26 -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/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/services/check-runner-evidence.js +51 -4
- package/dist/services/cli-operation-classifier.js +8 -47
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +387 -34
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +18 -2
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +71 -12
- package/dist/services/dashboard.js +8 -2
- 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 +39 -22
- package/dist/services/eval-snapshots.js +83 -25
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +20 -16
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +93 -15
- 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 +28 -2
- 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 +24 -6
- 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/lifecycle-invocations.js +257 -78
- package/dist/services/managed-processes.js +31 -10
- 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-server.js +2 -4
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/projects.js +17 -2
- package/dist/services/{codex-capacity.js → provider-capacity.js} +34 -16
- package/dist/services/recovery-snapshot-database.js +417 -2
- 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 +1 -1
- package/dist/services/review-copy.js +10 -2
- package/dist/services/run-control.js +80 -9
- package/dist/services/run-controller-adapter.js +1 -1
- package/dist/services/run-controller-capture.js +10 -3
- package/dist/services/run-controller-process.js +21 -0
- package/dist/services/run-controller-recovery.js +5 -5
- package/dist/services/run-controller-state.js +1 -1
- package/dist/services/run-controller.js +158 -64
- package/dist/services/run-recovery-guard.js +5 -3
- package/dist/services/run-recovery-runtime.js +3 -3
- package/dist/services/run-recovery.js +145 -5
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +413 -30
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-context.js +9 -1
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-report-renderer.js +63 -0
- package/dist/services/stage-work-graph.js +17 -0
- package/dist/services/vnext-code-review.js +1 -1
- package/dist/services/vnext-code.js +6 -6
- package/dist/services/vnext-execution-profile.js +2 -2
- package/dist/services/vnext-fanout.js +73 -11
- package/dist/services/vnext-merge.js +8 -3
- package/dist/services/vnext-plan-review.js +7 -5
- package/dist/services/vnext-plan.js +3 -3
- package/dist/services/vnext-protocolize.js +20 -6
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +616 -71
- package/dist/services/workspace-files.js +1 -1
- package/dist/storage/database.js +84 -6
- package/dist/storage/writer-contract.js +11 -7
- package/package.json +13 -3
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/dist/harness-runtime/lib/codex-capacity-policy.d.mts +0 -10
- package/dist/harness-runtime/lib/codex-capacity-policy.mjs +0 -102
|
@@ -93,8 +93,8 @@ export function validateVnextExecutionProfile(value) {
|
|
|
93
93
|
|| profile.merge_delivery?.strategy !== "local"
|
|
94
94
|
|| !["retain", "delete_after_success"].includes(String(profile.merge_cleanup?.source))
|
|
95
95
|
|| !["code_completed", "code_review_completed", "merge_completed"].includes(String(profile.stop_target))
|
|
96
|
-
|| !profile.code_bootstrap || typeof profile.code_bootstrap.command !== "string" || !profile.code_bootstrap.command.trim()
|
|
97
|
-
|
|
96
|
+
|| (profile.code_bootstrap !== undefined && (!profile.code_bootstrap || typeof profile.code_bootstrap.command !== "string" || !profile.code_bootstrap.command.trim()
|
|
97
|
+
|| typeof profile.code_bootstrap.policy_ref !== "string" || !profile.code_bootstrap.policy_ref.trim()))
|
|
98
98
|
|| (profile.execution === undefined ? profile.schema_id === "dd-flow/project-execution@3" : !isExecutionRouting(profile.execution))) {
|
|
99
99
|
throw new AppError("execution_profile_invalid", "Project execution profile does not match its supported dd-flow schema", 1);
|
|
100
100
|
}
|
|
@@ -3,14 +3,17 @@ import path from "node:path";
|
|
|
3
3
|
import { AppError } from "../shared/errors.js";
|
|
4
4
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
5
5
|
import { requireProjectByRoot } from "./projects.js";
|
|
6
|
-
import { getFlowRunVariables, appendFlowRunTimelineEvent } from "./runs.js";
|
|
6
|
+
import { getFlowRunVariables, appendFlowRunTimelineEvent, readFlowRunExecutionBundle } from "./runs.js";
|
|
7
7
|
import { resolveExecutionPolicy } from "./execution-policy.js";
|
|
8
|
-
import { listWorks, failWork, cancelWork } from "./work-registry.js";
|
|
8
|
+
import { listWorks, failWork, cancelWork, readWorkBatchCustody } from "./work-registry.js";
|
|
9
9
|
import { storageSessionId } from "./session-identity.js";
|
|
10
10
|
import { reconcileUnfinishedChecks } from "./code-checks.js";
|
|
11
11
|
import { vnextStageDirectory } from "../domain/stage-catalog.js";
|
|
12
12
|
import { stageWorkIds, stageGraphScope } from "./stage-work-graph.js";
|
|
13
|
-
import {
|
|
13
|
+
import { eligibleProviderChildOverload } from "./provider-capacity.js";
|
|
14
|
+
import { renderNativeChildFollowupInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
|
|
15
|
+
import { readDescriptorRootWork } from "./descriptor-root-work.js";
|
|
16
|
+
import { selectMemoryCompositionRegistration } from "./memory-composition-registration.js";
|
|
14
17
|
import { safeNativeFailure } from "../harness-runtime/lib/native-children.mjs";
|
|
15
18
|
export const subagentCapacityKey = "runtime.subagent.available_slots";
|
|
16
19
|
/** External concurrency is policy, never evidence of native child qualification. */
|
|
@@ -20,17 +23,29 @@ export function getStageFanoutCapacity(context, input) {
|
|
|
20
23
|
const run = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
|
|
21
24
|
if (!run)
|
|
22
25
|
throw new AppError("not_found", "RUN is unavailable", 1);
|
|
23
|
-
const
|
|
24
|
-
|
|
26
|
+
const index = JSON.parse(run.index_json);
|
|
27
|
+
const bundle = index.execution_bundle ? readFlowRunExecutionBundle(context, { projectRoot, runId: input.runId }) : null;
|
|
28
|
+
const generic = bundle?.execution.work(input.stage).delegation;
|
|
29
|
+
if (generic?.mode === "external")
|
|
30
|
+
return input.nativeOnly
|
|
31
|
+
? { run_key: null, available_slots: null, source: "harness_qualification" }
|
|
32
|
+
: { run_key: null, available_slots: generic.max_parallel, source: "external_policy" };
|
|
33
|
+
// Generic native admission already requires a scoped capacity receipt whose
|
|
34
|
+
// observed count covers this frozen limit. Mutable legacy variables cannot
|
|
35
|
+
// replace that receipt or silently prevent its productive dispatch.
|
|
36
|
+
if (generic?.mode === "native")
|
|
37
|
+
return { run_key: null, available_slots: generic.max_parallel, source: "harness_qualification" };
|
|
38
|
+
const frozen = index.execution_profile;
|
|
39
|
+
if (!bundle && frozen?.settings.execution) {
|
|
25
40
|
if (!frozen.agent_profiles)
|
|
26
41
|
throw new AppError("execution_profile_not_frozen", "Fan-out needs the RUN's frozen profiles", 1);
|
|
27
42
|
const resolved = resolveExecutionPolicy({ ddFlowHome: context.ddFlowHome, policy: frozen.settings.execution, profiles: frozen.agent_profiles, stage: input.stage });
|
|
28
|
-
if (resolved.delegation.mode === "external")
|
|
43
|
+
if (resolved.delegation.mode === "external" && !input.nativeOnly)
|
|
29
44
|
return { run_key: null, available_slots: resolved.delegation.max_parallel, source: "external_policy" };
|
|
30
45
|
}
|
|
31
46
|
const variables = getFlowRunVariables(context, { projectRoot, runId: input.runId });
|
|
32
47
|
const available = variables.variables[subagentCapacityKey];
|
|
33
|
-
return { run_key: subagentCapacityKey, available_slots: typeof available === "number" && Number.isInteger(available) && available >= 0 ? available : null, source: "harness_qualification" };
|
|
48
|
+
return { run_key: subagentCapacityKey, available_slots: typeof available === "number" && Number.isInteger(available) && available >= 0 ? Math.min(available, generic?.max_parallel ?? available) : null, source: "harness_qualification" };
|
|
34
49
|
}
|
|
35
50
|
function nativeFailureController(context, projectId, runId, stage, parent) {
|
|
36
51
|
if (!context.db.get("SELECT 1 FROM sqlite_master WHERE type='table' AND name='run_controllers'"))
|
|
@@ -113,17 +128,64 @@ export function readFanoutDescriptor(stageRoot) {
|
|
|
113
128
|
}
|
|
114
129
|
return value;
|
|
115
130
|
}
|
|
131
|
+
/** Memory graphs are SQL-owned; a mutable/missing fanout.json is not their
|
|
132
|
+
* dispatch authority. Domain registrations and Work launch owners still fence
|
|
133
|
+
* productive assignments, including reviewer/correction waves.
|
|
134
|
+
*/
|
|
135
|
+
export function memoryStageFanoutDescriptor(context, input) {
|
|
136
|
+
if (!["compose", "review"].includes(input.stage))
|
|
137
|
+
return null;
|
|
138
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
139
|
+
const run = context.db.get("SELECT flow_kind,index_json FROM runs WHERE project_id=? AND id=?", [project.id, input.runId]);
|
|
140
|
+
if (run?.flow_kind !== "mb-init")
|
|
141
|
+
return null;
|
|
142
|
+
const bundle = readFlowRunExecutionBundle(context, input);
|
|
143
|
+
if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
|
|
144
|
+
throw new AppError("stage_graph_compatibility_required", "Memory graph requires its admitted RUN bundle", 1);
|
|
145
|
+
const index = JSON.parse(run.index_json);
|
|
146
|
+
if (index.current_stage !== input.stage || !index.stage_runs.some(stage => stage.stage === input.stage && stage.status === "running"))
|
|
147
|
+
return null;
|
|
148
|
+
const root = readDescriptorRootWork(context, { projectId: project.id, runId: input.runId }, bundle);
|
|
149
|
+
const scope = stageGraphScope(context, project.id, input.runId, input.stage), members = stageWorkIds(context, project.id, input.runId, input.stage);
|
|
150
|
+
if (root.status !== "running" || !members.has(root.workId))
|
|
151
|
+
throw new AppError("stage_graph_conflict", "Memory graph lost its active coordinator", 1);
|
|
152
|
+
for (const id of members) {
|
|
153
|
+
if (id === root.workId)
|
|
154
|
+
continue;
|
|
155
|
+
const work = context.db.get("SELECT parent_work_id,result_schema FROM works WHERE project_id=? AND run_id=? AND work_id=?", [project.id, input.runId, id]);
|
|
156
|
+
const schemas = input.stage === "compose" ? ["dd-flow/memory-composition-work-result@1"] : ["dd-flow/memory-review-work-result@1", "dd-flow/memory-repair-work-result@1"];
|
|
157
|
+
if (!work || work.parent_work_id !== root.workId || !schemas.includes(work.result_schema ?? ""))
|
|
158
|
+
throw new AppError("stage_graph_conflict", "Memory graph contains an unassigned Work", 1);
|
|
159
|
+
const custody = readWorkBatchCustody(context, id);
|
|
160
|
+
if (custody.scope.project_id !== scope.project_id || custody.scope.run_id !== scope.run_id || custody.scope.attempt !== scope.attempt
|
|
161
|
+
|| custody.scope.cycle !== scope.cycle || custody.scope.stage !== input.stage || custody.scope.parent_work_id !== root.workId)
|
|
162
|
+
throw new AppError("stage_graph_conflict", "Memory graph contains foreign allocation custody", 1);
|
|
163
|
+
}
|
|
164
|
+
if (input.stage === "compose") {
|
|
165
|
+
const children = context.db.all("SELECT work_id FROM works WHERE project_id=? AND run_id=? AND parent_work_id=?", [project.id, input.runId, root.workId]).filter(work => members.has(work.work_id));
|
|
166
|
+
const registered = context.db.get("SELECT 1 FROM audit_events WHERE project_id=? AND event_type='memory.composition_registration' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.scope.attempt')=? AND json_extract(payload_json,'$.scope.cycle')=? LIMIT 1", [project.id, input.runId, scope.attempt, scope.cycle]);
|
|
167
|
+
if (children.length || registered) {
|
|
168
|
+
const registration = selectMemoryCompositionRegistration(context, { ...scope, stage: "compose", parent_work_id: root.workId });
|
|
169
|
+
const ids = new Set(Object.values(registration.work_ids));
|
|
170
|
+
if (ids.size !== children.length || children.some(work => !ids.has(work.work_id)))
|
|
171
|
+
throw new AppError("stage_graph_conflict", "Memory composition differs from its registered graph", 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { schema_id: "dd-flow/work-fanout@2", kind: "work_fanout", scope, stage: input.stage, parent_work_id: root.workId, dispatch: "none", capacity_required: true };
|
|
175
|
+
}
|
|
116
176
|
/** Read-only projection for an external orchestrator. dd-flow keeps the graph authority. */
|
|
117
177
|
export function getVnextFanoutStatus(context, input) {
|
|
118
178
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
119
179
|
const project = requireProjectByRoot(context, projectRoot);
|
|
120
|
-
const run = context.db.get("SELECT run_root FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
|
|
180
|
+
const run = context.db.get("SELECT run_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, input.runId]);
|
|
121
181
|
if (!run)
|
|
122
182
|
throw new AppError("not_found", "RUN is not registered", 1, { run_id: input.runId });
|
|
123
183
|
if (!run.run_root)
|
|
124
184
|
throw new AppError("runtime_artifact_missing", "RUN artifact root is unavailable", 1, { run_id: input.runId });
|
|
125
|
-
const
|
|
126
|
-
|
|
185
|
+
const index = JSON.parse(run.index_json);
|
|
186
|
+
const bundle = index.execution_bundle ? readFlowRunExecutionBundle(context, { projectRoot, runId: input.runId }) : null;
|
|
187
|
+
const stageRoot = path.join(run.run_root, bundle ? bundle.descriptor.stage(input.stage).directory : vnextStageDirectory(input.stage));
|
|
188
|
+
let descriptor = memoryStageFanoutDescriptor(context, input) ?? readFanoutDescriptor(stageRoot);
|
|
127
189
|
// Registration is authoritative even if projection publication was interrupted,
|
|
128
190
|
// or an otherwise review-off stage acquired an aggregate repair graph.
|
|
129
191
|
if (!descriptor && context.db.get("SELECT 1 FROM sqlite_master WHERE name = 'repair_intents'")) {
|
|
@@ -201,7 +263,7 @@ export async function reconcileVnextFanout(context, input) {
|
|
|
201
263
|
continue;
|
|
202
264
|
}
|
|
203
265
|
const link = active[0];
|
|
204
|
-
if (value.harness_id
|
|
266
|
+
if (renderNativeChildFollowupInstruction(String(value.harness_id), String(child.session_id)) && eligibleProviderChildOverload(child, value.parent_session_id)) {
|
|
205
267
|
issues.push({ code: "child_capacity_pending", work_id: link.work_id, session_id: child.session_id });
|
|
206
268
|
continue;
|
|
207
269
|
}
|
|
@@ -232,6 +232,7 @@ export function applyVnextMerge(context, input) {
|
|
|
232
232
|
throw new AppError("merge_apply_receipt_invalid", "Existing merge apply receipt does not match the active request", 1, { receipt_path: receiptFile });
|
|
233
233
|
return receipt;
|
|
234
234
|
}
|
|
235
|
+
requireBootstrapCommand(requireRun(context, request.project_id, request.run_id));
|
|
235
236
|
heartbeatLaneLock(context, { projectRoot: input.projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, ttlSeconds: 7200 });
|
|
236
237
|
assertExecutionBaseline(request);
|
|
237
238
|
input.progress?.(`applying ${request.source_commit} to ${request.target_branch}`);
|
|
@@ -316,7 +317,7 @@ export async function finishVnextMerge(context, input, prepared = prepareVnextMe
|
|
|
316
317
|
if (failed.length) {
|
|
317
318
|
context.db.run("UPDATE merge_requests SET status = 'action_required', last_error_json = ?, updated_at = ? WHERE merge_request_id = ?", [JSON.stringify({ code: "merge_gate_failed", failures: failed }), context.now(), request.merge_request_id]);
|
|
318
319
|
const sourceRepair = failed.every(receipt => checkRepairDisposition(receipt) === "source_repair");
|
|
319
|
-
throw new AppError("merge_gate_failed", "Integrated target checks failed. Only
|
|
320
|
+
throw new AppError("merge_gate_failed", "Integrated target checks failed. Only candidate-source-backed failures permit source repair; tooling or unknown evidence requires diagnosis/settlement first.", 2, { failures: failed, ...(sourceRepair ? { repair_command: repairCommand(context, request, projectRoot), continuation: { ...recoveryEvidenceInput(run.run_root, failed[0].id), command_after_input: finishCommand(context, run.id, request, projectRoot, failed[0].id) } } : { outcome: "blocked", recoverable: false }) });
|
|
320
321
|
}
|
|
321
322
|
const passedRefs = new Set(receipts.flatMap((receipt) => receipt.check_refs));
|
|
322
323
|
const missingRefs = frozenGateRefs(gate).filter((ref) => !passedRefs.has(ref));
|
|
@@ -567,10 +568,14 @@ function protocolIds(home) { const root = path.join(home, "03-plan"); if (!fs.ex
|
|
|
567
568
|
function workspaceRoute(home) { const file = path.join(home, "02-protocolize", "workspace-route.json"); const value = JSON.parse(fs.readFileSync(file, "utf8")); if (!value.policy?.integration_branch)
|
|
568
569
|
throw new AppError("workspace_route_missing", "MERGE requires the frozen integration branch", 1, { file }); return { integration_branch: value.policy.integration_branch }; }
|
|
569
570
|
function executionSettings(run) { return JSON.parse(run.index_json).execution_profile?.settings ?? {}; }
|
|
570
|
-
|
|
571
|
+
function requireBootstrapCommand(run) {
|
|
571
572
|
const command = executionSettings(run).code_bootstrap?.command;
|
|
572
573
|
if (!command)
|
|
573
|
-
throw new AppError("execution_profile_invalid", "MERGE requires
|
|
574
|
+
throw new AppError("execution_profile_invalid", "MERGE requires an evidenced bootstrap command in the frozen RUN profile; configure code_bootstrap before starting a code-ready RUN", 2);
|
|
575
|
+
return command;
|
|
576
|
+
}
|
|
577
|
+
async function runBootstrap(context, run, cwd, mergeId, progress) {
|
|
578
|
+
const command = requireBootstrapCommand(run);
|
|
574
579
|
const result = await runWorkspaceBootstrap(context, { projectId: run.project_id, runId: run.id, runHome: requireHome(run), workspaceRoot: cwd, command, artifactDir: `${stageDir}/${mergeId}/readiness`, progress });
|
|
575
580
|
if (result.exit_code !== 0 || result.error)
|
|
576
581
|
throw new AppError("merge_bootstrap_failed", "Integrated target bootstrap failed; inspect its retained logs", 2, { command, ...result });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { managedLifecycleCommand } from "./lifecycle-invocations.js";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import { withStageSettlement, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion } from "./runs.js";
|
|
3
|
+
import { withStageSettlement, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, readFlowRunExecutionBundle } from "./runs.js";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { AppError } from "../shared/errors.js";
|
|
@@ -50,7 +50,7 @@ export function prepareVnextPlanReviewStart(context, input) {
|
|
|
50
50
|
return { ...common, state: "terminal", report, outcome };
|
|
51
51
|
throw new AppError("invalid_work_state", "PLAN-REVIEW is already terminal; corrections are applied in the same review Work and are not re-reviewed automatically", 2, { run_id: run.id, outcome });
|
|
52
52
|
}
|
|
53
|
-
const workspaceRoute = requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan-review" });
|
|
53
|
+
const workspaceRoute = requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan-review" });
|
|
54
54
|
if (!fs.existsSync(path.join(planRoot, "stage-report.json")) || !fs.existsSync(batch))
|
|
55
55
|
throw new AppError("runtime_artifact_missing", "PLAN-REVIEW requires the accepted PLAN report and proposed CODE batch", 1);
|
|
56
56
|
const requested = requestedMode(run);
|
|
@@ -132,6 +132,7 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
|
|
|
132
132
|
commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
|
|
133
133
|
const reportGit = gitFacts(run.workspace_root);
|
|
134
134
|
const next = codeSuccessor(context, run.id, projectRoot);
|
|
135
|
+
const completionBundle = readFlowRunExecutionBundle(context, { projectRoot, runId: run.id });
|
|
135
136
|
return withStageSettlement(context, () => {
|
|
136
137
|
const registered = addWorkBatch(context, { parentWorkId: rootWork, prepared: prepared.codeItems, stage: "code" });
|
|
137
138
|
const report = reportFor(context, { run, mode: effective, requested, outcome: "off", groups: [], batchChecksum, planChecksum, code: registered, now, projectRoot, flow: flowCommand(context), git: reportGit, ...(next.command ? { nextCommand: next.command } : {}) });
|
|
@@ -141,7 +142,7 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
|
|
|
141
142
|
const preparedReport = prepareStageReport(report);
|
|
142
143
|
acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
|
|
143
144
|
const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
|
|
144
|
-
completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput));
|
|
145
|
+
completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput, completionBundle));
|
|
145
146
|
advanceFlowRun(context, { settlementStage: "plan-review", projectRoot, runId: run.id, status: "running", verdict: "plan_review_off", nextAction: "start_code" });
|
|
146
147
|
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_opened", mode: effective, outcome: "off" });
|
|
147
148
|
return { ok: true, run_id: run.id, stage, outcome: "review_off", ...(externalContext ? { external_context: externalContext } : {}), plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), reason: "No applicable independent review group." }, next };
|
|
@@ -209,7 +210,7 @@ export function dispatchVnextPlanReview(context, input) {
|
|
|
209
210
|
}
|
|
210
211
|
const created = context.db.all("SELECT work_id, task, status, launch_policy, created_at FROM works WHERE project_id = ? AND run_id = ? AND parent_work_id = ?", [project.id, run.id, parent.work_id]);
|
|
211
212
|
const reviews = groups.map((group) => { const work = created.find((item) => item.task === group.task); if (!work)
|
|
212
|
-
throw new AppError("runtime_missing", "Reviewer Work was not created", 1, { group: group.key }); const ready = work.status === "created" && isReviewWorkReady(context, work.work_id); return { group: group.key, work_id: work.work_id, status: work.status, depends_on: group.depends_on, ready, ...(ready ? { start_command: workStartCommand(context, { ...work, project_id: project.id, run_id: run.id }) } : {}) }; });
|
|
213
|
+
throw new AppError("runtime_missing", "Reviewer Work was not created", 1, { group: group.key }); const ready = work.status === "created" && isReviewWorkReady(context, work.work_id); return { group: group.key, work_id: work.work_id, status: work.status, depends_on: group.depends_on, ready, ...(ready ? { start_command: workStartCommand(context, { ...work, project_id: project.id, run_id: run.id }, { preview: true }) } : {}) }; });
|
|
213
214
|
refreshRunWorkProjection(context, project.id, run.id);
|
|
214
215
|
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_dispatched", work_id: parent.work_id, groups: reviews.map((item) => ({ group: item.group, work_id: item.work_id })) });
|
|
215
216
|
const waves = plannedReviewWaves(groups, capacity.availableSlots);
|
|
@@ -329,6 +330,7 @@ function finishPlanReviewOwned(context, input) {
|
|
|
329
330
|
const reportGit = gitFacts(run.workspace_root);
|
|
330
331
|
const next = codeSuccessor(context, run.id, projectRoot);
|
|
331
332
|
const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", semanticFile: decisionPath });
|
|
333
|
+
const completionBundle = readFlowRunExecutionBundle(context, { projectRoot, runId: run.id });
|
|
332
334
|
return withStageSettlement(context, () => {
|
|
333
335
|
const registered = addWorkBatch(context, { parentWorkId: codeParentId, prepared: codeItems, stage: "code" });
|
|
334
336
|
const now = context.now();
|
|
@@ -343,7 +345,7 @@ function finishPlanReviewOwned(context, input) {
|
|
|
343
345
|
acceptStagePacket(context, { projectId: project.id, runId: run.id, stage, prepared: decisionReceipt });
|
|
344
346
|
acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
|
|
345
347
|
const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
|
|
346
|
-
completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput));
|
|
348
|
+
completeFlowRunStage(context, completionInput, prepareFlowRunStageCompletion(context, completionInput, completionBundle));
|
|
347
349
|
refreshRunWorkProjection(context, project.id, run.id);
|
|
348
350
|
advanceFlowRun(context, { settlementStage: "plan-review", projectRoot, runId: run.id, status: "running", verdict: "plan_review_accepted", nextAction: "start_code" });
|
|
349
351
|
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "plan_review_accepted", work_id: parent.work_id, code: registered });
|
|
@@ -33,7 +33,7 @@ export function prepareVnextPlanStart(context, input) {
|
|
|
33
33
|
const project = requireProjectByRoot(context, projectRoot);
|
|
34
34
|
const run = requireRun(context, projectRoot, input.runId);
|
|
35
35
|
const home = requireHome(run);
|
|
36
|
-
const workspaceRoute = requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
36
|
+
const workspaceRoute = requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
37
37
|
const protocols = protocolIds(home);
|
|
38
38
|
if (!protocols.length)
|
|
39
39
|
throw new AppError("runtime_artifact_missing", "PLAN requires accepted PROTOCOLIZE protocols", 1);
|
|
@@ -136,7 +136,7 @@ export function startVnextPlan(context, input, prepared = prepareVnextPlanStart(
|
|
|
136
136
|
export function prepareVnextPlanFinish(context, input) {
|
|
137
137
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
138
138
|
const run = requireRun(context, projectRoot, input.runId), home = requireHome(run);
|
|
139
|
-
requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
139
|
+
requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
140
140
|
const protocols = protocolIds(home);
|
|
141
141
|
assertProtocolWorkspace(run.workspace_root, protocols);
|
|
142
142
|
const prepared = prepareVnextPlanArtifacts(context, { projectRoot, workspaceRoot: run.workspace_root, runId: run.id, home, protocols });
|
|
@@ -153,7 +153,7 @@ function finishVnextPlanOwned(context, input) {
|
|
|
153
153
|
const run = requireRun(context, projectRoot, input.runId);
|
|
154
154
|
const home = requireHome(run);
|
|
155
155
|
const root = path.join(home, "03-plan");
|
|
156
|
-
requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
156
|
+
requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, stage: "plan" });
|
|
157
157
|
const protocols = protocolIds(home);
|
|
158
158
|
if (!protocols.length)
|
|
159
159
|
throw new AppError("runtime_artifact_missing", "PLAN requires accepted PROTOCOLIZE protocols", 1);
|
|
@@ -10,7 +10,7 @@ import { projectFeatureWorktreePath, resolveProjectRoot } from "../storage/paths
|
|
|
10
10
|
import { previewNextEntityId } from "./ids.js";
|
|
11
11
|
import { registerProtocol } from "./protocols.js";
|
|
12
12
|
import { requireProjectByRoot } from "./projects.js";
|
|
13
|
-
import { advanceFlowRun, appendFlowRunTimelineEvent, attachFlowRunStage, completeFlowRunStage, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, withStageSettlement, gitFacts, rebindFlowRunWorkspace } from "./runs.js";
|
|
13
|
+
import { advanceFlowRun, appendFlowRunTimelineEvent, attachFlowRunStage, completeFlowRunStage, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, withStageSettlement, gitFacts, rebindFlowRunWorkspace, publishRunProjections } from "./runs.js";
|
|
14
14
|
import { resolveStageTransition, stageExecutionCwd } from "./execution-policy.js";
|
|
15
15
|
import { validateSchema } from "./schema-validation.js";
|
|
16
16
|
import { assertStageStartHookEvent, hookSessionIdentity } from "./hooks.js";
|
|
@@ -21,6 +21,8 @@ import { loadVnextWorkspacePolicy, requireVnextWorkspaceRoute, vnextRunBranch }
|
|
|
21
21
|
import { readVnextSpecifyResult } from "./vnext-specify.js";
|
|
22
22
|
import { flowCommand, stagePauseCommand, stagePauseCommandTemplate } from "./stage-pause.js";
|
|
23
23
|
import { acceptStageReport, prepareStageReport, publishStageReport } from "./stage-report-renderer.js";
|
|
24
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
25
|
+
import { assertRunMutationAllowed } from "./run-recovery.js";
|
|
24
26
|
import { currentStagePrompt, applyExternalStageContext, prepareExternalStageContext, renderRunOperationalContext } from "./stage-context.js";
|
|
25
27
|
import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.js";
|
|
26
28
|
import { stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
|
|
@@ -202,7 +204,7 @@ export async function finishVnextProtocolize(context, input) {
|
|
|
202
204
|
throw new AppError("protocolize_intent_conflict", "Completed PROTOCOLIZE result differs from accepted input", 1, { run_id: run.id });
|
|
203
205
|
const currentStage = acceptedIndex.current_stage ?? [...acceptedIndex.stage_runs].reverse().find(item => item.status === "running" || item.status === "done")?.stage;
|
|
204
206
|
const activeReplay = currentStage === stage && acceptedIndex.status === "running";
|
|
205
|
-
const resolvedPolicy = activeReplay ? requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: requireRunHome(run), workspaceRoot: run.workspace_root, stage: "protocolize" }) : accepted.mechanical?.workspace_route;
|
|
207
|
+
const resolvedPolicy = activeReplay ? requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: requireRunHome(run), workspaceRoot: run.workspace_root, stage: "protocolize" }) : accepted.mechanical?.workspace_route;
|
|
206
208
|
if (activeReplay) {
|
|
207
209
|
if (acceptedIndex.verdict !== "protocolized")
|
|
208
210
|
advanceFlowRun(context, { settlementStage: "protocolize", projectRoot, runId: run.id, status: "running", verdict: "protocolized", nextAction: "start_plan" });
|
|
@@ -212,7 +214,7 @@ export async function finishVnextProtocolize(context, input) {
|
|
|
212
214
|
return { ok: true, outcome: "protocolized", run_id: run.id, protocol_ids: accepted.semantic.acceptance, workspace_root: run.workspace_root, git_policy: resolvedPolicy, next_action: activeReplay ? "await_plan" : acceptedIndex.next_action, ...("command" in next && next.command ? { next_command: next.command } : {}), next,
|
|
213
215
|
artifacts: { result: resultFile, report_json: file, report_markdown: path.join(root, "stage-report.md"), report_html: path.join(root, "stage-report.html"), protocols: accepted.semantic.acceptance.map(id => path.join(run.workspace_root, ".memory-bank/protocol", id, "summary.md")) } };
|
|
214
216
|
}
|
|
215
|
-
const resolvedPolicy = requireVnextWorkspaceRoute({ projectRoot, runId: run.id, runHome: requireRunHome(run), workspaceRoot: run.workspace_root, stage: "protocolize" });
|
|
217
|
+
const resolvedPolicy = requireVnextWorkspaceRoute(context, { projectRoot, runId: run.id, runHome: requireRunHome(run), workspaceRoot: run.workspace_root, stage: "protocolize" });
|
|
216
218
|
const acceptedObligations = acceptedSpecifyObligations(context, projectRoot, run.id, path.join(requireRunHome(run), "01-specify", "specify.json"));
|
|
217
219
|
let obligations;
|
|
218
220
|
try {
|
|
@@ -699,6 +701,11 @@ function renderPrompt(input) {
|
|
|
699
701
|
return ["<work_context>", `- RUN: ${input.runId}`, `- Work: ${input.workId}`, `- Accepted SPECIFY JSON: ${input.specifyPath}`, `- Handoff: ${input.handoff.stage_handoff.effective}`, "</work_context>", "", "<accepted_obligations>", obligationList, "</accepted_obligations>", "", "<frozen_git_policy>", `- route: ${input.policy.route}`, `- integration branch: ${input.policy.integration_branch ?? "not applicable"}`, `- base ref: ${input.policy.base_ref ?? "not applicable"}`, `- feature branch: ${input.policy.feature_branch ?? "not applicable"}`, `- service worktree: ${input.policy.worktree_path ?? "not applicable"}`, `- bootstrap: ${input.policy.bootstrap_status}`, `- decision: ${input.policy.reason}`, "The CLI already created and bootstrapped this workspace at PROTOCOLIZE start. Do not create branches, worktrees, protocol files or Git commits yourself. This transition Work may run from the stable session; edit only the supplied RUN result file.", "</frozen_git_policy>", "", "<catalog>", input.catalog.length ? input.catalog.map((x) => `- ${x}`).join("\n") : "- inactive", "</catalog>", "", "<stage_instructions>", input.template.trim(), "</stage_instructions>", "", "<output_contract>", `Edit only ${input.resultPath}. Keep this exact JSON shape; CLI allocates ids and writes PRT/PSET/feature documents in the already provisioned workspace:\n\n\`\`\`json\n${JSON.stringify(resultTemplate(input.obligations), null, 2)}\n\`\`\``, "obligation_id values and their one-per-obligation entries are prefilled by the CLI. Preserve those IDs exactly; change only member_keys when semantic ownership requires it. The CLI rejects unknown, duplicate or missing obligations, and every member must own at least one AC-*.", "If PROTOCOLIZE itself exposes a material user decision with no reasonable default, do not finish and do not return to SPECIFY. Write the question packet to the named text file, then run the separate standalone lifecycle command below. Do not combine file creation and dd-flow in one shell command:", "```sh", input.pauseCommandTemplate, "```", "Ask the returned user_message and stop. Resume this same PROTOCOLIZE Work using the exact command returned by pause.", `When the answer is incorporated and the result is protocolized, finish:\n\n${input.finishCommand}`, stageLifecycleInstruction, "</output_contract>", ""].join("\n");
|
|
700
702
|
}
|
|
701
703
|
function prepareWorkspaceForProtocolize(context, projectRoot, projectId, run, stageRoot) {
|
|
704
|
+
// A committed rebind may have survived a projection crash. Repair only from
|
|
705
|
+
// its SQL authority before considering physical creation again.
|
|
706
|
+
const stored = context.db.get("SELECT index_json FROM runs WHERE project_id = ? AND id = ?", [projectId, run.id]);
|
|
707
|
+
if (stored && JSON.parse(stored.index_json).workspace_route)
|
|
708
|
+
publishRunProjections(context, projectId, run.id);
|
|
702
709
|
const receiptPath = path.join(stageRoot, "workspace-route.json");
|
|
703
710
|
if (fs.existsSync(receiptPath))
|
|
704
711
|
return { kind: "retained", policy: readWorkspaceRouteReceipt(stageRoot).policy };
|
|
@@ -727,7 +734,9 @@ function prepareWorkspaceForProtocolize(context, projectRoot, projectId, run, st
|
|
|
727
734
|
throw error;
|
|
728
735
|
}
|
|
729
736
|
prepareManagedWorktreeBootstrapSources(projectRoot);
|
|
730
|
-
return { kind: "feature", receiptPath, config, stableFacts, sessionMode, branch, create
|
|
737
|
+
return { kind: "feature", receiptPath, config, stableFacts, sessionMode, branch, create,
|
|
738
|
+
expectedRevision: stored ? JSON.parse(stored.index_json).runtime_revision ?? 0 : 0,
|
|
739
|
+
expectedGeneration: assertRunMutationAllowed(context, projectId, run.id) };
|
|
731
740
|
}
|
|
732
741
|
async function provisionWorkspaceForProtocolize(context, projectRoot, run, prepared) {
|
|
733
742
|
if (prepared.kind === "retained")
|
|
@@ -740,9 +749,14 @@ async function provisionWorkspaceForProtocolize(context, projectRoot, run, prepa
|
|
|
740
749
|
const { config, stableFacts, sessionMode, branch, create } = prepared;
|
|
741
750
|
const created = await createManagedWorktree(context, create, create);
|
|
742
751
|
const bootstrap = await bootstrapManagedWorktree(context, { projectRoot, worktreePath: created.worktreePath });
|
|
743
|
-
rebindFlowRunWorkspace(context, { projectRoot, runId: run.id, workspaceRoot: created.worktreePath, reason: "PROTOCOLIZE start provisioned the configured feature-worktree route" });
|
|
744
752
|
const policy = { route: "feature_worktree", integration_branch: config.workspace.integration_branch, base_ref: stableFacts.head, feature_branch: branch, worktree_path: created.worktreePath, reason: "Project workspace configuration requires a managed feature worktree before durable protocol documents exist.", provision_stage: "protocolize_start", stage_session_mode: sessionMode, provisioned: true, bootstrap_status: bootstrap.status };
|
|
745
|
-
|
|
753
|
+
const receipt = { schema_id: "dd-flow/vnext-workspace-route@1", policy,
|
|
754
|
+
created: { worktreePath: created.worktreePath, worktrunk: created.worktrunk, command: [], result: { exit_code: 0, stdout: "", stderr: "" }, response: { workspace_path: created.worktreePath } },
|
|
755
|
+
bootstrap: { status: bootstrap.status, commands: [] },
|
|
756
|
+
proof: { creation_sha256: hybridDigest(created), bootstrap_sha256: hybridDigest(bootstrap) } };
|
|
757
|
+
rebindFlowRunWorkspace(context, { projectRoot, runId: run.id, workspaceRoot: created.worktreePath, routeReceipt: receipt,
|
|
758
|
+
expectedRevision: prepared.expectedRevision, expectedGeneration: prepared.expectedGeneration,
|
|
759
|
+
reason: "PROTOCOLIZE start provisioned the configured feature-worktree route" });
|
|
746
760
|
return policy;
|
|
747
761
|
}
|
|
748
762
|
function readWorkspaceRouteReceipt(root) {
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { AppError } from "../shared/errors.js";
|
|
5
|
-
import { gitFacts } from "./runs.js";
|
|
5
|
+
import { gitFacts, publishRunProjections } from "./runs.js";
|
|
6
6
|
const configRelativePath = path.join(".memory-bank", "dd-flow", "project-workspace.json");
|
|
7
7
|
/** The runtime reads explicit configuration; it never attempts to infer route from policy prose. */
|
|
8
8
|
export function loadVnextWorkspacePolicy(projectRoot) {
|
|
@@ -53,8 +53,13 @@ export function vnextRunBranch(policy, run) {
|
|
|
53
53
|
* The route receipt is the single source of truth after PROTOCOLIZE provisions
|
|
54
54
|
* the workspace. Every later mutating vNext stage calls this before it opens.
|
|
55
55
|
*/
|
|
56
|
-
export function requireVnextWorkspaceRoute(input) {
|
|
56
|
+
export function requireVnextWorkspaceRoute(context, input) {
|
|
57
57
|
const file = path.join(input.runHome, "02-protocolize", "workspace-route.json");
|
|
58
|
+
const stored = context.db.get("SELECT project_id,index_json FROM runs WHERE project_root = ? AND id = ?", [input.projectRoot, input.runId]);
|
|
59
|
+
// Productive lifecycle readback repairs only SQL-committed projections. The
|
|
60
|
+
// status command never calls this path and cannot mutate historical files.
|
|
61
|
+
if (stored && JSON.parse(stored.index_json).workspace_route)
|
|
62
|
+
publishRunProjections(context, stored.project_id, input.runId);
|
|
58
63
|
let receipt;
|
|
59
64
|
try {
|
|
60
65
|
receipt = JSON.parse(fs.readFileSync(file, "utf8"));
|