ruby_reactor 0.8.6 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/.specify/feature.json +1 -1
  4. data/CHANGELOG.md +81 -0
  5. data/CLAUDE.md +1 -1
  6. data/README.md +42 -3
  7. data/lib/ruby_reactor/dsl/map_builder.rb +20 -1
  8. data/lib/ruby_reactor/error/recorded_failure.rb +18 -0
  9. data/lib/ruby_reactor/executor/compensation_manager.rb +27 -1
  10. data/lib/ruby_reactor/executor/result_handler.rb +16 -0
  11. data/lib/ruby_reactor/executor/retry_manager.rb +2 -2
  12. data/lib/ruby_reactor/executor/step_executor.rb +20 -0
  13. data/lib/ruby_reactor/executor.rb +62 -10
  14. data/lib/ruby_reactor/interrupt_claims.rb +81 -0
  15. data/lib/ruby_reactor/map/step_rollback.rb +121 -0
  16. data/lib/ruby_reactor/reactor.rb +270 -105
  17. data/lib/ruby_reactor/rspec/matchers.rb +64 -16
  18. data/lib/ruby_reactor/rspec/test_subject.rb +25 -24
  19. data/lib/ruby_reactor/step/compose_step.rb +10 -0
  20. data/lib/ruby_reactor/step/map_step.rb +12 -0
  21. data/lib/ruby_reactor/storage/adapter.rb +17 -0
  22. data/lib/ruby_reactor/storage/redis_adapter.rb +42 -0
  23. data/lib/ruby_reactor/version.rb +1 -1
  24. data/lib/ruby_reactor/web/api.rb +17 -0
  25. data/lib/ruby_reactor/web/public/assets/{index-C6ejszCQ.js → index-DxjQlW7Q.js} +8 -8
  26. data/lib/ruby_reactor/web/public/index.html +1 -1
  27. data/lib/ruby_reactor/worker.rb +73 -6
  28. data/specs/010-interrupt-in-composed-child/checklists/requirements.md +43 -0
  29. data/specs/010-interrupt-in-composed-child/contracts/api-surface.md +75 -0
  30. data/specs/010-interrupt-in-composed-child/data-model.md +40 -0
  31. data/specs/010-interrupt-in-composed-child/plan.md +149 -0
  32. data/specs/010-interrupt-in-composed-child/quickstart.md +64 -0
  33. data/specs/010-interrupt-in-composed-child/research.md +207 -0
  34. data/specs/010-interrupt-in-composed-child/spec.md +267 -0
  35. data/specs/010-interrupt-in-composed-child/tasks.md +315 -0
  36. data/specs/010-rollback-follow-ups/checklists/requirements.md +47 -0
  37. data/specs/010-rollback-follow-ups/contracts/api-surface.md +123 -0
  38. data/specs/010-rollback-follow-ups/contracts/resume-protocol.md +132 -0
  39. data/specs/010-rollback-follow-ups/data-model.md +139 -0
  40. data/specs/010-rollback-follow-ups/plan.md +273 -0
  41. data/specs/010-rollback-follow-ups/pr-description.md +110 -0
  42. data/specs/010-rollback-follow-ups/quickstart.md +157 -0
  43. data/specs/010-rollback-follow-ups/research.md +440 -0
  44. data/specs/010-rollback-follow-ups/spec.md +489 -0
  45. data/specs/010-rollback-follow-ups/tasks.md +1022 -0
  46. data/specs/future_improvements.md +17 -14
  47. metadata +22 -2
@@ -59,8 +59,8 @@ Found while auditing for 005 R-19:
59
59
  | `Worker#handle_deserialization_failure` | before any executor | Writes a failed payload with no lock. |
60
60
  | ~~Map collector, failure branch~~ | ~~`Map::Helpers#resume_parent_execution`~~ | Fixed by 009 R-03: the collector writes no context; it signals the owner run, whose Worker adopts the map's outcome. |
61
61
  | `Reactor.cancel` | any process (app code, console) | `find` → mutate → `save_context`: a blind write racing a live worker. (`Reactor.undo` holds the run's `async:` lock since 009 R-12.) |
62
- | `Reactor#continue` (interrupt resume) | web request / app code | Writes the payload before `resume_execution` takes the lock. |
63
- | Synchronous `Reactor.run` / `Executor#execute` | caller's process | Never takes the context lock, so its saves are unfenced. |
62
+ | ~~`Reactor#continue` (interrupt resume)~~ | ~~web request / app code~~ | Fixed by 010 R-04/R-05: the payload is claimed in its own key; the run is written only after taking its lock and reloading it (or by the Worker it hands off to). |
63
+ | ~~Synchronous `Reactor.run` / `Executor#execute`~~ | ~~caller's process~~ | Fixed by 010 R-01: it holds the run's `async:` lock for its whole run and saves before releasing it. |
64
64
 
65
65
  Writers that already belong to the owning execution:
66
66
  - `Executor#save_context` and `#checkpoint!` on the worker path;
@@ -117,6 +117,10 @@ create-only `SET NX` equivalent. It covers `Reactor#save_context` at enqueue and
117
117
 
118
118
  #### 2. Lock, then load
119
119
 
120
+ **Status (010):** done for `Worker#perform` (R-02), `Reactor#continue` (R-05) and `Reactor#undo`
121
+ (R-09); a synchronous `Reactor.run` holds the lock for its whole run (R-01). Versioned writes
122
+ (part 1) and the remaining writers above are still open.
123
+
120
124
  - A worker takes the context lock **before** it reads the context. The restructure:
121
125
  `Worker#perform` acquires `async:<root id>`, then retrieves and deserializes, then hands
122
126
  the token and the loaded version to `Executor`. Today `resume_execution` acquires the lock
@@ -222,18 +226,6 @@ All against real Redis, per the constitution.
222
226
  - Docs: `locks_and_semaphores.md`, `background_and_async.md`, and the README's "Durability &
223
227
  Recovery" section.
224
228
 
225
- ## Interrupt inside a composed child
226
-
227
- **Status:** unsupported on main, found while implementing 009 (US2-AS2). An `interrupt` in a
228
- `compose`d child does not pause the root: `ComposeStep#handle_execution_result` calls `success?` on
229
- the child's `InterruptResult`, and the compose step fails with `NoMethodError`. It fails the same
230
- way with or without a fan-out map in the child.
231
-
232
- Direction: propagate the child's `InterruptResult` as the compose step's result, so the root
233
- pauses; let `Reactor.continue` and the `be_paused_at` matcher name the nested interrupt (a step
234
- path such as `:fulfil, :approve`); and resume through `ComposeStep#run`, which already re-enters
235
- an admitted child. Spec: `spec/map/map_compose_fan_out_spec.rb` keeps a `pending` example for it.
236
-
237
229
  ## Rollback follow-ups (008)
238
230
 
239
231
  Raised while implementing specs/008-rollback-reliability. None blocks it.
@@ -284,3 +276,14 @@ Raised while implementing specs/008-rollback-reliability. None blocks it.
284
276
  saves `running` are separated only by the per-run context lock, which inline Sidekiq testing
285
277
  skips. Accepted for now (008 review, 2026-09-27). Direction: claim the resume with an atomic
286
278
  status compare-and-set.
279
+ ## Interrupt inside a composed child
280
+
281
+ **Status:** unsupported on main, found while implementing 009 (US2-AS2). An `interrupt` in a
282
+ `compose`d child does not pause the root: `ComposeStep#handle_execution_result` calls `success?` on
283
+ the child's `InterruptResult`, and the compose step fails with `NoMethodError`. It fails the same
284
+ way with or without a fan-out map in the child.
285
+
286
+ Direction: propagate the child's `InterruptResult` as the compose step's result, so the root
287
+ pauses; let `Reactor.continue` and the `be_paused_at` matcher name the nested interrupt (a step
288
+ path such as `:fulfil, :approve`); and resume through `ComposeStep#run`, which already re-enters
289
+ an admitted child. Spec: `spec/map/map_compose_fan_out_spec.rb` keeps a `pending` example for it.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_reactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.8.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artur
@@ -184,6 +184,7 @@ files:
184
184
  - lib/ruby_reactor/error/execution_parked.rb
185
185
  - lib/ruby_reactor/error/input_validation_error.rb
186
186
  - lib/ruby_reactor/error/reactor_contention_park.rb
187
+ - lib/ruby_reactor/error/recorded_failure.rb
187
188
  - lib/ruby_reactor/error/rescuable.rb
188
189
  - lib/ruby_reactor/error/rollback_handed_off.rb
189
190
  - lib/ruby_reactor/error/schema_version_error.rb
@@ -202,6 +203,7 @@ files:
202
203
  - lib/ruby_reactor/executor/retry_manager.rb
203
204
  - lib/ruby_reactor/executor/step_coordination.rb
204
205
  - lib/ruby_reactor/executor/step_executor.rb
206
+ - lib/ruby_reactor/interrupt_claims.rb
205
207
  - lib/ruby_reactor/interrupt_result.rb
206
208
  - lib/ruby_reactor/lock.rb
207
209
  - lib/ruby_reactor/map.rb
@@ -273,8 +275,8 @@ files:
273
275
  - lib/ruby_reactor/web/application.rb
274
276
  - lib/ruby_reactor/web/config.ru
275
277
  - lib/ruby_reactor/web/coordination_serializer.rb
276
- - lib/ruby_reactor/web/public/assets/index-C6ejszCQ.js
277
278
  - lib/ruby_reactor/web/public/assets/index-DM5darhy.css
279
+ - lib/ruby_reactor/web/public/assets/index-DxjQlW7Q.js
278
280
  - lib/ruby_reactor/web/public/index.html
279
281
  - lib/ruby_reactor/web/public/vite.svg
280
282
  - lib/ruby_reactor/worker.rb
@@ -330,6 +332,24 @@ files:
330
332
  - specs/009-distributed-map-undo/research.md
331
333
  - specs/009-distributed-map-undo/spec.md
332
334
  - specs/009-distributed-map-undo/tasks.md
335
+ - specs/010-interrupt-in-composed-child/checklists/requirements.md
336
+ - specs/010-interrupt-in-composed-child/contracts/api-surface.md
337
+ - specs/010-interrupt-in-composed-child/data-model.md
338
+ - specs/010-interrupt-in-composed-child/plan.md
339
+ - specs/010-interrupt-in-composed-child/quickstart.md
340
+ - specs/010-interrupt-in-composed-child/research.md
341
+ - specs/010-interrupt-in-composed-child/spec.md
342
+ - specs/010-interrupt-in-composed-child/tasks.md
343
+ - specs/010-rollback-follow-ups/checklists/requirements.md
344
+ - specs/010-rollback-follow-ups/contracts/api-surface.md
345
+ - specs/010-rollback-follow-ups/contracts/resume-protocol.md
346
+ - specs/010-rollback-follow-ups/data-model.md
347
+ - specs/010-rollback-follow-ups/plan.md
348
+ - specs/010-rollback-follow-ups/pr-description.md
349
+ - specs/010-rollback-follow-ups/quickstart.md
350
+ - specs/010-rollback-follow-ups/research.md
351
+ - specs/010-rollback-follow-ups/spec.md
352
+ - specs/010-rollback-follow-ups/tasks.md
333
353
  - specs/future_improvements.md
334
354
  - specs/scoped_rspec_matchers.md
335
355
  - specs/specs-inputs-by-method-md-piped-wigderson.md