cmdx 1.1.2 → 1.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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/CHANGELOG.md +5 -133
  11. data/LLM.md +3317 -0
  12. data/README.md +68 -44
  13. data/docs/attributes/coercions.md +162 -0
  14. data/docs/attributes/defaults.md +90 -0
  15. data/docs/attributes/definitions.md +281 -0
  16. data/docs/attributes/naming.md +78 -0
  17. data/docs/attributes/validations.md +309 -0
  18. data/docs/basics/chain.md +56 -249
  19. data/docs/basics/context.md +56 -289
  20. data/docs/basics/execution.md +114 -0
  21. data/docs/basics/setup.md +37 -334
  22. data/docs/callbacks.md +89 -467
  23. data/docs/deprecation.md +91 -174
  24. data/docs/getting_started.md +212 -202
  25. data/docs/internationalization.md +11 -647
  26. data/docs/interruptions/exceptions.md +23 -198
  27. data/docs/interruptions/faults.md +71 -151
  28. data/docs/interruptions/halt.md +109 -186
  29. data/docs/logging.md +44 -256
  30. data/docs/middlewares.md +113 -426
  31. data/docs/outcomes/result.md +81 -228
  32. data/docs/outcomes/states.md +33 -221
  33. data/docs/outcomes/statuses.md +21 -311
  34. data/docs/tips_and_tricks.md +120 -70
  35. data/docs/workflows.md +99 -283
  36. data/lib/cmdx/.DS_Store +0 -0
  37. data/lib/cmdx/attribute.rb +229 -0
  38. data/lib/cmdx/attribute_registry.rb +94 -0
  39. data/lib/cmdx/attribute_value.rb +193 -0
  40. data/lib/cmdx/callback_registry.rb +69 -77
  41. data/lib/cmdx/chain.rb +56 -73
  42. data/lib/cmdx/coercion_registry.rb +52 -68
  43. data/lib/cmdx/coercions/array.rb +19 -18
  44. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  45. data/lib/cmdx/coercions/boolean.rb +26 -25
  46. data/lib/cmdx/coercions/complex.rb +21 -22
  47. data/lib/cmdx/coercions/date.rb +25 -23
  48. data/lib/cmdx/coercions/date_time.rb +24 -25
  49. data/lib/cmdx/coercions/float.rb +25 -22
  50. data/lib/cmdx/coercions/hash.rb +31 -32
  51. data/lib/cmdx/coercions/integer.rb +30 -24
  52. data/lib/cmdx/coercions/rational.rb +29 -24
  53. data/lib/cmdx/coercions/string.rb +19 -22
  54. data/lib/cmdx/coercions/symbol.rb +37 -0
  55. data/lib/cmdx/coercions/time.rb +26 -25
  56. data/lib/cmdx/configuration.rb +49 -108
  57. data/lib/cmdx/context.rb +222 -44
  58. data/lib/cmdx/deprecator.rb +61 -0
  59. data/lib/cmdx/errors.rb +42 -252
  60. data/lib/cmdx/exceptions.rb +39 -0
  61. data/lib/cmdx/faults.rb +78 -39
  62. data/lib/cmdx/freezer.rb +51 -0
  63. data/lib/cmdx/identifier.rb +30 -0
  64. data/lib/cmdx/locale.rb +52 -0
  65. data/lib/cmdx/log_formatters/json.rb +21 -22
  66. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  67. data/lib/cmdx/log_formatters/line.rb +15 -22
  68. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  69. data/lib/cmdx/log_formatters/raw.rb +16 -22
  70. data/lib/cmdx/middleware_registry.rb +70 -74
  71. data/lib/cmdx/middlewares/correlate.rb +90 -54
  72. data/lib/cmdx/middlewares/runtime.rb +58 -0
  73. data/lib/cmdx/middlewares/timeout.rb +48 -68
  74. data/lib/cmdx/railtie.rb +12 -45
  75. data/lib/cmdx/result.rb +229 -314
  76. data/lib/cmdx/task.rb +194 -366
  77. data/lib/cmdx/utils/call.rb +49 -0
  78. data/lib/cmdx/utils/condition.rb +71 -0
  79. data/lib/cmdx/utils/format.rb +61 -0
  80. data/lib/cmdx/validator_registry.rb +63 -72
  81. data/lib/cmdx/validators/exclusion.rb +38 -67
  82. data/lib/cmdx/validators/format.rb +48 -49
  83. data/lib/cmdx/validators/inclusion.rb +43 -74
  84. data/lib/cmdx/validators/length.rb +91 -154
  85. data/lib/cmdx/validators/numeric.rb +87 -162
  86. data/lib/cmdx/validators/presence.rb +37 -50
  87. data/lib/cmdx/version.rb +1 -1
  88. data/lib/cmdx/worker.rb +178 -0
  89. data/lib/cmdx/workflow.rb +85 -81
  90. data/lib/cmdx.rb +19 -13
  91. data/lib/generators/cmdx/install_generator.rb +14 -13
  92. data/lib/generators/cmdx/task_generator.rb +25 -50
  93. data/lib/generators/cmdx/templates/install.rb +11 -46
  94. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  95. data/lib/locales/en.yml +18 -4
  96. data/src/cmdx-logo.png +0 -0
  97. metadata +32 -116
  98. data/docs/ai_prompts.md +0 -393
  99. data/docs/basics/call.md +0 -317
  100. data/docs/configuration.md +0 -344
  101. data/docs/parameters/coercions.md +0 -396
  102. data/docs/parameters/defaults.md +0 -335
  103. data/docs/parameters/definitions.md +0 -446
  104. data/docs/parameters/namespacing.md +0 -378
  105. data/docs/parameters/validations.md +0 -405
  106. data/docs/testing.md +0 -553
  107. data/lib/cmdx/callback.rb +0 -53
  108. data/lib/cmdx/chain_inspector.rb +0 -56
  109. data/lib/cmdx/chain_serializer.rb +0 -63
  110. data/lib/cmdx/coercion.rb +0 -57
  111. data/lib/cmdx/coercions/virtual.rb +0 -29
  112. data/lib/cmdx/core_ext/hash.rb +0 -83
  113. data/lib/cmdx/core_ext/module.rb +0 -98
  114. data/lib/cmdx/core_ext/object.rb +0 -125
  115. data/lib/cmdx/correlator.rb +0 -122
  116. data/lib/cmdx/error.rb +0 -67
  117. data/lib/cmdx/fault.rb +0 -140
  118. data/lib/cmdx/immutator.rb +0 -52
  119. data/lib/cmdx/lazy_struct.rb +0 -246
  120. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  121. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  122. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  123. data/lib/cmdx/logger.rb +0 -49
  124. data/lib/cmdx/logger_ansi.rb +0 -68
  125. data/lib/cmdx/logger_serializer.rb +0 -116
  126. data/lib/cmdx/middleware.rb +0 -70
  127. data/lib/cmdx/parameter.rb +0 -312
  128. data/lib/cmdx/parameter_evaluator.rb +0 -231
  129. data/lib/cmdx/parameter_inspector.rb +0 -66
  130. data/lib/cmdx/parameter_registry.rb +0 -106
  131. data/lib/cmdx/parameter_serializer.rb +0 -59
  132. data/lib/cmdx/result_ansi.rb +0 -71
  133. data/lib/cmdx/result_inspector.rb +0 -71
  134. data/lib/cmdx/result_logger.rb +0 -59
  135. data/lib/cmdx/result_serializer.rb +0 -104
  136. data/lib/cmdx/rspec/matchers.rb +0 -28
  137. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  138. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  139. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  141. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  142. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  143. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  144. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  145. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  146. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  147. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  148. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  149. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  150. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  151. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  152. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  153. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  154. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  155. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  156. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  157. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  158. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  159. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  160. data/lib/cmdx/task_deprecator.rb +0 -58
  161. data/lib/cmdx/task_processor.rb +0 -246
  162. data/lib/cmdx/task_serializer.rb +0 -57
  163. data/lib/cmdx/utils/ansi_color.rb +0 -73
  164. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  165. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  166. data/lib/cmdx/utils/name_affix.rb +0 -52
  167. data/lib/cmdx/validator.rb +0 -57
  168. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  169. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  170. data/lib/locales/ar.yml +0 -35
  171. data/lib/locales/cs.yml +0 -35
  172. data/lib/locales/da.yml +0 -35
  173. data/lib/locales/de.yml +0 -35
  174. data/lib/locales/el.yml +0 -35
  175. data/lib/locales/es.yml +0 -35
  176. data/lib/locales/fi.yml +0 -35
  177. data/lib/locales/fr.yml +0 -35
  178. data/lib/locales/he.yml +0 -35
  179. data/lib/locales/hi.yml +0 -35
  180. data/lib/locales/it.yml +0 -35
  181. data/lib/locales/ja.yml +0 -35
  182. data/lib/locales/ko.yml +0 -35
  183. data/lib/locales/nl.yml +0 -35
  184. data/lib/locales/no.yml +0 -35
  185. data/lib/locales/pl.yml +0 -35
  186. data/lib/locales/pt.yml +0 -35
  187. data/lib/locales/ru.yml +0 -35
  188. data/lib/locales/sv.yml +0 -35
  189. data/lib/locales/th.yml +0 -35
  190. data/lib/locales/tr.yml +0 -35
  191. data/lib/locales/vi.yml +0 -35
  192. data/lib/locales/zh.yml +0 -35
data/docs/basics/chain.md CHANGED
@@ -4,123 +4,106 @@ Chains automatically group related task executions within a thread, providing un
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [TLDR](#tldr)
8
- - [Thread-Local Chain Management](#thread-local-chain-management)
9
- - [Automatic Chain Creation](#automatic-chain-creation)
10
- - [Chain Inheritance](#chain-inheritance)
11
- - [Chain Structure and Metadata](#chain-structure-and-metadata)
12
- - [Correlation ID Integration](#correlation-id-integration)
13
- - [State Delegation](#state-delegation)
14
- - [Serialization and Logging](#serialization-and-logging)
15
- - [Error Handling](#error-handling)
7
+ - [Management](#management)
8
+ - [Links](#links)
9
+ - [Inheritance](#inheritance)
10
+ - [Structure](#structure)
16
11
 
17
- ## TLDR
12
+ ## Management
18
13
 
19
- ```ruby
20
- # Automatic chain creation per thread
21
- result = ProcessOrderTask.call(order_id: 123)
22
- result.chain.id # Unique chain ID
23
- result.chain.results.size # All tasks in this chain
24
-
25
- # Access current thread's chain
26
- CMDx::Chain.current # Current chain or nil
27
- CMDx::Chain.clear # Clear thread's chain
14
+ Each thread maintains its own chain context through thread-local storage, providing automatic isolation without manual coordination.
28
15
 
29
- # Subtasks automatically inherit chain
30
- class ProcessOrderTask < CMDx::Task
31
- def call
32
- # These inherit the same chain automatically
33
- ValidateOrderTask.call!(order_id: order_id)
34
- ChargePaymentTask.call!(order_id: order_id)
35
- end
36
- end
37
- ```
38
-
39
- ## Thread-Local Chain Management
40
-
41
- > [!NOTE]
42
- > Each thread maintains its own chain context through thread-local storage, providing automatic isolation without manual coordination.
16
+ > [!WARNING]
17
+ > Chain operations are thread-local. Never share chain references across threads as this can lead to race conditions and data corruption.
43
18
 
44
19
  ```ruby
45
20
  # Thread A
46
21
  Thread.new do
47
- result = ProcessOrderTask.call(order_id: 123)
48
- result.chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5"
22
+ result = ImportDataset.execute(file_path: "/data/batch1.csv")
23
+ result.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
49
24
  end
50
25
 
51
26
  # Thread B (completely separate chain)
52
27
  Thread.new do
53
- result = ProcessOrderTask.call(order_id: 456)
54
- result.chain.id # "018c2b95-c821-7892-b156-dd7c921fe2a3"
28
+ result = ImportDataset.execute(file_path: "/data/batch2.csv")
29
+ result.chain.id #=> "z3a42b95-c821-7892-b156-dd7c921fe2a3"
55
30
  end
56
31
 
57
32
  # Access current thread's chain
58
- CMDx::Chain.current # Returns current chain or nil
59
- CMDx::Chain.clear # Clears current thread's chain
33
+ CMDx::Chain.current #=> Returns current chain or nil
34
+ CMDx::Chain.clear #=> Clears current thread's chain
60
35
  ```
61
36
 
62
- ## Automatic Chain Creation
37
+ ## Links
63
38
 
64
39
  Every task execution automatically creates or joins the current thread's chain:
65
40
 
66
- ```ruby
67
- # First task creates new chain
68
- result1 = ProcessOrderTask.call(order_id: 123)
69
- result1.chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5"
70
- result1.chain.results.size # 1
71
-
72
- # Second task joins existing chain
73
- result2 = SendEmailTask.call(to: "user@example.com")
74
- result2.chain.id == result1.chain.id # true
75
- result2.chain.results.size # 2
41
+ > [!IMPORTANT]
42
+ > Chain creation is automatic and transparent. You don't need to manually manage chain lifecycle.
76
43
 
77
- # Both results reference the same chain
78
- result1.chain.results == result2.chain.results # true
44
+ ```ruby
45
+ class ImportDataset < CMDx::Task
46
+ def work
47
+ # First task creates new chain
48
+ result1 = ValidateHeaders.execute(file_path: context.file_path)
49
+ result1.chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
50
+ result1.chain.results.size #=> 1
51
+
52
+ # Second task joins existing chain
53
+ result2 = SendNotification.execute(to: "admin@company.com")
54
+ result2.chain.id == result1.chain.id #=> true
55
+ result2.chain.results.size #=> 2
56
+
57
+ # Both results reference the same chain
58
+ result1.chain.results == result2.chain.results #=> true
59
+ end
60
+ end
79
61
  ```
80
62
 
81
- ## Chain Inheritance
63
+ ## Inheritance
82
64
 
83
- > [!IMPORTANT]
84
- > When tasks call subtasks within the same thread, all executions automatically inherit the current chain, creating a unified execution trail.
65
+ When tasks call subtasks within the same thread, all executions automatically inherit the current chain, creating a unified execution trail.
85
66
 
86
67
  ```ruby
87
- class ProcessOrderTask < CMDx::Task
88
- def call
89
- context.order = Order.find(order_id)
68
+ class ImportDataset < CMDx::Task
69
+ def work
70
+ context.dataset = Dataset.find(context.dataset_id)
90
71
 
91
72
  # Subtasks automatically inherit current chain
92
- ValidateOrderTask.call!(order_id: order_id)
93
- ChargePaymentTask.call!(order_id: order_id)
94
- SendConfirmationTask.call!(order_id: order_id)
73
+ ValidateSchema.execute
74
+ TransformData.execute!(context)
75
+ SaveToDatabase.execute(dataset_id: context.dataset_id)
95
76
  end
96
77
  end
97
78
 
98
- result = ProcessOrderTask.call(order_id: 123)
79
+ result = ImportDataset.execute(dataset_id: 456)
99
80
  chain = result.chain
100
81
 
101
82
  # All tasks share the same chain
102
- chain.results.size # 4 (main task + 3 subtasks)
103
- chain.results.map(&:task).map(&:class)
104
- # [ProcessOrderTask, ValidateOrderTask, ChargePaymentTask, SendConfirmationTask]
83
+ chain.results.size #=> 4 (main task + 3 subtasks)
84
+ chain.results.map { |r| r.task.class }
85
+ #=> [ImportDataset, ValidateSchema, TransformData, SaveToDatabase]
105
86
  ```
106
87
 
107
- ## Chain Structure and Metadata
88
+ ## Structure
108
89
 
109
90
  Chains provide comprehensive execution information with state delegation:
110
91
 
92
+ > [!IMPORTANT]
93
+ > Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
94
+
111
95
  ```ruby
112
- result = ProcessOrderTask.call(order_id: 123)
96
+ result = ImportDataset.execute(dataset_id: 456)
113
97
  chain = result.chain
114
98
 
115
99
  # Chain identification
116
- chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5"
117
- chain.results # Array of all results in execution order
100
+ chain.id #=> "018c2b95-b764-7615-a924-cc5b910ed1e5"
101
+ chain.results #=> Array of all results in execution order
118
102
 
119
103
  # State delegation (from first/outer-most result)
120
- chain.state # "complete"
121
- chain.status # "success"
122
- chain.outcome # "success"
123
- chain.runtime # 1.2 (total execution time)
104
+ chain.state #=> "complete"
105
+ chain.status #=> "success"
106
+ chain.outcome #=> "success"
124
107
 
125
108
  # Access individual results
126
109
  chain.results.each_with_index do |result, index|
@@ -128,182 +111,6 @@ chain.results.each_with_index do |result, index|
128
111
  end
129
112
  ```
130
113
 
131
- ## Correlation ID Integration
132
-
133
- > [!TIP]
134
- > Chain IDs serve as correlation identifiers, enabling request tracing across distributed systems and complex workflows.
135
-
136
- ### Automatic Correlation
137
-
138
- Chains integrate with the correlation system using hierarchical precedence:
139
-
140
- ```ruby
141
- # 1. Existing chain ID takes precedence
142
- CMDx::Chain.current = CMDx::Chain.new(id: "request-123")
143
- result = ProcessOrderTask.call(order_id: 456)
144
- result.chain.id # "request-123"
145
-
146
- # 2. Thread-local correlation used if no chain exists
147
- CMDx::Chain.clear
148
- CMDx::Correlator.id = "session-456"
149
- result = ProcessOrderTask.call(order_id: 789)
150
- result.chain.id # "session-456"
151
-
152
- # 3. Generated UUID when no correlation exists
153
- CMDx::Correlator.clear
154
- result = ProcessOrderTask.call(order_id: 101)
155
- result.chain.id # "018c2b95-b764-7615-a924-cc5b910ed1e5" (generated)
156
- ```
157
-
158
- ### Custom Chain IDs
159
-
160
- ```ruby
161
- # Create chain with specific correlation ID
162
- chain = CMDx::Chain.new(id: "api-request-789")
163
- CMDx::Chain.current = chain
164
-
165
- result = ProcessApiRequestTask.call(data: payload)
166
- result.chain.id # "api-request-789"
167
-
168
- # All subtasks inherit the same correlation ID
169
- result.chain.results.all? { |r| r.chain.id == "api-request-789" } # true
170
- ```
171
-
172
- ### Correlation Context Management
173
-
174
- ```ruby
175
- # Scoped correlation context
176
- CMDx::Correlator.use("user-session-123") do
177
- result = ProcessUserActionTask.call(action: "purchase")
178
- result.chain.id # "user-session-123"
179
-
180
- # Nested operations inherit correlation
181
- AuditLogTask.call(event: "purchase_completed")
182
- end
183
-
184
- # Outside block, correlation context restored
185
- result = OtherTask.call
186
- result.chain.id # Different correlation ID
187
- ```
188
-
189
- ## State Delegation
190
-
191
- > [!WARNING]
192
- > Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
193
-
194
- ```ruby
195
- class ProcessOrderTask < CMDx::Task
196
- def call
197
- ValidateOrderTask.call!(order_id: order_id) # Success
198
- ChargePaymentTask.call!(order_id: order_id) # Failure
199
- end
200
- end
201
-
202
- result = ProcessOrderTask.call(order_id: 123)
203
- chain = result.chain
204
-
205
- # Chain delegates to main task (first result)
206
- chain.status # "failed" (ProcessOrderTask failed due to subtask)
207
- chain.state # "interrupted"
208
-
209
- # Individual results maintain their own state
210
- chain.results[0].status # "failed" (ProcessOrderTask - main)
211
- chain.results[1].status # "success" (ValidateOrderTask)
212
- chain.results[2].status # "failed" (ChargePaymentTask)
213
- ```
214
-
215
- ## Serialization and Logging
216
-
217
- Chains provide comprehensive serialization for monitoring and debugging:
218
-
219
- ```ruby
220
- result = ProcessOrderTask.call(order_id: 123)
221
- chain = result.chain
222
-
223
- # Structured data representation
224
- chain.to_h
225
- # {
226
- # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
227
- # state: "complete",
228
- # status: "success",
229
- # outcome: "success",
230
- # runtime: 0.8,
231
- # results: [
232
- # { class: "ProcessOrderTask", state: "complete", status: "success", ... },
233
- # { class: "ValidateOrderTask", state: "complete", status: "success", ... },
234
- # { class: "ChargePaymentTask", state: "complete", status: "success", ... }
235
- # ]
236
- # }
237
-
238
- # Human-readable execution summary
239
- puts chain.to_s
240
- # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
241
- # ================================================
242
- #
243
- # ProcessOrderTask: index=0 state=complete status=success runtime=0.8
244
- # ValidateOrderTask: index=1 state=complete status=success runtime=0.1
245
- # ChargePaymentTask: index=2 state=complete status=success runtime=0.5
246
- #
247
- # ================================================
248
- # state: complete | status: success | outcome: success | runtime: 0.8
249
- ```
250
-
251
- ## Error Handling
252
-
253
- ### Chain Access Patterns
254
-
255
- ```ruby
256
- # Safe chain access
257
- result = ProcessOrderTask.call(order_id: 123)
258
-
259
- if result.chain
260
- correlation_id = result.chain.id
261
- execution_count = result.chain.results.size
262
- else
263
- # Handle missing chain (shouldn't happen in normal execution)
264
- correlation_id = "unknown"
265
- end
266
- ```
267
-
268
- ### Thread Safety
269
-
270
- > [!IMPORTANT]
271
- > Chain operations are thread-safe within individual threads but chains should not be shared across threads. Each thread maintains its own isolated chain context.
272
-
273
- ```ruby
274
- # Safe: Each thread has its own chain
275
- threads = 3.times.map do |i|
276
- Thread.new do
277
- result = ProcessOrderTask.call(order_id: 100 + i)
278
- result.chain.id # Unique per thread
279
- end
280
- end
281
-
282
- # Collect results safely
283
- chain_ids = threads.map(&:value)
284
- chain_ids.uniq.size # 3 (all different)
285
- ```
286
-
287
- ### Chain State Validation
288
-
289
- ```ruby
290
- result = ProcessOrderTask.call(order_id: 123)
291
- chain = result.chain
292
-
293
- # Validate chain integrity
294
- case chain.state
295
- when "complete"
296
- # All tasks finished normally
297
- process_successful_chain(chain)
298
- when "interrupted"
299
- # Task was halted or failed
300
- handle_chain_interruption(chain)
301
- else
302
- # Unexpected state
303
- log_chain_anomaly(chain)
304
- end
305
- ```
306
-
307
114
  ---
308
115
 
309
116
  - **Prev:** [Basics - Context](context.md)