omnibase_infra 0.2.1__py3-none-any.whl

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 (675) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/cli/__init__.py +1 -0
  3. omnibase_infra/cli/commands.py +216 -0
  4. omnibase_infra/clients/__init__.py +0 -0
  5. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
  6. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
  7. omnibase_infra/decorators/__init__.py +29 -0
  8. omnibase_infra/decorators/allow_any.py +109 -0
  9. omnibase_infra/dlq/__init__.py +90 -0
  10. omnibase_infra/dlq/constants_dlq.py +57 -0
  11. omnibase_infra/dlq/models/__init__.py +26 -0
  12. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  13. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  14. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  15. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  16. omnibase_infra/enums/__init__.py +123 -0
  17. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  18. omnibase_infra/enums/enum_backend_type.py +27 -0
  19. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  20. omnibase_infra/enums/enum_capture_state.py +88 -0
  21. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  22. omnibase_infra/enums/enum_circuit_state.py +51 -0
  23. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  24. omnibase_infra/enums/enum_contract_type.py +84 -0
  25. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  26. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  27. omnibase_infra/enums/enum_environment.py +46 -0
  28. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  29. omnibase_infra/enums/enum_handler_error_type.py +101 -0
  30. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  31. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  32. omnibase_infra/enums/enum_handler_type.py +77 -0
  33. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  34. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  35. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  36. omnibase_infra/enums/enum_message_category.py +213 -0
  37. omnibase_infra/enums/enum_node_archetype.py +74 -0
  38. omnibase_infra/enums/enum_node_output_type.py +185 -0
  39. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  40. omnibase_infra/enums/enum_policy_type.py +32 -0
  41. omnibase_infra/enums/enum_registration_state.py +261 -0
  42. omnibase_infra/enums/enum_registration_status.py +33 -0
  43. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  44. omnibase_infra/enums/enum_response_status.py +26 -0
  45. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  46. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  47. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  48. omnibase_infra/enums/enum_topic_standard.py +42 -0
  49. omnibase_infra/enums/enum_validation_severity.py +78 -0
  50. omnibase_infra/errors/__init__.py +156 -0
  51. omnibase_infra/errors/error_architecture_violation.py +152 -0
  52. omnibase_infra/errors/error_chain_propagation.py +188 -0
  53. omnibase_infra/errors/error_compute_registry.py +92 -0
  54. omnibase_infra/errors/error_consul.py +132 -0
  55. omnibase_infra/errors/error_container_wiring.py +243 -0
  56. omnibase_infra/errors/error_event_bus_registry.py +102 -0
  57. omnibase_infra/errors/error_infra.py +608 -0
  58. omnibase_infra/errors/error_message_type_registry.py +101 -0
  59. omnibase_infra/errors/error_policy_registry.py +112 -0
  60. omnibase_infra/errors/error_vault.py +123 -0
  61. omnibase_infra/event_bus/__init__.py +72 -0
  62. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
  63. omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
  64. omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
  65. omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
  66. omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
  67. omnibase_infra/event_bus/models/__init__.py +29 -0
  68. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  69. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
  70. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  71. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  72. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  73. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  74. omnibase_infra/event_bus/topic_constants.py +376 -0
  75. omnibase_infra/handlers/__init__.py +75 -0
  76. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  77. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  78. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  79. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  80. omnibase_infra/handlers/handler_consul.py +787 -0
  81. omnibase_infra/handlers/handler_db.py +1039 -0
  82. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  83. omnibase_infra/handlers/handler_graph.py +1154 -0
  84. omnibase_infra/handlers/handler_http.py +920 -0
  85. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  86. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  87. omnibase_infra/handlers/handler_mcp.py +748 -0
  88. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  89. omnibase_infra/handlers/handler_vault.py +422 -0
  90. omnibase_infra/handlers/mcp/__init__.py +19 -0
  91. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  92. omnibase_infra/handlers/mcp/protocols.py +178 -0
  93. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  94. omnibase_infra/handlers/mixins/__init__.py +42 -0
  95. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  96. omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
  97. omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
  98. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  99. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  100. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  101. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  102. omnibase_infra/handlers/models/__init__.py +286 -0
  103. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  104. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  105. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  106. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  107. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  108. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  109. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  110. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  111. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  112. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  113. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  114. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  115. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  116. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  117. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  118. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  119. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  120. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  121. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  122. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  123. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  124. omnibase_infra/handlers/models/http/__init__.py +50 -0
  125. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  126. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  127. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  128. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  129. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  130. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  131. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  132. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  133. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  134. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  135. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  136. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  137. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  138. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  139. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  140. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  141. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  142. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  143. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  144. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  145. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  146. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  147. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  148. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  149. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  150. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  151. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  152. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  153. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  154. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  155. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  156. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  157. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  158. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  159. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  160. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  161. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  162. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  163. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  164. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  165. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  166. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  167. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  168. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  169. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  170. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  171. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  172. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  173. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  174. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  175. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  176. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  177. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  178. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  179. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  180. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  181. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  182. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  183. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  184. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  185. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  186. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  187. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  188. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  189. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  190. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  191. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
  192. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  193. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  194. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  195. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  196. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  197. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
  198. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  199. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  200. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  201. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  202. omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
  203. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  204. omnibase_infra/idempotency/__init__.py +94 -0
  205. omnibase_infra/idempotency/models/__init__.py +43 -0
  206. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  207. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  208. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  209. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  210. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  211. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  212. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  213. omnibase_infra/idempotency/store_inmemory.py +265 -0
  214. omnibase_infra/idempotency/store_postgres.py +923 -0
  215. omnibase_infra/infrastructure/__init__.py +0 -0
  216. omnibase_infra/mixins/__init__.py +71 -0
  217. omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
  218. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  219. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  220. omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
  221. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  222. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  223. omnibase_infra/models/__init__.py +136 -0
  224. omnibase_infra/models/corpus/__init__.py +17 -0
  225. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  226. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  227. omnibase_infra/models/discovery/__init__.py +42 -0
  228. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  229. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  230. omnibase_infra/models/discovery/model_introspection_config.py +311 -0
  231. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  232. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  233. omnibase_infra/models/dispatch/__init__.py +147 -0
  234. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  235. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  236. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  237. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  238. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  239. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  240. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  241. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  242. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  243. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  244. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  245. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  246. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  247. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  248. omnibase_infra/models/errors/__init__.py +45 -0
  249. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  250. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  251. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  252. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  253. omnibase_infra/models/handlers/__init__.py +37 -0
  254. omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
  255. omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
  256. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  257. omnibase_infra/models/health/__init__.py +9 -0
  258. omnibase_infra/models/health/model_health_check_result.py +40 -0
  259. omnibase_infra/models/lifecycle/__init__.py +39 -0
  260. omnibase_infra/models/logging/__init__.py +51 -0
  261. omnibase_infra/models/logging/model_log_context.py +756 -0
  262. omnibase_infra/models/model_retry_error_classification.py +78 -0
  263. omnibase_infra/models/projection/__init__.py +43 -0
  264. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  265. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  266. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  267. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  268. omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
  269. omnibase_infra/models/projectors/__init__.py +41 -0
  270. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  271. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  272. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  273. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  274. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  275. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  276. omnibase_infra/models/registration/__init__.py +59 -0
  277. omnibase_infra/models/registration/commands/__init__.py +15 -0
  278. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  279. omnibase_infra/models/registration/events/__init__.py +56 -0
  280. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  281. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  282. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  283. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  284. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  285. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  286. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  287. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  288. omnibase_infra/models/registration/model_node_capabilities.py +179 -0
  289. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  290. omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
  291. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  292. omnibase_infra/models/registration/model_node_registration.py +162 -0
  293. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  294. omnibase_infra/models/registry/__init__.py +29 -0
  295. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  296. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  297. omnibase_infra/models/resilience/__init__.py +9 -0
  298. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  299. omnibase_infra/models/routing/__init__.py +25 -0
  300. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  301. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  302. omnibase_infra/models/runtime/__init__.py +40 -0
  303. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  304. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  305. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  306. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  307. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  308. omnibase_infra/models/runtime/model_handler_contract.py +280 -0
  309. omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
  310. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  311. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  312. omnibase_infra/models/security/__init__.py +50 -0
  313. omnibase_infra/models/security/classification_levels.py +99 -0
  314. omnibase_infra/models/security/model_environment_policy.py +145 -0
  315. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  316. omnibase_infra/models/security/model_security_error.py +81 -0
  317. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  318. omnibase_infra/models/security/model_security_warning.py +67 -0
  319. omnibase_infra/models/snapshot/__init__.py +27 -0
  320. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  321. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  322. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  323. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  324. omnibase_infra/models/types/__init__.py +71 -0
  325. omnibase_infra/models/validation/__init__.py +89 -0
  326. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  327. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  328. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  329. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  330. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  331. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  332. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  333. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  334. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  335. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  336. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  337. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  338. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  339. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  340. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  341. omnibase_infra/nodes/__init__.py +48 -0
  342. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  343. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  344. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
  345. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  346. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  347. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  348. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  349. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  350. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  351. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  352. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  353. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  354. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  355. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  356. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  357. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  358. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  359. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
  360. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  361. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  362. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  363. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  364. omnibase_infra/nodes/effects/README.md +358 -0
  365. omnibase_infra/nodes/effects/__init__.py +26 -0
  366. omnibase_infra/nodes/effects/contract.yaml +172 -0
  367. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  368. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  369. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  370. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  371. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  372. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  373. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  374. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  375. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  376. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  377. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  378. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  379. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
  380. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  381. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  382. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  383. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  384. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  385. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  386. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
  387. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  388. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  389. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  390. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  391. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  392. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  393. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  394. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  395. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  396. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  397. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  398. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  399. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  400. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  401. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  402. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  403. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  404. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  405. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  406. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  407. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  408. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
  409. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
  410. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
  411. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  412. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  413. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  414. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  415. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  416. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  417. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  418. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  419. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
  420. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  421. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  422. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  423. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  424. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  425. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  426. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  427. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  428. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  429. omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
  430. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  431. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  432. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  433. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
  434. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  435. omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
  436. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  437. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  438. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  439. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
  440. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  441. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  442. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  443. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  444. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  445. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  446. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  447. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  448. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  449. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  450. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  451. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  452. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  453. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  454. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  455. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  456. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  457. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  458. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  459. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  460. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  461. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  462. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  463. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  464. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  465. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
  466. omnibase_infra/nodes/reducers/__init__.py +30 -0
  467. omnibase_infra/nodes/reducers/models/__init__.py +32 -0
  468. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
  469. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  470. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  471. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  472. omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
  473. omnibase_infra/observability/__init__.py +143 -0
  474. omnibase_infra/observability/constants_metrics.py +91 -0
  475. omnibase_infra/observability/factory_observability_sink.py +525 -0
  476. omnibase_infra/observability/handlers/__init__.py +118 -0
  477. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  478. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  479. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  480. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  481. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  482. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  483. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  484. omnibase_infra/observability/hooks/__init__.py +74 -0
  485. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  486. omnibase_infra/observability/models/__init__.py +30 -0
  487. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  488. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  489. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  490. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  491. omnibase_infra/observability/sinks/__init__.py +69 -0
  492. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  493. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  494. omnibase_infra/plugins/__init__.py +27 -0
  495. omnibase_infra/plugins/examples/__init__.py +28 -0
  496. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  497. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  498. omnibase_infra/plugins/models/__init__.py +21 -0
  499. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  500. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  501. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  502. omnibase_infra/plugins/plugin_compute_base.py +435 -0
  503. omnibase_infra/projectors/__init__.py +30 -0
  504. omnibase_infra/projectors/contracts/__init__.py +63 -0
  505. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  506. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  507. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  508. omnibase_infra/protocols/__init__.py +99 -0
  509. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  510. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  511. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  512. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  513. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  514. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  515. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  516. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  517. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  518. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  519. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  520. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  521. omnibase_infra/runtime/__init__.py +296 -0
  522. omnibase_infra/runtime/binding_config_resolver.py +2706 -0
  523. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  524. omnibase_infra/runtime/contract_handler_discovery.py +582 -0
  525. omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
  526. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  527. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  528. omnibase_infra/runtime/enums/__init__.py +18 -0
  529. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  530. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  531. omnibase_infra/runtime/envelope_validator.py +179 -0
  532. omnibase_infra/runtime/handler_contract_source.py +669 -0
  533. omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
  534. omnibase_infra/runtime/handler_registry.py +321 -0
  535. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  536. omnibase_infra/runtime/kernel.py +40 -0
  537. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  538. omnibase_infra/runtime/mixin_semver_cache.py +378 -0
  539. omnibase_infra/runtime/mixins/__init__.py +17 -0
  540. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
  541. omnibase_infra/runtime/models/__init__.py +192 -0
  542. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  543. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  544. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  545. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  546. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  547. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  548. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  549. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  550. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  551. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  552. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  553. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  554. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  555. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  556. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  557. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  558. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  559. omnibase_infra/runtime/models/model_health_check_result.py +228 -0
  560. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  561. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  562. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  563. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  564. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  565. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  566. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  567. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  568. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  569. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  570. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  571. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  572. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  573. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  574. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
  575. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  576. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  577. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  578. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  579. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  580. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  581. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  582. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  583. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  584. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  585. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  586. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  587. omnibase_infra/runtime/projector_shell.py +1102 -0
  588. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  589. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  590. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  591. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  592. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  593. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  594. omnibase_infra/runtime/protocol_policy.py +366 -0
  595. omnibase_infra/runtime/protocols/__init__.py +27 -0
  596. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  597. omnibase_infra/runtime/registry/__init__.py +93 -0
  598. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  599. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  600. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  601. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  602. omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
  603. omnibase_infra/runtime/registry_compute.py +1143 -0
  604. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  605. omnibase_infra/runtime/registry_policy.py +1502 -0
  606. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  607. omnibase_infra/runtime/secret_resolver.py +2110 -0
  608. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  609. omnibase_infra/runtime/service_kernel.py +1573 -0
  610. omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
  611. omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
  612. omnibase_infra/runtime/util_container_wiring.py +1123 -0
  613. omnibase_infra/runtime/util_validation.py +314 -0
  614. omnibase_infra/runtime/util_version.py +98 -0
  615. omnibase_infra/runtime/util_wiring.py +566 -0
  616. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  617. omnibase_infra/services/__init__.py +68 -0
  618. omnibase_infra/services/corpus_capture.py +678 -0
  619. omnibase_infra/services/service_capability_query.py +945 -0
  620. omnibase_infra/services/service_health.py +897 -0
  621. omnibase_infra/services/service_node_selector.py +530 -0
  622. omnibase_infra/services/service_timeout_emitter.py +682 -0
  623. omnibase_infra/services/service_timeout_scanner.py +390 -0
  624. omnibase_infra/services/snapshot/__init__.py +31 -0
  625. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  626. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  627. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  628. omnibase_infra/shared/__init__.py +8 -0
  629. omnibase_infra/testing/__init__.py +10 -0
  630. omnibase_infra/testing/utils.py +23 -0
  631. omnibase_infra/types/__init__.py +48 -0
  632. omnibase_infra/types/type_cache_info.py +49 -0
  633. omnibase_infra/types/type_dsn.py +173 -0
  634. omnibase_infra/types/type_infra_aliases.py +60 -0
  635. omnibase_infra/types/typed_dict/__init__.py +21 -0
  636. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  637. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  638. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  639. omnibase_infra/utils/__init__.py +89 -0
  640. omnibase_infra/utils/correlation.py +208 -0
  641. omnibase_infra/utils/util_datetime.py +372 -0
  642. omnibase_infra/utils/util_dsn_validation.py +333 -0
  643. omnibase_infra/utils/util_env_parsing.py +264 -0
  644. omnibase_infra/utils/util_error_sanitization.py +457 -0
  645. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  646. omnibase_infra/utils/util_semver.py +233 -0
  647. omnibase_infra/validation/__init__.py +307 -0
  648. omnibase_infra/validation/enums/__init__.py +11 -0
  649. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  650. omnibase_infra/validation/infra_validators.py +1486 -0
  651. omnibase_infra/validation/linter_contract.py +907 -0
  652. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  653. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  654. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  655. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  656. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  657. omnibase_infra/validation/models/__init__.py +15 -0
  658. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  659. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  660. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  661. omnibase_infra/validation/validation_exemptions.yaml +1710 -0
  662. omnibase_infra/validation/validator_any_type.py +715 -0
  663. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  664. omnibase_infra/validation/validator_execution_shape.py +465 -0
  665. omnibase_infra/validation/validator_localhandler.py +261 -0
  666. omnibase_infra/validation/validator_registration_security.py +410 -0
  667. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  668. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  669. omnibase_infra/validation/validator_security.py +410 -0
  670. omnibase_infra/validation/validator_topic_category.py +1152 -0
  671. omnibase_infra-0.2.1.dist-info/METADATA +197 -0
  672. omnibase_infra-0.2.1.dist-info/RECORD +675 -0
  673. omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
  674. omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
  675. omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1070 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """RuntimeScheduler - Core service for emitting RuntimeTick events.
4
+
5
+ This module implements the RuntimeScheduler service that emits RuntimeTick events
6
+ at configured intervals. The scheduler is the single source of truth for "now"
7
+ across all orchestrators in the ONEX infrastructure.
8
+
9
+ Key Features:
10
+ - Configurable tick interval (10ms - 60,000ms)
11
+ - Circuit breaker pattern for publish resilience
12
+ - Restart-safe sequence number tracking
13
+ - Jitter to prevent thundering herd
14
+ - Graceful shutdown with proper lifecycle management
15
+ - Metrics collection for observability
16
+
17
+ Architecture:
18
+ The RuntimeScheduler is an INFRASTRUCTURE concern. It emits RuntimeTick events
19
+ that orchestrators subscribe to for timeout decisions (DOMAIN concern). This
20
+ separation ensures clear ownership and testability.
21
+
22
+ Concurrency Safety:
23
+ This scheduler is coroutine-safe, not thread-safe. All locking uses
24
+ asyncio primitives which protect against concurrent coroutine access:
25
+ - Circuit breaker operations protected by `_circuit_breaker_lock` (asyncio.Lock)
26
+ - State variables protected by `_state_lock` (asyncio.Lock)
27
+ - Tick loop runs as background task with shutdown signaling via `asyncio.Event`
28
+ For multi-threaded access, additional synchronization would be required.
29
+
30
+ Usage:
31
+ ```python
32
+ from omnibase_infra.runtime.runtime_scheduler import RuntimeScheduler
33
+ from omnibase_infra.runtime.models import ModelRuntimeSchedulerConfig
34
+ from omnibase_infra.event_bus.event_bus_kafka import EventBusKafka
35
+
36
+ # Create scheduler with configuration
37
+ config = ModelRuntimeSchedulerConfig.default()
38
+ event_bus = EventBusKafka.default()
39
+ await event_bus.start()
40
+
41
+ scheduler = RuntimeScheduler(config=config, event_bus=event_bus)
42
+ await scheduler.start()
43
+
44
+ try:
45
+ # Scheduler runs, emitting ticks at configured interval
46
+ while scheduler.is_running:
47
+ await asyncio.sleep(1.0)
48
+ finally:
49
+ await scheduler.stop()
50
+ metrics = scheduler.get_metrics()
51
+ print(f"Emitted {metrics.ticks_emitted} ticks")
52
+ ```
53
+
54
+ Related:
55
+ - OMN-953: RuntimeTick scheduler implementation
56
+ - ModelRuntimeTick: The event model emitted by the scheduler
57
+ - ModelRuntimeSchedulerConfig: Configuration model
58
+ - ModelRuntimeSchedulerMetrics: Metrics model for observability
59
+ - ProtocolRuntimeScheduler: Protocol interface
60
+
61
+ .. versionadded:: 0.4.0
62
+ """
63
+
64
+ from __future__ import annotations
65
+
66
+ import asyncio
67
+ import logging
68
+ import random
69
+ import time
70
+ from datetime import UTC, datetime
71
+ from uuid import UUID, uuid4
72
+
73
+ import redis.asyncio as redis
74
+ from redis.asyncio import Redis
75
+ from redis.exceptions import ConnectionError as RedisConnectionError
76
+ from redis.exceptions import RedisError
77
+ from redis.exceptions import TimeoutError as RedisTimeoutError
78
+
79
+ from omnibase_infra.enums import EnumInfraTransportType
80
+ from omnibase_infra.errors import (
81
+ InfraConnectionError,
82
+ InfraTimeoutError,
83
+ InfraUnavailableError,
84
+ ModelInfraErrorContext,
85
+ ModelTimeoutErrorContext,
86
+ ProtocolConfigurationError,
87
+ )
88
+ from omnibase_infra.event_bus.event_bus_kafka import EventBusKafka
89
+ from omnibase_infra.event_bus.models import ModelEventHeaders
90
+ from omnibase_infra.mixins import MixinAsyncCircuitBreaker
91
+ from omnibase_infra.runtime.enums import EnumSchedulerStatus
92
+ from omnibase_infra.runtime.models import (
93
+ ModelRuntimeSchedulerConfig,
94
+ ModelRuntimeSchedulerMetrics,
95
+ ModelRuntimeTick,
96
+ )
97
+ from omnibase_infra.utils.util_error_sanitization import sanitize_error_string
98
+
99
+ logger = logging.getLogger(__name__)
100
+
101
+
102
+ class RuntimeScheduler(MixinAsyncCircuitBreaker):
103
+ """Runtime scheduler that emits RuntimeTick events at configured intervals.
104
+
105
+ The scheduler is the single source of truth for "now" across all orchestrators.
106
+ It emits RuntimeTick events that orchestrators subscribe to for timeout decisions.
107
+
108
+ This is an INFRASTRUCTURE concern - orchestrators derive timeout decisions
109
+ (DOMAIN concern) from the ticks.
110
+
111
+ Attributes:
112
+ scheduler_id: Unique identifier for this scheduler instance.
113
+ is_running: Whether the scheduler is currently running.
114
+ current_sequence_number: Current sequence number for restart-safety.
115
+
116
+ Concurrency Safety:
117
+ This scheduler is coroutine-safe using asyncio primitives:
118
+ - Circuit breaker operations protected by `_circuit_breaker_lock` (asyncio.Lock)
119
+ - State variables protected by `_state_lock` (asyncio.Lock)
120
+ - Shutdown signaling via `asyncio.Event`
121
+ Note: This is coroutine-safe, not thread-safe.
122
+
123
+ Restart Safety:
124
+ The `current_sequence_number` property returns a monotonically increasing
125
+ value that helps orchestrators detect scheduler restarts. If the sequence
126
+ number decreases or resets, orchestrators know a restart occurred.
127
+
128
+ Example:
129
+ ```python
130
+ config = ModelRuntimeSchedulerConfig.default()
131
+ event_bus = EventBusKafka.default()
132
+ await event_bus.start()
133
+
134
+ scheduler = RuntimeScheduler(config=config, event_bus=event_bus)
135
+ await scheduler.start()
136
+
137
+ # Scheduler runs in background
138
+ await asyncio.sleep(10.0)
139
+
140
+ await scheduler.stop()
141
+ print(f"Emitted {scheduler.get_metrics().ticks_emitted} ticks")
142
+ ```
143
+ """
144
+
145
+ def __init__(
146
+ self,
147
+ config: ModelRuntimeSchedulerConfig,
148
+ event_bus: EventBusKafka,
149
+ ) -> None:
150
+ """Initialize the RuntimeScheduler.
151
+
152
+ Args:
153
+ config: Configuration model containing all scheduler settings.
154
+ event_bus: EventBusKafka instance for publishing tick events.
155
+
156
+ Raises:
157
+ ProtocolConfigurationError: If config or event_bus is None.
158
+ """
159
+ context = ModelInfraErrorContext(
160
+ transport_type=EnumInfraTransportType.RUNTIME,
161
+ operation="scheduler_init",
162
+ )
163
+ if config is None:
164
+ raise ProtocolConfigurationError("config cannot be None", context=context)
165
+ if event_bus is None:
166
+ raise ProtocolConfigurationError(
167
+ "event_bus cannot be None", context=context
168
+ )
169
+
170
+ # Store configuration
171
+ self._config = config
172
+ self._event_bus = event_bus
173
+
174
+ # Initialize circuit breaker mixin
175
+ self._init_circuit_breaker(
176
+ threshold=config.circuit_breaker_threshold,
177
+ reset_timeout=config.circuit_breaker_reset_timeout_seconds,
178
+ service_name=f"runtime-scheduler.{config.scheduler_id}",
179
+ transport_type=EnumInfraTransportType.KAFKA,
180
+ )
181
+
182
+ # State variables (protected by _state_lock)
183
+ self._status = EnumSchedulerStatus.STOPPED
184
+ self._sequence_number: int = 0
185
+ self._started_at: datetime | None = None
186
+ self._last_tick_at: datetime | None = None
187
+ self._last_tick_duration_ms: float = 0.0
188
+ self._total_tick_duration_ms: float = 0.0
189
+ self._max_tick_duration_ms: float = 0.0
190
+ self._ticks_emitted: int = 0
191
+ self._ticks_failed: int = 0
192
+ self._consecutive_failures: int = 0
193
+ self._last_persisted_sequence: int = 0
194
+
195
+ # Synchronization primitives
196
+ self._state_lock = asyncio.Lock()
197
+ self._shutdown_event = asyncio.Event()
198
+ self._tick_task: asyncio.Task[None] | None = None
199
+
200
+ # Valkey client for sequence number persistence
201
+ # Created lazily on first use to avoid blocking __init__
202
+ self._valkey_client: Redis | None = None
203
+ self._valkey_available: bool = True # Assume available until proven otherwise
204
+
205
+ # =========================================================================
206
+ # Properties (ProtocolRuntimeScheduler interface)
207
+ # =========================================================================
208
+
209
+ @property
210
+ def scheduler_id(self) -> str:
211
+ """Return the unique identifier for this scheduler instance.
212
+
213
+ Returns:
214
+ Unique scheduler identifier from configuration.
215
+ """
216
+ return self._config.scheduler_id
217
+
218
+ @property
219
+ def is_running(self) -> bool:
220
+ """Return whether the scheduler is currently running.
221
+
222
+ Returns:
223
+ True if running and emitting ticks, False otherwise.
224
+ """
225
+ return self._status == EnumSchedulerStatus.RUNNING
226
+
227
+ @property
228
+ def current_sequence_number(self) -> int:
229
+ """Return the current sequence number for restart-safety tracking.
230
+
231
+ Returns:
232
+ Current sequence number (non-negative).
233
+ """
234
+ return self._sequence_number
235
+
236
+ # =========================================================================
237
+ # Lifecycle Methods
238
+ # =========================================================================
239
+
240
+ async def start(self) -> None:
241
+ """Start the scheduler and begin emitting ticks.
242
+
243
+ Initializes the scheduler and starts the tick emission loop.
244
+ After calling start(), the scheduler will emit RuntimeTick events
245
+ at its configured interval.
246
+
247
+ Idempotency:
248
+ Calling start() on an already-running scheduler is a no-op
249
+ with a warning log.
250
+
251
+ Raises:
252
+ InfraUnavailableError: If the circuit breaker is open.
253
+ """
254
+ async with self._state_lock:
255
+ if self._status.is_active():
256
+ logger.warning(
257
+ "Scheduler already active, ignoring start()",
258
+ extra={
259
+ "scheduler_id": self.scheduler_id,
260
+ "status": str(self._status),
261
+ },
262
+ )
263
+ return
264
+
265
+ # Transition to STARTING
266
+ self._status = EnumSchedulerStatus.STARTING
267
+
268
+ # Check circuit breaker before starting (outside state lock)
269
+ async with self._circuit_breaker_lock:
270
+ try:
271
+ await self._check_circuit_breaker(
272
+ operation="start",
273
+ correlation_id=uuid4(),
274
+ )
275
+ except InfraUnavailableError:
276
+ async with self._state_lock:
277
+ self._status = EnumSchedulerStatus.ERROR
278
+ raise
279
+
280
+ # Load persisted sequence number (if enabled)
281
+ await self._load_sequence_number()
282
+
283
+ # Start tick loop
284
+ async with self._state_lock:
285
+ self._shutdown_event.clear()
286
+ self._started_at = datetime.now(UTC)
287
+ self._status = EnumSchedulerStatus.RUNNING
288
+ self._tick_task = asyncio.create_task(self._tick_loop())
289
+
290
+ logger.info(
291
+ "Scheduler started",
292
+ extra={
293
+ "scheduler_id": self.scheduler_id,
294
+ "tick_interval_ms": self._config.tick_interval_ms,
295
+ "max_jitter_ms": self._config.max_tick_jitter_ms,
296
+ },
297
+ )
298
+
299
+ async def stop(self) -> None:
300
+ """Stop the scheduler gracefully.
301
+
302
+ Performs a graceful shutdown of the scheduler:
303
+ - Signals the tick loop to stop
304
+ - Waits for any in-flight tick emission to complete
305
+ - Sets status to STOPPED
306
+ - Persists sequence number if enabled
307
+
308
+ Idempotency:
309
+ Calling stop() on an already-stopped scheduler is a no-op.
310
+ """
311
+ async with self._state_lock:
312
+ if self._status.is_terminal():
313
+ logger.debug(
314
+ "Scheduler already stopped, ignoring stop()",
315
+ extra={
316
+ "scheduler_id": self.scheduler_id,
317
+ "status": str(self._status),
318
+ },
319
+ )
320
+ return
321
+
322
+ # Transition to STOPPING
323
+ self._status = EnumSchedulerStatus.STOPPING
324
+
325
+ # Signal shutdown to tick loop
326
+ self._shutdown_event.set()
327
+
328
+ # Wait for tick task to complete
329
+ if self._tick_task is not None:
330
+ try:
331
+ await asyncio.wait_for(self._tick_task, timeout=5.0)
332
+ except TimeoutError:
333
+ logger.warning(
334
+ "Tick task did not complete within timeout, cancelling",
335
+ extra={"scheduler_id": self.scheduler_id},
336
+ )
337
+ self._tick_task.cancel()
338
+ try:
339
+ await self._tick_task
340
+ except asyncio.CancelledError:
341
+ pass
342
+ except asyncio.CancelledError:
343
+ pass
344
+ self._tick_task = None
345
+
346
+ # Persist sequence number (if enabled)
347
+ await self._persist_sequence_number()
348
+
349
+ # Finalize state
350
+ async with self._state_lock:
351
+ self._status = EnumSchedulerStatus.STOPPED
352
+
353
+ logger.info(
354
+ "Scheduler stopped",
355
+ extra={
356
+ "scheduler_id": self.scheduler_id,
357
+ "ticks_emitted": self._ticks_emitted,
358
+ "ticks_failed": self._ticks_failed,
359
+ "final_sequence": self._sequence_number,
360
+ },
361
+ )
362
+
363
+ # Always close Valkey client if it exists (idempotent)
364
+ await self._close_valkey_client()
365
+
366
+ # =========================================================================
367
+ # Core Methods
368
+ # =========================================================================
369
+
370
+ async def emit_tick(self, now: datetime | None = None) -> None:
371
+ """Emit a single tick immediately.
372
+
373
+ This method emits a RuntimeTick event outside the normal interval loop.
374
+ It is primarily used for testing and manual intervention.
375
+
376
+ Args:
377
+ now: Optional override for current time. If None, uses actual
378
+ current time (datetime.now(timezone.utc)).
379
+
380
+ Raises:
381
+ InfraUnavailableError: When the circuit breaker is open.
382
+ InfraTimeoutError: When tick emission to Kafka times out.
383
+ InfraConnectionError: When tick emission fails due to connection issues.
384
+
385
+ Concurrency Safety:
386
+ This method is safe for concurrent coroutine calls. State modifications
387
+ are protected by `_state_lock` (asyncio.Lock).
388
+ """
389
+ tick_time = now or datetime.now(UTC)
390
+ correlation_id = uuid4()
391
+ tick_id = uuid4()
392
+ start_time = time.monotonic()
393
+
394
+ # Increment sequence number (protected)
395
+ async with self._state_lock:
396
+ self._sequence_number += 1
397
+ current_sequence = self._sequence_number
398
+
399
+ # Create tick event
400
+ tick = ModelRuntimeTick(
401
+ now=tick_time,
402
+ tick_id=tick_id,
403
+ sequence_number=current_sequence,
404
+ scheduled_at=tick_time,
405
+ correlation_id=correlation_id,
406
+ scheduler_id=self.scheduler_id,
407
+ tick_interval_ms=self._config.tick_interval_ms,
408
+ )
409
+
410
+ # Check circuit breaker before publishing
411
+ async with self._circuit_breaker_lock:
412
+ try:
413
+ await self._check_circuit_breaker(
414
+ operation="emit_tick",
415
+ correlation_id=correlation_id,
416
+ )
417
+ except InfraUnavailableError:
418
+ await self._record_tick_failure(correlation_id)
419
+ raise
420
+
421
+ # Create headers for the event
422
+ headers = ModelEventHeaders(
423
+ correlation_id=correlation_id,
424
+ message_id=tick_id,
425
+ timestamp=tick_time,
426
+ source=f"runtime-scheduler.{self.scheduler_id}",
427
+ event_type="runtime.tick.v1",
428
+ )
429
+
430
+ # Serialize tick to JSON bytes
431
+ tick_bytes = tick.model_dump_json().encode("utf-8")
432
+
433
+ # Prepare error context for ONEX error types
434
+ ctx = ModelInfraErrorContext(
435
+ transport_type=EnumInfraTransportType.KAFKA,
436
+ operation="emit_tick",
437
+ target_name=self._config.tick_topic,
438
+ correlation_id=correlation_id,
439
+ )
440
+
441
+ try:
442
+ # Publish tick event
443
+ await self._event_bus.publish(
444
+ topic=self._config.tick_topic,
445
+ key=self.scheduler_id.encode("utf-8"),
446
+ value=tick_bytes,
447
+ headers=headers,
448
+ )
449
+
450
+ # Record success
451
+ async with self._circuit_breaker_lock:
452
+ await self._reset_circuit_breaker()
453
+
454
+ await self._record_tick_success(start_time, tick_time)
455
+
456
+ logger.debug(
457
+ "Tick emitted",
458
+ extra={
459
+ "scheduler_id": self.scheduler_id,
460
+ "sequence_number": current_sequence,
461
+ "tick_id": str(tick_id),
462
+ "correlation_id": str(correlation_id),
463
+ },
464
+ )
465
+
466
+ except TimeoutError as e:
467
+ # Record failure for timeout
468
+ async with self._circuit_breaker_lock:
469
+ await self._record_circuit_failure(
470
+ operation="emit_tick",
471
+ correlation_id=correlation_id,
472
+ )
473
+
474
+ await self._record_tick_failure(correlation_id)
475
+
476
+ timeout_ctx = ModelTimeoutErrorContext(
477
+ transport_type=EnumInfraTransportType.KAFKA,
478
+ operation="emit_tick",
479
+ target_name=self._config.tick_topic,
480
+ correlation_id=correlation_id,
481
+ # timeout_seconds omitted - Kafka timeout is event bus level, not available here
482
+ )
483
+ raise InfraTimeoutError(
484
+ f"Timeout emitting tick to topic {self._config.tick_topic}",
485
+ context=timeout_ctx,
486
+ ) from e
487
+
488
+ except Exception as e:
489
+ # Record failure for other errors
490
+ async with self._circuit_breaker_lock:
491
+ await self._record_circuit_failure(
492
+ operation="emit_tick",
493
+ correlation_id=correlation_id,
494
+ )
495
+
496
+ await self._record_tick_failure(correlation_id)
497
+
498
+ raise InfraConnectionError(
499
+ f"Failed to emit tick to topic {self._config.tick_topic}",
500
+ context=ctx,
501
+ ) from e
502
+
503
+ async def get_metrics(self) -> ModelRuntimeSchedulerMetrics:
504
+ """Get current scheduler metrics.
505
+
506
+ Returns a snapshot of the scheduler's operational metrics for
507
+ observability and monitoring purposes.
508
+
509
+ Returns:
510
+ ModelRuntimeSchedulerMetrics: Current metrics snapshot.
511
+
512
+ Concurrency Safety:
513
+ This method acquires both ``_circuit_breaker_lock`` and ``_state_lock``
514
+ (asyncio.Lock instances) to ensure a consistent snapshot of all metrics.
515
+ Circuit breaker state is read under its own lock first (consistent with
516
+ modification patterns), then scheduler state is read under the state lock.
517
+ The returned Pydantic model is immutable and safe to use after locks are
518
+ released. Note: This is coroutine-safe, not thread-safe.
519
+
520
+ Example:
521
+ >>> scheduler = RuntimeScheduler(config=config, event_bus=event_bus)
522
+ >>> await scheduler.start()
523
+ >>> # After some ticks have been emitted...
524
+ >>> metrics = await scheduler.get_metrics()
525
+ >>> print(f"Scheduler: {metrics.scheduler_id}")
526
+ >>> print(f"Status: {metrics.status}")
527
+ >>> print(f"Ticks emitted: {metrics.ticks_emitted}")
528
+ >>> print(f"Ticks failed: {metrics.ticks_failed}")
529
+ >>> print(f"Success rate: {metrics.tick_success_rate()}")
530
+ >>> print(f"Average tick duration: {metrics.average_tick_duration_ms}ms")
531
+ >>> print(f"Circuit breaker open: {metrics.circuit_breaker_open}")
532
+ >>> print(f"Consecutive failures: {metrics.consecutive_failures}")
533
+ >>> print(f"Uptime: {metrics.total_uptime_seconds}s")
534
+ >>> if metrics.is_healthy():
535
+ ... print("Scheduler is healthy")
536
+ """
537
+ # First, capture circuit breaker state under its own lock
538
+ # This ensures consistency with how _circuit_breaker_open is modified
539
+ async with self._circuit_breaker_lock:
540
+ circuit_breaker_open = self._circuit_breaker_open
541
+
542
+ # Then capture scheduler state under state lock
543
+ async with self._state_lock:
544
+ # Calculate uptime
545
+ uptime_seconds = 0.0
546
+ if self._started_at is not None:
547
+ uptime_seconds = (datetime.now(UTC) - self._started_at).total_seconds()
548
+
549
+ # Calculate average tick duration
550
+ average_tick_duration_ms = 0.0
551
+ if self._ticks_emitted > 0:
552
+ average_tick_duration_ms = (
553
+ self._total_tick_duration_ms / self._ticks_emitted
554
+ )
555
+
556
+ return ModelRuntimeSchedulerMetrics(
557
+ scheduler_id=self.scheduler_id,
558
+ status=self._status,
559
+ ticks_emitted=self._ticks_emitted,
560
+ ticks_failed=self._ticks_failed,
561
+ last_tick_at=self._last_tick_at,
562
+ last_tick_duration_ms=self._last_tick_duration_ms,
563
+ average_tick_duration_ms=average_tick_duration_ms,
564
+ max_tick_duration_ms=self._max_tick_duration_ms,
565
+ current_sequence_number=self._sequence_number,
566
+ last_persisted_sequence=self._last_persisted_sequence,
567
+ circuit_breaker_open=circuit_breaker_open,
568
+ consecutive_failures=self._consecutive_failures,
569
+ started_at=self._started_at,
570
+ total_uptime_seconds=uptime_seconds,
571
+ )
572
+
573
+ # =========================================================================
574
+ # Internal Methods
575
+ # =========================================================================
576
+
577
+ async def _tick_loop(self) -> None:
578
+ """Main tick loop that emits ticks at configured intervals.
579
+
580
+ This method runs as a background task and continuously emits ticks
581
+ until the shutdown event is set. It handles jitter and graceful
582
+ shutdown.
583
+ """
584
+ logger.debug(
585
+ "Tick loop started",
586
+ extra={
587
+ "scheduler_id": self.scheduler_id,
588
+ "tick_interval_ms": self._config.tick_interval_ms,
589
+ },
590
+ )
591
+
592
+ try:
593
+ while not self._shutdown_event.is_set():
594
+ # Calculate interval with jitter
595
+ interval_seconds = self._config.tick_interval_ms / 1000.0
596
+ if self._config.max_tick_jitter_ms > 0:
597
+ jitter_ms = random.randint(0, self._config.max_tick_jitter_ms)
598
+ interval_seconds += jitter_ms / 1000.0
599
+
600
+ # Wait for interval or shutdown
601
+ try:
602
+ await asyncio.wait_for(
603
+ self._shutdown_event.wait(),
604
+ timeout=interval_seconds,
605
+ )
606
+ # Shutdown event was set - exit loop
607
+ break
608
+ except TimeoutError:
609
+ # Timeout expired - time to emit tick
610
+ pass
611
+
612
+ # Check if we should still be running
613
+ if self._shutdown_event.is_set():
614
+ break
615
+
616
+ # Emit tick (errors are logged but don't crash the loop)
617
+ try:
618
+ await self.emit_tick()
619
+ except Exception as e:
620
+ # Log but don't crash the loop
621
+ logger.exception(
622
+ "Error in tick loop, continuing",
623
+ extra={
624
+ "scheduler_id": self.scheduler_id,
625
+ "error": str(e),
626
+ "error_type": type(e).__name__,
627
+ },
628
+ )
629
+ # Increment consecutive failures for monitoring
630
+ async with self._state_lock:
631
+ self._consecutive_failures += 1
632
+
633
+ except asyncio.CancelledError:
634
+ logger.info(
635
+ "Tick loop cancelled",
636
+ extra={"scheduler_id": self.scheduler_id},
637
+ )
638
+ raise
639
+
640
+ except Exception as e:
641
+ logger.exception(
642
+ "Unexpected error in tick loop",
643
+ extra={
644
+ "scheduler_id": self.scheduler_id,
645
+ "error": str(e),
646
+ },
647
+ )
648
+ async with self._state_lock:
649
+ self._status = EnumSchedulerStatus.ERROR
650
+
651
+ finally:
652
+ logger.debug(
653
+ "Tick loop exiting",
654
+ extra={
655
+ "scheduler_id": self.scheduler_id,
656
+ "ticks_emitted": self._ticks_emitted,
657
+ },
658
+ )
659
+
660
+ async def _record_tick_success(
661
+ self,
662
+ start_time: float,
663
+ tick_time: datetime,
664
+ ) -> None:
665
+ """Record a successful tick emission.
666
+
667
+ Args:
668
+ start_time: Monotonic time when tick started.
669
+ tick_time: The timestamp used in the tick.
670
+ """
671
+ duration_ms = (time.monotonic() - start_time) * 1000.0
672
+
673
+ async with self._state_lock:
674
+ self._ticks_emitted += 1
675
+ self._last_tick_at = tick_time
676
+ self._last_tick_duration_ms = duration_ms
677
+ self._total_tick_duration_ms += duration_ms
678
+ self._max_tick_duration_ms = max(duration_ms, self._max_tick_duration_ms)
679
+ self._consecutive_failures = 0
680
+
681
+ async def _record_tick_failure(self, correlation_id: UUID) -> None:
682
+ """Record a failed tick emission.
683
+
684
+ Args:
685
+ correlation_id: Correlation ID for tracing.
686
+ """
687
+ async with self._state_lock:
688
+ self._ticks_failed += 1
689
+ self._consecutive_failures += 1
690
+
691
+ # =========================================================================
692
+ # Valkey Persistence (for restart-safety)
693
+ # =========================================================================
694
+
695
+ async def _get_valkey_client(self) -> Redis | None:
696
+ """Get or create the Valkey client for sequence number persistence.
697
+
698
+ This method lazily creates a Valkey client on first use. If the client
699
+ has been marked as unavailable (due to connection failures), it returns
700
+ None without attempting to reconnect.
701
+
702
+ Returns:
703
+ Redis client instance if available, None if unavailable or disabled.
704
+
705
+ Note:
706
+ The client is created with the configured host, port, password, and
707
+ timeout settings. Connection failures are handled gracefully with
708
+ retry logic.
709
+ """
710
+ # Skip if persistence is disabled or Valkey was marked unavailable
711
+ if not self._config.persist_sequence_number:
712
+ return None
713
+
714
+ if not self._valkey_available:
715
+ return None
716
+
717
+ # Return existing client if already created
718
+ if self._valkey_client is not None:
719
+ return self._valkey_client
720
+
721
+ # Create new client with retry logic
722
+ correlation_id = uuid4()
723
+ retries = self._config.valkey_connection_retries
724
+
725
+ for attempt in range(retries + 1):
726
+ try:
727
+ self._valkey_client = redis.Redis(
728
+ host=self._config.valkey_host,
729
+ port=self._config.valkey_port,
730
+ password=self._config.valkey_password,
731
+ socket_timeout=self._config.valkey_timeout_seconds,
732
+ socket_connect_timeout=self._config.valkey_timeout_seconds,
733
+ decode_responses=True,
734
+ )
735
+
736
+ # Test connection with a ping
737
+ await asyncio.wait_for(
738
+ self._valkey_client.ping(),
739
+ timeout=self._config.valkey_timeout_seconds,
740
+ )
741
+
742
+ logger.info(
743
+ "Valkey client connected for sequence persistence",
744
+ extra={
745
+ "scheduler_id": self.scheduler_id,
746
+ "valkey_host": self._config.valkey_host,
747
+ "valkey_port": self._config.valkey_port,
748
+ "correlation_id": str(correlation_id),
749
+ },
750
+ )
751
+ return self._valkey_client
752
+
753
+ except (RedisConnectionError, RedisTimeoutError, TimeoutError) as e:
754
+ if attempt < retries:
755
+ # Calculate exponential backoff delay: 1s, 2s, 4s, 8s... max 60s
756
+ backoff_delay = min(1.0 * (2**attempt), 60.0)
757
+
758
+ logger.warning(
759
+ "Valkey connection attempt %d/%d failed, retrying in %.1fs",
760
+ attempt + 1,
761
+ retries + 1,
762
+ backoff_delay,
763
+ extra={
764
+ "scheduler_id": self.scheduler_id,
765
+ "valkey_host": self._config.valkey_host,
766
+ "valkey_port": self._config.valkey_port,
767
+ # SECURITY: Sanitize error to prevent credential exposure
768
+ "error": sanitize_error_string(str(e)),
769
+ "error_type": type(e).__name__,
770
+ "correlation_id": str(correlation_id),
771
+ "backoff_delay_seconds": backoff_delay,
772
+ },
773
+ )
774
+ await asyncio.sleep(backoff_delay)
775
+ else:
776
+ # All retries exhausted - mark as unavailable
777
+ self._valkey_available = False
778
+ self._valkey_client = None
779
+
780
+ logger.warning(
781
+ "Valkey unavailable after %d attempts, using in-memory",
782
+ retries + 1,
783
+ extra={
784
+ "scheduler_id": self.scheduler_id,
785
+ "valkey_host": self._config.valkey_host,
786
+ "valkey_port": self._config.valkey_port,
787
+ # SECURITY: Sanitize error to prevent credential exposure
788
+ "error": sanitize_error_string(str(e)),
789
+ "error_type": type(e).__name__,
790
+ "correlation_id": str(correlation_id),
791
+ },
792
+ )
793
+ return None
794
+
795
+ except RedisError as e:
796
+ # Unexpected Redis error - mark as unavailable
797
+ self._valkey_available = False
798
+ self._valkey_client = None
799
+
800
+ logger.warning(
801
+ "Valkey error during connection, using in-memory fallback",
802
+ extra={
803
+ "scheduler_id": self.scheduler_id,
804
+ "valkey_host": self._config.valkey_host,
805
+ "valkey_port": self._config.valkey_port,
806
+ # SECURITY: Sanitize error to prevent credential exposure
807
+ "error": sanitize_error_string(str(e)),
808
+ "error_type": type(e).__name__,
809
+ "correlation_id": str(correlation_id),
810
+ },
811
+ )
812
+ return None
813
+
814
+ return None
815
+
816
+ async def _close_valkey_client(self) -> None:
817
+ """Close the Valkey client connection.
818
+
819
+ This method gracefully closes the Valkey client connection if one exists.
820
+ It is called during scheduler shutdown to ensure proper resource cleanup.
821
+
822
+ Idempotency:
823
+ This method is safe to call multiple times. It atomically swaps the
824
+ client reference to None before attempting close, preventing double-close
825
+ scenarios even with concurrent coroutine access.
826
+ """
827
+ # Atomically swap client reference to None to prevent double-close
828
+ client = self._valkey_client
829
+ self._valkey_client = None
830
+
831
+ if client is not None:
832
+ try:
833
+ await client.aclose()
834
+ logger.debug(
835
+ "Valkey client closed",
836
+ extra={"scheduler_id": self.scheduler_id},
837
+ )
838
+ except Exception as e:
839
+ logger.warning(
840
+ "Error closing Valkey client",
841
+ extra={
842
+ "scheduler_id": self.scheduler_id,
843
+ # SECURITY: Sanitize error to prevent credential exposure
844
+ "error": sanitize_error_string(str(e)),
845
+ "error_type": type(e).__name__,
846
+ },
847
+ )
848
+
849
+ async def _load_sequence_number(self) -> None:
850
+ """Load persisted sequence number from Valkey for restart-safety.
851
+
852
+ Attempts to read the sequence number from Valkey using the configured
853
+ `sequence_number_key`. If Valkey is unavailable or the key doesn't exist,
854
+ gracefully falls back to starting from 0.
855
+
856
+ Graceful Fallback:
857
+ - If Valkey is unavailable: Logs warning and starts from 0
858
+ - If key doesn't exist: Logs debug and starts from 0
859
+ - If value is not a valid integer: Logs warning and starts from 0
860
+
861
+ Note:
862
+ This method is called during start() if `persist_sequence_number`
863
+ is enabled in configuration.
864
+ """
865
+ if not self._config.persist_sequence_number:
866
+ logger.debug(
867
+ "Sequence number persistence disabled, starting from 0",
868
+ extra={"scheduler_id": self.scheduler_id},
869
+ )
870
+ return
871
+
872
+ correlation_id = uuid4()
873
+ client = await self._get_valkey_client()
874
+
875
+ if client is None:
876
+ logger.warning(
877
+ "Valkey unavailable for sequence load, starting from 0",
878
+ extra={
879
+ "scheduler_id": self.scheduler_id,
880
+ "sequence_key": self._config.sequence_number_key,
881
+ "correlation_id": str(correlation_id),
882
+ },
883
+ )
884
+ return
885
+
886
+ try:
887
+ # Read sequence number from Valkey
888
+ value = await asyncio.wait_for(
889
+ client.get(self._config.sequence_number_key),
890
+ timeout=self._config.valkey_timeout_seconds,
891
+ )
892
+
893
+ if value is None:
894
+ # Key doesn't exist - this is a fresh start
895
+ logger.debug(
896
+ "No persisted sequence number found, starting from 0",
897
+ extra={
898
+ "scheduler_id": self.scheduler_id,
899
+ "sequence_key": self._config.sequence_number_key,
900
+ "correlation_id": str(correlation_id),
901
+ },
902
+ )
903
+ return
904
+
905
+ # Parse the sequence number
906
+ try:
907
+ loaded_sequence = int(value)
908
+ if loaded_sequence < 0:
909
+ logger.warning(
910
+ "Persisted sequence number is negative, starting from 0",
911
+ extra={
912
+ "scheduler_id": self.scheduler_id,
913
+ "sequence_key": self._config.sequence_number_key,
914
+ "persisted_value": value,
915
+ "correlation_id": str(correlation_id),
916
+ },
917
+ )
918
+ return
919
+
920
+ # Successfully loaded - update state
921
+ async with self._state_lock:
922
+ self._sequence_number = loaded_sequence
923
+ self._last_persisted_sequence = loaded_sequence
924
+
925
+ logger.info(
926
+ "Loaded persisted sequence number",
927
+ extra={
928
+ "scheduler_id": self.scheduler_id,
929
+ "sequence_number": loaded_sequence,
930
+ "sequence_key": self._config.sequence_number_key,
931
+ "correlation_id": str(correlation_id),
932
+ },
933
+ )
934
+
935
+ except ValueError:
936
+ logger.warning(
937
+ "Persisted sequence number is not a valid integer, starting from 0",
938
+ extra={
939
+ "scheduler_id": self.scheduler_id,
940
+ "sequence_key": self._config.sequence_number_key,
941
+ "persisted_value": value,
942
+ "correlation_id": str(correlation_id),
943
+ },
944
+ )
945
+
946
+ except (RedisConnectionError, RedisTimeoutError, TimeoutError) as e:
947
+ # Connection failed during operation - mark unavailable
948
+ self._valkey_available = False
949
+
950
+ logger.warning(
951
+ "Valkey connection failed during sequence load, starting from 0",
952
+ extra={
953
+ "scheduler_id": self.scheduler_id,
954
+ "sequence_key": self._config.sequence_number_key,
955
+ # SECURITY: Sanitize error to prevent credential exposure
956
+ "error": sanitize_error_string(str(e)),
957
+ "error_type": type(e).__name__,
958
+ "correlation_id": str(correlation_id),
959
+ },
960
+ )
961
+
962
+ except RedisError as e:
963
+ logger.warning(
964
+ "Valkey error during sequence load, starting from 0",
965
+ extra={
966
+ "scheduler_id": self.scheduler_id,
967
+ "sequence_key": self._config.sequence_number_key,
968
+ # SECURITY: Sanitize error to prevent credential exposure
969
+ "error": sanitize_error_string(str(e)),
970
+ "error_type": type(e).__name__,
971
+ "correlation_id": str(correlation_id),
972
+ },
973
+ )
974
+
975
+ async def _persist_sequence_number(self) -> None:
976
+ """Persist current sequence number to Valkey for restart-safety.
977
+
978
+ Writes the current sequence number to Valkey using the configured
979
+ `sequence_number_key`. If Valkey is unavailable, gracefully logs a
980
+ warning and continues without persistence.
981
+
982
+ Graceful Fallback:
983
+ - If Valkey is unavailable: Logs warning and skips persistence
984
+ - If write fails: Logs warning with error details
985
+
986
+ Note:
987
+ This method is called during stop() if `persist_sequence_number`
988
+ is enabled in configuration.
989
+ """
990
+ if not self._config.persist_sequence_number:
991
+ return
992
+
993
+ correlation_id = uuid4()
994
+ client = await self._get_valkey_client()
995
+
996
+ if client is None:
997
+ # Valkey unavailable - log but don't fail shutdown
998
+ # Note: _last_persisted_sequence is NOT updated because persistence failed
999
+ logger.warning(
1000
+ "Valkey unavailable for sequence persistence",
1001
+ extra={
1002
+ "scheduler_id": self.scheduler_id,
1003
+ "sequence_number": self._sequence_number,
1004
+ "sequence_key": self._config.sequence_number_key,
1005
+ "correlation_id": str(correlation_id),
1006
+ },
1007
+ )
1008
+ return
1009
+
1010
+ try:
1011
+ # Write sequence number to Valkey
1012
+ await asyncio.wait_for(
1013
+ client.set(
1014
+ self._config.sequence_number_key,
1015
+ str(self._sequence_number),
1016
+ ),
1017
+ timeout=self._config.valkey_timeout_seconds,
1018
+ )
1019
+
1020
+ self._last_persisted_sequence = self._sequence_number
1021
+
1022
+ logger.info(
1023
+ "Persisted sequence number to Valkey",
1024
+ extra={
1025
+ "scheduler_id": self.scheduler_id,
1026
+ "sequence_number": self._sequence_number,
1027
+ "sequence_key": self._config.sequence_number_key,
1028
+ "correlation_id": str(correlation_id),
1029
+ },
1030
+ )
1031
+
1032
+ except (RedisConnectionError, RedisTimeoutError, TimeoutError) as e:
1033
+ # Connection failed during operation - log but don't fail shutdown
1034
+ # Note: _last_persisted_sequence is NOT updated because persistence failed
1035
+ self._valkey_available = False
1036
+
1037
+ logger.warning(
1038
+ "Valkey connection failed during sequence persistence",
1039
+ extra={
1040
+ "scheduler_id": self.scheduler_id,
1041
+ "sequence_number": self._sequence_number,
1042
+ "sequence_key": self._config.sequence_number_key,
1043
+ # SECURITY: Sanitize error to prevent credential exposure
1044
+ "error": sanitize_error_string(str(e)),
1045
+ "error_type": type(e).__name__,
1046
+ "correlation_id": str(correlation_id),
1047
+ },
1048
+ )
1049
+
1050
+ except RedisError as e:
1051
+ # Note: _last_persisted_sequence is NOT updated because persistence failed
1052
+ logger.warning(
1053
+ "Valkey error during sequence persistence",
1054
+ extra={
1055
+ "scheduler_id": self.scheduler_id,
1056
+ "sequence_number": self._sequence_number,
1057
+ "sequence_key": self._config.sequence_number_key,
1058
+ # SECURITY: Sanitize error to prevent credential exposure
1059
+ "error": sanitize_error_string(str(e)),
1060
+ "error_type": type(e).__name__,
1061
+ "correlation_id": str(correlation_id),
1062
+ },
1063
+ )
1064
+
1065
+ finally:
1066
+ # Close the Valkey client during shutdown
1067
+ await self._close_valkey_client()
1068
+
1069
+
1070
+ __all__: list[str] = ["RuntimeScheduler"]