@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.
Files changed (87) hide show
  1. package/.jules/sentinel.md +4 -0
  2. package/AGENTS.md +53 -1
  3. package/CHANGELOG.md +41 -15
  4. package/README.md +1 -1
  5. package/coverage/coverage-final.json +8 -4
  6. package/coverage/index.html +24 -9
  7. package/coverage/lcov-report/index.html +24 -9
  8. package/coverage/lcov-report/src/EventBus.ts.html +8 -8
  9. package/coverage/lcov-report/src/TaskGraphValidator.ts.html +143 -62
  10. package/coverage/lcov-report/src/TaskRunner.ts.html +213 -21
  11. package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +313 -0
  12. package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +124 -0
  13. package/coverage/lcov-report/src/TaskStateManager.ts.html +511 -0
  14. package/coverage/lcov-report/src/WorkflowExecutor.ts.html +125 -137
  15. package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
  16. package/coverage/lcov-report/src/contracts/index.html +1 -1
  17. package/coverage/lcov-report/src/index.html +59 -14
  18. package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +190 -0
  19. package/coverage/lcov-report/src/strategies/index.html +116 -0
  20. package/coverage/lcov.info +383 -184
  21. package/coverage/src/EventBus.ts.html +8 -8
  22. package/coverage/src/TaskGraphValidator.ts.html +143 -62
  23. package/coverage/src/TaskRunner.ts.html +213 -21
  24. package/coverage/src/TaskRunnerBuilder.ts.html +313 -0
  25. package/coverage/src/TaskRunnerExecutionConfig.ts.html +124 -0
  26. package/coverage/src/TaskStateManager.ts.html +511 -0
  27. package/coverage/src/WorkflowExecutor.ts.html +125 -137
  28. package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
  29. package/coverage/src/contracts/index.html +1 -1
  30. package/coverage/src/index.html +59 -14
  31. package/coverage/src/strategies/StandardExecutionStrategy.ts.html +190 -0
  32. package/coverage/src/strategies/index.html +116 -0
  33. package/dist/TaskGraphValidator.js +39 -16
  34. package/dist/TaskGraphValidator.js.map +1 -1
  35. package/dist/TaskRunner.d.ts +12 -2
  36. package/dist/TaskRunner.js +55 -3
  37. package/dist/TaskRunner.js.map +1 -1
  38. package/dist/TaskRunnerBuilder.d.ts +33 -0
  39. package/dist/TaskRunnerBuilder.js +54 -0
  40. package/dist/TaskRunnerBuilder.js.map +1 -0
  41. package/dist/TaskRunnerExecutionConfig.d.ts +13 -0
  42. package/dist/TaskRunnerExecutionConfig.js +2 -0
  43. package/dist/TaskRunnerExecutionConfig.js.map +1 -0
  44. package/dist/TaskStateManager.d.ts +54 -0
  45. package/dist/TaskStateManager.js +130 -0
  46. package/dist/TaskStateManager.js.map +1 -0
  47. package/dist/TaskStatus.d.ts +1 -1
  48. package/dist/TaskStep.d.ts +2 -1
  49. package/dist/WorkflowExecutor.d.ts +11 -10
  50. package/dist/WorkflowExecutor.js +64 -73
  51. package/dist/WorkflowExecutor.js.map +1 -1
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.js +3 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/strategies/IExecutionStrategy.d.ts +16 -0
  56. package/dist/strategies/IExecutionStrategy.js +2 -0
  57. package/dist/strategies/IExecutionStrategy.js.map +1 -0
  58. package/dist/strategies/StandardExecutionStrategy.d.ts +9 -0
  59. package/dist/strategies/StandardExecutionStrategy.js +25 -0
  60. package/dist/strategies/StandardExecutionStrategy.js.map +1 -0
  61. package/openspec/changes/add-concurrency-control/proposal.md +14 -0
  62. package/openspec/changes/add-concurrency-control/tasks.md +9 -0
  63. package/openspec/changes/add-task-retry-policy/proposal.md +13 -0
  64. package/openspec/changes/add-task-retry-policy/tasks.md +10 -0
  65. package/openspec/changes/add-workflow-preview/proposal.md +12 -0
  66. package/openspec/changes/add-workflow-preview/tasks.md +7 -0
  67. package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/tasks.md +10 -0
  68. package/openspec/changes/archive/2026-01-18-add-integration-tests/proposal.md +18 -0
  69. package/openspec/changes/archive/2026-01-18-add-integration-tests/tasks.md +17 -0
  70. package/openspec/changes/archive/2026-01-18-refactor-core-architecture/proposal.md +14 -0
  71. package/openspec/changes/archive/2026-01-18-refactor-core-architecture/tasks.md +8 -0
  72. package/package.json +1 -1
  73. package/src/TaskGraphValidator.ts +46 -19
  74. package/src/TaskRunner.ts +68 -4
  75. package/src/TaskRunnerBuilder.ts +76 -0
  76. package/src/TaskRunnerExecutionConfig.ts +13 -0
  77. package/src/TaskStateManager.ts +142 -0
  78. package/src/TaskStatus.ts +1 -1
  79. package/src/TaskStep.ts +2 -1
  80. package/src/WorkflowExecutor.ts +75 -79
  81. package/src/index.ts +4 -0
  82. package/src/strategies/IExecutionStrategy.ts +21 -0
  83. package/src/strategies/StandardExecutionStrategy.ts +35 -0
  84. package/test-report.xml +121 -43
  85. package/GEMINI.md +0 -46
  86. package/openspec/changes/add-external-task-cancellation/tasks.md +0 -10
  87. /package/openspec/changes/{add-external-task-cancellation → archive/2026-01-18-add-external-task-cancellation}/proposal.md +0 -0
@@ -25,28 +25,28 @@
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'>121/121</span>
28
+ <span class='fraction'>208/208</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
33
  <span class="strong">100% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>42/42</span>
35
+ <span class='fraction'>78/78</span>
36
36
  </div>
37
37
 
38
38
 
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'>19/19</span>
42
+ <span class='fraction'>39/39</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'>119/119</span>
49
+ <span class='fraction'>206/206</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,13 +84,13 @@
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="121" class="abs high">121/121</td>
87
+ <td data-value="203" class="abs high">203/203</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
- <td data-value="42" class="abs high">42/42</td>
89
+ <td data-value="70" class="abs high">70/70</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="19" class="abs high">19/19</td>
91
+ <td data-value="38" class="abs high">38/38</td>
92
92
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="119" class="abs high">119/119</td>
93
+ <td data-value="201" class="abs high">201/201</td>
94
94
  </tr>
95
95
 
96
96
  <tr>
@@ -108,6 +108,21 @@
108
108
  <td data-value="0" class="abs empty">0/0</td>
109
109
  </tr>
110
110
 
111
+ <tr>
112
+ <td class="file high" data-value="src/strategies"><a href="src/strategies/index.html">src/strategies</a></td>
113
+ <td data-value="100" class="pic high">
114
+ <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
115
+ </td>
116
+ <td data-value="100" class="pct high">100%</td>
117
+ <td data-value="5" class="abs high">5/5</td>
118
+ <td data-value="100" class="pct high">100%</td>
119
+ <td data-value="8" class="abs high">8/8</td>
120
+ <td data-value="100" class="pct high">100%</td>
121
+ <td data-value="1" class="abs high">1/1</td>
122
+ <td data-value="100" class="pct high">100%</td>
123
+ <td data-value="5" class="abs high">5/5</td>
124
+ </tr>
125
+
111
126
  </tbody>
112
127
  </table>
113
128
  </div>
@@ -116,7 +131,7 @@
116
131
  <div class='footer quiet pad2 space-top1 center small'>
117
132
  Code coverage generated by
118
133
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
119
- at 2026-01-18T14:25:37.520Z
134
+ at 2026-01-18T16:53:50.068Z
120
135
  </div>
121
136
  <script src="prettify.js"></script>
122
137
  <script>
@@ -158,7 +158,7 @@
158
158
  <span class="cline-any cline-neutral">&nbsp;</span>
159
159
  <span class="cline-any cline-neutral">&nbsp;</span>
160
160
  <span class="cline-any cline-neutral">&nbsp;</span>
161
- <span class="cline-any cline-yes">26x</span>
161
+ <span class="cline-any cline-yes">56x</span>
162
162
  <span class="cline-any cline-neutral">&nbsp;</span>
163
163
  <span class="cline-any cline-neutral">&nbsp;</span>
164
164
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -169,13 +169,13 @@
169
169
  <span class="cline-any cline-neutral">&nbsp;</span>
170
170
  <span class="cline-any cline-neutral">&nbsp;</span>
171
171
  <span class="cline-any cline-neutral">&nbsp;</span>
172
- <span class="cline-any cline-yes">13x</span>
172
+ <span class="cline-any cline-yes">16x</span>
173
173
  <span class="cline-any cline-neutral">&nbsp;</span>
174
174
  <span class="cline-any cline-neutral">&nbsp;</span>
175
- <span class="cline-any cline-yes">12x</span>
175
+ <span class="cline-any cline-yes">14x</span>
176
176
  <span class="cline-any cline-neutral">&nbsp;</span>
177
177
  <span class="cline-any cline-neutral">&nbsp;</span>
178
- <span class="cline-any cline-yes">13x</span>
178
+ <span class="cline-any cline-yes">16x</span>
179
179
  <span class="cline-any cline-neutral">&nbsp;</span>
180
180
  <span class="cline-any cline-neutral">&nbsp;</span>
181
181
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -189,8 +189,8 @@
189
189
  <span class="cline-any cline-neutral">&nbsp;</span>
190
190
  <span class="cline-any cline-neutral">&nbsp;</span>
191
191
  <span class="cline-any cline-neutral">&nbsp;</span>
192
+ <span class="cline-any cline-yes">3x</span>
192
193
  <span class="cline-any cline-yes">2x</span>
193
- <span class="cline-any cline-yes">1x</span>
194
194
  <span class="cline-any cline-neutral">&nbsp;</span>
195
195
  <span class="cline-any cline-neutral">&nbsp;</span>
196
196
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -205,10 +205,10 @@
205
205
  <span class="cline-any cline-neutral">&nbsp;</span>
206
206
  <span class="cline-any cline-neutral">&nbsp;</span>
207
207
  <span class="cline-any cline-neutral">&nbsp;</span>
208
- <span class="cline-any cline-yes">106x</span>
208
+ <span class="cline-any cline-yes">294x</span>
209
209
  <span class="cline-any cline-neutral">&nbsp;</span>
210
210
  <span class="cline-any cline-neutral">&nbsp;</span>
211
- <span class="cline-any cline-yes">106x</span>
211
+ <span class="cline-any cline-yes">294x</span>
212
212
  <span class="cline-any cline-yes">14x</span>
213
213
  <span class="cline-any cline-yes">14x</span>
214
214
  <span class="cline-any cline-yes">14x</span>
@@ -322,7 +322,7 @@ export class EventBus&lt;TContext&gt; {
322
322
  <div class='footer quiet pad2 space-top1 center small'>
323
323
  Code coverage generated by
324
324
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
325
- at 2026-01-18T14:25:37.520Z
325
+ at 2026-01-18T16:53:50.068Z
326
326
  </div>
327
327
  <script src="../prettify.js"></script>
328
328
  <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'>46/46</span>
28
+ <span class='fraction'>56/56</span>
29
29
  </div>
30
30
 
31
31
 
@@ -46,7 +46,7 @@
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'>44/44</span>
49
+ <span class='fraction'>54/54</span>
50
50
  </div>
51
51
 
52
52
 
@@ -199,7 +199,34 @@
199
199
  <a name='L134'></a><a href='#L134'>134</a>
200
200
  <a name='L135'></a><a href='#L135'>135</a>
201
201
  <a name='L136'></a><a href='#L136'>136</a>
202
- <a name='L137'></a><a href='#L137'>137</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
202
+ <a name='L137'></a><a href='#L137'>137</a>
203
+ <a name='L138'></a><a href='#L138'>138</a>
204
+ <a name='L139'></a><a href='#L139'>139</a>
205
+ <a name='L140'></a><a href='#L140'>140</a>
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>
210
+ <a name='L145'></a><a href='#L145'>145</a>
211
+ <a name='L146'></a><a href='#L146'>146</a>
212
+ <a name='L147'></a><a href='#L147'>147</a>
213
+ <a name='L148'></a><a href='#L148'>148</a>
214
+ <a name='L149'></a><a href='#L149'>149</a>
215
+ <a name='L150'></a><a href='#L150'>150</a>
216
+ <a name='L151'></a><a href='#L151'>151</a>
217
+ <a name='L152'></a><a href='#L152'>152</a>
218
+ <a name='L153'></a><a href='#L153'>153</a>
219
+ <a name='L154'></a><a href='#L154'>154</a>
220
+ <a name='L155'></a><a href='#L155'>155</a>
221
+ <a name='L156'></a><a href='#L156'>156</a>
222
+ <a name='L157'></a><a href='#L157'>157</a>
223
+ <a name='L158'></a><a href='#L158'>158</a>
224
+ <a name='L159'></a><a href='#L159'>159</a>
225
+ <a name='L160'></a><a href='#L160'>160</a>
226
+ <a name='L161'></a><a href='#L161'>161</a>
227
+ <a name='L162'></a><a href='#L162'>162</a>
228
+ <a name='L163'></a><a href='#L163'>163</a>
229
+ <a name='L164'></a><a href='#L164'>164</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
203
230
  <span class="cline-any cline-neutral">&nbsp;</span>
204
231
  <span class="cline-any cline-neutral">&nbsp;</span>
205
232
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -216,27 +243,27 @@
216
243
  <span class="cline-any cline-neutral">&nbsp;</span>
217
244
  <span class="cline-any cline-neutral">&nbsp;</span>
218
245
  <span class="cline-any cline-neutral">&nbsp;</span>
219
- <span class="cline-any cline-yes">29x</span>
246
+ <span class="cline-any cline-yes">54x</span>
220
247
  <span class="cline-any cline-neutral">&nbsp;</span>
221
248
  <span class="cline-any cline-neutral">&nbsp;</span>
222
- <span class="cline-any cline-yes">29x</span>
223
- <span class="cline-any cline-yes">29x</span>
224
- <span class="cline-any cline-yes">61x</span>
249
+ <span class="cline-any cline-yes">54x</span>
250
+ <span class="cline-any cline-yes">54x</span>
251
+ <span class="cline-any cline-yes">20139x</span>
225
252
  <span class="cline-any cline-yes">3x</span>
226
253
  <span class="cline-any cline-neutral">&nbsp;</span>
227
254
  <span class="cline-any cline-neutral">&nbsp;</span>
228
255
  <span class="cline-any cline-neutral">&nbsp;</span>
229
256
  <span class="cline-any cline-neutral">&nbsp;</span>
230
257
  <span class="cline-any cline-neutral">&nbsp;</span>
231
- <span class="cline-any cline-yes">58x</span>
258
+ <span class="cline-any cline-yes">20136x</span>
232
259
  <span class="cline-any cline-neutral">&nbsp;</span>
233
260
  <span class="cline-any cline-neutral">&nbsp;</span>
234
261
  <span class="cline-any cline-neutral">&nbsp;</span>
235
262
  <span class="cline-any cline-neutral">&nbsp;</span>
236
- <span class="cline-any cline-yes">29x</span>
237
- <span class="cline-any cline-yes">61x</span>
238
- <span class="cline-any cline-yes">41x</span>
239
- <span class="cline-any cline-yes">5x</span>
263
+ <span class="cline-any cline-yes">54x</span>
264
+ <span class="cline-any cline-yes">20139x</span>
265
+ <span class="cline-any cline-yes">20166x</span>
266
+ <span class="cline-any cline-yes">6x</span>
240
267
  <span class="cline-any cline-neutral">&nbsp;</span>
241
268
  <span class="cline-any cline-neutral">&nbsp;</span>
242
269
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -247,48 +274,51 @@
247
274
  <span class="cline-any cline-neutral">&nbsp;</span>
248
275
  <span class="cline-any cline-neutral">&nbsp;</span>
249
276
  <span class="cline-any cline-neutral">&nbsp;</span>
250
- <span class="cline-any cline-yes">29x</span>
277
+ <span class="cline-any cline-yes">54x</span>
251
278
  <span class="cline-any cline-neutral">&nbsp;</span>
252
- <span class="cline-any cline-yes">29x</span>
253
- <span class="cline-any cline-yes">4x</span>
279
+ <span class="cline-any cline-yes">54x</span>
280
+ <span class="cline-any cline-yes">5x</span>
254
281
  <span class="cline-any cline-neutral">&nbsp;</span>
255
282
  <span class="cline-any cline-neutral">&nbsp;</span>
256
283
  <span class="cline-any cline-neutral">&nbsp;</span>
257
284
  <span class="cline-any cline-neutral">&nbsp;</span>
258
285
  <span class="cline-any cline-neutral">&nbsp;</span>
259
286
  <span class="cline-any cline-neutral">&nbsp;</span>
260
- <span class="cline-any cline-yes">25x</span>
261
- <span class="cline-any cline-yes">25x</span>
262
- <span class="cline-any cline-yes">54x</span>
287
+ <span class="cline-any cline-yes">49x</span>
288
+ <span class="cline-any cline-yes">49x</span>
289
+ <span class="cline-any cline-yes">20131x</span>
263
290
  <span class="cline-any cline-neutral">&nbsp;</span>
264
291
  <span class="cline-any cline-neutral">&nbsp;</span>
265
- <span class="cline-any cline-yes">25x</span>
266
- <span class="cline-any cline-yes">25x</span>
292
+ <span class="cline-any cline-yes">49x</span>
293
+ <span class="cline-any cline-yes">49x</span>
267
294
  <span class="cline-any cline-neutral">&nbsp;</span>
268
- <span class="cline-any cline-yes">25x</span>
269
295
  <span class="cline-any cline-yes">49x</span>
296
+ <span class="cline-any cline-yes">20124x</span>
270
297
  <span class="cline-any cline-yes">3x</span>
271
298
  <span class="cline-any cline-neutral">&nbsp;</span>
272
299
  <span class="cline-any cline-neutral">&nbsp;</span>
273
- <span class="cline-any cline-yes">46x</span>
274
- <span class="cline-any cline-yes">46x</span>
300
+ <span class="cline-any cline-yes">20121x</span>
301
+ <span class="cline-any cline-yes">20121x</span>
302
+ <span class="cline-any cline-neutral">&nbsp;</span>
303
+ <span class="cline-any cline-neutral">&nbsp;</span>
304
+ <span class="cline-any cline-yes">5x</span>
305
+ <span class="cline-any cline-yes">5x</span>
306
+ <span class="cline-any cline-yes">5x</span>
307
+ <span class="cline-any cline-neutral">&nbsp;</span>
308
+ <span class="cline-any cline-yes">5x</span>
275
309
  <span class="cline-any cline-neutral">&nbsp;</span>
276
310
  <span class="cline-any cline-neutral">&nbsp;</span>
277
- <span class="cline-any cline-yes">4x</span>
278
- <span class="cline-any cline-yes">4x</span>
279
- <span class="cline-any cline-yes">4x</span>
280
311
  <span class="cline-any cline-neutral">&nbsp;</span>
281
- <span class="cline-any cline-yes">4x</span>
282
312
  <span class="cline-any cline-neutral">&nbsp;</span>
283
313
  <span class="cline-any cline-neutral">&nbsp;</span>
314
+ <span class="cline-any cline-yes">5x</span>
284
315
  <span class="cline-any cline-neutral">&nbsp;</span>
285
316
  <span class="cline-any cline-neutral">&nbsp;</span>
286
317
  <span class="cline-any cline-neutral">&nbsp;</span>
287
- <span class="cline-any cline-yes">4x</span>
318
+ <span class="cline-any cline-yes">49x</span>
288
319
  <span class="cline-any cline-neutral">&nbsp;</span>
289
320
  <span class="cline-any cline-neutral">&nbsp;</span>
290
321
  <span class="cline-any cline-neutral">&nbsp;</span>
291
- <span class="cline-any cline-yes">25x</span>
292
322
  <span class="cline-any cline-neutral">&nbsp;</span>
293
323
  <span class="cline-any cline-neutral">&nbsp;</span>
294
324
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -297,11 +327,11 @@
297
327
  <span class="cline-any cline-neutral">&nbsp;</span>
298
328
  <span class="cline-any cline-neutral">&nbsp;</span>
299
329
  <span class="cline-any cline-neutral">&nbsp;</span>
330
+ <span class="cline-any cline-yes">8x</span>
331
+ <span class="cline-any cline-yes">8x</span>
300
332
  <span class="cline-any cline-neutral">&nbsp;</span>
301
333
  <span class="cline-any cline-neutral">&nbsp;</span>
302
334
  <span class="cline-any cline-neutral">&nbsp;</span>
303
- <span class="cline-any cline-yes">6x</span>
304
- <span class="cline-any cline-yes">6x</span>
305
335
  <span class="cline-any cline-neutral">&nbsp;</span>
306
336
  <span class="cline-any cline-neutral">&nbsp;</span>
307
337
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -309,30 +339,54 @@
309
339
  <span class="cline-any cline-neutral">&nbsp;</span>
310
340
  <span class="cline-any cline-neutral">&nbsp;</span>
311
341
  <span class="cline-any cline-neutral">&nbsp;</span>
342
+ <span class="cline-any cline-yes">20121x</span>
312
343
  <span class="cline-any cline-neutral">&nbsp;</span>
344
+ <span class="cline-any cline-yes">20121x</span>
345
+ <span class="cline-any cline-yes">20121x</span>
346
+ <span class="cline-any cline-yes">20121x</span>
313
347
  <span class="cline-any cline-neutral">&nbsp;</span>
314
- <span class="cline-any cline-yes">51x</span>
315
- <span class="cline-any cline-yes">51x</span>
316
- <span class="cline-any cline-yes">51x</span>
348
+ <span class="cline-any cline-yes">20121x</span>
317
349
  <span class="cline-any cline-neutral">&nbsp;</span>
318
- <span class="cline-any cline-yes">51x</span>
319
- <span class="cline-any cline-yes">51x</span>
320
- <span class="cline-any cline-yes">33x</span>
321
350
  <span class="cline-any cline-neutral">&nbsp;</span>
322
351
  <span class="cline-any cline-neutral">&nbsp;</span>
323
352
  <span class="cline-any cline-neutral">&nbsp;</span>
353
+ <span class="cline-any cline-neutral">&nbsp;</span>
354
+ <span class="cline-any cline-neutral">&nbsp;</span>
355
+ <span class="cline-any cline-yes">20121x</span>
356
+ <span class="cline-any cline-yes">40273x</span>
357
+ <span class="cline-any cline-yes">40273x</span>
358
+ <span class="cline-any cline-neutral">&nbsp;</span>
359
+ <span class="cline-any cline-yes">40273x</span>
360
+ <span class="cline-any cline-yes">20157x</span>
361
+ <span class="cline-any cline-yes">20157x</span>
362
+ <span class="cline-any cline-neutral">&nbsp;</span>
363
+ <span class="cline-any cline-yes">20157x</span>
364
+ <span class="cline-any cline-neutral">&nbsp;</span>
365
+ <span class="cline-any cline-yes">5x</span>
324
366
  <span class="cline-any cline-yes">5x</span>
325
- <span class="cline-any cline-yes">28x</span>
326
367
  <span class="cline-any cline-neutral">&nbsp;</span>
327
368
  <span class="cline-any cline-neutral">&nbsp;</span>
328
- <span class="cline-any cline-yes">4x</span>
329
- <span class="cline-any cline-yes">4x</span>
369
+ <span class="cline-any cline-yes">20152x</span>
370
+ <span class="cline-any cline-yes">7x</span>
371
+ <span class="cline-any cline-yes">7x</span>
372
+ <span class="cline-any cline-yes">7x</span>
373
+ <span class="cline-any cline-neutral">&nbsp;</span>
374
+ <span class="cline-any cline-yes">7x</span>
375
+ <span class="cline-any cline-neutral">&nbsp;</span>
376
+ <span class="cline-any cline-neutral">&nbsp;</span>
377
+ <span class="cline-any cline-neutral">&nbsp;</span>
378
+ <span class="cline-any cline-neutral">&nbsp;</span>
379
+ <span class="cline-any cline-neutral">&nbsp;</span>
380
+ <span class="cline-any cline-neutral">&nbsp;</span>
330
381
  <span class="cline-any cline-neutral">&nbsp;</span>
331
382
  <span class="cline-any cline-neutral">&nbsp;</span>
383
+ <span class="cline-any cline-yes">20116x</span>
384
+ <span class="cline-any cline-yes">20116x</span>
385
+ <span class="cline-any cline-yes">20116x</span>
332
386
  <span class="cline-any cline-neutral">&nbsp;</span>
333
- <span class="cline-any cline-yes">42x</span>
334
- <span class="cline-any cline-yes">42x</span>
335
- <span class="cline-any cline-yes">42x</span>
387
+ <span class="cline-any cline-neutral">&nbsp;</span>
388
+ <span class="cline-any cline-neutral">&nbsp;</span>
389
+ <span class="cline-any cline-yes">20116x</span>
336
390
  <span class="cline-any cline-neutral">&nbsp;</span>
337
391
  <span class="cline-any cline-neutral">&nbsp;</span>
338
392
  <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { ITaskGraphValidator } from "./contracts/ITaskGraphValidator.js";
@@ -441,33 +495,60 @@ export class TaskGraphValidator implements ITaskGraphValidator {
441
495
  }
442
496
  &nbsp;
443
497
  private detectCycle(
444
- taskId: string,
498
+ startTaskId: string,
445
499
  path: string[],
446
500
  visited: Set&lt;string&gt;,
447
501
  recursionStack: Set&lt;string&gt;,
448
502
  adjacencyList: Map&lt;string, string[]&gt;
449
503
  ): boolean {
450
- visited.add(taskId);
451
- recursionStack.add(taskId);
452
- path.push(taskId);
504
+ // Use an explicit stack to avoid maximum call stack size exceeded errors
505
+ const stack: { taskId: string; index: number; dependencies: string[] }[] = [];
506
+ &nbsp;
507
+ visited.add(startTaskId);
508
+ recursionStack.add(startTaskId);
509
+ path.push(startTaskId);
510
+ &nbsp;
511
+ stack.push({
512
+ taskId: startTaskId,
513
+ index: 0,
514
+ /* v8 ignore next */
515
+ dependencies: adjacencyList.get(startTaskId) ?? []
516
+ });
517
+ &nbsp;
518
+ while (stack.length &gt; 0) {
519
+ const frame = stack[stack.length - 1];
520
+ const { taskId, dependencies } = frame;
521
+ &nbsp;
522
+ if (frame.index &lt; dependencies.length) {
523
+ const dependenceId = dependencies[frame.index];
524
+ frame.index++;
453
525
  &nbsp;
454
- const dependencies = adjacencyList.get(taskId)!;
455
- for (const dependenceId of dependencies) {
456
- if (
457
- !visited.has(dependenceId) &amp;&amp;
458
- this.detectCycle(dependenceId, path, visited, recursionStack, adjacencyList)
459
- ) {
460
- return true;
461
- } else if (recursionStack.has(dependenceId)) {
462
- // Cycle detected
463
- // Add the dependency to complete the visual cycle
464
- path.push(dependenceId);
465
- return true;
526
+ if (recursionStack.has(dependenceId)) {
527
+ // Cycle detected
528
+ path.push(dependenceId);
529
+ return true;
530
+ }
531
+ &nbsp;
532
+ if (!visited.has(dependenceId)) {
533
+ visited.add(dependenceId);
534
+ recursionStack.add(dependenceId);
535
+ path.push(dependenceId);
536
+ &nbsp;
537
+ stack.push({
538
+ taskId: dependenceId,
539
+ index: 0,
540
+ /* v8 ignore next */
541
+ dependencies: adjacencyList.get(dependenceId) ?? []
542
+ });
543
+ }
544
+ } else {
545
+ // Finished all dependencies for this node
546
+ recursionStack.delete(taskId);
547
+ path.pop();
548
+ stack.pop();
466
549
  }
467
550
  }
468
551
  &nbsp;
469
- recursionStack.delete(taskId);
470
- path.pop();
471
552
  return false;
472
553
  }
473
554
  }
@@ -478,7 +559,7 @@ export class TaskGraphValidator implements ITaskGraphValidator {
478
559
  <div class='footer quiet pad2 space-top1 center small'>
479
560
  Code coverage generated by
480
561
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
481
- at 2026-01-18T14:25:37.520Z
562
+ at 2026-01-18T16:53:50.068Z
482
563
  </div>
483
564
  <script src="../prettify.js"></script>
484
565
  <script>