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,1039 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """PostgreSQL Database Handler - MVP implementation using asyncpg async client.
4
+
5
+ Supports query and execute operations with fixed pool size (5).
6
+ Transaction support deferred to Beta. Configurable pool size deferred to Beta.
7
+
8
+ All queries MUST use parameterized statements for SQL injection protection.
9
+
10
+ Envelope-Based Routing:
11
+ This handler uses envelope-based operation routing. See CLAUDE.md section
12
+ "Intent Model Architecture > Envelope-Based Handler Routing" for the full
13
+ design pattern and how orchestrators translate intents to handler envelopes.
14
+
15
+ Single-Statement SQL Limitation
16
+ ===============================
17
+
18
+ This handler uses asyncpg's ``execute()`` and ``fetch()`` methods, which only
19
+ support **single SQL statements per call**. Multi-statement SQL (statements
20
+ separated by semicolons) is NOT supported and will raise an error.
21
+
22
+ **Example - Incorrect (will fail):**
23
+
24
+ .. code-block:: python
25
+
26
+ # This will fail - multiple statements in one call
27
+ envelope = {
28
+ "operation": "db.execute",
29
+ "payload": {
30
+ "sql": "CREATE TABLE foo (id INT); INSERT INTO foo VALUES (1);",
31
+ "parameters": [],
32
+ },
33
+ }
34
+
35
+ **Example - Correct (split into separate calls):**
36
+
37
+ .. code-block:: python
38
+
39
+ # Execute each statement separately
40
+ create_envelope = {
41
+ "operation": "db.execute",
42
+ "payload": {"sql": "CREATE TABLE foo (id INT)", "parameters": []},
43
+ }
44
+ await handler.execute(create_envelope)
45
+
46
+ insert_envelope = {
47
+ "operation": "db.execute",
48
+ "payload": {"sql": "INSERT INTO foo VALUES (1)", "parameters": []},
49
+ }
50
+ await handler.execute(insert_envelope)
51
+
52
+ This is a deliberate design choice for security and clarity:
53
+ 1. Prevents SQL injection through statement concatenation
54
+ 2. Provides clear error attribution per statement
55
+ 3. Enables proper row count tracking per operation
56
+ 4. Aligns with asyncpg's native API design
57
+
58
+ For multi-statement operations requiring atomicity, use the ``db.transaction``
59
+ operation (planned for Beta release).
60
+
61
+ Note:
62
+ Environment variable configuration (ONEX_DB_POOL_SIZE, ONEX_DB_TIMEOUT) is parsed
63
+ at module import time, not at handler instantiation. This means:
64
+
65
+ - Changes to environment variables require application restart to take effect
66
+ - Tests should use ``unittest.mock.patch.dict(os.environ, ...)`` before importing,
67
+ or use ``importlib.reload()`` to re-import the module after patching
68
+ - This is an intentional design choice for startup-time validation
69
+ """
70
+
71
+ from __future__ import annotations
72
+
73
+ import logging
74
+ from uuid import UUID, uuid4
75
+
76
+ import asyncpg
77
+
78
+ from omnibase_core.models.dispatch import ModelHandlerOutput
79
+ from omnibase_infra.enums import (
80
+ EnumHandlerType,
81
+ EnumHandlerTypeCategory,
82
+ EnumInfraTransportType,
83
+ EnumResponseStatus,
84
+ )
85
+ from omnibase_infra.errors import (
86
+ InfraAuthenticationError,
87
+ InfraConnectionError,
88
+ InfraTimeoutError,
89
+ ModelInfraErrorContext,
90
+ ModelTimeoutErrorContext,
91
+ RuntimeHostError,
92
+ )
93
+ from omnibase_infra.handlers.models import (
94
+ ModelDbDescribeResponse,
95
+ ModelDbQueryPayload,
96
+ ModelDbQueryResponse,
97
+ )
98
+ from omnibase_infra.mixins import MixinAsyncCircuitBreaker, MixinEnvelopeExtraction
99
+ from omnibase_infra.utils.util_env_parsing import parse_env_float, parse_env_int
100
+
101
+ logger = logging.getLogger(__name__)
102
+
103
+ # MVP pool size fixed at 5 connections.
104
+ # Note: Recommended range is 10-20 for production workloads.
105
+ # Configurable pool size deferred to Beta release.
106
+ _DEFAULT_POOL_SIZE: int = parse_env_int(
107
+ "ONEX_DB_POOL_SIZE",
108
+ 5,
109
+ min_value=1,
110
+ max_value=100,
111
+ transport_type=EnumInfraTransportType.DATABASE,
112
+ service_name="db_handler",
113
+ )
114
+
115
+ # Handler ID for ModelHandlerOutput
116
+ HANDLER_ID_DB: str = "db-handler"
117
+ _DEFAULT_TIMEOUT_SECONDS: float = parse_env_float(
118
+ "ONEX_DB_TIMEOUT",
119
+ 30.0,
120
+ min_value=0.1,
121
+ max_value=3600.0,
122
+ transport_type=EnumInfraTransportType.DATABASE,
123
+ service_name="db_handler",
124
+ )
125
+ _SUPPORTED_OPERATIONS: frozenset[str] = frozenset({"db.query", "db.execute"})
126
+
127
+ # Error message prefixes for PostgreSQL errors
128
+ # Used by _map_postgres_error to build descriptive error messages
129
+ _POSTGRES_ERROR_PREFIXES: dict[type[asyncpg.PostgresError], str] = {
130
+ asyncpg.PostgresSyntaxError: "SQL syntax error",
131
+ asyncpg.UndefinedTableError: "Table not found",
132
+ asyncpg.UndefinedColumnError: "Column not found",
133
+ asyncpg.UniqueViolationError: "Unique constraint violation",
134
+ asyncpg.ForeignKeyViolationError: "Foreign key constraint violation",
135
+ asyncpg.NotNullViolationError: "Not null constraint violation",
136
+ asyncpg.CheckViolationError: "Check constraint violation",
137
+ }
138
+
139
+ # PostgreSQL SQLSTATE class codes for error classification
140
+ # See: https://www.postgresql.org/docs/current/errcodes-appendix.html
141
+ #
142
+ # TRANSIENT errors (should trip circuit breaker):
143
+ # - Class 08: Connection Exception (database unreachable, connection lost)
144
+ # - Class 53: Insufficient Resources (out of memory, disk full, too many connections)
145
+ # - Class 57: Operator Intervention (admin shutdown, crash recovery, cannot connect now)
146
+ # - Class 58: System Error (I/O error, undefined file, duplicate file)
147
+ #
148
+ # PERMANENT errors (should NOT trip circuit breaker):
149
+ # - Class 23: Integrity Constraint Violation (FK, NOT NULL, unique, check)
150
+ # - Class 42: Syntax Error or Access Rule Violation (bad SQL, undefined table/column)
151
+ # - Class 28: Invalid Authorization Specification (bad credentials)
152
+ # - Class 22: Data Exception (division by zero, string data truncation)
153
+ # - Class 40: Transaction Rollback (serialization failure, deadlock detected)
154
+ #
155
+ # DESIGN DECISION: Classified as PERMANENT despite deadlocks being retry-able.
156
+ #
157
+ # Rationale:
158
+ # 1. Deadlocks indicate transaction contention, not infrastructure failure
159
+ # 2. The database is healthy - it correctly detected and resolved the deadlock
160
+ # 3. Retrying at application level (with backoff) typically succeeds
161
+ # 4. Tripping the circuit would block ALL queries, not just the conflicting ones
162
+ # 5. High deadlock rates indicate application design issues (lock ordering,
163
+ # transaction scope) that should be fixed in code, not masked by circuit breaker
164
+ #
165
+ # Note: If sustained deadlock storms occur, this is a symptom of application
166
+ # issues or schema contention that monitoring/alerting should surface, but
167
+ # the circuit breaker is not the right mitigation tool.
168
+ #
169
+ # The key insight: transient errors indicate the DATABASE INFRASTRUCTURE is unhealthy,
170
+ # while permanent errors indicate the QUERY/APPLICATION is invalid. The circuit breaker
171
+ # protects against infrastructure failures, not application bugs.
172
+ _TRANSIENT_SQLSTATE_CLASSES: frozenset[str] = frozenset({"08", "53", "57", "58"})
173
+ _PERMANENT_SQLSTATE_CLASSES: frozenset[str] = frozenset({"22", "23", "28", "42"})
174
+
175
+
176
+ class HandlerDb(MixinAsyncCircuitBreaker, MixinEnvelopeExtraction):
177
+ """PostgreSQL database handler using asyncpg connection pool (MVP: query, execute only).
178
+
179
+ Security Policy - DSN Handling:
180
+ The database connection string (DSN) contains sensitive credentials and is
181
+ treated as a secret throughout this handler. The following security measures
182
+ are enforced:
183
+
184
+ 1. DSN is stored internally in ``_dsn`` but NEVER logged or exposed in errors
185
+ 2. All error messages use generic descriptions (e.g., "check host and port")
186
+ rather than exposing connection details
187
+ 3. The ``_sanitize_dsn()`` method is available if DSN info ever needs to be
188
+ logged for debugging, but should only be used in development environments
189
+ 4. The ``describe()`` method returns capabilities without credentials
190
+
191
+ See CLAUDE.md "Error Sanitization Guidelines" for the full security policy
192
+ on what information is safe vs unsafe to include in errors and logs.
193
+
194
+ Production Database Safety:
195
+ When connecting to production databases, ensure the following safeguards:
196
+
197
+ 1. **Use read-only credentials** when possible to prevent accidental mutations
198
+ 2. **Connection isolation**: Use separate DSNs for read and write operations
199
+ 3. **Query timeouts**: Configure appropriate timeouts to prevent long-running
200
+ queries from exhausting connection pools (default: 30 seconds)
201
+ 4. **Pool limits**: Production workloads should use 10-20 connections
202
+ (currently fixed at 5 for MVP - see Beta roadmap)
203
+ 5. **SSL/TLS**: Always use encrypted connections (sslmode=require/verify-full)
204
+ 6. **Audit logging**: Enable PostgreSQL statement logging for compliance
205
+ 7. **Connection pooling**: Consider PgBouncer for high-traffic scenarios
206
+
207
+ WARNING: This handler executes arbitrary SQL. Ensure all queries use
208
+ parameterized statements to prevent SQL injection. Multi-statement SQL
209
+ is intentionally blocked for security.
210
+
211
+ Circuit Breaker:
212
+ This handler uses MixinAsyncCircuitBreaker for connection resilience.
213
+ The circuit breaker is initialized after the connection pool is created
214
+ in the initialize() method. Only connection-related errors (PostgresConnectionError,
215
+ QueryCanceledError) trip the circuit - application errors (syntax errors,
216
+ missing tables/columns) do not affect the circuit state.
217
+
218
+ States:
219
+ - CLOSED: Normal operation, requests allowed
220
+ - OPEN: Circuit tripped after threshold failures, requests blocked
221
+ - HALF_OPEN: Testing recovery after reset timeout, limited requests allowed
222
+ """
223
+
224
+ def __init__(self) -> None:
225
+ """Initialize HandlerDb in uninitialized state."""
226
+ self._pool: asyncpg.Pool | None = None
227
+ self._pool_size: int = _DEFAULT_POOL_SIZE
228
+ self._timeout: float = _DEFAULT_TIMEOUT_SECONDS
229
+ self._initialized: bool = False
230
+ self._dsn: str = ""
231
+ self._circuit_breaker_initialized: bool = False
232
+
233
+ @property
234
+ def handler_type(self) -> EnumHandlerType:
235
+ """Return the architectural role of this handler.
236
+
237
+ Returns:
238
+ EnumHandlerType.INFRA_HANDLER - This handler is an infrastructure
239
+ protocol/transport handler (as opposed to NODE_HANDLER for event
240
+ processing, PROJECTION_HANDLER for read models, or COMPUTE_HANDLER
241
+ for pure computation).
242
+
243
+ Note:
244
+ handler_type determines lifecycle, protocol selection, and runtime
245
+ invocation patterns. It answers "what is this handler in the architecture?"
246
+
247
+ See Also:
248
+ - handler_category: Behavioral classification (EFFECT/COMPUTE)
249
+ - docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
250
+ """
251
+ return EnumHandlerType.INFRA_HANDLER
252
+
253
+ @property
254
+ def handler_category(self) -> EnumHandlerTypeCategory:
255
+ """Return the behavioral classification of this handler.
256
+
257
+ Returns:
258
+ EnumHandlerTypeCategory.EFFECT - This handler performs side-effecting
259
+ I/O operations (database queries and mutations). EFFECT handlers are
260
+ not deterministic and interact with external systems.
261
+
262
+ Note:
263
+ handler_category determines security rules, determinism guarantees,
264
+ replay safety, and permissions. It answers "how does this handler
265
+ behave at runtime?"
266
+
267
+ Categories:
268
+ - COMPUTE: Pure, deterministic transformations (no side effects)
269
+ - EFFECT: Side-effecting I/O (database, HTTP, service calls)
270
+ - NONDETERMINISTIC_COMPUTE: Pure but not deterministic (UUID, random)
271
+
272
+ See Also:
273
+ - handler_type: Architectural role (INFRA_HANDLER/NODE_HANDLER/etc.)
274
+ - docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
275
+ """
276
+ return EnumHandlerTypeCategory.EFFECT
277
+
278
+ async def initialize(self, config: dict[str, object]) -> None:
279
+ """Initialize database connection pool with fixed size (5).
280
+
281
+ Args:
282
+ config: Configuration dict containing:
283
+ - dsn: PostgreSQL connection string (required)
284
+ - timeout: Optional timeout in seconds (default: 30.0)
285
+
286
+ Raises:
287
+ RuntimeHostError: If DSN is missing or pool creation fails.
288
+ """
289
+ # Generate correlation_id for initialization tracing
290
+ init_correlation_id = uuid4()
291
+
292
+ logger.info(
293
+ "Initializing %s",
294
+ self.__class__.__name__,
295
+ extra={
296
+ "handler": self.__class__.__name__,
297
+ "correlation_id": str(init_correlation_id),
298
+ },
299
+ )
300
+
301
+ dsn = config.get("dsn")
302
+ if not isinstance(dsn, str) or not dsn:
303
+ ctx = ModelInfraErrorContext(
304
+ transport_type=EnumInfraTransportType.DATABASE,
305
+ operation="initialize",
306
+ target_name="db_handler",
307
+ correlation_id=init_correlation_id,
308
+ )
309
+ raise RuntimeHostError(
310
+ "Missing or invalid 'dsn' in config - PostgreSQL connection string required",
311
+ context=ctx,
312
+ )
313
+
314
+ timeout_raw = config.get("timeout", _DEFAULT_TIMEOUT_SECONDS)
315
+ if isinstance(timeout_raw, int | float):
316
+ self._timeout = float(timeout_raw)
317
+
318
+ try:
319
+ self._pool = await asyncpg.create_pool(
320
+ dsn=dsn,
321
+ min_size=1,
322
+ max_size=self._pool_size,
323
+ command_timeout=self._timeout,
324
+ )
325
+ self._dsn = dsn
326
+ # Note: DSN stored internally but never logged or exposed in errors.
327
+ # Use _sanitize_dsn() if DSN info ever needs to be logged.
328
+ self._initialized = True
329
+
330
+ # Initialize circuit breaker after pool creation succeeds
331
+ self._init_circuit_breaker(
332
+ threshold=5,
333
+ reset_timeout=30.0,
334
+ service_name="db_handler",
335
+ transport_type=EnumInfraTransportType.DATABASE,
336
+ )
337
+ self._circuit_breaker_initialized = True
338
+
339
+ logger.info(
340
+ "%s initialized successfully",
341
+ self.__class__.__name__,
342
+ extra={
343
+ "handler": self.__class__.__name__,
344
+ "pool_min_size": 1,
345
+ "pool_max_size": self._pool_size,
346
+ "timeout_seconds": self._timeout,
347
+ "correlation_id": str(init_correlation_id),
348
+ },
349
+ )
350
+ except asyncpg.InvalidPasswordError as e:
351
+ ctx = ModelInfraErrorContext(
352
+ transport_type=EnumInfraTransportType.DATABASE,
353
+ operation="initialize",
354
+ target_name="db_handler",
355
+ correlation_id=init_correlation_id,
356
+ )
357
+ raise InfraAuthenticationError(
358
+ "Database authentication failed - check credentials", context=ctx
359
+ ) from e
360
+ except asyncpg.InvalidCatalogNameError as e:
361
+ ctx = ModelInfraErrorContext(
362
+ transport_type=EnumInfraTransportType.DATABASE,
363
+ operation="initialize",
364
+ target_name="db_handler",
365
+ correlation_id=init_correlation_id,
366
+ )
367
+ raise RuntimeHostError(
368
+ "Database not found - check database name", context=ctx
369
+ ) from e
370
+ except OSError as e:
371
+ ctx = ModelInfraErrorContext(
372
+ transport_type=EnumInfraTransportType.DATABASE,
373
+ operation="initialize",
374
+ target_name="db_handler",
375
+ correlation_id=init_correlation_id,
376
+ )
377
+ raise InfraConnectionError(
378
+ "Failed to connect to database - check host and port", context=ctx
379
+ ) from e
380
+ except Exception as e:
381
+ ctx = ModelInfraErrorContext(
382
+ transport_type=EnumInfraTransportType.DATABASE,
383
+ operation="initialize",
384
+ target_name="db_handler",
385
+ correlation_id=init_correlation_id,
386
+ )
387
+ raise RuntimeHostError(
388
+ f"Failed to initialize database pool: {type(e).__name__}", context=ctx
389
+ ) from e
390
+
391
+ async def shutdown(self) -> None:
392
+ """Close database connection pool and release resources."""
393
+ # Reset circuit breaker state
394
+ if self._circuit_breaker_initialized:
395
+ async with self._circuit_breaker_lock:
396
+ await self._reset_circuit_breaker()
397
+ self._circuit_breaker_initialized = False
398
+
399
+ if self._pool is not None:
400
+ await self._pool.close()
401
+ self._pool = None
402
+ self._initialized = False
403
+ logger.info("HandlerDb shutdown complete")
404
+
405
+ async def execute(
406
+ self, envelope: dict[str, object]
407
+ ) -> ModelHandlerOutput[ModelDbQueryResponse]:
408
+ """Execute database operation (db.query or db.execute) from envelope.
409
+
410
+ Args:
411
+ envelope: Request envelope containing:
412
+ - operation: "db.query" or "db.execute"
413
+ - payload: dict with "sql" (required) and "parameters" (optional list)
414
+ - correlation_id: Optional correlation ID for tracing
415
+ - envelope_id: Optional envelope ID for causality tracking
416
+
417
+ Returns:
418
+ ModelHandlerOutput[ModelDbQueryResponse] containing:
419
+ - result: ModelDbQueryResponse with status, payload, and correlation_id
420
+ - input_envelope_id: UUID for causality tracking
421
+ - correlation_id: UUID for request/response correlation
422
+ - handler_id: "db-handler"
423
+
424
+ Raises:
425
+ RuntimeHostError: If handler not initialized or invalid input.
426
+ InfraConnectionError: If database connection fails.
427
+ InfraTimeoutError: If query times out.
428
+ """
429
+ correlation_id = self._extract_correlation_id(envelope)
430
+ input_envelope_id = self._extract_envelope_id(envelope)
431
+
432
+ if not self._initialized or self._pool is None:
433
+ ctx = ModelInfraErrorContext(
434
+ transport_type=EnumInfraTransportType.DATABASE,
435
+ operation="execute",
436
+ target_name="db_handler",
437
+ correlation_id=correlation_id,
438
+ )
439
+ raise RuntimeHostError(
440
+ "HandlerDb not initialized. Call initialize() first.", context=ctx
441
+ )
442
+
443
+ operation = envelope.get("operation")
444
+ if not isinstance(operation, str):
445
+ ctx = ModelInfraErrorContext(
446
+ transport_type=EnumInfraTransportType.DATABASE,
447
+ operation="execute",
448
+ target_name="db_handler",
449
+ correlation_id=correlation_id,
450
+ )
451
+ raise RuntimeHostError(
452
+ "Missing or invalid 'operation' in envelope", context=ctx
453
+ )
454
+
455
+ if operation not in _SUPPORTED_OPERATIONS:
456
+ ctx = ModelInfraErrorContext(
457
+ transport_type=EnumInfraTransportType.DATABASE,
458
+ operation=operation,
459
+ target_name="db_handler",
460
+ correlation_id=correlation_id,
461
+ )
462
+ raise RuntimeHostError(
463
+ f"Operation '{operation}' not supported in MVP. Available: {', '.join(sorted(_SUPPORTED_OPERATIONS))}",
464
+ context=ctx,
465
+ )
466
+
467
+ payload = envelope.get("payload")
468
+ if not isinstance(payload, dict):
469
+ ctx = ModelInfraErrorContext(
470
+ transport_type=EnumInfraTransportType.DATABASE,
471
+ operation=operation,
472
+ target_name="db_handler",
473
+ correlation_id=correlation_id,
474
+ )
475
+ raise RuntimeHostError(
476
+ "Missing or invalid 'payload' in envelope", context=ctx
477
+ )
478
+
479
+ sql = payload.get("sql")
480
+ if not isinstance(sql, str) or not sql.strip():
481
+ ctx = ModelInfraErrorContext(
482
+ transport_type=EnumInfraTransportType.DATABASE,
483
+ operation=operation,
484
+ target_name="db_handler",
485
+ correlation_id=correlation_id,
486
+ )
487
+ raise RuntimeHostError("Missing or invalid 'sql' in payload", context=ctx)
488
+
489
+ parameters = self._extract_parameters(payload, operation, correlation_id)
490
+
491
+ if operation == "db.query":
492
+ return await self._execute_query(
493
+ sql, parameters, correlation_id, input_envelope_id
494
+ )
495
+ else: # db.execute
496
+ return await self._execute_statement(
497
+ sql, parameters, correlation_id, input_envelope_id
498
+ )
499
+
500
+ def _sanitize_dsn(self, dsn: str) -> str:
501
+ """Sanitize DSN by removing password for safe logging.
502
+
503
+ SECURITY: This method exists to support debugging scenarios where
504
+ connection information may be helpful, while ensuring credentials
505
+ are never exposed. The raw DSN should NEVER be logged directly.
506
+
507
+ Uses urllib.parse for robust parsing instead of regex, handling
508
+ edge cases like IPv6 addresses and URL-encoded passwords.
509
+
510
+ Args:
511
+ dsn: Raw PostgreSQL connection string containing credentials.
512
+
513
+ Returns:
514
+ Sanitized DSN with password replaced by '***'.
515
+
516
+ Example:
517
+ >>> handler._sanitize_dsn("postgresql://user:secret@host:5432/db")
518
+ 'postgresql://user:***@host:5432/db'
519
+
520
+ >>> handler._sanitize_dsn("postgresql://user:p%40ss@[::1]:5432/db")
521
+ 'postgresql://user:***@[::1]:5432/db'
522
+
523
+ Note:
524
+ This method is intentionally NOT used in production error paths.
525
+ It exists as a utility for development/debugging only. See class
526
+ docstring "Security Policy - DSN Handling" for full policy.
527
+ """
528
+ from omnibase_infra.utils.util_dsn_validation import sanitize_dsn
529
+
530
+ return sanitize_dsn(dsn)
531
+
532
+ def _extract_parameters(
533
+ self, payload: dict[str, object], operation: str, correlation_id: UUID
534
+ ) -> list[object]:
535
+ """Extract and validate parameters from payload."""
536
+ params_raw = payload.get("parameters")
537
+ if params_raw is None:
538
+ return []
539
+ if isinstance(params_raw, list):
540
+ return list(params_raw)
541
+ ctx = ModelInfraErrorContext(
542
+ transport_type=EnumInfraTransportType.DATABASE,
543
+ operation=operation,
544
+ target_name="db_handler",
545
+ correlation_id=correlation_id,
546
+ )
547
+ raise RuntimeHostError(
548
+ "Invalid 'parameters' in payload - must be a list", context=ctx
549
+ )
550
+
551
+ def _is_transient_error(self, error: asyncpg.PostgresError) -> bool:
552
+ """Determine if a PostgreSQL error is transient (should trip circuit breaker).
553
+
554
+ Transient errors indicate infrastructure issues where retrying later may succeed:
555
+ - Connection failures (Class 08)
556
+ - Resource exhaustion (Class 53)
557
+ - Server shutdown/restart (Class 57)
558
+ - System I/O errors (Class 58)
559
+
560
+ Permanent errors indicate application/query bugs that won't be fixed by retrying:
561
+ - Constraint violations (Class 23): FK, NOT NULL, unique, check
562
+ - Syntax errors (Class 42): bad SQL, undefined table/column
563
+ - Authorization failures (Class 28): invalid credentials
564
+ - Data exceptions (Class 22): division by zero, truncation
565
+
566
+ The circuit breaker ONLY trips on transient errors because:
567
+ 1. Transient errors suggest the database infrastructure is unhealthy
568
+ 2. Opening the circuit prevents cascading failures and gives the DB time to recover
569
+ 3. Permanent errors are application bugs that should be fixed in code, not retried
570
+
571
+ Args:
572
+ error: The asyncpg PostgresError exception to classify.
573
+
574
+ Returns:
575
+ True if the error is transient (should increment circuit breaker failure count),
576
+ False if the error is permanent (should NOT affect circuit breaker state).
577
+
578
+ Examples:
579
+ >>> # Connection lost - transient, should trip circuit
580
+ >>> handler._is_transient_error(asyncpg.PostgresConnectionError())
581
+ True
582
+
583
+ >>> # FK violation - permanent, should NOT trip circuit
584
+ >>> handler._is_transient_error(asyncpg.ForeignKeyViolationError())
585
+ False
586
+ """
587
+ # Get SQLSTATE code from exception (5-character code like '23503')
588
+ sqlstate = getattr(error, "sqlstate", None)
589
+
590
+ if sqlstate is None:
591
+ # No SQLSTATE available - fall back to exception type classification
592
+ # Connection-related errors are always transient
593
+ if isinstance(error, asyncpg.PostgresConnectionError):
594
+ return True
595
+ # Query canceled (timeout) is transient - indicates server overload
596
+ if isinstance(error, asyncpg.QueryCanceledError):
597
+ return True
598
+ # Default: assume permanent (don't trip circuit for unknown errors)
599
+ # This is conservative - we'd rather miss a transient error than
600
+ # incorrectly trip the circuit on application bugs
601
+ logger.debug(
602
+ "No SQLSTATE for PostgreSQL error, defaulting to permanent classification",
603
+ extra={
604
+ "error_type": type(error).__name__,
605
+ },
606
+ )
607
+ return False
608
+
609
+ # Extract class code (first 2 characters of SQLSTATE)
610
+ # e.g., '23503' -> '23' (Integrity Constraint Violation)
611
+ sqlstate_class = sqlstate[:2]
612
+
613
+ # Check if class is explicitly transient
614
+ if sqlstate_class in _TRANSIENT_SQLSTATE_CLASSES:
615
+ logger.debug(
616
+ "Classified PostgreSQL error as TRANSIENT (will trip circuit)",
617
+ extra={
618
+ "sqlstate": sqlstate,
619
+ "sqlstate_class": sqlstate_class,
620
+ "error_type": type(error).__name__,
621
+ },
622
+ )
623
+ return True
624
+
625
+ # Check if class is explicitly permanent
626
+ if sqlstate_class in _PERMANENT_SQLSTATE_CLASSES:
627
+ logger.debug(
628
+ "Classified PostgreSQL error as PERMANENT (will NOT trip circuit)",
629
+ extra={
630
+ "sqlstate": sqlstate,
631
+ "sqlstate_class": sqlstate_class,
632
+ "error_type": type(error).__name__,
633
+ },
634
+ )
635
+ return False
636
+
637
+ # Unknown class - log warning and default to permanent (conservative)
638
+ # Unknown errors are more likely to be application bugs than infrastructure issues
639
+ logger.warning(
640
+ "Unknown PostgreSQL SQLSTATE class, defaulting to permanent classification",
641
+ extra={
642
+ "sqlstate": sqlstate,
643
+ "sqlstate_class": sqlstate_class,
644
+ "error_type": type(error).__name__,
645
+ },
646
+ )
647
+ return False
648
+
649
+ async def _execute_query(
650
+ self,
651
+ sql: str,
652
+ parameters: list[object],
653
+ correlation_id: UUID,
654
+ input_envelope_id: UUID,
655
+ ) -> ModelHandlerOutput[ModelDbQueryResponse]:
656
+ """Execute SELECT query and return rows."""
657
+ if self._pool is None:
658
+ ctx = ModelInfraErrorContext(
659
+ transport_type=EnumInfraTransportType.DATABASE,
660
+ operation="db.query",
661
+ target_name="db_handler",
662
+ correlation_id=correlation_id,
663
+ )
664
+ raise RuntimeHostError(
665
+ "HandlerDb not initialized - call initialize() first", context=ctx
666
+ )
667
+
668
+ ctx = ModelInfraErrorContext(
669
+ transport_type=EnumInfraTransportType.DATABASE,
670
+ operation="db.query",
671
+ target_name="db_handler",
672
+ correlation_id=correlation_id,
673
+ )
674
+
675
+ # Check circuit breaker before operation
676
+ if self._circuit_breaker_initialized:
677
+ async with self._circuit_breaker_lock:
678
+ await self._check_circuit_breaker(
679
+ operation="db.query",
680
+ correlation_id=correlation_id,
681
+ )
682
+
683
+ try:
684
+ async with self._pool.acquire() as conn:
685
+ rows = await conn.fetch(sql, *parameters)
686
+
687
+ # Reset circuit breaker immediately after successful operation,
688
+ # within connection context to avoid race conditions
689
+ if self._circuit_breaker_initialized:
690
+ async with self._circuit_breaker_lock:
691
+ await self._reset_circuit_breaker()
692
+
693
+ return self._build_response(
694
+ [dict(row) for row in rows],
695
+ len(rows),
696
+ correlation_id,
697
+ input_envelope_id,
698
+ )
699
+ except asyncpg.QueryCanceledError as e:
700
+ # Record failure for timeout errors (database overloaded)
701
+ if self._circuit_breaker_initialized:
702
+ async with self._circuit_breaker_lock:
703
+ await self._record_circuit_failure(
704
+ operation="db.query",
705
+ correlation_id=correlation_id,
706
+ )
707
+ timeout_ctx = ModelTimeoutErrorContext(
708
+ transport_type=EnumInfraTransportType.DATABASE,
709
+ operation="db.query",
710
+ target_name="db_handler",
711
+ correlation_id=correlation_id,
712
+ timeout_seconds=self._timeout,
713
+ )
714
+ raise InfraTimeoutError(
715
+ f"Query timed out after {self._timeout}s",
716
+ context=timeout_ctx,
717
+ ) from e
718
+ except asyncpg.PostgresConnectionError as e:
719
+ # Record failure for connection errors (database unavailable)
720
+ if self._circuit_breaker_initialized:
721
+ async with self._circuit_breaker_lock:
722
+ await self._record_circuit_failure(
723
+ operation="db.query",
724
+ correlation_id=correlation_id,
725
+ )
726
+ raise InfraConnectionError(
727
+ "Database connection lost during query", context=ctx
728
+ ) from e
729
+ except asyncpg.PostgresSyntaxError as e:
730
+ # Application error - do NOT trip circuit
731
+ raise RuntimeHostError(f"SQL syntax error: {e.message}", context=ctx) from e
732
+ except asyncpg.UndefinedTableError as e:
733
+ # Application error - do NOT trip circuit
734
+ raise RuntimeHostError(f"Table not found: {e.message}", context=ctx) from e
735
+ except asyncpg.UndefinedColumnError as e:
736
+ # Application error - do NOT trip circuit
737
+ raise RuntimeHostError(f"Column not found: {e.message}", context=ctx) from e
738
+ except asyncpg.ForeignKeyViolationError as e:
739
+ # Application error - do NOT trip circuit
740
+ raise RuntimeHostError(
741
+ f"Foreign key constraint violation: {e.message}", context=ctx
742
+ ) from e
743
+ except asyncpg.NotNullViolationError as e:
744
+ # Application error - do NOT trip circuit
745
+ raise RuntimeHostError(
746
+ f"Not null constraint violation: {e.message}", context=ctx
747
+ ) from e
748
+ except asyncpg.UniqueViolationError as e:
749
+ # Application error - do NOT trip circuit
750
+ raise RuntimeHostError(
751
+ f"Unique constraint violation: {e.message}", context=ctx
752
+ ) from e
753
+ except asyncpg.PostgresError as e:
754
+ # Generic PostgreSQL error - use intelligent classification based on SQLSTATE
755
+ # to determine if this is a transient infrastructure issue or permanent app bug
756
+ if self._is_transient_error(e):
757
+ # Transient error (e.g., resource exhaustion, system error)
758
+ # Record failure to potentially trip circuit breaker
759
+ if self._circuit_breaker_initialized:
760
+ async with self._circuit_breaker_lock:
761
+ await self._record_circuit_failure(
762
+ operation="db.query",
763
+ correlation_id=correlation_id,
764
+ )
765
+ # Re-raise as RuntimeHostError regardless of classification
766
+ raise RuntimeHostError(
767
+ f"Database error: {type(e).__name__}", context=ctx
768
+ ) from e
769
+
770
+ async def _execute_statement(
771
+ self,
772
+ sql: str,
773
+ parameters: list[object],
774
+ correlation_id: UUID,
775
+ input_envelope_id: UUID,
776
+ ) -> ModelHandlerOutput[ModelDbQueryResponse]:
777
+ """Execute INSERT/UPDATE/DELETE statement and return affected row count."""
778
+ if self._pool is None:
779
+ ctx = ModelInfraErrorContext(
780
+ transport_type=EnumInfraTransportType.DATABASE,
781
+ operation="db.execute",
782
+ target_name="db_handler",
783
+ correlation_id=correlation_id,
784
+ )
785
+ raise RuntimeHostError(
786
+ "HandlerDb not initialized - call initialize() first", context=ctx
787
+ )
788
+
789
+ ctx = ModelInfraErrorContext(
790
+ transport_type=EnumInfraTransportType.DATABASE,
791
+ operation="db.execute",
792
+ target_name="db_handler",
793
+ correlation_id=correlation_id,
794
+ )
795
+
796
+ # Check circuit breaker before operation
797
+ if self._circuit_breaker_initialized:
798
+ async with self._circuit_breaker_lock:
799
+ await self._check_circuit_breaker(
800
+ operation="db.execute",
801
+ correlation_id=correlation_id,
802
+ )
803
+
804
+ try:
805
+ async with self._pool.acquire() as conn:
806
+ result = await conn.execute(sql, *parameters)
807
+
808
+ # Reset circuit breaker immediately after successful operation,
809
+ # within connection context to avoid race conditions
810
+ if self._circuit_breaker_initialized:
811
+ async with self._circuit_breaker_lock:
812
+ await self._reset_circuit_breaker()
813
+
814
+ # asyncpg returns string like "INSERT 0 1" or "UPDATE 5"
815
+ row_count = self._parse_row_count(result)
816
+ return self._build_response(
817
+ [], row_count, correlation_id, input_envelope_id
818
+ )
819
+ except asyncpg.QueryCanceledError as e:
820
+ # Record failure for timeout errors (database overloaded)
821
+ if self._circuit_breaker_initialized:
822
+ async with self._circuit_breaker_lock:
823
+ await self._record_circuit_failure(
824
+ operation="db.execute",
825
+ correlation_id=correlation_id,
826
+ )
827
+ timeout_ctx = ModelTimeoutErrorContext(
828
+ transport_type=EnumInfraTransportType.DATABASE,
829
+ operation="db.execute",
830
+ target_name="db_handler",
831
+ correlation_id=correlation_id,
832
+ timeout_seconds=self._timeout,
833
+ )
834
+ raise InfraTimeoutError(
835
+ f"Statement timed out after {self._timeout}s",
836
+ context=timeout_ctx,
837
+ ) from e
838
+ except asyncpg.PostgresConnectionError as e:
839
+ # Record failure for connection errors (database unavailable)
840
+ if self._circuit_breaker_initialized:
841
+ async with self._circuit_breaker_lock:
842
+ await self._record_circuit_failure(
843
+ operation="db.execute",
844
+ correlation_id=correlation_id,
845
+ )
846
+ raise InfraConnectionError(
847
+ "Database connection lost during statement execution", context=ctx
848
+ ) from e
849
+ except asyncpg.PostgresSyntaxError as e:
850
+ # Application error - do NOT trip circuit
851
+ raise RuntimeHostError(f"SQL syntax error: {e.message}", context=ctx) from e
852
+ except asyncpg.UndefinedTableError as e:
853
+ # Application error - do NOT trip circuit
854
+ raise RuntimeHostError(f"Table not found: {e.message}", context=ctx) from e
855
+ except asyncpg.UndefinedColumnError as e:
856
+ # Application error - do NOT trip circuit
857
+ raise RuntimeHostError(f"Column not found: {e.message}", context=ctx) from e
858
+ except asyncpg.ForeignKeyViolationError as e:
859
+ # Application error - do NOT trip circuit
860
+ raise RuntimeHostError(
861
+ f"Foreign key constraint violation: {e.message}", context=ctx
862
+ ) from e
863
+ except asyncpg.NotNullViolationError as e:
864
+ # Application error - do NOT trip circuit
865
+ raise RuntimeHostError(
866
+ f"Not null constraint violation: {e.message}", context=ctx
867
+ ) from e
868
+ except asyncpg.UniqueViolationError as e:
869
+ # Application error - do NOT trip circuit
870
+ raise RuntimeHostError(
871
+ f"Unique constraint violation: {e.message}", context=ctx
872
+ ) from e
873
+ except asyncpg.PostgresError as e:
874
+ # Generic PostgreSQL error - use intelligent classification based on SQLSTATE
875
+ # to determine if this is a transient infrastructure issue or permanent app bug
876
+ if self._is_transient_error(e):
877
+ # Transient error (e.g., resource exhaustion, system error)
878
+ # Record failure to potentially trip circuit breaker
879
+ if self._circuit_breaker_initialized:
880
+ async with self._circuit_breaker_lock:
881
+ await self._record_circuit_failure(
882
+ operation="db.execute",
883
+ correlation_id=correlation_id,
884
+ )
885
+ # Re-raise as RuntimeHostError regardless of classification
886
+ raise RuntimeHostError(
887
+ f"Database error: {type(e).__name__}", context=ctx
888
+ ) from e
889
+
890
+ def _parse_row_count(self, result: str) -> int:
891
+ """Parse row count from asyncpg execute result string.
892
+
893
+ asyncpg returns strings like:
894
+ - "INSERT 0 1" -> 1 row inserted
895
+ - "UPDATE 5" -> 5 rows updated
896
+ - "DELETE 3" -> 3 rows deleted
897
+ """
898
+ try:
899
+ parts = result.split()
900
+ if len(parts) >= 2:
901
+ return int(parts[-1])
902
+ except (ValueError, IndexError):
903
+ pass
904
+ return 0
905
+
906
+ def _map_postgres_error(
907
+ self,
908
+ exc: asyncpg.PostgresError,
909
+ ctx: ModelInfraErrorContext,
910
+ ) -> RuntimeHostError | InfraTimeoutError | InfraConnectionError:
911
+ """Map asyncpg exception to ONEX infrastructure error.
912
+
913
+ This helper reduces complexity of _execute_statement and _execute_query
914
+ by centralizing exception-to-error mapping logic.
915
+
916
+ Args:
917
+ exc: The asyncpg exception that was raised.
918
+ ctx: Error context with transport type, operation, and correlation ID.
919
+
920
+ Returns:
921
+ Appropriate ONEX infrastructure error based on exception type.
922
+ """
923
+ exc_type = type(exc)
924
+
925
+ # Special cases requiring specific error types or additional arguments
926
+ if exc_type is asyncpg.QueryCanceledError:
927
+ # Convert ModelInfraErrorContext to ModelTimeoutErrorContext for stricter typing
928
+ timeout_ctx = ModelTimeoutErrorContext(
929
+ transport_type=ctx.transport_type or EnumInfraTransportType.DATABASE,
930
+ operation=ctx.operation or "db.statement",
931
+ target_name=ctx.target_name,
932
+ correlation_id=ctx.correlation_id,
933
+ timeout_seconds=self._timeout,
934
+ )
935
+ return InfraTimeoutError(
936
+ f"Statement timed out after {self._timeout}s",
937
+ context=timeout_ctx,
938
+ )
939
+
940
+ if exc_type is asyncpg.PostgresConnectionError:
941
+ return InfraConnectionError(
942
+ "Database connection lost during statement execution",
943
+ context=ctx,
944
+ )
945
+
946
+ # All other errors map to RuntimeHostError with descriptive message
947
+ prefix = _POSTGRES_ERROR_PREFIXES.get(exc_type, "Database error")
948
+ # Use message attribute if available and non-empty, else use type name
949
+ message = getattr(exc, "message", None) or type(exc).__name__
950
+ return RuntimeHostError(f"{prefix}: {message}", context=ctx)
951
+
952
+ def _build_response(
953
+ self,
954
+ rows: list[dict[str, object]],
955
+ row_count: int,
956
+ correlation_id: UUID,
957
+ input_envelope_id: UUID,
958
+ ) -> ModelHandlerOutput[ModelDbQueryResponse]:
959
+ """Build response wrapped in ModelHandlerOutput from query/execute result."""
960
+ result = ModelDbQueryResponse(
961
+ status=EnumResponseStatus.SUCCESS,
962
+ payload=ModelDbQueryPayload(rows=rows, row_count=row_count),
963
+ correlation_id=correlation_id,
964
+ )
965
+ return ModelHandlerOutput.for_compute(
966
+ input_envelope_id=input_envelope_id,
967
+ correlation_id=correlation_id,
968
+ handler_id=HANDLER_ID_DB,
969
+ result=result,
970
+ )
971
+
972
+ def describe(self) -> ModelDbDescribeResponse:
973
+ """Return handler metadata and capabilities for introspection.
974
+
975
+ This method exposes the handler's type classification along with its
976
+ operational configuration and capabilities.
977
+
978
+ Returns:
979
+ ModelDbDescribeResponse containing:
980
+ - handler_type: Architectural role from handler_type property
981
+ (e.g., "infra_handler"). See EnumHandlerType for valid values.
982
+ - handler_category: Behavioral classification from handler_category
983
+ property (e.g., "effect"). See EnumHandlerTypeCategory for valid values.
984
+ - supported_operations: List of supported operations
985
+ - pool_size: Connection pool size
986
+ - timeout_seconds: Query timeout in seconds
987
+ - initialized: Whether the handler is initialized
988
+ - version: Handler version string
989
+
990
+ Note:
991
+ The handler_type and handler_category fields form the handler
992
+ classification system:
993
+
994
+ 1. handler_type (architectural role): Determines lifecycle and invocation
995
+ patterns. This handler is INFRA_HANDLER (protocol/transport handler).
996
+
997
+ 2. handler_category (behavioral classification): Determines security rules
998
+ and replay safety. This handler is EFFECT (side-effecting I/O).
999
+
1000
+ The transport type for this handler is DATABASE (PostgreSQL).
1001
+
1002
+ Security Consideration:
1003
+ The circuit_breaker field exposes operational state including failure counts.
1004
+ This information is intended for internal monitoring and observability.
1005
+
1006
+ WARNING: If describe() is exposed via external APIs, failure counts could
1007
+ reveal information useful to attackers (e.g., timing attacks when circuit
1008
+ is about to open). For external exposure, consider:
1009
+
1010
+ 1. Restricting describe() to authenticated admin/monitoring endpoints only
1011
+ 2. Sanitizing output to show only state (OPEN/CLOSED/HALF_OPEN), not counts
1012
+ 3. Rate-limiting describe() calls to prevent information harvesting
1013
+
1014
+ The current implementation assumes describe() is used for internal
1015
+ monitoring dashboards and health checks, not public APIs.
1016
+
1017
+ See Also:
1018
+ - handler_type property: Full documentation of architectural role
1019
+ - handler_category property: Full documentation of behavioral classification
1020
+ - docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
1021
+ """
1022
+ # Get circuit breaker state if initialized
1023
+ cb_state: dict[str, object] | None = None
1024
+ if self._circuit_breaker_initialized:
1025
+ cb_state = self._get_circuit_breaker_state()
1026
+
1027
+ return ModelDbDescribeResponse(
1028
+ handler_type=self.handler_type.value,
1029
+ handler_category=self.handler_category.value,
1030
+ supported_operations=sorted(_SUPPORTED_OPERATIONS),
1031
+ pool_size=self._pool_size,
1032
+ timeout_seconds=self._timeout,
1033
+ initialized=self._initialized,
1034
+ version="0.1.0-mvp",
1035
+ circuit_breaker=cb_state,
1036
+ )
1037
+
1038
+
1039
+ __all__: list[str] = ["HandlerDb"]