@wrongstack/sdd 1.0.19 → 1.0.26
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.js +124 -76
- package/dist/intake-kickoff.d.ts +3 -3
- package/dist/sdd-interview-driver.d.ts +1 -1
- package/dist/sdd-parallel-run.d.ts +1 -19
- package/dist/sdd-task-recovery.d.ts +20 -0
- package/dist/spec-builder.d.ts +3 -2
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -739,9 +739,9 @@ var SpecDrivenDev = class {
|
|
|
739
739
|
};
|
|
740
740
|
|
|
741
741
|
// src/spec-store.ts
|
|
742
|
+
import { randomUUID } from "node:crypto";
|
|
742
743
|
import * as fsp from "node:fs/promises";
|
|
743
744
|
import * as path from "node:path";
|
|
744
|
-
import { randomUUID } from "node:crypto";
|
|
745
745
|
import { atomicWrite, ensureDir } from "@wrongstack/core/utils";
|
|
746
746
|
var SpecStore = class {
|
|
747
747
|
baseDir;
|
|
@@ -833,7 +833,7 @@ var SpecStore = class {
|
|
|
833
833
|
const dir = path.resolve(this.baseDir);
|
|
834
834
|
const resolved = path.resolve(dir, `${id}.json`);
|
|
835
835
|
const rel = path.relative(dir, resolved);
|
|
836
|
-
if (rel.startsWith(
|
|
836
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel) || rel.includes(path.sep)) {
|
|
837
837
|
throw new Error(`Invalid spec id: ${JSON.stringify(id)}`);
|
|
838
838
|
}
|
|
839
839
|
return resolved;
|
|
@@ -970,7 +970,7 @@ var TaskGraphStore = class {
|
|
|
970
970
|
const dir = path2.resolve(this.baseDir);
|
|
971
971
|
const resolved = path2.resolve(dir, `${id}.json`);
|
|
972
972
|
const rel = path2.relative(dir, resolved);
|
|
973
|
-
if (rel.startsWith(
|
|
973
|
+
if (rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel) || rel.includes(path2.sep)) {
|
|
974
974
|
throw new Error(`Invalid task-graph id: ${JSON.stringify(id)}`);
|
|
975
975
|
}
|
|
976
976
|
return resolved;
|
|
@@ -1358,14 +1358,16 @@ var SddBoardStore = class {
|
|
|
1358
1358
|
});
|
|
1359
1359
|
}
|
|
1360
1360
|
async removeFromIndex(runId) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1361
|
+
await withFileLock(this.indexPath, async () => {
|
|
1362
|
+
const current = await this.readIndex();
|
|
1363
|
+
const index = {
|
|
1364
|
+
version: 1,
|
|
1365
|
+
entries: current.entries.filter((entry) => entry.runId !== runId).map((entry) => ({ ...entry }))
|
|
1366
|
+
};
|
|
1367
|
+
await atomicWrite3(this.indexPath, JSON.stringify(index, null, 2), { mode: 384 });
|
|
1368
|
+
this.cachedIndex = index;
|
|
1369
|
+
this.cachedIndexSignature = await this.indexSignature();
|
|
1370
|
+
});
|
|
1369
1371
|
}
|
|
1370
1372
|
async indexSignature() {
|
|
1371
1373
|
try {
|
|
@@ -2011,6 +2013,7 @@ var AISpecBuilder = class {
|
|
|
2011
2013
|
store;
|
|
2012
2014
|
minQuestions;
|
|
2013
2015
|
maxQuestions;
|
|
2016
|
+
projectContext;
|
|
2014
2017
|
sessionPath;
|
|
2015
2018
|
sessionPersistence;
|
|
2016
2019
|
pendingSessionWrite = Promise.resolve();
|
|
@@ -2018,6 +2021,7 @@ var AISpecBuilder = class {
|
|
|
2018
2021
|
this.store = opts.store;
|
|
2019
2022
|
this.minQuestions = opts.minQuestions ?? 2;
|
|
2020
2023
|
this.maxQuestions = opts.maxQuestions ?? 10;
|
|
2024
|
+
this.projectContext = opts.projectContext ?? "";
|
|
2021
2025
|
this.sessionPath = opts.sessionPath;
|
|
2022
2026
|
this.sessionPersistence = opts.sessionPersistence;
|
|
2023
2027
|
this.session = {
|
|
@@ -2025,7 +2029,7 @@ var AISpecBuilder = class {
|
|
|
2025
2029
|
phase: "questioning",
|
|
2026
2030
|
title: "",
|
|
2027
2031
|
userIntent: "",
|
|
2028
|
-
projectContext:
|
|
2032
|
+
projectContext: this.projectContext,
|
|
2029
2033
|
answers: [],
|
|
2030
2034
|
questionCount: 0,
|
|
2031
2035
|
approved: false,
|
|
@@ -2105,10 +2109,11 @@ var AISpecBuilder = class {
|
|
|
2105
2109
|
void this.saveSession();
|
|
2106
2110
|
}
|
|
2107
2111
|
// ── Session Lifecycle ─────────────────────────────────────────────────────
|
|
2108
|
-
/** Start a new session with a title and
|
|
2109
|
-
startSession(title, intent) {
|
|
2112
|
+
/** Start a new session with a title, optional intent, and per-session context. */
|
|
2113
|
+
startSession(title, intent, projectContext) {
|
|
2110
2114
|
this.session.title = title;
|
|
2111
2115
|
this.session.userIntent = intent ?? "";
|
|
2116
|
+
this.session.projectContext = projectContext ?? this.projectContext;
|
|
2112
2117
|
this.session.phase = "questioning";
|
|
2113
2118
|
this.session.updatedAt = Date.now();
|
|
2114
2119
|
this.autoSave();
|
|
@@ -2308,6 +2313,7 @@ var AISpecBuilder = class {
|
|
|
2308
2313
|
this.session.phase = "questioning";
|
|
2309
2314
|
this.session.title = "";
|
|
2310
2315
|
this.session.userIntent = "";
|
|
2316
|
+
this.session.projectContext = this.projectContext;
|
|
2311
2317
|
this.session.answers = [];
|
|
2312
2318
|
this.session.questionCount = 0;
|
|
2313
2319
|
this.session.spec = void 0;
|
|
@@ -2500,9 +2506,9 @@ var SddInterviewDriver = class {
|
|
|
2500
2506
|
});
|
|
2501
2507
|
}
|
|
2502
2508
|
/** Begin a fresh interview. Returns the first AI prompt (a question kickoff). */
|
|
2503
|
-
start(title, intent) {
|
|
2509
|
+
start(title, intent, projectContext) {
|
|
2504
2510
|
this.builder.resetForNewInterview();
|
|
2505
|
-
this.builder.startSession(title, intent);
|
|
2511
|
+
this.builder.startSession(title, intent, projectContext);
|
|
2506
2512
|
this.tracker = null;
|
|
2507
2513
|
this.graph = null;
|
|
2508
2514
|
this.resumedFromDisk = false;
|
|
@@ -2816,6 +2822,68 @@ import {
|
|
|
2816
2822
|
writeKanbanWorkflowState
|
|
2817
2823
|
} from "@wrongstack/kanban";
|
|
2818
2824
|
|
|
2825
|
+
// src/sdd-task-recovery.ts
|
|
2826
|
+
function computeDeadlockChains(host) {
|
|
2827
|
+
const tracker = host.opts.tracker;
|
|
2828
|
+
const chains = [];
|
|
2829
|
+
for (const node of tracker.getAllNodes()) {
|
|
2830
|
+
if (node.status === "completed" || node.status === "failed") continue;
|
|
2831
|
+
const blockedBy = tracker.getBlockers(node.id).filter((id) => tracker.getNode(id)?.status !== "completed");
|
|
2832
|
+
if (blockedBy.length > 0) chains.push({ blocked: node.id, blockedBy });
|
|
2833
|
+
}
|
|
2834
|
+
return chains;
|
|
2835
|
+
}
|
|
2836
|
+
function recoverFailedBlockers(host) {
|
|
2837
|
+
const tracker = host.opts.tracker;
|
|
2838
|
+
let recovered = false;
|
|
2839
|
+
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
2840
|
+
const blocksIncomplete = tracker.getDependents(node.id).some((d) => {
|
|
2841
|
+
const s = tracker.getNode(d)?.status;
|
|
2842
|
+
return s !== "completed" && s !== "failed";
|
|
2843
|
+
});
|
|
2844
|
+
if (blocksIncomplete) {
|
|
2845
|
+
host.retryMap.delete(node.id);
|
|
2846
|
+
host.persistRetries(node.id, 0);
|
|
2847
|
+
tracker.updateNodeStatus(node.id, "pending", "deadlock recovery");
|
|
2848
|
+
recovered = true;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
return recovered;
|
|
2852
|
+
}
|
|
2853
|
+
function requeueFailedTasks(host, reason = "retry failed sweep") {
|
|
2854
|
+
const tracker = host.opts.tracker;
|
|
2855
|
+
let n = 0;
|
|
2856
|
+
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
2857
|
+
if (host.cancelledTasks.has(node.id) || node.metadata?.cancelled) continue;
|
|
2858
|
+
host.retryMap.delete(node.id);
|
|
2859
|
+
host.persistRetries(node.id, 0);
|
|
2860
|
+
tracker.updateNodeStatus(node.id, "pending", reason);
|
|
2861
|
+
host.emit("sdd.task.retrying", {
|
|
2862
|
+
runId: host.runId,
|
|
2863
|
+
taskId: node.id,
|
|
2864
|
+
attempt: 0,
|
|
2865
|
+
maxRetries: host.maxRetries
|
|
2866
|
+
});
|
|
2867
|
+
n++;
|
|
2868
|
+
}
|
|
2869
|
+
return n;
|
|
2870
|
+
}
|
|
2871
|
+
function restoreRetryMap(host) {
|
|
2872
|
+
host.retryMap.clear();
|
|
2873
|
+
for (const node of host.opts.tracker.getAllNodes()) {
|
|
2874
|
+
const r = node.metadata?.retries;
|
|
2875
|
+
if (typeof r === "number" && r > 0) host.retryMap.set(node.id, r);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
function resetOrphans(tracker) {
|
|
2879
|
+
let n = 0;
|
|
2880
|
+
for (const node of tracker.getAllNodes({ status: ["in_progress"] })) {
|
|
2881
|
+
tracker.updateNodeStatus(node.id, "pending", "resume: orphaned in_progress");
|
|
2882
|
+
n++;
|
|
2883
|
+
}
|
|
2884
|
+
return n;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2819
2887
|
// src/sdd-parallel-run.ts
|
|
2820
2888
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
2821
2889
|
import {
|
|
@@ -3357,10 +3425,10 @@ var SddParallelRun = class {
|
|
|
3357
3425
|
/** Type-safe emit on the optional EventBus (no-op when unwired). */
|
|
3358
3426
|
emit(event, payload) {
|
|
3359
3427
|
const sessionId = this.currentSessionId();
|
|
3360
|
-
this.events?.emit(
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
);
|
|
3428
|
+
this.events?.emit(event, {
|
|
3429
|
+
...payload,
|
|
3430
|
+
sessionId
|
|
3431
|
+
});
|
|
3364
3432
|
}
|
|
3365
3433
|
currentSessionId() {
|
|
3366
3434
|
const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
|
|
@@ -3686,32 +3754,11 @@ var SddParallelRun = class {
|
|
|
3686
3754
|
* included since they're the usual deadlock cause once retries are exhausted.
|
|
3687
3755
|
*/
|
|
3688
3756
|
computeDeadlockChains() {
|
|
3689
|
-
|
|
3690
|
-
const chains = [];
|
|
3691
|
-
for (const node of tracker.getAllNodes()) {
|
|
3692
|
-
if (node.status === "completed" || node.status === "failed") continue;
|
|
3693
|
-
const blockedBy = tracker.getBlockers(node.id).filter((id) => tracker.getNode(id)?.status !== "completed");
|
|
3694
|
-
if (blockedBy.length > 0) chains.push({ blocked: node.id, blockedBy });
|
|
3695
|
-
}
|
|
3696
|
-
return chains;
|
|
3757
|
+
return computeDeadlockChains(this.sddTaskRecoveryHost());
|
|
3697
3758
|
}
|
|
3698
3759
|
/** Requeue failed tasks that block an incomplete dependent. Returns true if any. */
|
|
3699
3760
|
recoverFailedBlockers() {
|
|
3700
|
-
|
|
3701
|
-
let recovered = false;
|
|
3702
|
-
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
3703
|
-
const blocksIncomplete = tracker.getDependents(node.id).some((d) => {
|
|
3704
|
-
const s = tracker.getNode(d)?.status;
|
|
3705
|
-
return s !== "completed" && s !== "failed";
|
|
3706
|
-
});
|
|
3707
|
-
if (blocksIncomplete) {
|
|
3708
|
-
this.retryMap.delete(node.id);
|
|
3709
|
-
this.persistRetries(node.id, 0);
|
|
3710
|
-
tracker.updateNodeStatus(node.id, "pending", "deadlock recovery");
|
|
3711
|
-
recovered = true;
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
return recovered;
|
|
3761
|
+
return recoverFailedBlockers(this.sddTaskRecoveryHost());
|
|
3715
3762
|
}
|
|
3716
3763
|
/**
|
|
3717
3764
|
* Requeue every terminal-failed task that the user did NOT cancel, giving each
|
|
@@ -3719,22 +3766,7 @@ var SddParallelRun = class {
|
|
|
3719
3766
|
* the manual "retry all failed" control. Returns the number requeued.
|
|
3720
3767
|
*/
|
|
3721
3768
|
requeueFailedTasks(reason = "retry failed sweep") {
|
|
3722
|
-
|
|
3723
|
-
let n = 0;
|
|
3724
|
-
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
3725
|
-
if (this.cancelledTasks.has(node.id) || node.metadata?.cancelled) continue;
|
|
3726
|
-
this.retryMap.delete(node.id);
|
|
3727
|
-
this.persistRetries(node.id, 0);
|
|
3728
|
-
tracker.updateNodeStatus(node.id, "pending", reason);
|
|
3729
|
-
this.emit("sdd.task.retrying", {
|
|
3730
|
-
runId: this.runId,
|
|
3731
|
-
taskId: node.id,
|
|
3732
|
-
attempt: 0,
|
|
3733
|
-
maxRetries: this.maxRetries
|
|
3734
|
-
});
|
|
3735
|
-
n++;
|
|
3736
|
-
}
|
|
3737
|
-
return n;
|
|
3769
|
+
return requeueFailedTasks(this.sddTaskRecoveryHost(), reason);
|
|
3738
3770
|
}
|
|
3739
3771
|
/**
|
|
3740
3772
|
* Manually requeue all failed tasks to `pending` (board "Retry all failed").
|
|
@@ -3753,11 +3785,7 @@ var SddParallelRun = class {
|
|
|
3753
3785
|
}
|
|
3754
3786
|
/** Restore per-task retry counts persisted in node metadata (resume support). */
|
|
3755
3787
|
restoreRetryMap() {
|
|
3756
|
-
this.
|
|
3757
|
-
for (const node of this.opts.tracker.getAllNodes()) {
|
|
3758
|
-
const r = node.metadata?.retries;
|
|
3759
|
-
if (typeof r === "number" && r > 0) this.retryMap.set(node.id, r);
|
|
3760
|
-
}
|
|
3788
|
+
restoreRetryMap(this.sddTaskRecoveryHost());
|
|
3761
3789
|
}
|
|
3762
3790
|
/**
|
|
3763
3791
|
* Reset orphaned `in_progress` tasks (no agent runs them after a crash) back
|
|
@@ -3765,12 +3793,7 @@ var SddParallelRun = class {
|
|
|
3765
3793
|
* from a reloaded graph. Static so callers don't need a run instance.
|
|
3766
3794
|
*/
|
|
3767
3795
|
static resetOrphans(tracker) {
|
|
3768
|
-
|
|
3769
|
-
for (const node of tracker.getAllNodes({ status: ["in_progress"] })) {
|
|
3770
|
-
tracker.updateNodeStatus(node.id, "pending", "resume: orphaned in_progress");
|
|
3771
|
-
n++;
|
|
3772
|
-
}
|
|
3773
|
-
return n;
|
|
3796
|
+
return resetOrphans(tracker);
|
|
3774
3797
|
}
|
|
3775
3798
|
/** Clean teardown after a stop: reset interrupted tasks + release worktrees. */
|
|
3776
3799
|
async teardown() {
|
|
@@ -4021,6 +4044,24 @@ var SddParallelRun = class {
|
|
|
4021
4044
|
deadlocked: isDeadlocked
|
|
4022
4045
|
};
|
|
4023
4046
|
}
|
|
4047
|
+
sddTaskRecoveryHost() {
|
|
4048
|
+
const self = this;
|
|
4049
|
+
return {
|
|
4050
|
+
get opts() {
|
|
4051
|
+
return self.opts;
|
|
4052
|
+
},
|
|
4053
|
+
get retryMap() {
|
|
4054
|
+
return self.retryMap;
|
|
4055
|
+
},
|
|
4056
|
+
persistRetries: (...args) => this.persistRetries(...args),
|
|
4057
|
+
get cancelledTasks() {
|
|
4058
|
+
return self.cancelledTasks;
|
|
4059
|
+
},
|
|
4060
|
+
emit: (...args) => this.emit(...args),
|
|
4061
|
+
runId: this.runId,
|
|
4062
|
+
maxRetries: this.maxRetries
|
|
4063
|
+
};
|
|
4064
|
+
}
|
|
4024
4065
|
};
|
|
4025
4066
|
|
|
4026
4067
|
// src/start-sdd-run.ts
|
|
@@ -4493,10 +4534,13 @@ function createKanbanSddSessionPersistence(projectRoot, legacySessionPath) {
|
|
|
4493
4534
|
await pending;
|
|
4494
4535
|
},
|
|
4495
4536
|
async delete() {
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4537
|
+
const pending = writeChain.then(async () => {
|
|
4538
|
+
await deleteKanbanWorkflowState2(projectRoot, SDD_SESSION_WORKFLOW_ID);
|
|
4539
|
+
revision = 0;
|
|
4540
|
+
if (legacySessionPath) await fsp5.unlink(legacySessionPath).catch(() => void 0);
|
|
4541
|
+
});
|
|
4542
|
+
writeChain = pending.catch(() => void 0);
|
|
4543
|
+
await pending;
|
|
4500
4544
|
}
|
|
4501
4545
|
};
|
|
4502
4546
|
}
|
|
@@ -4581,7 +4625,7 @@ function intakeToInterviewKickoff(record) {
|
|
|
4581
4625
|
}
|
|
4582
4626
|
function startInterviewFromIntake(driver, record) {
|
|
4583
4627
|
const kickoff = intakeToInterviewKickoff(record);
|
|
4584
|
-
return driver.start(kickoff.title, kickoff.intent);
|
|
4628
|
+
return driver.start(kickoff.title, kickoff.intent, kickoff.projectContext);
|
|
4585
4629
|
}
|
|
4586
4630
|
|
|
4587
4631
|
// src/spec-templates.ts
|
|
@@ -5051,7 +5095,9 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
5051
5095
|
}
|
|
5052
5096
|
function computeParallelGroups(graph, blockedByMap) {
|
|
5053
5097
|
const groups = [];
|
|
5054
|
-
const assigned =
|
|
5098
|
+
const assigned = new Set(
|
|
5099
|
+
Array.from(graph.nodes.values()).filter((n) => n.status === "completed").map((n) => n.id)
|
|
5100
|
+
);
|
|
5055
5101
|
const nodes = Array.from(graph.nodes.values()).filter((n) => n.status !== "completed");
|
|
5056
5102
|
const remaining = new Set(nodes.map((n) => n.id));
|
|
5057
5103
|
while (remaining.size > 0) {
|
|
@@ -5314,6 +5360,8 @@ var AutoExecutor = class {
|
|
|
5314
5360
|
this.opts.onTaskComplete?.(task, execResult);
|
|
5315
5361
|
} else if (execResult.retry) {
|
|
5316
5362
|
retried++;
|
|
5363
|
+
this.opts.tracker.updateNodeStatus(task.id, "failed", execResult.error);
|
|
5364
|
+
failed++;
|
|
5317
5365
|
} else {
|
|
5318
5366
|
this.opts.tracker.updateNodeStatus(task.id, "failed", execResult.error);
|
|
5319
5367
|
failed++;
|
package/dist/intake-kickoff.d.ts
CHANGED
|
@@ -25,9 +25,9 @@ export interface IntakeInterviewKickoff {
|
|
|
25
25
|
export declare function intakeToInterviewKickoff(record: RequirementIntakeRecord): IntakeInterviewKickoff;
|
|
26
26
|
/**
|
|
27
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.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* prompt (a question kickoff), ready to feed the agent loop. Collected intake
|
|
29
|
+
* facts are passed as this interview's project context so the questioning
|
|
30
|
+
* prompt already knows them.
|
|
31
31
|
*/
|
|
32
32
|
export declare function startInterviewFromIntake(driver: Pick<SddInterviewDriver, 'start'>, record: RequirementIntakeRecord): string;
|
|
33
33
|
//# sourceMappingURL=intake-kickoff.d.ts.map
|
|
@@ -78,7 +78,7 @@ export declare class SddInterviewDriver {
|
|
|
78
78
|
private resumedFromDisk;
|
|
79
79
|
constructor(opts: SddInterviewDriverOptions);
|
|
80
80
|
/** Begin a fresh interview. Returns the first AI prompt (a question kickoff). */
|
|
81
|
-
start(title: string, intent?: string): string;
|
|
81
|
+
start(title: string, intent?: string, projectContext?: string): string;
|
|
82
82
|
/**
|
|
83
83
|
* Resume a previously-persisted interview. Re-hydrates the task
|
|
84
84
|
* graph too when one was already produced. Returns true if a session loaded.
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SddParallelRun
|
|
3
|
-
*
|
|
4
|
-
* Drives a TaskGraph through ParallelEternalEngine's infrastructure
|
|
5
|
-
* (DefaultMultiAgentCoordinator + AgentSubagentRunner) but powered by
|
|
6
|
-
* SddTaskDecomposer — producing dependency-aware waves instead of
|
|
7
|
-
* goal-driven iterations.
|
|
8
|
-
*
|
|
9
|
-
* One-shot: completes when all tasks are done OR a deadlock is detected.
|
|
10
|
-
* Does NOT loop — each run() call is a discrete execution.
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* ```
|
|
14
|
-
* const run = new SddParallelRun({ tracker, graph, agent, projectRoot });
|
|
15
|
-
* await run.run({ onWave });
|
|
16
|
-
* // or with progress callback:
|
|
17
|
-
* await run.run({ onProgress: (p) => console.log(renderProgress(p)) });
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
1
|
import type { TaskTracker } from '@wrongstack/core/tasking';
|
|
21
2
|
import type { TaskNode } from '@wrongstack/core/types';
|
|
22
3
|
import type { RunResult, SddParallelRunOptions, SddSubtaskSpec, TaskOutcome, WaveResult } from './sdd-parallel-run-types.js';
|
|
@@ -270,5 +251,6 @@ export declare class SddParallelRun {
|
|
|
270
251
|
/** Persist a task's retry count into node metadata (survives crash → resume). */
|
|
271
252
|
private persistRetries;
|
|
272
253
|
private buildProgress;
|
|
254
|
+
private sddTaskRecoveryHost;
|
|
273
255
|
}
|
|
274
256
|
//# sourceMappingURL=sdd-parallel-run.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TaskTracker } from '@wrongstack/core/tasking';
|
|
2
|
+
import type { SddParallelRunOptions } from './sdd-parallel-run-types.js';
|
|
3
|
+
export interface SddTaskRecoveryHost {
|
|
4
|
+
opts: SddParallelRunOptions;
|
|
5
|
+
retryMap: Map<string, number>;
|
|
6
|
+
persistRetries: (taskId: string, retries: number) => void;
|
|
7
|
+
cancelledTasks: Set<string>;
|
|
8
|
+
emit: <K extends keyof import('@wrongstack/core/kernel').EventMap>(event: K, payload: import('@wrongstack/core/kernel').EventMap[K]) => void;
|
|
9
|
+
runId: string;
|
|
10
|
+
maxRetries: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function computeDeadlockChains(host: SddTaskRecoveryHost): Array<{
|
|
13
|
+
blocked: string;
|
|
14
|
+
blockedBy: string[];
|
|
15
|
+
}>;
|
|
16
|
+
export declare function recoverFailedBlockers(host: SddTaskRecoveryHost): boolean;
|
|
17
|
+
export declare function requeueFailedTasks(host: SddTaskRecoveryHost, reason?: string): number;
|
|
18
|
+
export declare function restoreRetryMap(host: SddTaskRecoveryHost): void;
|
|
19
|
+
export declare function resetOrphans(tracker: TaskTracker): number;
|
|
20
|
+
//# sourceMappingURL=sdd-task-recovery.d.ts.map
|
package/dist/spec-builder.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare class AISpecBuilder {
|
|
|
25
25
|
private readonly store;
|
|
26
26
|
private readonly minQuestions;
|
|
27
27
|
private readonly maxQuestions;
|
|
28
|
+
private readonly projectContext;
|
|
28
29
|
private readonly sessionPath?;
|
|
29
30
|
private readonly sessionPersistence?;
|
|
30
31
|
private pendingSessionWrite;
|
|
@@ -37,8 +38,8 @@ export declare class AISpecBuilder {
|
|
|
37
38
|
deleteSession(): Promise<void>;
|
|
38
39
|
/** Auto-save helper. saveSession() already handles best-effort persistence. */
|
|
39
40
|
private autoSave;
|
|
40
|
-
/** Start a new session with a title and
|
|
41
|
-
startSession(title: string, intent?: string): void;
|
|
41
|
+
/** Start a new session with a title, optional intent, and per-session context. */
|
|
42
|
+
startSession(title: string, intent?: string, projectContext?: string): void;
|
|
42
43
|
/** Get current session state (readonly). */
|
|
43
44
|
getSession(): Readonly<AISpecSession>;
|
|
44
45
|
/** Get the current phase. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
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,10 +27,10 @@
|
|
|
27
27
|
"!dist/**/*.map"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/core": "1.0.
|
|
31
|
-
"@wrongstack/kanban": "1.0.
|
|
32
|
-
"@wrongstack/primitives": "1.0.
|
|
33
|
-
"@wrongstack/requirement-intake": "1.0.
|
|
30
|
+
"@wrongstack/core": "1.0.26",
|
|
31
|
+
"@wrongstack/kanban": "1.0.26",
|
|
32
|
+
"@wrongstack/primitives": "1.0.26",
|
|
33
|
+
"@wrongstack/requirement-intake": "1.0.26"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.5.1",
|