@calmo/task-runner 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +7 -7
- package/.github/workflows/ci.yml +4 -4
- package/.jules/backlog_maniac.md +1 -0
- package/.jules/nexus.md +1 -0
- package/.jules/sentinel.md +1 -0
- package/.releaserc.json +2 -7
- package/AGENTS.md +21 -16
- package/CHANGELOG.md +192 -174
- package/README.md +95 -88
- package/coverage/coverage-final.json +9 -9
- package/coverage/index.html +9 -9
- package/coverage/lcov-report/index.html +9 -9
- package/coverage/lcov-report/src/EventBus.ts.html +30 -24
- package/coverage/lcov-report/src/TaskGraphValidationError.ts.html +12 -3
- package/coverage/lcov-report/src/TaskGraphValidator.ts.html +152 -137
- package/coverage/lcov-report/src/TaskRunner.ts.html +48 -45
- package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +29 -5
- package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +1 -1
- package/coverage/lcov-report/src/TaskStateManager.ts.html +82 -52
- package/coverage/lcov-report/src/WorkflowExecutor.ts.html +210 -66
- package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/lcov-report/src/contracts/index.html +1 -1
- package/coverage/lcov-report/src/index.html +16 -16
- package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +4 -4
- package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +29 -11
- package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +7 -7
- package/coverage/lcov-report/src/strategies/index.html +1 -1
- package/coverage/lcov.info +426 -383
- package/coverage/src/EventBus.ts.html +30 -24
- package/coverage/src/TaskGraphValidationError.ts.html +12 -3
- package/coverage/src/TaskGraphValidator.ts.html +152 -137
- package/coverage/src/TaskRunner.ts.html +48 -45
- package/coverage/src/TaskRunnerBuilder.ts.html +29 -5
- package/coverage/src/TaskRunnerExecutionConfig.ts.html +1 -1
- package/coverage/src/TaskStateManager.ts.html +82 -52
- package/coverage/src/WorkflowExecutor.ts.html +210 -66
- package/coverage/src/contracts/RunnerEvents.ts.html +1 -1
- package/coverage/src/contracts/index.html +1 -1
- package/coverage/src/index.html +16 -16
- package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +4 -4
- package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +29 -11
- package/coverage/src/strategies/StandardExecutionStrategy.ts.html +7 -7
- package/coverage/src/strategies/index.html +1 -1
- package/dist/EventBus.js +13 -11
- package/dist/EventBus.js.map +1 -1
- package/dist/TaskGraphValidationError.js.map +1 -1
- package/dist/TaskGraphValidator.js +9 -9
- package/dist/TaskGraphValidator.js.map +1 -1
- package/dist/TaskRunner.js.map +1 -1
- package/dist/TaskRunnerBuilder.js.map +1 -1
- package/dist/TaskStateManager.d.ts +6 -0
- package/dist/TaskStateManager.js +11 -2
- package/dist/TaskStateManager.js.map +1 -1
- package/dist/TaskStep.d.ts +5 -0
- package/dist/WorkflowExecutor.js +49 -7
- package/dist/WorkflowExecutor.js.map +1 -1
- package/dist/strategies/RetryingExecutionStrategy.js +3 -1
- package/dist/strategies/RetryingExecutionStrategy.js.map +1 -1
- package/dist/strategies/StandardExecutionStrategy.js +1 -1
- package/dist/strategies/StandardExecutionStrategy.js.map +1 -1
- package/openspec/AGENTS.md +81 -15
- package/openspec/changes/archive/2026-01-18-add-concurrency-control/proposal.md +7 -4
- package/openspec/changes/archive/2026-01-18-add-concurrency-control/tasks.md +1 -0
- package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/proposal.md +4 -1
- package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/tasks.md +2 -1
- package/openspec/changes/archive/2026-01-18-add-integration-tests/proposal.md +3 -0
- package/openspec/changes/archive/2026-01-18-add-integration-tests/tasks.md +1 -0
- package/openspec/changes/archive/2026-01-18-add-task-retry-policy/proposal.md +3 -0
- package/openspec/changes/archive/2026-01-18-add-task-retry-policy/tasks.md +1 -0
- package/openspec/changes/archive/2026-01-18-add-workflow-preview/proposal.md +3 -0
- package/openspec/changes/archive/2026-01-18-add-workflow-preview/tasks.md +1 -0
- package/openspec/changes/archive/2026-01-18-feat-conditional-execution/proposal.md +35 -0
- package/openspec/changes/archive/2026-01-18-feat-conditional-execution/tasks.md +32 -0
- package/openspec/changes/archive/2026-01-18-refactor-core-architecture/proposal.md +3 -0
- package/openspec/changes/archive/2026-01-18-refactor-core-architecture/tasks.md +1 -0
- package/openspec/changes/feat-per-task-timeout/proposal.md +11 -6
- package/openspec/changes/feat-per-task-timeout/tasks.md +1 -1
- package/openspec/project.md +21 -15
- package/package.json +2 -1
- package/src/EventBus.ts +18 -16
- package/src/TaskGraph.ts +8 -8
- package/src/TaskGraphValidationError.ts +4 -1
- package/src/TaskGraphValidator.ts +148 -143
- package/src/TaskRunner.ts +42 -41
- package/src/TaskRunnerBuilder.ts +11 -3
- package/src/TaskStateManager.ts +12 -2
- package/src/TaskStep.ts +6 -0
- package/src/WorkflowExecutor.ts +63 -15
- package/src/contracts/ITaskGraphValidator.ts +12 -12
- package/src/contracts/ValidationError.ts +6 -6
- package/src/contracts/ValidationResult.ts +4 -4
- package/src/strategies/DryRunExecutionStrategy.ts +3 -3
- package/src/strategies/RetryingExecutionStrategy.ts +15 -9
- package/src/strategies/StandardExecutionStrategy.ts +4 -4
- package/test-report.xml +132 -108
package/openspec/AGENTS.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
|
409
|
-
|
|
410
|
-
| Find files by pattern | Glob | Fast pattern matching
|
|
411
|
-
| Search code content
|
|
412
|
-
| Read specific files
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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`
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
## Implementation
|
|
2
|
+
|
|
2
3
|
- [x] 1.1 Update `TaskRunnerExecutionConfig` to include an optional `concurrency` property.
|
|
3
4
|
- [x] 1.2 Update `WorkflowExecutor` to accept the `concurrency` limit.
|
|
4
5
|
- [x] 1.3 Implement a task queueing mechanism in `WorkflowExecutor` to manage pending tasks.
|
|
@@ -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,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,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`).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Feature: Conditional Task Execution
|
|
2
|
+
|
|
3
|
+
## 🎯 User Story
|
|
4
|
+
|
|
5
|
+
"As a developer, I want to define a condition for a task so that it only executes when specific criteria are met (e.g., only on CI, or only if a previous task generated a specific output), avoiding unnecessary execution and manual checks inside the task logic."
|
|
6
|
+
|
|
7
|
+
## ❓ Why
|
|
8
|
+
|
|
9
|
+
Currently, to skip a task based on context, a developer must implement the check inside the `run` method and return `{ status: 'skipped' }`. This has downsides:
|
|
10
|
+
|
|
11
|
+
1. **Late Binding**: The task is already "started" (event emitted) before it decides to skip itself.
|
|
12
|
+
2. **Boilerplate**: Every task needs `if (!shouldRun) return { status: 'skipped' }`.
|
|
13
|
+
3. **Clarity**: Examining the task definition (e.g. `TaskStep` object) doesn't reveal *when* it runs, only *what* it does. A declarative `condition` property makes the workflow logic more transparent.
|
|
14
|
+
4. **Dry Run Accuracy**: In a dry run, we might want to know if a task *would* run. If the logic is inside `run`, strictly disjoint from the runner, a dry run (which skips `run`) cannot predict if the task would be skipped.
|
|
15
|
+
|
|
16
|
+
## 🛠️ What Changes
|
|
17
|
+
|
|
18
|
+
1. **Interface Update**: Update `TaskStep<T>` to accept an optional `condition` property:
|
|
19
|
+
```typescript
|
|
20
|
+
condition?: (context: T) => boolean | Promise<boolean>;
|
|
21
|
+
```
|
|
22
|
+
2. **State Manager/Workflow Executor Update**:
|
|
23
|
+
- Before marking a task as `running`, evaluate `condition(context)`.
|
|
24
|
+
- If `false`, mark the task as `skipped` immediately without calling `run()`.
|
|
25
|
+
- Emit `taskSkipped` event.
|
|
26
|
+
- Consistency: Ensure that skipping a task triggers the standard skip propagation for dependent tasks (as currently documented in README).
|
|
27
|
+
|
|
28
|
+
## ✅ Acceptance Criteria
|
|
29
|
+
|
|
30
|
+
- [ ] A task with `condition: () => false` must result in a `skipped` status.
|
|
31
|
+
- [ ] The `run` method of a conditionally skipped task must **not** be called.
|
|
32
|
+
- [ ] A task with `condition: () => true` (or undefined) must execute normally.
|
|
33
|
+
- [ ] The `condition` function receives the current `context`.
|
|
34
|
+
- [ ] Async `condition` functions are awaited.
|
|
35
|
+
- [ ] `taskSkipped` event is emitted when condition fails.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Engineering Tasks
|
|
2
|
+
|
|
3
|
+
- [ ] **Task 1: Update Interface**
|
|
4
|
+
- Modify `src/TaskStep.ts` to add `condition?: (context: T) => boolean | Promise<boolean>;` to the `TaskStep` interface.
|
|
5
|
+
- Add JSDoc to explain that if condition returns false, the task is skipped.
|
|
6
|
+
|
|
7
|
+
- [ ] **Task 2: Implement Conditional Logic in TaskStateManager**
|
|
8
|
+
- Analyze `src/TaskStateManager.ts` and `src/WorkflowExecutor.ts`.
|
|
9
|
+
- Determine the best place to evaluate the condition. Likely in `WorkflowExecutor` before calling `strategy.execute`, OR in `TaskStateManager` when processing dependencies?
|
|
10
|
+
- Actually, `WorkflowExecutor.processLoop` calls `stateManager.markRunning(step)`.
|
|
11
|
+
- It should probably be:
|
|
12
|
+
```typescript
|
|
13
|
+
const shouldRun = await evaluateCondition(step, context);
|
|
14
|
+
if (!shouldRun) {
|
|
15
|
+
stateManager.markResult(step, { status: 'skipped' });
|
|
16
|
+
// emit skipped event
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
stateManager.markRunning(step);
|
|
20
|
+
strategy.execute(...)
|
|
21
|
+
```
|
|
22
|
+
- Note: `evaluateCondition` needs to handle both sync and async results.
|
|
23
|
+
|
|
24
|
+
- [ ] **Task 3: Unit Tests**
|
|
25
|
+
- Create `tests/conditional.test.ts` (or add to `tests/TaskRunner.test.ts`).
|
|
26
|
+
- Test: Task with condition `() => false` is skipped.
|
|
27
|
+
- Test: Task with condition `() => true` runs.
|
|
28
|
+
- Test: Task with async condition skipping.
|
|
29
|
+
- Test: Dependent tasks are also skipped (or handle skip propagation correctly).
|
|
30
|
+
|
|
31
|
+
- [ ] **Task 4: Verify Dry Run Behavior**
|
|
32
|
+
- Ensure Dry Run strategy still respects condition if possible, OR clarify in spec that condition is evaluated normally even in dry run (since it's a predicate, not side-effect).
|
|
@@ -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
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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
|
|
25
|
-
- [ ] A task completing
|
|
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
|
|
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**
|