@wrongstack/sdd 1.1.4 → 1.1.5
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/dist/index.js +56 -39
- package/dist/task-graph-store.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1040,6 +1040,10 @@ var TaskGraphStore = class {
|
|
|
1040
1040
|
saveGraph(graph) {
|
|
1041
1041
|
return this.save(graph);
|
|
1042
1042
|
}
|
|
1043
|
+
/** Wait until every queued graph/index mutation has settled. */
|
|
1044
|
+
flush() {
|
|
1045
|
+
return this.writeChain;
|
|
1046
|
+
}
|
|
1043
1047
|
loadGraph(id) {
|
|
1044
1048
|
return this.load(id);
|
|
1045
1049
|
}
|
|
@@ -5155,39 +5159,39 @@ function renderTaskGraph(graph, opts) {
|
|
|
5155
5159
|
const progress = computeTaskProgress2(graph);
|
|
5156
5160
|
lines.push(renderProgress(progress));
|
|
5157
5161
|
lines.push("");
|
|
5158
|
-
const
|
|
5162
|
+
const blockersMap = /* @__PURE__ */ new Map();
|
|
5159
5163
|
const dependentsMap = /* @__PURE__ */ new Map();
|
|
5160
5164
|
for (const edge of graph.edges) {
|
|
5161
5165
|
if (edge.type === "depends_on") {
|
|
5162
|
-
const
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
if (graph.nodes.has(edge.
|
|
5166
|
-
const dependents = dependentsMap.get(edge.
|
|
5167
|
-
dependents.push(edge.
|
|
5168
|
-
dependentsMap.set(edge.
|
|
5166
|
+
const blockers = blockersMap.get(edge.to) ?? [];
|
|
5167
|
+
blockers.push(edge.from);
|
|
5168
|
+
blockersMap.set(edge.to, blockers);
|
|
5169
|
+
if (graph.nodes.has(edge.to)) {
|
|
5170
|
+
const dependents = dependentsMap.get(edge.from) ?? [];
|
|
5171
|
+
dependents.push(edge.to);
|
|
5172
|
+
dependentsMap.set(edge.from, dependents);
|
|
5169
5173
|
}
|
|
5170
5174
|
}
|
|
5171
5175
|
}
|
|
5172
5176
|
const rendered = /* @__PURE__ */ new Set();
|
|
5173
5177
|
const rootNodes = graph.rootNodes.filter((id) => graph.nodes.has(id));
|
|
5174
5178
|
const startNodes = rootNodes.length > 0 ? rootNodes : Array.from(graph.nodes.keys()).filter((id) => {
|
|
5175
|
-
const
|
|
5176
|
-
return !
|
|
5179
|
+
const blockers = blockersMap.get(id);
|
|
5180
|
+
return !blockers || blockers.length === 0;
|
|
5177
5181
|
});
|
|
5178
5182
|
for (const rootId of startNodes) {
|
|
5179
|
-
renderNode(graph, rootId, lines, rendered,
|
|
5183
|
+
renderNode(graph, rootId, lines, rendered, blockersMap, dependentsMap, compact, "");
|
|
5180
5184
|
}
|
|
5181
5185
|
for (const [id] of graph.nodes) {
|
|
5182
5186
|
if (!rendered.has(id)) {
|
|
5183
|
-
renderNode(graph, id, lines, rendered,
|
|
5187
|
+
renderNode(graph, id, lines, rendered, blockersMap, dependentsMap, compact, "");
|
|
5184
5188
|
}
|
|
5185
5189
|
}
|
|
5186
5190
|
lines.push("");
|
|
5187
5191
|
lines.push("Legend: \u25CF done \u25D0 in-progress \u25CB pending \u2297 blocked \u2717 failed \u25D2 review");
|
|
5188
5192
|
return lines.join("\n");
|
|
5189
5193
|
}
|
|
5190
|
-
function renderNode(graph, nodeId, lines, rendered,
|
|
5194
|
+
function renderNode(graph, nodeId, lines, rendered, blockersMap, dependentsMap, compact, prefix) {
|
|
5191
5195
|
if (rendered.has(nodeId)) return;
|
|
5192
5196
|
rendered.add(nodeId);
|
|
5193
5197
|
const node = expectDefined5(graph.nodes.get(nodeId));
|
|
@@ -5195,7 +5199,7 @@ function renderNode(graph, nodeId, lines, rendered, childrenMap, dependentsMap,
|
|
|
5195
5199
|
const prioIcon = PRIORITY_ICON[node.priority];
|
|
5196
5200
|
const typeIcon = TYPE_ICON[node.type];
|
|
5197
5201
|
const title = compact ? truncate(node.title, 40) : node.title;
|
|
5198
|
-
const blockedBy =
|
|
5202
|
+
const blockedBy = blockersMap.get(nodeId) ?? [];
|
|
5199
5203
|
const depsStr = blockedBy.length > 0 ? ` \u2190 [${blockedBy.map((d) => graph.nodes.get(d)?.title?.slice(0, 12) ?? "?").join(", ")}]` : "";
|
|
5200
5204
|
lines.push(`${prefix}${icon} ${typeIcon} ${prioIcon} ${title}${depsStr}`);
|
|
5201
5205
|
if (!compact && node.description) {
|
|
@@ -5206,7 +5210,7 @@ function renderNode(graph, nodeId, lines, rendered, childrenMap, dependentsMap,
|
|
|
5206
5210
|
}
|
|
5207
5211
|
const dependents = dependentsMap.get(nodeId) ?? [];
|
|
5208
5212
|
for (const depId of dependents) {
|
|
5209
|
-
renderNode(graph, depId, lines, rendered,
|
|
5213
|
+
renderNode(graph, depId, lines, rendered, blockersMap, dependentsMap, compact, prefix + " ");
|
|
5210
5214
|
}
|
|
5211
5215
|
}
|
|
5212
5216
|
function renderProgress(progress) {
|
|
@@ -5279,19 +5283,18 @@ function renderSpecAnalysis(spec, analysis) {
|
|
|
5279
5283
|
}
|
|
5280
5284
|
|
|
5281
5285
|
// src/critical-path.ts
|
|
5282
|
-
import { topologicalSort } from "@wrongstack/core/tasking";
|
|
5283
5286
|
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
5284
5287
|
function analyzeCriticalPath(graph) {
|
|
5285
5288
|
const nodes = Array.from(graph.nodes.values());
|
|
5286
|
-
const topoOrder =
|
|
5289
|
+
const topoOrder = dependencyFirstOrder(graph);
|
|
5287
5290
|
const blockedByMap = /* @__PURE__ */ new Map();
|
|
5288
5291
|
const blocksMap = /* @__PURE__ */ new Map();
|
|
5289
5292
|
for (const edge of graph.edges) {
|
|
5290
5293
|
if (edge.type === "depends_on") {
|
|
5291
|
-
if (!blockedByMap.has(edge.
|
|
5292
|
-
blockedByMap.get(edge.
|
|
5293
|
-
if (!blocksMap.has(edge.
|
|
5294
|
-
blocksMap.get(edge.
|
|
5294
|
+
if (!blockedByMap.has(edge.to)) blockedByMap.set(edge.to, /* @__PURE__ */ new Set());
|
|
5295
|
+
blockedByMap.get(edge.to)?.add(edge.from);
|
|
5296
|
+
if (!blocksMap.has(edge.from)) blocksMap.set(edge.from, /* @__PURE__ */ new Set());
|
|
5297
|
+
blocksMap.get(edge.from)?.add(edge.to);
|
|
5295
5298
|
}
|
|
5296
5299
|
}
|
|
5297
5300
|
const readyTasks = [];
|
|
@@ -5336,7 +5339,7 @@ function analyzeCriticalPath(graph) {
|
|
|
5336
5339
|
}
|
|
5337
5340
|
}
|
|
5338
5341
|
bottlenecks.sort((a, b) => b.severity - a.severity);
|
|
5339
|
-
const criticalPath = computeCriticalPath(graph,
|
|
5342
|
+
const criticalPath = computeCriticalPath(graph, blocksMap);
|
|
5340
5343
|
const totalHours = criticalPath.reduce((sum, id) => {
|
|
5341
5344
|
return sum + taskEstimateHours(graph, id, 0);
|
|
5342
5345
|
}, 0);
|
|
@@ -5355,6 +5358,35 @@ function analyzeCriticalPath(graph) {
|
|
|
5355
5358
|
blockedTasks
|
|
5356
5359
|
};
|
|
5357
5360
|
}
|
|
5361
|
+
function dependencyFirstOrder(graph) {
|
|
5362
|
+
const ids = Array.from(graph.nodes.keys());
|
|
5363
|
+
const indegree = new Map(ids.map((id) => [id, 0]));
|
|
5364
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
5365
|
+
for (const edge of graph.edges) {
|
|
5366
|
+
if (edge.type !== "depends_on") continue;
|
|
5367
|
+
if (!graph.nodes.has(edge.from) || !graph.nodes.has(edge.to)) continue;
|
|
5368
|
+
indegree.set(edge.to, indegree.get(edge.to) + 1);
|
|
5369
|
+
const dependents = outgoing.get(edge.from);
|
|
5370
|
+
if (dependents) dependents.push(edge.to);
|
|
5371
|
+
else outgoing.set(edge.from, [edge.to]);
|
|
5372
|
+
}
|
|
5373
|
+
const ready = ids.filter((id) => indegree.get(id) === 0);
|
|
5374
|
+
const order = [];
|
|
5375
|
+
for (let cursor = 0; cursor < ready.length; cursor++) {
|
|
5376
|
+
const id = ready[cursor];
|
|
5377
|
+
order.push(id);
|
|
5378
|
+
for (const dependent of outgoing.get(id) ?? []) {
|
|
5379
|
+
const next = indegree.get(dependent) - 1;
|
|
5380
|
+
indegree.set(dependent, next);
|
|
5381
|
+
if (next === 0) ready.push(dependent);
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
const ordered = new Set(order);
|
|
5385
|
+
for (const id of ids) {
|
|
5386
|
+
if (!ordered.has(id)) order.push(id);
|
|
5387
|
+
}
|
|
5388
|
+
return order;
|
|
5389
|
+
}
|
|
5358
5390
|
function getTransitiveBlocked(_graph, taskId, blocksMap) {
|
|
5359
5391
|
const visited = /* @__PURE__ */ new Set();
|
|
5360
5392
|
const queue = [taskId];
|
|
@@ -5371,7 +5403,7 @@ function getTransitiveBlocked(_graph, taskId, blocksMap) {
|
|
|
5371
5403
|
}
|
|
5372
5404
|
return visited;
|
|
5373
5405
|
}
|
|
5374
|
-
function computeCriticalPath(graph,
|
|
5406
|
+
function computeCriticalPath(graph, blocksMap) {
|
|
5375
5407
|
const allIds = Array.from(graph.nodes.keys());
|
|
5376
5408
|
if (allIds.length === 0) return [];
|
|
5377
5409
|
const dist = /* @__PURE__ */ new Map();
|
|
@@ -5380,13 +5412,6 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
5380
5412
|
dist.set(id, taskEstimateHours(graph, id, 1));
|
|
5381
5413
|
prev.set(id, null);
|
|
5382
5414
|
}
|
|
5383
|
-
const blocksMap = /* @__PURE__ */ new Map();
|
|
5384
|
-
for (const [taskId, blockers] of blockedByMap) {
|
|
5385
|
-
for (const blockerId of blockers) {
|
|
5386
|
-
if (!blocksMap.has(blockerId)) blocksMap.set(blockerId, /* @__PURE__ */ new Set());
|
|
5387
|
-
blocksMap.get(blockerId)?.add(taskId);
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
5415
|
const n = allIds.length;
|
|
5391
5416
|
for (let i = 0; i < n - 1; i++) {
|
|
5392
5417
|
let changed = false;
|
|
@@ -5662,14 +5687,6 @@ var SpecVersioning = class {
|
|
|
5662
5687
|
};
|
|
5663
5688
|
|
|
5664
5689
|
// src/auto-executor.ts
|
|
5665
|
-
function asDependentFirstGraph(graph) {
|
|
5666
|
-
return {
|
|
5667
|
-
...graph,
|
|
5668
|
-
edges: graph.edges.map(
|
|
5669
|
-
(edge) => edge.type === "depends_on" ? { ...edge, from: edge.to, to: edge.from } : edge
|
|
5670
|
-
)
|
|
5671
|
-
};
|
|
5672
|
-
}
|
|
5673
5690
|
var AutoExecutor = class {
|
|
5674
5691
|
opts;
|
|
5675
5692
|
stopped = false;
|
|
@@ -5686,7 +5703,7 @@ var AutoExecutor = class {
|
|
|
5686
5703
|
this.stopped = false;
|
|
5687
5704
|
this.retryMap.clear();
|
|
5688
5705
|
const startTime = Date.now();
|
|
5689
|
-
const critical = analyzeCriticalPath(
|
|
5706
|
+
const critical = analyzeCriticalPath(graph);
|
|
5690
5707
|
let completed = 0;
|
|
5691
5708
|
let failed = 0;
|
|
5692
5709
|
const skipped = 0;
|
|
@@ -29,6 +29,8 @@ export declare class TaskGraphStore implements TaskStore {
|
|
|
29
29
|
delete(id: string): Promise<boolean>;
|
|
30
30
|
exists(id: string): Promise<boolean>;
|
|
31
31
|
saveGraph(graph: TaskGraph): Promise<void>;
|
|
32
|
+
/** Wait until every queued graph/index mutation has settled. */
|
|
33
|
+
flush(): Promise<void>;
|
|
32
34
|
loadGraph(id: string): Promise<TaskGraph | null>;
|
|
33
35
|
listGraphs(): Promise<Array<{
|
|
34
36
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack Spec-Driven Development engine — standalone package extracted from @wrongstack/core. Task graph generation, tracking, execution, lifecycle management, and AI-driven spec building for SDD workflows.",
|
|
6
6
|
"repository": {
|