omnibase_infra 0.2.1__py3-none-any.whl

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