@wrongstack/sdd 0.302.2 → 0.305.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 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
- const estimateHours = REQUIREMENT_ESTIMATE_HOURS[req.priority] ?? 1;
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,
@@ -1845,6 +1964,7 @@ var AISpecBuilder = class {
1845
1964
  maxQuestions;
1846
1965
  sessionPath;
1847
1966
  sessionPersistence;
1967
+ pendingSessionWrite = Promise.resolve();
1848
1968
  constructor(opts) {
1849
1969
  this.store = opts.store;
1850
1970
  this.minQuestions = opts.minQuestions ?? 2;
@@ -1868,27 +1988,31 @@ var AISpecBuilder = class {
1868
1988
  /** Save session state to the configured durable owner. */
1869
1989
  async saveSession() {
1870
1990
  if (!this.sessionPersistence && !this.sessionPath) return;
1871
- try {
1872
- if (this.sessionPersistence) {
1873
- await this.sessionPersistence.save(structuredClone(this.session));
1874
- return;
1991
+ const snapshot = structuredClone(this.session);
1992
+ this.pendingSessionWrite = this.pendingSessionWrite.then(async () => {
1993
+ try {
1994
+ if (this.sessionPersistence) {
1995
+ await this.sessionPersistence.save(snapshot);
1996
+ return;
1997
+ }
1998
+ const fsp7 = await import("node:fs/promises");
1999
+ const path6 = await import("node:path");
2000
+ const { atomicWrite: atomicWrite4 } = await import("@wrongstack/core/utils");
2001
+ const sessionPath = expectDefined2(this.sessionPath);
2002
+ await fsp7.mkdir(path6.dirname(sessionPath), { recursive: true });
2003
+ await atomicWrite4(sessionPath, JSON.stringify(snapshot, null, 2));
2004
+ } catch (error) {
2005
+ console.warn(
2006
+ JSON.stringify({
2007
+ level: "warn",
2008
+ event: "sdd.persist.failed",
2009
+ message: String(error),
2010
+ timestamp: Date.now()
2011
+ })
2012
+ );
1875
2013
  }
1876
- const fsp7 = await import("node:fs/promises");
1877
- const path6 = await import("node:path");
1878
- const { atomicWrite: atomicWrite4 } = await import("@wrongstack/core/utils");
1879
- const sessionPath = expectDefined2(this.sessionPath);
1880
- await fsp7.mkdir(path6.dirname(sessionPath), { recursive: true });
1881
- await atomicWrite4(sessionPath, JSON.stringify(this.session, null, 2));
1882
- } catch (error) {
1883
- console.warn(
1884
- JSON.stringify({
1885
- level: "warn",
1886
- event: "sdd.persist.failed",
1887
- message: String(error),
1888
- timestamp: Date.now()
1889
- })
1890
- );
1891
- }
2014
+ });
2015
+ await this.pendingSessionWrite;
1892
2016
  }
1893
2017
  /** Load session state from the configured durable owner. */
1894
2018
  async loadSession() {
@@ -1915,6 +2039,7 @@ var AISpecBuilder = class {
1915
2039
  }
1916
2040
  /** Delete the saved session from the configured durable owner. */
1917
2041
  async deleteSession() {
2042
+ await this.pendingSessionWrite;
1918
2043
  if (this.sessionPersistence) {
1919
2044
  await this.sessionPersistence.delete();
1920
2045
  return;
@@ -2426,9 +2551,17 @@ var SddInterviewDriver = class {
2426
2551
  * emitted a parseable task array.
2427
2552
  */
2428
2553
  async ensureTaskGraph() {
2429
- if (this.graph) return this.graph;
2430
2554
  const spec = this.builder.getSession().spec;
2431
2555
  if (!spec) return null;
2556
+ if (this.graph && this.tracker) {
2557
+ const generator2 = new TaskGenerator({
2558
+ taskTracker: this.tracker,
2559
+ verificationFromAcceptance: process.env["WRONGSTACK_SDD_VERIFY_FROM_ACCEPTANCE"] === "1"
2560
+ });
2561
+ generator2.ensureRequirementCoverage(spec, this.graph);
2562
+ await this.persistGraph(this.graph);
2563
+ return this.graph;
2564
+ }
2432
2565
  const tracker = new TaskTracker2({ store: this.o.graphStore });
2433
2566
  const generator = new TaskGenerator({
2434
2567
  taskTracker: tracker,
@@ -2550,6 +2683,10 @@ var SddInterviewDriver = class {
2550
2683
  if (depId && depId !== nodeId) tracker.addDependency(depId, nodeId);
2551
2684
  }
2552
2685
  }
2686
+ new TaskGenerator({
2687
+ taskTracker: tracker,
2688
+ verificationFromAcceptance: process.env["WRONGSTACK_SDD_VERIFY_FROM_ACCEPTANCE"] === "1"
2689
+ }).ensureRequirementCoverage(spec, graph);
2553
2690
  await this.persistGraph(graph);
2554
2691
  await this.builder.setTaskGraphId(graph.id);
2555
2692
  await this.builder.saveSession();
@@ -2562,6 +2699,8 @@ function normalizeTaskRef(ref) {
2562
2699
  return ref.trim().toLowerCase();
2563
2700
  }
2564
2701
  function addTaskToTracker(tracker, task) {
2702
+ const rawRequirementId = task.specRequirementId ?? task.requirementId;
2703
+ const specRequirementId = typeof rawRequirementId === "string" && rawRequirementId.trim() ? rawRequirementId.trim() : void 0;
2565
2704
  return tracker.addNode({
2566
2705
  title: String(task.title),
2567
2706
  description: String(task.description ?? ""),
@@ -2569,7 +2708,8 @@ function addTaskToTracker(tracker, task) {
2569
2708
  priority: TASK_PRIORITIES.includes(String(task.priority)) ? String(task.priority) : "medium",
2570
2709
  status: "pending",
2571
2710
  estimateHours: Number(task.estimateHours) || 2,
2572
- tags: Array.isArray(task.tags) ? task.tags.map(String) : []
2711
+ tags: Array.isArray(task.tags) ? task.tags.map(String) : [],
2712
+ ...specRequirementId ? { specRequirementId } : {}
2573
2713
  });
2574
2714
  }
2575
2715
  function isExplanatoryText(text) {
@@ -3772,6 +3912,10 @@ function applySddControlCommand(run, command) {
3772
3912
  });
3773
3913
  }
3774
3914
  function startSddRun(opts) {
3915
+ assertTaskGraphExecutionIntegrity(opts.graph);
3916
+ if (opts.graph.requiredRequirementIds !== void 0) {
3917
+ assertTaskGraphRequirementCoverage(opts.graph);
3918
+ }
3775
3919
  SddParallelRun.resetOrphans(opts.tracker);
3776
3920
  const run = new SddParallelRun({
3777
3921
  tracker: opts.tracker,
@@ -4912,6 +5056,8 @@ var AutoExecutor = class {
4912
5056
  * Execute all tasks in the graph, respecting dependencies.
4913
5057
  */
4914
5058
  async execute(graph, spec) {
5059
+ assertSpecTaskGraphCoverage(graph, spec);
5060
+ assertTaskGraphExecutionIntegrity(graph);
4915
5061
  this.stopped = false;
4916
5062
  this.retryMap.clear();
4917
5063
  const startTime = Date.now();
@@ -4980,7 +5126,7 @@ var AutoExecutor = class {
4980
5126
  const ready = [];
4981
5127
  for (const node of graph.nodes.values()) {
4982
5128
  if (node.status !== "pending") continue;
4983
- const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.from === node.id).map((e) => graph.nodes.get(e.to)).filter(Boolean);
5129
+ const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.to === node.id).map((e) => graph.nodes.get(e.from)).filter(Boolean);
4984
5130
  const allBlockersDone = blockers.every((b) => b.status === "completed");
4985
5131
  if (allBlockersDone) {
4986
5132
  ready.push(node);
@@ -5041,11 +5187,11 @@ var AutoExecutor = class {
5041
5187
  }
5042
5188
  /** Get tasks that this task depends on. */
5043
5189
  getTaskDependencies(taskId, graph) {
5044
- return graph.edges.filter((e) => e.type === "depends_on" && e.from === taskId).map((e) => graph.nodes.get(e.to)).filter(Boolean);
5190
+ return graph.edges.filter((e) => e.type === "depends_on" && e.to === taskId).map((e) => graph.nodes.get(e.from)).filter(Boolean);
5045
5191
  }
5046
5192
  /** Get tasks that depend on this task. */
5047
5193
  getTaskDependents(taskId, graph) {
5048
- return graph.edges.filter((e) => e.type === "depends_on" && e.to === taskId).map((e) => graph.nodes.get(e.from)).filter(Boolean);
5194
+ return graph.edges.filter((e) => e.type === "depends_on" && e.from === taskId).map((e) => graph.nodes.get(e.to)).filter(Boolean);
5049
5195
  }
5050
5196
  /** Detect deadlock: all remaining tasks are blocked by failed tasks. */
5051
5197
  detectDeadlock(graph) {
@@ -5054,7 +5200,7 @@ var AutoExecutor = class {
5054
5200
  );
5055
5201
  if (remaining.length === 0) return false;
5056
5202
  return remaining.every((node) => {
5057
- const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.from === node.id).map((e) => graph.nodes.get(e.to)).filter(Boolean);
5203
+ const blockers = graph.edges.filter((e) => e.type === "depends_on" && e.to === node.id).map((e) => graph.nodes.get(e.from)).filter(Boolean);
5058
5204
  return blockers.some((b) => b.status === "failed");
5059
5205
  });
5060
5206
  }
@@ -5583,6 +5729,9 @@ export {
5583
5729
  TaskTracker3 as TaskTracker,
5584
5730
  analyzeCriticalPath,
5585
5731
  applySddLifecycle,
5732
+ assertSpecTaskGraphCoverage,
5733
+ assertTaskGraphExecutionIntegrity,
5734
+ assertTaskGraphRequirementCoverage,
5586
5735
  assessGeneratedTaskAtomicity,
5587
5736
  assessTaskNodeAtomicity,
5588
5737
  buildBoardSnapshot,
@@ -5594,6 +5743,7 @@ export {
5594
5743
  createKanbanSddSessionPersistence,
5595
5744
  decomposeNonAtomicTasks,
5596
5745
  destroySddProject,
5746
+ evaluateTaskGraphRequirementCoverage,
5597
5747
  extractVerificationCommand,
5598
5748
  gatherProjectContext,
5599
5749
  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
@@ -27,6 +27,7 @@ export declare class AISpecBuilder {
27
27
  private readonly maxQuestions;
28
28
  private readonly sessionPath?;
29
29
  private readonly sessionPersistence?;
30
+ private pendingSessionWrite;
30
31
  constructor(opts: AISpecBuilderOptions);
31
32
  /** Save session state to the configured durable owner. */
32
33
  saveSession(): Promise<void>;
@@ -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.302.2",
3
+ "version": "0.305.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.302.2",
31
- "@wrongstack/requirement-intake": "0.302.2",
32
- "@wrongstack/kanban": "0.302.2"
30
+ "@wrongstack/core": "0.305.0",
31
+ "@wrongstack/kanban": "0.305.0",
32
+ "@wrongstack/requirement-intake": "0.305.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^26.1.2",