@calmo/task-runner 3.8.4 → 4.1.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/.agent/workflows/openspec-apply.md +20 -0
- package/.agent/workflows/openspec-archive.md +24 -0
- package/.agent/workflows/openspec-proposal.md +25 -0
- package/.github/workflows/codeql.yml +1 -1
- package/.github/workflows/release-please.yml +46 -0
- package/.husky/commit-msg +0 -0
- package/.husky/pre-commit +0 -0
- package/.jules/nexus.md +5 -0
- package/.release-please-manifest.json +3 -0
- package/AGENTS.md +1 -4
- package/CHANGELOG.md +123 -0
- package/CODE_OF_CONDUCT.md +131 -0
- package/CONTRIBUTING.md +89 -0
- package/README.md +1 -1
- package/dist/TaskResult.d.ts +9 -0
- package/dist/TaskRunner.js +47 -34
- package/dist/TaskRunner.js.map +1 -1
- package/dist/TaskStateManager.d.ts +22 -6
- package/dist/TaskStateManager.js +101 -45
- package/dist/TaskStateManager.js.map +1 -1
- package/dist/WorkflowExecutor.js +17 -10
- package/dist/WorkflowExecutor.js.map +1 -1
- package/dist/strategies/DryRunExecutionStrategy.d.ts +1 -1
- package/dist/strategies/DryRunExecutionStrategy.js +2 -4
- package/dist/strategies/DryRunExecutionStrategy.js.map +1 -1
- package/dist/utils/PriorityQueue.d.ts +13 -0
- package/dist/utils/PriorityQueue.js +82 -0
- package/dist/utils/PriorityQueue.js.map +1 -0
- package/openspec/changes/add-resource-concurrency/proposal.md +18 -0
- package/openspec/changes/add-resource-concurrency/specs/task-runner/spec.md +25 -0
- package/openspec/changes/add-resource-concurrency/tasks.md +9 -0
- package/openspec/changes/archive/2026-01-18-add-concurrency-control/specs/task-runner/spec.md +26 -0
- package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/specs/task-runner/spec.md +63 -0
- package/openspec/changes/archive/2026-01-18-add-integration-tests/specs/task-runner/spec.md +22 -0
- package/openspec/changes/archive/2026-01-18-add-task-retry-policy/specs/task-runner/spec.md +40 -0
- package/openspec/changes/archive/2026-01-18-add-workflow-preview/specs/task-runner/spec.md +25 -0
- package/openspec/changes/archive/2026-01-18-refactor-core-architecture/specs/task-runner/spec.md +31 -0
- package/openspec/changes/archive/2026-01-22-adopt-release-pr/design.md +40 -0
- package/openspec/changes/archive/2026-01-22-adopt-release-pr/proposal.md +47 -0
- package/openspec/changes/archive/2026-01-22-adopt-release-pr/specs/release-pr/spec.md +34 -0
- package/openspec/changes/archive/2026-01-22-adopt-release-pr/tasks.md +14 -0
- package/openspec/changes/{feat-task-metrics → archive/2026-01-22-feat-task-metrics}/proposal.md +1 -1
- package/openspec/changes/archive/2026-01-22-feat-task-metrics/specs/001-generic-task-runner/spec.md +13 -0
- package/openspec/changes/archive/2026-01-22-feat-task-metrics/tasks.md +6 -0
- package/openspec/changes/feat-conditional-retries/proposal.md +18 -0
- package/openspec/changes/feat-conditional-retries/specs/task-runner/spec.md +23 -0
- package/openspec/changes/feat-conditional-retries/tasks.md +37 -0
- package/openspec/changes/feat-per-task-timeout/specs/task-runner/spec.md +34 -0
- package/openspec/changes/feat-state-persistence/specs/task-runner/spec.md +47 -0
- package/openspec/specs/release-pr/spec.md +31 -0
- package/openspec/specs/task-runner/spec.md +174 -0
- package/package.json +11 -20
- package/release-please-config.json +9 -0
- package/src/TaskResult.ts +9 -0
- package/src/TaskRunner.ts +55 -36
- package/src/TaskStateManager.ts +114 -46
- package/src/WorkflowExecutor.ts +21 -11
- package/src/strategies/DryRunExecutionStrategy.ts +2 -3
- package/src/utils/PriorityQueue.ts +101 -0
- package/.gemini/commands/speckit.analyze.toml +0 -188
- package/.gemini/commands/speckit.checklist.toml +0 -298
- package/.gemini/commands/speckit.clarify.toml +0 -185
- package/.gemini/commands/speckit.constitution.toml +0 -86
- package/.gemini/commands/speckit.implement.toml +0 -139
- package/.gemini/commands/speckit.plan.toml +0 -93
- package/.gemini/commands/speckit.specify.toml +0 -262
- package/.gemini/commands/speckit.tasks.toml +0 -141
- package/.gemini/commands/speckit.taskstoissues.toml +0 -34
- package/.github/workflows/release.yml +0 -46
- package/.releaserc.json +0 -27
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -15
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -146
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -146
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/EventBus.ts.html +0 -379
- package/coverage/lcov-report/src/ExecutionConstants.ts.html +0 -121
- package/coverage/lcov-report/src/TaskGraphValidationError.ts.html +0 -130
- package/coverage/lcov-report/src/TaskGraphValidator.ts.html +0 -643
- package/coverage/lcov-report/src/TaskRunner.ts.html +0 -706
- package/coverage/lcov-report/src/TaskRunnerBuilder.ts.html +0 -337
- package/coverage/lcov-report/src/TaskRunnerExecutionConfig.ts.html +0 -154
- package/coverage/lcov-report/src/TaskStateManager.ts.html +0 -529
- package/coverage/lcov-report/src/WorkflowExecutor.ts.html +0 -712
- package/coverage/lcov-report/src/contracts/ErrorTypes.ts.html +0 -103
- package/coverage/lcov-report/src/contracts/RunnerEvents.ts.html +0 -217
- package/coverage/lcov-report/src/contracts/index.html +0 -131
- package/coverage/lcov-report/src/index.html +0 -236
- package/coverage/lcov-report/src/strategies/DryRunExecutionStrategy.ts.html +0 -178
- package/coverage/lcov-report/src/strategies/RetryingExecutionStrategy.ts.html +0 -373
- package/coverage/lcov-report/src/strategies/StandardExecutionStrategy.ts.html +0 -190
- package/coverage/lcov-report/src/strategies/index.html +0 -146
- package/coverage/lcov.info +0 -671
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/EventBus.ts.html +0 -379
- package/coverage/src/ExecutionConstants.ts.html +0 -121
- package/coverage/src/TaskGraphValidationError.ts.html +0 -130
- package/coverage/src/TaskGraphValidator.ts.html +0 -643
- package/coverage/src/TaskRunner.ts.html +0 -706
- package/coverage/src/TaskRunnerBuilder.ts.html +0 -337
- package/coverage/src/TaskRunnerExecutionConfig.ts.html +0 -154
- package/coverage/src/TaskStateManager.ts.html +0 -529
- package/coverage/src/WorkflowExecutor.ts.html +0 -712
- package/coverage/src/contracts/ErrorTypes.ts.html +0 -103
- package/coverage/src/contracts/RunnerEvents.ts.html +0 -217
- package/coverage/src/contracts/index.html +0 -131
- package/coverage/src/index.html +0 -236
- package/coverage/src/strategies/DryRunExecutionStrategy.ts.html +0 -178
- package/coverage/src/strategies/RetryingExecutionStrategy.ts.html +0 -373
- package/coverage/src/strategies/StandardExecutionStrategy.ts.html +0 -190
- package/coverage/src/strategies/index.html +0 -146
- package/openspec/changes/feat-task-metrics/tasks.md +0 -6
- package/test-report.xml +0 -299
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Resource-Based Concurrency Control
|
|
4
|
+
|
|
5
|
+
The system SHALL support limiting concurrency based on abstract resources defined by tasks.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Task defines resource usage
|
|
8
|
+
- **GIVEN** a `TaskStep` with `resources: { "api_call": 1 }`
|
|
9
|
+
- **WHEN** the task is executed
|
|
10
|
+
- **THEN** the system SHALL account for 1 unit of "api_call" consumption.
|
|
11
|
+
|
|
12
|
+
#### Scenario: Execution limited by resource availability
|
|
13
|
+
- **GIVEN** `resourceLimits` is configured with `{ "db_connection": 2 }`
|
|
14
|
+
- **AND** 3 tasks are ready, each requiring 1 "db_connection"
|
|
15
|
+
- **WHEN** execution proceeds
|
|
16
|
+
- **THEN** only 2 tasks SHALL execute concurrently.
|
|
17
|
+
- **AND** the 3rd task SHALL wait until resources are released.
|
|
18
|
+
|
|
19
|
+
#### Scenario: Global and Resource limits combined
|
|
20
|
+
- **GIVEN** `concurrency` is set to 5
|
|
21
|
+
- **AND** `resourceLimits` is `{ "heavy_job": 2 }`
|
|
22
|
+
- **AND** 10 tasks are ready: 5 "heavy_job" tasks and 5 "light" tasks (no resources)
|
|
23
|
+
- **WHEN** execution proceeds
|
|
24
|
+
- **THEN** at most 2 "heavy_job" tasks SHALL run.
|
|
25
|
+
- **AND** up to 3 "light" tasks MAY run concurrently (totaling 5).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## 1. Implementation
|
|
2
|
+
|
|
3
|
+
- [ ] 1.1 Update `TaskStep` interface in `src/TaskStep.ts` to include `resources?: Record<string, number>`.
|
|
4
|
+
- [ ] 1.2 Update `TaskRunnerExecutionConfig` in `src/TaskRunnerExecutionConfig.ts` to include `resourceLimits?: Record<string, number>`.
|
|
5
|
+
- [ ] 1.3 Update `WorkflowExecutor` to track active resource usage.
|
|
6
|
+
- [ ] 1.4 Update `WorkflowExecutor.processLoop` to validate resource availability before scheduling tasks.
|
|
7
|
+
- [ ] 1.5 Update `WorkflowExecutor.executeTaskStep` (or equivalent completion logic) to release resources when a task finishes.
|
|
8
|
+
- [ ] 1.6 Add unit tests for resource limiting in `tests/WorkflowExecutor.test.ts`.
|
|
9
|
+
- [ ] 1.7 Add integration test for mixed resource usage in `tests/integration/resource-concurrency.test.ts`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Concurrency Limit
|
|
4
|
+
|
|
5
|
+
The `WorkflowExecutor` SHALL limit the number of concurrently executing tasks if a `concurrency` limit is provided.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Concurrency limit applied
|
|
8
|
+
|
|
9
|
+
- **GIVEN** a `WorkflowExecutor` configured with `concurrency: N`
|
|
10
|
+
- **WHEN** multiple independent tasks become ready
|
|
11
|
+
- **THEN** no more than N tasks SHALL be in the 'running' state simultaneously.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Queueing ready tasks
|
|
14
|
+
|
|
15
|
+
- **WHEN** the number of running tasks equals the `concurrency` limit
|
|
16
|
+
- **THEN** any additional tasks that become ready SHALL remain in the 'ready' state until a slot becomes available.
|
|
17
|
+
|
|
18
|
+
#### Scenario: Slot release
|
|
19
|
+
|
|
20
|
+
- **WHEN** a running task completes or fails
|
|
21
|
+
- **THEN** the loop SHALL immediately check for ready tasks and start them up to the limit.
|
|
22
|
+
|
|
23
|
+
#### Scenario: Default unlimited concurrency
|
|
24
|
+
|
|
25
|
+
- **WHEN** `WorkflowExecutor` is initialized without a `concurrency` option (or 0/undefined)
|
|
26
|
+
- **THEN** it SHALL execute all ready tasks immediately (unlimited concurrency).
|
package/openspec/changes/archive/2026-01-18-add-external-task-cancellation/specs/task-runner/spec.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: TaskRunner Execution
|
|
4
|
+
|
|
5
|
+
The `TaskRunner` SHALL execute a sequence of `TaskStep`s based on their dependencies, processing inputs and producing outputs.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Successful execution
|
|
8
|
+
|
|
9
|
+
- **WHEN** all `TaskStep`s complete successfully
|
|
10
|
+
- **THEN** the `TaskRunner` returns a successful workflow result.
|
|
11
|
+
|
|
12
|
+
#### Scenario: Execution with AbortSignal
|
|
13
|
+
|
|
14
|
+
- **WHEN** `TaskRunner.execute` is called with an `AbortSignal`
|
|
15
|
+
- **THEN** the `TaskRunner` monitors the `AbortSignal` for cancellation requests.
|
|
16
|
+
|
|
17
|
+
#### Scenario: Execution with Global Timeout
|
|
18
|
+
|
|
19
|
+
- **WHEN** `TaskRunner.execute` is called with a `timeout` option
|
|
20
|
+
- **THEN** the `TaskRunner` monitors the elapsed time for the workflow.
|
|
21
|
+
|
|
22
|
+
### Requirement: External Workflow Cancellation
|
|
23
|
+
|
|
24
|
+
The `TaskRunner` SHALL allow external cancellation of an ongoing workflow.
|
|
25
|
+
|
|
26
|
+
#### Scenario: Workflow cancelled by AbortSignal
|
|
27
|
+
|
|
28
|
+
- **WHEN** an `AbortSignal` provided to `TaskRunner.execute` is triggered
|
|
29
|
+
- **THEN** the `TaskRunner` immediately attempts to stop execution of current and pending tasks.
|
|
30
|
+
|
|
31
|
+
#### Scenario: Workflow cancelled by Global Timeout
|
|
32
|
+
|
|
33
|
+
- **WHEN** the specified global `timeout` for `TaskRunner.execute` is reached
|
|
34
|
+
- **THEN** the `TaskRunner` immediately attempts to stop execution of current and pending tasks.
|
|
35
|
+
|
|
36
|
+
#### Scenario: Tasks marked as cancelled
|
|
37
|
+
|
|
38
|
+
- **WHEN** a workflow is cancelled (by `AbortSignal` or `timeout`)
|
|
39
|
+
- **THEN** all unexecuted `TaskStep`s SHALL be marked with a 'cancelled' status in the final result.
|
|
40
|
+
|
|
41
|
+
#### Scenario: Pre-aborted workflow
|
|
42
|
+
|
|
43
|
+
- **WHEN** `TaskRunner.execute` is called with an `AbortSignal` that is already aborted
|
|
44
|
+
- **THEN** the `TaskRunner` SHALL return immediately with all tasks marked as cancelled, without executing any steps.
|
|
45
|
+
|
|
46
|
+
#### Scenario: Graceful interruption of current task
|
|
47
|
+
|
|
48
|
+
- **WHEN** a workflow is cancelled and a `TaskStep` is currently executing
|
|
49
|
+
- **THEN** the `TaskStep` SHALL receive the cancellation signal (e.g., via `AbortSignal` context) to allow for graceful interruption.
|
|
50
|
+
|
|
51
|
+
### Requirement: Cancellation Conflict Resolution
|
|
52
|
+
|
|
53
|
+
The `TaskRunner` SHALL handle scenarios where both `AbortSignal` and global `timeout` are provided.
|
|
54
|
+
|
|
55
|
+
#### Scenario: AbortSignal precedes Timeout
|
|
56
|
+
|
|
57
|
+
- **WHEN** both `AbortSignal` and `timeout` are provided, and `AbortSignal` is triggered first
|
|
58
|
+
- **THEN** the `TaskRunner` SHALL cancel the workflow based on the `AbortSignal`, ignoring the `timeout`.
|
|
59
|
+
|
|
60
|
+
#### Scenario: Timeout precedes AbortSignal
|
|
61
|
+
|
|
62
|
+
- **WHEN** both `AbortSignal` and `timeout` are provided, and `timeout` is reached first
|
|
63
|
+
- **THEN** the `TaskRunner` SHALL cancel the workflow based on the `timeout`, ignoring the `AbortSignal`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Integration Verification
|
|
4
|
+
|
|
5
|
+
The system's integrity SHALL be verified through comprehensive integration scenarios executed against the real runtime environment without mocks.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Complex Graph Execution
|
|
8
|
+
|
|
9
|
+
- **WHEN** a complex task graph (diamonds, sequences, parallel branches) is executed
|
|
10
|
+
- **THEN** the system SHALL respect all dependency constraints and execution orders.
|
|
11
|
+
- **AND** the final state MUST reflect the cumulative side effects of all successful tasks.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Failure Propagation
|
|
14
|
+
|
|
15
|
+
- **WHEN** a task fails in a complex graph
|
|
16
|
+
- **THEN** ONLY dependent tasks SHALL be skipped
|
|
17
|
+
- **AND** independent branches SHALL continue to execute to completion.
|
|
18
|
+
|
|
19
|
+
#### Scenario: Context Integrity
|
|
20
|
+
|
|
21
|
+
- **WHEN** multiple tasks mutate the shared context
|
|
22
|
+
- **THEN** state changes MUST be propagated correctly to downstream tasks.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Task Retry Configuration
|
|
4
|
+
|
|
5
|
+
The `TaskStep` interface SHALL support an optional `retry` property of type `TaskRetryConfig`.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Retry Config Structure
|
|
8
|
+
|
|
9
|
+
- **GIVEN** a `TaskRetryConfig` object
|
|
10
|
+
- **THEN** it SHALL support:
|
|
11
|
+
- `attempts`: Number of retry attempts (default: 0).
|
|
12
|
+
- `delay`: Base delay in milliseconds (default: 0).
|
|
13
|
+
- `backoff`: Backoff strategy ('fixed' | 'exponential') (default: 'fixed').
|
|
14
|
+
|
|
15
|
+
### Requirement: Retrying Execution Strategy
|
|
16
|
+
|
|
17
|
+
The system SHALL provide a `RetryingExecutionStrategy` that implements `IExecutionStrategy` and wraps another `IExecutionStrategy`.
|
|
18
|
+
|
|
19
|
+
#### Scenario: Successful execution
|
|
20
|
+
|
|
21
|
+
- **WHEN** the inner strategy returns a successful `TaskResult`
|
|
22
|
+
- **THEN** `RetryingExecutionStrategy` SHALL return that result immediately.
|
|
23
|
+
|
|
24
|
+
#### Scenario: Retry on failure
|
|
25
|
+
|
|
26
|
+
- **WHEN** the inner strategy throws or returns a failed `TaskResult`
|
|
27
|
+
- **AND** the task has `retry.attempts > 0`
|
|
28
|
+
- **THEN** it SHALL wait for the configured `delay`.
|
|
29
|
+
- **AND** it SHALL re-execute the task using the inner strategy.
|
|
30
|
+
- **AND** it SHALL decrement the remaining attempts.
|
|
31
|
+
|
|
32
|
+
#### Scenario: Max attempts reached
|
|
33
|
+
|
|
34
|
+
- **WHEN** the task fails and no attempts remain
|
|
35
|
+
- **THEN** it SHALL return the failed result (or throw).
|
|
36
|
+
|
|
37
|
+
#### Scenario: Exponential Backoff
|
|
38
|
+
|
|
39
|
+
- **WHEN** `retry.backoff` is 'exponential'
|
|
40
|
+
- **THEN** the delay SHALL increase for each attempt (e.g., `delay * 2^attempt`).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Dry Run Execution Strategy
|
|
4
|
+
|
|
5
|
+
The system SHALL provide a `DryRunExecutionStrategy` that implements `IExecutionStrategy`.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Simulating execution
|
|
8
|
+
|
|
9
|
+
- **WHEN** `WorkflowExecutor` is configured with `DryRunExecutionStrategy`
|
|
10
|
+
- **AND** `execute` is called
|
|
11
|
+
- **THEN** it SHALL traverse the dependency graph respecting order
|
|
12
|
+
- **AND** it SHALL NOT execute the actual work of the `TaskStep`.
|
|
13
|
+
- **AND** it SHALL return `TaskResult`s with a status indicating successful simulation (e.g., `simulated` or `success`).
|
|
14
|
+
|
|
15
|
+
### Requirement: Mermaid Visualization
|
|
16
|
+
|
|
17
|
+
The system SHALL provide a utility to generate a Mermaid.js graph from task steps.
|
|
18
|
+
|
|
19
|
+
#### Scenario: Generate Mermaid Graph
|
|
20
|
+
|
|
21
|
+
- **GIVEN** a list of `TaskStep`s with dependencies
|
|
22
|
+
- **WHEN** `generateMermaidGraph` is called
|
|
23
|
+
- **THEN** it SHALL return a valid Mermaid flowchart syntax string.
|
|
24
|
+
- **AND** dependencies SHALL be represented as arrows (`-->`).
|
|
25
|
+
- **AND** independent tasks SHALL appear as nodes.
|
package/openspec/changes/archive/2026-01-18-refactor-core-architecture/specs/task-runner/spec.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## MODIFIED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: TaskRunner Execution
|
|
4
|
+
|
|
5
|
+
The `TaskRunner` SHALL execute a sequence of `TaskStep`s based on their dependencies, processing inputs and producing outputs.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Successful execution
|
|
8
|
+
|
|
9
|
+
- **WHEN** all `TaskStep`s complete successfully
|
|
10
|
+
- **THEN** the `TaskRunner` returns a successful workflow result.
|
|
11
|
+
|
|
12
|
+
#### Scenario: Execution with AbortSignal
|
|
13
|
+
|
|
14
|
+
- **WHEN** `TaskRunner.execute` is called with an `AbortSignal`
|
|
15
|
+
- **THEN** the `TaskRunner` monitors the `AbortSignal` for cancellation requests.
|
|
16
|
+
|
|
17
|
+
#### Scenario: Execution with Global Timeout
|
|
18
|
+
|
|
19
|
+
- **WHEN** `TaskRunner.execute` is called with a `timeout` option
|
|
20
|
+
- **THEN** the `TaskRunner` monitors the elapsed time for the workflow.
|
|
21
|
+
|
|
22
|
+
## ADDED Requirements
|
|
23
|
+
|
|
24
|
+
### Requirement: Modular Execution Architecture
|
|
25
|
+
|
|
26
|
+
The system SHALL support pluggable execution strategies and decoupled state management.
|
|
27
|
+
|
|
28
|
+
#### Scenario: Pluggable Strategy
|
|
29
|
+
|
|
30
|
+
- **WHEN** configured with a custom execution strategy
|
|
31
|
+
- **THEN** the `TaskRunner` SHALL delegate the execution logic to that strategy.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Design: Release PR Pattern
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
The current system uses `semantic-release` within a GitHub Actions workflow (`release.yml`). This workflow triggers on every push to `main`, executing an immediate release (version bump, changelog, git tag, npm publish, github release). This leads to high frequency "version explosion" in a trunk-based high-velocity environment.
|
|
5
|
+
|
|
6
|
+
## Solution
|
|
7
|
+
We will adopt the **Release PR Pattern** using **Release Please**.
|
|
8
|
+
|
|
9
|
+
### Architecture
|
|
10
|
+
- **Tool**: `googleapis/release-please-action`.
|
|
11
|
+
- **Trigger**: Pushes to `main`.
|
|
12
|
+
- **Mechanism**:
|
|
13
|
+
1. **Analysis**: The action analyzes commits since the last tagged version.
|
|
14
|
+
2. **PR Creation**: It creates (or updates) a special Pull Request containing:
|
|
15
|
+
- The proposed next version numbers.
|
|
16
|
+
- The generated CHANGELOG entry.
|
|
17
|
+
3. **Release**:
|
|
18
|
+
- When the Release PR is **merged**, the action runs again.
|
|
19
|
+
- It detects the accidental merge of the release commit.
|
|
20
|
+
- It creates the GitHub Release and Tags.
|
|
21
|
+
- It outputs a `release_created` boolean.
|
|
22
|
+
4. **Publish**:
|
|
23
|
+
- A downstream step in the same workflow listens for `if: ${{ steps.release.outputs.release_created }}`.
|
|
24
|
+
- If true, it executes the build and publish commands (e.g., `pnpm publish`).
|
|
25
|
+
|
|
26
|
+
### Migration Strategy
|
|
27
|
+
1. **Cleanup**: Completely remove `semantic-release` configuration and workflow to prevent dual-release mechanisms or conflicts.
|
|
28
|
+
2. **Configuration**:
|
|
29
|
+
- `release-please-config.json`: Defines the release type (`node`), packages (for monorepo support, though this is seemingly a single package config, using the root as the package is standard).
|
|
30
|
+
- `.release-please-manifest.json`: Stores the current version (source of truth for Release Please). We will initialize it with the current version from `package.json`.
|
|
31
|
+
3. **Workflow**:
|
|
32
|
+
- Replace `.github/workflows/release.yml` with `.github/workflows/release-please.yml`.
|
|
33
|
+
|
|
34
|
+
### Key Differences
|
|
35
|
+
| Feature | Old (Semantic Release) | New (Release Please) |
|
|
36
|
+
| :------------ | :--------------------- | :---------------------------------- |
|
|
37
|
+
| **Trigger** | Every push to `main` | Merge of Release PR |
|
|
38
|
+
| **Changelog** | Generated on commit | Previewed in PR, committed on merge |
|
|
39
|
+
| **Version** | Bumped on commit | Bumped in PR, committed on merge |
|
|
40
|
+
| **Control** | Automated | Manual approval via Merge |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Proposal: Adopt Release PR Pattern
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Switch from "continuous deployment on every commit" to the **Release PR Pattern** (Google Style) using [Release Please](https://github.com/google-github-actions/release-please-action).
|
|
5
|
+
This ensures that releases are batched, version explosion is prevented, and human control is restored via PR merges, while maintaining automation.
|
|
6
|
+
|
|
7
|
+
## User Review Required
|
|
8
|
+
> [!IMPORTANT]
|
|
9
|
+
> This is a breaking change to the release process.
|
|
10
|
+
> - **Current Behavior**: Every commit to `main` triggers a release/publish.
|
|
11
|
+
> - **New Behavior**: Commits to `main` update a "Release PR". A release only happens when you **merge** that PR.
|
|
12
|
+
|
|
13
|
+
## Proposed Changes
|
|
14
|
+
|
|
15
|
+
### Configuration
|
|
16
|
+
#### [DELETE] [.releaserc.json](file:///home/thales/projects/task-runner/.releaserc.json)
|
|
17
|
+
- Remove Semantic Release configuration.
|
|
18
|
+
|
|
19
|
+
#### [NEW] [release-please-config.json](file:///home/thales/projects/task-runner/release-please-config.json)
|
|
20
|
+
- Configuration for Release Please (monorepo-friendly structure, standard conventional commits).
|
|
21
|
+
|
|
22
|
+
#### [NEW] [.release-please-manifest.json](file:///home/thales/projects/task-runner/.release-please-manifest.json)
|
|
23
|
+
- Tracks versions (required for Release Please).
|
|
24
|
+
|
|
25
|
+
### Infra / CI
|
|
26
|
+
#### [DELETE] [.github/workflows/release.yml](file:///home/thales/projects/task-runner/.github/workflows/release.yml)
|
|
27
|
+
- Remove successful `semantic-release` workflow.
|
|
28
|
+
|
|
29
|
+
#### [NEW] [.github/workflows/release-please.yml](file:///home/thales/projects/task-runner/.github/workflows/release-please.yml)
|
|
30
|
+
- New GitHub Action workflow that:
|
|
31
|
+
1. Runs `release-please` to update the PR.
|
|
32
|
+
2. If a release is created (PR merged), runs build/publish steps.
|
|
33
|
+
|
|
34
|
+
## Verification Plan
|
|
35
|
+
|
|
36
|
+
### Automated Tests
|
|
37
|
+
- **Dry Run**: We cannot easily "test" the GitHub Action without merging, but we can verify the configuration files are valid JSON.
|
|
38
|
+
- **Lint**: Ensure new workflow file syntax is valid (tools like `action-validator` if available, otherwise manual review).
|
|
39
|
+
|
|
40
|
+
### Manual Verification
|
|
41
|
+
- **Post-Merge**:
|
|
42
|
+
1. Push a `fix: test` commit to `main`.
|
|
43
|
+
2. Verify `release-please` bot creates a PR "chore: release 1.0.1".
|
|
44
|
+
3. Push another `feat: cool` commit.
|
|
45
|
+
4. Verify PR updates to includes the feature.
|
|
46
|
+
5. Merge the PR.
|
|
47
|
+
6. Verify GitHub Release created and NPM publish (if configured).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Spec: Release PR Pattern
|
|
2
|
+
|
|
3
|
+
## ADDED Requirements
|
|
4
|
+
|
|
5
|
+
### Requirement: Release PR Generation
|
|
6
|
+
The system MUST automatically maintain a "Release PR" that targets the `main` branch. This PR must accumulate all conventional changes since the last release, calculating the next semantic version and generating a corresponding changelog entry.
|
|
7
|
+
|
|
8
|
+
#### Scenario: Feature commit triggers PR update
|
|
9
|
+
Given the latest release is `v1.0.0`
|
|
10
|
+
And a developer merges a commit with message `feat: add awesome feature` to `main`
|
|
11
|
+
Then the system should create or update the Release PR
|
|
12
|
+
And the PR title should be `chore: release 1.1.0`
|
|
13
|
+
And the PR body should contain the changelog entry for "add awesome feature"
|
|
14
|
+
And the `package.json` version in the PR should be `1.1.0`
|
|
15
|
+
|
|
16
|
+
#### Scenario: Fix commit triggers patch update
|
|
17
|
+
Given the latest release is `v1.0.0`
|
|
18
|
+
And a developer merges a commit `fix: urgent bug` to `main`
|
|
19
|
+
Then the Release PR should be updated to target version `1.0.1`
|
|
20
|
+
|
|
21
|
+
### Requirement: Release Publication
|
|
22
|
+
The system MUST execute the release process (git tag, GitHub Release, npm publish) ONLY when the Release PR is merged into `main`.
|
|
23
|
+
|
|
24
|
+
#### Scenario: Merge triggers publish
|
|
25
|
+
Given the Release PR for `v1.1.0` exists
|
|
26
|
+
When a maintainer merges the PR into `main`
|
|
27
|
+
Then the system should create a GitHub Release `v1.1.0`
|
|
28
|
+
And the system should publish the package to the configured registry (NPM)
|
|
29
|
+
And the system should NOT publish any other commits merged to `main` until the next Release PR merge
|
|
30
|
+
|
|
31
|
+
## REMOVED Requirements
|
|
32
|
+
|
|
33
|
+
### Requirement: Immediate Release
|
|
34
|
+
The system MUST NOT trigger a release or publish artifacts on every commit to `main`, unless that commit is the merge of a Release PR.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
- [x] Remove Semantic Release configuration <!-- id: 0 -->
|
|
2
|
+
- Delete `.releaserc.json`
|
|
3
|
+
- Remove `release.yml`
|
|
4
|
+
- Uninstall `semantic-release` and its plugins
|
|
5
|
+
- [x] Initialize Release Please configuration <!-- id: 1 -->
|
|
6
|
+
- Create `release-please-config.json` (root package type: node)
|
|
7
|
+
- Create `.release-please-manifest.json` (synced with current package.json version)
|
|
8
|
+
- [x] Create Release Workflow <!-- id: 2 -->
|
|
9
|
+
- Create `.github/workflows/release-please.yml`
|
|
10
|
+
- Step 1: `googleapis/release-please-action`
|
|
11
|
+
- Step 2: `pnpm publish` (conditional on release creation)
|
|
12
|
+
- [x] Verification <!-- id: 3 -->
|
|
13
|
+
- Validate CI YAML syntax
|
|
14
|
+
- Verify JSON config validity
|
package/openspec/changes/{feat-task-metrics → archive/2026-01-22-feat-task-metrics}/proposal.md
RENAMED
|
@@ -7,7 +7,7 @@ Users currently lack visibility into the performance of individual tasks within
|
|
|
7
7
|
## What Changes
|
|
8
8
|
|
|
9
9
|
- Update `TaskResult` interface to include an optional `metrics` property containing `startTime`, `endTime`, and `duration`.
|
|
10
|
-
- Update `WorkflowExecutor` to capture these timestamps during task execution and populate the `metrics` property.
|
|
10
|
+
- Update `WorkflowExecutor` to capture these timestamps using `performance.now()` for high-precision timing during task execution and populate the `metrics` property.
|
|
11
11
|
- Ensure these metrics are available in the final `TaskResult` map returned by `TaskRunner.execute`.
|
|
12
12
|
|
|
13
13
|
## Impact
|
package/openspec/changes/archive/2026-01-22-feat-task-metrics/specs/001-generic-task-runner/spec.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Task Execution Metrics
|
|
4
|
+
|
|
5
|
+
The system SHALL record timing metrics for each executed task, including start time, end time, and duration.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Successful execution
|
|
8
|
+
- **WHEN** a task completes successfully
|
|
9
|
+
- **THEN** the task result contains the start timestamp, end timestamp, and duration in milliseconds
|
|
10
|
+
|
|
11
|
+
#### Scenario: Failed execution
|
|
12
|
+
- **WHEN** a task fails
|
|
13
|
+
- **THEN** the task result contains the start timestamp, end timestamp, and duration in milliseconds
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
## 1. Implementation
|
|
2
|
+
|
|
3
|
+
- [x] 1.1 Update `TaskResult` interface in `src/TaskResult.ts` to include `metrics`.
|
|
4
|
+
- [x] 1.2 Update `WorkflowExecutor.ts` to capture start/end times using `performance.now()` and calculate duration.
|
|
5
|
+
- [x] 1.3 Update `WorkflowExecutor.ts` to inject metrics into the `TaskResult`.
|
|
6
|
+
- [x] 1.4 Add unit tests in `tests/TaskMetrics.test.ts` to verify metrics are present and correct.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Change: Conditional Retries
|
|
2
|
+
|
|
3
|
+
## Why
|
|
4
|
+
|
|
5
|
+
Currently, the `RetryingExecutionStrategy` treats all task failures equally. If a task has retry attempts configured, it will blindly retry even if the error is permanent (e.g., syntax error, invalid configuration) or logic-based (e.g., validation failure). This wastes resources and execution time. Users need a way to specify *which* errors should trigger a retry, allowing them to fail fast on critical errors while retrying on transient ones (e.g., network timeouts).
|
|
6
|
+
|
|
7
|
+
## What Changes
|
|
8
|
+
|
|
9
|
+
- Update `TaskRetryConfig` interface in `src/contracts/TaskRetryConfig.ts` to include an optional `shouldRetry` predicate.
|
|
10
|
+
- Update `RetryingExecutionStrategy` in `src/strategies/RetryingExecutionStrategy.ts` to evaluate this predicate (if present) before deciding to retry.
|
|
11
|
+
- If `shouldRetry` returns `false`, the retry loop is broken immediately, and the failure result is returned.
|
|
12
|
+
- If `shouldRetry` is undefined, the existing behavior (retry on any failure) is preserved.
|
|
13
|
+
|
|
14
|
+
## Impact
|
|
15
|
+
|
|
16
|
+
- **Affected specs**: `001-generic-task-runner` (or simply `task-runner`)
|
|
17
|
+
- **Affected code**: `src/contracts/TaskRetryConfig.ts`, `src/strategies/RetryingExecutionStrategy.ts`
|
|
18
|
+
- **Non-breaking change**: The `shouldRetry` property is optional. Existing retry configurations will continue to work as before.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Conditional Retries
|
|
4
|
+
|
|
5
|
+
The system SHALL support conditional retries where a user-defined predicate determines if a task failure warrants a retry attempt.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Retry allowed by predicate
|
|
8
|
+
- **GIVEN** a task configured with retries and a `shouldRetry` predicate
|
|
9
|
+
- **WHEN** the task fails with an error
|
|
10
|
+
- **AND** the `shouldRetry` predicate returns `true` for that error
|
|
11
|
+
- **THEN** the system SHALL proceed with the retry logic (respecting attempt limits and delays).
|
|
12
|
+
|
|
13
|
+
#### Scenario: Retry denied by predicate
|
|
14
|
+
- **GIVEN** a task configured with retries and a `shouldRetry` predicate
|
|
15
|
+
- **WHEN** the task fails with an error
|
|
16
|
+
- **AND** the `shouldRetry` predicate returns `false` for that error
|
|
17
|
+
- **THEN** the system SHALL NOT retry the task.
|
|
18
|
+
- **AND** the task status SHALL be immediately marked as 'failure'.
|
|
19
|
+
|
|
20
|
+
#### Scenario: Default retry behavior
|
|
21
|
+
- **GIVEN** a task configured with retries but NO `shouldRetry` predicate
|
|
22
|
+
- **WHEN** the task fails with an error
|
|
23
|
+
- **THEN** the system SHALL retry the task (respecting attempt limits and delays), preserving backward compatibility.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Engineering Tasks
|
|
2
|
+
|
|
3
|
+
- [ ] **Task 1: Update TaskRetryConfig Interface**
|
|
4
|
+
- Modify `src/contracts/TaskRetryConfig.ts`.
|
|
5
|
+
- Add `shouldRetry?: (error: unknown) => boolean;` to the `TaskRetryConfig` interface.
|
|
6
|
+
- Document the property with JSDoc explaining its purpose (return true to retry, false to abort).
|
|
7
|
+
|
|
8
|
+
- [ ] **Task 2: Update RetryingExecutionStrategy Logic**
|
|
9
|
+
- Modify `src/strategies/RetryingExecutionStrategy.ts`.
|
|
10
|
+
- Inside the `execute` loop, after receiving a "failure" result:
|
|
11
|
+
- Check if `config.shouldRetry` is defined.
|
|
12
|
+
- If defined, call it with `result.error`.
|
|
13
|
+
- If it returns `false`, break the loop and return the failure result immediately.
|
|
14
|
+
- If it returns `true` (or is undefined), proceed with the existing retry logic (check attempts, delay).
|
|
15
|
+
|
|
16
|
+
- [ ] **Task 3: Unit Tests**
|
|
17
|
+
- Create `tests/strategies/RetryingExecutionStrategy.conditional.test.ts` (or add to existing test file if small).
|
|
18
|
+
- **Scenario 1**: `shouldRetry` returns `true`.
|
|
19
|
+
- Setup a task that fails 2 times then succeeds.
|
|
20
|
+
- Configure `shouldRetry: () => true`.
|
|
21
|
+
- Verify it retries and eventually succeeds.
|
|
22
|
+
- **Scenario 2**: `shouldRetry` returns `false`.
|
|
23
|
+
- Setup a task that fails with a specific error "FatalError".
|
|
24
|
+
- Configure `shouldRetry: (err) => err !== "FatalError"`.
|
|
25
|
+
- Verify it does *not* retry and returns failure immediately after the first attempt.
|
|
26
|
+
- **Scenario 3**: `shouldRetry` is undefined (Legacy behavior).
|
|
27
|
+
- Setup a failing task.
|
|
28
|
+
- Verify it retries up to max attempts.
|
|
29
|
+
|
|
30
|
+
- [ ] **Task 4: Integration Test**
|
|
31
|
+
- Create `tests/integration-tests/conditional-retries.test.ts`.
|
|
32
|
+
- Define a workflow with two tasks:
|
|
33
|
+
- Task A: Fails with "Transient" error (retries allowed).
|
|
34
|
+
- Task B: Fails with "Permanent" error (retries blocked by predicate).
|
|
35
|
+
- Execute workflow.
|
|
36
|
+
- Verify Task A consumed its retries (or succeeded).
|
|
37
|
+
- Verify Task B failed immediately (did not consume retries).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Per-Task Timeout
|
|
4
|
+
|
|
5
|
+
The `TaskStep` interface SHALL support an optional `timeout` property, and the `StandardExecutionStrategy` SHALL enforce this timeout.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Task completes within timeout
|
|
8
|
+
|
|
9
|
+
- **GIVEN** a task with `timeout: 100` (ms)
|
|
10
|
+
- **WHEN** the task execution takes 50ms
|
|
11
|
+
- **THEN** the task SHALL complete successfully.
|
|
12
|
+
- **AND** the timeout timer SHALL be cleared immediately.
|
|
13
|
+
|
|
14
|
+
#### Scenario: Task exceeds timeout
|
|
15
|
+
|
|
16
|
+
- **GIVEN** a task with `timeout: 100` (ms)
|
|
17
|
+
- **WHEN** the task execution attempts to run longer than 100ms
|
|
18
|
+
- **THEN** the task execution SHALL be aborted with an error indicating a timeout.
|
|
19
|
+
- **AND** the `TaskResult` status SHALL be 'failure' (or 'cancelled' if appropriate, but typically 'failure' due to timeout error).
|
|
20
|
+
- **AND** the `AbortSignal` passed to the task's `run` method SHALL be triggered.
|
|
21
|
+
|
|
22
|
+
#### Scenario: Global cancellation overrides task timeout
|
|
23
|
+
|
|
24
|
+
- **GIVEN** a task with `timeout: 5000` (ms)
|
|
25
|
+
- **WHEN** the workflow's global `AbortSignal` is triggered at 100ms
|
|
26
|
+
- **THEN** the task SHALL receive the abort signal immediately (at 100ms).
|
|
27
|
+
- **AND** the task SHALL NOT wait for the 5000ms timeout.
|
|
28
|
+
- **AND** the `TaskResult` status SHALL be 'cancelled'.
|
|
29
|
+
|
|
30
|
+
#### Scenario: Task without timeout
|
|
31
|
+
|
|
32
|
+
- **GIVEN** a task without a `timeout` property
|
|
33
|
+
- **WHEN** it executes
|
|
34
|
+
- **THEN** it SHALL NOT be subject to any local timeout constraints (only global workflow timeout).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Workflow State Persistence Specification
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Enables the `TaskRunner` to save its execution state and resume from that state later, allowing for recovery from failures or pausing long-running workflows without re-executing completed tasks.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: State Snapshot Exposure
|
|
10
|
+
|
|
11
|
+
The system SHALL provide a mechanism to retrieve the current execution state of a workflow.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Retrieving state from TaskRunner
|
|
14
|
+
- **WHEN** a workflow is running or completed
|
|
15
|
+
- **THEN** the `TaskRunner` (or its underlying `TaskStateManager`) SHALL expose a method to retrieve a snapshot of the current state.
|
|
16
|
+
- **AND** the snapshot SHALL contain the `results` of all executed tasks.
|
|
17
|
+
- **AND** the snapshot SHALL be serializable (e.g., to JSON).
|
|
18
|
+
|
|
19
|
+
### Requirement: Hydrated Initialization
|
|
20
|
+
|
|
21
|
+
The system SHALL allow initializing a `TaskRunner` with a pre-existing state snapshot.
|
|
22
|
+
|
|
23
|
+
#### Scenario: Initializing with a snapshot
|
|
24
|
+
- **GIVEN** a valid state snapshot from a previous execution
|
|
25
|
+
- **WHEN** the `TaskRunner` is built using `TaskRunnerBuilder`
|
|
26
|
+
- **THEN** the builder SHALL accept the snapshot as an initial state.
|
|
27
|
+
- **AND** the `TaskRunner` SHALL start with the internal state reflecting the snapshot (i.e., known task results).
|
|
28
|
+
|
|
29
|
+
### Requirement: Resumable Execution Logic
|
|
30
|
+
|
|
31
|
+
The `WorkflowExecutor` SHALL respect the initial hydrated state during execution, skipping already completed tasks.
|
|
32
|
+
|
|
33
|
+
#### Scenario: Skipping successful tasks
|
|
34
|
+
- **GIVEN** a `TaskRunner` initialized with a snapshot where Task A is marked as `success`
|
|
35
|
+
- **WHEN** `execute()` is called
|
|
36
|
+
- **THEN** Task A SHALL NOT be executed again.
|
|
37
|
+
- **AND** Task A SHALL be considered completed for the purpose of checking dependencies of downstream tasks.
|
|
38
|
+
|
|
39
|
+
#### Scenario: Re-running non-successful tasks
|
|
40
|
+
- **GIVEN** a `TaskRunner` initialized with a snapshot where Task B is marked as `failure`, `cancelled`, or `skipped`
|
|
41
|
+
- **WHEN** `execute()` is called
|
|
42
|
+
- **THEN** Task B SHOULD be evaluated for execution (subject to dependency checks).
|
|
43
|
+
|
|
44
|
+
#### Scenario: Handling Context
|
|
45
|
+
- **GIVEN** a resumed workflow
|
|
46
|
+
- **THEN** it is the caller's responsibility to provide the necessary `Context` for task execution.
|
|
47
|
+
- **AND** the `TaskRunner` SHALL NOT attempt to automatically restore the context object from the state snapshot (as it may contain non-serializable data).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# release-pr Specification
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
TBD - created by archiving change adopt-release-pr. Update Purpose after archive.
|
|
5
|
+
## Requirements
|
|
6
|
+
### Requirement: Release PR Generation
|
|
7
|
+
The system MUST automatically maintain a "Release PR" that targets the `main` branch. This PR must accumulate all conventional changes since the last release, calculating the next semantic version and generating a corresponding changelog entry.
|
|
8
|
+
|
|
9
|
+
#### Scenario: Feature commit triggers PR update
|
|
10
|
+
Given the latest release is `v1.0.0`
|
|
11
|
+
And a developer merges a commit with message `feat: add awesome feature` to `main`
|
|
12
|
+
Then the system should create or update the Release PR
|
|
13
|
+
And the PR title should be `chore: release 1.1.0`
|
|
14
|
+
And the PR body should contain the changelog entry for "add awesome feature"
|
|
15
|
+
And the `package.json` version in the PR should be `1.1.0`
|
|
16
|
+
|
|
17
|
+
#### Scenario: Fix commit triggers patch update
|
|
18
|
+
Given the latest release is `v1.0.0`
|
|
19
|
+
And a developer merges a commit `fix: urgent bug` to `main`
|
|
20
|
+
Then the Release PR should be updated to target version `1.0.1`
|
|
21
|
+
|
|
22
|
+
### Requirement: Release Publication
|
|
23
|
+
The system MUST execute the release process (git tag, GitHub Release, npm publish) ONLY when the Release PR is merged into `main`.
|
|
24
|
+
|
|
25
|
+
#### Scenario: Merge triggers publish
|
|
26
|
+
Given the Release PR for `v1.1.0` exists
|
|
27
|
+
When a maintainer merges the PR into `main`
|
|
28
|
+
Then the system should create a GitHub Release `v1.1.0`
|
|
29
|
+
And the system should publish the package to the configured registry (NPM)
|
|
30
|
+
And the system should NOT publish any other commits merged to `main` until the next Release PR merge
|
|
31
|
+
|