cmdx 0.5.0 → 1.0.1

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/rules/cursor-instructions.mdc +6 -0
  4. data/.rubocop.yml +19 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +95 -28
  7. data/README.md +73 -25
  8. data/docs/ai_prompts.md +319 -0
  9. data/docs/basics/call.md +234 -14
  10. data/docs/basics/chain.md +280 -0
  11. data/docs/basics/context.md +241 -33
  12. data/docs/basics/setup.md +85 -12
  13. data/docs/callbacks.md +283 -0
  14. data/docs/configuration.md +155 -30
  15. data/docs/getting_started.md +145 -22
  16. data/docs/internationalization.md +148 -0
  17. data/docs/interruptions/exceptions.md +198 -11
  18. data/docs/interruptions/faults.md +196 -44
  19. data/docs/interruptions/halt.md +188 -35
  20. data/docs/logging.md +204 -53
  21. data/docs/middlewares.md +745 -0
  22. data/docs/outcomes/result.md +305 -10
  23. data/docs/outcomes/states.md +212 -31
  24. data/docs/outcomes/statuses.md +284 -30
  25. data/docs/parameters/coercions.md +411 -29
  26. data/docs/parameters/defaults.md +258 -25
  27. data/docs/parameters/definitions.md +247 -72
  28. data/docs/parameters/namespacing.md +259 -27
  29. data/docs/parameters/validations.md +173 -168
  30. data/docs/testing.md +560 -0
  31. data/docs/tips_and_tricks.md +103 -42
  32. data/docs/workflows.md +329 -0
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +69 -0
  35. data/lib/cmdx/callback_registry.rb +106 -0
  36. data/lib/cmdx/chain.rb +190 -0
  37. data/lib/cmdx/chain_inspector.rb +149 -0
  38. data/lib/cmdx/chain_serializer.rb +175 -0
  39. data/lib/cmdx/coercions/array.rb +37 -0
  40. data/lib/cmdx/coercions/big_decimal.rb +33 -0
  41. data/lib/cmdx/coercions/boolean.rb +41 -1
  42. data/lib/cmdx/coercions/complex.rb +31 -0
  43. data/lib/cmdx/coercions/date.rb +39 -0
  44. data/lib/cmdx/coercions/date_time.rb +39 -0
  45. data/lib/cmdx/coercions/float.rb +31 -0
  46. data/lib/cmdx/coercions/hash.rb +42 -0
  47. data/lib/cmdx/coercions/integer.rb +32 -0
  48. data/lib/cmdx/coercions/rational.rb +31 -0
  49. data/lib/cmdx/coercions/string.rb +31 -0
  50. data/lib/cmdx/coercions/time.rb +39 -0
  51. data/lib/cmdx/coercions/virtual.rb +31 -0
  52. data/lib/cmdx/configuration.rb +217 -9
  53. data/lib/cmdx/context.rb +173 -2
  54. data/lib/cmdx/core_ext/hash.rb +72 -0
  55. data/lib/cmdx/core_ext/module.rb +94 -0
  56. data/lib/cmdx/core_ext/object.rb +105 -0
  57. data/lib/cmdx/correlator.rb +217 -0
  58. data/lib/cmdx/error.rb +210 -8
  59. data/lib/cmdx/errors.rb +256 -1
  60. data/lib/cmdx/fault.rb +177 -2
  61. data/lib/cmdx/faults.rb +158 -2
  62. data/lib/cmdx/immutator.rb +121 -2
  63. data/lib/cmdx/lazy_struct.rb +261 -18
  64. data/lib/cmdx/log_formatters/json.rb +46 -0
  65. data/lib/cmdx/log_formatters/key_value.rb +46 -0
  66. data/lib/cmdx/log_formatters/line.rb +54 -0
  67. data/lib/cmdx/log_formatters/logstash.rb +64 -0
  68. data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
  69. data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
  70. data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
  71. data/lib/cmdx/log_formatters/raw.rb +54 -0
  72. data/lib/cmdx/logger.rb +85 -0
  73. data/lib/cmdx/logger_ansi.rb +93 -7
  74. data/lib/cmdx/logger_serializer.rb +116 -0
  75. data/lib/cmdx/middleware.rb +74 -0
  76. data/lib/cmdx/middleware_registry.rb +106 -0
  77. data/lib/cmdx/middlewares/correlate.rb +266 -0
  78. data/lib/cmdx/middlewares/timeout.rb +232 -0
  79. data/lib/cmdx/parameter.rb +228 -1
  80. data/lib/cmdx/parameter_inspector.rb +61 -0
  81. data/lib/cmdx/parameter_registry.rb +125 -0
  82. data/lib/cmdx/parameter_serializer.rb +83 -0
  83. data/lib/cmdx/parameter_validator.rb +62 -0
  84. data/lib/cmdx/parameter_value.rb +109 -1
  85. data/lib/cmdx/parameters_inspector.rb +59 -0
  86. data/lib/cmdx/parameters_serializer.rb +102 -0
  87. data/lib/cmdx/railtie.rb +123 -3
  88. data/lib/cmdx/result.rb +367 -25
  89. data/lib/cmdx/result_ansi.rb +105 -9
  90. data/lib/cmdx/result_inspector.rb +76 -0
  91. data/lib/cmdx/result_logger.rb +90 -3
  92. data/lib/cmdx/result_serializer.rb +137 -0
  93. data/lib/cmdx/rspec/result_matchers.rb +917 -0
  94. data/lib/cmdx/rspec/task_matchers.rb +570 -0
  95. data/lib/cmdx/task.rb +405 -37
  96. data/lib/cmdx/task_serializer.rb +74 -2
  97. data/lib/cmdx/utils/ansi_color.rb +95 -0
  98. data/lib/cmdx/utils/log_timestamp.rb +48 -0
  99. data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
  100. data/lib/cmdx/utils/name_affix.rb +78 -0
  101. data/lib/cmdx/validators/custom.rb +82 -0
  102. data/lib/cmdx/validators/exclusion.rb +94 -0
  103. data/lib/cmdx/validators/format.rb +102 -8
  104. data/lib/cmdx/validators/inclusion.rb +104 -0
  105. data/lib/cmdx/validators/length.rb +128 -0
  106. data/lib/cmdx/validators/numeric.rb +128 -0
  107. data/lib/cmdx/validators/presence.rb +93 -7
  108. data/lib/cmdx/version.rb +7 -1
  109. data/lib/cmdx/workflow.rb +394 -0
  110. data/lib/cmdx.rb +25 -64
  111. data/lib/generators/cmdx/install_generator.rb +37 -1
  112. data/lib/generators/cmdx/task_generator.rb +69 -1
  113. data/lib/generators/cmdx/templates/install.rb +43 -15
  114. data/lib/generators/cmdx/workflow_generator.rb +109 -0
  115. data/lib/locales/ar.yml +36 -0
  116. data/lib/locales/cs.yml +36 -0
  117. data/lib/locales/da.yml +36 -0
  118. data/lib/locales/de.yml +36 -0
  119. data/lib/locales/el.yml +36 -0
  120. data/lib/locales/en.yml +20 -20
  121. data/lib/locales/es.yml +20 -20
  122. data/lib/locales/fi.yml +36 -0
  123. data/lib/locales/fr.yml +36 -0
  124. data/lib/locales/he.yml +36 -0
  125. data/lib/locales/hi.yml +36 -0
  126. data/lib/locales/it.yml +36 -0
  127. data/lib/locales/ja.yml +36 -0
  128. data/lib/locales/ko.yml +36 -0
  129. data/lib/locales/nl.yml +36 -0
  130. data/lib/locales/no.yml +36 -0
  131. data/lib/locales/pl.yml +36 -0
  132. data/lib/locales/pt.yml +36 -0
  133. data/lib/locales/ru.yml +36 -0
  134. data/lib/locales/sv.yml +36 -0
  135. data/lib/locales/th.yml +36 -0
  136. data/lib/locales/tr.yml +36 -0
  137. data/lib/locales/vi.yml +36 -0
  138. data/lib/locales/zh.yml +36 -0
  139. metadata +77 -15
  140. data/docs/basics/run.md +0 -34
  141. data/docs/batch.md +0 -53
  142. data/docs/example.md +0 -82
  143. data/docs/hooks.md +0 -62
  144. data/lib/cmdx/batch.rb +0 -43
  145. data/lib/cmdx/parameters.rb +0 -35
  146. data/lib/cmdx/run.rb +0 -39
  147. data/lib/cmdx/run_inspector.rb +0 -26
  148. data/lib/cmdx/run_serializer.rb +0 -20
  149. data/lib/cmdx/task_hook.rb +0 -18
  150. data/lib/generators/cmdx/batch_generator.rb +0 -30
  151. /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
@@ -1,17 +1,312 @@
1
- # Outcomes - Result
1
+ # Outcomes - Result
2
2
 
3
- The result object is returned after a task execution. This is the main object
4
- that will be interacted with post call.
3
+ The result object is the comprehensive return value of task execution, providing
4
+ complete information about the execution outcome, state, timing, and any data
5
+ produced during the task lifecycle. Results serve as the primary interface for
6
+ inspecting task execution outcomes and chaining task operations.
7
+
8
+ ## Table of Contents
9
+
10
+ - [TLDR](#tldr)
11
+ - [Core Result Attributes](#core-result-attributes)
12
+ - [State and Status Information](#state-and-status-information)
13
+ - [Execution Outcome Analysis](#execution-outcome-analysis)
14
+ - [Runtime and Performance](#runtime-and-performance)
15
+ - [Failure Chain Analysis](#failure-chain-analysis)
16
+ - [Index and Position](#index-and-position)
17
+ - [Result Callbacks and Chaining](#result-callbacks-and-chaining)
18
+ - [Pattern Matching](#pattern-matching)
19
+ - [Serialization and Inspection](#serialization-and-inspection)
20
+
21
+ ## TLDR
22
+
23
+ - **Result object** - Comprehensive return value from task execution with `task`, `context`, `chain`, `metadata`
24
+ - **Status checking** - Use `result.success?`, `result.failed?`, `result.skipped?` for outcomes
25
+ - **State checking** - Use `result.complete?`, `result.interrupted?`, `result.executed?` for lifecycle
26
+ - **Callbacks** - Chain with `.on_success`, `.on_failed`, `.on_good`, `.on_bad` for conditional logic
27
+ - **Failure analysis** - Use `result.caused_failure`, `result.threw_failure` to trace failure chains
28
+
29
+ ## Core Result Attributes
30
+
31
+ Every result provides access to essential execution information:
5
32
 
6
33
  ```ruby
7
- result = ProcessOrderTask.call
8
- result.task #=> <ProcessOrderTask ...>
9
- result.context #=> <CMDx::Context ...>
10
- result.metadata #=> { ... }
11
- result.run #=> <CMDx::Run ...>
34
+ result = ProcessUserOrderTask.call(order_id: 123)
35
+
36
+ # Core objects
37
+ result.task #=> <ProcessUserOrderTask instance>
38
+ result.context #=> <CMDx::Context with all task data>
39
+ result.chain #=> <CMDx::Chain execution tracking>
40
+ result.metadata #=> Hash with execution metadata
41
+
42
+ # Execution information
43
+ result.id #=> "abc123..." (unique task execution ID)
44
+ result.state #=> "complete" (execution state)
45
+ result.status #=> "success" (execution outcome)
46
+ result.runtime #=> 0.5 (execution time in seconds)
12
47
  ```
13
48
 
49
+ > [!NOTE]
50
+ > Result objects are immutable after task execution completes. All result data reflects the final state of the task execution and cannot be modified.
51
+
52
+ ## State and Status Information
53
+
54
+ Results provide comprehensive methods for checking execution state and status:
55
+
56
+ ```ruby
57
+ result = ProcessUserOrderTask.call
58
+
59
+ # State predicates (execution lifecycle)
60
+ result.initialized? #=> false (after execution)
61
+ result.executing? #=> false (after execution)
62
+ result.complete? #=> true (successful completion)
63
+ result.interrupted? #=> false (no interruption)
64
+ result.executed? #=> true (execution finished)
65
+
66
+ # Status predicates (execution outcome)
67
+ result.success? #=> true (successful execution)
68
+ result.skipped? #=> false (not skipped)
69
+ result.failed? #=> false (no failure)
70
+
71
+ # Outcome categorization
72
+ result.good? #=> true (success or skipped)
73
+ result.bad? #=> false (skipped or failed)
74
+ ```
75
+
76
+ ## Execution Outcome Analysis
77
+
78
+ Results provide additional methods for understanding execution outcomes:
79
+
80
+ ```ruby
81
+ result = ProcessOrderWorkflowTask.call
82
+
83
+ # Outcome determination
84
+ result.outcome #=> "success" (combines state and status)
85
+ ```
86
+
87
+ ## Runtime and Performance
88
+
89
+ Results capture detailed timing information:
90
+
91
+ ```ruby
92
+ result = ProcessUserOrderTask.call
93
+
94
+ # Execution timing
95
+ result.runtime #=> 0.5 (total execution time in seconds)
96
+ ```
97
+
98
+ ## Failure Chain Analysis
99
+
100
+ For failed results, comprehensive failure analysis is available:
101
+
102
+ ```ruby
103
+ result = ProcessOrderWorkflowTask.call
104
+
105
+ if result.failed?
106
+ # Find the original cause of failure
107
+ original_failure = result.caused_failure
108
+ if original_failure
109
+ puts "Original failure: #{original_failure.task.class.name}"
110
+ puts "Reason: #{original_failure.metadata[:reason]}"
111
+ end
112
+
113
+ # Find what threw the failure to this result
114
+ throwing_task = result.threw_failure
115
+ if throwing_task
116
+ puts "Failure thrown by: #{throwing_task.task.class.name}"
117
+ end
118
+
119
+ # Failure classification
120
+ result.caused_failure? #=> true if this result was the original cause
121
+ result.threw_failure? #=> true if this result threw a failure
122
+ result.thrown_failure? #=> true if this result received a thrown failure
123
+ end
124
+ ```
125
+
126
+ > [!IMPORTANT]
127
+ > Failure chain analysis is only available for failed results. Use these methods to trace the root cause of failures in complex task workflows.
128
+
129
+ ## Index and Position
130
+
131
+ Results track their position within execution chains:
132
+
133
+ ```ruby
134
+ result = ProcessUserOrderTask.call
135
+
136
+ # Position in execution sequence
137
+ result.index #=> 0 (first task in chain)
138
+
139
+ # Access via chain
140
+ result.chain.results[result.index] == result #=> true
141
+ ```
142
+
143
+ ## Result Callbacks and Chaining
144
+
145
+ Results support fluent callback patterns for conditional logic:
146
+
147
+ ```ruby
148
+ result = ProcessUserOrderTask.call
149
+
150
+ # State-based callbacks
151
+ result
152
+ .on_complete { |r| logger.info "Task completed successfully" }
153
+ .on_interrupted { |r| logger.warn "Task was interrupted" }
154
+ .on_executed { |r| update_metrics(r.runtime) }
155
+
156
+ # Status-based callbacks
157
+ result
158
+ .on_success { |r| send_confirmation_email(r.context) }
159
+ .on_skipped { |r| logger.info "Skipped: #{r.metadata[:reason]}" }
160
+ .on_failed { |r| handle_failure(r) }
161
+
162
+ # Outcome-based callbacks
163
+ result
164
+ .on_good { |r| celebrate_success }
165
+ .on_bad { |r| handle_problem }
166
+ ```
167
+
168
+ ### Callback Chaining Examples
169
+
170
+ ```ruby
171
+ ProcessUserOrderTask
172
+ .call(order_id: 123)
173
+ .on_success { |result|
174
+ SendOrderNotificationTask.call(result.context)
175
+ }
176
+ .on_failed { |result|
177
+ ErrorReporter.notify(result.metadata)
178
+ }
179
+ .on_executed { |result|
180
+ MetricsService.record_execution_time(result.runtime)
181
+ }
182
+ ```
183
+
184
+ > [!TIP]
185
+ > Use result callbacks for clean, functional-style conditional logic. Callbacks return the result object, enabling method chaining and fluent interfaces.
186
+
187
+ ## Pattern Matching
188
+
189
+ Results support Ruby's pattern matching (Ruby 3.0+) through array and hash deconstruction:
190
+
191
+ ### Array Pattern Matching
192
+
193
+ Match against state and status in order:
194
+
195
+ ```ruby
196
+ result = ProcessUserOrderTask.call
197
+
198
+ case result
199
+ in ["complete", "success"]
200
+ puts "Task completed successfully"
201
+ in ["interrupted", "failed"]
202
+ puts "Task failed during execution"
203
+ in ["complete", "skipped"]
204
+ puts "Task was skipped but completed"
205
+ end
206
+ ```
207
+
208
+ ### Hash Pattern Matching
209
+
210
+ Match against specific result attributes:
211
+
212
+ ```ruby
213
+ result = ProcessUserOrderTask.call
214
+
215
+ case result
216
+ in { state: "complete", status: "success" }
217
+ puts "Perfect execution!"
218
+ in { state: "interrupted", status: "failed", metadata: { retryable: true } }
219
+ puts "Failed but can retry"
220
+ in { good: true }
221
+ puts "Execution went well overall"
222
+ in { bad: true, metadata: { reason: String => reason } }
223
+ puts "Something went wrong: #{reason}"
224
+ end
225
+ ```
226
+
227
+ ### Pattern Matching with Guards
228
+
229
+ Use guard clauses for conditional matching:
230
+
231
+ ```ruby
232
+ case result
233
+ in { status: "failed", metadata: { attempts: n } } if n < 3
234
+ retry_task(result)
235
+ in { status: "failed", metadata: { attempts: n } } if n >= 3
236
+ give_up_task(result)
237
+ in { runtime: time } if time > threshold
238
+ log_performance_issue(result)
239
+ end
240
+ ```
241
+
242
+ > [!NOTE]
243
+ > Pattern matching requires Ruby 3.0+. The `deconstruct` method returns `[state, status]` for array patterns, while `deconstruct_keys` provides hash access to `state`, `status`, `metadata`, `executed`, `good`, and `bad` attributes.
244
+
245
+ ## Serialization and Inspection
246
+
247
+ Results provide comprehensive serialization and inspection capabilities:
248
+
249
+ ### Hash Serialization
250
+
251
+ ```ruby
252
+ result = ProcessUserOrderTask.call
253
+
254
+ result.to_h
255
+ #=> {
256
+ # class: "ProcessUserOrderTask",
257
+ # type: "Task",
258
+ # index: 0,
259
+ # id: "abc123...",
260
+ # chain_id: "def456...",
261
+ # tags: [],
262
+ # state: "complete",
263
+ # status: "success",
264
+ # outcome: "success",
265
+ # metadata: {},
266
+ # runtime: 0.5
267
+ # }
268
+ ```
269
+
270
+ ### Human-Readable Inspection
271
+
272
+ ```ruby
273
+ result = ProcessUserOrderTask.call
274
+
275
+ result.to_s
276
+ #=> "ProcessUserOrderTask: type=Task index=0 id=abc123... state=complete status=success outcome=success metadata={} runtime=0.5"
277
+ ```
278
+
279
+ ### Failure Chain Serialization
280
+
281
+ Failed results include failure chain information:
282
+
283
+ ```ruby
284
+ failed_result = ProcessOrderWorkflowTask.call
285
+
286
+ failed_result.to_h
287
+ #=> {
288
+ # # ... standard result data ...
289
+ # caused_failure: {
290
+ # class: "ValidateUserOrderTask",
291
+ # index: 1,
292
+ # id: "xyz789...",
293
+ # state: "interrupted",
294
+ # status: "failed"
295
+ # },
296
+ # threw_failure: {
297
+ # class: "ProcessOrderPaymentTask",
298
+ # index: 2,
299
+ # id: "uvw123...",
300
+ # state: "interrupted",
301
+ # status: "failed"
302
+ # }
303
+ # }
304
+ ```
305
+
306
+ > [!NOTE]
307
+ > Serialized results include complete failure chain information for debugging and audit trails. Use `to_h` for structured data and `to_s` for human-readable output.
308
+
14
309
  ---
15
310
 
16
- - **Prev:** [Interruptions - Exceptions](https://github.com/drexed/cmdx/blob/main/docs/interruptions/exceptions.md)
17
- - **Next:** [Outcomes - Statuses](https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md)
311
+ - **Prev:** [Interruptions - Exceptions](../interruptions/exceptions.md)
312
+ - **Next:** [Outcomes - Statuses](statuses.md)
@@ -1,52 +1,233 @@
1
- # Outcomes - States
1
+ # Outcomes - States
2
2
 
3
- State represents the condition of all the code task should execute.
3
+ States represent the execution lifecycle condition of task execution, tracking
4
+ the progress of tasks through their complete execution journey. States provide
5
+ insight into where a task is in its lifecycle and enable lifecycle-based
6
+ decision making and monitoring.
4
7
 
5
- | Status | Description |
8
+ ## Table of Contents
9
+
10
+ - [TLDR](#tldr)
11
+ - [State Definitions](#state-definitions)
12
+ - [State Transitions](#state-transitions)
13
+ - [State Predicates](#state-predicates)
14
+ - [State-Based Callbacks](#state-based-callbacks)
15
+ - [State vs Status Distinction](#state-vs-status-distinction)
16
+ - [State Inspection and Monitoring](#state-inspection-and-monitoring)
17
+ - [State Persistence and Logging](#state-persistence-and-logging)
18
+
19
+ ## TLDR
20
+
21
+ - **States** - Track execution lifecycle: `initialized` → `executing` → `complete`/`interrupted`
22
+ - **Automatic** - States are managed automatically by the framework, never modify manually
23
+ - **Predicates** - Check with `result.complete?`, `result.interrupted?`, `result.executed?`
24
+ - **Callbacks** - Use `.on_complete`, `.on_interrupted`, `.on_executed` for lifecycle events
25
+ - **vs Status** - State = where in lifecycle, Status = how execution ended
26
+
27
+ ## State Definitions
28
+
29
+ | State | Description |
6
30
  | ------------- | ----------- |
7
- | `initialized` | Initial task state prior to any execution. |
8
- | `executing` | Task is actively executing code. |
9
- | `complete` | Task executed to completion without halting for any reason. |
10
- | `interrupted` | Task could **NOT** be executed to completion due to a fault/exception. |
31
+ | `initialized` | Task created but execution not yet started. Default state for new tasks. |
32
+ | `executing` | Task is actively running its business logic. Transient state during execution. |
33
+ | `complete` | Task finished execution successfully without any interruption or halt. |
34
+ | `interrupted` | Task execution was stopped due to a fault, exception, or explicit halt. |
35
+
36
+ ## State Transitions
11
37
 
12
- > [!CAUTION]
13
- > States are automatically transitioned and should **NEVER** be altered manually.
38
+ States follow a strict lifecycle with controlled transitions:
14
39
 
15
40
  ```ruby
16
- result = ProcessOrderTask.call
17
- result.state #=> "complete"
41
+ # Valid state transition flow
42
+ initialized -> executing -> complete (successful execution)
43
+ initialized -> executing -> interrupted (failed/halted execution)
44
+ ```
45
+
46
+ > [!IMPORTANT]
47
+ > States are automatically managed during task execution and should **never** be modified manually. State transitions are handled internally by the CMDx framework.
18
48
 
19
- result.pending? #=> false
20
- result.executing? #=> false
21
- result.complete? #=> true
22
- result.interrupted? #=> false
49
+ ### Automatic State Management
23
50
 
24
- # `complete` or `interrupted`
25
- result.executed?
51
+ States are automatically managed during task execution and should **never** be modified manually:
52
+
53
+ ```ruby
54
+ task = ProcessUserOrderTask.new
55
+ task.result.state #=> "initialized"
56
+
57
+ # During task execution, states transition automatically:
58
+ # 1. initialized -> executing (when call begins)
59
+ # 2. executing -> complete (successful completion)
60
+ # 3. executing -> interrupted (on failure/halt)
61
+
62
+ result = ProcessUserOrderTask.call
63
+ result.state #=> "complete" (if successful)
26
64
  ```
27
65
 
28
- ## Handlers
66
+ ### State Transition Methods (Internal Use)
29
67
 
30
- Results can be used to trigger state based callbacks. Handlers require a block
31
- and will have the result available as local variable. Callback handlers can be
32
- chained and repeated.
68
+ These methods handle state transitions internally and are not intended for direct use:
33
69
 
34
70
  ```ruby
35
- result = ProcessOrderTask.call
36
- result.on_complete { do_work }
71
+ result = ProcessUserOrderTask.new.result
72
+
73
+ # Internal state transition methods
74
+ result.executing! # initialized -> executing
75
+ result.complete! # executing -> complete
76
+ result.interrupt! # executing -> interrupted
77
+ result.executed! # executing -> complete OR interrupted (based on status)
78
+ ```
79
+
80
+ > [!WARNING]
81
+ > State transition methods (`executing!`, `complete!`, `interrupt!`) are for internal framework use only. Never call these methods directly in your application code.
37
82
 
38
- # - or -
83
+ ## State Predicates
39
84
 
40
- ProcessOrderTask
41
- .call(...)
42
- .on_executing { do_work }
43
- .on_executed { |result| $statsd.increment(result.state) }
85
+ Use state predicates to check the current execution lifecycle:
86
+
87
+ ```ruby
88
+ result = ProcessUserOrderTask.call
89
+
90
+ # Check current state
91
+ result.initialized? #=> false (after execution)
92
+ result.executing? #=> false (after execution)
93
+ result.complete? #=> true (successful completion)
94
+ result.interrupted? #=> false (no interruption)
95
+
96
+ # Combined state checking
97
+ result.executed? #=> true (complete OR interrupted)
98
+ ```
99
+
100
+ ## State-Based Callbacks
101
+
102
+ Results provide callback methods for state-based conditional execution:
103
+
104
+ ```ruby
105
+ result = ProcessUserOrderTask.call
106
+
107
+ # Individual state callbacks
108
+ result
109
+ .on_initialized { |r| log_task_created(r) }
110
+ .on_executing { |r| show_progress_indicator(r) }
111
+ .on_complete { |r| celebrate_success(r) }
112
+ .on_interrupted { |r| handle_interruption(r) }
113
+
114
+ # Execution completion callback (complete OR interrupted)
115
+ result
116
+ .on_executed { |r| cleanup_resources(r) }
117
+ ```
118
+
119
+ ### Callback Chaining and Combinations
120
+
121
+ ```ruby
122
+ ProcessUserOrderTask
123
+ .call(order_id: 123)
124
+ .on_complete { |result|
125
+ # Only runs if task completed successfully
126
+ send_confirmation_email(result.context)
127
+ update_order_status(result.context.order)
128
+ }
129
+ .on_interrupted { |result|
130
+ # Only runs if task was interrupted
131
+ log_interruption(result.metadata)
132
+ schedule_retry(result) if result.metadata[:retryable]
133
+ }
134
+ .on_executed { |result|
135
+ # Always runs after execution (complete OR interrupted)
136
+ update_metrics(result.runtime)
137
+ cleanup_temporary_files(result.context)
138
+ }
44
139
  ```
45
140
 
46
141
  > [!TIP]
47
- > Handlers help execute you logical branches without `if/else` blocks.
142
+ > Use state-based callbacks for lifecycle event handling. The `on_executed` callback is particularly useful for cleanup operations that should run regardless of success or failure.
143
+
144
+ ## State vs Status Distinction
145
+
146
+ Understanding the difference between states and statuses is crucial:
147
+
148
+ - **State**: Execution lifecycle position (`initialized` → `executing` → `complete`/`interrupted`)
149
+ - **Status**: Execution outcome (`success`, `skipped`, `failed`)
150
+
151
+ ```ruby
152
+ result = ProcessUserOrderTask.call
153
+
154
+ # State indicates WHERE in the lifecycle
155
+ result.state #=> "complete" (finished executing)
156
+
157
+ # Status indicates HOW the execution ended
158
+ result.status #=> "success" (executed successfully)
159
+
160
+ # Both can be different for interrupted tasks
161
+ failed_result = ProcessFailingOrderTask.call
162
+ failed_result.state #=> "interrupted" (execution stopped)
163
+ failed_result.status #=> "failed" (outcome was failure)
164
+ ```
165
+
166
+ ### State-Status Combinations
167
+
168
+ | State | Status | Meaning |
169
+ | ------------- | --------- | ------- |
170
+ | `initialized` | `success` | Task created, not yet executed |
171
+ | `executing` | `success` | Task currently running |
172
+ | `complete` | `success` | Task finished successfully |
173
+ | `complete` | `skipped` | Task finished by skipping execution |
174
+ | `interrupted` | `failed` | Task stopped due to failure |
175
+ | `interrupted` | `skipped` | Task stopped by skip condition |
176
+
177
+ > [!NOTE]
178
+ > State tracks the execution lifecycle (where the task is), while status tracks the outcome (how the task ended). Both provide valuable but different information about task execution.
179
+
180
+ ## State Inspection and Monitoring
181
+
182
+ States provide valuable information for monitoring and debugging:
183
+
184
+ ```ruby
185
+ result = ProcessUserOrderTask.call
186
+
187
+ # Basic state information
188
+ puts "Execution state: #{result.state}"
189
+ puts "Task completed: #{result.executed?}"
190
+
191
+ # State in serialized form
192
+ result.to_h[:state] #=> "complete"
193
+
194
+ # Human-readable inspection
195
+ result.to_s
196
+ #=> "ProcessUserOrderTask: type=Task index=0 state=complete status=success outcome=success..."
197
+ ```
198
+
199
+ ## State Persistence and Logging
200
+
201
+ States are automatically captured in result serialization:
202
+
203
+ ```ruby
204
+ result = ProcessUserOrderTask.call
205
+
206
+ # Hash representation includes state
207
+ result.to_h
208
+ #=> {
209
+ # class: "ProcessUserOrderTask",
210
+ # index: 0,
211
+ # state: "complete",
212
+ # status: "success",
213
+ # outcome: "success",
214
+ # # ... other attributes
215
+ # }
216
+
217
+ # Chain-level state aggregation
218
+ result.chain.to_h
219
+ #=> {
220
+ # id: "chain-uuid...",
221
+ # state: "complete", # Derived from first result
222
+ # status: "success", # Derived from first result
223
+ # results: [
224
+ # { state: "complete", status: "success", ... },
225
+ # # ... other results
226
+ # ]
227
+ # }
228
+ ```
48
229
 
49
230
  ---
50
231
 
51
- - **Prev:** [Outcomes - Statuses](https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md)
52
- - **Next:** [Hooks](https://github.com/drexed/cmdx/blob/main/docs/hooks.md)
232
+ - **Prev:** [Outcomes - Statuses](statuses.md)
233
+ - **Next:** [Parameters - Definitions](../parameters/definitions.md)