tasker-engine 0.1.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 (601) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +440 -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/tasks_controller.rb +123 -0
  13. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  14. data/app/graphql/examples/all_tasks.graphql +22 -0
  15. data/app/graphql/examples/pending_tasks.graphql +23 -0
  16. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  17. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  18. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  19. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  20. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  21. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  22. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  24. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  25. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  26. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  27. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  29. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  30. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  32. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  33. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  34. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  35. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  36. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  38. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  39. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  40. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  41. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  42. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  43. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  44. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  45. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  46. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  47. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  48. data/app/graphql/tasker/queries/base_query.rb +9 -0
  49. data/app/graphql/tasker/queries/helpers.rb +16 -0
  50. data/app/graphql/tasker/queries/one_step.rb +24 -0
  51. data/app/graphql/tasker/queries/one_task.rb +18 -0
  52. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  53. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  54. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  55. data/app/jobs/tasker/application_job.rb +8 -0
  56. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  57. data/app/jobs/tasker/task_runner_job.rb +224 -0
  58. data/app/models/tasker/annotation_type.rb +26 -0
  59. data/app/models/tasker/application_record.rb +70 -0
  60. data/app/models/tasker/dependent_system.rb +26 -0
  61. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  62. data/app/models/tasker/named_step.rb +41 -0
  63. data/app/models/tasker/named_task.rb +121 -0
  64. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  65. data/app/models/tasker/step_dag_relationship.rb +65 -0
  66. data/app/models/tasker/step_readiness_status.rb +59 -0
  67. data/app/models/tasker/task.rb +414 -0
  68. data/app/models/tasker/task_annotation.rb +36 -0
  69. data/app/models/tasker/task_execution_context.rb +29 -0
  70. data/app/models/tasker/task_namespace.rb +41 -0
  71. data/app/models/tasker/task_transition.rb +235 -0
  72. data/app/models/tasker/workflow_step.rb +461 -0
  73. data/app/models/tasker/workflow_step_edge.rb +95 -0
  74. data/app/models/tasker/workflow_step_transition.rb +434 -0
  75. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  76. data/app/serializers/tasker/handler_serializer.rb +109 -0
  77. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  78. data/app/serializers/tasker/task_serializer.rb +168 -0
  79. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  80. data/app/services/tasker/analytics_service.rb +409 -0
  81. data/config/initializers/dry_struct.rb +11 -0
  82. data/config/initializers/statesman.rb +6 -0
  83. data/config/initializers/tasker_orchestration.rb +17 -0
  84. data/config/initializers/time_formats.rb +4 -0
  85. data/config/routes.rb +34 -0
  86. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  87. data/config/tasker/system_events.yml +305 -0
  88. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  89. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  90. data/db/functions/get_slowest_steps_v01.sql +82 -0
  91. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  92. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  93. data/db/functions/get_step_readiness_status_single_and_batch_v02.sql +223 -0
  94. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  95. data/db/functions/get_system_health_counts_v01.sql +108 -0
  96. data/db/functions/get_task_execution_context_v01.sql +108 -0
  97. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  98. data/db/init/schema.sql +2254 -0
  99. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  100. data/db/migrate/20250710110830_step_readiness_sql_functions_v02.rb +39 -0
  101. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  102. data/docs/APPLICATION_GENERATOR.md +384 -0
  103. data/docs/AUTH.md +1741 -0
  104. data/docs/CIRCUIT_BREAKER.md +224 -0
  105. data/docs/DEVELOPER_GUIDE.md +2664 -0
  106. data/docs/EVENT_SYSTEM.md +637 -0
  107. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  108. data/docs/FLOW_CHART.md +149 -0
  109. data/docs/HEALTH.md +542 -0
  110. data/docs/METRICS.md +731 -0
  111. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  112. data/docs/OVERVIEW.md +548 -0
  113. data/docs/QUICK_START.md +270 -0
  114. data/docs/REGISTRY_SYSTEMS.md +373 -0
  115. data/docs/REST_API.md +632 -0
  116. data/docs/REVERSIONING.md +404 -0
  117. data/docs/ROADMAP.md +221 -0
  118. data/docs/SQL_FUNCTIONS.md +1408 -0
  119. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  120. data/docs/TELEMETRY.md +795 -0
  121. data/docs/TROUBLESHOOTING.md +756 -0
  122. data/docs/TaskHandlerGenerator.html +255 -0
  123. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  124. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  125. data/docs/Tasker/Analysis.html +117 -0
  126. data/docs/Tasker/AnalyticsController.html +450 -0
  127. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  128. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  129. data/docs/Tasker/AnalyticsService.html +2221 -0
  130. data/docs/Tasker/AnnotationType.html +137 -0
  131. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  132. data/docs/Tasker/ApplicationController.html +147 -0
  133. data/docs/Tasker/ApplicationJob.html +128 -0
  134. data/docs/Tasker/ApplicationRecord.html +378 -0
  135. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  136. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  137. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  138. data/docs/Tasker/Authentication/Interface.html +560 -0
  139. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  140. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  141. data/docs/Tasker/Authentication.html +119 -0
  142. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  143. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  144. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  145. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  146. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +360 -0
  147. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  148. data/docs/Tasker/Authorization/ResourceRegistry.html +875 -0
  149. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  150. data/docs/Tasker/Authorization.html +582 -0
  151. data/docs/Tasker/CacheCapabilities.html +167 -0
  152. data/docs/Tasker/CacheStrategy.html +1297 -0
  153. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  154. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  155. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  156. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  157. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  158. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  159. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  160. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  161. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  162. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  163. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  164. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  165. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  166. data/docs/Tasker/Concerns.html +117 -0
  167. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  168. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  169. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  170. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  171. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  172. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  173. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  174. data/docs/Tasker/Configuration.html +1669 -0
  175. data/docs/Tasker/ConfigurationError.html +143 -0
  176. data/docs/Tasker/ConfiguredTask.html +514 -0
  177. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  178. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  179. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  180. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  181. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  182. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  183. data/docs/Tasker/Constants/StepEvents.html +177 -0
  184. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  185. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  186. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  187. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  188. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  189. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  190. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  191. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  192. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  193. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  194. data/docs/Tasker/Constants/TestEvents.html +163 -0
  195. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  196. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  197. data/docs/Tasker/Constants.html +561 -0
  198. data/docs/Tasker/DependentSystem.html +137 -0
  199. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  200. data/docs/Tasker/DetectorRegistry.html +598 -0
  201. data/docs/Tasker/Diagram/Edge.html +1191 -0
  202. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  203. data/docs/Tasker/Diagram/Node.html +1165 -0
  204. data/docs/Tasker/Diagram.html +117 -0
  205. data/docs/Tasker/Engine.html +215 -0
  206. data/docs/Tasker/Error.html +139 -0
  207. data/docs/Tasker/Events/Bus.html +1226 -0
  208. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  209. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  210. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  211. data/docs/Tasker/Events/Catalog.html +1291 -0
  212. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  213. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  214. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  215. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  216. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  217. data/docs/Tasker/Events/Publisher.html +365 -0
  218. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  219. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  220. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  221. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  222. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  223. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  224. data/docs/Tasker/Events/Subscribers.html +117 -0
  225. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  226. data/docs/Tasker/Events.html +294 -0
  227. data/docs/Tasker/EventsGenerator.html +459 -0
  228. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  229. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  230. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  231. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  232. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  233. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  234. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  235. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  236. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  237. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  238. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  239. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  240. data/docs/Tasker/Functions.html +117 -0
  241. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  242. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  243. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  244. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  245. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  246. data/docs/Tasker/Generators.html +117 -0
  247. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  248. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  249. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  250. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  251. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  252. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  255. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  256. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  259. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  260. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  261. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  262. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  265. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  266. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  268. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  269. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  270. data/docs/Tasker/GraphQLTypes.html +130 -0
  271. data/docs/Tasker/GraphqlController.html +251 -0
  272. data/docs/Tasker/HandlerFactory.html +1528 -0
  273. data/docs/Tasker/HandlerSerializer.html +682 -0
  274. data/docs/Tasker/HandlersController.html +574 -0
  275. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  276. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  277. data/docs/Tasker/Health/StatusChecker.html +653 -0
  278. data/docs/Tasker/Health.html +117 -0
  279. data/docs/Tasker/HealthController.html +523 -0
  280. data/docs/Tasker/IdentityStrategy.html +276 -0
  281. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  282. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  283. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  284. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  285. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  286. data/docs/Tasker/LifecycleEvents.html +799 -0
  287. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  288. data/docs/Tasker/Logging.html +115 -0
  289. data/docs/Tasker/MetricsController.html +293 -0
  290. data/docs/Tasker/MetricsExportJob.html +414 -0
  291. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  292. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  293. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  294. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  295. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  296. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  297. data/docs/Tasker/Mutations.html +117 -0
  298. data/docs/Tasker/NamedStep.html +216 -0
  299. data/docs/Tasker/NamedTask.html +910 -0
  300. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  301. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  302. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  303. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  304. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  305. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  306. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  307. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  308. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  309. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  310. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  311. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  312. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  313. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  314. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  315. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  316. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  322. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  323. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  324. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  325. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  326. data/docs/Tasker/Orchestration.html +130 -0
  327. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  328. data/docs/Tasker/PageSort.html +247 -0
  329. data/docs/Tasker/PermanentError.html +518 -0
  330. data/docs/Tasker/ProceduralError.html +147 -0
  331. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  332. data/docs/Tasker/Queries/AllTasks.html +221 -0
  333. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  334. data/docs/Tasker/Queries/Helpers.html +187 -0
  335. data/docs/Tasker/Queries/OneStep.html +225 -0
  336. data/docs/Tasker/Queries/OneTask.html +217 -0
  337. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  338. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  339. data/docs/Tasker/Queries.html +119 -0
  340. data/docs/Tasker/Railtie.html +124 -0
  341. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  342. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  343. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  344. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  345. data/docs/Tasker/Registry/RegistryError.html +139 -0
  346. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  347. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  348. data/docs/Tasker/Registry/ValidationError.html +132 -0
  349. data/docs/Tasker/Registry.html +119 -0
  350. data/docs/Tasker/RetryableError.html +515 -0
  351. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  352. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  353. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  354. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  355. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  356. data/docs/Tasker/StateMachine.html +602 -0
  357. data/docs/Tasker/StepDagRelationship.html +657 -0
  358. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  359. data/docs/Tasker/StepHandler/Api.html +884 -0
  360. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  361. data/docs/Tasker/StepHandler/Base.html +970 -0
  362. data/docs/Tasker/StepHandler.html +119 -0
  363. data/docs/Tasker/StepReadinessStatus.html +836 -0
  364. data/docs/Tasker/Task.html +2478 -0
  365. data/docs/Tasker/TaskAnnotation.html +137 -0
  366. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  367. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  368. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  369. data/docs/Tasker/TaskBuilder.html +764 -0
  370. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  371. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  372. data/docs/Tasker/TaskDiagram.html +548 -0
  373. data/docs/Tasker/TaskDiagramsController.html +240 -0
  374. data/docs/Tasker/TaskExecutionContext.html +469 -0
  375. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  376. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  377. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  378. data/docs/Tasker/TaskHandler/ClassMethods.html +395 -0
  379. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  380. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  381. data/docs/Tasker/TaskHandler.html +271 -0
  382. data/docs/Tasker/TaskNamespace.html +312 -0
  383. data/docs/Tasker/TaskRunnerJob.html +406 -0
  384. data/docs/Tasker/TaskSerializer.html +474 -0
  385. data/docs/Tasker/TaskTransition.html +1517 -0
  386. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  387. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  388. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  389. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  390. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  391. data/docs/Tasker/TasksController.html +595 -0
  392. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  393. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  394. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  395. data/docs/Tasker/Telemetry/Events.html +115 -0
  396. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  397. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  398. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  399. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  400. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  401. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  402. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  403. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  404. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  405. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  406. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  407. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  408. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  409. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  410. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  411. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  412. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  413. data/docs/Tasker/Telemetry.html +130 -0
  414. data/docs/Tasker/Types/AuthConfig.html +886 -0
  415. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  416. data/docs/Tasker/Types/BaseConfig.html +227 -0
  417. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  418. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  419. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  420. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  421. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  422. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  423. data/docs/Tasker/Types/GraphEdge.html +517 -0
  424. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  425. data/docs/Tasker/Types/GraphNode.html +694 -0
  426. data/docs/Tasker/Types/HealthConfig.html +784 -0
  427. data/docs/Tasker/Types/StepSequence.html +353 -0
  428. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  429. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  430. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  431. data/docs/Tasker/Types.html +154 -0
  432. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  433. data/docs/Tasker/WorkflowStep.html +2724 -0
  434. data/docs/Tasker/WorkflowStepEdge.html +306 -0
  435. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  436. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  437. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  438. data/docs/Tasker/WorkflowStepsController.html +462 -0
  439. data/docs/Tasker.html +468 -0
  440. data/docs/VISION.md +584 -0
  441. data/docs/WHY.md +21 -0
  442. data/docs/_index.html +2319 -0
  443. data/docs/class_list.html +54 -0
  444. data/docs/css/common.css +1 -0
  445. data/docs/css/full_list.css +58 -0
  446. data/docs/css/style.css +503 -0
  447. data/docs/events/migration_plan_outcomes.md +80 -0
  448. data/docs/file.README.html +537 -0
  449. data/docs/file_list.html +59 -0
  450. data/docs/frames.html +22 -0
  451. data/docs/index.html +537 -0
  452. data/docs/js/app.js +344 -0
  453. data/docs/js/full_list.js +242 -0
  454. data/docs/js/jquery.js +4 -0
  455. data/docs/method_list.html +8854 -0
  456. data/docs/top-level-namespace.html +110 -0
  457. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  458. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  459. data/lib/generators/tasker/events_generator.rb +91 -0
  460. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  461. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  462. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  463. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  464. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +82 -0
  465. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +136 -0
  466. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  467. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  468. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  469. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  470. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  471. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  472. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  473. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  474. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  475. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  476. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  477. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  478. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  479. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  480. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  481. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  482. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  483. data/lib/generators/tasker/templates/task_handler.rb.erb +60 -0
  484. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +165 -0
  485. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  486. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  487. data/lib/tasker/authentication/coordinator.rb +78 -0
  488. data/lib/tasker/authentication/errors.rb +9 -0
  489. data/lib/tasker/authentication/interface.rb +36 -0
  490. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  491. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  492. data/lib/tasker/authorization/errors.rb +26 -0
  493. data/lib/tasker/authorization/resource_constants.rb +73 -0
  494. data/lib/tasker/authorization/resource_registry.rb +136 -0
  495. data/lib/tasker/authorization.rb +75 -0
  496. data/lib/tasker/cache_capabilities.rb +131 -0
  497. data/lib/tasker/cache_strategy.rb +469 -0
  498. data/lib/tasker/concerns/authenticatable.rb +41 -0
  499. data/lib/tasker/concerns/authorizable.rb +204 -0
  500. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  501. data/lib/tasker/concerns/event_publisher.rb +716 -0
  502. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  503. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  504. data/lib/tasker/concerns/structured_logging.rb +387 -0
  505. data/lib/tasker/configuration.rb +325 -0
  506. data/lib/tasker/constants/event_definitions.rb +147 -0
  507. data/lib/tasker/constants/registry_events.rb +54 -0
  508. data/lib/tasker/constants.rb +417 -0
  509. data/lib/tasker/engine.rb +90 -0
  510. data/lib/tasker/errors.rb +90 -0
  511. data/lib/tasker/events/catalog.rb +432 -0
  512. data/lib/tasker/events/custom_registry.rb +175 -0
  513. data/lib/tasker/events/definition_loader.rb +199 -0
  514. data/lib/tasker/events/event_payload_builder.rb +461 -0
  515. data/lib/tasker/events/publisher.rb +149 -0
  516. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  517. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  518. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  519. data/lib/tasker/events/subscription_loader.rb +161 -0
  520. data/lib/tasker/events.rb +37 -0
  521. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  522. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  523. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  524. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  525. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  526. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  527. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  528. data/lib/tasker/functions/function_wrapper.rb +42 -0
  529. data/lib/tasker/functions.rb +12 -0
  530. data/lib/tasker/handler_factory.rb +327 -0
  531. data/lib/tasker/health/readiness_checker.rb +186 -0
  532. data/lib/tasker/health/status_checker.rb +203 -0
  533. data/lib/tasker/identity_strategy.rb +38 -0
  534. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  535. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  536. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  537. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  538. data/lib/tasker/orchestration/coordinator.rb +119 -0
  539. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  540. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  541. data/lib/tasker/orchestration/response_processor.rb +168 -0
  542. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  543. data/lib/tasker/orchestration/step_executor.rb +941 -0
  544. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  545. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  546. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  547. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  548. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  549. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  550. data/lib/tasker/orchestration.rb +45 -0
  551. data/lib/tasker/railtie.rb +9 -0
  552. data/lib/tasker/registry/base_registry.rb +177 -0
  553. data/lib/tasker/registry/event_publisher.rb +91 -0
  554. data/lib/tasker/registry/interface_validator.rb +140 -0
  555. data/lib/tasker/registry/statistics_collector.rb +381 -0
  556. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  557. data/lib/tasker/registry.rb +22 -0
  558. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  559. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  560. data/lib/tasker/state_machine.rb +83 -0
  561. data/lib/tasker/step_handler/api.rb +410 -0
  562. data/lib/tasker/step_handler/base.rb +206 -0
  563. data/lib/tasker/task_builder.rb +432 -0
  564. data/lib/tasker/task_handler/class_methods.rb +327 -0
  565. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  566. data/lib/tasker/task_handler/step_group.rb +182 -0
  567. data/lib/tasker/task_handler.rb +43 -0
  568. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  569. data/lib/tasker/telemetry/event_router.rb +318 -0
  570. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  571. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  572. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  573. data/lib/tasker/telemetry/log_backend.rb +224 -0
  574. data/lib/tasker/telemetry/metric_types.rb +416 -0
  575. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  576. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  577. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  578. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  579. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  580. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  581. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  582. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  583. data/lib/tasker/telemetry.rb +59 -0
  584. data/lib/tasker/types/auth_config.rb +81 -0
  585. data/lib/tasker/types/backoff_config.rb +142 -0
  586. data/lib/tasker/types/cache_config.rb +257 -0
  587. data/lib/tasker/types/database_config.rb +39 -0
  588. data/lib/tasker/types/dependency_graph.rb +225 -0
  589. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  590. data/lib/tasker/types/engine_config.rb +131 -0
  591. data/lib/tasker/types/execution_config.rb +289 -0
  592. data/lib/tasker/types/health_config.rb +84 -0
  593. data/lib/tasker/types/step_sequence.rb +24 -0
  594. data/lib/tasker/types/step_template.rb +63 -0
  595. data/lib/tasker/types/task_request.rb +60 -0
  596. data/lib/tasker/types/telemetry_config.rb +273 -0
  597. data/lib/tasker/types.rb +64 -0
  598. data/lib/tasker/version.rb +8 -0
  599. data/lib/tasker.rb +82 -0
  600. data/lib/tasks/tasker_tasks.rake +383 -0
  601. metadata +954 -0
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tasker
4
+ # TaskTransition represents state transitions for Task entities using Statesman
5
+ #
6
+ # This model stores the audit trail of all task state changes, providing
7
+ # a complete history of task lifecycle events with metadata and timestamps.
8
+ class TaskTransition < ApplicationRecord
9
+ # NOTE: We don't include Statesman::Adapters::ActiveRecordTransition
10
+ # because we're using PostgreSQL JSONB column for metadata
11
+
12
+ # Associations
13
+ belongs_to :task, inverse_of: :task_transitions
14
+
15
+ # Validations
16
+ validates :to_state, inclusion: {
17
+ in: %w[pending in_progress complete error cancelled resolved_manually],
18
+ message: 'is not a valid task state'
19
+ }
20
+
21
+ # Validate that the task exists before creating transition
22
+ validate :task_must_exist
23
+
24
+ validates :sort_key, presence: true, uniqueness: { scope: :task_id }
25
+ # Custom validation for metadata that allows empty hash but not nil
26
+ validate :metadata_must_be_hash
27
+
28
+ # Ensure metadata is always a hash
29
+ after_initialize :ensure_metadata_hash
30
+ # Ensure metadata defaults to empty hash if not provided
31
+ before_validation :ensure_metadata_presence
32
+
33
+ # Scopes
34
+ scope :recent, -> { order(sort_key: :desc) }
35
+ scope :to_state, ->(state) { where(to_state: state) }
36
+ scope :with_metadata_key, ->(key) { where('metadata ? :key', key: key.to_s) }
37
+
38
+ # Class methods for querying transitions
39
+ class << self
40
+ # Find the most recent transition to a specific state
41
+ #
42
+ # @param state [String, Symbol] The state to find the most recent transition to
43
+ # @return [TaskTransition, nil] The most recent transition to the given state
44
+ def most_recent_to_state(state)
45
+ to_state(state.to_s).order(sort_key: :desc).first
46
+ end
47
+
48
+ # Find all transitions that occurred within a time range
49
+ #
50
+ # @param start_time [Time] The start of the time range
51
+ # @param end_time [Time] The end of the time range
52
+ # @return [ActiveRecord::Relation] Transitions within the time range
53
+ def in_time_range(start_time, end_time)
54
+ where(created_at: start_time..end_time)
55
+ end
56
+
57
+ # Find transitions with specific metadata values
58
+ #
59
+ # @param key [String, Symbol] The metadata key to search for
60
+ # @param value [Object] The value to match
61
+ # @return [ActiveRecord::Relation] Transitions with matching metadata
62
+ def with_metadata_value(key, value)
63
+ where('metadata->:key = :value', key: key.to_s, value: value.to_json.delete('"'))
64
+ end
65
+
66
+ # Get transition statistics for analytics
67
+ #
68
+ # @return [Hash] Statistics about transitions
69
+ def statistics
70
+ {
71
+ total_transitions: count,
72
+ states: group(:to_state).count,
73
+ recent_activity: where(created_at: 24.hours.ago..Time.current).count,
74
+ average_time_between_transitions: average_time_between_transitions
75
+ }
76
+ end
77
+
78
+ private
79
+
80
+ # Calculate average time between transitions
81
+ #
82
+ # @return [Float, nil] Average seconds between transitions
83
+ def average_time_between_transitions
84
+ transitions = order(:created_at).pluck(:created_at)
85
+ return nil if transitions.size < 2
86
+
87
+ total_time = 0
88
+ (1...transitions.size).each do |i|
89
+ total_time += (transitions[i] - transitions[i - 1])
90
+ end
91
+
92
+ total_time / (transitions.size - 1)
93
+ end
94
+ end
95
+
96
+ # Instance methods
97
+
98
+ # Get the duration since the previous transition
99
+ #
100
+ # @return [Float, nil] Duration in seconds since previous transition
101
+ def duration_since_previous
102
+ previous_transition = self.class.where(task_id: task_id)
103
+ .where(sort_key: ...sort_key)
104
+ .order(sort_key: :desc)
105
+ .first
106
+
107
+ return nil unless previous_transition
108
+
109
+ created_at - previous_transition.created_at
110
+ end
111
+
112
+ # Check if this transition represents an error state
113
+ #
114
+ # @return [Boolean] True if transitioning to an error state
115
+ def error_transition?
116
+ to_state == 'error'
117
+ end
118
+
119
+ # Check if this transition represents completion
120
+ #
121
+ # @return [Boolean] True if transitioning to a completion state
122
+ def completion_transition?
123
+ %w[complete resolved_manually].include?(to_state)
124
+ end
125
+
126
+ # Check if this transition represents cancellation
127
+ #
128
+ # @return [Boolean] True if transitioning to cancelled state
129
+ def cancellation_transition?
130
+ to_state == 'cancelled'
131
+ end
132
+
133
+ # Get human-readable description of the transition
134
+ #
135
+ # @return [String] Description of what this transition represents
136
+ def description
137
+ case to_state
138
+ when 'pending'
139
+ 'Task initialized and ready for processing'
140
+ when 'in_progress'
141
+ 'Task execution started'
142
+ when 'complete'
143
+ 'Task completed successfully'
144
+ when 'error'
145
+ 'Task encountered an error'
146
+ when 'cancelled'
147
+ 'Task was cancelled'
148
+ when 'resolved_manually'
149
+ 'Task was manually resolved'
150
+ else
151
+ "Task transitioned to #{to_state}"
152
+ end
153
+ end
154
+
155
+ # Get formatted metadata for display
156
+ #
157
+ # @return [Hash] Formatted metadata with additional computed fields
158
+ def formatted_metadata
159
+ base_metadata = metadata.dup
160
+
161
+ # Add computed fields
162
+ base_metadata['duration_since_previous'] = duration_since_previous
163
+ base_metadata['transition_description'] = description
164
+ base_metadata['transition_timestamp'] = created_at.iso8601
165
+
166
+ base_metadata
167
+ end
168
+
169
+ # Check if transition has specific metadata
170
+ #
171
+ # @param key [String, Symbol] The metadata key to check for
172
+ # @return [Boolean] True if the metadata contains the key
173
+ def has_metadata?(key)
174
+ metadata.key?(key.to_s)
175
+ end
176
+
177
+ # Get metadata value with default
178
+ #
179
+ # @param key [String, Symbol] The metadata key
180
+ # @param default [Object] Default value if key not found
181
+ # @return [Object] The metadata value or default
182
+ def get_metadata(key, default = nil)
183
+ metadata.fetch(key.to_s, default)
184
+ end
185
+
186
+ # Set metadata value
187
+ #
188
+ # @param key [String, Symbol] The metadata key
189
+ # @param value [Object] The value to set
190
+ # @return [Object] The set value
191
+ def set_metadata(key, value)
192
+ self.metadata = metadata.merge(key.to_s => value)
193
+ value
194
+ end
195
+
196
+ private
197
+
198
+ # Ensure metadata is always initialized as a hash
199
+ #
200
+ # @return [void]
201
+ def ensure_metadata_hash
202
+ self.metadata ||= {}
203
+ end
204
+
205
+ # Ensure metadata is present for validation
206
+ #
207
+ # @return [void]
208
+ def ensure_metadata_presence
209
+ self.metadata = {} if metadata.blank?
210
+ end
211
+
212
+ # Custom validation for metadata
213
+ #
214
+ # @return [void]
215
+ def metadata_must_be_hash
216
+ if metadata.nil?
217
+ self.metadata = {}
218
+ elsif !metadata.is_a?(Hash)
219
+ errors.add(:metadata, 'must be a hash')
220
+ end
221
+ end
222
+
223
+ # Validate that the task exists
224
+ #
225
+ # @return [void]
226
+ def task_must_exist
227
+ return if task_id.blank?
228
+
229
+ errors.add(:task, 'must exist before creating transition') unless Tasker::Task.exists?(task_id: task_id)
230
+ rescue ActiveRecord::StatementInvalid => e
231
+ # Handle cases where the table might not exist (e.g., during migrations)
232
+ Rails.logger.warn { "Could not validate task existence: #{e.message}" }
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,461 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../../../lib/tasker/state_machine/step_state_machine'
5
+
6
+ module Tasker
7
+ class WorkflowStep < ApplicationRecord
8
+ PROVIDES_EDGE_NAME = 'provides'
9
+
10
+ self.primary_key = :workflow_step_id
11
+ belongs_to :task
12
+ belongs_to :named_step
13
+ # belongs_to :depends_on_step, class_name: 'WorkflowStep', optional: true
14
+ has_many :incoming_edges,
15
+ class_name: 'WorkflowStepEdge',
16
+ foreign_key: :to_step_id,
17
+ dependent: :destroy,
18
+ inverse_of: :to_step
19
+ has_many :outgoing_edges,
20
+ class_name: 'WorkflowStepEdge',
21
+ foreign_key:
22
+ :from_step_id,
23
+ dependent: :destroy,
24
+ inverse_of: :from_step
25
+ has_many :parents, through: :incoming_edges, source: :from_step
26
+ has_many :children, through: :outgoing_edges, source: :to_step
27
+ has_many :siblings, through: :outgoing_edges, source: :from_step
28
+ has_many :workflow_step_transitions, inverse_of: :workflow_step, dependent: :destroy
29
+
30
+ validates :named_step_id, uniqueness: { scope: :task_id, message: 'must be unique within the same task' }
31
+ validate :name_uniqueness_within_task
32
+
33
+ delegate :name, to: :named_step
34
+
35
+ has_one :step_dag_relationship, class_name: 'Tasker::StepDagRelationship', primary_key: :workflow_step_id
36
+ # NOTE: step_readiness_status is now accessed via function-based approach, not ActiveRecord association
37
+
38
+ # Optimized scopes for efficient querying using state machine transitions
39
+ scope :completed, lambda {
40
+ joins(:workflow_step_transitions)
41
+ .where(
42
+ workflow_step_transitions: {
43
+ most_recent: true,
44
+ to_state: [
45
+ Constants::WorkflowStepStatuses::COMPLETE,
46
+ Constants::WorkflowStepStatuses::RESOLVED_MANUALLY
47
+ ]
48
+ }
49
+ )
50
+ }
51
+
52
+ scope :failed, lambda {
53
+ joins(:workflow_step_transitions)
54
+ .where(
55
+ workflow_step_transitions: {
56
+ most_recent: true,
57
+ to_state: Constants::WorkflowStepStatuses::ERROR
58
+ }
59
+ )
60
+ }
61
+
62
+ scope :pending, lambda {
63
+ # Include steps with no transitions (initial state) AND steps with pending/in_progress transitions
64
+ where.missing(:workflow_step_transitions)
65
+ .or(
66
+ joins(:workflow_step_transitions)
67
+ .where(
68
+ workflow_step_transitions: {
69
+ most_recent: true,
70
+ to_state: [
71
+ Constants::WorkflowStepStatuses::PENDING,
72
+ Constants::WorkflowStepStatuses::IN_PROGRESS
73
+ ]
74
+ }
75
+ )
76
+ )
77
+ }
78
+
79
+ scope :for_task, lambda { |task|
80
+ where(task_id: task.task_id)
81
+ }
82
+
83
+ # Scopes workflow steps by their current state using state machine transitions
84
+ #
85
+ # @scope class
86
+ # @param state [String, nil] The state to filter by. If nil, returns all steps with current state information
87
+ # @return [ActiveRecord::Relation] Steps with current state, optionally filtered by specific state
88
+ scope :by_current_state, lambda { |state = nil|
89
+ relation = joins(<<-SQL.squish)
90
+ INNER JOIN (
91
+ SELECT DISTINCT ON (workflow_step_id) workflow_step_id, to_state
92
+ FROM tasker_workflow_step_transitions
93
+ WHERE most_recent = true
94
+ ORDER BY workflow_step_id, sort_key DESC
95
+ ) current_transitions ON current_transitions.workflow_step_id = tasker_workflow_steps.workflow_step_id
96
+ SQL
97
+
98
+ if state.present?
99
+ relation.where(current_transitions: { to_state: state })
100
+ else
101
+ relation
102
+ end
103
+ }
104
+
105
+ # Scopes workflow steps completed since a specific time
106
+ #
107
+ # @scope class
108
+ # @param since_time [Time] The earliest completion time to include
109
+ # @return [ActiveRecord::Relation] Steps completed since the specified time
110
+ scope :completed_since, lambda { |since_time|
111
+ joins(:workflow_step_transitions)
112
+ .where('tasker_workflow_step_transitions.most_recent = ? AND tasker_workflow_step_transitions.to_state = ?', true, 'complete')
113
+ .where('tasker_workflow_step_transitions.created_at > ?', since_time)
114
+ }
115
+
116
+ # Scopes workflow steps that failed since a specific time
117
+ #
118
+ # @scope class
119
+ # @param since_time [Time] The earliest failure time to include
120
+ # @return [ActiveRecord::Relation] Steps that failed since the specified time
121
+ scope :failed_since, lambda { |since_time|
122
+ joins(:workflow_step_transitions)
123
+ .where('tasker_workflow_step_transitions.most_recent = ? AND tasker_workflow_step_transitions.to_state = ?', true, 'error')
124
+ .where('tasker_workflow_step_transitions.created_at > ?', since_time)
125
+ }
126
+
127
+ # Scopes workflow steps for tasks created since a specific time
128
+ #
129
+ # @scope class
130
+ # @param since_time [Time] The earliest task creation time to include
131
+ # @return [ActiveRecord::Relation] Steps for tasks created since the specified time
132
+ scope :for_tasks_since, lambda { |since_time|
133
+ joins(:task).where('tasker_tasks.created_at > ?', since_time)
134
+ }
135
+
136
+ # Efficient method to get task completion statistics using ActiveRecord scopes
137
+ # This avoids the N+1 query problem while working with the state machine system
138
+ #
139
+ # @param task [Task] The task to analyze
140
+ # @return [Hash] Hash with completion statistics and latest completion time
141
+ def self.task_completion_stats(task)
142
+ # Use efficient ActiveRecord queries with the state machine
143
+ task_steps = for_task(task)
144
+
145
+ # Get completion statistics with optimized queries
146
+ total_steps = task_steps.count
147
+ completed_steps = task_steps.completed
148
+ failed_steps = task_steps.failed
149
+
150
+ # Calculate counts
151
+ completed_count = completed_steps.count
152
+ failed_count = failed_steps.count
153
+
154
+ # For pending count, calculate as total minus completed and failed
155
+ # This handles the case where new steps don't have transitions yet
156
+ pending_count = total_steps - completed_count - failed_count
157
+
158
+ # Get latest completion time from completed steps
159
+ latest_completion_time = completed_steps.maximum(:processed_at)
160
+
161
+ {
162
+ total_steps: total_steps,
163
+ completed_steps: completed_count,
164
+ failed_steps: failed_count,
165
+ pending_steps: pending_count,
166
+ latest_completion_time: latest_completion_time,
167
+ all_complete: completed_count == total_steps && total_steps.positive?
168
+ }
169
+ end
170
+
171
+ # State machine integration
172
+ def state_machine
173
+ @state_machine ||= Tasker::StateMachine::StepStateMachine.new(
174
+ self,
175
+ transition_class: Tasker::WorkflowStepTransition,
176
+ association_name: :workflow_step_transitions
177
+ )
178
+ end
179
+
180
+ # Status is now entirely managed by the state machine
181
+ def status
182
+ if new_record?
183
+ # For new records, return the initial state
184
+ Tasker::Constants::WorkflowStepStatuses::PENDING
185
+ else
186
+ # For persisted records, use state machine
187
+ state_machine.current_state
188
+ end
189
+ end
190
+
191
+ # Finds a WorkflowStep with the given name by traversing the DAG efficiently
192
+ # @param steps [Array<WorkflowStep>] Collection of steps to search through
193
+ # @param name [String] Name of the step to find
194
+ # @return [WorkflowStep, nil] The first matching step found or nil if none exists
195
+ def self.find_step_by_name(steps, name)
196
+ StepFinder.find_by_name(steps, name)
197
+ end
198
+
199
+ # Service class to find steps by name
200
+ # Reduces complexity by organizing step search logic
201
+ class StepFinder
202
+ class << self
203
+ # Find step by name in provided collection or task hierarchy
204
+ #
205
+ # @param steps [Array<WorkflowStep>] Collection of steps to search through
206
+ # @param name [String] Name of the step to find
207
+ # @return [WorkflowStep, nil] The first matching step found or nil if none exists
208
+ def find_by_name(steps, name)
209
+ return nil if steps.empty? || name.nil?
210
+
211
+ # First check direct match in provided steps
212
+ direct_match = find_direct_match(steps, name)
213
+ return direct_match if direct_match
214
+
215
+ # Fall back to task-wide search using DAG relationships
216
+ find_in_task_hierarchy(steps, name)
217
+ end
218
+
219
+ private
220
+
221
+ # Find direct match in provided steps
222
+ #
223
+ # @param steps [Array<WorkflowStep>] Collection of steps
224
+ # @param name [String] Name to search for
225
+ # @return [WorkflowStep, nil] Matching step or nil
226
+ def find_direct_match(steps, name)
227
+ steps.find { |step| step.name == name }
228
+ end
229
+
230
+ # Find step in task hierarchy using efficient DAG traversal
231
+ #
232
+ # @param steps [Array<WorkflowStep>] Collection of steps to get task context
233
+ # @param name [String] Name to search for
234
+ # @return [WorkflowStep, nil] Matching step or nil
235
+ def find_in_task_hierarchy(steps, name)
236
+ task_ids = extract_task_ids(steps)
237
+
238
+ task_ids.each do |task_id|
239
+ found_step = find_in_single_task(task_id, name)
240
+ return found_step if found_step
241
+ end
242
+
243
+ nil
244
+ end
245
+
246
+ # Extract unique task IDs from steps
247
+ #
248
+ # @param steps [Array<WorkflowStep>] Collection of steps
249
+ # @return [Array<Integer>] Unique task IDs
250
+ def extract_task_ids(steps)
251
+ steps.map(&:task_id).uniq
252
+ end
253
+
254
+ # Find step by name in a single task
255
+ #
256
+ # @param task_id [Integer] Task ID to search in
257
+ # @param name [String] Name to search for
258
+ # @return [WorkflowStep, nil] Matching step or nil
259
+ def find_in_single_task(task_id, name)
260
+ # Get all workflow steps for this task with their DAG relationships
261
+ all_task_steps = WorkflowStep.joins(:named_step)
262
+ .includes(:step_dag_relationship)
263
+ .where(task_id: task_id)
264
+
265
+ # Find step by name using simple lookup instead of recursive traversal
266
+ all_task_steps.joins(:named_step)
267
+ .find_by(named_steps: { name: name })
268
+ end
269
+ end
270
+ end
271
+
272
+ def self.get_steps_for_task(task, templates)
273
+ named_steps = NamedStep.create_named_steps_from_templates(templates)
274
+ steps =
275
+ templates.map do |template|
276
+ named_step = named_steps.find { |ns| template.name == ns.name }
277
+ NamedTasksNamedStep.associate_named_step_with_named_task(task.named_task, template, named_step)
278
+ step = where(task_id: task.task_id, named_step_id: named_step.named_step_id).first
279
+ step ||= build_default_step!(task, template, named_step)
280
+ step
281
+ end
282
+ set_up_dependent_steps(steps, templates)
283
+ end
284
+
285
+ def self.set_up_dependent_steps(steps, templates)
286
+ templates.each do |template|
287
+ next if template.all_dependencies.empty?
288
+
289
+ dependent_step = steps.find { |step| step.name == template.name }
290
+ template.all_dependencies.each do |dependency|
291
+ provider_step = steps.find { |step| step.name == dependency }
292
+ unless provider_step.outgoing_edges.exists?(to_step: dependent_step)
293
+ provider_step.add_provides_edge!(dependent_step)
294
+ end
295
+ end
296
+ end
297
+ steps
298
+ end
299
+
300
+ def self.build_default_step!(task, template, named_step)
301
+ # Create the step first without status
302
+ step_attributes = {
303
+ task_id: task.task_id,
304
+ named_step_id: named_step.named_step_id,
305
+ retryable: template.default_retryable,
306
+ retry_limit: template.default_retry_limit,
307
+ skippable: template.skippable,
308
+ in_process: false,
309
+ inputs: task.context,
310
+ processed: false,
311
+ attempts: 0,
312
+ results: {}
313
+ }
314
+
315
+ step = new(step_attributes)
316
+ step.save!
317
+
318
+ # REMOVED: Automatic state machine initialization to prevent duplicate key violations
319
+ # The state machine will initialize naturally when accessed, and factories may
320
+ # have already created transitions through their own setup
321
+ # step.state_machine.initialize_state_machine!
322
+
323
+ step
324
+ end
325
+
326
+ def self.get_viable_steps(task, sequence)
327
+ # Get step IDs from sequence
328
+ step_ids = sequence.steps.map(&:workflow_step_id)
329
+
330
+ # Use function-based approach for high-performance readiness checking
331
+ ready_statuses = StepReadinessStatus.for_task(task.task_id, step_ids)
332
+ ready_step_ids = ready_statuses.select(&:ready_for_execution).map(&:workflow_step_id)
333
+
334
+ # Return WorkflowStep objects for ready steps
335
+ WorkflowStep.where(workflow_step_id: ready_step_ids)
336
+ .includes(:named_step)
337
+ end
338
+
339
+ def add_provides_edge!(to_step)
340
+ outgoing_edges.create!(to_step: to_step, name: PROVIDES_EDGE_NAME)
341
+ end
342
+
343
+ # Helper method to get step readiness status using function-based approach
344
+ def step_readiness_status
345
+ @step_readiness_status ||= StepReadinessStatus.for_task(task_id, [workflow_step_id]).first
346
+ end
347
+
348
+ def complete?
349
+ # Use function-based approach for consistent state checking
350
+ step_readiness_status&.current_state&.in?([
351
+ Constants::WorkflowStepStatuses::COMPLETE,
352
+ Constants::WorkflowStepStatuses::RESOLVED_MANUALLY
353
+ ]) || false
354
+ end
355
+
356
+ def in_progress?
357
+ # Use function-based approach for consistent state checking
358
+ step_readiness_status&.current_state == Constants::WorkflowStepStatuses::IN_PROGRESS
359
+ end
360
+
361
+ def pending?
362
+ # Use function-based approach for consistent state checking
363
+ step_readiness_status&.current_state == Constants::WorkflowStepStatuses::PENDING
364
+ end
365
+
366
+ def in_error?
367
+ # Use function-based approach for consistent state checking
368
+ step_readiness_status&.current_state == Constants::WorkflowStepStatuses::ERROR
369
+ end
370
+
371
+ def cancelled?
372
+ # Use function-based approach for consistent state checking
373
+ step_readiness_status&.current_state == Constants::WorkflowStepStatuses::CANCELLED
374
+ end
375
+
376
+ def ready_status?
377
+ # Use function-based approach for efficient ready status checking
378
+ Constants::UNREADY_WORKFLOW_STEP_STATUSES.exclude?(
379
+ step_readiness_status&.current_state || Constants::WorkflowStepStatuses::PENDING
380
+ )
381
+ end
382
+
383
+ def ready?
384
+ # Use function-based approach's comprehensive readiness calculation
385
+ step_readiness_status&.ready_for_execution || false
386
+ end
387
+
388
+ # Function-based predicate methods
389
+ def dependencies_satisfied?
390
+ # Use function-based approach's pre-calculated dependency analysis
391
+ step_readiness_status&.dependencies_satisfied || false
392
+ end
393
+
394
+ def retry_eligible?
395
+ # Use function-based approach's retry/backoff calculation
396
+ step_readiness_status&.retry_eligible || false
397
+ end
398
+
399
+ def has_retry_attempts?
400
+ # Check if step has made retry attempts
401
+ (step_readiness_status&.attempts || 0).positive?
402
+ end
403
+
404
+ def retry_exhausted?
405
+ # Check if step has exhausted retry attempts
406
+ return false unless step_readiness_status
407
+
408
+ attempts = step_readiness_status.attempts || 0
409
+ retry_limit = step_readiness_status.retry_limit || 3
410
+ attempts >= retry_limit
411
+ end
412
+
413
+ def waiting_for_backoff?
414
+ # Check if step is waiting for backoff period to expire
415
+ return false unless step_readiness_status&.next_retry_at
416
+
417
+ step_readiness_status.next_retry_at > Time.current
418
+ end
419
+
420
+ def can_retry_now?
421
+ # Comprehensive check if step can be retried right now
422
+ return false unless in_error?
423
+ return false unless retry_eligible?
424
+ return false if waiting_for_backoff?
425
+
426
+ true
427
+ end
428
+
429
+ def root_step?
430
+ # Check if this is a root step (no dependencies)
431
+ (step_readiness_status&.total_parents || 0).zero?
432
+ end
433
+
434
+ def leaf_step?
435
+ # Check if this is a leaf step using DAG relationship view
436
+ step_dag_relationship&.child_count&.zero?
437
+ end
438
+
439
+ def reload
440
+ # Override reload to ensure step readiness status is refreshed
441
+ super.tap do
442
+ @step_readiness_status = nil # Reset cached readiness status
443
+ end
444
+ end
445
+
446
+ private
447
+
448
+ # Custom validation to ensure step names are unique within a task
449
+ def name_uniqueness_within_task
450
+ return unless named_step && task
451
+
452
+ # Find all steps within the same task that have the same name
453
+ matching_steps = self.class.where(task_id: task_id)
454
+ .joins(:named_step)
455
+ .where(named_step: { name: name })
456
+ .where.not(workflow_step_id: workflow_step_id) # Exclude self when updating
457
+
458
+ errors.add(:base, "Step name '#{name}' must be unique within the same task") if matching_steps.exists?
459
+ end
460
+ end
461
+ end