omnibase_infra 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (833) hide show
  1. omnibase_infra/__init__.py +101 -0
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/__init__.py +1 -0
  8. omnibase_infra/cli/commands.py +216 -0
  9. omnibase_infra/clients/__init__.py +0 -0
  10. omnibase_infra/configs/widget_mapping.yaml +176 -0
  11. omnibase_infra/constants_topic_patterns.py +26 -0
  12. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
  13. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
  14. omnibase_infra/decorators/__init__.py +29 -0
  15. omnibase_infra/decorators/allow_any.py +109 -0
  16. omnibase_infra/dlq/__init__.py +90 -0
  17. omnibase_infra/dlq/constants_dlq.py +57 -0
  18. omnibase_infra/dlq/models/__init__.py +26 -0
  19. omnibase_infra/dlq/models/enum_replay_status.py +37 -0
  20. omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
  21. omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
  22. omnibase_infra/dlq/service_dlq_tracking.py +611 -0
  23. omnibase_infra/enums/__init__.py +132 -0
  24. omnibase_infra/enums/enum_any_type_violation.py +104 -0
  25. omnibase_infra/enums/enum_backend_type.py +27 -0
  26. omnibase_infra/enums/enum_capture_outcome.py +42 -0
  27. omnibase_infra/enums/enum_capture_state.py +88 -0
  28. omnibase_infra/enums/enum_chain_violation_type.py +119 -0
  29. omnibase_infra/enums/enum_circuit_state.py +51 -0
  30. omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
  31. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  32. omnibase_infra/enums/enum_contract_type.py +84 -0
  33. omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
  34. omnibase_infra/enums/enum_dispatch_status.py +191 -0
  35. omnibase_infra/enums/enum_environment.py +46 -0
  36. omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
  37. omnibase_infra/enums/enum_handler_error_type.py +111 -0
  38. omnibase_infra/enums/enum_handler_loader_error.py +178 -0
  39. omnibase_infra/enums/enum_handler_source_mode.py +86 -0
  40. omnibase_infra/enums/enum_handler_source_type.py +87 -0
  41. omnibase_infra/enums/enum_handler_type.py +77 -0
  42. omnibase_infra/enums/enum_handler_type_category.py +61 -0
  43. omnibase_infra/enums/enum_infra_transport_type.py +73 -0
  44. omnibase_infra/enums/enum_introspection_reason.py +154 -0
  45. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  46. omnibase_infra/enums/enum_message_category.py +213 -0
  47. omnibase_infra/enums/enum_node_archetype.py +74 -0
  48. omnibase_infra/enums/enum_node_output_type.py +185 -0
  49. omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
  50. omnibase_infra/enums/enum_policy_type.py +32 -0
  51. omnibase_infra/enums/enum_registration_state.py +261 -0
  52. omnibase_infra/enums/enum_registration_status.py +33 -0
  53. omnibase_infra/enums/enum_registry_response_status.py +28 -0
  54. omnibase_infra/enums/enum_response_status.py +26 -0
  55. omnibase_infra/enums/enum_retry_error_category.py +98 -0
  56. omnibase_infra/enums/enum_security_rule_id.py +103 -0
  57. omnibase_infra/enums/enum_selection_strategy.py +91 -0
  58. omnibase_infra/enums/enum_topic_standard.py +42 -0
  59. omnibase_infra/enums/enum_validation_severity.py +78 -0
  60. omnibase_infra/errors/__init__.py +160 -0
  61. omnibase_infra/errors/error_architecture_violation.py +152 -0
  62. omnibase_infra/errors/error_binding_resolution.py +128 -0
  63. omnibase_infra/errors/error_chain_propagation.py +188 -0
  64. omnibase_infra/errors/error_compute_registry.py +95 -0
  65. omnibase_infra/errors/error_consul.py +132 -0
  66. omnibase_infra/errors/error_container_wiring.py +243 -0
  67. omnibase_infra/errors/error_event_bus_registry.py +105 -0
  68. omnibase_infra/errors/error_infra.py +610 -0
  69. omnibase_infra/errors/error_message_type_registry.py +101 -0
  70. omnibase_infra/errors/error_policy_registry.py +115 -0
  71. omnibase_infra/errors/error_vault.py +123 -0
  72. omnibase_infra/event_bus/__init__.py +72 -0
  73. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
  74. omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
  75. omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
  76. omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
  77. omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
  78. omnibase_infra/event_bus/models/__init__.py +29 -0
  79. omnibase_infra/event_bus/models/config/__init__.py +20 -0
  80. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
  81. omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
  82. omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
  83. omnibase_infra/event_bus/models/model_event_headers.py +115 -0
  84. omnibase_infra/event_bus/models/model_event_message.py +60 -0
  85. omnibase_infra/event_bus/testing/__init__.py +26 -0
  86. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  87. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  88. omnibase_infra/event_bus/topic_constants.py +376 -0
  89. omnibase_infra/handlers/__init__.py +82 -0
  90. omnibase_infra/handlers/filesystem/__init__.py +48 -0
  91. omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
  92. omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
  93. omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
  94. omnibase_infra/handlers/handler_consul.py +795 -0
  95. omnibase_infra/handlers/handler_db.py +1046 -0
  96. omnibase_infra/handlers/handler_filesystem.py +1478 -0
  97. omnibase_infra/handlers/handler_graph.py +2015 -0
  98. omnibase_infra/handlers/handler_http.py +926 -0
  99. omnibase_infra/handlers/handler_intent.py +387 -0
  100. omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
  101. omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
  102. omnibase_infra/handlers/handler_mcp.py +1430 -0
  103. omnibase_infra/handlers/handler_qdrant.py +1076 -0
  104. omnibase_infra/handlers/handler_vault.py +428 -0
  105. omnibase_infra/handlers/mcp/__init__.py +19 -0
  106. omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
  107. omnibase_infra/handlers/mcp/protocols.py +178 -0
  108. omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
  109. omnibase_infra/handlers/mixins/__init__.py +47 -0
  110. omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
  111. omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
  112. omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
  113. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  114. omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
  115. omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
  116. omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
  117. omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
  118. omnibase_infra/handlers/models/__init__.py +286 -0
  119. omnibase_infra/handlers/models/consul/__init__.py +81 -0
  120. omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
  121. omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
  122. omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
  123. omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
  124. omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
  125. omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
  126. omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
  127. omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
  128. omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
  129. omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
  130. omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
  131. omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
  132. omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
  133. omnibase_infra/handlers/models/graph/__init__.py +35 -0
  134. omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
  135. omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
  136. omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
  137. omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
  138. omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
  139. omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
  140. omnibase_infra/handlers/models/http/__init__.py +50 -0
  141. omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
  142. omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
  143. omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
  144. omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
  145. omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
  146. omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
  147. omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
  148. omnibase_infra/handlers/models/mcp/__init__.py +23 -0
  149. omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
  150. omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
  151. omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
  152. omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
  153. omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
  154. omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
  155. omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
  156. omnibase_infra/handlers/models/model_db_query_response.py +60 -0
  157. omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
  158. omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
  159. omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
  160. omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
  161. omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
  162. omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
  163. omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
  164. omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
  165. omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
  166. omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
  167. omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
  168. omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
  169. omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
  170. omnibase_infra/handlers/models/model_handler_response.py +103 -0
  171. omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
  172. omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
  173. omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
  174. omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
  175. omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
  176. omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
  177. omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
  178. omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
  179. omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
  180. omnibase_infra/handlers/models/model_operation_context.py +187 -0
  181. omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
  182. omnibase_infra/handlers/models/model_retry_state.py +162 -0
  183. omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
  184. omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
  185. omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
  186. omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
  187. omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
  188. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
  189. omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
  190. omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
  191. omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
  192. omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
  193. omnibase_infra/handlers/models/vault/__init__.py +69 -0
  194. omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
  195. omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
  196. omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
  197. omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
  198. omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
  199. omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
  200. omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
  201. omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
  202. omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
  203. omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
  204. omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
  205. omnibase_infra/handlers/registration_storage/__init__.py +43 -0
  206. omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
  207. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
  208. omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
  209. omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
  210. omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
  211. omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
  212. omnibase_infra/handlers/service_discovery/__init__.py +43 -0
  213. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
  214. omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
  215. omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
  216. omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
  217. omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
  218. omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
  219. omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
  220. omnibase_infra/idempotency/__init__.py +94 -0
  221. omnibase_infra/idempotency/models/__init__.py +43 -0
  222. omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
  223. omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
  224. omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
  225. omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
  226. omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
  227. omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
  228. omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
  229. omnibase_infra/idempotency/store_inmemory.py +265 -0
  230. omnibase_infra/idempotency/store_postgres.py +923 -0
  231. omnibase_infra/infrastructure/__init__.py +0 -0
  232. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  233. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  234. omnibase_infra/mixins/__init__.py +71 -0
  235. omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
  236. omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
  237. omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
  238. omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
  239. omnibase_infra/mixins/mixin_retry_execution.py +386 -0
  240. omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
  241. omnibase_infra/models/__init__.py +144 -0
  242. omnibase_infra/models/bindings/__init__.py +59 -0
  243. omnibase_infra/models/bindings/constants.py +144 -0
  244. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  245. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  246. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  247. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  248. omnibase_infra/models/corpus/__init__.py +17 -0
  249. omnibase_infra/models/corpus/model_capture_config.py +133 -0
  250. omnibase_infra/models/corpus/model_capture_result.py +86 -0
  251. omnibase_infra/models/discovery/__init__.py +42 -0
  252. omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
  253. omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
  254. omnibase_infra/models/discovery/model_introspection_config.py +330 -0
  255. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
  256. omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
  257. omnibase_infra/models/dispatch/__init__.py +155 -0
  258. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  259. omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
  260. omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
  261. omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
  262. omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
  263. omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
  264. omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
  265. omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
  266. omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
  267. omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
  268. omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
  269. omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
  270. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  271. omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
  272. omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
  273. omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
  274. omnibase_infra/models/errors/__init__.py +45 -0
  275. omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
  276. omnibase_infra/models/errors/model_infra_error_context.py +99 -0
  277. omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
  278. omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
  279. omnibase_infra/models/handlers/__init__.py +80 -0
  280. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  281. omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
  282. omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
  283. omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
  284. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  285. omnibase_infra/models/health/__init__.py +9 -0
  286. omnibase_infra/models/health/model_health_check_result.py +40 -0
  287. omnibase_infra/models/lifecycle/__init__.py +39 -0
  288. omnibase_infra/models/logging/__init__.py +51 -0
  289. omnibase_infra/models/logging/model_log_context.py +756 -0
  290. omnibase_infra/models/mcp/__init__.py +15 -0
  291. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  292. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  293. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  294. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  295. omnibase_infra/models/model_node_identity.py +126 -0
  296. omnibase_infra/models/model_retry_error_classification.py +78 -0
  297. omnibase_infra/models/projection/__init__.py +43 -0
  298. omnibase_infra/models/projection/model_capability_fields.py +112 -0
  299. omnibase_infra/models/projection/model_registration_projection.py +434 -0
  300. omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
  301. omnibase_infra/models/projection/model_sequence_info.py +182 -0
  302. omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
  303. omnibase_infra/models/projectors/__init__.py +41 -0
  304. omnibase_infra/models/projectors/model_projector_column.py +289 -0
  305. omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
  306. omnibase_infra/models/projectors/model_projector_index.py +270 -0
  307. omnibase_infra/models/projectors/model_projector_schema.py +415 -0
  308. omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
  309. omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
  310. omnibase_infra/models/registration/__init__.py +68 -0
  311. omnibase_infra/models/registration/commands/__init__.py +15 -0
  312. omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
  313. omnibase_infra/models/registration/events/__init__.py +56 -0
  314. omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
  315. omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
  316. omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
  317. omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
  318. omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
  319. omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
  320. omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
  321. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  322. omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
  323. omnibase_infra/models/registration/model_node_capabilities.py +190 -0
  324. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  325. omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
  326. omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
  327. omnibase_infra/models/registration/model_node_metadata.py +79 -0
  328. omnibase_infra/models/registration/model_node_registration.py +162 -0
  329. omnibase_infra/models/registration/model_node_registration_record.py +162 -0
  330. omnibase_infra/models/registry/__init__.py +29 -0
  331. omnibase_infra/models/registry/model_domain_constraint.py +202 -0
  332. omnibase_infra/models/registry/model_message_type_entry.py +271 -0
  333. omnibase_infra/models/resilience/__init__.py +9 -0
  334. omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
  335. omnibase_infra/models/routing/__init__.py +25 -0
  336. omnibase_infra/models/routing/model_routing_entry.py +52 -0
  337. omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
  338. omnibase_infra/models/runtime/__init__.py +49 -0
  339. omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
  340. omnibase_infra/models/runtime/model_discovery_error.py +81 -0
  341. omnibase_infra/models/runtime/model_discovery_result.py +162 -0
  342. omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
  343. omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
  344. omnibase_infra/models/runtime/model_handler_contract.py +296 -0
  345. omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
  346. omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
  347. omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
  348. omnibase_infra/models/security/__init__.py +50 -0
  349. omnibase_infra/models/security/classification_levels.py +99 -0
  350. omnibase_infra/models/security/model_environment_policy.py +145 -0
  351. omnibase_infra/models/security/model_handler_security_policy.py +107 -0
  352. omnibase_infra/models/security/model_security_error.py +81 -0
  353. omnibase_infra/models/security/model_security_validation_result.py +328 -0
  354. omnibase_infra/models/security/model_security_warning.py +67 -0
  355. omnibase_infra/models/snapshot/__init__.py +27 -0
  356. omnibase_infra/models/snapshot/model_field_change.py +65 -0
  357. omnibase_infra/models/snapshot/model_snapshot.py +270 -0
  358. omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
  359. omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
  360. omnibase_infra/models/types/__init__.py +71 -0
  361. omnibase_infra/models/validation/__init__.py +89 -0
  362. omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
  363. omnibase_infra/models/validation/model_any_type_violation.py +141 -0
  364. omnibase_infra/models/validation/model_category_match_result.py +345 -0
  365. omnibase_infra/models/validation/model_chain_violation.py +166 -0
  366. omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
  367. omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
  368. omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
  369. omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
  370. omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
  371. omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
  372. omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
  373. omnibase_infra/models/validation/model_output_validation_params.py +74 -0
  374. omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
  375. omnibase_infra/models/validation/model_validation_error_params.py +84 -0
  376. omnibase_infra/models/validation/model_validation_outcome.py +287 -0
  377. omnibase_infra/nodes/__init__.py +57 -0
  378. omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
  379. omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
  380. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
  381. omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
  382. omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
  383. omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
  384. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
  385. omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
  386. omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
  387. omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
  388. omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
  389. omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
  390. omnibase_infra/nodes/architecture_validator/node.py +262 -0
  391. omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
  392. omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
  393. omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
  394. omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
  395. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
  396. omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
  397. omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
  398. omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
  399. omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
  400. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  401. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  402. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  403. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  404. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  405. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  406. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  407. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  408. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  409. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  410. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  411. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  412. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  413. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  414. omnibase_infra/nodes/effects/README.md +358 -0
  415. omnibase_infra/nodes/effects/__init__.py +26 -0
  416. omnibase_infra/nodes/effects/contract.yaml +167 -0
  417. omnibase_infra/nodes/effects/models/__init__.py +32 -0
  418. omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
  419. omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
  420. omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
  421. omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
  422. omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
  423. omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
  424. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
  425. omnibase_infra/nodes/effects/registry_effect.py +525 -0
  426. omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
  427. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  428. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  429. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  430. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  431. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  432. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  433. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  434. omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
  435. omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
  436. omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
  437. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
  438. omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
  439. omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
  440. omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
  441. omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
  442. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  443. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  444. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  445. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  446. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  447. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  448. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  449. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  450. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  451. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  452. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  453. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  454. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  455. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  456. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  457. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  458. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  459. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  460. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  461. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  462. omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
  463. omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
  464. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
  465. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
  466. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
  467. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
  468. omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
  469. omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
  470. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
  471. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
  472. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
  473. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
  474. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
  475. omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
  476. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
  477. omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
  478. omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
  479. omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
  480. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
  481. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
  482. omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
  483. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
  484. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
  485. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
  486. omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
  487. omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
  488. omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
  489. omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
  490. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
  491. omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
  492. omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
  493. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
  494. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
  495. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
  496. omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
  497. omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
  498. omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
  499. omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
  500. omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
  501. omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
  502. omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
  503. omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
  504. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
  505. omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
  506. omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
  507. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
  508. omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
  509. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
  510. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
  511. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
  512. omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
  513. omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
  514. omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
  515. omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
  516. omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
  517. omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
  518. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
  519. omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
  520. omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
  521. omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
  522. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
  523. omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
  524. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
  525. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
  526. omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
  527. omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
  528. omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
  529. omnibase_infra/nodes/node_registry_effect/node.py +165 -0
  530. omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
  531. omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
  532. omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
  533. omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
  534. omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
  535. omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
  536. omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
  537. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
  538. omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
  539. omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
  540. omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
  541. omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
  542. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
  543. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
  544. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
  545. omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
  546. omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
  547. omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
  548. omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
  549. omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
  550. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
  551. omnibase_infra/nodes/reducers/__init__.py +30 -0
  552. omnibase_infra/nodes/reducers/models/__init__.py +37 -0
  553. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
  554. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  555. omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
  556. omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
  557. omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
  558. omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
  559. omnibase_infra/observability/__init__.py +143 -0
  560. omnibase_infra/observability/constants_metrics.py +91 -0
  561. omnibase_infra/observability/factory_observability_sink.py +525 -0
  562. omnibase_infra/observability/handlers/__init__.py +118 -0
  563. omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
  564. omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
  565. omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
  566. omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
  567. omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
  568. omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
  569. omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
  570. omnibase_infra/observability/hooks/__init__.py +74 -0
  571. omnibase_infra/observability/hooks/hook_observability.py +1223 -0
  572. omnibase_infra/observability/models/__init__.py +30 -0
  573. omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
  574. omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
  575. omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
  576. omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
  577. omnibase_infra/observability/sinks/__init__.py +69 -0
  578. omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
  579. omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
  580. omnibase_infra/plugins/__init__.py +27 -0
  581. omnibase_infra/plugins/examples/__init__.py +28 -0
  582. omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
  583. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
  584. omnibase_infra/plugins/models/__init__.py +21 -0
  585. omnibase_infra/plugins/models/model_plugin_context.py +76 -0
  586. omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
  587. omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
  588. omnibase_infra/plugins/plugin_compute_base.py +449 -0
  589. omnibase_infra/projectors/__init__.py +30 -0
  590. omnibase_infra/projectors/contracts/__init__.py +63 -0
  591. omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
  592. omnibase_infra/projectors/projection_reader_registration.py +1559 -0
  593. omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
  594. omnibase_infra/protocols/__init__.py +104 -0
  595. omnibase_infra/protocols/protocol_capability_projection.py +253 -0
  596. omnibase_infra/protocols/protocol_capability_query.py +251 -0
  597. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  598. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  599. omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
  600. omnibase_infra/protocols/protocol_event_projector.py +96 -0
  601. omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
  602. omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
  603. omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
  604. omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
  605. omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
  606. omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
  607. omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
  608. omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
  609. omnibase_infra/runtime/__init__.py +445 -0
  610. omnibase_infra/runtime/binding_config_resolver.py +2771 -0
  611. omnibase_infra/runtime/binding_resolver.py +753 -0
  612. omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
  613. omnibase_infra/runtime/constants_notification.py +75 -0
  614. omnibase_infra/runtime/constants_security.py +70 -0
  615. omnibase_infra/runtime/contract_handler_discovery.py +587 -0
  616. omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
  617. omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
  618. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  619. omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
  620. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  621. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  622. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  623. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  624. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  625. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  626. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  627. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  628. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  629. omnibase_infra/runtime/enums/__init__.py +18 -0
  630. omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
  631. omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
  632. omnibase_infra/runtime/envelope_validator.py +179 -0
  633. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  634. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  635. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  636. omnibase_infra/runtime/handler_contract_source.py +750 -0
  637. omnibase_infra/runtime/handler_identity.py +81 -0
  638. omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
  639. omnibase_infra/runtime/handler_registry.py +329 -0
  640. omnibase_infra/runtime/handler_source_resolver.py +367 -0
  641. omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
  642. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  643. omnibase_infra/runtime/kernel.py +40 -0
  644. omnibase_infra/runtime/mixin_policy_validation.py +522 -0
  645. omnibase_infra/runtime/mixin_semver_cache.py +402 -0
  646. omnibase_infra/runtime/mixins/__init__.py +24 -0
  647. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  648. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
  649. omnibase_infra/runtime/models/__init__.py +229 -0
  650. omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
  651. omnibase_infra/runtime/models/model_binding_config.py +168 -0
  652. omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
  653. omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
  654. omnibase_infra/runtime/models/model_cached_secret.py +138 -0
  655. omnibase_infra/runtime/models/model_compute_key.py +138 -0
  656. omnibase_infra/runtime/models/model_compute_registration.py +97 -0
  657. omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
  658. omnibase_infra/runtime/models/model_config_ref.py +331 -0
  659. omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
  660. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  661. omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
  662. omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
  663. omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
  664. omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
  665. omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
  666. omnibase_infra/runtime/models/model_failed_component.py +55 -0
  667. omnibase_infra/runtime/models/model_health_check_response.py +168 -0
  668. omnibase_infra/runtime/models/model_health_check_result.py +229 -0
  669. omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
  670. omnibase_infra/runtime/models/model_logging_config.py +42 -0
  671. omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
  672. omnibase_infra/runtime/models/model_optional_string.py +94 -0
  673. omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
  674. omnibase_infra/runtime/models/model_policy_context.py +100 -0
  675. omnibase_infra/runtime/models/model_policy_key.py +138 -0
  676. omnibase_infra/runtime/models/model_policy_registration.py +139 -0
  677. omnibase_infra/runtime/models/model_policy_result.py +103 -0
  678. omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
  679. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  680. omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
  681. omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
  682. omnibase_infra/runtime/models/model_retry_policy.py +105 -0
  683. omnibase_infra/runtime/models/model_runtime_config.py +150 -0
  684. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  685. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
  686. omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
  687. omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
  688. omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
  689. omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
  690. omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
  691. omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
  692. omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
  693. omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
  694. omnibase_infra/runtime/models/model_security_config.py +109 -0
  695. omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
  696. omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
  697. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  698. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  699. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  700. omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
  701. omnibase_infra/runtime/projector_schema_manager.py +565 -0
  702. omnibase_infra/runtime/projector_shell.py +1330 -0
  703. omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
  704. omnibase_infra/runtime/protocol_contract_source.py +92 -0
  705. omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
  706. omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
  707. omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
  708. omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
  709. omnibase_infra/runtime/protocol_policy.py +366 -0
  710. omnibase_infra/runtime/protocols/__init__.py +37 -0
  711. omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
  712. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  713. omnibase_infra/runtime/registry/__init__.py +93 -0
  714. omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
  715. omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
  716. omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
  717. omnibase_infra/runtime/registry/registry_message_type.py +542 -0
  718. omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
  719. omnibase_infra/runtime/registry_compute.py +1143 -0
  720. omnibase_infra/runtime/registry_contract_source.py +693 -0
  721. omnibase_infra/runtime/registry_dispatcher.py +678 -0
  722. omnibase_infra/runtime/registry_policy.py +1185 -0
  723. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  724. omnibase_infra/runtime/runtime_scheduler.py +1070 -0
  725. omnibase_infra/runtime/secret_resolver.py +2112 -0
  726. omnibase_infra/runtime/security_metadata_validator.py +776 -0
  727. omnibase_infra/runtime/service_kernel.py +1651 -0
  728. omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
  729. omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
  730. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  731. omnibase_infra/runtime/transition_notification_publisher.py +765 -0
  732. omnibase_infra/runtime/util_container_wiring.py +1124 -0
  733. omnibase_infra/runtime/util_validation.py +314 -0
  734. omnibase_infra/runtime/util_version.py +98 -0
  735. omnibase_infra/runtime/util_wiring.py +723 -0
  736. omnibase_infra/schemas/schema_registration_projection.sql +320 -0
  737. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  738. omnibase_infra/services/__init__.py +89 -0
  739. omnibase_infra/services/corpus_capture.py +684 -0
  740. omnibase_infra/services/mcp/__init__.py +31 -0
  741. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  742. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  743. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  744. omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
  745. omnibase_infra/services/registry_api/__init__.py +40 -0
  746. omnibase_infra/services/registry_api/main.py +261 -0
  747. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  748. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  749. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  750. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  751. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  752. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  753. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  754. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  755. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  756. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  757. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  758. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  759. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  760. omnibase_infra/services/registry_api/routes.py +371 -0
  761. omnibase_infra/services/registry_api/service.py +837 -0
  762. omnibase_infra/services/service_capability_query.py +945 -0
  763. omnibase_infra/services/service_health.py +898 -0
  764. omnibase_infra/services/service_node_selector.py +530 -0
  765. omnibase_infra/services/service_timeout_emitter.py +699 -0
  766. omnibase_infra/services/service_timeout_scanner.py +394 -0
  767. omnibase_infra/services/session/__init__.py +56 -0
  768. omnibase_infra/services/session/config_consumer.py +137 -0
  769. omnibase_infra/services/session/config_store.py +139 -0
  770. omnibase_infra/services/session/consumer.py +1007 -0
  771. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  772. omnibase_infra/services/session/store.py +997 -0
  773. omnibase_infra/services/snapshot/__init__.py +31 -0
  774. omnibase_infra/services/snapshot/service_snapshot.py +647 -0
  775. omnibase_infra/services/snapshot/store_inmemory.py +637 -0
  776. omnibase_infra/services/snapshot/store_postgres.py +1279 -0
  777. omnibase_infra/shared/__init__.py +8 -0
  778. omnibase_infra/testing/__init__.py +10 -0
  779. omnibase_infra/testing/utils.py +23 -0
  780. omnibase_infra/topics/__init__.py +45 -0
  781. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  782. omnibase_infra/topics/util_topic_composition.py +95 -0
  783. omnibase_infra/types/__init__.py +48 -0
  784. omnibase_infra/types/type_cache_info.py +49 -0
  785. omnibase_infra/types/type_dsn.py +173 -0
  786. omnibase_infra/types/type_infra_aliases.py +60 -0
  787. omnibase_infra/types/typed_dict/__init__.py +29 -0
  788. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  789. omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
  790. omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
  791. omnibase_infra/types/typed_dict_capabilities.py +64 -0
  792. omnibase_infra/utils/__init__.py +117 -0
  793. omnibase_infra/utils/correlation.py +208 -0
  794. omnibase_infra/utils/util_atomic_file.py +261 -0
  795. omnibase_infra/utils/util_consumer_group.py +232 -0
  796. omnibase_infra/utils/util_datetime.py +372 -0
  797. omnibase_infra/utils/util_db_transaction.py +239 -0
  798. omnibase_infra/utils/util_dsn_validation.py +333 -0
  799. omnibase_infra/utils/util_env_parsing.py +264 -0
  800. omnibase_infra/utils/util_error_sanitization.py +457 -0
  801. omnibase_infra/utils/util_pydantic_validators.py +477 -0
  802. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  803. omnibase_infra/utils/util_semver.py +233 -0
  804. omnibase_infra/validation/__init__.py +307 -0
  805. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  806. omnibase_infra/validation/enums/__init__.py +11 -0
  807. omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
  808. omnibase_infra/validation/infra_validators.py +1514 -0
  809. omnibase_infra/validation/linter_contract.py +907 -0
  810. omnibase_infra/validation/mixin_any_type_classification.py +120 -0
  811. omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
  812. omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
  813. omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
  814. omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
  815. omnibase_infra/validation/models/__init__.py +15 -0
  816. omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
  817. omnibase_infra/validation/models/model_contract_violation.py +41 -0
  818. omnibase_infra/validation/service_validation_aggregator.py +395 -0
  819. omnibase_infra/validation/validation_exemptions.yaml +2033 -0
  820. omnibase_infra/validation/validator_any_type.py +715 -0
  821. omnibase_infra/validation/validator_chain_propagation.py +839 -0
  822. omnibase_infra/validation/validator_execution_shape.py +465 -0
  823. omnibase_infra/validation/validator_localhandler.py +261 -0
  824. omnibase_infra/validation/validator_registration_security.py +410 -0
  825. omnibase_infra/validation/validator_routing_coverage.py +1020 -0
  826. omnibase_infra/validation/validator_runtime_shape.py +915 -0
  827. omnibase_infra/validation/validator_security.py +513 -0
  828. omnibase_infra/validation/validator_topic_category.py +1152 -0
  829. omnibase_infra-0.2.6.dist-info/METADATA +197 -0
  830. omnibase_infra-0.2.6.dist-info/RECORD +833 -0
  831. omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
  832. omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
  833. omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1514 @@
1
+ """
2
+ Infrastructure-specific validation wrappers.
3
+
4
+ Provides validators from omnibase_core with sensible defaults for infrastructure code.
5
+ All wrappers maintain strong typing and follow ONEX validation patterns.
6
+
7
+ Exemption System:
8
+ This module uses a YAML-based exemption system for managing validation exceptions.
9
+ Exemption patterns are defined in `validation_exemptions.yaml` alongside this module.
10
+
11
+ The exemption system provides:
12
+ - Centralized management of all validation exemptions
13
+ - Clear documentation of rationale and ticket references
14
+ - Regex-based matching resilient to code changes (no line numbers)
15
+ - Separation of exemption configuration from validation logic
16
+
17
+ See validation_exemptions.yaml for:
18
+ - pattern_exemptions: Method count, parameter count, naming violations
19
+ - union_exemptions: Complex union type violations
20
+
21
+ Adding new exemptions:
22
+ 1. Identify the exact violation message from validator output
23
+ 2. Add entry to appropriate section in validation_exemptions.yaml
24
+ 3. Document the rationale and link to relevant tickets
25
+ 4. Run tests to verify the exemption works
26
+ """
27
+
28
+ # Standard library imports
29
+ import ast
30
+ import logging
31
+ import re
32
+ from functools import lru_cache
33
+ from pathlib import Path
34
+ from typing import TypedDict
35
+
36
+ # Third-party imports
37
+ import yaml
38
+
39
+ from omnibase_core.models.common import ModelValidationMetadata
40
+ from omnibase_core.models.primitives import ModelSemVer
41
+ from omnibase_core.models.validation.model_union_pattern import ModelUnionPattern
42
+ from omnibase_core.validation import (
43
+ CircularImportValidator,
44
+ ModelContractValidationResult,
45
+ ModelImportValidationResult,
46
+ ModelModuleImportResult,
47
+ ModelValidationResult,
48
+ validate_architecture,
49
+ validate_contracts,
50
+ validate_patterns,
51
+ validate_union_usage_file,
52
+ validate_yaml_file,
53
+ )
54
+
55
+ # Local imports
56
+ from omnibase_infra.types import PathInput
57
+
58
+ # Module-level initialization (AFTER all imports)
59
+ logger = logging.getLogger(__name__)
60
+
61
+ # Type alias for cleaner return types in infrastructure validators
62
+ # Most validation results return None as the data payload (validation only)
63
+ # Using Python 3.12+ type keyword for modern type alias syntax
64
+ type ValidationResult = ModelValidationResult[None]
65
+
66
+
67
+ class ExemptionPattern(TypedDict, total=False):
68
+ """
69
+ Structure for validation exemption patterns.
70
+
71
+ Uses regex-based matching to handle code evolution gracefully without
72
+ hardcoded line numbers that break when code changes.
73
+
74
+ Fields:
75
+ file_pattern: Regex pattern matching the filename (e.g., r"event_bus_kafka\\.py")
76
+ class_pattern: Optional regex for class name (e.g., r"Class 'EventBusKafka'")
77
+ method_pattern: Optional regex for method name (e.g., r"Function '__init__'")
78
+ violation_pattern: Regex matching the violation type (e.g., r"too many (methods|parameters)")
79
+
80
+ Example:
81
+ {
82
+ "file_pattern": r"event_bus_kafka\\.py",
83
+ "class_pattern": r"Class 'EventBusKafka'",
84
+ "violation_pattern": r"has \\d+ methods"
85
+ }
86
+
87
+ Notes:
88
+ - Patterns are matched using re.search() for flexibility
89
+ - All specified patterns must match for an exemption to apply
90
+ - Omitted optional fields are not checked
91
+ - Use raw strings (r"...") for regex patterns
92
+ """
93
+
94
+ file_pattern: str
95
+ class_pattern: str
96
+ method_pattern: str
97
+ violation_pattern: str
98
+
99
+
100
+ # Path to the exemptions YAML file (alongside this module)
101
+ EXEMPTIONS_YAML_PATH = Path(__file__).parent / "validation_exemptions.yaml"
102
+
103
+
104
+ @lru_cache(maxsize=1)
105
+ def _load_exemptions_yaml() -> dict[str, list[ExemptionPattern]]:
106
+ """
107
+ Load and cache exemption patterns from YAML configuration.
108
+
109
+ The exemption patterns are cached to avoid repeated file I/O during validation.
110
+ Cache is cleared when the module is reloaded.
111
+
112
+ Returns:
113
+ Dictionary with 'pattern_exemptions', 'union_exemptions', and
114
+ 'architecture_exemptions' keys, each containing a list of
115
+ ExemptionPattern dictionaries.
116
+ Returns empty lists if file is missing or malformed.
117
+
118
+ Note:
119
+ The YAML file is expected to be at validation_exemptions.yaml alongside
120
+ this module. See that file for schema documentation and exemption rationale.
121
+ """
122
+ if not EXEMPTIONS_YAML_PATH.exists():
123
+ # Fallback to empty exemptions if file is missing
124
+ return {
125
+ "pattern_exemptions": [],
126
+ "union_exemptions": [],
127
+ "architecture_exemptions": [],
128
+ }
129
+
130
+ try:
131
+ with EXEMPTIONS_YAML_PATH.open("r", encoding="utf-8") as f:
132
+ data = yaml.safe_load(f)
133
+
134
+ if not isinstance(data, dict):
135
+ return {
136
+ "pattern_exemptions": [],
137
+ "union_exemptions": [],
138
+ "architecture_exemptions": [],
139
+ }
140
+
141
+ # Extract exemption lists, converting YAML structure to ExemptionPattern format
142
+ pattern_exemptions = _convert_yaml_exemptions(
143
+ data.get("pattern_exemptions", [])
144
+ )
145
+ union_exemptions = _convert_yaml_exemptions(data.get("union_exemptions", []))
146
+ architecture_exemptions = _convert_yaml_exemptions(
147
+ data.get("architecture_exemptions", [])
148
+ )
149
+
150
+ return {
151
+ "pattern_exemptions": pattern_exemptions,
152
+ "union_exemptions": union_exemptions,
153
+ "architecture_exemptions": architecture_exemptions,
154
+ }
155
+ except (yaml.YAMLError, OSError) as e:
156
+ # Log warning but continue with empty exemptions
157
+ logger.warning(
158
+ "Failed to load validation exemptions from %s: %s. Using empty exemptions.",
159
+ EXEMPTIONS_YAML_PATH,
160
+ e,
161
+ )
162
+ return {
163
+ "pattern_exemptions": [],
164
+ "union_exemptions": [],
165
+ "architecture_exemptions": [],
166
+ }
167
+
168
+
169
+ def _convert_yaml_exemptions(yaml_list: list[dict]) -> list[ExemptionPattern]:
170
+ """
171
+ Convert YAML exemption entries to ExemptionPattern format.
172
+
173
+ The YAML format includes additional metadata (reason, ticket) that is used
174
+ for documentation but not for pattern matching. This function extracts only
175
+ the pattern fields needed for matching.
176
+
177
+ Regex patterns are validated at load time to prevent runtime errors during
178
+ validation. Entries with invalid regex patterns are skipped with a warning.
179
+
180
+ Args:
181
+ yaml_list: List of exemption entries from YAML.
182
+
183
+ Returns:
184
+ List of ExemptionPattern dictionaries with only pattern fields.
185
+ Entries with invalid regex patterns are excluded.
186
+
187
+ Invalid Entry Handling:
188
+ This function is defensive and skips invalid entries to ensure
189
+ validation continues even with malformed exemption configuration:
190
+
191
+ - If yaml_list is not a list: returns empty list (no exemptions applied)
192
+ - If an entry is not a dict: entry is skipped silently
193
+ - If entry lacks required fields (file_pattern AND violation_pattern):
194
+ entry is skipped silently (both fields are required for meaningful matching)
195
+ - If any pattern field contains an invalid regex: entry is skipped
196
+ with a warning log (prevents runtime errors during pattern matching)
197
+ - All pattern field values are coerced to str via str() to handle
198
+ non-string values gracefully
199
+
200
+ Design Rationale:
201
+ Skipping invalid entries (rather than raising exceptions) is intentional:
202
+ 1. Validation should not fail due to exemption configuration issues
203
+ 2. Missing exemptions result in stricter validation (safer default)
204
+ 3. Errors in exemption config are detected during exemption testing
205
+ 4. Production validation continues even with partial exemption config
206
+ 5. Invalid regex patterns are logged to aid debugging
207
+ """
208
+ if not isinstance(yaml_list, list):
209
+ return []
210
+
211
+ result: list[ExemptionPattern] = []
212
+ for entry in yaml_list:
213
+ if not isinstance(entry, dict):
214
+ continue
215
+
216
+ # Extract only pattern fields (ignore reason, ticket metadata)
217
+ # Validate each regex pattern before adding to prevent runtime errors
218
+ pattern: ExemptionPattern = {}
219
+ entry_valid = True
220
+
221
+ if "file_pattern" in entry:
222
+ file_pattern = str(entry["file_pattern"])
223
+ try:
224
+ re.compile(file_pattern)
225
+ pattern["file_pattern"] = file_pattern
226
+ except re.error as e:
227
+ logger.warning(
228
+ "Invalid regex in file_pattern '%s': %s. Skipping exemption entry.",
229
+ file_pattern,
230
+ e,
231
+ )
232
+ entry_valid = False
233
+
234
+ if entry_valid and "class_pattern" in entry:
235
+ class_pattern = str(entry["class_pattern"])
236
+ try:
237
+ re.compile(class_pattern)
238
+ pattern["class_pattern"] = class_pattern
239
+ except re.error as e:
240
+ logger.warning(
241
+ "Invalid regex in class_pattern '%s': %s. Skipping exemption entry.",
242
+ class_pattern,
243
+ e,
244
+ )
245
+ entry_valid = False
246
+
247
+ if entry_valid and "method_pattern" in entry:
248
+ method_pattern = str(entry["method_pattern"])
249
+ try:
250
+ re.compile(method_pattern)
251
+ pattern["method_pattern"] = method_pattern
252
+ except re.error as e:
253
+ logger.warning(
254
+ "Invalid regex in method_pattern '%s': %s. Skipping exemption entry.",
255
+ method_pattern,
256
+ e,
257
+ )
258
+ entry_valid = False
259
+
260
+ if entry_valid and "violation_pattern" in entry:
261
+ violation_pattern = str(entry["violation_pattern"])
262
+ try:
263
+ re.compile(violation_pattern)
264
+ pattern["violation_pattern"] = violation_pattern
265
+ except re.error as e:
266
+ logger.warning(
267
+ "Invalid regex in violation_pattern '%s': %s. Skipping exemption entry.",
268
+ violation_pattern,
269
+ e,
270
+ )
271
+ entry_valid = False
272
+
273
+ # Only include if entry is valid and has required patterns
274
+ if entry_valid and "file_pattern" in pattern and "violation_pattern" in pattern:
275
+ result.append(pattern)
276
+
277
+ return result
278
+
279
+
280
+ def get_pattern_exemptions() -> list[ExemptionPattern]:
281
+ """
282
+ Get pattern validator exemptions from YAML configuration.
283
+
284
+ Returns:
285
+ List of ExemptionPattern dictionaries for pattern validation.
286
+ """
287
+ return _load_exemptions_yaml()["pattern_exemptions"]
288
+
289
+
290
+ def get_union_exemptions() -> list[ExemptionPattern]:
291
+ """
292
+ Get union validator exemptions from YAML configuration.
293
+
294
+ Returns:
295
+ List of ExemptionPattern dictionaries for union validation.
296
+ """
297
+ return _load_exemptions_yaml()["union_exemptions"]
298
+
299
+
300
+ def get_architecture_exemptions() -> list[ExemptionPattern]:
301
+ """
302
+ Get architecture validator exemptions from YAML configuration.
303
+
304
+ Returns:
305
+ List of ExemptionPattern dictionaries for architecture validation.
306
+ """
307
+ exemptions = _load_exemptions_yaml()
308
+ return exemptions.get("architecture_exemptions", [])
309
+
310
+
311
+ # Default paths for infrastructure validation
312
+ INFRA_SRC_PATH = "src/omnibase_infra/"
313
+ INFRA_NODES_PATH = "src/omnibase_infra/nodes/"
314
+
315
+ # ============================================================================
316
+ # Pattern Validator Threshold Reference (from omnibase_core.validation)
317
+ # ============================================================================
318
+ # These thresholds are defined in omnibase_core and applied by validate_patterns().
319
+ # Documented here for reference and to explain infrastructure exemptions.
320
+ #
321
+ # See CLAUDE.md "Accepted Pattern Exceptions" section for full rationale.
322
+ # Ticket: OMN-934 (message dispatch engine implementation)
323
+ # Updated: PR #61 review feedback - added explicit threshold documentation
324
+ #
325
+ # DEFAULT_MAX_METHODS = 10 # Maximum methods per class
326
+ # DEFAULT_MAX_INIT_PARAMS = 5 # Maximum __init__ parameters
327
+ #
328
+ # Infrastructure Pattern Exemptions (OMN-934, PR #61):
329
+ # ----------------------------------------------------
330
+ # EventBusKafka (14 methods, 10 __init__ params):
331
+ # - Event bus pattern requires: lifecycle (start/stop/health), pub/sub
332
+ # (subscribe/unsubscribe/publish), circuit breaker, protocol compatibility
333
+ # - Backwards compatibility during config migration requires multiple __init__ params
334
+ # - See: event_bus_kafka.py class docstring, CLAUDE.md "Accepted Pattern Exceptions"
335
+ #
336
+ # RuntimeHostProcess (11+ methods, 6+ __init__ params):
337
+ # - Central coordinator requires: lifecycle management, message handling,
338
+ # graceful shutdown, handler management
339
+ # - See: runtime_host_process.py class docstring, CLAUDE.md "Accepted Pattern Exceptions"
340
+ #
341
+ # These exemptions are handled via exempted_patterns in validate_infra_patterns(),
342
+ # NOT by modifying global thresholds.
343
+ #
344
+ # Exemption Pattern Examples (explicit format):
345
+ # ---------------------------------------------
346
+ # EventBusKafka method count:
347
+ # {"file_pattern": r"event_bus_kafka\.py", "class_pattern": r"Class 'EventBusKafka'",
348
+ # "violation_pattern": r"has \d+ methods"}
349
+ #
350
+ # EventBusKafka __init__ params:
351
+ # {"file_pattern": r"event_bus_kafka\.py", "method_pattern": r"Function '__init__'",
352
+ # "violation_pattern": r"has \d+ parameters"}
353
+ #
354
+ # RuntimeHostProcess method count:
355
+ # {"file_pattern": r"runtime_host_process\.py", "class_pattern": r"Class 'RuntimeHostProcess'",
356
+ # "violation_pattern": r"has \d+ methods"}
357
+ #
358
+ # RuntimeHostProcess __init__ params:
359
+ # {"file_pattern": r"runtime_host_process\.py", "method_pattern": r"Function '__init__'",
360
+ # "violation_pattern": r"has \d+ parameters"}
361
+ #
362
+ # See exempted_patterns list in validate_infra_patterns() for complete definitions.
363
+ # ============================================================================
364
+
365
+ # Maximum allowed union count in infrastructure code.
366
+ # This threshold counts ONLY complex type annotation unions.
367
+ #
368
+ # Excluded patterns (NOT counted toward threshold):
369
+ # 1. Simple optionals (`X | None`) - idiomatic nullable pattern (PEP 604)
370
+ # 2. isinstance() unions (`isinstance(x, A | B)`) - runtime type checks, not annotations
371
+ #
372
+ # What IS counted (threshold applies to):
373
+ # - Multi-type unions in annotations: `def foo(x: str | int)`
374
+ # - Complex patterns: `dict[str, str | int]` (nested unions)
375
+ # - Unions with 3+ types (potential "primitive soup")
376
+ #
377
+ # What is NOT counted (excluded from threshold):
378
+ # - Simple optionals: `str | None`, `int | None`, `ModelFoo | None`
379
+ # - These are idiomatic Python nullable patterns, not complexity concerns
380
+ # - isinstance() unions: `isinstance(x, str | int)` (ruff UP038 modern syntax)
381
+ # - These are runtime type checks, not type annotations
382
+ # - Encouraged by ruff UP038 over isinstance(x, (str, int))
383
+ #
384
+ # Threshold history (after exclusion logic):
385
+ # - 120 (2025-12-25): Initial threshold after excluding ~470 `X | None` patterns
386
+ # - ~568 total unions in codebase
387
+ # - ~468 are simple `X | None` optionals (82%)
388
+ # - ~100 non-optional unions remain
389
+ # - Buffer of 20 above baseline for codebase growth
390
+ # - 121 (2025-12-25): OMN-881 introspection feature (+1 non-optional union)
391
+ # - 121 (2025-12-25): OMN-949 DLQ, OMN-816, OMN-811, OMN-1006 merges (all used X | None patterns, excluded)
392
+ # - 121 (2025-12-26): OMN-1007 registry pattern + merge with main (X | None patterns excluded)
393
+ # - 122 (2026-01-15): OMN-1203 corpus capture service, OMN-1346 extract registration domain plugin
394
+ # - 142 (2026-01-16): OMN-1305 ruff UP038 isinstance union syntax modernization (+20 unions)
395
+ # - 121 (2026-01-16): OMN-1305 isinstance union exclusion (excluding 21 isinstance unions)
396
+ # - Updated validator to exclude isinstance(x, A | B) patterns
397
+ # - These are runtime checks, not type annotations
398
+ # - 70 (2026-01-16): OMN-1358 type alias replacements reduced from 122 to 63 non-optional unions
399
+ # Applied HandlerMap, NodeId, PayloadDict, EventMetadata, MetadataDict type aliases
400
+ # - 81 (2026-01-16): OMN-1305 PR #151 merge with main - combined changes
401
+ # isinstance exclusion + type alias refactoring + PR #151 fixes
402
+ # - 83 (2026-01-16): OMN-1181 structured errors merge with main
403
+ # (+2 unions for EnumPolicyType | str in validate_policy_type_value)
404
+ # - 82 (2026-01-16): OMN-1181 fix PolicyTypeInput validator coercion
405
+ # (-1 union: changed return type from str | EnumPolicyType to EnumPolicyType)
406
+ # Validators now coerce strings to EnumPolicyType, ensuring type-safe access.
407
+ #
408
+ # Soft ceiling guidance:
409
+ # - 100-120: Healthy range, minor increments OK for legitimate features
410
+ # - 120-140: Caution zone, consider refactoring before incrementing
411
+ # - 140+: Refactor required - extract type aliases or use domain models from omnibase_core
412
+ #
413
+ # When incrementing threshold:
414
+ # 1. Document the ticket/PR that added unions in threshold history above
415
+ # 2. Verify new unions are not simple X | None (those should be excluded automatically)
416
+ # 3. Verify new unions are not isinstance() patterns (also excluded automatically)
417
+ # 4. Consider if a domain-specific type from omnibase_core would be cleaner
418
+ #
419
+ # Target: Keep below 150 - if this grows, consider typed patterns from omnibase_core.
420
+ # - 95 (2026-01-16): OMN-1142 Qdrant/Graph handlers (+14 legitimate union patterns)
421
+ # - str | int for graph node IDs (5 occurrences in handler_graph.py)
422
+ # - UUID | str for Qdrant point IDs (2 occurrences in Qdrant models)
423
+ # - float | int for score fields (1 occurrence)
424
+ # - 96 (2026-01-16): OMN-1181 structured errors merge with main (+1 net)
425
+ # (+2 unions for EnumPolicyType | str in validate_policy_type_value)
426
+ # (-1 union: fix PolicyTypeInput validator coercion, changed return
427
+ # type from str | EnumPolicyType to EnumPolicyType)
428
+ # - 98 (2026-01-20): OMN-1277 security validator contract refactoring (+2 unions)
429
+ # ast.FunctionDef | ast.AsyncFunctionDef for AST method type checking
430
+ # - 105 (2026-01-21): Contract-driven handler config loading (+4 unions)
431
+ # ModelHandlerContract transport config fields and lifecycle types
432
+ # - 108 (2026-01-27): OMN-1518 declarative operation bindings (+3 unions)
433
+ # ModelEventEnvelope[object] | dict[str, object] for materialized
434
+ # envelopes in dispatch engine (3 occurrences in type aliases)
435
+ # - 105 (2026-01-27): OMN-1518 simplify to always-dict envelope format (-3 unions)
436
+ # Removed hybrid union types by always materializing to dict format
437
+ # Dispatchers now receive consistent dict[str, object] with __bindings
438
+ # - 112 (2026-01-27): OMN-1610 emit daemon for persistent Kafka connections (+7 unions)
439
+ # BoundedEventQueue, EmitClient, EventRegistry return types
440
+ # - 112 (2026-01-29): OMN-1610 emit daemon + refactor to strongly typed input model
441
+ # BoundedEventQueue, EmitClient, EventRegistry, socket_permissions
442
+ # Replaced dict unions with ModelEmitDaemonConfigInput
443
+ # - 113 (2026-01-29): OMN-1610 properly typed daemon protocol models (+1 union)
444
+ # Added ModelDaemonRequest, ModelDaemonResponse discriminated unions
445
+ # Replaced dict[str, object] soup with strongly typed Pydantic models
446
+ # - 115 (2026-01-29): OMN-1653 contract registry reducer (+2 unions)
447
+ # ContractRegistryEvent: 4-type union for event routing
448
+ # contract_yaml: dict | str for flexible YAML handling
449
+ INFRA_MAX_UNIONS = 115
450
+
451
+ # Maximum allowed architecture violations in infrastructure code.
452
+ # Set to 0 (strict enforcement) to ensure one-model-per-file principle is always followed.
453
+ # Infrastructure nodes require strict architecture compliance for maintainability and
454
+ # contract-driven code generation.
455
+ INFRA_MAX_VIOLATIONS = 0
456
+
457
+ # Strict mode for pattern validation.
458
+ # Enabled: All violations must be exempted or fixed.
459
+ # See validate_infra_patterns() exempted_patterns list for documented exemptions.
460
+ INFRA_PATTERNS_STRICT = True
461
+
462
+ # Strict mode for union usage validation.
463
+ # Enabled: The validator will flag actual violations (not just count unions).
464
+ INFRA_UNIONS_STRICT = True
465
+
466
+
467
+ def validate_infra_architecture(
468
+ directory: PathInput = INFRA_SRC_PATH,
469
+ max_violations: int = INFRA_MAX_VIOLATIONS,
470
+ ) -> ValidationResult:
471
+ """
472
+ Validate infrastructure architecture with strict defaults.
473
+
474
+ Enforces ONEX one-model-per-file principle critical for infrastructure nodes.
475
+
476
+ Exemptions:
477
+ Exemption patterns are loaded from validation_exemptions.yaml (architecture_exemptions section).
478
+ See that file for the complete list of exemptions with rationale and ticket references.
479
+
480
+ Key exemption categories:
481
+ - contract_linter.py: Domain-grouped validation models (PR-57)
482
+ - protocols.py: Domain-grouped protocols per CLAUDE.md convention (OMN-888)
483
+
484
+ Args:
485
+ directory: Directory to validate. Defaults to infrastructure source.
486
+ max_violations: Maximum allowed violations. Defaults to INFRA_MAX_VIOLATIONS (0).
487
+
488
+ Returns:
489
+ ModelValidationResult with validation status and filtered errors.
490
+ Documented exemptions are filtered from error list but logged for transparency.
491
+ """
492
+ # Run base validation
493
+ base_result = validate_architecture(str(directory), max_violations=max_violations)
494
+
495
+ # Load exemption patterns from YAML configuration
496
+ # See validation_exemptions.yaml for pattern definitions and rationale
497
+ exempted_patterns = get_architecture_exemptions()
498
+
499
+ # Filter errors using regex-based pattern matching
500
+ filtered_errors = _filter_exempted_errors(base_result.errors, exempted_patterns)
501
+
502
+ # Create wrapper result (avoid mutation)
503
+ return _create_filtered_result(base_result, filtered_errors)
504
+
505
+
506
+ def validate_infra_contracts(
507
+ directory: PathInput = INFRA_NODES_PATH,
508
+ ) -> ValidationResult:
509
+ """
510
+ Validate all infrastructure node contracts.
511
+
512
+ Validates YAML contract files for Consul, Kafka, Vault, PostgreSQL adapters.
513
+
514
+ Args:
515
+ directory: Directory containing node contracts. Defaults to nodes path.
516
+
517
+ Returns:
518
+ ModelValidationResult with validation status and any errors.
519
+ """
520
+ return validate_contracts(str(directory))
521
+
522
+
523
+ def validate_infra_patterns(
524
+ directory: PathInput = INFRA_SRC_PATH,
525
+ strict: bool = INFRA_PATTERNS_STRICT,
526
+ ) -> ValidationResult:
527
+ """
528
+ Validate infrastructure code patterns with infrastructure-specific exemptions.
529
+
530
+ Enforces:
531
+ - Model prefix naming (Model*)
532
+ - snake_case file naming
533
+ - Anti-pattern detection (no *Manager, *Handler, *Helper)
534
+
535
+ Exemptions:
536
+ Exemption patterns are loaded from validation_exemptions.yaml (pattern_exemptions section).
537
+ See that file for the complete list of exemptions with rationale and ticket references.
538
+
539
+ Key exemption categories:
540
+ - EventBusKafka: Event bus pattern with many methods/params (OMN-934)
541
+ - RuntimeHostProcess: Central coordinator pattern (OMN-756)
542
+ - RegistryPolicy: Domain registry pattern
543
+ - ExecutionShapeValidator: AST analysis validator pattern (OMN-958)
544
+ - MixinNodeIntrospection: Introspection mixin pattern (OMN-958)
545
+
546
+ Exemption Pattern Format:
547
+ Uses regex-based matching instead of hardcoded line numbers for resilience
548
+ to code changes. See ExemptionPattern TypedDict for structure details.
549
+
550
+ Args:
551
+ directory: Directory to validate. Defaults to infrastructure source.
552
+ strict: Enable strict mode. Defaults to INFRA_PATTERNS_STRICT (True).
553
+
554
+ Returns:
555
+ ModelValidationResult with validation status and filtered errors.
556
+ Documented exemptions are filtered from error list but logged for transparency.
557
+ """
558
+ # Run base validation
559
+ base_result = validate_patterns(str(directory), strict=strict)
560
+
561
+ # Load exemption patterns from YAML configuration
562
+ # See validation_exemptions.yaml for pattern definitions and rationale
563
+ exempted_patterns = get_pattern_exemptions()
564
+
565
+ # Filter errors using regex-based pattern matching
566
+ filtered_errors = _filter_exempted_errors(base_result.errors, exempted_patterns)
567
+
568
+ # Create wrapper result (avoid mutation)
569
+ return _create_filtered_result(base_result, filtered_errors)
570
+
571
+
572
+ def _filter_exempted_errors(
573
+ errors: list[str],
574
+ exempted_patterns: list[ExemptionPattern],
575
+ ) -> list[str]:
576
+ """
577
+ Filter errors based on regex exemption patterns.
578
+
579
+ Uses regex-based matching to identify exempted violations without relying on
580
+ hardcoded line numbers or exact counts. This makes exemptions resilient to
581
+ code changes while still precisely targeting specific violations.
582
+
583
+ Pattern Matching Logic:
584
+ - All specified pattern fields must match for exemption to apply
585
+ - Unspecified optional fields are not checked (e.g., missing method_pattern)
586
+ - Uses re.search() for flexible substring matching
587
+ - Case-sensitive matching for precision
588
+
589
+ Args:
590
+ errors: List of error messages from validation.
591
+ exempted_patterns: List of ExemptionPattern dictionaries with regex patterns.
592
+
593
+ Returns:
594
+ Filtered list of errors excluding exempted patterns.
595
+ Returns empty list if inputs are not the expected types.
596
+
597
+ Example:
598
+ Pattern:
599
+ {
600
+ "file_pattern": r"event_bus_kafka\\.py",
601
+ "class_pattern": r"Class 'EventBusKafka'",
602
+ "violation_pattern": r"has \\d+ methods"
603
+ }
604
+
605
+ Matches error:
606
+ "event_bus_kafka.py:123: Class 'EventBusKafka' has 14 methods (threshold: 10)"
607
+
608
+ Does not match:
609
+ "event_bus_kafka.py:50: Function 'connect' has 7 parameters" (no class_pattern)
610
+ "other_file.py:10: Class 'EventBusKafka' has 14 methods" (wrong file)
611
+ """
612
+ # Defensive type checks for list inputs
613
+ if not isinstance(errors, list):
614
+ return []
615
+ if not isinstance(exempted_patterns, list):
616
+ # If no valid exemption patterns, return errors as-is (no filtering)
617
+ return [err for err in errors if isinstance(err, str)]
618
+
619
+ filtered = []
620
+ for err in errors:
621
+ # Skip non-string errors
622
+ if not isinstance(err, str):
623
+ continue
624
+ is_exempted = False
625
+
626
+ for pattern in exempted_patterns:
627
+ # Skip non-dict patterns
628
+ if not isinstance(pattern, dict):
629
+ continue
630
+
631
+ # Extract pattern fields (all are optional except file_pattern in practice)
632
+ file_pattern = pattern.get("file_pattern", "")
633
+ class_pattern = pattern.get("class_pattern", "")
634
+ method_pattern = pattern.get("method_pattern", "")
635
+ violation_pattern = pattern.get("violation_pattern", "")
636
+
637
+ # Check if all specified patterns match
638
+ # Skip unspecified (empty) patterns - they match everything
639
+ matches_file = not file_pattern or re.search(file_pattern, err)
640
+ matches_class = not class_pattern or re.search(class_pattern, err)
641
+ matches_method = not method_pattern or re.search(method_pattern, err)
642
+ matches_violation = not violation_pattern or re.search(
643
+ violation_pattern, err
644
+ )
645
+
646
+ # All specified patterns must match for exemption
647
+ if matches_file and matches_class and matches_method and matches_violation:
648
+ is_exempted = True
649
+ break
650
+
651
+ if not is_exempted:
652
+ filtered.append(err)
653
+
654
+ return filtered
655
+
656
+
657
+ def _create_filtered_result(
658
+ base_result: ValidationResult,
659
+ filtered_errors: list[str],
660
+ ) -> ValidationResult:
661
+ """
662
+ Create a new validation result with filtered errors (wrapper approach).
663
+
664
+ Avoids mutating the original result object for better functional programming practices.
665
+ Creates new metadata using model_validate to prevent mutation of Pydantic models.
666
+
667
+ Guards against missing attributes on base_result to handle edge cases where
668
+ validation results may have incomplete or missing fields.
669
+
670
+ Args:
671
+ base_result: Original validation result.
672
+ filtered_errors: Filtered error list.
673
+
674
+ Returns:
675
+ New ValidationResult with filtered errors and updated metadata.
676
+ """
677
+ # Guard against missing errors attribute on base_result
678
+ base_errors = getattr(base_result, "errors", None)
679
+ base_errors_count = len(base_errors) if base_errors is not None else 0
680
+
681
+ # Calculate filtering statistics
682
+ violations_filtered = base_errors_count - len(filtered_errors)
683
+ all_violations_exempted = violations_filtered > 0 and len(filtered_errors) == 0
684
+
685
+ # Create new metadata if present (avoid mutation)
686
+ # Use getattr to guard against missing metadata attribute on base_result
687
+ new_metadata = None
688
+ base_metadata = getattr(base_result, "metadata", None)
689
+ if base_metadata is not None:
690
+ # Use model_copy for deep copy with updates (Pydantic v2 pattern)
691
+ # This works with both real Pydantic models and test mocks
692
+ try:
693
+ new_metadata = base_metadata.model_copy(deep=True)
694
+ # Update violations_found if the field exists and is writable
695
+ # Guard against None return from model_copy and missing/read-only attributes
696
+ if new_metadata is not None and hasattr(new_metadata, "violations_found"):
697
+ try:
698
+ new_metadata.violations_found = len(filtered_errors)
699
+ except (AttributeError, TypeError):
700
+ # violations_found may be a read-only property or frozen field
701
+ pass
702
+ except AttributeError:
703
+ # Fallback for test mocks that don't support model_copy
704
+ # Use original metadata without modification to avoid mutation
705
+ new_metadata = base_metadata
706
+
707
+ # Guard against missing attributes on base_result
708
+ # Use getattr with sensible defaults to handle incomplete validation results
709
+ base_is_valid = getattr(base_result, "is_valid", False)
710
+ base_validated_value = getattr(base_result, "validated_value", None)
711
+ base_issues = getattr(base_result, "issues", [])
712
+ base_warnings = getattr(base_result, "warnings", [])
713
+ base_suggestions = getattr(base_result, "suggestions", [])
714
+ base_summary = getattr(base_result, "summary", None)
715
+ base_details = getattr(base_result, "details", None)
716
+
717
+ # Create new result (wrapper pattern - no mutation)
718
+ return ModelValidationResult(
719
+ is_valid=all_violations_exempted or base_is_valid,
720
+ validated_value=base_validated_value,
721
+ issues=base_issues if base_issues is not None else [],
722
+ errors=filtered_errors,
723
+ warnings=base_warnings if base_warnings is not None else [],
724
+ suggestions=base_suggestions if base_suggestions is not None else [],
725
+ summary=base_summary,
726
+ details=base_details,
727
+ metadata=new_metadata,
728
+ )
729
+
730
+
731
+ def validate_infra_contract_deep(
732
+ contract_path: PathInput,
733
+ ) -> ModelContractValidationResult:
734
+ """
735
+ Perform deep contract validation for ONEX compliance.
736
+
737
+ Uses validate_yaml_file() from omnibase_core for comprehensive contract
738
+ checking suitable for autonomous code generation.
739
+
740
+ Args:
741
+ contract_path: Path to the contract YAML file.
742
+
743
+ Returns:
744
+ ModelContractValidationResult with validation status, score, and any errors.
745
+
746
+ Raises:
747
+ OnexError: If YAML validation fails with an unexpected error.
748
+ """
749
+ from uuid import uuid4
750
+
751
+ from omnibase_core.enums import EnumCoreErrorCode
752
+ from omnibase_core.errors import OnexError
753
+
754
+ correlation_id = uuid4()
755
+
756
+ # Use the validation API from omnibase_core 0.6.x directly
757
+ try:
758
+ result = validate_yaml_file(Path(contract_path))
759
+ except Exception as e:
760
+ raise OnexError(
761
+ message=f"YAML validation failed for {contract_path}: {e}",
762
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
763
+ correlation_id=correlation_id,
764
+ contract_path=str(contract_path),
765
+ ) from e
766
+
767
+ # Return a ModelContractValidationResult
768
+ # The API may return a different type, so we adapt it
769
+ if isinstance(result, ModelContractValidationResult):
770
+ return result
771
+
772
+ # If result is a different type, wrap it in ModelContractValidationResult
773
+ # Default to is_valid=False for unknown result types to avoid silently masking failures
774
+ # Check 'is_valid' first, then 'passed' as fallback (some validators use passed)
775
+ return ModelContractValidationResult(
776
+ is_valid=getattr(result, "is_valid", getattr(result, "passed", False)),
777
+ score=getattr(result, "score", 0.0),
778
+ violations=getattr(result, "violations", getattr(result, "errors", [])),
779
+ warnings=getattr(result, "warnings", []),
780
+ interface_version=ModelSemVer(major=1, minor=0, patch=0),
781
+ )
782
+
783
+
784
+ # ==============================================================================
785
+ # Skip Directory Configuration
786
+ # ==============================================================================
787
+ #
788
+ # Skip directories are loaded from validation_exemptions.yaml for configurability.
789
+ # If the YAML file is missing or doesn't contain skip_directories, we fall back
790
+ # to a hardcoded default set.
791
+ #
792
+ # This follows the same pattern as exemption loading to keep all validation
793
+ # configuration in one place.
794
+
795
+
796
+ @lru_cache(maxsize=1)
797
+ def load_skip_directories_from_yaml() -> frozenset[str] | None:
798
+ """
799
+ Load skip directory configuration from YAML.
800
+
801
+ Returns:
802
+ frozenset of directory names to skip, or None if not configured in YAML.
803
+ Returns None (not empty set) to distinguish "not configured" from
804
+ "explicitly empty".
805
+ """
806
+ if not EXEMPTIONS_YAML_PATH.exists():
807
+ return None
808
+
809
+ try:
810
+ with EXEMPTIONS_YAML_PATH.open("r", encoding="utf-8") as f:
811
+ data = yaml.safe_load(f)
812
+
813
+ if not isinstance(data, dict):
814
+ return None
815
+
816
+ skip_dirs = data.get("skip_directories")
817
+ if skip_dirs is None:
818
+ return None
819
+
820
+ # Handle both list and dict formats
821
+ if isinstance(skip_dirs, list):
822
+ # Simple list format: ["archive", "examples", ...]
823
+ return frozenset(str(d) for d in skip_dirs if d)
824
+ elif isinstance(skip_dirs, dict):
825
+ # Dict format with categories: {historical: [...], caches: [...]}
826
+ all_dirs: set[str] = set()
827
+ for category_dirs in skip_dirs.values():
828
+ if isinstance(category_dirs, list):
829
+ all_dirs.update(str(d) for d in category_dirs if d)
830
+ return frozenset(all_dirs) if all_dirs else None
831
+
832
+ return None
833
+
834
+ except (yaml.YAMLError, OSError) as e:
835
+ logger.warning(
836
+ "Failed to load skip directories from %s: %s. Using defaults.",
837
+ EXEMPTIONS_YAML_PATH,
838
+ e,
839
+ )
840
+ return None
841
+
842
+
843
+ def get_skip_directories() -> frozenset[str]:
844
+ """
845
+ Get the set of directory names to skip during validation.
846
+
847
+ Returns skip directories from YAML configuration if available,
848
+ otherwise falls back to the hardcoded SKIP_DIRECTORY_NAMES default.
849
+
850
+ Returns:
851
+ frozenset of directory names that should be excluded from validation.
852
+ """
853
+ yaml_dirs = load_skip_directories_from_yaml()
854
+ if yaml_dirs is not None:
855
+ return yaml_dirs
856
+ return SKIP_DIRECTORY_NAMES
857
+
858
+
859
+ def is_simple_optional(pattern: ModelUnionPattern) -> bool:
860
+ """
861
+ Determine if a union pattern is a simple optional (`X | None`).
862
+
863
+ Simple optionals are the ONEX-preferred pattern for nullable types and should
864
+ NOT count toward the union complexity threshold. They represent:
865
+ - `str | None` - nullable string
866
+ - `int | None` - nullable integer
867
+ - `ModelFoo | None` - nullable model
868
+ - `list[str] | None` - nullable list
869
+
870
+ These are NOT considered complex unions because:
871
+ 1. They are idiomatic Python (PEP 604)
872
+ 2. They express optionality, not type ambiguity
873
+ 3. They don't require complex type narrowing logic
874
+
875
+ Args:
876
+ pattern: The ModelUnionPattern to check.
877
+
878
+ Returns:
879
+ True if the pattern is a simple optional (`X | None`), False otherwise.
880
+
881
+ Examples:
882
+ >>> is_simple_optional(ModelUnionPattern(["str", "None"], 1, "test.py"))
883
+ True
884
+ >>> is_simple_optional(ModelUnionPattern(["int", "None"], 1, "test.py"))
885
+ True
886
+ >>> is_simple_optional(ModelUnionPattern(["str", "int"], 1, "test.py"))
887
+ False
888
+ >>> is_simple_optional(ModelUnionPattern(["str", "int", "None"], 1, "test.py"))
889
+ False
890
+ """
891
+ # Simple optional: exactly 2 types, one of which is None
892
+ return len(pattern.types) == 2 and "None" in pattern.types
893
+
894
+
895
+ # ==============================================================================
896
+ # isinstance() Union Exclusion
897
+ # ==============================================================================
898
+ #
899
+ # Modern Python (PEP 604) allows isinstance(x, A | B) syntax instead of
900
+ # isinstance(x, (A, B)). These are runtime type checks, NOT type annotations.
901
+ # Ruff UP038 encourages this modern syntax.
902
+ #
903
+ # The union validator's goal is to limit complex TYPE ANNOTATIONS that indicate
904
+ # type ambiguity in function signatures. isinstance() unions are:
905
+ # - Runtime expressions, not type hints
906
+ # - Used for dynamic type checking, not static typing
907
+ # - Encouraged by modern Python style guides (ruff UP038)
908
+ #
909
+ # Therefore, isinstance() unions should NOT count toward the union threshold.
910
+
911
+
912
+ class IsinstanceUnionVisitor(ast.NodeVisitor):
913
+ """
914
+ AST visitor to find line numbers where unions appear inside isinstance() calls.
915
+
916
+ This visitor tracks context when descending into isinstance() call arguments,
917
+ marking any union (BitOr) expressions found in the second argument as
918
+ "isinstance unions" that should be excluded from the complexity threshold.
919
+
920
+ The visitor correctly handles:
921
+ - isinstance(x, A | B) - simple isinstance union
922
+ - isinstance(x, A | B | C) - multi-type isinstance union
923
+ - isinstance(x, list[A | B]) - union inside generic (NOT excluded, it's a type hint)
924
+
925
+ Attributes:
926
+ isinstance_union_lines: Set of line numbers containing isinstance unions.
927
+ """
928
+
929
+ def __init__(self) -> None:
930
+ """Initialize the visitor with empty line tracking."""
931
+ self.isinstance_union_lines: set[int] = set()
932
+ self._in_isinstance_type_arg: bool = False
933
+
934
+ def visit_Call(self, node: ast.Call) -> None:
935
+ """
936
+ Visit a Call node and check if it's an isinstance() call.
937
+
938
+ When an isinstance() call is found, we mark that we're inside its
939
+ second argument (the type specification) before visiting children.
940
+ Any BitOr (union) found in this context is tracked.
941
+
942
+ Args:
943
+ node: The Call AST node to visit.
944
+ """
945
+ # Check if this is an isinstance() call
946
+ is_isinstance_call = (
947
+ isinstance(node.func, ast.Name) and node.func.id == "isinstance"
948
+ )
949
+
950
+ if is_isinstance_call and len(node.args) >= 2:
951
+ # Visit the first argument (the object being checked) normally
952
+ self.visit(node.args[0])
953
+
954
+ # Mark that we're in the type argument, then visit it
955
+ self._in_isinstance_type_arg = True
956
+ self.visit(node.args[1])
957
+ self._in_isinstance_type_arg = False
958
+
959
+ # Visit any remaining arguments normally
960
+ for arg in node.args[2:]:
961
+ self.visit(arg)
962
+
963
+ # Visit keyword arguments normally
964
+ for keyword in node.keywords:
965
+ self.visit(keyword)
966
+ else:
967
+ # Not isinstance(), visit normally
968
+ self.generic_visit(node)
969
+
970
+ def visit_BinOp(self, node: ast.BinOp) -> None:
971
+ """
972
+ Visit a BinOp node and track if it's a union inside isinstance().
973
+
974
+ When we're inside an isinstance() type argument and find a BitOr
975
+ (union) operator, we record this line as an isinstance union.
976
+
977
+ Args:
978
+ node: The BinOp AST node to visit.
979
+ """
980
+ if self._in_isinstance_type_arg and isinstance(node.op, ast.BitOr):
981
+ # This is a union inside isinstance() - track the line
982
+ self.isinstance_union_lines.add(node.lineno)
983
+
984
+ # Continue visiting children (for nested unions like A | B | C)
985
+ self.generic_visit(node)
986
+
987
+
988
+ @lru_cache(maxsize=128)
989
+ def _find_isinstance_union_lines(file_path: Path) -> frozenset[int]:
990
+ """
991
+ Find all line numbers containing unions inside isinstance() calls.
992
+
993
+ This function parses the file once and returns all lines where unions
994
+ appear inside isinstance() type arguments. Results are cached to avoid
995
+ repeated parsing when checking multiple patterns from the same file.
996
+
997
+ Args:
998
+ file_path: Path to the Python file to analyze.
999
+
1000
+ Returns:
1001
+ Frozenset of line numbers (1-based) containing isinstance unions.
1002
+ Returns empty frozenset if file cannot be parsed or doesn't exist.
1003
+
1004
+ Examples:
1005
+ >>> # File with: isinstance(x, str | int) on line 5
1006
+ >>> _find_isinstance_union_lines(Path("example.py"))
1007
+ frozenset({5})
1008
+
1009
+ Note:
1010
+ Uses lru_cache to avoid re-parsing files during the same validation run.
1011
+ Cache should be cleared between validation runs if files may have changed.
1012
+ """
1013
+ try:
1014
+ source = file_path.read_text(encoding="utf-8")
1015
+ tree = ast.parse(source, filename=str(file_path))
1016
+ except (OSError, SyntaxError) as e:
1017
+ logger.debug("Cannot parse %s for isinstance detection: %s", file_path, e)
1018
+ return frozenset()
1019
+
1020
+ visitor = IsinstanceUnionVisitor()
1021
+ visitor.visit(tree)
1022
+ return frozenset(visitor.isinstance_union_lines)
1023
+
1024
+
1025
+ def is_isinstance_union(pattern: ModelUnionPattern) -> bool:
1026
+ """
1027
+ Determine if a union pattern is inside an isinstance() call.
1028
+
1029
+ isinstance() unions are runtime type checks, not type annotations.
1030
+ They should NOT count toward the union complexity threshold because:
1031
+ 1. They are runtime expressions, not static type hints
1032
+ 2. Modern Python (PEP 604) encourages isinstance(x, A | B) syntax
1033
+ 3. Ruff UP038 recommends this syntax over isinstance(x, (A, B))
1034
+
1035
+ Args:
1036
+ pattern: The ModelUnionPattern to check.
1037
+
1038
+ Returns:
1039
+ True if the pattern is inside an isinstance() call, False otherwise.
1040
+
1041
+ Examples:
1042
+ >>> # Pattern from: isinstance(x, str | int)
1043
+ >>> is_isinstance_union(pattern_from_isinstance)
1044
+ True
1045
+ >>> # Pattern from: def foo(x: str | int)
1046
+ >>> is_isinstance_union(pattern_from_annotation)
1047
+ False
1048
+
1049
+ Note:
1050
+ This function caches file parsing results for efficiency.
1051
+ """
1052
+ file_path = Path(pattern.file_path)
1053
+ isinstance_lines = _find_isinstance_union_lines(file_path)
1054
+ return pattern.line in isinstance_lines
1055
+
1056
+
1057
+ # ==============================================================================
1058
+ # Path Skipping Configuration
1059
+ # ==============================================================================
1060
+ #
1061
+ # These directories are excluded from validation because:
1062
+ # - archive/archived: Historical code not subject to current validation rules
1063
+ # - examples: Demo code that may intentionally show anti-patterns
1064
+ # - __pycache__: Compiled Python bytecode, not source code
1065
+ # - .git: Git repository metadata
1066
+ # - .venv/venv: Virtual environment directories
1067
+ # - .tox: Tox testing directory
1068
+ # - .mypy_cache: mypy type checking cache
1069
+ # - .pytest_cache: pytest cache directory
1070
+ # - build/dist: Build output directories
1071
+ # - .eggs: setuptools eggs directory
1072
+ # - node_modules: Node.js dependencies (if any JS in repo)
1073
+ #
1074
+ # The set is used for O(1) lookup when checking path components.
1075
+ #
1076
+ # Note: Matching is case-sensitive (Linux standard). On case-insensitive
1077
+ # filesystems (macOS, Windows), "Archive" would NOT match "archive".
1078
+ # This is intentional for portability and consistency.
1079
+ SKIP_DIRECTORY_NAMES: frozenset[str] = frozenset(
1080
+ {
1081
+ # Historical/demo code
1082
+ "archive",
1083
+ "archived",
1084
+ "examples",
1085
+ # Bytecode and caches
1086
+ "__pycache__",
1087
+ ".mypy_cache",
1088
+ ".pytest_cache",
1089
+ # Virtual environments
1090
+ ".venv",
1091
+ "venv",
1092
+ # Build outputs
1093
+ "build",
1094
+ "dist",
1095
+ ".eggs",
1096
+ # Version control
1097
+ ".git",
1098
+ # Testing
1099
+ ".tox",
1100
+ # Node.js (if present)
1101
+ "node_modules",
1102
+ }
1103
+ )
1104
+
1105
+
1106
+ def is_skip_directory(component: str) -> bool:
1107
+ """
1108
+ Check if a path component is a directory that should be skipped.
1109
+
1110
+ This predicate is extracted for reuse and testability. It checks if the
1111
+ given component matches one of the known skip directory names exactly.
1112
+
1113
+ Uses exact string matching (case-sensitive) via set membership for O(1) lookup.
1114
+ This prevents false positives from substring matching.
1115
+
1116
+ Skip directories are loaded from validation_exemptions.yaml if configured,
1117
+ otherwise falls back to the hardcoded SKIP_DIRECTORY_NAMES default.
1118
+ See get_skip_directories() for the configuration loading logic.
1119
+
1120
+ Args:
1121
+ component: A single path component (directory or file name).
1122
+
1123
+ Returns:
1124
+ True if the component is a skip directory name, False otherwise.
1125
+
1126
+ Examples:
1127
+ Exact matches (skipped):
1128
+ >>> is_skip_directory("archived")
1129
+ True
1130
+ >>> is_skip_directory("archive")
1131
+ True
1132
+ >>> is_skip_directory("__pycache__")
1133
+ True
1134
+ >>> is_skip_directory(".venv")
1135
+ True
1136
+ >>> is_skip_directory(".git")
1137
+ True
1138
+
1139
+ Partial/similar names (NOT skipped - prevents false positives):
1140
+ >>> is_skip_directory("archived_feature")
1141
+ False
1142
+ >>> is_skip_directory("my_archive")
1143
+ False
1144
+ >>> is_skip_directory("Archive") # Case-sensitive
1145
+ False
1146
+ >>> is_skip_directory(".git_backup")
1147
+ False
1148
+ """
1149
+ return component in get_skip_directories()
1150
+
1151
+
1152
+ def should_skip_path(path: Path) -> bool:
1153
+ """
1154
+ Check if a path should be skipped for validation.
1155
+
1156
+ Uses exact path component matching to avoid false positives from substring
1157
+ matching. A path is skipped if ANY of its PARENT directory components match
1158
+ a known skip directory name exactly. The filename itself is NOT checked
1159
+ to avoid false positives from files that happen to share names with skip
1160
+ directories (e.g., `archive.py` should not be skipped).
1161
+
1162
+ This approach prevents false positives like:
1163
+ - /foo/archived_feature/bar.py - NOT skipped ("archived_feature" != "archived")
1164
+ - /foo/archive_manager.py - NOT skipped (only checks parent dirs, not filename)
1165
+ - /foo/examples_utils.py - NOT skipped (only checks parent dirs, not filename)
1166
+ - /foo/my_archive/bar.py - NOT skipped ("my_archive" != "archive")
1167
+ - /foo/.git_backup/bar.py - NOT skipped (".git_backup" != ".git")
1168
+
1169
+ While correctly skipping:
1170
+ - /foo/archived/bar.py - Skipped (has "archived" directory component)
1171
+ - /foo/archive/bar.py - Skipped (has "archive" directory component)
1172
+ - /foo/examples/bar.py - Skipped (has "examples" directory component)
1173
+ - /foo/__pycache__/bar.pyc - Skipped (has "__pycache__" directory component)
1174
+ - /foo/.venv/lib/bar.py - Skipped (has ".venv" directory component)
1175
+ - /foo/.git/hooks/pre-commit - Skipped (has ".git" directory component)
1176
+ - /foo/build/lib/bar.py - Skipped (has "build" directory component)
1177
+
1178
+ Args:
1179
+ path: The file path to check.
1180
+
1181
+ Returns:
1182
+ True if the path should be skipped, False otherwise.
1183
+
1184
+ Note:
1185
+ Matching is case-sensitive (Linux standard). On case-insensitive
1186
+ filesystems (macOS, Windows), directories like "Build" or "VENV"
1187
+ would NOT be skipped. This is intentional for cross-platform
1188
+ consistency - use lowercase directory names for skipped directories.
1189
+ """
1190
+ # Check PARENT directory components only (exclude the filename)
1191
+ # This prevents false positives from files named like skip directories
1192
+ # (e.g., archive.py, examples.py)
1193
+ #
1194
+ # path.parts includes all components including filename:
1195
+ # "/foo/archived/bar.py" -> ('/', 'foo', 'archived', 'bar.py')
1196
+ #
1197
+ # path.parent.parts excludes the filename:
1198
+ # "/foo/archived/bar.py" -> ('/', 'foo', 'archived')
1199
+ #
1200
+ # Using parent.parts ensures we only match DIRECTORY names, not filenames
1201
+ return any(is_skip_directory(part) for part in path.parent.parts)
1202
+
1203
+
1204
+ def _count_non_optional_unions(
1205
+ directory: Path,
1206
+ ) -> tuple[int, int, int, int, list[str]]:
1207
+ """
1208
+ Count unions in a directory, excluding simple optional and isinstance patterns.
1209
+
1210
+ This function provides accurate union counting for threshold checks by
1211
+ excluding:
1212
+ - Idiomatic `X | None` patterns (simple optionals) that are valid ONEX style
1213
+ - isinstance(x, A | B) patterns that are runtime type checks, not annotations
1214
+
1215
+ Args:
1216
+ directory: Directory to scan for Python files.
1217
+
1218
+ Returns:
1219
+ Tuple of (threshold_count, total_count, optional_count, isinstance_count, issues):
1220
+ - threshold_count: Count of unions that count toward threshold
1221
+ (excludes both `X | None` and isinstance patterns)
1222
+ - total_count: Total count of all unions (for reporting)
1223
+ - optional_count: Count of simple `X | None` patterns excluded
1224
+ - isinstance_count: Count of isinstance unions excluded
1225
+ - issues: List of validation issues found
1226
+ """
1227
+ total_unions = 0
1228
+ threshold_unions = 0
1229
+ optional_unions = 0
1230
+ isinstance_unions = 0
1231
+ all_issues: list[str] = []
1232
+
1233
+ for py_file in directory.rglob("*.py"):
1234
+ # Filter out archived files, examples, and __pycache__
1235
+ if should_skip_path(py_file):
1236
+ continue
1237
+
1238
+ union_count, issues, patterns = validate_union_usage_file(py_file)
1239
+ total_unions += union_count
1240
+
1241
+ # Count and categorize patterns
1242
+ for pattern in patterns:
1243
+ if is_simple_optional(pattern):
1244
+ optional_unions += 1
1245
+ elif is_isinstance_union(pattern):
1246
+ isinstance_unions += 1
1247
+ else:
1248
+ threshold_unions += 1
1249
+
1250
+ # Prefix issues with file path
1251
+ if issues:
1252
+ all_issues.extend([f"{py_file}: {issue}" for issue in issues])
1253
+
1254
+ return (
1255
+ threshold_unions,
1256
+ total_unions,
1257
+ optional_unions,
1258
+ isinstance_unions,
1259
+ all_issues,
1260
+ )
1261
+
1262
+
1263
+ def validate_infra_union_usage(
1264
+ directory: PathInput = INFRA_SRC_PATH,
1265
+ max_unions: int = INFRA_MAX_UNIONS,
1266
+ strict: bool = INFRA_UNIONS_STRICT,
1267
+ ) -> ValidationResult:
1268
+ """
1269
+ Validate Union type usage in infrastructure code.
1270
+
1271
+ Prevents overly complex union types that complicate infrastructure code.
1272
+
1273
+ This validator EXCLUDES the following patterns from the count:
1274
+ - Simple optional patterns (`X | None`) - idiomatic nullable types
1275
+ - isinstance() unions (`isinstance(x, A | B)`) - runtime type checks
1276
+
1277
+ Only actual complex TYPE ANNOTATIONS count toward the threshold.
1278
+
1279
+ What IS counted (threshold applies to):
1280
+ - Multi-type unions in annotations: `def foo(x: str | int)`
1281
+ - Complex patterns: unions with 3+ types in annotations
1282
+ - Non-optional type hints: any annotation union without `None`
1283
+
1284
+ What is NOT counted (excluded from threshold):
1285
+ - Simple optionals: `X | None` where X is any single type
1286
+ - isinstance() unions: `isinstance(x, A | B)` (runtime checks, not annotations)
1287
+ - These are either idiomatic Python or runtime expressions, not type complexity
1288
+
1289
+ Exemptions:
1290
+ Exemption patterns are loaded from validation_exemptions.yaml (union_exemptions section).
1291
+ See that file for the complete list of exemptions with rationale.
1292
+
1293
+ Key exemption categories:
1294
+ - ModelNodeCapabilities.config: JSON-like configuration pattern with primitive unions
1295
+
1296
+ Args:
1297
+ directory: Directory to validate. Defaults to infrastructure source.
1298
+ max_unions: Maximum union count threshold. Defaults to INFRA_MAX_UNIONS.
1299
+ Note: This threshold applies only after excluding optionals and isinstance.
1300
+ strict: Enable strict mode. Defaults to INFRA_UNIONS_STRICT (True).
1301
+
1302
+ Returns:
1303
+ ModelValidationResult with validation status and any errors.
1304
+ The metadata includes total_unions (all unions), threshold_unions (what counts),
1305
+ and breakdown of excluded patterns for transparency.
1306
+
1307
+ Metadata Extension Fields:
1308
+ ModelValidationMetadata uses `extra="allow"` to support domain-specific fields.
1309
+ The following extension fields are used by this validator and are properly typed:
1310
+
1311
+ - non_optional_unions (int): Count of unions after all exclusions.
1312
+ This is what the threshold check applies to.
1313
+ - optional_unions_excluded (int): Count of simple `X | None` optionals excluded.
1314
+ - isinstance_unions_excluded (int): Count of isinstance() unions excluded.
1315
+
1316
+ These fields are additional to the base ModelValidationMetadata fields like
1317
+ total_unions and max_unions which are formally defined on the model.
1318
+ """
1319
+ # Convert to Path if string
1320
+ dir_path = Path(directory) if isinstance(directory, str) else directory
1321
+
1322
+ # Count unions with exclusion of simple optionals and isinstance patterns
1323
+ threshold_count, total_count, optional_count, isinstance_count, issues = (
1324
+ _count_non_optional_unions(dir_path)
1325
+ )
1326
+
1327
+ # Load exemption patterns from YAML configuration
1328
+ exempted_patterns = get_union_exemptions()
1329
+
1330
+ # Filter errors using regex-based pattern matching
1331
+ filtered_issues = _filter_exempted_errors(issues, exempted_patterns)
1332
+
1333
+ # Determine validity: threshold count must be within max
1334
+ # and no issues in strict mode
1335
+ is_valid = (threshold_count <= max_unions) and (not filtered_issues or not strict)
1336
+
1337
+ # Count Python files for metadata (excluding archive, examples, __pycache__)
1338
+ python_files = list(dir_path.rglob("*.py"))
1339
+ files_processed = len([f for f in python_files if not should_skip_path(f)])
1340
+
1341
+ # Create result with enhanced metadata showing all counts
1342
+ # Note: ModelValidationMetadata uses extra="allow", so extension fields
1343
+ # are accepted as int values.
1344
+ # See docstring "Metadata Extension Fields" section for field documentation.
1345
+ #
1346
+ # Extension fields are passed via model_construct() to satisfy type checker
1347
+ # while preserving runtime behavior with extra="allow".
1348
+ metadata_fields: dict[str, object] = {
1349
+ # Standard ModelValidationMetadata fields (formally defined)
1350
+ "validation_type": "union_usage",
1351
+ "files_processed": files_processed,
1352
+ "violations_found": len(filtered_issues),
1353
+ "total_unions": total_count, # Base field: all unions found
1354
+ "max_unions": max_unions, # Base field: configured threshold
1355
+ "strict_mode": strict, # Base field: whether strict mode enabled
1356
+ # Extension fields (via extra="allow", typed as int)
1357
+ # These provide transparency into the exclusion logic:
1358
+ "non_optional_unions": threshold_count, # What threshold actually checks
1359
+ "optional_unions_excluded": optional_count, # X | None patterns
1360
+ "isinstance_unions_excluded": isinstance_count, # isinstance(x, A | B) patterns
1361
+ }
1362
+ return ModelValidationResult(
1363
+ is_valid=is_valid,
1364
+ errors=filtered_issues,
1365
+ metadata=ModelValidationMetadata.model_construct(**metadata_fields), # type: ignore[arg-type]
1366
+ )
1367
+
1368
+
1369
+ def validate_infra_circular_imports(
1370
+ directory: PathInput = INFRA_SRC_PATH,
1371
+ ) -> ModelImportValidationResult:
1372
+ """
1373
+ Check for circular imports in infrastructure code.
1374
+
1375
+ Infrastructure packages have complex dependencies; circular imports
1376
+ cause runtime issues that are hard to debug.
1377
+
1378
+ Args:
1379
+ directory: Directory to check. Defaults to infrastructure source.
1380
+
1381
+ Returns:
1382
+ ModelImportValidationResult with detailed import validation results.
1383
+ Use result.has_circular_imports to check for issues.
1384
+ """
1385
+ validator = CircularImportValidator(source_path=Path(directory))
1386
+ return validator.validate()
1387
+
1388
+
1389
+ def validate_infra_all(
1390
+ directory: PathInput = INFRA_SRC_PATH,
1391
+ nodes_directory: PathInput = INFRA_NODES_PATH,
1392
+ ) -> dict[str, ValidationResult | ModelImportValidationResult]:
1393
+ """
1394
+ Run all validations on infrastructure code.
1395
+
1396
+ Executes all 5 validators with infrastructure-appropriate defaults:
1397
+ - Architecture (strict, 0 violations)
1398
+ - Contracts (nodes directory)
1399
+ - Patterns (strict mode)
1400
+ - Union usage (max INFRA_MAX_UNIONS)
1401
+ - Circular imports
1402
+
1403
+ Args:
1404
+ directory: Main source directory. Defaults to infrastructure source.
1405
+ nodes_directory: Nodes directory for contract validation.
1406
+
1407
+ Returns:
1408
+ Dictionary mapping validator name to result.
1409
+ """
1410
+ results: dict[str, ValidationResult | ModelImportValidationResult] = {}
1411
+
1412
+ # HIGH priority validators
1413
+ results["architecture"] = validate_infra_architecture(directory)
1414
+ results["contracts"] = validate_infra_contracts(nodes_directory)
1415
+ results["patterns"] = validate_infra_patterns(directory)
1416
+
1417
+ # MEDIUM priority validators
1418
+ results["union_usage"] = validate_infra_union_usage(directory)
1419
+ results["circular_imports"] = validate_infra_circular_imports(directory)
1420
+
1421
+ return results
1422
+
1423
+
1424
+ def get_validation_summary(
1425
+ results: dict[str, ValidationResult | ModelImportValidationResult],
1426
+ ) -> dict[str, int | list[str]]:
1427
+ """
1428
+ Generate a summary of validation results.
1429
+
1430
+ Args:
1431
+ results: Dictionary of validation results from validate_infra_all().
1432
+
1433
+ Returns:
1434
+ Dictionary with summary statistics including passed/failed counts and failed validators.
1435
+ Returns zero counts if input is not a dictionary.
1436
+ """
1437
+ # Defensive type check for dict input
1438
+ if not isinstance(results, dict):
1439
+ return {
1440
+ "total_validators": 0,
1441
+ "passed": 0,
1442
+ "failed": 0,
1443
+ "failed_validators": [],
1444
+ }
1445
+
1446
+ passed = 0
1447
+ failed = 0
1448
+ failed_validators: list[str] = []
1449
+
1450
+ for name, result in results.items():
1451
+ # Skip entries with non-string keys
1452
+ if not isinstance(name, str):
1453
+ continue
1454
+ # Use duck typing to determine result API:
1455
+ # - ModelModuleImportResult has 'has_circular_imports' attribute
1456
+ # - ModelValidationResult has 'is_valid' attribute
1457
+ # This follows ONEX convention of duck typing over isinstance for protocols.
1458
+ if hasattr(result, "has_circular_imports"):
1459
+ # Circular import validator uses has_circular_imports
1460
+ if not result.has_circular_imports:
1461
+ passed += 1
1462
+ else:
1463
+ failed += 1
1464
+ failed_validators.append(name)
1465
+ elif hasattr(result, "is_valid"):
1466
+ # Standard ModelValidationResult uses is_valid
1467
+ if result.is_valid:
1468
+ passed += 1
1469
+ else:
1470
+ failed += 1
1471
+ failed_validators.append(name)
1472
+
1473
+ return {
1474
+ "total_validators": passed + failed,
1475
+ "passed": passed,
1476
+ "failed": failed,
1477
+ "failed_validators": failed_validators,
1478
+ }
1479
+
1480
+
1481
+ __all__ = [
1482
+ # Constants
1483
+ "EXEMPTIONS_YAML_PATH", # Path to exemptions YAML file
1484
+ "INFRA_MAX_UNIONS", # Maximum union count threshold
1485
+ "INFRA_MAX_VIOLATIONS", # Maximum violations threshold
1486
+ "INFRA_NODES_PATH", # Nodes directory path
1487
+ "INFRA_PATTERNS_STRICT", # Strict pattern validation flag
1488
+ "INFRA_SRC_PATH", # Source directory path
1489
+ "INFRA_UNIONS_STRICT", # Strict union validation flag
1490
+ "SKIP_DIRECTORY_NAMES", # Directories to skip
1491
+ # Types
1492
+ "ExemptionPattern", # Exemption pattern TypedDict
1493
+ "ModelModuleImportResult", # Re-export from omnibase_core
1494
+ "ValidationResult", # Type alias for validation result
1495
+ # Exemption loaders
1496
+ "get_architecture_exemptions", # Architecture exemption loader
1497
+ "get_pattern_exemptions", # Pattern exemption loader
1498
+ "get_skip_directories", # Skip directory loader
1499
+ "get_union_exemptions", # Union exemption loader
1500
+ "get_validation_summary", # Validation summary generator
1501
+ # Path utilities
1502
+ "is_isinstance_union", # Check if union is in isinstance() call
1503
+ "is_simple_optional", # Check if union is X | None
1504
+ "is_skip_directory", # Check if directory should be skipped
1505
+ "load_skip_directories_from_yaml", # Load skip dirs from YAML
1506
+ "should_skip_path", # Check if path should be skipped
1507
+ # Validators
1508
+ "validate_infra_all", # Run all validators
1509
+ "validate_infra_architecture", # Architecture validation
1510
+ "validate_infra_circular_imports", # Circular import check
1511
+ "validate_infra_contracts", # Contract validation
1512
+ "validate_infra_patterns", # Pattern validation
1513
+ "validate_infra_union_usage", # Union usage validation
1514
+ ]