@deksden-com/dd-flow-cli 0.6.0 → 0.7.0
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 +6 -0
- package/README.md +8 -1
- package/dist/build-info.json +5 -5
- package/dist/cli/help.js +20 -8
- package/dist/cli/run-cli.js +104 -24
- package/dist/domain/flow-contract.js +11 -0
- package/dist/runtime/context.js +8 -2
- package/dist/schemas/code-stage-report.schema.json +7 -2
- package/dist/schemas/engine-manifest.schema.json +22 -0
- package/dist/schemas/flow-run.schema.json +1 -0
- package/dist/schemas/mb-upgrade-migration-report.schema.json +3 -1
- package/dist/schemas/merge-stage-report-legacy-0.4.2.schema.json +24 -0
- package/dist/schemas/run-engine-binding.schema.json +37 -0
- package/dist/schemas/stage-prompt.schema.json +4 -4
- package/dist/services/canon.js +15 -1
- package/dist/services/cli-operation-classifier.js +52 -8
- package/dist/services/compatibility-preflight.js +1 -1
- package/dist/services/dashboard.js +2 -2
- package/dist/services/engines.js +408 -30
- package/dist/services/hooks.js +1 -5
- package/dist/services/lanes.js +0 -4
- package/dist/services/merge-queue.js +48 -0
- package/dist/services/merge-worker.js +3 -4
- package/dist/services/migrations.js +307 -44
- package/dist/services/plan-runtime.js +4 -4
- package/dist/services/plans.js +5 -3
- package/dist/services/protocols.js +23 -2
- package/dist/services/run-engine-bindings.js +157 -0
- package/dist/services/runs.js +21 -7
- package/dist/services/schema-validation.js +96 -0
- package/dist/services/stage-lifecycle.js +98 -10
- package/dist/services/status.js +8 -3
- package/dist/storage/database.js +32 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add target-driven engine routing, explicit storage migration safety, immutable RUN engine bindings, engine schema registry validation, and self-contained plan execution guidance.
|
|
8
|
+
|
|
3
9
|
## 0.6.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -355,9 +355,16 @@ Codex integration is explicit and auditable. Managed Codex homes and hooks are l
|
|
|
355
355
|
```bash
|
|
356
356
|
dd-flow codex home plan --project-root "$PROJECT_ROOT" --json
|
|
357
357
|
dd-flow codex hooks status --project-root "$PROJECT_ROOT" --target isolated --json
|
|
358
|
-
dd-flow codex hook handle --event Stop --
|
|
358
|
+
dd-flow codex hook handle --event Stop --json
|
|
359
359
|
```
|
|
360
360
|
|
|
361
|
+
Installed Codex hooks intentionally omit `--project-root`: Codex supplies the
|
|
362
|
+
session `cwd` in the hook JSON on stdin, and the handler resolves the registered
|
|
363
|
+
project from that path. The option remains available as a manual override. If
|
|
364
|
+
the CLI is upgraded, reinstall the hook in each active `CODEX_HOME`, verify it
|
|
365
|
+
with `dd-flow codex hooks status`, and start a new Codex session;
|
|
366
|
+
`--project-root` belongs to installer/status commands, not the stored handler.
|
|
367
|
+
|
|
361
368
|
## Verification
|
|
362
369
|
|
|
363
370
|
Local verification for this repository:
|
package/dist/build-info.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"cli_version": "0.
|
|
4
|
-
"cli_commit": "
|
|
5
|
-
"built_at": "2026-08-
|
|
3
|
+
"cli_version": "0.7.0",
|
|
4
|
+
"cli_commit": "a09f6b663231d3179bea64b2fd9bb4ffa30096c2",
|
|
5
|
+
"built_at": "2026-08-13T04:31:51.147Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
|
-
"version": "3.
|
|
8
|
-
"commit": "
|
|
7
|
+
"version": "3.2.0",
|
|
8
|
+
"commit": "2a1aaec84ee7d62b9f5a6549de5d1f0cb88082c0",
|
|
9
9
|
"flow_contract": "dd-flow-canonical-2026-08",
|
|
10
10
|
"repo_root": "/Users/deksden/Documents/_Projects/dd-memorybank",
|
|
11
11
|
"memorybank_root": "/Users/deksden/Documents/_Projects/dd-memorybank/.memory-bank",
|
package/dist/cli/help.js
CHANGED
|
@@ -12,13 +12,13 @@ Core commands:
|
|
|
12
12
|
version Print the installed dd-flow CLI package version.
|
|
13
13
|
status Inspect dd-flow runtime, project and canon roots.
|
|
14
14
|
engine install/list/info Manage locally installed dd-flow engine snapshots.
|
|
15
|
-
engine resolve/doctor
|
|
15
|
+
engine resolve/doctor/bind-run Inspect router selection, health, and recover a legacy RUN binding.
|
|
16
16
|
canon register/status/resolve Register and resolve the canonical Memory Bank.
|
|
17
17
|
project register/status/summary Register a project, inspect state, and publish project summaries.
|
|
18
18
|
project resolve/archive Resolve typed ids and archive stale project roots.
|
|
19
19
|
project migrate-ids Migrate old project ids.
|
|
20
20
|
protocol register/status/transition Register, inspect, transition and repair protocol state.
|
|
21
|
-
migration impact/plan/report/verify Assess release impact and
|
|
21
|
+
migration impact/plan/report/verify/apply Assess release impact and explicitly apply mb-upgrade migration evidence.
|
|
22
22
|
cleanup scan/apply Detect and repair stale local runtime state.
|
|
23
23
|
run start/status/list/timeline/usage/flags Track concrete flow executions, timing, source-aware usage and RUN-local flow flags.
|
|
24
24
|
stage start/finish Run the canonical two-command stage lifecycle and generate evidence.
|
|
@@ -28,7 +28,7 @@ Core commands:
|
|
|
28
28
|
lane workspace/lock/status Manage shared workspace lanes and leases.
|
|
29
29
|
merge status/one-shot/bundle Inspect, claim, or complete merge work from the current session.
|
|
30
30
|
merge-worker status/start/stop Manage long-lived project merge workers.
|
|
31
|
-
merge-queue status/next
|
|
31
|
+
merge-queue status/claim/next Claim, wait for, complete, or fail merge jobs.
|
|
32
32
|
session register/status/stop Register flow sessions and stop workers.
|
|
33
33
|
dashboard data/render/open/refresh Render, refresh, open, and inspect project/global dashboards.
|
|
34
34
|
schema validate Validate canonical dd-flow JSON data contracts.
|
|
@@ -86,6 +86,7 @@ Usage:
|
|
|
86
86
|
dd-flow engine info [--package <name>] [--version <version>] --json
|
|
87
87
|
dd-flow engine resolve [--project-root <root>] --json
|
|
88
88
|
dd-flow engine doctor [--project-root <root>] --json
|
|
89
|
+
dd-flow engine bind-run <RUN-ID> --project-root <root> --engine-version <version> --reason <text> [--integrity-checksum <sha256>] --json
|
|
89
90
|
|
|
90
91
|
The globally invoked dd-flow command is the stable router surface. Engine snapshots live under DD_FLOW_HOME/engines and are selected by project Memory Bank compatibility data. Running an old package through npx can install that package's own engine without replacing the global router:
|
|
91
92
|
|
|
@@ -93,11 +94,14 @@ The globally invoked dd-flow command is the stable router surface. Engine snapsh
|
|
|
93
94
|
|
|
94
95
|
Router-native engine commands never mutate project runtime data. Normal project commands are routed to a compatible engine or fail closed with a missing-engine diagnostic.
|
|
95
96
|
|
|
97
|
+
engine bind-run is the one recovery command for a legacy RUN without an engine-binding sidecar. It probes the requested installed snapshot with run status before atomically writing an immutable binding; later RUN commands route through that snapshot.
|
|
98
|
+
|
|
96
99
|
Examples:
|
|
97
100
|
dd-flow engine install --json
|
|
98
101
|
dd-flow engine list --json
|
|
99
102
|
dd-flow engine resolve --project-root "$PWD" --json
|
|
100
|
-
dd-flow engine doctor --project-root "$PWD" --json
|
|
103
|
+
dd-flow engine doctor --project-root "$PWD" --json
|
|
104
|
+
dd-flow engine bind-run RUN-193 --project-root "$PWD" --engine-version 0.4.2 --reason "legacy flow-run@1" --json`
|
|
101
105
|
],
|
|
102
106
|
[
|
|
103
107
|
"status",
|
|
@@ -234,11 +238,12 @@ New registrations use full typed ids such as PRJ-001-dd-flow-playground and shor
|
|
|
234
238
|
`dd-flow migration - plan and verify mb-upgrade runtime/home data migrations
|
|
235
239
|
|
|
236
240
|
Usage:
|
|
237
|
-
dd-flow migration plan --project-root <root> [--source-version <version>] [--target-version <version>] [--run <RUN-ID>] [--backup-path <path>] [--backup-created-at <iso>]
|
|
238
|
-
dd-flow migration report --project-root <root> [--source-version <version>] [--target-version <version>] [--run <RUN-ID>] [--backup-path <path>] [--backup-created-at <iso>]
|
|
241
|
+
dd-flow migration plan --project-root <root> [--source-version <version>] [--target-version <version>] [--run <RUN-ID>] [--backup-path <path>] [--backup-created-at <iso>] --json
|
|
242
|
+
dd-flow migration report --project-root <root> [--source-version <version>] [--target-version <version>] [--run <RUN-ID>] [--backup-path <path>] [--backup-created-at <iso>] --json
|
|
239
243
|
dd-flow migration verify --file <mb-upgrade-migration-report.json> --json
|
|
244
|
+
dd-flow migration apply --project-root <root> --file <mb-upgrade-migration-report.json> --json
|
|
240
245
|
|
|
241
|
-
Migration commands are evidence and guardrail helpers for canonical mb-upgrade. They do not silently migrate runtime/home data during ordinary CLI use. Applying migrations
|
|
246
|
+
Migration commands are evidence and guardrail helpers for canonical mb-upgrade. They do not silently migrate runtime/home data during ordinary CLI use. Applying migrations is an explicit contour requiring a real verified backup, adjacent migration chain, quiescent shared home, recovery evidence and idempotent rerun.
|
|
242
247
|
|
|
243
248
|
Related commands:
|
|
244
249
|
dd-flow status --project-root <root> --json
|
|
@@ -354,13 +359,14 @@ start is idempotent: if a worker, claimed job, or active merge lock already exis
|
|
|
354
359
|
Usage:
|
|
355
360
|
dd-flow merge-queue status --project-root <root> --json
|
|
356
361
|
dd-flow merge-queue next --project-root <root> --worker-id <id> [--path <workspace>] --json
|
|
362
|
+
dd-flow merge-queue claim <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --json
|
|
357
363
|
dd-flow merge-queue wait-next --project-root <root> --worker-id <id> [--path <workspace>] --timeout <seconds> --poll-interval <seconds> [--acquire-lock true] --json
|
|
358
364
|
dd-flow merge-queue complete <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --summary <text> --json
|
|
359
365
|
dd-flow merge-queue note <protocol-id> [--project-root <root>] --worker-id <id> --summary <text> --json
|
|
360
366
|
dd-flow merge-queue fail <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --reason <text> --requeue true|false --json
|
|
361
367
|
dd-flow merge-queue cancel <protocol-id> [--project-root <root>] --reason <text> [--worker-id <id>] [--path <workspace>] [--force] --json
|
|
362
368
|
|
|
363
|
-
The worker must own the merge lane from the registered merge workspace before claiming, completing, or failing queued protocols. Outputs use queue_item/protocol/claim as the primary contract and retain job as a compatibility alias. note updates the final completion summary after post-complete cleanup and does not require a lane lock. --path defaults to the current working directory. Use --worker-id as the owner identity.
|
|
369
|
+
The worker must own the merge lane from the registered merge workspace before claiming, completing, or failing queued protocols. next remains FIFO; claim <protocol-id> atomically claims only that ready/requeued job. Outputs use queue_item/protocol/claim as the primary contract and retain job as a compatibility alias. note updates the final completion summary after post-complete cleanup and does not require a lane lock. --path defaults to the current working directory. Use --worker-id as the owner identity.
|
|
364
370
|
|
|
365
371
|
For branch-level integration prefer:
|
|
366
372
|
dd-flow merge bundle status --project-root "$PWD" --path "$PWD" --json
|
|
@@ -527,6 +533,12 @@ Usage:
|
|
|
527
533
|
dd-flow codex home print-env --project-root <root> [--profile <name>] --json
|
|
528
534
|
dd-flow codex home remove --project-root <root> [--profile <name>] --mode keep-shared|remove-owned --json
|
|
529
535
|
|
|
536
|
+
dd-flow codex hook handle --event <HookEventName> [--project-root <root>] --json
|
|
537
|
+
|
|
538
|
+
Managed hooks omit --project-root. Codex supplies cwd in the stdin payload; the
|
|
539
|
+
handler resolves the registered project from cwd. The option is only a manual
|
|
540
|
+
override.
|
|
541
|
+
|
|
530
542
|
Managed homes isolate dd-flow hooks while sharing selected Codex state from the source home.`
|
|
531
543
|
],
|
|
532
544
|
[
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { createContext } from "../runtime/context.js";
|
|
2
|
+
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
3
3
|
import { helpForArgs } from "./help.js";
|
|
4
4
|
import { AppError, isAppError } from "../shared/errors.js";
|
|
5
5
|
import { writeJson } from "../shared/json.js";
|
|
6
6
|
import { archiveProject, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
7
7
|
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, requireProtocol, syncProtocolFromRun, transitionProtocol } from "../services/protocols.js";
|
|
8
8
|
import { blockPlanItem, completePlanItem, getPlanStatus, skipPlanItem, startPlanItem } from "../services/plans.js";
|
|
9
|
-
import { cancelMergeQueueJob, claimNextMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
|
|
9
|
+
import { cancelMergeQueueJob, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
|
|
10
10
|
import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
|
|
11
11
|
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
12
12
|
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
@@ -24,12 +24,13 @@ import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunTi
|
|
|
24
24
|
import { previewNextEntityId } from "../services/ids.js";
|
|
25
25
|
import { validateSchema } from "../services/schema-validation.js";
|
|
26
26
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
27
|
-
import { assessMigrationImpact, planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
27
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
28
28
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
29
29
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
30
30
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
31
|
-
import { doctorEngines, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
31
|
+
import { doctorEngines, bindRunEngine, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
32
32
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
33
|
+
import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
|
|
33
34
|
import { renderWorkerPrompt } from "../services/prompts.js";
|
|
34
35
|
import { finishStage, startStage } from "../services/stage-lifecycle.js";
|
|
35
36
|
const defaultIo = {
|
|
@@ -58,20 +59,27 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
58
59
|
io.stdout.write(`${help}\n`);
|
|
59
60
|
return 0;
|
|
60
61
|
}
|
|
61
|
-
const
|
|
62
|
+
const classification = classifyCliOperation(output.args, env);
|
|
63
|
+
if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
|
|
64
|
+
throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
|
|
65
|
+
operation: classification.operation,
|
|
66
|
+
required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const routerContext = createRouterContext(env);
|
|
62
70
|
emitHumanProgress(io, output, progress, "start");
|
|
63
|
-
const scopeProjectRoot =
|
|
64
|
-
const routed = await routeArgsThroughEngine(
|
|
71
|
+
const scopeProjectRoot = resolveProjectRootBeforeRouting(output.args, env);
|
|
72
|
+
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...output.args, "--json"] : output.args, io, io.stdin, env, scopeProjectRoot);
|
|
65
73
|
if (routed?.routed && !routed.inProcess) {
|
|
66
74
|
emitHumanProgress(io, output, progress, "done");
|
|
67
75
|
return routed.exitCode;
|
|
68
76
|
}
|
|
69
77
|
const dispatchContext = routed?.inProcess
|
|
70
|
-
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version })
|
|
71
|
-
:
|
|
78
|
+
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(output.args, classification))
|
|
79
|
+
: createContext(env, databaseModeForCommand(output.args, classification));
|
|
72
80
|
preflightCliCompatibility(dispatchContext, output.args, env, scopeProjectRoot);
|
|
73
|
-
const result = await dispatch(output.args, dispatchContext, io, scopeProjectRoot);
|
|
74
|
-
const refreshWarnings = refreshDashboardsAfterMutation(
|
|
81
|
+
const result = await dispatch(output.args, dispatchContext, io, scopeProjectRoot, classification);
|
|
82
|
+
const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
|
|
75
83
|
const resultWithWarnings = addWarnings(result, refreshWarnings);
|
|
76
84
|
emitHumanProgress(io, output, progress, "done");
|
|
77
85
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
@@ -94,6 +102,34 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
94
102
|
return isAppError(error) ? error.exitCode : 1;
|
|
95
103
|
}
|
|
96
104
|
}
|
|
105
|
+
function resolveProjectRootBeforeRouting(args, env) {
|
|
106
|
+
const explicit = resolveExplicitProjectRoot(args);
|
|
107
|
+
if (explicit)
|
|
108
|
+
return explicit;
|
|
109
|
+
try {
|
|
110
|
+
// Router identity lookup is read-only; it must not initialize or migrate runtime storage.
|
|
111
|
+
return resolveOperationProjectRoot(createContext(env, "read_existing"), args);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function databaseModeForCommand(args, classification) {
|
|
118
|
+
const [family, command] = args;
|
|
119
|
+
if (family === "engine" ||
|
|
120
|
+
family === "version" ||
|
|
121
|
+
family === "schema" ||
|
|
122
|
+
family === "canon" && command === "resolve" ||
|
|
123
|
+
classification.mode === "read_only_diagnostics" ||
|
|
124
|
+
classification.mode === "mb_upgrade" && (family === "status" ||
|
|
125
|
+
family === "engine" ||
|
|
126
|
+
family === "migration" && ["impact", "plan", "report", "verify", "apply"].includes(command ?? "") ||
|
|
127
|
+
family === "memory" && command === "permissions" ||
|
|
128
|
+
family === "dashboard" && ["data", "open"].includes(command ?? "") ||
|
|
129
|
+
family === "session" && command === "status"))
|
|
130
|
+
return "read_existing";
|
|
131
|
+
return "initialize";
|
|
132
|
+
}
|
|
97
133
|
function renderHumanError(payload) {
|
|
98
134
|
const lines = [`dd-flow: ${payload.error.message}`];
|
|
99
135
|
const details = recordObject(payload.error.details);
|
|
@@ -380,11 +416,11 @@ function stringValue(value) {
|
|
|
380
416
|
function shortSha(value) {
|
|
381
417
|
return value ? value.slice(0, 12) : "unknown";
|
|
382
418
|
}
|
|
383
|
-
async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
419
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env)) {
|
|
384
420
|
const [family, command, ...rest] = args;
|
|
385
421
|
const parsed = parseArgs(rest);
|
|
386
422
|
if (family === "engine") {
|
|
387
|
-
return dispatchEngine(context, command, parsed);
|
|
423
|
+
return dispatchEngine(context, command, parsed, classification);
|
|
388
424
|
}
|
|
389
425
|
if (family === "status") {
|
|
390
426
|
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
@@ -597,13 +633,13 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
597
633
|
return dispatchDashboard(context, command, parsed);
|
|
598
634
|
}
|
|
599
635
|
if (family === "schema") {
|
|
600
|
-
return dispatchSchema(command, parsed);
|
|
636
|
+
return dispatchSchema(context, command, parsed);
|
|
601
637
|
}
|
|
602
638
|
if (family === "memory") {
|
|
603
639
|
return dispatchMemory(command, parsed);
|
|
604
640
|
}
|
|
605
641
|
if (family === "migration") {
|
|
606
|
-
return dispatchMigration(context, command, parsed);
|
|
642
|
+
return dispatchMigration(context, command, parsed, classification);
|
|
607
643
|
}
|
|
608
644
|
if (family === "codex" && command === "hooks") {
|
|
609
645
|
return dispatchCodexHooks(context, parsed);
|
|
@@ -619,7 +655,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null) {
|
|
|
619
655
|
}
|
|
620
656
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
621
657
|
}
|
|
622
|
-
function dispatchEngine(context, command, parsed) {
|
|
658
|
+
function dispatchEngine(context, command, parsed, classification) {
|
|
623
659
|
if (command === "install") {
|
|
624
660
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
625
661
|
}
|
|
@@ -630,14 +666,32 @@ function dispatchEngine(context, command, parsed) {
|
|
|
630
666
|
return engineInfo(context, { packageName: optionalOption(parsed, "package"), version: optionalOption(parsed, "version") });
|
|
631
667
|
}
|
|
632
668
|
if (command === "resolve") {
|
|
633
|
-
return resolveEngine(context, {
|
|
669
|
+
return resolveEngine(context, {
|
|
670
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
671
|
+
env: context.env,
|
|
672
|
+
classification
|
|
673
|
+
});
|
|
634
674
|
}
|
|
635
675
|
if (command === "doctor") {
|
|
636
|
-
return doctorEngines(context, {
|
|
676
|
+
return doctorEngines(context, {
|
|
677
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
678
|
+
env: context.env,
|
|
679
|
+
classification
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
if (command === "bind-run") {
|
|
683
|
+
return bindRunEngine(context, {
|
|
684
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
685
|
+
runId: requiredPosition(parsed, 0, "run-id"),
|
|
686
|
+
packageName: optionalOption(parsed, "package"),
|
|
687
|
+
engineVersion: requiredOption(parsed, "engine-version"),
|
|
688
|
+
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
689
|
+
reason: requiredOption(parsed, "reason")
|
|
690
|
+
});
|
|
637
691
|
}
|
|
638
692
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
639
693
|
}
|
|
640
|
-
function dispatchMigration(context, command, parsed) {
|
|
694
|
+
function dispatchMigration(context, command, parsed, classification) {
|
|
641
695
|
if (command === "impact")
|
|
642
696
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
643
697
|
if (command === "plan" || command === "report") {
|
|
@@ -648,12 +702,26 @@ function dispatchMigration(context, command, parsed) {
|
|
|
648
702
|
runId: optionalOption(parsed, "run"),
|
|
649
703
|
backupPath: optionalOption(parsed, "backup-path"),
|
|
650
704
|
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
651
|
-
allowActive:
|
|
705
|
+
allowActive: false,
|
|
706
|
+
classification
|
|
652
707
|
});
|
|
653
708
|
}
|
|
654
709
|
if (command === "verify") {
|
|
655
710
|
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
656
711
|
}
|
|
712
|
+
if (command === "apply") {
|
|
713
|
+
return applyMigration(context, {
|
|
714
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
715
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
716
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
717
|
+
runId: optionalOption(parsed, "run"),
|
|
718
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
719
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
720
|
+
reportFile: optionalOption(parsed, "file"),
|
|
721
|
+
allowActive: false,
|
|
722
|
+
classification
|
|
723
|
+
});
|
|
724
|
+
}
|
|
657
725
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
658
726
|
}
|
|
659
727
|
function dispatchCanon(context, command, parsed) {
|
|
@@ -682,6 +750,14 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
682
750
|
workspacePath: workspacePathOption(parsed)
|
|
683
751
|
});
|
|
684
752
|
}
|
|
753
|
+
if (command === "claim") {
|
|
754
|
+
return claimMergeJob(context, {
|
|
755
|
+
protocolId: requiredPosition(parsed, 0, "protocol-id"),
|
|
756
|
+
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
757
|
+
workerId: requiredWorkerId(parsed),
|
|
758
|
+
workspacePath: workspacePathOption(parsed)
|
|
759
|
+
});
|
|
760
|
+
}
|
|
685
761
|
if (command === "wait-next") {
|
|
686
762
|
return waitNextMergeJob(context, {
|
|
687
763
|
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
@@ -887,6 +963,9 @@ function dispatchSession(context, command, parsed) {
|
|
|
887
963
|
}
|
|
888
964
|
function dispatchRun(context, command, parsed) {
|
|
889
965
|
if (command === "start") {
|
|
966
|
+
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
967
|
+
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
968
|
+
}
|
|
890
969
|
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
891
970
|
return startFlowRun(context, {
|
|
892
971
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
@@ -1144,7 +1223,7 @@ function withDashboardGuidance(action, target, result, options = {}) {
|
|
|
1144
1223
|
related_commands: relatedDashboardCommands(action, target.kind)
|
|
1145
1224
|
};
|
|
1146
1225
|
}
|
|
1147
|
-
function dispatchSchema(command, parsed) {
|
|
1226
|
+
function dispatchSchema(context, command, parsed) {
|
|
1148
1227
|
if (command === "validate") {
|
|
1149
1228
|
const schemaDir = optionalOption(parsed, "schema-dir");
|
|
1150
1229
|
const projectRoot = optionalOption(parsed, "project-root");
|
|
@@ -1152,7 +1231,8 @@ function dispatchSchema(command, parsed) {
|
|
|
1152
1231
|
schemaName: requiredOption(parsed, "schema"),
|
|
1153
1232
|
file: requiredOption(parsed, "file"),
|
|
1154
1233
|
...(schemaDir ? { schemaDir } : {}),
|
|
1155
|
-
...(projectRoot ? { projectRoot } : {})
|
|
1234
|
+
...(projectRoot ? { projectRoot } : {}),
|
|
1235
|
+
ddFlowHome: context.ddFlowHome
|
|
1156
1236
|
});
|
|
1157
1237
|
}
|
|
1158
1238
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
@@ -1679,7 +1759,7 @@ function projectRootForLaneMutation(command, parsed) {
|
|
|
1679
1759
|
return ["acquire", "heartbeat", "release", "wait-acquire"].includes(action) ? requiredOption(parsed, "project-root") : undefined;
|
|
1680
1760
|
}
|
|
1681
1761
|
function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
1682
|
-
if (["next", "wait-next"].includes(command ?? "")) {
|
|
1762
|
+
if (["next", "wait-next", "claim"].includes(command ?? "")) {
|
|
1683
1763
|
return requiredOption(parsed, "project-root");
|
|
1684
1764
|
}
|
|
1685
1765
|
if (["complete", "fail", "cancel"].includes(command ?? "")) {
|
|
@@ -1700,7 +1780,7 @@ function isScopedProtocolMutation(family, command) {
|
|
|
1700
1780
|
if (family === "worktree")
|
|
1701
1781
|
return ["create", "bootstrap", "close"].includes(command ?? "");
|
|
1702
1782
|
if (family === "merge-queue")
|
|
1703
|
-
return ["complete", "note", "fail", "cancel"].includes(command ?? "");
|
|
1783
|
+
return ["claim", "complete", "note", "fail", "cancel"].includes(command ?? "");
|
|
1704
1784
|
return false;
|
|
1705
1785
|
}
|
|
1706
1786
|
function projectRootFromResult(result) {
|
|
@@ -64,6 +64,17 @@ export function loadProjectFlowContract(projectRoot) {
|
|
|
64
64
|
if (!contractPath) {
|
|
65
65
|
return defaultFlowContract;
|
|
66
66
|
}
|
|
67
|
+
return loadFlowContractFile(contractPath);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Canonical-only flows execute against the pinned canonical contract before
|
|
71
|
+
* the target project flow pack is synchronized. Keep this path explicit so a
|
|
72
|
+
* stale project-local contract cannot become the upgrade RUN authority.
|
|
73
|
+
*/
|
|
74
|
+
export function loadCanonicalFlowContract(flowRoot) {
|
|
75
|
+
return loadFlowContractFile(path.join(flowRoot, "flow-contract.json"));
|
|
76
|
+
}
|
|
77
|
+
function loadFlowContractFile(contractPath) {
|
|
67
78
|
try {
|
|
68
79
|
const value = readFlowContractObject(contractPath);
|
|
69
80
|
validateFlowContractStructure(value);
|
package/dist/runtime/context.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { getDatabase } from "../storage/database.js";
|
|
2
2
|
import { resolveDdFlowHome } from "../storage/paths.js";
|
|
3
|
-
export function
|
|
3
|
+
export function createRouterContext(env) {
|
|
4
|
+
return {
|
|
5
|
+
ddFlowHome: resolveDdFlowHome(env),
|
|
6
|
+
now: () => new Date().toISOString()
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function createContext(env, mode = "initialize") {
|
|
4
10
|
const ddFlowHome = resolveDdFlowHome(env);
|
|
5
11
|
return {
|
|
6
12
|
ddFlowHome,
|
|
7
|
-
db: getDatabase(ddFlowHome),
|
|
13
|
+
db: getDatabase(ddFlowHome, mode),
|
|
8
14
|
env,
|
|
9
15
|
now: () => new Date().toISOString()
|
|
10
16
|
};
|
|
@@ -124,10 +124,15 @@
|
|
|
124
124
|
"run": {
|
|
125
125
|
"type": "object",
|
|
126
126
|
"additionalProperties": true,
|
|
127
|
-
"required": ["run_id"
|
|
127
|
+
"required": ["run_id"],
|
|
128
|
+
"anyOf": [
|
|
129
|
+
{"required": ["run_state"]},
|
|
130
|
+
{"required": ["run_index"]}
|
|
131
|
+
],
|
|
128
132
|
"properties": {
|
|
129
133
|
"run_id": { "type": "string", "pattern": "^RUN-[0-9]{3}-[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
130
|
-
"run_state": { "type": "string", "minLength": 1 }
|
|
134
|
+
"run_state": { "type": "string", "minLength": 1 },
|
|
135
|
+
"run_index": { "type": "string", "minLength": 1 }
|
|
131
136
|
}
|
|
132
137
|
},
|
|
133
138
|
"stage": {
|
|
@@ -44,6 +44,28 @@
|
|
|
44
44
|
"items": { "type": "string", "minLength": 1 }
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
+
"schema_registry": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["schema_id", "entries"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"schema_id": { "const": "dd-flow/engine-schema-registry@1" },
|
|
53
|
+
"entries": {
|
|
54
|
+
"type": "array",
|
|
55
|
+
"items": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"additionalProperties": false,
|
|
58
|
+
"required": ["name", "id", "path", "checksum"],
|
|
59
|
+
"properties": {
|
|
60
|
+
"name": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
61
|
+
"id": { "type": "string", "minLength": 1 },
|
|
62
|
+
"path": { "type": "string", "pattern": "^dist/schemas/[a-z0-9][a-z0-9-]*\\.schema\\.json$" },
|
|
63
|
+
"checksum": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
47
69
|
"integrity": {
|
|
48
70
|
"type": "object",
|
|
49
71
|
"additionalProperties": false,
|
|
@@ -123,6 +123,7 @@
|
|
|
123
123
|
"expected": {"type": "array", "items": {"type": "string"}},
|
|
124
124
|
"observed": {"type": "array", "items": {"type": "string"}},
|
|
125
125
|
"missing": {"type": "array", "items": {"type": "string"}},
|
|
126
|
+
"diagnostics": {"type": "array", "items": {"type": "string"}},
|
|
126
127
|
"reason": {"type": "string"}
|
|
127
128
|
}
|
|
128
129
|
},
|
|
@@ -70,12 +70,14 @@
|
|
|
70
70
|
},
|
|
71
71
|
"backup": {
|
|
72
72
|
"type": "object",
|
|
73
|
-
"required": ["status", "path", "created_at", "rollback_route"],
|
|
73
|
+
"required": ["status", "path", "created_at", "rollback_route", "size_bytes", "sha256"],
|
|
74
74
|
"additionalProperties": true,
|
|
75
75
|
"properties": {
|
|
76
76
|
"status": { "enum": ["present", "planned", "missing"] },
|
|
77
77
|
"path": { "type": ["string", "null"] },
|
|
78
78
|
"created_at": { "type": ["string", "null"] },
|
|
79
|
+
"size_bytes": { "type": ["integer", "null"], "minimum": 1 },
|
|
80
|
+
"sha256": { "type": ["string", "null"], "pattern": "^[a-f0-9]{64}$" },
|
|
79
81
|
"rollback_route": { "type": "string", "minLength": 1 }
|
|
80
82
|
}
|
|
81
83
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "dd-flow/merge-stage-report@2/engine-0.4.2",
|
|
4
|
+
"title": "Merge stage report emitted by dd-flow engine 0.4.2",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": true,
|
|
7
|
+
"required": ["schema_id", "run", "stage", "project", "subject", "overall", "breadcrumbs", "implemented_goals", "acceptance_scenarios", "changed_files", "checks", "review", "review_gates", "defs"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_id": { "const": "dd-flow/merge-stage-report@2" },
|
|
10
|
+
"run": { "type": "object", "required": ["run_id", "run_index"], "properties": { "run_id": { "type": "string", "pattern": "^RUN-[0-9]{3}-[a-z0-9]+(?:-[a-z0-9]+)*$" }, "run_index": { "type": "string", "minLength": 1 } } },
|
|
11
|
+
"stage": { "type": "object", "required": ["name", "dir", "status"] },
|
|
12
|
+
"project": { "type": "object", "required": ["id", "title"] },
|
|
13
|
+
"subject": { "type": "object", "required": ["id", "title"] },
|
|
14
|
+
"overall": { "type": "object", "required": ["verdict", "summary", "next_action"], "properties": { "verdict": { "enum": ["accepted", "blocked", "failed"] }, "summary": { "type": "string", "minLength": 1 }, "next_action": { "type": "string", "minLength": 1 } } },
|
|
15
|
+
"breadcrumbs": { "type": "array" },
|
|
16
|
+
"implemented_goals": { "type": "array" },
|
|
17
|
+
"acceptance_scenarios": { "type": "array" },
|
|
18
|
+
"changed_files": { "type": "array" },
|
|
19
|
+
"checks": { "type": "array" },
|
|
20
|
+
"review": { "type": "object" },
|
|
21
|
+
"review_gates": { "type": "array" },
|
|
22
|
+
"defs": { "type": "array" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "dd-flow/run-engine-binding@1",
|
|
4
|
+
"title": "Immutable RUN engine binding",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_id", "run_id", "project_root", "bound_at", "source", "reason", "engine", "probe"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_id": {"const": "dd-flow/run-engine-binding@1"},
|
|
10
|
+
"run_id": {"type": "string", "pattern": "^RUN-[0-9]+-[a-z0-9][a-z0-9-]*$"},
|
|
11
|
+
"project_root": {"type": "string", "minLength": 1},
|
|
12
|
+
"bound_at": {"type": "string", "format": "date-time"},
|
|
13
|
+
"source": {"enum": ["run_creation", "legacy_recovery"]},
|
|
14
|
+
"reason": {"type": "string", "minLength": 1},
|
|
15
|
+
"engine": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"required": ["package_name", "package_version", "engine_version", "integrity_checksum", "snapshot_root"],
|
|
19
|
+
"properties": {
|
|
20
|
+
"package_name": {"type": "string", "minLength": 1},
|
|
21
|
+
"package_version": {"type": "string", "minLength": 1},
|
|
22
|
+
"engine_version": {"type": "string", "minLength": 1},
|
|
23
|
+
"integrity_checksum": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
|
|
24
|
+
"snapshot_root": {"type": "string", "minLength": 1}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"probe": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"required": ["status", "command"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"status": {"enum": ["not_required", "passed"]},
|
|
33
|
+
"command": {"type": ["string", "null"]}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"stage": {"type": "string", "minLength": 1},
|
|
24
24
|
"generated_at": {"type": "string", "format": "date-time"},
|
|
25
25
|
"prompt_path": {"type": "string", "minLength": 1},
|
|
26
|
-
"sections": {"
|
|
26
|
+
"sections": {"const": ["stage_identity", "authoritative_runtime_facts", "preflight", "task_intake", "applicable_instructions", "required_context", "work_contract", "completion_contract"]},
|
|
27
27
|
"aliases": {"type": "object", "required": ["project", "workspace", "run", "stage", "protocol", "intake"], "additionalProperties": {"type": "string"}},
|
|
28
28
|
"write_boundary": {"type": "object", "required": ["current", "archive", "archive_writable"], "properties": {"current": {"const": "@stage"}, "archive": {"pattern": "try-[0-9]{3}"}, "archive_writable": {"const": false}}, "additionalProperties": false},
|
|
29
29
|
"source_fragments": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
30
|
-
"authoritative_facts": {"type": "object", "additionalProperties": true},
|
|
31
|
-
"preflight": {"type": "object", "additionalProperties": true},
|
|
32
|
-
"required_context": {"type": "array", "items": {"type": "string", "minLength": 1}},
|
|
30
|
+
"authoritative_facts": {"type": "object", "required": ["git", "runtime"], "additionalProperties": true},
|
|
31
|
+
"preflight": {"type": "object", "required": ["compatibility", "permissions", "session_binding"], "additionalProperties": true},
|
|
32
|
+
"required_context": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["path", "reason", "stop_condition"], "properties": {"path": {"type": "string", "minLength": 1}, "reason": {"type": "string", "minLength": 1}, "stop_condition": {"type": "string", "minLength": 1}}}},
|
|
33
33
|
"worker_prompt_markdown": {"type": "string", "minLength": 1}
|
|
34
34
|
}
|
|
35
35
|
}
|
package/dist/services/canon.js
CHANGED
|
@@ -36,7 +36,21 @@ export function getCanonStatus(context, input = {}) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
export function resolveCanonRoot(context, input = {}) {
|
|
39
|
-
|
|
39
|
+
return resolveCanonCandidates(canonCandidates(context, input));
|
|
40
|
+
}
|
|
41
|
+
/** Read only: resolve an explicit/environment/nearby canon without opening DD_FLOW_HOME. */
|
|
42
|
+
export function resolveCanonRootBeforeContext(env, input = {}) {
|
|
43
|
+
const candidates = [];
|
|
44
|
+
if (input.explicitRoot)
|
|
45
|
+
candidates.push(validateCanonCandidate("explicit", input.explicitRoot));
|
|
46
|
+
else if (env.DD_MEMORYBANK)
|
|
47
|
+
candidates.push(validateCanonCandidate("env", env.DD_MEMORYBANK));
|
|
48
|
+
else
|
|
49
|
+
for (const known of knownCanonRoots())
|
|
50
|
+
candidates.push(validateCanonCandidate("known", known));
|
|
51
|
+
return resolveCanonCandidates(uniqueBy(candidates, (candidate) => `${candidate.source}:${candidate.root}`));
|
|
52
|
+
}
|
|
53
|
+
function resolveCanonCandidates(candidates) {
|
|
40
54
|
const validCandidates = candidates.filter((candidate) => candidate.valid && candidate.realpath);
|
|
41
55
|
const uniqueValid = uniqueBy(validCandidates, (candidate) => candidate.realpath ?? candidate.root);
|
|
42
56
|
const blockers = candidates.flatMap((candidate) => candidate.blockers);
|