omnibase_infra 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (675) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/cli/__init__.py +1 -0
  3. omnibase_infra/cli/commands.py +216 -0
  4. omnibase_infra/clients/__init__.py +0 -0
  5. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
  6. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
  7. omnibase_infra/decorators/__init__.py +29 -0
  8. omnibase_infra/decorators/allow_any.py +109 -0
  9. omnibase_infra/dlq/__init__.py +90 -0
  10. omnibase_infra/dlq/constants_dlq.py +57 -0
  11. omnibase_infra/dlq/models/__init__.py +26 -0
  12. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  13. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  14. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  15. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  16. omnibase_infra/enums/__init__.py +123 -0
  17. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  18. omnibase_infra/enums/enum_backend_type.py +27 -0
  19. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  20. omnibase_infra/enums/enum_capture_state.py +88 -0
  21. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  22. omnibase_infra/enums/enum_circuit_state.py +51 -0
  23. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  24. omnibase_infra/enums/enum_contract_type.py +84 -0
  25. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  26. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  27. omnibase_infra/enums/enum_environment.py +46 -0
  28. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  29. omnibase_infra/enums/enum_handler_error_type.py +101 -0
  30. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  31. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  32. omnibase_infra/enums/enum_handler_type.py +77 -0
  33. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  34. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  35. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  36. omnibase_infra/enums/enum_message_category.py +213 -0
  37. omnibase_infra/enums/enum_node_archetype.py +74 -0
  38. omnibase_infra/enums/enum_node_output_type.py +185 -0
  39. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  40. omnibase_infra/enums/enum_policy_type.py +32 -0
  41. omnibase_infra/enums/enum_registration_state.py +261 -0
  42. omnibase_infra/enums/enum_registration_status.py +33 -0
  43. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  44. omnibase_infra/enums/enum_response_status.py +26 -0
  45. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  46. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  47. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  48. omnibase_infra/enums/enum_topic_standard.py +42 -0
  49. omnibase_infra/enums/enum_validation_severity.py +78 -0
  50. omnibase_infra/errors/__init__.py +156 -0
  51. omnibase_infra/errors/error_architecture_violation.py +152 -0
  52. omnibase_infra/errors/error_chain_propagation.py +188 -0
  53. omnibase_infra/errors/error_compute_registry.py +92 -0
  54. omnibase_infra/errors/error_consul.py +132 -0
  55. omnibase_infra/errors/error_container_wiring.py +243 -0
  56. omnibase_infra/errors/error_event_bus_registry.py +102 -0
  57. omnibase_infra/errors/error_infra.py +608 -0
  58. omnibase_infra/errors/error_message_type_registry.py +101 -0
  59. omnibase_infra/errors/error_policy_registry.py +112 -0
  60. omnibase_infra/errors/error_vault.py +123 -0
  61. omnibase_infra/event_bus/__init__.py +72 -0
  62. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
  63. omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
  64. omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
  65. omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
  66. omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
  67. omnibase_infra/event_bus/models/__init__.py +29 -0
  68. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  69. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
  70. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  71. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  72. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  73. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  74. omnibase_infra/event_bus/topic_constants.py +376 -0
  75. omnibase_infra/handlers/__init__.py +75 -0
  76. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  77. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  78. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  79. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  80. omnibase_infra/handlers/handler_consul.py +787 -0
  81. omnibase_infra/handlers/handler_db.py +1039 -0
  82. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  83. omnibase_infra/handlers/handler_graph.py +1154 -0
  84. omnibase_infra/handlers/handler_http.py +920 -0
  85. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  86. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  87. omnibase_infra/handlers/handler_mcp.py +748 -0
  88. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  89. omnibase_infra/handlers/handler_vault.py +422 -0
  90. omnibase_infra/handlers/mcp/__init__.py +19 -0
  91. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  92. omnibase_infra/handlers/mcp/protocols.py +178 -0
  93. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  94. omnibase_infra/handlers/mixins/__init__.py +42 -0
  95. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  96. omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
  97. omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
  98. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  99. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  100. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  101. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  102. omnibase_infra/handlers/models/__init__.py +286 -0
  103. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  104. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  105. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  106. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  107. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  108. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  109. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  110. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  111. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  112. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  113. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  114. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  115. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  116. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  117. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  118. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  119. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  120. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  121. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  122. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  123. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  124. omnibase_infra/handlers/models/http/__init__.py +50 -0
  125. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  126. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  127. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  128. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  129. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  130. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  131. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  132. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  133. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  134. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  135. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  136. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  137. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  138. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  139. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  140. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  141. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  142. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  143. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  144. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  145. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  146. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  147. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  148. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  149. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  150. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  151. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  152. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  153. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  154. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  155. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  156. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  157. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  158. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  159. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  160. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  161. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  162. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  163. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  164. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  165. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  166. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  167. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  168. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  169. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  170. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  171. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  172. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  173. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  174. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  175. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  176. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  177. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  178. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  179. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  180. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  181. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  182. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  183. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  184. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  185. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  186. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  187. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  188. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  189. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  190. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  191. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
  192. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  193. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  194. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  195. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  196. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  197. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
  198. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  199. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  200. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  201. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  202. omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
  203. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  204. omnibase_infra/idempotency/__init__.py +94 -0
  205. omnibase_infra/idempotency/models/__init__.py +43 -0
  206. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  207. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  208. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  209. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  210. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  211. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  212. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  213. omnibase_infra/idempotency/store_inmemory.py +265 -0
  214. omnibase_infra/idempotency/store_postgres.py +923 -0
  215. omnibase_infra/infrastructure/__init__.py +0 -0
  216. omnibase_infra/mixins/__init__.py +71 -0
  217. omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
  218. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  219. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  220. omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
  221. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  222. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  223. omnibase_infra/models/__init__.py +136 -0
  224. omnibase_infra/models/corpus/__init__.py +17 -0
  225. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  226. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  227. omnibase_infra/models/discovery/__init__.py +42 -0
  228. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  229. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  230. omnibase_infra/models/discovery/model_introspection_config.py +311 -0
  231. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  232. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  233. omnibase_infra/models/dispatch/__init__.py +147 -0
  234. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  235. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  236. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  237. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  238. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  239. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  240. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  241. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  242. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  243. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  244. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  245. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  246. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  247. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  248. omnibase_infra/models/errors/__init__.py +45 -0
  249. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  250. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  251. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  252. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  253. omnibase_infra/models/handlers/__init__.py +37 -0
  254. omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
  255. omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
  256. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  257. omnibase_infra/models/health/__init__.py +9 -0
  258. omnibase_infra/models/health/model_health_check_result.py +40 -0
  259. omnibase_infra/models/lifecycle/__init__.py +39 -0
  260. omnibase_infra/models/logging/__init__.py +51 -0
  261. omnibase_infra/models/logging/model_log_context.py +756 -0
  262. omnibase_infra/models/model_retry_error_classification.py +78 -0
  263. omnibase_infra/models/projection/__init__.py +43 -0
  264. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  265. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  266. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  267. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  268. omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
  269. omnibase_infra/models/projectors/__init__.py +41 -0
  270. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  271. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  272. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  273. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  274. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  275. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  276. omnibase_infra/models/registration/__init__.py +59 -0
  277. omnibase_infra/models/registration/commands/__init__.py +15 -0
  278. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  279. omnibase_infra/models/registration/events/__init__.py +56 -0
  280. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  281. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  282. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  283. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  284. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  285. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  286. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  287. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  288. omnibase_infra/models/registration/model_node_capabilities.py +179 -0
  289. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  290. omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
  291. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  292. omnibase_infra/models/registration/model_node_registration.py +162 -0
  293. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  294. omnibase_infra/models/registry/__init__.py +29 -0
  295. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  296. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  297. omnibase_infra/models/resilience/__init__.py +9 -0
  298. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  299. omnibase_infra/models/routing/__init__.py +25 -0
  300. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  301. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  302. omnibase_infra/models/runtime/__init__.py +40 -0
  303. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  304. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  305. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  306. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  307. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  308. omnibase_infra/models/runtime/model_handler_contract.py +280 -0
  309. omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
  310. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  311. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  312. omnibase_infra/models/security/__init__.py +50 -0
  313. omnibase_infra/models/security/classification_levels.py +99 -0
  314. omnibase_infra/models/security/model_environment_policy.py +145 -0
  315. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  316. omnibase_infra/models/security/model_security_error.py +81 -0
  317. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  318. omnibase_infra/models/security/model_security_warning.py +67 -0
  319. omnibase_infra/models/snapshot/__init__.py +27 -0
  320. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  321. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  322. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  323. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  324. omnibase_infra/models/types/__init__.py +71 -0
  325. omnibase_infra/models/validation/__init__.py +89 -0
  326. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  327. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  328. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  329. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  330. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  331. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  332. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  333. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  334. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  335. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  336. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  337. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  338. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  339. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  340. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  341. omnibase_infra/nodes/__init__.py +48 -0
  342. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  343. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  344. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
  345. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  346. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  347. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  348. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  349. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  350. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  351. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  352. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  353. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  354. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  355. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  356. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  357. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  358. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  359. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
  360. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  361. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  362. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  363. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  364. omnibase_infra/nodes/effects/README.md +358 -0
  365. omnibase_infra/nodes/effects/__init__.py +26 -0
  366. omnibase_infra/nodes/effects/contract.yaml +172 -0
  367. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  368. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  369. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  370. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  371. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  372. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  373. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  374. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  375. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  376. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  377. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  378. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  379. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
  380. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  381. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  382. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  383. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  384. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  385. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  386. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
  387. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  388. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  389. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  390. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  391. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  392. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  393. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  394. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  395. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  396. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  397. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  398. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  399. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  400. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  401. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  402. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  403. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  404. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  405. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  406. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  407. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  408. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
  409. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
  410. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
  411. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  412. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  413. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  414. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  415. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  416. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  417. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  418. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  419. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
  420. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  421. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  422. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  423. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  424. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  425. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  426. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  427. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  428. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  429. omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
  430. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  431. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  432. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  433. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
  434. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  435. omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
  436. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  437. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  438. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  439. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
  440. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  441. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  442. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  443. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  444. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  445. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  446. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  447. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  448. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  449. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  450. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  451. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  452. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  453. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  454. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  455. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  456. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  457. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  458. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  459. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  460. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  461. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  462. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  463. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  464. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  465. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
  466. omnibase_infra/nodes/reducers/__init__.py +30 -0
  467. omnibase_infra/nodes/reducers/models/__init__.py +32 -0
  468. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
  469. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  470. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  471. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  472. omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
  473. omnibase_infra/observability/__init__.py +143 -0
  474. omnibase_infra/observability/constants_metrics.py +91 -0
  475. omnibase_infra/observability/factory_observability_sink.py +525 -0
  476. omnibase_infra/observability/handlers/__init__.py +118 -0
  477. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  478. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  479. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  480. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  481. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  482. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  483. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  484. omnibase_infra/observability/hooks/__init__.py +74 -0
  485. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  486. omnibase_infra/observability/models/__init__.py +30 -0
  487. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  488. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  489. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  490. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  491. omnibase_infra/observability/sinks/__init__.py +69 -0
  492. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  493. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  494. omnibase_infra/plugins/__init__.py +27 -0
  495. omnibase_infra/plugins/examples/__init__.py +28 -0
  496. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  497. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  498. omnibase_infra/plugins/models/__init__.py +21 -0
  499. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  500. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  501. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  502. omnibase_infra/plugins/plugin_compute_base.py +435 -0
  503. omnibase_infra/projectors/__init__.py +30 -0
  504. omnibase_infra/projectors/contracts/__init__.py +63 -0
  505. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  506. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  507. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  508. omnibase_infra/protocols/__init__.py +99 -0
  509. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  510. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  511. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  512. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  513. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  514. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  515. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  516. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  517. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  518. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  519. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  520. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  521. omnibase_infra/runtime/__init__.py +296 -0
  522. omnibase_infra/runtime/binding_config_resolver.py +2706 -0
  523. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  524. omnibase_infra/runtime/contract_handler_discovery.py +582 -0
  525. omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
  526. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  527. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  528. omnibase_infra/runtime/enums/__init__.py +18 -0
  529. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  530. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  531. omnibase_infra/runtime/envelope_validator.py +179 -0
  532. omnibase_infra/runtime/handler_contract_source.py +669 -0
  533. omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
  534. omnibase_infra/runtime/handler_registry.py +321 -0
  535. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  536. omnibase_infra/runtime/kernel.py +40 -0
  537. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  538. omnibase_infra/runtime/mixin_semver_cache.py +378 -0
  539. omnibase_infra/runtime/mixins/__init__.py +17 -0
  540. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
  541. omnibase_infra/runtime/models/__init__.py +192 -0
  542. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  543. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  544. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  545. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  546. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  547. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  548. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  549. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  550. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  551. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  552. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  553. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  554. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  555. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  556. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  557. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  558. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  559. omnibase_infra/runtime/models/model_health_check_result.py +228 -0
  560. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  561. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  562. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  563. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  564. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  565. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  566. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  567. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  568. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  569. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  570. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  571. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  572. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  573. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  574. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
  575. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  576. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  577. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  578. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  579. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  580. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  581. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  582. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  583. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  584. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  585. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  586. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  587. omnibase_infra/runtime/projector_shell.py +1102 -0
  588. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  589. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  590. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  591. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  592. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  593. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  594. omnibase_infra/runtime/protocol_policy.py +366 -0
  595. omnibase_infra/runtime/protocols/__init__.py +27 -0
  596. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  597. omnibase_infra/runtime/registry/__init__.py +93 -0
  598. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  599. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  600. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  601. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  602. omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
  603. omnibase_infra/runtime/registry_compute.py +1143 -0
  604. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  605. omnibase_infra/runtime/registry_policy.py +1502 -0
  606. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  607. omnibase_infra/runtime/secret_resolver.py +2110 -0
  608. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  609. omnibase_infra/runtime/service_kernel.py +1573 -0
  610. omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
  611. omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
  612. omnibase_infra/runtime/util_container_wiring.py +1123 -0
  613. omnibase_infra/runtime/util_validation.py +314 -0
  614. omnibase_infra/runtime/util_version.py +98 -0
  615. omnibase_infra/runtime/util_wiring.py +566 -0
  616. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  617. omnibase_infra/services/__init__.py +68 -0
  618. omnibase_infra/services/corpus_capture.py +678 -0
  619. omnibase_infra/services/service_capability_query.py +945 -0
  620. omnibase_infra/services/service_health.py +897 -0
  621. omnibase_infra/services/service_node_selector.py +530 -0
  622. omnibase_infra/services/service_timeout_emitter.py +682 -0
  623. omnibase_infra/services/service_timeout_scanner.py +390 -0
  624. omnibase_infra/services/snapshot/__init__.py +31 -0
  625. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  626. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  627. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  628. omnibase_infra/shared/__init__.py +8 -0
  629. omnibase_infra/testing/__init__.py +10 -0
  630. omnibase_infra/testing/utils.py +23 -0
  631. omnibase_infra/types/__init__.py +48 -0
  632. omnibase_infra/types/type_cache_info.py +49 -0
  633. omnibase_infra/types/type_dsn.py +173 -0
  634. omnibase_infra/types/type_infra_aliases.py +60 -0
  635. omnibase_infra/types/typed_dict/__init__.py +21 -0
  636. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  637. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  638. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  639. omnibase_infra/utils/__init__.py +89 -0
  640. omnibase_infra/utils/correlation.py +208 -0
  641. omnibase_infra/utils/util_datetime.py +372 -0
  642. omnibase_infra/utils/util_dsn_validation.py +333 -0
  643. omnibase_infra/utils/util_env_parsing.py +264 -0
  644. omnibase_infra/utils/util_error_sanitization.py +457 -0
  645. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  646. omnibase_infra/utils/util_semver.py +233 -0
  647. omnibase_infra/validation/__init__.py +307 -0
  648. omnibase_infra/validation/enums/__init__.py +11 -0
  649. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  650. omnibase_infra/validation/infra_validators.py +1486 -0
  651. omnibase_infra/validation/linter_contract.py +907 -0
  652. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  653. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  654. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  655. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  656. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  657. omnibase_infra/validation/models/__init__.py +15 -0
  658. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  659. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  660. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  661. omnibase_infra/validation/validation_exemptions.yaml +1710 -0
  662. omnibase_infra/validation/validator_any_type.py +715 -0
  663. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  664. omnibase_infra/validation/validator_execution_shape.py +465 -0
  665. omnibase_infra/validation/validator_localhandler.py +261 -0
  666. omnibase_infra/validation/validator_registration_security.py +410 -0
  667. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  668. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  669. omnibase_infra/validation/validator_security.py +410 -0
  670. omnibase_infra/validation/validator_topic_category.py +1152 -0
  671. omnibase_infra-0.2.1.dist-info/METADATA +197 -0
  672. omnibase_infra-0.2.1.dist-info/RECORD +675 -0
  673. omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
  674. omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
  675. omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1102 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Generic Contract-Driven Projector Shell.
4
+
5
+ Implements ProtocolEventProjector for contract-based event-to-state projection.
6
+ All behavior is driven by ModelProjectorContract - NO domain-specific logic.
7
+
8
+ Features:
9
+ - Event type matching via envelope metadata, payload attribute, or classname
10
+ - Dynamic column value extraction from nested event payloads
11
+ - Three projection modes: upsert, insert_only, append
12
+ - Parameterized SQL queries for injection protection
13
+ - Bulk state queries for N+1 optimization
14
+ - Configurable query timeouts
15
+
16
+ See Also:
17
+ - ProtocolEventProjector: Protocol definition from omnibase_infra.protocols
18
+ - ModelProjectorContract: Contract model from omnibase_core
19
+ - ProjectorPluginLoader: Loader that instantiates ProjectorShell
20
+
21
+ Related Tickets:
22
+ - OMN-1169: ProjectorShell contract-driven projections (implemented)
23
+
24
+ .. versionadded:: 0.7.0
25
+ Created as part of OMN-1169 projector shell implementation.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import logging
31
+ from uuid import UUID
32
+
33
+ import asyncpg
34
+ from pydantic import BaseModel
35
+
36
+ from omnibase_core.models.events.model_event_envelope import ModelEventEnvelope
37
+ from omnibase_core.models.projectors import (
38
+ ModelProjectionResult,
39
+ ModelProjectorContract,
40
+ )
41
+ from omnibase_infra.enums import EnumInfraTransportType
42
+ from omnibase_infra.errors import (
43
+ InfraConnectionError,
44
+ InfraTimeoutError,
45
+ ModelInfraErrorContext,
46
+ ModelTimeoutErrorContext,
47
+ ProtocolConfigurationError,
48
+ RuntimeHostError,
49
+ )
50
+ from omnibase_infra.models.projectors.util_sql_identifiers import quote_identifier
51
+ from omnibase_infra.runtime.mixins import MixinProjectorSqlOperations
52
+
53
+ logger = logging.getLogger(__name__)
54
+
55
+
56
+ class ProjectorShell(MixinProjectorSqlOperations):
57
+ """Generic contract-driven projector implementation.
58
+
59
+ Transforms events into persistent state projections based on a
60
+ ModelProjectorContract definition. All behavior is declarative -
61
+ no domain-specific logic in this class.
62
+
63
+ The projector supports three projection modes:
64
+ - upsert: INSERT or UPDATE based on upsert_key (default)
65
+ - insert_only: INSERT only, fail on conflict
66
+ - append: Always INSERT, event-log style
67
+
68
+ UniqueViolationError Handling:
69
+ The projector handles ``asyncpg.UniqueViolationError`` differently based
70
+ on the projection mode. Understanding these semantics is critical for
71
+ correct schema design and error handling.
72
+
73
+ **insert_only mode**:
74
+ A unique violation indicates duplicate event processing (idempotency).
75
+ This is expected behavior when replaying events or when at-least-once
76
+ delivery causes duplicates. Returns ``ModelProjectionResult(success=False)``
77
+ with an error message. The caller can decide whether to log, retry,
78
+ or ignore based on their requirements.
79
+
80
+ **upsert mode**:
81
+ Unique violations should NEVER occur because the generated SQL uses
82
+ ``ON CONFLICT ... DO UPDATE``. If a violation is raised, it indicates
83
+ a schema mismatch (e.g., the ``upsert_key`` in the contract doesn't
84
+ match the actual unique constraint). Raises ``RuntimeHostError`` to
85
+ signal a configuration error that needs investigation.
86
+
87
+ **append mode**:
88
+ This mode assumes **event-driven primary keys** where each row is
89
+ uniquely identified by event-specific data (e.g., ``envelope_id``,
90
+ ``event_sequence``, or composite keys including timestamp). With this
91
+ assumption, a unique violation indicates duplicate event processing -
92
+ the same event was projected twice.
93
+
94
+ **IMPORTANT ASSUMPTION**: Append mode primary key design must ensure
95
+ that each event produces a unique key. Common patterns:
96
+ - ``envelope_id`` (UUID from event envelope)
97
+ - ``(aggregate_id, event_sequence)`` composite key
98
+ - ``(aggregate_id, event_type, timestamp)`` composite key
99
+
100
+ If your schema uses **non-event primary keys** (e.g., auto-increment,
101
+ domain-specific business keys), a ``UniqueViolationError`` in append
102
+ mode may indicate a **legitimate conflict** rather than a duplicate
103
+ event. In such cases, you should either:
104
+ 1. Switch to ``upsert`` mode with appropriate conflict resolution
105
+ 2. Redesign the primary key to be event-driven
106
+ 3. Implement custom projection logic outside ProjectorShell
107
+
108
+ When a violation occurs in append mode, ``RuntimeHostError`` is raised
109
+ to fail fast and signal the need for investigation.
110
+
111
+ Composite Primary Key Handling:
112
+ The contract model (``ModelProjectorContract``) only supports single-column
113
+ ``primary_key`` and ``upsert_key`` values (type: ``str``). For schemas with
114
+ composite primary keys, there are two approaches:
115
+
116
+ **Approach 1: Add UNIQUE constraint (Recommended)**
117
+ Add a UNIQUE constraint on the single column specified in the contract.
118
+ This allows ``ON CONFLICT (column)`` to work even with composite PKs.
119
+
120
+ Example SQL schema::
121
+
122
+ PRIMARY KEY (entity_id, domain),
123
+ UNIQUE (entity_id) -- Enables ON CONFLICT (entity_id)
124
+
125
+ **Approach 2: Use upsert_partial() with explicit conflict_columns**
126
+ For operations requiring composite key semantics, use ``upsert_partial()``
127
+ with the ``conflict_columns`` parameter instead of ``project()``.
128
+
129
+ Example::
130
+
131
+ await projector.upsert_partial(
132
+ aggregate_id=entity_id,
133
+ values={"entity_id": entity_id, "domain": domain, ...},
134
+ correlation_id=correlation_id,
135
+ conflict_columns=["entity_id", "domain"], # Composite key
136
+ )
137
+
138
+ **get_state() / get_states() behavior with composite keys**:
139
+ These methods use only the first column of the primary key for lookups.
140
+ For schemas where the single-column lookup is not unique, the results
141
+ may be ambiguous. Consider querying the database directly in such cases.
142
+
143
+ Thread Safety:
144
+ This implementation is coroutine-safe for concurrent async calls.
145
+ Uses asyncpg connection pool for connection management.
146
+
147
+ Security:
148
+ All queries use parameterized statements for SQL injection protection.
149
+ Table and column names are validated by the contract model validators
150
+ and quoted using ``quote_identifier()`` for safe SQL generation.
151
+
152
+ Query Timeout:
153
+ Configurable via ``query_timeout_seconds`` parameter. Defaults to 30
154
+ seconds. Set to None to disable timeout (not recommended for production).
155
+
156
+ Default Values:
157
+ Column defaults specified in the contract (``column.default``) are treated
158
+ as **runtime literal values**, not SQL expressions. They are inserted as
159
+ parameter values, not embedded in SQL. For database-level defaults (e.g.,
160
+ ``CURRENT_TIMESTAMP``), use PostgreSQL column defaults instead.
161
+
162
+ Example:
163
+ >>> from omnibase_core.models.projectors import ModelProjectorContract
164
+ >>> contract = ModelProjectorContract.model_validate(yaml_data)
165
+ >>> pool = await asyncpg.create_pool(dsn)
166
+ >>> projector = ProjectorShell(contract, pool, query_timeout_seconds=10.0)
167
+ >>> result = await projector.project(event_envelope, correlation_id)
168
+ >>> if result.success:
169
+ ... print(f"Projected {result.rows_affected} rows")
170
+
171
+ Related:
172
+ - OMN-1169: ProjectorShell contract-driven projections (implemented)
173
+ - OMN-1168: ProjectorPluginLoader contract discovery
174
+ """
175
+
176
+ # Default query timeout in seconds (30s is reasonable for projections)
177
+ DEFAULT_QUERY_TIMEOUT_SECONDS: float = 30.0
178
+
179
+ def __init__(
180
+ self,
181
+ contract: ModelProjectorContract,
182
+ pool: asyncpg.Pool,
183
+ query_timeout_seconds: float | None = None,
184
+ ) -> None:
185
+ """Initialize projector shell with contract and database pool.
186
+
187
+ Args:
188
+ contract: The projector contract defining projection behavior.
189
+ All projection rules (table, columns, modes) come from this.
190
+ pool: asyncpg connection pool for database access.
191
+ Pool should be created by the caller (e.g., from container).
192
+ query_timeout_seconds: Timeout for individual database queries in
193
+ seconds. Defaults to 30.0 seconds. Set to None to disable
194
+ timeout (not recommended for production).
195
+ """
196
+ self._contract = contract
197
+ self._pool = pool
198
+ self._query_timeout = (
199
+ query_timeout_seconds
200
+ if query_timeout_seconds is not None
201
+ else self.DEFAULT_QUERY_TIMEOUT_SECONDS
202
+ )
203
+ logger.debug(
204
+ "ProjectorShell initialized for projector '%s'",
205
+ contract.projector_id,
206
+ extra={
207
+ "projector_id": contract.projector_id,
208
+ "aggregate_type": contract.aggregate_type,
209
+ "consumed_events": contract.consumed_events,
210
+ "mode": contract.behavior.mode,
211
+ "query_timeout_seconds": self._query_timeout,
212
+ },
213
+ )
214
+
215
+ @property
216
+ def projector_id(self) -> str:
217
+ """Unique identifier from contract."""
218
+ return str(self._contract.projector_id)
219
+
220
+ @property
221
+ def aggregate_type(self) -> str:
222
+ """Aggregate type from contract."""
223
+ return str(self._contract.aggregate_type)
224
+
225
+ @property
226
+ def consumed_events(self) -> list[str]:
227
+ """Event types from contract."""
228
+ return list(self._contract.consumed_events)
229
+
230
+ @property
231
+ def contract(self) -> ModelProjectorContract:
232
+ """Access the underlying contract."""
233
+ return self._contract
234
+
235
+ @property
236
+ def is_placeholder(self) -> bool:
237
+ """Whether this is a placeholder implementation.
238
+
239
+ Returns:
240
+ False, as this is a full implementation.
241
+ """
242
+ return False
243
+
244
+ async def project(
245
+ self,
246
+ event: ModelEventEnvelope[object],
247
+ correlation_id: UUID,
248
+ ) -> ModelProjectionResult:
249
+ """Project event to persistence store.
250
+
251
+ Transforms the event into a database row based on the contract
252
+ configuration. The projection mode (upsert, insert_only, append)
253
+ determines how conflicts are handled.
254
+
255
+ Args:
256
+ event: The event envelope to project. The payload is accessed
257
+ via dot-notation paths defined in the contract columns.
258
+ correlation_id: Correlation ID for distributed tracing.
259
+
260
+ Returns:
261
+ ModelProjectionResult with success status and rows affected.
262
+ Returns skipped=True if event type is not in consumed_events.
263
+
264
+ Raises:
265
+ InfraConnectionError: If database connection fails.
266
+ InfraTimeoutError: If projection times out.
267
+ RuntimeHostError: For other database errors.
268
+ """
269
+ ctx = ModelInfraErrorContext(
270
+ transport_type=EnumInfraTransportType.DATABASE,
271
+ operation="project",
272
+ target_name=f"projector.{self.projector_id}",
273
+ correlation_id=correlation_id,
274
+ )
275
+
276
+ # Get event type from envelope
277
+ event_type = self._get_event_type(event)
278
+
279
+ # Check if this projector consumes this event type
280
+ if event_type not in self._contract.consumed_events:
281
+ logger.debug(
282
+ "Skipping event type '%s' not in consumed_events",
283
+ event_type,
284
+ extra={
285
+ "projector_id": self.projector_id,
286
+ "event_type": event_type,
287
+ "consumed_events": self._contract.consumed_events,
288
+ "correlation_id": str(correlation_id),
289
+ },
290
+ )
291
+ return ModelProjectionResult(success=True, skipped=True, rows_affected=0)
292
+
293
+ # Extract column values from event
294
+ values = self._extract_values(event, event_type)
295
+
296
+ # Execute projection based on mode
297
+ try:
298
+ rows_affected = await self._execute_projection(
299
+ values, correlation_id, event_type
300
+ )
301
+
302
+ logger.debug(
303
+ "Projection completed",
304
+ extra={
305
+ "projector_id": self.projector_id,
306
+ "event_type": event_type,
307
+ "rows_affected": rows_affected,
308
+ "correlation_id": str(correlation_id),
309
+ },
310
+ )
311
+
312
+ return ModelProjectionResult(
313
+ success=True,
314
+ skipped=False,
315
+ rows_affected=rows_affected,
316
+ )
317
+
318
+ except asyncpg.PostgresConnectionError as e:
319
+ raise InfraConnectionError(
320
+ f"Failed to connect to database for projection: {self.projector_id}",
321
+ context=ctx,
322
+ ) from e
323
+
324
+ except asyncpg.QueryCanceledError as e:
325
+ timeout_ctx = ModelTimeoutErrorContext(
326
+ transport_type=EnumInfraTransportType.DATABASE,
327
+ operation="project",
328
+ target_name=f"projector.{self.projector_id}",
329
+ correlation_id=correlation_id,
330
+ # timeout_seconds omitted - database timeout is connection-pool level, not available here
331
+ )
332
+ raise InfraTimeoutError(
333
+ f"Projection timed out for: {self.projector_id}",
334
+ context=timeout_ctx,
335
+ ) from e
336
+
337
+ except asyncpg.UniqueViolationError as e:
338
+ # ============================================================
339
+ # UniqueViolationError Handling by Projection Mode
340
+ # ============================================================
341
+ #
342
+ # Different projection modes have different semantics for unique
343
+ # constraint violations. See class docstring for full documentation.
344
+ #
345
+ # insert_only mode:
346
+ # - EXPECTED: Duplicate events (idempotency, at-least-once delivery)
347
+ # - BEHAVIOR: Return failure result, let caller decide how to handle
348
+ # - RATIONALE: insert_only is designed for idempotent projections
349
+ # where duplicates are tolerated but should be reported
350
+ #
351
+ # upsert mode:
352
+ # - UNEXPECTED: Should NEVER occur (ON CONFLICT handles duplicates)
353
+ # - BEHAVIOR: Raise RuntimeHostError
354
+ # - RATIONALE: If we get here, the upsert_key in the contract doesn't
355
+ # match the actual unique constraint in the database schema
356
+ # - ACTION REQUIRED: Verify contract.upsert_key matches DB constraint
357
+ #
358
+ # append mode:
359
+ # - UNEXPECTED: Assumes event-driven primary keys (envelope_id, etc.)
360
+ # - BEHAVIOR: Raise RuntimeHostError
361
+ # - RATIONALE: With event-driven PKs, duplicates indicate either:
362
+ # (a) Same event processed twice (infrastructure issue), or
363
+ # (b) Schema uses non-event PKs (design mismatch)
364
+ # - ACTION REQUIRED:
365
+ # * If (a): Investigate event delivery/replay logic
366
+ # * If (b): Consider switching to upsert mode or redesigning PK
367
+ #
368
+ # IMPORTANT ASSUMPTION for append mode:
369
+ # The primary key MUST be derived from event data (e.g., envelope_id,
370
+ # event_sequence) such that each unique event produces a unique key.
371
+ # If using auto-increment or business keys, append mode violations
372
+ # may indicate legitimate conflicts, not duplicates.
373
+ #
374
+ # ============================================================
375
+
376
+ if self._contract.behavior.mode == "insert_only":
377
+ # insert_only: Expected duplicate - report as failure, don't raise
378
+ logger.warning(
379
+ "Unique constraint violation during insert_only projection "
380
+ "(likely duplicate event - expected for idempotent processing)",
381
+ extra={
382
+ "projector_id": self.projector_id,
383
+ "event_type": event_type,
384
+ "correlation_id": str(correlation_id),
385
+ "mode": "insert_only",
386
+ "hint": "This is expected behavior for at-least-once delivery",
387
+ },
388
+ )
389
+ return ModelProjectionResult(
390
+ success=False,
391
+ skipped=False,
392
+ rows_affected=0,
393
+ error="Unique constraint violation: duplicate key for insert_only mode",
394
+ )
395
+
396
+ # upsert/append modes: Unexpected violation - fail fast with error
397
+ # For upsert: indicates contract.upsert_key doesn't match DB constraint
398
+ # For append: indicates either duplicate event or non-event-driven PK
399
+ mode = self._contract.behavior.mode
400
+ if mode == "upsert":
401
+ hint = (
402
+ "Verify that contract.behavior.upsert_key matches the actual "
403
+ "unique constraint in the database schema"
404
+ )
405
+ else: # append mode
406
+ hint = (
407
+ "Append mode assumes event-driven primary keys (e.g., envelope_id). "
408
+ "If using non-event PKs, consider switching to upsert mode or "
409
+ "redesigning the primary key to be event-derived"
410
+ )
411
+
412
+ logger.exception(
413
+ "Unexpected unique constraint violation in %s mode",
414
+ mode,
415
+ extra={
416
+ "projector_id": self.projector_id,
417
+ "event_type": event_type,
418
+ "correlation_id": str(correlation_id),
419
+ "mode": mode,
420
+ "hint": hint,
421
+ },
422
+ )
423
+
424
+ raise RuntimeHostError(
425
+ f"Unexpected unique constraint violation in {mode} mode "
426
+ f"for projector: {self.projector_id}. {hint}",
427
+ context=ctx,
428
+ ) from e
429
+
430
+ except Exception as e:
431
+ raise RuntimeHostError(
432
+ f"Failed to execute projection: {type(e).__name__}",
433
+ context=ctx,
434
+ ) from e
435
+
436
+ async def get_state(
437
+ self,
438
+ aggregate_id: UUID,
439
+ correlation_id: UUID,
440
+ ) -> dict[str, object] | None:
441
+ """Get current projected state for an aggregate.
442
+
443
+ Queries the projection table for the current state of the
444
+ specified aggregate. Uses configurable query timeout.
445
+
446
+ Args:
447
+ aggregate_id: The unique identifier of the aggregate.
448
+ correlation_id: Correlation ID for distributed tracing.
449
+
450
+ Returns:
451
+ Dictionary mapping column names (str) to their values if found,
452
+ None if no state exists. Values are typed as ``object`` because
453
+ asyncpg can return various PostgreSQL types (str, int, float,
454
+ datetime, UUID, etc.) and the schema is defined at runtime via
455
+ the projector contract.
456
+
457
+ Raises:
458
+ InfraConnectionError: If database connection fails.
459
+ InfraTimeoutError: If query times out.
460
+ RuntimeHostError: For other database errors.
461
+
462
+ Note:
463
+ **Composite Primary Key Handling**: For schemas with composite primary
464
+ keys (e.g., ``(entity_id, domain)``), this method queries using only
465
+ the first key column. This works correctly when the first column is
466
+ globally unique (e.g., UUID), but may return arbitrary results if
467
+ multiple rows share the same first-column value. For composite key
468
+ queries, use a direct database query with all key columns.
469
+ """
470
+ ctx = ModelInfraErrorContext(
471
+ transport_type=EnumInfraTransportType.DATABASE,
472
+ operation="get_state",
473
+ target_name=f"projector.{self.projector_id}",
474
+ correlation_id=correlation_id,
475
+ )
476
+
477
+ schema = self._contract.projection_schema
478
+ table_quoted = quote_identifier(schema.table)
479
+ # primary_key can be str | list[str] - normalize to first column for single-value queries
480
+ pk_column = (
481
+ schema.primary_key[0]
482
+ if isinstance(schema.primary_key, list)
483
+ else schema.primary_key
484
+ )
485
+ pk_quoted = quote_identifier(pk_column)
486
+
487
+ # Build SELECT query - table/column names from trusted contract
488
+ # S608: Safe - identifiers quoted via quote_identifier(), not user input
489
+ query = f"SELECT * FROM {table_quoted} WHERE {pk_quoted} = $1" # noqa: S608
490
+
491
+ try:
492
+ async with self._pool.acquire() as conn:
493
+ row = await conn.fetchrow(
494
+ query, aggregate_id, timeout=self._query_timeout
495
+ )
496
+
497
+ if row is None:
498
+ logger.debug(
499
+ "No state found for aggregate",
500
+ extra={
501
+ "projector_id": self.projector_id,
502
+ "aggregate_id": str(aggregate_id),
503
+ "correlation_id": str(correlation_id),
504
+ },
505
+ )
506
+ return None
507
+
508
+ # Convert asyncpg Record to dict
509
+ result: dict[str, object] = dict(row)
510
+ logger.debug(
511
+ "State retrieved for aggregate",
512
+ extra={
513
+ "projector_id": self.projector_id,
514
+ "aggregate_id": str(aggregate_id),
515
+ "correlation_id": str(correlation_id),
516
+ },
517
+ )
518
+ return result
519
+
520
+ except asyncpg.PostgresConnectionError as e:
521
+ raise InfraConnectionError(
522
+ f"Failed to connect to database for state query: {self.projector_id}",
523
+ context=ctx,
524
+ ) from e
525
+
526
+ except asyncpg.QueryCanceledError as e:
527
+ timeout_ctx = ModelTimeoutErrorContext(
528
+ transport_type=EnumInfraTransportType.DATABASE,
529
+ operation="get_state",
530
+ target_name=f"projector.{self.projector_id}",
531
+ correlation_id=correlation_id,
532
+ # timeout_seconds omitted - database timeout is connection-pool level, not available here
533
+ )
534
+ raise InfraTimeoutError(
535
+ f"State query timed out for: {self.projector_id}",
536
+ context=timeout_ctx,
537
+ ) from e
538
+
539
+ except Exception as e:
540
+ raise RuntimeHostError(
541
+ f"Failed to get state: {type(e).__name__}",
542
+ context=ctx,
543
+ ) from e
544
+
545
+ async def get_states(
546
+ self,
547
+ aggregate_ids: list[UUID],
548
+ correlation_id: UUID,
549
+ ) -> dict[UUID, dict[str, object]]:
550
+ """Get current projected states for multiple aggregates.
551
+
552
+ Bulk query for N+1 optimization. Fetches states for all provided
553
+ aggregate IDs in a single database query.
554
+
555
+ Args:
556
+ aggregate_ids: List of unique aggregate identifiers to query.
557
+ correlation_id: Correlation ID for distributed tracing.
558
+
559
+ Returns:
560
+ Dictionary mapping aggregate_id (UUID) to its state (dict).
561
+ Aggregates with no state are omitted from the result.
562
+ Empty dict if no aggregate_ids provided or none found.
563
+
564
+ Raises:
565
+ InfraConnectionError: If database connection fails.
566
+ InfraTimeoutError: If query times out.
567
+ RuntimeHostError: For other database errors.
568
+
569
+ Note:
570
+ **Composite Primary Key Handling**: For schemas with composite primary
571
+ keys, this method queries using only the first key column. See the
572
+ note on ``get_state()`` for implications and alternatives.
573
+
574
+ Example:
575
+ >>> states = await projector.get_states(
576
+ ... [order_id_1, order_id_2, order_id_3],
577
+ ... correlation_id,
578
+ ... )
579
+ >>> for order_id, state in states.items():
580
+ ... print(f"Order {order_id}: {state['status']}")
581
+ """
582
+ if not aggregate_ids:
583
+ return {}
584
+
585
+ ctx = ModelInfraErrorContext(
586
+ transport_type=EnumInfraTransportType.DATABASE,
587
+ operation="get_states",
588
+ target_name=f"projector.{self.projector_id}",
589
+ correlation_id=correlation_id,
590
+ )
591
+
592
+ schema = self._contract.projection_schema
593
+ table_quoted = quote_identifier(schema.table)
594
+ # primary_key can be str | list[str] - normalize to first column for single-value queries
595
+ pk_column = (
596
+ schema.primary_key[0]
597
+ if isinstance(schema.primary_key, list)
598
+ else schema.primary_key
599
+ )
600
+ pk_quoted = quote_identifier(pk_column)
601
+
602
+ # Build SELECT query with IN clause for bulk fetch
603
+ # S608: Safe - identifiers quoted via quote_identifier(), not user input
604
+ query = f"SELECT * FROM {table_quoted} WHERE {pk_quoted} = ANY($1)" # noqa: S608
605
+
606
+ try:
607
+ async with self._pool.acquire() as conn:
608
+ rows = await conn.fetch(
609
+ query, aggregate_ids, timeout=self._query_timeout
610
+ )
611
+
612
+ # Build result dict keyed by aggregate ID
613
+ result: dict[UUID, dict[str, object]] = {}
614
+ for row in rows:
615
+ row_dict: dict[str, object] = dict(row)
616
+ aggregate_id = row_dict.get(pk_column)
617
+ if isinstance(aggregate_id, UUID):
618
+ result[aggregate_id] = row_dict
619
+
620
+ logger.debug(
621
+ "Bulk state retrieval completed",
622
+ extra={
623
+ "projector_id": self.projector_id,
624
+ "requested_count": len(aggregate_ids),
625
+ "found_count": len(result),
626
+ "correlation_id": str(correlation_id),
627
+ },
628
+ )
629
+ return result
630
+
631
+ except asyncpg.PostgresConnectionError as e:
632
+ raise InfraConnectionError(
633
+ f"Failed to connect to database for bulk state query: {self.projector_id}",
634
+ context=ctx,
635
+ ) from e
636
+
637
+ except asyncpg.QueryCanceledError as e:
638
+ timeout_ctx = ModelTimeoutErrorContext(
639
+ transport_type=EnumInfraTransportType.DATABASE,
640
+ operation="get_states",
641
+ target_name=f"projector.{self.projector_id}",
642
+ correlation_id=correlation_id,
643
+ # timeout_seconds omitted - database timeout is connection-pool level, not available here
644
+ )
645
+ raise InfraTimeoutError(
646
+ f"Bulk state query timed out for: {self.projector_id}",
647
+ context=timeout_ctx,
648
+ ) from e
649
+
650
+ except Exception as e:
651
+ raise RuntimeHostError(
652
+ f"Failed to get states: {type(e).__name__}",
653
+ context=ctx,
654
+ ) from e
655
+
656
+ async def partial_update(
657
+ self,
658
+ aggregate_id: UUID,
659
+ updates: dict[str, object],
660
+ correlation_id: UUID,
661
+ ) -> bool:
662
+ """Perform a partial update on specific columns.
663
+
664
+ Updates only the specified columns for the row matching the aggregate_id,
665
+ without requiring a full event envelope or event-driven value extraction.
666
+ This is useful for lightweight operations like:
667
+
668
+ - Updating heartbeat timestamps (last_heartbeat_at, liveness_deadline)
669
+ - Setting timeout marker columns (ack_timeout_emitted_at)
670
+ - Updating tracking fields (updated_at)
671
+
672
+ Unlike project() which performs full event-driven projection based on
673
+ consumed_events and column source paths, partial_update() directly updates
674
+ the specified columns using the provided values.
675
+
676
+ Args:
677
+ aggregate_id: The primary key value identifying the row to update.
678
+ Must match the contract's primary_key column type (typically UUID).
679
+ updates: Dictionary mapping column names to their new values.
680
+ Column names are quoted for SQL safety. Values are passed as
681
+ parameterized query arguments for injection protection.
682
+ correlation_id: Correlation ID for distributed tracing.
683
+
684
+ Returns:
685
+ True if a row was updated (entity found and modified).
686
+ False if no row was found matching the aggregate_id.
687
+
688
+ Raises:
689
+ ProtocolConfigurationError: If updates dict is empty.
690
+ InfraConnectionError: If database connection fails.
691
+ InfraTimeoutError: If update times out.
692
+ RuntimeHostError: For other database errors.
693
+
694
+ Security:
695
+ - Column names are quoted using quote_identifier() for SQL safety
696
+ - Values use parameterized queries ($1, $2, etc.) to prevent injection
697
+ - Table and primary key names come from the trusted contract definition
698
+
699
+ Example:
700
+ >>> from datetime import UTC, datetime, timedelta
701
+ >>> # Update heartbeat tracking fields
702
+ >>> updated = await projector.partial_update(
703
+ ... aggregate_id=node_id,
704
+ ... updates={
705
+ ... "last_heartbeat_at": datetime.now(UTC),
706
+ ... "liveness_deadline": datetime.now(UTC) + timedelta(seconds=90),
707
+ ... "updated_at": datetime.now(UTC),
708
+ ... },
709
+ ... correlation_id=correlation_id,
710
+ ... )
711
+ >>> if not updated:
712
+ ... logger.warning("Entity not found for heartbeat update")
713
+
714
+ >>> # Set a timeout marker
715
+ >>> await projector.partial_update(
716
+ ... aggregate_id=node_id,
717
+ ... updates={"ack_timeout_emitted_at": datetime.now(UTC)},
718
+ ... correlation_id=correlation_id,
719
+ ... )
720
+
721
+ Related:
722
+ - OMN-1170: Converting ProjectorRegistration to declarative contracts
723
+ - project(): Full event-driven projection (uses event envelopes)
724
+ - get_state(): Read current projected state
725
+ """
726
+ ctx = ModelInfraErrorContext(
727
+ transport_type=EnumInfraTransportType.DATABASE,
728
+ operation="partial_update",
729
+ target_name=f"projector.{self.projector_id}",
730
+ correlation_id=correlation_id,
731
+ )
732
+
733
+ try:
734
+ return await self._partial_update(aggregate_id, updates, correlation_id)
735
+
736
+ except asyncpg.PostgresConnectionError as e:
737
+ raise InfraConnectionError(
738
+ f"Failed to connect to database for partial update: {self.projector_id}",
739
+ context=ctx,
740
+ ) from e
741
+
742
+ except asyncpg.QueryCanceledError as e:
743
+ timeout_ctx = ModelTimeoutErrorContext(
744
+ transport_type=EnumInfraTransportType.DATABASE,
745
+ operation="partial_update",
746
+ target_name=f"projector.{self.projector_id}",
747
+ correlation_id=correlation_id,
748
+ # timeout_seconds omitted - database timeout is connection-pool level, not available here
749
+ )
750
+ raise InfraTimeoutError(
751
+ f"Partial update timed out for: {self.projector_id}",
752
+ context=timeout_ctx,
753
+ ) from e
754
+
755
+ except ProtocolConfigurationError:
756
+ # Re-raise ProtocolConfigurationError (empty updates) as-is
757
+ raise
758
+
759
+ except Exception as e:
760
+ raise RuntimeHostError(
761
+ f"Failed to execute partial update: {type(e).__name__}",
762
+ context=ctx,
763
+ ) from e
764
+
765
+ async def upsert_partial(
766
+ self,
767
+ aggregate_id: UUID,
768
+ values: dict[str, object],
769
+ correlation_id: UUID,
770
+ conflict_columns: list[str] | None = None,
771
+ ) -> bool:
772
+ """Perform a partial UPSERT (INSERT ON CONFLICT DO UPDATE) on specific columns.
773
+
774
+ Inserts a new row if no row exists with the given conflict key(s), or updates
775
+ only the specified columns if the row already exists. This is useful for
776
+ state transition operations where:
777
+
778
+ - A new entity may be created if it doesn't exist (e.g., first registration)
779
+ - An existing entity should be updated with new state
780
+
781
+ Unlike partial_update() which only does UPDATE, upsert_partial() handles
782
+ both INSERT and UPDATE cases atomically using PostgreSQL's
783
+ INSERT ON CONFLICT DO UPDATE.
784
+
785
+ Supports composite conflict keys for tables with unique constraints on
786
+ multiple columns (e.g., ``(entity_id, domain)``).
787
+
788
+ Args:
789
+ aggregate_id: The primary aggregate identifier (for logging/tracing).
790
+ values: Dictionary mapping column names to their values.
791
+ MUST include all conflict columns specified.
792
+ Column names are quoted for SQL safety. Values are passed as
793
+ parameterized query arguments for injection protection.
794
+ correlation_id: Correlation ID for distributed tracing.
795
+ conflict_columns: Optional list of column names for ON CONFLICT clause.
796
+ If not provided, defaults to the contract's primary_key.
797
+ Use this for composite unique constraints (e.g., ["entity_id", "domain"]).
798
+
799
+ Returns:
800
+ True if a row was inserted or updated successfully.
801
+
802
+ Raises:
803
+ ProtocolConfigurationError: If values dict is empty or missing required conflict columns.
804
+ InfraConnectionError: If database connection fails.
805
+ InfraTimeoutError: If upsert times out.
806
+ RuntimeHostError: For other database errors.
807
+
808
+ Security:
809
+ - Column names are quoted using quote_identifier() for SQL safety
810
+ - Values use parameterized queries ($1, $2, etc.) to prevent injection
811
+ - Table and primary key names come from the trusted contract definition
812
+
813
+ Example:
814
+ >>> from datetime import UTC, datetime
815
+ >>> # Upsert with composite conflict key (creates row if not exists)
816
+ >>> result = await projector.upsert_partial(
817
+ ... aggregate_id=node_id,
818
+ ... values={
819
+ ... "entity_id": node_id,
820
+ ... "domain": "registration",
821
+ ... "current_state": "pending_registration",
822
+ ... "node_type": "effect",
823
+ ... "node_version": "1.0.0",
824
+ ... "capabilities": "{}",
825
+ ... "registered_at": datetime.now(UTC),
826
+ ... "updated_at": datetime.now(UTC),
827
+ ... },
828
+ ... correlation_id=correlation_id,
829
+ ... conflict_columns=["entity_id", "domain"], # Composite key
830
+ ... )
831
+
832
+ Related:
833
+ - OMN-1170: Converting ProjectorRegistration to declarative contracts
834
+ - partial_update(): UPDATE only (row must exist)
835
+ - project(): Full event-driven projection (uses event envelopes)
836
+ """
837
+ ctx = ModelInfraErrorContext(
838
+ transport_type=EnumInfraTransportType.DATABASE,
839
+ operation="upsert_partial",
840
+ target_name=f"projector.{self.projector_id}",
841
+ correlation_id=correlation_id,
842
+ )
843
+
844
+ try:
845
+ return await self._partial_upsert(
846
+ aggregate_id, values, correlation_id, conflict_columns
847
+ )
848
+
849
+ except asyncpg.PostgresConnectionError as e:
850
+ raise InfraConnectionError(
851
+ f"Failed to connect to database for partial upsert: {self.projector_id}",
852
+ context=ctx,
853
+ ) from e
854
+
855
+ except asyncpg.QueryCanceledError as e:
856
+ timeout_ctx = ModelTimeoutErrorContext(
857
+ transport_type=EnumInfraTransportType.DATABASE,
858
+ operation="upsert_partial",
859
+ target_name=f"projector.{self.projector_id}",
860
+ correlation_id=correlation_id,
861
+ # timeout_seconds omitted - database timeout is connection-pool level, not available here
862
+ )
863
+ raise InfraTimeoutError(
864
+ f"Partial upsert timed out for: {self.projector_id}",
865
+ context=timeout_ctx,
866
+ ) from e
867
+
868
+ except ProtocolConfigurationError:
869
+ # Re-raise ProtocolConfigurationError (empty values or missing PK) as-is
870
+ raise
871
+
872
+ except Exception as e:
873
+ raise RuntimeHostError(
874
+ f"Failed to execute partial upsert: {type(e).__name__}",
875
+ context=ctx,
876
+ ) from e
877
+
878
+ def _get_event_type(self, event: ModelEventEnvelope[object]) -> str:
879
+ """Extract event type from envelope.
880
+
881
+ Event type is resolved in the following order:
882
+ 1. envelope.metadata.tags['event_type'] if present
883
+ 2. payload.event_type attribute if present
884
+ 3. payload class name
885
+
886
+ Args:
887
+ event: The event envelope to extract type from.
888
+
889
+ Returns:
890
+ Event type string.
891
+ """
892
+ # Check metadata tags first
893
+ if event.metadata and event.metadata.tags:
894
+ event_type_tag = event.metadata.tags.get("event_type")
895
+ if event_type_tag is not None:
896
+ return str(event_type_tag)
897
+
898
+ # Check payload attribute
899
+ payload = event.payload
900
+ if hasattr(payload, "event_type"):
901
+ event_type_attr = payload.event_type
902
+ if event_type_attr:
903
+ return str(event_type_attr)
904
+
905
+ # Fall back to class name
906
+ return type(payload).__name__
907
+
908
+ def _extract_values(
909
+ self,
910
+ event: ModelEventEnvelope[object],
911
+ event_type: str,
912
+ ) -> dict[str, object]:
913
+ """Extract column values from event based on contract schema.
914
+
915
+ Iterates through the contract's column definitions and resolves
916
+ each column's source path to extract the value from the event.
917
+
918
+ Path Resolution Failures:
919
+ When path resolution fails (returns None), a WARNING is logged
920
+ to alert operators of potential contract configuration issues.
921
+ This is critical for production monitoring as silent None values
922
+ could indicate typos in contract source paths.
923
+
924
+ Args:
925
+ event: The event envelope containing the data.
926
+ event_type: The resolved event type for filtering.
927
+
928
+ Returns:
929
+ Dictionary mapping column names to their extracted values.
930
+ """
931
+ values: dict[str, object] = {}
932
+ schema = self._contract.projection_schema
933
+
934
+ for column in schema.columns:
935
+ # Skip columns with on_event filter that doesn't match
936
+ if column.on_event is not None and column.on_event != event_type:
937
+ continue
938
+
939
+ # Resolve the source path
940
+ value = self._resolve_path(event, column.source)
941
+
942
+ # Log warning for path resolution failures
943
+ if value is None:
944
+ if column.default is not None:
945
+ # Default will be applied - less critical but still noteworthy
946
+ logger.warning(
947
+ "Path resolution failed for column '%s' with source '%s' on "
948
+ "event type '%s'. Using default value '%s'. "
949
+ "Check contract source path for typos.",
950
+ column.name,
951
+ column.source,
952
+ event_type,
953
+ column.default,
954
+ extra={
955
+ "projector_id": self.projector_id,
956
+ "column_name": column.name,
957
+ "source_path": column.source,
958
+ "event_type": event_type,
959
+ "default_applied": True,
960
+ "default_value": column.default,
961
+ },
962
+ )
963
+ value = column.default
964
+ else:
965
+ # No default - value will be None, potentially risky
966
+ logger.warning(
967
+ "Path resolution failed for column '%s' with source '%s' on "
968
+ "event type '%s'. Value will be None. "
969
+ "Check contract source path for typos.",
970
+ column.name,
971
+ column.source,
972
+ event_type,
973
+ extra={
974
+ "projector_id": self.projector_id,
975
+ "column_name": column.name,
976
+ "source_path": column.source,
977
+ "event_type": event_type,
978
+ "default_applied": False,
979
+ },
980
+ )
981
+
982
+ values[column.name] = value
983
+
984
+ return values
985
+
986
+ def _resolve_path(
987
+ self,
988
+ root: object,
989
+ path: str,
990
+ ) -> object | None:
991
+ """Resolve a dot-notation path to extract a value.
992
+
993
+ Supports navigation through:
994
+ - Dictionary keys
995
+ - Object attributes
996
+ - Pydantic model fields (via model_dump())
997
+
998
+ Args:
999
+ root: The root object to start navigation from.
1000
+ path: Dot-notation path (e.g., "event.payload.node_name").
1001
+
1002
+ Returns:
1003
+ The resolved value, or None if path resolution fails.
1004
+
1005
+ Example:
1006
+ >>> event = ModelEventEnvelope(payload={"node_name": "test"})
1007
+ >>> self._resolve_path(event, "payload.node_name")
1008
+ 'test'
1009
+ """
1010
+ parts = path.split(".")
1011
+ current: object = root
1012
+
1013
+ for part in parts:
1014
+ if current is None:
1015
+ return None
1016
+
1017
+ # Try dictionary access
1018
+ if isinstance(current, dict):
1019
+ current = current.get(part)
1020
+ continue
1021
+
1022
+ # Try attribute access first (avoids Pydantic model_dump side effects)
1023
+ if hasattr(current, part):
1024
+ current = getattr(current, part)
1025
+ continue
1026
+
1027
+ # Fall back to Pydantic model_dump for nested access
1028
+ if isinstance(current, BaseModel):
1029
+ dumped = current.model_dump()
1030
+ current = dumped.get(part)
1031
+ continue
1032
+
1033
+ # Path resolution failed
1034
+ logger.debug(
1035
+ "Path resolution failed at part '%s'",
1036
+ part,
1037
+ extra={
1038
+ "path": path,
1039
+ "current_type": type(current).__name__,
1040
+ },
1041
+ )
1042
+ return None
1043
+
1044
+ return current
1045
+
1046
+ async def _execute_projection(
1047
+ self,
1048
+ values: dict[str, object],
1049
+ correlation_id: UUID,
1050
+ event_type: str,
1051
+ ) -> int:
1052
+ """Execute the projection based on behavior mode.
1053
+
1054
+ Dispatches to the appropriate SQL execution method based on
1055
+ the contract's behavior.mode setting.
1056
+
1057
+ Args:
1058
+ values: Column name to value mapping.
1059
+ correlation_id: Correlation ID for tracing.
1060
+ event_type: The event type being projected (for logging context).
1061
+
1062
+ Returns:
1063
+ Number of rows affected.
1064
+
1065
+ Raises:
1066
+ asyncpg exceptions on database errors.
1067
+ """
1068
+ mode = self._contract.behavior.mode
1069
+
1070
+ if mode == "upsert":
1071
+ return await self._upsert(values, correlation_id, event_type)
1072
+ elif mode == "insert_only":
1073
+ return await self._insert(values, correlation_id, event_type)
1074
+ elif mode == "append":
1075
+ return await self._append(values, correlation_id, event_type)
1076
+ else:
1077
+ # This should never happen due to contract validation
1078
+ context = ModelInfraErrorContext(
1079
+ transport_type=EnumInfraTransportType.RUNTIME,
1080
+ operation="execute_projection",
1081
+ target_name=f"projector.{self.projector_id}",
1082
+ correlation_id=correlation_id,
1083
+ )
1084
+ raise ProtocolConfigurationError(
1085
+ f"Unknown projection mode: {mode}",
1086
+ context=context,
1087
+ )
1088
+
1089
+ def __repr__(self) -> str:
1090
+ """Return string representation."""
1091
+ return (
1092
+ f"ProjectorShell("
1093
+ f"id={self.projector_id!r}, "
1094
+ f"aggregate_type={self.aggregate_type!r}, "
1095
+ f"events={len(self.consumed_events)}, "
1096
+ f"mode={self._contract.behavior.mode!r})"
1097
+ )
1098
+
1099
+
1100
+ __all__ = [
1101
+ "ProjectorShell",
1102
+ ]