Skip to content

refactor(runtime): extract Registration-specific code to domain plugin [OMN-1346] - #154

Merged
jonahgabriel merged 7 commits into
mainfrom
jonah/omn-1346-extract-registration-specific-code-from-runtime-layer
Jan 15, 2026
Merged

jonahgabriel merged 7 commits into
mainfrom
jonah/omn-1346-extract-registration-specific-code-from-runtime-layer

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Jan 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Moves Registration-specific code from the generic runtime layer to the Registration domain (nodes/node_registration_orchestrator/), implementing a plugin architecture for domain-specific initialization.

  • Extract dispatchers, wiring functions, and event router to Registration domain
  • Implement ProtocolDomainPlugin for domain-specific initialization hooks
  • Add PluginRegistration implementing the plugin protocol
  • Refactor kernel to use plugin hooks instead of hardcoded Registration init

Changes

Component Before After
Dispatchers runtime/dispatchers/ nodes/node_registration_orchestrator/dispatchers/
Event Router runtime/introspection_event_router.py nodes/node_registration_orchestrator/introspection_event_router.py
Wiring container_wiring.py (embedded) nodes/node_registration_orchestrator/wiring.py
Init kernel.py (hardcoded) PluginRegistration via plugin hooks

New Architecture

src/omnibase_infra/
├── runtime/                            ← GENERIC ONLY
│   ├── kernel.py                       ← Uses plugin hooks
│   ├── container_wiring.py             ← Delegation functions only
│   └── protocol_domain_plugin.py       ← Plugin protocol
│
└── nodes/node_registration_orchestrator/
    ├── dispatchers/                    ← Domain-specific dispatchers
    ├── introspection_event_router.py   ← Domain-specific router
    ├── wiring.py                       ← Domain wiring functions
    └── plugin.py                       ← PluginRegistration

Test plan

  • Runtime tests: 1950 passed, 2 failed (pre-existing), 8 skipped
  • Registration tests: 238 passed (100%)
  • No Registration imports in core runtime files (except delegation/plugin discovery)
  • All acceptance criteria from OMN-1346 verified

Related

  • Closes OMN-1346
  • Unblocks OMN-1140 (Intelligence ONEX conversion)
  • Unblocks OMN-1142 (Qdrant/Memgraph handlers)

Summary by CodeRabbit

  • Refactor

    • Moved registration into a dedicated, plugin-driven domain and removed its prior exports from the runtime root to enable modular startup/shutdown.
  • New Features

    • Added a Registration domain plugin with lifecycle hooks, public wiring/routing APIs, event-bus‑agnostic routing, and plugin config/result models.
  • Bug Fixes / Ops

    • Made PostgreSQL index creation idempotent and added a correlation_id partial index; bumped infra union threshold 121 → 122.
  • Tests

    • Added unit tests for plugin shutdown order, registry behavior, and shutdown resilience.

✏️ Tip: You can customize this high-level summary in your review settings.

…n [OMN-1346]

Move Registration-specific code from the generic runtime layer to the
Registration domain (nodes/node_registration_orchestrator/).

Changes:
- Move dispatchers to nodes/node_registration_orchestrator/dispatchers/
- Move introspection_event_router.py to Registration domain
- Extract wire_registration_handlers() to domain wiring.py
- Extract wire_registration_dispatchers() to domain wiring.py
- Implement ProtocolDomainPlugin for domain-specific initialization
- Add PluginRegistration implementing the plugin protocol
- Refactor kernel.py to use plugin hooks instead of hardcoded init
- Update container_wiring.py to delegate to domain wiring functions
- Split domain plugin models per ONEX architecture conventions

The runtime layer now contains only generic infrastructure code.
Other domains (like Intelligence) can implement their own plugins.
@linear

linear Bot commented Jan 15, 2026

Copy link
Copy Markdown

OMN-1346

@coderabbitai

coderabbitai Bot commented Jan 15, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jonahgabriel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c6b052e and 158e5c3.

📒 Files selected for processing (4)
  • src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_result.py
📝 Walkthrough

Walkthrough

Adds a domain-plugin system and moves registration orchestration into a registration domain: PluginRegistration, wiring/dispatchers, router, route IDs, and kernel plugin-driven bootstrap; plus plugin models/protocols, tests, and minor DB/validation tweaks.

Changes

Cohort / File(s) Summary
Plugin framework & models
src/omnibase_infra/runtime/protocol_domain_plugin.py, src/omnibase_infra/runtime/models/model_domain_plugin_config.py, src/omnibase_infra/runtime/models/model_domain_plugin_result.py, src/omnibase_infra/runtime/models/__init__.py
Adds ProtocolDomainPlugin and RegistryDomainPlugin, plus ModelDomainPluginConfig and ModelDomainPluginResult dataclasses and exposes them in models init.
Kernel bootstrap -> plugin-driven
src/omnibase_infra/runtime/kernel.py, src/omnibase_infra/runtime/__init__.py, src/omnibase_infra/runtime/container_wiring.py
Replaces inline registration/projector wiring with plugin registry and plugin lifecycle loop (activate → initialize → wire_handlers → wire_dispatchers → start_consumers). Delegates registration wiring to the registration domain.
Registration domain package
src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py, src/omnibase_infra/nodes/node_registration_orchestrator/*
Exposes registration domain public API: dispatchers, IntrospectionEventRouter, PluginRegistration, route IDs, wiring functions, and handler/getter adapters.
Registration plugin implementation
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
New PluginRegistration implementing domain lifecycle: env-gated activation, Postgres pool & projector initialization, optional Consul, wiring helpers, consumer startup/unsubscribe management, shutdown and cleanup.
Wiring & routing
src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py
New wiring module defining ROUTE_ID_* constants, wire_registration_handlers, wire_registration_dispatchers, getters for handlers/projection reader, and validation/error semantics for container wiring.
Dispatchers & router relocation
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/*, src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
Moved dispatcher imports/paths into registration domain; updated docstrings and type-checking to be event-bus-agnostic.
Runtime wrappers
src/omnibase_infra/runtime/container_wiring.py
container_wiring functions now delegate to registration domain wiring (wire_registration_handlers, wire_registration_dispatchers) and return WiringResult where applicable.
Tests
tests/unit/runtime/test_domain_plugin_shutdown.py, tests/unit/validation/test_validator_defaults.py
Adds plugin shutdown/registry tests; updates INFRA_MAX_UNIONS expectation from 121 to 122.
DB & infra tweaks
src/omnibase_infra/idempotency/store_postgres.py, src/omnibase_infra/validation/infra_validators.py
Make index creation idempotent, add partial index on correlation_id, and bump INFRA_MAX_UNIONS constant to 122.

Sequence Diagram(s)

sequenceDiagram
    participant Kernel
    participant PluginRegistration
    participant PostgreSQL as Postgres
    participant EventBus
    participant DispatchEngine as MessageDispatchEngine

    Kernel->>PluginRegistration: should_activate(config)
    alt plugin activates
        Kernel->>PluginRegistration: initialize(config)
        PluginRegistration->>Postgres: create_pool() / init_schema()
        PluginRegistration->>PluginRegistration: _load_projector()
        PluginRegistration-->>Kernel: initialize result
        Kernel->>PluginRegistration: wire_handlers(config)
        PluginRegistration->>DispatchEngine: register handlers (wiring)
        Kernel->>PluginRegistration: wire_dispatchers(config)
        PluginRegistration->>DispatchEngine: register dispatchers & routes
        Kernel->>PluginRegistration: start_consumers(config)
        PluginRegistration->>EventBus: start consumer(s)
        PluginRegistration-->>Kernel: unsubscribe callbacks
    end

    Kernel->>Kernel: run

    Kernel->>PluginRegistration: shutdown(config)
    PluginRegistration->>EventBus: stop consumers (call unsubscribe)
    PluginRegistration->>Postgres: close_pool()
    PluginRegistration-->>Kernel: shutdown result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰
A tiny rabbit wires the flow,
Plugins sprout where kernels grow,
Postgres hums and dispatchers sing,
Shutdown hops back in LIFO spring,
A nuzzle for the new wiring show!


Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

Code Review: Registration Domain Plugin Extraction (OMN-1346)

Overview

This is an excellent refactoring that successfully implements the domain plugin architecture. The PR extracts Registration-specific code from the generic runtime kernel into a clean plugin pattern following dependency inversion principles.

Overall Assessment: ✅ APPROVED with minor suggestions

The code quality is high, follows ONEX patterns, and significantly improves the separation of concerns.


Strengths

1. Clean Architecture ✅

  • Excellent separation of concerns: Runtime kernel (kernel.py) is now truly generic
  • Plugin protocol (ProtocolDomainPlugin) provides clear lifecycle hooks
  • Dependency inversion: Kernel depends on abstract protocol, not concrete implementations
  • Explicit registration: No magic discovery, audit-friendly plugin loading

2. Strong Typing & Models ✅

  • Zero Any types - fully compliant with ONEX policy
  • ModelDomainPluginResult with custom __bool__ for idiomatic checks
  • Proper use of | None (PEP 604) throughout
  • TYPE_CHECKING guards prevent circular imports

3. Error Handling ✅

  • Comprehensive error context with correlation IDs
  • Proper cleanup on failure (_cleanup_on_failure)
  • Graceful degradation (PostgreSQL primary, Consul optional)
  • Error sanitization prevents credential leakage

4. Documentation ✅

  • Extensive docstrings with examples
  • Clear lifecycle documentation in protocol
  • Bootstrap sequence well-documented in kernel.py:346-408
  • Related ticket references (OMN-1346, OMN-888)

Security Review

Resource Management ✅

  • PostgreSQL pool: Properly closed in shutdown hook (plugin.py:720-735)
  • Event consumers: Unsubscribe callbacks tracked and invoked (kernel.py:981-990)
  • Consul handler: Optional, gracefully handles init failures
  • Finally blocks: Comprehensive cleanup guards (kernel.py:1117-1173)

Error Exposure ✅

  • All errors sanitized via sanitize_error_message()
  • No password/DSN leakage in logs (plugin.py:196-203 uses env vars safely)
  • Correlation IDs enable tracing without exposing sensitive data

Performance Considerations

Bootstrap Timing ✅

  • Parallel operations: Not used, but sequential is acceptable for bootstrap
  • Timing instrumentation: Comprehensive time.time() tracking throughout
  • Connection pooling: PostgreSQL pool sized appropriately (min=2, max=10)

Recommendation: Consider adding timing thresholds with warnings:

if duration > 5.0:
    logger.warning("Slow plugin initialization: %.2fs", duration)

Potential Issues & Suggestions

1. Plugin Shutdown Order ⚠️ Minor

Location: kernel.py:1043-1066

Plugins are shut down in reverse order (reversed(activated_plugins)), which is correct. However, if plugin B depends on resources from plugin A, this could fail.

Suggestion: Consider adding inter-plugin dependency tracking or document that plugins must be self-contained.

2. Projector Discovery Path ⚠️ Minor

Location: plugin.py:270-273

Path resolution uses Path(__file__).parent.parent.parent / "projectors". This is fragile if package structure changes.

Suggestion: Use a constant or environment variable:

PROJECTOR_CONTRACTS_DIR = os.getenv(
    "ONEX_PROJECTOR_CONTRACTS_DIR",
    str(Path(__file__).parent.parent.parent / "projectors" / "contracts")
)

3. Schema Initialization Idempotency ⚠️ Minor

Location: plugin.py:356-396

Schema init logs warning if schema already exists, which is expected behavior. This may create noise in logs.

Suggestion: Catch specific exceptions for "table already exists" and log at DEBUG level instead.

4. Type Narrowing in wire_handlers 🔍 Observation

Location: plugin.py:502-503

Good type narrowing for dictionary value:

services_value = registration_summary.get("services", [])
services_list = services_value if isinstance(services_value, list) else []

Observation: This suggests registration_summary["services"] has type list[str] | str. Consider refining the return type of wire_registration_handlers to always return list[str] for the "services" key.

5. Protocol Compliance Verification ✅ Excellent

Location: plugin.py:770-771

# Verify protocol compliance at module load time
_: ProtocolDomainPlugin = PluginRegistration()

This is excellent! Catches protocol violations immediately at import time.


ONEX Pattern Compliance

Pattern Status Notes
No Any types ✅ Zero violations detected
Strong typing ✅ `
Error hierarchy ✅ ProtocolConfigurationError, RuntimeHostError
Container injection ✅ ModelONEXContainer used throughout
Correlation IDs ✅ Tracked across all operations
Declarative nodes ✅ Plugin delegates to domain wiring
No backwards compat ✅ Breaking change, properly documented

Test Coverage

From PR description:

  • ✅ Runtime tests: 1950 passed
  • ✅ Registration tests: 238 passed (100%)
  • ✅ No Registration imports in core runtime (verified via grep)

Recommendation: Add integration test for plugin failure scenarios:

  • Plugin initialization fails mid-bootstrap
  • Plugin shutdown fails during cleanup
  • Multiple plugins activated with interdependencies

Code Quality Metrics

  • Lines changed: +2479 / -829 (net +1650)
  • Files modified: 15
  • New abstractions: 3 (ProtocolDomainPlugin, RegistryDomainPlugin, PluginRegistration)
  • Documentation: Excellent (comprehensive docstrings)
  • Complexity: Low (clear lifecycle, single responsibility)

Migration Impact

Breaking Changes ✅ Properly Documented

From runtime/__init__.py diff:

  • Removed: DispatcherNodeIntrospected, DispatcherRuntimeTick, DispatcherNodeRegistrationAcked
  • Moved to: nodes.node_registration_orchestrator.dispatchers

Import Path Changes

Old:

from omnibase_infra.runtime.dispatchers import DispatcherNodeIntrospected

New:

from omnibase_infra.nodes.node_registration_orchestrator.dispatchers import (
    DispatcherNodeIntrospected,
)

Note: Per ONEX policy, no migration guide required. Downstream consumers expected to update immediately.


Final Recommendations

Must Fix

None - code is production-ready as-is.

Should Consider

  1. Add timing thresholds for slow plugin initialization warnings
  2. Make projector contracts directory configurable via environment variable
  3. Refine schema init error handling to reduce log noise

Nice to Have

  1. Add integration tests for plugin failure scenarios
  2. Document plugin dependency ordering constraints
  3. Add example of creating a custom domain plugin

Conclusion

This PR demonstrates excellent software engineering:

  • Clean architectural separation via plugin pattern
  • Comprehensive error handling and resource cleanup
  • Strong typing with zero Any violations
  • Production-ready documentation

The refactoring successfully achieves OMN-1346 goals and unblocks future domain plugins (Intelligence, Observability, etc.).

Recommendation: APPROVE and MERGE


Reviewer: Claude Sonnet 4.5
Correlation ID:
Review Date: 2026-01-15

… [OMN-1346]

- Add plugin shutdown order documentation with LIFO constraint
- Make projector contracts path configurable via ONEX_PROJECTOR_CONTRACTS_DIR
- Handle schema idempotency errors at DEBUG level (not WARNING)
- Add WiringResult TypedDict for precise return typing

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/omnibase_infra/runtime/kernel.py`:
- Around line 684-690: The logging extra currently uses a truthiness check on
wire_result which calls its __bool__ (e.g., ModelDomainPluginResult.success) and
can hide services_registered when wiring returned an instance with
success=False; update the ternary to explicitly check for a non-None result
(e.g., use "wire_result.services_registered if wire_result is not None else []")
or, if you want empty list on failure, use "wire_result.services_registered if
getattr(wire_result, 'services_registered', None) is not None and
wire_result.success else []" so that the code inspects the actual presence of
the ModelDomainPluginResult instance (wire_result) and/or its success flag
before accessing services_registered in the logging extra.
- Around line 904-922: The current check conflates None, failed, and skipped
consumer_result states; update the plugin consumer startup handling around
plugin.start_consumers/consumer_result so it explicitly branches: first handle
consumer_result is None (log warning with "unknown" correlation_id), then handle
consumer_result.success == False (log warning including
consumer_result.error_message), then handle
consumer_result.unsubscribe_callbacks truthy (extend
plugin_unsubscribe_callbacks and emit the debug log referencing plugin.plugin_id
and correlation_id), and finally handle the success-but-no-callbacks case (emit
an info or debug that plugin.plugin_id started zero consumers/skipped). Ensure
references to consumer_result.error_message, consumer_result.success, and
unsubscribe_callbacks are only accessed in the branches where they are defined.
🧹 Nitpick comments (1)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (1)

656-663: isinstance check for KafkaEventBus.

Per coding guidelines, protocol resolution should use duck typing via protocols, not isinstance checks. However, this is checking a concrete class to determine if Kafka-specific consumer subscription is appropriate.

Consider checking for a capability (e.g., hasattr(config.event_bus, 'subscribe') with async signature) rather than type, though the current approach is pragmatic since InMemoryEventBus may have different subscription semantics.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 8840fd8 and 4239a3d.

📒 Files selected for processing (15)
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py
  • src/omnibase_infra/runtime/__init__.py
  • src/omnibase_infra/runtime/container_wiring.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/runtime/models/__init__.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_config.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_result.py
  • src/omnibase_infra/runtime/protocol_domain_plugin.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Never use Any type - use object for generic payloads in function parameters and return types
Use X | None (PEP 604) instead of Optional[X] for nullable types
All services must use ModelONEXContainer for dependency injection via __init__(self, container: ModelONEXContainer)
Use @allow_any decorator with documented reason as exemption mechanism for Any type violations
Use JsonType from omnibase_core.types as the canonical type alias for JSON-compatible values
Use ModelEventEnvelope[object] for generic dispatcher interfaces and object for generic payloads
Infrastructure error handling must use OnexError base class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
Use InfraConnectionError, InfraTimeoutError, InfraAuthenticationError, InfraUnavailableError for transport failures with proper ModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated with uuid4() if missing, and included in all error contexts
External service adapters must implement MixinAsyncCircuitBreaker with appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never use isinstance checks

Files:

  • src/omnibase_infra/runtime/protocol_domain_plugin.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_config.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py
  • src/omnibase_infra/runtime/models/__init__.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py
  • src/omnibase_infra/runtime/__init__.py
  • src/omnibase_infra/runtime/container_wiring.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_result.py
**/protocol_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

File naming convention: Protocols must use protocol_<name>.py or protocols.py with class name Protocol<Name>

Files:

  • src/omnibase_infra/runtime/protocol_domain_plugin.py
**/model_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/model_*.py: File naming convention: Models must use model_<name>.py with class name Model<Name>
Each model file must contain exactly one Model* class
Pydantic workaround for Any type must include # NOTE: comment documenting the reason when technically required
Use SerializeAsAny type wrapper for Pydantic fields containing complex nested models to preserve subclass fields during serialization

Files:

  • src/omnibase_infra/runtime/models/model_domain_plugin_config.py
  • src/omnibase_infra/runtime/models/model_domain_plugin_result.py
**/model_*result*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Result models may override __bool__ to enable idiomatic conditional checks, with required Warning section in docstring explaining non-standard behavior

Files:

  • src/omnibase_infra/runtime/models/model_domain_plugin_result.py
🧠 Learnings (23)
📚 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_domain_plugin.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/runtime/models/model_domain_plugin_config.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/runtime/models/__init__.py
📚 Learning: 2026-01-15T13:12:08.552Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.552Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use ModelONEXContainer (not ModelContainer) in node constructors for dependency injection

Applied to files:

  • src/omnibase_infra/runtime/models/model_domain_plugin_config.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/models/**/*.py : Every Pydantic model MUST have an explicit model_config ConfigDict; empty ConfigDict is forbidden. Immutable models must use ConfigDict(frozen=True, extra='forbid', from_attributes=True)

Applied to files:

  • src/omnibase_infra/runtime/models/model_domain_plugin_config.py
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/nodes/**/*.py : Import mixins from omnibase_core.mixins.* and use Mixin* naming pattern (e.g., MixinHealthCheck, MixinMetrics, MixinEventBus) - never use local custom mixins unless experimental and documented

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py
  • src/omnibase_infra/runtime/__init__.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 event bus mixins from `omnibase_core` for Kafka publishing instead of direct Kafka clients

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/runtime/kernel.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/nodes/node_orchestrator.py : ORCHESTRATOR nodes CANNOT return typed results; they can only emit events and intents. Only COMPUTE nodes return typed results. This enforces separation between coordination and transformation.

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/nodes/**/*.py : All nodes in omninode_bridge MUST use omnibase_core standards (ModelServiceEffect, ModelServiceCompute for effect/compute nodes; NodeOrchestrator, NodeReducer with mixins for orchestrator/reducer nodes)

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.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: All ONEX nodes must use the node_kafka_event_bus as a secondary reference only for complex backend and event bus logic and advanced configuration patterns

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.py
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/events/**/*.py : Kafka event publishing MUST use OnexEnvelopeV1 format with 13 topics for event streaming at all workflow lifecycle stages

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.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: Node Introspection Security - use Python reflection for service discovery, but prefix internal/sensitive methods with `_`, use generic parameter names, and configure Kafka topic ACLs for introspection topics

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.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_*.py : Handlers must NOT have direct event bus access - only orchestrators may have bus parameters and publish events

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.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 **/node.py : All nodes must extend base classes from `omnibase_core.nodes` (NodeOrchestrator, NodeEffect, NodeCompute, NodeReducer) with container injection and no custom logic in node.py

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.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/nodes/node_registration_orchestrator/__init__.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py
  • src/omnibase_infra/runtime/__init__.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 : Use `ModelEventEnvelope[object]` for generic dispatcher interfaces and `object` for generic payloads

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.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 communication must use event-driven patterns through `ModelEventEnvelope` from `omnibase_core.models.events.model_event_envelope`

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.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 implementations must use mixin-based composition from `omnibase_core.mixins` (e.g., `MixinHealthCheck`, `MixinNodeExecutor`) to add capabilities

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/nodes/node_orchestrator.py : Implement NodeOrchestrator with ModelAction Pattern for lease-based single-writer semantics and workflow-driven actions

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/__init__.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/runtime/models/__init__.py
📚 Learning: 2026-01-15T13:12:08.552Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.552Z
Learning: Applies to src/omnibase_core/**/*.py : Use protocol names (e.g., 'ProtocolEventBus', 'ProtocolLogger') for service resolution, not concrete class names

Applied to files:

  • src/omnibase_infra/runtime/kernel.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/models/**/*.py : Use ModelEventEnvelope for inter-service event communication with structured event metadata and routing

Applied to files:

  • src/omnibase_infra/runtime/kernel.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/**/*.py : SPI MUST NOT import from omnibase_infra (no imports, even transitively)

Applied to files:

  • src/omnibase_infra/runtime/__init__.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/effect/**/*.py : Use handler envelopes from `omnibase_infra` for all I/O operations (HTTP, database, Kafka) instead of custom clients

Applied to files:

  • src/omnibase_infra/runtime/__init__.py
🧬 Code graph analysis (9)
src/omnibase_infra/runtime/protocol_domain_plugin.py (3)
src/omnibase_infra/runtime/models/model_domain_plugin_config.py (1)
  • ModelDomainPluginConfig (51-90)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (1)
  • ModelDomainPluginResult (50-241)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (8)
  • plugin_id (124-126)
  • display_name (129-131)
  • should_activate (148-168)
  • initialize (170-258)
  • wire_handlers (475-540)
  • wire_dispatchers (542-626)
  • start_consumers (628-717)
  • shutdown (719-772)
src/omnibase_infra/runtime/models/model_domain_plugin_config.py (3)
src/omnibase_infra/event_bus/inmemory_event_bus.py (1)
  • InMemoryEventBus (64-709)
src/omnibase_infra/event_bus/kafka_event_bus.py (1)
  • KafkaEventBus (208-2318)
src/omnibase_infra/runtime/message_dispatch_engine.py (1)
  • MessageDispatchEngine (308-1787)
src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py (1)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py (1)
  • DispatcherNodeIntrospected (96-366)
src/omnibase_infra/runtime/models/__init__.py (2)
src/omnibase_infra/runtime/models/model_domain_plugin_config.py (1)
  • ModelDomainPluginConfig (51-90)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (1)
  • ModelDomainPluginResult (50-241)
src/omnibase_infra/runtime/kernel.py (6)
src/omnibase_infra/runtime/container_wiring.py (1)
  • wire_infrastructure_services (198-352)
src/omnibase_infra/runtime/registry/registry_protocol_binding.py (1)
  • ProtocolBindingRegistry (130-438)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (1)
  • ModelDomainPluginResult (50-241)
src/omnibase_infra/runtime/protocol_domain_plugin.py (12)
  • ProtocolDomainPlugin (131-329)
  • RegistryDomainPlugin (332-417)
  • register (368-390)
  • get_all (403-409)
  • should_activate (199-219)
  • plugin_id (175-186)
  • display_name (189-197)
  • initialize (221-249)
  • wire_handlers (251-278)
  • wire_dispatchers (280-298)
  • start_consumers (300-312)
  • clear (411-413)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (10)
  • PluginRegistration (91-785)
  • should_activate (148-168)
  • plugin_id (124-126)
  • display_name (129-131)
  • initialize (170-258)
  • wire_handlers (475-540)
  • wire_dispatchers (542-626)
  • start_consumers (628-717)
  • get_status_line (774-785)
  • shutdown (719-772)
src/omnibase_infra/nodes/node_registration_orchestrator/protocols.py (1)
  • correlation_id (148-158)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (7)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py (1)
  • DispatcherNodeIntrospected (96-366)
src/omnibase_infra/runtime/models/model_domain_plugin_config.py (1)
  • ModelDomainPluginConfig (51-90)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (4)
  • ModelDomainPluginResult (50-241)
  • failed (176-210)
  • skipped (213-241)
  • succeeded (133-173)
src/omnibase_infra/runtime/protocol_domain_plugin.py (9)
  • ProtocolDomainPlugin (131-329)
  • plugin_id (175-186)
  • display_name (189-197)
  • should_activate (199-219)
  • initialize (221-249)
  • wire_handlers (251-278)
  • wire_dispatchers (280-298)
  • start_consumers (300-312)
  • shutdown (314-329)
src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py (1)
  • wire_registration_handlers (355-505)
src/omnibase_infra/runtime/container_wiring.py (1)
  • wire_registration_handlers (760-838)
src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py (2)
  • IntrospectionEventRouter (85-432)
  • handle_message (195-432)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py (3)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py (1)
  • DispatcherNodeIntrospected (96-366)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py (1)
  • DispatcherNodeRegistrationAcked (96-366)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py (1)
  • DispatcherRuntimeTick (95-363)
src/omnibase_infra/runtime/container_wiring.py (1)
src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py (6)
  • wire_registration_handlers (355-505)
  • get_projection_reader_from_container (513-532)
  • get_handler_node_introspected_from_container (535-554)
  • get_handler_runtime_tick_from_container (557-576)
  • get_handler_node_registration_acked_from_container (579-600)
  • wire_registration_dispatchers (153-347)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (2)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (1)
  • plugin_id (124-126)
src/omnibase_infra/runtime/protocol_domain_plugin.py (1)
  • plugin_id (175-186)
🔇 Additional comments (43)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py (2)

53-54: LGTM!

Cross-reference to OMN-1346 properly documents the refactoring context.


122-124: LGTM!

Import path in docstring example correctly updated to reflect the new module location.

src/omnibase_infra/runtime/__init__.py (2)

134-138: LGTM!

Clear migration guidance for developers looking for the relocated Registration dispatchers. The comment correctly identifies the new import locations.


269-270: LGTM!

Documentation in __all__ section provides inline context about the moved items.

src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py (1)

49-51: LGTM!

TYPE_CHECKING import correctly updated to reference the dispatcher from within the same domain package. This improves cohesion now that both the router and dispatcher reside in node_registration_orchestrator.

src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py (2)

54-55: LGTM!

Cross-reference to OMN-1346 properly documents the refactoring context.


121-123: LGTM!

Import path in docstring example correctly updated to reflect the new module location.

src/omnibase_infra/runtime/models/__init__.py (3)

11-12: LGTM!

Docstring entries clearly describe the purpose of the new domain plugin models.


46-51: LGTM!

Imports for the new domain plugin models follow the established pattern and are properly organized.


102-103: LGTM!

New model exports added to __all__ in alphabetical order, consistent with the rest of the list.

src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py (1)

53-53: LGTM!

The cross-reference addition (Line 53) and updated import path in the docstring example (Lines 122-124) correctly reflect the relocation of this dispatcher to the Registration domain. Documentation is consistent with the PR's architectural changes.

Also applies to: 122-124

src/omnibase_infra/runtime/protocol_domain_plugin.py (3)

130-172: LGTM! Well-designed protocol with duck typing.

The @runtime_checkable decorator enables isinstance checks while maintaining structural typing. The lifecycle order is clearly documented, and the protocol methods align with the kernel bootstrap sequence described in the PR objectives. Based on learnings, this follows the recommended pattern of using Protocol for plugin APIs.


332-417: LGTM! Clean registry implementation.

The registry correctly:

  • Documents non-thread-safe behavior with clear usage guidance
  • Returns a defensive copy from get_all() to prevent external mutation
  • Raises ValueError with clear message for duplicate registrations
  • Provides clear() method for test isolation

420-425: LGTM!

The __all__ correctly includes both the locally-defined protocol/registry and the re-exported models from the models subpackage. This provides a convenient single import location for plugin-related types.

src/omnibase_infra/runtime/models/model_domain_plugin_config.py (1)

50-91: LGTM! Appropriate use of dataclass for internal configuration.

The ModelDomainPluginConfig correctly:

  • Uses @dataclass for a simple configuration container (Pydantic would add unnecessary overhead for internal wiring)
  • Follows PEP 604 union syntax (InMemoryEventBus | KafkaEventBus, MessageDispatchEngine | None)
  • Uses TYPE_CHECKING guard to avoid circular imports while maintaining type safety
  • Documents mutability and thread-safety considerations

The naming follows the model_<name>.py convention per coding guidelines.

src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py (1)

39-53: LGTM!

The import paths correctly reference the local dispatcher modules within the node_registration_orchestrator.dispatchers package. The __all__ export list is complete with all three dispatchers, and the documentation updates reflect the new module structure.

src/omnibase_infra/runtime/container_wiring.py (3)

760-838: LGTM! Clean delegation to Registration domain.

The wire_registration_handlers function correctly:

  • Maintains backward-compatible public signature
  • Uses deferred import to avoid circular dependencies
  • Delegates all parameters to the Registration domain implementation
  • Documents the delegation pattern in the docstring

This follows the domain-driven design principle established in the PR objectives.


841-951: LGTM!

The getter functions (get_projection_reader_from_container, get_handler_node_introspected_from_container, get_handler_runtime_tick_from_container, get_handler_node_registration_acked_from_container) all follow a consistent delegation pattern with appropriate documentation noting the OMN-1346 reference.


954-1027: LGTM!

The wire_registration_dispatchers function correctly delegates to the Registration domain while maintaining comprehensive documentation including prerequisites, engine frozen behavior, and usage examples.

src/omnibase_infra/runtime/models/model_domain_plugin_result.py (4)

49-93: Well-structured result model with proper mutable default handling.

The dataclass correctly uses field(default_factory=list) for mutable defaults (resources_created, services_registered, unsubscribe_callbacks), avoiding the common Python gotcha of shared mutable defaults.


94-130: Compliant __bool__ override with required Warning section.

The __bool__ override follows coding guidelines for result models by including a comprehensive Warning section in the docstring explaining the non-standard behavior. The examples clearly demonstrate both the idiomatic usage and alternatives for explicit checks.


132-173: Factory method succeeded() is appropriately minimal.

The docstring correctly notes that results requiring additional fields (like resources_created) should use the constructor directly. This prevents API misuse where callers might expect succeeded() to accept all fields.


175-242: failed() and skipped() factory methods correctly implement plugin lifecycle semantics.

  • failed() correctly defaults message to "Plugin {plugin_id} failed" when not provided
  • skipped() sets success=True, treating plugin skipping as a non-failure outcome (not an error)
  • The kernel's conditional checks (if not result: for failures, if result: for success) correctly leverage the __bool__ implementation that returns self.success
src/omnibase_infra/nodes/node_registration_orchestrator/__init__.py (2)

48-87: Clean import organization for relocated domain components.

The imports are well-organized into logical groups (dispatchers, event router, plugin, wiring) with clear OMN-1346 comments indicating the refactoring context. The deferred grouping pattern keeps related imports together.


89-120: __all__ exports align with imports.

All newly imported symbols are properly exported in __all__. The categorization comments in __all__ match the import groupings, aiding discoverability.

src/omnibase_infra/runtime/kernel.py (5)

423-426: Plugin infrastructure initialization is well-placed.

The plugin registry and tracking lists are initialized before the try block, ensuring they're available for cleanup in the finally block regardless of where failures occur.


610-626: Deferred import and explicit plugin registration.

Good design choice to use deferred import for PluginRegistration and explicit registration rather than magic discovery. This keeps the kernel's dependencies explicit and testable.


645-665: Partial activation continues after initialization failure.

The loop correctly skips plugins that fail initialization (continue on line 654). This allows other plugins to activate even if one fails, which is appropriate for graceful degradation.


1042-1077: Well-documented LIFO shutdown order with self-containment constraint.

The shutdown documentation clearly explains the LIFO rationale and the critical constraint that plugins must be self-contained during shutdown. This prevents subtle ordering bugs.


1165-1184: Cleanup config uses minimal container/event_bus is intentional and safe.

The cleanup_config creates fresh minimal instances (ModelONEXContainer(), InMemoryEventBus()) for shutdown. Since PluginRegistration.shutdown() only accesses config.correlation_id from the config (for logging), passing minimal instances is safe and appropriate for cleanup phase.

src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (6)

116-146: Clean plugin initialization with typed optional resources.

The constructor initializes all resource references to None, and properties provide typed access. The TYPE_CHECKING imports keep runtime imports minimal while maintaining type safety.


194-219: DSN construction and pool creation.

The DSN is constructed from environment variables and used directly in asyncpg.create_pool(). The host is logged but the DSN itself (containing password) is not logged, which is correct. The pool size defaults (min=2, max=10) are reasonable for a registration service.


359-417: Schema initialization with proper idempotent handling.

The duplicate table/object errors are correctly caught and logged at DEBUG level, treating them as expected idempotent behavior. The import of asyncpg.exceptions at runtime is appropriate since it's only needed in the error handling path.


457-473: Cleanup on failure properly resets all resource references.

The _cleanup_on_failure method closes the pool if created and resets all resource references to None. This prevents resource leaks on partial initialization failures.


719-772: Shutdown correctly handles partial cleanup.

The shutdown method:

  1. Closes the pool with error handling
  2. Clears all resource references
  3. Aggregates errors and returns failure result if any occurred

This ensures cleanup completes even if individual operations fail.


788-789: Protocol compliance verification at module load.

The assignment _: ProtocolDomainPlugin = PluginRegistration() verifies at import time that PluginRegistration satisfies the protocol. This is a useful pattern for catching protocol mismatches early.

src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py (7)

47-56: WiringResult TypedDict provides precise typing.

Using a TypedDict for the return value eliminates the need for type narrowing in callers and makes the API contract explicit. Good pattern for internal wiring results.


80-102: Route ID constants with comprehensive docstrings.

Each route constant includes topic pattern, message type, and category documentation. This aids discoverability and ensures consistency when routes are referenced elsewhere.


105-151: Validation helper duplicated intentionally to avoid circular imports.

The _validate_service_registry function is a local copy as noted in the docstring. The detailed hints in the error messages help diagnose common configuration issues.


232-341: Dispatcher wiring with clear error propagation.

The function:

  1. Validates prerequisites before imports (fail-fast)
  2. Creates adapters for all three handlers
  3. Registers dispatchers and routes with clear structure
  4. Provides actionable error message on failure

The exception handler correctly suggests calling wire_registration_handlers first.


355-505: Handler wiring registers services with metadata.

Services are registered with descriptive metadata including version, capabilities (has_projector, has_consul_handler), and configuration (liveness_interval_seconds). This aids debugging and introspection.


513-600: Getter functions provide consistent error messages.

Each getter function validates the service registry and provides a consistent error message pattern suggesting wire_registration_handlers be called first. The cast() calls are appropriate since resolve_service returns a generic type.


603-618: __all__ exports all public symbols.

All public constants, functions, and types are properly exported. The categorization comments help navigation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread src/omnibase_infra/runtime/kernel.py
Comment thread src/omnibase_infra/runtime/kernel.py
@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

PR Review: Registration Plugin Architecture Extraction

Overall Assessment

APPROVED with recommendations - This is a well-executed refactoring that successfully extracts Registration-specific code from the generic runtime layer into a domain plugin architecture. The implementation demonstrates strong adherence to SOLID principles and the codebase's declarative patterns.


Strengths

1. Excellent Separation of Concerns

The plugin pattern properly implements dependency inversion - the kernel depends on ProtocolDomainPlugin (abstraction) rather than concrete PluginRegistration (implementation).

2. Strong Type Safety

  • No Any types detected (compliant with CLAUDE.md zero-tolerance policy)
  • Proper use of TypedDict for WiringResult
  • Correct use of TYPE_CHECKING guards to avoid circular imports
  • ModelDomainPluginResult implements custom __bool__ per ADR conventions

3. Robust Error Handling

  • Proper error sanitization throughout
  • Graceful degradation (Consul failure doesn't break PostgreSQL flow)
  • Cleanup on partial initialization failure
  • Clear error contexts with correlation IDs

Issues Found

🔴 CRITICAL: Missing Test Coverage for Plugin System

Impact: HIGH - Zero test coverage for:

  • Plugin lifecycle (initialize → wire_handlers → wire_dispatchers → start_consumers → shutdown)
  • Plugin activation conditions (should_activate())
  • Error handling during plugin initialization
  • Resource cleanup on failure
  • Registry operations (register, get, duplicate detection)

Recommendation: Add comprehensive test coverage for PluginRegistration, ProtocolDomainPlugin, and RegistryDomainPlugin before merge.


⚠️ SECURITY: Database Password Handling

Location: src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py:196-201

Issues:

  1. Empty password default allows connection attempts with no password
  2. DSN may leak to logs if asyncpg raises exceptions
  3. No validation that POSTGRES_PASSWORD is set when POSTGRES_HOST is set

Recommendation: Use asyncpg.create_pool() with separate parameters instead of DSN string, and validate POSTGRES_PASSWORD is set.


⚠️ CODE QUALITY: Duplicate Validation Logic

Location: src/omnibase_infra/nodes/node_registration_orchestrator/wiring.py:105-150

Code duplication to avoid circular imports. Extract to shared utility module omnibase_infra/utils/util_container_validation.py.


🟡 PERFORMANCE: Environment Variable Reads

Environment variables are read multiple times. Cache in ModelDomainPluginConfig for startup performance and testability.


🟡 OBSERVABILITY: Incomplete Metric Tracking

Plugin lifecycle methods track duration but don't emit metrics. Add Prometheus/StatsD emission for initialization time, error rates, and resource counts.


Code Quality Score: 8.5/10

Deductions: -1.0 (missing tests), -0.5 (password security)

Strengths: Excellent separation of concerns, strong type safety, comprehensive documentation, proper error handling


Final Verdict

This PR successfully achieves its goal of extracting Registration code into a plugin architecture. The design is sound and extensible. However, the lack of test coverage for fundamental plugin infrastructure is a significant gap.

Recommendation: Merge after adding test coverage and fixing the password security issue. Other issues can be addressed in follow-up PRs.

Great work on the architecture! The plugin pattern will enable clean integration of Intelligence and other domains. 🎯

CI Fixes:
- Fix event bus test failures by adding `if activated_plugins:` guard
- Update union threshold 121→122 for domain plugin extraction

PR Review Fixes:
- Plugin shutdown: Add protocol documentation and 10 new tests
- Projector discovery: Replace fragile parent counting with package root
- Schema idempotency: Fix docstring to match IF NOT EXISTS implementation
- Kernel None checks: Use explicit `is not None` at lines 690, 922, 1078
- KafkaEventBus isinstance: Replace with hasattr + ProtocolEventBusLike

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/omnibase_infra/runtime/kernel.py`:
- Around line 1065-1096: The shutdown loop may leave activated_plugins unchanged
if an exception occurs, causing the later finally block to re-run shutdowns and
double-shutdown plugins; change the loop in the main shutdown sequence to remove
each plugin as it is processed (e.g., use a while activated_plugins: plugin =
activated_plugins.pop() to process in LIFO order or explicitly remove plugin
from activated_plugins after successful/failed shutdown handling) so that
activated_plugins.clear() in the normal path is no longer required and the
finally block will only see unprocessed plugins; ensure the same removal
approach is applied to the backup/final shutdown loop that references
activated_plugins to avoid duplicate shutdown calls.
- Around line 646-676: The code currently does falsy checks like "if not
init_result" then accesses init_result.error_message which raises AttributeError
when the result is None; update the three checks for init_result, wire_result,
and dispatcher_result to use explicit None checks and safe logging: replace "if
not X" with "if X is None" and when logging include either a fallback message or
branch to log X.error_message only if X is not None (e.g., if wire_result is
None: logger.warning('Plugin %s handler wiring failed: no result
(correlation_id=%s)', plugin.plugin_id, correlation_id) else:
logger.warning('Plugin %s handler wiring failed: %s (correlation_id=%s)',
plugin.plugin_id, wire_result.error_message, correlation_id)); apply the same
pattern for initialize, wire_handlers, and wire_dispatchers (init_result,
wire_result, dispatcher_result).
🧹 Nitpick comments (1)
src/omnibase_infra/runtime/kernel.py (1)

919-933: Logic conflates None result with failed result.

The current implementation has confusing control flow. When consumer_result.success=False, the object is truthy-falsy (evaluates to False in boolean context due to __bool__), so not consumer_result is True. However, consumer_result is not None on line 925 would then be True, making the ternary always return consumer_result.error_message.

The branch never handles an actual None result clearly. Consider restructuring for clarity:

♻️ Suggested restructure
-            elif not consumer_result:
-                # Use explicit None check for type narrowing - consumer_result may be
-                # a failed result (success=False) which is falsy but still has valid
-                # error_message. We only use "unknown" when consumer_result is None.
-                error_msg = (
-                    consumer_result.error_message
-                    if consumer_result is not None
-                    else "unknown"
-                )
+            elif consumer_result is None:
+                logger.warning(
+                    "Plugin %s consumer startup returned None (correlation_id=%s)",
+                    plugin.plugin_id,
+                    correlation_id,
+                )
+            elif not consumer_result.success:
                 logger.warning(
                     "Plugin %s consumer startup failed: %s (correlation_id=%s)",
                     plugin.plugin_id,
-                    error_msg,
+                    consumer_result.error_message or "unknown",
                     correlation_id,
                 )
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 4239a3d and 96c9ec6.

📒 Files selected for processing (9)
  • src/omnibase_infra/idempotency/store_postgres.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
  • src/omnibase_infra/runtime/container_wiring.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/runtime/protocol_domain_plugin.py
  • src/omnibase_infra/validation/infra_validators.py
  • tests/unit/runtime/test_domain_plugin_shutdown.py
  • tests/unit/validation/test_validator_defaults.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Never use Any type - use object for generic payloads in function parameters and return types
Use X | None (PEP 604) instead of Optional[X] for nullable types
All services must use ModelONEXContainer for dependency injection via __init__(self, container: ModelONEXContainer)
Use @allow_any decorator with documented reason as exemption mechanism for Any type violations
Use JsonType from omnibase_core.types as the canonical type alias for JSON-compatible values
Use ModelEventEnvelope[object] for generic dispatcher interfaces and object for generic payloads
Infrastructure error handling must use OnexError base class and never expose passwords, API keys, PII, or connection strings with credentials in error messages
Use InfraConnectionError, InfraTimeoutError, InfraAuthenticationError, InfraUnavailableError for transport failures with proper ModelInfraErrorContext
Correlation IDs must be propagated from incoming requests, auto-generated with uuid4() if missing, and included in all error contexts
External service adapters must implement MixinAsyncCircuitBreaker with appropriate threshold and reset_timeout configuration
Protocol resolution must use duck typing via protocols, never use isinstance checks

Files:

  • src/omnibase_infra/validation/infra_validators.py
  • src/omnibase_infra/idempotency/store_postgres.py
  • tests/unit/validation/test_validator_defaults.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.py
  • src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py
  • src/omnibase_infra/runtime/container_wiring.py
  • src/omnibase_infra/runtime/protocol_domain_plugin.py
  • tests/unit/runtime/test_domain_plugin_shutdown.py
**/protocol_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

File naming convention: Protocols must use protocol_<name>.py or protocols.py with class name Protocol<Name>

Files:

  • src/omnibase_infra/runtime/protocol_domain_plugin.py
🧠 Learnings (15)
📚 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 event bus mixins from `omnibase_core` for Kafka publishing instead of direct Kafka clients

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.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: Node Introspection Security - use Python reflection for service discovery, but prefix internal/sensitive methods with `_`, use generic parameter names, and configure Kafka topic ACLs for introspection topics

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
📚 Learning: 2026-01-15T13:12:08.552Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.552Z
Learning: Applies to src/omnibase_core/**/*.py : Use protocol names (e.g., 'ProtocolEventBus', 'ProtocolLogger') for service resolution, not concrete class names

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.py
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/nodes/**/*.py : Import mixins from omnibase_core.mixins.* and use Mixin* naming pattern (e.g., MixinHealthCheck, MixinMetrics, MixinEventBus) - never use local custom mixins unless experimental and documented

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.py
📚 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: Implement Kafka event-driven architecture with proper topic naming using prefix dev.archon-intelligence. and proper event flow pattern with Effect nodes consuming events, processing, and publishing results with Dead Letter Queue routing

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/models/**/*.py : Use ModelEventEnvelope for inter-service event communication with structured event metadata and routing

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.py
📚 Learning: 2025-11-29T17:13:38.776Z
Learnt from: CR
Repo: OmniNode-ai/omniarchon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-29T17:13:38.776Z
Learning: Use event-driven architecture with Kafka topics for asynchronous processing: enrichment, code analysis, manifest processing, and entity embedding pipelines.

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/events/**/*.py : Kafka event publishing MUST use OnexEnvelopeV1 format with 13 topics for event streaming at all workflow lifecycle stages

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
📚 Learning: 2025-12-03T16:55:49.755Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T16:55:49.755Z
Learning: Applies to agents/**/*.py : Use routing_event_client from agents/lib/routing_event_client.py for agent routing via Kafka with route_via_events() function

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.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 : Use `ModelEventEnvelope[object]` for generic dispatcher interfaces and `object` for generic payloads

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.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: All ONEX nodes must use the node_kafka_event_bus as a secondary reference only for complex backend and event bus logic and advanced configuration patterns

Applied to files:

  • src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py
  • src/omnibase_infra/runtime/kernel.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_*.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/kernel.py
📚 Learning: 2026-01-15T13:12:08.552Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.552Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use ModelONEXContainer (not ModelContainer) in node constructors for dependency injection

Applied to files:

  • src/omnibase_infra/runtime/container_wiring.py
📚 Learning: 2026-01-15T13:12:08.553Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T13:12:08.553Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Distinguish between ModelContainer[T] (value wrapper) and ModelONEXContainer (dependency injection); ModelContainer must NEVER be used in node __init__ constructors

Applied to files:

  • src/omnibase_infra/runtime/container_wiring.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_domain_plugin.py
🧬 Code graph analysis (2)
src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py (2)
src/omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py (1)
  • DispatcherNodeIntrospected (96-366)
src/omnibase_infra/runtime/runtime_host_process.py (1)
  • output_topic (545-551)
src/omnibase_infra/runtime/protocol_domain_plugin.py (4)
src/omnibase_infra/runtime/models/model_domain_plugin_config.py (1)
  • ModelDomainPluginConfig (51-90)
src/omnibase_infra/runtime/models/model_domain_plugin_result.py (1)
  • ModelDomainPluginResult (50-241)
src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (8)
  • plugin_id (158-160)
  • display_name (163-165)
  • should_activate (182-202)
  • initialize (204-292)
  • wire_handlers (511-576)
  • wire_dispatchers (578-662)
  • start_consumers (664-754)
  • shutdown (756-809)
tests/unit/runtime/test_domain_plugin_shutdown.py (8)
  • plugin_id (47-49)
  • display_name (52-54)
  • should_activate (56-58)
  • initialize (60-65)
  • wire_handlers (67-72)
  • wire_dispatchers (74-79)
  • start_consumers (81-86)
  • shutdown (88-99)
🔇 Additional comments (28)
src/omnibase_infra/validation/infra_validators.py (1)

384-397: LGTM! Well-documented threshold bump.

The threshold history comment properly documents the ticket reference (OMN-1346), date, and rationale (+1 non-optional union from extract registration domain plugin). The increment from 121 to 122 is consistent with the documented history pattern.

tests/unit/validation/test_validator_defaults.py (1)

64-70: LGTM! Test expectation properly synchronized.

The threshold history comment and assertion are correctly updated to match the new INFRA_MAX_UNIONS value of 122. The assertion message accurately reflects the expected value and the exclusion of X | None patterns.

src/omnibase_infra/idempotency/store_postgres.py (2)

27-30: LGTM! Docstring accurately documents idempotent index creation.

The schema documentation now correctly reflects the IF NOT EXISTS pattern for all indexes, and the new partial index on correlation_id is appropriately documented with the WHERE correlation_id IS NOT NULL clause. This aligns with the implementation in _ensure_table_exists.


345-368: LGTM! Idempotent index creation with appropriate partial index optimization.

The changes provide two improvements:

  1. Idempotent initialization: Using CREATE INDEX IF NOT EXISTS ensures safe re-initialization without errors, supporting the broader domain-plugin architecture's emphasis on deterministic resource setup.

  2. Storage optimization: The partial index on correlation_id (lines 358-362) only indexes non-null values, which is appropriate since correlation_id is optional. This saves storage and improves insert performance for records without correlation IDs while still enabling efficient lookups for distributed tracing queries.

src/omnibase_infra/runtime/protocol_domain_plugin.py (3)

130-172: LGTM! Well-designed protocol with comprehensive documentation.

The @runtime_checkable decorator enables isinstance checks against the protocol while maintaining duck typing semantics. The protocol follows structural typing best practices as recommended in the learnings, and the lifecycle documentation is thorough with clear ordering guarantees.


381-466: Registry implementation is clean and follows explicit registration pattern.

The RegistryDomainPlugin correctly documents thread-safety limitations and provides the expected registry operations. The duplicate registration check with a clear error message is helpful for debugging.

One minor note: the clear() method is documented as "useful for testing" - consider if this should be protected or have additional safeguards in production code, though for the current use case it appears appropriate.


469-474: Verify all exports include all expected types.

The exports include ModelDomainPluginConfig and ModelDomainPluginResult which are imported from omnibase_infra.runtime.models but re-exported here. This is a reasonable convenience pattern, but ensure consumers are aware these types originate from the models module.

src/omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py (3)

50-54: Good alignment with duck typing guidelines.

The import changes correctly move DispatcherNodeIntrospected to its new domain location and introduce ProtocolEventBusLike for type hints. This follows the coding guideline: "Protocol resolution must use duck typing via protocols, never use isinstance checks."


119-134: Type annotation update is correct.

The event_bus parameter and attribute are now typed as ProtocolEventBusLike, enabling any compliant event bus implementation to be used. The docstrings are updated consistently.


341-345: publish_envelope method is correctly available on ProtocolEventBusLike.

The method signature at lines 74-78 of protocol_event_bus_like.py matches the usage: async def publish_envelope(self, envelope: object, topic: str) -> None:. The call at lines 341-345 passes the correct parameters, and the protocol documentation confirms this is the preferred method when available.

src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py (6)

106-122: Good approach for robust path resolution.

Using the package root via omnibase_infra.__file__ instead of counting parent directories is more robust against internal restructuring. The environment variable override provides necessary flexibility for different deployment layouts.


228-253: Consider masking password in DSN construction for logs.

The DSN is constructed with POSTGRES_PASSWORD and while it's not logged directly here, ensure this pattern doesn't inadvertently expose credentials elsewhere. The logging at line 245-252 correctly only logs host/port/database, not the password.


395-453: Well-handled idempotent schema initialization.

The schema initialization correctly handles DuplicateTableError and DuplicateObjectError at DEBUG level, treating them as expected for idempotent operations. This follows best practices for database migrations.


694-700: Duck typing check aligns with coding guidelines.

Using hasattr(config.event_bus, "subscribe") instead of isinstance check correctly follows the coding guideline: "Protocol resolution must use duck typing via protocols, never use isinstance checks."


756-809: Shutdown implementation follows self-contained constraint.

The shutdown method correctly:

  1. Handles pool closure errors gracefully without re-raising
  2. Clears all resource references to None after cleanup
  3. Returns failed result with error details instead of raising

This aligns with the documented constraint in ProtocolDomainPlugin.shutdown.


825-826: Good practice: Protocol compliance verification at module load.

The assignment _: ProtocolDomainPlugin = PluginRegistration() verifies structural compatibility with the protocol at import time, catching interface mismatches early.

src/omnibase_infra/runtime/container_wiring.py (3)

842-871: Delegation wrappers maintain backward compatibility.

The get_*_from_container functions now serve as thin wrappers that delegate to the Registration domain. This maintains the existing public API while keeping domain-specific code in its proper location.


955-1028: Dispatcher wiring delegation is consistent.

The wire_registration_dispatchers function follows the same delegation pattern. The comprehensive docstring is preserved, providing good context for consumers.


761-839: Clean delegation pattern for domain separation.

The function now delegates entirely to the Registration domain wiring module while preserving the same public API. The docstring correctly documents this architectural decision with reference to OMN-1346.

The return type changed from dict[str, list[str] | str] to WiringResult (a TypedDict with services: list[str] and status: str fields). TypedDict maintains backward compatibility with dictionary key access, so all existing callers that use summary["services"] and summary["status"] patterns continue to work without modification. The import is in place and the delegation pattern successfully separates concerns.

tests/unit/runtime/test_domain_plugin_shutdown.py (4)

31-100: Well-designed mock plugin for testing.

The MockPlugin class correctly implements all ProtocolDomainPlugin methods and includes useful testing features:

  • Shared shutdown order tracking via _shutdown_order list
  • Configurable error injection via _shutdown_error
  • Call tracking via _shutdown_called flag

This enables comprehensive testing of plugin lifecycle behavior.


102-166: Good test coverage for LIFO shutdown order and error resilience.

The tests verify:

  1. test_shutdown_order_is_lifo: Confirms reversed iteration produces expected LIFO order
  2. test_shutdown_continues_after_plugin_error: Validates that errors in one plugin don't block others

These tests directly validate the documented shutdown constraints in ProtocolDomainPlugin.shutdown.


229-245: Documentation verification test is valuable.

Testing that the protocol docstring contains expected keywords (LIFO, self-contained, MUST NOT depend) ensures documentation stays synchronized with implementation. This is a good practice for maintaining protocol contracts.


294-320: Integration test validates minimal cleanup config pattern.

This test verifies that the kernel's finally-block cleanup pattern works correctly - creating a minimal ModelDomainPluginConfig for shutdown even in error scenarios. This is important for ensuring graceful degradation.

src/omnibase_infra/runtime/kernel.py (5)

77-85: LGTM!

The new imports for the domain plugin architecture are well-organized. Using protocol types (ProtocolDomainPlugin, RegistryDomainPlugin) and model types (ModelDomainPluginConfig, ModelDomainPluginResult) aligns with the coding guidelines for protocol resolution via duck typing.


423-427: LGTM!

Plugin infrastructure variables are properly typed and initialized before the try block, ensuring they're available for cleanup in the finally block.


593-626: LGTM!

The plugin setup is well-documented with clear rationale. The deferred import prevents circular dependencies, and explicit plugin registration (rather than magic discovery) makes the code more maintainable and debuggable.


939-948: LGTM!

The duck-typing approach with hasattr for the optional get_status_line method aligns with the coding guidelines for protocol resolution without isinstance checks.


1053-1096: Excellent shutdown documentation and implementation.

The LIFO shutdown order with clear constraints is well-documented. The explicit None check pattern on lines 1078-1082 correctly handles the case where shutdown_result could be None or a failed result.

However, for consistency with the suggested refactor in consumer startup, consider the same pattern here:

💡 Optional: Consistent pattern suggestion
-                else:
-                    # Use explicit None check for type narrowing - shutdown_result may
-                    # be a failed result (success=False) which is falsy but still has
-                    # valid error_message. We only use "unknown" when it's None.
-                    error_msg = (
-                        shutdown_result.error_message
-                        if shutdown_result is not None
-                        else "unknown"
-                    )
+                elif shutdown_result is None:
+                    logger.warning(
+                        "Plugin %s shutdown returned None (correlation_id=%s)",
+                        plugin.plugin_id,
+                        correlation_id,
+                    )
+                else:  # shutdown_result.success is False
                     logger.warning(
                         "Plugin %s shutdown failed: %s (correlation_id=%s)",
                         plugin.plugin_id,
-                        error_msg,
+                        shutdown_result.error_message or "unknown",
                         correlation_id,
                     )

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread src/omnibase_infra/runtime/kernel.py
Comment thread src/omnibase_infra/runtime/kernel.py Outdated
…fety fixes [OMN-1346]

- Resolve merge conflict in kernel.py by accepting domain plugin architecture
- Fix double-shutdown of plugins using pop() pattern to prevent re-processing
  in finally block if exception occurs mid-shutdown
- Fix potential AttributeError when accessing .error_message on None results
  by using explicit None checks with safe fallback for init_result, wire_result,
  and dispatcher_result
- Verify MAJOR issues already addressed:
  - Schema Initialization Idempotency: catches DuplicateTableError/DuplicateObjectError
  - Projector Discovery Path: uses package root via _get_default_projector_contracts_dir()
@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

Code Review: Registration Domain Plugin Extraction [OMN-1346]

Overview

This PR successfully extracts Registration-specific code from the generic runtime layer into a domain plugin architecture. The refactoring demonstrates strong architectural discipline with excellent separation of concerns.

✅ Strengths

  1. Excellent Architecture & Design

    • Plugin pattern with clear lifecycle hooks (should_activate, initialize, wire_handlers, shutdown)
    • Dependency inversion: kernel depends on abstract ProtocolDomainPlugin protocol
    • LIFO shutdown order with proper resource cleanup
    • Self-contained plugins with clear documentation
  2. Strong Type Safety

    • WiringResult TypedDict provides precise typing without Any
    • @runtime_checkable decorator enables protocol validation
    • Consistent patterns: all domain types moved together
  3. Comprehensive Documentation

    • Detailed module docstrings explaining the extraction
    • Clear lifecycle documentation
    • OMN-1346 references make refactoring traceable
  4. Test Coverage

    • test_domain_plugin_shutdown.py validates LIFO order
    • 238 Registration tests: 100% passing
    • 1950 Runtime tests: only 2 pre-existing failures

⚠️ Issues & Concerns

1. Security: Credential Exposure Risk (CRITICAL)

Location: src/omnibase_infra/nodes/node_registration_orchestrator/plugin.py:233

Problem: DSN strings with embedded credentials can leak in exception messages, logs, and stack traces.

Recommendation: Use asyncpg.create_pool() with separate parameters to prevent credential leakage since asyncpg sanitizes parameters internally.

2. Error Handling: Resource Cleanup (HIGH)

Location: plugin.py:247 (initialize method)

Verification Needed:

  • Does _cleanup_on_failure() properly close the asyncpg pool?
  • Are there resource leaks if Consul handler init fails?
  • Is cleanup idempotent (safe to call multiple times)?

3. Path Traversal: Projector Contract Directory (MEDIUM)

Location: plugin.py:286-291

Problem: ONEX_PROJECTOR_CONTRACTS_DIR env var is directly converted to Path without validation. An attacker with env var control could read arbitrary YAML files.

Recommendation: Add path validation to ensure contracts directory is under expected package root.

4. Code Quality: Magic Numbers (LOW)

Location: plugin.py:237

Pool sizes (min_size=2, max_size=10) should be extracted to constants or made configurable via environment variables.

🎯 CLAUDE.md Compliance

All rules passing: No Any types, container injection, protocol resolution, error sanitization, correlation ID propagation, proper file naming, and no backwards compatibility as per policy.

🎬 Final Verdict

Overall Assessment: APPROVE with CRITICAL changes required

This is a well-architected refactoring that successfully decouples Registration logic. The plugin pattern is clean, testable, and extensible.

Must Fix Before Merge:

  1. ✋ BLOCKER: Replace DSN string with separate asyncpg.create_pool() parameters

Strongly Recommended:
2. Add path validation for ONEX_PROJECTOR_CONTRACTS_DIR
3. Verify _cleanup_on_failure() handles all resources
4. Add timeout to pool creation

Nice to Have:
5. Extract pool size constants
6. Add plugin dependency metadata for multi-plugin scenarios
7. Document plugin registration workflow

💡 Lessons for Future Plugins

This PR establishes excellent patterns for Intelligence (OMN-1140) and Qdrant/Memgraph (OMN-1142):

  • ✅ Self-contained lifecycle management
  • ✅ Clear protocol contracts
  • ✅ Comprehensive error handling
  • ✅ Test coverage for shutdown resilience

Great work on this refactoring! Address the credential exposure concern and this will be a solid foundation for the plugin ecosystem.


Review by Claude Code following CLAUDE.md and ONEX architecture standards

… [OMN-1346]

Fix mypy error by passing graceful_mode through ModelProjectorPluginLoaderConfig
instead of as a direct keyword argument to ProjectorPluginLoader.__init__.
@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

Pull Request Review: Registration Domain Plugin Extraction (OMN-1346)

Summary

This PR successfully implements a plugin architecture to extract Registration-specific code from the generic runtime layer. The refactoring follows solid architectural principles and demonstrates excellent attention to ONEX conventions.


✅ Strengths

1. Excellent Architectural Design

  • Plugin Pattern Implementation: The ProtocolDomainPlugin protocol provides a clean abstraction for domain-specific initialization
  • Dependency Inversion: Runtime kernel depends on the protocol, not concrete implementations
  • LIFO Shutdown Order: Proper cleanup sequence (Last In, First Out) with double-shutdown prevention (lines 1090-1122 in kernel.py)
  • Self-Contained Constraint: Plugins manage their own resources during shutdown

2. Strong Type Safety

  • No Any type violations detected
  • Proper use of TypedDict for WiringResult
  • Comprehensive type annotations throughout
  • Proper use of TYPE_CHECKING for circular import prevention

3. Robust Error Handling

  • Graceful Degradation: Plugins failing to initialize don't crash the kernel
  • Error Sanitization: Consistent use of sanitize_error_message() to prevent credential leakage
  • Structured Error Context: All errors include correlation_id for tracing
  • Resource Cleanup: Proper cleanup in _cleanup_on_failure() and finally blocks

4. ONEX Compliance

  • ✅ Container-Based DI: All services use ModelONEXContainer
  • ✅ Protocol-Based Resolution: Duck typing with protocols, no isinstance checks
  • ✅ Declarative Nodes: Registration orchestrator remains declarative (contract-driven)
  • ✅ No Backwards Compatibility: Clean removal of old patterns

5. Excellent Documentation

  • Comprehensive docstrings with examples
  • Clear lifecycle hook explanations
  • Architectural diagrams in comments
  • ADR references and cross-links

6. Test Coverage

  • Plugin shutdown order tests (test_domain_plugin_shutdown.py)
  • Error resilience tests
  • Registry behavior verification
  • Integration test preservation (1950 runtime tests, 238 registration tests)

🔍 Observations & Minor Issues

1. Explicit None Checks (Not a Bug, Just Verbose)

Lines 650-660, 667-677, 684-696 in kernel.py:

if init_result is None or not init_result.success:
    error_msg = (
        init_result.error_message if init_result is not None else "unknown"
    )

Observation: The pattern is correct but verbose. Since ModelDomainPluginResult methods return non-None results, the explicit is None checks are defensive but likely unnecessary. The code comments explain this is for type narrowing, which is valid.

Recommendation: Consider adding a helper method to ModelDomainPluginResult:

def get_error_message_or_default(self, default: str = "unknown") -> str:
    return self.error_message if self.error_message else default

2. Contract Directory Path Calculation

plugin.py lines 106-119:

def _get_default_projector_contracts_dir() -> Path:
    import omnibase_infra
    package_root = Path(omnibase_infra.__file__).parent
    return package_root / "projectors" / "contracts"

Issue: Uses omnibase_infra.__file__ which may not work in certain deployment scenarios (e.g., frozen executables, certain packaging tools).

Recommendation: Consider using importlib.resources for robust resource path resolution:

from importlib.resources import files
return files("omnibase_infra").joinpath("projectors/contracts")

3. Schema Idempotency Error Handling

plugin.py lines 442-456:

duplicate_errors = (
    asyncpg.exceptions.DuplicateTableError,
    asyncpg.exceptions.DuplicateObjectError,
)
if isinstance(schema_error, duplicate_errors):
    logger.debug("Schema already initialized (idempotent, correlation_id=%s)", ...)

Observation: This violates the "duck typing, never isinstance" rule from CLAUDE.md. However, this is an exception-handling scenario where checking exception types is standard practice.

Recommendation: Document why isinstance is acceptable here (exception handling is a standard use case) or consider a protocol-based approach if you want strict adherence.

4. Windows Signal Handling

kernel.py lines 848-862:

def windows_handler(signum: int, frame: object) -> None:
    sig = signal.Signals(signum)
    logger.info("Received %s...", sig.name, correlation_id)
    loop.call_soon_threadsafe(shutdown_event.set)

Issue: The logger.info call includes correlation_id as a positional argument, but it should be in extra.

Fix:

logger.info(
    "Received %s, initiating graceful shutdown... (correlation_id=%s)",
    sig.name,
    correlation_id,
)

🛡️ Security Considerations

1. Credential Sanitization

  • ✅ Excellent: Consistent use of sanitize_error_message() throughout
  • ✅ DSN Handling: PostgreSQL DSN constructed safely (line 231-236 in plugin.py)

2. Plugin Discovery

  • ✅ Explicit Registration: Plugins registered explicitly (line 616 in kernel.py), no magic discovery
  • ✅ No Untrusted Code: Plugin loading is controlled, not file-system based

📊 Performance Considerations

1. Bootstrap Timing

  • ✅ Proper Metrics: All phases tracked with timing (config load, event bus, container wiring, plugin init)
  • ✅ Parallel Potential: Plugins initialized sequentially but could be parallelized in future if dependencies allow

2. Resource Pooling

  • ✅ PostgreSQL Pool: Properly configured with min_size=2, max_size=10 (line 239-243 in plugin.py)
  • ✅ Connection Reuse: Pool lifecycle managed by plugin

🧪 Test Coverage Analysis

Test Files Analyzed:

  • test_domain_plugin_shutdown.py: Plugin lifecycle and shutdown order ✅
  • PR description mentions: 1950 runtime tests passed, 238 registration tests passed ✅

Coverage Assessment: Excellent

  • Unit tests for shutdown order (LIFO)
  • Error resilience tests
  • Integration tests preserved
  • Acceptance criteria verified

🎯 Recommendations

High Priority

  1. Fix Windows signal handler logging (kernel.py:856)
  2. Review CI check status (ensure all checks passing)

Medium Priority

  1. Consider importlib.resources for path resolution (plugin.py:117)
  2. Add helper method for error message extraction (reduces verbosity)

Low Priority

  1. Document exception isinstance usage (why it's acceptable in schema error handling)

🚀 Final Verdict

Approve with Minor Comments

This is high-quality work that demonstrates:

  • Strong architectural thinking (plugin pattern, dependency inversion)
  • Excellent ONEX compliance (type safety, container DI, protocols)
  • Robust error handling and resource management
  • Comprehensive documentation and testing

The identified issues are minor and don't block merging. The refactoring successfully achieves the goal of extracting Registration code from the runtime layer while maintaining backward compatibility (in terms of functionality, not API - per ONEX policy).

Recommended Action: ✅ Approve (with optional follow-up fixes for minor issues)


📝 Checklist

  • ✅ Code quality and best practices: Excellent
  • ✅ Potential bugs: 1 minor logging issue found
  • ✅ Performance: Well-optimized with proper pooling
  • ✅ Security: Excellent credential sanitization
  • ✅ Test coverage: Comprehensive (1950 + 238 tests passing)
  • ✅ ONEX compliance: Fully compliant with CLAUDE.md

Reviewed by: Claude Sonnet 4.5
Date: 2026-01-15

…ss [OMN-1346]

- Add get_error_message_or_default() helper to ModelDomainPluginResult
- Update contract directory path to use importlib.resources for robustness
- Document isinstance usage for exception handling (standard practice)
- Add structured logging extra dict to Windows signal handler
@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

PR #154 Review: Registration Domain Plugin Architecture

Executive Summary

This PR successfully extracts Registration-specific code from the kernel into a domain plugin architecture. The implementation follows ONEX architecture principles with strong type safety, proper error handling, and clean separation of concerns. The code quality is excellent overall.

Overall Assessment: APPROVED ✅


1. ONEX Architecture Compliance ✅

Strong Typing & Type Safety (EXCELLENT)

  • ✅ No Any types detected - All types are properly specified
  • ✅ PEP 604 unions consistently used (X | None not Optional[X])
  • ✅ Pydantic models for all data structures
  • ✅ Type guards using explicit None checks (kernel.py:1102-1108)
  • ✅ Proper typing for callbacks: list[Callable[[], Awaitable[None]]]

Container-Based Dependency Injection (EXCELLENT)

  • ✅ Container properly passed through plugin config
  • ✅ Service registry validation with clear error messages
  • ✅ Async service resolution patterns correctly implemented

Protocol Usage (GOOD with minor note)

  • ✅ ProtocolDomainPlugin properly defined with @runtime_checkable
  • ✅ Duck typing for event bus subscribe check (plugin.py:721-723)
  • ⚠️ Minor: Two isinstance checks could use comments

plugin.py:375 - Type narrowing after runtime discovery:

# Suggestion: Add comment
# Type narrowing after runtime discovery - isinstance acceptable here
if isinstance(discovered, ProjectorShell):
    self._projector = discovered

plugin.py:466 - Exception handling - Already documented properly ✅

Error Handling (EXCELLENT)

  • ✅ Consistent use of sanitize_error_message() to prevent credential leakage
  • ✅ Proper error context with correlation IDs
  • ✅ Error propagation with ModelDomainPluginResult.failed()
  • ✅ Graceful degradation patterns

2. Code Quality

Plugin Architecture Design (EXCELLENT)

  • Clean separation of concerns - Kernel is generic, domain-specific logic in plugins
  • Well-defined lifecycle hooks with clear ordering
  • Self-contained constraint properly documented
  • LIFO shutdown order with double-shutdown prevention using while/pop()

Resource Management (EXCELLENT)

  • ✅ Proper cleanup on failure
  • ✅ Safe resource nulling
  • ✅ Error recovery during shutdown
  • ✅ Memory leak prevention

Potential Issue - Concurrent Shutdown Protection

kernel.py:1091-1123 - Plugin shutdown uses while/pop() which prevents double-shutdown in the kernel, but individual plugins should also guard against concurrent shutdown.

Recommendation for plugin.py:

class PluginRegistration:
    def __init__(self) -> None:
        self._pool: asyncpg.Pool | None = None
        self._shutdown_in_progress = False  # Add this
        
    async def shutdown(self, config: ModelDomainPluginConfig) -> ModelDomainPluginResult:
        if self._shutdown_in_progress:
            return ModelDomainPluginResult.skipped(
                plugin_id=self.plugin_id,
                reason="Shutdown already in progress"
            )
        self._shutdown_in_progress = True
        try:
            # ... existing shutdown logic ...
        finally:
            self._shutdown_in_progress = False

3. Security (EXCELLENT)

Password Handling ✅

plugin.py:250-255 - PostgreSQL DSN construction is secure:

  • ✅ DSN only used locally, never logged
  • ✅ All error messages use sanitize_error_message()
  • ✅ No credential leakage in error paths

Plugin Isolation (GOOD)

  • Each plugin manages its own resources
  • No shared state between plugins
  • Services registered with scope="global" (acceptable, but worth documenting potential conflicts)

Recommendation for wiring.py:355:

"""
Note: Services are registered with scope="global" and may conflict if
multiple plugins register the same interface. Use domain-specific
interface types to ensure isolation.
"""

4. Input Validation

Good Coverage

  • ✅ should_activate() checks for POSTGRES_HOST
  • ✅ Port validation in kernel.py:885-904
  • ✅ Graceful handling of missing optional config

Minor Issue - Pool Creation Validation

plugin.py:258-262 - Should validate pool creation succeeded:

self._pool = await asyncpg.create_pool(...)
if self._pool is None:  # Add this check
    raise RuntimeError("PostgreSQL pool creation returned None")

5. Documentation (EXCELLENT)

Strengths

  1. Comprehensive module docstrings with design patterns, examples
  2. Lifecycle documentation with clear ordering
  3. Self-contained constraint well-documented
  4. Custom __bool__ behavior documented with warnings

Minor Fix Needed

protocol_domain_plugin.py:53 - Docstring refers to wrong class name:

  • Says: PluginDomainRegistry.register()
  • Should be: RegistryDomainPlugin.register()

6. Test Coverage

Good Coverage ✅

  • ✅ test_domain_plugin_shutdown.py - Comprehensive shutdown tests
  • ✅ LIFO order verification
  • ✅ Error handling during shutdown
  • ✅ Self-contained constraint verification

Recommended Additional Tests (can be post-merge)

  1. PluginRegistration lifecycle tests
    • should_activate() with/without POSTGRES_HOST
    • initialize() failure scenarios
    • shutdown() idempotency
  2. Concurrent shutdown tests
  3. Resource cleanup verification (memory leak tests)

7. Performance (EXCELLENT)

  • ✅ Timing metrics for all stages
  • ✅ Deferred imports reduce startup time
  • ✅ No runtime overhead after bootstrap
  • ✅ Connection pooling for database access
  • ✅ Efficient LIFO shutdown using pop()

Summary

Code Quality Score: 9.5/10 - Excellent work!

Strengths

  • Strong type safety (no Any types)
  • Well-designed plugin architecture
  • Comprehensive error handling and sanitization
  • Clear separation of concerns
  • Safe resource management

Recommended Actions Before Merge

  1. ✅ Add _shutdown_in_progress flag to PluginRegistration (high priority)
  2. ✅ Add explicit pool creation validation (high priority)
  3. ✅ Fix PluginDomainRegistry docstring typo (medium priority)
  4. 📝 Create issue for comprehensive PluginRegistration unit tests (can be post-merge)

Breaking Changes

Per CLAUDE.md policy, this PR correctly introduces breaking changes without backwards compatibility. Downstream consumers will need to update to the new plugin architecture.


Verdict: APPROVED ✅ - Ready to merge after addressing the two high-priority items above. The plugin architecture is clean, well-documented, and follows ONEX principles rigorously.

…OMN-1346]

- Add _shutdown_in_progress flag to prevent concurrent shutdown calls
- Extract shutdown logic to _do_shutdown() with try/finally guard
- Add pool creation validation after asyncpg.create_pool()
- Document global scope service registration in wiring.py docstring
@claude

claude Bot commented Jan 15, 2026

Copy link
Copy Markdown

PR Review: Domain Plugin Architecture for Registration Extraction

Summary

This PR successfully implements a plugin-based architecture to extract Registration-specific code from the generic runtime layer into the Registration domain. The implementation follows ONEX architectural principles and demonstrates strong adherence to SOLID principles.

Overall Assessment: APPROVE - High-quality refactoring with excellent separation of concerns, comprehensive testing, and solid error handling.

Strengths

1. Excellent Architectural Design

  • Protocol-based plugin system (ProtocolDomainPlugin) enables duck typing and eliminates tight coupling
  • Dependency inversion principle: Kernel depends on abstractions, not concrete Registration code
  • LIFO shutdown order with self-contained constraint properly documented
  • Clear lifecycle hooks: should_activate() → initialize() → wire_handlers() → wire_dispatchers() → start_consumers() → shutdown()

2. Strong ONEX Compliance

  • Zero Any types - Uses object for generic payloads (CLAUDE.md requirement met)
  • PEP 604 unions - X | None pattern throughout
  • Protocol resolution - Duck typing via ProtocolDomainPlugin instead of isinstance
  • Container-based DI - All resources injected via ModelONEXContainer
  • Error hierarchy compliance - Uses ProtocolConfigurationError, proper error context

3. Comprehensive Error Handling

  • Graceful degradation in PluginRegistration.initialize() - PostgreSQL pool validation
  • Resource cleanup with _cleanup_on_failure() for initialization failures
  • Concurrent shutdown protection via _shutdown_in_progress flag
  • Safe error accumulation in shutdown without cascading failures
  • Sanitized error messages throughout using sanitize_error_message()

4. Excellent Test Coverage

  • 10 new tests in test_domain_plugin_shutdown.py covering LIFO shutdown order, concurrent shutdown protection, registry behavior, and shutdown resilience
  • Tests verify protocol compliance at runtime
  • 1950 runtime tests passed, 238 registration tests passed (100%)

5. Robust Path Resolution

  • Uses importlib.resources for projector discovery
  • Fallback to file-based resolution for edge cases
  • Configurable via ONEX_PROJECTOR_CONTRACTS_DIR environment variable

6. Strong Documentation

  • Extensive docstrings with examples, thread safety notes, and lifecycle documentation
  • Plugin shutdown constraints clearly documented
  • WiringResult TypedDict eliminates type narrowing

Minor Recommendations (Non-blocking)

  1. Pool Validation Edge Case - asyncpg.create_pool() should raise on failure rather than return None. Consider using type assertion instead of runtime check.

  2. Explicit None Checks - Document when to use is not None vs truthy checks for Result models with custom bool

  3. isinstance Usage - Ensure CLAUDE.md clarifies when isinstance is acceptable (exception type checks) vs protocol resolution

  4. Shutdown Double-Guard - Clarify if finally block resetting flag is intentional for retry-after-failure

Security & Performance

  • No credential exposure: PostgreSQL DSN built from env vars without logging passwords
  • Error sanitization throughout
  • Lazy imports reduce startup time
  • Connection pooling configured appropriately
  • All I/O is async
  • Graceful shutdown with LIFO ordering

Breaking Changes

Properly handled per CLAUDE.md policy (no backwards compatibility maintained):

  • Dispatcher imports moved to Registration domain
  • IntrospectionEventRouter moved to Registration domain
  • All exports updated in init.py files

Verdict

APPROVE - This is an exemplary refactoring that:

  • Cleanly separates domain concerns from runtime infrastructure
  • Enables future domain additions (Intelligence, Qdrant/Memgraph) without kernel changes
  • Maintains test coverage and adds comprehensive shutdown tests
  • Follows ONEX architectural principles rigorously
  • Demonstrates excellent error handling and resource management

The minor issues raised are non-blocking and can be addressed in follow-up work if desired.

Great work on this architectural improvement!


Reviewed by Claude Code following ONEX infrastructure guidelines from CLAUDE.md

@jonahgabriel
jonahgabriel merged commit 3a69992 into main Jan 15, 2026
11 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-1346-extract-registration-specific-code-from-runtime-layer branch January 15, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant