@wrongstack/sdd 0.298.3 → 0.299.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
|
@@ -3,7 +3,7 @@ export { TaskGenerator, extractVerificationCommand, assessGeneratedTaskAtomicity
|
|
|
3
3
|
export { TaskTracker, DefaultTaskStore, type TaskStore, type TaskTrackerOptions, type TaskTransition, type TaskTrackerChange, type TaskTrackerListener, } from '@wrongstack/core/tasking';
|
|
4
4
|
export { TaskFlow, SpecDrivenDev, type TaskFlowPhase, type TaskFlowOptions, type TaskFlowExecutionContext, type TaskFlowEventMap, type TaskFlowEventName, type SpecDrivenDevOptions, } from './task-flow.js';
|
|
5
5
|
export { SpecStore, type SpecStoreOptions, type SpecIndexEntry } from './spec-store.js';
|
|
6
|
-
export { TaskGraphStore, type TaskGraphStoreOptions, type TaskGraphIndexEntry } from './task-graph-store.js';
|
|
6
|
+
export { TaskGraphStore, type TaskGraphStoreOptions, type TaskGraphIndexEntry, } from './task-graph-store.js';
|
|
7
7
|
export { buildBoardTasks, buildBoardSnapshot, shortIdMap, type SddBoardSnapshot, type SddBoardTask, type SddBoardColumn, type SddBoardStatus, type SddTaskDisplayStatus, type SddDeadlockChain, type SddBoardFeedEntry, } from './board-types.js';
|
|
8
8
|
export { SddBoardStore, type SddBoardStoreOptions, type SddBoardIndexEntry, type SddBoardEvent, } from './sdd-board-store.js';
|
|
9
9
|
export { SddBoardProjector, type SddBoardPersistence, type SddBoardProjectorOptions, } from './sdd-board-projector.js';
|
|
@@ -14,9 +14,10 @@ export { cleanupSddWorktrees, cleanupStaleWorktrees, cleanupStaleSddWorktrees, r
|
|
|
14
14
|
export { AISpecBuilder, type AISpecBuilderOptions, type AISpecPhase, type AISpecSession, type AISpecSessionPersistence, type CollectedAnswer, isAISpecSession, } from './spec-builder.js';
|
|
15
15
|
export { createKanbanSddSessionPersistence } from './kanban-sdd-session.js';
|
|
16
16
|
export { gatherProjectContext } from './project-context.js';
|
|
17
|
+
export { intakeToInterviewKickoff, startInterviewFromIntake, type IntakeInterviewKickoff, } from './intake-kickoff.js';
|
|
17
18
|
export { SPEC_TEMPLATES, getTemplate, listTemplates, templateToMarkdown, } from './spec-templates.js';
|
|
18
19
|
export { renderTaskGraph, renderProgress, renderTaskList, renderSpecAnalysis, } from './task-visualizer.js';
|
|
19
|
-
export { analyzeCriticalPath, type CriticalPathAnalysis, type BottleneckTask } from './critical-path.js';
|
|
20
|
+
export { analyzeCriticalPath, type CriticalPathAnalysis, type BottleneckTask, } from './critical-path.js';
|
|
20
21
|
export { SpecVersioning, type SpecVersion, type SpecDiff } from './spec-versioning.js';
|
|
21
22
|
export { AutoExecutor, createAutoExecutor, type AutoExecutorOptions, type TaskExecutionContext, type TaskExecutionResult, type ExecutionSummary, } from './auto-executor.js';
|
|
22
23
|
export { SddTaskDecomposer, type SddTaskDecomposerOptions, type TaskBatch, } from './sdd-task-decomposer.js';
|
package/dist/index.js
CHANGED
|
@@ -2730,7 +2730,17 @@ async function executeSddTask(params) {
|
|
|
2730
2730
|
taskId,
|
|
2731
2731
|
reason: merged.reason
|
|
2732
2732
|
});
|
|
2733
|
-
|
|
2733
|
+
if (merged.fatal) {
|
|
2734
|
+
opts.tracker.updateNodeStatus(taskId, "failed", merged.reason);
|
|
2735
|
+
params.emit("sdd.task.failed", {
|
|
2736
|
+
runId: params.runId,
|
|
2737
|
+
taskId,
|
|
2738
|
+
subagentId,
|
|
2739
|
+
error: merged.reason
|
|
2740
|
+
});
|
|
2741
|
+
} else {
|
|
2742
|
+
await params.applyTaskFailure(taskId, subagentId, merged.reason);
|
|
2743
|
+
}
|
|
2734
2744
|
} else {
|
|
2735
2745
|
const conflictFiles = merged.conflictFiles ?? [];
|
|
2736
2746
|
params.emit("sdd.task.conflict", { runId: params.runId, taskId, conflictFiles });
|
|
@@ -2976,6 +2986,12 @@ var SddParallelRun = class {
|
|
|
2976
2986
|
* snapshot so a post-run rollback can read them off disk.
|
|
2977
2987
|
*/
|
|
2978
2988
|
mergedCommits = [];
|
|
2989
|
+
/**
|
|
2990
|
+
* Fatal, non-recoverable run error — set together with `stopRequested` when
|
|
2991
|
+
* the run hard-stops (e.g. a known-invalid merge that could not be rolled
|
|
2992
|
+
* back). Surfaced on `run()`'s result so the caller can see WHY it stopped.
|
|
2993
|
+
*/
|
|
2994
|
+
fatalError;
|
|
2979
2995
|
/** Monotonic dispatch counter (unique subagent ids) + dispatch-round counter. */
|
|
2980
2996
|
dispatchSeq = 0;
|
|
2981
2997
|
round = 0;
|
|
@@ -3175,6 +3191,7 @@ var SddParallelRun = class {
|
|
|
3175
3191
|
*/
|
|
3176
3192
|
async run() {
|
|
3177
3193
|
this.stopRequested = false;
|
|
3194
|
+
this.fatalError = void 0;
|
|
3178
3195
|
this.restoreRetryMap();
|
|
3179
3196
|
const startTime = Date.now();
|
|
3180
3197
|
this.round = 0;
|
|
@@ -3265,14 +3282,18 @@ var SddParallelRun = class {
|
|
|
3265
3282
|
this.opts.onProgress?.(this.buildProgress());
|
|
3266
3283
|
}
|
|
3267
3284
|
}
|
|
3268
|
-
if (this.stopRequested)
|
|
3285
|
+
if (this.stopRequested) {
|
|
3286
|
+
await Promise.allSettled(running.values());
|
|
3287
|
+
await this.teardown();
|
|
3288
|
+
}
|
|
3269
3289
|
const finalProgress = this.opts.tracker.getProgress();
|
|
3270
3290
|
this.emit("sdd.run.finished", {
|
|
3271
3291
|
runId: this.runId,
|
|
3272
3292
|
deadlocked,
|
|
3273
3293
|
completed: finalProgress.completed,
|
|
3274
3294
|
failed: finalProgress.failed,
|
|
3275
|
-
stopped: this.stopRequested
|
|
3295
|
+
stopped: this.stopRequested,
|
|
3296
|
+
...this.fatalError ? { fatalError: this.fatalError } : {}
|
|
3276
3297
|
});
|
|
3277
3298
|
return {
|
|
3278
3299
|
totalWaves: this.round,
|
|
@@ -3281,6 +3302,7 @@ var SddParallelRun = class {
|
|
|
3281
3302
|
totalDurationMs: Date.now() - startTime,
|
|
3282
3303
|
deadlocked,
|
|
3283
3304
|
stopRequested: this.stopRequested,
|
|
3305
|
+
...this.fatalError ? { fatalError: this.fatalError } : {},
|
|
3284
3306
|
finalProgress
|
|
3285
3307
|
};
|
|
3286
3308
|
}
|
|
@@ -3390,6 +3412,17 @@ var SddParallelRun = class {
|
|
|
3390
3412
|
}
|
|
3391
3413
|
}
|
|
3392
3414
|
}
|
|
3415
|
+
/**
|
|
3416
|
+
* Hard-stop the run after an unrecoverable error: the base branch is in a
|
|
3417
|
+
* state no retry can fix (e.g. a known-invalid merge that could not be rolled
|
|
3418
|
+
* back), so continuing would contaminate every task forked after this point.
|
|
3419
|
+
* The reason is surfaced on `run()`'s result and the `sdd.run.finished` event.
|
|
3420
|
+
*/
|
|
3421
|
+
abortRun(reason) {
|
|
3422
|
+
this.fatalError = reason;
|
|
3423
|
+
this.stopRequested = true;
|
|
3424
|
+
this.coordinator?.stopAll();
|
|
3425
|
+
}
|
|
3393
3426
|
// -------------------------------------------------------------------
|
|
3394
3427
|
// Internal
|
|
3395
3428
|
// -------------------------------------------------------------------
|
|
@@ -3592,8 +3625,16 @@ var SddParallelRun = class {
|
|
|
3592
3625
|
regressed = `verification error after conflict resolution: ${String(err)}`;
|
|
3593
3626
|
}
|
|
3594
3627
|
if (regressed) {
|
|
3595
|
-
await wt.revertBaseTo(handle, baseSha).catch(() =>
|
|
3596
|
-
|
|
3628
|
+
const rolledBack = await wt.revertBaseTo(handle, baseSha).catch(() => false);
|
|
3629
|
+
if (!rolledBack) {
|
|
3630
|
+
this.abortRun(
|
|
3631
|
+
`cannot roll back invalid merge of "${task.title}" (${task.id}): ${regressed}`
|
|
3632
|
+
);
|
|
3633
|
+
await wt.release(handle, { keep: true }).catch(() => {
|
|
3634
|
+
});
|
|
3635
|
+
this.forgetWorktree(task.id, { keepBranchLabel: true });
|
|
3636
|
+
return { ok: false, conflictFiles: [], reason: regressed, fatal: true };
|
|
3637
|
+
}
|
|
3597
3638
|
await wt.release(handle, { keep: false }).catch(() => {
|
|
3598
3639
|
});
|
|
3599
3640
|
this.forgetWorktree(task.id, { keepBranchLabel: true });
|
|
@@ -4226,6 +4267,32 @@ async function gatherProjectContext(projectRoot) {
|
|
|
4226
4267
|
return parts.join("\n");
|
|
4227
4268
|
}
|
|
4228
4269
|
|
|
4270
|
+
// src/intake-kickoff.ts
|
|
4271
|
+
function deriveTitle(value) {
|
|
4272
|
+
const firstLine = value.split("\n").map((line) => line.trim()).find(Boolean);
|
|
4273
|
+
if (!firstLine) return "New SDD Project";
|
|
4274
|
+
const sentence = firstLine.split(/(?<=[.!?])\s/)[0] ?? firstLine;
|
|
4275
|
+
return sentence.length <= 64 ? sentence : `${sentence.slice(0, 63).trimEnd()}\u2026`;
|
|
4276
|
+
}
|
|
4277
|
+
function intakeToInterviewKickoff(record) {
|
|
4278
|
+
const contextLines = [];
|
|
4279
|
+
if (record.businessGoal) contextLines.push(`Business goal: ${record.businessGoal}`);
|
|
4280
|
+
if (record.expectedOutcome) contextLines.push(`Expected outcome: ${record.expectedOutcome}`);
|
|
4281
|
+
if (record.scopeNotes) contextLines.push(`Scope notes: ${record.scopeNotes}`);
|
|
4282
|
+
for (const targetUser of record.targetUsers) contextLines.push(`Target user: ${targetUser}`);
|
|
4283
|
+
for (const constraint of record.constraints) contextLines.push(`Constraint: ${constraint}`);
|
|
4284
|
+
for (const context of record.providedContext) contextLines.push(`Context: ${context}`);
|
|
4285
|
+
return {
|
|
4286
|
+
title: deriveTitle(record.title),
|
|
4287
|
+
intent: record.originalRequest,
|
|
4288
|
+
projectContext: contextLines.length > 0 ? contextLines.join("\n") : ""
|
|
4289
|
+
};
|
|
4290
|
+
}
|
|
4291
|
+
function startInterviewFromIntake(driver, record) {
|
|
4292
|
+
const kickoff = intakeToInterviewKickoff(record);
|
|
4293
|
+
return driver.start(kickoff.title, kickoff.intent);
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4229
4296
|
// src/spec-templates.ts
|
|
4230
4297
|
var SPEC_TEMPLATES = [
|
|
4231
4298
|
{
|
|
@@ -5530,6 +5597,7 @@ export {
|
|
|
5530
5597
|
gatherProjectContext,
|
|
5531
5598
|
getTemplate,
|
|
5532
5599
|
hasConflictMarkers,
|
|
5600
|
+
intakeToInterviewKickoff,
|
|
5533
5601
|
isAISpecSession,
|
|
5534
5602
|
isExplanatoryText,
|
|
5535
5603
|
listTemplates,
|
|
@@ -5548,6 +5616,7 @@ export {
|
|
|
5548
5616
|
rollbackSddRunFromDisk,
|
|
5549
5617
|
shortIdMap,
|
|
5550
5618
|
splitGraphNode,
|
|
5619
|
+
startInterviewFromIntake,
|
|
5551
5620
|
startSddRun,
|
|
5552
5621
|
templateToMarkdown,
|
|
5553
5622
|
tokenizeCommand
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requirements Intake → SDD interview kickoff.
|
|
3
|
+
*
|
|
4
|
+
* Bridges `@wrongstack/requirement-intake` records into the spec-builder
|
|
5
|
+
* interview. The original request stays the interview's intent verbatim;
|
|
6
|
+
* collected high-level facts (goal, outcome, constraints, target users) are
|
|
7
|
+
* folded into the project context the questioning prompt receives. Nothing
|
|
8
|
+
* here rewrites or summarizes the original request — it only routes it.
|
|
9
|
+
*/
|
|
10
|
+
import type { RequirementIntakeRecord } from '@wrongstack/requirement-intake';
|
|
11
|
+
import type { SddInterviewDriver } from './sdd-interview-driver.js';
|
|
12
|
+
export interface IntakeInterviewKickoff {
|
|
13
|
+
/** Short interview title (≤ 64 chars, first sentence of the record title). */
|
|
14
|
+
title: string;
|
|
15
|
+
/** Interview intent — the exact original request, verbatim. */
|
|
16
|
+
intent: string;
|
|
17
|
+
/** Extra project context (goal/outcome/constraints/users) for the prompt. */
|
|
18
|
+
projectContext: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Map an intake record to interview kickoff values. `intent` is the exact
|
|
22
|
+
* `originalRequest`; `projectContext` carries the collected high-level fields
|
|
23
|
+
* so the spec interview does not re-ask for already-provided information.
|
|
24
|
+
*/
|
|
25
|
+
export declare function intakeToInterviewKickoff(record: RequirementIntakeRecord): IntakeInterviewKickoff;
|
|
26
|
+
/**
|
|
27
|
+
* Start a spec-builder interview from an intake record. Returns the first AI
|
|
28
|
+
* prompt (a question kickoff), ready to feed the agent loop. The driver should
|
|
29
|
+
* be constructed with `projectContext: kickoff.projectContext` so the
|
|
30
|
+
* questioning prompt already knows the collected facts.
|
|
31
|
+
*/
|
|
32
|
+
export declare function startInterviewFromIntake(driver: Pick<SddInterviewDriver, 'start'>, record: RequirementIntakeRecord): string;
|
|
33
|
+
//# sourceMappingURL=intake-kickoff.d.ts.map
|
|
@@ -195,6 +195,12 @@ export interface RunResult {
|
|
|
195
195
|
totalDurationMs: number;
|
|
196
196
|
deadlocked: boolean;
|
|
197
197
|
stopRequested: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Fatal, non-recoverable run error — set when the run hard-stopped
|
|
200
|
+
* (`stopRequested: true`) because no retry could fix the base state
|
|
201
|
+
* (e.g. a known-invalid merge that could not be rolled back).
|
|
202
|
+
*/
|
|
203
|
+
fatalError?: string | undefined;
|
|
198
204
|
finalProgress: TaskProgress;
|
|
199
205
|
}
|
|
200
206
|
//# sourceMappingURL=sdd-parallel-run-types.d.ts.map
|
|
@@ -74,6 +74,12 @@ export declare class SddParallelRun {
|
|
|
74
74
|
* snapshot so a post-run rollback can read them off disk.
|
|
75
75
|
*/
|
|
76
76
|
private mergedCommits;
|
|
77
|
+
/**
|
|
78
|
+
* Fatal, non-recoverable run error — set together with `stopRequested` when
|
|
79
|
+
* the run hard-stops (e.g. a known-invalid merge that could not be rolled
|
|
80
|
+
* back). Surfaced on `run()`'s result so the caller can see WHY it stopped.
|
|
81
|
+
*/
|
|
82
|
+
private fatalError;
|
|
77
83
|
/** Monotonic dispatch counter (unique subagent ids) + dispatch-round counter. */
|
|
78
84
|
private dispatchSeq;
|
|
79
85
|
private round;
|
|
@@ -197,6 +203,13 @@ export declare class SddParallelRun {
|
|
|
197
203
|
static resetOrphans(tracker: TaskTracker): number;
|
|
198
204
|
/** Clean teardown after a stop: reset interrupted tasks + release worktrees. */
|
|
199
205
|
private teardown;
|
|
206
|
+
/**
|
|
207
|
+
* Hard-stop the run after an unrecoverable error: the base branch is in a
|
|
208
|
+
* state no retry can fix (e.g. a known-invalid merge that could not be rolled
|
|
209
|
+
* back), so continuing would contaminate every task forked after this point.
|
|
210
|
+
* The reason is surfaced on `run()`'s result and the `sdd.run.finished` event.
|
|
211
|
+
*/
|
|
212
|
+
private abortRun;
|
|
200
213
|
private buildCoordinator;
|
|
201
214
|
private defaultFactory;
|
|
202
215
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.299.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,8 +27,9 @@
|
|
|
27
27
|
"!dist/**/*.map"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/core": "0.
|
|
31
|
-
"@wrongstack/kanban": "0.
|
|
30
|
+
"@wrongstack/core": "0.299.0",
|
|
31
|
+
"@wrongstack/kanban": "0.299.0",
|
|
32
|
+
"@wrongstack/requirement-intake": "0.299.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/node": "^26.1.2",
|