ruby_reactor 0.8.3 → 0.8.5
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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/.specify/feature.json +1 -1
- data/CHANGELOG.md +208 -0
- data/CLAUDE.md +1 -1
- data/README.md +140 -102
- data/lib/ruby_reactor/dsl/async_macros.rb +30 -1
- data/lib/ruby_reactor/dsl/async_reactor_builder.rb +10 -7
- data/lib/ruby_reactor/dsl/compose_builder.rb +10 -11
- data/lib/ruby_reactor/dsl/interrupt_builder.rb +1 -3
- data/lib/ruby_reactor/dsl/map_builder.rb +0 -2
- data/lib/ruby_reactor/dsl/reactor.rb +8 -11
- data/lib/ruby_reactor/dsl/retryable.rb +45 -0
- data/lib/ruby_reactor/dsl/step_builder.rb +131 -33
- data/lib/ruby_reactor/error/argument_resolution_error.rb +19 -0
- data/lib/ruby_reactor/error/rescuable.rb +28 -0
- data/lib/ruby_reactor/error/undeclared_input_error.rb +13 -0
- data/lib/ruby_reactor/executor/compensation_manager.rb +30 -26
- data/lib/ruby_reactor/executor/result_handler.rb +21 -18
- data/lib/ruby_reactor/executor/retry_manager.rb +11 -12
- data/lib/ruby_reactor/executor/step_coordination.rb +11 -8
- data/lib/ruby_reactor/executor/step_executor.rb +59 -49
- data/lib/ruby_reactor/executor.rb +38 -4
- data/lib/ruby_reactor/map/collector.rb +21 -11
- data/lib/ruby_reactor/map/dispatcher.rb +29 -3
- data/lib/ruby_reactor/map/element_executor.rb +9 -3
- data/lib/ruby_reactor/map/helpers.rb +32 -2
- data/lib/ruby_reactor/map/result_enumerator.rb +18 -12
- data/lib/ruby_reactor/reactor.rb +24 -0
- data/lib/ruby_reactor/rspec/matchers.rb +19 -3
- data/lib/ruby_reactor/rspec/test_subject.rb +3 -5
- data/lib/ruby_reactor/step/async_reactor_step.rb +6 -2
- data/lib/ruby_reactor/step/compose_step.rb +14 -4
- data/lib/ruby_reactor/step/input_contract.rb +5 -0
- data/lib/ruby_reactor/step/inputs.rb +57 -0
- data/lib/ruby_reactor/step/map_step.rb +140 -25
- data/lib/ruby_reactor/step.rb +20 -7
- data/lib/ruby_reactor/step_worker.rb +46 -22
- data/lib/ruby_reactor/storage/adapter.rb +4 -0
- data/lib/ruby_reactor/storage/redis_adapter.rb +9 -0
- data/lib/ruby_reactor/storage/redis_reactor_scan.rb +1 -1
- data/lib/ruby_reactor/version.rb +1 -1
- data/lib/ruby_reactor/web/api.rb +1 -1
- data/lib/ruby_reactor/web/public/assets/{index-CeZU-ESu.js → index-CQbgHtd0.js} +10 -10
- data/lib/ruby_reactor/web/public/index.html +1 -1
- data/lib/ruby_reactor/worker.rb +3 -1
- data/lib/ruby_reactor.rb +20 -7
- data/specs/006-step-retry-declarations/checklists/requirements.md +41 -0
- data/specs/006-step-retry-declarations/contracts/dsl-surface.md +89 -0
- data/specs/006-step-retry-declarations/data-model.md +58 -0
- data/specs/006-step-retry-declarations/plan.md +187 -0
- data/specs/006-step-retry-declarations/quickstart.md +80 -0
- data/specs/006-step-retry-declarations/research.md +194 -0
- data/specs/006-step-retry-declarations/spec.md +453 -0
- data/specs/006-step-retry-declarations/tasks.md +382 -0
- data/specs/007-execution-flow-analysis/analysis/README.md +147 -0
- data/specs/007-execution-flow-analysis/analysis/execution-order.md +359 -0
- data/specs/007-execution-flow-analysis/analysis/findings-and-options.md +502 -0
- data/specs/007-execution-flow-analysis/analysis/invariants.md +109 -0
- data/specs/007-execution-flow-analysis/checklists/requirements.md +39 -0
- data/specs/007-execution-flow-analysis/contracts/report-structure.md +71 -0
- data/specs/007-execution-flow-analysis/data-model.md +83 -0
- data/specs/007-execution-flow-analysis/evidence/harness.rb +229 -0
- data/specs/007-execution-flow-analysis/evidence/output.txt +333 -0
- data/specs/007-execution-flow-analysis/evidence/probes/01_plain.rb +122 -0
- data/specs/007-execution-flow-analysis/evidence/probes/02_compose.rb +182 -0
- data/specs/007-execution-flow-analysis/evidence/probes/03_map.rb +232 -0
- data/specs/007-execution-flow-analysis/evidence/probes/04_async.rb +132 -0
- data/specs/007-execution-flow-analysis/evidence/probes/05_background.rb +58 -0
- data/specs/007-execution-flow-analysis/evidence/probes/06_coordination.rb +158 -0
- data/specs/007-execution-flow-analysis/evidence/probes/07_interrupts_manual.rb +185 -0
- data/specs/007-execution-flow-analysis/evidence/run.rb +15 -0
- data/specs/007-execution-flow-analysis/plan.md +127 -0
- data/specs/007-execution-flow-analysis/quickstart.md +51 -0
- data/specs/007-execution-flow-analysis/research.md +202 -0
- data/specs/007-execution-flow-analysis/spec.md +270 -0
- data/specs/007-execution-flow-analysis/tasks.md +257 -0
- data/specs/008-rollback-reliability/checklists/requirements.md +43 -0
- data/specs/008-rollback-reliability/contracts/api-surface.md +126 -0
- data/specs/008-rollback-reliability/contracts/rollback-semantics.md +76 -0
- data/specs/008-rollback-reliability/data-model.md +139 -0
- data/specs/008-rollback-reliability/plan.md +233 -0
- data/specs/008-rollback-reliability/quickstart.md +105 -0
- data/specs/008-rollback-reliability/research.md +653 -0
- data/specs/008-rollback-reliability/spec.md +561 -0
- data/specs/008-rollback-reliability/tasks.md +1110 -0
- data/specs/future_improvements.md +48 -0
- data/specs/specs-inputs-by-method-md-piped-wigderson.md +77 -0
- metadata +47 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4187fd51277447b11381c872a01b46d8e7663323b5b470316d8600ead76906e
|
|
4
|
+
data.tar.gz: 540b0c6b94d007a020f3bfbb3ff035eb47469691bf138cd41588e5a6a28ba468
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59c5faa4c755625a2172a5a7613941a82970aa3b52ac170f6c2f12015f41b289700d320d0195b51d4b13e1cf1f9b86e209bc8c59d5dcc84830a2e65c1c49a48d
|
|
7
|
+
data.tar.gz: 0be05ffe9539a5ae4c012a8f8e3f26394d27853a86219a7c02d82d9685f7f018bd760c26f749cf34c4899f0c9346c6a54a91ee64e0e274b2f2ad2d12048ee818
|
data/.specify/feature.json
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@
|
|
|
4
4
|
|
|
5
5
|
### ⚠ BREAKING CHANGES
|
|
6
6
|
|
|
7
|
+
* **`retries` on a `compose` or an `async_reactor` raises `RubyReactor::Error::DeprecatedDslError`
|
|
8
|
+
at class definition.** A parent never retries a nested reactor as a whole: the child retries its
|
|
9
|
+
own steps. Before, a compose-level retry resumed a child whose earlier steps had already been
|
|
10
|
+
undone. Declare `retries` on the child's steps instead.
|
|
11
|
+
See *Migration notes: reliable rollback* below.
|
|
12
|
+
* **`where` and `guard` are removed.** Declaring either on a `step`, `async_step` or `interrupt`
|
|
13
|
+
raises `RubyReactor::Error::DeprecatedDslError` at class definition. A step that should not run
|
|
14
|
+
returns `Skipped(value)` (or calls `skip!(value)`) from its body.
|
|
15
|
+
See *Migration notes: reliable rollback* below.
|
|
16
|
+
* **`Skipped` no longer changes execution.** It is an instrumentation mark (the trace records it;
|
|
17
|
+
`skipped?` is true): a `background after:` step that returns `Skipped` now hands the rest of
|
|
18
|
+
the run off like any completed step (before, the rest ran in the calling process), and a
|
|
19
|
+
`with_period` step whose body returns `Skipped` marks its bucket. A skipped step is still never
|
|
20
|
+
undone.
|
|
21
|
+
* **An exception that is not a `StandardError` fails the step and rolls back.** A
|
|
22
|
+
`NotImplementedError`, a `LoadError`, a `SystemStackError` or a custom `Exception` subclass
|
|
23
|
+
raised by reactor code (a step body, an argument transform, a `compensate`/`undo`, a key proc, a
|
|
24
|
+
`collect` block) is now that step's failure: the step is compensated if its body ran, completed
|
|
25
|
+
steps are undone, and `Reactor.run` returns the `Failure` instead of raising. Only interruptions
|
|
26
|
+
(`SignalException` including `Interrupt`, `SystemExit`, `NoMemoryError`, and an enclosing
|
|
27
|
+
`Timeout.timeout`'s interruption) still skip rollback and propagate.
|
|
28
|
+
See *Migration notes: reliable rollback* below.
|
|
29
|
+
* **An `async_step`'s `compensate` runs, in the unit's own job, when its final attempt fails.**
|
|
30
|
+
Before, `compensate`/`undo` blocks on an `async_step` were accepted and never ran. Now the unit
|
|
31
|
+
compensates itself once, after its last retry, whether or not any step reads its result —
|
|
32
|
+
never for a retried attempt, a halt, or a body that never started. The outcome is recorded on
|
|
33
|
+
the unit's Step Result Record as `compensation: { status, rollback_failures, completed_at }`,
|
|
34
|
+
and the compensation middleware events fire in the unit's job. A reader that surfaces the
|
|
35
|
+
failure no longer leads to a second compensation of the unit.
|
|
36
|
+
See *Migration notes: reliable rollback* below.
|
|
37
|
+
|
|
38
|
+
* **An inline `undo` inside `async_step` raises `RubyReactor::Error::ValidationError` at class
|
|
39
|
+
definition.** An independent async unit is never undone, so the block could never run. A step
|
|
40
|
+
class that defines `undo` and is used with `async_step` prints a definition-time warning
|
|
41
|
+
instead (the same class may be reused by ordinary steps, where its `undo` runs).
|
|
42
|
+
See *Migration notes: reliable rollback* below.
|
|
43
|
+
* **A map rolls back the elements that completed.** When a map fails (an element fails under
|
|
44
|
+
`fail_fast`, or `collect` raises), and when a later step fails or the run is undone manually,
|
|
45
|
+
every completed element is rolled back by replaying its own step `undo`s, highest index first —
|
|
46
|
+
in inline and fan-out mode, whatever order the element jobs ran in. Before, completed elements
|
|
47
|
+
were never rolled back. A fail-fast fan-out map now waits for elements already in flight before
|
|
48
|
+
it reports its failure, and settles the elements it never started as skipped (which also stops
|
|
49
|
+
the map sweeper re-dispatching them). An element rollback that does not complete is listed in
|
|
50
|
+
`Failure#rollback_failures` with `map_step:` and `element_index:`; an element whose context
|
|
51
|
+
(or the map's element index) expired is reported with `reason: :context_unavailable`, and a
|
|
52
|
+
still-running duplicate with `reason: :element_in_flight`. A fan-out map whose rollback is
|
|
53
|
+
incomplete fails with the same `CompensationError` shape as an inline map.
|
|
54
|
+
See *Migration notes: reliable rollback* below.
|
|
7
55
|
* **`RubyReactor::Step` is now a base class, not a mixin.** `include RubyReactor::Step` on a
|
|
8
56
|
plain class with `def self.run(arguments, context)` is gone — no compatibility shim, no dual
|
|
9
57
|
authoring style. A step subclasses `RubyReactor::Step` and writes `run` (and optionally
|
|
@@ -78,8 +126,130 @@
|
|
|
78
126
|
`RubyReactor.Failure`, and a hash error needs braces, `Failure({ code: 1 })`, because a braceless
|
|
79
127
|
`Failure(code: 1)` is now read as options.
|
|
80
128
|
|
|
129
|
+
|
|
130
|
+
### Migration notes: reliable rollback
|
|
131
|
+
|
|
132
|
+
Every breaking or shape-changing item of the rollback work, with what to change.
|
|
133
|
+
|
|
134
|
+
1. **Map element `undo`s now run** (breaking, behavior). They run when the map fails, when a later
|
|
135
|
+
step fails, and on a manual `Reactor.undo(id)`. Make them idempotent.
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# Before: this undo never ran for a map element; charges stayed on a failure.
|
|
139
|
+
# After: it refunds each charged element, highest index first. Guard against a double refund.
|
|
140
|
+
class ChargeStep < RubyReactor::Step
|
|
141
|
+
def undo
|
|
142
|
+
Payments.refund(result[:charge_id]) unless Payments.refunded?(result[:charge_id])
|
|
143
|
+
Success()
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
2. **An `async_step`'s `compensate` now runs in the unit's job** (breaking, behavior), once, after
|
|
149
|
+
its final attempt fails, whether or not a reader exists. Move reader-only cleanup into the
|
|
150
|
+
reader.
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# Before: never ran.
|
|
154
|
+
async_step :notify, NotifyStep do
|
|
155
|
+
compensate { |error, inputs, _ctx| Audit.undelivered(inputs.user_id, error) }
|
|
156
|
+
end
|
|
157
|
+
# After: runs in the unit's job after the last retry; the outcome is on the unit's record
|
|
158
|
+
# as `compensation`. Cleanup that should run only when a reader fails the reactor:
|
|
159
|
+
step :confirm do
|
|
160
|
+
argument :delivery, result(:notify)
|
|
161
|
+
run { |inputs, _ctx| inputs.delivery.is_a?(RubyReactor::Failure) ? Failure("undelivered") : Success() }
|
|
162
|
+
compensate { |_error, _inputs, _ctx| Support.open_ticket }
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
3. **An inline `undo` inside `async_step` raises at class definition** (breaking, API).
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
# Before: accepted, never ran.
|
|
170
|
+
async_step(:notify) { run { ... }; undo { ... } }
|
|
171
|
+
# After: raises RubyReactor::Error::ValidationError. Use the unit's `compensate`, a reader's
|
|
172
|
+
# `compensate`, or an `async_reactor` child whose steps declare `undo`.
|
|
173
|
+
async_reactor :notify, NotifyReactor # NotifyReactor's steps declare `undo`
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
4. **`retries` on `compose` / `async_reactor` raises at class definition** (breaking, API). Move
|
|
177
|
+
the retries onto the child step that can fail transiently. The child retries it itself; the
|
|
178
|
+
other child steps run once.
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
# Before: retried the whole child from the parent.
|
|
182
|
+
compose(:booking, BookingReactor) { retries max_attempts: 2 }
|
|
183
|
+
# After: raises RubyReactor::Error::DeprecatedDslError. Declare it on the child's step:
|
|
184
|
+
class ConfirmBookingStep < RubyReactor::Step
|
|
185
|
+
retries max_attempts: 2
|
|
186
|
+
end
|
|
187
|
+
compose :booking, BookingReactor
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
5. **`where` / `guard` are removed** (breaking, API). Skip from the step body. Unlike `where`,
|
|
191
|
+
the body decides after the step started: its arguments are resolved and validated (a step
|
|
192
|
+
that relied on `where` to avoid invalid arguments now fails on them), and its lock, semaphore
|
|
193
|
+
and rate-limit slot are taken first. A `background before:` hand-off at that step now always
|
|
194
|
+
fires; the body decides in the worker.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# Before
|
|
198
|
+
step :sync_user do
|
|
199
|
+
where { |ctx| ctx.get_input(:enabled) }
|
|
200
|
+
run { |inputs, _ctx| Success(sync!(inputs.user)) }
|
|
201
|
+
end
|
|
202
|
+
# After
|
|
203
|
+
step :sync_user do
|
|
204
|
+
run do |inputs, ctx|
|
|
205
|
+
next Skipped(nil) unless ctx.get_input(:enabled)
|
|
206
|
+
Success(sync!(inputs.user))
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
6. **Non-`StandardError` exceptions from reactor code roll back** (breaking, behavior). They no
|
|
212
|
+
longer propagate out of `Reactor.run`; check the returned `Failure` instead. A test assertion
|
|
213
|
+
error raised inside a step body (an RSpec expectation, a strict double) now surfaces as the
|
|
214
|
+
step's `Failure`, so assert on the result.
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
# Before: NotImplementedError propagated; nothing was undone; the run was stored `aborted`.
|
|
218
|
+
# After:
|
|
219
|
+
result = MyReactor.run(inputs)
|
|
220
|
+
result.failure? # => true, completed steps undone
|
|
221
|
+
result.exception_class # => "NotImplementedError"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
7. **Argument and unknown errors roll back and carry `step_name`** (fix). The Failure's
|
|
225
|
+
shape changes on these paths.
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
# Before: "Execution failed: invalid value for Float(): 'abc'", step_name nil, nothing undone.
|
|
229
|
+
# After: "Step 'charge' failed: Step 'charge' could not resolve its arguments: …",
|
|
230
|
+
# step_name :charge, exception_class "ArgumentError", completed steps undone.
|
|
231
|
+
result.step_name # => :charge
|
|
232
|
+
result.exception_class # => "ArgumentError"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
8. **New `aborted` status** (additive), only for runs in the caller's process cut short by an
|
|
236
|
+
interruption. Dashboards and status filters gain a value; an `aborted` run needs
|
|
237
|
+
`MyReactor.undo(id)` to roll back.
|
|
238
|
+
|
|
239
|
+
9. **`rollback_failures` entries may carry `map_step:` / `element_index:`** and the reasons
|
|
240
|
+
`:context_unavailable` / `:element_in_flight` (additive).
|
|
241
|
+
|
|
81
242
|
### Features
|
|
82
243
|
|
|
244
|
+
* **`aborted` execution status.** A run in the caller's process that an interruption
|
|
245
|
+
(`SignalException` including `Interrupt`, `SystemExit`, `NoMemoryError`, or an enclosing
|
|
246
|
+
`Timeout.timeout`) cuts short runs no rollback code: the exception reaches the caller unchanged,
|
|
247
|
+
and the run is stored as `aborted` with only the steps not yet undone still outstanding (an
|
|
248
|
+
interruption during a rollback keeps exactly the rest, whichever failure started that rollback).
|
|
249
|
+
Workers and the sweeper never resume it; `Reactor.undo(id)` rolls it back, including the steps a
|
|
250
|
+
composed child or the elements of an inline `map` completed when the interruption hit inside
|
|
251
|
+
them. The dashboard and web API show and filter it, next to `failed`. A worker run is unchanged
|
|
252
|
+
(its job is redelivered).
|
|
83
253
|
* **Step-scoped coordination.** Steps can declare `with_lock`, `with_semaphore`, `with_rate_limit`,
|
|
84
254
|
`with_period`, and `with_ordered_lock` — the same macros as the reactor form, keyed on the step's
|
|
85
255
|
own resolved arguments instead of the reactor's inputs — so one step of a workflow can be
|
|
@@ -140,6 +310,25 @@
|
|
|
140
310
|
|
|
141
311
|
### Bug Fixes
|
|
142
312
|
|
|
313
|
+
* `Reactor.continue` accepts a resume only while the reactor is paused at an interrupt. A resume
|
|
314
|
+
that arrives while the reactor is executing or rolling back, or after it finished or was
|
|
315
|
+
aborted, raises `RubyReactor::Error::ValidationError` and changes nothing. Before, it resumed the
|
|
316
|
+
run from its stored state, which could run a rolled-back or aborted run forward again. An
|
|
317
|
+
accepted resume marks the run `running` before executing, so a concurrent second resume fails.
|
|
318
|
+
A resume that cannot take the reactor's lock or semaphore raises its `AcquisitionError` and
|
|
319
|
+
leaves the run paused, so the caller can retry it.
|
|
320
|
+
* A `background after:` step that returns `Halt` no longer hands the rest of the run to a worker:
|
|
321
|
+
the run halts, as `Halt` promises. Before, the remaining steps ran in a worker.
|
|
322
|
+
* A stored `Failure` keeps at most 100 backtrace frames, plus a `"... N more frames"` line. A stack
|
|
323
|
+
overflow's backtrace no longer inflates the stored context (about 2.4 MB to 23 KB).
|
|
324
|
+
* Every error after a completed step now rolls back, and the failure names its step. An `argument`
|
|
325
|
+
source, `transform` or result path that raises fails the step with
|
|
326
|
+
`RubyReactor::Error::ArgumentResolutionError`: completed steps are undone, the step is neither
|
|
327
|
+
compensated (its body never started) nor retried, and the Failure carries `step_name`,
|
|
328
|
+
`reactor_name` and the original `exception_class`. Before, an argument error rolled nothing back
|
|
329
|
+
and carried no step. The same holds in a worker (an `async_step` unit, a `background` hand-off).
|
|
330
|
+
Any other exception raised outside a step body ("Execution failed: …") now rolls back completed
|
|
331
|
+
steps too, and a failure whose compensation raised ("Execution error: …") carries `step_name`.
|
|
143
332
|
* A supplied `false` reactor input or step result no longer resolves to `nil`.
|
|
144
333
|
`Context#get_input`, `Context#get_result` and `Template::Result#fetch` now check whether the key
|
|
145
334
|
exists instead of whether the value is truthy. Code that relied on `false` arriving as `nil`
|
|
@@ -204,6 +393,25 @@
|
|
|
204
393
|
* Docs: a park keeps each level's lock without a second `:lock_acquired` only while the gap stays
|
|
205
394
|
within the lock's `ttl`; a lapsed lock is acquired again.
|
|
206
395
|
|
|
396
|
+
## [0.8.5](https://github.com/arturictus/ruby_reactor/compare/v0.8.4...v0.8.5) (2026-09-30)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
### Miscellaneous Chores
|
|
400
|
+
|
|
401
|
+
* Execution flows improvements and consolidation ([#65](https://github.com/arturictus/ruby_reactor/issues/65)) ([21a4c59](https://github.com/arturictus/ruby_reactor/commit/21a4c59fa13c0e7e66c8e08ccb5f20ea43875353))
|
|
402
|
+
|
|
403
|
+
## [0.8.4](https://github.com/arturictus/ruby_reactor/compare/v0.8.3...v0.8.4) (2026-09-26)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
### ⚠ BREAKING CHANGES
|
|
407
|
+
|
|
408
|
+
* `retry_defaults` on a reactor raises RubyReactor::Error::DeprecatedDslError. Migration: move the values onto each step that should retry (`retries max_attempts: 3, backoff: :exponential, base_delay: 2` inside the step block, or on the step class once supported). A step without `retries` runs once. `max_attempts: 0` is not valid: use `max_attempts: 1` (or omit `retries`) for a step that must never retry.
|
|
409
|
+
|
|
410
|
+
### Features
|
|
411
|
+
|
|
412
|
+
* implement step-scoped retry declarations ([#61](https://github.com/arturictus/ruby_reactor/issues/61)) ([75d9c4e](https://github.com/arturictus/ruby_reactor/commit/75d9c4edca1a6ec53183d56c9c6261e792c98cc2))
|
|
413
|
+
* Inputs protection ([#63](https://github.com/arturictus/ruby_reactor/issues/63)) ([faf90e8](https://github.com/arturictus/ruby_reactor/commit/faf90e8dbfff87f6492af863b90e8247c84bd673))
|
|
414
|
+
|
|
207
415
|
## [0.8.3](https://github.com/arturictus/ruby_reactor/compare/v0.8.2...v0.8.3) (2026-09-25)
|
|
208
416
|
|
|
209
417
|
|
data/CLAUDE.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- SPECKIT START -->
|
|
2
2
|
For additional context about technologies to be used, project structure,
|
|
3
3
|
shell commands, and other important information, read the current plan
|
|
4
|
-
at specs/
|
|
4
|
+
at specs/008-rollback-reliability/plan.md
|
|
5
5
|
<!-- SPECKIT END -->
|