@wrongstack/sdd 1.0.24 → 1.0.27
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 +146 -68
- package/dist/sdd-parallel-run.d.ts +1 -19
- package/dist/sdd-supervisor.d.ts +2 -0
- package/dist/sdd-task-recovery.d.ts +20 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2049,10 +2049,10 @@ var AISpecBuilder = class {
|
|
|
2049
2049
|
return;
|
|
2050
2050
|
}
|
|
2051
2051
|
const fsp7 = await import("node:fs/promises");
|
|
2052
|
-
const
|
|
2052
|
+
const path7 = await import("node:path");
|
|
2053
2053
|
const { atomicWrite: atomicWrite4 } = await import("@wrongstack/core/utils");
|
|
2054
2054
|
const sessionPath = expectDefined2(this.sessionPath);
|
|
2055
|
-
await fsp7.mkdir(
|
|
2055
|
+
await fsp7.mkdir(path7.dirname(sessionPath), { recursive: true });
|
|
2056
2056
|
await atomicWrite4(sessionPath, JSON.stringify(snapshot, null, 2));
|
|
2057
2057
|
} catch (error) {
|
|
2058
2058
|
console.warn(
|
|
@@ -2822,6 +2822,68 @@ import {
|
|
|
2822
2822
|
writeKanbanWorkflowState
|
|
2823
2823
|
} from "@wrongstack/kanban";
|
|
2824
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
|
+
|
|
2825
2887
|
// src/sdd-parallel-run.ts
|
|
2826
2888
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
2827
2889
|
import {
|
|
@@ -3363,10 +3425,10 @@ var SddParallelRun = class {
|
|
|
3363
3425
|
/** Type-safe emit on the optional EventBus (no-op when unwired). */
|
|
3364
3426
|
emit(event, payload) {
|
|
3365
3427
|
const sessionId = this.currentSessionId();
|
|
3366
|
-
this.events?.emit(
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
);
|
|
3428
|
+
this.events?.emit(event, {
|
|
3429
|
+
...payload,
|
|
3430
|
+
sessionId
|
|
3431
|
+
});
|
|
3370
3432
|
}
|
|
3371
3433
|
currentSessionId() {
|
|
3372
3434
|
const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
|
|
@@ -3692,32 +3754,11 @@ var SddParallelRun = class {
|
|
|
3692
3754
|
* included since they're the usual deadlock cause once retries are exhausted.
|
|
3693
3755
|
*/
|
|
3694
3756
|
computeDeadlockChains() {
|
|
3695
|
-
|
|
3696
|
-
const chains = [];
|
|
3697
|
-
for (const node of tracker.getAllNodes()) {
|
|
3698
|
-
if (node.status === "completed" || node.status === "failed") continue;
|
|
3699
|
-
const blockedBy = tracker.getBlockers(node.id).filter((id) => tracker.getNode(id)?.status !== "completed");
|
|
3700
|
-
if (blockedBy.length > 0) chains.push({ blocked: node.id, blockedBy });
|
|
3701
|
-
}
|
|
3702
|
-
return chains;
|
|
3757
|
+
return computeDeadlockChains(this.sddTaskRecoveryHost());
|
|
3703
3758
|
}
|
|
3704
3759
|
/** Requeue failed tasks that block an incomplete dependent. Returns true if any. */
|
|
3705
3760
|
recoverFailedBlockers() {
|
|
3706
|
-
|
|
3707
|
-
let recovered = false;
|
|
3708
|
-
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
3709
|
-
const blocksIncomplete = tracker.getDependents(node.id).some((d) => {
|
|
3710
|
-
const s = tracker.getNode(d)?.status;
|
|
3711
|
-
return s !== "completed" && s !== "failed";
|
|
3712
|
-
});
|
|
3713
|
-
if (blocksIncomplete) {
|
|
3714
|
-
this.retryMap.delete(node.id);
|
|
3715
|
-
this.persistRetries(node.id, 0);
|
|
3716
|
-
tracker.updateNodeStatus(node.id, "pending", "deadlock recovery");
|
|
3717
|
-
recovered = true;
|
|
3718
|
-
}
|
|
3719
|
-
}
|
|
3720
|
-
return recovered;
|
|
3761
|
+
return recoverFailedBlockers(this.sddTaskRecoveryHost());
|
|
3721
3762
|
}
|
|
3722
3763
|
/**
|
|
3723
3764
|
* Requeue every terminal-failed task that the user did NOT cancel, giving each
|
|
@@ -3725,22 +3766,7 @@ var SddParallelRun = class {
|
|
|
3725
3766
|
* the manual "retry all failed" control. Returns the number requeued.
|
|
3726
3767
|
*/
|
|
3727
3768
|
requeueFailedTasks(reason = "retry failed sweep") {
|
|
3728
|
-
|
|
3729
|
-
let n = 0;
|
|
3730
|
-
for (const node of tracker.getAllNodes({ status: ["failed"] })) {
|
|
3731
|
-
if (this.cancelledTasks.has(node.id) || node.metadata?.cancelled) continue;
|
|
3732
|
-
this.retryMap.delete(node.id);
|
|
3733
|
-
this.persistRetries(node.id, 0);
|
|
3734
|
-
tracker.updateNodeStatus(node.id, "pending", reason);
|
|
3735
|
-
this.emit("sdd.task.retrying", {
|
|
3736
|
-
runId: this.runId,
|
|
3737
|
-
taskId: node.id,
|
|
3738
|
-
attempt: 0,
|
|
3739
|
-
maxRetries: this.maxRetries
|
|
3740
|
-
});
|
|
3741
|
-
n++;
|
|
3742
|
-
}
|
|
3743
|
-
return n;
|
|
3769
|
+
return requeueFailedTasks(this.sddTaskRecoveryHost(), reason);
|
|
3744
3770
|
}
|
|
3745
3771
|
/**
|
|
3746
3772
|
* Manually requeue all failed tasks to `pending` (board "Retry all failed").
|
|
@@ -3759,11 +3785,7 @@ var SddParallelRun = class {
|
|
|
3759
3785
|
}
|
|
3760
3786
|
/** Restore per-task retry counts persisted in node metadata (resume support). */
|
|
3761
3787
|
restoreRetryMap() {
|
|
3762
|
-
this.
|
|
3763
|
-
for (const node of this.opts.tracker.getAllNodes()) {
|
|
3764
|
-
const r = node.metadata?.retries;
|
|
3765
|
-
if (typeof r === "number" && r > 0) this.retryMap.set(node.id, r);
|
|
3766
|
-
}
|
|
3788
|
+
restoreRetryMap(this.sddTaskRecoveryHost());
|
|
3767
3789
|
}
|
|
3768
3790
|
/**
|
|
3769
3791
|
* Reset orphaned `in_progress` tasks (no agent runs them after a crash) back
|
|
@@ -3771,12 +3793,7 @@ var SddParallelRun = class {
|
|
|
3771
3793
|
* from a reloaded graph. Static so callers don't need a run instance.
|
|
3772
3794
|
*/
|
|
3773
3795
|
static resetOrphans(tracker) {
|
|
3774
|
-
|
|
3775
|
-
for (const node of tracker.getAllNodes({ status: ["in_progress"] })) {
|
|
3776
|
-
tracker.updateNodeStatus(node.id, "pending", "resume: orphaned in_progress");
|
|
3777
|
-
n++;
|
|
3778
|
-
}
|
|
3779
|
-
return n;
|
|
3796
|
+
return resetOrphans(tracker);
|
|
3780
3797
|
}
|
|
3781
3798
|
/** Clean teardown after a stop: reset interrupted tasks + release worktrees. */
|
|
3782
3799
|
async teardown() {
|
|
@@ -4027,6 +4044,24 @@ var SddParallelRun = class {
|
|
|
4027
4044
|
deadlocked: isDeadlocked
|
|
4028
4045
|
};
|
|
4029
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
|
+
}
|
|
4030
4065
|
};
|
|
4031
4066
|
|
|
4032
4067
|
// src/start-sdd-run.ts
|
|
@@ -5048,15 +5083,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
5048
5083
|
maxId = id;
|
|
5049
5084
|
}
|
|
5050
5085
|
}
|
|
5051
|
-
const
|
|
5086
|
+
const path7 = [];
|
|
5052
5087
|
let current = maxId;
|
|
5053
5088
|
const visited = /* @__PURE__ */ new Set();
|
|
5054
5089
|
while (current && !visited.has(current)) {
|
|
5055
5090
|
visited.add(current);
|
|
5056
|
-
|
|
5091
|
+
path7.unshift(current);
|
|
5057
5092
|
current = prev.get(current) ?? null;
|
|
5058
5093
|
}
|
|
5059
|
-
return
|
|
5094
|
+
return path7;
|
|
5060
5095
|
}
|
|
5061
5096
|
function computeParallelGroups(graph, blockedByMap) {
|
|
5062
5097
|
const groups = [];
|
|
@@ -5277,6 +5312,14 @@ var SpecVersioning = class {
|
|
|
5277
5312
|
};
|
|
5278
5313
|
|
|
5279
5314
|
// src/auto-executor.ts
|
|
5315
|
+
function asDependentFirstGraph(graph) {
|
|
5316
|
+
return {
|
|
5317
|
+
...graph,
|
|
5318
|
+
edges: graph.edges.map(
|
|
5319
|
+
(edge) => edge.type === "depends_on" ? { ...edge, from: edge.to, to: edge.from } : edge
|
|
5320
|
+
)
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5280
5323
|
var AutoExecutor = class {
|
|
5281
5324
|
opts;
|
|
5282
5325
|
stopped = false;
|
|
@@ -5293,7 +5336,7 @@ var AutoExecutor = class {
|
|
|
5293
5336
|
this.stopped = false;
|
|
5294
5337
|
this.retryMap.clear();
|
|
5295
5338
|
const startTime = Date.now();
|
|
5296
|
-
const critical = analyzeCriticalPath(graph);
|
|
5339
|
+
const critical = analyzeCriticalPath(asDependentFirstGraph(graph));
|
|
5297
5340
|
let completed = 0;
|
|
5298
5341
|
let failed = 0;
|
|
5299
5342
|
const skipped = 0;
|
|
@@ -5325,6 +5368,8 @@ var AutoExecutor = class {
|
|
|
5325
5368
|
this.opts.onTaskComplete?.(task, execResult);
|
|
5326
5369
|
} else if (execResult.retry) {
|
|
5327
5370
|
retried++;
|
|
5371
|
+
this.opts.tracker.updateNodeStatus(task.id, "failed", execResult.error);
|
|
5372
|
+
failed++;
|
|
5328
5373
|
} else {
|
|
5329
5374
|
this.opts.tracker.updateNodeStatus(task.id, "failed", execResult.error);
|
|
5330
5375
|
failed++;
|
|
@@ -5464,6 +5509,7 @@ var SddSupervisor = class {
|
|
|
5464
5509
|
const canSplit = Boolean(this.opts.generateSubtasks);
|
|
5465
5510
|
const decision = await this.opts.brain.decide({
|
|
5466
5511
|
id: `sdd-supervisor-${task.id}-${attempts}`,
|
|
5512
|
+
sessionId: this.opts.sessionId,
|
|
5467
5513
|
source: "system",
|
|
5468
5514
|
question: `SDD task "${task.title}" exhausted its retries. How should the run proceed?`,
|
|
5469
5515
|
context: `Error: ${error}
|
|
@@ -5502,6 +5548,28 @@ Supervisor rescues already used: ${attempts}`,
|
|
|
5502
5548
|
|
|
5503
5549
|
// src/verify-task.ts
|
|
5504
5550
|
import { spawn } from "node:child_process";
|
|
5551
|
+
import { accessSync } from "node:fs";
|
|
5552
|
+
import * as path6 from "node:path";
|
|
5553
|
+
import { buildWin32CmdShimInvocation } from "@wrongstack/core/utils";
|
|
5554
|
+
function win32BatchTarget(executable) {
|
|
5555
|
+
if (/\.(?:cmd|bat)$/i.test(executable)) return executable;
|
|
5556
|
+
if (/[\\/]/.test(executable) || path6.extname(executable)) return void 0;
|
|
5557
|
+
const exts = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").toLowerCase().split(";");
|
|
5558
|
+
for (const dir of (process.env["PATH"] ?? "").split(path6.delimiter)) {
|
|
5559
|
+
if (!dir) continue;
|
|
5560
|
+
for (const ext of exts) {
|
|
5561
|
+
if (!ext) continue;
|
|
5562
|
+
const full = path6.join(dir, `${executable}${ext}`);
|
|
5563
|
+
try {
|
|
5564
|
+
accessSync(full);
|
|
5565
|
+
} catch {
|
|
5566
|
+
continue;
|
|
5567
|
+
}
|
|
5568
|
+
return ext === ".cmd" || ext === ".bat" ? full : void 0;
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
return void 0;
|
|
5572
|
+
}
|
|
5505
5573
|
function tokenizeCommand(command) {
|
|
5506
5574
|
const trimmed = command.trim();
|
|
5507
5575
|
if (!trimmed) return void 0;
|
|
@@ -5638,12 +5706,22 @@ function makeCommandVerifier(options = {}) {
|
|
|
5638
5706
|
return { ok: false, reason: `verification command is malformed: ${rawCommand}` };
|
|
5639
5707
|
}
|
|
5640
5708
|
const [executable, ...args] = argv;
|
|
5709
|
+
const batch = process.platform === "win32" ? win32BatchTarget(executable) : void 0;
|
|
5710
|
+
let shim;
|
|
5711
|
+
if (batch) {
|
|
5712
|
+
try {
|
|
5713
|
+
shim = buildWin32CmdShimInvocation(batch, args);
|
|
5714
|
+
} catch (err) {
|
|
5715
|
+
return { ok: false, reason: `verification command refused: ${String(err)}` };
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5641
5718
|
return await new Promise((resolve3) => {
|
|
5642
|
-
const child = spawn(executable, args, {
|
|
5719
|
+
const child = spawn(shim?.command ?? executable, shim?.args ?? args, {
|
|
5643
5720
|
cwd: info.cwd,
|
|
5644
5721
|
shell: false,
|
|
5645
5722
|
windowsHide: true,
|
|
5646
|
-
stdio: "ignore"
|
|
5723
|
+
stdio: "ignore",
|
|
5724
|
+
...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
|
|
5647
5725
|
});
|
|
5648
5726
|
let timedOut = false;
|
|
5649
5727
|
const timer = setTimeout(() => {
|
|
@@ -5819,12 +5897,12 @@ async function decomposeNonAtomicTasks(opts) {
|
|
|
5819
5897
|
|
|
5820
5898
|
// src/conflict-resolver.ts
|
|
5821
5899
|
import { readFile as readFile6, writeFile } from "node:fs/promises";
|
|
5822
|
-
import { isAbsolute as isAbsolute3, join as
|
|
5900
|
+
import { isAbsolute as isAbsolute3, join as join7 } from "node:path";
|
|
5823
5901
|
import { readBundledInstructionText as readBundledInstructionText2, renderInstructionTemplate as renderInstructionTemplate2 } from "@wrongstack/core/utils";
|
|
5824
5902
|
var defaultFileIO = {
|
|
5825
|
-
read: (
|
|
5826
|
-
write: async (
|
|
5827
|
-
await writeFile(
|
|
5903
|
+
read: (path7) => readFile6(path7, "utf8"),
|
|
5904
|
+
write: async (path7, content) => {
|
|
5905
|
+
await writeFile(path7, content, "utf8");
|
|
5828
5906
|
}
|
|
5829
5907
|
};
|
|
5830
5908
|
var START = "<<<<<<<";
|
|
@@ -5868,7 +5946,7 @@ function makePreferSideConflictResolver(side, io = defaultFileIO) {
|
|
|
5868
5946
|
return async function conflictResolver(info) {
|
|
5869
5947
|
if (info.conflictFiles.length === 0) return false;
|
|
5870
5948
|
for (const rel of info.conflictFiles) {
|
|
5871
|
-
const abs = isAbsolute3(rel) ? rel :
|
|
5949
|
+
const abs = isAbsolute3(rel) ? rel : join7(info.cwd, rel);
|
|
5872
5950
|
let content;
|
|
5873
5951
|
try {
|
|
5874
5952
|
content = await io.read(abs);
|
|
@@ -5902,7 +5980,7 @@ function makeLlmConflictResolver(opts) {
|
|
|
5902
5980
|
return async function conflictResolver(info) {
|
|
5903
5981
|
if (info.conflictFiles.length === 0) return false;
|
|
5904
5982
|
for (const rel of info.conflictFiles) {
|
|
5905
|
-
const abs = isAbsolute3(rel) ? rel :
|
|
5983
|
+
const abs = isAbsolute3(rel) ? rel : join7(info.cwd, rel);
|
|
5906
5984
|
let content;
|
|
5907
5985
|
try {
|
|
5908
5986
|
content = await io.read(abs);
|
|
@@ -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
|
package/dist/sdd-supervisor.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { SddSubtaskSpec, SddSupervisorVerdict } from './sdd-parallel-run.js
|
|
|
4
4
|
export interface SddSupervisorOptions {
|
|
5
5
|
/** Decision authority (policy/LLM/human). Reuse the session's TOKENS.BrainArbiter. */
|
|
6
6
|
brain: BrainArbiter;
|
|
7
|
+
/** Session that started this run, retained when another tab becomes foreground. */
|
|
8
|
+
sessionId?: string | undefined;
|
|
7
9
|
/**
|
|
8
10
|
* Models to rotate through on a `reassign` verdict (e.g. the run's fallback
|
|
9
11
|
* chain). Omit to drop the reassign option entirely.
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
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.27",
|
|
31
|
+
"@wrongstack/kanban": "1.0.27",
|
|
32
|
+
"@wrongstack/primitives": "1.0.27",
|
|
33
|
+
"@wrongstack/requirement-intake": "1.0.27"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.5.1",
|