@calmo/task-runner 1.2.3 → 3.0.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/.jules/sentinel.md +4 -0
- package/AGENTS.md +53 -1
- package/CHANGELOG.md +41 -15
- package/README.md +1 -1
- package/coverage/coverage-final.json +8 -4
- package/coverage/index.html +24 -9
- package/coverage/lcov-report/index.html +24 -9
- package/coverage/lcov-report/src/EventBus.ts.html +8 -8
- package/coverage/lcov-report/src/TaskGraphValidator.ts.html +143 -62
- package/coverage/lcov-report/src/TaskRunner.ts.html +213 -21
- package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +313 -0
- package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +124 -0
- package/coverage/lcov-report/src/TaskStateManager.ts.html +511 -0
- package/coverage/lcov-report/src/WorkflowExecutor.ts.html +125 -137
- package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/lcov-report/src/contracts/index.html +1 -1
- package/coverage/lcov-report/src/index.html +59 -14
- package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +190 -0
- package/coverage/lcov-report/src/strategies/index.html +116 -0
- package/coverage/lcov.info +383 -184
- package/coverage/src/EventBus.ts.html +8 -8
- package/coverage/src/TaskGraphValidator.ts.html +143 -62
- package/coverage/src/TaskRunner.ts.html +213 -21
- package/coverage/src/TaskRunnerBuilder.ts.html +313 -0
- package/coverage/src/TaskRunnerExecutionConfig.ts.html +124 -0
- package/coverage/src/TaskStateManager.ts.html +511 -0
- package/coverage/src/WorkflowExecutor.ts.html +125 -137
- package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/src/contracts/index.html +1 -1
- package/coverage/src/index.html +59 -14
- package/coverage/src/strategies/StandardExecutionStrategy.ts.html +190 -0
- package/coverage/src/strategies/index.html +116 -0
- package/dist/TaskGraphValidator.js +39 -16
- package/dist/TaskGraphValidator.js.map +1 -1
- package/dist/TaskRunner.d.ts +12 -2
- package/dist/TaskRunner.js +55 -3
- package/dist/TaskRunner.js.map +1 -1
- package/dist/TaskRunnerBuilder.d.ts +33 -0
- package/dist/TaskRunnerBuilder.js +54 -0
- package/dist/TaskRunnerBuilder.js.map +1 -0
- package/dist/TaskRunnerExecutionConfig.d.ts +13 -0
- package/dist/TaskRunnerExecutionConfig.js +2 -0
- package/dist/TaskRunnerExecutionConfig.js.map +1 -0
- package/dist/TaskStateManager.d.ts +54 -0
- package/dist/TaskStateManager.js +130 -0
- package/dist/TaskStateManager.js.map +1 -0
- package/dist/TaskStatus.d.ts +1 -1
- package/dist/TaskStep.d.ts +2 -1
- package/dist/WorkflowExecutor.d.ts +11 -10
- package/dist/WorkflowExecutor.js +64 -73
- package/dist/WorkflowExecutor.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/strategies/IExecutionStrategy.d.ts +16 -0
- package/dist/strategies/IExecutionStrategy.js +2 -0
- package/dist/strategies/IExecutionStrategy.js.map +1 -0
- package/dist/strategies/StandardExecutionStrategy.d.ts +9 -0
- package/dist/strategies/StandardExecutionStrategy.js +25 -0
- package/dist/strategies/StandardExecutionStrategy.js.map +1 -0
- package/openspec/changes/add-concurrency-control/proposal.md +14 -0
- package/openspec/changes/add-concurrency-control/tasks.md +9 -0
- package/openspec/changes/add-task-retry-policy/proposal.md +13 -0
- package/openspec/changes/add-task-retry-policy/tasks.md +10 -0
- package/openspec/changes/add-workflow-preview/proposal.md +12 -0
- package/openspec/changes/add-workflow-preview/tasks.md +7 -0
- package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/tasks.md +10 -0
- package/openspec/changes/archive/2026-01-18-add-integration-tests/proposal.md +18 -0
- package/openspec/changes/archive/2026-01-18-add-integration-tests/tasks.md +17 -0
- package/openspec/changes/archive/2026-01-18-refactor-core-architecture/proposal.md +14 -0
- package/openspec/changes/archive/2026-01-18-refactor-core-architecture/tasks.md +8 -0
- package/package.json +1 -1
- package/src/TaskGraphValidator.ts +46 -19
- package/src/TaskRunner.ts +68 -4
- package/src/TaskRunnerBuilder.ts +76 -0
- package/src/TaskRunnerExecutionConfig.ts +13 -0
- package/src/TaskStateManager.ts +142 -0
- package/src/TaskStatus.ts +1 -1
- package/src/TaskStep.ts +2 -1
- package/src/WorkflowExecutor.ts +75 -79
- package/src/index.ts +4 -0
- package/src/strategies/IExecutionStrategy.ts +21 -0
- package/src/strategies/StandardExecutionStrategy.ts +35 -0
- package/test-report.xml +121 -43
- package/GEMINI.md +0 -46
- package/openspec/changes/add-external-task-cancellation/tasks.md +0 -10
- /package/openspec/changes/{add-external-task-cancellation → archive/2026-01-18-add-external-task-cancellation}/proposal.md +0 -0
package/src/WorkflowExecutor.ts
CHANGED
|
@@ -1,124 +1,120 @@
|
|
|
1
1
|
import { TaskStep } from "./TaskStep.js";
|
|
2
2
|
import { TaskResult } from "./TaskResult.js";
|
|
3
3
|
import { EventBus } from "./EventBus.js";
|
|
4
|
+
import { TaskStateManager } from "./TaskStateManager.js";
|
|
5
|
+
import { IExecutionStrategy } from "./strategies/IExecutionStrategy.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Handles the execution of the workflow steps.
|
|
7
9
|
* @template TContext The shape of the shared context object.
|
|
8
10
|
*/
|
|
9
11
|
export class WorkflowExecutor<TContext> {
|
|
10
|
-
private running = new Set<string>();
|
|
11
|
-
|
|
12
12
|
/**
|
|
13
13
|
* @param context The shared context object.
|
|
14
14
|
* @param eventBus The event bus to emit events.
|
|
15
|
+
* @param stateManager Manages execution state.
|
|
16
|
+
* @param strategy Execution strategy.
|
|
15
17
|
*/
|
|
16
18
|
constructor(
|
|
17
19
|
private context: TContext,
|
|
18
|
-
private eventBus: EventBus<TContext
|
|
20
|
+
private eventBus: EventBus<TContext>,
|
|
21
|
+
private stateManager: TaskStateManager<TContext>,
|
|
22
|
+
private strategy: IExecutionStrategy<TContext>
|
|
19
23
|
) {}
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Executes the given steps.
|
|
23
27
|
* @param steps The list of steps to execute.
|
|
28
|
+
* @param signal Optional AbortSignal for cancellation.
|
|
24
29
|
* @returns A Promise that resolves to a map of task results.
|
|
25
30
|
*/
|
|
26
|
-
async execute(
|
|
31
|
+
async execute(
|
|
32
|
+
steps: TaskStep<TContext>[],
|
|
33
|
+
signal?: AbortSignal
|
|
34
|
+
): Promise<Map<string, TaskResult>> {
|
|
27
35
|
this.eventBus.emit("workflowStart", { context: this.context, steps });
|
|
36
|
+
this.stateManager.initialize(steps);
|
|
37
|
+
|
|
38
|
+
// Check if already aborted
|
|
39
|
+
if (signal?.aborted) {
|
|
40
|
+
this.stateManager.cancelAllPending("Workflow cancelled before execution started.");
|
|
41
|
+
const results = this.stateManager.getResults();
|
|
42
|
+
this.eventBus.emit("workflowEnd", { context: this.context, results });
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
28
45
|
|
|
29
|
-
const results = new Map<string, TaskResult>();
|
|
30
46
|
const executingPromises = new Set<Promise<void>>();
|
|
31
|
-
const pendingSteps = new Set(steps);
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
const onAbort = () => {
|
|
49
|
+
// Mark all pending tasks as cancelled
|
|
50
|
+
this.stateManager.cancelAllPending("Workflow cancelled.");
|
|
51
|
+
};
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
await Promise.race(executingPromises);
|
|
39
|
-
// After a task finishes, check for new work
|
|
40
|
-
this.processQueue(pendingSteps, results, executingPromises);
|
|
53
|
+
if (signal) {
|
|
54
|
+
signal.addEventListener("abort", onAbort);
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Logic to identify tasks that can be started or must be skipped.
|
|
49
|
-
* Iterate only over pending steps to avoid O(N^2) checks on completed tasks.
|
|
50
|
-
*/
|
|
51
|
-
private processQueue(
|
|
52
|
-
pendingSteps: Set<TaskStep<TContext>>,
|
|
53
|
-
results: Map<string, TaskResult>,
|
|
54
|
-
executingPromises: Set<Promise<void>>
|
|
55
|
-
): void {
|
|
56
|
-
const toRemove: TaskStep<TContext>[] = [];
|
|
57
|
-
const toRun: TaskStep<TContext>[] = [];
|
|
58
|
-
|
|
59
|
-
for (const step of pendingSteps) {
|
|
60
|
-
const deps = step.dependencies ?? [];
|
|
61
|
-
let blocked = false;
|
|
62
|
-
let failedDep: string | undefined;
|
|
57
|
+
try {
|
|
58
|
+
// Initial pass
|
|
59
|
+
this.processLoop(executingPromises, signal);
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
while (
|
|
62
|
+
this.stateManager.hasPendingTasks() ||
|
|
63
|
+
this.stateManager.hasRunningTasks()
|
|
64
|
+
) {
|
|
65
|
+
// Safety check: if no tasks are running and we still have pending tasks,
|
|
66
|
+
// it means we are stuck (e.g. cycle or unhandled dependency).
|
|
67
|
+
// Since valid graphs shouldn't have this, we break to avoid infinite loop.
|
|
68
|
+
if (executingPromises.size === 0) {
|
|
71
69
|
break;
|
|
70
|
+
} else {
|
|
71
|
+
// Wait for the next task to finish
|
|
72
|
+
await Promise.race(executingPromises);
|
|
72
73
|
}
|
|
73
|
-
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.eventBus.emit("taskSkipped", { step, result });
|
|
82
|
-
toRemove.push(step);
|
|
83
|
-
} else if (!blocked) {
|
|
84
|
-
toRun.push(step);
|
|
85
|
-
toRemove.push(step);
|
|
75
|
+
if (signal?.aborted) {
|
|
76
|
+
this.stateManager.cancelAllPending("Workflow cancelled.");
|
|
77
|
+
} else {
|
|
78
|
+
// After a task finishes, check for new work
|
|
79
|
+
this.processLoop(executingPromises, signal);
|
|
80
|
+
}
|
|
86
81
|
}
|
|
87
|
-
}
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
pendingSteps.delete(step);
|
|
92
|
-
}
|
|
83
|
+
// Ensure everything is accounted for (e.g. if loop exited early)
|
|
84
|
+
this.stateManager.cancelAllPending("Workflow cancelled.");
|
|
93
85
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
const results = this.stateManager.getResults();
|
|
87
|
+
this.eventBus.emit("workflowEnd", { context: this.context, results });
|
|
88
|
+
return results;
|
|
89
|
+
} finally {
|
|
90
|
+
if (signal) {
|
|
91
|
+
signal.removeEventListener("abort", onAbort);
|
|
92
|
+
}
|
|
100
93
|
}
|
|
101
94
|
}
|
|
102
95
|
|
|
103
96
|
/**
|
|
104
|
-
*
|
|
97
|
+
* Logic to identify tasks that can be started and run them.
|
|
105
98
|
*/
|
|
106
|
-
private
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
private processLoop(
|
|
100
|
+
executingPromises: Set<Promise<void>>,
|
|
101
|
+
signal?: AbortSignal
|
|
102
|
+
): void {
|
|
103
|
+
const toRun = this.stateManager.processDependencies();
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
105
|
+
// Execute ready tasks
|
|
106
|
+
for (const step of toRun) {
|
|
107
|
+
this.stateManager.markRunning(step);
|
|
108
|
+
|
|
109
|
+
const taskPromise = this.strategy.execute(step, this.context, signal)
|
|
110
|
+
.then((result) => {
|
|
111
|
+
this.stateManager.markCompleted(step, result);
|
|
112
|
+
})
|
|
113
|
+
.finally(() => {
|
|
114
|
+
executingPromises.delete(taskPromise);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
executingPromises.add(taskPromise);
|
|
122
118
|
}
|
|
123
119
|
}
|
|
124
120
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export { TaskRunner } from "./TaskRunner.js";
|
|
2
|
+
export { TaskRunnerBuilder } from "./TaskRunnerBuilder.js";
|
|
3
|
+
export { TaskStateManager } from "./TaskStateManager.js";
|
|
4
|
+
export { StandardExecutionStrategy } from "./strategies/StandardExecutionStrategy.js";
|
|
5
|
+
export type { IExecutionStrategy } from "./strategies/IExecutionStrategy.js";
|
|
2
6
|
export type { TaskStep } from "./TaskStep.js";
|
|
3
7
|
export type { TaskResult } from "./TaskResult.js";
|
|
4
8
|
export type { TaskStatus } from "./TaskStatus.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TaskStep } from "../TaskStep.js";
|
|
2
|
+
import { TaskResult } from "../TaskResult.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Interface for task execution strategies.
|
|
6
|
+
* Allows different execution behaviors (e.g., standard, dry-run, debugging).
|
|
7
|
+
*/
|
|
8
|
+
export interface IExecutionStrategy<TContext> {
|
|
9
|
+
/**
|
|
10
|
+
* Executes a single task step.
|
|
11
|
+
* @param step The task step to execute.
|
|
12
|
+
* @param context The shared context.
|
|
13
|
+
* @param signal Optional abort signal.
|
|
14
|
+
* @returns A promise resolving to the task result.
|
|
15
|
+
*/
|
|
16
|
+
execute(
|
|
17
|
+
step: TaskStep<TContext>,
|
|
18
|
+
context: TContext,
|
|
19
|
+
signal?: AbortSignal
|
|
20
|
+
): Promise<TaskResult>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IExecutionStrategy } from "./IExecutionStrategy.js";
|
|
2
|
+
import { TaskStep } from "../TaskStep.js";
|
|
3
|
+
import { TaskResult } from "../TaskResult.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Standard execution strategy that runs the task's run method.
|
|
7
|
+
*/
|
|
8
|
+
export class StandardExecutionStrategy<TContext>
|
|
9
|
+
implements IExecutionStrategy<TContext>
|
|
10
|
+
{
|
|
11
|
+
async execute(
|
|
12
|
+
step: TaskStep<TContext>,
|
|
13
|
+
context: TContext,
|
|
14
|
+
signal?: AbortSignal
|
|
15
|
+
): Promise<TaskResult> {
|
|
16
|
+
try {
|
|
17
|
+
return await step.run(context, signal);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// Check if error is due to abort
|
|
20
|
+
if (
|
|
21
|
+
signal?.aborted &&
|
|
22
|
+
(e instanceof Error && e.name === "AbortError" || signal.reason === e)
|
|
23
|
+
) {
|
|
24
|
+
return {
|
|
25
|
+
status: "cancelled",
|
|
26
|
+
message: "Task cancelled during execution",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
status: "failure",
|
|
31
|
+
error: e instanceof Error ? e.message : String(e),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/test-report.xml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
-
<testsuites name="vitest tests" tests="
|
|
3
|
-
<testsuite name="tests/ComplexScenario.test.ts" timestamp="2026-01-
|
|
4
|
-
<testcase classname="tests/ComplexScenario.test.ts" name="Complex Scenario Integration Tests > 1. All steps execute when all succeed and context is hydrated" time="0.
|
|
2
|
+
<testsuites name="vitest tests" tests="66" failures="0" errors="0" time="0.734267979">
|
|
3
|
+
<testsuite name="tests/ComplexScenario.test.ts" timestamp="2026-01-18T16:53:50.036Z" hostname="runnervmmtnos" tests="2" failures="0" errors="0" skipped="0" time="0.012401489">
|
|
4
|
+
<testcase classname="tests/ComplexScenario.test.ts" name="Complex Scenario Integration Tests > 1. All steps execute when all succeed and context is hydrated" time="0.007855447">
|
|
5
5
|
<system-out>
|
|
6
6
|
Running StepA
|
|
7
7
|
Running StepB
|
|
@@ -15,85 +15,163 @@ Running StepG
|
|
|
15
15
|
|
|
16
16
|
</system-out>
|
|
17
17
|
</testcase>
|
|
18
|
-
<testcase classname="tests/ComplexScenario.test.ts" name="Complex Scenario Integration Tests > 2. The 'skip' propagation works as intended if something breaks up in the tree" time="0.
|
|
18
|
+
<testcase classname="tests/ComplexScenario.test.ts" name="Complex Scenario Integration Tests > 2. The 'skip' propagation works as intended if something breaks up in the tree" time="0.002288234">
|
|
19
19
|
</testcase>
|
|
20
20
|
</testsuite>
|
|
21
|
-
<testsuite name="tests/EventBus.test.ts" timestamp="2026-01-
|
|
22
|
-
<testcase classname="tests/EventBus.test.ts" name="EventBus > should handle async listeners throwing errors without crashing" time="0.
|
|
21
|
+
<testsuite name="tests/EventBus.test.ts" timestamp="2026-01-18T16:53:50.037Z" hostname="runnervmmtnos" tests="1" failures="0" errors="0" skipped="0" time="0.021934861">
|
|
22
|
+
<testcase classname="tests/EventBus.test.ts" name="EventBus > should handle async listeners throwing errors without crashing" time="0.019540929">
|
|
23
23
|
</testcase>
|
|
24
24
|
</testsuite>
|
|
25
|
-
<testsuite name="tests/TaskGraphValidator.test.ts" timestamp="2026-01-
|
|
26
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should be instantiated" time="0.
|
|
25
|
+
<testsuite name="tests/TaskGraphValidator.test.ts" timestamp="2026-01-18T16:53:50.038Z" hostname="runnervmmtnos" tests="9" failures="0" errors="0" skipped="0" time="0.009766435">
|
|
26
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should be instantiated" time="0.002042233">
|
|
27
27
|
</testcase>
|
|
28
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should return valid result for empty graph" time="0.
|
|
28
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should return valid result for empty graph" time="0.001433754">
|
|
29
29
|
</testcase>
|
|
30
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect duplicate tasks" time="0.
|
|
30
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect duplicate tasks" time="0.001395153">
|
|
31
31
|
</testcase>
|
|
32
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect missing dependencies" time="0.
|
|
32
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect missing dependencies" time="0.000435706">
|
|
33
33
|
</testcase>
|
|
34
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect cycles" time="0.
|
|
34
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect cycles" time="0.000776113">
|
|
35
35
|
</testcase>
|
|
36
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should return valid for a correct graph" time="0.
|
|
36
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should return valid for a correct graph" time="0.000369241">
|
|
37
37
|
</testcase>
|
|
38
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should not detect cycles if missing dependencies are present" time="0.
|
|
38
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should not detect cycles if missing dependencies are present" time="0.000350526">
|
|
39
39
|
</testcase>
|
|
40
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect more complex cycles" time="0.
|
|
40
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should detect more complex cycles" time="0.000261449">
|
|
41
41
|
</testcase>
|
|
42
|
-
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should handle cycle detection with no cycles but shared dependencies" time="0.
|
|
42
|
+
<testcase classname="tests/TaskGraphValidator.test.ts" name="TaskGraphValidator > should handle cycle detection with no cycles but shared dependencies" time="0.000339385">
|
|
43
43
|
</testcase>
|
|
44
44
|
</testsuite>
|
|
45
|
-
<testsuite name="tests/
|
|
46
|
-
<testcase classname="tests/
|
|
45
|
+
<testsuite name="tests/TaskGraphValidatorDoS.test.ts" timestamp="2026-01-18T16:53:50.040Z" hostname="runnervmmtnos" tests="1" failures="0" errors="0" skipped="0" time="0.037493531">
|
|
46
|
+
<testcase classname="tests/TaskGraphValidatorDoS.test.ts" name="TaskGraphValidator - Deep Recursion > should handle deep graphs without stack overflow" time="0.035728917">
|
|
47
47
|
</testcase>
|
|
48
|
-
|
|
48
|
+
</testsuite>
|
|
49
|
+
<testsuite name="tests/TaskRunner.test.ts" timestamp="2026-01-18T16:53:50.040Z" hostname="runnervmmtnos" tests="10" failures="0" errors="0" skipped="0" time="0.111490101">
|
|
50
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should run tasks in the correct sequential order" time="0.003037407">
|
|
51
|
+
</testcase>
|
|
52
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should handle an empty list of tasks gracefully" time="0.000322775">
|
|
53
|
+
</testcase>
|
|
54
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should run independent tasks in parallel" time="0.100688769">
|
|
55
|
+
</testcase>
|
|
56
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should skip dependent tasks if a root task fails" time="0.000418764">
|
|
57
|
+
</testcase>
|
|
58
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should throw an error for 'circular dependency'" time="0.00237658">
|
|
49
59
|
</testcase>
|
|
50
|
-
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should
|
|
60
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should throw an error for 'missing dependency'" time="0.00042754">
|
|
51
61
|
</testcase>
|
|
52
|
-
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should
|
|
62
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should handle tasks that throw an error during execution" time="0.000402213">
|
|
63
|
+
</testcase>
|
|
64
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should skip tasks whose dependencies are skipped" time="0.000513091">
|
|
65
|
+
</testcase>
|
|
66
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should handle tasks that throw a non-Error object during execution" time="0.000398907">
|
|
67
|
+
</testcase>
|
|
68
|
+
<testcase classname="tests/TaskRunner.test.ts" name="TaskRunner > should handle duplicate steps where one gets skipped due to failed dependency" time="0.00050197">
|
|
69
|
+
</testcase>
|
|
70
|
+
</testsuite>
|
|
71
|
+
<testsuite name="tests/TaskRunnerEvents.test.ts" timestamp="2026-01-18T16:53:50.042Z" hostname="runnervmmtnos" tests="7" failures="0" errors="0" skipped="0" time="0.01911567">
|
|
72
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should fire all lifecycle events in a successful run" time="0.00764296">
|
|
53
73
|
</testcase>
|
|
54
|
-
<testcase classname="tests/
|
|
74
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should fire taskSkipped event when dependency fails" time="0.005221716">
|
|
55
75
|
</testcase>
|
|
56
|
-
<testcase classname="tests/
|
|
76
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should not crash if a listener throws an error" time="0.001525195">
|
|
57
77
|
</testcase>
|
|
58
|
-
<testcase classname="tests/
|
|
78
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should fire workflow events even for empty step list" time="0.00055575">
|
|
59
79
|
</testcase>
|
|
60
|
-
<testcase classname="tests/
|
|
80
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should handle unsubscribe correctly" time="0.00070472">
|
|
61
81
|
</testcase>
|
|
62
|
-
<testcase classname="tests/
|
|
82
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should safely handle off() when no listeners exist" time="0.00037408">
|
|
63
83
|
</testcase>
|
|
64
|
-
<testcase classname="tests/
|
|
84
|
+
<testcase classname="tests/TaskRunnerEvents.test.ts" name="TaskRunner Events > should support multiple listeners for the same event" time="0.000612918">
|
|
65
85
|
</testcase>
|
|
66
86
|
</testsuite>
|
|
67
|
-
<testsuite name="tests/
|
|
68
|
-
<testcase classname="tests/
|
|
87
|
+
<testsuite name="tests/TaskRunnerRefactor.test.ts" timestamp="2026-01-18T16:53:50.043Z" hostname="runnervmmtnos" tests="6" failures="0" errors="0" skipped="0" time="0.009369642">
|
|
88
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > should allow setting execution strategy" time="0.002330794">
|
|
69
89
|
</testcase>
|
|
70
|
-
<testcase classname="tests/
|
|
90
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > should allow unsubscribing from events" time="0.001040117">
|
|
71
91
|
</testcase>
|
|
72
|
-
<testcase classname="tests/
|
|
92
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > TaskStateManager.cancelAllPending should handle non-existing tasks gracefully" time="0.000549209">
|
|
73
93
|
</testcase>
|
|
74
|
-
<testcase classname="tests/
|
|
94
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > TaskRunnerBuilder should build a TaskRunner" time="0.000695793">
|
|
75
95
|
</testcase>
|
|
76
|
-
<testcase classname="tests/
|
|
96
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > TaskRunnerBuilder should build a TaskRunner with no listeners" time="0.000276137">
|
|
77
97
|
</testcase>
|
|
78
|
-
<testcase classname="tests/
|
|
98
|
+
<testcase classname="tests/TaskRunnerRefactor.test.ts" name="TaskRunner Refactor Coverage > WorkflowExecutor should break infinite loop if no progress can be made" time="0.002033066">
|
|
79
99
|
</testcase>
|
|
80
|
-
|
|
100
|
+
</testsuite>
|
|
101
|
+
<testsuite name="tests/WorkflowExecutor.test.ts" timestamp="2026-01-18T16:53:50.044Z" hostname="runnervmmtnos" tests="3" failures="0" errors="0" skipped="0" time="0.056872202">
|
|
102
|
+
<testcase classname="tests/WorkflowExecutor.test.ts" name="WorkflowExecutor > should execute steps sequentially when dependencies exist" time="0.003496687">
|
|
103
|
+
</testcase>
|
|
104
|
+
<testcase classname="tests/WorkflowExecutor.test.ts" name="WorkflowExecutor > should skip dependent steps if dependency fails" time="0.000477163">
|
|
105
|
+
</testcase>
|
|
106
|
+
<testcase classname="tests/WorkflowExecutor.test.ts" name="WorkflowExecutor > should run independent steps in parallel" time="0.051018852">
|
|
81
107
|
</testcase>
|
|
82
108
|
</testsuite>
|
|
83
|
-
<testsuite name="tests/
|
|
84
|
-
<testcase classname="tests/
|
|
109
|
+
<testsuite name="tests/cancellation.test.ts" timestamp="2026-01-18T16:53:50.045Z" hostname="runnervmmtnos" tests="10" failures="0" errors="0" skipped="0" time="0.160536321">
|
|
110
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should execute tasks normally without cancellation" time="0.003288668">
|
|
111
|
+
</testcase>
|
|
112
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should cancel workflow via AbortSignal" time="0.011297793">
|
|
113
|
+
</testcase>
|
|
114
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should cancel workflow via global timeout" time="0.04959712">
|
|
115
|
+
</testcase>
|
|
116
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should handle pre-aborted signal" time="0.00046518">
|
|
117
|
+
</testcase>
|
|
118
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should propagate cancellation to running task" time="0.010460564">
|
|
119
|
+
</testcase>
|
|
120
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should respect timeout over signal if timeout happens first" time="0.050041281">
|
|
121
|
+
</testcase>
|
|
122
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should handle case where timeout is set AND signal is already aborted" time="0.000498924">
|
|
123
|
+
</testcase>
|
|
124
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should cancel workflow via AbortSignal when timeout is also set" time="0.010598674">
|
|
85
125
|
</testcase>
|
|
86
|
-
<testcase classname="tests/
|
|
126
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should fail task if it throws non-abort error during cancellation" time="0.01120543">
|
|
87
127
|
</testcase>
|
|
88
|
-
<testcase classname="tests/
|
|
128
|
+
<testcase classname="tests/cancellation.test.ts" name="TaskRunner Cancellation > should cancel task if it rejects with signal.reason" time="0.010730942">
|
|
89
129
|
</testcase>
|
|
90
130
|
</testsuite>
|
|
91
|
-
<testsuite name="tests/integration.test.ts" timestamp="2026-01-
|
|
92
|
-
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for duplicate tasks" time="0.
|
|
131
|
+
<testsuite name="tests/integration.test.ts" timestamp="2026-01-18T16:53:50.047Z" hostname="runnervmmtnos" tests="3" failures="0" errors="0" skipped="0" time="0.005559619">
|
|
132
|
+
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for duplicate tasks" time="0.003324235">
|
|
93
133
|
</testcase>
|
|
94
|
-
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for missing dependencies" time="0.
|
|
134
|
+
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for missing dependencies" time="0.000340878">
|
|
95
135
|
</testcase>
|
|
96
|
-
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for cycles" time="0.
|
|
136
|
+
<testcase classname="tests/integration.test.ts" name="TaskRunner Validation Integration > should throw validation error with clear message for cycles" time="0.000324869">
|
|
137
|
+
</testcase>
|
|
138
|
+
</testsuite>
|
|
139
|
+
<testsuite name="tests/integration-tests/basic-structure.test.ts" timestamp="2026-01-18T16:53:50.047Z" hostname="runnervmmtnos" tests="4" failures="0" errors="0" skipped="0" time="0.13078604">
|
|
140
|
+
<testcase classname="tests/integration-tests/basic-structure.test.ts" name="Integration: Basic Structure > Scenario 1: Basic linear workflow (A -> B -> C)" time="0.034031066">
|
|
141
|
+
</testcase>
|
|
142
|
+
<testcase classname="tests/integration-tests/basic-structure.test.ts" name="Integration: Basic Structure > Scenario 2: Branching workflow (A -> [B, C] -> D)" time="0.032156516">
|
|
143
|
+
</testcase>
|
|
144
|
+
<testcase classname="tests/integration-tests/basic-structure.test.ts" name="Integration: Basic Structure > Scenario 12: Complex 'Diamond' dependency graph (A -> B -> D, A -> C -> D)" time="0.041686058">
|
|
145
|
+
</testcase>
|
|
146
|
+
<testcase classname="tests/integration-tests/basic-structure.test.ts" name="Integration: Basic Structure > Scenario 14: Zero-dependency parallel burst" time="0.020922221">
|
|
147
|
+
</testcase>
|
|
148
|
+
</testsuite>
|
|
149
|
+
<testsuite name="tests/integration-tests/concurrency-timing.test.ts" timestamp="2026-01-18T16:53:50.048Z" hostname="runnervmmtnos" tests="3" failures="0" errors="0" skipped="0" time="0.126419406">
|
|
150
|
+
<testcase classname="tests/integration-tests/concurrency-timing.test.ts" name="Integration: Concurrency and Timing > Scenario 6: Mixed duration tasks (verifying parallel efficiency)" time="0.053041689">
|
|
151
|
+
</testcase>
|
|
152
|
+
<testcase classname="tests/integration-tests/concurrency-timing.test.ts" name="Integration: Concurrency and Timing > Scenario 7: Cancellation via AbortSignal" time="0.021208639">
|
|
153
|
+
</testcase>
|
|
154
|
+
<testcase classname="tests/integration-tests/concurrency-timing.test.ts" name="Integration: Concurrency and Timing > Scenario 8: Global timeout interrupting long tasks" time="0.050030744">
|
|
155
|
+
</testcase>
|
|
156
|
+
</testsuite>
|
|
157
|
+
<testsuite name="tests/integration-tests/context-state.test.ts" timestamp="2026-01-18T16:53:50.049Z" hostname="runnervmmtnos" tests="3" failures="0" errors="0" skipped="0" time="0.019135968">
|
|
158
|
+
<testcase classname="tests/integration-tests/context-state.test.ts" name="Integration: Context and State > Scenario 4: Shared context mutation (A writes, B reads)" time="0.005077797">
|
|
159
|
+
</testcase>
|
|
160
|
+
<testcase classname="tests/integration-tests/context-state.test.ts" name="Integration: Context and State > Scenario 9: Dynamic context validation" time="0.000954216">
|
|
161
|
+
</testcase>
|
|
162
|
+
<testcase classname="tests/integration-tests/context-state.test.ts" name="Integration: Context and State > Scenario 13: Tasks with side-effects" time="0.010823344">
|
|
163
|
+
</testcase>
|
|
164
|
+
</testsuite>
|
|
165
|
+
<testsuite name="tests/integration-tests/error-handling.test.ts" timestamp="2026-01-18T16:53:50.049Z" hostname="runnervmmtnos" tests="3" failures="0" errors="0" skipped="0" time="0.008227594">
|
|
166
|
+
<testcase classname="tests/integration-tests/error-handling.test.ts" name="Integration: Error Handling > Scenario 3: Task failure and downstream skipping" time="0.003512046">
|
|
167
|
+
</testcase>
|
|
168
|
+
<testcase classname="tests/integration-tests/error-handling.test.ts" name="Integration: Error Handling > Scenario 10: Circular dependency detection" time="0.002290789">
|
|
169
|
+
</testcase>
|
|
170
|
+
<testcase classname="tests/integration-tests/error-handling.test.ts" name="Integration: Error Handling > Scenario 11: Missing dependency handling" time="0.000406341">
|
|
171
|
+
</testcase>
|
|
172
|
+
</testsuite>
|
|
173
|
+
<testsuite name="tests/integration-tests/stress.test.ts" timestamp="2026-01-18T16:53:50.050Z" hostname="runnervmmtnos" tests="1" failures="0" errors="0" skipped="0" time="0.0051591">
|
|
174
|
+
<testcase classname="tests/integration-tests/stress.test.ts" name="Integration: Stress Tests > Scenario 5: Large graph execution (e.g., 20+ nodes)" time="0.003673567">
|
|
97
175
|
</testcase>
|
|
98
176
|
</testsuite>
|
|
99
177
|
</testsuites>
|
package/GEMINI.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# task-runner Development Guidelines
|
|
2
|
-
|
|
3
|
-
Auto-generated from all feature plans. Last updated: 2026-01-17
|
|
4
|
-
|
|
5
|
-
## Active Technologies
|
|
6
|
-
- TypeScript 5.9.3 + vitest 4.0.17 (for testing) (004-pre-execution-validation)
|
|
7
|
-
- TypeScript 5.9.3 + vitest 4.0.17, AbortSignal/AbortController (standard Web APIs) (002-task-cancellation)
|
|
8
|
-
- N/A (in-memory context object) (002-task-cancellation)
|
|
9
|
-
- N/A (in-memory queue for tasks) (005-concurrency-control)
|
|
10
|
-
|
|
11
|
-
- TypeScript 5.9.3 + vitest 4.0.17 (003-refactor-file-structure)
|
|
12
|
-
|
|
13
|
-
- (001-generic-task-runner)
|
|
14
|
-
|
|
15
|
-
## Project Structure
|
|
16
|
-
|
|
17
|
-
```text
|
|
18
|
-
src/
|
|
19
|
-
tests/
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Commands
|
|
23
|
-
|
|
24
|
-
# Add commands for
|
|
25
|
-
|
|
26
|
-
## Code Style
|
|
27
|
-
|
|
28
|
-
: Follow standard conventions
|
|
29
|
-
|
|
30
|
-
## Recent Changes
|
|
31
|
-
- 005-concurrency-control: Added TypeScript 5.9.3 + vitest 4.0.17
|
|
32
|
-
- 002-task-cancellation: Added TypeScript 5.9.3 + vitest 4.0.17, AbortSignal/AbortController (standard Web APIs)
|
|
33
|
-
- 004-pre-execution-validation: Added TypeScript 5.9.3 + vitest 4.0.17 (for testing)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<!-- MANUAL ADDITIONS START -->
|
|
38
|
-
Before marking a task as concluded, YOU MUST:
|
|
39
|
-
|
|
40
|
-
1. run pnpm install
|
|
41
|
-
2. run pnpm build
|
|
42
|
-
3. run pnpm test
|
|
43
|
-
4. run pnpm lint
|
|
44
|
-
|
|
45
|
-
If any of those command fail, review your changes.
|
|
46
|
-
<!-- MANUAL ADDITIONS END -->
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
## Implementation
|
|
2
|
-
- [ ] 1.1 Update `TaskRunner.execute` signature to accept an optional config object.
|
|
3
|
-
- [ ] 1.2 Implement logic within `TaskRunner` to listen for `AbortSignal` and initiate cancellation.
|
|
4
|
-
- [ ] 1.3 Implement global timeout mechanism using `AbortController` and `setTimeout`.
|
|
5
|
-
- [ ] 1.4 Propagate `AbortSignal` to individual `TaskStep` executions.
|
|
6
|
-
- [ ] 1.5 Ensure `TaskRunner` correctly handles tasks that are already aborted before execution.
|
|
7
|
-
- [ ] 1.6 Update `TaskResult` and `RunnerEvents` to reflect cancellation status.
|
|
8
|
-
- [ ] 1.7 Add unit tests for `AbortSignal` cancellation scenarios.
|
|
9
|
-
- [ ] 1.8 Add unit tests for global timeout scenarios.
|
|
10
|
-
- [ ] 1.9 Add integration tests covering both `AbortSignal` and timeout interactions.
|