@calmo/task-runner 3.3.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.github/dependabot.yml +7 -7
  2. package/.github/workflows/ci.yml +4 -4
  3. package/.jules/backlog_maniac.md +4 -0
  4. package/.jules/nexus.md +1 -0
  5. package/.jules/sentinel.md +1 -0
  6. package/.releaserc.json +2 -7
  7. package/AGENTS.md +8 -2
  8. package/CHANGELOG.md +181 -167
  9. package/README.md +23 -23
  10. package/coverage/coverage-final.json +8 -7
  11. package/coverage/index.html +7 -7
  12. package/coverage/lcov-report/index.html +7 -7
  13. package/coverage/lcov-report/src/EventBus.ts.html +28 -22
  14. package/coverage/lcov-report/src/TaskGraphValidationError.ts.html +130 -0
  15. package/coverage/lcov-report/src/TaskGraphValidator.ts.html +166 -151
  16. package/coverage/lcov-report/src/TaskRunner.ts.html +69 -54
  17. package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +29 -5
  18. package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +1 -1
  19. package/coverage/lcov-report/src/TaskStateManager.ts.html +1 -1
  20. package/coverage/lcov-report/src/WorkflowExecutor.ts.html +21 -12
  21. package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
  22. package/coverage/lcov-report/src/contracts/index.html +1 -1
  23. package/coverage/lcov-report/src/index.html +23 -8
  24. package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +4 -4
  25. package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +30 -12
  26. package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +5 -5
  27. package/coverage/lcov-report/src/strategies/index.html +1 -1
  28. package/coverage/lcov.info +296 -278
  29. package/coverage/src/EventBus.ts.html +28 -22
  30. package/coverage/src/TaskGraphValidationError.ts.html +130 -0
  31. package/coverage/src/TaskGraphValidator.ts.html +166 -151
  32. package/coverage/src/TaskRunner.ts.html +69 -54
  33. package/coverage/src/TaskRunnerBuilder.ts.html +29 -5
  34. package/coverage/src/TaskRunnerExecutionConfig.ts.html +1 -1
  35. package/coverage/src/TaskStateManager.ts.html +1 -1
  36. package/coverage/src/WorkflowExecutor.ts.html +21 -12
  37. package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
  38. package/coverage/src/contracts/index.html +1 -1
  39. package/coverage/src/index.html +23 -8
  40. package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +4 -4
  41. package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +30 -12
  42. package/coverage/src/strategies/StandardExecutionStrategy.ts.html +5 -5
  43. package/coverage/src/strategies/index.html +1 -1
  44. package/dist/EventBus.js +13 -11
  45. package/dist/EventBus.js.map +1 -1
  46. package/dist/TaskGraphValidationError.d.ts +9 -0
  47. package/dist/TaskGraphValidationError.js +13 -0
  48. package/dist/TaskGraphValidationError.js.map +1 -0
  49. package/dist/TaskGraphValidator.js +9 -9
  50. package/dist/TaskGraphValidator.js.map +1 -1
  51. package/dist/TaskRunner.js +2 -1
  52. package/dist/TaskRunner.js.map +1 -1
  53. package/dist/TaskRunnerBuilder.js.map +1 -1
  54. package/dist/WorkflowExecutor.js +2 -1
  55. package/dist/WorkflowExecutor.js.map +1 -1
  56. package/dist/index.d.ts +1 -0
  57. package/dist/index.js +1 -0
  58. package/dist/index.js.map +1 -1
  59. package/dist/strategies/RetryingExecutionStrategy.js +3 -1
  60. package/dist/strategies/RetryingExecutionStrategy.js.map +1 -1
  61. package/dist/strategies/StandardExecutionStrategy.js +1 -1
  62. package/dist/strategies/StandardExecutionStrategy.js.map +1 -1
  63. package/openspec/AGENTS.md +81 -15
  64. package/openspec/changes/{add-concurrency-control → archive/2026-01-18-add-concurrency-control}/proposal.md +7 -4
  65. package/openspec/changes/archive/2026-01-18-add-concurrency-control/tasks.md +10 -0
  66. package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/proposal.md +4 -1
  67. package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/tasks.md +2 -1
  68. package/openspec/changes/archive/2026-01-18-add-integration-tests/proposal.md +3 -0
  69. package/openspec/changes/archive/2026-01-18-add-integration-tests/tasks.md +1 -0
  70. package/openspec/changes/archive/2026-01-18-add-task-retry-policy/proposal.md +3 -0
  71. package/openspec/changes/archive/2026-01-18-add-task-retry-policy/tasks.md +1 -0
  72. package/openspec/changes/archive/2026-01-18-add-workflow-preview/proposal.md +3 -0
  73. package/openspec/changes/archive/2026-01-18-add-workflow-preview/tasks.md +1 -0
  74. package/openspec/changes/archive/2026-01-18-refactor-core-architecture/proposal.md +3 -0
  75. package/openspec/changes/archive/2026-01-18-refactor-core-architecture/tasks.md +1 -0
  76. package/openspec/changes/feat-per-task-timeout/proposal.md +11 -6
  77. package/openspec/changes/feat-per-task-timeout/tasks.md +1 -1
  78. package/openspec/project.md +21 -15
  79. package/package.json +1 -1
  80. package/src/EventBus.ts +18 -16
  81. package/src/TaskGraph.ts +8 -8
  82. package/src/TaskGraphValidationError.ts +15 -0
  83. package/src/TaskGraphValidator.ts +148 -143
  84. package/src/TaskRunner.ts +47 -42
  85. package/src/TaskRunnerBuilder.ts +11 -3
  86. package/src/WorkflowExecutor.ts +13 -10
  87. package/src/contracts/ITaskGraphValidator.ts +12 -12
  88. package/src/contracts/ValidationError.ts +6 -6
  89. package/src/contracts/ValidationResult.ts +4 -4
  90. package/src/index.ts +1 -0
  91. package/src/strategies/DryRunExecutionStrategy.ts +3 -3
  92. package/src/strategies/RetryingExecutionStrategy.ts +15 -9
  93. package/src/strategies/StandardExecutionStrategy.ts +4 -4
  94. package/test-report.xml +109 -107
  95. package/openspec/changes/add-concurrency-control/tasks.md +0 -9
@@ -260,7 +260,12 @@
260
260
  <a name='L195'></a><a href='#L195'>195</a>
261
261
  <a name='L196'></a><a href='#L196'>196</a>
262
262
  <a name='L197'></a><a href='#L197'>197</a>
263
- <a name='L198'></a><a href='#L198'>198</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
264
269
  <span class="cline-any cline-neutral">&nbsp;</span>
265
270
  <span class="cline-any cline-neutral">&nbsp;</span>
266
271
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -283,16 +288,19 @@
283
288
  <span class="cline-any cline-neutral">&nbsp;</span>
284
289
  <span class="cline-any cline-neutral">&nbsp;</span>
285
290
  <span class="cline-any cline-neutral">&nbsp;</span>
286
- <span class="cline-any cline-yes">56x</span>
287
- <span class="cline-any cline-yes">56x</span>
288
- <span class="cline-any cline-yes">56x</span>
289
291
  <span class="cline-any cline-neutral">&nbsp;</span>
290
292
  <span class="cline-any cline-neutral">&nbsp;</span>
291
293
  <span class="cline-any cline-neutral">&nbsp;</span>
292
294
  <span class="cline-any cline-neutral">&nbsp;</span>
295
+ <span class="cline-any cline-yes">57x</span>
296
+ <span class="cline-any cline-yes">57x</span>
293
297
  <span class="cline-any cline-neutral">&nbsp;</span>
298
+ <span class="cline-any cline-yes">57x</span>
294
299
  <span class="cline-any cline-neutral">&nbsp;</span>
295
- <span class="cline-any cline-yes">56x</span>
300
+ <span class="cline-any cline-neutral">&nbsp;</span>
301
+ <span class="cline-any cline-neutral">&nbsp;</span>
302
+ <span class="cline-any cline-neutral">&nbsp;</span>
303
+ <span class="cline-any cline-yes">57x</span>
296
304
  <span class="cline-any cline-neutral">&nbsp;</span>
297
305
  <span class="cline-any cline-neutral">&nbsp;</span>
298
306
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -347,7 +355,6 @@
347
355
  <span class="cline-any cline-yes">24x</span>
348
356
  <span class="cline-any cline-neutral">&nbsp;</span>
349
357
  <span class="cline-any cline-neutral">&nbsp;</span>
350
- <span class="cline-any cline-neutral">&nbsp;</span>
351
358
  <span class="cline-any cline-yes">4x</span>
352
359
  <span class="cline-any cline-neutral">&nbsp;</span>
353
360
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -364,8 +371,6 @@
364
371
  <span class="cline-any cline-neutral">&nbsp;</span>
365
372
  <span class="cline-any cline-neutral">&nbsp;</span>
366
373
  <span class="cline-any cline-neutral">&nbsp;</span>
367
- <span class="cline-any cline-neutral">&nbsp;</span>
368
- <span class="cline-any cline-neutral">&nbsp;</span>
369
374
  <span class="cline-any cline-yes">4x</span>
370
375
  <span class="cline-any cline-neutral">&nbsp;</span>
371
376
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -391,16 +396,19 @@
391
396
  <span class="cline-any cline-neutral">&nbsp;</span>
392
397
  <span class="cline-any cline-neutral">&nbsp;</span>
393
398
  <span class="cline-any cline-neutral">&nbsp;</span>
394
- <span class="cline-any cline-yes">51x</span>
395
- <span class="cline-any cline-yes">139x</span>
399
+ <span class="cline-any cline-yes">52x</span>
400
+ <span class="cline-any cline-yes">140x</span>
401
+ <span class="cline-any cline-neutral">&nbsp;</span>
402
+ <span class="cline-any cline-neutral">&nbsp;</span>
396
403
  <span class="cline-any cline-neutral">&nbsp;</span>
397
404
  <span class="cline-any cline-neutral">&nbsp;</span>
398
405
  <span class="cline-any cline-neutral">&nbsp;</span>
406
+ <span class="cline-any cline-yes">52x</span>
407
+ <span class="cline-any cline-yes">52x</span>
408
+ <span class="cline-any cline-yes">9x</span>
409
+ <span class="cline-any cline-neutral">&nbsp;</span>
399
410
  <span class="cline-any cline-neutral">&nbsp;</span>
400
411
  <span class="cline-any cline-neutral">&nbsp;</span>
401
- <span class="cline-any cline-yes">51x</span>
402
- <span class="cline-any cline-yes">51x</span>
403
- <span class="cline-any cline-yes">8x</span>
404
412
  <span class="cline-any cline-neutral">&nbsp;</span>
405
413
  <span class="cline-any cline-neutral">&nbsp;</span>
406
414
  <span class="cline-any cline-yes">43x</span>
@@ -419,12 +427,14 @@
419
427
  <span class="cline-any cline-neutral">&nbsp;</span>
420
428
  <span class="cline-any cline-neutral">&nbsp;</span>
421
429
  <span class="cline-any cline-neutral">&nbsp;</span>
422
- <span class="cline-any cline-yes">51x</span>
430
+ <span class="cline-any cline-yes">52x</span>
423
431
  <span class="cline-any cline-yes">5x</span>
424
432
  <span class="cline-any cline-yes">5x</span>
425
433
  <span class="cline-any cline-yes">3x</span>
426
434
  <span class="cline-any cline-neutral">&nbsp;</span>
427
435
  <span class="cline-any cline-neutral">&nbsp;</span>
436
+ <span class="cline-any cline-neutral">&nbsp;</span>
437
+ <span class="cline-any cline-neutral">&nbsp;</span>
428
438
  <span class="cline-any cline-yes">5x</span>
429
439
  <span class="cline-any cline-neutral">&nbsp;</span>
430
440
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -461,11 +471,15 @@
461
471
  import { TaskResult } from "./TaskResult.js";
462
472
  import { TaskGraphValidator } from "./TaskGraphValidator.js";
463
473
  import { TaskGraph } from "./TaskGraph.js";
464
- import { RunnerEventPayloads, RunnerEventListener } from "./contracts/RunnerEvents.js";
474
+ import {
475
+ RunnerEventPayloads,
476
+ RunnerEventListener,
477
+ } from "./contracts/RunnerEvents.js";
465
478
  import { EventBus } from "./EventBus.js";
466
479
  import { WorkflowExecutor } from "./WorkflowExecutor.js";
467
480
  import { TaskRunnerExecutionConfig } from "./TaskRunnerExecutionConfig.js";
468
481
  import { TaskStateManager } from "./TaskStateManager.js";
482
+ import { TaskGraphValidationError } from "./TaskGraphValidationError.js";
469
483
  import { IExecutionStrategy } from "./strategies/IExecutionStrategy.js";
470
484
  import { StandardExecutionStrategy } from "./strategies/StandardExecutionStrategy.js";
471
485
  import { RetryingExecutionStrategy } from "./strategies/RetryingExecutionStrategy.js";
@@ -482,9 +496,8 @@ export { RunnerEventPayloads, RunnerEventListener, TaskRunnerExecutionConfig };
482
496
  export class TaskRunner&lt;TContext&gt; {
483
497
  private eventBus = new EventBus&lt;TContext&gt;();
484
498
  private validator = new TaskGraphValidator();
485
- private executionStrategy: IExecutionStrategy&lt;TContext&gt; = new RetryingExecutionStrategy(
486
- new StandardExecutionStrategy()
487
- );
499
+ private executionStrategy: IExecutionStrategy&lt;TContext&gt; =
500
+ new RetryingExecutionStrategy(new StandardExecutionStrategy());
488
501
  &nbsp;
489
502
  /**
490
503
  * @param context The shared context object to be passed to each task.
@@ -542,7 +555,6 @@ export class TaskRunner&lt;TContext&gt; {
542
555
  // Actually, Mermaid ID cannot have spaces without quotes.
543
556
  const safeId = (name: string) =&gt; JSON.stringify(name);
544
557
  const sanitize = (name: string) =&gt; this.sanitizeMermaidId(name);
545
- &nbsp;
546
558
  &nbsp;
547
559
  // Add all nodes first to ensure they exist
548
560
  for (const step of steps) {
@@ -556,9 +568,7 @@ export class TaskRunner&lt;TContext&gt; {
556
568
  for (const step of steps) {
557
569
  if (step.dependencies) {
558
570
  for (const dep of step.dependencies) {
559
- graphLines.push(
560
- ` ${sanitize(dep)} --&gt; ${sanitize(step.name)}`
561
- );
571
+ graphLines.push(` ${sanitize(dep)} --&gt; ${sanitize(step.name)}`);
562
572
  }
563
573
  }
564
574
  }
@@ -597,7 +607,10 @@ export class TaskRunner&lt;TContext&gt; {
597
607
  &nbsp;
598
608
  const validationResult = this.validator.validate(taskGraph);
599
609
  if (!validationResult.isValid) {
600
- throw new Error(this.validator.createErrorMessage(validationResult));
610
+ throw new TaskGraphValidationError(
611
+ validationResult,
612
+ this.validator.createErrorMessage(validationResult)
613
+ );
601
614
  }
602
615
  &nbsp;
603
616
  const stateManager = new TaskStateManager(this.eventBus);
@@ -617,40 +630,42 @@ export class TaskRunner&lt;TContext&gt; {
617
630
  &nbsp;
618
631
  // We need to handle the timeout cleanup properly.
619
632
  if (config?.timeout !== undefined) {
620
- const controller = new AbortController();
621
- const timeoutId = setTimeout(() =&gt; {
622
- controller.abort(new Error(`Workflow timed out after ${config.timeout}ms`));
623
- }, config.timeout);
633
+ const controller = new AbortController();
634
+ const timeoutId = setTimeout(() =&gt; {
635
+ controller.abort(
636
+ new Error(`Workflow timed out after ${config.timeout}ms`)
637
+ );
638
+ }, config.timeout);
624
639
  &nbsp;
625
- let effectiveSignal = controller.signal;
626
- let onAbort: (() =&gt; void) | undefined;
640
+ let effectiveSignal = controller.signal;
641
+ let onAbort: (() =&gt; void) | undefined;
627
642
  &nbsp;
628
- // Handle combination of signals if user provided one
629
- if (config.signal) {
630
- if (config.signal.aborted) {
631
- // If already aborted, use it directly (WorkflowExecutor handles early abort)
632
- // We can cancel timeout immediately
633
- clearTimeout(timeoutId);
634
- effectiveSignal = config.signal;
635
- } else {
636
- // Listen to user signal to abort our controller
637
- onAbort = () =&gt; {
638
- controller.abort(config.signal?.reason);
639
- };
640
- config.signal.addEventListener("abort", onAbort);
641
- }
642
- }
643
+ // Handle combination of signals if user provided one
644
+ if (config.signal) {
645
+ if (config.signal.aborted) {
646
+ // If already aborted, use it directly (WorkflowExecutor handles early abort)
647
+ // We can cancel timeout immediately
648
+ clearTimeout(timeoutId);
649
+ effectiveSignal = config.signal;
650
+ } else {
651
+ // Listen to user signal to abort our controller
652
+ onAbort = () =&gt; {
653
+ controller.abort(config.signal?.reason);
654
+ };
655
+ config.signal.addEventListener("abort", onAbort);
656
+ }
657
+ }
643
658
  &nbsp;
644
- try {
645
- return await executor.execute(steps, effectiveSignal);
646
- } finally {
647
- clearTimeout(timeoutId);
648
- if (config.signal &amp;&amp; onAbort) {
649
- config.signal.removeEventListener("abort", onAbort);
650
- }
651
- }
659
+ try {
660
+ return await executor.execute(steps, effectiveSignal);
661
+ } finally {
662
+ clearTimeout(timeoutId);
663
+ if (config.signal &amp;&amp; onAbort) {
664
+ config.signal.removeEventListener("abort", onAbort);
665
+ }
666
+ }
652
667
  } else {
653
- return executor.execute(steps, config?.signal);
668
+ return executor.execute(steps, config?.signal);
654
669
  }
655
670
  }
656
671
  }
@@ -661,7 +676,7 @@ export class TaskRunner&lt;TContext&gt; {
661
676
  <div class='footer quiet pad2 space-top1 center small'>
662
677
  Code coverage generated by
663
678
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
664
- at 2026-01-18T20:12:36.619Z
679
+ at 2026-01-18T21:02:31.377Z
665
680
  </div>
666
681
  <script src="../prettify.js"></script>
667
682
  <script>
@@ -139,7 +139,21 @@
139
139
  <a name='L74'></a><a href='#L74'>74</a>
140
140
  <a name='L75'></a><a href='#L75'>75</a>
141
141
  <a name='L76'></a><a href='#L76'>76</a>
142
- <a name='L77'></a><a href='#L77'>77</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
151
+ <span class="cline-any cline-neutral">&nbsp;</span>
152
+ <span class="cline-any cline-neutral">&nbsp;</span>
153
+ <span class="cline-any cline-neutral">&nbsp;</span>
154
+ <span class="cline-any cline-neutral">&nbsp;</span>
155
+ <span class="cline-any cline-neutral">&nbsp;</span>
156
+ <span class="cline-any cline-neutral">&nbsp;</span>
143
157
  <span class="cline-any cline-neutral">&nbsp;</span>
144
158
  <span class="cline-any cline-neutral">&nbsp;</span>
145
159
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -198,7 +212,9 @@
198
212
  <span class="cline-any cline-yes">1x</span>
199
213
  <span class="cline-any cline-neutral">&nbsp;</span>
200
214
  <span class="cline-any cline-neutral">&nbsp;</span>
215
+ <span class="cline-any cline-neutral">&nbsp;</span>
201
216
  <span class="cline-any cline-yes">2x</span>
217
+ <span class="cline-any cline-neutral">&nbsp;</span>
202
218
  <span class="cline-any cline-yes">1x</span>
203
219
  <span class="cline-any cline-neutral">&nbsp;</span>
204
220
  <span class="cline-any cline-yes">1x</span>
@@ -216,7 +232,10 @@
216
232
  <span class="cline-any cline-neutral">&nbsp;</span>
217
233
  <span class="cline-any cline-neutral">&nbsp;</span>
218
234
  <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { TaskRunner } from "./TaskRunner.js";
219
- import { RunnerEventPayloads, RunnerEventListener } from "./contracts/RunnerEvents.js";
235
+ import {
236
+ RunnerEventPayloads,
237
+ RunnerEventListener,
238
+ } from "./contracts/RunnerEvents.js";
220
239
  import { IExecutionStrategy } from "./strategies/IExecutionStrategy.js";
221
240
  &nbsp;
222
241
  /**
@@ -226,7 +245,10 @@ export class TaskRunnerBuilder&lt;TContext&gt; {
226
245
  private context: TContext;
227
246
  private strategy?: IExecutionStrategy&lt;TContext&gt;;
228
247
  private listeners: {
229
- [K in keyof RunnerEventPayloads&lt;TContext&gt;]?: RunnerEventListener&lt;TContext, K&gt;[];
248
+ [K in keyof RunnerEventPayloads&lt;TContext&gt;]?: RunnerEventListener&lt;
249
+ TContext,
250
+ K
251
+ &gt;[];
230
252
  } = {};
231
253
  &nbsp;
232
254
  /**
@@ -274,7 +296,9 @@ export class TaskRunnerBuilder&lt;TContext&gt; {
274
296
  runner.setExecutionStrategy(this.strategy);
275
297
  }
276
298
  &nbsp;
277
- (Object.keys(this.listeners) as Array&lt;keyof RunnerEventPayloads&lt;TContext&gt;&gt;).forEach((event) =&gt; {
299
+ (
300
+ Object.keys(this.listeners) as Array&lt;keyof RunnerEventPayloads&lt;TContext&gt;&gt;
301
+ ).forEach((event) =&gt; {
278
302
  const callbacks = this.listeners[event];
279
303
  // callbacks is always defined because we are iterating keys of the object
280
304
  callbacks!.forEach((callback) =&gt;
@@ -298,7 +322,7 @@ export class TaskRunnerBuilder&lt;TContext&gt; {
298
322
  <div class='footer quiet pad2 space-top1 center small'>
299
323
  Code coverage generated by
300
324
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
301
- at 2026-01-18T20:12:36.619Z
325
+ at 2026-01-18T21:02:31.377Z
302
326
  </div>
303
327
  <script src="../prettify.js"></script>
304
328
  <script>
@@ -139,7 +139,7 @@ export interface TaskRunnerExecutionConfig {
139
139
  <div class='footer quiet pad2 space-top1 center small'>
140
140
  Code coverage generated by
141
141
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
142
- at 2026-01-18T20:12:36.619Z
142
+ at 2026-01-18T21:02:31.377Z
143
143
  </div>
144
144
  <script src="../prettify.js"></script>
145
145
  <script>
@@ -496,7 +496,7 @@ export class TaskStateManager&lt;TContext&gt; {
496
496
  <div class='footer quiet pad2 space-top1 center small'>
497
497
  Code coverage generated by
498
498
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
499
- at 2026-01-18T20:12:36.619Z
499
+ at 2026-01-18T21:02:31.377Z
500
500
  </div>
501
501
  <script src="../prettify.js"></script>
502
502
  <script>
@@ -203,7 +203,10 @@
203
203
  <a name='L138'></a><a href='#L138'>138</a>
204
204
  <a name='L139'></a><a href='#L139'>139</a>
205
205
  <a name='L140'></a><a href='#L140'>140</a>
206
- <a name='L141'></a><a href='#L141'>141</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
206
+ <a name='L141'></a><a href='#L141'>141</a>
207
+ <a name='L142'></a><a href='#L142'>142</a>
208
+ <a name='L143'></a><a href='#L143'>143</a>
209
+ <a name='L144'></a><a href='#L144'>144</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
207
210
  <span class="cline-any cline-neutral">&nbsp;</span>
208
211
  <span class="cline-any cline-neutral">&nbsp;</span>
209
212
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -247,6 +250,8 @@
247
250
  <span class="cline-any cline-neutral">&nbsp;</span>
248
251
  <span class="cline-any cline-yes">47x</span>
249
252
  <span class="cline-any cline-yes">2x</span>
253
+ <span class="cline-any cline-neutral">&nbsp;</span>
254
+ <span class="cline-any cline-neutral">&nbsp;</span>
250
255
  <span class="cline-any cline-yes">2x</span>
251
256
  <span class="cline-any cline-yes">2x</span>
252
257
  <span class="cline-any cline-yes">2x</span>
@@ -331,6 +336,7 @@
331
336
  <span class="cline-any cline-neutral">&nbsp;</span>
332
337
  <span class="cline-any cline-yes">119x</span>
333
338
  <span class="cline-any cline-neutral">&nbsp;</span>
339
+ <span class="cline-any cline-neutral">&nbsp;</span>
334
340
  <span class="cline-any cline-yes">119x</span>
335
341
  <span class="cline-any cline-neutral">&nbsp;</span>
336
342
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -386,7 +392,9 @@ export class WorkflowExecutor&lt;TContext&gt; {
386
392
  &nbsp;
387
393
  // Check if already aborted
388
394
  if (signal?.aborted) {
389
- this.stateManager.cancelAllPending("Workflow cancelled before execution started.");
395
+ this.stateManager.cancelAllPending(
396
+ "Workflow cancelled before execution started."
397
+ );
390
398
  const results = this.stateManager.getResults();
391
399
  this.eventBus.emit("workflowEnd", { context: this.context, results });
392
400
  return results;
@@ -400,7 +408,7 @@ export class WorkflowExecutor&lt;TContext&gt; {
400
408
  };
401
409
  &nbsp;
402
410
  if (signal) {
403
- signal.addEventListener("abort", onAbort);
411
+ signal.addEventListener("abort", onAbort);
404
412
  }
405
413
  &nbsp;
406
414
  try {
@@ -422,10 +430,10 @@ export class WorkflowExecutor&lt;TContext&gt; {
422
430
  }
423
431
  &nbsp;
424
432
  if (signal?.aborted) {
425
- this.stateManager.cancelAllPending("Workflow cancelled.");
433
+ this.stateManager.cancelAllPending("Workflow cancelled.");
426
434
  } else {
427
- // After a task finishes, check for new work
428
- this.processLoop(executingPromises, signal);
435
+ // After a task finishes, check for new work
436
+ this.processLoop(executingPromises, signal);
429
437
  }
430
438
  }
431
439
  &nbsp;
@@ -469,14 +477,15 @@ export class WorkflowExecutor&lt;TContext&gt; {
469
477
  &nbsp;
470
478
  this.stateManager.markRunning(step);
471
479
  &nbsp;
472
- const taskPromise = this.strategy.execute(step, this.context, signal)
480
+ const taskPromise = this.strategy
481
+ .execute(step, this.context, signal)
473
482
  .then((result) =&gt; {
474
- this.stateManager.markCompleted(step, result);
483
+ this.stateManager.markCompleted(step, result);
475
484
  })
476
485
  .finally(() =&gt; {
477
- executingPromises.delete(taskPromise);
478
- // When a task finishes, we try to run more
479
- this.processLoop(executingPromises, signal);
486
+ executingPromises.delete(taskPromise);
487
+ // When a task finishes, we try to run more
488
+ this.processLoop(executingPromises, signal);
480
489
  });
481
490
  &nbsp;
482
491
  executingPromises.add(taskPromise);
@@ -490,7 +499,7 @@ export class WorkflowExecutor&lt;TContext&gt; {
490
499
  <div class='footer quiet pad2 space-top1 center small'>
491
500
  Code coverage generated by
492
501
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
493
- at 2026-01-18T20:12:36.619Z
502
+ at 2026-01-18T21:02:31.377Z
494
503
  </div>
495
504
  <script src="../prettify.js"></script>
496
505
  <script>
@@ -202,7 +202,7 @@ export type ListenerMap&lt;TContext&gt; = {
202
202
  <div class='footer quiet pad2 space-top1 center small'>
203
203
  Code coverage generated by
204
204
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
205
- at 2026-01-18T20:12:36.619Z
205
+ at 2026-01-18T21:02:31.377Z
206
206
  </div>
207
207
  <script src="../../prettify.js"></script>
208
208
  <script>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-01-18T20:12:36.619Z
104
+ at 2026-01-18T21:02:31.377Z
105
105
  </div>
106
106
  <script src="../../prettify.js"></script>
107
107
  <script>
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>227/227</span>
28
+ <span class='fraction'>231/231</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>42/42</span>
42
+ <span class='fraction'>44/44</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>223/223</span>
49
+ <span class='fraction'>228/228</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,13 +84,28 @@
84
84
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
85
  </td>
86
86
  <td data-value="100" class="pct high">100%</td>
87
- <td data-value="15" class="abs high">15/15</td>
87
+ <td data-value="16" class="abs high">16/16</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
89
  <td data-value="8" class="abs high">8/8</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="4" class="abs high">4/4</td>
91
+ <td data-value="5" class="abs high">5/5</td>
92
+ <td data-value="100" class="pct high">100%</td>
93
+ <td data-value="16" class="abs high">16/16</td>
94
+ </tr>
95
+
96
+ <tr>
97
+ <td class="file high" data-value="TaskGraphValidationError.ts"><a href="TaskGraphValidationError.ts.html">TaskGraphValidationError.ts</a></td>
98
+ <td data-value="100" class="pic high">
99
+ <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
100
+ </td>
101
+ <td data-value="100" class="pct high">100%</td>
102
+ <td data-value="3" class="abs high">3/3</td>
103
+ <td data-value="100" class="pct high">100%</td>
104
+ <td data-value="0" class="abs high">0/0</td>
105
+ <td data-value="100" class="pct high">100%</td>
106
+ <td data-value="1" class="abs high">1/1</td>
92
107
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="15" class="abs high">15/15</td>
108
+ <td data-value="3" class="abs high">3/3</td>
94
109
  </tr>
95
110
 
96
111
  <tr>
@@ -105,7 +120,7 @@
105
120
  <td data-value="100" class="pct high">100%</td>
106
121
  <td data-value="5" class="abs high">5/5</td>
107
122
  <td data-value="100" class="pct high">100%</td>
108
- <td data-value="54" class="abs high">54/54</td>
123
+ <td data-value="55" class="abs high">55/55</td>
109
124
  </tr>
110
125
 
111
126
  <tr>
@@ -191,7 +206,7 @@
191
206
  <div class='footer quiet pad2 space-top1 center small'>
192
207
  Code coverage generated by
193
208
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
194
- at 2026-01-18T20:12:36.619Z
209
+ at 2026-01-18T21:02:31.377Z
195
210
  </div>
196
211
  <script src="../prettify.js"></script>
197
212
  <script>
@@ -132,9 +132,9 @@ import { TaskResult } from "../TaskResult.js";
132
132
  /**
133
133
  * Execution strategy that simulates task execution without running the actual logic.
134
134
  */
135
- export class DryRunExecutionStrategy&lt;TContext&gt;
136
- implements IExecutionStrategy&lt;TContext&gt;
137
- {
135
+ export class DryRunExecutionStrategy&lt;
136
+ TContext,
137
+ &gt; implements IExecutionStrategy&lt;TContext&gt; {
138
138
  /**
139
139
  * Simulates execution by returning a success result immediately.
140
140
  * @param step The task step (ignored).
@@ -163,7 +163,7 @@ export class DryRunExecutionStrategy&lt;TContext&gt;
163
163
  <div class='footer quiet pad2 space-top1 center small'>
164
164
  Code coverage generated by
165
165
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
166
- at 2026-01-18T20:12:36.619Z
166
+ at 2026-01-18T21:02:31.377Z
167
167
  </div>
168
168
  <script src="../../prettify.js"></script>
169
169
  <script>
@@ -153,7 +153,13 @@
153
153
  <a name='L88'></a><a href='#L88'>88</a>
154
154
  <a name='L89'></a><a href='#L89'>89</a>
155
155
  <a name='L90'></a><a href='#L90'>90</a>
156
- <a name='L91'></a><a href='#L91'>91</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
157
163
  <span class="cline-any cline-neutral">&nbsp;</span>
158
164
  <span class="cline-any cline-neutral">&nbsp;</span>
159
165
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -161,7 +167,9 @@
161
167
  <span class="cline-any cline-neutral">&nbsp;</span>
162
168
  <span class="cline-any cline-neutral">&nbsp;</span>
163
169
  <span class="cline-any cline-neutral">&nbsp;</span>
164
- <span class="cline-any cline-yes">66x</span>
170
+ <span class="cline-any cline-neutral">&nbsp;</span>
171
+ <span class="cline-any cline-neutral">&nbsp;</span>
172
+ <span class="cline-any cline-yes">67x</span>
165
173
  <span class="cline-any cline-neutral">&nbsp;</span>
166
174
  <span class="cline-any cline-neutral">&nbsp;</span>
167
175
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -186,6 +194,10 @@
186
194
  <span class="cline-any cline-yes">12x</span>
187
195
  <span class="cline-any cline-neutral">&nbsp;</span>
188
196
  <span class="cline-any cline-yes">12x</span>
197
+ <span class="cline-any cline-neutral">&nbsp;</span>
198
+ <span class="cline-any cline-neutral">&nbsp;</span>
199
+ <span class="cline-any cline-neutral">&nbsp;</span>
200
+ <span class="cline-any cline-neutral">&nbsp;</span>
189
201
  <span class="cline-any cline-yes">1x</span>
190
202
  <span class="cline-any cline-neutral">&nbsp;</span>
191
203
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -250,7 +262,9 @@ import { TaskResult } from "../TaskResult.js";
250
262
  /**
251
263
  * Execution strategy that retries tasks upon failure based on their retry configuration.
252
264
  */
253
- export class RetryingExecutionStrategy&lt;TContext&gt; implements IExecutionStrategy&lt;TContext&gt; {
265
+ export class RetryingExecutionStrategy&lt;
266
+ TContext,
267
+ &gt; implements IExecutionStrategy&lt;TContext&gt; {
254
268
  constructor(private innerStrategy: IExecutionStrategy&lt;TContext&gt;) {}
255
269
  &nbsp;
256
270
  async execute(
@@ -275,7 +289,11 @@ export class RetryingExecutionStrategy&lt;TContext&gt; implements IExecutionStra
275
289
  &nbsp;
276
290
  const result = await this.innerStrategy.execute(step, context, signal);
277
291
  &nbsp;
278
- if (result.status === "success" || result.status === "cancelled" || result.status === "skipped") {
292
+ if (
293
+ result.status === "success" ||
294
+ result.status === "cancelled" ||
295
+ result.status === "skipped"
296
+ ) {
279
297
  return result;
280
298
  }
281
299
  &nbsp;
@@ -296,13 +314,13 @@ export class RetryingExecutionStrategy&lt;TContext&gt; implements IExecutionStra
296
314
  try {
297
315
  await this.sleep(delay, signal);
298
316
  } catch (e) {
299
- if (signal?.aborted) {
300
- return {
301
- status: "cancelled",
302
- message: "Task cancelled during retry delay",
303
- };
304
- }
305
- throw e;
317
+ if (signal?.aborted) {
318
+ return {
319
+ status: "cancelled",
320
+ message: "Task cancelled during retry delay",
321
+ };
322
+ }
323
+ throw e;
306
324
  }
307
325
  }
308
326
  }
@@ -340,7 +358,7 @@ export class RetryingExecutionStrategy&lt;TContext&gt; implements IExecutionStra
340
358
  <div class='footer quiet pad2 space-top1 center small'>
341
359
  Code coverage generated by
342
360
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
343
- at 2026-01-18T20:12:36.619Z
361
+ at 2026-01-18T21:02:31.377Z
344
362
  </div>
345
363
  <script src="../../prettify.js"></script>
346
364
  <script>