@wrongstack/sdd 0.313.1 → 0.316.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.
@@ -1,6 +1,6 @@
1
1
  import type { TaskNode } from '@wrongstack/core/types';
2
2
  export type ConflictSide = 'incoming' | 'base';
3
- export interface ConflictFileIO {
3
+ interface ConflictFileIO {
4
4
  read(path: string): Promise<string>;
5
5
  write(path: string, content: string): Promise<void>;
6
6
  }
@@ -48,4 +48,5 @@ export declare function makeLlmConflictResolver(opts: LlmConflictResolverOptions
48
48
  conflictFiles: string[];
49
49
  cwd: string;
50
50
  }) => Promise<boolean>;
51
+ export {};
51
52
  //# sourceMappingURL=conflict-resolver.d.ts.map
package/dist/index.js CHANGED
@@ -2793,6 +2793,7 @@ import {
2793
2793
  makeAgentSubagentRunner,
2794
2794
  withDisabledToolFiltering
2795
2795
  } from "@wrongstack/core/coordination";
2796
+ import { requireSessionId } from "@wrongstack/primitives";
2796
2797
 
2797
2798
  // src/graph-split.ts
2798
2799
  function splitGraphNode(tracker, taskId, subtasks, options = {}) {
@@ -2825,6 +2826,112 @@ function splitGraphNode(tracker, taskId, subtasks, options = {}) {
2825
2826
  return leafIds;
2826
2827
  }
2827
2828
 
2829
+ // src/sdd-task-decomposer.ts
2830
+ var SddTaskDecomposer = class {
2831
+ constructor(tracker, _graph, opts = {}) {
2832
+ this.tracker = tracker;
2833
+ this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
2834
+ }
2835
+ tracker;
2836
+ slots;
2837
+ wave = 0;
2838
+ // -------------------------------------------------------------------
2839
+ // Public API
2840
+ // -------------------------------------------------------------------
2841
+ /**
2842
+ * Return the next batch of runnable tasks.
2843
+ * Returns `allDone: true` when every node is completed.
2844
+ * Returns `deadlocked: true` when no batch can be produced because
2845
+ * all remaining tasks are blocked by failed nodes.
2846
+ */
2847
+ nextBatch() {
2848
+ if (this.isDone()) {
2849
+ return { tasks: [], wave: this.wave, allDone: true, deadlocked: false };
2850
+ }
2851
+ const pending = this.pendingReadyNodes();
2852
+ if (pending.length === 0) {
2853
+ const hasBlockedTasks = this.hasAnyBlockedTasks();
2854
+ return { tasks: [], wave: this.wave, allDone: false, deadlocked: hasBlockedTasks };
2855
+ }
2856
+ const batch = pending.slice(0, this.slots);
2857
+ return { tasks: batch, wave: this.wave, allDone: false, deadlocked: false };
2858
+ }
2859
+ /**
2860
+ * Advance the wave counter after a batch completes.
2861
+ * Call this once per `nextBatch()` result that was fan-out.
2862
+ */
2863
+ acknowledgeBatch(_completedTaskIds) {
2864
+ this.wave++;
2865
+ }
2866
+ /**
2867
+ * True when every node in the graph is completed.
2868
+ * Use this to exit the fan-out loop after `isDone() || deadlocked`.
2869
+ */
2870
+ isDone() {
2871
+ const progress = this.tracker.getProgress();
2872
+ return progress.total > 0 && progress.completed === progress.total;
2873
+ }
2874
+ /**
2875
+ * Total waves produced so far.
2876
+ */
2877
+ getWaveCount() {
2878
+ return this.wave;
2879
+ }
2880
+ /**
2881
+ * All ready (dependency-satisfied) pending tasks, priority-sorted — UNSLICED.
2882
+ * The continuous scheduler fills its own free slots from this list, so unlike
2883
+ * `nextBatch()` it does not cap at `slots`.
2884
+ */
2885
+ readyNodes() {
2886
+ return this.pendingReadyNodes();
2887
+ }
2888
+ /**
2889
+ * True when every node has reached a terminal state (completed or failed).
2890
+ * This — not `isDone()` (which requires ALL completed) — is the correct loop
2891
+ * exit for the continuous scheduler: a terminally-failed task must not keep
2892
+ * the run spinning to its backstop.
2893
+ */
2894
+ isSettled() {
2895
+ const nodes = this.tracker.getAllNodes();
2896
+ return nodes.length > 0 && nodes.every((n) => n.status === "completed" || n.status === "failed");
2897
+ }
2898
+ // -------------------------------------------------------------------
2899
+ // Internal helpers
2900
+ // -------------------------------------------------------------------
2901
+ /**
2902
+ * Return pending nodes whose blockers are all completed.
2903
+ * Sorted by priority (critical first), then by creation time.
2904
+ */
2905
+ pendingReadyNodes() {
2906
+ const allPending = this.tracker.getAllNodes({ status: ["pending"] });
2907
+ const ready = [];
2908
+ for (const node of allPending) {
2909
+ if (this.tracker.canStart(node.id)) {
2910
+ ready.push(node);
2911
+ }
2912
+ }
2913
+ const priorityRank = {
2914
+ critical: 0,
2915
+ high: 1,
2916
+ medium: 2,
2917
+ low: 3
2918
+ };
2919
+ ready.sort((a, b) => {
2920
+ const pr = priorityRank[a.priority] - priorityRank[b.priority];
2921
+ if (pr !== 0) return pr;
2922
+ return a.createdAt - b.createdAt;
2923
+ });
2924
+ return ready;
2925
+ }
2926
+ /** True when at least one non-completed, non-failed task is blocked. */
2927
+ hasAnyBlockedTasks() {
2928
+ const nodes = this.tracker.getAllNodes({
2929
+ status: ["pending", "in_progress", "blocked"]
2930
+ });
2931
+ return nodes.some((n) => n.status === "blocked");
2932
+ }
2933
+ };
2934
+
2828
2935
  // src/sdd-task-execution.ts
2829
2936
  import { randomUUID as randomUUID2 } from "node:crypto";
2830
2937
  import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
@@ -3009,112 +3116,6 @@ async function verifyTaskResult(params, result) {
3009
3116
  return verificationFailReason;
3010
3117
  }
3011
3118
 
3012
- // src/sdd-task-decomposer.ts
3013
- var SddTaskDecomposer = class {
3014
- constructor(tracker, _graph, opts = {}) {
3015
- this.tracker = tracker;
3016
- this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
3017
- }
3018
- tracker;
3019
- slots;
3020
- wave = 0;
3021
- // -------------------------------------------------------------------
3022
- // Public API
3023
- // -------------------------------------------------------------------
3024
- /**
3025
- * Return the next batch of runnable tasks.
3026
- * Returns `allDone: true` when every node is completed.
3027
- * Returns `deadlocked: true` when no batch can be produced because
3028
- * all remaining tasks are blocked by failed nodes.
3029
- */
3030
- nextBatch() {
3031
- if (this.isDone()) {
3032
- return { tasks: [], wave: this.wave, allDone: true, deadlocked: false };
3033
- }
3034
- const pending = this.pendingReadyNodes();
3035
- if (pending.length === 0) {
3036
- const hasBlockedTasks = this.hasAnyBlockedTasks();
3037
- return { tasks: [], wave: this.wave, allDone: false, deadlocked: hasBlockedTasks };
3038
- }
3039
- const batch = pending.slice(0, this.slots);
3040
- return { tasks: batch, wave: this.wave, allDone: false, deadlocked: false };
3041
- }
3042
- /**
3043
- * Advance the wave counter after a batch completes.
3044
- * Call this once per `nextBatch()` result that was fan-out.
3045
- */
3046
- acknowledgeBatch(_completedTaskIds) {
3047
- this.wave++;
3048
- }
3049
- /**
3050
- * True when every node in the graph is completed.
3051
- * Use this to exit the fan-out loop after `isDone() || deadlocked`.
3052
- */
3053
- isDone() {
3054
- const progress = this.tracker.getProgress();
3055
- return progress.total > 0 && progress.completed === progress.total;
3056
- }
3057
- /**
3058
- * Total waves produced so far.
3059
- */
3060
- getWaveCount() {
3061
- return this.wave;
3062
- }
3063
- /**
3064
- * All ready (dependency-satisfied) pending tasks, priority-sorted — UNSLICED.
3065
- * The continuous scheduler fills its own free slots from this list, so unlike
3066
- * `nextBatch()` it does not cap at `slots`.
3067
- */
3068
- readyNodes() {
3069
- return this.pendingReadyNodes();
3070
- }
3071
- /**
3072
- * True when every node has reached a terminal state (completed or failed).
3073
- * This — not `isDone()` (which requires ALL completed) — is the correct loop
3074
- * exit for the continuous scheduler: a terminally-failed task must not keep
3075
- * the run spinning to its backstop.
3076
- */
3077
- isSettled() {
3078
- const nodes = this.tracker.getAllNodes();
3079
- return nodes.length > 0 && nodes.every((n) => n.status === "completed" || n.status === "failed");
3080
- }
3081
- // -------------------------------------------------------------------
3082
- // Internal helpers
3083
- // -------------------------------------------------------------------
3084
- /**
3085
- * Return pending nodes whose blockers are all completed.
3086
- * Sorted by priority (critical first), then by creation time.
3087
- */
3088
- pendingReadyNodes() {
3089
- const allPending = this.tracker.getAllNodes({ status: ["pending"] });
3090
- const ready = [];
3091
- for (const node of allPending) {
3092
- if (this.tracker.canStart(node.id)) {
3093
- ready.push(node);
3094
- }
3095
- }
3096
- const priorityRank = {
3097
- critical: 0,
3098
- high: 1,
3099
- medium: 2,
3100
- low: 3
3101
- };
3102
- ready.sort((a, b) => {
3103
- const pr = priorityRank[a.priority] - priorityRank[b.priority];
3104
- if (pr !== 0) return pr;
3105
- return a.createdAt - b.createdAt;
3106
- });
3107
- return ready;
3108
- }
3109
- /** True when at least one non-completed, non-failed task is blocked. */
3110
- hasAnyBlockedTasks() {
3111
- const nodes = this.tracker.getAllNodes({
3112
- status: ["pending", "in_progress", "blocked"]
3113
- });
3114
- return nodes.some((n) => n.status === "blocked");
3115
- }
3116
- };
3117
-
3118
3119
  // src/sdd-worktree-integration.ts
3119
3120
  function forgetTaskWorktree(state, taskId, opts = {}) {
3120
3121
  state.taskWorktrees.delete(taskId);
@@ -3332,7 +3333,7 @@ var SddParallelRun = class {
3332
3333
  }
3333
3334
  currentSessionId() {
3334
3335
  const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
3335
- return typeof value === "string" && value.length > 0 ? value : void 0;
3336
+ return requireSessionId(value, "SDD session operation");
3336
3337
  }
3337
3338
  // -------------------------------------------------------------------
3338
3339
  // Public API
@@ -3781,7 +3782,9 @@ var SddParallelRun = class {
3781
3782
  ...this.timeoutMs ? { timeoutMs: this.timeoutMs } : {}
3782
3783
  }
3783
3784
  };
3784
- this.coordinator = new DefaultMultiAgentCoordinator(config);
3785
+ this.coordinator = new DefaultMultiAgentCoordinator(config, {
3786
+ sessionId: () => this.currentSessionId()
3787
+ });
3785
3788
  const baseFactory = this.opts.subagentFactory ?? this.defaultFactory();
3786
3789
  const filteredFactory = withDisabledToolFiltering(baseFactory);
3787
3790
  const runner = makeAgentSubagentRunner({
@@ -11,7 +11,7 @@ export interface SddBoardStoreOptions {
11
11
  /** Injectable control-queue file operations for fault testing. */
12
12
  controlFileIO?: SddBoardControlFileIO | undefined;
13
13
  }
14
- export interface SddBoardControlFileIO {
14
+ interface SddBoardControlFileIO {
15
15
  stat(filePath: string): Promise<{
16
16
  size: number;
17
17
  }>;
@@ -27,7 +27,7 @@ export interface SddBoardIndexEntry {
27
27
  completed: number;
28
28
  updatedAt: number;
29
29
  }
30
- export interface IndexSignature {
30
+ interface IndexSignature {
31
31
  size: number;
32
32
  mtimeMs: number;
33
33
  ctimeMs: number;
@@ -95,4 +95,5 @@ export declare class SddBoardStore {
95
95
  private indexSignature;
96
96
  }
97
97
  export declare function sameIndexSignature(a: IndexSignature | null, b: IndexSignature | null): boolean;
98
+ export {};
98
99
  //# sourceMappingURL=sdd-board-store.d.ts.map
@@ -19,8 +19,8 @@
19
19
  */
20
20
  import type { TaskTracker } from '@wrongstack/core/tasking';
21
21
  import type { TaskNode } from '@wrongstack/core/types';
22
- import { type TaskBatch } from './sdd-task-decomposer.js';
23
22
  import type { RunResult, SddParallelRunOptions, SddSubtaskSpec, TaskOutcome, WaveResult } from './sdd-parallel-run-types.js';
23
+ import { type TaskBatch } from './sdd-task-decomposer.js';
24
24
  export type { RunResult, SddParallelRunOptions, SddProgress, SddSubtaskSpec, SddSupervisorVerdict, WaveResult, } from './sdd-parallel-run-types.js';
25
25
  export declare class SddParallelRun {
26
26
  private readonly opts;
@@ -1,7 +1,7 @@
1
1
  import type { TaskNode, TaskResult } from '@wrongstack/core/types';
2
2
  import type { WorktreeHandle } from '@wrongstack/core/worktree';
3
3
  import type { SddParallelRunOptions } from './sdd-parallel-run-types.js';
4
- export interface SddWorktreeState {
4
+ interface SddWorktreeState {
5
5
  taskCwds: Map<string, string>;
6
6
  taskBranches: Map<string, string>;
7
7
  taskWorktrees: Map<string, WorktreeHandle>;
@@ -30,4 +30,5 @@ export declare function integrateTaskWorktree(params: {
30
30
  reason?: string;
31
31
  fatal?: boolean;
32
32
  }>;
33
+ export {};
33
34
  //# sourceMappingURL=sdd-worktree-integration.d.ts.map
@@ -62,7 +62,7 @@ export interface SddRunHandle {
62
62
  /** Request a clean stop (idempotent). */
63
63
  stop(): void;
64
64
  }
65
- export interface SddControlCommand {
65
+ interface SddControlCommand {
66
66
  type: string;
67
67
  payload?: unknown;
68
68
  }
@@ -74,4 +74,5 @@ export declare function applySddControlCommand(run: SddParallelRun, command: Sdd
74
74
  * Orphaned in_progress tasks are reset up-front so a crashed prior run re-executes.
75
75
  */
76
76
  export declare function startSddRun(opts: StartSddRunOptions): SddRunHandle;
77
+ export {};
77
78
  //# sourceMappingURL=start-sdd-run.d.ts.map
@@ -1,13 +1,6 @@
1
1
  import type { TaskStore, TaskTracker } from '@wrongstack/core/tasking';
2
2
  import type { Specification, TaskGraph, TaskNode, TaskPriority, TaskType } from '@wrongstack/core/types';
3
3
  import { type AtomicityRuleSetConfig } from '@wrongstack/kanban';
4
- /** Named estimate constants shared with the atomicity candidate mapping. */
5
- export declare const OVERVIEW_ESTIMATE_HOURS = 4;
6
- export declare const REQUIREMENT_ESTIMATE_HOURS: Record<string, number>;
7
- export declare const API_PARENT_ESTIMATE_HOURS = 0;
8
- export declare const API_BASE_ESTIMATE_HOURS = 2;
9
- export declare const TESTS_ESTIMATE_HOURS = 4;
10
- export declare const DOCS_ESTIMATE_HOURS = 2;
11
4
  export interface TaskGeneratorOptions {
12
5
  taskTracker: TaskTracker;
13
6
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/sdd",
3
- "version": "0.313.1",
3
+ "version": "0.316.1",
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,10 @@
27
27
  "!dist/**/*.map"
28
28
  ],
29
29
  "dependencies": {
30
- "@wrongstack/kanban": "0.313.1",
31
- "@wrongstack/core": "0.313.1",
32
- "@wrongstack/requirement-intake": "0.313.1"
30
+ "@wrongstack/core": "0.316.1",
31
+ "@wrongstack/primitives": "0.316.1",
32
+ "@wrongstack/kanban": "0.316.1",
33
+ "@wrongstack/requirement-intake": "0.316.1"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/node": "^26.2.0",