@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
@@ -15,14 +15,17 @@ Instructions for AI coding assistants using OpenSpec for spec-driven development
15
15
  ## Three-Stage Workflow
16
16
 
17
17
  ### Stage 1: Creating Changes
18
+
18
19
  Create proposal when you need to:
20
+
19
21
  - Add features or functionality
20
22
  - Make breaking changes (API, schema)
21
- - Change architecture or patterns
23
+ - Change architecture or patterns
22
24
  - Optimize performance (changes behavior)
23
25
  - Update security patterns
24
26
 
25
27
  Triggers (examples):
28
+
26
29
  - "Help me create a change proposal"
27
30
  - "Help me plan a change"
28
31
  - "Help me create a proposal"
@@ -30,10 +33,12 @@ Triggers (examples):
30
33
  - "I want to create a spec"
31
34
 
32
35
  Loose matching guidance:
36
+
33
37
  - Contains one of: `proposal`, `change`, `spec`
34
38
  - With one of: `create`, `plan`, `make`, `start`, `help`
35
39
 
36
40
  Skip proposal for:
41
+
37
42
  - Bug fixes (restore intended behavior)
38
43
  - Typos, formatting, comments
39
44
  - Dependency updates (non-breaking)
@@ -41,13 +46,16 @@ Skip proposal for:
41
46
  - Tests for existing behavior
42
47
 
43
48
  **Workflow**
49
+
44
50
  1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
45
51
  2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
46
52
  3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
47
53
  4. Run `openspec validate <id> --strict --no-interactive` and resolve any issues before sharing the proposal.
48
54
 
49
55
  ### Stage 2: Implementing Changes
56
+
50
57
  Track these steps as TODOs and complete them one by one.
58
+
51
59
  1. **Read proposal.md** - Understand what's being built
52
60
  2. **Read design.md** (if exists) - Review technical decisions
53
61
  3. **Read tasks.md** - Get implementation checklist
@@ -57,7 +65,9 @@ Track these steps as TODOs and complete them one by one.
57
65
  7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
58
66
 
59
67
  ### Stage 3: Archiving Changes
68
+
60
69
  After deployment, create separate PR to:
70
+
61
71
  - Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
62
72
  - Update `specs/` if capabilities changed
63
73
  - Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
@@ -66,6 +76,7 @@ After deployment, create separate PR to:
66
76
  ## Before Any Task
67
77
 
68
78
  **Context Checklist:**
79
+
69
80
  - [ ] Read relevant specs in `specs/[capability]/spec.md`
70
81
  - [ ] Check pending changes in `changes/` for conflicts
71
82
  - [ ] Read `openspec/project.md` for conventions
@@ -73,12 +84,14 @@ After deployment, create separate PR to:
73
84
  - [ ] Run `openspec list --specs` to see existing capabilities
74
85
 
75
86
  **Before Creating Specs:**
87
+
76
88
  - Always check if capability already exists
77
89
  - Prefer modifying existing specs over creating duplicates
78
90
  - Use `openspec show [spec]` to review current state
79
91
  - If request is ambiguous, ask 1–2 clarifying questions before scaffolding
80
92
 
81
93
  ### Search Guidance
94
+
82
95
  - Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
83
96
  - Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
84
97
  - Show details:
@@ -147,7 +160,7 @@ openspec/
147
160
  ```
148
161
  New request?
149
162
  ├─ Bug fix restoring spec behavior? → Fix directly
150
- ├─ Typo/format/comment? → Fix directly
163
+ ├─ Typo/format/comment? → Fix directly
151
164
  ├─ New feature/capability? → Create proposal
152
165
  ├─ Breaking change? → Create proposal
153
166
  ├─ Architecture change? → Create proposal
@@ -159,45 +172,60 @@ New request?
159
172
  1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
160
173
 
161
174
  2. **Write proposal.md:**
175
+
162
176
  ```markdown
163
177
  # Change: [Brief description of change]
164
178
 
165
179
  ## Why
180
+
166
181
  [1-2 sentences on problem/opportunity]
167
182
 
168
183
  ## What Changes
184
+
169
185
  - [Bullet list of changes]
170
186
  - [Mark breaking changes with **BREAKING**]
171
187
 
172
188
  ## Impact
189
+
173
190
  - Affected specs: [list capabilities]
174
191
  - Affected code: [key files/systems]
175
192
  ```
176
193
 
177
194
  3. **Create spec deltas:** `specs/[capability]/spec.md`
195
+
178
196
  ```markdown
179
197
  ## ADDED Requirements
198
+
180
199
  ### Requirement: New Feature
200
+
181
201
  The system SHALL provide...
182
202
 
183
203
  #### Scenario: Success case
204
+
184
205
  - **WHEN** user performs action
185
206
  - **THEN** expected result
186
207
 
187
208
  ## MODIFIED Requirements
209
+
188
210
  ### Requirement: Existing Feature
211
+
189
212
  [Complete modified requirement]
190
213
 
191
214
  ## REMOVED Requirements
215
+
192
216
  ### Requirement: Old Feature
217
+
193
218
  **Reason**: [Why removing]
194
219
  **Migration**: [How to handle]
195
220
  ```
221
+
196
222
  If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
197
223
 
198
224
  4. **Create tasks.md:**
225
+
199
226
  ```markdown
200
227
  ## 1. Implementation
228
+
201
229
  - [ ] 1.1 Create database schema
202
230
  - [ ] 1.2 Implement API endpoint
203
231
  - [ ] 1.3 Add frontend component
@@ -205,32 +233,40 @@ If multiple capabilities are affected, create multiple delta files under `change
205
233
  ```
206
234
 
207
235
  5. **Create design.md when needed:**
208
- Create `design.md` if any of the following apply; otherwise omit it:
236
+ Create `design.md` if any of the following apply; otherwise omit it:
237
+
209
238
  - Cross-cutting change (multiple services/modules) or a new architectural pattern
210
239
  - New external dependency or significant data model changes
211
240
  - Security, performance, or migration complexity
212
241
  - Ambiguity that benefits from technical decisions before coding
213
242
 
214
243
  Minimal `design.md` skeleton:
244
+
215
245
  ```markdown
216
246
  ## Context
247
+
217
248
  [Background, constraints, stakeholders]
218
249
 
219
250
  ## Goals / Non-Goals
251
+
220
252
  - Goals: [...]
221
253
  - Non-Goals: [...]
222
254
 
223
255
  ## Decisions
256
+
224
257
  - Decision: [What and why]
225
258
  - Alternatives considered: [Options + rationale]
226
259
 
227
260
  ## Risks / Trade-offs
261
+
228
262
  - [Risk] → Mitigation
229
263
 
230
264
  ## Migration Plan
265
+
231
266
  [Steps, rollback]
232
267
 
233
268
  ## Open Questions
269
+
234
270
  - [...]
235
271
  ```
236
272
 
@@ -239,22 +275,27 @@ Minimal `design.md` skeleton:
239
275
  ### Critical: Scenario Formatting
240
276
 
241
277
  **CORRECT** (use #### headers):
278
+
242
279
  ```markdown
243
280
  #### Scenario: User login success
281
+
244
282
  - **WHEN** valid credentials provided
245
283
  - **THEN** return JWT token
246
284
  ```
247
285
 
248
286
  **WRONG** (don't use bullets or bold):
287
+
249
288
  ```markdown
250
- - **Scenario: User login**
251
- **Scenario**: User login
252
- ### Scenario: User login ❌
289
+ - **Scenario: User login**
290
+ **Scenario**: User login
291
+
292
+ ### Scenario: User login ❌
253
293
  ```
254
294
 
255
295
  Every requirement MUST have at least one scenario.
256
296
 
257
297
  ### Requirement Wording
298
+
258
299
  - Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
259
300
 
260
301
  ### Delta Operations
@@ -267,6 +308,7 @@ Every requirement MUST have at least one scenario.
267
308
  Headers matched with `trim(header)` - whitespace ignored.
268
309
 
269
310
  #### When to use ADDED vs MODIFIED
311
+
270
312
  - ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
271
313
  - MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
272
314
  - RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
@@ -274,14 +316,17 @@ Headers matched with `trim(header)` - whitespace ignored.
274
316
  Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.
275
317
 
276
318
  Authoring a MODIFIED requirement correctly:
277
- 1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
278
- 2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
279
- 3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
280
- 4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
319
+
320
+ 1. Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
321
+ 2. Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
322
+ 3. Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
323
+ 4. Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
281
324
 
282
325
  Example for RENAMED:
326
+
283
327
  ```markdown
284
328
  ## RENAMED Requirements
329
+
285
330
  - FROM: `### Requirement: Login`
286
331
  - TO: `### Requirement: User Authentication`
287
332
  ```
@@ -291,14 +336,17 @@ Example for RENAMED:
291
336
  ### Common Errors
292
337
 
293
338
  **"Change must have at least one delta"**
339
+
294
340
  - Check `changes/[name]/specs/` exists with .md files
295
341
  - Verify files have operation prefixes (## ADDED Requirements)
296
342
 
297
343
  **"Requirement must have at least one scenario"**
344
+
298
345
  - Check scenarios use `#### Scenario:` format (4 hashtags)
299
346
  - Don't use bullet points or bold for scenario headers
300
347
 
301
348
  **Silent scenario parsing failures**
349
+
302
350
  - Exact format required: `#### Scenario: Name`
303
351
  - Debug with: `openspec show [change] --json --deltas-only`
304
352
 
@@ -360,73 +408,88 @@ openspec/changes/add-2fa-notify/
360
408
  ```
361
409
 
362
410
  auth/spec.md
411
+
363
412
  ```markdown
364
413
  ## ADDED Requirements
414
+
365
415
  ### Requirement: Two-Factor Authentication
416
+
366
417
  ...
367
418
  ```
368
419
 
369
420
  notifications/spec.md
421
+
370
422
  ```markdown
371
423
  ## ADDED Requirements
424
+
372
425
  ### Requirement: OTP Email Notification
426
+
373
427
  ...
374
428
  ```
375
429
 
376
430
  ## Best Practices
377
431
 
378
432
  ### Simplicity First
433
+
379
434
  - Default to <100 lines of new code
380
435
  - Single-file implementations until proven insufficient
381
436
  - Avoid frameworks without clear justification
382
437
  - Choose boring, proven patterns
383
438
 
384
439
  ### Complexity Triggers
440
+
385
441
  Only add complexity with:
442
+
386
443
  - Performance data showing current solution too slow
387
444
  - Concrete scale requirements (>1000 users, >100MB data)
388
445
  - Multiple proven use cases requiring abstraction
389
446
 
390
447
  ### Clear References
448
+
391
449
  - Use `file.ts:42` format for code locations
392
450
  - Reference specs as `specs/auth/spec.md`
393
451
  - Link related changes and PRs
394
452
 
395
453
  ### Capability Naming
454
+
396
455
  - Use verb-noun: `user-auth`, `payment-capture`
397
456
  - Single purpose per capability
398
457
  - 10-minute understandability rule
399
458
  - Split if description needs "AND"
400
459
 
401
460
  ### Change ID Naming
461
+
402
462
  - Use kebab-case, short and descriptive: `add-two-factor-auth`
403
463
  - Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
404
464
  - Ensure uniqueness; if taken, append `-2`, `-3`, etc.
405
465
 
406
466
  ## Tool Selection Guide
407
467
 
408
- | Task | Tool | Why |
409
- |------|------|-----|
410
- | Find files by pattern | Glob | Fast pattern matching |
411
- | Search code content | Grep | Optimized regex search |
412
- | Read specific files | Read | Direct file access |
468
+ | Task | Tool | Why |
469
+ | --------------------- | ---- | ------------------------ |
470
+ | Find files by pattern | Glob | Fast pattern matching |
471
+ | Search code content | Grep | Optimized regex search |
472
+ | Read specific files | Read | Direct file access |
413
473
  | Explore unknown scope | Task | Multi-step investigation |
414
474
 
415
475
  ## Error Recovery
416
476
 
417
477
  ### Change Conflicts
478
+
418
479
  1. Run `openspec list` to see active changes
419
480
  2. Check for overlapping specs
420
481
  3. Coordinate with change owners
421
482
  4. Consider combining proposals
422
483
 
423
484
  ### Validation Failures
485
+
424
486
  1. Run with `--strict` flag
425
487
  2. Check JSON output for details
426
488
  3. Verify spec file format
427
489
  4. Ensure scenarios properly formatted
428
490
 
429
491
  ### Missing Context
492
+
430
493
  1. Read project.md first
431
494
  2. Check related specs
432
495
  3. Review recent archives
@@ -435,17 +498,20 @@ Only add complexity with:
435
498
  ## Quick Reference
436
499
 
437
500
  ### Stage Indicators
501
+
438
502
  - `changes/` - Proposed, not yet built
439
503
  - `specs/` - Built and deployed
440
504
  - `archive/` - Completed changes
441
505
 
442
506
  ### File Purposes
507
+
443
508
  - `proposal.md` - Why and what
444
509
  - `tasks.md` - Implementation steps
445
510
  - `design.md` - Technical decisions
446
511
  - `spec.md` - Requirements and behavior
447
512
 
448
513
  ### CLI Essentials
514
+
449
515
  ```bash
450
516
  openspec list # What's in progress?
451
517
  openspec show [item] # View details
@@ -1,14 +1,17 @@
1
1
  # Change: Add Concurrency Control
2
2
 
3
3
  ## Why
4
- The current implementation executes *all* independent tasks in parallel. In large graphs with many independent nodes, this could overwhelm system resources (CPU, memory) or trigger external API rate limits.
4
+
5
+ The current implementation executes _all_ independent tasks in parallel. In large graphs with many independent nodes, this could overwhelm system resources (CPU, memory) or trigger external API rate limits.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - Add `concurrency: number` optional property to `WorkflowExecutor`.
8
10
  - Modify `WorkflowExecutor.processLoop` to respect the concurrency limit.
9
- - Track the active promise count.
10
- - Only start new tasks from the ready queue if `active < concurrency`.
11
- - Continue to defer ready tasks until slots open up.
11
+ - Track the active promise count.
12
+ - Only start new tasks from the ready queue if `active < concurrency`.
13
+ - Continue to defer ready tasks until slots open up.
12
14
 
13
15
  ## Impact
16
+
14
17
  - **Affected Components**: `WorkflowExecutor`
@@ -0,0 +1,10 @@
1
+ ## Implementation
2
+
3
+ - [x] 1.1 Update `TaskRunnerExecutionConfig` to include an optional `concurrency` property.
4
+ - [x] 1.2 Update `WorkflowExecutor` to accept the `concurrency` limit.
5
+ - [x] 1.3 Implement a task queueing mechanism in `WorkflowExecutor` to manage pending tasks.
6
+ - [x] 1.4 Update execution logic to check available concurrency slots before starting a task.
7
+ - [x] 1.5 Ensure task completion triggers the execution of queued tasks.
8
+ - [x] 1.6 Verify that unlimited concurrency (default behavior) is preserved when no limit is set.
9
+ - [x] 1.7 Add unit tests for concurrency limits (e.g., ensure no more than N tasks run at once).
10
+ - [x] 1.8 Add integration tests with mixed dependencies and concurrency limits.
@@ -1,14 +1,17 @@
1
1
  # Change: Add External Task Cancellation
2
2
 
3
3
  ## Why
4
+
4
5
  Once `TaskRunner.execute()` is called, there is no way to cancel the operation externally. If a task hangs or if the user wants to abort the workflow (e.g., in a CLI or UI context), the runner continues until completion or failure. This limits the responsiveness and control users have over long-running operations.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - The `TaskRunner.execute()` method will be updated to accept an optional configuration object.
8
10
  - This configuration object will support an `AbortSignal` for external cancellation.
9
11
  - The configuration object will also support a global timeout for the entire workflow.
10
12
  - Tasks will be able to respond to cancellation signals and report their status accordingly.
11
13
 
12
14
  ## Impact
15
+
13
16
  - Affected specs: `001-generic-task-runner` (specifically the `TaskRunner`'s `execute` method behavior)
14
- - Affected code: `src/TaskRunner.ts`, `src/contracts/RunnerEvents.ts` (for cancellation events), potentially `src/TaskStep.ts` (to propagate `AbortSignal` to individual steps).
17
+ - Affected code: `src/TaskRunner.ts`, `src/contracts/RunnerEvents.ts` (for cancellation events), potentially `src/TaskStep.ts` (to propagate `AbortSignal` to individual steps).
@@ -1,4 +1,5 @@
1
1
  ## Implementation
2
+
2
3
  - [x] 1.1 Update `TaskRunner.execute` signature to accept an optional config object.
3
4
  - [x] 1.2 Implement logic within `TaskRunner` to listen for `AbortSignal` and initiate cancellation.
4
5
  - [x] 1.3 Implement global timeout mechanism using `AbortController` and `setTimeout`.
@@ -7,4 +8,4 @@
7
8
  - [x] 1.6 Update `TaskResult` and `RunnerEvents` to reflect cancellation status.
8
9
  - [x] 1.7 Add unit tests for `AbortSignal` cancellation scenarios.
9
10
  - [x] 1.8 Add unit tests for global timeout scenarios.
10
- - [x] 1.9 Add integration tests covering both `AbortSignal` and timeout interactions.
11
+ - [x] 1.9 Add integration tests covering both `AbortSignal` and timeout interactions.
@@ -1,9 +1,11 @@
1
1
  # Change: Add Comprehensive Integration Tests
2
2
 
3
3
  ## Why
4
+
4
5
  The current test suite relies heavily on unit tests and mocks. To ensure robust behavior in real-world scenarios, we need comprehensive integration tests that execute full task graphs without mocks, validating complex configurations and interactions.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - Create a dedicated `tests/integration-tests/` directory.
8
10
  - Implement 10-20 integration test scenarios covering:
9
11
  - Linear and branching dependencies.
@@ -14,5 +16,6 @@ The current test suite relies heavily on unit tests and mocks. To ensure robust
14
16
  - Error recovery (if retry policy is implemented, otherwise standard error states).
15
17
 
16
18
  ## Impact
19
+
17
20
  - Affected specs: `task-runner` (no functional changes to the runtime, but validates existing specs)
18
21
  - Affected code: `tests/integration-tests/*`
@@ -1,4 +1,5 @@
1
1
  ## Implementation
2
+
2
3
  - [x] 1.1 Setup `tests/integration-tests/` directory and test runner config if needed.
3
4
  - [x] 1.2 Implement Scenario 1: Basic linear workflow (A -> B -> C) success.
4
5
  - [x] 1.3 Implement Scenario 2: Branching workflow (A -> [B, C] -> D) success.
@@ -1,9 +1,11 @@
1
1
  # Change: Add Task Retry Policy
2
2
 
3
3
  ## Why
4
+
4
5
  Tasks currently run once and fail immediately if they throw an error or return a failure status. Network glitches or transient issues can therefore cause an entire workflow to fail unnecessarily.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - Add `TaskRetryConfig` interface to define retry behavior (attempts, delay, backoff).
8
10
  - Update `TaskStep` interface to include optional `retry: TaskRetryConfig`.
9
11
  - Implement `RetryingExecutionStrategy` which decorates any `IExecutionStrategy`.
@@ -12,5 +14,6 @@ Tasks currently run once and fail immediately if they throw an error or return a
12
14
  - It waits and re-executes the step if applicable.
13
15
 
14
16
  ## Impact
17
+
15
18
  - **New Components**: `RetryingExecutionStrategy`, `TaskRetryConfig`
16
19
  - **Affected Components**: `TaskStep` (interface update)
@@ -1,4 +1,5 @@
1
1
  ## Implementation
2
+
2
3
  - [x] 1.1 Create `TaskRetryConfig` interface with `attempts`, `delay`, and `backoff`.
3
4
  - [x] 1.2 Update `TaskStep` interface to include optional `retry: TaskRetryConfig`.
4
5
  - [x] 1.3 Update execution logic to catch task failures.
@@ -1,13 +1,16 @@
1
1
  # Change: Add Workflow Preview
2
2
 
3
3
  ## Why
4
+
4
5
  It can be difficult to understand the execution flow of complex dependency graphs just by looking at the code. Users also currently cannot easily verify the execution plan without running the side effects, which carries risk.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - Add a `DryRunExecutionStrategy` which implements `IExecutionStrategy`. This allows `WorkflowExecutor` to simulate execution without side effects.
8
10
  - Add a standalone utility `generateMermaidGraph(steps: TaskStep[])` to generate a Mermaid.js diagram of the dependency graph.
9
11
  - Expose these features via the main `TaskRunner` facade if applicable, or as separate utilities.
10
12
 
11
13
  ## Impact
14
+
12
15
  - **New Components**: `DryRunExecutionStrategy`, `generateMermaidGraph`
13
16
  - **Affected Components**: `WorkflowExecutor` (indirectly, via strategy injection)
@@ -1,4 +1,5 @@
1
1
  ## Implementation
2
+
2
3
  - [x] 1.1 Update `TaskRunnerExecutionConfig` to include an optional `dryRun: boolean` property.
3
4
  - [x] 1.2 Implement `dryRun` logic in `WorkflowExecutor` (traverse graph, validate order, skip `step.run()`, return `skipped` or `success` pseudo-status).
4
5
  - [x] 1.3 Implement `getMermaidGraph(steps: TaskStep[])` method (can be static or instance method on `TaskRunner`).
@@ -1,14 +1,17 @@
1
1
  # Change: Refactor Core Architecture
2
2
 
3
3
  ## Why
4
+
4
5
  When multiple developers work on the project, conflicts arise due to tight coupling and poor separation of concerns. Large classes like `WorkflowExecutor` are taking on too many responsibilities, and there is duplicated logic around graph traversal and state management.
5
6
 
6
7
  ## What Changes
8
+
7
9
  - Decouple `WorkflowExecutor` from `EventBus` (pass a listener interface or use a mediating controller).
8
10
  - Extract `TaskExecutionStrategy` to allow pluggable execution modes (e.g., standard, dry-run, debug).
9
11
  - Centralize state management for task results and context, moving it out of the executor loop.
10
12
  - Standardize error handling and logging (QoL improvements).
11
13
 
12
14
  ## Impact
15
+
13
16
  - Affected specs: `task-runner` (no behavior change, but structural refactor)
14
17
  - Affected code: `src/WorkflowExecutor.ts`, `src/TaskRunner.ts`, new files for extracted logic.
@@ -1,4 +1,5 @@
1
1
  ## Implementation
2
+
2
3
  - [x] 1.1 Extract `TaskStateManager` to handle `TaskResult` storage, updates, and context mutations.
3
4
  - [x] 1.2 Define `IExecutionStrategy` interface for running tasks (Strategy Pattern).
4
5
  - [x] 1.3 Refactor `WorkflowExecutor` to use `TaskStateManager` and `IExecutionStrategy`.
@@ -1,25 +1,30 @@
1
1
  # Feature: Per-Task Timeout
2
2
 
3
3
  ## 🎯 User Story
4
+
4
5
  "As a developer, I want to define a maximum execution time for specific tasks so that a single hung task (e.g., a stalled network request) fails fast without blocking the rest of the independent tasks or waiting for the global workflow timeout."
5
6
 
6
7
  ## ❓ Why
8
+
7
9
  Currently, the `TaskRunner` allows a **global** timeout for the entire `execute()` call. However, this is insufficient for granular control:
10
+
8
11
  1. **Varying Latency**: Some tasks are expected to be fast (local validation), others slow (data fetching). A global timeout of 30s is too loose for the fast ones.
9
12
  2. **Boilerplate**: Developers currently have to manually implement `setTimeout`, `Promise.race`, and `AbortController` logic inside every `run()` method to handle timeouts properly.
10
13
  3. **Resilience**: A single "zombie" task can hold up the entire pipeline until the global timeout kills everything. Per-task timeouts allow failing that specific task (and skipping its dependents) while letting other independent tasks continue.
11
14
 
12
15
  ## 🛠️ What Changes
16
+
13
17
  1. **Interface Update**: Update `TaskStep<T>` to accept an optional `timeout` property (in milliseconds).
14
18
  2. **Execution Strategy**: Update `StandardExecutionStrategy` to:
15
- - Create a local timeout timer for the task.
16
- - Create a combined `AbortSignal` (merging the workflow's signal and the local timeout).
17
- - Race the task execution against the timer.
18
- - Return a specific failure result if the timeout wins.
19
+ - Create a local timeout timer for the task.
20
+ - Create a combined `AbortSignal` (merging the workflow's signal and the local timeout).
21
+ - Race the task execution against the timer.
22
+ - Return a specific failure result if the timeout wins.
19
23
 
20
24
  ## ✅ Acceptance Criteria
25
+
21
26
  - [ ] A task with `timeout: 100` must fail if the `run` method takes > 100ms.
22
27
  - [ ] The error message for a timed-out task should clearly state "Task timed out after 100ms".
23
28
  - [ ] The `AbortSignal` passed to the task's `run` method must be triggered when the timeout occurs.
24
- - [ ] If the Global Workflow is cancelled *before* the task times out, the task should receive the cancellation signal immediately.
25
- - [ ] A task completing *before* the timeout should clear the timer to prevent open handles.
29
+ - [ ] If the Global Workflow is cancelled _before_ the task times out, the task should receive the cancellation signal immediately.
30
+ - [ ] A task completing _before_ the timeout should clear the timer to prevent open handles.
@@ -11,7 +11,7 @@
11
11
  - If yes, create an `AbortController`.
12
12
  - Set a `setTimeout` to trigger the controller.
13
13
  - Use `Promise.race` (or simply pass the new signal and wait) to handle the timeout.
14
- - **Crucial**: Ensure the new signal respects the *parent* `signal` (if global cancel happens, local signal must also abort).
14
+ - **Crucial**: Ensure the new signal respects the _parent_ `signal` (if global cancel happens, local signal must also abort).
15
15
  - **Crucial**: Clean up the timer (`clearTimeout`) in a `finally` block.
16
16
 
17
17
  - [ ] **Task 3: Unit Tests**
@@ -1,32 +1,38 @@
1
1
  # Project: Task Runner
2
2
 
3
3
  ## Overview
4
+
4
5
  The 'task-runner' project is a TypeScript-based utility designed to manage and execute tasks. It incorporates features such as task cancellation, pre-execution validation, and concurrency control, providing a robust framework for workflow automation.
5
6
 
6
7
  ## Tech Stack
8
+
7
9
  - **Languages:** TypeScript 5.9.3
8
10
  - **Testing:** Vitest 4.0.17
9
11
  - **Core APIs:** AbortSignal/AbortController (Standard Web APIs for cancellation)
10
12
  - **Package Manager:** pnpm
11
13
 
12
14
  ## Architecture
15
+
13
16
  The project follows a modular architecture with distinct components for managing different aspects of task execution:
14
- - `EventBus.ts`: Handles event propagation within the system.
15
- - `TaskGraph.ts`: Represents the structure and dependencies of tasks.
16
- - `TaskGraphValidator.ts`: Ensures the validity of task graphs before execution.
17
- - `TaskRunner.ts`: Orchestrates the execution of tasks.
18
- - `WorkflowExecutor.ts`: Manages the overall workflow.
19
- - `contracts/`: Defines interfaces and types for various components, promoting loose coupling and clear API boundaries.
17
+
18
+ - `EventBus.ts`: Handles event propagation within the system.
19
+ - `TaskGraph.ts`: Represents the structure and dependencies of tasks.
20
+ - `TaskGraphValidator.ts`: Ensures the validity of task graphs before execution.
21
+ - `TaskRunner.ts`: Orchestrates the execution of tasks.
22
+ - `WorkflowExecutor.ts`: Manages the overall workflow.
23
+ - `contracts/`: Defines interfaces and types for various components, promoting loose coupling and clear API boundaries.
20
24
 
21
25
  ## Conventions
22
- - **Coding Style:** Adheres to standard TypeScript conventions, enforced by ESLint and Prettier.
23
- - **Commit Messages:** Follows conventional commits enforced by Commitlint.
24
- - **Git Hooks:** Utilizes Husky for pre-commit and commit-msg hooks.
25
- - **Testing:** Uses Vitest for unit and integration testing.
26
- - **Atomic Commits:** When working on complex multi-task features, commit after each distinct task, ensuring build, lint, and test success to establish safe rollback points.
26
+
27
+ - **Coding Style:** Adheres to standard TypeScript conventions, enforced by ESLint and Prettier.
28
+ - **Commit Messages:** Follows conventional commits enforced by Commitlint.
29
+ - **Git Hooks:** Utilizes Husky for pre-commit and commit-msg hooks.
30
+ - **Testing:** Uses Vitest for unit and integration testing.
31
+ - **Atomic Commits:** When working on complex multi-task features, commit after each distinct task, ensuring build, lint, and test success to establish safe rollback points.
27
32
 
28
33
  ## Build/Test/Run Commands
29
- - **Install Dependencies:** `pnpm install`
30
- - **Build Project:** `pnpm build`
31
- - **Run Tests:** `pnpm test`
32
- - **Lint Code:** `pnpm lint`
34
+
35
+ - **Install Dependencies:** `pnpm install`
36
+ - **Build Project:** `pnpm build`
37
+ - **Run Tests:** `pnpm test`
38
+ - **Lint Code:** `pnpm lint`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calmo/task-runner",
3
- "version": "3.3.0",
3
+ "version": "3.4.1",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",