@wrongstack/sdd 0.302.0 → 0.303.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/dist/index.d.ts +1 -0
- package/dist/index.js +185 -41
- package/dist/requirement-coverage.d.ts +14 -0
- package/dist/task-generator.d.ts +8 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { SpecParser } from './spec-parser.js';
|
|
2
2
|
export { TaskGenerator, extractVerificationCommand, assessGeneratedTaskAtomicity, type TaskGeneratorOptions, type GeneratedTask, } from './task-generator.js';
|
|
3
|
+
export { assertSpecTaskGraphCoverage, assertTaskGraphExecutionIntegrity, assertTaskGraphRequirementCoverage, evaluateTaskGraphRequirementCoverage, type TaskGraphRequirementCoverage, } from './requirement-coverage.js';
|
|
3
4
|
export { TaskTracker, DefaultTaskStore, type TaskStore, type TaskTrackerOptions, type TaskTransition, type TaskTrackerChange, type TaskTrackerListener, } from '@wrongstack/core/tasking';
|
|
4
5
|
export { TaskFlow, SpecDrivenDev, type TaskFlowPhase, type TaskFlowOptions, type TaskFlowExecutionContext, type TaskFlowEventMap, type TaskFlowEventName, type SpecDrivenDevOptions, } from './task-flow.js';
|
|
5
6
|
export { SpecStore, type SpecStoreOptions, type SpecIndexEntry } from './spec-store.js';
|
package/dist/index.js
CHANGED
|
@@ -280,6 +280,11 @@ var TaskGenerator = class {
|
|
|
280
280
|
}
|
|
281
281
|
async generateFromSpec(spec) {
|
|
282
282
|
const graph = await this.opts.taskTracker.createGraph(spec.id, spec.title);
|
|
283
|
+
graph.requiredRequirementIds = Array.from(
|
|
284
|
+
new Set(
|
|
285
|
+
(spec.requirements ?? []).map((requirement) => requirement.id.trim()).filter(Boolean)
|
|
286
|
+
)
|
|
287
|
+
);
|
|
283
288
|
const overviewSection = spec.sections?.find((s) => s.type === "overview");
|
|
284
289
|
if (overviewSection?.content) {
|
|
285
290
|
const overview = {
|
|
@@ -303,41 +308,7 @@ var TaskGenerator = class {
|
|
|
303
308
|
(a, b) => (priorityOrder[a.priority] ?? 9) - (priorityOrder[b.priority] ?? 9)
|
|
304
309
|
);
|
|
305
310
|
for (const req of sorted) {
|
|
306
|
-
|
|
307
|
-
const tags = [req.type, req.priority];
|
|
308
|
-
const acLines = (req.acceptanceCriteria ?? []).map((ac) => `- ${ac}`).join("\n");
|
|
309
|
-
const blockedLine = req.blockedBy?.length ? `
|
|
310
|
-
|
|
311
|
-
**Blocked by:** ${req.blockedBy.join(", ")}` : "";
|
|
312
|
-
const description = `${req.description}
|
|
313
|
-
|
|
314
|
-
**Type:** ${req.type}` + (acLines ? `
|
|
315
|
-
|
|
316
|
-
**Acceptance Criteria:**
|
|
317
|
-
${acLines}` : "") + blockedLine;
|
|
318
|
-
const metadata = {
|
|
319
|
-
...this.atomicityMetadata({
|
|
320
|
-
title: req.description,
|
|
321
|
-
description,
|
|
322
|
-
estimateHours,
|
|
323
|
-
acceptanceCriteria: req.acceptanceCriteria ?? []
|
|
324
|
-
})
|
|
325
|
-
};
|
|
326
|
-
if (this.opts.verificationFromAcceptance) {
|
|
327
|
-
const cmd = extractVerificationCommand(req.acceptanceCriteria ?? []);
|
|
328
|
-
if (cmd) metadata.verificationCommand = cmd;
|
|
329
|
-
}
|
|
330
|
-
this.opts.taskTracker.addNode({
|
|
331
|
-
title: req.description,
|
|
332
|
-
description,
|
|
333
|
-
type: "feature",
|
|
334
|
-
priority: req.priority,
|
|
335
|
-
status: "pending",
|
|
336
|
-
estimateHours,
|
|
337
|
-
tags,
|
|
338
|
-
specRequirementId: req.id,
|
|
339
|
-
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
340
|
-
});
|
|
311
|
+
this.addRequirementTask(req);
|
|
341
312
|
}
|
|
342
313
|
if (spec.apiEndpoints?.length) {
|
|
343
314
|
const apiParent = this.opts.taskTracker.addNode({
|
|
@@ -401,6 +372,78 @@ ${acLines}` : "") + blockedLine;
|
|
|
401
372
|
});
|
|
402
373
|
return graph;
|
|
403
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Repair an LLM-authored or resumed graph against the authoritative spec.
|
|
377
|
+
* Missing requirements become deterministic tasks, so omission is never
|
|
378
|
+
* rewarded by a smaller task list or an earlier "complete" result.
|
|
379
|
+
*/
|
|
380
|
+
ensureRequirementCoverage(spec, graph) {
|
|
381
|
+
if (graph.specId !== spec.id) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Task graph spec mismatch: graph targets "${graph.specId}" but spec is "${spec.id}".`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
graph.requiredRequirementIds = Array.from(
|
|
387
|
+
new Set(
|
|
388
|
+
(spec.requirements ?? []).map((requirement) => requirement.id.trim()).filter(Boolean)
|
|
389
|
+
)
|
|
390
|
+
);
|
|
391
|
+
const required = new Set(graph.requiredRequirementIds);
|
|
392
|
+
for (const node of graph.nodes.values()) {
|
|
393
|
+
if (node.specRequirementId && !required.has(node.specRequirementId)) {
|
|
394
|
+
delete node.specRequirementId;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const covered = new Set(
|
|
398
|
+
Array.from(graph.nodes.values()).flatMap(
|
|
399
|
+
(node) => node.specRequirementId ? [node.specRequirementId] : []
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
const added = [];
|
|
403
|
+
for (const requirement of spec.requirements ?? []) {
|
|
404
|
+
if (covered.has(requirement.id)) continue;
|
|
405
|
+
added.push(this.addRequirementTask(requirement));
|
|
406
|
+
covered.add(requirement.id);
|
|
407
|
+
}
|
|
408
|
+
return added;
|
|
409
|
+
}
|
|
410
|
+
addRequirementTask(req) {
|
|
411
|
+
const estimateHours = REQUIREMENT_ESTIMATE_HOURS[req.priority] ?? 1;
|
|
412
|
+
const tags = [req.type, req.priority];
|
|
413
|
+
const acLines = (req.acceptanceCriteria ?? []).map((ac) => `- ${ac}`).join("\n");
|
|
414
|
+
const blockedLine = req.blockedBy?.length ? `
|
|
415
|
+
|
|
416
|
+
**Blocked by:** ${req.blockedBy.join(", ")}` : "";
|
|
417
|
+
const description = `${req.description}
|
|
418
|
+
|
|
419
|
+
**Type:** ${req.type}` + (acLines ? `
|
|
420
|
+
|
|
421
|
+
**Acceptance Criteria:**
|
|
422
|
+
${acLines}` : "") + blockedLine;
|
|
423
|
+
const metadata = {
|
|
424
|
+
...this.atomicityMetadata({
|
|
425
|
+
title: req.description,
|
|
426
|
+
description,
|
|
427
|
+
estimateHours,
|
|
428
|
+
acceptanceCriteria: req.acceptanceCriteria ?? []
|
|
429
|
+
})
|
|
430
|
+
};
|
|
431
|
+
if (this.opts.verificationFromAcceptance) {
|
|
432
|
+
const command = extractVerificationCommand(req.acceptanceCriteria ?? []);
|
|
433
|
+
if (command) metadata.verificationCommand = command;
|
|
434
|
+
}
|
|
435
|
+
return this.opts.taskTracker.addNode({
|
|
436
|
+
title: req.description,
|
|
437
|
+
description,
|
|
438
|
+
type: "feature",
|
|
439
|
+
priority: req.priority,
|
|
440
|
+
status: "pending",
|
|
441
|
+
estimateHours,
|
|
442
|
+
tags,
|
|
443
|
+
specRequirementId: req.id,
|
|
444
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
404
447
|
async generateSubtasks(parentTaskId, spec) {
|
|
405
448
|
const reqId = this.opts.taskTracker.getNode(parentTaskId)?.specRequirementId;
|
|
406
449
|
if (!reqId) return;
|
|
@@ -421,6 +464,82 @@ ${acLines}` : "") + blockedLine;
|
|
|
421
464
|
}
|
|
422
465
|
};
|
|
423
466
|
|
|
467
|
+
// src/requirement-coverage.ts
|
|
468
|
+
function uniqueIds(ids) {
|
|
469
|
+
return Array.from(new Set(ids.map((id) => id.trim()).filter(Boolean)));
|
|
470
|
+
}
|
|
471
|
+
function evaluateTaskGraphRequirementCoverage(graph, requiredIds = graph.requiredRequirementIds ?? []) {
|
|
472
|
+
const requiredRequirementIds = uniqueIds(requiredIds);
|
|
473
|
+
const required = new Set(requiredRequirementIds);
|
|
474
|
+
const mapped = uniqueIds(
|
|
475
|
+
Array.from(graph.nodes.values()).flatMap(
|
|
476
|
+
(node) => node.specRequirementId ? [node.specRequirementId] : []
|
|
477
|
+
)
|
|
478
|
+
);
|
|
479
|
+
const mappedSet = new Set(mapped);
|
|
480
|
+
const missingRequirementIds = requiredRequirementIds.filter((id) => !mappedSet.has(id));
|
|
481
|
+
const unknownRequirementIds = mapped.filter((id) => !required.has(id));
|
|
482
|
+
return {
|
|
483
|
+
valid: missingRequirementIds.length === 0 && unknownRequirementIds.length === 0,
|
|
484
|
+
requiredRequirementIds,
|
|
485
|
+
missingRequirementIds,
|
|
486
|
+
unknownRequirementIds
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function assertTaskGraphRequirementCoverage(graph, requiredIds = graph.requiredRequirementIds ?? []) {
|
|
490
|
+
const result = evaluateTaskGraphRequirementCoverage(graph, requiredIds);
|
|
491
|
+
if (result.valid) return;
|
|
492
|
+
const problems = [
|
|
493
|
+
result.missingRequirementIds.length ? `missing task coverage for ${result.missingRequirementIds.join(", ")}` : "",
|
|
494
|
+
result.unknownRequirementIds.length ? `tasks reference unknown requirements ${result.unknownRequirementIds.join(", ")}` : ""
|
|
495
|
+
].filter(Boolean);
|
|
496
|
+
throw new Error(`Task graph requirement coverage is invalid: ${problems.join("; ")}.`);
|
|
497
|
+
}
|
|
498
|
+
function assertSpecTaskGraphCoverage(graph, spec) {
|
|
499
|
+
if (graph.specId !== spec.id) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`Task graph spec mismatch: graph targets "${graph.specId}" but execution received "${spec.id}".`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
assertTaskGraphRequirementCoverage(
|
|
505
|
+
graph,
|
|
506
|
+
(spec.requirements ?? []).map((requirement) => requirement.id)
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
function assertTaskGraphExecutionIntegrity(graph) {
|
|
510
|
+
const dependencies = graph.edges.filter((edge) => edge.type === "depends_on");
|
|
511
|
+
for (const edge of dependencies) {
|
|
512
|
+
if (!graph.nodes.has(edge.from) || !graph.nodes.has(edge.to)) {
|
|
513
|
+
throw new Error(
|
|
514
|
+
`Task graph dependency ${edge.id} references a missing task (${edge.from} -> ${edge.to}).`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
if (edge.from === edge.to) {
|
|
518
|
+
throw new Error(`Task graph dependency ${edge.id} is a self-cycle.`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const indegree = new Map(Array.from(graph.nodes.keys(), (id) => [id, 0]));
|
|
522
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
523
|
+
for (const edge of dependencies) {
|
|
524
|
+
indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
|
|
525
|
+
outgoing.set(edge.from, [...outgoing.get(edge.from) ?? [], edge.to]);
|
|
526
|
+
}
|
|
527
|
+
const ready = Array.from(indegree.entries()).filter(([, degree]) => degree === 0).map(([id]) => id);
|
|
528
|
+
let visited = 0;
|
|
529
|
+
while (ready.length > 0) {
|
|
530
|
+
const id = ready.shift();
|
|
531
|
+
visited += 1;
|
|
532
|
+
for (const dependent of outgoing.get(id) ?? []) {
|
|
533
|
+
const next = (indegree.get(dependent) ?? 0) - 1;
|
|
534
|
+
indegree.set(dependent, next);
|
|
535
|
+
if (next === 0) ready.push(dependent);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (visited !== graph.nodes.size) {
|
|
539
|
+
throw new Error("Task graph dependency cycle prevents deterministic execution.");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
424
543
|
// src/index.ts
|
|
425
544
|
import {
|
|
426
545
|
TaskTracker as TaskTracker3,
|
|
@@ -2426,9 +2545,17 @@ var SddInterviewDriver = class {
|
|
|
2426
2545
|
* emitted a parseable task array.
|
|
2427
2546
|
*/
|
|
2428
2547
|
async ensureTaskGraph() {
|
|
2429
|
-
if (this.graph) return this.graph;
|
|
2430
2548
|
const spec = this.builder.getSession().spec;
|
|
2431
2549
|
if (!spec) return null;
|
|
2550
|
+
if (this.graph && this.tracker) {
|
|
2551
|
+
const generator2 = new TaskGenerator({
|
|
2552
|
+
taskTracker: this.tracker,
|
|
2553
|
+
verificationFromAcceptance: process.env["WRONGSTACK_SDD_VERIFY_FROM_ACCEPTANCE"] === "1"
|
|
2554
|
+
});
|
|
2555
|
+
generator2.ensureRequirementCoverage(spec, this.graph);
|
|
2556
|
+
await this.persistGraph(this.graph);
|
|
2557
|
+
return this.graph;
|
|
2558
|
+
}
|
|
2432
2559
|
const tracker = new TaskTracker2({ store: this.o.graphStore });
|
|
2433
2560
|
const generator = new TaskGenerator({
|
|
2434
2561
|
taskTracker: tracker,
|
|
@@ -2550,6 +2677,10 @@ var SddInterviewDriver = class {
|
|
|
2550
2677
|
if (depId && depId !== nodeId) tracker.addDependency(depId, nodeId);
|
|
2551
2678
|
}
|
|
2552
2679
|
}
|
|
2680
|
+
new TaskGenerator({
|
|
2681
|
+
taskTracker: tracker,
|
|
2682
|
+
verificationFromAcceptance: process.env["WRONGSTACK_SDD_VERIFY_FROM_ACCEPTANCE"] === "1"
|
|
2683
|
+
}).ensureRequirementCoverage(spec, graph);
|
|
2553
2684
|
await this.persistGraph(graph);
|
|
2554
2685
|
await this.builder.setTaskGraphId(graph.id);
|
|
2555
2686
|
await this.builder.saveSession();
|
|
@@ -2562,6 +2693,8 @@ function normalizeTaskRef(ref) {
|
|
|
2562
2693
|
return ref.trim().toLowerCase();
|
|
2563
2694
|
}
|
|
2564
2695
|
function addTaskToTracker(tracker, task) {
|
|
2696
|
+
const rawRequirementId = task.specRequirementId ?? task.requirementId;
|
|
2697
|
+
const specRequirementId = typeof rawRequirementId === "string" && rawRequirementId.trim() ? rawRequirementId.trim() : void 0;
|
|
2565
2698
|
return tracker.addNode({
|
|
2566
2699
|
title: String(task.title),
|
|
2567
2700
|
description: String(task.description ?? ""),
|
|
@@ -2569,7 +2702,8 @@ function addTaskToTracker(tracker, task) {
|
|
|
2569
2702
|
priority: TASK_PRIORITIES.includes(String(task.priority)) ? String(task.priority) : "medium",
|
|
2570
2703
|
status: "pending",
|
|
2571
2704
|
estimateHours: Number(task.estimateHours) || 2,
|
|
2572
|
-
tags: Array.isArray(task.tags) ? task.tags.map(String) : []
|
|
2705
|
+
tags: Array.isArray(task.tags) ? task.tags.map(String) : [],
|
|
2706
|
+
...specRequirementId ? { specRequirementId } : {}
|
|
2573
2707
|
});
|
|
2574
2708
|
}
|
|
2575
2709
|
function isExplanatoryText(text) {
|
|
@@ -3772,6 +3906,10 @@ function applySddControlCommand(run, command) {
|
|
|
3772
3906
|
});
|
|
3773
3907
|
}
|
|
3774
3908
|
function startSddRun(opts) {
|
|
3909
|
+
assertTaskGraphExecutionIntegrity(opts.graph);
|
|
3910
|
+
if (opts.graph.requiredRequirementIds !== void 0) {
|
|
3911
|
+
assertTaskGraphRequirementCoverage(opts.graph);
|
|
3912
|
+
}
|
|
3775
3913
|
SddParallelRun.resetOrphans(opts.tracker);
|
|
3776
3914
|
const run = new SddParallelRun({
|
|
3777
3915
|
tracker: opts.tracker,
|
|
@@ -4912,6 +5050,8 @@ var AutoExecutor = class {
|
|
|
4912
5050
|
* Execute all tasks in the graph, respecting dependencies.
|
|
4913
5051
|
*/
|
|
4914
5052
|
async execute(graph, spec) {
|
|
5053
|
+
assertSpecTaskGraphCoverage(graph, spec);
|
|
5054
|
+
assertTaskGraphExecutionIntegrity(graph);
|
|
4915
5055
|
this.stopped = false;
|
|
4916
5056
|
this.retryMap.clear();
|
|
4917
5057
|
const startTime = Date.now();
|
|
@@ -4980,7 +5120,7 @@ var AutoExecutor = class {
|
|
|
4980
5120
|
const ready = [];
|
|
4981
5121
|
for (const node of graph.nodes.values()) {
|
|
4982
5122
|
if (node.status !== "pending") continue;
|
|
4983
|
-
const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.
|
|
5123
|
+
const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.to === node.id).map((e) => graph.nodes.get(e.from)).filter(Boolean);
|
|
4984
5124
|
const allBlockersDone = blockers.every((b) => b.status === "completed");
|
|
4985
5125
|
if (allBlockersDone) {
|
|
4986
5126
|
ready.push(node);
|
|
@@ -5041,11 +5181,11 @@ var AutoExecutor = class {
|
|
|
5041
5181
|
}
|
|
5042
5182
|
/** Get tasks that this task depends on. */
|
|
5043
5183
|
getTaskDependencies(taskId, graph) {
|
|
5044
|
-
return graph.edges.filter((e) => e.type === "depends_on" && e.
|
|
5184
|
+
return graph.edges.filter((e) => e.type === "depends_on" && e.to === taskId).map((e) => graph.nodes.get(e.from)).filter(Boolean);
|
|
5045
5185
|
}
|
|
5046
5186
|
/** Get tasks that depend on this task. */
|
|
5047
5187
|
getTaskDependents(taskId, graph) {
|
|
5048
|
-
return graph.edges.filter((e) => e.type === "depends_on" && e.
|
|
5188
|
+
return graph.edges.filter((e) => e.type === "depends_on" && e.from === taskId).map((e) => graph.nodes.get(e.to)).filter(Boolean);
|
|
5049
5189
|
}
|
|
5050
5190
|
/** Detect deadlock: all remaining tasks are blocked by failed tasks. */
|
|
5051
5191
|
detectDeadlock(graph) {
|
|
@@ -5054,7 +5194,7 @@ var AutoExecutor = class {
|
|
|
5054
5194
|
);
|
|
5055
5195
|
if (remaining.length === 0) return false;
|
|
5056
5196
|
return remaining.every((node) => {
|
|
5057
|
-
const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.
|
|
5197
|
+
const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.to === node.id).map((e) => graph.nodes.get(e.from)).filter(Boolean);
|
|
5058
5198
|
return blockers.some((b) => b.status === "failed");
|
|
5059
5199
|
});
|
|
5060
5200
|
}
|
|
@@ -5583,6 +5723,9 @@ export {
|
|
|
5583
5723
|
TaskTracker3 as TaskTracker,
|
|
5584
5724
|
analyzeCriticalPath,
|
|
5585
5725
|
applySddLifecycle,
|
|
5726
|
+
assertSpecTaskGraphCoverage,
|
|
5727
|
+
assertTaskGraphExecutionIntegrity,
|
|
5728
|
+
assertTaskGraphRequirementCoverage,
|
|
5586
5729
|
assessGeneratedTaskAtomicity,
|
|
5587
5730
|
assessTaskNodeAtomicity,
|
|
5588
5731
|
buildBoardSnapshot,
|
|
@@ -5594,6 +5737,7 @@ export {
|
|
|
5594
5737
|
createKanbanSddSessionPersistence,
|
|
5595
5738
|
decomposeNonAtomicTasks,
|
|
5596
5739
|
destroySddProject,
|
|
5740
|
+
evaluateTaskGraphRequirementCoverage,
|
|
5597
5741
|
extractVerificationCommand,
|
|
5598
5742
|
gatherProjectContext,
|
|
5599
5743
|
getTemplate,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Specification, TaskGraph } from '@wrongstack/core/types';
|
|
2
|
+
export interface TaskGraphRequirementCoverage {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
requiredRequirementIds: string[];
|
|
5
|
+
missingRequirementIds: string[];
|
|
6
|
+
unknownRequirementIds: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Deterministic requirement-to-task coverage; no model judgment is involved. */
|
|
9
|
+
export declare function evaluateTaskGraphRequirementCoverage(graph: TaskGraph, requiredIds?: readonly string[]): TaskGraphRequirementCoverage;
|
|
10
|
+
export declare function assertTaskGraphRequirementCoverage(graph: TaskGraph, requiredIds?: readonly string[]): void;
|
|
11
|
+
export declare function assertSpecTaskGraphCoverage(graph: TaskGraph, spec: Specification): void;
|
|
12
|
+
/** Fail before dispatch when dependency edges cannot form an executable DAG. */
|
|
13
|
+
export declare function assertTaskGraphExecutionIntegrity(graph: TaskGraph): void;
|
|
14
|
+
//# sourceMappingURL=requirement-coverage.d.ts.map
|
package/dist/task-generator.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TaskStore, TaskTracker } from '@wrongstack/core/tasking';
|
|
2
|
-
import type { Specification, TaskGraph, TaskPriority, TaskType } from '@wrongstack/core/types';
|
|
2
|
+
import type { Specification, TaskGraph, TaskNode, TaskPriority, TaskType } from '@wrongstack/core/types';
|
|
3
3
|
import { type AtomicityRuleSetConfig } from '@wrongstack/kanban';
|
|
4
4
|
/** Named estimate constants shared with the atomicity candidate mapping. */
|
|
5
5
|
export declare const OVERVIEW_ESTIMATE_HOURS = 4;
|
|
@@ -62,6 +62,13 @@ export declare class TaskGenerator {
|
|
|
62
62
|
/** metadata.atomicity payload when the option is enabled, else undefined. */
|
|
63
63
|
private atomicityMetadata;
|
|
64
64
|
generateFromSpec(spec: Specification): Promise<TaskGraph>;
|
|
65
|
+
/**
|
|
66
|
+
* Repair an LLM-authored or resumed graph against the authoritative spec.
|
|
67
|
+
* Missing requirements become deterministic tasks, so omission is never
|
|
68
|
+
* rewarded by a smaller task list or an earlier "complete" result.
|
|
69
|
+
*/
|
|
70
|
+
ensureRequirementCoverage(spec: Specification, graph: TaskGraph): TaskNode[];
|
|
71
|
+
private addRequirementTask;
|
|
65
72
|
generateSubtasks(parentTaskId: string, spec: Specification): Promise<void>;
|
|
66
73
|
}
|
|
67
74
|
export type { TaskStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.303.0",
|
|
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": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"!dist/**/*.map"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/core": "0.
|
|
31
|
-
"@wrongstack/kanban": "0.
|
|
32
|
-
"@wrongstack/requirement-intake": "0.
|
|
30
|
+
"@wrongstack/core": "0.303.0",
|
|
31
|
+
"@wrongstack/kanban": "0.303.0",
|
|
32
|
+
"@wrongstack/requirement-intake": "0.303.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^26.1.2",
|