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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +123 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +101 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +156 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +92 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +102 -0
- omnibase_infra/errors/error_infra.py +608 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +112 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +75 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +787 -0
- omnibase_infra/handlers/handler_db.py +1039 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +1154 -0
- omnibase_infra/handlers/handler_http.py +920 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +748 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +422 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +42 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +136 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +311 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +147 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +37 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +59 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +179 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +40 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +280 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +48 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +172 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +32 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +435 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +99 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +296 -0
- omnibase_infra/runtime/binding_config_resolver.py +2706 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/contract_handler_discovery.py +582 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/handler_contract_source.py +669 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
- omnibase_infra/runtime/handler_registry.py +321 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +378 -0
- omnibase_infra/runtime/mixins/__init__.py +17 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
- omnibase_infra/runtime/models/__init__.py +192 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +228 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1102 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +27 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1502 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2110 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1573 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
- omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
- omnibase_infra/runtime/util_container_wiring.py +1123 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +566 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/services/__init__.py +68 -0
- omnibase_infra/services/corpus_capture.py +678 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +897 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +682 -0
- omnibase_infra/services/service_timeout_scanner.py +390 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +21 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +89 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1486 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +1710 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +410 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.1.dist-info/METADATA +197 -0
- omnibase_infra-0.2.1.dist-info/RECORD +675 -0
- omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
- omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Runtime Shape Validator for ONEX Handler Output Validation.
|
|
4
|
+
|
|
5
|
+
This module provides runtime validation of handler outputs against the
|
|
6
|
+
ONEX 4-node architecture execution shape constraints. It ensures that
|
|
7
|
+
handlers only produce message types that are allowed for their handler type.
|
|
8
|
+
|
|
9
|
+
Scope and Design:
|
|
10
|
+
This is a RUNTIME validator, not static analysis. It operates under these
|
|
11
|
+
assumptions:
|
|
12
|
+
|
|
13
|
+
1. **Syntactically Valid Code**: Code has already been parsed by Python's
|
|
14
|
+
interpreter. Syntax errors are detected at import/compile time before
|
|
15
|
+
this validator runs.
|
|
16
|
+
|
|
17
|
+
2. **Executing Handler Context**: The validator operates on actual return
|
|
18
|
+
values from executing handlers, not on AST representations.
|
|
19
|
+
|
|
20
|
+
3. **Known Handler Type**: Handler type is explicitly declared via the
|
|
21
|
+
@enforce_execution_shape decorator or passed to validation methods.
|
|
22
|
+
|
|
23
|
+
**Complementary Validators**:
|
|
24
|
+
- validator_execution_shape.py: AST-based static analysis (catches issues
|
|
25
|
+
before runtime, analyzes code structure)
|
|
26
|
+
- validator_runtime_shape.py (this module): Runtime validation (catches
|
|
27
|
+
issues when actual values are produced)
|
|
28
|
+
|
|
29
|
+
The runtime validator catches violations that cannot be determined statically,
|
|
30
|
+
such as dynamically constructed return values or conditional returns.
|
|
31
|
+
|
|
32
|
+
Execution Shape Rules (imported from validator_execution_shape):
|
|
33
|
+
- EFFECT: Can return EVENTs and COMMANDs, but not PROJECTIONs
|
|
34
|
+
- COMPUTE: Can return any message type (most permissive)
|
|
35
|
+
- REDUCER: Can return PROJECTIONs only, not EVENTs (deterministic state management)
|
|
36
|
+
- ORCHESTRATOR: Can return COMMANDs and EVENTs, but not INTENTs or PROJECTIONs
|
|
37
|
+
|
|
38
|
+
Thread Safety:
|
|
39
|
+
This module uses a module-level singleton validator (`_default_validator`) for
|
|
40
|
+
efficiency. The singleton is thread-safe because:
|
|
41
|
+
|
|
42
|
+
- The validator is stateless after initialization (only stores immutable rules)
|
|
43
|
+
- No per-validation state is stored in the validator instance
|
|
44
|
+
- Each validation creates fresh violation result objects (no shared mutable state)
|
|
45
|
+
- The singleton is created at module import time (before any threads)
|
|
46
|
+
- The @enforce_execution_shape decorator uses this singleton safely from any thread
|
|
47
|
+
|
|
48
|
+
For custom validation rules, create a new RuntimeShapeValidator instance with
|
|
49
|
+
a custom rules dictionary. For most use cases, the singleton provides optimal
|
|
50
|
+
performance without thread safety concerns.
|
|
51
|
+
|
|
52
|
+
Security Design (Intentional Fail-Open Architecture):
|
|
53
|
+
This validator uses a FAIL-OPEN design by default. This is an INTENTIONAL
|
|
54
|
+
architectural decision, NOT a security vulnerability. Understanding the
|
|
55
|
+
rationale is critical for proper security architecture.
|
|
56
|
+
|
|
57
|
+
**What Fail-Open Means Here**:
|
|
58
|
+
- Unknown handler types: Validation returns True (allowed) instead of blocking
|
|
59
|
+
- Undetectable message categories: Decorator skips validation and allows return
|
|
60
|
+
- Missing rules: No exception raised, output is permitted
|
|
61
|
+
|
|
62
|
+
**Why Fail-Open is Correct for This Validator**:
|
|
63
|
+
|
|
64
|
+
1. **This is an Architectural Validator, NOT a Security Boundary**:
|
|
65
|
+
The RuntimeShapeValidator enforces ONEX 4-node design patterns (Effect,
|
|
66
|
+
Compute, Reducer, Orchestrator) to catch developer mistakes at build/test
|
|
67
|
+
time. It is NOT designed to prevent malicious inputs or unauthorized access.
|
|
68
|
+
|
|
69
|
+
2. **Defense-in-Depth Model**:
|
|
70
|
+
Security boundaries should be implemented at the infrastructure layer,
|
|
71
|
+
not in architectural pattern validators:
|
|
72
|
+
|
|
73
|
+
- Authentication: Verify identity at API gateway/ingress (OAuth, JWT, mTLS)
|
|
74
|
+
- Authorization: Enforce permissions in service layer (RBAC, ABAC policies)
|
|
75
|
+
- Input Validation: Sanitize untrusted data at entry points (schema validation)
|
|
76
|
+
- Network Security: Restrict access via firewall rules and service mesh
|
|
77
|
+
|
|
78
|
+
This validator operates AFTER these security layers, on trusted internal
|
|
79
|
+
handler outputs, making fail-open safe and appropriate.
|
|
80
|
+
|
|
81
|
+
3. **Extensibility and Forward Compatibility**:
|
|
82
|
+
New handler types or message categories should work by default without
|
|
83
|
+
requiring immediate rule definitions. Fail-closed would break valid code
|
|
84
|
+
during handler type evolution and prevent progressive adoption.
|
|
85
|
+
|
|
86
|
+
4. **Developer Experience**:
|
|
87
|
+
Teams can adopt execution shape validation incrementally. Handlers
|
|
88
|
+
returning non-categorized types continue working while teams add category
|
|
89
|
+
annotations to their message types.
|
|
90
|
+
|
|
91
|
+
**When Strict Validation is Needed**:
|
|
92
|
+
If your use case requires fail-closed behavior (e.g., security-critical
|
|
93
|
+
enforcement in production), implement one of these approaches:
|
|
94
|
+
|
|
95
|
+
1. **Fail-Closed Wrapper**::
|
|
96
|
+
|
|
97
|
+
def strict_is_output_allowed(node_archetype, output_category) -> bool:
|
|
98
|
+
if node_archetype not in EXECUTION_SHAPE_RULES:
|
|
99
|
+
return False # Fail-closed for unknown types
|
|
100
|
+
return validator.is_output_allowed(node_archetype, output_category)
|
|
101
|
+
|
|
102
|
+
2. **Policy Decorator**::
|
|
103
|
+
|
|
104
|
+
@require_known_category # Custom decorator that rejects None categories
|
|
105
|
+
@enforce_execution_shape(EnumNodeArchetype.REDUCER)
|
|
106
|
+
def my_strict_handler(event):
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
3. **Pre-Validation Check**::
|
|
110
|
+
|
|
111
|
+
from omnibase_infra.models.validation import ModelValidateAndRaiseParams
|
|
112
|
+
|
|
113
|
+
category = detect_message_category(result)
|
|
114
|
+
if category is None:
|
|
115
|
+
raise ValueError("All outputs must have detectable categories")
|
|
116
|
+
params = ModelValidateAndRaiseParams(
|
|
117
|
+
node_archetype=node_archetype,
|
|
118
|
+
output=result,
|
|
119
|
+
output_category=category,
|
|
120
|
+
)
|
|
121
|
+
validator.validate_and_raise(params)
|
|
122
|
+
|
|
123
|
+
**Security Responsibility Boundaries**:
|
|
124
|
+
- This validator: Architectural pattern enforcement (developer guardrails)
|
|
125
|
+
- Infrastructure layer: Authentication, authorization, input validation
|
|
126
|
+
- Application layer: Business logic validation, access control
|
|
127
|
+
- Network layer: TLS, firewall rules, service mesh policies
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
>>> from omnibase_infra.validation.validator_runtime_shape import (
|
|
131
|
+
... RuntimeShapeValidator,
|
|
132
|
+
... enforce_execution_shape,
|
|
133
|
+
... )
|
|
134
|
+
>>> from omnibase_infra.models.validation import ModelOutputValidationParams
|
|
135
|
+
>>> from omnibase_infra.enums import EnumNodeArchetype, EnumMessageCategory
|
|
136
|
+
>>>
|
|
137
|
+
>>> # Direct validation
|
|
138
|
+
>>> validator = RuntimeShapeValidator()
|
|
139
|
+
>>> params = ModelOutputValidationParams(
|
|
140
|
+
... node_archetype=EnumNodeArchetype.REDUCER,
|
|
141
|
+
... output=some_event,
|
|
142
|
+
... output_category=EnumMessageCategory.EVENT,
|
|
143
|
+
... )
|
|
144
|
+
>>> violation = validator.validate_handler_output(params)
|
|
145
|
+
>>> if violation:
|
|
146
|
+
... print(f"Violation: {violation.message}")
|
|
147
|
+
>>>
|
|
148
|
+
>>> # Decorator usage
|
|
149
|
+
>>> @enforce_execution_shape(EnumNodeArchetype.REDUCER)
|
|
150
|
+
... def my_reducer_handler(event):
|
|
151
|
+
... return ProjectionResult(...) # OK
|
|
152
|
+
... # return EventResult(...) # Would raise ExecutionShapeViolationError
|
|
153
|
+
|
|
154
|
+
Exception:
|
|
155
|
+
ExecutionShapeViolationError: Raised when handler output violates
|
|
156
|
+
execution shape constraints. Contains the full violation details
|
|
157
|
+
in the `violation` attribute.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
from __future__ import annotations
|
|
161
|
+
|
|
162
|
+
import functools
|
|
163
|
+
import inspect
|
|
164
|
+
from collections.abc import Callable
|
|
165
|
+
from typing import TypeVar, cast
|
|
166
|
+
from uuid import UUID, uuid4
|
|
167
|
+
|
|
168
|
+
from omnibase_core.enums import EnumCoreErrorCode
|
|
169
|
+
from omnibase_core.models.errors import ModelOnexError
|
|
170
|
+
from omnibase_infra.enums import (
|
|
171
|
+
EnumExecutionShapeViolation,
|
|
172
|
+
EnumInfraTransportType,
|
|
173
|
+
EnumMessageCategory,
|
|
174
|
+
EnumNodeArchetype,
|
|
175
|
+
EnumNodeOutputType,
|
|
176
|
+
EnumValidationSeverity,
|
|
177
|
+
)
|
|
178
|
+
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
179
|
+
from omnibase_infra.models.validation.model_execution_shape_rule import (
|
|
180
|
+
ModelExecutionShapeRule,
|
|
181
|
+
)
|
|
182
|
+
from omnibase_infra.models.validation.model_execution_shape_violation import (
|
|
183
|
+
ModelExecutionShapeViolationResult,
|
|
184
|
+
)
|
|
185
|
+
from omnibase_infra.models.validation.model_output_validation_params import (
|
|
186
|
+
ModelOutputValidationParams,
|
|
187
|
+
)
|
|
188
|
+
from omnibase_infra.models.validation.model_validate_and_raise_params import (
|
|
189
|
+
ModelValidateAndRaiseParams,
|
|
190
|
+
)
|
|
191
|
+
from omnibase_infra.types import MessageOutputCategory
|
|
192
|
+
|
|
193
|
+
# Import canonical execution shape rules from validator_execution_shape (single source of truth)
|
|
194
|
+
from omnibase_infra.validation.validator_execution_shape import (
|
|
195
|
+
EXECUTION_SHAPE_RULES,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Type variable for generic function signature preservation
|
|
199
|
+
F = TypeVar("F", bound=Callable[..., object])
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ==============================================================================
|
|
203
|
+
# Violation Type Mapping
|
|
204
|
+
# ==============================================================================
|
|
205
|
+
|
|
206
|
+
# Maps (node_archetype, forbidden_category) to specific violation type.
|
|
207
|
+
#
|
|
208
|
+
# This mapping covers explicitly forbidden return types from EXECUTION_SHAPE_RULES:
|
|
209
|
+
# - EFFECT: Cannot return PROJECTION (explicit forbidden)
|
|
210
|
+
# - REDUCER: Cannot return EVENT (explicit forbidden)
|
|
211
|
+
# - ORCHESTRATOR: Cannot return INTENT, PROJECTION (explicit forbidden)
|
|
212
|
+
#
|
|
213
|
+
# Note: Some categories are implicitly forbidden because they're not in the
|
|
214
|
+
# allowed_return_types list (allow-list mode). For example:
|
|
215
|
+
# - REDUCER only allows PROJECTION, so COMMAND and INTENT are implicitly forbidden
|
|
216
|
+
# - EFFECT only allows EVENT and COMMAND, so INTENT is implicitly forbidden
|
|
217
|
+
#
|
|
218
|
+
# These implicit violations use FORBIDDEN_RETURN_TYPE as the fallback, which is
|
|
219
|
+
# semantically correct: the handler is returning a type that's not in its allow-list.
|
|
220
|
+
_VIOLATION_TYPE_MAP: dict[
|
|
221
|
+
tuple[EnumNodeArchetype, MessageOutputCategory],
|
|
222
|
+
EnumExecutionShapeViolation,
|
|
223
|
+
] = {
|
|
224
|
+
(
|
|
225
|
+
EnumNodeArchetype.REDUCER,
|
|
226
|
+
EnumMessageCategory.EVENT,
|
|
227
|
+
): EnumExecutionShapeViolation.REDUCER_RETURNS_EVENTS,
|
|
228
|
+
(
|
|
229
|
+
EnumNodeArchetype.ORCHESTRATOR,
|
|
230
|
+
EnumMessageCategory.INTENT,
|
|
231
|
+
): EnumExecutionShapeViolation.ORCHESTRATOR_RETURNS_INTENTS,
|
|
232
|
+
(
|
|
233
|
+
EnumNodeArchetype.ORCHESTRATOR,
|
|
234
|
+
EnumNodeOutputType.PROJECTION,
|
|
235
|
+
): EnumExecutionShapeViolation.ORCHESTRATOR_RETURNS_PROJECTIONS,
|
|
236
|
+
(
|
|
237
|
+
EnumNodeArchetype.EFFECT,
|
|
238
|
+
EnumNodeOutputType.PROJECTION,
|
|
239
|
+
): EnumExecutionShapeViolation.EFFECT_RETURNS_PROJECTIONS,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ==============================================================================
|
|
244
|
+
# Exception Class
|
|
245
|
+
# ==============================================================================
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class ExecutionShapeViolationError(ModelOnexError):
|
|
249
|
+
"""Raised when handler output violates execution shape constraints.
|
|
250
|
+
|
|
251
|
+
This error is raised at runtime when a handler produces output that
|
|
252
|
+
is forbidden for its declared handler type. For example, a REDUCER
|
|
253
|
+
handler returning an EVENT message.
|
|
254
|
+
|
|
255
|
+
Attributes:
|
|
256
|
+
violation: The full violation result with type, handler, and context.
|
|
257
|
+
|
|
258
|
+
Example:
|
|
259
|
+
>>> from omnibase_infra.models.validation import ModelValidateAndRaiseParams
|
|
260
|
+
>>> from omnibase_infra.enums import EnumNodeArchetype, EnumMessageCategory
|
|
261
|
+
>>> validator = RuntimeShapeValidator()
|
|
262
|
+
>>> try:
|
|
263
|
+
... params = ModelValidateAndRaiseParams(
|
|
264
|
+
... node_archetype=EnumNodeArchetype.REDUCER,
|
|
265
|
+
... output=event_output,
|
|
266
|
+
... output_category=EnumMessageCategory.EVENT,
|
|
267
|
+
... )
|
|
268
|
+
... validator.validate_and_raise(params)
|
|
269
|
+
... except ExecutionShapeViolationError as e:
|
|
270
|
+
... print(f"Violation: {e.violation.violation_type}")
|
|
271
|
+
... print(f"Message: {e.violation.message}")
|
|
272
|
+
|
|
273
|
+
Correlation ID Support:
|
|
274
|
+
When a correlation_id is provided, it is included in the error context
|
|
275
|
+
for distributed tracing. This enables tracking validation failures back
|
|
276
|
+
to specific requests in multi-service architectures::
|
|
277
|
+
|
|
278
|
+
>>> from uuid import uuid4
|
|
279
|
+
>>> correlation_id = uuid4()
|
|
280
|
+
>>> raise ExecutionShapeViolationError(violation, correlation_id=correlation_id)
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
def __init__(
|
|
284
|
+
self,
|
|
285
|
+
violation: ModelExecutionShapeViolationResult,
|
|
286
|
+
correlation_id: UUID | None = None,
|
|
287
|
+
) -> None:
|
|
288
|
+
"""Initialize ExecutionShapeViolationError.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
violation: The execution shape violation result with full context.
|
|
292
|
+
correlation_id: Optional correlation ID for distributed tracing.
|
|
293
|
+
When provided, enables tracking this validation failure back
|
|
294
|
+
to specific requests across service boundaries.
|
|
295
|
+
"""
|
|
296
|
+
self.violation = violation
|
|
297
|
+
# node_archetype may be None if the archetype couldn't be determined
|
|
298
|
+
node_archetype_value = (
|
|
299
|
+
violation.node_archetype.value
|
|
300
|
+
if violation.node_archetype is not None
|
|
301
|
+
else None
|
|
302
|
+
)
|
|
303
|
+
super().__init__(
|
|
304
|
+
message=violation.message,
|
|
305
|
+
error_code=EnumCoreErrorCode.VALIDATION_FAILED,
|
|
306
|
+
correlation_id=correlation_id,
|
|
307
|
+
violation_type=violation.violation_type.value,
|
|
308
|
+
node_archetype=node_archetype_value,
|
|
309
|
+
severity=violation.severity,
|
|
310
|
+
file_path=violation.file_path,
|
|
311
|
+
line_number=violation.line_number,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# ==============================================================================
|
|
316
|
+
# Type Conversion Utilities
|
|
317
|
+
# ==============================================================================
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Mapping from EnumMessageCategory to EnumNodeOutputType for validation.
|
|
321
|
+
# Both enums share EVENT, COMMAND, INTENT with identical string values.
|
|
322
|
+
_MESSAGE_CATEGORY_TO_NODE_OUTPUT: dict[EnumMessageCategory, EnumNodeOutputType] = {
|
|
323
|
+
EnumMessageCategory.EVENT: EnumNodeOutputType.EVENT,
|
|
324
|
+
EnumMessageCategory.COMMAND: EnumNodeOutputType.COMMAND,
|
|
325
|
+
EnumMessageCategory.INTENT: EnumNodeOutputType.INTENT,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _to_node_output_type(
|
|
330
|
+
category: MessageOutputCategory,
|
|
331
|
+
) -> EnumNodeOutputType:
|
|
332
|
+
"""Convert a message category or node output type to EnumNodeOutputType.
|
|
333
|
+
|
|
334
|
+
This utility handles the mapping between EnumMessageCategory (routing)
|
|
335
|
+
and EnumNodeOutputType (validation). Since EVENT, COMMAND, and INTENT
|
|
336
|
+
exist in both enums with the same values, they can be safely converted.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
category: Either an EnumMessageCategory or EnumNodeOutputType.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
The corresponding EnumNodeOutputType.
|
|
343
|
+
|
|
344
|
+
Raises:
|
|
345
|
+
ValueError: If an EnumMessageCategory cannot be mapped to EnumNodeOutputType
|
|
346
|
+
(should not happen with valid enum values).
|
|
347
|
+
"""
|
|
348
|
+
if isinstance(category, EnumNodeOutputType):
|
|
349
|
+
return category
|
|
350
|
+
|
|
351
|
+
if isinstance(category, EnumMessageCategory):
|
|
352
|
+
if category in _MESSAGE_CATEGORY_TO_NODE_OUTPUT:
|
|
353
|
+
return _MESSAGE_CATEGORY_TO_NODE_OUTPUT[category]
|
|
354
|
+
# This should never happen with valid EnumMessageCategory values
|
|
355
|
+
context = ModelInfraErrorContext(
|
|
356
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
357
|
+
operation="to_node_output_type",
|
|
358
|
+
correlation_id=uuid4(),
|
|
359
|
+
)
|
|
360
|
+
raise ProtocolConfigurationError(
|
|
361
|
+
f"Cannot convert {category} to EnumNodeOutputType",
|
|
362
|
+
context=context,
|
|
363
|
+
category=str(category),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Type safety fallback (should not be reached with proper typing)
|
|
367
|
+
context = ModelInfraErrorContext(
|
|
368
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
369
|
+
operation="to_node_output_type",
|
|
370
|
+
correlation_id=uuid4(),
|
|
371
|
+
)
|
|
372
|
+
raise ProtocolConfigurationError(
|
|
373
|
+
f"Expected EnumMessageCategory or EnumNodeOutputType, got {type(category)}",
|
|
374
|
+
context=context,
|
|
375
|
+
actual_type=type(category).__name__,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# ==============================================================================
|
|
380
|
+
# Message Category Detection
|
|
381
|
+
# ==============================================================================
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def detect_message_category(
|
|
385
|
+
message: object,
|
|
386
|
+
) -> MessageOutputCategory | None:
|
|
387
|
+
"""Detect message category or node output type from object type or attributes.
|
|
388
|
+
|
|
389
|
+
This function attempts to determine the message category or node output type
|
|
390
|
+
of an object using several strategies:
|
|
391
|
+
|
|
392
|
+
1. Check for explicit `category` attribute (EnumMessageCategory or EnumNodeOutputType)
|
|
393
|
+
2. Check for explicit `message_category` or `output_type` attribute
|
|
394
|
+
3. Check type name patterns (Event*, Command*, Intent*, Projection*)
|
|
395
|
+
|
|
396
|
+
Note: PROJECTION is detected as EnumNodeOutputType.PROJECTION since projections
|
|
397
|
+
are node output types (used by REDUCERs), not message routing categories.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
message: The message object to analyze.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
The detected EnumMessageCategory or EnumNodeOutputType,
|
|
404
|
+
or None if category cannot be determined.
|
|
405
|
+
|
|
406
|
+
Example:
|
|
407
|
+
>>> class OrderCreatedEvent:
|
|
408
|
+
... pass
|
|
409
|
+
>>> detect_message_category(OrderCreatedEvent())
|
|
410
|
+
EnumMessageCategory.EVENT
|
|
411
|
+
|
|
412
|
+
>>> class UserProjection:
|
|
413
|
+
... output_type = EnumNodeOutputType.PROJECTION
|
|
414
|
+
>>> detect_message_category(UserProjection())
|
|
415
|
+
EnumNodeOutputType.PROJECTION
|
|
416
|
+
"""
|
|
417
|
+
if message is None:
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
# Strategy 1: Check for explicit category attribute
|
|
421
|
+
if hasattr(message, "category"):
|
|
422
|
+
category = message.category
|
|
423
|
+
if isinstance(category, EnumMessageCategory | EnumNodeOutputType):
|
|
424
|
+
return category
|
|
425
|
+
|
|
426
|
+
# Strategy 2a: Check for message_category attribute
|
|
427
|
+
if hasattr(message, "message_category"):
|
|
428
|
+
category = message.message_category
|
|
429
|
+
if isinstance(category, EnumMessageCategory):
|
|
430
|
+
return category
|
|
431
|
+
|
|
432
|
+
# Strategy 2b: Check for output_type attribute (for node outputs like projections)
|
|
433
|
+
if hasattr(message, "output_type"):
|
|
434
|
+
output_type = message.output_type
|
|
435
|
+
if isinstance(output_type, EnumNodeOutputType):
|
|
436
|
+
return output_type
|
|
437
|
+
|
|
438
|
+
# Strategy 3: Check type name patterns
|
|
439
|
+
type_name = type(message).__name__
|
|
440
|
+
|
|
441
|
+
# Check for common naming patterns
|
|
442
|
+
# Note: PROJECTION returns EnumNodeOutputType since it's a node output type
|
|
443
|
+
name_patterns: list[tuple[str, MessageOutputCategory]] = [
|
|
444
|
+
("Event", EnumMessageCategory.EVENT),
|
|
445
|
+
("Command", EnumMessageCategory.COMMAND),
|
|
446
|
+
("Intent", EnumMessageCategory.INTENT),
|
|
447
|
+
("Projection", EnumNodeOutputType.PROJECTION),
|
|
448
|
+
]
|
|
449
|
+
|
|
450
|
+
for pattern, category in name_patterns:
|
|
451
|
+
# Check if type name ends with pattern (e.g., OrderCreatedEvent)
|
|
452
|
+
if type_name.endswith(pattern):
|
|
453
|
+
return category
|
|
454
|
+
# Check if type name starts with pattern (e.g., EventOrderCreated)
|
|
455
|
+
if type_name.startswith(pattern):
|
|
456
|
+
return category
|
|
457
|
+
|
|
458
|
+
# Could not determine category
|
|
459
|
+
return None
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
# ==============================================================================
|
|
463
|
+
# Runtime Shape Validator
|
|
464
|
+
# ==============================================================================
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
class RuntimeShapeValidator:
|
|
468
|
+
"""Runtime validator for ONEX handler execution shape constraints.
|
|
469
|
+
|
|
470
|
+
This validator checks handler outputs at runtime against the ONEX 4-node
|
|
471
|
+
architecture execution shape rules. Each node archetype has specific
|
|
472
|
+
constraints on what message categories it can produce.
|
|
473
|
+
|
|
474
|
+
Attributes:
|
|
475
|
+
rules: Dictionary mapping node archetypes to their execution shape rules.
|
|
476
|
+
|
|
477
|
+
Thread Safety:
|
|
478
|
+
RuntimeShapeValidator instances are stateless after initialization.
|
|
479
|
+
The rules dictionary is immutable and no per-validation state is stored.
|
|
480
|
+
Multiple threads can safely call validate_handler_output() or
|
|
481
|
+
validate_and_raise() on the same validator instance concurrently.
|
|
482
|
+
|
|
483
|
+
Performance:
|
|
484
|
+
For repeated validation (e.g., decorated handlers in hot paths), use the
|
|
485
|
+
module-level singleton via `enforce_execution_shape()` decorator or the
|
|
486
|
+
`_default_validator` instance for optimal performance.
|
|
487
|
+
|
|
488
|
+
Creating new validator instances is cheap but unnecessary in most cases.
|
|
489
|
+
Create a new instance only if you need custom rules or isolation.
|
|
490
|
+
|
|
491
|
+
Example:
|
|
492
|
+
>>> from omnibase_infra.models.validation import (
|
|
493
|
+
... ModelOutputValidationParams,
|
|
494
|
+
... ModelValidateAndRaiseParams,
|
|
495
|
+
... )
|
|
496
|
+
>>> validator = RuntimeShapeValidator()
|
|
497
|
+
>>>
|
|
498
|
+
>>> # Check if output is allowed
|
|
499
|
+
>>> if not validator.is_output_allowed(EnumNodeArchetype.REDUCER, EnumMessageCategory.EVENT):
|
|
500
|
+
... print("Reducer cannot return events!")
|
|
501
|
+
>>>
|
|
502
|
+
>>> # Get full violation details
|
|
503
|
+
>>> params = ModelOutputValidationParams(
|
|
504
|
+
... node_archetype=EnumNodeArchetype.REDUCER,
|
|
505
|
+
... output=event_output,
|
|
506
|
+
... output_category=EnumMessageCategory.EVENT,
|
|
507
|
+
... )
|
|
508
|
+
>>> violation = validator.validate_handler_output(params)
|
|
509
|
+
>>> if violation:
|
|
510
|
+
... print(f"Violation: {violation.format_for_ci()}")
|
|
511
|
+
>>>
|
|
512
|
+
>>> # Raise exception on violation
|
|
513
|
+
>>> raise_params = ModelValidateAndRaiseParams(
|
|
514
|
+
... node_archetype=EnumNodeArchetype.REDUCER,
|
|
515
|
+
... output=event_output,
|
|
516
|
+
... output_category=EnumMessageCategory.EVENT,
|
|
517
|
+
... )
|
|
518
|
+
>>> validator.validate_and_raise(raise_params) # Raises ExecutionShapeViolationError
|
|
519
|
+
"""
|
|
520
|
+
|
|
521
|
+
def __init__(
|
|
522
|
+
self, rules: dict[EnumNodeArchetype, ModelExecutionShapeRule] | None = None
|
|
523
|
+
) -> None:
|
|
524
|
+
"""Initialize RuntimeShapeValidator.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
rules: Optional custom rules dictionary. If not provided,
|
|
528
|
+
uses the default EXECUTION_SHAPE_RULES.
|
|
529
|
+
"""
|
|
530
|
+
self.rules = rules if rules is not None else EXECUTION_SHAPE_RULES
|
|
531
|
+
|
|
532
|
+
def get_rule(self, node_archetype: EnumNodeArchetype) -> ModelExecutionShapeRule:
|
|
533
|
+
"""Get execution shape rule for a node archetype.
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
node_archetype: The node archetype to get the rule for.
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
The ModelExecutionShapeRule for the specified node archetype.
|
|
540
|
+
|
|
541
|
+
Raises:
|
|
542
|
+
KeyError: If no rule is defined for the node archetype.
|
|
543
|
+
"""
|
|
544
|
+
if node_archetype not in self.rules:
|
|
545
|
+
raise KeyError(
|
|
546
|
+
f"No execution shape rule defined for: {node_archetype.value}"
|
|
547
|
+
)
|
|
548
|
+
return self.rules[node_archetype]
|
|
549
|
+
|
|
550
|
+
def _get_allowed_types_for_handler(self, node_archetype: EnumNodeArchetype) -> str:
|
|
551
|
+
"""Get a human-readable string of allowed output types for a handler.
|
|
552
|
+
|
|
553
|
+
Used to generate helpful error messages that suggest valid alternatives
|
|
554
|
+
when a handler attempts to return a forbidden output type.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
node_archetype: The node archetype to get allowed types for.
|
|
558
|
+
|
|
559
|
+
Returns:
|
|
560
|
+
A formatted string listing allowed output types (e.g., "EVENTs or COMMANDs").
|
|
561
|
+
"""
|
|
562
|
+
try:
|
|
563
|
+
rule = self.get_rule(node_archetype)
|
|
564
|
+
allowed = [t.value.upper() + "s" for t in rule.allowed_return_types]
|
|
565
|
+
if len(allowed) == 0:
|
|
566
|
+
return "no specific types (check EXECUTION_SHAPE_RULES)"
|
|
567
|
+
if len(allowed) == 1:
|
|
568
|
+
return allowed[0]
|
|
569
|
+
if len(allowed) == 2:
|
|
570
|
+
return f"{allowed[0]} or {allowed[1]}"
|
|
571
|
+
return ", ".join(allowed[:-1]) + f", or {allowed[-1]}"
|
|
572
|
+
except KeyError:
|
|
573
|
+
return "appropriate types (check EXECUTION_SHAPE_RULES)"
|
|
574
|
+
|
|
575
|
+
def is_output_allowed(
|
|
576
|
+
self,
|
|
577
|
+
node_archetype: EnumNodeArchetype,
|
|
578
|
+
output_category: MessageOutputCategory,
|
|
579
|
+
) -> bool:
|
|
580
|
+
"""Check if an output category is allowed for a node archetype.
|
|
581
|
+
|
|
582
|
+
This is a quick check that returns True/False without creating
|
|
583
|
+
a full violation result.
|
|
584
|
+
|
|
585
|
+
Args:
|
|
586
|
+
node_archetype: The node archetype to check.
|
|
587
|
+
output_category: The message category or node output type of the output.
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
True if the output category is allowed, False if forbidden.
|
|
591
|
+
|
|
592
|
+
Security Note (Intentional Fail-Open Design):
|
|
593
|
+
This method returns True (allowing the output) when no rule exists
|
|
594
|
+
for the given node archetype. This is an INTENTIONAL design decision,
|
|
595
|
+
not a security vulnerability:
|
|
596
|
+
|
|
597
|
+
1. **Extensibility**: New node archetypes should work by default without
|
|
598
|
+
requiring immediate rule definitions. This prevents blocking valid
|
|
599
|
+
code during archetype evolution.
|
|
600
|
+
|
|
601
|
+
2. **Validation vs Security Boundary**: This validator enforces
|
|
602
|
+
architectural constraints (ONEX 4-node patterns), NOT security
|
|
603
|
+
policies. It catches developer errors at build/test time, not
|
|
604
|
+
malicious inputs at runtime.
|
|
605
|
+
|
|
606
|
+
3. **Defense in Depth**: Security boundaries should be implemented
|
|
607
|
+
at the infrastructure layer (authentication, authorization,
|
|
608
|
+
input validation) - not in architectural pattern validators.
|
|
609
|
+
|
|
610
|
+
4. **Fail-Safe for Unknown Types**: Unknown node archetypes represent
|
|
611
|
+
future extensions or custom implementations. Blocking them would
|
|
612
|
+
break forward compatibility without security benefit.
|
|
613
|
+
|
|
614
|
+
If strict validation is required for security-critical contexts,
|
|
615
|
+
use a fail-closed wrapper or policy decorator.
|
|
616
|
+
"""
|
|
617
|
+
try:
|
|
618
|
+
rule = self.get_rule(node_archetype)
|
|
619
|
+
# Convert to EnumNodeOutputType for validation
|
|
620
|
+
node_output_type = _to_node_output_type(output_category)
|
|
621
|
+
return rule.is_return_type_allowed(node_output_type)
|
|
622
|
+
except KeyError:
|
|
623
|
+
# SECURITY DESIGN: Fail-open for unknown node archetypes.
|
|
624
|
+
# See docstring "Security Note" for rationale.
|
|
625
|
+
# This is intentional - new archetypes should be allowed by default.
|
|
626
|
+
return True
|
|
627
|
+
|
|
628
|
+
def validate_handler_output(
|
|
629
|
+
self,
|
|
630
|
+
params: ModelOutputValidationParams,
|
|
631
|
+
) -> ModelExecutionShapeViolationResult | None:
|
|
632
|
+
"""Validate handler output against execution shape constraints.
|
|
633
|
+
|
|
634
|
+
Args:
|
|
635
|
+
params: Parameters encapsulating node_archetype, output, output_category,
|
|
636
|
+
and optional file_path and line_number for violation reporting.
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
A ModelExecutionShapeViolationResult if a violation is detected,
|
|
640
|
+
or None if the output is valid.
|
|
641
|
+
"""
|
|
642
|
+
if self.is_output_allowed(params.node_archetype, params.output_category):
|
|
643
|
+
return None
|
|
644
|
+
|
|
645
|
+
# Determine specific violation type.
|
|
646
|
+
#
|
|
647
|
+
# The _VIOLATION_TYPE_MAP contains explicit mappings for known forbidden
|
|
648
|
+
# combinations (e.g., REDUCER returning EVENT). The fallback to
|
|
649
|
+
# FORBIDDEN_RETURN_TYPE is used for implicitly forbidden categories
|
|
650
|
+
# that aren't in the handler's allow-list but don't have a specific
|
|
651
|
+
# violation type. Examples:
|
|
652
|
+
# - REDUCER returning COMMAND (not in allowed_return_types=[PROJECTION])
|
|
653
|
+
# - EFFECT returning INTENT (not in allowed_return_types=[EVENT, COMMAND])
|
|
654
|
+
#
|
|
655
|
+
# The generic FORBIDDEN_RETURN_TYPE is semantically appropriate for these
|
|
656
|
+
# cases because they represent allow-list violations rather than explicit
|
|
657
|
+
# architectural constraint violations.
|
|
658
|
+
violation_key = (params.node_archetype, params.output_category)
|
|
659
|
+
violation_type = _VIOLATION_TYPE_MAP.get(
|
|
660
|
+
violation_key,
|
|
661
|
+
EnumExecutionShapeViolation.FORBIDDEN_RETURN_TYPE,
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# Build descriptive message with clear actionable guidance.
|
|
665
|
+
# The message distinguishes between explicit forbidden types (architectural
|
|
666
|
+
# constraint) and implicit forbidden types (not in allow-list).
|
|
667
|
+
output_type_name = type(params.output).__name__
|
|
668
|
+
archetype_name = params.node_archetype.value.upper()
|
|
669
|
+
category_name = params.output_category.value.upper()
|
|
670
|
+
|
|
671
|
+
# Check if this is a PROJECTION violation - these need educational context
|
|
672
|
+
# about the distinction between node output types and message categories
|
|
673
|
+
is_projection_violation = (
|
|
674
|
+
params.output_category == EnumNodeOutputType.PROJECTION
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
# Provide context-specific guidance based on whether this is an explicit
|
|
678
|
+
# or implicit violation
|
|
679
|
+
if violation_type == EnumExecutionShapeViolation.FORBIDDEN_RETURN_TYPE:
|
|
680
|
+
# Implicit violation: category not in allow-list
|
|
681
|
+
if is_projection_violation:
|
|
682
|
+
# Special case: PROJECTION needs educational message
|
|
683
|
+
message = (
|
|
684
|
+
f"{archetype_name} handler cannot return PROJECTION type "
|
|
685
|
+
f"'{output_type_name}'. PROJECTION is a node output type "
|
|
686
|
+
f"(EnumNodeOutputType), not a message routing category "
|
|
687
|
+
f"(EnumMessageCategory). Projections represent aggregated state "
|
|
688
|
+
f"and are only valid for REDUCER node output types."
|
|
689
|
+
)
|
|
690
|
+
else:
|
|
691
|
+
message = (
|
|
692
|
+
f"{archetype_name} handler cannot return {category_name} type "
|
|
693
|
+
f"'{output_type_name}'. {category_name} is not in the allowed "
|
|
694
|
+
f"return types for {archetype_name} handlers. "
|
|
695
|
+
f"Check EXECUTION_SHAPE_RULES for allowed message categories."
|
|
696
|
+
)
|
|
697
|
+
# Explicit violation: known architectural constraint
|
|
698
|
+
elif is_projection_violation:
|
|
699
|
+
# PROJECTION violations get enhanced educational message
|
|
700
|
+
allowed_types = self._get_allowed_types_for_handler(params.node_archetype)
|
|
701
|
+
message = (
|
|
702
|
+
f"{archetype_name} handler cannot return PROJECTION type "
|
|
703
|
+
f"'{output_type_name}'. PROJECTION is a node output type "
|
|
704
|
+
f"(EnumNodeOutputType), not a message routing category "
|
|
705
|
+
f"(EnumMessageCategory). Projections represent aggregated state "
|
|
706
|
+
f"and are only valid for REDUCER node output types. "
|
|
707
|
+
f"{archetype_name} handlers should return {allowed_types} instead."
|
|
708
|
+
)
|
|
709
|
+
else:
|
|
710
|
+
message = (
|
|
711
|
+
f"{archetype_name} handler cannot return {category_name} type "
|
|
712
|
+
f"'{output_type_name}'. This violates ONEX 4-node architecture "
|
|
713
|
+
f"execution shape constraints ({violation_type.value})."
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
return ModelExecutionShapeViolationResult(
|
|
717
|
+
violation_type=violation_type,
|
|
718
|
+
node_archetype=params.node_archetype,
|
|
719
|
+
file_path=params.file_path,
|
|
720
|
+
line_number=params.line_number if params.line_number > 0 else 1,
|
|
721
|
+
message=message,
|
|
722
|
+
severity=EnumValidationSeverity.ERROR,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
def validate_and_raise(
|
|
726
|
+
self,
|
|
727
|
+
params: ModelValidateAndRaiseParams,
|
|
728
|
+
) -> None:
|
|
729
|
+
"""Validate handler output and raise exception if invalid.
|
|
730
|
+
|
|
731
|
+
This method performs the same validation as validate_handler_output,
|
|
732
|
+
but raises an ExecutionShapeViolationError if a violation is detected.
|
|
733
|
+
|
|
734
|
+
Args:
|
|
735
|
+
params: Parameters encapsulating node_archetype, output, output_category,
|
|
736
|
+
optional file_path and line_number for violation reporting,
|
|
737
|
+
and optional correlation_id for distributed tracing.
|
|
738
|
+
|
|
739
|
+
Raises:
|
|
740
|
+
ExecutionShapeViolationError: If the output violates execution
|
|
741
|
+
shape constraints for the node archetype.
|
|
742
|
+
"""
|
|
743
|
+
validation_params = ModelOutputValidationParams(
|
|
744
|
+
node_archetype=params.node_archetype,
|
|
745
|
+
output=params.output,
|
|
746
|
+
output_category=params.output_category,
|
|
747
|
+
file_path=params.file_path,
|
|
748
|
+
line_number=params.line_number,
|
|
749
|
+
)
|
|
750
|
+
violation = self.validate_handler_output(validation_params)
|
|
751
|
+
if violation is not None:
|
|
752
|
+
raise ExecutionShapeViolationError(
|
|
753
|
+
violation, correlation_id=params.correlation_id
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# ==============================================================================
|
|
758
|
+
# Module-Level Singleton Validator
|
|
759
|
+
# ==============================================================================
|
|
760
|
+
#
|
|
761
|
+
# Performance Optimization: The RuntimeShapeValidator is stateless after
|
|
762
|
+
# initialization (only stores a reference to EXECUTION_SHAPE_RULES which is
|
|
763
|
+
# a module-level constant). Creating new instances on every validation call
|
|
764
|
+
# is wasteful in hot paths (e.g., the @enforce_execution_shape decorator).
|
|
765
|
+
# Instead, we use a module-level singleton.
|
|
766
|
+
#
|
|
767
|
+
# Why a singleton is safe here:
|
|
768
|
+
# - The validator's rules dictionary is immutable after initialization
|
|
769
|
+
# - No per-validation state is stored in the validator instance
|
|
770
|
+
# - All validation methods are pure functions that produce new results
|
|
771
|
+
# - Each validation creates fresh ModelExecutionShapeViolationResult objects
|
|
772
|
+
#
|
|
773
|
+
# Thread Safety:
|
|
774
|
+
# - The singleton is created at module import time (before any threads)
|
|
775
|
+
# - All read operations on the rules dictionary are thread-safe
|
|
776
|
+
# - Each validation creates fresh local state (violation results)
|
|
777
|
+
# - No locks are needed because there's no shared mutable state
|
|
778
|
+
# - The @enforce_execution_shape decorator uses this singleton safely from any thread
|
|
779
|
+
# - Concurrent calls from multiple threads will each create independent violation
|
|
780
|
+
# results without interference
|
|
781
|
+
#
|
|
782
|
+
# When NOT to use the singleton:
|
|
783
|
+
# - If you need custom execution shape rules (create your own instance)
|
|
784
|
+
# - If you need to mock the validator in tests (inject or patch)
|
|
785
|
+
# - If you're validating in a context that requires isolation
|
|
786
|
+
#
|
|
787
|
+
# For repeated validation (e.g., decorated handlers in hot paths), the singleton
|
|
788
|
+
# pattern provides optimal performance without sacrificing thread safety.
|
|
789
|
+
|
|
790
|
+
_default_validator = RuntimeShapeValidator()
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
# ==============================================================================
|
|
794
|
+
# Decorator
|
|
795
|
+
# ==============================================================================
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def enforce_execution_shape(node_archetype: EnumNodeArchetype) -> Callable[[F], F]:
|
|
799
|
+
"""Decorator to enforce execution shape constraints at runtime.
|
|
800
|
+
|
|
801
|
+
This decorator wraps a handler function and validates its return value
|
|
802
|
+
against the execution shape rules for the specified node archetype.
|
|
803
|
+
If the return value violates the constraints, an ExecutionShapeViolationError
|
|
804
|
+
is raised.
|
|
805
|
+
|
|
806
|
+
The decorator attempts to detect the message category of the return value
|
|
807
|
+
using the `detect_message_category` function. If the category cannot be
|
|
808
|
+
determined, no validation is performed (fail-open behavior).
|
|
809
|
+
|
|
810
|
+
Args:
|
|
811
|
+
node_archetype: The node archetype that determines allowed output categories.
|
|
812
|
+
|
|
813
|
+
Returns:
|
|
814
|
+
A decorator function that wraps the handler with runtime validation.
|
|
815
|
+
|
|
816
|
+
Example:
|
|
817
|
+
>>> @enforce_execution_shape(EnumNodeArchetype.REDUCER)
|
|
818
|
+
... def my_reducer(event):
|
|
819
|
+
... # This is OK - reducer can return projections
|
|
820
|
+
... return UserProjection(user_id=event.user_id)
|
|
821
|
+
>>>
|
|
822
|
+
>>> @enforce_execution_shape(EnumNodeArchetype.REDUCER)
|
|
823
|
+
... def bad_reducer(event):
|
|
824
|
+
... # This will raise ExecutionShapeViolationError
|
|
825
|
+
... return UserCreatedEvent(user_id=event.user_id)
|
|
826
|
+
|
|
827
|
+
Note:
|
|
828
|
+
The decorator uses inspect to determine the source file and line
|
|
829
|
+
number of the decorated function for better error reporting.
|
|
830
|
+
|
|
831
|
+
Security Note (Intentional Fail-Open Design):
|
|
832
|
+
When the message category cannot be determined from the return value,
|
|
833
|
+
the decorator skips validation and allows the return. This is an
|
|
834
|
+
INTENTIONAL design decision for the following reasons:
|
|
835
|
+
|
|
836
|
+
1. **Graceful Handling of Unknown Types**: Not all return types will
|
|
837
|
+
have detectable categories (e.g., primitive types, third-party
|
|
838
|
+
objects, custom domain objects). Blocking these would cause false
|
|
839
|
+
positives and break legitimate code.
|
|
840
|
+
|
|
841
|
+
2. **Progressive Adoption**: Teams can adopt execution shape validation
|
|
842
|
+
incrementally. Handlers returning non-categorized types continue
|
|
843
|
+
working while teams add category annotations to their message types.
|
|
844
|
+
|
|
845
|
+
3. **Validation Tool, Not Security Gate**: This decorator catches
|
|
846
|
+
architectural mistakes (e.g., reducer returning events), not
|
|
847
|
+
security threats. Unknown categories don't represent attack vectors.
|
|
848
|
+
|
|
849
|
+
4. **Type Detection Limitations**: Category detection relies on naming
|
|
850
|
+
conventions and explicit attributes. Overly strict enforcement would
|
|
851
|
+
require all types to implement specific interfaces, which is
|
|
852
|
+
impractical for existing codebases.
|
|
853
|
+
|
|
854
|
+
For strict enforcement, ensure all message types either:
|
|
855
|
+
- Have a `category` or `message_category` attribute
|
|
856
|
+
- Follow naming conventions (e.g., `*Event`, `*Command`, `*Projection`)
|
|
857
|
+
"""
|
|
858
|
+
|
|
859
|
+
def decorator(func: F) -> F:
|
|
860
|
+
# Get source info for better error reporting
|
|
861
|
+
try:
|
|
862
|
+
source_file = inspect.getfile(func)
|
|
863
|
+
source_lines = inspect.getsourcelines(func)
|
|
864
|
+
line_number = source_lines[1] if source_lines else 0
|
|
865
|
+
except (TypeError, OSError):
|
|
866
|
+
source_file = "<unknown>"
|
|
867
|
+
line_number = 0
|
|
868
|
+
|
|
869
|
+
@functools.wraps(func)
|
|
870
|
+
def wrapper(*args: object, **kwargs: object) -> object:
|
|
871
|
+
result = func(*args, **kwargs)
|
|
872
|
+
|
|
873
|
+
# Detect category of the result
|
|
874
|
+
output_category = detect_message_category(result)
|
|
875
|
+
|
|
876
|
+
# SECURITY DESIGN: Fail-open for undetectable message categories.
|
|
877
|
+
# See docstring "Security Note" for detailed rationale.
|
|
878
|
+
# This is intentional - unknown types shouldn't block execution.
|
|
879
|
+
if output_category is None:
|
|
880
|
+
return result
|
|
881
|
+
|
|
882
|
+
# Validate against execution shape rules
|
|
883
|
+
validate_params = ModelValidateAndRaiseParams(
|
|
884
|
+
node_archetype=node_archetype,
|
|
885
|
+
output=result,
|
|
886
|
+
output_category=output_category,
|
|
887
|
+
file_path=source_file,
|
|
888
|
+
line_number=line_number,
|
|
889
|
+
)
|
|
890
|
+
_default_validator.validate_and_raise(validate_params)
|
|
891
|
+
|
|
892
|
+
return result
|
|
893
|
+
|
|
894
|
+
# Cast wrapper to F - functools.wraps preserves the signature at runtime,
|
|
895
|
+
# and mypy cannot prove the equivalence, so we use an explicit cast.
|
|
896
|
+
return cast(F, wrapper)
|
|
897
|
+
|
|
898
|
+
return decorator
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
# ==============================================================================
|
|
902
|
+
# Module Exports
|
|
903
|
+
# ==============================================================================
|
|
904
|
+
|
|
905
|
+
__all__ = [
|
|
906
|
+
# Constants
|
|
907
|
+
"EXECUTION_SHAPE_RULES",
|
|
908
|
+
# Exception
|
|
909
|
+
"ExecutionShapeViolationError",
|
|
910
|
+
# Validator class
|
|
911
|
+
"RuntimeShapeValidator",
|
|
912
|
+
# Functions
|
|
913
|
+
"detect_message_category",
|
|
914
|
+
"enforce_execution_shape",
|
|
915
|
+
]
|