tasker-engine 1.0.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 (605) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +443 -0
  4. data/Rakefile +10 -0
  5. data/app/controllers/tasker/analytics_controller.rb +179 -0
  6. data/app/controllers/tasker/application_controller.rb +45 -0
  7. data/app/controllers/tasker/graphql_controller.rb +193 -0
  8. data/app/controllers/tasker/handlers_controller.rb +217 -0
  9. data/app/controllers/tasker/health_controller.rb +229 -0
  10. data/app/controllers/tasker/metrics_controller.rb +111 -0
  11. data/app/controllers/tasker/page_sort.rb +97 -0
  12. data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
  13. data/app/controllers/tasker/tasks_controller.rb +123 -0
  14. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  15. data/app/graphql/examples/all_tasks.graphql +22 -0
  16. data/app/graphql/examples/pending_tasks.graphql +23 -0
  17. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  18. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  19. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  20. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  21. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  22. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  24. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  25. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  26. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  27. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  29. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  30. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  32. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  33. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  34. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  35. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  36. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  38. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  39. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  40. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  41. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  42. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  43. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  44. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  45. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  46. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  47. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  48. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  49. data/app/graphql/tasker/queries/base_query.rb +9 -0
  50. data/app/graphql/tasker/queries/helpers.rb +16 -0
  51. data/app/graphql/tasker/queries/one_step.rb +24 -0
  52. data/app/graphql/tasker/queries/one_task.rb +18 -0
  53. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  54. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  55. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  56. data/app/jobs/tasker/application_job.rb +8 -0
  57. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  58. data/app/jobs/tasker/task_runner_job.rb +224 -0
  59. data/app/models/tasker/annotation_type.rb +26 -0
  60. data/app/models/tasker/application_record.rb +70 -0
  61. data/app/models/tasker/dependent_system.rb +26 -0
  62. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  63. data/app/models/tasker/diagram/edge.rb +106 -0
  64. data/app/models/tasker/diagram/flowchart.rb +137 -0
  65. data/app/models/tasker/diagram/node.rb +99 -0
  66. data/app/models/tasker/named_step.rb +41 -0
  67. data/app/models/tasker/named_task.rb +121 -0
  68. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  69. data/app/models/tasker/step_dag_relationship.rb +65 -0
  70. data/app/models/tasker/step_readiness_status.rb +59 -0
  71. data/app/models/tasker/task.rb +424 -0
  72. data/app/models/tasker/task_annotation.rb +36 -0
  73. data/app/models/tasker/task_diagram.rb +332 -0
  74. data/app/models/tasker/task_execution_context.rb +29 -0
  75. data/app/models/tasker/task_namespace.rb +41 -0
  76. data/app/models/tasker/task_transition.rb +235 -0
  77. data/app/models/tasker/workflow_step.rb +461 -0
  78. data/app/models/tasker/workflow_step_edge.rb +94 -0
  79. data/app/models/tasker/workflow_step_transition.rb +434 -0
  80. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  81. data/app/serializers/tasker/handler_serializer.rb +109 -0
  82. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  83. data/app/serializers/tasker/task_serializer.rb +168 -0
  84. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  85. data/app/services/tasker/analytics_service.rb +409 -0
  86. data/app/views/tasker/task/_diagram.html.erb +32 -0
  87. data/config/initializers/dry_struct.rb +11 -0
  88. data/config/initializers/statesman.rb +6 -0
  89. data/config/initializers/tasker_orchestration.rb +17 -0
  90. data/config/initializers/time_formats.rb +4 -0
  91. data/config/routes.rb +34 -0
  92. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  93. data/config/tasker/system_events.yml +305 -0
  94. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  95. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  96. data/db/functions/get_slowest_steps_v01.sql +82 -0
  97. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  98. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  99. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  100. data/db/functions/get_system_health_counts_v01.sql +108 -0
  101. data/db/functions/get_task_execution_context_v01.sql +108 -0
  102. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  103. data/db/init/schema.sql +2277 -0
  104. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  105. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  106. data/docs/APPLICATION_GENERATOR.md +384 -0
  107. data/docs/AUTH.md +1780 -0
  108. data/docs/CIRCUIT_BREAKER.md +224 -0
  109. data/docs/DEVELOPER_GUIDE.md +2665 -0
  110. data/docs/EVENT_SYSTEM.md +637 -0
  111. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  112. data/docs/FLOW_CHART.md +149 -0
  113. data/docs/HEALTH.md +542 -0
  114. data/docs/METRICS.md +731 -0
  115. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  116. data/docs/OVERVIEW.md +552 -0
  117. data/docs/QUICK_START.md +270 -0
  118. data/docs/REGISTRY_SYSTEMS.md +373 -0
  119. data/docs/REST_API.md +632 -0
  120. data/docs/ROADMAP.md +221 -0
  121. data/docs/SQL_FUNCTIONS.md +1408 -0
  122. data/docs/TASK_DIAGRAM.md +252 -0
  123. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  124. data/docs/TELEMETRY.md +795 -0
  125. data/docs/TROUBLESHOOTING.md +756 -0
  126. data/docs/TaskHandlerGenerator.html +255 -0
  127. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  128. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  129. data/docs/Tasker/Analysis.html +117 -0
  130. data/docs/Tasker/AnalyticsController.html +450 -0
  131. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  132. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  133. data/docs/Tasker/AnalyticsService.html +2221 -0
  134. data/docs/Tasker/AnnotationType.html +137 -0
  135. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  136. data/docs/Tasker/ApplicationController.html +147 -0
  137. data/docs/Tasker/ApplicationJob.html +128 -0
  138. data/docs/Tasker/ApplicationRecord.html +378 -0
  139. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  140. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  141. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  142. data/docs/Tasker/Authentication/Interface.html +560 -0
  143. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  144. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  145. data/docs/Tasker/Authentication.html +119 -0
  146. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  147. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  148. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  149. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  150. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
  151. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  152. data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
  153. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  154. data/docs/Tasker/Authorization.html +582 -0
  155. data/docs/Tasker/CacheCapabilities.html +167 -0
  156. data/docs/Tasker/CacheStrategy.html +1297 -0
  157. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  158. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  159. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  160. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  161. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  162. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  163. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  164. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  165. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  166. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  167. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  168. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  169. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  170. data/docs/Tasker/Concerns.html +117 -0
  171. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  172. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  173. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  174. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  175. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  176. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  177. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  178. data/docs/Tasker/Configuration.html +1669 -0
  179. data/docs/Tasker/ConfigurationError.html +143 -0
  180. data/docs/Tasker/ConfiguredTask.html +514 -0
  181. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  182. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  183. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  184. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  185. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  186. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  187. data/docs/Tasker/Constants/StepEvents.html +177 -0
  188. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  189. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  190. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  191. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  192. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  193. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  194. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  195. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  196. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  197. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  198. data/docs/Tasker/Constants/TestEvents.html +163 -0
  199. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  200. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  201. data/docs/Tasker/Constants.html +561 -0
  202. data/docs/Tasker/DependentSystem.html +137 -0
  203. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  204. data/docs/Tasker/DetectorRegistry.html +598 -0
  205. data/docs/Tasker/Diagram/Edge.html +1191 -0
  206. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  207. data/docs/Tasker/Diagram/Node.html +1165 -0
  208. data/docs/Tasker/Diagram.html +117 -0
  209. data/docs/Tasker/Engine.html +215 -0
  210. data/docs/Tasker/Error.html +139 -0
  211. data/docs/Tasker/Events/Bus.html +1226 -0
  212. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  213. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  214. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  215. data/docs/Tasker/Events/Catalog.html +1291 -0
  216. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  217. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  218. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  219. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  220. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  221. data/docs/Tasker/Events/Publisher.html +365 -0
  222. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  223. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  224. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  225. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  226. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  227. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  228. data/docs/Tasker/Events/Subscribers.html +117 -0
  229. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  230. data/docs/Tasker/Events.html +294 -0
  231. data/docs/Tasker/EventsGenerator.html +459 -0
  232. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  233. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  234. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  235. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  236. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  237. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  238. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  239. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  240. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  241. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  242. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  243. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  244. data/docs/Tasker/Functions.html +117 -0
  245. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  246. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  247. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  248. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  249. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  250. data/docs/Tasker/Generators.html +117 -0
  251. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  252. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  255. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  256. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  259. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  260. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  261. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  262. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  265. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  266. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  268. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  269. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  270. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  271. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  272. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  273. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  274. data/docs/Tasker/GraphQLTypes.html +130 -0
  275. data/docs/Tasker/GraphqlController.html +251 -0
  276. data/docs/Tasker/HandlerFactory.html +1518 -0
  277. data/docs/Tasker/HandlerSerializer.html +682 -0
  278. data/docs/Tasker/HandlersController.html +574 -0
  279. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  280. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  281. data/docs/Tasker/Health/StatusChecker.html +653 -0
  282. data/docs/Tasker/Health.html +117 -0
  283. data/docs/Tasker/HealthController.html +523 -0
  284. data/docs/Tasker/IdentityStrategy.html +276 -0
  285. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  286. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  287. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  288. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  289. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  290. data/docs/Tasker/LifecycleEvents.html +799 -0
  291. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  292. data/docs/Tasker/Logging.html +115 -0
  293. data/docs/Tasker/MetricsController.html +293 -0
  294. data/docs/Tasker/MetricsExportJob.html +414 -0
  295. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  296. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  297. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  298. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  299. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  300. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  301. data/docs/Tasker/Mutations.html +117 -0
  302. data/docs/Tasker/NamedStep.html +216 -0
  303. data/docs/Tasker/NamedTask.html +910 -0
  304. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  305. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  306. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  307. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  308. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  309. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  310. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  311. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  312. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  313. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  314. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  315. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  316. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  322. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  323. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  324. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  325. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  326. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  327. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  328. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  329. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  330. data/docs/Tasker/Orchestration.html +130 -0
  331. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  332. data/docs/Tasker/PageSort.html +247 -0
  333. data/docs/Tasker/PermanentError.html +518 -0
  334. data/docs/Tasker/ProceduralError.html +147 -0
  335. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  336. data/docs/Tasker/Queries/AllTasks.html +221 -0
  337. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  338. data/docs/Tasker/Queries/Helpers.html +187 -0
  339. data/docs/Tasker/Queries/OneStep.html +225 -0
  340. data/docs/Tasker/Queries/OneTask.html +217 -0
  341. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  342. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  343. data/docs/Tasker/Queries.html +119 -0
  344. data/docs/Tasker/Railtie.html +124 -0
  345. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  346. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  347. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  348. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  349. data/docs/Tasker/Registry/RegistryError.html +139 -0
  350. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  351. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  352. data/docs/Tasker/Registry/ValidationError.html +132 -0
  353. data/docs/Tasker/Registry.html +119 -0
  354. data/docs/Tasker/RetryableError.html +515 -0
  355. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  356. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  357. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  358. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  359. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  360. data/docs/Tasker/StateMachine.html +602 -0
  361. data/docs/Tasker/StepDagRelationship.html +657 -0
  362. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  363. data/docs/Tasker/StepHandler/Api.html +884 -0
  364. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  365. data/docs/Tasker/StepHandler/Base.html +970 -0
  366. data/docs/Tasker/StepHandler.html +119 -0
  367. data/docs/Tasker/StepReadinessStatus.html +836 -0
  368. data/docs/Tasker/Task.html +2575 -0
  369. data/docs/Tasker/TaskAnnotation.html +137 -0
  370. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  371. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  372. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  373. data/docs/Tasker/TaskBuilder.html +764 -0
  374. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  375. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  376. data/docs/Tasker/TaskDiagram.html +548 -0
  377. data/docs/Tasker/TaskDiagramsController.html +240 -0
  378. data/docs/Tasker/TaskExecutionContext.html +469 -0
  379. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  380. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  381. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  382. data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
  383. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  384. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  385. data/docs/Tasker/TaskHandler.html +271 -0
  386. data/docs/Tasker/TaskNamespace.html +312 -0
  387. data/docs/Tasker/TaskRunnerJob.html +406 -0
  388. data/docs/Tasker/TaskSerializer.html +474 -0
  389. data/docs/Tasker/TaskTransition.html +1517 -0
  390. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  391. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  392. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  393. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  394. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  395. data/docs/Tasker/TasksController.html +595 -0
  396. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  397. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  398. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  399. data/docs/Tasker/Telemetry/Events.html +115 -0
  400. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  401. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  402. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  403. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  404. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  405. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  406. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  407. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  408. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  409. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  410. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  411. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  412. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  413. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  414. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  415. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  416. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  417. data/docs/Tasker/Telemetry.html +130 -0
  418. data/docs/Tasker/Types/AuthConfig.html +886 -0
  419. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  420. data/docs/Tasker/Types/BaseConfig.html +227 -0
  421. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  422. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  423. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  424. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  425. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  426. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  427. data/docs/Tasker/Types/GraphEdge.html +517 -0
  428. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  429. data/docs/Tasker/Types/GraphNode.html +694 -0
  430. data/docs/Tasker/Types/HealthConfig.html +784 -0
  431. data/docs/Tasker/Types/StepSequence.html +353 -0
  432. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  433. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  434. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  435. data/docs/Tasker/Types.html +154 -0
  436. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  437. data/docs/Tasker/WorkflowStep.html +2724 -0
  438. data/docs/Tasker/WorkflowStepEdge.html +304 -0
  439. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  440. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  441. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  442. data/docs/Tasker/WorkflowStepsController.html +462 -0
  443. data/docs/Tasker.html +452 -0
  444. data/docs/VISION.md +584 -0
  445. data/docs/WHY.md +21 -0
  446. data/docs/_index.html +2375 -0
  447. data/docs/class_list.html +54 -0
  448. data/docs/css/common.css +1 -0
  449. data/docs/css/full_list.css +58 -0
  450. data/docs/css/style.css +503 -0
  451. data/docs/events/migration_plan_outcomes.md +80 -0
  452. data/docs/file.README.html +541 -0
  453. data/docs/file_list.html +59 -0
  454. data/docs/frames.html +22 -0
  455. data/docs/index.html +541 -0
  456. data/docs/js/app.js +344 -0
  457. data/docs/js/full_list.js +242 -0
  458. data/docs/js/jquery.js +4 -0
  459. data/docs/method_list.html +9182 -0
  460. data/docs/top-level-namespace.html +110 -0
  461. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  462. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  463. data/lib/generators/tasker/events_generator.rb +91 -0
  464. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  465. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  466. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  467. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  468. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
  469. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
  470. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  471. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  472. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  473. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  474. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  475. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  476. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  477. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  478. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  479. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  480. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  481. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  482. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  483. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  484. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  485. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  486. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  487. data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
  488. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
  489. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  490. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  491. data/lib/tasker/authentication/coordinator.rb +78 -0
  492. data/lib/tasker/authentication/errors.rb +9 -0
  493. data/lib/tasker/authentication/interface.rb +36 -0
  494. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  495. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  496. data/lib/tasker/authorization/errors.rb +26 -0
  497. data/lib/tasker/authorization/resource_constants.rb +74 -0
  498. data/lib/tasker/authorization/resource_registry.rb +143 -0
  499. data/lib/tasker/authorization.rb +75 -0
  500. data/lib/tasker/cache_capabilities.rb +131 -0
  501. data/lib/tasker/cache_strategy.rb +469 -0
  502. data/lib/tasker/concerns/authenticatable.rb +41 -0
  503. data/lib/tasker/concerns/authorizable.rb +204 -0
  504. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  505. data/lib/tasker/concerns/event_publisher.rb +716 -0
  506. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  507. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  508. data/lib/tasker/concerns/structured_logging.rb +387 -0
  509. data/lib/tasker/configuration.rb +325 -0
  510. data/lib/tasker/constants/event_definitions.rb +147 -0
  511. data/lib/tasker/constants/registry_events.rb +54 -0
  512. data/lib/tasker/constants.rb +417 -0
  513. data/lib/tasker/engine.rb +90 -0
  514. data/lib/tasker/errors.rb +90 -0
  515. data/lib/tasker/events/catalog.rb +432 -0
  516. data/lib/tasker/events/custom_registry.rb +175 -0
  517. data/lib/tasker/events/definition_loader.rb +199 -0
  518. data/lib/tasker/events/event_payload_builder.rb +461 -0
  519. data/lib/tasker/events/publisher.rb +149 -0
  520. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  521. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  522. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  523. data/lib/tasker/events/subscription_loader.rb +161 -0
  524. data/lib/tasker/events.rb +37 -0
  525. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  526. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  527. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  528. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  529. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  530. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  531. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  532. data/lib/tasker/functions/function_wrapper.rb +42 -0
  533. data/lib/tasker/functions.rb +12 -0
  534. data/lib/tasker/handler_factory.rb +322 -0
  535. data/lib/tasker/health/readiness_checker.rb +186 -0
  536. data/lib/tasker/health/status_checker.rb +203 -0
  537. data/lib/tasker/identity_strategy.rb +38 -0
  538. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  539. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  540. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  541. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  542. data/lib/tasker/orchestration/coordinator.rb +119 -0
  543. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  544. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  545. data/lib/tasker/orchestration/response_processor.rb +168 -0
  546. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  547. data/lib/tasker/orchestration/step_executor.rb +941 -0
  548. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  549. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  550. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  551. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  552. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  553. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  554. data/lib/tasker/orchestration.rb +45 -0
  555. data/lib/tasker/railtie.rb +9 -0
  556. data/lib/tasker/registry/base_registry.rb +177 -0
  557. data/lib/tasker/registry/event_publisher.rb +91 -0
  558. data/lib/tasker/registry/interface_validator.rb +140 -0
  559. data/lib/tasker/registry/statistics_collector.rb +381 -0
  560. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  561. data/lib/tasker/registry.rb +22 -0
  562. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  563. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  564. data/lib/tasker/state_machine.rb +83 -0
  565. data/lib/tasker/step_handler/api.rb +410 -0
  566. data/lib/tasker/step_handler/base.rb +206 -0
  567. data/lib/tasker/task_builder.rb +432 -0
  568. data/lib/tasker/task_handler/class_methods.rb +324 -0
  569. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  570. data/lib/tasker/task_handler/step_group.rb +182 -0
  571. data/lib/tasker/task_handler.rb +43 -0
  572. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  573. data/lib/tasker/telemetry/event_router.rb +318 -0
  574. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  575. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  576. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  577. data/lib/tasker/telemetry/log_backend.rb +224 -0
  578. data/lib/tasker/telemetry/metric_types.rb +368 -0
  579. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  580. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  581. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  582. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  583. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  584. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  585. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  586. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  587. data/lib/tasker/telemetry.rb +59 -0
  588. data/lib/tasker/types/auth_config.rb +81 -0
  589. data/lib/tasker/types/backoff_config.rb +142 -0
  590. data/lib/tasker/types/cache_config.rb +257 -0
  591. data/lib/tasker/types/database_config.rb +39 -0
  592. data/lib/tasker/types/dependency_graph.rb +225 -0
  593. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  594. data/lib/tasker/types/engine_config.rb +131 -0
  595. data/lib/tasker/types/execution_config.rb +289 -0
  596. data/lib/tasker/types/health_config.rb +84 -0
  597. data/lib/tasker/types/step_sequence.rb +24 -0
  598. data/lib/tasker/types/step_template.rb +63 -0
  599. data/lib/tasker/types/task_request.rb +60 -0
  600. data/lib/tasker/types/telemetry_config.rb +273 -0
  601. data/lib/tasker/types.rb +64 -0
  602. data/lib/tasker/version.rb +7 -0
  603. data/lib/tasker.rb +82 -0
  604. data/lib/tasks/tasker_tasks.rake +302 -0
  605. metadata +958 -0
@@ -0,0 +1,2665 @@
1
+ # Tasker Developer Guide
2
+
3
+ ## Overview
4
+
5
+ This guide provides a comprehensive overview of developing with Tasker, covering all the key components that make up the workflow engine. Tasker is designed around six main developer-facing components:
6
+
7
+ 1. **TaskNamespaces & Versioning** - Organize and version task handlers for scalable workflow management
8
+ 2. **Task Handlers** - Define and coordinate multi-step workflows
9
+ 3. **Step Handlers** - Implement the business logic for individual workflow steps
10
+ 4. **Event Subscribers** - Create integrations with external services and monitoring systems
11
+ 5. **YAML Configuration** - Declarative workflow and step configuration with namespace support
12
+ 6. **Authentication & Authorization** - Secure your workflows with flexible authentication strategies
13
+
14
+ ## New in Tasker 2.3.0: TaskNamespace + Versioning Architecture
15
+
16
+ Tasker now supports **hierarchical task organization** and **semantic versioning** for enterprise-scale workflow management:
17
+
18
+ ### Key Benefits
19
+ - **Namespace Isolation**: Organize tasks by domain (`payments`, `inventory`, `notifications`)
20
+ - **Version Coexistence**: Multiple versions of the same task can run simultaneously
21
+ - **Zero Breaking Changes**: Existing tasks continue working with automatic defaults
22
+ - **Enterprise Scalability**: Clean separation of concerns for large organizations
23
+
24
+ ### Quick Example
25
+ ```ruby
26
+ # Create namespaced task handlers
27
+ task_request = Tasker::Types::TaskRequest.new(
28
+ name: 'process_order',
29
+ namespace: 'payments', # NEW: Namespace organization
30
+ version: '2.1.0', # NEW: Semantic versioning
31
+ context: { order_id: 123 }
32
+ )
33
+
34
+ # Handler lookup with namespace + version
35
+ handler = Tasker::HandlerFactory.instance.get(
36
+ 'process_order',
37
+ namespace_name: 'payments',
38
+ version: '2.1.0'
39
+ )
40
+ ```
41
+
42
+ ### Namespace Organization Patterns
43
+ - **`payments`** - Payment processing, billing, refunds
44
+ - **`inventory`** - Stock management, fulfillment, warehouse operations
45
+ - **`notifications`** - Email, SMS, push notifications, alerts
46
+ - **`integrations`** - Third-party APIs, webhooks, data synchronization
47
+ - **`data_processing`** - ETL, data transformation, analytics pipelines
48
+ - **`default`** - General workflows (automatic fallback when unspecified)
49
+
50
+ ## Architecture Overview
51
+
52
+ ```mermaid
53
+ flowchart TB
54
+ subgraph Config["YAML Configuration"]
55
+ TaskYAML["Task Handler YAML<br/>config/tasker/tasks/"]
56
+ StepConfig["Step Configuration<br/>Dependencies & Settings"]
57
+ end
58
+
59
+ subgraph Handlers["Handler Classes"]
60
+ TaskHandler["Task Handler<br/>app/tasks/"]
61
+ StepHandler["Step Handler<br/>app/tasks/*/step_handler/"]
62
+ end
63
+
64
+ subgraph Events["Event System"]
65
+ EventCatalog["Event Catalog<br/>Tasker::Events.catalog"]
66
+ Subscribers["Event Subscribers<br/>app/subscribers/"]
67
+ end
68
+
69
+ subgraph External["External Integrations"]
70
+ Sentry["Error Tracking<br/>(Sentry)"]
71
+ Metrics["Metrics<br/>(DataDog/StatsD)"]
72
+ Alerts["Alerting<br/>(PagerDuty)"]
73
+ Notifications["Notifications<br/>(Slack/Email)"]
74
+ end
75
+
76
+ TaskYAML --> TaskHandler
77
+ StepConfig --> StepHandler
78
+ TaskHandler --> Events
79
+ StepHandler --> Events
80
+ EventCatalog --> Subscribers
81
+ Subscribers --> Sentry
82
+ Subscribers --> Metrics
83
+ Subscribers --> Alerts
84
+ Subscribers --> Notifications
85
+
86
+ classDef config fill:#e1f5fe,stroke:#01579b
87
+ classDef handlers fill:#f3e5f5,stroke:#4a148c
88
+ classDef events fill:#fff3e0,stroke:#e65100
89
+ classDef external fill:#e8f5e8,stroke:#2e7d32
90
+
91
+ class TaskYAML,StepConfig config
92
+ class TaskHandler,StepHandler handlers
93
+ class EventCatalog,Subscribers events
94
+ class Sentry,Metrics,Alerts,Notifications external
95
+ ```
96
+
97
+ ## 1. TaskNamespaces & Versioning
98
+
99
+ TaskNamespaces provide organizational hierarchy for task handlers, while versioning enables multiple versions of the same task to coexist. This is essential for enterprise environments with complex workflows and deployment strategies.
100
+
101
+ ### TaskNamespace Management
102
+
103
+ #### Creating TaskNamespaces
104
+
105
+ TaskNamespaces are automatically created when referenced, but you can also create them explicitly:
106
+
107
+ ```ruby
108
+ # Automatic creation during task creation
109
+ task_request = Tasker::Types::TaskRequest.new(
110
+ name: 'process_payment',
111
+ namespace: 'payments', # Automatically creates 'payments' namespace if needed
112
+ version: '1.0.0',
113
+ context: { payment_id: 123 }
114
+ )
115
+
116
+ # Manual creation with description
117
+ namespace = Tasker::TaskNamespace.create!(
118
+ name: 'payments',
119
+ description: 'Payment processing and billing workflows'
120
+ )
121
+ ```
122
+
123
+ #### Namespace Patterns & Best Practices
124
+
125
+ **Domain-Based Organization**:
126
+ ```yaml
127
+ # config/tasker/tasks/payments/process_order.yaml
128
+ ---
129
+ name: process_order
130
+ namespace_name: payments
131
+ version: 2.1.0
132
+ task_handler_class: Payments::ProcessOrderHandler
133
+
134
+ # config/tasker/tasks/inventory/process_order.yaml
135
+ ---
136
+ name: process_order
137
+ namespace_name: inventory
138
+ version: 1.5.0
139
+ task_handler_class: Inventory::ProcessOrderHandler
140
+ ```
141
+
142
+ **Version Management Strategies**:
143
+ ```ruby
144
+ # Gradual rollout - start with specific version
145
+ handler_v2 = Tasker::HandlerFactory.instance.get(
146
+ 'process_payment',
147
+ namespace_name: 'payments',
148
+ version: '2.0.0'
149
+ )
150
+
151
+ # Legacy support - maintain old version
152
+ handler_v1 = Tasker::HandlerFactory.instance.get(
153
+ 'process_payment',
154
+ namespace_name: 'payments',
155
+ version: '1.5.0'
156
+ )
157
+
158
+ # Default behavior - uses latest registered version
159
+ handler_default = Tasker::HandlerFactory.instance.get('process_payment')
160
+ ```
161
+
162
+ #### HandlerFactory Registry Architecture
163
+
164
+ The HandlerFactory now uses a **thread-safe 3-level registry** with enterprise-grade capabilities:
165
+
166
+ ```ruby
167
+ # Registry Structure: namespace_name → handler_name → version → handler_class
168
+ # Thread-safe storage: Concurrent::Hash for all levels
169
+ # Example internal structure:
170
+ {
171
+ payments: {
172
+ process_order: {
173
+ '1.0.0' => Payments::ProcessOrderV1,
174
+ '2.0.0' => Payments::ProcessOrderV2
175
+ }
176
+ },
177
+ inventory: {
178
+ process_order: {
179
+ '1.5.0' => Inventory::ProcessOrder
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ **Enterprise Features**:
186
+ - **Thread-Safe Operations**: `Concurrent::Hash` storage eliminates race conditions
187
+ - **Structured Logging**: Every operation logged with correlation IDs
188
+ - **Interface Validation**: Fail-fast validation with detailed error messages
189
+ - **Conflict Resolution**: `replace: true` parameter for graceful updates
190
+ - **Health Monitoring**: Built-in statistics and health checks
191
+ - **Event Integration**: Registry operations trigger 56-event system
192
+
193
+ **Registration Examples**:
194
+ ```ruby
195
+ # Class-based registration with namespace + version
196
+ class PaymentHandler < Tasker::TaskHandler
197
+ register_handler(
198
+ 'process_payment',
199
+ namespace_name: 'payments',
200
+ version: '2.0.0'
201
+ )
202
+ end
203
+
204
+ # Thread-safe manual registration with conflict resolution
205
+ Tasker::HandlerFactory.instance.register(
206
+ 'payment_processor',
207
+ PaymentHandler,
208
+ namespace_name: 'payments',
209
+ version: '2.1.0',
210
+ replace: true # Gracefully handles conflicts
211
+ )
212
+
213
+ # Automatic structured logging output:
214
+ # {"correlation_id":"tsk_abc123","component":"handler_factory","message":"Registry item registered","entity_id":"payments/payment_processor/2.1.0","event_type":"registered"}
215
+
216
+ # YAML-based registration
217
+ # config/tasker/tasks/payments/process_payment.yaml
218
+ ---
219
+ name: process_payment
220
+ namespace_name: payments
221
+ version: 2.0.0
222
+ task_handler_class: PaymentHandler
223
+ ```
224
+
225
+ ### Versioning Best Practices
226
+
227
+ #### Semantic Versioning
228
+ Follow [semver.org](https://semver.org) conventions:
229
+ - **Major version** (`2.0.0`): Breaking changes, incompatible API changes
230
+ - **Minor version** (`1.1.0`): New features, backward compatible
231
+ - **Patch version** (`1.0.1`): Bug fixes, backward compatible
232
+
233
+ #### Version Lifecycle Management
234
+ ```ruby
235
+ # Development workflow
236
+ class PaymentHandler < Tasker::TaskHandler
237
+ register_handler('process_payment',
238
+ namespace_name: 'payments',
239
+ version: '2.0.0-beta.1') # Pre-release
240
+ end
241
+
242
+ # Production deployment
243
+ class PaymentHandler < Tasker::TaskHandler
244
+ register_handler('process_payment',
245
+ namespace_name: 'payments',
246
+ version: '2.0.0') # Stable release
247
+ end
248
+
249
+ # Maintenance mode
250
+ class PaymentHandler < Tasker::TaskHandler
251
+ register_handler('process_payment',
252
+ namespace_name: 'payments',
253
+ version: '1.5.2') # Patch release for legacy
254
+ end
255
+ ```
256
+
257
+ ## 2. Task Handlers
258
+
259
+ Task handlers define the overall workflow structure and coordinate step execution. They are the entry point for creating and managing multi-step processes.
260
+
261
+ ### Creating Task Handlers
262
+
263
+ Use the generator to create a complete task handler structure:
264
+
265
+ ```bash
266
+ rails generate tasker:task_handler OrderHandler --module_namespace OrderProcess
267
+ ```
268
+
269
+ This creates:
270
+ - **Handler Class**: `app/tasks/order_process/order_handler.rb`
271
+ - **YAML Configuration**: `config/tasker/tasks/order_process/order_handler.yaml`
272
+ - **Test File**: `spec/tasks/order_process/order_handler_spec.rb`
273
+
274
+ ### Task Handler Class Structure
275
+
276
+ ```ruby
277
+ # app/tasks/order_process/order_handler.rb
278
+ module OrderProcess
279
+ class OrderHandler < Tasker::ConfiguredTask
280
+ # The task handler class is primarily configuration-driven
281
+ # Most behavior is defined in the YAML file
282
+
283
+ # Optional: Override the default task name (defaults to class name underscored)
284
+ def self.task_name
285
+ 'custom_order_process'
286
+ end
287
+
288
+ # Optional: Override the default YAML path
289
+ def self.yaml_path
290
+ Rails.root.join('config/custom_tasks/order_handler.yaml')
291
+ end
292
+
293
+ # Optional: Establish custom step dependencies beyond YAML configuration
294
+ def establish_step_dependencies_and_defaults(task, steps)
295
+ # Add runtime dependencies based on task context
296
+ if task.context['priority'] == 'expedited'
297
+ # Modify step configuration for expedited orders
298
+ payment_step = steps.find { |s| s.name == 'process_payment' }
299
+ payment_step&.update(retry_limit: 1) # Faster failure for expedited orders
300
+ end
301
+ end
302
+
303
+ # Optional: Update annotations after steps complete
304
+ def update_annotations(task, sequence, steps)
305
+ # Add custom annotations based on step results
306
+ total_amount = steps.find { |s| s.name == 'calculate_total' }&.results&.dig('amount')
307
+ if total_amount && total_amount > 1000
308
+ task.annotations.create!(
309
+ annotation_type: 'high_value_order',
310
+ content: { amount: total_amount, flagged_at: Time.current }
311
+ )
312
+ end
313
+ end
314
+
315
+ # Optional: Custom validation schema (beyond YAML schema)
316
+ def schema
317
+ # This overrides any schema defined in YAML
318
+ {
319
+ type: 'object',
320
+ required: ['order_id', 'customer_id'],
321
+ properties: {
322
+ order_id: { type: 'integer', minimum: 1 },
323
+ customer_id: { type: 'integer', minimum: 1 },
324
+ priority: { type: 'string', enum: ['standard', 'expedited', 'rush'] }
325
+ }
326
+ }
327
+ end
328
+ end
329
+ end
330
+ ```
331
+
332
+ ### Task Handler Capabilities
333
+
334
+ - **Workflow Orchestration**: Manages step dependencies and execution order
335
+ - **Parallel Processing**: Supports concurrent execution of independent steps
336
+ - **Error Handling**: Comprehensive retry logic with exponential backoff
337
+ - **Context Management**: Passes data between steps through task context
338
+ - **Validation**: JSON schema validation plus custom business rules
339
+ - **Event Publishing**: Automatic lifecycle event generation
340
+
341
+ ### Available Override Methods
342
+
343
+ **ConfiguredTask Class Methods** (for customizing configuration):
344
+ - `self.task_name` - Override the default task name (defaults to class name underscored)
345
+ - `self.yaml_path` - Override the default YAML configuration file path
346
+ - `self.config` - Override how configuration is loaded (defaults to YAML.load_file)
347
+
348
+ **TaskHandler Instance Methods** (for customizing behavior):
349
+ - `establish_step_dependencies_and_defaults(task, steps)` - Modify step configuration at runtime
350
+ - `update_annotations(task, sequence, steps)` - Add custom annotations after step completion
351
+ - `schema` - Define custom validation schema for task context (overrides YAML schema)
352
+
353
+ ## 2. Step Handlers
354
+
355
+ Step handlers implement the specific business logic for individual workflow steps. They are the workhorses that perform the actual operations.
356
+
357
+ ### Step Handler Types
358
+
359
+ **Base Step Handler** - For general business logic with custom events:
360
+ ```ruby
361
+ module OrderProcess
362
+ module StepHandler
363
+ class ProcessPaymentHandler < Tasker::StepHandler::Base
364
+ # Define custom events that this handler can publish
365
+ # These are automatically registered when the task handler is loaded
366
+ def self.custom_event_configuration
367
+ [
368
+ {
369
+ name: 'payment.processed',
370
+ description: 'Published when payment processing completes successfully'
371
+ },
372
+ {
373
+ name: 'payment.risk_flagged',
374
+ description: 'Published when payment is flagged for manual review'
375
+ }
376
+ ]
377
+ end
378
+
379
+ def process(task, sequence, step)
380
+ order_id = task.context[:order_id]
381
+ payment_amount = task.context[:payment_amount]
382
+
383
+ # Perform risk assessment
384
+ risk_score = assess_payment_risk(order_id, payment_amount)
385
+
386
+ if risk_score > 0.8
387
+ # Publish custom event for high-risk payments
388
+ publish_custom_event('payment.risk_flagged', {
389
+ order_id: order_id,
390
+ risk_score: risk_score,
391
+ requires_manual_review: true,
392
+ flagged_at: Time.current
393
+ })
394
+
395
+ { status: 'risk_review', risk_score: risk_score }
396
+ else
397
+ # Process payment normally
398
+ payment_result = process_payment_transaction(order_id, payment_amount)
399
+
400
+ # Publish custom event for successful payments
401
+ publish_custom_event('payment.processed', {
402
+ order_id: order_id,
403
+ payment_amount: payment_amount,
404
+ transaction_id: payment_result[:transaction_id],
405
+ processed_at: Time.current
406
+ })
407
+
408
+ { status: 'completed', transaction_id: payment_result[:transaction_id] }
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def assess_payment_risk(order_id, amount)
415
+ # Risk assessment logic here
416
+ # Returns a score between 0.0 and 1.0
417
+ rand(0.0..1.0)
418
+ end
419
+
420
+ def process_payment_transaction(order_id, amount)
421
+ # Payment processing logic here
422
+ { transaction_id: "txn_#{SecureRandom.hex(8)}" }
423
+ end
424
+ end
425
+ end
426
+ end
427
+ ```
428
+
429
+ **API Step Handler** - For external API integrations:
430
+ ```ruby
431
+ module OrderProcess
432
+ module StepHandler
433
+ class FetchInventoryHandler < Tasker::StepHandler::Api
434
+ include OrderProcess::ApiUtils
435
+
436
+ def process(task, sequence, step)
437
+ product_ids = get_previous_step_data(sequence, 'fetch_products', 'product_ids')
438
+
439
+ # Make HTTP request (automatic retry, timeout, error handling)
440
+ connection.get('/inventory/check', { product_ids: product_ids })
441
+ end
442
+
443
+ def process_results(step, process_output, initial_results)
444
+ # Custom response processing
445
+ inventory_data = JSON.parse(process_output.body)
446
+ step.results = {
447
+ inventory_levels: inventory_data['levels'],
448
+ availability: inventory_data['available'],
449
+ last_updated: inventory_data['timestamp']
450
+ }
451
+ end
452
+ end
453
+ end
454
+ end
455
+ ```
456
+
457
+ ### API Step Handler Implementation
458
+
459
+ **The `process` Method - Your Extension Point**:
460
+
461
+ API step handlers use the `process` method as the developer extension point. The framework handles all orchestration, error handling, retries, and event publishing automatically:
462
+
463
+ ```ruby
464
+ module OrderProcess
465
+ module StepHandler
466
+ class FetchUserProfileHandler < Tasker::StepHandler::Api
467
+ include OrderProcess::ApiUtils
468
+
469
+ def process(task, sequence, step)
470
+ # Simply implement your HTTP request logic
471
+ # Framework handles retries, timeouts, and error handling automatically
472
+ user_id = task.context['user_id']
473
+ connection.get("/users/#{user_id}/profile")
474
+ end
475
+ end
476
+ end
477
+ end
478
+ ```
479
+
480
+ **Making Different Types of API Requests**:
481
+
482
+ ```ruby
483
+ module OrderProcess
484
+ module StepHandler
485
+ # GET request example
486
+ class FetchOrderHandler < Tasker::StepHandler::Api
487
+ def process(task, sequence, step)
488
+ order_id = task.context['order_id']
489
+ connection.get("/orders/#{order_id}")
490
+ end
491
+ end
492
+
493
+ # POST request example
494
+ class CreatePaymentHandler < Tasker::StepHandler::Api
495
+ def process(task, sequence, step)
496
+ payment_data = {
497
+ amount: task.context['amount'],
498
+ currency: task.context['currency'],
499
+ customer_id: task.context['customer_id']
500
+ }
501
+ connection.post('/payments', payment_data)
502
+ end
503
+ end
504
+
505
+ # PUT request with data from previous steps
506
+ class UpdateInventoryHandler < Tasker::StepHandler::Api
507
+ def process(task, sequence, step)
508
+ # Get data from previous steps
509
+ order_items = get_previous_step_data(sequence, 'fetch_order', 'items')
510
+
511
+ inventory_updates = order_items.map do |item|
512
+ { product_id: item['product_id'], quantity: -item['quantity'] }
513
+ end
514
+
515
+ connection.put('/inventory/bulk-update', { updates: inventory_updates })
516
+ end
517
+ end
518
+ end
519
+ end
520
+ ```
521
+
522
+ **Custom Response Processing**:
523
+
524
+ Override the `process_results` method to customize how API responses are processed and stored:
525
+
526
+ ```ruby
527
+ module OrderProcess
528
+ module StepHandler
529
+ class FetchUserDataHandler < Tasker::StepHandler::Api
530
+ def process(task, sequence, step)
531
+ user_id = task.context['user_id']
532
+ connection.get("/users/#{user_id}/profile")
533
+ end
534
+
535
+ # Override to customize response processing
536
+ def process_results(step, process_output, initial_results)
537
+ # process_output is the Faraday::Response from your process method
538
+ if process_output.status == 200
539
+ data = JSON.parse(process_output.body)
540
+ step.results = {
541
+ profile: data['user_profile'],
542
+ preferences: data['preferences'],
543
+ last_login: data['last_login_at'],
544
+ api_response_time: process_output.headers['x-response-time']
545
+ }
546
+ else
547
+ # Let framework handle error - this will trigger retries if configured
548
+ raise "API error: #{process_output.status} - #{process_output.body}"
549
+ end
550
+ end
551
+ end
552
+ end
553
+ end
554
+ ```
555
+
556
+ **Setting Results Directly in `process`**:
557
+
558
+ You can also set `step.results` directly in your `process` method if you prefer:
559
+
560
+ ```ruby
561
+ module OrderProcess
562
+ module StepHandler
563
+ class ProcessOrderHandler < Tasker::StepHandler::Api
564
+ def process(task, sequence, step)
565
+ response = connection.post('/orders', task.context)
566
+
567
+ # Set results directly - framework will respect this
568
+ if response.status == 201
569
+ order_data = JSON.parse(response.body)
570
+ step.results = {
571
+ order_id: order_data['id'],
572
+ status: order_data['status'],
573
+ created_at: order_data['created_at']
574
+ }
575
+ end
576
+
577
+ response # Return response for framework processing
578
+ end
579
+ end
580
+ end
581
+ end
582
+ ```
583
+
584
+ **Key API Step Handler Principles**:
585
+
586
+ 1. **Implement `process` method** - This is your extension point for HTTP request logic
587
+ 2. **Use the `connection` object** - Pre-configured Faraday connection with retry/timeout handling
588
+ 3. **Return the response** - Let the framework handle response processing and error detection
589
+ 4. **Override `process_results` for custom processing** - Transform responses while preserving framework behavior
590
+ 5. **Set `step.results` for custom data** - Either in `process` or `process_results`
591
+ 6. **Raise exceptions for failures** - Framework handles error states and retry orchestration
592
+ 7. **Never override `handle`** - This is framework-only coordination code
593
+
594
+ ### Step Handler Features
595
+
596
+ - **Automatic Result Storage**: Return values automatically stored in `step.results`
597
+ - **Context Access**: Full access to task context and previous step results
598
+ - **Error Handling**: Framework determines success/failure based on exceptions (see Error Handling Patterns in Best Practices)
599
+ - **Custom Processing**: Override `process_results` for custom result handling
600
+ - **Event Integration**: Automatic event publishing for observability
601
+
602
+ ### Accessing Previous Step Data
603
+
604
+ ```ruby
605
+ def process(task, sequence, step)
606
+ # Access task context
607
+ order_id = task.context['order_id']
608
+
609
+ # Find specific step by name
610
+ payment_step = sequence.find_step_by_name('process_payment')
611
+ payment_id = payment_step.results['payment_id']
612
+
613
+ # Get data from multiple steps
614
+ product_data = get_previous_step_data(sequence, 'fetch_products', 'products')
615
+ inventory_data = get_previous_step_data(sequence, 'check_inventory', 'levels')
616
+
617
+ # Your business logic here
618
+ process_order_fulfillment(order_id, payment_id, product_data, inventory_data)
619
+ end
620
+ ```
621
+
622
+ ## 3. Event Subscribers
623
+
624
+ Event subscribers handle **"collateral" or "secondary" logic** - operations that support observability, monitoring, and alerting but are not core business requirements. They respond to workflow events and provide operational visibility into system behavior.
625
+
626
+ ### Architectural Distinction: Subscribers vs Steps
627
+
628
+ **Event Subscribers** are for collateral concerns:
629
+ - **Operational Observability**: Logging, metrics, telemetry, traces
630
+ - **Alerting & Monitoring**: Sentry errors, PagerDuty alerts, operational notifications
631
+ - **Analytics**: Business intelligence, usage tracking, performance monitoring
632
+ - **External Integrations**: Non-critical third-party service notifications
633
+
634
+ **Workflow Steps** are for business-critical operations requiring:
635
+ - **Idempotency**: Can be safely retried without side effects
636
+ - **Retryability**: Built-in retry logic with exponential backoff
637
+ - **Explicit Lifecycle Tracking**: Success/failure states that matter to the business
638
+ - **Transactional Integrity**: Operations that need to be rolled back on failure
639
+
640
+ **Rule of Thumb**: If the operation must succeed for the workflow to be considered complete, it should be a workflow step. If it's supporting infrastructure (logging, monitoring, analytics), it should be an event subscriber.
641
+
642
+ ### Creating Event Subscribers
643
+
644
+ Use the generator to create subscribers with automatic method routing:
645
+
646
+ ```bash
647
+ # Generate a subscriber for specific events
648
+ rails generate tasker:subscriber notification --events task.completed task.failed step.failed
649
+
650
+ # Generate a metrics collector
651
+ rails generate tasker:subscriber metrics --events task.completed step.completed task.failed step.failed
652
+ ```
653
+
654
+ ### Event Subscriber Structure
655
+
656
+ ```ruby
657
+ # app/subscribers/observability_subscriber.rb
658
+ class ObservabilitySubscriber < Tasker::Events::Subscribers::BaseSubscriber
659
+ # Subscribe to specific events for operational monitoring
660
+ subscribe_to 'task.completed', 'task.failed', 'step.failed', 'order.fulfilled'
661
+
662
+ # Automatic method routing: task.completed -> handle_task_completed
663
+ def handle_task_completed(event)
664
+ task_id = safe_get(event, :task_id)
665
+ task_name = safe_get(event, :task_name, 'unknown')
666
+ execution_duration = safe_get(event, :execution_duration, 0)
667
+
668
+ # Operational logging and metrics (collateral concerns)
669
+ Rails.logger.info "Task completed: #{task_name} (#{task_id}) in #{execution_duration}s"
670
+ StatsD.histogram('tasker.task.duration', execution_duration, tags: ["task:#{task_name}"])
671
+ end
672
+
673
+ def handle_task_failed(event)
674
+ task_id = safe_get(event, :task_id)
675
+ error_message = safe_get(event, :error_message, 'Unknown error')
676
+
677
+ # Send alerts to operational tools (collateral concerns)
678
+ Sentry.capture_message("Task failed: #{task_id}", level: 'error', extra: { error: error_message })
679
+ PagerDutyService.trigger_alert(
680
+ summary: "Tasker workflow failed",
681
+ severity: 'error',
682
+ details: { task_id: task_id, error: error_message }
683
+ )
684
+ end
685
+
686
+ def handle_step_failed(event)
687
+ step_id = safe_get(event, :step_id)
688
+ step_name = safe_get(event, :step_name, 'unknown')
689
+ task_id = safe_get(event, :task_id)
690
+
691
+ # Operational logging for debugging (collateral concern)
692
+ Rails.logger.error "Step failure in task #{task_id}: #{step_name} (#{step_id})"
693
+ end
694
+
695
+ def handle_order_fulfilled(event)
696
+ order_id = safe_get(event, :order_id)
697
+ customer_id = safe_get(event, :customer_id)
698
+
699
+ # Analytics and monitoring (collateral concerns)
700
+ AnalyticsService.track_order_fulfillment(order_id, customer_id)
701
+ Rails.logger.info "Order fulfilled: #{order_id} for customer #{customer_id}"
702
+ end
703
+ end
704
+ ```
705
+
706
+ ### Available Events
707
+
708
+ Discover all available events using the event catalog:
709
+
710
+ ```ruby
711
+ # In Rails console or your code
712
+ Tasker::Events.catalog.keys
713
+ # => ["task.started", "task.completed", "task.failed", "step.started", ...]
714
+
715
+ # Get detailed event information
716
+ Tasker::Events.event_info('task.completed')
717
+ # => { name: "task.completed", category: "task", description: "...", ... }
718
+
719
+ # Browse by category
720
+ Tasker::Events.task_events.keys # Task lifecycle events
721
+ Tasker::Events.step_events.keys # Step execution events
722
+ Tasker::Events.workflow_events.keys # Orchestration events
723
+ ```
724
+
725
+ ### Real-World Integration Examples
726
+
727
+ **Metrics Collection (DataDog)**:
728
+ ```ruby
729
+ class MetricsSubscriber < Tasker::Events::Subscribers::BaseSubscriber
730
+ subscribe_to 'task.completed', 'task.failed', 'step.completed'
731
+
732
+ def handle_task_completed(event)
733
+ task_name = safe_get(event, :task_name, 'unknown')
734
+ execution_duration = safe_get(event, :execution_duration, 0)
735
+
736
+ StatsD.histogram('tasker.task.duration', execution_duration, tags: ["task:#{task_name}"])
737
+ StatsD.increment('tasker.task.completed', tags: ["task:#{task_name}"])
738
+ end
739
+
740
+ def handle_task_failed(event)
741
+ task_name = safe_get(event, :task_name, 'unknown')
742
+ error_class = safe_get(event, :exception_class, 'unknown')
743
+
744
+ StatsD.increment('tasker.task.failed', tags: ["task:#{task_name}", "error:#{error_class}"])
745
+ end
746
+ end
747
+ ```
748
+
749
+ **Error Tracking (Sentry)**:
750
+ ```ruby
751
+ class SentrySubscriber < Tasker::Events::Subscribers::BaseSubscriber
752
+ subscribe_to 'task.failed', 'step.failed'
753
+
754
+ def handle_task_failed(event)
755
+ task_id = safe_get(event, :task_id)
756
+ error_message = safe_get(event, :error_message, 'Unknown error')
757
+
758
+ Sentry.capture_message(error_message,
759
+ level: 'error',
760
+ fingerprint: ['tasker', 'task_failed', task_id],
761
+ tags: { task_id: task_id, component: 'tasker' }
762
+ )
763
+ end
764
+ end
765
+ ```
766
+
767
+ ## 4. YAML Configuration
768
+
769
+ YAML files provide declarative configuration for task handlers, defining workflows, dependencies, and step settings without requiring code changes.
770
+
771
+ ### Task Handler YAML Structure
772
+
773
+ ```yaml
774
+ # config/tasker/tasks/payments/order_process.yaml
775
+ ---
776
+ name: order_process
777
+ namespace_name: payments # NEW: TaskNamespace organization
778
+ version: 1.2.0 # NEW: Semantic versioning
779
+ module_namespace: OrderProcess # Ruby module namespace
780
+ task_handler_class: OrderHandler
781
+
782
+ # JSON Schema validation for task context
783
+ schema:
784
+ type: object
785
+ required:
786
+ - order_id
787
+ - customer_id
788
+ properties:
789
+ order_id:
790
+ type: integer
791
+ customer_id:
792
+ type: integer
793
+ priority:
794
+ type: string
795
+ enum: [low, normal, high, critical]
796
+
797
+ # Step definitions with dependencies
798
+ step_templates:
799
+ - name: fetch_order
800
+ description: Retrieve order details from database
801
+ handler_class: OrderProcess::StepHandler::FetchOrderHandler
802
+
803
+ - name: validate_inventory
804
+ description: Check product availability
805
+ depends_on_step: fetch_order
806
+ handler_class: OrderProcess::StepHandler::ValidateInventoryHandler
807
+ default_retryable: true
808
+ default_retry_limit: 3
809
+
810
+ - name: process_payment
811
+ description: Charge customer payment method
812
+ depends_on_step: validate_inventory
813
+ handler_class: OrderProcess::StepHandler::ProcessPaymentHandler
814
+ default_retryable: true
815
+ default_retry_limit: 2
816
+
817
+ - name: update_inventory
818
+ description: Decrement inventory levels
819
+ depends_on_step: process_payment
820
+ handler_class: OrderProcess::StepHandler::UpdateInventoryHandler
821
+
822
+ - name: send_confirmation
823
+ description: Send order confirmation email
824
+ depends_on_step: update_inventory
825
+ handler_class: OrderProcess::StepHandler::SendConfirmationHandler
826
+ default_retryable: true
827
+ default_retry_limit: 5
828
+
829
+ # Environment-specific overrides
830
+ environments:
831
+ development:
832
+ step_templates:
833
+ - name: process_payment
834
+ # Use test payment processor in development
835
+ handler_config:
836
+ payment_processor: test
837
+
838
+ production:
839
+ step_templates:
840
+ - name: process_payment
841
+ # Production payment configuration
842
+ handler_config:
843
+ payment_processor: stripe
844
+ timeout: 30
845
+ ```
846
+
847
+ ### Advanced YAML Features
848
+
849
+ **Multiple Dependencies**:
850
+ ```yaml
851
+ - name: finalize_order
852
+ description: Complete order processing
853
+ depends_on_steps: # Multiple dependencies
854
+ - process_payment
855
+ - update_inventory
856
+ - reserve_shipping
857
+ handler_class: OrderProcess::StepHandler::FinalizeOrderHandler
858
+ ```
859
+
860
+ **Environment-Specific Configuration**:
861
+ ```yaml
862
+ environments:
863
+ test:
864
+ step_templates:
865
+ - name: send_email
866
+ handler_config:
867
+ email_service: mock
868
+
869
+ staging:
870
+ step_templates:
871
+ - name: send_email
872
+ handler_config:
873
+ email_service: sendgrid_test
874
+
875
+ production:
876
+ step_templates:
877
+ - name: send_email
878
+ handler_config:
879
+ email_service: sendgrid_production
880
+ api_key: ${SENDGRID_API_KEY}
881
+ ```
882
+
883
+ **API Step Configuration**:
884
+ ```yaml
885
+ - name: fetch_external_data
886
+ handler_class: OrderProcess::StepHandler::FetchExternalDataHandler
887
+ handler_config:
888
+ type: api
889
+ url: https://api.external-service.com/data
890
+ method: GET
891
+ headers:
892
+ Authorization: "Bearer ${API_TOKEN}"
893
+ timeout: 15
894
+ retries: 3
895
+ ```
896
+
897
+ ### TaskNamespace + Versioning in YAML
898
+
899
+ The new namespace and versioning system provides enterprise-scale organization:
900
+
901
+ #### Namespace-Based File Organization
902
+
903
+ **Recommended File Structure**:
904
+ ```
905
+ config/tasker/tasks/
906
+ ├── payments/
907
+ │ ├── process_order.yaml # version: 1.0.0
908
+ │ ├── process_refund.yaml # version: 1.1.0
909
+ │ └── validate_payment.yaml # version: 2.0.0
910
+ ├── inventory/
911
+ │ ├── process_order.yaml # version: 1.5.0 (same name, different namespace)
912
+ │ ├── stock_check.yaml # version: 1.0.0
913
+ │ └── reorder_items.yaml # version: 1.2.0
914
+ └── notifications/
915
+ ├── send_email.yaml # version: 1.0.0
916
+ ├── send_sms.yaml # version: 1.1.0
917
+ └── push_notification.yaml # version: 2.0.0
918
+ ```
919
+
920
+ #### Version Coexistence Examples
921
+
922
+ **Multiple Versions of Same Task**:
923
+ ```yaml
924
+ # config/tasker/tasks/payments/process_order_v1.yaml
925
+ ---
926
+ name: process_order
927
+ namespace_name: payments
928
+ version: 1.0.0 # Legacy version
929
+ task_handler_class: Payments::ProcessOrderV1
930
+
931
+ # config/tasker/tasks/payments/process_order_v2.yaml
932
+ ---
933
+ name: process_order
934
+ namespace_name: payments
935
+ version: 2.0.0 # Current version
936
+ task_handler_class: Payments::ProcessOrderV2
937
+ ```
938
+
939
+ #### Namespace-Specific Configuration
940
+
941
+ **Domain-Specific Settings**:
942
+ ```yaml
943
+ # config/tasker/tasks/integrations/api_sync.yaml
944
+ ---
945
+ name: api_sync
946
+ namespace_name: integrations
947
+ version: 1.3.0
948
+ task_handler_class: Integrations::ApiSyncHandler
949
+
950
+ # Integration-specific settings
951
+ handler_config:
952
+ max_concurrent_requests: 5
953
+ rate_limit_per_second: 10
954
+ dependent_systems: # External systems this task interacts with
955
+ - salesforce_api
956
+ - inventory_service
957
+ - notification_queue
958
+
959
+ step_templates:
960
+ - name: fetch_data
961
+ handler_class: Integrations::FetchDataHandler
962
+ dependent_system: salesforce_api # Step-level system identification
963
+
964
+ - name: transform_data
965
+ handler_class: Integrations::TransformDataHandler
966
+
967
+ - name: upload_data
968
+ handler_class: Integrations::UploadDataHandler
969
+ dependent_system: inventory_service
970
+ ```
971
+
972
+ #### Configuration Validation & Defaults
973
+
974
+ **YAML Configuration Schema**:
975
+ ```yaml
976
+ # Full configuration with all optional fields
977
+ ---
978
+ name: process_payment # Required
979
+ namespace_name: payments # Optional - defaults to 'default'
980
+ version: 2.1.0 # Optional - defaults to '0.1.0'
981
+ module_namespace: Payments # Optional - Ruby module namespace
982
+ task_handler_class: ProcessPaymentHandler # Required
983
+ description: "Advanced payment processing" # Optional - for documentation
984
+
985
+ # Database configuration (optional)
986
+ configuration:
987
+ priority: high
988
+ max_execution_time: 300
989
+ custom_metadata:
990
+ team: payments
991
+ owner: alice@company.com
992
+ documentation_url: https://wiki.company.com/payments
993
+ ```
994
+
995
+ #### Migration Strategy for Existing Configurations
996
+
997
+ **Backward Compatibility**: Existing YAML files continue working unchanged:
998
+ ```yaml
999
+ # Existing file - continues working with defaults
1000
+ ---
1001
+ name: legacy_task
1002
+ task_handler_class: LegacyHandler
1003
+ # Automatically gets: namespace_name: 'default', version: '0.1.0'
1004
+
1005
+ # Enhanced file - new capabilities
1006
+ ---
1007
+ name: legacy_task
1008
+ namespace_name: default # Explicit default
1009
+ version: 0.1.0 # Explicit version
1010
+ task_handler_class: LegacyHandler
1011
+ ```
1012
+
1013
+ ## 5. Authentication & Authorization
1014
+
1015
+ Tasker provides a comprehensive, production-ready authentication and authorization system that works with any Rails authentication solution. The system uses **dependency injection** and **resource-based authorization** to provide enterprise-grade security for both REST APIs and GraphQL endpoints.
1016
+
1017
+ ### Key Features
1018
+
1019
+ - **Provider Agnostic**: Works with Devise, JWT, OmniAuth, custom authentication, or no authentication
1020
+ - **Resource-Based Authorization**: Granular permissions using resource:action patterns (e.g., `tasker.task:create`)
1021
+ - **GraphQL Operation-Level Authorization**: Revolutionary security for GraphQL that maps operations to resource permissions
1022
+ - **Automatic Controller Integration**: Authentication and authorization work seamlessly across REST and GraphQL
1023
+ - **Comprehensive Generators**: Create production-ready authenticators and authorization coordinators
1024
+ - **Zero Breaking Changes**: All features are opt-in and backward compatible
1025
+
1026
+ ### Quick Configuration Example
1027
+
1028
+ ```ruby
1029
+ # config/initializers/tasker.rb
1030
+ Tasker.configuration do |config|
1031
+ config.auth do |auth|
1032
+ # Authentication
1033
+ auth.authentication_enabled = true
1034
+ auth.authenticator_class = 'YourCustomAuthenticator'
1035
+
1036
+ # Authorization
1037
+ auth.authorization_enabled = true
1038
+ auth.authorization_coordinator_class = 'YourAuthorizationCoordinator'
1039
+ auth.user_class = 'User'
1040
+ end
1041
+ end
1042
+ ```
1043
+
1044
+ ### Available Generators
1045
+
1046
+ ```bash
1047
+ # Generate authenticators for different systems
1048
+ rails generate tasker:authenticator CompanyJWT --type=jwt
1049
+ rails generate tasker:authenticator AdminAuth --type=devise --user-class=Admin
1050
+ rails generate tasker:authenticator ApiAuth --type=api_token
1051
+ rails generate tasker:authenticator SocialAuth --type=omniauth
1052
+
1053
+ # Generate authorization coordinator
1054
+ rails generate tasker:authorization_coordinator CompanyAuth
1055
+ ```
1056
+
1057
+ ### GraphQL Authorization Example
1058
+
1059
+ The system automatically maps GraphQL operations to resource permissions:
1060
+
1061
+ ```ruby
1062
+ # This GraphQL query:
1063
+ query { tasks { taskId status } }
1064
+
1065
+ # Automatically requires: tasker.task:index permission
1066
+
1067
+ # This mutation:
1068
+ mutation { createTask(input: {...}) { taskId } }
1069
+
1070
+ # Automatically requires: tasker.task:create permission
1071
+ ```
1072
+
1073
+ ### Resource-Based Permissions
1074
+
1075
+ Authorization uses a simple resource:action permission model:
1076
+
1077
+ ```ruby
1078
+ # Available permissions:
1079
+ 'tasker.task:index' # List all tasks
1080
+ 'tasker.task:create' # Create new tasks
1081
+ 'tasker.workflow_step:show' # View individual workflow steps
1082
+ 'tasker.task_diagram:index' # List task diagrams
1083
+ ```
1084
+
1085
+ ### Complete Documentation
1086
+
1087
+ For comprehensive documentation including:
1088
+ - **Quick Start Guides** - Get authentication working in minutes
1089
+ - **Custom Authenticator Examples** - JWT, Devise, API tokens, and more
1090
+ - **Authorization Coordinator Patterns** - Role-based, context-aware, and time-based authorization
1091
+ - **GraphQL Authorization Details** - Operation mapping and context handling
1092
+ - **Production Best Practices** - Security, performance, and monitoring guidelines
1093
+ - **Testing Strategies** - Complete test examples and isolation techniques
1094
+
1095
+ **See [Authentication & Authorization Guide](AUTH.md)** for complete documentation.
1096
+
1097
+ ## 6. Multi-Database Support
1098
+
1099
+ Tasker provides optional multi-database support using Rails' standard multi-database conventions. This allows Tasker models to use a separate database from the host application for data isolation, performance, or compliance requirements.
1100
+
1101
+ ### Key Features
1102
+
1103
+ - **Rails Multi-Database Integration**: Uses Rails' `connects_to` API following official conventions
1104
+ - **Standard Configuration**: Leverages Rails database.yml patterns with named databases
1105
+ - **Automatic Model Support**: All Tasker models inherit multi-database capability automatically
1106
+ - **Zero Breaking Changes**: Fully backward compatible with existing installations
1107
+ - **Environment-Specific**: Supports different database configurations per environment
1108
+
1109
+ ### Configuration
1110
+
1111
+ ```ruby
1112
+ # config/initializers/tasker.rb
1113
+
1114
+ # Default: Use host application database (shared)
1115
+ Tasker.configuration do |config|
1116
+ config.database.enable_secondary_database = false
1117
+ end
1118
+
1119
+ # Use dedicated Tasker database
1120
+ Tasker.configuration do |config|
1121
+ config.database.enable_secondary_database = true
1122
+ config.database.name = :tasker
1123
+ end
1124
+
1125
+ # Environment-specific configuration
1126
+ Tasker.configuration do |config|
1127
+ config.database.enable_secondary_database = Rails.env.production?
1128
+ config.database.name = Rails.env.production? ? :tasker : nil
1129
+ end
1130
+ ```
1131
+
1132
+ ### Database Configuration
1133
+
1134
+ ```yaml
1135
+ # config/database.yml
1136
+ production:
1137
+ primary:
1138
+ database: my_primary_database
1139
+ adapter: postgresql
1140
+ username: app_user
1141
+ password: <%= ENV['DATABASE_PASSWORD'] %>
1142
+
1143
+ tasker:
1144
+ database: my_tasker_database
1145
+ adapter: postgresql
1146
+ username: tasker_user
1147
+ password: <%= ENV['TASKER_DATABASE_PASSWORD'] %>
1148
+ ```
1149
+
1150
+ ### Benefits of Multi-Database Setup
1151
+
1152
+ **Data Isolation**: Separate Tasker data from application data for security or compliance
1153
+
1154
+ **Performance**: Dedicated database resources for workflow processing
1155
+
1156
+ **Scaling**: Independent scaling of workflow database based on usage patterns
1157
+
1158
+ **Backup Strategy**: Separate backup and recovery policies for workflow data
1159
+
1160
+ **Development**: Easier testing and development with isolated workflow data
1161
+
1162
+ ### Migration Support
1163
+
1164
+ When using a secondary database, Tasker migrations automatically target the correct database:
1165
+
1166
+ ```bash
1167
+ # Migrations run against the configured Tasker database
1168
+ bundle exec rails tasker:install:migrations
1169
+ bundle exec rails tasker:install:database_objects
1170
+ bundle exec rails db:migrate
1171
+ ```
1172
+
1173
+ ### Production Considerations
1174
+
1175
+ - **Connection Pooling**: Configure appropriate connection pool sizes for both databases
1176
+ - **Monitoring**: Monitor connection usage and performance for both databases
1177
+ - **Backup Strategy**: Implement coordinated backup strategies if data consistency across databases is required
1178
+ - **Network Latency**: Consider network latency if databases are on different servers
1179
+
1180
+ ## 7. Dependency Graph & Bottleneck Analysis Configuration
1181
+
1182
+ Tasker provides advanced dependency graph analysis and bottleneck detection capabilities that can be fine-tuned for your specific workflow patterns. The dependency graph configuration controls how Tasker analyzes workflow dependencies, identifies bottlenecks, and calculates impact scores for optimization recommendations.
1183
+
1184
+ ### Key Features
1185
+
1186
+ - **Configurable Impact Scoring**: Customize how different factors influence bottleneck calculations
1187
+ - **Adaptive Severity Classification**: Define custom thresholds for Critical/High/Medium/Low priority bottlenecks
1188
+ - **Dynamic Duration Estimation**: Configure time estimates for path analysis and planning
1189
+ - **State-Based Multipliers**: Adjust scoring based on step states and execution conditions
1190
+ - **Retry Pattern Analysis**: Configure penalties for instability and failure patterns
1191
+
1192
+ ### Quick Configuration Example
1193
+
1194
+ ```ruby
1195
+ # config/initializers/tasker.rb
1196
+ Tasker.configuration do |config|
1197
+ config.dependency_graph do |graph|
1198
+ # Prioritize blocked steps more heavily
1199
+ graph.impact_multipliers = {
1200
+ blocked_weight: 20, # Increase from default 15
1201
+ error_penalty: 40 # Increase from default 30
1202
+ }
1203
+
1204
+ # Lower thresholds for faster bottleneck detection
1205
+ graph.severity_thresholds = {
1206
+ critical: 80, # Decrease from default 100
1207
+ high: 40, # Decrease from default 50
1208
+ medium: 15 # Decrease from default 20
1209
+ }
1210
+ end
1211
+ end
1212
+ ```
1213
+
1214
+ ### Configuration Options
1215
+
1216
+ #### Impact Multipliers
1217
+
1218
+ Control how different factors contribute to the overall bottleneck impact score:
1219
+
1220
+ ```ruby
1221
+ config.dependency_graph do |graph|
1222
+ graph.impact_multipliers = {
1223
+ downstream_weight: 5, # Weight for downstream step count
1224
+ blocked_weight: 15, # Weight for blocked step count (most critical)
1225
+ path_length_weight: 10, # Weight for critical path length
1226
+ completed_penalty: 15, # Penalty to reduce priority of completed work
1227
+ blocked_penalty: 25, # Penalty to increase priority of blocked work
1228
+ error_penalty: 30, # Penalty for steps in error state (highest)
1229
+ retry_penalty: 10 # Penalty for steps requiring retries
1230
+ }
1231
+ end
1232
+ ```
1233
+
1234
+ **Impact Score Calculation:**
1235
+ ```
1236
+ base_score = (downstream_count * downstream_weight) + (blocked_count * blocked_weight)
1237
+ path_score = path_length * path_length_weight
1238
+ penalties = (completed_steps * completed_penalty) + (blocked_steps * blocked_penalty) +
1239
+ (error_steps * error_penalty) + (retry_steps * retry_penalty)
1240
+ final_score = (base_score + path_score + penalties) * severity_multiplier
1241
+ ```
1242
+
1243
+ #### Severity Multipliers
1244
+
1245
+ Adjust impact scores based on step states and execution conditions:
1246
+
1247
+ ```ruby
1248
+ config.dependency_graph do |graph|
1249
+ graph.severity_multipliers = {
1250
+ error_state: 2.0, # Multiply score by 2.0 for steps in error state
1251
+ exhausted_retry_bonus: 0.5, # Additional 0.5x multiplier for exhausted retries
1252
+ dependency_issue: 1.2 # 1.2x multiplier for dependency-related issues
1253
+ }
1254
+ end
1255
+ ```
1256
+
1257
+ **State-Based Scoring Examples:**
1258
+ - Normal step: `base_score * 1.0`
1259
+ - Error step: `base_score * 2.0`
1260
+ - Error step with exhausted retries: `base_score * (2.0 + 0.5) = base_score * 2.5`
1261
+ - Dependency blocked step: `base_score * 1.2`
1262
+
1263
+ #### Penalty Constants
1264
+
1265
+ Add fixed penalty points for specific problematic conditions:
1266
+
1267
+ ```ruby
1268
+ config.dependency_graph do |graph|
1269
+ graph.penalty_constants = {
1270
+ retry_instability: 3, # +3 points per retry attempt
1271
+ non_retryable: 10, # +10 points for non-retryable failures
1272
+ exhausted_retry: 20 # +20 points for exhausted retry attempts
1273
+ }
1274
+ end
1275
+ ```
1276
+
1277
+ **Penalty Application:**
1278
+ - Step with 2 retry attempts: `+6 penalty points`
1279
+ - Non-retryable failure: `+10 penalty points`
1280
+ - Exhausted retries (3+ attempts): `+20 penalty points`
1281
+
1282
+ #### Severity Thresholds
1283
+
1284
+ Define score ranges for bottleneck classification:
1285
+
1286
+ ```ruby
1287
+ config.dependency_graph do |graph|
1288
+ graph.severity_thresholds = {
1289
+ critical: 100, # Score >= 100: Requires immediate attention
1290
+ high: 50, # Score >= 50: High priority for optimization
1291
+ medium: 20 # Score >= 20: Monitor and plan improvements
1292
+ } # Score < 20: Low priority
1293
+ end
1294
+ ```
1295
+
1296
+ **Classification Examples:**
1297
+ - Score 150: **Critical** - Blocking multiple workflows, immediate intervention required
1298
+ - Score 75: **High** - Significant impact, should be addressed in current sprint
1299
+ - Score 35: **Medium** - Moderate impact, address in upcoming planning cycle
1300
+ - Score 10: **Low** - Minor impact, monitor for trends
1301
+
1302
+ #### Duration Estimates
1303
+
1304
+ Configure time estimates for path analysis and planning:
1305
+
1306
+ ```ruby
1307
+ config.dependency_graph do |graph|
1308
+ graph.duration_estimates = {
1309
+ base_step_seconds: 30, # Default time estimate per step
1310
+ error_penalty_seconds: 60, # Additional time for error recovery
1311
+ retry_penalty_seconds: 30 # Additional time per retry attempt
1312
+ }
1313
+ end
1314
+ ```
1315
+
1316
+ **Duration Calculation:**
1317
+ ```
1318
+ estimated_duration = (step_count * base_step_seconds) +
1319
+ (error_steps * error_penalty_seconds) +
1320
+ (total_retry_attempts * retry_penalty_seconds)
1321
+ ```
1322
+
1323
+ ### Use Cases & Recommendations
1324
+
1325
+ #### High-Volume Transaction Processing
1326
+
1327
+ For workflows processing thousands of transactions per hour:
1328
+
1329
+ ```ruby
1330
+ config.dependency_graph do |graph|
1331
+ # Emphasize blocking issues more heavily
1332
+ graph.impact_multipliers = {
1333
+ blocked_weight: 25, # Increase sensitivity to blocked steps
1334
+ error_penalty: 40 # Prioritize error resolution
1335
+ }
1336
+
1337
+ # Lower thresholds for faster detection
1338
+ graph.severity_thresholds = {
1339
+ critical: 75, # Faster escalation
1340
+ high: 35,
1341
+ medium: 15
1342
+ }
1343
+
1344
+ # Faster execution estimates for high-volume patterns
1345
+ graph.duration_estimates = {
1346
+ base_step_seconds: 15, # Optimized processes run faster
1347
+ error_penalty_seconds: 45 # Reduced error recovery time
1348
+ }
1349
+ end
1350
+ ```
1351
+
1352
+ #### Long-Running Batch Processes
1353
+
1354
+ For workflows that run for hours or days:
1355
+
1356
+ ```ruby
1357
+ config.dependency_graph do |graph|
1358
+ # Focus on path length and completion tracking
1359
+ graph.impact_multipliers = {
1360
+ path_length_weight: 20, # Longer paths have higher impact
1361
+ completed_penalty: 5 # Reduce penalty for completed work
1362
+ }
1363
+
1364
+ # Higher thresholds due to expected longer execution
1365
+ graph.severity_thresholds = {
1366
+ critical: 200,
1367
+ high: 100,
1368
+ medium: 50
1369
+ }
1370
+
1371
+ # Longer execution estimates
1372
+ graph.duration_estimates = {
1373
+ base_step_seconds: 120, # Steps take longer in batch processes
1374
+ error_penalty_seconds: 300 # Error recovery is more expensive
1375
+ }
1376
+ end
1377
+ ```
1378
+
1379
+ #### Real-Time Processing Systems
1380
+
1381
+ For workflows requiring sub-second response times:
1382
+
1383
+ ```ruby
1384
+ config.dependency_graph do |graph|
1385
+ # Maximize sensitivity to any delays
1386
+ graph.impact_multipliers = {
1387
+ retry_penalty: 20, # Retries are very costly
1388
+ error_penalty: 50 # Errors must be resolved immediately
1389
+ }
1390
+
1391
+ # Very low thresholds for immediate response
1392
+ graph.severity_thresholds = {
1393
+ critical: 30,
1394
+ high: 15,
1395
+ medium: 5
1396
+ }
1397
+
1398
+ # Tight time estimates
1399
+ graph.duration_estimates = {
1400
+ base_step_seconds: 1, # Sub-second execution expected
1401
+ error_penalty_seconds: 10, # Errors add significant delay
1402
+ retry_penalty_seconds: 5 # Each retry is expensive
1403
+ }
1404
+ end
1405
+ ```
1406
+
1407
+ ### Monitoring & Observability
1408
+
1409
+ The dependency graph analysis integrates with Tasker's observability system to provide actionable insights:
1410
+
1411
+ ```ruby
1412
+ # Published events include bottleneck severity levels
1413
+ Tasker::Events.subscribe('task.bottleneck_detected') do |event|
1414
+ severity = event.payload['severity'] # 'Critical', 'High', 'Medium', 'Low'
1415
+ impact_score = event.payload['impact_score']
1416
+
1417
+ case severity
1418
+ when 'Critical'
1419
+ PagerDuty.alert("Critical workflow bottleneck detected: #{impact_score}")
1420
+ when 'High'
1421
+ Slack.notify("#ops", "High priority bottleneck: #{impact_score}")
1422
+ end
1423
+ end
1424
+ ```
1425
+
1426
+ ### Production Best Practices
1427
+
1428
+ 1. **Start with Defaults**: Begin with default values and adjust based on observed patterns
1429
+ 2. **Monitor Thresholds**: Track how often each severity level is triggered
1430
+ 3. **A/B Testing**: Test configuration changes on a subset of workflows first
1431
+ 4. **Gradual Tuning**: Make small adjustments and measure impact over time
1432
+ 5. **Documentation**: Document your configuration rationale for team knowledge
1433
+
1434
+ The dependency graph configuration provides powerful tools for optimizing workflow performance while maintaining the flexibility to adapt to your specific operational requirements.
1435
+
1436
+ ## 8. Cache Strategy & Custom Store Capabilities
1437
+
1438
+ Tasker includes a **Hybrid Cache Detection System** that provides intelligent cache strategy selection and supports both built-in Rails cache stores and custom cache implementations. This system automatically adapts coordination strategies based on cache store capabilities, ensuring optimal performance across different deployment environments.
1439
+
1440
+ ### Key Features
1441
+
1442
+ - **🎯 Developer-Friendly API**: Declarative capability system for custom cache stores
1443
+ - **🚀 Performance Optimized**: Frozen constants provide O(1) lookup for built-in stores
1444
+ - **🔄 Hybrid Detection**: Priority-based detection system (declared → constants → custom → runtime)
1445
+ - **📊 Production-Ready**: Comprehensive structured logging and error handling
1446
+ - **⚡ Rails Integration**: Always uses `Rails.cache` as the source of truth
1447
+
1448
+ ### Quick Example
1449
+
1450
+ ```ruby
1451
+ # For custom cache stores - declare capabilities explicitly
1452
+ class MyAwesomeCacheStore < ActiveSupport::Cache::Store
1453
+ include Tasker::CacheCapabilities
1454
+
1455
+ # Use convenience methods for common capabilities
1456
+ supports_distributed_caching!
1457
+ supports_atomic_increment!
1458
+ supports_locking!
1459
+
1460
+ # Or declare specific capabilities
1461
+ declare_cache_capability(:advanced_analytics, true)
1462
+ declare_cache_capability(:compression_support, false)
1463
+ end
1464
+
1465
+ # Configure Rails to use your custom store
1466
+ Rails.application.configure do
1467
+ config.cache_store = MyAwesomeCacheStore.new
1468
+ end
1469
+
1470
+ # Tasker automatically detects capabilities and selects optimal strategy
1471
+ strategy = Tasker::CacheStrategy.detect
1472
+ puts strategy.coordination_mode # => :distributed_atomic
1473
+ puts strategy.supports?(:distributed) # => true
1474
+ ```
1475
+
1476
+ ### Architecture Overview
1477
+
1478
+ The cache detection system uses a **4-level priority hierarchy**:
1479
+
1480
+ ```mermaid
1481
+ flowchart TD
1482
+ A[Cache Store Detection] --> B{Priority 1: Declared?}
1483
+ B -->|Yes| C[Use Declared Capabilities]
1484
+ B -->|No| D{Priority 2: Built-in Store?}
1485
+ D -->|Yes| E[Use Frozen Constants]
1486
+ D -->|No| F{Priority 3: Custom Detector?}
1487
+ F -->|Yes| G[Apply Custom Detector]
1488
+ F -->|No| H[Priority 4: Runtime Detection]
1489
+
1490
+ C --> I[Select Coordination Strategy]
1491
+ E --> I
1492
+ G --> I
1493
+ H --> I
1494
+
1495
+ I --> J{Strategy Selection}
1496
+ J --> K[distributed_atomic]
1497
+ J --> L[distributed_basic]
1498
+ J --> M[local_only]
1499
+
1500
+ classDef priority fill:#e1f5fe,stroke:#01579b
1501
+ classDef strategy fill:#f3e5f5,stroke:#4a148c
1502
+
1503
+ class B,D,F priority
1504
+ class K,L,M strategy
1505
+ ```
1506
+
1507
+ ### Built-in Store Support
1508
+
1509
+ Tasker includes **frozen constants** for fast, reliable detection of official Rails cache stores:
1510
+
1511
+ ```ruby
1512
+ # Automatically detected with O(1) lookup
1513
+ DISTRIBUTED_CACHE_STORES = %w[
1514
+ ActiveSupport::Cache::RedisCacheStore
1515
+ ActiveSupport::Cache::MemCacheStore
1516
+ SolidCache::Store
1517
+ ].freeze
1518
+
1519
+ ATOMIC_INCREMENT_STORES = %w[
1520
+ ActiveSupport::Cache::RedisCacheStore
1521
+ ActiveSupport::Cache::MemCacheStore
1522
+ SolidCache::Store
1523
+ ].freeze
1524
+
1525
+ LOCKING_CAPABLE_STORES = %w[
1526
+ ActiveSupport::Cache::RedisCacheStore
1527
+ SolidCache::Store
1528
+ ].freeze
1529
+
1530
+ LOCAL_CACHE_STORES = %w[
1531
+ ActiveSupport::Cache::MemoryStore
1532
+ ActiveSupport::Cache::FileStore
1533
+ ActiveSupport::Cache::NullStore
1534
+ ].freeze
1535
+ ```
1536
+
1537
+ **Coordination Strategies by Store Type**:
1538
+ - **Redis/SolidCache**: `distributed_atomic` (full distributed coordination with locking)
1539
+ - **Memcached**: `distributed_basic` (distributed coordination without locking)
1540
+ - **Memory/File/Null**: `local_only` (single-process coordination)
1541
+
1542
+ ### Custom Cache Store Integration
1543
+
1544
+ #### Using CacheCapabilities Module
1545
+
1546
+ The `Tasker::CacheCapabilities` module provides a clean API for declaring your cache store's capabilities:
1547
+
1548
+ ```ruby
1549
+ class MyRedisCluster < ActiveSupport::Cache::Store
1550
+ include Tasker::CacheCapabilities
1551
+
1552
+ # Convenience methods for common capabilities
1553
+ supports_distributed_caching! # Sets distributed: true
1554
+ supports_atomic_increment! # Sets atomic_increment: true
1555
+ supports_locking! # Sets locking: true
1556
+ supports_ttl_inspection! # Sets ttl_inspection: true
1557
+ supports_namespace_isolation! # Sets namespace_support: true
1558
+ supports_compression! # Sets compression_support: true
1559
+
1560
+ # Custom capabilities specific to your implementation
1561
+ declare_cache_capability(:cluster_failover, true)
1562
+ declare_cache_capability(:read_replicas, true)
1563
+ declare_cache_capability(:geo_replication, false)
1564
+
1565
+ # Your cache store implementation...
1566
+ def read(name, options = nil)
1567
+ # Implementation
1568
+ end
1569
+
1570
+ def write(name, value, options = nil)
1571
+ # Implementation
1572
+ end
1573
+
1574
+ # Optional: Implement advanced features
1575
+ def increment(name, amount = 1, options = nil)
1576
+ # Atomic increment implementation
1577
+ end
1578
+
1579
+ def with_lock(name, options = {}, &block)
1580
+ # Distributed locking implementation
1581
+ end
1582
+ end
1583
+ ```
1584
+
1585
+ #### Capability Reference
1586
+
1587
+ | Capability | Description | Impact |
1588
+ |------------|-------------|---------|
1589
+ | `distributed` | Cache is shared across processes/containers | Enables distributed coordination |
1590
+ | `atomic_increment` | Supports atomic increment/decrement operations | Enables optimistic locking patterns |
1591
+ | `locking` | Supports distributed locking (with_lock) | Enables `distributed_atomic` strategy |
1592
+ | `ttl_inspection` | Can inspect/extend TTL of cached entries | Enables advanced TTL management |
1593
+ | `namespace_support` | Supports cache key namespacing | Enables namespace isolation |
1594
+ | `compression_support` | Supports automatic value compression | Enables bandwidth optimization |
1595
+
1596
+ #### Chaining and Inheritance
1597
+
1598
+ ```ruby
1599
+ class BaseDistributedStore < ActiveSupport::Cache::Store
1600
+ include Tasker::CacheCapabilities
1601
+
1602
+ supports_distributed_caching!
1603
+ supports_ttl_inspection!
1604
+ end
1605
+
1606
+ class RedisClusterStore < BaseDistributedStore
1607
+ # Inherits distributed and ttl_inspection capabilities
1608
+ supports_atomic_increment!
1609
+ supports_locking!
1610
+
1611
+ # Override inherited capabilities if needed
1612
+ declare_cache_capability(:ttl_inspection, false) # Override parent
1613
+ end
1614
+
1615
+ class MemcachedClusterStore < BaseDistributedStore
1616
+ # Different capabilities for different technology
1617
+ supports_atomic_increment!
1618
+ # Note: Memcached doesn't support distributed locking
1619
+ end
1620
+ ```
1621
+
1622
+ ### Advanced Usage Patterns
1623
+
1624
+ #### Custom Detector Registration
1625
+
1626
+ For complex detection logic that can't be expressed through simple capability declarations:
1627
+
1628
+ ```ruby
1629
+ # Register a custom detector for pattern-based detection
1630
+ Tasker::CacheStrategy.register_detector(/MyCustomCache/, lambda do |store|
1631
+ {
1632
+ distributed: store.respond_to?(:cluster_nodes) && store.cluster_nodes.size > 1,
1633
+ atomic_increment: store.respond_to?(:atomic_ops),
1634
+ locking: store.respond_to?(:distributed_lock),
1635
+ custom_feature: store.respond_to?(:advanced_query)
1636
+ }
1637
+ end)
1638
+
1639
+ # The detector will be applied automatically during detection
1640
+ strategy = Tasker::CacheStrategy.detect
1641
+ ```
1642
+
1643
+ #### Runtime Strategy Inspection
1644
+
1645
+ ```ruby
1646
+ strategy = Tasker::CacheStrategy.detect
1647
+
1648
+ # Check coordination mode
1649
+ puts strategy.coordination_mode # => :distributed_atomic
1650
+
1651
+ # Check specific capabilities
1652
+ puts strategy.supports?(:distributed) # => true
1653
+ puts strategy.supports?(:locking) # => true
1654
+ puts strategy.supports?(:custom_feature) # => true
1655
+
1656
+ # Export all capabilities (useful for debugging)
1657
+ puts strategy.export_capabilities
1658
+ # => {
1659
+ # distributed: true,
1660
+ # atomic_increment: true,
1661
+ # locking: true,
1662
+ # ttl_inspection: true,
1663
+ # namespace_support: true,
1664
+ # compression_support: false,
1665
+ # key_transformation: true,
1666
+ # store_class: "MyAwesomeCacheStore"
1667
+ # }
1668
+ ```
1669
+
1670
+ #### Production Monitoring
1671
+
1672
+ The cache strategy system includes comprehensive structured logging:
1673
+
1674
+ ```ruby
1675
+ # Automatic logging when strategy is detected
1676
+ strategy = Tasker::CacheStrategy.detect
1677
+
1678
+ # Logs output (JSON format):
1679
+ {
1680
+ "timestamp": "2025-06-28T17:15:53.386Z",
1681
+ "correlation_id": "tsk_mcgi5my9_a24eXc",
1682
+ "component": "cache_strategy",
1683
+ "message": "Cache strategy detected",
1684
+ "environment": "production",
1685
+ "tasker_version": "2.5.0",
1686
+ "store_class": "MyAwesomeCacheStore",
1687
+ "coordination_strategy": "distributed_atomic",
1688
+ "capabilities": {
1689
+ "distributed": true,
1690
+ "atomic_increment": true,
1691
+ "locking": true,
1692
+ "ttl_inspection": true,
1693
+ "namespace_support": true,
1694
+ "compression_support": false,
1695
+ "key_transformation": true,
1696
+ "store_class": "MyAwesomeCacheStore"
1697
+ },
1698
+ "instance_id": "web-01-12345"
1699
+ }
1700
+ ```
1701
+
1702
+ ### Best Practices
1703
+
1704
+ #### Cache Store Selection
1705
+
1706
+ **For High-Performance Applications**:
1707
+ ```ruby
1708
+ # Redis with full capabilities
1709
+ class ProductionRedisStore < ActiveSupport::Cache::RedisCacheStore
1710
+ include Tasker::CacheCapabilities
1711
+
1712
+ supports_distributed_caching!
1713
+ supports_atomic_increment!
1714
+ supports_locking!
1715
+ supports_ttl_inspection!
1716
+ supports_namespace_isolation!
1717
+ supports_compression!
1718
+ end
1719
+ ```
1720
+
1721
+ **For Development/Testing**:
1722
+ ```ruby
1723
+ # Memory store with local-only coordination
1724
+ # No additional configuration needed - automatically detected
1725
+ Rails.application.configure do
1726
+ config.cache_store = :memory_store
1727
+ end
1728
+ ```
1729
+
1730
+ **For Legacy Memcached**:
1731
+ ```ruby
1732
+ class LegacyMemcachedStore < ActiveSupport::Cache::MemCacheStore
1733
+ include Tasker::CacheCapabilities
1734
+
1735
+ supports_distributed_caching!
1736
+ supports_atomic_increment!
1737
+ # Note: Don't declare locking support - Memcached doesn't support it
1738
+ supports_ttl_inspection!
1739
+ end
1740
+ ```
1741
+
1742
+ #### Testing Custom Stores
1743
+
1744
+ ```ruby
1745
+ # spec/lib/my_awesome_cache_store_spec.rb
1746
+ RSpec.describe MyAwesomeCacheStore do
1747
+ let(:store) { described_class.new }
1748
+
1749
+ describe 'capability declarations' do
1750
+ it 'declares expected capabilities' do
1751
+ capabilities = store.class.declared_cache_capabilities
1752
+
1753
+ expect(capabilities[:distributed]).to be(true)
1754
+ expect(capabilities[:atomic_increment]).to be(true)
1755
+ expect(capabilities[:locking]).to be(true)
1756
+ expect(capabilities[:advanced_analytics]).to be(true)
1757
+ expect(capabilities[:compression_support]).to be(false)
1758
+ end
1759
+ end
1760
+
1761
+ describe 'integration with CacheStrategy' do
1762
+ before do
1763
+ allow(Rails).to receive(:cache).and_return(store)
1764
+ end
1765
+
1766
+ it 'is detected correctly by CacheStrategy' do
1767
+ strategy = Tasker::CacheStrategy.detect
1768
+
1769
+ expect(strategy.coordination_mode).to eq(:distributed_atomic)
1770
+ expect(strategy.supports?(:distributed)).to be(true)
1771
+ expect(strategy.supports?(:advanced_analytics)).to be(true)
1772
+ end
1773
+ end
1774
+ end
1775
+ ```
1776
+
1777
+ ### Migration Guide
1778
+
1779
+ #### Upgrading Existing Custom Stores
1780
+
1781
+ If you have existing custom cache stores, add capability declarations:
1782
+
1783
+ ```ruby
1784
+ # Before: No capability declarations
1785
+ class MyLegacyStore < ActiveSupport::Cache::Store
1786
+ # Implementation...
1787
+ end
1788
+
1789
+ # After: With capability declarations
1790
+ class MyLegacyStore < ActiveSupport::Cache::Store
1791
+ include Tasker::CacheCapabilities
1792
+
1793
+ # Declare what your store actually supports
1794
+ supports_distributed_caching! if respond_to?(:cluster_mode?)
1795
+ supports_atomic_increment! if respond_to?(:increment)
1796
+ # etc.
1797
+ end
1798
+ ```
1799
+
1800
+ #### Backwards Compatibility
1801
+
1802
+ The system maintains full backwards compatibility:
1803
+ - **Undeclared stores**: Fall back to runtime pattern detection
1804
+ - **Existing configurations**: Continue working without changes
1805
+ - **Built-in stores**: Automatically detected with frozen constants
1806
+
1807
+ The cache strategy system provides a robust foundation for building cache-aware applications that adapt intelligently to different deployment environments while maintaining optimal performance characteristics.
1808
+
1809
+ ## Extensibility & Advanced Patterns
1810
+
1811
+ ### Custom Step Handler Types
1812
+
1813
+ Beyond the built-in `Base` and `Api` step handlers, you can create specialized step handler types for common patterns in your application:
1814
+
1815
+ ```ruby
1816
+ # Base class for database-heavy operations
1817
+ module YourApp
1818
+ module StepHandler
1819
+ class DatabaseBase < Tasker::StepHandler::Base
1820
+ protected
1821
+
1822
+ def with_transaction(&block)
1823
+ ActiveRecord::Base.transaction(&block)
1824
+ rescue ActiveRecord::StatementInvalid => e
1825
+ # Transform database errors for consistent handling
1826
+ raise Tasker::RetryableError, "Database operation failed: #{e.message}"
1827
+ end
1828
+
1829
+ def bulk_insert(model_class, records, batch_size: 1000)
1830
+ records.each_slice(batch_size) do |batch|
1831
+ model_class.insert_all(batch)
1832
+ end
1833
+ end
1834
+ end
1835
+ end
1836
+ end
1837
+
1838
+ # Usage in your step handlers
1839
+ class ProcessBulkOrdersHandler < YourApp::StepHandler::DatabaseBase
1840
+ def process(task, sequence, step)
1841
+ orders_data = get_previous_step_data(sequence, 'fetch_orders', 'orders')
1842
+
1843
+ with_transaction do
1844
+ bulk_insert(Order, orders_data, batch_size: 500)
1845
+ end
1846
+
1847
+ { processed_count: orders_data.size }
1848
+ end
1849
+ end
1850
+ ```
1851
+
1852
+ ### Custom Event Publishing Patterns
1853
+
1854
+ Create reusable event publishing patterns for consistent observability:
1855
+
1856
+ ```ruby
1857
+ # Mixin for consistent business event publishing
1858
+ module BusinessEventPublisher
1859
+ extend ActiveSupport::Concern
1860
+
1861
+ def publish_business_event(domain, action, entity_id, data = {})
1862
+ event_name = "#{domain}.#{action}"
1863
+ event_data = {
1864
+ entity_id: entity_id,
1865
+ domain: domain,
1866
+ action: action,
1867
+ timestamp: Time.current.iso8601,
1868
+ **data
1869
+ }
1870
+
1871
+ publish_custom_event(event_name, event_data)
1872
+ end
1873
+ end
1874
+
1875
+ # Usage in step handlers
1876
+ class ProcessOrderHandler < Tasker::StepHandler::Base
1877
+ include BusinessEventPublisher
1878
+
1879
+ def process(task, sequence, step)
1880
+ order = Order.find(task.context['order_id'])
1881
+
1882
+ # Process order logic here...
1883
+ order.update!(status: 'processed')
1884
+
1885
+ # Publish consistent business event
1886
+ publish_business_event('order', 'processed', order.id, {
1887
+ customer_id: order.customer_id,
1888
+ total_amount: order.total,
1889
+ processing_duration: Time.current - order.created_at
1890
+ })
1891
+
1892
+ { order_id: order.id, status: 'processed' }
1893
+ end
1894
+ end
1895
+ ```
1896
+
1897
+ ### Advanced Configuration Patterns
1898
+
1899
+ **Environment-Specific Step Configuration**:
1900
+ ```yaml
1901
+ # config/tasker/tasks/order_process/order_handler.yaml
1902
+ step_templates:
1903
+ - name: send_notification
1904
+ handler_class: OrderProcess::SendNotificationHandler
1905
+ handler_config:
1906
+ <% if Rails.env.production? %>
1907
+ notification_service: sms_gateway
1908
+ priority: high
1909
+ <% else %>
1910
+ notification_service: email_only
1911
+ priority: low
1912
+ <% end %>
1913
+ ```
1914
+
1915
+ **Dynamic Step Generation**:
1916
+ ```ruby
1917
+ # Support for dynamically generated workflows
1918
+ class DynamicOrderProcess < Tasker::TaskHandler::Base
1919
+ def self.generate_step_templates(product_types)
1920
+ base_steps = [
1921
+ { name: 'validate_order', handler_class: 'OrderProcess::ValidateOrderHandler' }
1922
+ ]
1923
+
1924
+ # Generate product-specific steps
1925
+ product_steps = product_types.map do |type|
1926
+ {
1927
+ name: "process_#{type}",
1928
+ depends_on_step: 'validate_order',
1929
+ handler_class: "OrderProcess::Process#{type.camelize}Handler"
1930
+ }
1931
+ end
1932
+
1933
+ merge_step = {
1934
+ name: 'finalize_order',
1935
+ depends_on_steps: product_steps.map { |step| step[:name] },
1936
+ handler_class: 'OrderProcess::FinalizeOrderHandler'
1937
+ }
1938
+
1939
+ base_steps + product_steps + [merge_step]
1940
+ end
1941
+ end
1942
+ ```
1943
+
1944
+ ### Custom Workflow Coordination
1945
+
1946
+ **Priority-Based Step Execution**:
1947
+ ```ruby
1948
+ module PriorityWorkflow
1949
+ class Coordinator < Tasker::Orchestration::Coordinator
1950
+ private
1951
+
1952
+ def prioritize_ready_steps(ready_steps)
1953
+ # Custom sorting based on step configuration
1954
+ ready_steps.sort_by do |step|
1955
+ priority = step.handler_config['priority'] || 'normal'
1956
+ case priority
1957
+ when 'critical' then 0
1958
+ when 'high' then 1
1959
+ when 'normal' then 2
1960
+ when 'low' then 3
1961
+ else 2
1962
+ end
1963
+ end
1964
+ end
1965
+ end
1966
+ end
1967
+
1968
+ # Configure in initializer
1969
+ Tasker.configuration do |config|
1970
+ config.orchestration.coordinator_class = 'PriorityWorkflow::Coordinator'
1971
+ end
1972
+ ```
1973
+
1974
+ **Resource-Aware Execution**:
1975
+ ```ruby
1976
+ class ResourceAwareStepHandler < Tasker::StepHandler::Base
1977
+ def process(task, sequence, step)
1978
+ # Check system resources before proceeding
1979
+ if system_under_load?
1980
+ # Delay execution by raising a retryable error
1981
+ raise Tasker::RetryableError, "System under load, retrying later"
1982
+ end
1983
+
1984
+ # Normal processing
1985
+ perform_resource_intensive_operation
1986
+ end
1987
+
1988
+ private
1989
+
1990
+ def system_under_load?
1991
+ # Check CPU, memory, or external service health
1992
+ cpu_usage > 80 || memory_usage > 90 || external_service_degraded?
1993
+ end
1994
+ end
1995
+ ```
1996
+
1997
+ ### Custom Result Processing
1998
+
1999
+ **Structured Result Schemas**:
2000
+ ```ruby
2001
+ class SchemaValidatedStepHandler < Tasker::StepHandler::Base
2002
+ RESULT_SCHEMA = {
2003
+ type: 'object',
2004
+ required: ['status', 'data'],
2005
+ properties: {
2006
+ status: { type: 'string', enum: ['success', 'partial', 'warning'] },
2007
+ data: { type: 'object' },
2008
+ metadata: { type: 'object' }
2009
+ }
2010
+ }.freeze
2011
+
2012
+ def process(task, sequence, step)
2013
+ result = perform_business_logic
2014
+
2015
+ # Validate result structure
2016
+ validate_result_schema(result)
2017
+
2018
+ result
2019
+ end
2020
+
2021
+ private
2022
+
2023
+ def validate_result_schema(result)
2024
+ errors = JSON::Validator.fully_validate(RESULT_SCHEMA, result)
2025
+ raise ArgumentError, "Invalid result schema: #{errors.join(', ')}" if errors.any?
2026
+ end
2027
+ end
2028
+ ```
2029
+
2030
+ **Result Transformation Pipelines**:
2031
+ ```ruby
2032
+ module ResultTransformers
2033
+ class SensitiveDataFilter
2034
+ def self.transform(results)
2035
+ # Remove sensitive data from results before storage
2036
+ results.except('password', 'api_key', 'secret_token')
2037
+ end
2038
+ end
2039
+
2040
+ class MetricsExtractor
2041
+ def self.transform(results)
2042
+ # Extract metrics for monitoring
2043
+ {
2044
+ **results,
2045
+ performance_metrics: {
2046
+ execution_time: results['duration'],
2047
+ memory_used: results['memory_peak'],
2048
+ records_processed: results['record_count']
2049
+ }
2050
+ }
2051
+ end
2052
+ end
2053
+ end
2054
+
2055
+ class TransformingStepHandler < Tasker::StepHandler::Base
2056
+ def process_results(step, process_output, initial_results)
2057
+ # Apply transformation pipeline
2058
+ transformed = process_output
2059
+ transformed = ResultTransformers::SensitiveDataFilter.transform(transformed)
2060
+ transformed = ResultTransformers::MetricsExtractor.transform(transformed)
2061
+
2062
+ step.results = transformed
2063
+ end
2064
+ end
2065
+ ```
2066
+
2067
+ ## Task Execution & Orchestration
2068
+
2069
+ For detailed understanding of Tasker's sophisticated orchestration patterns, including the dual finalization strategy and coordinated vs autonomous execution contexts, see:
2070
+
2071
+ **📖 [Task Execution Control Flow](TASK_EXECUTION_CONTROL_FLOW.md)** - Comprehensive documentation of workflow orchestration patterns, synchronous vs asynchronous finalization, and the coordination between WorkflowCoordinator and TaskFinalizer components.
2072
+
2073
+ ## Best Practices
2074
+
2075
+ ### Task Handler Design
2076
+
2077
+ 1. **Single Responsibility**: Each task handler should represent one business process
2078
+ 2. **Meaningful Names**: Use descriptive names that clearly indicate the workflow purpose
2079
+ 3. **Proper Dependencies**: Define clear step dependencies that reflect business logic
2080
+ 4. **Error Handling**: Configure appropriate retry limits based on operation reliability
2081
+ 5. **Context Design**: Include all necessary data in task context for step execution
2082
+ 6. **Extensibility**: Design task handlers to support configuration-driven customization
2083
+
2084
+ ### Step Handler Implementation
2085
+
2086
+ 1. **Focused Logic**: Each step should do one thing well
2087
+ 2. **Clear Results**: Return meaningful data structure for dependent steps
2088
+ 3. **Error Handling**: Understand how the framework determines step success/failure (see Error Handling Patterns below)
2089
+ 4. **Idempotency**: Design steps to be safely retryable
2090
+ 5. **Resource Cleanup**: Clean up resources in error scenarios
2091
+
2092
+ #### Error Handling Patterns
2093
+
2094
+ The framework determines step success/failure based on whether the `process` method raises an exception:
2095
+
2096
+ - **Exception raised** → Step marked as FAILED, retry logic triggered, workflow stops
2097
+ - **No exception raised** → Step marked as COMPLETED, workflow continues
2098
+
2099
+ **Pattern 1: Let exceptions bubble up (Recommended)**
2100
+ ```ruby
2101
+ def process(task, sequence, step)
2102
+ # Attempt the operation - let exceptions propagate naturally
2103
+ result = perform_complex_operation(task.context)
2104
+ { success: true, data: result }
2105
+
2106
+ # Framework automatically handles exceptions:
2107
+ # - Publishes step_failed event with error details
2108
+ # - Stores error information in step.results
2109
+ # - Transitions step to error state
2110
+ # - Triggers retry logic if configured
2111
+ end
2112
+ ```
2113
+
2114
+ **Pattern 2: Catch, record error details, then re-raise**
2115
+ ```ruby
2116
+ def process(task, sequence, step)
2117
+ begin
2118
+ result = perform_complex_operation(task.context)
2119
+ { success: true, data: result }
2120
+ rescue StandardError => e
2121
+ # Add custom error context to step.results
2122
+ step.results = {
2123
+ error: e.message,
2124
+ error_type: e.class.name,
2125
+ custom_context: "Additional business context",
2126
+ retry_recommended: should_retry?(e)
2127
+ }
2128
+ # Re-raise so framework knows this step failed
2129
+ raise
2130
+ end
2131
+ end
2132
+ ```
2133
+
2134
+ **Pattern 3: Treat handled exceptions as success**
2135
+ ```ruby
2136
+ def process(task, sequence, step)
2137
+ begin
2138
+ result = perform_complex_operation(task.context)
2139
+ { success: true, data: result }
2140
+ rescue RecoverableError => e
2141
+ # This exception is handled and considered a success case
2142
+ # Step will be marked as COMPLETED, not failed
2143
+ {
2144
+ success: true,
2145
+ data: get_fallback_data(task.context),
2146
+ recovered_from_error: e.message,
2147
+ used_fallback: true
2148
+ }
2149
+ end
2150
+ end
2151
+ ```
2152
+
2153
+ ⚠️ **Important**: Only catch exceptions in your `process` method if you intend to either:
2154
+ - Add custom error context to `step.results` and re-raise (Pattern 2)
2155
+ - Treat the exception as a recoverable success case (Pattern 3)
2156
+
2157
+ **Never** catch an exception, return error data, and expect the framework to treat it as a failure - it will be marked as successful.
2158
+
2159
+ ### Event Subscriber Guidelines
2160
+
2161
+ 1. **Extend BaseSubscriber**: Always use `Tasker::Events::Subscribers::BaseSubscriber`
2162
+ 2. **Safe Data Access**: Use `safe_get(event, :key, default)` for robust data access
2163
+ 3. **Error Isolation**: Don't let subscriber errors break task execution
2164
+ 4. **Async Operations**: Move heavy operations to background jobs
2165
+ 5. **Monitoring**: Monitor subscriber performance and error rates
2166
+
2167
+ ### YAML Configuration Management
2168
+
2169
+ 1. **Environment Separation**: Use environment-specific configurations appropriately
2170
+ 2. **Sensitive Data**: Use environment variables for secrets and API keys
2171
+ 3. **Documentation**: Include clear descriptions for all steps
2172
+ 4. **Validation**: Use JSON schema validation for task context requirements
2173
+ 5. **Versioning**: Version control all YAML configuration files
2174
+
2175
+ ## Development Workflow
2176
+
2177
+ ### 1. Plan Your Workflow
2178
+ ```bash
2179
+ # Define your business process steps and dependencies
2180
+ # Example: Order Processing
2181
+ # 1. Validate Order → 2. Process Payment → 3. Ship Order
2182
+ ```
2183
+
2184
+ ### 2. Generate Task Handler
2185
+ ```bash
2186
+ rails generate tasker:task_handler OrderHandler --module_namespace OrderProcess --steps="validate_order,process_payment,ship_order"
2187
+ ```
2188
+
2189
+ ### 3. Implement Step Handlers
2190
+ ```ruby
2191
+ # app/tasks/order_process/validate_order_handler.rb
2192
+ class ValidateOrderHandler < Tasker::StepHandler::Base
2193
+ def self.custom_event_configuration
2194
+ [
2195
+ { name: 'order.validation_failed', description: 'Order validation failed' },
2196
+ { name: 'order.validated', description: 'Order validation successful' }
2197
+ ]
2198
+ end
2199
+
2200
+ def process(task, sequence, step)
2201
+ # Implementation here
2202
+ # Publish custom events as needed
2203
+ end
2204
+ end
2205
+ ```
2206
+
2207
+ ### 4. Configure Dependencies
2208
+ ```yaml
2209
+ # config/tasker/tasks/order_process/order_handler.yaml
2210
+ step_templates:
2211
+ - name: validate_order
2212
+ handler_class: OrderProcess::ValidateOrderHandler
2213
+ - name: process_payment
2214
+ depends_on_step: validate_order
2215
+ handler_class: OrderProcess::ProcessPaymentHandler
2216
+ custom_events:
2217
+ - name: payment.gateway_error
2218
+ description: Payment gateway returned an error
2219
+ - name: ship_order
2220
+ depends_on_step: process_payment
2221
+ handler_class: OrderProcess::ShipOrderHandler
2222
+ ```
2223
+
2224
+ ### 5. Test Your Workflow
2225
+ ```ruby
2226
+ # spec/tasks/order_process/order_handler_spec.rb
2227
+ RSpec.describe OrderProcess::OrderHandler do
2228
+ it 'processes orders successfully' do
2229
+ # Test implementation
2230
+ end
2231
+ end
2232
+ ```
2233
+
2234
+ ### 6. Deploy and Monitor
2235
+ - Custom events are automatically registered and discoverable
2236
+ - External systems can subscribe to your custom events
2237
+ - Monitor workflow execution through event subscriptions
2238
+
2239
+ ## Testing Strategies
2240
+
2241
+ ### Testing Task Handlers
2242
+ ```ruby
2243
+ RSpec.describe OrderProcess::OrderHandler do
2244
+ describe '#initialize_task!' do
2245
+ let(:task_request) do
2246
+ Tasker::Types::TaskRequest.new(
2247
+ name: 'order_process',
2248
+ context: { order_id: 12345, customer_id: 67890 }
2249
+ )
2250
+ end
2251
+
2252
+ it 'creates task with proper step configuration' do
2253
+ handler = described_class.new
2254
+ task = handler.initialize_task!(task_request)
2255
+
2256
+ expect(task.workflow_steps.count).to eq(5)
2257
+ expect(task.workflow_steps.pluck(:name)).to include('fetch_order', 'process_payment')
2258
+ end
2259
+ end
2260
+ end
2261
+ ```
2262
+
2263
+ ### Testing Step Handlers
2264
+ ```ruby
2265
+ RSpec.describe OrderProcess::StepHandler::ProcessPaymentHandler do
2266
+ describe '#process' do
2267
+ let(:task) { create(:task, context: { order_id: 123, payment_method: 'credit_card' }) }
2268
+ let(:sequence) { build_sequence_for(task) }
2269
+ let(:step) { sequence.find_step_by_name('process_payment') }
2270
+
2271
+ it 'processes payment and returns payment details' do
2272
+ handler = described_class.new
2273
+ result = handler.process(task, sequence, step)
2274
+
2275
+ expect(result).to include(:payment_id, :amount_charged, :transaction_id)
2276
+ end
2277
+ end
2278
+ end
2279
+ ```
2280
+
2281
+ ### Testing Event Subscribers
2282
+ ```ruby
2283
+ RSpec.describe NotificationSubscriber do
2284
+ describe '#handle_task_completed' do
2285
+ let(:event) { { task_id: 'task_123', task_name: 'order_process', execution_duration: 45.2 } }
2286
+
2287
+ it 'sends completion notification' do
2288
+ subscriber = described_class.new
2289
+
2290
+ expect(NotificationService).to receive(:send_email)
2291
+ .with(hash_including(subject: /Task Completed/))
2292
+
2293
+ subscriber.handle_task_completed(event)
2294
+ end
2295
+ end
2296
+ end
2297
+ ```
2298
+
2299
+ ## Integration Validation & Production Readiness
2300
+
2301
+ Tasker includes comprehensive integration validation scripts that prove production readiness through real-world testing of the complete observability stack. These scripts are essential for validating enterprise deployments and ensuring reliable metrics collection and distributed tracing.
2302
+
2303
+ ### Validation Script Architecture
2304
+
2305
+ The validation scripts (`scripts/validate_*.rb`) provide **enterprise-grade validation** of Tasker's integration with observability stack components:
2306
+
2307
+ #### **Jaeger Integration Validator** (`validate_jaeger_integration.rb`)
2308
+ Validates OpenTelemetry distributed tracing integration:
2309
+
2310
+ ```bash
2311
+ # Run comprehensive Jaeger validation
2312
+ ./scripts/validate_jaeger_integration.rb
2313
+ ```
2314
+
2315
+ **Validation Categories**:
2316
+ - **Connection Testing**: Verifies Jaeger HTTP API connectivity
2317
+ - **Workflow Execution**: Creates and executes real workflow patterns (linear, diamond, parallel)
2318
+ - **Trace Collection**: Validates trace export and collection in Jaeger
2319
+ - **Span Hierarchy**: Analyzes parent-child relationships across workflow steps
2320
+ - **Trace Correlation**: Ensures proper trace correlation across distributed operations
2321
+
2322
+ **Sample Results**:
2323
+ ```
2324
+ 🎯 Tasker 2.5.0 - Jaeger Integration Validator
2325
+ ✅ Jaeger Connection: PASS - Successfully connected to Jaeger
2326
+ ✅ Workflow Execution: PASS - Created and executed 3 workflows
2327
+ ✅ Trace Collection: PASS - Successfully collected 13 spans
2328
+ ✅ Span Hierarchy: PASS - Validated 10 parent-child relationships
2329
+ ✅ Trace Correlation: PASS - All spans properly correlated
2330
+
2331
+ 📊 Span Analysis Results:
2332
+ Linear Workflow: 4 spans, 3 parent-child relationships
2333
+ Diamond Workflow: 5 spans, 4 parent-child relationships
2334
+ Parallel Workflow: 4 spans, 3 parent-child relationships
2335
+ ```
2336
+
2337
+ #### **Prometheus Integration Validator** (`validate_prometheus_integration.rb`)
2338
+ Validates metrics collection and Prometheus integration:
2339
+
2340
+ ```bash
2341
+ # Run comprehensive Prometheus validation
2342
+ ./scripts/validate_prometheus_integration.rb
2343
+ ```
2344
+
2345
+ **Validation Categories**:
2346
+ - **Prometheus Connection**: Verifies Prometheus server connectivity
2347
+ - **Metrics Endpoint**: Tests Tasker's `/tasker/metrics` endpoint
2348
+ - **Workflow Execution**: Executes workflows to generate authentic metrics
2349
+ - **Metrics Collection**: Validates event-driven metrics collection via EventRouter
2350
+ - **Query Validation**: Tests PromQL queries for dashboard compatibility
2351
+ - **Performance Analysis**: Analyzes TSDB integration and performance
2352
+
2353
+ **Sample Results**:
2354
+ ```
2355
+ 🎯 Tasker 2.5.0 - Prometheus Integration Validator
2356
+ ✅ MetricsSubscriber registered successfully
2357
+ ✅ Prometheus Connection: PASS - Successfully connected to Prometheus
2358
+ ✅ Metrics Endpoint: PASS - Tasker metrics endpoint accessible
2359
+ ✅ Workflow Execution: PASS - Created and executed 3 workflows
2360
+ ✅ Metrics Collection: PASS - Successfully collected 3 total metrics
2361
+ ✅ Query Validation: PASS - All 4 PromQL queries successful
2362
+
2363
+ 📊 Metrics Analysis Results:
2364
+ Counter metrics: 2 (step_completed_total: 22, task_completed_total: 3)
2365
+ Histogram metrics: 1 (step_duration_seconds)
2366
+ Total workflow activity: 22 steps completed across 3 tasks
2367
+ ```
2368
+
2369
+ ### Event-Driven Architecture Validation
2370
+
2371
+ The validation scripts prove Tasker's sophisticated event-driven architecture:
2372
+
2373
+ ```mermaid
2374
+ flowchart LR
2375
+ subgraph Validation["Integration Validation"]
2376
+ WF["Workflow<br/>Execution"]
2377
+ EP["Event<br/>Publishing"]
2378
+ end
2379
+
2380
+ subgraph EventSystem["Event System"]
2381
+ PUB["Events::<br/>Publisher"]
2382
+ TS["Telemetry<br/>Subscriber"]
2383
+ MS["Metrics<br/>Subscriber"]
2384
+ end
2385
+
2386
+ subgraph Observability["Observability Stack"]
2387
+ OT["OpenTelemetry<br/>Spans"]
2388
+ ER["EventRouter<br/>Metrics"]
2389
+ J["Jaeger<br/>Tracing"]
2390
+ P["Prometheus<br/>Metrics"]
2391
+ end
2392
+
2393
+ WF --> EP
2394
+ EP --> PUB
2395
+ PUB --> TS
2396
+ PUB --> MS
2397
+ TS --> OT
2398
+ MS --> ER
2399
+ OT --> J
2400
+ ER --> P
2401
+
2402
+ classDef validation fill:#e1f5fe,stroke:#01579b
2403
+ classDef events fill:#fff3e0,stroke:#e65100
2404
+ classDef observability fill:#e8f5e8,stroke:#2e7d32
2405
+
2406
+ class WF,EP validation
2407
+ class PUB,TS,MS events
2408
+ class OT,ER,J,P observability
2409
+ ```
2410
+
2411
+ ### Critical Technical Breakthrough: MetricsSubscriber
2412
+
2413
+ The Prometheus validator discovered and resolved a **critical missing component** in Tasker's metrics architecture:
2414
+
2415
+ **Problem**: Events were being published via `Events::Publisher` and creating OpenTelemetry spans via `TelemetrySubscriber`, but **no metrics were being collected** because there was no bridge between the event system and the `EventRouter` → `MetricsBackend` system.
2416
+
2417
+ **Solution**: Created `MetricsSubscriber` that bridges events to the EventRouter:
2418
+
2419
+ ```ruby
2420
+ # lib/tasker/events/subscribers/metrics_subscriber.rb
2421
+ class MetricsSubscriber < BaseSubscriber
2422
+ def handle_event(event_name, payload)
2423
+ # Bridge events to EventRouter for automatic metrics collection
2424
+ Tasker::Telemetry::EventRouter.instance.route_event(event_name, payload)
2425
+ rescue StandardError => e
2426
+ Rails.logger.error("MetricsSubscriber failed to route event: #{e.message}")
2427
+ end
2428
+ end
2429
+ ```
2430
+
2431
+ **Integration**: The `MetricsSubscriber` is now automatically registered in the `Orchestration::Coordinator` alongside the `TelemetrySubscriber`, ensuring complete observability coverage.
2432
+
2433
+ ### Production Deployment Validation
2434
+
2435
+ #### Prerequisites for Validation
2436
+ ```bash
2437
+ # 1. Start observability stack
2438
+ docker-compose up -d jaeger prometheus
2439
+
2440
+ # 2. Ensure Tasker Rails application is running
2441
+ bundle exec rails server
2442
+
2443
+ # 3. Run validation scripts
2444
+ ./scripts/validate_jaeger_integration.rb
2445
+ ./scripts/validate_prometheus_integration.rb
2446
+ ```
2447
+
2448
+ #### Continuous Integration Integration
2449
+ Both scripts are designed for CI/CD pipelines:
2450
+
2451
+ ```yaml
2452
+ # .github/workflows/integration-validation.yml
2453
+ name: Integration Validation
2454
+ on: [push, pull_request]
2455
+
2456
+ jobs:
2457
+ validate-integrations:
2458
+ runs-on: ubuntu-latest
2459
+ services:
2460
+ jaeger:
2461
+ image: jaegertracing/all-in-one:latest
2462
+ ports:
2463
+ - 14268:14268
2464
+ prometheus:
2465
+ image: prom/prometheus:latest
2466
+ ports:
2467
+ - 9090:9090
2468
+
2469
+ steps:
2470
+ - uses: actions/checkout@v2
2471
+ - name: Setup Ruby
2472
+ uses: ruby/setup-ruby@v1
2473
+ with:
2474
+ bundler-cache: true
2475
+
2476
+ - name: Start Rails Application
2477
+ run: bundle exec rails server &
2478
+
2479
+ - name: Validate Jaeger Integration
2480
+ run: ./scripts/validate_jaeger_integration.rb
2481
+
2482
+ - name: Validate Prometheus Integration
2483
+ run: ./scripts/validate_prometheus_integration.rb
2484
+ ```
2485
+
2486
+ #### Enterprise Deployment Checklist
2487
+
2488
+ **Pre-Deployment Validation**:
2489
+ - [ ] ✅ Jaeger integration validator passes (100% success rate)
2490
+ - [ ] ✅ Prometheus integration validator passes (100% success rate)
2491
+ - [ ] ✅ All observability stack components responding
2492
+ - [ ] ✅ Metrics collection functioning (non-zero metrics count)
2493
+ - [ ] ✅ Distributed tracing working (proper span hierarchies)
2494
+ - [ ] ✅ Dashboard queries validated (PromQL compatibility)
2495
+
2496
+ **Post-Deployment Monitoring**:
2497
+ ```bash
2498
+ # Validate production observability stack
2499
+ PROMETHEUS_URL=https://prometheus.production.com \
2500
+ JAEGER_URL=https://jaeger.production.com \
2501
+ ./scripts/validate_prometheus_integration.rb
2502
+
2503
+ # Monitor metrics collection in production
2504
+ bundle exec rake app:tasker:metrics_status
2505
+ ```
2506
+
2507
+ ### Development Workflow Integration
2508
+
2509
+ #### Local Development Setup
2510
+ ```bash
2511
+ # 1. Start local observability stack
2512
+ docker-compose up -d
2513
+
2514
+ # 2. Run Tasker with observability enabled
2515
+ RAILS_ENV=development bundle exec rails server
2516
+
2517
+ # 3. Validate integrations during development
2518
+ ./scripts/validate_jaeger_integration.rb
2519
+ ./scripts/validate_prometheus_integration.rb
2520
+ ```
2521
+
2522
+ #### Debugging Integration Issues
2523
+ The validation scripts provide comprehensive diagnostics:
2524
+
2525
+ ```bash
2526
+ # Detailed diagnostic output for troubleshooting
2527
+ ./scripts/validate_jaeger_integration.rb --verbose
2528
+ ./scripts/validate_prometheus_integration.rb --verbose
2529
+ ```
2530
+
2531
+ **Common Issues & Solutions**:
2532
+ - **No Metrics Collected**: Ensure `MetricsSubscriber` is registered (automatic in Tasker 2.5.0+)
2533
+ - **Missing Spans**: Verify OpenTelemetry exporter configuration
2534
+ - **Connection Failures**: Check service ports and network connectivity
2535
+ - **Query Failures**: Validate Prometheus data retention and configuration
2536
+
2537
+ ### Strategic Value
2538
+
2539
+ The integration validation scripts provide:
2540
+
2541
+ - **🎯 Production Confidence**: Comprehensive proof of enterprise readiness
2542
+ - **🔧 Developer Experience**: Clear validation results with actionable feedback
2543
+ - **📊 Integration Foundation**: Proven patterns for observability stack integration
2544
+ - **📈 Content Creation**: Technical excellence provides foundation for documentation and presentations
2545
+ - **🚀 Enterprise Adoption**: Validated observability enables confident production deployment
2546
+
2547
+ **Status**: **COMPLETE** - Both Jaeger and Prometheus integration validators fully implemented and tested with 100% success rates, proving Tasker's production readiness through comprehensive observability stack validation.
2548
+
2549
+ ## Application Template Validation (v2.6.0)
2550
+
2551
+ Tasker includes a comprehensive dry-run validation system for application template generation that ensures template consistency and correctness without generating files. This system is essential for maintaining template quality and CI/CD pipeline integration.
2552
+
2553
+ ### Dry-Run Validation Overview
2554
+
2555
+ The validation system tests five critical categories of template correctness:
2556
+
2557
+ ```bash
2558
+ # Run comprehensive validation
2559
+ ruby scripts/create_tasker_app.rb dry_run
2560
+
2561
+ # Run specific validation categories
2562
+ ruby scripts/create_tasker_app.rb dry_run --mode=templates # File existence
2563
+ ruby scripts/create_tasker_app.rb dry_run --mode=syntax # ERB/Ruby/YAML syntax
2564
+ ruby scripts/create_tasker_app.rb dry_run --mode=cli # CLI option mapping
2565
+ ruby scripts/create_tasker_app.rb dry_run --mode=bindings # Template variables
2566
+
2567
+ # Validate Docker templates
2568
+ ruby scripts/create_tasker_app.rb dry_run --docker --with-observability
2569
+ ```
2570
+
2571
+ ### Validation Categories
2572
+
2573
+ **1. Template File Existence Validation**
2574
+ - Verifies all required ERB templates exist for selected task types
2575
+ - Adapts template requirements based on Docker/observability modes
2576
+ - Reports missing templates with specific file paths
2577
+
2578
+ **2. ERB Template Syntax Validation**
2579
+ - Parses all ERB templates for syntax errors using `ERB.new(template).check_syntax`
2580
+ - Validates template structure without executing Ruby code
2581
+ - Catches malformed ERB blocks and Ruby expressions
2582
+
2583
+ **3. Generated Code Syntax Validation**
2584
+ - Renders templates with test data to validate actual output
2585
+ - Uses `RubyVM::InstructionSequence.compile` for Ruby syntax validation
2586
+ - Uses `YAML.safe_load` for YAML syntax validation
2587
+ - Tests real generated output, not just template correctness
2588
+
2589
+ **4. CLI Options Mapping Validation**
2590
+ - Ensures all Thor CLI options have corresponding instance variables
2591
+ - Validates option-to-variable mapping (e.g., `--docker` → `@docker_mode`)
2592
+ - Checks for missing or incorrectly mapped CLI options
2593
+
2594
+ **5. Template Variable Bindings Validation**
2595
+ - Tests template rendering with expected binding contexts
2596
+ - Validates all required variables are available during rendering
2597
+ - Tests step handlers, configuration files, and Docker-specific bindings
2598
+
2599
+ ### CI/CD Integration
2600
+
2601
+ The dry-run validation system is designed for continuous integration:
2602
+
2603
+ ```yaml
2604
+ # .github/workflows/template-validation.yml
2605
+ name: Template Validation
2606
+ on: [push, pull_request]
2607
+
2608
+ jobs:
2609
+ validate-templates:
2610
+ runs-on: ubuntu-latest
2611
+ steps:
2612
+ - uses: actions/checkout@v2
2613
+ - name: Setup Ruby
2614
+ uses: ruby/setup-ruby@v1
2615
+ with:
2616
+ bundler-cache: true
2617
+
2618
+ - name: Validate Application Templates
2619
+ run: |
2620
+ # Test all template combinations
2621
+ ruby scripts/create_tasker_app.rb dry_run --mode=all
2622
+ ruby scripts/create_tasker_app.rb dry_run --docker --with-observability
2623
+
2624
+ - name: Validate Specific Categories
2625
+ run: |
2626
+ ruby scripts/create_tasker_app.rb dry_run --mode=templates
2627
+ ruby scripts/create_tasker_app.rb dry_run --mode=syntax
2628
+ ruby scripts/create_tasker_app.rb dry_run --mode=cli
2629
+ ruby scripts/create_tasker_app.rb dry_run --mode=bindings
2630
+ ```
2631
+
2632
+ ### Development Workflow Integration
2633
+
2634
+ **Template Development Process**:
2635
+ 1. Modify ERB templates in `scripts/templates/`
2636
+ 2. Run dry-run validation to catch issues early
2637
+ 3. Fix validation errors before committing
2638
+ 4. CI/CD pipeline validates all template combinations
2639
+
2640
+ **Local Development Validation**:
2641
+ ```bash
2642
+ # Quick validation during template development
2643
+ ruby scripts/create_tasker_app.rb dry_run --mode=syntax
2644
+
2645
+ # Full validation before committing
2646
+ ruby scripts/create_tasker_app.rb dry_run --mode=all
2647
+
2648
+ # Test Docker-specific templates
2649
+ ruby scripts/create_tasker_app.rb dry_run --docker --with-observability
2650
+ ```
2651
+
2652
+ ### Performance Characteristics
2653
+
2654
+ - **Zero File System Impact**: No files created during validation
2655
+ - **Sub-second Execution**: Validation completes in under 1 second
2656
+ - **Clear Exit Codes**: 0 for success, 1 for failure (CI/CD friendly)
2657
+ - **Detailed Error Reporting**: Specific file paths and line numbers for errors
2658
+
2659
+ ### Strategic Benefits
2660
+
2661
+ - **Template Quality Assurance**: Prevents broken templates from reaching production
2662
+ - **Developer Confidence**: Comprehensive validation reduces template-related issues
2663
+ - **CI/CD Integration**: Automated validation in deployment pipelines
2664
+ - **Rapid Feedback**: Immediate validation results during development
2665
+ - **Enterprise Reliability**: Ensures consistent template generation across environments