@deksden-com/dd-flow-cli 0.2.0 → 0.3.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +76 -5
  3. package/dist/build-info.json +10 -6
  4. package/dist/cli/help.js +165 -20
  5. package/dist/cli/run-cli.js +427 -17
  6. package/dist/schemas/compatibility.schema.json +105 -0
  7. package/dist/schemas/engine-manifest.schema.json +61 -0
  8. package/dist/schemas/flow-guidance.schema.json +90 -0
  9. package/dist/schemas/flow-run-index.schema.json +30 -4
  10. package/dist/schemas/global-dashboard-data.schema.json +126 -0
  11. package/dist/schemas/mb-sdlc-review-report.schema.json +242 -0
  12. package/dist/schemas/mb-upgrade-migration-report.schema.json +93 -0
  13. package/dist/schemas/plan-stage-report.schema.json +83 -0
  14. package/dist/schemas/project-dashboard-data.schema.json +122 -0
  15. package/dist/schemas/project-flow-pack-manifest.schema.json +5 -1
  16. package/dist/schemas/project-summary.schema.json +73 -0
  17. package/dist/schemas/protocol-dashboard-data.schema.json +112 -0
  18. package/dist/schemas/status-report.schema.json +38 -2
  19. package/dist/schemas/version-report.schema.json +22 -0
  20. package/dist/services/build-info.js +26 -3
  21. package/dist/services/canon.js +93 -22
  22. package/dist/services/cleanup.js +45 -1
  23. package/dist/services/cli-operation-classifier.js +104 -0
  24. package/dist/services/compatibility-preflight.js +124 -0
  25. package/dist/services/config.js +31 -0
  26. package/dist/services/dashboard-targets.js +95 -0
  27. package/dist/services/dashboard.js +972 -10
  28. package/dist/services/engines.js +532 -0
  29. package/dist/services/flow-guidance.js +221 -0
  30. package/dist/services/hooks.js +1 -1
  31. package/dist/services/ids.js +106 -0
  32. package/dist/services/lanes.js +333 -1
  33. package/dist/services/merge-queue.js +106 -16
  34. package/dist/services/merge-worker.js +67 -4
  35. package/dist/services/migrations.js +231 -0
  36. package/dist/services/project-summary.js +122 -0
  37. package/dist/services/projects.js +44 -3
  38. package/dist/services/protocol-lifecycle.js +144 -0
  39. package/dist/services/protocols.js +660 -7
  40. package/dist/services/runs.js +98 -21
  41. package/dist/services/schema-validation.js +84 -4
  42. package/dist/services/sessions.js +21 -4
  43. package/dist/services/status.js +199 -1
  44. package/dist/services/version-status.js +59 -9
  45. package/dist/storage/database.js +31 -0
  46. package/dist/storage/paths.js +33 -0
  47. package/package.json +3 -2
@@ -22,7 +22,8 @@ export function getProjectVersionStatus(input) {
22
22
  const flowPack = readFlowPackMetadata(input.projectRoot, memoryBank.root);
23
23
  const memoryBankDrift = compareMemoryBankVersion(memoryBank.version, input.canon?.metadata.version ?? null, memoryBank.status);
24
24
  const flowPackDrift = compareFlowPackCommit(flowPack.source_commit, input.canon?.metadata.commit ?? null, input.canon?.root ?? null, flowPack.status);
25
- const overall = overallDrift(memoryBankDrift, flowPackDrift, memoryBank.status, flowPack.status);
25
+ const flowContractDrift = compareFlowContract(flowPack.flow_contract, input.canon?.metadata.flow_contract ?? null, flowPack.status);
26
+ const overall = overallDrift(memoryBankDrift, flowPackDrift, flowContractDrift, memoryBank.status, flowPack.status);
26
27
  return {
27
28
  root: input.projectRoot,
28
29
  root_source: input.rootSource,
@@ -31,8 +32,9 @@ export function getProjectVersionStatus(input) {
31
32
  drift: {
32
33
  memory_bank_version: memoryBankDrift,
33
34
  flow_pack_commit: flowPackDrift,
35
+ flow_contract: flowContractDrift,
34
36
  overall,
35
- next_action: nextAction(overall, memoryBank.status, flowPack.status)
37
+ next_action: nextAction(overall, memoryBank.status, flowPack.status, flowContractDrift.status)
36
38
  }
37
39
  };
38
40
  }
@@ -62,8 +64,12 @@ function readFlowPackMetadata(projectRoot, memoryBankRoot) {
62
64
  manifest_path: null,
63
65
  schema_id: null,
64
66
  pack_version: null,
67
+ canon_root: null,
68
+ canon_memory_bank_root: null,
69
+ canon_flow_root: null,
65
70
  source_commit: null,
66
71
  canon_version_at_source_commit: null,
72
+ flow_contract: null,
67
73
  status: "missing",
68
74
  reason: "no_memory_bank",
69
75
  canonical_only_files: []
@@ -77,8 +83,12 @@ function readFlowPackMetadata(projectRoot, memoryBankRoot) {
77
83
  manifest_path: path.relative(projectRoot, manifestPath),
78
84
  schema_id: null,
79
85
  pack_version: null,
86
+ canon_root: null,
87
+ canon_memory_bank_root: null,
88
+ canon_flow_root: null,
80
89
  source_commit: null,
81
90
  canon_version_at_source_commit: null,
91
+ flow_contract: null,
82
92
  status: "missing",
83
93
  reason: "flow_pack_manifest_missing",
84
94
  canonical_only_files: canonicalOnlyFiles
@@ -96,8 +106,12 @@ function readFlowPackMetadata(projectRoot, memoryBankRoot) {
96
106
  manifest_path: path.relative(projectRoot, manifestPath),
97
107
  schema_id: null,
98
108
  pack_version: null,
109
+ canon_root: null,
110
+ canon_memory_bank_root: null,
111
+ canon_flow_root: null,
99
112
  source_commit: null,
100
113
  canon_version_at_source_commit: null,
114
+ flow_contract: null,
101
115
  status: "invalid",
102
116
  reason: "flow_pack_manifest_invalid_json",
103
117
  canonical_only_files: canonicalOnlyFiles
@@ -106,14 +120,35 @@ function readFlowPackMetadata(projectRoot, memoryBankRoot) {
106
120
  const schemaId = stringOrNull(manifest.schema_id);
107
121
  const canonVersion = stringOrNull(manifest.canon_version_at_source_commit) ?? stringOrNull(manifest.canon_version);
108
122
  const sourceCommit = stringOrNull(manifest.source_commit);
109
- const status = schemaId === "dd-flow/project-flow-pack-manifest@2" && canonVersion ? "present" : schemaId === "dd-flow/project-flow-pack-manifest@1" ? "degraded" : "invalid";
110
- const reason = status === "degraded" ? "legacy_flow_pack_manifest" : status === "invalid" ? "flow_pack_manifest_schema_unknown" : undefined;
123
+ const canonRoot = stringOrNull(manifest.canon_root);
124
+ const canonMemoryBankRoot = stringOrNull(manifest.canon_memory_bank_root);
125
+ const canonFlowRoot = stringOrNull(manifest.canon_flow_root);
126
+ const flowContract = stringOrNull(manifest.flow_contract);
127
+ const status = schemaId === "dd-flow/project-flow-pack-manifest@2" && canonVersion && canonMemoryBankRoot && canonFlowRoot && flowContract
128
+ ? "present"
129
+ : schemaId === "dd-flow/project-flow-pack-manifest@1" ||
130
+ (schemaId === "dd-flow/project-flow-pack-manifest@2" && canonVersion && (!canonMemoryBankRoot || !canonFlowRoot || !flowContract))
131
+ ? "degraded"
132
+ : "invalid";
133
+ const reason = status === "degraded" && schemaId === "dd-flow/project-flow-pack-manifest@2" && (!canonMemoryBankRoot || !canonFlowRoot)
134
+ ? "flow_pack_manifest_missing_explicit_canon_roots"
135
+ : status === "degraded" && schemaId === "dd-flow/project-flow-pack-manifest@2" && !flowContract
136
+ ? "flow_pack_manifest_missing_flow_contract"
137
+ : status === "degraded"
138
+ ? "legacy_flow_pack_manifest"
139
+ : status === "invalid"
140
+ ? "flow_pack_manifest_schema_unknown"
141
+ : undefined;
111
142
  return {
112
143
  manifest_path: path.relative(projectRoot, manifestPath),
113
144
  schema_id: schemaId,
114
145
  pack_version: stringOrNull(manifest.pack_version),
146
+ canon_root: canonRoot,
147
+ canon_memory_bank_root: canonMemoryBankRoot,
148
+ canon_flow_root: canonFlowRoot,
115
149
  source_commit: sourceCommit,
116
150
  canon_version_at_source_commit: canonVersion,
151
+ flow_contract: flowContract,
117
152
  status,
118
153
  ...(reason ? { reason } : {}),
119
154
  canonical_only_files: canonicalOnlyFiles
@@ -190,28 +225,43 @@ function compareFlowPackCommit(current, target, canonRoot, artifactStatus) {
190
225
  target
191
226
  };
192
227
  }
193
- function overallDrift(memoryBank, flowPack, memoryStatus, flowStatus) {
228
+ function compareFlowContract(current, target, artifactStatus) {
229
+ if (artifactStatus === "missing")
230
+ return { status: "missing", confidence: "none", reason: "flow_pack_manifest_missing", current, target };
231
+ if (artifactStatus === "invalid")
232
+ return { status: "invalid", confidence: "none", reason: "flow_pack_manifest_invalid", current, target };
233
+ if (!current)
234
+ return { status: "unknown", confidence: "none", reason: "flow_pack_flow_contract_missing", current, target };
235
+ if (!target)
236
+ return { status: "unknown", confidence: "none", reason: "canon_flow_contract_unknown", current, target };
237
+ if (current === target)
238
+ return { status: "same", confidence: "exact", reason: "flow_contract_equal", current, target };
239
+ return { status: "diverged", confidence: "declared", reason: "flow_contract_mismatch", current, target };
240
+ }
241
+ function overallDrift(memoryBank, flowPack, flowContract, memoryStatus, flowStatus) {
194
242
  if (memoryStatus === "missing")
195
243
  return "not_applicable";
196
- if (memoryBank.status === "invalid" || flowPack.status === "invalid")
244
+ if (memoryBank.status === "invalid" || flowPack.status === "invalid" || flowContract.status === "invalid")
197
245
  return "invalid";
198
- if (memoryBank.status === "missing" || flowPack.status === "missing")
246
+ if (memoryBank.status === "missing" || flowPack.status === "missing" || flowContract.status === "missing")
199
247
  return "missing";
200
248
  if (memoryBank.status === "ahead" || flowPack.status === "ahead")
201
249
  return "ahead";
202
250
  if (memoryBank.status === "behind" || flowPack.status === "behind")
203
251
  return "behind";
204
- if (memoryBank.status === "diverged" || flowPack.status === "diverged")
252
+ if (memoryBank.status === "diverged" || flowPack.status === "diverged" || flowContract.status === "diverged")
205
253
  return "diverged";
206
254
  if (memoryBank.status === "same" && (flowPack.status === "same" || flowStatus === "degraded"))
207
255
  return flowStatus === "degraded" ? "unknown" : "same";
208
256
  return "unknown";
209
257
  }
210
- function nextAction(overall, memoryStatus, flowStatus) {
258
+ function nextAction(overall, memoryStatus, flowStatus, flowContractStatus) {
211
259
  if (overall === "same")
212
260
  return "no_action";
213
261
  if (overall === "not_applicable" && memoryStatus === "missing")
214
262
  return "run_mb_init_if_project_needs_memory_bank";
263
+ if (flowContractStatus === "diverged" || flowContractStatus === "unknown")
264
+ return "run_canonical_mb_upgrade_before_lifecycle_mutations";
215
265
  if (overall === "behind" || flowStatus === "degraded")
216
266
  return "run_canonical_mb_upgrade";
217
267
  if (overall === "missing")
@@ -134,10 +134,34 @@ function migrate(db) {
134
134
  FOREIGN KEY(project_id) REFERENCES projects(id)
135
135
  );
136
136
 
137
+ CREATE TABLE IF NOT EXISTS lane_waiters (
138
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
139
+ project_id TEXT NOT NULL,
140
+ lane TEXT NOT NULL,
141
+ worker_id TEXT NOT NULL,
142
+ status TEXT NOT NULL,
143
+ queued_at TEXT NOT NULL,
144
+ acquired_at TEXT,
145
+ cancelled_at TEXT,
146
+ expires_at TEXT,
147
+ reason TEXT,
148
+ metadata_json TEXT NOT NULL,
149
+ created_at TEXT NOT NULL,
150
+ updated_at TEXT NOT NULL,
151
+ FOREIGN KEY(project_id) REFERENCES projects(id)
152
+ );
153
+
137
154
  CREATE UNIQUE INDEX IF NOT EXISTS idx_lane_locks_one_active
138
155
  ON lane_locks(project_id, lane)
139
156
  WHERE status = 'active';
140
157
 
158
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_lane_waiters_one_queued_worker
159
+ ON lane_waiters(project_id, lane, worker_id)
160
+ WHERE status = 'queued';
161
+
162
+ CREATE INDEX IF NOT EXISTS idx_lane_waiters_fifo
163
+ ON lane_waiters(project_id, lane, status, queued_at, id);
164
+
141
165
  CREATE TABLE IF NOT EXISTS hook_installations (
142
166
  project_id TEXT NOT NULL,
143
167
  scope TEXT NOT NULL,
@@ -243,6 +267,9 @@ function migrate(db) {
243
267
  runtime_path TEXT NOT NULL,
244
268
  run_dir TEXT NOT NULL,
245
269
  run_index_path TEXT NOT NULL,
270
+ run_home_path TEXT,
271
+ layout_version TEXT,
272
+ artifact_root_kind TEXT,
246
273
  index_json TEXT NOT NULL,
247
274
  created_at TEXT NOT NULL,
248
275
  updated_at TEXT NOT NULL,
@@ -318,6 +345,10 @@ function migrate(db) {
318
345
  ensureColumn(db, "flow_sessions", "next_action", "ALTER TABLE flow_sessions ADD COLUMN next_action TEXT");
319
346
  ensureColumn(db, "flow_sessions", "metadata_json", "ALTER TABLE flow_sessions ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}'");
320
347
  ensureColumn(db, "flow_sessions", "run_id", "ALTER TABLE flow_sessions ADD COLUMN run_id TEXT");
348
+ ensureColumn(db, "flow_runs", "run_home_path", "ALTER TABLE flow_runs ADD COLUMN run_home_path TEXT");
349
+ ensureColumn(db, "flow_runs", "layout_version", "ALTER TABLE flow_runs ADD COLUMN layout_version TEXT");
350
+ ensureColumn(db, "flow_runs", "artifact_root_kind", "ALTER TABLE flow_runs ADD COLUMN artifact_root_kind TEXT");
351
+ ensureColumn(db, "lane_waiters", "expires_at", "ALTER TABLE lane_waiters ADD COLUMN expires_at TEXT");
321
352
  }
322
353
  function ensureColumn(db, table, column, sql) {
323
354
  const columns = db.prepare(`PRAGMA table_info(${table})`).all();
@@ -33,12 +33,30 @@ export function runtimeRunDir(ddFlowHome, projectId, runId) {
33
33
  export function runtimeRunJsonPath(ddFlowHome, projectId, runId) {
34
34
  return path.join(runtimeRunDir(ddFlowHome, projectId, runId), "run.json");
35
35
  }
36
+ export function projectRunHome(ddFlowHome, projectId, runId) {
37
+ return path.join(projectHome(ddFlowHome, projectId), "runs", runId);
38
+ }
39
+ export function projectRunJsonPath(ddFlowHome, projectId, runId) {
40
+ return path.join(projectRunHome(ddFlowHome, projectId, runId), "run.json");
41
+ }
42
+ export function projectRunIndexPath(ddFlowHome, projectId, runId) {
43
+ return path.join(projectRunHome(ddFlowHome, projectId, runId), "run-index.json");
44
+ }
45
+ export function projectRunStageDir(ddFlowHome, projectId, runId, stageDir) {
46
+ return path.join(projectRunHome(ddFlowHome, projectId, runId), stageDir);
47
+ }
36
48
  export function userFacingRunDir(workspaceRoot, runId) {
37
49
  return path.join(workspaceRoot, ".tasks", "dd-flow-runs", runId);
38
50
  }
51
+ export function legacyWorkspaceRunDir(workspaceRoot, runId) {
52
+ return userFacingRunDir(workspaceRoot, runId);
53
+ }
39
54
  export function userFacingRunIndexPath(workspaceRoot, runId) {
40
55
  return path.join(userFacingRunDir(workspaceRoot, runId), "run-index.json");
41
56
  }
57
+ export function userFacingRunStageDir(workspaceRoot, runId, stageDir) {
58
+ return path.join(userFacingRunDir(workspaceRoot, runId), stageDir);
59
+ }
42
60
  export function stateJsonPath(projectRoot, protocolId) {
43
61
  return path.join(protocolDir(projectRoot, protocolId), "state.json");
44
62
  }
@@ -54,3 +72,18 @@ export function projectRuntimeRoot(ddFlowHome, projectId) {
54
72
  export function projectCheckoutRoot(ddFlowHome, projectId) {
55
73
  return path.join(projectHome(ddFlowHome, projectId), "checkouts");
56
74
  }
75
+ export function projectFeatureWorktreePath(ddFlowHome, projectId, runOrProtocolId, repoName) {
76
+ return path.join(projectCheckoutRoot(ddFlowHome, projectId), "worktrees", runOrProtocolId, repoName);
77
+ }
78
+ export function engineStoreRoot(ddFlowHome) {
79
+ return path.join(ddFlowHome, "engines");
80
+ }
81
+ export function packageEngineRoot(ddFlowHome, packageName) {
82
+ return path.join(engineStoreRoot(ddFlowHome), safePathSegment(packageName));
83
+ }
84
+ export function engineVersionRoot(ddFlowHome, packageName, version) {
85
+ return path.join(packageEngineRoot(ddFlowHome, packageName), safePathSegment(version));
86
+ }
87
+ function safePathSegment(value) {
88
+ return value.replace(/[^a-zA-Z0-9_.@-]+/g, "_");
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deksden-com/dd-flow-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Mechanical runtime CLI for dd-flow workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "README.md"
11
+ "README.md",
12
+ "CHANGELOG.md"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=26.0.0",