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,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
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tasker
4
+ class WorkflowStepEdge < ApplicationRecord
5
+ belongs_to :from_step, class_name: 'WorkflowStep'
6
+ belongs_to :to_step, class_name: 'WorkflowStep'
7
+
8
+ validates :name, presence: true
9
+
10
+ before_create :ensure_no_cycles!
11
+
12
+ scope :children_of, ->(step) { where(from_step: step) }
13
+ scope :parents_of, ->(step) { where(to_step: step) }
14
+ scope :siblings_of, ->(step) { find_by_sql(sibling_sql(step.id)) }
15
+ scope :provides_edges, -> { where(name: WorkflowStep::PROVIDES_EDGE_NAME) }
16
+ scope :provides_to_children, -> { where(name: WorkflowStep::PROVIDES_EDGE_NAME, to_step: children_of(from_step)) }
17
+
18
+ def self.create_edge!(from_step, to_step, name)
19
+ create!(from_step: from_step, to_step: to_step, name: name)
20
+ end
21
+
22
+ def self.sibling_sql(step_id)
23
+ <<~SQL.squish
24
+ WITH step_parents AS (
25
+ SELECT from_step_id
26
+ FROM tasker_workflow_step_edges
27
+ WHERE to_step_id = #{step_id}
28
+ ),
29
+ potential_siblings AS (
30
+ SELECT to_step_id
31
+ FROM tasker_workflow_step_edges
32
+ WHERE from_step_id IN (SELECT from_step_id FROM step_parents)
33
+ AND to_step_id != #{step_id}
34
+ ),
35
+ siblings AS (
36
+ SELECT to_step_id
37
+ FROM tasker_workflow_step_edges
38
+ WHERE to_step_id IN (SELECT to_step_id FROM potential_siblings)
39
+ GROUP BY to_step_id
40
+ HAVING ARRAY_AGG(from_step_id ORDER BY from_step_id) =
41
+ (SELECT ARRAY_AGG(from_step_id ORDER BY from_step_id) FROM step_parents)
42
+ )
43
+ SELECT e.*
44
+ FROM tasker_workflow_step_edges e
45
+ JOIN siblings ON e.to_step_id = siblings.to_step_id
46
+ SQL
47
+ end
48
+
49
+ private
50
+
51
+ def ensure_no_cycles!
52
+ return unless from_step && to_step
53
+
54
+ # Check for direct cycles first (A->B, B->A)
55
+ if self.class.exists?(from_step: to_step, to_step: from_step)
56
+ raise ActiveRecord::RecordInvalid.new(self), 'Adding this edge would create a cycle in the workflow'
57
+ end
58
+
59
+ # Check for indirect cycles (A->B->C->A)
60
+ # Use a recursive CTE that includes our new edge
61
+ cycle_sql = <<~SQL
62
+ WITH RECURSIVE all_edges AS (
63
+ -- Combine existing edges with our new edge
64
+ SELECT from_step_id, to_step_id
65
+ FROM tasker_workflow_step_edges
66
+ UNION ALL
67
+ SELECT #{from_step.id}::bigint, #{to_step.id}::bigint
68
+ ),
69
+ path AS (
70
+ -- Start with edges from to_step
71
+ SELECT from_step_id, to_step_id, ARRAY[from_step_id] as path
72
+ FROM all_edges
73
+ WHERE from_step_id = #{to_step.id}::bigint
74
+
75
+ UNION ALL
76
+
77
+ -- Follow edges recursively
78
+ SELECT e.from_step_id, e.to_step_id, p.path || e.from_step_id
79
+ FROM all_edges e
80
+ JOIN path p ON e.from_step_id = p.to_step_id
81
+ WHERE NOT e.from_step_id = ANY(p.path) -- Avoid cycles in traversal
82
+ )
83
+ SELECT COUNT(*) as cycle_count
84
+ FROM path
85
+ WHERE to_step_id = #{from_step.id}::bigint
86
+ SQL
87
+
88
+ result = self.class.connection.execute(cycle_sql).first
89
+ if result['cycle_count'].to_i > 0 # rubocop:disable Style/NumericPredicate,Style/GuardClause
90
+ raise ActiveRecord::RecordInvalid.new(self), 'Adding this edge would create a cycle in the workflow'
91
+ end
92
+ end
93
+ end
94
+ end