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,1502 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Policy Registry - SINGLE SOURCE OF TRUTH for policy plugin registration.
4
+
5
+ This module provides the RegistryPolicy class for registering and resolving
6
+ pure decision policy plugins in the ONEX infrastructure layer.
7
+
8
+ The registry is responsible for:
9
+ - Registering policy plugins by (policy_id, policy_type, version) tuple
10
+ - Resolving policy classes for specific policy configurations
11
+ - Thread-safe registration operations
12
+ - Listing all registered policies
13
+ - Enforcing synchronous-by-default policy execution
14
+
15
+ Design Principles:
16
+ - Single source of truth: All policy registrations go through this registry
17
+ - Sync enforcement: Async policies must be explicitly flagged
18
+ - Type-safe: Full typing for policy registrations (no Any types)
19
+ - Thread-safe: Registration operations protected by lock
20
+ - Testable: Easy to mock and test policy configurations
21
+
22
+ Policy Categories (by policy type):
23
+ - Orchestrator policies: Workflow coordination, retry strategies, routing
24
+ - Reducer policies: State aggregation, conflict resolution, projections
25
+
26
+ CRITICAL: Policy plugins are PURE decision logic only.
27
+
28
+ Policy plugins MUST NOT:
29
+ - Perform I/O operations (file, network, database)
30
+ - Have side effects (state mutation outside return values)
31
+ - Make external service calls
32
+ - Log at runtime
33
+ - Depend on mutable global state
34
+
35
+ Example Usage:
36
+ ```python
37
+ from omnibase_core.container import ModelONEXContainer
38
+ from omnibase_infra.runtime.registry_policy import RegistryPolicy, ModelPolicyRegistration
39
+ from omnibase_infra.runtime.util_container_wiring import wire_infrastructure_services
40
+ from omnibase_infra.enums import EnumPolicyType
41
+
42
+ # Container-based DI (preferred)
43
+ container = ModelONEXContainer()
44
+ await wire_infrastructure_services(container)
45
+ registry = await container.service_registry.resolve_service(RegistryPolicy)
46
+
47
+ # Register a synchronous policy using the model (PREFERRED API)
48
+ registration = ModelPolicyRegistration(
49
+ policy_id="exponential_backoff",
50
+ policy_class=ExponentialBackoffPolicy,
51
+ policy_type=EnumPolicyType.ORCHESTRATOR,
52
+ version="1.0.0",
53
+ )
54
+ registry.register(registration)
55
+
56
+ # Register using convenience method (preserves original API)
57
+ # Note: For new code, prefer register(ModelPolicyRegistration(...)) instead
58
+ registry.register_policy(
59
+ policy_id="async_merge",
60
+ policy_class=AsyncMergePolicy,
61
+ policy_type=EnumPolicyType.REDUCER,
62
+ version="1.0.0",
63
+ allow_async=True, # MUST be explicit for async policies
64
+ )
65
+
66
+ # Retrieve a policy
67
+ policy_cls = registry.get("exponential_backoff")
68
+ policy = policy_cls()
69
+ result = policy.evaluate(context)
70
+
71
+ # List all policies
72
+ policies = registry.list_keys() # [(id, type, version), ...]
73
+ ```
74
+
75
+ Integration Points:
76
+ - RuntimeHostProcess uses this registry to discover and instantiate policies
77
+ - Policies are loaded based on contract definitions
78
+ - Supports hot-reload patterns for development
79
+ """
80
+
81
+ from __future__ import annotations
82
+
83
+ import functools
84
+ import threading
85
+ import warnings
86
+ from collections.abc import Callable
87
+ from typing import TYPE_CHECKING
88
+
89
+ from pydantic import ValidationError
90
+
91
+ from omnibase_core.errors import ModelOnexError
92
+ from omnibase_core.models.primitives import ModelSemVer
93
+ from omnibase_infra.enums import EnumInfraTransportType, EnumPolicyType
94
+ from omnibase_infra.errors import PolicyRegistryError, ProtocolConfigurationError
95
+ from omnibase_infra.models.errors.model_infra_error_context import (
96
+ ModelInfraErrorContext,
97
+ )
98
+ from omnibase_infra.runtime.mixin_policy_validation import MixinPolicyValidation
99
+ from omnibase_infra.runtime.mixin_semver_cache import MixinSemverCache
100
+ from omnibase_infra.runtime.models import ModelPolicyKey, ModelPolicyRegistration
101
+ from omnibase_infra.runtime.util_version import normalize_version
102
+ from omnibase_infra.types import PolicyTypeInput
103
+
104
+ if TYPE_CHECKING:
105
+ from omnibase_infra.runtime.protocol_policy import ProtocolPolicy
106
+
107
+
108
+ # =============================================================================
109
+ # Policy Registry
110
+ # =============================================================================
111
+
112
+
113
+ class RegistryPolicy(MixinPolicyValidation, MixinSemverCache):
114
+ """SINGLE SOURCE OF TRUTH for policy plugin registration in omnibase_infra.
115
+
116
+ Thread-safe registry for policy plugins. Manages pure decision logic plugins
117
+ that can be used by orchestrator and reducer nodes.
118
+
119
+ The registry maintains a mapping from ModelPolicyKey instances to policy classes
120
+ that implement the ProtocolPolicy protocol. ModelPolicyKey provides strong typing
121
+ and replaces the legacy tuple[str, str, str] pattern.
122
+
123
+ Container Integration:
124
+ RegistryPolicy is designed to be managed by ModelONEXContainer from omnibase_core.
125
+ Use container_wiring.wire_infrastructure_services() to register RegistryPolicy
126
+ in the container, then resolve it via:
127
+
128
+ ```python
129
+ from omnibase_core.container import ModelONEXContainer
130
+ from omnibase_infra.runtime.registry_policy import RegistryPolicy
131
+
132
+ # Resolve from container (preferred) - async in omnibase_core v0.5.6+
133
+ registry = await container.service_registry.resolve_service(RegistryPolicy)
134
+
135
+ # Or use helper function (also async)
136
+ from omnibase_infra.runtime.util_container_wiring import get_policy_registry_from_container
137
+ registry = await get_policy_registry_from_container(container)
138
+ ```
139
+
140
+ Thread Safety:
141
+ All registration operations are protected by a threading.Lock to ensure
142
+ thread-safe access in concurrent environments.
143
+
144
+ Sync Enforcement:
145
+ By default, policies must be synchronous. If a policy has async methods
146
+ (evaluate, decide, reduce), registration will fail unless
147
+ allow_async=True is explicitly specified.
148
+
149
+ Scale and Performance Characteristics:
150
+
151
+ Expected Registry Scale:
152
+ - Typical ONEX system: 20-50 unique policies across 2-5 versions each
153
+ - Medium deployment: 50-100 policies across 3-8 versions each
154
+ - Large deployment: 100-200 policies across 5-10 versions each
155
+ - Stress tested: 500+ total registrations (100 policies x 5 versions)
156
+
157
+ Policy categories (by policy_type):
158
+ - Orchestrator policies: Workflow coordination, retry strategies, routing
159
+ - Reducer policies: State aggregation, conflict resolution, projections
160
+
161
+ Typical distribution: 60% orchestrator policies, 40% reducer policies
162
+
163
+ Performance Characteristics:
164
+
165
+ Primary Operations:
166
+ - register(): O(1) - Direct dictionary insert with secondary index update
167
+ - get(policy_id): O(1) best case, O(k) average, O(k*log k) filtered worst case
168
+ where k = number of matching versions after filtering
169
+ - Uses secondary index (_policy_id_index) for O(1) policy_id lookup
170
+ - Fast path (no filters, single version): O(1) direct lookup
171
+ - Multi-version (no filters): O(k) to find max version via comparison
172
+ - Filtered path (policy_type + multi-version): O(k*log k) for filter + semver sort
173
+ - Deferred error generation: Expensive _list_internal() only on error
174
+ - Cached semver parsing: LRU cache (128 entries) avoids re-parsing
175
+
176
+ - is_registered(): O(k) where k = versions for policy_id
177
+ - list_keys(): O(n*log n) where n = total registrations (full scan + sort)
178
+ - list_versions(): O(k) where k = versions for policy_id
179
+ - unregister(): O(k) where k = versions for policy_id
180
+
181
+ Benchmark Results (500 policy registrations):
182
+ - 1000 sequential get() calls: < 100ms (< 0.1ms per lookup)
183
+ - 1000 concurrent get() calls (10 threads): < 500ms
184
+ - 100 failed lookups (missing policy_id): < 500ms (early exit optimization)
185
+ - Fast path speedup vs filtered path: > 1.1x
186
+
187
+ Lock Contention:
188
+ - Read operations (get, is_registered): Hold lock during lookup only
189
+ - Write operations (register, unregister): Hold lock for full operation
190
+ - Critical sections minimized to reduce contention
191
+ - Expected concurrent throughput: > 2000 reads/sec under 10-thread load
192
+
193
+ Memory Footprint:
194
+
195
+ Per Policy Registration:
196
+ - ModelPolicyKey: ~200 bytes (3 strings: policy_id, policy_type, version)
197
+ - Policy class reference: 8 bytes (Python object pointer)
198
+ - Secondary index entry: ~50 bytes (list entry + key reference)
199
+ - Total per registration: ~260 bytes
200
+
201
+ Estimated Registry Memory:
202
+ - 50 registrations: ~13 KB
203
+ - 100 registrations: ~26 KB
204
+ - 500 registrations: ~130 KB
205
+ - 1000 registrations: ~260 KB
206
+
207
+ Cache Overhead:
208
+ - Semver LRU cache: 128 entries x ~100 bytes = ~12.8 KB
209
+ - Total with cache: Registry memory + 12.8 KB
210
+
211
+ Note: Memory footprint is negligible compared to typical ONEX process memory
212
+ (100-500 MB). Registry memory is not a bottleneck in production systems.
213
+
214
+ Secondary Indexes (Performance Optimization):
215
+
216
+ Current Indexes:
217
+ - _policy_id_index: Maps policy_id -> list[ModelPolicyKey]
218
+ - Purpose: O(1) lookup by policy_id (avoids O(n) scan of all registrations)
219
+ - Updated on: register(), unregister()
220
+ - Memory: ~50 bytes per policy_id + 8 bytes per version
221
+ - Hit rate: 100% for all get() operations
222
+
223
+ When to Add Additional Indexes:
224
+
225
+ Consider _policy_type_index if:
226
+ - Frequent list_keys(policy_type=...) calls (currently O(n))
227
+ - Deployment has > 500 total registrations
228
+ - Profiling shows list_keys filtering as bottleneck
229
+
230
+ Consider _version_index if:
231
+ - Frequent cross-policy version queries
232
+ - Complex version-based policy routing logic
233
+ - Deployment has > 10 versions per policy on average
234
+
235
+ Trade-off Analysis:
236
+ - Each index adds ~50-100 bytes per entry
237
+ - Benefits: O(n) -> O(1) for filtered queries
238
+ - Costs: Write amplification (update multiple indexes per register/unregister)
239
+ - Recommendation: Profile first, optimize only if proven bottleneck
240
+
241
+ Monitoring Recommendations:
242
+
243
+ Key Metrics to Track:
244
+ 1. Registry size: len(registry) - Track growth over time
245
+ 2. Lookup latency: Time for get() operations (p50, p95, p99)
246
+ 3. Lookup errors: PolicyRegistryError frequency (indicates config issues)
247
+ 4. Cache hit rate: LRU cache effectiveness (_parse_semver cache)
248
+ 5. Lock contention: Concurrent access patterns and throughput
249
+
250
+ Performance Thresholds (alert if exceeded):
251
+ - Average get() latency: > 1ms (indicates potential lock contention)
252
+ - P99 get() latency: > 10ms (indicates blocking on write operations)
253
+ - Registry size: > 1000 registrations (may need index optimization)
254
+ - Cache miss rate: > 10% (indicates cache size insufficient)
255
+ - Concurrent throughput: < 1000 reads/sec (indicates lock bottleneck)
256
+
257
+ Recommended Instrumentation:
258
+ ```python
259
+ import time
260
+ from omnibase_core.metrics import histogram, counter
261
+
262
+ # In production RegistryPolicy wrapper:
263
+ start = time.perf_counter()
264
+ policy_cls = registry.get(policy_id)
265
+ histogram("policy_registry.get_latency_ms", (time.perf_counter() - start) * 1000)
266
+ counter("policy_registry.get_total")
267
+
268
+ # Track registry growth:
269
+ histogram("policy_registry.size", len(registry))
270
+ ```
271
+
272
+ Health Check Integration:
273
+ - Include len(registry) in health check response
274
+ - Alert if registry empty (indicates bootstrap failure)
275
+ - Alert if registry size changes unexpectedly (> 20% delta)
276
+
277
+ Trust Model and Security Considerations:
278
+
279
+ RegistryPolicy performs LIMITED validation on registered policy classes.
280
+ This section documents what guarantees exist and what the caller is
281
+ responsible for.
282
+
283
+ VALIDATED (by RegistryPolicy):
284
+ - Async method detection: Policies with async methods (reduce, decide,
285
+ evaluate) must explicitly set allow_async=True. This prevents
286
+ accidental async policy registration that could cause runtime issues.
287
+ - Policy type validation: policy_type must be a valid EnumPolicyType
288
+ value ("orchestrator" or "reducer"). Invalid types raise PolicyRegistryError.
289
+ - Version format validation: version must be valid semver format
290
+ (e.g., "1.0.0", "1.2.3-beta"). Invalid formats raise ProtocolConfigurationError.
291
+ - Non-empty policy_id: Validated via ModelPolicyRegistration Pydantic model.
292
+ - Thread-safe registration: Registration operations are protected by lock.
293
+
294
+ NOT VALIDATED (caller's responsibility):
295
+ - Policy class correctness: The registry does not verify that a policy
296
+ class correctly implements ProtocolPolicy methods. A class missing
297
+ required methods will only fail at runtime when invoked.
298
+ - Policy class safety: No static analysis, sandboxing, or security
299
+ scanning is performed. Malicious code in a policy class will execute
300
+ with the same privileges as the host process.
301
+ - Policy behavior: The registry cannot validate that policy decision
302
+ logic is correct, deterministic, or free of bugs.
303
+ - Policy dependencies: Import-time side effects, malicious dependencies,
304
+ or resource-intensive imports are not prevented.
305
+ - Runtime behavior: Policies that hang, exhaust memory, raise unexpected
306
+ exceptions, or violate timeouts are not sandboxed.
307
+ - Idempotency: The registry does not verify that policies are idempotent
308
+ or safe to retry.
309
+
310
+ Trust Assumptions:
311
+ 1. Policy classes come from TRUSTED sources only:
312
+ - Internal codebase modules
313
+ - Vetted first-party packages
314
+ - Audited third-party packages
315
+ 2. Policy classes do not execute arbitrary code on registration:
316
+ - No __init_subclass__ side effects
317
+ - No metaclass execution during class reference
318
+ - No import-time network calls or file I/O
319
+ 3. Policy instances are created and used within the same trust boundary:
320
+ - No cross-tenant policy sharing
321
+ - No user-provided policy classes at runtime
322
+ 4. Policy implementers follow the purity contract:
323
+ - No I/O operations (file, network, database)
324
+ - No side effects (state mutation outside return values)
325
+ - No external service calls
326
+ - No runtime logging (use structured outputs only)
327
+
328
+ For High-Security Environments:
329
+ If deploying RegistryPolicy in environments with stricter security
330
+ requirements, consider implementing additional safeguards:
331
+
332
+ - Code Review: Mandatory review for all policy implementations before
333
+ registration approval.
334
+
335
+ - Static Analysis: Run linters and security scanners on policy modules
336
+ before allowing registration:
337
+ ```python
338
+ # Example: Pre-registration validation hook
339
+ def validate_policy_module(module_path: str) -> bool:
340
+ # Run bandit, semgrep, or custom security checks
341
+ result = run_security_scan(module_path)
342
+ return result.passed
343
+ ```
344
+
345
+ - Allowlist Pattern: Maintain an explicit allowlist of approved policy_ids
346
+ and reject registration attempts for unlisted policies:
347
+ ```python
348
+ APPROVED_POLICIES = {"exponential_backoff", "rate_limiter", "retry_strategy"}
349
+
350
+ def register_with_allowlist(registration: ModelPolicyRegistration) -> None:
351
+ if registration.policy_id not in APPROVED_POLICIES:
352
+ raise PolicyRegistryError(
353
+ f"Policy '{registration.policy_id}' not in approved list",
354
+ policy_id=registration.policy_id,
355
+ )
356
+ registry.register(registration)
357
+ ```
358
+
359
+ - Sandboxing: Execute policy code in isolated environments (not built
360
+ into RegistryPolicy, requires external infrastructure):
361
+ - Process isolation (subprocess with resource limits)
362
+ - Container isolation (Docker with security profiles)
363
+ - WASM isolation (for extreme security requirements)
364
+
365
+ - Runtime Monitoring: Instrument policy execution with timeouts and
366
+ resource monitoring:
367
+ ```python
368
+ async def execute_policy_with_limits(
369
+ policy: ProtocolPolicy,
370
+ context: dict,
371
+ timeout_seconds: float = 1.0,
372
+ ) -> PolicyResult:
373
+ try:
374
+ return await asyncio.wait_for(
375
+ policy.evaluate(context),
376
+ timeout=timeout_seconds,
377
+ )
378
+ except asyncio.TimeoutError:
379
+ raise PolicyRegistryError(
380
+ f"Policy '{policy.policy_id}' exceeded timeout",
381
+ policy_id=policy.policy_id,
382
+ )
383
+ ```
384
+
385
+ Safe Usage Patterns:
386
+
387
+ DO:
388
+ - Register policies from known, reviewed source modules
389
+ - Use container-based DI for better lifecycle management
390
+ - Document policy dependencies and requirements
391
+ - Test policies in isolation before registration
392
+ - Monitor policy execution metrics (latency, error rates)
393
+
394
+ DON'T:
395
+ - Register policy classes provided by untrusted users
396
+ - Allow dynamic policy class construction from user input
397
+ - Skip code review for new policy implementations
398
+ - Assume policies are safe because they're in the registry
399
+ - Share registries across trust boundaries
400
+
401
+ See Also:
402
+ - docs/patterns/policy_registry_trust_model.md for detailed security guide
403
+ - ProtocolPolicy for interface requirements
404
+ - ModelPolicyRegistration for registration model validation
405
+
406
+ Attributes:
407
+ _registry: Internal dictionary mapping ModelPolicyKey instances to policy classes
408
+ _lock: Threading lock for thread-safe registration operations
409
+ _policy_id_index: Secondary index for O(1) policy_id lookup
410
+
411
+ Inherited from MixinSemverCache:
412
+ SEMVER_CACHE_SIZE: Class variable for configuring LRU cache size (default: 128)
413
+
414
+ Class-Level Configuration:
415
+ The semver parsing cache size can be configured for large deployments:
416
+
417
+ ```python
418
+ # Option 1: Set class attribute before first use
419
+ RegistryPolicy.SEMVER_CACHE_SIZE = 256
420
+
421
+ # Option 2: Use configure method (recommended)
422
+ RegistryPolicy.configure_semver_cache(maxsize=256)
423
+
424
+ # Must be done BEFORE any registry operations
425
+ registry = RegistryPolicy()
426
+ ```
427
+
428
+ For testing, use _reset_semver_cache() to clear and reconfigure.
429
+
430
+ Example:
431
+ >>> from omnibase_infra.runtime.models import ModelPolicyRegistration
432
+ >>> registry = RegistryPolicy()
433
+ >>> registration = ModelPolicyRegistration(
434
+ ... policy_id="retry_backoff",
435
+ ... policy_class=RetryBackoffPolicy,
436
+ ... policy_type=EnumPolicyType.ORCHESTRATOR,
437
+ ... )
438
+ >>> registry.register(registration)
439
+ >>> policy_cls = registry.get("retry_backoff")
440
+ >>> print(registry.list_keys())
441
+ [('retry_backoff', 'orchestrator', '1.0.0')]
442
+ """
443
+
444
+ def __init__(self) -> None:
445
+ """Initialize an empty policy registry with thread lock."""
446
+ # Key: ModelPolicyKey -> policy_class (strong typing replaces tuple pattern)
447
+ self._registry: dict[ModelPolicyKey, type[ProtocolPolicy]] = {}
448
+ self._lock: threading.Lock = threading.Lock()
449
+
450
+ # Performance optimization: Secondary indexes for O(1) lookups
451
+ # Maps policy_id -> list of ModelPolicyKey instances
452
+ self._policy_id_index: dict[str, list[ModelPolicyKey]] = {}
453
+
454
+ # Note: _validate_protocol_implementation and _validate_sync_enforcement
455
+ # are inherited from MixinPolicyValidation with the correct signatures.
456
+ # Do not override them here.
457
+
458
+ def _normalize_policy_type(
459
+ self,
460
+ policy_type: PolicyTypeInput,
461
+ ) -> str:
462
+ """Normalize policy type to string value and validate against EnumPolicyType.
463
+
464
+ This method provides centralized policy type validation logic used by all
465
+ registration and query methods. It accepts both EnumPolicyType enum values
466
+ and string literals, normalizing them to their string representation while
467
+ ensuring they match valid EnumPolicyType values.
468
+
469
+ Validation Process:
470
+ 1. If policy_type is EnumPolicyType instance, extract .value
471
+ 2. If policy_type is string, validate against EnumPolicyType values
472
+ 3. Raise PolicyRegistryError if string doesn't match any enum value
473
+ 4. Return normalized string value
474
+
475
+ This centralized validation ensures consistent policy type handling across
476
+ all registry operations (register, get, list_keys, is_registered, unregister).
477
+
478
+ Args:
479
+ policy_type: Policy type as EnumPolicyType enum or string literal.
480
+ Valid values: "orchestrator", "reducer"
481
+
482
+ Returns:
483
+ Normalized string value for the policy type (e.g., "orchestrator", "reducer")
484
+
485
+ Raises:
486
+ PolicyRegistryError: If policy_type is a string that doesn't match any
487
+ EnumPolicyType value. Error includes the invalid value
488
+ and list of valid options.
489
+
490
+ Example:
491
+ >>> from omnibase_infra.enums import EnumPolicyType
492
+ >>> registry = RegistryPolicy()
493
+ >>> # Enum to string
494
+ >>> registry._normalize_policy_type(EnumPolicyType.ORCHESTRATOR)
495
+ 'orchestrator'
496
+ >>> # Valid string passthrough
497
+ >>> registry._normalize_policy_type("reducer")
498
+ 'reducer'
499
+ >>> # Invalid string raises error
500
+ >>> registry._normalize_policy_type("invalid")
501
+ PolicyRegistryError: Invalid policy_type: 'invalid'.
502
+ Must be one of: ['orchestrator', 'reducer']
503
+ """
504
+ if isinstance(policy_type, EnumPolicyType):
505
+ return policy_type.value
506
+
507
+ # Validate string against enum values
508
+ valid_types = {e.value for e in EnumPolicyType}
509
+ if policy_type not in valid_types:
510
+ context = ModelInfraErrorContext.with_correlation(
511
+ transport_type=EnumInfraTransportType.RUNTIME,
512
+ operation="normalize_policy_type",
513
+ )
514
+ raise PolicyRegistryError(
515
+ f"Invalid policy_type: {policy_type!r}. "
516
+ f"Must be one of: {sorted(valid_types)}",
517
+ policy_id=None,
518
+ policy_type=policy_type,
519
+ context=context,
520
+ )
521
+
522
+ return policy_type
523
+
524
+ @staticmethod
525
+ def _normalize_version(version: str) -> str:
526
+ """Normalize version string for consistent lookups.
527
+
528
+ Delegates to the shared normalize_version utility which is the
529
+ SINGLE SOURCE OF TRUTH for version normalization in omnibase_infra.
530
+
531
+ This method wraps the shared utility to convert ValueError to
532
+ ProtocolConfigurationError for RegistryPolicy's error contract.
533
+
534
+ Normalization rules:
535
+ 1. Strip leading/trailing whitespace
536
+ 2. Strip leading 'v' or 'V' prefix
537
+ 3. Expand partial versions (1 -> 1.0.0, 1.0 -> 1.0.0)
538
+ 4. Parse with ModelSemVer.parse() for validation
539
+ 5. Preserve prerelease suffix if present
540
+
541
+ Args:
542
+ version: The version string to normalize
543
+
544
+ Returns:
545
+ Normalized version string in "x.y.z" or "x.y.z-prerelease" format
546
+
547
+ Raises:
548
+ ProtocolConfigurationError: If the version format is invalid
549
+
550
+ Example:
551
+ >>> RegistryPolicy._normalize_version("1.0")
552
+ '1.0.0'
553
+ >>> RegistryPolicy._normalize_version("v2.1")
554
+ '2.1.0'
555
+ """
556
+ try:
557
+ return normalize_version(version)
558
+ except ValueError as e:
559
+ context = ModelInfraErrorContext.with_correlation(
560
+ transport_type=EnumInfraTransportType.RUNTIME,
561
+ operation="normalize_version",
562
+ )
563
+ raise ProtocolConfigurationError(
564
+ str(e),
565
+ version=version,
566
+ context=context,
567
+ ) from e
568
+
569
+ def register(
570
+ self,
571
+ registration: ModelPolicyRegistration,
572
+ ) -> None:
573
+ """Register a policy plugin using a registration model.
574
+
575
+ Associates a (policy_id, policy_type, version) tuple with a policy class.
576
+ If the combination is already registered, the existing registration is
577
+ overwritten.
578
+
579
+ This is the PREFERRED API for registering policies. For new code, use this
580
+ method with ModelPolicyRegistration instead of register_policy().
581
+
582
+ Args:
583
+ registration: ModelPolicyRegistration containing all registration parameters:
584
+ - policy_id: Unique identifier for the policy
585
+ - policy_class: The policy class to register (must implement ProtocolPolicy)
586
+ - policy_type: Whether this is orchestrator or reducer policy
587
+ - version: Semantic version string (default: "1.0.0")
588
+ - allow_async: If True, allows async interface
589
+
590
+ Raises:
591
+ PolicyRegistryError: If policy has async methods and
592
+ allow_async=False, or if policy_type is invalid
593
+
594
+ Example:
595
+ >>> from omnibase_infra.runtime.models import ModelPolicyRegistration
596
+ >>> registry = RegistryPolicy()
597
+ >>> registration = ModelPolicyRegistration(
598
+ ... policy_id="retry_backoff",
599
+ ... policy_class=RetryBackoffPolicy,
600
+ ... policy_type=EnumPolicyType.ORCHESTRATOR,
601
+ ... version="1.0.0",
602
+ ... )
603
+ >>> registry.register(registration)
604
+ """
605
+ # Extract fields from model
606
+ policy_id = registration.policy_id
607
+ policy_class = registration.policy_class
608
+ policy_type = registration.policy_type
609
+ version = registration.version
610
+ allow_async = registration.allow_async
611
+
612
+ # Validate protocol implementation (evaluate() method exists and is callable)
613
+ # Pass policy_type for complete parameter validation and error context
614
+ self._validate_protocol_implementation(policy_id, policy_class, policy_type)
615
+
616
+ # Validate sync enforcement (pass policy_type for better error context)
617
+ self._validate_sync_enforcement(
618
+ policy_id, policy_class, allow_async, policy_type
619
+ )
620
+
621
+ # Normalize policy type
622
+ normalized_type = self._normalize_policy_type(policy_type)
623
+
624
+ # Normalize version string before storing to prevent lookup mismatches
625
+ # This ensures "1.0", "1.0.0", and "v1.0.0" all resolve to "1.0.0"
626
+ # Note: ModelPolicyRegistration already normalizes, but we normalize again
627
+ # here to guarantee consistency with lookup operations
628
+ normalized_version = self._normalize_version(version)
629
+
630
+ # Validate version format (ensures semantic versioning compliance)
631
+ # This calls _parse_semver which will raise ProtocolConfigurationError if invalid
632
+ self._parse_semver(normalized_version)
633
+
634
+ # Register the policy using ModelPolicyKey with normalized version
635
+ key = ModelPolicyKey(
636
+ policy_id=policy_id,
637
+ policy_type=normalized_type,
638
+ version=normalized_version,
639
+ )
640
+ with self._lock:
641
+ self._registry[key] = policy_class
642
+ # Update secondary index for performance optimization
643
+ if policy_id not in self._policy_id_index:
644
+ self._policy_id_index[policy_id] = []
645
+ if key not in self._policy_id_index[policy_id]:
646
+ self._policy_id_index[policy_id].append(key)
647
+
648
+ def register_policy(
649
+ self,
650
+ policy_id: str,
651
+ policy_class: type[ProtocolPolicy],
652
+ policy_type: PolicyTypeInput,
653
+ version: str = "1.0.0",
654
+ allow_async: bool = False,
655
+ ) -> None:
656
+ """Convenience method to register a policy with individual parameters.
657
+
658
+ Wraps parameters in ModelPolicyRegistration and calls register().
659
+ Partial version strings (e.g., "1", "1.0") are auto-normalized to
660
+ "x.y.z" format by ModelPolicyRegistration.
661
+
662
+ Note:
663
+ For new code, prefer using register(ModelPolicyRegistration(...))
664
+ directly. This is a convenience method for simple registrations.
665
+
666
+ Args:
667
+ policy_id: Unique identifier for the policy (e.g., 'exponential_backoff')
668
+ policy_class: The policy class to register. Must implement ProtocolPolicy.
669
+ policy_type: Whether this is orchestrator or reducer policy.
670
+ Can be EnumPolicyType or string literal.
671
+ version: Semantic version string (default: "1.0.0"). Partial versions
672
+ like "1" or "1.0" are auto-normalized to "1.0.0" or "1.0.0".
673
+ allow_async: If True, allows async interface. MUST be explicitly
674
+ flagged for policies with async methods.
675
+
676
+ Raises:
677
+ PolicyRegistryError: If policy has async methods and
678
+ allow_async=False, or if policy_type is invalid
679
+ ProtocolConfigurationError: If version format is invalid
680
+
681
+ Example:
682
+ >>> registry = RegistryPolicy()
683
+ >>> registry.register_policy(
684
+ ... policy_id="retry_backoff",
685
+ ... policy_class=RetryBackoffPolicy,
686
+ ... policy_type=EnumPolicyType.ORCHESTRATOR,
687
+ ... version="1.0.0",
688
+ ... )
689
+ """
690
+ # Version normalization is handled by ModelPolicyRegistration validator
691
+ # which normalizes partial versions and v-prefixed versions automatically
692
+ try:
693
+ registration = ModelPolicyRegistration(
694
+ policy_id=policy_id,
695
+ policy_class=policy_class,
696
+ policy_type=policy_type,
697
+ version=version,
698
+ allow_async=allow_async,
699
+ )
700
+ except ValidationError as e:
701
+ # Convert all validation errors to ProtocolConfigurationError for consistency
702
+ # This ensures uniform error handling across all validation failures
703
+ context = ModelInfraErrorContext.with_correlation(
704
+ transport_type=EnumInfraTransportType.RUNTIME,
705
+ operation="register_policy",
706
+ )
707
+ for error in e.errors():
708
+ field_loc = error.get("loc", ())
709
+ field_name = field_loc[0] if field_loc else "unknown"
710
+ error_msg = error.get("msg", str(e))
711
+
712
+ if field_name == "version":
713
+ raise ProtocolConfigurationError(
714
+ f"Invalid version format: {error_msg}",
715
+ version=version,
716
+ context=context,
717
+ ) from e
718
+ if field_name == "policy_id":
719
+ raise ProtocolConfigurationError(
720
+ f"Invalid policy_id: {error_msg}",
721
+ policy_id=policy_id,
722
+ context=context,
723
+ ) from e
724
+ if field_name == "policy_type":
725
+ raise ProtocolConfigurationError(
726
+ f"Invalid policy_type: {error_msg}",
727
+ policy_type=str(policy_type),
728
+ context=context,
729
+ ) from e
730
+ if field_name == "policy_class":
731
+ raise ProtocolConfigurationError(
732
+ f"Invalid policy_class: {error_msg}",
733
+ context=context,
734
+ ) from e
735
+ # Fallback for any unhandled validation errors
736
+ raise ProtocolConfigurationError(
737
+ f"Validation error in policy registration: {e}",
738
+ context=context,
739
+ ) from e
740
+
741
+ self.register(registration)
742
+
743
+ def get(
744
+ self,
745
+ policy_id: str,
746
+ policy_type: PolicyTypeInput | None = None,
747
+ version: str | None = None,
748
+ ) -> type[ProtocolPolicy]:
749
+ """Get policy class by ID, type, and optional version.
750
+
751
+ Resolves the policy class registered for the given policy configuration.
752
+ If policy_type is not specified, returns the first matching policy_id.
753
+ If version is not specified, returns the latest version (by semantic version).
754
+
755
+ Performance Characteristics:
756
+ - Best case: O(1) - Direct lookup with policy_id only (single version, no filters)
757
+ - Average case: O(k) where k = number of matching versions (multi-version, no filters)
758
+ - Worst case: O(k*log(k)) when policy_type filter applied with multiple versions
759
+ (requires both filtering candidates and sorting by semver to find latest)
760
+ - Uses secondary index for O(1) policy_id lookup instead of O(n) scan
761
+ - Defers expensive error message generation until actually needed
762
+ - Fast path optimization when no filters applied (common case)
763
+
764
+ Args:
765
+ policy_id: Policy identifier.
766
+ policy_type: Optional policy type filter (orchestrator or reducer).
767
+ version: Optional version filter. If None, returns latest version.
768
+
769
+ Returns:
770
+ Policy class registered for the configuration.
771
+
772
+ Raises:
773
+ PolicyRegistryError: If no matching policy is found.
774
+
775
+ Example:
776
+ >>> registry = RegistryPolicy()
777
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
778
+ >>> policy_cls = registry.get("retry")
779
+ >>> policy_cls = registry.get("retry", policy_type="orchestrator")
780
+ >>> policy_cls = registry.get("retry", version="1.0.0")
781
+ """
782
+ # Normalize policy_type if provided (outside lock for minimal critical section)
783
+ # Use empty string as sentinel for "no filter" to reduce union types
784
+ normalized_type: str = (
785
+ self._normalize_policy_type(policy_type) if policy_type is not None else ""
786
+ )
787
+
788
+ # Normalize version for consistent lookup (e.g., "1.0" matches "1.0.0")
789
+ normalized_version: str | None = None
790
+ if version is not None:
791
+ normalized_version = self._normalize_version(version)
792
+
793
+ with self._lock:
794
+ # Performance optimization: Use secondary index for O(1) lookup by policy_id
795
+ # This avoids iterating through all registry entries (O(n) -> O(1))
796
+ candidate_keys = self._policy_id_index.get(policy_id, [])
797
+
798
+ # Early exit if policy_id not found - avoid building matches list
799
+ if not candidate_keys:
800
+ # Defer expensive _list_internal() call until actually raising error
801
+ filters = [f"policy_id={policy_id!r}"]
802
+ if policy_type is not None:
803
+ filters.append(f"policy_type={policy_type!r}")
804
+ if version is not None:
805
+ filters.append(f"version={version!r}")
806
+
807
+ # Inline list generation for error message (avoids separate method)
808
+ registered = [
809
+ k.to_tuple()
810
+ for k in sorted(
811
+ self._registry.keys(),
812
+ key=lambda k: (k.policy_id, k.policy_type, k.version),
813
+ )
814
+ ]
815
+ context = ModelInfraErrorContext.with_correlation(
816
+ transport_type=EnumInfraTransportType.RUNTIME,
817
+ operation="get_policy",
818
+ )
819
+ raise PolicyRegistryError(
820
+ f"No policy registered matching: {', '.join(filters)}. "
821
+ f"Registered policies: {registered}",
822
+ policy_id=policy_id,
823
+ policy_type=str(policy_type) if policy_type else None,
824
+ context=context,
825
+ )
826
+
827
+ # Find matching entries from candidates (optimized to reduce allocations)
828
+ # Fast path: no filtering needed (common case - just get latest version)
829
+ if not normalized_type and normalized_version is None:
830
+ # Fast path optimization: avoid tuple allocation and batch dict lookups
831
+ # Only build the matches list if we have multiple versions
832
+ if len(candidate_keys) == 1:
833
+ # Single version - direct return without any allocations
834
+ return self._registry[candidate_keys[0]]
835
+ else:
836
+ # Multiple versions - need to find latest
837
+ # Use direct key comparison instead of building tuples
838
+ latest_key = max(
839
+ candidate_keys,
840
+ key=lambda k: self._parse_semver(k.version),
841
+ )
842
+ return self._registry[latest_key]
843
+ else:
844
+ # Filtered path: apply type and version filters
845
+ matches = []
846
+ for key in candidate_keys:
847
+ if normalized_type and key.policy_type != normalized_type:
848
+ continue
849
+ if (
850
+ normalized_version is not None
851
+ and key.version != normalized_version
852
+ ):
853
+ continue
854
+ matches.append((key, self._registry[key]))
855
+
856
+ if not matches:
857
+ # Filters eliminated all candidates - build error message
858
+ filters = [f"policy_id={policy_id!r}"]
859
+ if policy_type is not None:
860
+ filters.append(f"policy_type={policy_type!r}")
861
+ if version is not None:
862
+ filters.append(f"version={version!r}")
863
+
864
+ # Inline list generation for error message (avoids separate method)
865
+ registered = [
866
+ k.to_tuple()
867
+ for k in sorted(
868
+ self._registry.keys(),
869
+ key=lambda k: (k.policy_id, k.policy_type, k.version),
870
+ )
871
+ ]
872
+ context = ModelInfraErrorContext.with_correlation(
873
+ transport_type=EnumInfraTransportType.RUNTIME,
874
+ operation="get_policy",
875
+ )
876
+ raise PolicyRegistryError(
877
+ f"No policy registered matching: {', '.join(filters)}. "
878
+ f"Registered policies: {registered}",
879
+ policy_id=policy_id,
880
+ policy_type=str(policy_type) if policy_type else None,
881
+ context=context,
882
+ )
883
+
884
+ # If version not specified and multiple matches, return latest
885
+ # (using cached semantic version comparison)
886
+ if normalized_version is None and len(matches) > 1:
887
+ # Sort in-place to avoid allocating a new list
888
+ matches.sort(
889
+ key=lambda x: self._parse_semver(x[0].version), reverse=True
890
+ )
891
+
892
+ return matches[0][1]
893
+
894
+ # ==========================================================================
895
+ # Semver Cache Configuration Methods
896
+ # ==========================================================================
897
+
898
+ @classmethod
899
+ def configure_semver_cache(cls, maxsize: int) -> None:
900
+ """Configure semver cache size. Must be called before first parse.
901
+
902
+ This method allows configuring the LRU cache size for semver parsing
903
+ in large deployments with many policy versions. For most deployments,
904
+ the default of 128 entries is sufficient.
905
+
906
+ When to Increase Cache Size:
907
+ - Very large deployments with > 100 unique policy versions
908
+ - High-frequency lookups across many version combinations
909
+ - Observed cache eviction causing performance regression
910
+
911
+ Args:
912
+ maxsize: Maximum cache entries (default: 128).
913
+ Recommended range: 64-512 for most deployments.
914
+ Each entry uses ~100 bytes.
915
+
916
+ Raises:
917
+ ProtocolConfigurationError: If cache already initialized (first parse already occurred)
918
+
919
+ Example:
920
+ >>> # Configure before any registry operations
921
+ >>> RegistryPolicy.configure_semver_cache(maxsize=256)
922
+ >>> registry = RegistryPolicy()
923
+
924
+ Note:
925
+ For testing purposes, use _reset_semver_cache() to clear the cache
926
+ and allow reconfiguration.
927
+ """
928
+ with cls._semver_cache_lock:
929
+ if cls._semver_cache is not None:
930
+ context = ModelInfraErrorContext.with_correlation(
931
+ transport_type=EnumInfraTransportType.RUNTIME,
932
+ operation="configure_semver_cache",
933
+ )
934
+ raise ProtocolConfigurationError(
935
+ "Cannot reconfigure semver cache after first use. "
936
+ "Set RegistryPolicy.SEMVER_CACHE_SIZE before creating any "
937
+ "registry instances, or use _reset_semver_cache() for testing.",
938
+ context=context,
939
+ )
940
+ cls.SEMVER_CACHE_SIZE = maxsize
941
+
942
+ @classmethod
943
+ def _reset_semver_cache(cls) -> None:
944
+ """Reset semver cache. For testing only.
945
+
946
+ Clears the cached semver parser, allowing reconfiguration of cache size.
947
+ This should only be used in test fixtures to ensure test isolation.
948
+
949
+ Thread Safety:
950
+ This method is thread-safe and uses the class-level lock. The reset
951
+ operation is atomic - either the cache is fully reset or not at all.
952
+
953
+ In-flight Operations:
954
+ If other threads have already obtained a reference to the cache
955
+ via _get_semver_parser(), they will continue using the old cache
956
+ until they complete. This is safe because the old cache remains
957
+ a valid callable until garbage collected. New operations after
958
+ reset will get the new cache instance when created.
959
+
960
+ Memory Reclamation:
961
+ The old cache's internal LRU entries are explicitly cleared via
962
+ cache_clear() before the reference is released. This ensures
963
+ prompt memory reclamation rather than waiting for garbage
964
+ collection.
965
+
966
+ Concurrent Reset:
967
+ Multiple concurrent reset calls are safe. Each reset will clear
968
+ the current cache (if any) and set the reference to None. The
969
+ lock ensures only one reset executes at a time.
970
+
971
+ Example:
972
+ >>> # In test fixture
973
+ >>> RegistryPolicy._reset_semver_cache()
974
+ >>> RegistryPolicy.SEMVER_CACHE_SIZE = 64
975
+ >>> # Now cache will be initialized with size 64 on next use
976
+ """
977
+ with cls._semver_cache_lock:
978
+ # Clear the inner LRU-cached function (has the actual cache)
979
+ inner_cache = cls._semver_cache_inner
980
+ if inner_cache is not None:
981
+ # Clear internal LRU cache entries before releasing reference.
982
+ # This ensures prompt memory reclamation rather than waiting
983
+ # for garbage collection of the orphaned function object.
984
+ # Note: cache_clear() is added by @lru_cache decorator but not
985
+ # reflected in Callable type annotation. This is a known mypy
986
+ # limitation with lru_cache wrappers.
987
+ inner_cache.cache_clear() # type: ignore[attr-defined]
988
+ cls._semver_cache = None
989
+ cls._semver_cache_inner = None
990
+
991
+ @classmethod
992
+ def _get_semver_parser(cls) -> Callable[[str], ModelSemVer]:
993
+ """Get or create the semver parser with configured cache size.
994
+
995
+ This method implements lazy initialization of the LRU-cached semver parser.
996
+ The cache size is determined by SEMVER_CACHE_SIZE at initialization time.
997
+
998
+ Thread Safety:
999
+ Uses double-checked locking pattern for thread-safe lazy initialization.
1000
+ The fast path stores the cache reference in a local variable to prevent
1001
+ TOCTOU (time-of-check-time-of-use) race conditions where another thread
1002
+ could call _reset_semver_cache() between the None check and the return.
1003
+
1004
+ Cache Key Normalization:
1005
+ Version strings are normalized BEFORE being used as cache keys to ensure
1006
+ that equivalent versions (e.g., "1.0" and "1.0.0") share the same cache
1007
+ entry. This prevents cache fragmentation and improves hit rates.
1008
+
1009
+ Returns:
1010
+ Cached semver parsing function that returns ModelSemVer instances.
1011
+ The returned function accepts a version string and returns a ModelSemVer.
1012
+
1013
+ Raises:
1014
+ ProtocolConfigurationError: Raised by the returned parsing function if
1015
+ the version format is invalid (e.g., non-numeric components,
1016
+ negative numbers, or more than 3 version parts).
1017
+
1018
+ Performance:
1019
+ - First call: Creates LRU-cached function (one-time cost)
1020
+ - Subsequent calls: Returns cached function reference (O(1))
1021
+ - Cache hit rate improved by normalizing keys before lookup
1022
+ """
1023
+ # Fast path: cache already initialized
1024
+ # CRITICAL: Store in local variable to prevent TOCTOU race condition.
1025
+ # Without this, another thread could call _reset_semver_cache() between
1026
+ # the None check and the return, causing this method to return None.
1027
+ cache = cls._semver_cache
1028
+ if cache is not None:
1029
+ return cache
1030
+
1031
+ # Slow path: initialize with lock
1032
+ with cls._semver_cache_lock:
1033
+ # Double-check after acquiring lock
1034
+ if cls._semver_cache is not None:
1035
+ return cls._semver_cache
1036
+
1037
+ # Create LRU-cached parser with configured size
1038
+ # The cache key is the NORMALIZED version string to prevent
1039
+ # fragmentation (e.g., "1.0" and "1.0.0" share the same entry)
1040
+ @functools.lru_cache(maxsize=cls.SEMVER_CACHE_SIZE)
1041
+ def _parse_semver_cached(normalized_version: str) -> ModelSemVer:
1042
+ """Parse normalized semantic version string into ModelSemVer.
1043
+
1044
+ This function receives ALREADY NORMALIZED version strings.
1045
+ The normalization is done by the wrapper function before
1046
+ caching to ensure equivalent versions share cache entries.
1047
+
1048
+ Args:
1049
+ normalized_version: Pre-normalized version in "x.y.z" or
1050
+ "x.y.z-prerelease" format
1051
+
1052
+ Returns:
1053
+ ModelSemVer instance for comparison
1054
+
1055
+ Raises:
1056
+ ProtocolConfigurationError: If version format is invalid
1057
+ """
1058
+ # ModelOnexError is imported at module level
1059
+ try:
1060
+ return ModelSemVer.parse(normalized_version)
1061
+ except ModelOnexError as e:
1062
+ context = ModelInfraErrorContext.with_correlation(
1063
+ transport_type=EnumInfraTransportType.RUNTIME,
1064
+ operation="parse_semver",
1065
+ )
1066
+ raise ProtocolConfigurationError(
1067
+ str(e),
1068
+ version=normalized_version,
1069
+ context=context,
1070
+ ) from e
1071
+ except ValueError as e:
1072
+ context = ModelInfraErrorContext.with_correlation(
1073
+ transport_type=EnumInfraTransportType.RUNTIME,
1074
+ operation="parse_semver",
1075
+ )
1076
+ raise ProtocolConfigurationError(
1077
+ str(e),
1078
+ version=normalized_version,
1079
+ context=context,
1080
+ ) from e
1081
+
1082
+ def _parse_semver_impl(version: str) -> ModelSemVer:
1083
+ """Parse semantic version string into ModelSemVer.
1084
+
1085
+ Implementation moved here to support configurable cache size.
1086
+ See _parse_semver docstring for full documentation.
1087
+
1088
+ IMPORTANT: This wrapper normalizes version strings BEFORE
1089
+ passing to the LRU-cached parsing function. This ensures that
1090
+ equivalent versions (e.g., "1.0" and "1.0.0", "v1.0.0" and "1.0.0")
1091
+ share the same cache entry, improving cache hit rates.
1092
+
1093
+ All validation (empty strings, prerelease suffix, format) is
1094
+ delegated to _normalize_version to eliminate code duplication.
1095
+ """
1096
+ # Delegate all validation to _normalize_version (single source of truth)
1097
+ # This eliminates duplicated validation logic (empty check, prerelease suffix)
1098
+ normalized = RegistryPolicy._normalize_version(version)
1099
+
1100
+ # Now call the cached function with the NORMALIZED version
1101
+ # This ensures "1.0", "1.0.0", "v1.0.0" all use the same cache entry
1102
+ return _parse_semver_cached(normalized)
1103
+
1104
+ # Store both the outer wrapper and inner cached function
1105
+ # The wrapper is what callers use (_semver_cache)
1106
+ # The inner function is needed for cache_clear() access (_semver_cache_inner)
1107
+ cls._semver_cache = _parse_semver_impl
1108
+ cls._semver_cache_inner = _parse_semver_cached
1109
+ return cls._semver_cache
1110
+
1111
+ @classmethod
1112
+ def _parse_semver(cls, version: str) -> ModelSemVer:
1113
+ """Parse semantic version string into ModelSemVer for comparison.
1114
+
1115
+ This method implements SEMANTIC VERSION SORTING, not lexicographic sorting.
1116
+ This is critical for correct "latest version" selection.
1117
+
1118
+ Why This Matters (PR #36 feedback):
1119
+ Lexicographic sorting (string comparison):
1120
+ "1.10.0" < "1.9.0" WRONG (because '1' < '9' in strings)
1121
+ "10.0.0" < "2.0.0" WRONG (because '1' < '2' in strings)
1122
+
1123
+ Semantic version sorting (integer comparison):
1124
+ 1.10.0 > 1.9.0 CORRECT (because 10 > 9 as integers)
1125
+ 10.0.0 > 2.0.0 CORRECT (because 10 > 2 as integers)
1126
+
1127
+ Implementation:
1128
+ - Returns ModelSemVer instance with integer major, minor, patch
1129
+ - ModelSemVer implements comparison operators for correct ordering
1130
+ - Prerelease is parsed but NOT used in comparisons (major.minor.patch only)
1131
+ - "1.0.0-alpha" and "1.0.0" compare as EQUAL (same major.minor.patch)
1132
+
1133
+ Supported Formats:
1134
+ - Full: "1.2.3", "1.2.3-beta"
1135
+ - Partial: "1" -> (1, 0, 0), "1.2" -> (1, 2, 0)
1136
+ - Prerelease: "1.0.0-alpha", "2.1.0-rc.1"
1137
+
1138
+ Validation:
1139
+ - Rejects empty strings
1140
+ - Rejects non-numeric components
1141
+ - Rejects negative numbers
1142
+ - Rejects >3 version parts (e.g., "1.2.3.4")
1143
+
1144
+ Performance:
1145
+ This method uses an LRU cache with configurable size (default: 128)
1146
+ to avoid re-parsing the same version strings repeatedly, improving
1147
+ performance for lookups that compare multiple versions.
1148
+
1149
+ Cache Size Configuration:
1150
+ For large deployments, configure before first use:
1151
+ RegistryPolicy.configure_semver_cache(maxsize=256)
1152
+
1153
+ Cache Size Rationale (default 128):
1154
+ - Typical registry: 10-50 unique policy versions
1155
+ - Peak scenarios: 50-100 versions across multiple policy types
1156
+ - Each cache entry: ~200 bytes (string key + ModelSemVer instance)
1157
+ - Total memory: ~25.6KB worst case (negligible overhead)
1158
+ - Hit rate: >95% for repeated get() calls with version comparisons
1159
+ - Eviction: Rare in practice, LRU ensures least-used versions purged
1160
+
1161
+ Args:
1162
+ version: Semantic version string (e.g., "1.2.3" or "1.0.0-beta")
1163
+
1164
+ Returns:
1165
+ ModelSemVer instance for comparison.
1166
+ Components are INTEGERS (not strings) for correct semantic sorting.
1167
+ Prerelease is parsed and stored but ignored in version comparisons.
1168
+
1169
+ Raises:
1170
+ ProtocolConfigurationError: If version format is invalid
1171
+
1172
+ Examples:
1173
+ >>> RegistryPolicy._parse_semver("1.9.0")
1174
+ ModelSemVer(major=1, minor=9, patch=0, prerelease='')
1175
+ >>> RegistryPolicy._parse_semver("1.10.0")
1176
+ ModelSemVer(major=1, minor=10, patch=0, prerelease='')
1177
+ >>> RegistryPolicy._parse_semver("1.10.0") > RegistryPolicy._parse_semver("1.9.0")
1178
+ True
1179
+ >>> RegistryPolicy._parse_semver("10.0.0") > RegistryPolicy._parse_semver("2.0.0")
1180
+ True
1181
+ >>> RegistryPolicy._parse_semver("1.0.0-alpha")
1182
+ ModelSemVer(major=1, minor=0, patch=0, prerelease='alpha')
1183
+ >>> # Prerelease is parsed but NOT used in comparisons:
1184
+ >>> RegistryPolicy._parse_semver("1.0.0-alpha") == RegistryPolicy._parse_semver("1.0.0")
1185
+ True # Same major.minor.patch, prerelease ignored
1186
+ """
1187
+ parser = cls._get_semver_parser()
1188
+ return parser(version)
1189
+
1190
+ @classmethod
1191
+ def _get_semver_cache_info(cls) -> functools._CacheInfo | None:
1192
+ """Get cache statistics for the semver parser. For testing only.
1193
+
1194
+ Returns the cache_info() from the inner LRU-cached function.
1195
+ This allows tests to verify cache behavior without accessing
1196
+ internal implementation details.
1197
+
1198
+ Returns:
1199
+ functools._CacheInfo with hits, misses, maxsize, currsize,
1200
+ or None if cache not yet initialized.
1201
+
1202
+ Example:
1203
+ >>> RegistryPolicy._reset_semver_cache()
1204
+ >>> RegistryPolicy._parse_semver("1.0.0")
1205
+ >>> info = RegistryPolicy._get_semver_cache_info()
1206
+ >>> info.misses # First call is a miss
1207
+ 1
1208
+ >>> RegistryPolicy._parse_semver("1.0.0")
1209
+ >>> info = RegistryPolicy._get_semver_cache_info()
1210
+ >>> info.hits # Second call is a hit
1211
+ 1
1212
+ """
1213
+ if cls._semver_cache_inner is None:
1214
+ return None
1215
+ # cache_info() is added by @lru_cache decorator
1216
+ # The return type is functools._CacheInfo
1217
+ result: functools._CacheInfo = cls._semver_cache_inner.cache_info() # type: ignore[attr-defined]
1218
+ return result
1219
+
1220
+ def _list_internal(self) -> list[tuple[str, str, str]]:
1221
+ """Internal list method (assumes lock is held).
1222
+
1223
+ Returns:
1224
+ List of (policy_id, policy_type, version) tuples.
1225
+ """
1226
+ return [
1227
+ k.to_tuple()
1228
+ for k in sorted(
1229
+ self._registry.keys(),
1230
+ key=lambda k: (k.policy_id, k.policy_type, k.version),
1231
+ )
1232
+ ]
1233
+
1234
+ def list_keys(
1235
+ self,
1236
+ policy_type: PolicyTypeInput | None = None,
1237
+ ) -> list[tuple[str, str, str]]:
1238
+ """List registered policy keys as (id, type, version) tuples.
1239
+
1240
+ Args:
1241
+ policy_type: Optional filter to list only policies of a specific type.
1242
+
1243
+ Returns:
1244
+ List of (policy_id, policy_type, version) tuples, sorted alphabetically.
1245
+
1246
+ Example:
1247
+ >>> registry = RegistryPolicy()
1248
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1249
+ >>> registry.register("merge", MergePolicy, EnumPolicyType.REDUCER)
1250
+ >>> print(registry.list_keys())
1251
+ [('merge', 'reducer', '1.0.0'), ('retry', 'orchestrator', '1.0.0')]
1252
+ >>> print(registry.list_keys(policy_type="orchestrator"))
1253
+ [('retry', 'orchestrator', '1.0.0')]
1254
+ """
1255
+ # Normalize policy_type if provided
1256
+ # Use empty string as sentinel for "no filter" to reduce union types
1257
+ normalized_type: str = (
1258
+ self._normalize_policy_type(policy_type) if policy_type is not None else ""
1259
+ )
1260
+
1261
+ with self._lock:
1262
+ results: list[tuple[str, str, str]] = []
1263
+ for key in sorted(
1264
+ self._registry.keys(),
1265
+ key=lambda k: (k.policy_id, k.policy_type, k.version),
1266
+ ):
1267
+ if normalized_type and key.policy_type != normalized_type:
1268
+ continue
1269
+ results.append(key.to_tuple())
1270
+ return results
1271
+
1272
+ def list_policy_types(self) -> list[str]:
1273
+ """List registered policy types.
1274
+
1275
+ Returns:
1276
+ List of unique policy type strings that have registered policies.
1277
+
1278
+ Example:
1279
+ >>> registry = RegistryPolicy()
1280
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1281
+ >>> print(registry.list_policy_types())
1282
+ ['orchestrator']
1283
+ """
1284
+ with self._lock:
1285
+ types = {key.policy_type for key in self._registry}
1286
+ return sorted(types)
1287
+
1288
+ def list_versions(self, policy_id: str) -> list[str]:
1289
+ """List registered versions for a policy ID.
1290
+
1291
+ Args:
1292
+ policy_id: The policy ID to list versions for.
1293
+
1294
+ Returns:
1295
+ List of version strings registered for the policy ID, sorted.
1296
+
1297
+ Example:
1298
+ >>> registry = RegistryPolicy()
1299
+ >>> registry.register("retry", RetryPolicyV1, "orchestrator", "1.0.0")
1300
+ >>> registry.register("retry", RetryPolicyV2, "orchestrator", "2.0.0")
1301
+ >>> print(registry.list_versions("retry"))
1302
+ ['1.0.0', '2.0.0']
1303
+ """
1304
+ with self._lock:
1305
+ # Performance optimization: Use secondary index
1306
+ candidate_keys = self._policy_id_index.get(policy_id, [])
1307
+ versions = {key.version for key in candidate_keys}
1308
+ return sorted(versions)
1309
+
1310
+ def is_registered(
1311
+ self,
1312
+ policy_id: str,
1313
+ policy_type: PolicyTypeInput | None = None,
1314
+ version: str | None = None,
1315
+ ) -> bool:
1316
+ """Check if a policy is registered.
1317
+
1318
+ Args:
1319
+ policy_id: Policy identifier.
1320
+ policy_type: Optional policy type filter.
1321
+ version: Optional version filter.
1322
+
1323
+ Returns:
1324
+ True if a matching policy is registered, False otherwise.
1325
+
1326
+ Example:
1327
+ >>> registry = RegistryPolicy()
1328
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1329
+ >>> registry.is_registered("retry")
1330
+ True
1331
+ >>> registry.is_registered("unknown")
1332
+ False
1333
+ """
1334
+ # Normalize policy_type if provided
1335
+ # Use empty string as sentinel for "no filter" to reduce union types
1336
+ normalized_type: str = ""
1337
+ if policy_type is not None:
1338
+ try:
1339
+ normalized_type = self._normalize_policy_type(policy_type)
1340
+ except PolicyRegistryError:
1341
+ return False
1342
+
1343
+ # Normalize version for consistent lookup (e.g., "1.0" matches "1.0.0")
1344
+ normalized_version: str | None = None
1345
+ if version is not None:
1346
+ normalized_version = self._normalize_version(version)
1347
+
1348
+ with self._lock:
1349
+ # Performance optimization: Use secondary index
1350
+ candidate_keys = self._policy_id_index.get(policy_id, [])
1351
+ for key in candidate_keys:
1352
+ if normalized_type and key.policy_type != normalized_type:
1353
+ continue
1354
+ if normalized_version is not None and key.version != normalized_version:
1355
+ continue
1356
+ return True
1357
+ return False
1358
+
1359
+ def unregister(
1360
+ self,
1361
+ policy_id: str,
1362
+ policy_type: PolicyTypeInput | None = None,
1363
+ version: str | None = None,
1364
+ ) -> int:
1365
+ """Unregister policy plugins.
1366
+
1367
+ Removes policy registrations matching the given criteria.
1368
+ This is useful for testing and hot-reload scenarios.
1369
+
1370
+ Args:
1371
+ policy_id: Policy identifier to unregister.
1372
+ policy_type: Optional policy type filter.
1373
+ version: Optional version filter.
1374
+
1375
+ Returns:
1376
+ Number of policies unregistered.
1377
+
1378
+ Example:
1379
+ >>> registry = RegistryPolicy()
1380
+ >>> registry.register("retry", RetryPolicyV1, "orchestrator", "1.0.0")
1381
+ >>> registry.register("retry", RetryPolicyV2, "orchestrator", "2.0.0")
1382
+ >>> registry.unregister("retry") # Removes all versions
1383
+ 2
1384
+ >>> registry.unregister("retry", version="1.0.0") # Remove specific version
1385
+ 1
1386
+ """
1387
+ # Normalize policy_type if provided
1388
+ # Use empty string as sentinel for "no filter" to reduce union types
1389
+ normalized_type: str = ""
1390
+ if policy_type is not None:
1391
+ try:
1392
+ normalized_type = self._normalize_policy_type(policy_type)
1393
+ except PolicyRegistryError:
1394
+ return 0
1395
+
1396
+ # Normalize version for consistent lookup (e.g., "1.0" matches "1.0.0")
1397
+ normalized_version: str | None = None
1398
+ if version is not None:
1399
+ normalized_version = self._normalize_version(version)
1400
+
1401
+ # Thread safety: Lock held during full unregister operation (write operation)
1402
+ with self._lock:
1403
+ # Performance optimization: Use secondary index
1404
+ candidate_keys = self._policy_id_index.get(policy_id, [])
1405
+ keys_to_remove: list[ModelPolicyKey] = []
1406
+
1407
+ for key in candidate_keys:
1408
+ if normalized_type and key.policy_type != normalized_type:
1409
+ continue
1410
+ if normalized_version is not None and key.version != normalized_version:
1411
+ continue
1412
+ keys_to_remove.append(key)
1413
+
1414
+ for key in keys_to_remove:
1415
+ del self._registry[key]
1416
+ # Update secondary index
1417
+ self._policy_id_index[policy_id].remove(key)
1418
+
1419
+ # Clean up empty index entries
1420
+ if (
1421
+ policy_id in self._policy_id_index
1422
+ and not self._policy_id_index[policy_id]
1423
+ ):
1424
+ del self._policy_id_index[policy_id]
1425
+
1426
+ return len(keys_to_remove)
1427
+
1428
+ def clear(self) -> None:
1429
+ """Clear all policy registrations.
1430
+
1431
+ Removes all registered policies from the registry.
1432
+
1433
+ Warning:
1434
+ This method is intended for **testing purposes only**.
1435
+ Calling it in production code will emit a warning.
1436
+ It breaks the immutability guarantee after startup.
1437
+
1438
+ Example:
1439
+ >>> registry = RegistryPolicy()
1440
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1441
+ >>> registry.clear()
1442
+ >>> registry.list_keys()
1443
+ []
1444
+ """
1445
+ warnings.warn(
1446
+ "RegistryPolicy.clear() is intended for testing only. "
1447
+ "Do not use in production code.",
1448
+ UserWarning,
1449
+ stacklevel=2,
1450
+ )
1451
+ with self._lock:
1452
+ self._registry.clear()
1453
+ self._policy_id_index.clear()
1454
+
1455
+ def __len__(self) -> int:
1456
+ """Return the number of registered policies.
1457
+
1458
+ Returns:
1459
+ Number of registered policy (id, type, version) combinations.
1460
+
1461
+ Example:
1462
+ >>> registry = RegistryPolicy()
1463
+ >>> len(registry)
1464
+ 0
1465
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1466
+ >>> len(registry)
1467
+ 1
1468
+ """
1469
+ with self._lock:
1470
+ return len(self._registry)
1471
+
1472
+ def __contains__(self, policy_id: str) -> bool:
1473
+ """Check if policy ID is registered using 'in' operator.
1474
+
1475
+ Args:
1476
+ policy_id: Policy identifier.
1477
+
1478
+ Returns:
1479
+ True if policy ID is registered (any type/version), False otherwise.
1480
+
1481
+ Example:
1482
+ >>> registry = RegistryPolicy()
1483
+ >>> registry.register("retry", RetryPolicy, EnumPolicyType.ORCHESTRATOR)
1484
+ >>> "retry" in registry
1485
+ True
1486
+ >>> "unknown" in registry
1487
+ False
1488
+ """
1489
+ return self.is_registered(policy_id)
1490
+
1491
+
1492
+ # =============================================================================
1493
+ # Module Exports
1494
+ # =============================================================================
1495
+
1496
+ __all__: list[str] = [
1497
+ "ModelPolicyKey",
1498
+ # Models
1499
+ "ModelPolicyRegistration",
1500
+ # Registry class
1501
+ "RegistryPolicy",
1502
+ ]