omnibase_infra 0.2.1__py3-none-any.whl → 0.2.3__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 (161) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/commands.py +1 -1
  8. omnibase_infra/configs/widget_mapping.yaml +176 -0
  9. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +5 -2
  10. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +5 -2
  11. omnibase_infra/enums/__init__.py +6 -0
  12. omnibase_infra/enums/enum_handler_error_type.py +10 -0
  13. omnibase_infra/enums/enum_handler_source_mode.py +72 -0
  14. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  15. omnibase_infra/errors/error_compute_registry.py +4 -1
  16. omnibase_infra/errors/error_event_bus_registry.py +4 -1
  17. omnibase_infra/errors/error_infra.py +3 -1
  18. omnibase_infra/errors/error_policy_registry.py +4 -1
  19. omnibase_infra/event_bus/event_bus_kafka.py +1 -1
  20. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
  21. omnibase_infra/handlers/__init__.py +8 -1
  22. omnibase_infra/handlers/handler_consul.py +7 -1
  23. omnibase_infra/handlers/handler_db.py +10 -3
  24. omnibase_infra/handlers/handler_graph.py +10 -5
  25. omnibase_infra/handlers/handler_http.py +8 -2
  26. omnibase_infra/handlers/handler_intent.py +387 -0
  27. omnibase_infra/handlers/handler_mcp.py +745 -63
  28. omnibase_infra/handlers/handler_vault.py +11 -5
  29. omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
  30. omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
  31. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
  32. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +308 -4
  33. omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
  34. omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
  35. omnibase_infra/mixins/mixin_node_introspection.py +42 -7
  36. omnibase_infra/mixins/mixin_retry_execution.py +1 -1
  37. omnibase_infra/models/discovery/model_introspection_config.py +11 -0
  38. omnibase_infra/models/handlers/__init__.py +48 -5
  39. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  40. omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
  41. omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
  42. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  43. omnibase_infra/models/mcp/__init__.py +15 -0
  44. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  45. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  46. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  47. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  48. omnibase_infra/models/registration/model_node_capabilities.py +11 -0
  49. omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
  50. omnibase_infra/models/runtime/model_handler_contract.py +25 -9
  51. omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
  52. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
  53. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
  54. omnibase_infra/nodes/effects/contract.yaml +0 -5
  55. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
  56. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
  57. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
  58. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
  59. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
  60. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
  61. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
  62. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
  63. omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
  64. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +47 -26
  65. omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
  66. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
  67. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +28 -20
  68. omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
  69. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
  70. omnibase_infra/plugins/plugin_compute_base.py +16 -2
  71. omnibase_infra/protocols/__init__.py +2 -0
  72. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  73. omnibase_infra/protocols/protocol_event_projector.py +1 -1
  74. omnibase_infra/runtime/__init__.py +90 -1
  75. omnibase_infra/runtime/binding_config_resolver.py +102 -37
  76. omnibase_infra/runtime/constants_notification.py +75 -0
  77. omnibase_infra/runtime/contract_handler_discovery.py +6 -1
  78. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  79. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  80. omnibase_infra/runtime/handler_contract_source.py +267 -186
  81. omnibase_infra/runtime/handler_identity.py +81 -0
  82. omnibase_infra/runtime/handler_plugin_loader.py +19 -2
  83. omnibase_infra/runtime/handler_registry.py +11 -3
  84. omnibase_infra/runtime/handler_source_resolver.py +326 -0
  85. omnibase_infra/runtime/mixin_semver_cache.py +25 -1
  86. omnibase_infra/runtime/mixins/__init__.py +7 -0
  87. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  88. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
  89. omnibase_infra/runtime/models/__init__.py +24 -0
  90. omnibase_infra/runtime/models/model_health_check_result.py +2 -1
  91. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  92. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  93. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  94. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  95. omnibase_infra/runtime/projector_plugin_loader.py +1 -1
  96. omnibase_infra/runtime/projector_shell.py +229 -1
  97. omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
  98. omnibase_infra/runtime/protocols/__init__.py +10 -0
  99. omnibase_infra/runtime/registry/registry_protocol_binding.py +16 -15
  100. omnibase_infra/runtime/registry_contract_source.py +693 -0
  101. omnibase_infra/runtime/registry_policy.py +9 -326
  102. omnibase_infra/runtime/secret_resolver.py +4 -2
  103. omnibase_infra/runtime/service_kernel.py +11 -3
  104. omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
  105. omnibase_infra/runtime/service_runtime_host_process.py +589 -106
  106. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  107. omnibase_infra/runtime/transition_notification_publisher.py +764 -0
  108. omnibase_infra/runtime/util_container_wiring.py +6 -5
  109. omnibase_infra/runtime/util_wiring.py +17 -4
  110. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  111. omnibase_infra/services/__init__.py +21 -0
  112. omnibase_infra/services/corpus_capture.py +7 -1
  113. omnibase_infra/services/mcp/__init__.py +31 -0
  114. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  115. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  116. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  117. omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
  118. omnibase_infra/services/registry_api/__init__.py +40 -0
  119. omnibase_infra/services/registry_api/main.py +261 -0
  120. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  121. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  122. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  123. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  124. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  125. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  126. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  127. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  128. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  129. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  130. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  131. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  132. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  133. omnibase_infra/services/registry_api/routes.py +371 -0
  134. omnibase_infra/services/registry_api/service.py +837 -0
  135. omnibase_infra/services/service_capability_query.py +4 -4
  136. omnibase_infra/services/service_health.py +3 -2
  137. omnibase_infra/services/service_timeout_emitter.py +20 -3
  138. omnibase_infra/services/service_timeout_scanner.py +7 -3
  139. omnibase_infra/services/session/__init__.py +56 -0
  140. omnibase_infra/services/session/config_consumer.py +120 -0
  141. omnibase_infra/services/session/config_store.py +139 -0
  142. omnibase_infra/services/session/consumer.py +1007 -0
  143. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  144. omnibase_infra/services/session/store.py +997 -0
  145. omnibase_infra/utils/__init__.py +19 -0
  146. omnibase_infra/utils/util_atomic_file.py +261 -0
  147. omnibase_infra/utils/util_db_transaction.py +239 -0
  148. omnibase_infra/utils/util_dsn_validation.py +1 -1
  149. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  150. omnibase_infra/validation/__init__.py +3 -19
  151. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  152. omnibase_infra/validation/infra_validators.py +35 -24
  153. omnibase_infra/validation/validation_exemptions.yaml +140 -9
  154. omnibase_infra/validation/validator_chain_propagation.py +2 -2
  155. omnibase_infra/validation/validator_runtime_shape.py +1 -1
  156. omnibase_infra/validation/validator_security.py +473 -370
  157. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/METADATA +3 -3
  158. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/RECORD +161 -98
  159. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/WHEEL +0 -0
  160. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/entry_points.txt +0 -0
  161. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -90,13 +90,22 @@ from omnibase_infra.runtime.service_kernel import load_runtime_config
90
90
  from omnibase_infra.runtime.service_kernel import main as kernel_main
91
91
  from omnibase_infra.runtime.service_message_dispatch_engine import MessageDispatchEngine
92
92
  from omnibase_infra.runtime.models import (
93
+ ModelProjectorNotificationConfig,
93
94
  ModelRuntimeSchedulerConfig,
94
95
  ModelRuntimeSchedulerMetrics,
95
96
  ModelRuntimeTick,
97
+ ModelStateTransitionNotification,
98
+ ModelTransitionNotificationPublisherMetrics,
96
99
  )
97
100
  from omnibase_infra.runtime.registry_policy import RegistryPolicy
98
101
  from omnibase_infra.runtime.protocol_policy import ProtocolPolicy
99
- from omnibase_infra.runtime.protocols import ProtocolRuntimeScheduler
102
+ from omnibase_infra.runtime.protocols import (
103
+ ProtocolRuntimeScheduler,
104
+ ProtocolTransitionNotificationPublisher,
105
+ )
106
+ from omnibase_infra.runtime.mixins import (
107
+ ProtocolProjectorNotificationContext,
108
+ )
100
109
  from omnibase_infra.runtime.registry import (
101
110
  MessageTypeRegistryError,
102
111
  ModelDomainConstraint,
@@ -145,6 +154,28 @@ from omnibase_infra.runtime.handler_plugin_loader import (
145
154
  MAX_CONTRACT_SIZE,
146
155
  )
147
156
 
157
+ # Handler bootstrap source (OMN-1087)
158
+ from omnibase_infra.runtime.handler_bootstrap_source import (
159
+ HandlerBootstrapSource,
160
+ SOURCE_TYPE_BOOTSTRAP,
161
+ )
162
+
163
+ # Handler identity helper (OMN-1095)
164
+ from omnibase_infra.runtime.handler_identity import (
165
+ HANDLER_IDENTITY_PREFIX,
166
+ handler_identity,
167
+ )
168
+
169
+ # Handler source resolver (OMN-1095)
170
+ from omnibase_infra.runtime.handler_source_resolver import HandlerSourceResolver
171
+
172
+ # Handler contract config loader
173
+ from omnibase_infra.runtime.handler_contract_config_loader import (
174
+ MAX_CONTRACT_SIZE_BYTES,
175
+ extract_handler_config,
176
+ load_handler_contract_config,
177
+ )
178
+
148
179
  # Binding config resolver (OMN-765)
149
180
  from omnibase_infra.runtime.binding_config_resolver import BindingConfigResolver
150
181
  from omnibase_infra.runtime.protocol_handler_plugin_loader import (
@@ -181,6 +212,29 @@ from omnibase_infra.runtime.security_metadata_validator import (
181
212
  validate_handler_security,
182
213
  )
183
214
 
215
+ # Transition notification publisher and outbox (OMN-1139)
216
+ from omnibase_infra.runtime.constants_notification import FROM_STATE_INITIAL
217
+ from omnibase_infra.runtime.transition_notification_publisher import (
218
+ TransitionNotificationPublisher,
219
+ )
220
+ from omnibase_infra.runtime.transition_notification_outbox import (
221
+ TransitionNotificationOutbox,
222
+ )
223
+
224
+ # Registry contract source (OMN-1100)
225
+ from omnibase_infra.runtime.registry_contract_source import (
226
+ DEFAULT_CONSUL_HOST,
227
+ DEFAULT_CONSUL_PORT,
228
+ DEFAULT_CONTRACT_PREFIX,
229
+ RegistryContractSource,
230
+ adelete_contract_from_consul,
231
+ alist_contracts_in_consul,
232
+ astore_contract_in_consul,
233
+ delete_contract_from_consul,
234
+ list_contracts_in_consul,
235
+ store_contract_in_consul,
236
+ )
237
+
184
238
  # Chain-aware dispatch (OMN-951) - must be imported LAST to avoid circular import
185
239
  from omnibase_infra.runtime.chain_aware_dispatch import (
186
240
  ChainAwareDispatcher,
@@ -214,9 +268,12 @@ __all__: list[str] = [
214
268
  "MessageTypeRegistryError",
215
269
  "ModelDomainConstraint",
216
270
  "ModelMessageTypeEntry",
271
+ "ModelProjectorNotificationConfig",
217
272
  "ModelRuntimeSchedulerConfig",
218
273
  "ModelRuntimeSchedulerMetrics",
219
274
  "ModelRuntimeTick",
275
+ "ModelStateTransitionNotification",
276
+ "ModelTransitionNotificationPublisherMetrics",
220
277
  "ProtocolMessageDispatcher",
221
278
  "ProtocolMessageTypeRegistry",
222
279
  # Registry classes
@@ -230,8 +287,14 @@ __all__: list[str] = [
230
287
  "RegistryDispatcher",
231
288
  # Runtime scheduler (OMN-953)
232
289
  "ProtocolRuntimeScheduler",
290
+ # Transition notification (OMN-1139)
291
+ "ProtocolTransitionNotificationPublisher",
292
+ # Projector notification context protocol (OMN-1139)
293
+ "ProtocolProjectorNotificationContext",
233
294
  # Error class
234
295
  "RegistryError",
296
+ # Notification constants (OMN-1139)
297
+ "FROM_STATE_INITIAL",
235
298
  # Runtime host
236
299
  "RuntimeHostProcess",
237
300
  "RuntimeScheduler",
@@ -277,6 +340,18 @@ __all__: list[str] = [
277
340
  "HandlerPluginLoader",
278
341
  "MAX_CONTRACT_SIZE",
279
342
  "ProtocolHandlerPluginLoader",
343
+ # Handler bootstrap source (OMN-1087)
344
+ "HandlerBootstrapSource",
345
+ "SOURCE_TYPE_BOOTSTRAP",
346
+ # Handler identity helper (OMN-1095)
347
+ "HANDLER_IDENTITY_PREFIX",
348
+ "handler_identity",
349
+ # Handler source resolver (OMN-1095)
350
+ "HandlerSourceResolver",
351
+ # Handler contract config loader
352
+ "MAX_CONTRACT_SIZE_BYTES",
353
+ "extract_handler_config",
354
+ "load_handler_contract_config",
280
355
  # Binding config resolver (OMN-765)
281
356
  "BindingConfigResolver",
282
357
  # Handler discovery protocol and implementation (OMN-1133)
@@ -293,4 +368,18 @@ __all__: list[str] = [
293
368
  # Security metadata validator (OMN-1137)
294
369
  "SecurityMetadataValidator",
295
370
  "validate_handler_security",
371
+ # Transition notification publisher and outbox (OMN-1139)
372
+ "TransitionNotificationOutbox",
373
+ "TransitionNotificationPublisher",
374
+ # Registry contract source (OMN-1100)
375
+ "DEFAULT_CONSUL_HOST",
376
+ "DEFAULT_CONSUL_PORT",
377
+ "DEFAULT_CONTRACT_PREFIX",
378
+ "RegistryContractSource",
379
+ "adelete_contract_from_consul",
380
+ "alist_contracts_in_consul",
381
+ "astore_contract_in_consul",
382
+ "delete_contract_from_consul",
383
+ "list_contracts_in_consul",
384
+ "store_contract_in_consul",
296
385
  ]
@@ -56,10 +56,12 @@ Example:
56
56
  # Bootstrap container and register config
57
57
  container = ModelONEXContainer()
58
58
  config = ModelBindingConfigResolverConfig(env_prefix="HANDLER")
59
+ from omnibase_core.enums import EnumInjectionScope
60
+
59
61
  await container.service_registry.register_instance(
60
62
  interface=ModelBindingConfigResolverConfig,
61
63
  instance=config,
62
- scope="global",
64
+ scope=EnumInjectionScope.GLOBAL,
63
65
  )
64
66
 
65
67
  # Create resolver with container injection
@@ -235,14 +237,15 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
235
237
 
236
238
  Example:
237
239
  >>> # Container setup (async context required)
240
+ >>> from omnibase_core.enums import EnumInjectionScope
238
241
  >>> container = ModelONEXContainer()
239
242
  >>> config = ModelBindingConfigResolverConfig(env_prefix="HANDLER")
240
243
  >>> await container.service_registry.register_instance(
241
244
  ... interface=ModelBindingConfigResolverConfig,
242
245
  ... instance=config,
243
- ... scope="global",
246
+ ... scope=EnumInjectionScope.GLOBAL,
244
247
  ... )
245
- >>> resolver = BindingConfigResolver(container)
248
+ >>> resolver = await BindingConfigResolver.create(container)
246
249
  >>> binding = resolver.resolve(
247
250
  ... handler_type="db",
248
251
  ... inline_config={"pool_size": 10}
@@ -252,6 +255,9 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
252
255
  def __init__(
253
256
  self,
254
257
  container: ModelONEXContainer,
258
+ *,
259
+ _config: ModelBindingConfigResolverConfig | None = None,
260
+ _secret_resolver: SecretResolver | None = None,
255
261
  ) -> None:
256
262
  """Initialize BindingConfigResolver with container-based dependency injection.
257
263
 
@@ -259,52 +265,37 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
259
265
  Config is resolved from container's service registry, and SecretResolver
260
266
  is resolved as an optional dependency.
261
267
 
268
+ Note:
269
+ Prefer using the async factory method ``create()`` for initialization,
270
+ which properly resolves dependencies from the container's service registry.
271
+ Direct ``__init__`` usage requires pre-resolved config and secret_resolver.
272
+
262
273
  Args:
263
274
  container: ONEX container for dependency resolution.
275
+ _config: Pre-resolved config (used by create() factory). If None, raises error.
276
+ _secret_resolver: Pre-resolved secret resolver (optional, used by create() factory).
264
277
 
265
278
  Raises:
266
- ProtocolConfigurationError: If ModelBindingConfigResolverConfig is not registered
267
- in the container's service registry.
279
+ ProtocolConfigurationError: If _config is not provided (use create() instead).
268
280
  """
269
281
  self._container = container
270
282
 
271
- # Resolve config from container's service registry
272
- try:
273
- self._config: ModelBindingConfigResolverConfig = (
274
- container.service_registry.resolve_service(
275
- ModelBindingConfigResolverConfig
276
- )
277
- )
278
- except (LookupError, KeyError, TypeError, AttributeError) as e:
279
- # LookupError/KeyError: service not registered
280
- # TypeError: invalid interface specification
281
- # AttributeError: container/registry missing expected methods
283
+ # Validate that config was provided (either via create() or directly)
284
+ if _config is None:
282
285
  context = ModelInfraErrorContext.with_correlation(
283
286
  transport_type=EnumInfraTransportType.RUNTIME,
284
287
  operation="init",
285
288
  target_name="binding_config_resolver",
286
289
  )
287
290
  raise ProtocolConfigurationError(
288
- "Failed to resolve ModelBindingConfigResolverConfig from container. "
289
- "Ensure config is registered via container.service_registry.register_instance().",
291
+ "BindingConfigResolver requires config to be provided. "
292
+ "Use the async factory method BindingConfigResolver.create(container) "
293
+ "for proper initialization with dependency resolution.",
290
294
  context=context,
291
- ) from e
292
-
293
- # Resolve SecretResolver from container (optional dependency)
294
- # This replaces the config.secret_resolver pattern
295
- self._secret_resolver: SecretResolver | None = None
296
- try:
297
- from omnibase_infra.runtime.secret_resolver import SecretResolver
298
-
299
- self._secret_resolver = container.service_registry.resolve_service(
300
- SecretResolver
301
295
  )
302
- except (ImportError, KeyError, AttributeError):
303
- # SecretResolver is optional - if not registered, vault: schemes won't work
304
- # ImportError: SecretResolver module not available
305
- # KeyError: SecretResolver not registered in service registry
306
- # AttributeError: service_registry missing resolve_service method (test mocks)
307
- pass
296
+
297
+ self._config: ModelBindingConfigResolverConfig = _config
298
+ self._secret_resolver: SecretResolver | None = _secret_resolver
308
299
 
309
300
  # Use OrderedDict for LRU eviction support - entries are moved to end on access
310
301
  self._cache: OrderedDict[str, ModelConfigCacheEntry] = OrderedDict()
@@ -357,6 +348,80 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
357
348
  self._async_key_locks: dict[str, asyncio.Lock] = {}
358
349
  self._async_key_lock_timestamps: dict[str, float] = {}
359
350
 
351
+ @classmethod
352
+ async def create(
353
+ cls,
354
+ container: ModelONEXContainer,
355
+ ) -> BindingConfigResolver:
356
+ """Async factory method for creating BindingConfigResolver with proper DI.
357
+
358
+ This is the preferred method for creating BindingConfigResolver instances.
359
+ It properly resolves dependencies from the container's async service registry.
360
+
361
+ Args:
362
+ container: ONEX container for dependency resolution.
363
+
364
+ Returns:
365
+ Fully initialized BindingConfigResolver instance.
366
+
367
+ Raises:
368
+ ProtocolConfigurationError: If ModelBindingConfigResolverConfig is not registered
369
+ in the container's service registry.
370
+
371
+ Example:
372
+ >>> from omnibase_core.enums import EnumInjectionScope
373
+ >>> container = ModelONEXContainer()
374
+ >>> config = ModelBindingConfigResolverConfig(env_prefix="HANDLER")
375
+ >>> await container.service_registry.register_instance(
376
+ ... interface=ModelBindingConfigResolverConfig,
377
+ ... instance=config,
378
+ ... scope=EnumInjectionScope.GLOBAL,
379
+ ... )
380
+ >>> resolver = await BindingConfigResolver.create(container)
381
+ """
382
+ # Resolve config from container's service registry
383
+ try:
384
+ config: ModelBindingConfigResolverConfig = (
385
+ await container.service_registry.resolve_service(
386
+ ModelBindingConfigResolverConfig
387
+ )
388
+ )
389
+ except (LookupError, KeyError, TypeError, AttributeError) as e:
390
+ # LookupError/KeyError: service not registered
391
+ # TypeError: invalid interface specification
392
+ # AttributeError: container/registry missing expected methods
393
+ context = ModelInfraErrorContext.with_correlation(
394
+ transport_type=EnumInfraTransportType.RUNTIME,
395
+ operation="create",
396
+ target_name="binding_config_resolver",
397
+ )
398
+ raise ProtocolConfigurationError(
399
+ "Failed to resolve ModelBindingConfigResolverConfig from container. "
400
+ "Ensure config is registered via container.service_registry.register_instance().",
401
+ context=context,
402
+ ) from e
403
+
404
+ # Resolve SecretResolver from container (optional dependency)
405
+ secret_resolver: SecretResolver | None = None
406
+ try:
407
+ from omnibase_infra.runtime.secret_resolver import SecretResolver
408
+
409
+ secret_resolver = await container.service_registry.resolve_service(
410
+ SecretResolver
411
+ )
412
+ except (ImportError, KeyError, AttributeError):
413
+ # SecretResolver is optional - if not registered, vault: schemes won't work
414
+ # ImportError: SecretResolver module not available
415
+ # KeyError: SecretResolver not registered in service registry
416
+ # AttributeError: service_registry missing resolve_service method (test mocks)
417
+ pass
418
+
419
+ return cls(
420
+ container,
421
+ _config=config,
422
+ _secret_resolver=secret_resolver,
423
+ )
424
+
360
425
  # === Primary API (Sync) ===
361
426
 
362
427
  def resolve(
@@ -533,7 +598,7 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
533
598
  result = await self._resolve_config_async(
534
599
  handler_type=handler_type,
535
600
  config_ref=config_ref,
536
- inline_config=inline_config,
601
+ inline_config=inline_config, # type: ignore[arg-type]
537
602
  correlation_id=correlation_id,
538
603
  )
539
604
 
@@ -917,7 +982,7 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
917
982
  def _describe_source(
918
983
  self,
919
984
  config_ref: str | None,
920
- inline_config: dict[str, object] | None,
985
+ inline_config: dict[str, JsonType] | None,
921
986
  ) -> str:
922
987
  """Create a description of the configuration source for debugging.
923
988
 
@@ -945,7 +1010,7 @@ class BindingConfigResolver: # ONEX_EXCLUDE: method_count - follows SecretResol
945
1010
  self,
946
1011
  handler_type: str,
947
1012
  config_ref: str | None,
948
- inline_config: dict[str, object] | None,
1013
+ inline_config: dict[str, JsonType] | None,
949
1014
  correlation_id: UUID,
950
1015
  ) -> ModelBindingConfig:
951
1016
  """Resolve configuration from sources synchronously.
@@ -0,0 +1,75 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Notification Constants Module.
4
+
5
+ Provides constants for notification publishing, particularly for state
6
+ transition notifications used by projectors and orchestrators.
7
+
8
+ Sentinel Values
9
+ ---------------
10
+ - **FROM_STATE_INITIAL**: Sentinel value used when publishing a state transition
11
+ notification for a NEW entity (one that didn't exist before the projection).
12
+ This is used instead of an empty string to provide clear semantic meaning.
13
+
14
+ Usage:
15
+ >>> from omnibase_infra.runtime.constants_notification import FROM_STATE_INITIAL
16
+ >>>
17
+ >>> # In notification publishing code:
18
+ >>> effective_from_state = from_state if from_state is not None else FROM_STATE_INITIAL
19
+ >>>
20
+ >>> # In notification consumers:
21
+ >>> if notification.from_state == FROM_STATE_INITIAL:
22
+ ... # This is a new entity, not a state transition from existing state
23
+ ... handle_new_entity(notification)
24
+
25
+ Why Not Empty String?
26
+ Using an empty string ("") for new entities is semantically ambiguous:
27
+ - An empty string could be confused with an actual empty state value
28
+ - It doesn't clearly communicate "this entity was just created"
29
+ - Consumers cannot distinguish between "no previous state" and "empty state"
30
+
31
+ The "__INITIAL__" sentinel provides:
32
+ - Clear semantic meaning (obviously not a real FSM state name)
33
+ - Easy detection in consumer code
34
+ - Explicit intent communication
35
+
36
+ Related:
37
+ - ModelStateTransitionNotification: The notification model that uses this value
38
+ - MixinProjectorNotificationPublishing: The mixin that sets this value
39
+ - TransitionNotificationPublisher: The publisher that emits these notifications
40
+
41
+ Related Tickets:
42
+ - OMN-1139: Integrate TransitionNotificationPublisher with ProjectorShell
43
+
44
+ .. versionadded:: 0.8.0
45
+ Created as part of OMN-1139 notification integration.
46
+ """
47
+
48
+ from typing import Final
49
+
50
+ # Sentinel value for from_state when publishing notifications for NEW entities.
51
+ # This indicates the entity was just created and has no previous state.
52
+ # Consumers can check: if notification.from_state == FROM_STATE_INITIAL
53
+ FROM_STATE_INITIAL: Final[str] = "__INITIAL__"
54
+ """Sentinel value indicating a new entity with no previous state.
55
+
56
+ This value is used as the ``from_state`` field in ModelStateTransitionNotification
57
+ when publishing a notification for a newly created entity. It replaces the
58
+ semantically ambiguous empty string ("") with a clear, distinguishable marker.
59
+
60
+ Example:
61
+ >>> notification = ModelStateTransitionNotification(
62
+ ... aggregate_type="registration",
63
+ ... aggregate_id=uuid4(),
64
+ ... from_state=FROM_STATE_INITIAL, # New entity
65
+ ... to_state="pending",
66
+ ... # ... other fields
67
+ ... )
68
+
69
+ Note:
70
+ The double underscore prefix/suffix convention (``__INITIAL__``) follows
71
+ Python's pattern for special/dunder values, making it obviously not a
72
+ real FSM state name that would be used in domain logic.
73
+ """
74
+
75
+ __all__: list[str] = ["FROM_STATE_INITIAL"]
@@ -437,7 +437,12 @@ class ContractHandlerDiscovery:
437
437
  if hasattr(e, "model") and hasattr(e.model, "context"):
438
438
  context_dict = e.model.context
439
439
  if isinstance(context_dict, dict):
440
- error_code = context_dict.get("loader_error", error_code)
440
+ loader_error = context_dict.get("loader_error")
441
+ error_code = (
442
+ str(loader_error)
443
+ if loader_error is not None
444
+ else error_code
445
+ )
441
446
 
442
447
  errors.append(
443
448
  ModelDiscoveryError(