Repository navigation
feat(runtime): implement ContractHandlerDiscovery for contract-based handler registration [OMN-1133] - #142
Conversation
…handler registration [OMN-1133] Add ContractHandlerDiscovery service that discovers handlers from contract files and auto-registers them with the runtime, eliminating the need for manual wiring. New components: - ContractHandlerDiscovery: Bridges HandlerPluginLoader and BindingRegistry - ProtocolHandlerDiscovery: Runtime-checkable protocol for discovery services - ModelDiscoveryResult: Tracks discovered/registered handlers with errors/warnings - ModelDiscoveryError/ModelDiscoveryWarning: Structured error/warning tracking RuntimeHostProcess integration: - Added contract_paths parameter to __init__ - Auto-discovers handlers on start() if paths provided - Falls back to wire_default_handlers() when no paths given - Graceful degradation: discovery errors logged but don't block startup Test coverage: - 15 unit tests for ContractHandlerDiscovery - 14 integration tests for RuntimeHostProcess discovery
📝 WalkthroughWalkthroughAdds contract-based handler discovery: new discovery models and protocol, a ContractHandlerDiscovery implementation, runtime startup integration using contract paths (optional), validation exemptions, and extensive unit and integration tests for discovery and host behavior. Changes
Sequence Diagram(s)sequenceDiagram
participant RTH as RuntimeHostProcess
participant CHD as ContractHandlerDiscovery
participant PHL as ProtocolHandlerPluginLoader
participant HReg as ProtocolBindingRegistry
participant MDR as ModelDiscoveryResult
RTH->>CHD: discover_and_register(contract_paths, correlation_id?)
activate CHD
loop per contract path
CHD->>PHL: load_from_directory / load_from_contract(path)
activate PHL
PHL-->>CHD: handler metadata (class paths, names) / warnings
deactivate PHL
CHD->>CHD: _import_handler_class(class_path, correlation_id)
CHD->>HReg: register(handler_name, handler_class)
activate HReg
HReg-->>CHD: registration result / errors
deactivate HReg
CHD->>CHD: collect counts, errors, warnings
end
CHD-->>RTH: ModelDiscoveryResult (handlers_discovered, handlers_registered, errors, warnings, discovered_at)
deactivate CHD
RTH->>RTH: log results, cache last result, continue startup
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Comment |
Code Review: Contract-Based Handler Discovery (OMN-1133)This PR successfully implements contract-based handler discovery for ONEX infrastructure. The implementation is well-architected, thoroughly tested, and follows ONEX patterns consistently. Strengths
Issues FoundCRITICAL: Exception Leak in Error Handler (line 416)The catch-all exception handler at contract_handler_discovery.py:416 calls path.is_file() inside the except block, which could raise another OSError during error handling. This should be wrapped in a nested try/except or use cached path type. Severity: Medium - could cause discovery to fail if filesystem errors occur during exception handling. MINOR: Unused ImportInfraConnectionError appears to be imported but unused. Handler import errors are caught as ImportError, not InfraConnectionError. Verify if the InfraConnectionError handler at lines 393-408 is actually needed. Severity: Low - doesn't affect functionality but dead code should be cleaned up. Test CoverageExcellent coverage of happy paths, error conditions, and edge cases. Tests properly isolate handlers requiring external services by using HttpRestHandler for most scenarios. Security & PerformanceSecurity model is sound - inherits controls from HandlerPluginLoader (YAML safe loading, file size limits, protocol validation). Performance characteristics are appropriate for startup-time operations. ONEX Pattern AlignmentAll patterns PASS: Strong Typing, Container DI, Protocol Resolution, OnexError Only, Correlation IDs, Graceful Degradation. Final VerdictAPPROVE with minor fix required This is an excellent implementation. The critical issue (exception leak) is straightforward to fix and doesn't affect core architecture. Once addressed, this PR is ready to merge. Overall Quality: 5/5 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (1)
125-144: Consider adding assertion for error presence in mixed valid/invalid test.The test comment mentions "There may be errors from the invalid contracts" but doesn't assert on
result.has_errors. Consider adding explicit verification:# If invalid contracts produce errors, verify they're captured # assert result.has_errors or result.handlers_discovered == 1This would make the test behavior more explicit regarding whether invalid contracts are expected to produce errors in the result.
src/omnibase_infra/runtime/runtime_host_process.py (1)
957-1001: Consider caching discovery result for observability.The
discovery_resultfromdiscover_and_register()contains valuable information (discovered/registered counts, errors, warnings) that could be useful for:
- Health check enrichment
- Debugging startup issues
- Metrics/monitoring
Consider storing the result for later access:
💡 Suggested enhancement
+ # Store discovery result for observability (could be accessed via health_check) + self._last_discovery_result: ModelDiscoveryResult | None = None + # Discover and register handlers from contract paths discovery_result = await self._handler_discovery.discover_and_register( contract_paths=self._contract_paths, ) + self._last_discovery_result = discovery_resulttests/integration/runtime/test_runtime_host_handler_discovery.py (1)
356-386: Potential test pollution from singleton registry usage.Lines 376-378 use
get_handler_registry()(singleton) to verify default handlers. SinceRuntimeHostProcessmay register to the singleton when nohandler_registryis provided, this could cause test pollution if tests run in a specific order.Consider using the
isolated_handler_registryfixture and passing it toRuntimeHostProcess:💡 Suggested fix to avoid test pollution
@pytest.mark.asyncio - async def test_fallback_to_default_handlers(self) -> None: + async def test_fallback_to_default_handlers( + self, + isolated_handler_registry: ProtocolBindingRegistry, + ) -> None: """RuntimeHostProcess uses wire_default_handlers when no contract_paths.""" event_bus = InMemoryEventBus() process = RuntimeHostProcess( event_bus=event_bus, input_topic="test.input", + handler_registry=isolated_handler_registry, # No contract_paths - should use wire_default_handlers ) try: await process.start() # Verify default handlers are available - registry = get_handler_registry() - assert registry.is_registered(HANDLER_TYPE_HTTP) - assert registry.is_registered(HANDLER_TYPE_DATABASE) + assert isolated_handler_registry.is_registered(HANDLER_TYPE_HTTP) + assert isolated_handler_registry.is_registered(HANDLER_TYPE_DATABASE)src/omnibase_infra/runtime/contract_handler_discovery.py (3)
1-85: Docstring/examples look slightly stale (names don’t match actual protocol types).The example imports
HandlerPluginLoader/get_handler_registry, but this module type-hintsProtocolHandlerPluginLoader/ProtocolBindingRegistry. Consider aligning the example to the real public API to avoid misleading copy/paste.
137-217:async defwraps fully synchronous I/O/import work (likely blocks the event loop).
discover_and_register()performs filesystem checks, YAML loading (via loader), imports, and registry mutations synchronously. If this runs on an asyncio event loop (e.g., RuntimeHostProcess startup), it can stall other tasks.Consider either:
- making the API synchronous, or
- offloading the heavy sync work (
load_from_*, maybe imports) viaasyncio.to_thread.
289-363: Import failures aren’t consistently classified as “import” errors (AttributeError/TypeError leak into generic bucket).
_import_handler_class()can raiseAttributeError,ValueError,TypeError, but the caller only treatsImportErrorspecially; the rest becomeREGISTRATION_UNEXPECTED_ERROR, which is noisy and hides the common “class not found / not a class” cases.Consider normalizing
_import_handler_class()to raiseImportErrorfor “module/class resolution” problems so the caller’sexcept ImportErrorpath reliably captures them.Proposed fix
def _import_handler_class( @@ - if "." not in class_path: - raise ValueError( - f"Invalid class path '{class_path}': must be fully qualified " - "(e.g., 'myapp.handlers.AuthHandler')" - ) + if "." not in class_path: + raise ImportError( + f"Invalid class path {class_path!r}: must be fully qualified " + "(e.g., 'myapp.handlers.AuthHandler')" + ) module_path, class_name = class_path.rsplit(".", 1) - module = importlib.import_module(module_path) - handler_class = getattr(module, class_name) + try: + module = importlib.import_module(module_path) + except ModuleNotFoundError as e: + raise ImportError(f"Module not found: {module_path!r}") from e + + try: + handler_class = getattr(module, class_name) + except AttributeError as e: + raise ImportError( + f"Class not found: {class_name!r} in module {module_path!r}" + ) from e # Verify it's actually a class (also serves as type narrowing for mypy) if not isinstance(handler_class, type): - raise TypeError( - f"'{class_path}' is not a class (got {type(handler_class).__name__})" - ) + raise ImportError( + f"{class_path!r} is not a class (got {type(handler_class).__name__})" + )Also applies to: 448-514
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Lite
📒 Files selected for processing (13)
src/omnibase_infra/models/runtime/__init__.pysrc/omnibase_infra/models/runtime/model_discovery_error.pysrc/omnibase_infra/models/runtime/model_discovery_result.pysrc/omnibase_infra/models/runtime/model_discovery_warning.pysrc/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/runtime/protocol_handler_discovery.pysrc/omnibase_infra/runtime/runtime_host_process.pysrc/omnibase_infra/validation/validation_exemptions.yamltests/integration/runtime/test_runtime_host_handler_discovery.pytests/unit/runtime/contract_handler_discovery/__init__.pytests/unit/runtime/contract_handler_discovery/conftest.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Never useAnytype - useobjectfor generic payloads in function parameters and return types
UseX | None(PEP 604) instead ofOptional[X]for nullable types
All services must useModelONEXContainerfor dependency injection via__init__(self, container: ModelONEXContainer)
Use@allow_anydecorator with documented reason as exemption mechanism forAnytype violations
UseJsonTypefromomnibase_core.typesas the canonical type alias for JSON-compatible values
UseModelEventEnvelope[object]for generic dispatcher interfaces andobjectfor generic payloads
Infrastructure error handling must useOnexErrorbase class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
UseInfraConnectionError,InfraTimeoutError,InfraAuthenticationError,InfraUnavailableErrorfor transport failures with properModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated withuuid4()if missing, and included in all error contexts
External service adapters must implementMixinAsyncCircuitBreakerwith appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never useisinstancechecks
Files:
tests/unit/runtime/contract_handler_discovery/__init__.pysrc/omnibase_infra/models/runtime/model_discovery_warning.pysrc/omnibase_infra/models/runtime/model_discovery_error.pysrc/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/models/runtime/model_discovery_result.pysrc/omnibase_infra/runtime/runtime_host_process.pytests/unit/runtime/contract_handler_discovery/conftest.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.pysrc/omnibase_infra/runtime/protocol_handler_discovery.pysrc/omnibase_infra/models/runtime/__init__.pytests/integration/runtime/test_runtime_host_handler_discovery.py
**/model_*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/model_*.py: File naming convention: Models must usemodel_<name>.pywith class nameModel<Name>
Each model file must contain exactly oneModel*class
Pydantic workaround forAnytype must include# NOTE:comment documenting the reason when technically required
UseSerializeAsAnytype wrapper for Pydantic fields containing complex nested models to preserve subclass fields during serialization
Files:
src/omnibase_infra/models/runtime/model_discovery_warning.pysrc/omnibase_infra/models/runtime/model_discovery_error.pysrc/omnibase_infra/models/runtime/model_discovery_result.py
**/model_*result*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Result models may override
__bool__to enable idiomatic conditional checks, with requiredWarningsection in docstring explaining non-standard behavior
Files:
src/omnibase_infra/models/runtime/model_discovery_result.py
**/protocol_*.py
📄 CodeRabbit inference engine (CLAUDE.md)
File naming convention: Protocols must use
protocol_<name>.pyorprotocols.pywith class nameProtocol<Name>
Files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
🧠 Learnings (42)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
📚 Learning: 2026-01-06T17:57:00.677Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.677Z
Learning: Applies to src/omnibase_spi/**/*.py : SPI MUST NOT define Pydantic models; all `BaseModel` classes must be defined in omnibase_core
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/v[0-9]_[0-9]_[0-9]/models/*.py : All Pydantic models in models/ directory must be auto-generated from contract.yaml; never hand-write models
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Applies to shared/models/model_*.py : Use `model_*` prefix for Pydantic models in `shared/models/` directory
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/models/model_contract_*.py : Models must be auto-generated from contract.yaml files, never hand-written
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.py
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/models/model_contract_*.py : All ONEX node auto-generated Pydantic models must be organized in a `models/` directory with files for state.py, model_contract_actions.py, model_contract_models.py, model_contract_validation.py, model_contract_cli.py (optional), model_contract_capabilities.py (optional), and error_codes.py, generated from the corresponding contract definitions
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.pysrc/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/v[0-9]_[0-9]_[0-9]/models/*.py : All Pydantic models must use Field() with description for all properties; never use inline type hints without Field()
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Organize models under `src/omnibase_core/models/` by domain including: base, cli, common, config, core, contracts, discovery, health, infrastructure, logging, metadata, nodes, operations, results, security, service, tools, validation, and workflows
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_warning.pysrc/omnibase_infra/models/runtime/__init__.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use structured error handling with ModelOnexError and EnumCoreErrorCode, never generic Exception - provide message, error_code, and context
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_error.py
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/models/error_codes.py : All ONEX node error handling must use auto-generated error codes defined in `models/error_codes.py` from contract definitions
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_error.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Use `EnumCoreErrorCode` with `ModelOnexError` for proper error code usage
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_error.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/runtime/runtime_host_process.pytests/unit/runtime/contract_handler_discovery/conftest.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.pysrc/omnibase_infra/validation/validation_exemptions.yamlsrc/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/runtime/runtime_host_process.pytests/unit/runtime/contract_handler_discovery/conftest.pysrc/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handler contract files must declare handler routing with `routing_strategy`, event models, handler classes, and handler modules
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/*.py : Import protocols from `omnibase.protocol.protocol_*` paths
Applied to files:
src/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/runtime/runtime_host_process.pysrc/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/*.py : Import protocols from `omnibase.protocol.protocol_<name>` module paths
Applied to files:
src/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/runtime/runtime_host_process.pysrc/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-06T17:57:00.677Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.677Z
Learning: Applies to src/omnibase_spi/protocols/handlers/**/*.py : Protocol naming convention: Handler protocols must follow `Protocol{Type}Handler` pattern
Applied to files:
src/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/validation/validation_exemptions.yamlsrc/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Use Protocol for tool interfaces and plugin APIs based on method shape (structural typing), not Pydantic models with inheritance
Applied to files:
src/omnibase_infra/runtime/__init__.pysrc/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/model_*result*.py : Result models may override `__bool__` to enable idiomatic conditional checks, with required `Warning` section in docstring explaining non-standard behavior
Applied to files:
src/omnibase_infra/models/runtime/model_discovery_result.py
📚 Learning: 2025-11-24T16:32:55.606Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: Node contracts must be validated using `ModelCounter` from `omnibase_core.validation.architecture`
Applied to files:
src/omnibase_infra/runtime/runtime_host_process.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Applies to nodes/**/*.py : Use `omnibase_infra` handlers for OmniIntelligence queries via HttpRestAdapter envelope pattern
Applied to files:
src/omnibase_infra/runtime/runtime_host_process.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_*.py : Handlers must NOT have direct event bus access - only orchestrators may have bus parameters and publish events
Applied to files:
src/omnibase_infra/runtime/runtime_host_process.py
📚 Learning: 2025-11-24T16:33:51.604Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T16:33:51.604Z
Learning: Applies to tests/**/conftest.py : Test fixtures must be defined in `conftest.py` and should provide reusable sample data, UUIDs, semantic versions, and model data
Applied to files:
tests/unit/runtime/contract_handler_discovery/conftest.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to tests/**/*.py : Write comprehensive test coverage following the test structure under `tests/unit/` organized by subsystem (enums, models, mixins, utils)
Applied to files:
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contracts/contract_*.yaml : All ONEX node contract definitions must use the new subcontract architecture pattern, breaking down complex contracts into separate contract_actions.yaml, contract_models.yaml, contract_validation.yaml, and optional contract_cli.yaml and contract_capabilities.yaml files for separation of concerns, maintainability, reusability, modularity, and future tool-as-a-service readiness
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contracts/contract_capabilities.yaml : All ONEX node execution capability definitions, if applicable, must be included in contract_capabilities.yaml with supported_node_types, supported_delivery_modes, and performance_constraints specifications
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contract.yaml : All ONEX node contract definitions must support optional documents pattern with optional flag and required_capability field for future extensibility
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/service_*.py : File naming convention: Services must use `service_<name>.py` with class name `Service<Name>`
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/src/omnibase_core/**/*.py : Follow directory-specific file naming conventions enforced by checker_naming_convention.py: cli_*, container_*, decorator_*, enum_*, error_*, factory_*, mixin_*, model_*, node_*, runtime_*, service_*, etc.
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-12-07T17:50:13.678Z
Learnt from: CR
Repo: OmniNode-ai/omniintelligence PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T17:50:13.678Z
Learning: Applies to **/*.py : Use file prefix naming conventions in Python files: model_* for Pydantic models, enum_* for enumerations, protocol_* for protocol interfaces, service_* for service implementations, node_* for ONEX nodes
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/protocol_*.py : File naming convention: Protocols must use `protocol_<name>.py` or `protocols.py` with class name `Protocol<Name>`
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Protocol class names must follow the pattern `Protocol<Name>` (e.g., `ProtocolFileGenerator`)
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/models/model_contract_{actions,models,validation,cli,capabilities}.py : Generated models from subcontracts must follow the naming pattern: `model_contract_actions.py`, `model_contract_models.py`, `model_contract_validation.py`, etc.
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/models/model_contract_*.py : Contract-backed model files must follow the naming pattern `model_contract_<domain>.py` and be located in `*/models/` directories
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/models/model_contract_*.py : Contract model files must follow the naming pattern `model_contract_<domain>.py` and be located in `*/models/` directories
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/!(tool_)+(handler_|utils_|core_)*.py : Do not use prefix patterns `handler_`, `utils_`, or `core_` for file names; use `tool_` prefix instead for business logic files
Applied to files:
src/omnibase_infra/validation/validation_exemptions.yaml
📚 Learning: 2026-01-06T17:57:00.677Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.677Z
Learning: Applies to src/omnibase_spi/protocols/**/*.py : Every protocol must inherit from `typing.Protocol` and have the `runtime_checkable` decorator
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/protocols/protocol_*.py : Use Protocol from typing module for all interface definitions; never use ABC (Abstract Base Classes) for service interfaces
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Use Protocol for interface definitions when implementations may live outside core codebase; use Pydantic models only for base classes with shared logic
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Protocol files must follow the naming pattern `protocol_<name>.py` and be located in `*/protocols/` directories
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-06T17:57:00.677Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.677Z
Learning: Applies to src/omnibase_spi/protocols/contracts/**/*.py : Protocol naming convention: Compiler protocols must follow `Protocol{Type}ContractCompiler` pattern
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/*.py : Import models from shared core paths using `omnibase.model.core.model_*` pattern
Applied to files:
src/omnibase_infra/models/runtime/__init__.py
🧬 Code graph analysis (6)
src/omnibase_infra/runtime/contract_handler_discovery.py (6)
src/omnibase_infra/errors/error_infra.py (2)
InfraConnectionError(232-339)ProtocolConfigurationError(154-189)src/omnibase_infra/models/runtime/model_discovery_error.py (1)
ModelDiscoveryError(23-78)src/omnibase_infra/models/runtime/model_discovery_result.py (2)
ModelDiscoveryResult(26-159)has_errors(86-98)src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
ModelDiscoveryWarning(23-71)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (2)
RegistryError(82-123)ProtocolBindingRegistry(131-439)src/omnibase_infra/runtime/protocol_handler_plugin_loader.py (1)
ProtocolHandlerPluginLoader(80-322)
src/omnibase_infra/runtime/__init__.py (2)
src/omnibase_infra/runtime/protocol_handler_discovery.py (1)
ProtocolHandlerDiscovery(64-218)src/omnibase_infra/runtime/contract_handler_discovery.py (1)
ContractHandlerDiscovery(87-514)
src/omnibase_infra/models/runtime/model_discovery_result.py (2)
src/omnibase_infra/models/runtime/model_discovery_error.py (1)
ModelDiscoveryError(23-78)src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
ModelDiscoveryWarning(23-71)
tests/unit/runtime/contract_handler_discovery/conftest.py (3)
src/omnibase_infra/runtime/contract_handler_discovery.py (1)
ContractHandlerDiscovery(87-514)src/omnibase_infra/runtime/handler_plugin_loader.py (1)
HandlerPluginLoader(237-1984)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (1)
ProtocolBindingRegistry(131-439)
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (6)
src/omnibase_infra/models/runtime/model_discovery_result.py (3)
ModelDiscoveryResult(26-159)has_errors(86-98)has_warnings(101-113)src/omnibase_infra/runtime/contract_handler_discovery.py (2)
ContractHandlerDiscovery(87-514)discover_and_register(137-446)src/omnibase_infra/runtime/handler_plugin_loader.py (1)
HandlerPluginLoader(237-1984)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (1)
ProtocolBindingRegistry(131-439)src/omnibase_infra/runtime/protocol_handler_discovery.py (2)
ProtocolHandlerDiscovery(64-218)discover_and_register(104-218)tests/unit/runtime/contract_handler_discovery/conftest.py (6)
discovery_service(116-128)handler_registry(96-102)valid_contract_path(132-150)valid_contract_directory(154-186)empty_directory(225-233)mixed_valid_invalid_directory(190-221)
src/omnibase_infra/models/runtime/__init__.py (3)
src/omnibase_infra/models/runtime/model_discovery_error.py (1)
ModelDiscoveryError(23-78)src/omnibase_infra/models/runtime/model_discovery_result.py (1)
ModelDiscoveryResult(26-159)src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
ModelDiscoveryWarning(23-71)
🔇 Additional comments (28)
src/omnibase_infra/models/runtime/__init__.py (1)
12-14: LGTM!The new discovery model imports and exports are properly structured, follow the
model_*naming convention, and maintain alphabetical ordering in__all__.Also applies to: 28-30
tests/unit/runtime/contract_handler_discovery/__init__.py (1)
1-6: LGTM!Standard test package initializer with proper license header and documentation.
src/omnibase_infra/validation/validation_exemptions.yaml (2)
656-673: LGTM!Service naming exemptions properly documented with clear rationale and references to CLAUDE.md conventions.
1184-1208: LGTM!Contract handler discovery exemptions correctly follow the established exemption pattern and align with the PR's OMN-1133 objectives.
src/omnibase_infra/models/runtime/model_discovery_error.py (1)
1-81: LGTM!The
ModelDiscoveryErrormodel is well-implemented with proper typing (objectinstead ofAnyfor the details field), follows naming conventions, and includes comprehensive documentation. The frozen, strict configuration ensures data integrity.src/omnibase_infra/models/runtime/model_discovery_result.py (3)
26-83: LGTM!The
ModelDiscoveryResultmodel is well-structured with proper field typing, default factories for mutable fields, and appropriate validation constraints. The model is intentionally not frozen (unlikeModelDiscoveryError) to support potential result mutation during aggregation.
85-113: LGTM!The
has_errorsandhas_warningsproperties provide a clean, expressive API for checking discovery status.
115-159: LGTM!The
__bool__override correctly follows coding guidelines by including a comprehensiveWarningsection documenting the non-standard behavior (lines 118-147). This enables idiomatic conditional checks likeif result:while clearly documenting the semantic difference from standard Pydantic models.Based on coding guidelines for
**/model_*result*.pyfiles.src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
1-74: LGTM! Well-structured discovery warning model.The model follows all conventions:
- File naming (
model_discovery_warning.py→ModelDiscoveryWarning) is correct- Uses PEP 604 union syntax (
Path | None,str | None)- Immutable (
frozen=True) with strict validation- All fields use
Field()with descriptions- Proper
__all__exportsrc/omnibase_infra/runtime/__init__.py (1)
157-163: LGTM! Clean public API exposure for discovery components.The new exports follow the established module patterns:
- Section comment links to ticket OMN-1133
- Imports are organized after the plugin loader section
__all__entries are properly placed with matching commentAlso applies to: 286-288
src/omnibase_infra/runtime/protocol_handler_discovery.py (2)
57-60: Verify runtime type annotation availability.The
ModelDiscoveryResultis imported underTYPE_CHECKING, which means it's only available during static type checking. Since this is aProtocolwithruntime_checkable, the return type annotation should work correctly at runtime (Python handles forward references in annotations). However, if any runtime introspection of the return type is needed, it would fail.This pattern is acceptable for protocol definitions where runtime type checking of return values is not performed.
63-218: Well-designed protocol with comprehensive documentation.The protocol follows all ONEX conventions:
- Uses
@runtime_checkabledecorator- Inherits from
typing.Protocol(not ABC)- Uses duck typing pattern with guidance on verification via
hasattr- PEP 604 syntax for optional types (
UUID | None)- Comprehensive docstrings with examples, thread safety notes, and error handling documentation
The
...(Ellipsis) body is the correct convention for Protocol methods per PEP 544.tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (3)
27-44: LGTM! Protocol compliance tests.Good coverage of protocol compliance:
isinstancecheck works becauseProtocolHandlerDiscoveryis@runtime_checkable- Method existence and callability verification
255-275: LGTM! Registry integration tests with good coverage.The tests verify:
- Handlers can be retrieved after discovery
- Multiple discoveries accumulate (or overwrite) registrations
The comment on line 268 clarifies the expected behavior (overwrite on re-discovery), which is a good documentation practice.
109-123: The error code"PATH_NOT_FOUND"used in the test matches the implementation inContractHandlerDiscovery.py(line 254), where it is raised when a path does not exist. No changes needed.src/omnibase_infra/runtime/runtime_host_process.py (4)
284-324: Comprehensive documentation for contract_paths parameter.The docstring clearly explains:
- Purpose and behavior
- Path types (directories vs files)
- Error handling semantics (graceful degradation)
- Example usage
336-343: LGTM! Proper initialization of contract discovery state.Good implementation choices:
- Converts
list[str]tolist[Path]upfront for consistent filesystem operations- Lazy initialization of
_handler_discovery(only created if contract_paths provided)
892-919: Clean separation of discovery vs wiring logic.The
_discover_or_wire_handlers()method provides a clear branch point:
- If
contract_pathsis truthy: use contract-based discovery- Otherwise: fall back to
wire_handlers()(which aliaseswire_default_handlers)The docstring accurately describes both modes.
961-964: HandlerPluginLoader is created but never stored.A new
HandlerPluginLoader()is created each time_discover_handlers_from_contracts()is called. While the current implementation only calls this once duringstart(), consider whether this should be:
- Stored as an instance variable for potential reuse
- Passed via constructor for dependency injection (testability)
For the current use case (single call during startup), this is acceptable.
tests/unit/runtime/contract_handler_discovery/conftest.py (3)
50-88: LGTM! MockValidHandler correctly implements ProtocolHandler interface.The mock handler implements all 5 required protocol methods:
handler_type(property)initialize(config: dict[str, object])shutdown(timeout_seconds: float)execute(request: object, operation_config: object)describe()(classmethod)Type hints use
objectinstead ofAny, following coding guidelines.
189-221: Good coverage of error scenarios in mixed_valid_invalid_directory.The fixture creates three distinct scenarios:
- Valid contract with importable handler
- Invalid YAML syntax (unclosed bracket)
- Valid YAML but missing required
handler_classfieldThis provides comprehensive coverage for graceful degradation testing.
131-150: Handler class path resolution is correct and will resolve properly at test time.The fixture correctly uses
f"{__name__}.MockValidHandler", which resolves totests.unit.runtime.contract_handler_discovery.conftest.MockValidHandlerat runtime. TheMockValidHandlerclass exists in the conftest module, andContractHandlerDiscovery._import_handler_class()properly handles this path usingimportlib.import_module()to dynamically import the module andgetattr()to retrieve the class. The test module hierarchy includes the necessary__init__.pyfile, ensuring the path is importable by pytest.tests/integration/runtime/test_runtime_host_handler_discovery.py (4)
86-93: LGTM! Isolated handler registry fixture.Creating a fresh
ProtocolBindingRegistry()instance for tests provides isolation from the singleton registry, preventing test pollution.
559-617: Comprehensive lifecycle tests with proper cleanup.The lifecycle tests cover:
- Full start/stop cycle with discovered handlers
- Restart after stop (with fresh event bus)
- Idempotent start/stop behavior
Good practice: The comment on lines 606-607 acknowledges that singleton registry state from other tests may affect
healthystatus, focusing the test onis_runninginstead.
729-778: LGTM! Logging verification test.The test uses
caplogto verify that discovery/registration generates appropriate log messages. The flexible assertion (has_discovery_log or has_registered_log) accommodates implementation variations while still ensuring observability.
781-787: Test classes exported via all.Good practice for test module organization, making it clear which test classes are the public API of this test module.
src/omnibase_infra/runtime/contract_handler_discovery.py (2)
121-136: This class correctly uses constructor-based dependency injection with concrete protocol types rather thanModelONEXContainer. As a stateless utility coordinator that bridges the plugin loader and registry, concrete dependencies are the appropriate pattern here. Note thatHandlerPluginLoaderis never registered in the container and is instantiated inline throughout the codebase, including inRuntimeHostProcesswhich instantiatesContractHandlerDiscoverywith concrete deps. The container-based DI guideline applies to infrastructure services (PolicyRegistry, ProtocolBindingRegistry) registered incontainer_wiring.py, not to utility coordinators following the duck-typing protocol pattern.Likely an incorrect or invalid review comment.
289-307: Registry key must be protocol type, not handler name:loaded.handler_nameis incorrect.
ProtocolBindingRegistry.register(protocol_type, handler_cls)expects protocol type identifiers like"http","db","kafka"as the first argument. The code registers withloaded.handler_name(e.g.,"auth.validate_token"), which is a friendly identifier, not a protocol type. At runtime,handler_registry.get(handler_type)will fail because the registry was keyed by handler_name instead of the protocol type. Additionally,ModelLoadedHandlerlacks aprotocol_typefield—the contract model must extract or provide the protocol type identifier from the handler contract YAML.⛔ Skipped due to learnings
Learnt from: CR Repo: OmniNode-ai/omnibase_spi PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:57:00.677Z Learning: Applies to src/omnibase_spi/protocols/handlers/**/*.py : Protocol naming convention: Handler protocols must follow `Protocol{Type}Handler` patternLearnt from: CR Repo: OmniNode-ai/omnibase_infra PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-11T17:31:33.533Z Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registriesLearnt from: CR Repo: OmniNode-ai/omniclaude PR: 0 File: .cursor/rules/canonical_patterns.mdc:0-0 Timestamp: 2025-11-24T17:22:32.195Z Learning: Applies to **/protocols/protocol_*.py : All Protocol definitions must use model-only signatures: methods accept only validated Pydantic models, never dict, primitives, or argument modelsLearnt from: CR Repo: OmniNode-ai/omniclaude PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T17:24:41.687Z Learning: Applies to **/protocols/protocol_*.py : Protocol method signatures must use Pydantic models only, never primitives or dicts as parameters or return typesLearnt from: CR Repo: OmniNode-ai/omninode_bridge PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T16:33:32.747Z Learning: Applies to **/protocols/protocol_*.py : All protocol method signatures must use Pydantic models exclusively, never primitives or dictsLearnt from: CR Repo: OmniNode-ai/omninode_bridge PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T16:33:32.747Z Learning: Applies to **/protocols/protocol_*.py : Protocol class names must follow the pattern `Protocol<Name>` using PascalCaseLearnt from: CR Repo: OmniNode-ai/omniclaude PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T17:24:41.687Z Learning: Applies to **/protocols/protocol_*.py : Protocol class names must follow the pattern `Protocol<Name>` (e.g., `ProtocolFileGenerator`)Learnt from: CR Repo: OmniNode-ai/omniclaude PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T17:24:41.687Z Learning: Applies to **/protocols/protocol_*.py : Use Protocol for tool interfaces and plugin APIs based on method shape (structural typing), not Pydantic models with inheritanceLearnt from: CR Repo: OmniNode-ai/omniclaude PR: 0 File: .cursor/rules/standards.mdc:0-0 Timestamp: 2025-11-24T17:24:41.687Z Learning: Applies to **/protocols/protocol_*.py : Avoid using Any, dict, or primitive types in protocol signatures; use the strongest typing possible with Pydantic models
… [OMN-1133] Critical/Major fixes: - Fix exception leak: use stored is_file variable instead of calling path.is_file() inside exception handler (can raise OSError) - Classify AttributeError/TypeError as import errors alongside ImportError - Add explicit test assertions for error logging in mixed valid/invalid test Minor/Nitpick improvements: - Add last_discovery_result property for observability caching - Fix docstring: BindingRegistry → ProtocolBindingRegistry - Add docstring note explaining sync I/O in async method is intentional - Add 4 new observability tests for cached discovery result
Pull Request Review: Contract-Based Handler Discovery (OMN-1133)OverviewThis PR implements ✅ Code Quality & Best PracticesExcellent Adherence to ONEX PatternsStrong Points:
Documentation Quality
🔒 Security ConsiderationsPositive Security Patterns
Security RecommendationCRITICAL: Per CLAUDE.md, YAML contracts are treated as executable code. Consider adding namespace allowlisting: loader = HandlerPluginLoader(
allowed_namespaces=["omnibase_infra.", "omnibase_core."]
)Recommendation: Add 🐛 Potential Bugs & IssuesCritical IssuesNone identified - The second commit ( Minor Issues
⚡ Performance ConsiderationsPositive Patterns:
Notes:
🔬 Test Coverage AssessmentCoverage (29 tests total)✅ Unit Tests (15): Protocol compliance, discovery, error handling, correlation IDs Coverage Gaps
🏗️ Architecture & DesignExcellent Architectural Decisions
🎯 Recommendations SummaryMust Fix Before Merge
Should Fix (High Priority)
Consider (Medium Priority)
✅ Final VerdictAPPROVED with recommendations Strengths:
Action Items:
No blocking issues - Ready to merge once critical docstring fix is applied. Reviewed by: Claude Sonnet 4.5 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (2)
192-204: Consider strengthening correlation ID preservation test.The test verifies the method completes successfully with a provided
correlation_id, but doesn't actually assert that the ID was used in the result or logs. Consider adding verification, such as checkingcaplogfor the correlation ID in log output.♻️ Optional enhancement
@pytest.mark.asyncio async def test_preserves_provided_correlation_id( self, discovery_service: ContractHandlerDiscovery, empty_directory: Path, + caplog: pytest.LogCaptureFixture, ) -> None: """Test that provided correlation ID is used.""" + import logging correlation_id = uuid4() - result = await discovery_service.discover_and_register( - [empty_directory], - correlation_id=correlation_id, - ) - assert isinstance(result, ModelDiscoveryResult) + with caplog.at_level(logging.DEBUG): + result = await discovery_service.discover_and_register( + [empty_directory], + correlation_id=correlation_id, + ) + assert isinstance(result, ModelDiscoveryResult) + # Verify correlation ID appears in logs + assert any(str(correlation_id) in record.message or + str(correlation_id) in str(getattr(record, 'correlation_id', '')) + for record in caplog.records)
286-306: Minor: Comment-test alignment.The comment on line 299 states "should overwrite" but the test only verifies handlers remain registered, not that overwriting actually occurred. Consider either updating the comment to reflect what's actually being tested (idempotency) or adding verification that demonstrates overwriting behavior.
src/omnibase_infra/runtime/contract_handler_discovery.py (2)
391-404: Add exception comment marker for catch-all handler.Per coding guidelines, catch-all exception handlers should use standardized comment markers to document intent. This helps distinguish intentional catch-alls from accidental ones.
♻️ Suggested fix
- except Exception as e: - # Catch-all for unexpected registration errors + except Exception as e: # catch-all-ok: capture unexpected registration errors without blocking other handlers errors.append(
417-463: LGTM! Correct use of storedis_filevariable.The exception handlers correctly use the stored
is_fileboolean (defined at line 277) rather than callingpath.is_file()which could raise a secondary exception. This pattern ensures the error context is populated safely.However, I note the catch-all handler (lines 452-463) should also have the exception comment marker per coding guidelines.
♻️ Add exception marker
- except Exception as e: - # Unexpected errors during path processing - # NOTE: Use stored is_file boolean, NOT path.is_file() call - # which could raise OSError while already handling an exception + except Exception as e: # catch-all-ok: unexpected path processing errors; ensures graceful degradation + # NOTE: Use stored is_file boolean, NOT path.is_file() call + # which could raise OSError while already handling an exception errors.append(
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Lite
📒 Files selected for processing (2)
src/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Never useAnytype - useobjectfor generic payloads in function parameters and return types
UseX | None(PEP 604) instead ofOptional[X]for nullable types
All services must useModelONEXContainerfor dependency injection via__init__(self, container: ModelONEXContainer)
Use@allow_anydecorator with documented reason as exemption mechanism forAnytype violations
UseJsonTypefromomnibase_core.typesas the canonical type alias for JSON-compatible values
UseModelEventEnvelope[object]for generic dispatcher interfaces andobjectfor generic payloads
Infrastructure error handling must useOnexErrorbase class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
UseInfraConnectionError,InfraTimeoutError,InfraAuthenticationError,InfraUnavailableErrorfor transport failures with properModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated withuuid4()if missing, and included in all error contexts
External service adapters must implementMixinAsyncCircuitBreakerwith appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never useisinstancechecks
Files:
src/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handler contract files must declare handler routing with `routing_strategy`, event models, handler classes, and handler modules
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use standardized exception comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using catch-all or broad exception handlers to document intent
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/*.py : Infrastructure error handling must use `OnexError` base class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Do not use bare `except:` without re-raise
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Use `ModelOnexError` instead of standard Python exceptions for error handling
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
🧬 Code graph analysis (1)
src/omnibase_infra/runtime/contract_handler_discovery.py (8)
src/omnibase_infra/errors/error_infra.py (2)
InfraConnectionError(232-339)ProtocolConfigurationError(154-189)src/omnibase_infra/models/runtime/model_discovery_error.py (1)
ModelDiscoveryError(23-78)src/omnibase_infra/models/runtime/model_discovery_result.py (2)
ModelDiscoveryResult(26-159)has_errors(86-98)src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
ModelDiscoveryWarning(23-71)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (2)
RegistryError(82-123)ProtocolBindingRegistry(131-439)tests/unit/runtime/contract_handler_discovery/conftest.py (2)
handler_registry(96-102)plugin_loader(106-112)src/omnibase_infra/runtime/protocol_handler_plugin_loader.py (1)
ProtocolHandlerPluginLoader(80-322)tests/helpers/replay_utils.py (1)
append(364-385)
🔇 Additional comments (14)
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (7)
1-25: LGTM! Well-structured test file with clear organization.The imports are appropriate, and the test file follows good practices with clear class organization by feature area (protocol compliance, basic functionality, error handling, etc.). The test module docstring provides helpful context about what's being tested.
27-43: LGTM! Protocol compliance tests are appropriate.Using
isinstancecheck against the protocol in tests is acceptable for verifying protocol compliance. The production code follows duck typing principles while these tests validate the implementation correctly satisfies theProtocolHandlerDiscoveryinterface.
46-103: LGTM! Comprehensive basic functionality tests.The tests cover the essential scenarios: single file discovery, directory scanning with multiple contracts, and empty directory handling. The assertions appropriately verify both the
ModelDiscoveryResultstate and the actual registry registrations.
106-175: LGTM! Well-documented error handling tests.The docstring on lines 133-138 clearly explains the intentional design decision where
HandlerPluginLoaderfilters out failed contracts during loading. The log verification (lines 159-172) ensures observability requirements are met. This is a good example of documenting non-obvious behavior in tests.
207-228: LGTM!The loose assertions (
>= 1) appropriately decouple the test from specific fixture implementation details while still verifying the core behavior of processing both files and directories.
231-267: LGTM!Good coverage of
ModelDiscoveryResultproperties includinghas_errors,has_warnings, anddiscovered_attimestamp verification.
309-373: LGTM! Excellent observability tests.The tests comprehensively cover the
last_discovery_resultcaching behavior including initial state (None), caching after discovery, updates on subsequent discoveries, and querying for observability purposes. The use ofisfor object identity checks (lines 330, 345, 351-352) is appropriate for verifying caching behavior.src/omnibase_infra/runtime/contract_handler_discovery.py (7)
1-85: LGTM! Excellent module documentation.The module docstring is comprehensive with clear sections covering purpose, thread safety, error handling strategy, usage examples, and cross-references. The imports are well-organized with TYPE_CHECKING guard for type-only imports.
125-140: LGTM!The constructor follows the dependency injection pattern correctly. The absence of explicit protocol validation aligns with the coding guideline to use duck typing via protocols.
142-166: LGTM!Clean implementation of the observability property with clear documentation.
270-323: LGTM! Robust path type detection.The nested try-except for
path.is_dir()andpath.is_file()(lines 275-288) correctly handlesOSErrorfrom filesystem access issues. The edge case handling for paths that exist but are neither file nor directory (lines 314-322) is a good defensive measure.
465-493: LGTM! Good logging practices.The conditional log level (WARNING for errors, INFO otherwise) and structured logging with correlation ID in the
extradict follow observability best practices.
495-561: LGTM! Clean import helper implementation.The method properly validates the class path format, uses
rsplitcorrectly to separate module from class name, and verifies the imported object is actually a class. Theisinstance(handler_class, type)check on line 545 is appropriate here since it's validating the nature of the object, not protocol compliance.
564-566: LGTM!Appropriate minimal export surface.
…overy [OMN-1133] Fixes all issues from PR review: Critical/Major: - Initialize is_file/is_directory before try block to prevent NameError in exception handlers - Change IMPORT_FAILED to IMPORT_ERROR for consistent error classification Tests: - Add assertion for error presence in mixed valid/invalid test - Strengthen correlation ID preservation test with caplog verification - Improve docstrings to enumerate specific verification points Documentation: - Add WHY comments to catch-all exception handlers explaining graceful degradation
Code Review: ContractHandlerDiscovery Implementation [OMN-1133]SummaryThis PR implements contract-based handler discovery that auto-registers handlers from YAML contracts during runtime startup. The implementation is high quality with excellent error handling, comprehensive documentation, and strong test coverage. Recommend approval with minor suggestions. ✅ Strengths1. Excellent Error Handling & Graceful Degradation
Aligns with ONEX resilience patterns - individual component failures don't cascade 2. Strong Type Safety & Protocol Compliance
Follows ONEX typing conventions - strong types throughout 3. Container-Based Dependency Injection
Matches ONEX DI pattern from 4. Comprehensive Test Coverage
5. Documentation Excellence
🔍 Code Quality ObservationsProtocol Interface Design (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (1)
334-354: Consider clarifying test intent for multiple discoveries.The test name
test_multiple_discoveries_accumulate_registrationsand the comment "should overwrite" (line 347) describe different behaviors. The current assertion only verifies handlers are registered after two calls, but doesn't distinguish between accumulation and overwrite semantics.If the intent is to verify idempotent overwrite behavior, consider adding an assertion like:
# Verify same handler class is registered (not accumulated as duplicate) handler1 = handler_registry.get("handler.one") handler2 = handler_registry.get("handler.one") # After second discovery assert handler1 is handler2 # Same class reference
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Lite
📒 Files selected for processing (2)
src/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Never useAnytype - useobjectfor generic payloads in function parameters and return types
UseX | None(PEP 604) instead ofOptional[X]for nullable types
All services must useModelONEXContainerfor dependency injection via__init__(self, container: ModelONEXContainer)
Use@allow_anydecorator with documented reason as exemption mechanism forAnytype violations
UseJsonTypefromomnibase_core.typesas the canonical type alias for JSON-compatible values
UseModelEventEnvelope[object]for generic dispatcher interfaces andobjectfor generic payloads
Infrastructure error handling must useOnexErrorbase class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
UseInfraConnectionError,InfraTimeoutError,InfraAuthenticationError,InfraUnavailableErrorfor transport failures with properModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated withuuid4()if missing, and included in all error contexts
External service adapters must implementMixinAsyncCircuitBreakerwith appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never useisinstancechecks
Files:
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.pysrc/omnibase_infra/runtime/contract_handler_discovery.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handler contract files must declare handler routing with `routing_strategy`, event models, handler classes, and handler modules
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Applied to files:
tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.pysrc/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.296Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.296Z
Learning: Applies to **/*.py : Use standardized exception comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using catch-all or broad exception handlers to document intent
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.533Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.533Z
Learning: Applies to **/*.py : Infrastructure error handling must use `OnexError` base class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Do not use bare `except:` without re-raise
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
🔇 Additional comments (14)
src/omnibase_infra/runtime/contract_handler_discovery.py (7)
1-84: LGTM - Clean module structure and imports.The module docstring is comprehensive with clear documentation of thread safety guarantees, error handling strategy, and example usage. TYPE_CHECKING is correctly used for protocol imports to avoid circular dependencies.
87-140: LGTM - Well-structured class with proper DI.The class follows dependency injection pattern with protocol types for the plugin loader and registry. Type hints correctly use PEP 604 syntax (
ModelDiscoveryResult | None).
142-166: LGTM - Observability-friendly caching.The read-only property provides clean access to the last discovery result for monitoring and debugging without requiring re-execution of discovery.
399-416: Catch-all handlers correctly documented with WHY comments.The catch-all exception handlers follow the graceful degradation pattern - individual handler failures don't crash the entire discovery. The WHY comments explain the rationale clearly. Per the learnings, you could alternatively use the standardized marker
# catch-all-ok: graceful degradationbut the current approach is equally informative.
234-245: Thread safety note is accurate for typical usage.The docstring correctly notes this is intended for startup scenarios where blocking is acceptable. The
_last_discovery_resultassignment is a simple reference swap, which is effectively atomic in CPython due to the GIL. For production observability, the minor race on this cache is acceptable. The suggestion to useasyncio.to_thread()for high-concurrency scenarios is appropriate.
513-566: Solid import helper with proper validation.The method correctly validates the class path format, uses standard
importlib.import_module, and verifies the result is actually a class type. Theisinstancecheck here (line 563) is appropriate as it validates a type constraint rather than resolving a protocol.One minor note:
ValueErrorraised at line 553 for malformed class paths would be caught by the catch-all handler (line 399) resulting inREGISTRATION_UNEXPECTED_ERRORrather thanIMPORT_ERROR. This is arguably acceptable since malformed paths are configuration errors rather than import failures.
582-584: LGTM - Correct public API export.tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py (7)
1-25: LGTM - Clean test module setup.Appropriate imports for testing the discovery service including the protocol interface for compliance testing.
27-44: LGTM - Protocol compliance tests.The
isinstancecheck in line 35 is appropriate for test assertions verifying protocol compliance withruntime_checkableprotocols. This differs from the coding guideline againstisinstancechecks, which applies to production code protocol resolution via duck typing.
46-104: LGTM - Comprehensive basic functionality coverage.Good test coverage of core scenarios: single contract file, directory with multiple contracts, and empty directory. Assertions verify both the result model properties and the actual registry state.
125-191: Excellent documentation of partial success behavior.The
test_mixed_valid_invalid_contracts_partial_successtest has a thorough docstring explaining the intentional design where theHandlerPluginLoaderfilters out failed contracts and logs warnings, whileContractHandlerDiscoveryonly sees the successful handlers. This makes theassert not result.has_errorsassertion clear and justified.The verification that error indicators appear in warning logs (lines 181-188) confirms the loader is properly reporting issues despite the result having no errors from the discovery perspective.
194-252: LGTM - Thorough correlation ID verification.The tests correctly verify both auto-generation and preservation of correlation IDs. The preservation test (lines 237-248) checks multiple locations where the correlation ID might appear in logs, which is robust against different logging configurations.
255-277: LGTM - Mixed path type handling verified.The test confirms both file and directory paths are processed in a single discovery call. The
>= 1assertions are appropriate here since the exact count depends on fixture contents and the primary goal is verifying path type handling.
357-421: LGTM - Comprehensive observability testing.Excellent coverage of the
last_discovery_resultcaching feature including initial state, object identity verification (cached is result), updates on subsequent discoveries, and ability to query all result properties without re-running discovery.
… [OMN-1133] - Remove unused InfraConnectionError import and dead exception handler - Add ValueError to import error classification for invalid class paths - Add CATCH-ALL comment markers to exception handlers - Fix stale docstrings in protocol (field names, removed non-existent fields) - Enhance test assertions for error logging verification - Strengthen correlation ID preservation test with 4 detection methods - Rename test to clarify idempotent re-registration behavior - Document test isolation in conftest fixtures
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/omnibase_infra/runtime/protocol_handler_discovery.py:
- Around line 150-160: The protocol docstring for ProtocolHandlerDiscovery
currently claims that implementations will raise ProtocolConfigurationError with
codes DISCOVERY_001/002/003, but the ContractHandlerDiscovery implementation
captures errors in its result.errors instead; update the
ProtocolHandlerDiscovery docstring to accurately reflect reality by stating that
implementations MAY raise ProtocolConfigurationError with those codes but are
not required to, or remove the explicit error codes and state that
implementations should either raise those errors or report issues via the
result.errors list (mentioning ProtocolHandlerDiscovery and
ContractHandlerDiscovery by name so readers can find the relevant
implementations).
🧹 Nitpick comments (2)
src/omnibase_infra/runtime/contract_handler_discovery.py (2)
249-251: Unusedwarningslist is initialized but never populated.The
warningslist is initialized on line 250 but never appended to in this method. While this doesn't cause issues and may be intentional for future extension, consider either:
- Adding a TODO comment indicating future use, or
- Removing if warnings are not planned for this operation
435-442: Context loss when directory paths fail unexpectedly.When
ProtocolConfigurationErroror unexpected exceptions occur for a directory path,contract_pathis set toNone(viapath if is_file else Noneon lines 439 and 457). This loses the directory path context in the error.Consider preserving the path regardless of type:
♻️ Suggested improvement
errors.append( ModelDiscoveryError( error_code=error_code, message=str(e), - contract_path=path if is_file else None, + contract_path=path, # Preserve path for context details={"exception_type": type(e).__name__}, ) )Apply similar change to lines 453-460.
Also applies to: 453-460
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Lite
📒 Files selected for processing (4)
src/omnibase_infra/runtime/contract_handler_discovery.pysrc/omnibase_infra/runtime/protocol_handler_discovery.pytests/unit/runtime/contract_handler_discovery/conftest.pytests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/unit/runtime/contract_handler_discovery/test_contract_handler_discovery.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Never useAnytype - useobjectfor generic payloads in function parameters and return types
UseX | None(PEP 604) instead ofOptional[X]for nullable types
All services must useModelONEXContainerfor dependency injection via__init__(self, container: ModelONEXContainer)
Use@allow_anydecorator with documented reason as exemption mechanism forAnytype violations
UseJsonTypefromomnibase_core.typesas the canonical type alias for JSON-compatible values
UseModelEventEnvelope[object]for generic dispatcher interfaces andobjectfor generic payloads
Infrastructure error handling must useOnexErrorbase class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
UseInfraConnectionError,InfraTimeoutError,InfraAuthenticationError,InfraUnavailableErrorfor transport failures with properModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated withuuid4()if missing, and included in all error contexts
External service adapters must implementMixinAsyncCircuitBreakerwith appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never useisinstancechecks
Files:
src/omnibase_infra/runtime/protocol_handler_discovery.pysrc/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/conftest.py
**/protocol_*.py
📄 CodeRabbit inference engine (CLAUDE.md)
File naming convention: Protocols must use
protocol_<name>.pyorprotocols.pywith class nameProtocol<Name>
Files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.550Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.310Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
📚 Learning: 2026-01-06T17:57:00.689Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.689Z
Learning: Applies to src/omnibase_spi/protocols/**/*.py : Every protocol must inherit from `typing.Protocol` and have the `runtime_checkable` decorator
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-06T17:57:00.689Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.689Z
Learning: Applies to src/omnibase_spi/protocols/handlers/**/*.py : Protocol naming convention: Handler protocols must follow `Protocol{Type}Handler` pattern
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/protocols/protocol_*.py : Use Protocol from typing module for all interface definitions; never use ABC (Abstract Base Classes) for service interfaces
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Use Protocol for tool interfaces and plugin APIs based on method shape (structural typing), not Pydantic models with inheritance
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Use Protocol for interface definitions when implementations may live outside core codebase; use Pydantic models only for base classes with shared logic
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/*.py : Import protocols from `omnibase.protocol.protocol_*` paths
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/*.py : Import protocols from `omnibase.protocol.protocol_<name>` module paths
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-06T17:57:00.689Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:57:00.689Z
Learning: Applies to src/omnibase_spi/protocols/contracts/**/*.py : Protocol naming convention: Compiler protocols must follow `Protocol{Type}ContractCompiler` pattern
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Avoid using Any, dict, or primitive types in protocol signatures; use the strongest typing possible with Pydantic models
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/protocols/protocol_*.py : All Protocol definitions must use model-only signatures: methods accept only validated Pydantic models, never dict, primitives, or argument models
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.550Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.550Z
Learning: Applies to **/handler_contract.yaml : Handlers must be discovered and loaded dynamically via YAML contracts using plugin pattern, not hardcoded registries
Applied to files:
src/omnibase_infra/runtime/protocol_handler_discovery.pysrc/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/conftest.py
📚 Learning: 2026-01-11T18:12:47.310Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.310Z
Learning: Applies to **/*.py : Use FileRegistry from omnibase_core.runtime.runtime_file_registry for loading YAML contracts - handle error codes: FILE_NOT_FOUND, FILE_READ_ERROR, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, DUPLICATE_REGISTRATION, DIRECTORY_NOT_FOUND
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.pytests/unit/runtime/contract_handler_discovery/conftest.py
📚 Learning: 2026-01-11T17:31:33.550Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.550Z
Learning: Applies to **/handler_contract.yaml : Handler contract files must declare handler routing with `routing_strategy`, event models, handler classes, and handler modules
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T18:12:47.310Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T18:12:47.310Z
Learning: Applies to **/*.py : Use standardized exception comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using catch-all or broad exception handlers to document intent
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2026-01-11T17:31:33.550Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-11T17:31:33.550Z
Learning: Applies to **/*.py : Infrastructure error handling must use `OnexError` base class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Do not use bare `except:` without re-raise
Applied to files:
src/omnibase_infra/runtime/contract_handler_discovery.py
📚 Learning: 2025-11-24T16:33:51.604Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T16:33:51.604Z
Learning: Applies to tests/**/conftest.py : Test fixtures must be defined in `conftest.py` and should provide reusable sample data, UUIDs, semantic versions, and model data
Applied to files:
tests/unit/runtime/contract_handler_discovery/conftest.py
🧬 Code graph analysis (3)
src/omnibase_infra/runtime/protocol_handler_discovery.py (2)
src/omnibase_infra/models/runtime/model_discovery_result.py (1)
ModelDiscoveryResult(26-159)src/omnibase_infra/runtime/contract_handler_discovery.py (1)
discover_and_register(165-490)
src/omnibase_infra/runtime/contract_handler_discovery.py (5)
src/omnibase_infra/errors/error_infra.py (1)
ProtocolConfigurationError(154-189)src/omnibase_infra/models/runtime/model_discovery_error.py (1)
ModelDiscoveryError(23-78)src/omnibase_infra/models/runtime/model_discovery_result.py (2)
ModelDiscoveryResult(26-159)has_errors(86-98)src/omnibase_infra/models/runtime/model_discovery_warning.py (1)
ModelDiscoveryWarning(23-71)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (2)
RegistryError(82-123)ProtocolBindingRegistry(131-439)
tests/unit/runtime/contract_handler_discovery/conftest.py (3)
src/omnibase_infra/runtime/contract_handler_discovery.py (1)
ContractHandlerDiscovery(84-558)src/omnibase_infra/runtime/handler_plugin_loader.py (1)
HandlerPluginLoader(237-1984)src/omnibase_infra/runtime/registry/registry_protocol_binding.py (1)
ProtocolBindingRegistry(131-439)
🔇 Additional comments (15)
src/omnibase_infra/runtime/protocol_handler_discovery.py (2)
1-61: LGTM! Well-structured protocol definition.The protocol follows all required conventions:
- Uses
@runtime_checkabledecorator as required- Follows
Protocol<Name>naming convention- Uses
TYPE_CHECKINGto avoid circular imports- Uses
X | Nonepattern (PEP 604) for nullable types- Uses
...(Ellipsis) for method body per PEP 544
219-221: LGTM!The
__all__export correctly exposes only the protocol class.src/omnibase_infra/runtime/contract_handler_discovery.py (8)
1-82: LGTM! Clean imports and comprehensive module documentation.The module follows all coding guidelines:
- No
Anytype usage- Proper
TYPE_CHECKINGusage for type-only imports- Well-documented with usage examples and thread safety notes
139-163: LGTM!The read-only property correctly uses
X | Nonepattern and provides observability access to the last discovery result.
246-262: LGTM! Correlation ID handling follows ONEX guidelines.Auto-generating
correlation_idwhen not provided ensures all operations are traceable. The structured logging withextradict includes all relevant context.
267-302: Good defensive initialization of path type flags.Initializing
is_directoryandis_filetoFalsebefore the try block (lines 277-278) correctly preventsNameErrorin the outer exception handlers. The OSError handling for filesystem access issues is appropriate.
378-413: Exception grouping for import errors is well-structured.The reclassification of
AttributeError,TypeError, andValueErroralongsideImportErrorunder the unifiedIMPORT_ERRORcode (per PR objectives) provides clearer error categorization. The catch-all handler with the explanatory comment follows the established pattern for graceful degradation.
492-558: LGTM! Clean import helper with proper validation.The method correctly:
- Validates fully-qualified path format
- Uses standard
importlib.import_modulefor dynamic import- Verifies the result is a class (not protocol resolution, so
isinstance(..., type)is appropriate)- Provides detailed logging for debugging
561-563: LGTM!The
__all__correctly exports only the public class.
122-138: UseModelONEXContainerfor dependency injection.This class violates the coding guidelines which require all services to use
ModelONEXContainerfor dependency injection. Update the constructor to acceptcontainer: ModelONEXContainerand resolveProtocolHandlerPluginLoaderandProtocolBindingRegistryfrom the container instead of using direct injection.tests/unit/runtime/contract_handler_discovery/conftest.py (5)
20-43: LGTM! Well-defined test contract templates.The templates provide good coverage:
- Valid contract with placeholders for flexibility
- Invalid YAML syntax for parser error testing
- Missing required field for validation error testing
50-88: LGTM! Mock handler correctly implements protocol interface.The
MockValidHandler:
- Implements all 5 required protocol methods
- Uses
objectfor generic parameters (per coding guidelines, avoidingAny)- Uses
dict[str, object]for typed dictionaries- Properly documents the protocol contract in the docstring
95-143: LGTM! Excellent fixture design with proper isolation.The fixtures demonstrate good practices:
- Function scope (default) ensures fresh instances per test
- Comprehensive docstrings explain isolation guarantees
discovery_servicecorrectly composes other fixtures for full isolation- No shared mutable state between tests
145-201: LGTM! Path fixtures provide comprehensive test scenarios.Good use of:
tmp_pathfixture for automatic cleanup and isolation{__name__}.MockValidHandlerfor correct module path reference- Realistic directory structures matching expected contract layouts
203-247: LGTM! Edge case fixtures are well-structured.The
mixed_valid_invalid_directoryandempty_directoryfixtures provide essential test scenarios for:
- Graceful degradation with partial failures
- Handling of empty/no-contracts directories
| Raises: | ||
| ProtocolConfigurationError: If critical configuration issues prevent | ||
| discovery from proceeding. Error codes: | ||
|
|
||
| - DISCOVERY_001: Empty contract_paths list provided | ||
| - DISCOVERY_002: All provided paths are invalid (none exist) | ||
| - DISCOVERY_003: Configuration prevents any discovery | ||
|
|
||
| Note that individual path or contract failures do NOT raise | ||
| exceptions - they are captured in the result's ``errors`` list | ||
| to allow partial success. |
There was a problem hiding this comment.
Docstring describes exceptions that the implementation doesn't raise.
The protocol docstring specifies that ProtocolConfigurationError should be raised with error codes DISCOVERY_001, DISCOVERY_002, DISCOVERY_003 for empty paths, all invalid paths, and configuration issues. However, the ContractHandlerDiscovery implementation in contract_handler_discovery.py does not raise these exceptions—instead, it captures all errors in the result's errors list for graceful degradation.
Consider either:
- Updating the protocol docstring to reflect that implementations MAY raise these exceptions but are not required to, or
- Removing the specific error codes if the intent is for implementations to always use graceful degradation
🤖 Prompt for AI Agents
In @src/omnibase_infra/runtime/protocol_handler_discovery.py around lines 150 -
160, The protocol docstring for ProtocolHandlerDiscovery currently claims that
implementations will raise ProtocolConfigurationError with codes
DISCOVERY_001/002/003, but the ContractHandlerDiscovery implementation captures
errors in its result.errors instead; update the ProtocolHandlerDiscovery
docstring to accurately reflect reality by stating that implementations MAY
raise ProtocolConfigurationError with those codes but are not required to, or
remove the explicit error codes and state that implementations should either
raise those errors or report issues via the result.errors list (mentioning
ProtocolHandlerDiscovery and ContractHandlerDiscovery by name so readers can
find the relevant implementations).
Code Review: ContractHandlerDiscovery Implementation (OMN-1133)SummaryThis PR implements contract-based handler discovery for the ONEX runtime. The implementation is well-architected, thoroughly tested, and production-ready. ✅ Strengths1. Excellent Architecture
2. Strong Type Safety
3. Comprehensive Test Coverage
4. Excellent Documentation
5. Production-Ready Error Handling
🔍 Code Quality Observations1. Async Method with Sync I/O (contract_handler_discovery.py:237)
2. Path Type Checking Logic (contract_handler_discovery.py:268-327)
3. Container Resolution Caching (runtime_host_process.py:1112-1172)
🔒 Security ConsiderationsWell-Handled:
Deployment Checklist:
📊 PerformanceGood patterns: Fail-fast validation, path caching, registry caching Potential bottlenecks: Synchronous file I/O, sequential path processing
🎯 CLAUDE.md ComplianceFully Compliant:
🎉 Final VerdictAPPROVE - Ready to Merge This PR demonstrates excellent engineering practices:
Minor follow-ups (non-blocking):
Great work! The code quality and test coverage are exemplary. 🎉 |
Summary
Implements
ContractHandlerDiscoverythat discovers handlers from contracts and auto-registers them with the runtime. This eliminates the need for manual handler wiring and integrates withRuntimeHostProcess.start().Linear Ticket: OMN-1133
Changes
New Components
ContractHandlerDiscovery (
src/omnibase_infra/runtime/contract_handler_discovery.py)HandlerPluginLoaderandProtocolBindingRegistryProtocolHandlerDiscovery (
src/omnibase_infra/runtime/protocol_handler_discovery.py)discover_and_register(contract_paths)interfaceDiscovery Models (
src/omnibase_infra/models/runtime/)ModelDiscoveryResult: Tracks handlers discovered/registered with errors/warningsModelDiscoveryError: Structured error tracking with error codesModelDiscoveryWarning: Non-fatal warning trackingRuntimeHostProcess Integration
contract_paths: list[str] | Noneparameter to__init__start()if paths providedwire_default_handlers()when no paths givenTest Coverage
15 unit tests for
ContractHandlerDiscovery14 integration tests for
RuntimeHostProcessdiscoveryTest plan
Dependencies
Summary by CodeRabbit
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.