@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.
- package/dist/conflict-resolver.d.ts +2 -1
- package/dist/index.js +111 -108
- package/dist/sdd-board-store.d.ts +3 -2
- package/dist/sdd-parallel-run.d.ts +1 -1
- package/dist/sdd-worktree-integration.d.ts +2 -1
- package/dist/start-sdd-run.d.ts +2 -1
- package/dist/task-generator.d.ts +0 -7
- package/package.json +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TaskNode } from '@wrongstack/core/types';
|
|
2
2
|
export type ConflictSide = 'incoming' | 'base';
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/start-sdd-run.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface SddRunHandle {
|
|
|
62
62
|
/** Request a clean stop (idempotent). */
|
|
63
63
|
stop(): void;
|
|
64
64
|
}
|
|
65
|
-
|
|
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
|
package/dist/task-generator.d.ts
CHANGED
|
@@ -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.
|
|
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/
|
|
31
|
-
"@wrongstack/
|
|
32
|
-
"@wrongstack/
|
|
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",
|