omnibase_infra 0.2.1__py3-none-any.whl → 0.2.2__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 (116) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +446 -0
  3. omnibase_infra/cli/commands.py +1 -1
  4. omnibase_infra/configs/widget_mapping.yaml +176 -0
  5. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +4 -1
  6. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +4 -1
  7. omnibase_infra/errors/error_compute_registry.py +4 -1
  8. omnibase_infra/errors/error_event_bus_registry.py +4 -1
  9. omnibase_infra/errors/error_infra.py +3 -1
  10. omnibase_infra/errors/error_policy_registry.py +4 -1
  11. omnibase_infra/handlers/handler_db.py +2 -1
  12. omnibase_infra/handlers/handler_graph.py +10 -5
  13. omnibase_infra/handlers/handler_mcp.py +736 -63
  14. omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
  15. omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
  16. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +301 -4
  17. omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
  18. omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
  19. omnibase_infra/mixins/mixin_node_introspection.py +24 -7
  20. omnibase_infra/mixins/mixin_retry_execution.py +1 -1
  21. omnibase_infra/models/handlers/__init__.py +10 -0
  22. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  23. omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
  24. omnibase_infra/models/mcp/__init__.py +15 -0
  25. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  26. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  27. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  28. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  29. omnibase_infra/models/registration/model_node_capabilities.py +11 -0
  30. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
  31. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
  32. omnibase_infra/nodes/effects/contract.yaml +0 -5
  33. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
  34. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
  35. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
  36. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
  37. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
  38. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
  39. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +46 -25
  40. omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
  41. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
  42. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +24 -19
  43. omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
  44. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
  45. omnibase_infra/plugins/plugin_compute_base.py +16 -2
  46. omnibase_infra/protocols/protocol_event_projector.py +1 -1
  47. omnibase_infra/runtime/__init__.py +51 -1
  48. omnibase_infra/runtime/binding_config_resolver.py +102 -37
  49. omnibase_infra/runtime/constants_notification.py +75 -0
  50. omnibase_infra/runtime/contract_handler_discovery.py +6 -1
  51. omnibase_infra/runtime/handler_bootstrap_source.py +514 -0
  52. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  53. omnibase_infra/runtime/handler_contract_source.py +289 -167
  54. omnibase_infra/runtime/handler_plugin_loader.py +4 -2
  55. omnibase_infra/runtime/mixin_semver_cache.py +25 -1
  56. omnibase_infra/runtime/mixins/__init__.py +7 -0
  57. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  58. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
  59. omnibase_infra/runtime/models/__init__.py +24 -0
  60. omnibase_infra/runtime/models/model_health_check_result.py +2 -1
  61. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  62. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  63. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  64. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  65. omnibase_infra/runtime/projector_plugin_loader.py +1 -1
  66. omnibase_infra/runtime/projector_shell.py +229 -1
  67. omnibase_infra/runtime/protocols/__init__.py +10 -0
  68. omnibase_infra/runtime/registry/registry_protocol_binding.py +3 -2
  69. omnibase_infra/runtime/registry_policy.py +9 -326
  70. omnibase_infra/runtime/secret_resolver.py +4 -2
  71. omnibase_infra/runtime/service_kernel.py +10 -2
  72. omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
  73. omnibase_infra/runtime/service_runtime_host_process.py +225 -15
  74. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  75. omnibase_infra/runtime/transition_notification_publisher.py +764 -0
  76. omnibase_infra/runtime/util_container_wiring.py +6 -5
  77. omnibase_infra/runtime/util_wiring.py +5 -1
  78. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  79. omnibase_infra/services/mcp/__init__.py +31 -0
  80. omnibase_infra/services/mcp/mcp_server_lifecycle.py +443 -0
  81. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  82. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  83. omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
  84. omnibase_infra/services/registry_api/__init__.py +40 -0
  85. omnibase_infra/services/registry_api/main.py +243 -0
  86. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  87. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  88. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  89. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  90. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  91. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  92. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  93. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  94. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  95. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  96. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  97. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  98. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  99. omnibase_infra/services/registry_api/routes.py +371 -0
  100. omnibase_infra/services/registry_api/service.py +846 -0
  101. omnibase_infra/services/service_capability_query.py +4 -4
  102. omnibase_infra/services/service_health.py +3 -2
  103. omnibase_infra/services/service_timeout_emitter.py +13 -2
  104. omnibase_infra/utils/util_dsn_validation.py +1 -1
  105. omnibase_infra/validation/__init__.py +3 -19
  106. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  107. omnibase_infra/validation/infra_validators.py +35 -24
  108. omnibase_infra/validation/validation_exemptions.yaml +113 -9
  109. omnibase_infra/validation/validator_chain_propagation.py +2 -2
  110. omnibase_infra/validation/validator_runtime_shape.py +1 -1
  111. omnibase_infra/validation/validator_security.py +473 -370
  112. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/METADATA +2 -2
  113. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/RECORD +116 -74
  114. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/WHEEL +0 -0
  115. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/entry_points.txt +0 -0
  116. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -131,10 +131,10 @@ class ServiceCapabilityQuery(MixinAsyncCircuitBreaker):
131
131
  Dependency Injection Pattern: This service is a leaf infrastructure
132
132
  service that receives its dependencies directly via constructor
133
133
  parameters rather than resolving them from a container. Unlike
134
- orchestrators that use ``container.resolve()`` to obtain services
135
- dynamically, leaf services like this one are instantiated with
136
- concrete dependencies (projection_reader, node_selector) and are
137
- themselves resolved by higher-level components.
134
+ orchestrators that use ``container.service_registry.resolve_service()``
135
+ to obtain services dynamically, leaf services like this one are
136
+ instantiated with concrete dependencies (projection_reader, node_selector)
137
+ and are themselves resolved by higher-level components.
138
138
  """
139
139
 
140
140
  def __init__(
@@ -57,10 +57,11 @@ See Also:
57
57
  from __future__ import annotations
58
58
 
59
59
  import logging
60
- from typing import TYPE_CHECKING, Literal
60
+ from typing import TYPE_CHECKING, Literal, cast
61
61
 
62
62
  from aiohttp import web
63
63
 
64
+ from omnibase_core.types import JsonType
64
65
  from omnibase_infra.enums import EnumInfraTransportType
65
66
  from omnibase_infra.errors import (
66
67
  ModelInfraErrorContext,
@@ -859,7 +860,7 @@ class ServiceHealth:
859
860
  response = ModelHealthCheckResponse.success(
860
861
  status=status,
861
862
  version=self._version,
862
- details=health_details,
863
+ details=cast("dict[str, JsonType]", health_details),
863
864
  )
864
865
 
865
866
  return web.Response(
@@ -36,6 +36,7 @@ from uuid import UUID
36
36
 
37
37
  from pydantic import BaseModel, ConfigDict, Field, field_validator
38
38
 
39
+ from omnibase_core.models.events.model_event_envelope import ModelEventEnvelope
39
40
  from omnibase_infra.enums import EnumInfraTransportType
40
41
  from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
41
42
  from omnibase_infra.models.projection import ModelRegistrationProjection
@@ -568,8 +569,13 @@ class ServiceTimeoutEmitter:
568
569
  },
569
570
  )
570
571
 
572
+ # Wrap event in ModelEventEnvelope for protocol compliance
573
+ envelope: ModelEventEnvelope[object] = ModelEventEnvelope(
574
+ payload=event,
575
+ correlation_id=correlation_id,
576
+ )
571
577
  await self._event_bus.publish_envelope(
572
- envelope=event,
578
+ envelope=envelope, # type: ignore[arg-type]
573
579
  topic=topic,
574
580
  )
575
581
 
@@ -657,8 +663,13 @@ class ServiceTimeoutEmitter:
657
663
  },
658
664
  )
659
665
 
666
+ # Wrap event in ModelEventEnvelope for protocol compliance
667
+ envelope: ModelEventEnvelope[object] = ModelEventEnvelope(
668
+ payload=event,
669
+ correlation_id=correlation_id,
670
+ )
660
671
  await self._event_bus.publish_envelope(
661
- envelope=event,
672
+ envelope=envelope, # type: ignore[arg-type]
662
673
  topic=topic,
663
674
  )
664
675
 
@@ -73,7 +73,7 @@ def _assert_postgres_scheme(scheme: str) -> Literal["postgresql", "postgres"]:
73
73
  parameter="scheme",
74
74
  value=scheme,
75
75
  )
76
- return cast(Literal["postgresql", "postgres"], scheme)
76
+ return cast("Literal['postgresql', 'postgres']", scheme)
77
77
 
78
78
 
79
79
  def parse_and_validate_dsn(dsn: object) -> ModelParsedDSN:
@@ -200,17 +200,8 @@ from omnibase_infra.validation.validator_runtime_shape import (
200
200
  enforce_execution_shape,
201
201
  )
202
202
 
203
- # Security validation for handler introspection and security constraints
204
- from omnibase_infra.validation.validator_security import (
205
- SENSITIVE_METHOD_PATTERNS,
206
- SENSITIVE_PARAMETER_NAMES,
207
- SecurityRuleId,
208
- convert_to_validation_error,
209
- has_sensitive_parameters,
210
- is_sensitive_method_name,
211
- validate_handler_security,
212
- validate_method_exposure,
213
- )
203
+ # Security validation (OMN-1277) - contract-driven validator
204
+ from omnibase_infra.validation.validator_security import ValidatorSecurity
214
205
 
215
206
  # Topic category validation for execution shape enforcement
216
207
  from omnibase_infra.validation.validator_topic_category import (
@@ -229,8 +220,6 @@ __all__: list[str] = [
229
220
  "EXECUTION_SHAPE_RULES", # Runtime shape validation rules
230
221
  "INFRA_MAX_UNIONS", # Infrastructure max union threshold
231
222
  "NODE_ARCHETYPE_EXPECTED_CATEGORIES", # Node archetype categories
232
- "SENSITIVE_METHOD_PATTERNS", # Security validation patterns
233
- "SENSITIVE_PARAMETER_NAMES", # Security validation names
234
223
  "TOPIC_CATEGORY_PATTERNS", # Topic category patterns
235
224
  "TOPIC_SUFFIXES", # Topic suffix constants
236
225
  # Errors
@@ -239,7 +228,6 @@ __all__: list[str] = [
239
228
  "RoutingCoverageError", # Routing coverage error (OMN-958)
240
229
  # Enums
241
230
  "EnumContractViolationSeverity", # Contract violation severity
242
- "SecurityRuleId", # Security rule identifiers
243
231
  # Models
244
232
  "ModelAnyTypeValidationResult", # Any type validation result (OMN-1276)
245
233
  "ModelContractLintResult", # Contract lint result
@@ -261,9 +249,9 @@ __all__: list[str] = [
261
249
  "TopicCategoryASTVisitor", # Topic category AST visitor
262
250
  "TopicCategoryValidator", # Topic category validator
263
251
  "ValidationAggregator", # Validation error aggregation (OMN-1091)
252
+ "ValidatorSecurity", # Contract-driven security validator (OMN-1277)
264
253
  # Functions
265
254
  "check_routing_coverage_ci", # CI routing coverage check
266
- "convert_to_validation_error", # Error conversion utility
267
255
  "detect_message_category", # Message category detection
268
256
  "discover_message_types", # Message type discovery
269
257
  "discover_registered_routes", # Route discovery
@@ -271,9 +259,7 @@ __all__: list[str] = [
271
259
  "enforce_execution_shape", # Execution shape enforcement
272
260
  "get_execution_shape_rules", # Get shape rules
273
261
  "get_validation_summary", # Get validation summary
274
- "has_sensitive_parameters", # Sensitive parameter check
275
262
  "is_isinstance_union", # Check if union is in isinstance() call
276
- "is_sensitive_method_name", # Sensitive method check
277
263
  "lint_contract_file", # Lint single contract file
278
264
  "lint_contracts_ci", # CI contract linting
279
265
  "lint_contracts_in_directory", # Directory contract linting
@@ -286,7 +272,6 @@ __all__: list[str] = [
286
272
  "validate_execution_shapes", # Execution shape validation
287
273
  "validate_execution_shapes_ci", # CI shape validation
288
274
  "validate_handler_registration", # Handler registration validation (OMN-1098)
289
- "validate_handler_security", # Handler security validation
290
275
  "validate_infra_all", # Infrastructure validation
291
276
  "validate_infra_architecture", # Infrastructure architecture
292
277
  "validate_infra_circular_imports", # Circular import check
@@ -298,7 +283,6 @@ __all__: list[str] = [
298
283
  "validate_localhandler_in_file", # LocalHandler file validation (OMN-743)
299
284
  "validate_message_chain", # Message chain validation
300
285
  "validate_message_on_topic", # Topic message validation
301
- "validate_method_exposure", # Method exposure validation
302
286
  "validate_patterns", # Re-export from omnibase_core
303
287
  "validate_routing_coverage_on_startup", # Startup routing check
304
288
  "validate_topic_categories_in_directory", # Directory topic validation
@@ -0,0 +1,114 @@
1
+ contract_kind: validation_subcontract
2
+ validation:
3
+ version:
4
+ major: 1
5
+ minor: 0
6
+ patch: 0
7
+ validator_id: security
8
+ validator_name: ONEX Security Validator
9
+ validator_description: |
10
+ Validates Python source files for security concerns in handler implementations.
11
+ Detects sensitive method names that should be private, credential exposure in
12
+ method signatures, and admin/internal operations exposed publicly.
13
+
14
+ This validator performs static AST analysis to identify:
15
+ - Public methods with sensitive names (get_password, get_secret, etc.)
16
+ - Method signatures containing sensitive parameter names
17
+ - Admin/internal methods exposed without underscore prefix
18
+ - Decrypt operations exposed publicly
19
+
20
+ Part of OMN-1277: Refactor validators to be Handler and contract-driven.
21
+ target_patterns:
22
+ - "**/*.py"
23
+ exclude_patterns:
24
+ - "**/node_modules/**"
25
+ - "**/.git/**"
26
+ - "**/venv/**"
27
+ - "**/.venv/**"
28
+ - "**/__pycache__/**"
29
+ - "**/archived/**"
30
+ - "**/tests/**"
31
+ - "**/*_test.py"
32
+ - "**/test_*.py"
33
+ - "**/conftest.py"
34
+ rules:
35
+ - rule_id: sensitive_method_exposed
36
+ description: |
37
+ Detects public methods with sensitive names that should be private.
38
+ Matches patterns: get_password, get_secret, get_token, get_api_key,
39
+ get_credential*, fetch_password, fetch_secret, fetch_token,
40
+ validate_password, check_password, verify_password.
41
+ severity: error
42
+ enabled: true
43
+ parameters:
44
+ patterns:
45
+ - "^get_password$"
46
+ - "^get_secret$"
47
+ - "^get_token$"
48
+ - "^get_api_key$"
49
+ - "^get_credential"
50
+ - "^fetch_password$"
51
+ - "^fetch_secret$"
52
+ - "^fetch_token$"
53
+ - "^validate_password$"
54
+ - "^check_password$"
55
+ - "^verify_password$"
56
+ - rule_id: credential_in_signature
57
+ description: |
58
+ Detects method signatures containing sensitive parameter names.
59
+ Matches parameters: password, secret, token, api_key, apikey,
60
+ access_key, private_key, credential, auth_token, bearer_token,
61
+ decrypt_key, encryption_key.
62
+ severity: error
63
+ enabled: true
64
+ parameters:
65
+ sensitive_params:
66
+ - "password"
67
+ - "secret"
68
+ - "token"
69
+ - "api_key"
70
+ - "apikey"
71
+ - "access_key"
72
+ - "private_key"
73
+ - "credential"
74
+ - "auth_token"
75
+ - "bearer_token"
76
+ - "decrypt_key"
77
+ - "encryption_key"
78
+ - rule_id: admin_method_public
79
+ description: |
80
+ Detects admin or internal methods exposed without underscore prefix.
81
+ Matches patterns: admin_*, internal_*.
82
+ severity: warning
83
+ enabled: true
84
+ parameters:
85
+ patterns:
86
+ - "^admin_"
87
+ - "^internal_"
88
+ - rule_id: decrypt_method_public
89
+ description: |
90
+ Detects decrypt operations exposed publicly.
91
+ Matches patterns: decrypt_*.
92
+ severity: warning
93
+ enabled: true
94
+ parameters:
95
+ patterns:
96
+ - "^decrypt_"
97
+ # ============================================================================
98
+ # LINE-BASED SUPPRESSION
99
+ # ============================================================================
100
+ # If ANY pattern below appears on a source line, ALL security violations
101
+ # on that line are suppressed.
102
+ #
103
+ # Usage example:
104
+ # def get_password(self) -> str: # ONEX_EXCLUDE: security - required for legacy API
105
+ # def admin_reset(self) -> None: # security-ok: admin endpoint with auth guard
106
+ # ============================================================================
107
+ suppression_comments:
108
+ - "# ONEX_EXCLUDE: security"
109
+ - "# security-ok:"
110
+ severity_default: error
111
+ fail_on_error: true
112
+ fail_on_warning: false
113
+ max_violations: 0
114
+ parallel_execution: true
@@ -37,10 +37,12 @@ from typing import TypedDict
37
37
  import yaml
38
38
 
39
39
  from omnibase_core.models.common import ModelValidationMetadata
40
+ from omnibase_core.models.primitives import ModelSemVer
40
41
  from omnibase_core.models.validation.model_union_pattern import ModelUnionPattern
41
42
  from omnibase_core.validation import (
42
43
  CircularImportValidator,
43
44
  ModelContractValidationResult,
45
+ ModelImportValidationResult,
44
46
  ModelModuleImportResult,
45
47
  ModelValidationResult,
46
48
  validate_architecture,
@@ -423,7 +425,11 @@ INFRA_NODES_PATH = "src/omnibase_infra/nodes/"
423
425
  # (+2 unions for EnumPolicyType | str in validate_policy_type_value)
424
426
  # (-1 union: fix PolicyTypeInput validator coercion, changed return
425
427
  # type from str | EnumPolicyType to EnumPolicyType)
426
- INFRA_MAX_UNIONS = 96
428
+ # - 98 (2026-01-20): OMN-1277 security validator contract refactoring (+2 unions)
429
+ # ast.FunctionDef | ast.AsyncFunctionDef for AST method type checking
430
+ # - 105 (2026-01-21): Contract-driven handler config loading (+4 unions)
431
+ # ModelHandlerContract transport config fields and lifecycle types
432
+ INFRA_MAX_UNIONS = 105
427
433
 
428
434
  # Maximum allowed architecture violations in infrastructure code.
429
435
  # Set to 0 (strict enforcement) to ensure one-model-per-file principle is always followed.
@@ -747,13 +753,14 @@ def validate_infra_contract_deep(
747
753
  return result
748
754
 
749
755
  # If result is a different type, wrap it in ModelContractValidationResult
750
- # Default to passed=False for unknown result types to avoid silently masking failures
751
- # Check 'passed' first, then 'is_valid' as fallback (some validators use is_valid)
756
+ # Default to is_valid=False for unknown result types to avoid silently masking failures
757
+ # Check 'is_valid' first, then 'passed' as fallback (some validators use passed)
752
758
  return ModelContractValidationResult(
753
- passed=getattr(result, "passed", getattr(result, "is_valid", False)),
759
+ is_valid=getattr(result, "is_valid", getattr(result, "passed", False)),
754
760
  score=getattr(result, "score", 0.0),
755
- errors=getattr(result, "errors", []),
761
+ violations=getattr(result, "violations", getattr(result, "errors", [])),
756
762
  warnings=getattr(result, "warnings", []),
763
+ interface_version=ModelSemVer(major=1, minor=0, patch=0),
757
764
  )
758
765
 
759
766
 
@@ -1318,29 +1325,33 @@ def validate_infra_union_usage(
1318
1325
  # Note: ModelValidationMetadata uses extra="allow", so extension fields
1319
1326
  # are accepted as int values.
1320
1327
  # See docstring "Metadata Extension Fields" section for field documentation.
1328
+ #
1329
+ # Extension fields are passed via model_construct() to satisfy type checker
1330
+ # while preserving runtime behavior with extra="allow".
1331
+ metadata_fields: dict[str, object] = {
1332
+ # Standard ModelValidationMetadata fields (formally defined)
1333
+ "validation_type": "union_usage",
1334
+ "files_processed": files_processed,
1335
+ "violations_found": len(filtered_issues),
1336
+ "total_unions": total_count, # Base field: all unions found
1337
+ "max_unions": max_unions, # Base field: configured threshold
1338
+ "strict_mode": strict, # Base field: whether strict mode enabled
1339
+ # Extension fields (via extra="allow", typed as int)
1340
+ # These provide transparency into the exclusion logic:
1341
+ "non_optional_unions": threshold_count, # What threshold actually checks
1342
+ "optional_unions_excluded": optional_count, # X | None patterns
1343
+ "isinstance_unions_excluded": isinstance_count, # isinstance(x, A | B) patterns
1344
+ }
1321
1345
  return ModelValidationResult(
1322
1346
  is_valid=is_valid,
1323
1347
  errors=filtered_issues,
1324
- metadata=ModelValidationMetadata(
1325
- # Standard ModelValidationMetadata fields (formally defined)
1326
- validation_type="union_usage",
1327
- files_processed=files_processed,
1328
- violations_found=len(filtered_issues),
1329
- total_unions=total_count, # Base field: all unions found
1330
- max_unions=max_unions, # Base field: configured threshold
1331
- strict_mode=strict, # Base field: whether strict mode enabled
1332
- # Extension fields (via extra="allow", typed as int)
1333
- # These provide transparency into the exclusion logic:
1334
- non_optional_unions=threshold_count, # What threshold actually checks
1335
- optional_unions_excluded=optional_count, # X | None patterns
1336
- isinstance_unions_excluded=isinstance_count, # isinstance(x, A | B) patterns
1337
- ),
1348
+ metadata=ModelValidationMetadata.model_construct(**metadata_fields), # type: ignore[arg-type]
1338
1349
  )
1339
1350
 
1340
1351
 
1341
1352
  def validate_infra_circular_imports(
1342
1353
  directory: PathInput = INFRA_SRC_PATH,
1343
- ) -> ModelModuleImportResult:
1354
+ ) -> ModelImportValidationResult:
1344
1355
  """
1345
1356
  Check for circular imports in infrastructure code.
1346
1357
 
@@ -1351,7 +1362,7 @@ def validate_infra_circular_imports(
1351
1362
  directory: Directory to check. Defaults to infrastructure source.
1352
1363
 
1353
1364
  Returns:
1354
- ModelModuleImportResult with detailed import validation results.
1365
+ ModelImportValidationResult with detailed import validation results.
1355
1366
  Use result.has_circular_imports to check for issues.
1356
1367
  """
1357
1368
  validator = CircularImportValidator(source_path=Path(directory))
@@ -1361,7 +1372,7 @@ def validate_infra_circular_imports(
1361
1372
  def validate_infra_all(
1362
1373
  directory: PathInput = INFRA_SRC_PATH,
1363
1374
  nodes_directory: PathInput = INFRA_NODES_PATH,
1364
- ) -> dict[str, ValidationResult | ModelModuleImportResult]:
1375
+ ) -> dict[str, ValidationResult | ModelImportValidationResult]:
1365
1376
  """
1366
1377
  Run all validations on infrastructure code.
1367
1378
 
@@ -1379,7 +1390,7 @@ def validate_infra_all(
1379
1390
  Returns:
1380
1391
  Dictionary mapping validator name to result.
1381
1392
  """
1382
- results: dict[str, ValidationResult | ModelModuleImportResult] = {}
1393
+ results: dict[str, ValidationResult | ModelImportValidationResult] = {}
1383
1394
 
1384
1395
  # HIGH priority validators
1385
1396
  results["architecture"] = validate_infra_architecture(directory)
@@ -1394,7 +1405,7 @@ def validate_infra_all(
1394
1405
 
1395
1406
 
1396
1407
  def get_validation_summary(
1397
- results: dict[str, ValidationResult | ModelModuleImportResult],
1408
+ results: dict[str, ValidationResult | ModelImportValidationResult],
1398
1409
  ) -> dict[str, int | list[str]]:
1399
1410
  """
1400
1411
  Generate a summary of validation results.
@@ -486,6 +486,47 @@ pattern_exemptions:
486
486
  - CLAUDE.md (Registry Naming Conventions)
487
487
  ticket: OMN-953
488
488
  # ==========================================================================
489
+ # TransitionNotificationPublisher publisher_id Exemption (OMN-1139)
490
+ # ==========================================================================
491
+ # publisher_id is a human-readable semantic identifier similar to scheduler_id.
492
+ # It identifies the publisher instance and may be auto-generated or user-specified.
493
+ - file_pattern: 'model_transition_notification_publisher_metrics\.py'
494
+ violation_pattern: "Field 'publisher_id' should use UUID"
495
+ reason: >
496
+ publisher_id is a human-readable semantic identifier (e.g., "transition-publisher-<uuid>") that identifies the publisher instance. Follows the same pattern as scheduler_id.
497
+
498
+ documentation:
499
+ - CLAUDE.md (Registry Naming Conventions)
500
+ ticket: OMN-1139
501
+ # ==========================================================================
502
+ # ModelTransitionNotificationOutboxMetrics table_name Exemption (OMN-1139)
503
+ # ==========================================================================
504
+ # table_name is the database table name string, not an entity reference.
505
+ - file_pattern: 'model_transition_notification_outbox_metrics\.py'
506
+ violation_pattern: "Field 'table_name' might reference an entity"
507
+ reason: >
508
+ table_name is the database table name string (e.g., "transition_notification_outbox"), not an entity reference. It identifies the outbox table for metrics reporting and does not require ID + display_name pattern.
509
+
510
+ documentation:
511
+ - CLAUDE.md (Registry Naming Conventions)
512
+ ticket: OMN-1139
513
+ # ==========================================================================
514
+ # TransitionNotificationOutbox Method Count Exemption (OMN-1139)
515
+ # ==========================================================================
516
+ # Outbox pattern requires lifecycle (start/stop), storage (store/cleanup),
517
+ # processing (process_pending), and multiple config properties for observability.
518
+ # NOTE: Pattern anchored with src/omnibase_infra/runtime/ prefix to match only
519
+ # the production file, not test files.
520
+ - file_pattern: 'src/omnibase_infra/runtime/transition_notification_outbox\.py'
521
+ class_pattern: "Class 'TransitionNotificationOutbox'"
522
+ violation_pattern: 'has \d+ methods'
523
+ reason: >
524
+ Transactional outbox pattern inherently requires: lifecycle methods (start/stop), storage operations (store/cleanup_processed), processing (process_pending), background loop (_processor_loop), metrics (get_metrics), and configuration properties (table_name, batch_size, poll_interval, is_running, shutdown_timeout, notifications_stored/processed/failed, strict_transaction_mode). Method count is driven by pattern requirements, not poor design.
525
+
526
+ documentation:
527
+ - src/omnibase_infra/schemas/schema_transition_notification_outbox.sql
528
+ ticket: OMN-1139
529
+ # ==========================================================================
489
530
  # ServiceTimeoutEmitter/Scanner Naming Exemptions (OMN-1055)
490
531
  # ==========================================================================
491
532
  # These service classes follow the CLAUDE.md Service<Name> naming convention.
@@ -1032,6 +1073,27 @@ pattern_exemptions:
1032
1073
  - CLAUDE.md (ONEX Architecture - Handler Types)
1033
1074
  ticket: OMN-1097
1034
1075
  # ==========================================================================
1076
+ # Handler Bootstrap Source Exemptions (OMN-1087)
1077
+ # ==========================================================================
1078
+ # HandlerBootstrapSource provides hardcoded handler descriptors for core
1079
+ # infrastructure handlers. The "Handler" in the name refers to ONEX handler
1080
+ # concepts, consistent with HandlerContractSource and HandlerPluginLoader.
1081
+ #
1082
+ # Pattern specificity note: Uses separate class_pattern + violation_pattern
1083
+ # (not combined "Class name .* contains...") for precise targeting. All three
1084
+ # patterns must match: exact filename, exact class name, and violation type.
1085
+ # This is consistent with HandlerContractSource/HandlerPluginLoader exemptions.
1086
+ - file_pattern: 'handler_bootstrap_source\.py'
1087
+ class_pattern: "Class name 'HandlerBootstrapSource'"
1088
+ violation_pattern: "contains anti-pattern 'Handler'"
1089
+ reason: >
1090
+ HandlerBootstrapSource centralizes hardcoded handler registration for core infrastructure handlers (Consul, DB, HTTP, Vault). The "Handler" refers to ONEX handler concepts, consistent with HandlerContractSource. This is legitimate handler infrastructure code per OMN-1087.
1091
+
1092
+ documentation:
1093
+ - docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
1094
+ - CLAUDE.md (ONEX Architecture - Handler Types)
1095
+ ticket: OMN-1087
1096
+ # ==========================================================================
1035
1097
  # Handler Plugin Loader Exemptions (OMN-1132)
1036
1098
  # ==========================================================================
1037
1099
  # HandlerPluginLoader discovers and loads ONEX handlers from contract YAML files.
@@ -1456,16 +1518,17 @@ pattern_exemptions:
1456
1518
  # ProjectorShell Exemptions (OMN-1169)
1457
1519
  # ==========================================================================
1458
1520
  # ProjectorShell implements ProtocolEventProjector protocol which requires:
1459
- # - Protocol properties (projector_id, aggregate_type, consumed_events, is_placeholder)
1460
- # - Core methods (project, get_state)
1461
- # - SQL execution modes (_upsert, _insert, _append, _execute_projection)
1462
- # - Value extraction (_extract_values, _resolve_path, _get_event_type)
1463
- # - Utility (_parse_row_count)
1464
- # Total: 11 methods, all cohesive to the projector contract pattern.
1465
- # NOTE: Pattern anchored with omnibase_infra/runtime/ prefix and $ suffix to
1466
- # match only the production file at src/omnibase_infra/runtime/projector_shell.py.
1521
+ # - Protocol properties (projector_id, aggregate_type, consumed_events, contract, is_placeholder)
1522
+ # - Core methods (project, get_state, get_states, partial_update, upsert_partial)
1523
+ # - SQL execution modes handled by MixinProjectorSqlOperations
1524
+ # - Value extraction (_extract_values, _resolve_path, _get_event_type, _execute_projection)
1525
+ # - Notification integration (via MixinProjectorNotificationPublishing)
1526
+ # Total: 11 methods, all cohesive to the contract-driven projector pattern.
1527
+ # OMN-1139: Added notification publishing support via mixin composition.
1528
+ # NOTE: Pattern anchored with src/omnibase_infra/runtime/ prefix to match only
1529
+ # the production file at src/omnibase_infra/runtime/projector_shell.py.
1467
1530
  # This prevents matching test files in tests/unit/runtime/ or tests/integration/runtime/.
1468
- - file_pattern: 'omnibase_infra/runtime/projector_shell\.py$'
1531
+ - file_pattern: 'src/omnibase_infra/runtime/projector_shell\.py'
1469
1532
  class_pattern: "Class 'ProjectorShell'"
1470
1533
  violation_pattern: 'has \d+ methods'
1471
1534
  reason: >
@@ -1631,6 +1694,47 @@ pattern_exemptions:
1631
1694
  documentation:
1632
1695
  - docs/patterns/binding_config_resolver.md
1633
1696
  ticket: OMN-765
1697
+ # ==========================================================================
1698
+ # HandlerMCP Method Count Exemption (OMN-1282)
1699
+ # ==========================================================================
1700
+ # MCP handler is a complex infrastructure component requiring many methods:
1701
+ # - 4 ProtocolHandler methods (initialize, shutdown, execute, health_check)
1702
+ # - 2 factory methods for route handlers (_create_health_endpoint, _create_tools_list_endpoint)
1703
+ # - 2 server lifecycle methods (_start_http_server, _stop_http_server)
1704
+ # - 3 execution/operation methods (_run_server, _execute_tool_operation, _execute_describe)
1705
+ # Complexity is inherent to MCP protocol requirements and uvicorn server management.
1706
+ - file_pattern: 'handler_mcp\.py'
1707
+ class_pattern: "Class 'HandlerMCP'"
1708
+ violation_pattern: 'has \d+ methods'
1709
+ reason: >
1710
+ HandlerMCP is a complex infrastructure handler implementing MCP protocol with internal uvicorn server lifecycle. The 11 methods are cohesive: 4 ProtocolHandler interface methods, 2 factory methods for Starlette route handlers (explicit closure capture), 2 server lifecycle methods (start/stop), and 3 operation methods. Splitting would fragment the unified MCP server abstraction.
1711
+
1712
+ documentation:
1713
+ - CLAUDE.md (Handler Plugin Loader Patterns)
1714
+ ticket: OMN-1282
1715
+ # ==========================================================================
1716
+ # Registry API Models - service_name Exemptions (OMN-1278)
1717
+ # ==========================================================================
1718
+ # service_name in registry API models is the Consul service name for discovery,
1719
+ # NOT an entity reference. It's an external Consul identifier.
1720
+ - file_pattern: 'services/registry_api/models/model_registry_node_view\.py'
1721
+ violation_pattern: "Field 'service_name' might reference an entity"
1722
+ reason: >
1723
+ service_name is the Consul service name for service discovery (e.g., "onex-effect-abc123"). This is an external Consul identifier, NOT an ONEX entity reference. The node_id field provides the UUID identifier; service_name is how the node appears in Consul's catalog.
1724
+
1725
+ documentation:
1726
+ - CLAUDE.md (Type Annotation Conventions)
1727
+ - Consul service discovery documentation
1728
+ ticket: OMN-1278
1729
+ - file_pattern: 'services/registry_api/models/model_registry_instance_view\.py'
1730
+ violation_pattern: "Field 'service_name' might reference an entity"
1731
+ reason: >
1732
+ service_name is the Consul service name for live instance discovery (e.g., "my-service"). This is an external Consul identifier, NOT an ONEX entity reference. The node_id and service_id fields provide UUID identifiers; service_name is the Consul catalog name.
1733
+
1734
+ documentation:
1735
+ - CLAUDE.md (Type Annotation Conventions)
1736
+ - Consul service discovery documentation
1737
+ ticket: OMN-1278
1634
1738
  # Architecture validator exemptions
1635
1739
  # These handle one-model-per-file violations for domain-grouped protocols
1636
1740
  architecture_exemptions:
@@ -183,7 +183,7 @@ def get_message_id(envelope: ModelEventEnvelope[object]) -> UUID:
183
183
  """
184
184
  # envelope_id is typed as UUID in ModelEventEnvelope, but mypy sees it as Any
185
185
  # due to the generic type parameter. Cast is required for type safety.
186
- return cast(UUID, envelope.envelope_id)
186
+ return cast("UUID", envelope.envelope_id)
187
187
 
188
188
 
189
189
  def get_correlation_id(envelope: ModelEventEnvelope[object]) -> UUID | None:
@@ -200,7 +200,7 @@ def get_correlation_id(envelope: ModelEventEnvelope[object]) -> UUID | None:
200
200
  correlation_id = envelope.correlation_id
201
201
  if correlation_id is None:
202
202
  return None
203
- return cast(UUID, correlation_id)
203
+ return cast("UUID", correlation_id)
204
204
 
205
205
 
206
206
  def get_causation_id(envelope: ModelEventEnvelope[object]) -> UUID | None:
@@ -893,7 +893,7 @@ def enforce_execution_shape(node_archetype: EnumNodeArchetype) -> Callable[[F],
893
893
 
894
894
  # Cast wrapper to F - functools.wraps preserves the signature at runtime,
895
895
  # and mypy cannot prove the equivalence, so we use an explicit cast.
896
- return cast(F, wrapper)
896
+ return cast("F", wrapper)
897
897
 
898
898
  return decorator
899
899