@deksden-com/dd-flow-cli 0.9.0-beta.152 → 0.9.0-beta.156
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 +31 -0
- package/dist/build-info.json +8 -8
- package/dist/cli/command-inputs.js +33 -3
- package/dist/cli/command-metadata.js +73 -2
- package/dist/cli/delivery.js +108 -0
- package/dist/cli/diagnostic-invocation.js +23 -7
- package/dist/cli/diagnostic.js +14 -3
- package/dist/cli/help.js +117 -1
- package/dist/cli/hook-ingress.js +36 -49
- package/dist/cli/interactive-invocation.js +33 -0
- package/dist/cli/interactive.js +114 -0
- package/dist/cli/memory-fix-invocation.js +52 -0
- package/dist/cli/memory-fix.js +100 -0
- package/dist/cli/memory-flow-admission.js +7 -3
- package/dist/cli/memory-init.js +5 -4
- package/dist/cli/native-hook-ingress.js +5 -1
- package/dist/cli/native-hook-preparation.js +31 -4
- package/dist/cli/run-cli.js +274 -19
- package/dist/cli.js +17 -3
- package/dist/domain/delivery-flows.js +8 -0
- package/dist/domain/distill-flow.js +20 -0
- package/dist/domain/interactive-flow.js +1 -0
- package/dist/domain/memory-fix-flow.js +2 -0
- package/dist/domain/review-fix-flow.js +2 -0
- package/dist/domain/source-snapshot-flows.js +4 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +18 -3
- package/dist/harness-runtime/bin/dd-grok.mjs +15 -5
- package/dist/harness-runtime/bin/dd-zcode.mjs +15 -7
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +18 -10
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +12 -3
- package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +13 -6
- package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +12 -4
- package/dist/harness-runtime/lib/daemon-operations.mjs +20 -5
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +371 -114
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +43 -3
- package/dist/harness-runtime/lib/dd-codex.mjs +36 -4
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +25 -11
- package/dist/harness-runtime/lib/dd-droid.mjs +124 -11
- package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +137 -25
- package/dist/harness-runtime/lib/dd-grok.mjs +4 -2
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +115 -17
- package/dist/harness-runtime/lib/dd-opencode.mjs +10 -7
- package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +115 -22
- package/dist/harness-runtime/lib/dd-zcode.mjs +75 -36
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +2 -1
- package/dist/harness-runtime/lib/delegation-instructions.mjs +15 -5
- package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +0 -19
- package/dist/harness-runtime/lib/droid-observation.mjs +9 -5
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +1 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +2 -1
- package/dist/harness-runtime/lib/native-hook-command.d.mts +3 -2
- package/dist/harness-runtime/lib/native-hook-command.mjs +63 -8
- package/dist/harness-runtime/lib/native-streams.mjs +3 -10
- package/dist/harness-runtime/lib/opencode-plugin.mjs +36 -9
- package/dist/harness-runtime/lib/operation-errors.mjs +13 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +6 -2
- package/dist/harness-runtime/lib/process-json.mjs +4 -5
- package/dist/harness-runtime/lib/tool-observations.mjs +20 -10
- package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
- package/dist/schemas/code-work-result.schema.json +2 -1
- package/dist/schemas/delivery-stage-result.schema.json +286 -0
- package/dist/schemas/distill-stage-result.schema.json +667 -0
- package/dist/schemas/distill-work-result.schema.json +428 -0
- package/dist/schemas/execution-bundle.schema.json +6 -2
- package/dist/schemas/interactive-stage-result.schema.json +47 -0
- package/dist/schemas/interactive-work-result.schema.json +63 -0
- package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
- package/dist/schemas/review-fix-stage-result.schema.json +210 -0
- package/dist/schemas/run-control-receipt.schema.json +1 -0
- package/dist/services/assigned-lifecycle.js +382 -0
- package/dist/services/check-runner-evidence.js +45 -8
- package/dist/services/cli-operation-classifier.js +6 -1
- package/dist/services/code-checks.js +46 -29
- package/dist/services/codex-hook-delivery.js +11 -2
- package/dist/services/controller-child-repair.js +95 -0
- package/dist/services/controller-execution.js +4 -1
- package/dist/services/controller-fanout.js +19 -9
- package/dist/services/controller-memory-worker.js +6 -4
- package/dist/services/controller-result-authority.js +122 -0
- package/dist/services/dashboard.js +4 -2
- package/dist/services/delivery-access.js +123 -0
- package/dist/services/delivery-approval.js +68 -0
- package/dist/services/delivery-artifact.js +86 -0
- package/dist/services/delivery-checks.js +178 -0
- package/dist/services/delivery-execution.js +181 -0
- package/dist/services/delivery-intents.js +212 -0
- package/dist/services/delivery-operation.js +81 -0
- package/dist/services/delivery-policy.js +91 -0
- package/dist/services/delivery-procedures.js +43 -0
- package/dist/services/delivery-request.js +99 -0
- package/dist/services/delivery-result.js +144 -0
- package/dist/services/delivery-stage-commands.js +26 -0
- package/dist/services/delivery-stage-entry.js +56 -0
- package/dist/services/delivery-stage-handlers.js +135 -0
- package/dist/services/delivery-stage-results.js +94 -0
- package/dist/services/descriptor-coordinator-session.js +3 -1
- package/dist/services/descriptor-run-start.js +24 -4
- package/dist/services/descriptor-stage-coordinator.js +78 -10
- package/dist/services/descriptor-stage-settlement.js +59 -14
- package/dist/services/diagnostic-repair-intake.js +123 -0
- package/dist/services/diagnostic-repair-source-intake.js +102 -0
- package/dist/services/diagnostic-report.js +30 -0
- package/dist/services/diagnostic-run-bundle.js +48 -9
- package/dist/services/diagnostic-snapshot.js +2 -2
- package/dist/services/diagnostic-stage-handlers.js +3 -2
- package/dist/services/diagnostic-stage-results.js +26 -0
- package/dist/services/diagnostic-start.js +56 -6
- package/dist/services/diagnostic-work-collection.js +3 -3
- package/dist/services/distill-document-publication.js +280 -0
- package/dist/services/distill-promotion.js +197 -0
- package/dist/services/distill-publication-custody.js +162 -0
- package/dist/services/distill-stage-handlers.js +249 -0
- package/dist/services/distill-stage-results.js +105 -0
- package/dist/services/distill-user-inputs.js +4 -0
- package/dist/services/distill-work-batch.js +42 -0
- package/dist/services/distill-work-collection.js +74 -0
- package/dist/services/distill-work-registration.js +93 -0
- package/dist/services/distill-work-results.js +98 -0
- package/dist/services/eval-snapshots.js +17 -7
- package/dist/services/external-work-launch.js +8 -2
- package/dist/services/flow-catalog.js +24 -0
- package/dist/services/frozen-engine.js +19 -2
- package/dist/services/generic-execution.js +4 -3
- package/dist/services/harness-adapter.js +2 -14
- package/dist/services/hook-memory-routing.js +1 -1
- package/dist/services/hooks.js +31 -6
- package/dist/services/hybrid-contracts.js +6 -1
- package/dist/services/installed-flow-source.js +37 -2
- package/dist/services/interactive-attached-session.js +215 -0
- package/dist/services/interactive-baseline-custody.js +54 -0
- package/dist/services/interactive-before-versions.js +148 -0
- package/dist/services/interactive-bundle-data.js +85 -0
- package/dist/services/interactive-checks.js +179 -0
- package/dist/services/interactive-execution.js +33 -0
- package/dist/services/interactive-finish-integration.js +236 -0
- package/dist/services/interactive-git-baseline.js +230 -0
- package/dist/services/interactive-git-dispatch.js +278 -0
- package/dist/services/interactive-input-delivery.js +82 -0
- package/dist/services/interactive-mutation-scope.js +231 -0
- package/dist/services/interactive-request.js +287 -0
- package/dist/services/interactive-run-bundle.js +109 -0
- package/dist/services/interactive-stage-entry.js +41 -0
- package/dist/services/interactive-stage-handlers.js +289 -0
- package/dist/services/interactive-stage-observation.js +136 -0
- package/dist/services/interactive-stage-results.js +111 -0
- package/dist/services/interactive-user-inputs.js +142 -0
- package/dist/services/interactive-work-batch.js +43 -0
- package/dist/services/interactive-work-collection.js +119 -0
- package/dist/services/interactive-work-completion.js +180 -0
- package/dist/services/interactive-work-context.js +249 -0
- package/dist/services/interactive-work-history.js +112 -0
- package/dist/services/interactive-work-registration.js +124 -0
- package/dist/services/interactive-work-results.js +115 -0
- package/dist/services/interactive-work-start.js +155 -0
- package/dist/services/lifecycle-invocations.js +250 -33
- package/dist/services/managed-daemon-binding.js +52 -1
- package/dist/services/memory-compose-handler.js +2 -22
- package/dist/services/memory-finish-integration.js +58 -17
- package/dist/services/memory-fix-finish-integration.js +129 -0
- package/dist/services/memory-fix-git-dispatch.js +139 -0
- package/dist/services/memory-fix-intake-custody.js +11 -0
- package/dist/services/memory-fix-intake-settlement.js +127 -0
- package/dist/services/memory-fix-publication.js +196 -0
- package/dist/services/memory-fix-repair-admission.js +116 -0
- package/dist/services/memory-fix-request.js +141 -0
- package/dist/services/memory-fix-run-bundle.js +83 -0
- package/dist/services/memory-fix-scope.js +78 -0
- package/dist/services/memory-fix-sources.js +85 -0
- package/dist/services/memory-fix-stage-entry.js +24 -0
- package/dist/services/memory-fix-stage-handlers.js +282 -0
- package/dist/services/memory-fix-stage-results.js +106 -0
- package/dist/services/memory-fix-workspace.js +127 -0
- package/dist/services/memory-flow-user-inputs.js +88 -0
- package/dist/services/memory-init-contexts.js +55 -5
- package/dist/services/memory-init-controller-start.js +20 -4
- package/dist/services/memory-layer.js +7 -3
- package/dist/services/memory-native-review-copy.js +27 -3
- package/dist/services/memory-repair-document-delta.js +1 -0
- package/dist/services/project-flow-source.js +195 -0
- package/dist/services/project-git-integration.js +92 -0
- package/dist/services/repair-intents.js +2 -7
- package/dist/services/retained-native-hook.js +274 -0
- package/dist/services/review-fix-handoff.js +82 -0
- package/dist/services/review-fix-intake-custody.js +17 -0
- package/dist/services/review-fix-stage-entry.js +43 -0
- package/dist/services/review-fix-stage-handlers.js +176 -0
- package/dist/services/review-fix-stage-results.js +86 -0
- package/dist/services/run-control-worker.js +42 -1
- package/dist/services/run-control.js +40 -5
- package/dist/services/run-controller-adapter.js +37 -3
- package/dist/services/run-controller-process.js +25 -4
- package/dist/services/run-controller-recovery.js +46 -6
- package/dist/services/run-controller-state.js +3 -2
- package/dist/services/run-controller.js +257 -44
- package/dist/services/run-recovery.js +31 -2
- package/dist/services/run-timeline.js +59 -0
- package/dist/services/runs.js +18 -31
- package/dist/services/sessions.js +2 -1
- package/dist/services/stage-handlers.js +55 -3
- package/dist/services/stage-lifecycle.js +4 -3
- package/dist/services/stage-pause.js +26 -9
- package/dist/services/stage-report-renderer.js +2 -6
- package/dist/services/technical-notes.js +31 -0
- package/dist/services/verification-matrix.js +1 -1
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +74 -5
- package/dist/services/vnext-code.js +8 -32
- package/dist/services/vnext-fanout.js +53 -8
- package/dist/services/vnext-merge.js +10 -5
- package/dist/services/vnext-plan-review.js +1 -1
- package/dist/services/vnext-plan.js +1 -1
- package/dist/services/vnext-protocolize.js +1 -1
- package/dist/services/vnext-specify.js +10 -5
- package/dist/services/work-registry.js +255 -69
- package/dist/services/worktrees.js +7 -4
- package/dist/storage/database.js +2 -1
- package/dist/verification-components.json +82 -0
- package/package.json +3 -2
- package/tools/verification-jobs.mjs +35 -10
package/dist/cli/memory-init.js
CHANGED
|
@@ -13,7 +13,8 @@ import { PreparedMemoryInitLintCapability } from "../services/memory-init-lint-c
|
|
|
13
13
|
import { PreparedMemoryInitWorktrunkCapability } from "../services/memory-init-worktrunk-capability.js";
|
|
14
14
|
import { produceMemoryInitNativeCapacity } from "../services/memory-init-native-capacity.js";
|
|
15
15
|
import { openMemoryInitStores, assertMemoryInitStorePrerequisites } from "../services/memory-init-stores.js";
|
|
16
|
-
import { prepareMemoryInitControllerStart, prepareMemoryUpgradeControllerStart } from "../services/memory-init-controller-start.js";
|
|
16
|
+
import { prepareMemoryInitControllerStart, prepareMemoryUpgradeControllerStart, prepareInteractiveControllerStart, prepareDeliveryControllerStart } from "../services/memory-init-controller-start.js";
|
|
17
|
+
import { isDeliveryFlow } from "../domain/delivery-flows.js";
|
|
17
18
|
import { runControllerStatus } from "../services/run-controller.js";
|
|
18
19
|
import { requireRun } from "../services/run-controller-state.js";
|
|
19
20
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
@@ -28,8 +29,8 @@ import { onboardExecutor } from "../services/executor-onboarding.js";
|
|
|
28
29
|
import { readOperatorProfiles, readOperatorProfileInput } from "../services/operator-profiles.js";
|
|
29
30
|
import { resolveDdFlowHome } from "../storage/paths.js";
|
|
30
31
|
import { askExecutorChoice } from "./executor-onboarding.js";
|
|
31
|
-
import {
|
|
32
|
-
import { prepareDiagnosticControllerStart } from "../services/memory-init-controller-start.js";
|
|
32
|
+
import { isSourceSnapshotFlow } from "../domain/source-snapshot-flows.js";
|
|
33
|
+
import { prepareDiagnosticControllerStart, prepareMemoryFixControllerStart } from "../services/memory-init-controller-start.js";
|
|
33
34
|
/** Backpressure bounds the observing client's output; closing it never controls the RUN. */
|
|
34
35
|
export async function writeMemoryInitObservation(stdout, text) {
|
|
35
36
|
const disconnected = () => Boolean(stdout.destroyed);
|
|
@@ -186,7 +187,7 @@ export async function launchAndObserveMemoryFlow(context, invocation, started, f
|
|
|
186
187
|
await writeMemoryInitObservation(io.stdout, `${JSON.stringify(writeResponseFile(response, output), null, output.json || output.progressJsonl ? undefined : 2)}\n`);
|
|
187
188
|
return 0;
|
|
188
189
|
}
|
|
189
|
-
const launch =
|
|
190
|
+
const launch = isDeliveryFlow(flowId) ? prepareDeliveryControllerStart(context, invocation.scope, flowId) : flowId === "interactive" ? prepareInteractiveControllerStart(context, invocation.scope) : flowId === "mb-fix" ? prepareMemoryFixControllerStart(context, invocation.scope) : isSourceSnapshotFlow(flowId) ? prepareDiagnosticControllerStart(context, invocation.scope, flowId)
|
|
190
191
|
: (flowId === "mb-init" ? prepareMemoryInitControllerStart : prepareMemoryUpgradeControllerStart)(context, invocation.scope);
|
|
191
192
|
// Use the existing RUN-bound router: a global CLI must not launch its own
|
|
192
193
|
// controller code in place of the independently frozen RUN executable.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AppError, errorRecord } from "../shared/errors.js";
|
|
2
2
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
3
3
|
import { channel } from "node:diagnostics_channel";
|
|
4
|
-
import { nativeHookNonParticipation } from "./native-hook-preparation.js";
|
|
4
|
+
import { nativeHookAssignedSettlement, nativeHookNonParticipation } from "./native-hook-preparation.js";
|
|
5
5
|
/** These ingress commands consume a prepared store; they never prepare it. */
|
|
6
6
|
export function isNativeHookIngress(args) {
|
|
7
7
|
return args[2] === "handle" && ((args[1] === "event" && ["agy", "grok", "droid", "opencode", "zcode"].includes(args[0] ?? ""))
|
|
@@ -31,6 +31,10 @@ export async function dispatchNativeHook(args, stdin, env) {
|
|
|
31
31
|
phase("ack");
|
|
32
32
|
return unrelated;
|
|
33
33
|
}
|
|
34
|
+
if (nativeHookAssignedSettlement(args, stdin, env)) {
|
|
35
|
+
phase("ack");
|
|
36
|
+
return { ok: true, observed: false, reason: "cli_invocation_owns_admission" };
|
|
37
|
+
}
|
|
34
38
|
const [hooks, { createContext }, { resolveDdFlowHome }] = await Promise.all([
|
|
35
39
|
import("../services/hooks.js"), import("../runtime/context.js"), import("../storage/paths.js")
|
|
36
40
|
]);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { parseJsonObject } from "../shared/json.js";
|
|
3
|
-
import { continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
3
|
+
import { commandOption, continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
4
4
|
const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5
5
|
const text = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
6
6
|
/** Only a validated, ordinary before-tool command can skip the stateful handler.
|
|
7
7
|
* In particular after-tool, Stop, malformed and ambiguous events remain owned
|
|
8
8
|
* by their existing handler; this gate does not invent an admission policy. */
|
|
9
|
-
|
|
9
|
+
function beforeCommand(args, stdin) {
|
|
10
10
|
const payload = parseJsonObject(stdin || "{}", "native hook stdin");
|
|
11
11
|
let command;
|
|
12
12
|
if (args[0] === "zcode" && args[1] === "event") {
|
|
@@ -47,9 +47,36 @@ export function nativeHookNonParticipation(args, stdin) {
|
|
|
47
47
|
const input = object(payload.input);
|
|
48
48
|
command = text(input.command) ?? text(input.cmd) ?? text(input.CommandLine);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
return command ? { command, reason: args[0] === "zcode" && args[1] === "event" ? "acp_evidence_only" : "event_not_participating" } : null;
|
|
51
|
+
}
|
|
52
|
+
export function nativeHookNonParticipation(args, stdin) {
|
|
53
|
+
const before = beforeCommand(args, stdin);
|
|
54
|
+
if (!before)
|
|
51
55
|
return null;
|
|
56
|
+
const { command, reason } = before;
|
|
52
57
|
const parsed = parseLifecycleCommand(command);
|
|
53
58
|
return parsed.participation === "none" && !continuationCommand(command)
|
|
54
|
-
? { ok: true, observed: false, reason
|
|
59
|
+
? { ok: true, observed: false, reason } : null;
|
|
60
|
+
}
|
|
61
|
+
/** A supplied ID is not a grant. The executing CLI must prove its retained
|
|
62
|
+
* assignment; the synchronous native hook has no second admission role. */
|
|
63
|
+
export function assignedLifecycleHookCommand(command, env) {
|
|
64
|
+
if (!env.DD_FLOW_DAEMON_ID)
|
|
65
|
+
return false;
|
|
66
|
+
const parsed = parseLifecycleCommand(command);
|
|
67
|
+
return parsed.kind === "standalone" && Boolean(commandOption(parsed.invocation, "invocation-id"))
|
|
68
|
+
&& ["stage_start", "stage_finish", "stage_pause", "stage_resume", "work_finish", "work_fail"].includes(parsed.invocation.operation);
|
|
69
|
+
}
|
|
70
|
+
/** The native daemon has already retained this exact delivery; the CLI will
|
|
71
|
+
* verify and materialize it before admitting the issued Work invocation. */
|
|
72
|
+
export function retainedWorkStartHookCommand(command, binding) {
|
|
73
|
+
const retained = object(binding), parsed = parseLifecycleCommand(command);
|
|
74
|
+
return parsed.kind === "standalone" && parsed.invocation.operation === "work_start"
|
|
75
|
+
&& Boolean(commandOption(parsed.invocation, "invocation-id"))
|
|
76
|
+
&& retained.schema_id === "dd-flow/hook-request@1" && retained.source_harness === "codex-desktop"
|
|
77
|
+
&& retained.native_command === command && typeof retained.request_id === "string" && retained.request_id.length > 0;
|
|
78
|
+
}
|
|
79
|
+
export function nativeHookAssignedSettlement(args, stdin, env) {
|
|
80
|
+
const before = beforeCommand(args, stdin);
|
|
81
|
+
return Boolean(before && assignedLifecycleHookCommand(before.command, env));
|
|
55
82
|
}
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -3,13 +3,14 @@ import { prepareResponseDestination, writeResponseFile } from "./response-file.j
|
|
|
3
3
|
import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
|
|
4
4
|
import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
|
|
5
5
|
import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
|
|
6
|
-
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget } from "../services/lifecycle-invocations.js";
|
|
6
|
+
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget, currentAssignedLifecycle } from "../services/lifecycle-invocations.js";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { quote } from "shell-quote";
|
|
10
10
|
import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
|
|
11
11
|
import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
|
|
12
12
|
import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
|
|
13
|
+
import { readAcceptedAssignedWork } from "../services/assigned-lifecycle.js";
|
|
13
14
|
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
14
15
|
import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
|
|
15
16
|
import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
|
|
@@ -66,9 +67,43 @@ import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMer
|
|
|
66
67
|
import { sdlcStageHandlers } from "../services/stage-handlers.js";
|
|
67
68
|
import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
|
|
68
69
|
import { startDiagnosticStage, finishDiagnosticStage } from "../services/diagnostic-stage-handlers.js";
|
|
70
|
+
import { startDistillStage, finishDistillStage } from "../services/distill-stage-handlers.js";
|
|
71
|
+
import { startMemoryFixStage } from "../services/memory-fix-stage-entry.js";
|
|
72
|
+
import { finishMemoryFixStage } from "../services/memory-fix-stage-handlers.js";
|
|
73
|
+
import { startReviewFixStage } from "../services/review-fix-stage-entry.js";
|
|
74
|
+
import { finishReviewFixStage } from "../services/review-fix-stage-handlers.js";
|
|
75
|
+
import { startInteractiveStage } from "../services/interactive-stage-entry.js";
|
|
76
|
+
import { isDeliveryFlow } from "../domain/delivery-flows.js";
|
|
77
|
+
import { startDeliveryStage, readDeliveryStageScope, assertDeliveryCommandOwner } from "../services/delivery-stage-entry.js";
|
|
78
|
+
import { finishDeliveryStage } from "../services/delivery-stage-handlers.js";
|
|
79
|
+
import { prepareDeliveryChecks } from "../services/delivery-checks.js";
|
|
80
|
+
import { executeDeliveryOperation } from "../services/delivery-operation.js";
|
|
81
|
+
import { observeDeliveryResult } from "../services/delivery-result.js";
|
|
82
|
+
import { prepareDeliveryProjectBinding } from "../services/delivery-policy.js";
|
|
83
|
+
import { deliveryApprovalScope } from "../services/delivery-approval.js";
|
|
84
|
+
import { issueDeliveryStageCommands } from "../services/delivery-stage-commands.js";
|
|
85
|
+
import { readDescriptorStageInput } from "../services/descriptor-stage-coordinator.js";
|
|
86
|
+
import { readAcceptedStageData } from "../services/stage-report-renderer.js";
|
|
87
|
+
import { finishInteractiveStage, registerInteractiveReviewCorrection, registerInteractiveCorrectionReview } from "../services/interactive-stage-handlers.js";
|
|
88
|
+
import { observeInteractiveOriginalGitInputs } from "../services/interactive-finish-integration.js";
|
|
89
|
+
import { readInteractiveUserInputs } from "../services/interactive-user-inputs.js";
|
|
90
|
+
import { deliverInteractiveUserInput } from "../services/interactive-input-delivery.js";
|
|
91
|
+
import { PreparedInteractiveMutationScope } from "../services/interactive-mutation-scope.js";
|
|
92
|
+
import { PreparedInteractiveChecks } from "../services/interactive-checks.js";
|
|
93
|
+
import { registerInteractiveStageGraph } from "../services/interactive-work-registration.js";
|
|
94
|
+
import { readInteractiveWorkGit } from "../services/interactive-work-start.js";
|
|
95
|
+
import { integrateInteractiveFinish } from "../services/interactive-git-dispatch.js";
|
|
96
|
+
import { readMemoryFixSourceIntake } from "../services/memory-fix-intake-custody.js";
|
|
97
|
+
import { readMemoryFixUserInputs } from "../services/diagnostic-repair-intake.js";
|
|
98
|
+
import { prepareMemoryFixScopeEvidence } from "../services/memory-fix-scope.js";
|
|
99
|
+
import { prepareMemoryFixPublication } from "../services/memory-fix-publication.js";
|
|
100
|
+
import { integrateMemoryFixFinish } from "../services/memory-fix-git-dispatch.js";
|
|
69
101
|
import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
|
|
70
102
|
import { bindStageCoordinatorWork } from "../services/work-registry.js";
|
|
71
103
|
import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
|
|
104
|
+
import { publishDistillFromStage } from "../services/distill-promotion.js";
|
|
105
|
+
import { readDistillUserInputs } from "../services/distill-user-inputs.js";
|
|
106
|
+
import { hybridDigest } from "../services/hybrid-contracts.js";
|
|
72
107
|
import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
|
|
73
108
|
import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
|
|
74
109
|
import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
|
|
@@ -77,7 +112,7 @@ import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFrom
|
|
|
77
112
|
import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
|
|
78
113
|
import { registerMemoryRepairBaselineFromStage, registerMemorySecondRepairBaselineFromStage, reserveMemoryRepairWriterCopyFromStage, reserveMemorySecondRepairCopyFromStage, materializeMemoryRepairWriterCopyFromStage, materializeMemorySecondRepairCopyFromStage, prepareMemoryRepairDispatchFromStage, prepareMemorySecondRepairDispatchFromStage, registerMemoryRepairCurrentViewFromStage, registerMemorySecondRepairCurrentViewFromStage, registerMemoryRepairPublicationSourceFromStage, reserveMemoryRepairPublicationAgainstSourceFromStage, reserveMemorySecondRepairPublicationFromStage, applyMemoryRepairPublicationFromStage, applyMemorySecondRepairPublicationFromStage, finishMemoryRepairPublicationFromStage, finishMemorySecondRepairPublicationFromStage } from "../services/memory-review-handler.js";
|
|
79
114
|
import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
|
|
80
|
-
import { getVnextFanoutStatus,
|
|
115
|
+
import { getVnextFanoutStatus, registeredStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
81
116
|
import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
|
|
82
117
|
import { runtimeCommand } from "../services/runtime-command.js";
|
|
83
118
|
import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
|
|
@@ -132,6 +167,10 @@ async function runCliRequest(args, io, env) {
|
|
|
132
167
|
let output;
|
|
133
168
|
try {
|
|
134
169
|
output = parseOutputOptions(args);
|
|
170
|
+
// Expand before routing/context selection, after stripping transport flags.
|
|
171
|
+
// The alias keeps the existing RUN's engine and prepared-store rules.
|
|
172
|
+
if (output.args[0] === "finish")
|
|
173
|
+
output.args = ["interactive", "finish", ...output.args.slice(1)];
|
|
135
174
|
}
|
|
136
175
|
catch (error) {
|
|
137
176
|
const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
|
|
@@ -349,6 +388,7 @@ async function runCliRequest(args, io, env) {
|
|
|
349
388
|
// after admission; retained outcomes must not need a fresh hook receipt.
|
|
350
389
|
if (invocationId) {
|
|
351
390
|
const command = quote(["dd-flow", ...invocationArgs]);
|
|
391
|
+
let nextNativeObservation = 0;
|
|
352
392
|
const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
|
|
353
393
|
validateArgs: () => {
|
|
354
394
|
try {
|
|
@@ -362,7 +402,15 @@ async function runCliRequest(args, io, env) {
|
|
|
362
402
|
}
|
|
363
403
|
},
|
|
364
404
|
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
|
|
365
|
-
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output)
|
|
405
|
+
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output),
|
|
406
|
+
...((env.DD_FLOW_RUNTIME_OWNER || env.DD_FLOW_CODEX_STATE_DIR || env.DD_AGY_STATE_DIR || env.DD_GROK_STATE_DIR || env.DD_ZCODE_STATE_DIR) ? { reconcileNativeObservation: async (scope, onProgress) => {
|
|
407
|
+
if (Date.now() < nextNativeObservation)
|
|
408
|
+
return;
|
|
409
|
+
nextNativeObservation = Date.now() + 250;
|
|
410
|
+
const { reconcileRetainedNativeHook } = await import("../services/retained-native-hook.js");
|
|
411
|
+
await reconcileRetainedNativeHook(dispatchContext, { scope, invocationId, onProgress,
|
|
412
|
+
onDiagnostic: value => io.stderr.write(`${JSON.stringify({ kind: "native_hook_diagnostic", ...value })}\n`) });
|
|
413
|
+
} } : {}) });
|
|
366
414
|
if (admission.replay) {
|
|
367
415
|
retainedOutcome = admission.outcome;
|
|
368
416
|
settleHook = undefined;
|
|
@@ -372,8 +420,10 @@ async function runCliRequest(args, io, env) {
|
|
|
372
420
|
invocationSettlement = { context: dispatchContext, id: invocationId };
|
|
373
421
|
if (observedInvocation)
|
|
374
422
|
retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
|
|
375
|
-
|
|
376
|
-
|
|
423
|
+
if (admission.kind === "native_hook") {
|
|
424
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
|
|
425
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
|
|
426
|
+
}
|
|
377
427
|
dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
|
|
378
428
|
dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
|
|
379
429
|
}
|
|
@@ -498,6 +548,23 @@ async function runCliRequest(args, io, env) {
|
|
|
498
548
|
io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
|
|
499
549
|
}
|
|
500
550
|
}
|
|
551
|
+
if (invocationSettlement && error.details.work_result_accepted === true) {
|
|
552
|
+
const accepted = readAcceptedAssignedWork(invocationSettlement.context, invocationSettlement.id);
|
|
553
|
+
if (accepted) {
|
|
554
|
+
// The Work/WorkSession commit is authoritative. Report its retained
|
|
555
|
+
// answer even if a derivative publication failed after that commit.
|
|
556
|
+
const payload = addWarnings(accepted, [{ code: "work_post_commit_pending", message: "Work result accepted; derivative publication requires reconciliation" }]);
|
|
557
|
+
try {
|
|
558
|
+
writeResponseFile(payload, output);
|
|
559
|
+
}
|
|
560
|
+
catch { /* Accepted SQL reply remains available on stdout/replay. */ }
|
|
561
|
+
if (output.json)
|
|
562
|
+
writeJson(io.stdout, payload);
|
|
563
|
+
else
|
|
564
|
+
io.stdout.write(renderHumanResult(output.args, payload));
|
|
565
|
+
return completedCommandExitCode(payload);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
501
568
|
if (settleHook) {
|
|
502
569
|
try {
|
|
503
570
|
settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
|
|
@@ -560,6 +627,9 @@ function databaseModeForCommand(args, classification) {
|
|
|
560
627
|
if (isRuntimeMaintenance(args))
|
|
561
628
|
return "read_existing";
|
|
562
629
|
const [family, command] = args;
|
|
630
|
+
// Bound interactive operations never initialize or migrate a RUN store.
|
|
631
|
+
if (family === "interactive" && classification.mode !== "read_only_diagnostics")
|
|
632
|
+
return "prepared_existing";
|
|
563
633
|
if (family === "migration" && command === "readiness")
|
|
564
634
|
return "prepared_existing";
|
|
565
635
|
// Managed attempts are issued only from a prepared store. Opening that
|
|
@@ -872,6 +942,57 @@ async function prepareCliInput(...args) {
|
|
|
872
942
|
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
|
|
873
943
|
const { family, command, parsed } = route;
|
|
874
944
|
const prepared = { route };
|
|
945
|
+
if (family === "delivery" && ["checks", "execute"].includes(command ?? "")) {
|
|
946
|
+
const file = requiredOption(parsed, "result-file"), text = prepareTextFile(file, "Delivery input", 1024 * 1024);
|
|
947
|
+
const value = prepareJsonText(text.text, { file, label: "Delivery input" }).value;
|
|
948
|
+
const keys = command === "checks" ? "path,sha256,subject_ref,version" : "basis,expires_at,reason,scope_sha256,user_inputs_sha256";
|
|
949
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== keys)
|
|
950
|
+
throw new AppError("usage", "Delivery input must match the selected command's exact JSON contract", 2, { phase: "prepare", effect: "no_effect" });
|
|
951
|
+
if (command === "checks")
|
|
952
|
+
prepared.deliveryArtifact = value;
|
|
953
|
+
else
|
|
954
|
+
prepared.deliveryApproval = value;
|
|
955
|
+
}
|
|
956
|
+
if (family === "interactive" && command === "integrate" && hasOption(parsed, "result-file")) {
|
|
957
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive Git agreement", 1024 * 1024);
|
|
958
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive Git agreement" }).value;
|
|
959
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== "basis,include_existing_paths,reason")
|
|
960
|
+
throw new AppError("usage", "Interactive Git agreement requires basis, include_existing_paths and reason", 2, { phase: "prepare", effect: "no_effect" });
|
|
961
|
+
prepared.interactiveGitAgreement = value;
|
|
962
|
+
}
|
|
963
|
+
if (family === "interactive" && command === "checks") {
|
|
964
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive check selection", 1024 * 1024);
|
|
965
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive check selection" }).value;
|
|
966
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
967
|
+
|| !["additionalChecks", "additionalChecks,noChecksReason"].includes(Object.keys(value).sort().join()))
|
|
968
|
+
throw new AppError("usage", "Interactive checks require additionalChecks and optional noChecksReason", 2, { phase: "prepare", effect: "no_effect" });
|
|
969
|
+
prepared.interactiveChecksProposal = value;
|
|
970
|
+
}
|
|
971
|
+
if (family === "interactive" && command === "scope") {
|
|
972
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive mutation scope", 1024 * 1024);
|
|
973
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive mutation scope" }).value;
|
|
974
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
975
|
+
|| Object.keys(value).sort().join() !== "basis,expectedInputsSha256,paths,reason,requestId")
|
|
976
|
+
throw new AppError("usage", "Interactive scope requires requestId, paths, basis, reason and expectedInputsSha256", 2, { phase: "prepare", effect: "no_effect" });
|
|
977
|
+
const basis = recordObject(recordObject(value).basis);
|
|
978
|
+
if (!basis || Object.keys(basis).sort().join() !== "event_id,role,sha256"
|
|
979
|
+
|| !["request", "turn", "answer", "finish"].includes(String(basis.role))
|
|
980
|
+
|| typeof basis.event_id !== "number" || !Number.isSafeInteger(basis.event_id) || basis.event_id < 0
|
|
981
|
+
|| typeof basis.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(basis.sha256))
|
|
982
|
+
throw new AppError("usage", "Interactive scope basis requires role request|turn|answer|finish, a nonnegative integer event_id and a SHA-256", 2, {
|
|
983
|
+
reason: "invalid_scope_basis_shape", phase: "prepare", effect: "no_effect", recoverable: true,
|
|
984
|
+
retry_command: runtimeCommand(context, ["interactive", "scope", "--project-root", requiredOption(parsed, "project-root"),
|
|
985
|
+
"--run", requiredOption(parsed, "run"), "--result-file", file, "--json", "--progress-jsonl"]),
|
|
986
|
+
retry_instruction: "Correct only the rejected basis shape. For the original request use role=request, event_id=0 and initial_request.sha256 from interactive inputs. For a later user message copy its exact role, integer event_id and sha256 from messages; a question is not user approval. No scope was retained by this call. Reuse this exact retry_command; valid-shaped but unowned evidence or conflicting accepted scope identities remain refusals."
|
|
987
|
+
});
|
|
988
|
+
prepared.interactiveScopeProposal = value;
|
|
989
|
+
}
|
|
990
|
+
if (family === "interactive" && (command === "turn" || command === "finish")) {
|
|
991
|
+
const input = prepareTextFile(requiredOption(parsed, "intake-file"), "Interactive user input", 1024 * 1024);
|
|
992
|
+
prepared.interactiveInputBytes = Buffer.from(input.text);
|
|
993
|
+
if (!prepared.interactiveInputBytes.length || !/^[a-f0-9]{64}$/.test(requiredOption(parsed, "expected-inputs-sha256")))
|
|
994
|
+
throw new AppError("usage", "Interactive delivery requires nonempty UTF-8 input and its current input digest", 2, { phase: "prepare", effect: "no_effect" });
|
|
995
|
+
}
|
|
875
996
|
if (optionalOption(parsed, "reason-file")) {
|
|
876
997
|
const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
|
|
877
998
|
if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
|
|
@@ -1232,7 +1353,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1232
1353
|
const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
1233
1354
|
if (bundle)
|
|
1234
1355
|
prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
|
|
1235
|
-
if (bundle && (["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id))) {
|
|
1356
|
+
if (bundle && (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id) || isDeliveryFlow(bundle.descriptor.descriptor.id))) {
|
|
1236
1357
|
bundle.descriptor.stage(stage);
|
|
1237
1358
|
if (hasOption(parsed, "dir"))
|
|
1238
1359
|
throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
|
|
@@ -1240,7 +1361,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1240
1361
|
if (loaded)
|
|
1241
1362
|
prepared.stageStart = { externalContext: loaded };
|
|
1242
1363
|
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
|
|
1243
|
-
flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1364
|
+
flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1244
1365
|
return prepared;
|
|
1245
1366
|
}
|
|
1246
1367
|
}
|
|
@@ -1345,7 +1466,8 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1345
1466
|
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1346
1467
|
}
|
|
1347
1468
|
if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
|
|
1348
|
-
|| command === "repair" && parsed.positional[0] === "publication-reserve"
|
|
1469
|
+
|| command === "repair" && parsed.positional[0] === "publication-reserve"
|
|
1470
|
+
|| command === "distill" && parsed.positional[0] === "publish")) {
|
|
1349
1471
|
try {
|
|
1350
1472
|
const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
|
|
1351
1473
|
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
@@ -1359,6 +1481,12 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1359
1481
|
throw error;
|
|
1360
1482
|
}
|
|
1361
1483
|
}
|
|
1484
|
+
if (family === "memory" && command === "fix" && ["scope", "publish", "integrate"].includes(parsed.positional[0] ?? "") && hasOption(parsed, "result-file")) {
|
|
1485
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Memory repair proposal", 1024 * 1024);
|
|
1486
|
+
prepared.memoryFixProposal = prepareJsonText(input.text, { file, label: "Memory repair proposal" }).value;
|
|
1487
|
+
if (!prepared.memoryFixProposal || typeof prepared.memoryFixProposal !== "object" || Array.isArray(prepared.memoryFixProposal))
|
|
1488
|
+
throw new AppError("usage", "Memory repair proposal must be a JSON object", 2, { phase: "prepare", effect: "no_effect" });
|
|
1489
|
+
}
|
|
1362
1490
|
if (family !== "stage" || command !== "finish")
|
|
1363
1491
|
return prepared;
|
|
1364
1492
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
@@ -1375,9 +1503,9 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1375
1503
|
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1376
1504
|
const runtime = run(projectRoot, runId);
|
|
1377
1505
|
const retainedFlowId = JSON.parse(runtime.indexJson).execution_bundle?.flow_id;
|
|
1378
|
-
if (["mb-init", "mb-upgrade"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId)) {
|
|
1506
|
+
if (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId) || isDeliveryFlow(retainedFlowId)) {
|
|
1379
1507
|
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
1380
|
-
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id))
|
|
1508
|
+
if (!bundle || !["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id) && !isDeliveryFlow(bundle.descriptor.descriptor.id))
|
|
1381
1509
|
throw new AppError("cli_input_unprepared", "Registered RUN bundle is unavailable", 1);
|
|
1382
1510
|
bundle.descriptor.stage(stage);
|
|
1383
1511
|
if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
|
|
@@ -1387,7 +1515,7 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1387
1515
|
const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
|
|
1388
1516
|
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1389
1517
|
throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
|
|
1390
|
-
prepared.memoryStage = { operation: "finish", bytes, flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1518
|
+
prepared.memoryStage = { operation: "finish", bytes, flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1391
1519
|
return prepared;
|
|
1392
1520
|
}
|
|
1393
1521
|
const terminalReplay = (name) => {
|
|
@@ -1776,12 +1904,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1776
1904
|
const externalContext = prepared.stageStart?.externalContext;
|
|
1777
1905
|
if (prepared.memoryStage?.operation === "start") {
|
|
1778
1906
|
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1779
|
-
const event = hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1907
|
+
const event = hookEventId ?? (currentAssignedLifecycle(context) ? undefined : findRecentMatchingHookEvent(context, {
|
|
1780
1908
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1781
1909
|
matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
1782
1910
|
errorCode: "trusted_session_binding_required", operation: "Memory stage start"
|
|
1783
|
-
}).eventKey;
|
|
1784
|
-
const work = (isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1911
|
+
}).eventKey);
|
|
1912
|
+
const work = (isDeliveryFlow(prepared.memoryStage.flowId) ? startDeliveryStage : prepared.memoryStage.flowId === "interactive" ? startInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? startReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? startMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? startDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1785
1913
|
try {
|
|
1786
1914
|
const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
|
|
1787
1915
|
promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
|
|
@@ -1791,9 +1919,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1791
1919
|
const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
|
|
1792
1920
|
"--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
|
|
1793
1921
|
const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
|
|
1794
|
-
|
|
1922
|
+
// These assignments belong to this native coordinator delivery, not to
|
|
1923
|
+
// the immutable semantic packet restored by auxiliary owners.
|
|
1924
|
+
const deliveryInstructions = isDeliveryFlow(prepared.memoryStage.flowId) ? issueDeliveryStageCommands(context, { flowId: prepared.memoryStage.flowId, projectRoot, runId, stage, stageRoot: path.dirname(work.result_path) }).instructions : "";
|
|
1925
|
+
const orchestration = registeredStageFanoutDescriptor(context, { projectRoot, runId, stage });
|
|
1795
1926
|
const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
|
|
1796
|
-
worker_prompt_markdown: `${work.worker_prompt_markdown}\n## Current lifecycle commands\n\nWrite the semantic result to the assigned result path, then execute exactly:\n${finish}\n\nFor a required user choice, write the question file named by this command, then execute exactly:\n${pause}\n` };
|
|
1927
|
+
worker_prompt_markdown: `${work.worker_prompt_markdown}${deliveryInstructions}\n## Current lifecycle commands\n\nWrite the semantic result to the assigned result path, then execute exactly:\n${finish}\n\nFor a required user choice, write the question file named by this command, then execute exactly:\n${pause}\n` };
|
|
1797
1928
|
bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
|
|
1798
1929
|
return controlLifecycleReply(context, { projectRoot, runId }, delivery);
|
|
1799
1930
|
}
|
|
@@ -1803,6 +1934,8 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1803
1934
|
}
|
|
1804
1935
|
}
|
|
1805
1936
|
const claimVnextStageStart = (runId) => {
|
|
1937
|
+
if (currentAssignedLifecycle(context)?.operation === "stage_start")
|
|
1938
|
+
return undefined;
|
|
1806
1939
|
registerProject(context, { root: projectRoot });
|
|
1807
1940
|
return findRecentMatchingHookEvent(context, {
|
|
1808
1941
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
@@ -1900,12 +2033,12 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1900
2033
|
// Descriptor settlement validates exact coordinator custody and claims
|
|
1901
2034
|
// the fresh hook atomically with result acceptance. Do not consume it
|
|
1902
2035
|
// through the SDLC lifecycle owner before that settlement.
|
|
1903
|
-
return controlledReply((isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
1904
|
-
hookEventId: hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
2036
|
+
return controlledReply((isDeliveryFlow(prepared.memoryStage.flowId) ? finishDeliveryStage : prepared.memoryStage.flowId === "interactive" ? finishInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? finishReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? finishMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? finishDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
2037
|
+
hookEventId: hookEventId ?? (currentAssignedLifecycle(context) ? "" : findRecentMatchingHookEvent(context, {
|
|
1905
2038
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1906
2039
|
matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
|
|
1907
2040
|
errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
|
|
1908
|
-
}).eventKey }));
|
|
2041
|
+
}).eventKey) }));
|
|
1909
2042
|
}
|
|
1910
2043
|
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
1911
2044
|
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
@@ -2112,6 +2245,102 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
2112
2245
|
if (family === "schema") {
|
|
2113
2246
|
return prepared.schemaValidation;
|
|
2114
2247
|
}
|
|
2248
|
+
if (family === "delivery") {
|
|
2249
|
+
const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2250
|
+
const runId = resolveRun(context, project.id, requiredPosition(parsed, 0, "run-id")).id, stage = requiredOption(parsed, "stage");
|
|
2251
|
+
const input = { projectRoot, runId, stage, command: quote(["dd-flow", ...args]) };
|
|
2252
|
+
const selected = command === "inputs" ? readDeliveryStageScope(context, input) : assertDeliveryCommandOwner(context, input);
|
|
2253
|
+
if (command === "checks") {
|
|
2254
|
+
if (!prepared.deliveryArtifact)
|
|
2255
|
+
throw new AppError("cli_input_unprepared", "Delivery artifact was not prepared", 1);
|
|
2256
|
+
const checks = prepareDeliveryChecks(context, { ...input, artifact: prepared.deliveryArtifact });
|
|
2257
|
+
await checks.run();
|
|
2258
|
+
return { ok: true, verification: checks.evidence().receipt };
|
|
2259
|
+
}
|
|
2260
|
+
if (command === "execute") {
|
|
2261
|
+
if (!prepared.deliveryApproval)
|
|
2262
|
+
throw new AppError("cli_input_unprepared", "Delivery approval was not prepared", 1);
|
|
2263
|
+
return executeDeliveryOperation(context, { ...input, approval: prepared.deliveryApproval });
|
|
2264
|
+
}
|
|
2265
|
+
const packet = readDescriptorStageInput(context, selected.scope, selected.bundle);
|
|
2266
|
+
const previous = packet.input.predecessor ? readAcceptedStageData(context, { ...selected.scope, stage: packet.input.predecessor.stage }) : null;
|
|
2267
|
+
const details = previous ? JSON.parse(previous.bytes.toString("utf8")).details : null;
|
|
2268
|
+
const operation = details?.decision && details?.artifact ? deliveryApprovalScope(selected.scope, selected.bundle, { operationId: details.decision.operation_id, binding: details.decision.binding,
|
|
2269
|
+
artifact: { subject_ref: details.artifact.subject_ref, version: details.artifact.version, sha256: details.artifact.sha256 } }) : null;
|
|
2270
|
+
if (command === "inputs")
|
|
2271
|
+
return { flow_id: selected.flowId, run_id: runId, stage, user_inputs: selected.users,
|
|
2272
|
+
user_inputs_sha256: selected.userInputsSha256, operation_scope: operation, scope_sha256: operation ? hybridDigest(operation) : null };
|
|
2273
|
+
if (command === "reconcile") {
|
|
2274
|
+
if (!details?.decision)
|
|
2275
|
+
throw new AppError("delivery_stage_invalid", "Original delivery decision is required", 2, { effect: "no_effect" });
|
|
2276
|
+
const policy = prepareDeliveryProjectBinding(selected.bundle.execution.snapshot().scope.workspace, details.decision.policy, details.decision.binding.selector);
|
|
2277
|
+
if (hybridDigest(policy.binding) !== hybridDigest(details.decision.binding))
|
|
2278
|
+
throw new AppError("delivery_stage_invalid", "Original delivery policy changed", 2, { effect: "no_effect" });
|
|
2279
|
+
return observeDeliveryResult(context, { ...selected.scope, binding: policy.binding,
|
|
2280
|
+
assertCurrent() { assertDeliveryCommandOwner(context, input); policy.assertCurrent(); } });
|
|
2281
|
+
}
|
|
2282
|
+
throw new AppError("usage", "Unknown delivery command", 2);
|
|
2283
|
+
}
|
|
2284
|
+
if (family === "interactive") {
|
|
2285
|
+
const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2286
|
+
const runId = resolveRun(context, project.id, requiredOption(parsed, "run")).id;
|
|
2287
|
+
if (command === "integrate")
|
|
2288
|
+
return integrateInteractiveFinish(context, { projectRoot, runId,
|
|
2289
|
+
...(prepared.interactiveGitAgreement ? { agreement: prepared.interactiveGitAgreement } : {}) });
|
|
2290
|
+
if (command === "correction")
|
|
2291
|
+
return registerInteractiveReviewCorrection(context, { projectRoot, runId, reviewWorkId: requiredOption(parsed, "work") });
|
|
2292
|
+
if (command === "re-review")
|
|
2293
|
+
return registerInteractiveCorrectionReview(context, { projectRoot, runId, correctionWorkId: requiredOption(parsed, "work") });
|
|
2294
|
+
if (command === "checks") {
|
|
2295
|
+
if (!prepared.interactiveChecksProposal)
|
|
2296
|
+
throw new AppError("cli_input_unprepared", "Interactive check selection was not prepared", 1);
|
|
2297
|
+
let checks;
|
|
2298
|
+
try {
|
|
2299
|
+
checks = PreparedInteractiveChecks.prepare(context, { projectRoot, runId,
|
|
2300
|
+
stage: requiredOption(parsed, "stage"), ...prepared.interactiveChecksProposal });
|
|
2301
|
+
}
|
|
2302
|
+
catch (error) {
|
|
2303
|
+
if (isAppError(error) && error.code === "interactive_checks_invalid"
|
|
2304
|
+
&& error.details.reason === "invalid_check_selection" && error.details.effect === "no_effect") {
|
|
2305
|
+
Object.assign(error.details, { phase: "prepare", recoverable: true,
|
|
2306
|
+
retry_command: runtimeCommand(context, ["interactive", "checks", "--project-root", projectRoot, "--run", runId,
|
|
2307
|
+
"--stage", requiredOption(parsed, "stage"), "--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
|
|
2308
|
+
retry_instruction: "Correct only the rejected check selection JSON: additionalChecks contains unique id,command,purpose entries; IDs match ^CHK-[A-Za-z0-9-]{1,64}$ and must not collide with policy checks. No plan or check execution was retained by this call. Keep existing obligations and use this exact retry_command; this does not authorize replay of executed checks or bypass other refusals." });
|
|
2309
|
+
}
|
|
2310
|
+
throw error;
|
|
2311
|
+
}
|
|
2312
|
+
return checks.execute(context).then(receipts => ({ plan_sha256: checks.sha256, plan: checks.value(), receipts }));
|
|
2313
|
+
}
|
|
2314
|
+
if (command === "register") {
|
|
2315
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
2316
|
+
if (!bundle || bundle.manifest.flow_id !== "interactive")
|
|
2317
|
+
throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
|
|
2318
|
+
return registerInteractiveStageGraph(context, { projectRoot, runId, stage: requiredOption(parsed, "stage"),
|
|
2319
|
+
gitExecutable: readInteractiveWorkGit(bundle).git_executable });
|
|
2320
|
+
}
|
|
2321
|
+
if (command === "git-inputs")
|
|
2322
|
+
return observeInteractiveOriginalGitInputs(context, { projectRoot, runId, stage: requiredOption(parsed, "stage") });
|
|
2323
|
+
if (command === "inputs" || command === "turn" || command === "finish" || command === "scope") {
|
|
2324
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
2325
|
+
if (!bundle || bundle.manifest.flow_id !== "interactive")
|
|
2326
|
+
throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
|
|
2327
|
+
const scope = { projectId: project.id, runId };
|
|
2328
|
+
if (command === "inputs")
|
|
2329
|
+
return readInteractiveUserInputs(context, scope, bundle);
|
|
2330
|
+
if (command === "scope") {
|
|
2331
|
+
if (!prepared.interactiveScopeProposal)
|
|
2332
|
+
throw new AppError("cli_input_unprepared", "Interactive scope proposal was not prepared", 1);
|
|
2333
|
+
const result = PreparedInteractiveMutationScope.prepare(context, scope, bundle, prepared.interactiveScopeProposal).retain(context);
|
|
2334
|
+
return { changed: result.changed, scope_revision: result.scope.revision, user_inputs_sha256: result.scope.inputs.sha256,
|
|
2335
|
+
baseline_sha256: result.scope.baseline.sha256, paths: result.scope.active_paths, needs_scope_refresh: result.scope.needs_scope_refresh };
|
|
2336
|
+
}
|
|
2337
|
+
if (!prepared.interactiveInputBytes)
|
|
2338
|
+
throw new AppError("cli_input_unprepared", "Interactive user bytes were not prepared", 1);
|
|
2339
|
+
return deliverInteractiveUserInput(context, scope, bundle, { projectRoot, intakeFile: requiredOption(parsed, "intake-file"),
|
|
2340
|
+
requestId: requiredOption(parsed, "request-id"), kind: command, bytes: prepared.interactiveInputBytes,
|
|
2341
|
+
expectedInputsSha256: requiredOption(parsed, "expected-inputs-sha256") });
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2115
2344
|
if (family === "memory") {
|
|
2116
2345
|
return dispatchMemory(context, command, parsed, prepared);
|
|
2117
2346
|
}
|
|
@@ -2502,6 +2731,32 @@ function dispatchMemory(context, command, parsed, prepared) {
|
|
|
2502
2731
|
}
|
|
2503
2732
|
if (command === "upgrade" && subcommand === "publish")
|
|
2504
2733
|
return publishMemoryUpgradeFromStage(context, scope());
|
|
2734
|
+
if (command === "distill" && subcommand === "inputs") {
|
|
2735
|
+
const inputs = readDistillUserInputs(context, scope());
|
|
2736
|
+
return { ...inputs, user_inputs_sha256: hybridDigest(inputs) };
|
|
2737
|
+
}
|
|
2738
|
+
if (command === "fix" && subcommand === "context") {
|
|
2739
|
+
const intake = readMemoryFixSourceIntake(context, scope()), users = readMemoryFixUserInputs(context, scope());
|
|
2740
|
+
return { ...intake, user_inputs: users, user_inputs_sha256: hybridDigest(users) };
|
|
2741
|
+
}
|
|
2742
|
+
if (command === "fix" && subcommand === "scope") {
|
|
2743
|
+
const proposal = prepared.memoryFixProposal;
|
|
2744
|
+
if (!proposal || Object.keys(proposal).sort().join() !== "routing,write_scope")
|
|
2745
|
+
throw new AppError("usage", "Repair scope proposal requires routing and write_scope", 2, { effect: "no_effect" });
|
|
2746
|
+
return prepareMemoryFixScopeEvidence(context, { ...scope(), routing: proposal.routing, writeScope: proposal.write_scope }).snapshot();
|
|
2747
|
+
}
|
|
2748
|
+
if (command === "fix" && subcommand === "publish") {
|
|
2749
|
+
if (!prepared.memoryFixProposal)
|
|
2750
|
+
throw new AppError("cli_input_unprepared", "Repair proposal was not prepared", 1);
|
|
2751
|
+
return prepareMemoryFixPublication(context, { ...scope(), proposal: prepared.memoryFixProposal }).apply();
|
|
2752
|
+
}
|
|
2753
|
+
if (command === "fix" && subcommand === "integrate")
|
|
2754
|
+
return integrateMemoryFixFinish(context, { ...scope(), ...(prepared.memoryFixProposal ? { agreement: prepared.memoryFixProposal } : {}) });
|
|
2755
|
+
if (command === "distill" && subcommand === "publish") {
|
|
2756
|
+
if (!prepared.memoryOperationBytes)
|
|
2757
|
+
throw new AppError("cli_input_unprepared", "Distillation proposal bytes were not prepared", 1);
|
|
2758
|
+
return publishDistillFromStage(context, { ...scope(), bytes: prepared.memoryOperationBytes });
|
|
2759
|
+
}
|
|
2505
2760
|
if (command === "verify" && subcommand === "checks")
|
|
2506
2761
|
return runMemoryVerificationChecks(context, scope());
|
|
2507
2762
|
if (command === "review" && subcommand === "register")
|