omnibase_infra 0.2.6__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 (833) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/__init__.py +1 -0
  8. omnibase_infra/cli/commands.py +216 -0
  9. omnibase_infra/clients/__init__.py +0 -0
  10. omnibase_infra/configs/widget_mapping.yaml +176 -0
  11. omnibase_infra/constants_topic_patterns.py +26 -0
  12. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
  13. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
  14. omnibase_infra/decorators/__init__.py +29 -0
  15. omnibase_infra/decorators/allow_any.py +109 -0
  16. omnibase_infra/dlq/__init__.py +90 -0
  17. omnibase_infra/dlq/constants_dlq.py +57 -0
  18. omnibase_infra/dlq/models/__init__.py +26 -0
  19. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  20. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  21. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  22. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  23. omnibase_infra/enums/__init__.py +132 -0
  24. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  25. omnibase_infra/enums/enum_backend_type.py +27 -0
  26. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  27. omnibase_infra/enums/enum_capture_state.py +88 -0
  28. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  29. omnibase_infra/enums/enum_circuit_state.py +51 -0
  30. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  31. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  32. omnibase_infra/enums/enum_contract_type.py +84 -0
  33. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  34. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  35. omnibase_infra/enums/enum_environment.py +46 -0
  36. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  37. omnibase_infra/enums/enum_handler_error_type.py +111 -0
  38. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  39. omnibase_infra/enums/enum_handler_source_mode.py +86 -0
  40. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  41. omnibase_infra/enums/enum_handler_type.py +77 -0
  42. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  43. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  44. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  45. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  46. omnibase_infra/enums/enum_message_category.py +213 -0
  47. omnibase_infra/enums/enum_node_archetype.py +74 -0
  48. omnibase_infra/enums/enum_node_output_type.py +185 -0
  49. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  50. omnibase_infra/enums/enum_policy_type.py +32 -0
  51. omnibase_infra/enums/enum_registration_state.py +261 -0
  52. omnibase_infra/enums/enum_registration_status.py +33 -0
  53. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  54. omnibase_infra/enums/enum_response_status.py +26 -0
  55. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  56. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  57. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  58. omnibase_infra/enums/enum_topic_standard.py +42 -0
  59. omnibase_infra/enums/enum_validation_severity.py +78 -0
  60. omnibase_infra/errors/__init__.py +160 -0
  61. omnibase_infra/errors/error_architecture_violation.py +152 -0
  62. omnibase_infra/errors/error_binding_resolution.py +128 -0
  63. omnibase_infra/errors/error_chain_propagation.py +188 -0
  64. omnibase_infra/errors/error_compute_registry.py +95 -0
  65. omnibase_infra/errors/error_consul.py +132 -0
  66. omnibase_infra/errors/error_container_wiring.py +243 -0
  67. omnibase_infra/errors/error_event_bus_registry.py +105 -0
  68. omnibase_infra/errors/error_infra.py +610 -0
  69. omnibase_infra/errors/error_message_type_registry.py +101 -0
  70. omnibase_infra/errors/error_policy_registry.py +115 -0
  71. omnibase_infra/errors/error_vault.py +123 -0
  72. omnibase_infra/event_bus/__init__.py +72 -0
  73. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
  74. omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
  75. omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
  76. omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
  77. omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
  78. omnibase_infra/event_bus/models/__init__.py +29 -0
  79. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  80. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
  81. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  82. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  83. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  84. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  85. omnibase_infra/event_bus/testing/__init__.py +26 -0
  86. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  87. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  88. omnibase_infra/event_bus/topic_constants.py +376 -0
  89. omnibase_infra/handlers/__init__.py +82 -0
  90. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  91. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  92. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  93. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  94. omnibase_infra/handlers/handler_consul.py +795 -0
  95. omnibase_infra/handlers/handler_db.py +1046 -0
  96. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  97. omnibase_infra/handlers/handler_graph.py +2015 -0
  98. omnibase_infra/handlers/handler_http.py +926 -0
  99. omnibase_infra/handlers/handler_intent.py +387 -0
  100. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  101. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  102. omnibase_infra/handlers/handler_mcp.py +1430 -0
  103. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  104. omnibase_infra/handlers/handler_vault.py +428 -0
  105. omnibase_infra/handlers/mcp/__init__.py +19 -0
  106. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  107. omnibase_infra/handlers/mcp/protocols.py +178 -0
  108. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  109. omnibase_infra/handlers/mixins/__init__.py +47 -0
  110. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  111. omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
  112. omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
  113. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  114. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  115. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  116. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  117. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  118. omnibase_infra/handlers/models/__init__.py +286 -0
  119. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  120. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  121. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  122. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  123. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  124. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  125. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  126. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  127. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  128. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  129. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  130. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  131. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  132. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  133. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  134. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  135. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  136. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  137. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  138. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  139. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  140. omnibase_infra/handlers/models/http/__init__.py +50 -0
  141. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  142. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  143. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  144. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  145. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  146. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  147. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  148. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  149. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  150. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  151. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  152. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  153. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  154. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  155. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  156. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  157. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  158. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  159. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  160. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  161. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  162. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  163. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  164. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  165. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  166. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  167. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  168. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  169. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  170. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  171. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  172. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  173. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  174. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  175. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  176. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  177. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  178. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  179. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  180. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  181. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  182. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  183. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  184. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  185. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  186. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  187. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  188. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  189. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  190. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  191. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  192. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  193. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  194. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  195. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  196. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  197. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  198. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  199. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  200. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  201. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  202. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  203. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  204. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  205. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  206. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  207. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
  208. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  209. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  210. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  211. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  212. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  213. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
  214. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  215. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  216. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  217. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  218. omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
  219. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  220. omnibase_infra/idempotency/__init__.py +94 -0
  221. omnibase_infra/idempotency/models/__init__.py +43 -0
  222. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  223. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  224. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  225. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  226. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  227. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  228. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  229. omnibase_infra/idempotency/store_inmemory.py +265 -0
  230. omnibase_infra/idempotency/store_postgres.py +923 -0
  231. omnibase_infra/infrastructure/__init__.py +0 -0
  232. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  233. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  234. omnibase_infra/mixins/__init__.py +71 -0
  235. omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
  236. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  237. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  238. omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
  239. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  240. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  241. omnibase_infra/models/__init__.py +144 -0
  242. omnibase_infra/models/bindings/__init__.py +59 -0
  243. omnibase_infra/models/bindings/constants.py +144 -0
  244. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  245. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  246. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  247. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  248. omnibase_infra/models/corpus/__init__.py +17 -0
  249. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  250. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  251. omnibase_infra/models/discovery/__init__.py +42 -0
  252. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  253. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  254. omnibase_infra/models/discovery/model_introspection_config.py +330 -0
  255. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  256. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  257. omnibase_infra/models/dispatch/__init__.py +155 -0
  258. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  259. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  260. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  261. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  262. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  263. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  264. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  265. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  266. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  267. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  268. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  269. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  270. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  271. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  272. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  273. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  274. omnibase_infra/models/errors/__init__.py +45 -0
  275. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  276. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  277. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  278. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  279. omnibase_infra/models/handlers/__init__.py +80 -0
  280. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  281. omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
  282. omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
  283. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  284. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  285. omnibase_infra/models/health/__init__.py +9 -0
  286. omnibase_infra/models/health/model_health_check_result.py +40 -0
  287. omnibase_infra/models/lifecycle/__init__.py +39 -0
  288. omnibase_infra/models/logging/__init__.py +51 -0
  289. omnibase_infra/models/logging/model_log_context.py +756 -0
  290. omnibase_infra/models/mcp/__init__.py +15 -0
  291. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  292. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  293. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  294. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  295. omnibase_infra/models/model_node_identity.py +126 -0
  296. omnibase_infra/models/model_retry_error_classification.py +78 -0
  297. omnibase_infra/models/projection/__init__.py +43 -0
  298. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  299. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  300. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  301. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  302. omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
  303. omnibase_infra/models/projectors/__init__.py +41 -0
  304. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  305. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  306. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  307. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  308. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  309. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  310. omnibase_infra/models/registration/__init__.py +68 -0
  311. omnibase_infra/models/registration/commands/__init__.py +15 -0
  312. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  313. omnibase_infra/models/registration/events/__init__.py +56 -0
  314. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  315. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  316. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  317. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  318. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  319. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  320. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  321. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  322. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  323. omnibase_infra/models/registration/model_node_capabilities.py +190 -0
  324. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  325. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  326. omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
  327. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  328. omnibase_infra/models/registration/model_node_registration.py +162 -0
  329. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  330. omnibase_infra/models/registry/__init__.py +29 -0
  331. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  332. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  333. omnibase_infra/models/resilience/__init__.py +9 -0
  334. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  335. omnibase_infra/models/routing/__init__.py +25 -0
  336. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  337. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  338. omnibase_infra/models/runtime/__init__.py +49 -0
  339. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  340. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  341. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  342. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  343. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  344. omnibase_infra/models/runtime/model_handler_contract.py +296 -0
  345. omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
  346. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  347. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  348. omnibase_infra/models/security/__init__.py +50 -0
  349. omnibase_infra/models/security/classification_levels.py +99 -0
  350. omnibase_infra/models/security/model_environment_policy.py +145 -0
  351. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  352. omnibase_infra/models/security/model_security_error.py +81 -0
  353. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  354. omnibase_infra/models/security/model_security_warning.py +67 -0
  355. omnibase_infra/models/snapshot/__init__.py +27 -0
  356. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  357. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  358. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  359. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  360. omnibase_infra/models/types/__init__.py +71 -0
  361. omnibase_infra/models/validation/__init__.py +89 -0
  362. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  363. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  364. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  365. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  366. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  367. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  368. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  369. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  370. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  371. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  372. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  373. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  374. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  375. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  376. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  377. omnibase_infra/nodes/__init__.py +57 -0
  378. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  379. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  380. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
  381. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  382. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  383. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  384. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  385. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  386. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  387. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  388. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  389. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  390. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  391. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  392. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  393. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  394. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  395. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
  396. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  397. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  398. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  399. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  400. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  401. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  402. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  403. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  404. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  405. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  406. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  407. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  408. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  409. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  410. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  411. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  412. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  413. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  414. omnibase_infra/nodes/effects/README.md +358 -0
  415. omnibase_infra/nodes/effects/__init__.py +26 -0
  416. omnibase_infra/nodes/effects/contract.yaml +167 -0
  417. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  418. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  419. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  420. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  421. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  422. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  423. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  424. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  425. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  426. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  427. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  428. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  429. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  430. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  431. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  432. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  433. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  434. omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
  435. omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
  436. omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
  437. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
  438. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
  439. omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
  440. omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
  441. omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
  442. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  443. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  444. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  445. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  446. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  447. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  448. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  449. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  450. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  451. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  452. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  453. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  454. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  455. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  456. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  457. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  458. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  459. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  460. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  461. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  462. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  463. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  464. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
  465. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  466. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  467. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  468. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  469. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  470. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  471. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
  472. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  473. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  474. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  475. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  476. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  477. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  478. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  479. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  480. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  481. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  482. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  483. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  484. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  485. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  486. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  487. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  488. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  489. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  490. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  491. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  492. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  493. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
  494. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
  495. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
  496. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  497. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  498. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  499. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  500. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  501. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  502. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  503. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  504. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
  505. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  506. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  507. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  508. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  509. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  510. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  511. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  512. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  513. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  514. omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
  515. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  516. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  517. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  518. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
  519. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  520. omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
  521. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  522. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  523. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  524. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
  525. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  526. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  527. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  528. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  529. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  530. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  531. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  532. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  533. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  534. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  535. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  536. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  537. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  538. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  539. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  540. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  541. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  542. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  543. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  544. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  545. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  546. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  547. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  548. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  549. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  550. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
  551. omnibase_infra/nodes/reducers/__init__.py +30 -0
  552. omnibase_infra/nodes/reducers/models/__init__.py +37 -0
  553. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
  554. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  555. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  556. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  557. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  558. omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
  559. omnibase_infra/observability/__init__.py +143 -0
  560. omnibase_infra/observability/constants_metrics.py +91 -0
  561. omnibase_infra/observability/factory_observability_sink.py +525 -0
  562. omnibase_infra/observability/handlers/__init__.py +118 -0
  563. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  564. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  565. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  566. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  567. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  568. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  569. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  570. omnibase_infra/observability/hooks/__init__.py +74 -0
  571. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  572. omnibase_infra/observability/models/__init__.py +30 -0
  573. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  574. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  575. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  576. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  577. omnibase_infra/observability/sinks/__init__.py +69 -0
  578. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  579. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  580. omnibase_infra/plugins/__init__.py +27 -0
  581. omnibase_infra/plugins/examples/__init__.py +28 -0
  582. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  583. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  584. omnibase_infra/plugins/models/__init__.py +21 -0
  585. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  586. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  587. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  588. omnibase_infra/plugins/plugin_compute_base.py +449 -0
  589. omnibase_infra/projectors/__init__.py +30 -0
  590. omnibase_infra/projectors/contracts/__init__.py +63 -0
  591. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  592. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  593. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  594. omnibase_infra/protocols/__init__.py +104 -0
  595. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  596. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  597. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  598. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  599. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  600. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  601. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  602. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  603. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  604. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  605. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  606. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  607. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  608. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  609. omnibase_infra/runtime/__init__.py +445 -0
  610. omnibase_infra/runtime/binding_config_resolver.py +2771 -0
  611. omnibase_infra/runtime/binding_resolver.py +753 -0
  612. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  613. omnibase_infra/runtime/constants_notification.py +75 -0
  614. omnibase_infra/runtime/constants_security.py +70 -0
  615. omnibase_infra/runtime/contract_handler_discovery.py +587 -0
  616. omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
  617. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  618. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  619. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  620. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  621. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  622. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  623. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  624. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  625. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  626. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  627. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  628. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  629. omnibase_infra/runtime/enums/__init__.py +18 -0
  630. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  631. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  632. omnibase_infra/runtime/envelope_validator.py +179 -0
  633. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  634. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  635. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  636. omnibase_infra/runtime/handler_contract_source.py +750 -0
  637. omnibase_infra/runtime/handler_identity.py +81 -0
  638. omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
  639. omnibase_infra/runtime/handler_registry.py +329 -0
  640. omnibase_infra/runtime/handler_source_resolver.py +367 -0
  641. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  642. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  643. omnibase_infra/runtime/kernel.py +40 -0
  644. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  645. omnibase_infra/runtime/mixin_semver_cache.py +402 -0
  646. omnibase_infra/runtime/mixins/__init__.py +24 -0
  647. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  648. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
  649. omnibase_infra/runtime/models/__init__.py +229 -0
  650. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  651. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  652. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  653. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  654. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  655. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  656. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  657. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  658. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  659. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  660. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  661. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  662. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  663. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  664. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  665. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  666. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  667. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  668. omnibase_infra/runtime/models/model_health_check_result.py +229 -0
  669. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  670. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  671. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  672. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  673. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  674. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  675. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  676. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  677. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  678. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  679. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  680. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  681. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  682. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  683. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  684. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  685. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
  686. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  687. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  688. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  689. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  690. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  691. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  692. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  693. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  694. omnibase_infra/runtime/models/model_security_config.py +109 -0
  695. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  696. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  697. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  698. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  699. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  700. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  701. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  702. omnibase_infra/runtime/projector_shell.py +1330 -0
  703. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  704. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  705. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  706. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  707. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  708. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  709. omnibase_infra/runtime/protocol_policy.py +366 -0
  710. omnibase_infra/runtime/protocols/__init__.py +37 -0
  711. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  712. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  713. omnibase_infra/runtime/registry/__init__.py +93 -0
  714. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  715. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  716. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  717. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  718. omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
  719. omnibase_infra/runtime/registry_compute.py +1143 -0
  720. omnibase_infra/runtime/registry_contract_source.py +693 -0
  721. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  722. omnibase_infra/runtime/registry_policy.py +1185 -0
  723. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  724. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  725. omnibase_infra/runtime/secret_resolver.py +2112 -0
  726. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  727. omnibase_infra/runtime/service_kernel.py +1651 -0
  728. omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
  729. omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
  730. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  731. omnibase_infra/runtime/transition_notification_publisher.py +765 -0
  732. omnibase_infra/runtime/util_container_wiring.py +1124 -0
  733. omnibase_infra/runtime/util_validation.py +314 -0
  734. omnibase_infra/runtime/util_version.py +98 -0
  735. omnibase_infra/runtime/util_wiring.py +723 -0
  736. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  737. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  738. omnibase_infra/services/__init__.py +89 -0
  739. omnibase_infra/services/corpus_capture.py +684 -0
  740. omnibase_infra/services/mcp/__init__.py +31 -0
  741. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  742. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  743. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  744. omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
  745. omnibase_infra/services/registry_api/__init__.py +40 -0
  746. omnibase_infra/services/registry_api/main.py +261 -0
  747. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  748. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  749. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  750. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  751. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  752. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  753. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  754. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  755. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  756. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  757. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  758. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  759. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  760. omnibase_infra/services/registry_api/routes.py +371 -0
  761. omnibase_infra/services/registry_api/service.py +837 -0
  762. omnibase_infra/services/service_capability_query.py +945 -0
  763. omnibase_infra/services/service_health.py +898 -0
  764. omnibase_infra/services/service_node_selector.py +530 -0
  765. omnibase_infra/services/service_timeout_emitter.py +699 -0
  766. omnibase_infra/services/service_timeout_scanner.py +394 -0
  767. omnibase_infra/services/session/__init__.py +56 -0
  768. omnibase_infra/services/session/config_consumer.py +137 -0
  769. omnibase_infra/services/session/config_store.py +139 -0
  770. omnibase_infra/services/session/consumer.py +1007 -0
  771. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  772. omnibase_infra/services/session/store.py +997 -0
  773. omnibase_infra/services/snapshot/__init__.py +31 -0
  774. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  775. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  776. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  777. omnibase_infra/shared/__init__.py +8 -0
  778. omnibase_infra/testing/__init__.py +10 -0
  779. omnibase_infra/testing/utils.py +23 -0
  780. omnibase_infra/topics/__init__.py +45 -0
  781. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  782. omnibase_infra/topics/util_topic_composition.py +95 -0
  783. omnibase_infra/types/__init__.py +48 -0
  784. omnibase_infra/types/type_cache_info.py +49 -0
  785. omnibase_infra/types/type_dsn.py +173 -0
  786. omnibase_infra/types/type_infra_aliases.py +60 -0
  787. omnibase_infra/types/typed_dict/__init__.py +29 -0
  788. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  789. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  790. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  791. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  792. omnibase_infra/utils/__init__.py +117 -0
  793. omnibase_infra/utils/correlation.py +208 -0
  794. omnibase_infra/utils/util_atomic_file.py +261 -0
  795. omnibase_infra/utils/util_consumer_group.py +232 -0
  796. omnibase_infra/utils/util_datetime.py +372 -0
  797. omnibase_infra/utils/util_db_transaction.py +239 -0
  798. omnibase_infra/utils/util_dsn_validation.py +333 -0
  799. omnibase_infra/utils/util_env_parsing.py +264 -0
  800. omnibase_infra/utils/util_error_sanitization.py +457 -0
  801. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  802. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  803. omnibase_infra/utils/util_semver.py +233 -0
  804. omnibase_infra/validation/__init__.py +307 -0
  805. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  806. omnibase_infra/validation/enums/__init__.py +11 -0
  807. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  808. omnibase_infra/validation/infra_validators.py +1514 -0
  809. omnibase_infra/validation/linter_contract.py +907 -0
  810. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  811. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  812. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  813. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  814. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  815. omnibase_infra/validation/models/__init__.py +15 -0
  816. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  817. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  818. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  819. omnibase_infra/validation/validation_exemptions.yaml +2033 -0
  820. omnibase_infra/validation/validator_any_type.py +715 -0
  821. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  822. omnibase_infra/validation/validator_execution_shape.py +465 -0
  823. omnibase_infra/validation/validator_localhandler.py +261 -0
  824. omnibase_infra/validation/validator_registration_security.py +410 -0
  825. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  826. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  827. omnibase_infra/validation/validator_security.py +513 -0
  828. omnibase_infra/validation/validator_topic_category.py +1152 -0
  829. omnibase_infra-0.2.6.dist-info/METADATA +197 -0
  830. omnibase_infra-0.2.6.dist-info/RECORD +833 -0
  831. omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
  832. omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
  833. omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1651 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """ONEX Kernel - Minimal bootstrap for contract-driven runtime.
4
+
5
+ This is the kernel entrypoint for the ONEX runtime. It provides a contract-driven
6
+ bootstrap that wires configuration into the existing RuntimeHostProcess.
7
+
8
+ The kernel is responsible for:
9
+ 1. Loading runtime configuration from contracts or environment
10
+ 2. Creating and starting the event bus (EventBusInmemory or EventBusKafka)
11
+ 3. Building the dependency container (event_bus, config)
12
+ 4. Instantiating RuntimeHostProcess with contract-driven configuration
13
+ 5. Starting the HTTP health server for Docker/K8s probes
14
+ 6. Setting up graceful shutdown signal handlers
15
+ 7. Running the runtime until shutdown is requested
16
+
17
+ Event Bus Selection:
18
+ The kernel supports two event bus implementations:
19
+ - EventBusInmemory: For local development and testing (default)
20
+ - EventBusKafka: For production use with Kafka/Redpanda
21
+
22
+ Selection is determined by:
23
+ - KAFKA_BOOTSTRAP_SERVERS environment variable (if set, uses Kafka)
24
+ - config.event_bus.type field in runtime_config.yaml
25
+
26
+ Usage:
27
+ # Run with default contracts directory (./contracts)
28
+ python -m omnibase_infra.runtime.service_kernel
29
+
30
+ # Run with custom contracts directory
31
+ ONEX_CONTRACTS_DIR=/path/to/contracts python -m omnibase_infra.runtime.service_kernel
32
+
33
+ # Or via the installed entrypoint
34
+ onex-runtime
35
+
36
+ Environment Variables:
37
+ ONEX_CONTRACTS_DIR: Path to contracts directory (default: ./contracts)
38
+ ONEX_HTTP_PORT: Port for health check HTTP server (default: 8085)
39
+ ONEX_LOG_LEVEL: Logging level (default: INFO)
40
+ ONEX_ENVIRONMENT: Runtime environment name (default: local)
41
+
42
+ Note:
43
+ This kernel uses the existing RuntimeHostProcess as the core runtime engine.
44
+ A future refactor may integrate NodeOrchestrator as the primary execution
45
+ engine, but for MVP this lean kernel provides contract-driven bootstrap
46
+ with minimal risk and maximum reuse of tested code.
47
+ """
48
+
49
+ from __future__ import annotations
50
+
51
+ import asyncio
52
+ import logging
53
+ import os
54
+ import signal
55
+ import sys
56
+ import time
57
+ from collections.abc import Awaitable, Callable
58
+ from importlib.metadata import version as get_package_version
59
+ from pathlib import Path
60
+ from typing import cast
61
+ from uuid import UUID
62
+
63
+ import asyncpg
64
+ import yaml
65
+ from pydantic import ValidationError
66
+
67
+ from omnibase_core.container import ModelONEXContainer
68
+ from omnibase_infra.enums import EnumConsumerGroupPurpose, EnumInfraTransportType
69
+ from omnibase_infra.errors import (
70
+ ModelInfraErrorContext,
71
+ ProtocolConfigurationError,
72
+ RuntimeHostError,
73
+ ServiceResolutionError,
74
+ )
75
+ from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
76
+ from omnibase_infra.event_bus.event_bus_kafka import EventBusKafka
77
+ from omnibase_infra.event_bus.models.config import ModelKafkaEventBusConfig
78
+ from omnibase_infra.models import ModelNodeIdentity
79
+ from omnibase_infra.nodes.node_registration_orchestrator.dispatchers import (
80
+ DispatcherNodeIntrospected,
81
+ )
82
+ from omnibase_infra.nodes.node_registration_orchestrator.introspection_event_router import (
83
+ IntrospectionEventRouter,
84
+ )
85
+ from omnibase_infra.runtime.handler_registry import RegistryProtocolBinding
86
+ from omnibase_infra.runtime.models import (
87
+ ModelProjectorPluginLoaderConfig,
88
+ ModelRuntimeConfig,
89
+ )
90
+ from omnibase_infra.runtime.projector_plugin_loader import (
91
+ ProjectorPluginLoader,
92
+ ProjectorShell,
93
+ ProtocolEventProjector,
94
+ )
95
+ from omnibase_infra.runtime.service_runtime_host_process import RuntimeHostProcess
96
+ from omnibase_infra.runtime.util_container_wiring import (
97
+ wire_infrastructure_services,
98
+ wire_registration_handlers,
99
+ )
100
+
101
+ # Circular Import Note (OMN-529):
102
+ # ---------------------------------
103
+ # ServiceHealth and DEFAULT_HTTP_PORT are imported inside bootstrap() rather than
104
+ # at module level to avoid a circular import. The import chain is:
105
+ #
106
+ # 1. omnibase_infra/runtime/__init__.py imports kernel_bootstrap from kernel.py
107
+ # 2. If kernel.py imported ServiceHealth at module level, it would load service_health.py
108
+ # 3. service_health.py imports ModelHealthCheckResponse from runtime.models
109
+ # 4. This triggers initialization of omnibase_infra.runtime package (step 1)
110
+ # 5. Runtime package tries to import kernel.py which is still initializing -> circular!
111
+ #
112
+ # The lazy import in bootstrap() is acceptable because:
113
+ # - ServiceHealth is only instantiated at runtime, not at import time
114
+ # - Type checking uses forward references (no import needed)
115
+ # - No import-time side effects are bypassed
116
+ # - The omnibase_infra.services.__init__.py already excludes ServiceHealth exports
117
+ # to prevent accidental circular imports from other modules
118
+ #
119
+ # See also: omnibase_infra/services/__init__.py "ServiceHealth Import Guide" section
120
+ from omnibase_infra.runtime.util_validation import validate_runtime_config
121
+ from omnibase_infra.utils.correlation import generate_correlation_id
122
+ from omnibase_infra.utils.util_error_sanitization import sanitize_error_message
123
+
124
+ logger = logging.getLogger(__name__)
125
+
126
+ # Kernel version - read from installed package metadata to avoid version drift
127
+ # between code and pyproject.toml. Falls back to "unknown" if package is not
128
+ # installed (e.g., during development without editable install).
129
+ try:
130
+ KERNEL_VERSION = get_package_version("omnibase_infra")
131
+ except Exception:
132
+ KERNEL_VERSION = "unknown"
133
+
134
+ # Default configuration
135
+ DEFAULT_CONTRACTS_DIR = "./contracts"
136
+ DEFAULT_RUNTIME_CONFIG = "runtime/runtime_config.yaml"
137
+
138
+ # Environment variable name for contracts directory
139
+ ENV_CONTRACTS_DIR = "ONEX_CONTRACTS_DIR"
140
+ DEFAULT_INPUT_TOPIC = "requests"
141
+ DEFAULT_OUTPUT_TOPIC = "responses"
142
+ DEFAULT_GROUP_ID = "onex-runtime"
143
+
144
+ # Port validation constants
145
+ MIN_PORT = 1
146
+ MAX_PORT = 65535
147
+
148
+
149
+ def _get_contracts_dir() -> Path:
150
+ """Get contracts directory from environment.
151
+
152
+ Reads the ONEX_CONTRACTS_DIR environment variable. If not set,
153
+ returns the default contracts directory.
154
+
155
+ Returns:
156
+ Path to the contracts directory.
157
+ """
158
+ onex_value = os.environ.get(ENV_CONTRACTS_DIR)
159
+ if onex_value:
160
+ return Path(onex_value)
161
+
162
+ return Path(DEFAULT_CONTRACTS_DIR)
163
+
164
+
165
+ def load_runtime_config(
166
+ contracts_dir: Path,
167
+ correlation_id: UUID | None = None,
168
+ ) -> ModelRuntimeConfig:
169
+ """Load runtime configuration from contract file or return defaults.
170
+
171
+ Attempts to load runtime_config.yaml from the contracts directory.
172
+ If the file doesn't exist, returns sensible defaults to allow
173
+ the runtime to start without requiring a config file.
174
+
175
+ Configuration Loading Process:
176
+ 1. Check for runtime_config.yaml in contracts directory
177
+ 2. If found, parse YAML and validate against ModelRuntimeConfig schema
178
+ 3. If not found, construct config from environment variables and defaults
179
+ 4. Return fully validated configuration model
180
+
181
+ Configuration Precedence:
182
+ - File-based config is returned as-is when present (no environment overrides)
183
+ - Environment variables are only used when no config file exists
184
+ - Defaults are used when neither file nor environment variables are set
185
+ - Note: Environment overrides (e.g., ONEX_ENVIRONMENT) are applied by the
186
+ caller (bootstrap), not by this function
187
+
188
+ Args:
189
+ contracts_dir: Path to the contracts directory containing runtime_config.yaml.
190
+ Example: Path("./contracts") or Path("/app/contracts")
191
+ correlation_id: Optional correlation ID for distributed tracing. If not
192
+ provided, a new one will be generated. Passing a correlation_id from
193
+ the caller (e.g., bootstrap) ensures consistent tracing across the
194
+ initialization sequence.
195
+
196
+ Returns:
197
+ ModelRuntimeConfig: Fully validated configuration model with runtime settings.
198
+ Contains event bus configuration, topic names, consumer group, shutdown
199
+ behavior, and logging configuration.
200
+
201
+ Raises:
202
+ ProtocolConfigurationError: If config file exists but cannot be parsed,
203
+ fails validation, or cannot be read due to filesystem errors. Error
204
+ includes correlation_id for tracing and detailed context for debugging.
205
+
206
+ Example:
207
+ >>> contracts_dir = Path("./contracts")
208
+ >>> config = load_runtime_config(contracts_dir)
209
+ >>> print(config.input_topic)
210
+ requests
211
+ >>> print(config.event_bus.type)
212
+ inmemory
213
+
214
+ Example Error:
215
+ >>> # If runtime_config.yaml has invalid YAML syntax
216
+ >>> load_runtime_config(Path("./invalid"))
217
+ ProtocolConfigurationError: Failed to parse runtime config YAML at ./invalid/runtime/runtime_config.yaml
218
+ (correlation_id: 123e4567-e89b-12d3-a456-426614174000)
219
+ """
220
+ config_path = contracts_dir / DEFAULT_RUNTIME_CONFIG
221
+ # Use passed correlation_id for consistent tracing, or generate new one
222
+ effective_correlation_id = correlation_id or generate_correlation_id()
223
+ context = ModelInfraErrorContext(
224
+ transport_type=EnumInfraTransportType.RUNTIME,
225
+ operation="load_config",
226
+ target_name=str(config_path),
227
+ correlation_id=effective_correlation_id,
228
+ )
229
+
230
+ if config_path.exists():
231
+ logger.info(
232
+ "Loading runtime config from %s (correlation_id=%s)",
233
+ config_path,
234
+ effective_correlation_id,
235
+ )
236
+ try:
237
+ with config_path.open(encoding="utf-8") as f:
238
+ raw_config = yaml.safe_load(f) or {}
239
+
240
+ # Type guard: reject non-mapping YAML payloads
241
+ # yaml.safe_load() can return list, str, int, etc. for valid YAML
242
+ # but runtime config must be a dict (mapping) for model validation
243
+ if not isinstance(raw_config, dict):
244
+ raise ProtocolConfigurationError(
245
+ f"Runtime config at {config_path} must be a YAML mapping (dict), "
246
+ f"got {type(raw_config).__name__}",
247
+ context=context,
248
+ config_path=str(config_path),
249
+ error_details=f"Expected dict, got {type(raw_config).__name__}",
250
+ )
251
+
252
+ # Contract validation: validate against schema before Pydantic
253
+ # This provides early, actionable error messages for pattern/range violations
254
+ contract_errors = validate_runtime_config(raw_config)
255
+ if contract_errors:
256
+ error_count = len(contract_errors)
257
+ # Create concise summary for log message (first 3 errors)
258
+ error_summary = "; ".join(contract_errors[:3])
259
+ if error_count > 3:
260
+ error_summary += f" (and {error_count - 3} more...)"
261
+ raise ProtocolConfigurationError(
262
+ f"Contract validation failed at {config_path}: {error_count} error(s). "
263
+ f"First errors: {error_summary}",
264
+ context=context,
265
+ config_path=str(config_path),
266
+ # Full error list for structured debugging (not truncated)
267
+ validation_errors=contract_errors,
268
+ error_count=error_count,
269
+ )
270
+ logger.debug(
271
+ "Contract validation passed (correlation_id=%s)",
272
+ effective_correlation_id,
273
+ )
274
+
275
+ config = ModelRuntimeConfig.model_validate(raw_config)
276
+ logger.debug(
277
+ "Runtime config loaded successfully (correlation_id=%s)",
278
+ effective_correlation_id,
279
+ extra={
280
+ "input_topic": config.input_topic,
281
+ "output_topic": config.output_topic,
282
+ "consumer_group": config.consumer_group,
283
+ "event_bus_type": config.event_bus.type,
284
+ },
285
+ )
286
+ return config
287
+ except yaml.YAMLError as e:
288
+ raise ProtocolConfigurationError(
289
+ f"Failed to parse runtime config YAML at {config_path}: {e}",
290
+ context=context,
291
+ config_path=str(config_path),
292
+ error_details=str(e),
293
+ ) from e
294
+ except ValidationError as e:
295
+ # Extract validation error details for actionable error messages
296
+ error_count = e.error_count()
297
+ # Convert Pydantic errors to list[str] for consistency with contract validation
298
+ # Both validation_errors fields should have the same type: list[str]
299
+ pydantic_errors = [
300
+ f"{'.'.join(str(loc) for loc in err['loc'])}: {err['msg']}"
301
+ for err in e.errors()
302
+ ]
303
+ error_summary = "; ".join(pydantic_errors[:3])
304
+ raise ProtocolConfigurationError(
305
+ f"Runtime config validation failed at {config_path}: {error_count} error(s). "
306
+ f"First errors: {error_summary}",
307
+ context=context,
308
+ config_path=str(config_path),
309
+ validation_errors=pydantic_errors,
310
+ error_count=error_count,
311
+ ) from e
312
+ except UnicodeDecodeError as e:
313
+ raise ProtocolConfigurationError(
314
+ f"Runtime config file contains binary or non-UTF-8 content: {config_path}",
315
+ context=context,
316
+ config_path=str(config_path),
317
+ error_details=f"Encoding error at position {e.start}-{e.end}: {e.reason}",
318
+ ) from e
319
+ except OSError as e:
320
+ raise ProtocolConfigurationError(
321
+ f"Failed to read runtime config at {config_path}: {e}",
322
+ context=context,
323
+ config_path=str(config_path),
324
+ error_details=str(e),
325
+ ) from e
326
+
327
+ # No config file - use environment variables and defaults
328
+ logger.info(
329
+ "No runtime config found at %s, using environment/defaults (correlation_id=%s)",
330
+ config_path,
331
+ effective_correlation_id,
332
+ )
333
+ config = ModelRuntimeConfig(
334
+ input_topic=os.getenv("ONEX_INPUT_TOPIC", DEFAULT_INPUT_TOPIC),
335
+ output_topic=os.getenv("ONEX_OUTPUT_TOPIC", DEFAULT_OUTPUT_TOPIC),
336
+ consumer_group=os.getenv("ONEX_GROUP_ID", DEFAULT_GROUP_ID),
337
+ )
338
+ logger.debug(
339
+ "Runtime config constructed from environment/defaults (correlation_id=%s)",
340
+ effective_correlation_id,
341
+ extra={
342
+ "input_topic": config.input_topic,
343
+ "output_topic": config.output_topic,
344
+ "consumer_group": config.consumer_group,
345
+ },
346
+ )
347
+ return config
348
+
349
+
350
+ async def bootstrap() -> int:
351
+ """Bootstrap the ONEX runtime from contracts.
352
+
353
+ This is the main async entrypoint that orchestrates the complete runtime
354
+ initialization and lifecycle management. The bootstrap process follows a
355
+ structured sequence to ensure proper resource initialization and cleanup.
356
+
357
+ Bootstrap Sequence:
358
+ 1. Determine contracts directory from ONEX_CONTRACTS_DIR environment variable
359
+ 2. Load and validate runtime configuration from contracts or environment
360
+ 3. Create and initialize event bus (EventBusInmemory or EventBusKafka based on config)
361
+ 4. Create ModelONEXContainer and wire infrastructure services (async)
362
+ 5. Resolve RegistryProtocolBinding from container (async)
363
+ 6. Instantiate RuntimeHostProcess with validated configuration and pre-resolved registry
364
+ 7. Setup graceful shutdown signal handlers (SIGINT, SIGTERM)
365
+ 8. Start runtime and HTTP health server for Docker/Kubernetes health probes
366
+ 9. Run runtime until shutdown signal received
367
+ 10. Perform graceful shutdown with configurable timeout
368
+ 11. Clean up resources in finally block to prevent resource leaks
369
+
370
+ Error Handling:
371
+ - Configuration errors: Logged with full context and correlation_id
372
+ - Runtime errors: Caught and logged with detailed error information
373
+ - Unexpected errors: Logged with exception details for debugging
374
+ - All errors include correlation_id for distributed tracing
375
+
376
+ Shutdown Behavior:
377
+ - Health server stopped first (fast, non-blocking operation)
378
+ - Runtime stopped with configurable grace period (default: 30s)
379
+ - Timeout enforcement prevents indefinite shutdown hangs
380
+ - Finally block ensures cleanup even on unexpected errors
381
+
382
+ Returns:
383
+ Exit code (0 for success, non-zero for errors).
384
+ - 0: Clean shutdown after successful operation
385
+ - 1: Configuration error, runtime error, or unexpected failure
386
+
387
+ Environment Variables:
388
+ ONEX_CONTRACTS_DIR: Path to contracts directory (default: ./contracts)
389
+ ONEX_HTTP_PORT: Port for health check server (default: 8085)
390
+ ONEX_LOG_LEVEL: Logging level (default: INFO)
391
+ ONEX_ENVIRONMENT: Environment name (default: local)
392
+ ONEX_INPUT_TOPIC: Input topic override (default: requests)
393
+ ONEX_OUTPUT_TOPIC: Output topic override (default: responses)
394
+ ONEX_GROUP_ID: Consumer group override (default: onex-runtime)
395
+
396
+ Example:
397
+ >>> # Run bootstrap and handle exit code
398
+ >>> exit_code = await bootstrap()
399
+ >>> if exit_code == 0:
400
+ ... print("Runtime shutdown successfully")
401
+ ... else:
402
+ ... print("Runtime encountered errors")
403
+
404
+ Example Startup Log:
405
+ ============================================================
406
+ ONEX Runtime Kernel v0.1.0
407
+ Environment: production
408
+ Contracts: /app/contracts
409
+ Event Bus: inmemory (group: onex-runtime)
410
+ Topics: requests → responses
411
+ Health endpoint: http://0.0.0.0:8085/health
412
+ ============================================================
413
+ """
414
+ # Lazy import to break circular dependency chain - see "Circular Import Note"
415
+ # comment near line 98 for detailed explanation of the import cycle.
416
+ from omnibase_infra.services.service_health import (
417
+ DEFAULT_HTTP_PORT,
418
+ ServiceHealth,
419
+ )
420
+
421
+ # Initialize resources to None for cleanup guard in finally block
422
+ runtime: RuntimeHostProcess | None = None
423
+ health_server: ServiceHealth | None = None
424
+ postgres_pool: asyncpg.Pool | None = None
425
+ introspection_unsubscribe: Callable[[], Awaitable[None]] | None = None
426
+ correlation_id = generate_correlation_id()
427
+ bootstrap_start_time = time.time()
428
+
429
+ try:
430
+ # 1. Determine contracts directory
431
+ contracts_dir = _get_contracts_dir()
432
+ logger.info(
433
+ "ONEX Kernel starting with contracts_dir=%s (correlation_id=%s)",
434
+ contracts_dir,
435
+ correlation_id,
436
+ )
437
+
438
+ # 2. Load runtime configuration (may raise ProtocolConfigurationError)
439
+ # Pass correlation_id for consistent tracing across initialization sequence
440
+ config_start_time = time.time()
441
+ config = load_runtime_config(contracts_dir, correlation_id=correlation_id)
442
+ config_duration = time.time() - config_start_time
443
+ # Log only safe config fields (no credentials or sensitive data)
444
+ # Full config.model_dump() could leak passwords, API keys, connection strings
445
+ logger.debug(
446
+ "Runtime config loaded in %.3fs (correlation_id=%s)",
447
+ config_duration,
448
+ correlation_id,
449
+ extra={
450
+ "duration_seconds": config_duration,
451
+ "input_topic": config.input_topic,
452
+ "output_topic": config.output_topic,
453
+ "consumer_group": config.consumer_group,
454
+ "event_bus_type": config.event_bus.type,
455
+ "shutdown_grace_period": config.shutdown.grace_period_seconds,
456
+ },
457
+ )
458
+
459
+ # 3. Create event bus
460
+ # Dispatch based on configuration or environment variable:
461
+ # - If KAFKA_BOOTSTRAP_SERVERS env var is set, use EventBusKafka
462
+ # - If config.event_bus.type == "kafka", use EventBusKafka
463
+ # - Otherwise, use EventBusInmemory for local development/testing
464
+ # Environment override takes precedence over config for environment field.
465
+ environment = os.getenv("ONEX_ENVIRONMENT") or config.event_bus.environment
466
+ kafka_bootstrap_servers = os.getenv("KAFKA_BOOTSTRAP_SERVERS")
467
+
468
+ # Explicit bool evaluation (not truthy string) for kafka usage.
469
+ # KAFKA_BOOTSTRAP_SERVERS env var takes precedence over config.event_bus.type.
470
+ # This prevents implicit "kafka but localhost" fallback scenarios.
471
+ use_kafka: bool = (
472
+ bool(kafka_bootstrap_servers) or config.event_bus.type == "kafka"
473
+ )
474
+
475
+ # Validate bootstrap_servers is provided when kafka is requested via config
476
+ # This prevents confusing implicit localhost:9092 fallback
477
+ if use_kafka and not kafka_bootstrap_servers:
478
+ context = ModelInfraErrorContext(
479
+ transport_type=EnumInfraTransportType.KAFKA,
480
+ operation="configure_event_bus",
481
+ correlation_id=correlation_id,
482
+ )
483
+ raise ProtocolConfigurationError(
484
+ "Kafka event bus requested (config.event_bus.type='kafka') but "
485
+ "KAFKA_BOOTSTRAP_SERVERS environment variable is not set. "
486
+ "Set KAFKA_BOOTSTRAP_SERVERS to the broker address (e.g., 'kafka:9092') "
487
+ "or use event_bus.type='inmemory' for local development.",
488
+ context=context,
489
+ parameter="KAFKA_BOOTSTRAP_SERVERS",
490
+ )
491
+
492
+ event_bus_start_time = time.time()
493
+ event_bus: EventBusInmemory | EventBusKafka
494
+ event_bus_type: str
495
+
496
+ if use_kafka:
497
+ # Use EventBusKafka for production/integration testing
498
+ # NOTE: bootstrap_servers is guaranteed non-empty at this point due to validation
499
+ # above, but mypy cannot narrow the Optional[str] type through control flow.
500
+ kafka_config = ModelKafkaEventBusConfig(
501
+ bootstrap_servers=kafka_bootstrap_servers, # type: ignore[arg-type] # NOTE: control flow narrowing limitation
502
+ environment=environment,
503
+ circuit_breaker_threshold=config.event_bus.circuit_breaker_threshold,
504
+ )
505
+ event_bus = EventBusKafka(config=kafka_config)
506
+ event_bus_type = "kafka"
507
+
508
+ # Start EventBusKafka to connect to Kafka/Redpanda and enable consumers
509
+ # Without this, the event bus cannot publish or consume messages
510
+ try:
511
+ await event_bus.start()
512
+ logger.debug(
513
+ "EventBusKafka started successfully (correlation_id=%s)",
514
+ correlation_id,
515
+ )
516
+ except Exception as e:
517
+ context = ModelInfraErrorContext(
518
+ transport_type=EnumInfraTransportType.KAFKA,
519
+ operation="start_event_bus",
520
+ correlation_id=correlation_id,
521
+ target_name=kafka_bootstrap_servers,
522
+ )
523
+ raise RuntimeHostError(
524
+ f"Failed to start EventBusKafka: {sanitize_error_message(e)}",
525
+ context=context,
526
+ ) from e
527
+
528
+ logger.info(
529
+ "Using EventBusKafka (correlation_id=%s)",
530
+ correlation_id,
531
+ extra={
532
+ "bootstrap_servers": kafka_bootstrap_servers,
533
+ "environment": environment,
534
+ "consumer_group": config.consumer_group,
535
+ },
536
+ )
537
+ else:
538
+ # Use EventBusInmemory for local development/testing
539
+ event_bus = EventBusInmemory(
540
+ environment=environment,
541
+ group=config.consumer_group,
542
+ )
543
+ event_bus_type = "inmemory"
544
+
545
+ event_bus_duration = time.time() - event_bus_start_time
546
+ logger.debug(
547
+ "Event bus created in %.3fs (correlation_id=%s)",
548
+ event_bus_duration,
549
+ correlation_id,
550
+ extra={
551
+ "duration_seconds": event_bus_duration,
552
+ "event_bus_type": event_bus_type,
553
+ "environment": environment,
554
+ "consumer_group": config.consumer_group,
555
+ },
556
+ )
557
+
558
+ # 4. Create and wire container for dependency injection
559
+ container_start_time = time.time()
560
+ container = ModelONEXContainer()
561
+ if container.service_registry is None:
562
+ logger.warning(
563
+ "DEGRADED_MODE: service_registry is None (omnibase_core circular import bug?), "
564
+ "skipping container wiring (correlation_id=%s)",
565
+ correlation_id,
566
+ extra={
567
+ "error_type": "NoneType",
568
+ "correlation_id": correlation_id,
569
+ "degraded_mode": True,
570
+ "degraded_reason": "service_registry_unavailable",
571
+ "component": "container_wiring",
572
+ },
573
+ )
574
+ wire_summary: dict[str, list[str] | str] = {
575
+ "services": [],
576
+ "status": "degraded",
577
+ } # Empty summary for degraded mode
578
+ else:
579
+ try:
580
+ wire_summary = await wire_infrastructure_services(container)
581
+ except ServiceResolutionError as e:
582
+ # Service resolution failed during wiring - container configuration issue.
583
+ logger.warning(
584
+ "DEGRADED_MODE: Container wiring failed due to service resolution error, "
585
+ "continuing in degraded mode (correlation_id=%s): %s",
586
+ correlation_id,
587
+ e,
588
+ extra={
589
+ "error_type": type(e).__name__,
590
+ "correlation_id": correlation_id,
591
+ "degraded_mode": True,
592
+ "degraded_reason": "service_resolution_error",
593
+ "component": "container_wiring",
594
+ },
595
+ )
596
+ wire_summary = {"services": [], "status": "degraded"}
597
+ except (RuntimeError, AttributeError) as e:
598
+ # Unexpected error during wiring - container internals issue.
599
+ logger.warning(
600
+ "DEGRADED_MODE: Container wiring failed with unexpected error, "
601
+ "continuing in degraded mode (correlation_id=%s): %s",
602
+ correlation_id,
603
+ e,
604
+ extra={
605
+ "error_type": type(e).__name__,
606
+ "correlation_id": correlation_id,
607
+ "degraded_mode": True,
608
+ "degraded_reason": "wiring_error",
609
+ "component": "container_wiring",
610
+ },
611
+ )
612
+ wire_summary = {"services": [], "status": "degraded"}
613
+ container_duration = time.time() - container_start_time
614
+ logger.debug(
615
+ "Container wired in %.3fs (correlation_id=%s)",
616
+ container_duration,
617
+ correlation_id,
618
+ extra={
619
+ "duration_seconds": container_duration,
620
+ "services": wire_summary["services"],
621
+ },
622
+ )
623
+
624
+ # 4.5. Create PostgreSQL pool for projections
625
+ # Only create if POSTGRES_HOST is set (indicates registration should be enabled)
626
+ projector: ProjectorShell | None = None
627
+ introspection_dispatcher: DispatcherNodeIntrospected | None = None
628
+ consul_handler = None # Will be initialized if Consul is configured
629
+
630
+ postgres_host = os.getenv("POSTGRES_HOST")
631
+ if postgres_host:
632
+ postgres_pool_start_time = time.time()
633
+ try:
634
+ postgres_pool = await asyncpg.create_pool(
635
+ user=os.getenv("POSTGRES_USER", "postgres"),
636
+ password=os.getenv("POSTGRES_PASSWORD", ""),
637
+ host=postgres_host,
638
+ port=int(os.getenv("POSTGRES_PORT", "5432")),
639
+ database=os.getenv("POSTGRES_DATABASE", "omninode_bridge"),
640
+ min_size=2,
641
+ max_size=10,
642
+ )
643
+ postgres_pool_duration = time.time() - postgres_pool_start_time
644
+ logger.info(
645
+ "PostgreSQL pool created in %.3fs (correlation_id=%s)",
646
+ postgres_pool_duration,
647
+ correlation_id,
648
+ extra={
649
+ "host": postgres_host,
650
+ "port": os.getenv("POSTGRES_PORT", "5432"),
651
+ "database": os.getenv("POSTGRES_DATABASE", "omninode_bridge"),
652
+ },
653
+ )
654
+
655
+ # 4.6. Load projectors from contracts via ProjectorPluginLoader (OMN-1170/1169)
656
+ #
657
+ # This section implements fully contract-driven projector management. The
658
+ # loader discovers projector contracts from the package's projectors/contracts
659
+ # directory and creates ProjectorShell instances for runtime use.
660
+ #
661
+ # Contract-driven approach:
662
+ # - Projector behavior defined in YAML contracts (registration_projector.yaml)
663
+ # - ProjectorPluginLoader discovers and loads projectors from contracts
664
+ # - ProjectorShell provides generic projection operations (project, partial_update)
665
+ # - Schema initialization is decoupled - SQL executed directly from schema file
666
+ #
667
+ # The registration projector is identified by projector_id="registration-projector"
668
+ # and is passed to wire_registration_handlers() for handler injection.
669
+ #
670
+ projector_contracts_dir = (
671
+ Path(__file__).parent.parent / "projectors" / "contracts"
672
+ )
673
+
674
+ # Try to discover projectors from contracts
675
+ projector_loader = ProjectorPluginLoader(
676
+ config=ModelProjectorPluginLoaderConfig(graceful_mode=True),
677
+ container=container,
678
+ pool=postgres_pool,
679
+ )
680
+
681
+ discovered_projectors: list[ProtocolEventProjector] = []
682
+ if projector_contracts_dir.exists():
683
+ try:
684
+ discovered_projectors = (
685
+ await projector_loader.load_from_directory(
686
+ projector_contracts_dir
687
+ )
688
+ )
689
+ if discovered_projectors:
690
+ logger.info(
691
+ "Discovered %d projector(s) from contracts (correlation_id=%s)",
692
+ len(discovered_projectors),
693
+ correlation_id,
694
+ extra={
695
+ "discovered_count": len(discovered_projectors),
696
+ "contracts_dir": str(projector_contracts_dir),
697
+ "projector_ids": [
698
+ getattr(p, "projector_id", "unknown")
699
+ for p in discovered_projectors
700
+ ],
701
+ },
702
+ )
703
+ else:
704
+ logger.warning(
705
+ "No projector contracts found in %s (correlation_id=%s)",
706
+ projector_contracts_dir,
707
+ correlation_id,
708
+ extra={
709
+ "contracts_dir": str(projector_contracts_dir),
710
+ },
711
+ )
712
+ except Exception as discovery_error:
713
+ # Log warning but continue - projector discovery is best-effort
714
+ # Registration features will be unavailable if discovery fails
715
+ logger.warning(
716
+ "Projector contract discovery failed: %s (correlation_id=%s)",
717
+ sanitize_error_message(discovery_error),
718
+ correlation_id,
719
+ extra={
720
+ "error_type": type(discovery_error).__name__,
721
+ "contracts_dir": str(projector_contracts_dir),
722
+ },
723
+ )
724
+ else:
725
+ logger.debug(
726
+ "Projector contracts directory not found, skipping discovery "
727
+ "(correlation_id=%s)",
728
+ correlation_id,
729
+ extra={
730
+ "contracts_dir": str(projector_contracts_dir),
731
+ },
732
+ )
733
+
734
+ # Extract registration projector from discovered projectors (OMN-1169)
735
+ # This replaces the legacy ProjectorRegistration with contract-loaded ProjectorShell
736
+ registration_projector_id = "registration-projector"
737
+ for discovered in discovered_projectors:
738
+ if (
739
+ getattr(discovered, "projector_id", None)
740
+ == registration_projector_id
741
+ ):
742
+ # Cast to ProjectorShell (loader creates ProjectorShell when pool provided)
743
+ if isinstance(discovered, ProjectorShell):
744
+ projector = discovered
745
+ logger.info(
746
+ "Using contract-loaded ProjectorShell for registration "
747
+ "(correlation_id=%s)",
748
+ correlation_id,
749
+ extra={
750
+ "projector_id": registration_projector_id,
751
+ "aggregate_type": projector.aggregate_type,
752
+ },
753
+ )
754
+ break
755
+
756
+ if projector is None:
757
+ # Fallback: No registration projector discovered from contracts
758
+ logger.warning(
759
+ "Registration projector not found in contracts, "
760
+ "registration features will be unavailable (correlation_id=%s)",
761
+ correlation_id,
762
+ extra={
763
+ "expected_projector_id": registration_projector_id,
764
+ "discovered_count": len(discovered_projectors),
765
+ },
766
+ )
767
+
768
+ # Initialize schema by executing SQL file directly
769
+ # Schema initialization is decoupled from the projector - it just ensures
770
+ # the table and indexes exist. The ProjectorShell uses the schema at runtime.
771
+ schema_file = (
772
+ Path(__file__).parent.parent
773
+ / "schemas"
774
+ / "schema_registration_projection.sql"
775
+ )
776
+ if schema_file.exists():
777
+ try:
778
+ schema_sql = schema_file.read_text()
779
+ async with postgres_pool.acquire() as conn:
780
+ await conn.execute(schema_sql)
781
+ logger.info(
782
+ "Registration projection schema initialized (correlation_id=%s)",
783
+ correlation_id,
784
+ )
785
+ except Exception as schema_error:
786
+ # Log warning but continue - schema may already exist
787
+ logger.warning(
788
+ "Schema initialization encountered error: %s (correlation_id=%s)",
789
+ sanitize_error_message(schema_error),
790
+ correlation_id,
791
+ extra={
792
+ "error_type": type(schema_error).__name__,
793
+ },
794
+ )
795
+ else:
796
+ logger.warning(
797
+ "Schema file not found: %s (correlation_id=%s)",
798
+ schema_file,
799
+ correlation_id,
800
+ )
801
+
802
+ # 4.6.5. Initialize HandlerConsul if Consul is configured
803
+ # CONSUL_HOST determines whether to enable Consul registration
804
+ consul_host = os.getenv("CONSUL_HOST")
805
+ if consul_host:
806
+ # Validate CONSUL_PORT environment variable
807
+ consul_port_str = os.getenv("CONSUL_PORT", "8500")
808
+ try:
809
+ consul_port = int(consul_port_str)
810
+ if not MIN_PORT <= consul_port <= MAX_PORT:
811
+ logger.warning(
812
+ "CONSUL_PORT %d outside valid range %d-%d, using default 8500 (correlation_id=%s)",
813
+ consul_port,
814
+ MIN_PORT,
815
+ MAX_PORT,
816
+ correlation_id,
817
+ )
818
+ consul_port = 8500
819
+ except ValueError:
820
+ logger.warning(
821
+ "Invalid CONSUL_PORT value '%s', using default 8500 (correlation_id=%s)",
822
+ consul_port_str,
823
+ correlation_id,
824
+ )
825
+ consul_port = 8500
826
+
827
+ try:
828
+ # Deferred import: Only load HandlerConsul when Consul is configured.
829
+ # This avoids loading the consul dependency (and its transitive deps)
830
+ # when Consul integration is disabled, reducing startup time.
831
+ from omnibase_infra.handlers import HandlerConsul
832
+
833
+ consul_handler = HandlerConsul(container)
834
+ await consul_handler.initialize(
835
+ {
836
+ "host": consul_host,
837
+ "port": consul_port,
838
+ }
839
+ )
840
+ logger.info(
841
+ "HandlerConsul initialized for dual registration (correlation_id=%s)",
842
+ correlation_id,
843
+ extra={
844
+ "consul_host": consul_host,
845
+ "consul_port": consul_port,
846
+ },
847
+ )
848
+ except Exception as consul_error:
849
+ # Log warning but continue without Consul (PostgreSQL is source of truth)
850
+ # Use sanitize_error_message to prevent credential leakage in logs
851
+ logger.warning(
852
+ "Failed to initialize HandlerConsul, proceeding without Consul: %s (correlation_id=%s)",
853
+ sanitize_error_message(consul_error),
854
+ correlation_id,
855
+ extra={
856
+ "error_type": type(consul_error).__name__,
857
+ },
858
+ )
859
+ consul_handler = None
860
+ else:
861
+ logger.debug(
862
+ "CONSUL_HOST not set, Consul registration disabled (correlation_id=%s)",
863
+ correlation_id,
864
+ )
865
+
866
+ # 4.7. Wire registration handlers with projector and consul_handler
867
+ registration_summary = await wire_registration_handlers(
868
+ container,
869
+ postgres_pool,
870
+ projector=projector,
871
+ consul_handler=consul_handler,
872
+ )
873
+ logger.info(
874
+ "Registration handlers wired (correlation_id=%s)",
875
+ correlation_id,
876
+ extra={
877
+ "services": registration_summary["services"],
878
+ },
879
+ )
880
+
881
+ # 4.8. Create introspection dispatcher for routing events
882
+ # Deferred import: HandlerNodeIntrospected depends on PostgreSQL and
883
+ # registration infrastructure. Only loaded after PostgreSQL pool is
884
+ # successfully created and registration handlers are wired.
885
+ from omnibase_infra.nodes.node_registration_orchestrator.handlers import (
886
+ HandlerNodeIntrospected,
887
+ )
888
+
889
+ # Check if service_registry is available (may be None in omnibase_core 0.6.x)
890
+ if container.service_registry is None:
891
+ logger.warning(
892
+ "DEGRADED_MODE: ServiceRegistry not available, skipping introspection dispatcher creation (correlation_id=%s)",
893
+ correlation_id,
894
+ extra={
895
+ "error_type": "NoneType",
896
+ "correlation_id": correlation_id,
897
+ "degraded_mode": True,
898
+ "degraded_reason": "service_registry_unavailable",
899
+ "component": "introspection_dispatcher",
900
+ },
901
+ )
902
+ # Set introspection_dispatcher to None and continue without it
903
+ introspection_dispatcher = None
904
+ else:
905
+ logger.debug(
906
+ "Resolving HandlerNodeIntrospected from container (correlation_id=%s)",
907
+ correlation_id,
908
+ )
909
+ handler_introspected: HandlerNodeIntrospected = (
910
+ await container.service_registry.resolve_service(
911
+ HandlerNodeIntrospected
912
+ )
913
+ )
914
+ logger.debug(
915
+ "HandlerNodeIntrospected resolved successfully (correlation_id=%s)",
916
+ correlation_id,
917
+ extra={
918
+ "handler_class": handler_introspected.__class__.__name__,
919
+ },
920
+ )
921
+
922
+ introspection_dispatcher = DispatcherNodeIntrospected(
923
+ handler_introspected
924
+ )
925
+ logger.info(
926
+ "Introspection dispatcher created and wired (correlation_id=%s)",
927
+ correlation_id,
928
+ extra={
929
+ "dispatcher_class": introspection_dispatcher.__class__.__name__,
930
+ "handler_class": handler_introspected.__class__.__name__,
931
+ },
932
+ )
933
+
934
+ except Exception as pool_error:
935
+ # Log warning but continue without registration support
936
+ # Use sanitize_error_message to prevent credential leakage in logs
937
+ # (PostgreSQL connection errors may include DSN with password)
938
+ logger.warning(
939
+ "Failed to initialize PostgreSQL pool for registration: %s (correlation_id=%s)",
940
+ sanitize_error_message(pool_error),
941
+ correlation_id,
942
+ extra={
943
+ "error_type": type(pool_error).__name__,
944
+ },
945
+ )
946
+ if postgres_pool is not None:
947
+ try:
948
+ await postgres_pool.close()
949
+ except Exception as cleanup_error:
950
+ # Sanitize cleanup errors to prevent credential leakage
951
+ # NOTE: Do NOT use exc_info=True here - tracebacks may contain
952
+ # connection strings with credentials from PostgreSQL errors
953
+ logger.warning(
954
+ "Cleanup failed for PostgreSQL pool close: %s (correlation_id=%s)",
955
+ sanitize_error_message(cleanup_error),
956
+ correlation_id,
957
+ )
958
+ postgres_pool = None
959
+ projector = None
960
+ introspection_dispatcher = None
961
+ else:
962
+ logger.debug(
963
+ "POSTGRES_HOST not set, skipping registration handler wiring (correlation_id=%s)",
964
+ correlation_id,
965
+ )
966
+
967
+ # 5. Resolve RegistryProtocolBinding from container or create new instance
968
+ # NOTE: Fallback to creating new instance is intentional degraded mode behavior.
969
+ # The handler registry is optional for basic runtime operation - core event
970
+ # processing continues even without explicit handler bindings. However,
971
+ # ProtocolConfigurationError should NOT be masked as it indicates invalid
972
+ # configuration that would cause undefined behavior.
973
+ handler_registry: RegistryProtocolBinding | None = None
974
+
975
+ # Check if service_registry is available (may be None in omnibase_core 0.6.x)
976
+ if container.service_registry is not None:
977
+ try:
978
+ handler_registry = await container.service_registry.resolve_service(
979
+ RegistryProtocolBinding
980
+ )
981
+ except ServiceResolutionError as e:
982
+ # Service not registered - expected in minimal configurations.
983
+ # Create a new instance directly as fallback.
984
+ logger.warning(
985
+ "DEGRADED_MODE: RegistryProtocolBinding not registered in container, "
986
+ "creating new instance (correlation_id=%s): %s",
987
+ correlation_id,
988
+ e,
989
+ extra={
990
+ "error_type": type(e).__name__,
991
+ "correlation_id": correlation_id,
992
+ "degraded_mode": True,
993
+ "degraded_reason": "service_not_registered",
994
+ "component": "handler_registry",
995
+ },
996
+ )
997
+ handler_registry = RegistryProtocolBinding()
998
+ except (RuntimeError, AttributeError) as e:
999
+ # Unexpected resolution failure - container internals issue.
1000
+ # Log with more diagnostic context but still allow degraded operation.
1001
+ logger.warning(
1002
+ "DEGRADED_MODE: Unexpected error resolving RegistryProtocolBinding, "
1003
+ "creating new instance (correlation_id=%s): %s",
1004
+ correlation_id,
1005
+ e,
1006
+ extra={
1007
+ "error_type": type(e).__name__,
1008
+ "correlation_id": correlation_id,
1009
+ "degraded_mode": True,
1010
+ "degraded_reason": "resolution_error",
1011
+ "component": "handler_registry",
1012
+ },
1013
+ )
1014
+ handler_registry = RegistryProtocolBinding()
1015
+ # NOTE: ProtocolConfigurationError is NOT caught here - configuration
1016
+ # errors should propagate and stop startup to prevent undefined behavior.
1017
+ else:
1018
+ # ServiceRegistry not available, create a new RegistryProtocolBinding directly
1019
+ logger.warning(
1020
+ "DEGRADED_MODE: ServiceRegistry not available, creating RegistryProtocolBinding directly (correlation_id=%s)",
1021
+ correlation_id,
1022
+ extra={
1023
+ "error_type": "NoneType",
1024
+ "correlation_id": correlation_id,
1025
+ "degraded_mode": True,
1026
+ "degraded_reason": "service_registry_unavailable",
1027
+ "component": "handler_registry",
1028
+ },
1029
+ )
1030
+ handler_registry = RegistryProtocolBinding()
1031
+
1032
+ # 6. Create runtime host process with config and pre-resolved registry
1033
+ # RuntimeHostProcess accepts config as dict; cast model_dump() result to
1034
+ # dict[str, object] to avoid implicit Any typing (Pydantic's model_dump()
1035
+ # returns dict[str, Any] but all our model fields are strongly typed)
1036
+ #
1037
+ # NOTE: RuntimeHostProcess expects 'service_name' and 'node_name' keys,
1038
+ # but ModelRuntimeConfig uses 'name'. Map 'name' -> 'service_name'/'node_name'
1039
+ # for compatibility. (OMN-1602)
1040
+ #
1041
+ # INVARIANT: In the current runtime model, `ModelRuntimeConfig.name` represents
1042
+ # both `service_name` and `node_name` by design; multi-node services require
1043
+ # schema expansion.
1044
+ #
1045
+ # TRIGGER FOR SPLIT: Split when ServiceKernel supports registering multiple
1046
+ # node contracts under one service runtime.
1047
+ #
1048
+ # Why both fields get the same value:
1049
+ # - For services using simplified config with just 'name', there's no semantic
1050
+ # distinction between service and node - a single service hosts a single node
1051
+ # - RuntimeHostProcess uses these to construct ModelNodeIdentity for Kafka
1052
+ # consumer group IDs and event routing
1053
+ # - The introspection consumer group format is:
1054
+ # {env}.{service_name}.{node_name}.{purpose}.{version}
1055
+ # e.g., "local.my-service.my-service.introspection.v1"
1056
+ # - When service_name == node_name, the format is intentionally redundant but
1057
+ # maintains consistency with multi-node deployments where they would differ
1058
+ runtime_create_start_time = time.time()
1059
+ runtime_config_dict = cast("dict[str, object]", config.model_dump())
1060
+ if config.name:
1061
+ runtime_config_dict["service_name"] = config.name
1062
+ runtime_config_dict["node_name"] = config.name
1063
+ runtime = RuntimeHostProcess(
1064
+ container=container,
1065
+ event_bus=event_bus,
1066
+ input_topic=config.input_topic,
1067
+ output_topic=config.output_topic,
1068
+ config=runtime_config_dict,
1069
+ handler_registry=handler_registry,
1070
+ # Pass contracts directory for handler discovery (OMN-1317)
1071
+ # This enables contract-based handler registration instead of
1072
+ # falling back to wire_handlers() with an empty registry
1073
+ contract_paths=[str(contracts_dir)],
1074
+ )
1075
+ runtime_create_duration = time.time() - runtime_create_start_time
1076
+ logger.debug(
1077
+ "Runtime host process created in %.3fs (correlation_id=%s)",
1078
+ runtime_create_duration,
1079
+ correlation_id,
1080
+ extra={
1081
+ "duration_seconds": runtime_create_duration,
1082
+ "input_topic": config.input_topic,
1083
+ "output_topic": config.output_topic,
1084
+ },
1085
+ )
1086
+
1087
+ # 7. Setup graceful shutdown
1088
+ shutdown_event = asyncio.Event()
1089
+ loop = asyncio.get_running_loop()
1090
+
1091
+ def handle_shutdown(sig: signal.Signals) -> None:
1092
+ """Handle shutdown signal with correlation tracking."""
1093
+ logger.info(
1094
+ "Received %s, initiating graceful shutdown... (correlation_id=%s)",
1095
+ sig.name,
1096
+ correlation_id,
1097
+ )
1098
+ shutdown_event.set()
1099
+
1100
+ # Register signal handlers for graceful shutdown
1101
+ if sys.platform != "win32":
1102
+ # Unix: Use asyncio's signal handler for proper event loop integration
1103
+ for sig in (signal.SIGINT, signal.SIGTERM):
1104
+ loop.add_signal_handler(sig, handle_shutdown, sig)
1105
+ else:
1106
+ # Windows: asyncio signal handlers not supported, use signal.signal()
1107
+ # for SIGINT (Ctrl+C). Note: SIGTERM not available on Windows.
1108
+ #
1109
+ # Thread-safety: On Windows, signal.signal() handlers execute in a
1110
+ # different thread than the event loop. While asyncio.Event.set() is
1111
+ # documented as thread-safe, we use loop.call_soon_threadsafe() to
1112
+ # schedule the set() call on the event loop thread. This ensures
1113
+ # proper cross-thread communication and avoids potential race
1114
+ # conditions with any event loop state inspection.
1115
+ def windows_handler(signum: int, frame: object) -> None:
1116
+ """Windows-compatible signal handler wrapper.
1117
+
1118
+ Uses call_soon_threadsafe to safely communicate with the event
1119
+ loop from the signal handler thread.
1120
+ """
1121
+ sig = signal.Signals(signum)
1122
+ logger.info(
1123
+ "Received %s, initiating graceful shutdown... (correlation_id=%s)",
1124
+ sig.name,
1125
+ correlation_id,
1126
+ )
1127
+ loop.call_soon_threadsafe(shutdown_event.set)
1128
+
1129
+ signal.signal(signal.SIGINT, windows_handler)
1130
+
1131
+ # 8. Start runtime and health server
1132
+ runtime_start_time = time.time()
1133
+ logger.info(
1134
+ "Starting ONEX runtime... (correlation_id=%s)",
1135
+ correlation_id,
1136
+ )
1137
+ await runtime.start()
1138
+ runtime_start_duration = time.time() - runtime_start_time
1139
+ logger.debug(
1140
+ "Runtime started in %.3fs (correlation_id=%s)",
1141
+ runtime_start_duration,
1142
+ correlation_id,
1143
+ extra={
1144
+ "duration_seconds": runtime_start_duration,
1145
+ },
1146
+ )
1147
+
1148
+ # 9. Start HTTP health server for Docker/K8s probes
1149
+ # Port can be configured via ONEX_HTTP_PORT environment variable
1150
+ http_port_str = os.getenv("ONEX_HTTP_PORT", str(DEFAULT_HTTP_PORT))
1151
+ try:
1152
+ http_port = int(http_port_str)
1153
+ if not MIN_PORT <= http_port <= MAX_PORT:
1154
+ logger.warning(
1155
+ "ONEX_HTTP_PORT %d outside valid range %d-%d, using default %d (correlation_id=%s)",
1156
+ http_port,
1157
+ MIN_PORT,
1158
+ MAX_PORT,
1159
+ DEFAULT_HTTP_PORT,
1160
+ correlation_id,
1161
+ )
1162
+ http_port = DEFAULT_HTTP_PORT
1163
+ except ValueError:
1164
+ logger.warning(
1165
+ "Invalid ONEX_HTTP_PORT value '%s', using default %d (correlation_id=%s)",
1166
+ http_port_str,
1167
+ DEFAULT_HTTP_PORT,
1168
+ correlation_id,
1169
+ )
1170
+ http_port = DEFAULT_HTTP_PORT
1171
+
1172
+ health_server = ServiceHealth(
1173
+ container=container,
1174
+ runtime=runtime,
1175
+ port=http_port,
1176
+ version=KERNEL_VERSION,
1177
+ )
1178
+ health_start_time = time.time()
1179
+ await health_server.start()
1180
+ health_start_duration = time.time() - health_start_time
1181
+ logger.debug(
1182
+ "Health server started in %.3fs (correlation_id=%s)",
1183
+ health_start_duration,
1184
+ correlation_id,
1185
+ extra={
1186
+ "duration_seconds": health_start_duration,
1187
+ "port": http_port,
1188
+ },
1189
+ )
1190
+
1191
+ # 9.5. Start introspection event consumer if dispatcher is available
1192
+ # This consumer subscribes to the input topic and routes introspection
1193
+ # events to the HandlerNodeIntrospected via DispatcherNodeIntrospected.
1194
+ # Unlike RuntimeHostProcess which routes based on handler_type field,
1195
+ # this consumer directly parses introspection events from JSON.
1196
+ #
1197
+ # The message handler is extracted to IntrospectionMessageHandler for
1198
+ # better testability and separation of concerns (PR #101 code quality).
1199
+ #
1200
+ # Duck typing approach per CLAUDE.md architectural guidelines:
1201
+ # Check for subscribe() capability via hasattr/callable instead of isinstance.
1202
+ # This enables any event bus implementing subscribe() to participate in
1203
+ # introspection event consumption, following protocol-based polymorphism.
1204
+ #
1205
+ # Production considerations:
1206
+ # - EventBusKafka: Uses distributed consumer groups for production workloads
1207
+ # - EventBusInmemory: subscribe() works for testing scenarios
1208
+ # - Other implementations: Will work if they implement subscribe()
1209
+ #
1210
+ # The duck typing approach allows new event bus implementations to
1211
+ # participate in introspection without modifying this code.
1212
+ has_subscribe = hasattr(event_bus, "subscribe") and callable(
1213
+ getattr(event_bus, "subscribe", None)
1214
+ )
1215
+ if introspection_dispatcher is not None and has_subscribe:
1216
+ # Create extracted event router with container-based DI pattern
1217
+ # Dependencies are passed explicitly since they are created at runtime
1218
+ # by the kernel and may not be registered in the container yet
1219
+ introspection_event_router = IntrospectionEventRouter(
1220
+ container=container,
1221
+ output_topic=config.output_topic,
1222
+ dispatcher=introspection_dispatcher,
1223
+ event_bus=event_bus,
1224
+ )
1225
+
1226
+ # Create typed node identity for introspection subscription (OMN-1602)
1227
+ # Uses ModelNodeIdentity + EnumConsumerGroupPurpose instead of hardcoded
1228
+ # group_id suffix hack for proper semantic consumer group naming.
1229
+ #
1230
+ # Required fields from config (fail-fast if missing):
1231
+ # - service_name: from config.name (required for node identification)
1232
+ # - node_name: from config.name (required for node identification)
1233
+ # Optional with defaults:
1234
+ # - env: from environment variable or event_bus.environment
1235
+ # - version: from config.contract_version or "v1"
1236
+ if not config.name:
1237
+ context = ModelInfraErrorContext(
1238
+ transport_type=EnumInfraTransportType.RUNTIME,
1239
+ operation="create_node_identity",
1240
+ correlation_id=correlation_id,
1241
+ )
1242
+ raise ProtocolConfigurationError(
1243
+ "Runtime config requires 'name' field for service identification. "
1244
+ "Add 'name: your-service-name' to runtime_config.yaml. "
1245
+ "This is required for typed introspection subscription (OMN-1602).",
1246
+ context=context,
1247
+ parameter="name",
1248
+ )
1249
+
1250
+ introspection_node_identity = ModelNodeIdentity(
1251
+ env=environment,
1252
+ service=config.name,
1253
+ node_name=config.name,
1254
+ version=config.contract_version or "v1",
1255
+ )
1256
+
1257
+ # Subscribe with callback - returns unsubscribe function
1258
+ subscribe_start_time = time.time()
1259
+ logger.info(
1260
+ "Subscribing to introspection events on event bus (correlation_id=%s)",
1261
+ correlation_id,
1262
+ extra={
1263
+ "topic": config.input_topic,
1264
+ "node_identity": {
1265
+ "env": introspection_node_identity.env,
1266
+ "service": introspection_node_identity.service,
1267
+ "node_name": introspection_node_identity.node_name,
1268
+ "version": introspection_node_identity.version,
1269
+ },
1270
+ "purpose": EnumConsumerGroupPurpose.INTROSPECTION.value,
1271
+ "event_bus_type": event_bus_type,
1272
+ },
1273
+ )
1274
+
1275
+ introspection_unsubscribe = await event_bus.subscribe(
1276
+ topic=config.input_topic,
1277
+ node_identity=introspection_node_identity,
1278
+ on_message=introspection_event_router.handle_message,
1279
+ purpose=EnumConsumerGroupPurpose.INTROSPECTION,
1280
+ )
1281
+ subscribe_duration = time.time() - subscribe_start_time
1282
+
1283
+ logger.info(
1284
+ "Introspection event consumer started successfully in %.3fs (correlation_id=%s)",
1285
+ subscribe_duration,
1286
+ correlation_id,
1287
+ extra={
1288
+ "topic": config.input_topic,
1289
+ "node_identity": {
1290
+ "env": introspection_node_identity.env,
1291
+ "service": introspection_node_identity.service,
1292
+ "node_name": introspection_node_identity.node_name,
1293
+ "version": introspection_node_identity.version,
1294
+ },
1295
+ "purpose": EnumConsumerGroupPurpose.INTROSPECTION.value,
1296
+ "subscribe_duration_seconds": subscribe_duration,
1297
+ "event_bus_type": event_bus_type,
1298
+ },
1299
+ )
1300
+
1301
+ # Calculate total bootstrap time
1302
+ bootstrap_duration = time.time() - bootstrap_start_time
1303
+
1304
+ # Display startup banner with key configuration
1305
+ if introspection_dispatcher is not None:
1306
+ if consul_handler is not None:
1307
+ registration_status = "enabled (PostgreSQL + Consul)"
1308
+ else:
1309
+ registration_status = "enabled (PostgreSQL only)"
1310
+ else:
1311
+ registration_status = "disabled"
1312
+ banner_lines = [
1313
+ "=" * 60,
1314
+ f"ONEX Runtime Kernel v{KERNEL_VERSION}",
1315
+ f"Environment: {environment}",
1316
+ f"Contracts: {contracts_dir}",
1317
+ f"Event Bus: {event_bus_type} (group: {config.consumer_group})",
1318
+ f"Topics: {config.input_topic} → {config.output_topic}",
1319
+ f"Registration: {registration_status}",
1320
+ f"Health endpoint: http://0.0.0.0:{http_port}/health",
1321
+ f"Bootstrap time: {bootstrap_duration:.3f}s",
1322
+ f"Correlation ID: {correlation_id}",
1323
+ "=" * 60,
1324
+ ]
1325
+ banner = "\n".join(banner_lines)
1326
+ logger.info("\n%s", banner)
1327
+
1328
+ logger.info(
1329
+ "ONEX runtime started successfully in %.3fs (correlation_id=%s)",
1330
+ bootstrap_duration,
1331
+ correlation_id,
1332
+ extra={
1333
+ "bootstrap_duration_seconds": bootstrap_duration,
1334
+ "config_load_seconds": config_duration,
1335
+ "event_bus_create_seconds": event_bus_duration,
1336
+ "container_wire_seconds": container_duration,
1337
+ "runtime_create_seconds": runtime_create_duration,
1338
+ "runtime_start_seconds": runtime_start_duration,
1339
+ "health_start_seconds": health_start_duration,
1340
+ },
1341
+ )
1342
+
1343
+ # Wait for shutdown signal
1344
+ await shutdown_event.wait()
1345
+
1346
+ grace_period = config.shutdown.grace_period_seconds
1347
+ shutdown_start_time = time.time()
1348
+ logger.info(
1349
+ "Shutdown signal received, stopping runtime (timeout=%ss, correlation_id=%s)",
1350
+ grace_period,
1351
+ correlation_id,
1352
+ )
1353
+
1354
+ # Stop introspection consumer first (fast)
1355
+ if introspection_unsubscribe is not None:
1356
+ try:
1357
+ await introspection_unsubscribe()
1358
+ logger.debug(
1359
+ "Introspection consumer stopped (correlation_id=%s)",
1360
+ correlation_id,
1361
+ )
1362
+ except Exception as consumer_stop_error:
1363
+ logger.warning(
1364
+ "Failed to stop introspection consumer: %s (correlation_id=%s)",
1365
+ sanitize_error_message(consumer_stop_error),
1366
+ correlation_id,
1367
+ )
1368
+ introspection_unsubscribe = None
1369
+
1370
+ # Stop health server (fast, non-blocking)
1371
+ if health_server is not None:
1372
+ try:
1373
+ health_stop_start_time = time.time()
1374
+ await health_server.stop()
1375
+ health_stop_duration = time.time() - health_stop_start_time
1376
+ logger.debug(
1377
+ "Health server stopped in %.3fs (correlation_id=%s)",
1378
+ health_stop_duration,
1379
+ correlation_id,
1380
+ extra={
1381
+ "duration_seconds": health_stop_duration,
1382
+ },
1383
+ )
1384
+ except Exception as health_stop_error:
1385
+ logger.warning(
1386
+ "Failed to stop health server: %s (correlation_id=%s)",
1387
+ health_stop_error,
1388
+ correlation_id,
1389
+ extra={
1390
+ "error_type": type(health_stop_error).__name__,
1391
+ },
1392
+ )
1393
+ health_server = None
1394
+
1395
+ # Stop runtime with timeout
1396
+ try:
1397
+ runtime_stop_start_time = time.time()
1398
+ await asyncio.wait_for(runtime.stop(), timeout=grace_period)
1399
+ runtime_stop_duration = time.time() - runtime_stop_start_time
1400
+ logger.debug(
1401
+ "Runtime stopped in %.3fs (correlation_id=%s)",
1402
+ runtime_stop_duration,
1403
+ correlation_id,
1404
+ extra={
1405
+ "duration_seconds": runtime_stop_duration,
1406
+ },
1407
+ )
1408
+ except TimeoutError:
1409
+ logger.warning(
1410
+ "Graceful shutdown timed out after %s seconds, forcing stop (correlation_id=%s)",
1411
+ grace_period,
1412
+ correlation_id,
1413
+ )
1414
+ runtime = None # Mark as stopped to prevent double-stop in finally
1415
+
1416
+ # Close PostgreSQL pool
1417
+ if postgres_pool is not None:
1418
+ try:
1419
+ pool_close_start_time = time.time()
1420
+ await postgres_pool.close()
1421
+ pool_close_duration = time.time() - pool_close_start_time
1422
+ logger.debug(
1423
+ "PostgreSQL pool closed in %.3fs (correlation_id=%s)",
1424
+ pool_close_duration,
1425
+ correlation_id,
1426
+ )
1427
+ except Exception as pool_close_error:
1428
+ # Sanitize to prevent credential leakage
1429
+ logger.warning(
1430
+ "Failed to close PostgreSQL pool: %s (correlation_id=%s)",
1431
+ sanitize_error_message(pool_close_error),
1432
+ correlation_id,
1433
+ )
1434
+ postgres_pool = None
1435
+
1436
+ shutdown_duration = time.time() - shutdown_start_time
1437
+ logger.info(
1438
+ "ONEX runtime stopped successfully in %.3fs (correlation_id=%s)",
1439
+ shutdown_duration,
1440
+ correlation_id,
1441
+ extra={
1442
+ "shutdown_duration_seconds": shutdown_duration,
1443
+ },
1444
+ )
1445
+ return 0
1446
+
1447
+ except ProtocolConfigurationError as e:
1448
+ # Configuration errors already have proper context and chaining
1449
+ error_code = getattr(getattr(e, "model", None), "error_code", None)
1450
+ error_code_name = getattr(error_code, "name", None)
1451
+ logger.exception(
1452
+ "ONEX runtime configuration failed (correlation_id=%s)",
1453
+ correlation_id,
1454
+ extra={
1455
+ "error_type": type(e).__name__,
1456
+ "error_code": str(error_code_name)
1457
+ if error_code_name is not None
1458
+ else None,
1459
+ },
1460
+ )
1461
+ return 1
1462
+
1463
+ except RuntimeHostError as e:
1464
+ # Runtime host errors already have proper structure
1465
+ error_code = getattr(getattr(e, "model", None), "error_code", None)
1466
+ error_code_name = getattr(error_code, "name", None)
1467
+ logger.exception(
1468
+ "ONEX runtime host error (correlation_id=%s)",
1469
+ correlation_id,
1470
+ extra={
1471
+ "error_type": type(e).__name__,
1472
+ "error_code": str(error_code_name)
1473
+ if error_code_name is not None
1474
+ else None,
1475
+ },
1476
+ )
1477
+ return 1
1478
+
1479
+ except Exception as e:
1480
+ # Unexpected errors: log with full context and return error code
1481
+ # (consistent with ProtocolConfigurationError and RuntimeHostError handlers)
1482
+ # Sanitize error message to prevent credential leakage
1483
+ logger.exception(
1484
+ "ONEX runtime failed with unexpected error: %s (correlation_id=%s)",
1485
+ sanitize_error_message(e),
1486
+ correlation_id,
1487
+ extra={
1488
+ "error_type": type(e).__name__,
1489
+ },
1490
+ )
1491
+ return 1
1492
+
1493
+ finally:
1494
+ # Guard cleanup - stop all resources if not already stopped
1495
+ # Order: introspection consumer -> health server -> runtime -> pool
1496
+
1497
+ if introspection_unsubscribe is not None:
1498
+ try:
1499
+ await introspection_unsubscribe()
1500
+ except Exception as cleanup_error:
1501
+ logger.warning(
1502
+ "Failed to stop introspection consumer during cleanup: %s (correlation_id=%s)",
1503
+ sanitize_error_message(cleanup_error),
1504
+ correlation_id,
1505
+ )
1506
+
1507
+ if health_server is not None:
1508
+ try:
1509
+ await health_server.stop()
1510
+ except Exception as cleanup_error:
1511
+ logger.warning(
1512
+ "Failed to stop health server during cleanup: %s (correlation_id=%s)",
1513
+ sanitize_error_message(cleanup_error),
1514
+ correlation_id,
1515
+ )
1516
+
1517
+ if runtime is not None:
1518
+ try:
1519
+ await runtime.stop()
1520
+ except Exception as cleanup_error:
1521
+ # Log cleanup failures with context instead of suppressing them
1522
+ # Sanitize to prevent potential credential leakage from runtime errors
1523
+ logger.warning(
1524
+ "Failed to stop runtime during cleanup: %s (correlation_id=%s)",
1525
+ sanitize_error_message(cleanup_error),
1526
+ correlation_id,
1527
+ )
1528
+
1529
+ if postgres_pool is not None:
1530
+ try:
1531
+ await postgres_pool.close()
1532
+ except Exception as cleanup_error:
1533
+ # Sanitize to prevent credential leakage from PostgreSQL errors
1534
+ logger.warning(
1535
+ "Failed to close PostgreSQL pool during cleanup: %s (correlation_id=%s)",
1536
+ sanitize_error_message(cleanup_error),
1537
+ correlation_id,
1538
+ )
1539
+
1540
+
1541
+ def configure_logging() -> None:
1542
+ """Configure logging for the kernel with structured format.
1543
+
1544
+ Sets up structured logging with appropriate log level from the
1545
+ ONEX_LOG_LEVEL environment variable (default: INFO). This function
1546
+ must be called early in the bootstrap process to ensure logging
1547
+ is available for all subsequent operations.
1548
+
1549
+ Logging Configuration:
1550
+ - Log Level: Controlled by ONEX_LOG_LEVEL environment variable
1551
+ - Format: Timestamp, level, logger name, message, extras
1552
+ - Date Format: ISO-8601 compatible (YYYY-MM-DD HH:MM:SS)
1553
+ - Structured Extras: Support for correlation_id and custom fields
1554
+
1555
+ Bootstrap Order Rationale:
1556
+ This function is called BEFORE runtime config is loaded because logging
1557
+ must be available during config loading itself (to log errors, warnings,
1558
+ and info about config discovery). Therefore, logging configuration uses
1559
+ environment variables rather than contract-based config values.
1560
+
1561
+ This is a deliberate chicken-and-egg solution:
1562
+ - Environment variables control early bootstrap logging
1563
+ - Contract config controls runtime behavior after bootstrap
1564
+
1565
+ Environment Variables:
1566
+ ONEX_LOG_LEVEL: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
1567
+ Default: INFO
1568
+
1569
+ Log Format Example:
1570
+ 2025-01-15 10:30:45 [INFO] omnibase_infra.runtime.service_kernel: ONEX Kernel v0.1.0
1571
+ 2025-01-15 10:30:45 [DEBUG] omnibase_infra.runtime.service_kernel: Runtime config loaded
1572
+ (correlation_id=123e4567-e89b-12d3-a456-426614174000)
1573
+
1574
+ Structured Logging Extras:
1575
+ All log calls support structured extras for observability:
1576
+ - correlation_id: UUID for distributed tracing
1577
+ - duration_seconds: Operation timing metrics
1578
+ - error_type: Exception class name for error analysis
1579
+ - Custom fields: Any JSON-serializable data
1580
+
1581
+ Example:
1582
+ >>> configure_logging()
1583
+ >>> logger.info("Operation completed", extra={"duration_seconds": 1.234})
1584
+ """
1585
+ log_level = os.getenv("ONEX_LOG_LEVEL", "INFO").upper()
1586
+
1587
+ # Validate log level and provide helpful error if invalid
1588
+ valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
1589
+ if log_level not in valid_levels:
1590
+ print(
1591
+ f"Warning: Invalid ONEX_LOG_LEVEL '{log_level}', using INFO. "
1592
+ f"Valid levels: {', '.join(sorted(valid_levels))}",
1593
+ file=sys.stderr,
1594
+ )
1595
+ log_level = "INFO"
1596
+
1597
+ logging.basicConfig(
1598
+ level=getattr(logging, log_level, logging.INFO),
1599
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
1600
+ datefmt="%Y-%m-%d %H:%M:%S",
1601
+ )
1602
+
1603
+
1604
+ def main() -> None:
1605
+ """Entry point for the ONEX runtime kernel.
1606
+
1607
+ This is the synchronous entry point for the kernel. It configures
1608
+ logging, initiates the async bootstrap process, and handles the
1609
+ final exit code.
1610
+
1611
+ Execution Flow:
1612
+ 1. Configure logging from environment variables
1613
+ 2. Log kernel version for startup identification
1614
+ 3. Run async bootstrap function in event loop
1615
+ 4. Exit with appropriate exit code (0=success, 1=error)
1616
+
1617
+ Exit Codes:
1618
+ 0: Successful startup and clean shutdown
1619
+ 1: Configuration error, runtime error, or unexpected failure
1620
+
1621
+ This function is the target for:
1622
+ - The installed entrypoint: `onex-runtime`
1623
+ - Direct module execution: `python -m omnibase_infra.runtime.service_kernel`
1624
+ - Docker CMD/ENTRYPOINT in container deployments
1625
+
1626
+ Example:
1627
+ >>> # From command line
1628
+ >>> python -m omnibase_infra.runtime.service_kernel
1629
+ >>> # Or via installed entrypoint
1630
+ >>> onex-runtime
1631
+
1632
+ Docker Usage:
1633
+ CMD ["onex-runtime"]
1634
+ # Container will start runtime and expose health endpoint
1635
+ """
1636
+ configure_logging()
1637
+ logger.info("ONEX Kernel v%s initializing...", KERNEL_VERSION)
1638
+ exit_code = asyncio.run(bootstrap())
1639
+ sys.exit(exit_code)
1640
+
1641
+
1642
+ if __name__ == "__main__":
1643
+ main()
1644
+
1645
+
1646
+ __all__: list[str] = [
1647
+ "ENV_CONTRACTS_DIR",
1648
+ "bootstrap",
1649
+ "load_runtime_config",
1650
+ "main",
1651
+ ]