Skip to content

feat(runtime): implement central message type registry [OMN-937] - #67

Merged
jonahgabriel merged 4 commits into
mainfrom
jonah/omn-937-b1a-implement-central-message-type-registry
Dec 20, 2025
Merged

jonahgabriel merged 4 commits into
mainfrom
jonah/omn-937-b1a-implement-central-message-type-registry

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 20, 2025 •

Copy link
Copy Markdown
Collaborator

Summary

Implements the central message type registry (B1a) that maps message types to handler implementations and enforces topic category constraints for the ONEX runtime dispatch infrastructure.

Depends on: OMN-934 (B1) Runtime Dispatch Engine ✅ Done

Unblocks: OMN-941 (B2) Handler Output Model (already done), future runtime integration work

Key Features

  • Message type to handler mapping with fan-out support (multiple handlers per message type)
  • Topic category constraints (commands/events/intents validation)
  • Startup-time validation with fail-fast behavior before consumers start
  • Domain ownership enforcement (onex..* patterns)
  • Cross-domain consumption requires explicit opt-in
  • Runtime rejection of topic/message-type domain mismatches
  • Freeze-after-init pattern for thread-safe access

Files Added

File Purpose
runtime/registry/__init__.py Module exports
runtime/registry/model_domain_constraint.py Domain ownership rules and cross-domain constraints
runtime/registry/model_message_type_entry.py Registry entry for message type to handler mappings
runtime/registry/protocol_message_type_registry.py Interface protocol for registry implementations
runtime/registry/registry_message_type.py Main implementation with validation

Test Coverage

  • 73 unit tests covering all functionality
  • Domain constraint validation tests
  • Message type entry validation tests
  • Registry operations tests (registration, queries, validation)
  • Cross-domain permission tests
  • Fail-fast behavior tests

Acceptance Criteria

  • Registry maps message type to handler implementation(s)
  • Topic category constraints defined (what message types can appear where)
  • Startup-time validation that fails fast before consumers start processing messages
  • Missing handler mappings fail fast with clear error messages
  • Registry is extensible for new domains
  • Domain ownership enforcement
  • Cross-domain consumption requires explicit opt-in
  • Runtime rejects mismatches between topic domain and message type domain

Test plan

  • All 73 new unit tests pass
  • Pre-commit hooks pass (ruff, mypy, ONEX validation)
  • CI pipeline passes

Summary by CodeRabbit

  • New Features

    • Centralized message type registry for the runtime with freeze-after-init lifecycle, domain ownership, cross-domain consumption rules, category constraints, registration/querying, and startup validation.
  • Chores

    • Consolidated JSON type aliases into a dedicated types module and re-exported them for consistent usage.
  • Tests

    • Added comprehensive unit tests for domain constraints, message-type entries, registry behavior, topic-domain extraction, and lifecycle/validation scenarios.

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

Implement message type registry that maps message types to handler
implementations and enforces topic category constraints for the
ONEX runtime dispatch infrastructure.

Key features:
- Message type to handler mapping with fan-out support
- Topic category constraints (commands/events/intents)
- Startup-time validation with fail-fast behavior
- Domain ownership enforcement (onex.<domain>.* patterns)
- Cross-domain consumption requires explicit opt-in
- Runtime rejects topic/message-type domain mismatches
- Freeze-after-init pattern for thread-safe access

New files:
- runtime/registry/model_domain_constraint.py
- runtime/registry/model_message_type_entry.py
- runtime/registry/protocol_message_type_registry.py
- runtime/registry/registry_message_type.py
- 73 unit tests covering all functionality
@linear

linear Bot commented Dec 20, 2025

Copy link
Copy Markdown

OMN-937

@coderabbitai

coderabbitai Bot commented Dec 20, 2025 •

Copy link
Copy Markdown

Walkthrough

Centralizes JSON type aliases into a new types package and introduces a Message Type Registry subsystem: protocol, immutable models (ModelDomainConstraint, ModelMessageTypeEntry), and a concrete MessageTypeRegistry implementation with domain/category validation, freeze-after-init lifecycle, and comprehensive tests.

Changes

Cohort / File(s) Summary
Type System Consolidation
src/omnibase_infra/models/types/json_types.py, src/omnibase_infra/models/types/__init__.py, src/omnibase_infra/models/dispatch/model_dispatch_result.py
New json_types module defines JsonPrimitive, JsonValue, EnvelopeDict, ResultDict (recursive PEP 695 types). Package init re-exports them. Dispatch model replaced inline aliases with imports from the new package.
Registry Protocol & Models
src/omnibase_infra/runtime/registry/protocol_message_type_registry.py, src/omnibase_infra/runtime/registry/model_domain_constraint.py, src/omnibase_infra/runtime/registry/model_message_type_entry.py
Adds ProtocolMessageTypeRegistry (runtime-checkable protocol), ModelDomainConstraint (immutable domain ownership / cross-domain rules with validation), and ModelMessageTypeEntry (immutable registry entry with handler list, categories, domain constraint, and immutable update helpers).
Registry Implementation
src/omnibase_infra/runtime/registry/registry_message_type.py, src/omnibase_infra/runtime/registry/__init__.py, src/omnibase_infra/runtime/__init__.py
Implements MessageTypeRegistry and MessageTypeRegistryError: registration (fan-out merging), freeze-after-init, thread-safe query phase, startup validation, topic/domain extraction utilities, and package-level exports updated to expose registry API.
Call-site/type imports
src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
Replaced local JSON type aliases with imports from the new types package and updated exported names.
Tests — Registry package
tests/unit/runtime/registry/__init__.py, tests/unit/runtime/registry/test_model_domain_constraint.py, tests/unit/runtime/registry/test_model_message_type_entry.py, tests/unit/runtime/registry/test_registry_message_type.py
New test package and comprehensive unit tests covering ModelDomainConstraint, ModelMessageTypeEntry, MessageTypeRegistry behaviors (registration, freeze, queries, validation), and domain extraction edge cases.
sequenceDiagram
    participant Client as Client/Handler
    participant Registry as MessageTypeRegistry
    participant Entry as ModelMessageTypeEntry
    participant Domain as ModelDomainConstraint

    rect rgb(200,220,255)
    Note over Client,Registry: Registration phase (single-threaded)
    Client->>Registry: register_message_type(entry)
    Registry->>Entry: validate entry fields
    Entry->>Domain: validate domain/category constraints
    Domain-->>Entry: result (ok/err)
    Registry->>Registry: merge handlers / update indexes
    end

    rect rgb(200,255,220)
    Note over Client,Registry: Freeze phase
    Client->>Registry: freeze()
    Registry->>Registry: lock registry (is_frozen = true)
    end

    rect rgb(255,240,200)
    Note over Client,Registry: Query phase (concurrent-safe)
    Client->>Registry: get_handlers(message_type, category, topic_domain)
    Registry->>Entry: retrieve entry
    Entry->>Domain: validate consumption (category + domain)
    Domain-->>Registry: allowed
    Registry-->>Client: list of handler_ids
    end

    rect rgb(255,220,220)
    Note over Client,Registry: Startup validation
    Client->>Registry: validate_startup(available_handlers)
    Registry->>Registry: check missing handlers, domain consistency
    Registry-->>Client: list of error strings
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas requiring extra attention:
    • MessageTypeRegistry.register_message_type(): fan-out merging, cross-registration/category/domain consistency
    • MessageTypeRegistry.freeze() and thread-safety guarantees around registration vs query phases
    • Domain extraction utilities and edge-case parsing in extract_domain_from_topic()
    • ModelDomainConstraint.validate_consumption(): branching for allow_all, allowed_cross_domains, require_explicit_opt_in
    • ModelMessageTypeEntry validators enforcing immutability, non-empty allowed_categories, and timestamp handling

Poem

🐰 I hopped through types and registry rows,
I stitched domains where the cross-flow goes,
Immutable entries, a freeze at the gate,
Handlers align and no messages wait.
Tests cheer aloud — the dispatch now grows!


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

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Code Review - Central Message Type Registry [OMN-937]

Overall Assessment ✅

This is high-quality infrastructure code that follows ONEX patterns extremely well. The implementation demonstrates:

  • Strong adherence to ONEX architectural principles
  • Comprehensive error handling with proper error hierarchy
  • Excellent thread safety through freeze-after-init pattern
  • Thorough documentation and test coverage (73 unit tests)
  • Clean separation of concerns across models, protocols, and registry

Recommendation: Approve with minor suggestions below.


Code Quality ✅ Excellent

Strengths

  1. Perfect ONEX Compliance

    • ✅ Strong typing throughout (zero Any types)
    • ✅ Proper Pydantic models with frozen=True for immutability
    • ✅ Protocol-based design (ProtocolMessageTypeRegistry)
    • ✅ Follows naming conventions (Model*, Protocol*, Registry*)
    • ✅ One model per file pattern maintained
  2. Error Handling Excellence

    • ✅ Custom MessageTypeRegistryError extends RuntimeHostError
    • ✅ Appropriate error codes (VALIDATION_FAILED, INVALID_STATE)
    • ✅ Rich context in error messages with actionable guidance
    • ✅ No credential leakage - all errors are sanitized
  3. Thread Safety Design

    • ✅ Freeze-after-init pattern correctly implemented
    • ✅ threading.Lock for registration phase protection
    • ✅ Immutable models (frozen=True) for query phase
    • ✅ Clear _require_frozen() checks on query methods
  4. Documentation Quality

    • ✅ Comprehensive docstrings with examples
    • ✅ Clear design principles documented
    • ✅ Performance characteristics noted (O(1) lookups)
    • ✅ Thread safety explicitly documented

Potential Issues 🔍 Minor

1. Domain Extraction Regex Ambiguity (Low Priority)

Location: registry_message_type.py:129-184

The regex pattern may not handle all edge cases correctly:

_DOMAIN_PATTERN = re.compile(
    r"^(?:onex\.)?(?P<env>[a-zA-Z0-9_-]+)\.(?P<domain>[a-zA-Z0-9_-]+)\.",
    re.IGNORECASE,
)

Issue: For "onex.registration.events", the named groups capture:

  • env = "onex"
  • domain = "registration"

But the extraction logic at lines 180-184 checks if env_or_domain.lower() == "onex" and then returns domain. This works but is confusing since the group names don't match their usage.

Suggestion: Consider renaming groups for clarity or simplifying the regex:

# Option 1: Simpler pattern with clearer logic
_DOMAIN_PATTERN = re.compile(
    r"^(?:onex|[a-zA-Z0-9_-]+)\.(?P<domain>[a-zA-Z0-9_-]+)\.",
    re.IGNORECASE,
)
# Then just: return match.group('domain')

# Option 2: Two separate patterns for clarity
_ONEX_PATTERN = re.compile(r"^onex\.(?P<domain>[a-zA-Z0-9_-]+)\.")
_ENV_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+\.(?P<domain>[a-zA-Z0-9_-]+)\.")

Impact: Low - current implementation works, but clearer code reduces maintenance burden.


2. Missing Validation: Empty allowed_categories (Medium Priority)

Location: model_message_type_entry.py:103

The allowed_categories field allows empty frozensets:

allowed_categories: frozenset[EnumMessageCategory] = Field(
    ...,
    description="Message categories where this type can appear...",
)

Issue: An entry with allowed_categories=frozenset() would fail all category validation, making the entry unusable.

Suggestion: Add a Pydantic validator:

from pydantic import field_validator

@field_validator('allowed_categories')
@classmethod
def validate_nonempty_categories(
    cls, 
    v: frozenset[EnumMessageCategory]
) -> frozenset[EnumMessageCategory]:
    if not v:
        raise ValueError(
            "allowed_categories must contain at least one category. "
            "Empty categories make the message type unreachable."
        )
    return v

Alternative: Add min_length=1 constraint if Pydantic supports it for frozensets (may require custom validator).


3. Inconsistent None Handling in extract_domain_from_topic (Low Priority)

Location: registry_message_type.py:161-162

if not topic:
    return None

The function signature is topic: str but line 40 in tests shows it accepts None:

assert extract_domain_from_topic(None) is None  # type: ignore[arg-type]

Issue: Using # type: ignore suggests the function shouldn't accept None, but the implementation handles it.

Suggestion: Update signature to be explicit:

def extract_domain_from_topic(topic: str | None) -> str | None:
    """Extract domain from topic string (returns None if topic is None/empty)."""
    if not topic:
        return None
    # ...

This removes the need for # type: ignore in tests and makes the API clearer.


Performance Considerations ⚡ Good

✅ Excellent performance characteristics:

  • O(1) registration and lookup via dict storage
  • O(1) domain validation
  • Efficient indexes for filtered queries (_category_index, _domain_index)
  • Startup validation is O(n) but only runs once

No performance concerns identified.


Security Concerns 🔒 Excellent

✅ No security issues found:

  • Error messages properly sanitized (no credentials exposed)
  • Domain isolation with explicit opt-in for cross-domain access
  • Immutable models prevent tampering after registration
  • Thread-safe design prevents race conditions
  • Fail-fast validation prevents misconfigurations from reaching production

Security posture is strong.


Test Coverage 🧪 Excellent (73 tests)

✅ Comprehensive test suite:

  • Domain extraction tests (ONEX and environment formats)
  • Registration tests (single, fan-out, constraint validation)
  • Query tests (handlers, entries, filtering)
  • Validation tests (startup, category, domain constraints)
  • Error condition tests (frozen registry, missing types, etc.)

Coverage appears complete based on PR description.

Minor Suggestion: Consider adding integration tests that exercise the registry with MessageDispatchEngine to validate end-to-end flow (can be follow-up work).


Suggestions for Improvement 💡

  1. Add list_categories() query method (Nice to Have)

    For consistency with list_domains() and list_handlers():

    def list_categories(self) -> list[EnumMessageCategory]:
        """List all categories that have registered message types."""
        self._require_frozen("list_categories")
        return sorted(self._category_index.keys(), key=lambda c: c.value)
  2. Consider Caching Validation Results (Future Optimization)

    If get_handlers() is called frequently with the same parameters, consider caching validation results to avoid repeated constraint checks. However, this may be premature optimization - profile first.

  3. Export Helper Function (Documentation Clarity)

    Consider exporting extract_domain_from_topic in __init__.py if it's intended for external use. Currently it's only used internally and in tests.


Architecture Alignment 🏛️ Perfect

✅ Perfect alignment with ONEX infrastructure patterns:

  • Follows runtime dispatch architecture (OMN-934 dependency)
  • Enables handler resolution for MessageDispatchEngine
  • Supports domain-driven design with isolation
  • Extensible for new domains (as required)
  • Freeze-after-init pattern for thread safety

Files Review Summary

File Assessment Notes
registry_message_type.py ✅ Excellent Core implementation is solid, minor regex clarity suggestion
model_message_type_entry.py ✅ Excellent Add empty category validation
model_domain_constraint.py ✅ Perfect No issues
protocol_message_type_registry.py ✅ Perfect Clean protocol definition
__init__.py (module) ✅ Good Proper exports
Test files ✅ Excellent Comprehensive coverage

Final Verdict ✅ APPROVE

This PR is production-ready with only minor suggestions that could be addressed in follow-up work if desired. The implementation:

  • Meets all acceptance criteria
  • Follows ONEX patterns religiously
  • Has excellent test coverage
  • Includes clear documentation
  • Demonstrates strong software engineering practices

Outstanding work on this infrastructure component! 🚀


Related Tickets

As noted in the PR description, this unblocks:

  • ✅ OMN-941 (B2) Handler Output Model (already done)
  • Future runtime integration work (B3+)

Consider verifying integration with MessageDispatchEngine in a follow-up PR or integration test suite.

- Add validation for empty allowed_categories (must have at least one)
- Fix None/empty/whitespace handling in extract_domain_from_topic
- Clarify regex patterns with separate ONEX and env-aware patterns
- Add 7 new tests for edge cases
- Bump INFRA_MAX_UNIONS threshold (451→455) pending OMN-983 refactor

Fixes: empty categories, None handling, regex ambiguity
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Central Message Type Registry [OMN-937]

Overall Assessment ✅

This is a high-quality implementation that introduces the central message type registry for ONEX runtime dispatch infrastructure. The code demonstrates excellent adherence to ONEX patterns, comprehensive test coverage, and thoughtful design. I found only minor suggestions for improvement.


Strengths 💪

1. Exceptional Documentation

  • Comprehensive module, class, and method docstrings with examples
  • Clear design rationale explained throughout
  • Thread safety requirements explicitly documented
  • Version annotations (.. versionadded:: 0.5.0)

2. Strong Type Safety

  • ✅ Zero use of Any types (ONEX requirement)
  • Proper use of frozenset and tuple for immutability
  • Protocol-based design enables duck typing
  • Type hints throughout with proper return types

3. ONEX Compliance

  • ✅ Follows freeze-after-init pattern for thread safety
  • ✅ Proper error hierarchy: MessageTypeRegistryError extends RuntimeHostError
  • ✅ File naming: model_*.py, protocol_*.py, registry_*.py
  • ✅ One model per file convention
  • ✅ Immutable models (frozen=True on Pydantic models)

4. Comprehensive Test Coverage

  • 73 unit tests covering all functionality
  • Domain constraint validation edge cases
  • Message type entry validation
  • Registry operations (registration, queries, validation)
  • Cross-domain permission tests
  • Fail-fast behavior tests

5. Robust Error Handling

  • Clear error messages with context
  • Proper use of EnumCoreErrorCode for classification
  • Fail-fast validation at startup time
  • Detailed validation error messages guide users

Code Quality Observations

Thread Safety ✅

The freeze-after-init pattern is correctly implemented:

# registration_message_type.py:356-362
with self._lock:
    if self._frozen:
        raise ModelOnexError(
            message="Cannot register message type: MessageTypeRegistry is frozen...",
            error_code=EnumCoreErrorCode.INVALID_STATE,
        )

Observation: The _require_frozen() method checks frozen state without locking. This is acceptable because:

  • _frozen is set once and never cleared (one-way transition)
  • Python guarantees atomic assignment for simple types like bool
  • Read operations after freeze don't need locks

Domain Extraction Logic ✅

The extract_domain_from_topic() function handles edge cases well:

  • ✅ None/empty/whitespace-only inputs
  • ✅ ONEX format (onex.<domain>.*)
  • ✅ Environment-aware format (<env>.<domain>.*)
  • ✅ Case-insensitive matching
  • ✅ Fallback for two-segment topics

Test coverage: 14 tests covering all edge cases (test_registry_message_type.py:22-85)

Immutability Pattern ✅

Models use frozen=True with proper helper methods:

# model_message_type_entry.py:232-250
def with_additional_handler(self, handler_id: str) -> "ModelMessageTypeEntry":
    """Create a new entry with an additional handler ID."""
    if handler_id in self.handler_ids:
        return self
    
    new_handlers = tuple(list(self.handler_ids) + [handler_id])
    return self.model_copy(update={"handler_ids": new_handlers})

Suggestions for Improvement

1. Type Annotation Consistency (Minor)

Current (registry_message_type.py:142):

def extract_domain_from_topic(topic: str | None) -> str | None:

Observation: This is already using the preferred PEP 604 syntax (X | None). ✅ No change needed.

However, I notice the function accepts None but the docstring says "May be None". Consider if this should be two separate functions:

  • extract_domain_from_topic(topic: str) -> str | None (returns None on parse failure)
  • Callers validate non-None before calling

Rationale: Accepting None and returning None overloads the meaning. But this is a very minor style point and current implementation is fine.

2. Validation Error Aggregation (Enhancement Opportunity)

Current (registry_message_type.py:796-810):

if available_handler_ids is not None:
    missing_handlers = self._handler_references - available_handler_ids
    if missing_handlers:
        for handler_id in sorted(missing_handlers):
            # Find which message types reference this handler
            referencing_types = [
                mt
                for mt, entry in self._entries.items()
                if handler_id in entry.handler_ids
            ]
            errors.append(
                f"Handler '{handler_id}' is referenced by message types "
                f"{referencing_types} but is not registered..."
            )

Observation: This iterates _entries for each missing handler. With many missing handlers, this is O(n×m) where n=missing handlers, m=entries.

Suggestion: Build a reverse index during registration:

# In __init__
self._handler_to_types: dict[str, list[str]] = defaultdict(list)

# In register_message_type
for handler_id in entry.handler_ids:
    self._handler_to_types[handler_id].append(message_type)

Impact: Low - validation happens once at startup. Current implementation is acceptable. This is a future optimization if registries grow very large.

3. Consider Caching Domain Extraction (Performance)

Current (registry_message_type.py:886):

domain = extract_domain_from_topic(topic)

Observation: Domain extraction uses regex matching. If validate_topic_message_type() is called frequently with the same topics, consider memoization.

Suggestion:

from functools import lru_cache

@lru_cache(maxsize=256)
def extract_domain_from_topic_cached(topic: str) -> str | None:
    # Implementation

Impact: Depends on usage pattern. If validation is infrequent, current implementation is fine. Not a blocker.

4. Error Message Truncation (UX Enhancement)

Current (registry_message_type.py:567-574):

entry = self._entries.get(message_type)
if entry is None:
    registered = sorted(self._entries.keys())[:10]
    suffix = "..." if len(self._entries) > 10 else ""
    raise MessageTypeRegistryError(
        f"No handler mapping for message type '{message_type}'. "
        f"Registered types: {registered}{suffix}",
        ...
    )

Observation: Shows first 10 registered types alphabetically. Consider showing closest matches using fuzzy string matching (e.g., Levenshtein distance).

Example:

import difflib
closest = difflib.get_close_matches(message_type, self._entries.keys(), n=5)
if closest:
    hint = f"Did you mean: {closest}?"

Impact: Better developer experience. Nice to have, not required.


Security Considerations ✅

1. No Sensitive Data Exposure

  • Error messages don't leak credentials or secrets ✅
  • Domain names and message types are safe to log ✅
  • No PII in error context ✅

2. Input Validation

  • ✅ Pydantic models validate all inputs
  • ✅ String length constraints on fields (min_length, max_length)
  • ✅ Non-empty validation on allowed_categories (model_message_type_entry.py:108-134)

3. Immutability

  • ✅ frozen=True prevents tampering after construction
  • ✅ Thread-safe read access after freeze

Performance Considerations ✅

Indexing Strategy

The registry maintains indexes for efficient queries:

# registry_message_type.py:310-311
self._category_index: dict[EnumMessageCategory, list[str]] = defaultdict(list)
self._domain_index: dict[str, list[str]] = defaultdict(list)

Analysis:

  • ✅ Registration: O(1) per message type
  • ✅ Handler lookup: O(1) dictionary access
  • ✅ Domain validation: O(1) constraint check
  • ✅ List queries: O(n) but using indexes for filtering

No concerns - appropriate data structures for the use case.


Testing Quality ✅

Coverage Breakdown

  • Domain constraint tests: 13 tests (test_model_domain_constraint.py)
  • Domain extraction tests: 14 tests (test_registry_message_type.py:22-85)
  • Registry registration tests: ~20 tests
  • Registry query tests: ~20 tests
  • Validation tests: ~16 tests

Total: 73 tests as claimed in PR description ✅

Edge Cases Covered

  • ✅ Whitespace-only topics
  • ✅ Case-insensitive matching
  • ✅ Fan-out (multiple handlers)
  • ✅ Cross-domain permissions
  • ✅ Freeze state enforcement
  • ✅ Constraint mismatch errors

Excellent test discipline.


ONEX Pattern Compliance Checklist

  • ✅ No Any types (checked entire codebase)
  • ✅ Pydantic models for all data structures
  • ✅ One model per file (model_domain_constraint.py, model_message_type_entry.py)
  • ✅ Proper file naming (model_, protocol_, registry_*)
  • ✅ Protocol-based design (ProtocolMessageTypeRegistry)
  • ✅ Error hierarchy (extends RuntimeHostError)
  • ✅ Immutability (frozen=True on models)
  • ✅ Thread safety (freeze-after-init pattern)
  • ✅ Type annotations (PEP 604 union syntax)
  • ✅ Documentation (comprehensive docstrings)

Recommendations

Must Fix (Blocking)

None - No blocking issues found.

Should Consider (Pre-merge)

  1. ✅ Verify CI passes - Mentioned in test plan as pending
  2. Consider adding inline examples in ProtocolMessageTypeRegistry docstring showing protocol compliance check

Nice to Have (Post-merge)

  1. Consider fuzzy matching for error messages (better DX)
  2. Consider LRU cache for domain extraction if performance profiling shows it's needed
  3. Consider reverse index for handler-to-types mapping (future optimization)

Conclusion

This PR demonstrates exemplary code quality and is ready to merge pending CI verification. The implementation:

  • ✅ Meets all acceptance criteria from OMN-937
  • ✅ Follows ONEX architectural patterns rigorously
  • ✅ Has comprehensive test coverage (73 tests)
  • ✅ Includes excellent documentation
  • ✅ Handles edge cases and errors gracefully
  • ✅ Is thread-safe and performant

Recommendation: APPROVE ✅

Great work on this implementation! The freeze-after-init pattern, domain constraint validation, and fail-fast behavior will provide a solid foundation for the runtime dispatch infrastructure.


Files Reviewed

  • ✅ runtime/registry/__init__.py
  • ✅ runtime/registry/model_domain_constraint.py
  • ✅ runtime/registry/model_message_type_entry.py
  • ✅ runtime/registry/protocol_message_type_registry.py
  • ✅ runtime/registry/registry_message_type.py
  • ✅ runtime/__init__.py (exports)
  • ✅ Test files (73 unit tests)

… [OMN-937]

- Create centralized json_types.py module with JsonPrimitive, JsonValue,
  EnvelopeDict, and ResultDict type aliases
- Deduplicate union definitions from model_dispatch_result.py and
  protocol_types.py (now import from centralized types)
- Update INFRA_MAX_UNIONS from 455 to 450 to match threshold
- Union count reduced from 455 to 449 (6 unions saved)
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Code Review: Central Message Type Registry (OMN-937)

✅ Overall Assessment

This is excellent work. The implementation demonstrates strong adherence to ONEX patterns and provides a critical piece of runtime infrastructure. The code quality is high with comprehensive documentation, robust validation, and extensive test coverage.


🎯 Strengths

1. Architecture & Design ⭐⭐⭐⭐⭐

  • ✅ Freeze-after-init pattern correctly implemented for thread safety
  • ✅ Domain-driven design with proper separation of concerns
  • ✅ Protocol-based interfaces enabling duck typing and testability
  • ✅ Fail-fast validation at startup time prevents runtime errors
  • ✅ Immutable models (frozen=True) for thread-safe concurrent access
  • ✅ Fan-out support elegantly handles multiple handlers per message type

2. ONEX Compliance ⭐⭐⭐⭐⭐

  • ✅ No Any types - Uses object in protocol dispatchers per guidelines
  • ✅ Strong typing throughout with proper Pydantic models
  • ✅ PEP 604 union syntax (X | None instead of Optional[X])
  • ✅ Naming conventions follow ONEX patterns (Model*, registry_*)
  • ✅ Error handling uses ModelOnexError and domain-specific errors
  • ✅ Type aliases centralized in json_types.py reducing union count

3. Code Quality ⭐⭐⭐⭐⭐

  • ✅ Comprehensive documentation with excellent docstrings
  • ✅ 73 unit tests covering edge cases and error conditions
  • ✅ Clear error messages with actionable guidance
  • ✅ Performance notes documenting O(1) lookup characteristics
  • ✅ Thread safety explicitly documented with usage patterns
  • ✅ Validation with detailed error context for debugging

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🔍 Key Technical Analysis

Domain Extraction Logic (registry_message_type.py:142-203)

  • Well-designed regex patterns with clear separation between ONEX and env-aware formats
  • Correctly handles None, empty strings, and whitespace-only inputs
  • Case-insensitive ONEX prefix matching prevents edge case bugs
  • Fallback logic for two-segment topics is sound
  • Minor suggestion: Add design note about pattern precedence order (ONEX checked before env-aware) to avoid ambiguity with topics like 'onex.dev.events'

Thread Safety Implementation (registry_message_type.py:315-518)

  • Lock protects both frozen flag checks and modification operations ✅
  • Idempotent freeze() method prevents double-freeze errors ✅
  • Clear 3-phase lifecycle documented: register → freeze → query ✅
  • All query methods require frozen state before accessing shared data ✅
  • No race conditions between registration and freeze operations ✅

Validation & Error Handling (registry_message_type.py:753-840)

  • Returns error list instead of raising - allows collecting all validation errors ✅
  • Validates handler references against actual registered handlers ✅
  • Checks for enabled entries with no handlers (catches configuration bugs) ✅
  • Clear, actionable error messages guide developers to solutions ✅
  • Example: "Handler 'X' is referenced by message types [Y] but is not registered with the dispatch engine"

Security & Domain Isolation

  • Secure by default: require_explicit_opt_in=True prevents accidental cross-domain access ✅
  • Explicit opt-in required for cross-domain consumption (security best practice) ✅
  • No regex denial-of-service (ReDoS) vulnerabilities - patterns are simple and bounded ✅
  • Message type length limited to 200 chars (prevents memory exhaustion) ✅
  • Domain length limited to 100 chars ✅
  • Immutable models prevent data corruption in concurrent scenarios ✅

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🐛 Recommendations

Minor Improvements (Non-Blocking)

  1. Add design note about regex pattern precedence

    • Location: registry_message_type.py:186-195
    • Clarify that ONEX pattern MUST be checked before env-aware pattern
    • Prevents ambiguity with topics like 'onex.prod.events'
  2. Add test case for duplicate handler registration

    • Current behavior: Silently skips duplicate handler_ids (correct, but not explicitly tested)
    • Recommended test: Verify idempotency when same handler registered twice
  3. Add test case for completely invalid topic formats

    • Current behavior: Returns None (correct)
    • Document behavior with topics like '@#$%^&*' or 'no-dots-here'

Nice to Have (Future Work)

  1. Consider LRU caching for domain extraction if profiling shows bottleneck (unlikely with O(1) regex)
  2. Add structured logging for handler merges (debug logs already present)

🧪 Test Coverage Assessment: 5/5 ⭐⭐⭐⭐⭐

73 comprehensive tests covering:

  • ✅ Edge cases thoroughly covered (None, empty, whitespace)
  • ✅ Regex pattern variations tested (case-insensitive, special chars)
  • ✅ Domain constraint validation tested
  • ✅ Category constraint validation tested
  • ✅ Thread safety (freeze pattern) tested
  • ✅ Fan-out registration tested
  • ✅ Error conditions tested with proper exception assertions

Minor gaps identified above are low priority and non-blocking.


📊 Performance Analysis

Documented Performance Characteristics:

  • Registration: O(1) per message type ✅
  • Handler lookup: O(1) dictionary access ✅
  • Domain validation: O(1) constraint check ✅
  • Startup validation: O(n) where n = number of entries ✅

Analysis:

  • O(1) lookups for hot path (handler resolution during message processing) ✅
  • O(n) validation only at startup (acceptable trade-off) ✅
  • Regex compilation done at module load, not per-call ✅
  • Indexes maintained for efficient category/domain filtering ✅

No performance concerns identified.

@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: 1

🧹 Nitpick comments (2)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (1)

84-164: LGTM, with a minor consideration on metadata immutability.

The field definitions use appropriate types with proper constraints. The use of tuple[str, ...] for handler_ids and frozenset[EnumMessageCategory] for allowed_categories ensures immutability.

Note: The metadata: dict[str, str] | None field uses a mutable dict. While the frozen model prevents field reassignment, external code with a reference to the dict could mutate it. If strict immutability is required, consider using types.MappingProxyType or documenting that callers should not mutate the returned dict. This is a minor consideration given the overall design.

src/omnibase_infra/runtime/registry/registry_message_type.py (1)

997-1001: Consider documenting the __contains__ behavioral difference before/after freeze.

The in operator has subtly different semantics:

  • Pre-freeze: Checks if message type exists in _entries (includes disabled entries)
  • Post-freeze: Calls has_message_type() which returns False for disabled entries

This asymmetry is intentional (to avoid calling _require_frozen during registration), but could surprise users. Consider adding a docstring note or unifying the behavior.

🔎 Option: Unify behavior to always check enabled status
     def __contains__(self, message_type: str) -> bool:
         """Check if message type is registered using 'in' operator."""
-        if not self._frozen:
-            return message_type in self._entries
-        return self.has_message_type(message_type)
+        entry = self._entries.get(message_type)
+        return entry is not None and entry.enabled
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between a156fc3 and 0c6f7e6.

📒 Files selected for processing (14)
  • src/omnibase_infra/models/dispatch/model_dispatch_result.py (1 hunks)
  • src/omnibase_infra/models/types/__init__.py (1 hunks)
  • src/omnibase_infra/models/types/json_types.py (1 hunks)
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py (2 hunks)
  • src/omnibase_infra/runtime/__init__.py (2 hunks)
  • src/omnibase_infra/runtime/registry/__init__.py (1 hunks)
  • src/omnibase_infra/runtime/registry/model_domain_constraint.py (1 hunks)
  • src/omnibase_infra/runtime/registry/model_message_type_entry.py (1 hunks)
  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py (1 hunks)
  • src/omnibase_infra/runtime/registry/registry_message_type.py (1 hunks)
  • tests/unit/runtime/registry/__init__.py (1 hunks)
  • tests/unit/runtime/registry/test_model_domain_constraint.py (1 hunks)
  • tests/unit/runtime/registry/test_model_message_type_entry.py (1 hunks)
  • tests/unit/runtime/registry/test_registry_message_type.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: All data structures must be proper Pydantic models - never use Any types. Use specific types instead.
Use X | None (PEP 604) union syntax for nullable types instead of Optional[X]
Use ProtocolConfigurationError for configuration validation failures, SecretResolutionError for credential resolution failures, InfraConnectionError for connection failures, InfraTimeoutError for operation timeouts, InfraAuthenticationError for auth failures, and InfraUnavailableError for resource unavailable
Use EnumMessageCategory (EVENT, COMMAND, INTENT) for message routing and topic parsing. Use EnumNodeOutputType (EVENT, COMMAND, INTENT, PROJECTION) for execution shape and handler return type validation. PROJECTION only valid for REDUCER nodes
Do not use Any types anywhere in the codebase - always use specific types. If type is not known at definition time, use object as the type parameter instead
Use duck typing through protocols instead of isinstance checks. Protocol resolution based on structural matching, not type checking

Files:

  • tests/unit/runtime/registry/test_registry_message_type.py
  • src/omnibase_infra/models/types/__init__.py
  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py
  • src/omnibase_infra/runtime/registry/model_domain_constraint.py
  • src/omnibase_infra/runtime/__init__.py
  • src/omnibase_infra/models/types/json_types.py
  • src/omnibase_infra/runtime/registry/__init__.py
  • src/omnibase_infra/runtime/registry/model_message_type_entry.py
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
  • tests/unit/runtime/registry/test_model_message_type_entry.py
  • src/omnibase_infra/models/dispatch/model_dispatch_result.py
  • tests/unit/runtime/registry/__init__.py
  • src/omnibase_infra/runtime/registry/registry_message_type.py
  • tests/unit/runtime/registry/test_model_domain_constraint.py
**/*protocol*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use protocol_.py or protocols.py file naming with Protocol class naming for protocol definitions. Single protocol uses protocol_.py, domain-grouped protocols use protocols.py

Files:

  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
**/*model_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*model_*.py: Use model_.py file naming with Model class naming for data model files
Each Python file must contain exactly one Model* class for data structure definitions

Files:

  • src/omnibase_infra/runtime/registry/model_domain_constraint.py
  • src/omnibase_infra/runtime/registry/model_message_type_entry.py
  • tests/unit/runtime/registry/test_model_message_type_entry.py
  • src/omnibase_infra/models/dispatch/model_dispatch_result.py
  • tests/unit/runtime/registry/test_model_domain_constraint.py
**/*dispatch*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use ModelEventEnvelope[object] instead of Any for generic dispatcher parameters when type is not known at definition time

Files:

  • src/omnibase_infra/models/dispatch/model_dispatch_result.py
**/registry_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use registry_.py file naming with Registry class naming for standalone registries in domain directories

Files:

  • src/omnibase_infra/runtime/registry/registry_message_type.py
🧠 Learnings (23)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T19:53:07.665Z
Learning: Applies to **/*.py : Use EnumMessageCategory (EVENT, COMMAND, INTENT) for message routing and topic parsing. Use EnumNodeOutputType (EVENT, COMMAND, INTENT, PROJECTION) for execution shape and handler return type validation. PROJECTION only valid for REDUCER nodes
📚 Learning: 2025-12-08T00:48:30.737Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T00:48:30.737Z
Learning: Import `omnibase_core` models and types only for type hints and runtime usage - follow the SPI → Core dependency direction

Applied to files:

  • src/omnibase_infra/models/types/__init__.py
  • src/omnibase_infra/runtime/__init__.py
📚 Learning: 2025-12-08T00:48:30.737Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T00:48:30.737Z
Learning: Applies to src/omnibase_spi/**/*.py : SPI modules may import from `omnibase_core` for type hints and model runtime usage (allowed and required)

Applied to files:

  • src/omnibase_infra/models/types/__init__.py
  • src/omnibase_infra/runtime/__init__.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to **/protocols/protocol_*.py : Use Protocol from typing module for all interface definitions; never use ABC (Abstract Base Classes) for service interfaces

Applied to files:

  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.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/registry/protocol_message_type_registry.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Use Protocol for interface definitions when implementations may live outside core codebase; use Pydantic models only for base classes with shared logic

Applied to files:

  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py
📚 Learning: 2025-12-08T00:48:30.737Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T00:48:30.737Z
Learning: Applies to src/omnibase_spi/protocols/**/*.py : All public protocols must be decorated with `runtime_checkable`

Applied to files:

  • src/omnibase_infra/runtime/registry/protocol_message_type_registry.py
  • src/omnibase_infra/runtime/__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 **/models/model_contract_*.py : Contract model files must follow the naming pattern `model_contract_<domain>.py` and be located in `*/models/` directories

Applied to files:

  • src/omnibase_infra/runtime/registry/model_domain_constraint.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 **/models/model_contract_*.py : Contract-backed model files must follow the naming pattern `model_contract_<domain>.py` and be located in `*/models/` directories

Applied to files:

  • src/omnibase_infra/runtime/registry/model_domain_constraint.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 src/omnibase/constants/*_constants.py : Constants files must follow the naming pattern `<domain>_constants.py` and be located in `src/omnibase/constants/`

Applied to files:

  • src/omnibase_infra/runtime/registry/model_domain_constraint.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 src/omnibase/constants/*_constants.py : Constants files must follow the naming pattern `<domain>_constants.py` and be located in `src/omnibase/constants/` directory

Applied to files:

  • src/omnibase_infra/runtime/registry/model_domain_constraint.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/registry/model_domain_constraint.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/{models,protocols}/{model_*,protocol_*}.py : Avoid using Any, dict, or primitive types in model and protocol definitions; use strongest typing possible

Applied to files:

  • src/omnibase_infra/models/types/json_types.py
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/protocols/protocol_*.py : Avoid using Any, dict, or primitive types in protocol signatures; use the strongest typing possible with Pydantic models

Applied to files:

  • src/omnibase_infra/models/types/json_types.py
  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
📚 Learning: 2025-12-20T19:53:07.665Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T19:53:07.665Z
Learning: Applies to **/registry_*.py : Use registry_<purpose>.py file naming with Registry<Purpose> class naming for standalone registries in domain directories

Applied to files:

  • src/omnibase_infra/runtime/registry/__init__.py
📚 Learning: 2025-12-08T00:48:30.737Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_spi PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T00:48:30.737Z
Learning: Applies to src/omnibase_spi/protocols/nodes/*.py : Use Protocol naming convention `Protocol{Type}Node` for node protocols (e.g., `ProtocolComputeNode`, `ProtocolEffectNode`)

Applied to files:

  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.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 **/protocols/protocol_*.py : All protocol method signatures must use Pydantic models exclusively, never primitives or dicts

Applied to files:

  • src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py
📚 Learning: 2025-11-24T16:33:51.604Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T16:33:51.604Z
Learning: Applies to tests/unit/models/**/test_model_*.py : Model tests must achieve 100% coverage and test instantiation, inheritance, serialization, deserialization, JSON serialization, roundtrip serialization, equality, hashing, string representation, repr, attributes, validation, metadata, data creation, copying, and immutability

Applied to files:

  • tests/unit/runtime/registry/test_model_message_type_entry.py
  • tests/unit/runtime/registry/test_model_domain_constraint.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to tests/**/*.py : Write comprehensive test coverage following the test structure under `tests/unit/` organized by subsystem (enums, models, mixins, utils)

Applied to files:

  • tests/unit/runtime/registry/test_model_message_type_entry.py
📚 Learning: 2025-12-20T19:53:07.665Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T19:53:07.665Z
Learning: Applies to **/*dispatch*.py : Use ModelEventEnvelope[object] instead of Any for generic dispatcher parameters when type is not known at definition time

Applied to files:

  • src/omnibase_infra/models/dispatch/model_dispatch_result.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Applies to **/*.py : Use `EnumCoreErrorCode` with `ModelOnexError` for proper error code usage

Applied to files:

  • src/omnibase_infra/models/dispatch/model_dispatch_result.py
📚 Learning: 2025-11-24T17:24:54.193Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T17:24:54.193Z
Learning: Applies to **/*test*.py : Use registry=None in test harness to force registry resolver usage instead of manually creating registry instances

Applied to files:

  • tests/unit/runtime/registry/__init__.py
📚 Learning: 2025-12-20T19:53:07.665Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T19:53:07.665Z
Learning: Applies to **/*.py : Use EnumMessageCategory (EVENT, COMMAND, INTENT) for message routing and topic parsing. Use EnumNodeOutputType (EVENT, COMMAND, INTENT, PROJECTION) for execution shape and handler return type validation. PROJECTION only valid for REDUCER nodes

Applied to files:

  • src/omnibase_infra/runtime/registry/registry_message_type.py
🧬 Code graph analysis (7)
src/omnibase_infra/runtime/registry/protocol_message_type_registry.py (3)
src/omnibase_infra/enums/enum_message_category.py (1)
  • EnumMessageCategory (34-192)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (1)
  • ModelMessageTypeEntry (33-270)
src/omnibase_infra/runtime/registry/registry_message_type.py (10)
  • register_message_type (321-432)
  • freeze (487-518)
  • get_handlers (524-604)
  • get_entry (634-650)
  • has_message_type (652-669)
  • list_message_types (671-717)
  • list_domains (719-732)
  • validate_startup (753-840)
  • is_frozen (928-937)
  • entry_count (940-949)
src/omnibase_infra/runtime/__init__.py (3)
src/omnibase_infra/runtime/registry/registry_message_type.py (2)
  • MessageTypeRegistry (211-1028)
  • MessageTypeRegistryError (68-119)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (1)
  • ModelDomainConstraint (32-197)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (1)
  • ModelMessageTypeEntry (33-270)
src/omnibase_infra/runtime/registry/__init__.py (4)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (1)
  • ModelDomainConstraint (32-197)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (1)
  • ModelMessageTypeEntry (33-270)
src/omnibase_infra/runtime/registry/protocol_message_type_registry.py (1)
  • ProtocolMessageTypeRegistry (31-303)
src/omnibase_infra/runtime/registry/registry_message_type.py (2)
  • MessageTypeRegistry (211-1028)
  • MessageTypeRegistryError (68-119)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (2)
src/omnibase_infra/enums/enum_message_category.py (1)
  • EnumMessageCategory (34-192)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (1)
  • ModelDomainConstraint (32-197)
tests/unit/runtime/registry/test_model_message_type_entry.py (2)
src/omnibase_infra/enums/enum_message_category.py (1)
  • EnumMessageCategory (34-192)
tests/unit/runtime/registry/test_model_domain_constraint.py (1)
  • test_immutable (103-107)
src/omnibase_infra/runtime/registry/registry_message_type.py (4)
src/omnibase_infra/enums/enum_message_category.py (2)
  • EnumMessageCategory (34-192)
  • from_topic (121-159)
src/omnibase_infra/errors/infra_errors.py (1)
  • RuntimeHostError (35-100)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (2)
  • ModelDomainConstraint (32-197)
  • validate_consumption (150-197)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (3)
  • ModelMessageTypeEntry (33-270)
  • with_additional_handler (226-256)
  • validate_category (190-224)
tests/unit/runtime/registry/test_model_domain_constraint.py (1)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (3)
  • ModelDomainConstraint (32-197)
  • can_consume_from (117-148)
  • validate_consumption (150-197)
🔇 Additional comments (35)
src/omnibase_infra/nodes/node_registry_effect/v1_0_0/protocol_types.py (1)

5-24: LGTM! Type centralization reduces duplication.

The refactoring to import JSON type aliases from the centralized omnibase_infra.models.types module eliminates duplication while maintaining backward compatibility through re-exports. The updated docstring clearly documents the change.

src/omnibase_infra/models/dispatch/model_dispatch_result.py (1)

59-59: LGTM! Consistent type centralization.

The import change aligns with the broader refactoring effort to centralize JSON type definitions. The __all__ export on line 381 maintains backward compatibility for consumers of this module.

tests/unit/runtime/registry/test_model_domain_constraint.py (1)

12-128: LGTM! Comprehensive test coverage for domain constraints.

The test suite thoroughly validates ModelDomainConstraint behavior including:

  • Domain ownership and cross-domain consumption rules
  • Validation with detailed error messages
  • Immutability guarantees
  • Default values and edge cases

The test structure is clear and follows best practices.

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

96-102: LGTM! Proper exposure of new registry components.

The imports correctly expose the new message type registry infrastructure at the package level, making them accessible via omnibase_infra.runtime. The components are appropriately grouped in __all__ (lines 164-169) with a clear OMN-937 reference.

src/omnibase_infra/models/types/__init__.py (1)

1-21: LGTM! Clean package structure for centralized types.

The package initializer follows standard Python patterns for re-exporting types. The module docstring clearly explains the purpose of reducing union duplication across the codebase.

tests/unit/runtime/registry/test_model_message_type_entry.py (1)

18-261: LGTM! Excellent test coverage for ModelMessageTypeEntry.

The test suite comprehensively validates:

  • Basic instantiation with required fields
  • Fan-out support via multiple handlers
  • Multi-category message types
  • Category validation with detailed error messages
  • Immutable update patterns (with_additional_handler, with_enabled)
  • Duplicate handler prevention
  • All Pydantic validation constraints (required fields, min/max lengths)
  • Edge case: empty allowed_categories correctly rejected with clear error (lines 242-261)

The test structure is well-organized with separate classes for functional and validation tests.

src/omnibase_infra/models/types/json_types.py (1)

28-34: PEP 695 syntax is correctly supported by the project's Python 3.12+ baseline requirement.

The code uses PEP 695 type statement syntax for recursive type definitions (JsonValue). This aligns with the project's minimum Python version of 3.12, which introduced support for this feature. No action needed.

src/omnibase_infra/runtime/registry/__init__.py (1)

1-61: LGTM!

The module init file is well-structured with comprehensive documentation covering core components, key features, thread safety patterns, and related issues. The exports are properly organized and the __all__ list correctly exposes all public API entities. Based on learnings, the registry naming conventions are correctly followed.

src/omnibase_infra/runtime/registry/model_domain_constraint.py (3)

32-82: LGTM!

The ModelDomainConstraint class follows coding guidelines: proper model_<name>.py file naming with Model<Name> class naming, single class per file, frozen/immutable configuration for thread safety, and specific types instead of Any. The field definitions with appropriate constraints and defaults are well-designed.


117-148: LGTM!

The can_consume_from method has correct short-circuit logic prioritizing allow_all_domains, then owning_domain match, then allowed_cross_domains lookup. The implementation matches the documented behavior in the docstring.


150-198: LGTM!

The validate_consumption method correctly delegates to can_consume_from and provides detailed, actionable error messages that differ based on the require_explicit_opt_in flag. The return type uses proper PEP 604 union syntax.

src/omnibase_infra/runtime/registry/protocol_message_type_registry.py (4)

30-78: LGTM!

The protocol class is correctly decorated with @runtime_checkable as required by learnings, uses Protocol instead of ABC per guidelines, and follows the protocol_<name>.py file naming with Protocol<Name> class naming conventions. The comprehensive docstrings document thread safety requirements and the freeze-after-init pattern clearly.


83-164: LGTM!

The registration and query methods are well-documented with comprehensive docstrings covering arguments, return values, error conditions, and thread safety requirements. The method signatures align with the implementation.


166-246: LGTM!

The lookup and listing methods follow consistent patterns with proper optional parameter handling and clear documentation. Return types use PEP 604 union syntax correctly.


277-306: LGTM!

The properties is_frozen and entry_count are correctly defined with proper return types and documentation.

src/omnibase_infra/runtime/registry/model_message_type_entry.py (4)

33-82: LGTM!

The ModelMessageTypeEntry class follows coding guidelines with proper file and class naming conventions, frozen configuration for thread safety, and comprehensive docstrings documenting fan-out support and thread safety guarantees.


110-136: LGTM!

The field validator correctly ensures allowed_categories is non-empty with a clear, actionable error message explaining why a message type must be allowed in at least one category to be routable.


166-224: LGTM!

The supports_category method is a clean membership check, and validate_category correctly returns a tuple with a descriptive error message when validation fails. The error message includes the allowed categories for debugging.


226-271: LGTM!

The with_additional_handler method correctly implements idempotent fan-out registration, returning self if the handler is already present. The with_enabled method properly uses model_copy for immutable updates on the frozen model.

tests/unit/runtime/registry/test_registry_message_type.py (8)

1-20: LGTM!

The test file has well-organized imports and follows pytest conventions. The import of extract_domain_from_topic indicates this is a public utility function exported from the registry module.


22-85: LGTM!

The TestExtractDomainFromTopic class provides comprehensive test coverage for the domain extraction utility, including edge cases for ONEX Kafka format, environment-aware format, empty/whitespace inputs, special characters, case sensitivity, and fallback behavior. The type: ignore[arg-type] on line 40 is appropriately used for testing None input behavior.


87-223: LGTM!

The TestMessageTypeRegistryRegistration class thoroughly tests the registration functionality including basic registration, convenience methods, fan-out with multiple handlers, freeze enforcement, null rejection, and conflict detection for category and domain constraints. The tests properly verify error message content.


225-242: LGTM!

The freeze tests correctly verify basic freeze functionality and idempotent behavior.


244-444: LGTM!

The TestMessageTypeRegistryQueries class has excellent coverage with a well-designed fixture and tests for all query methods including success cases, error cases (not found, category/domain mismatch, pre-freeze access), and filtering capabilities. The tests properly verify error message content.


446-551: LGTM!

The TestMessageTypeRegistryValidation class covers startup validation with and without handler sets, detection of missing handlers, and topic-message type validation including category and domain mismatch scenarios. Error message verification is thorough.


553-603: LGTM!

The cross-domain consumption tests correctly verify that explicit opt-in via allowed_cross_domains enables cross-domain access, while the default behavior blocks it. This aligns with the domain isolation design principle.


605-748: LGTM!

The property tests correctly verify counting behavior for entries, handlers (unique), and domains (unique). The dunder method tests verify __len__, __contains__, __str__, and __repr__ implementations produce expected output formats.

src/omnibase_infra/runtime/registry/registry_message_type.py (8)

1-61: LGTM!

Module structure is well-organized with comprehensive documentation covering design principles, thread safety guarantees, and performance characteristics. The __all__ export list is properly defined and imports are appropriate.


68-119: LGTM!

The error class properly extends RuntimeHostError with registry-specific context fields (message_type, domain, category). The category value conversion ensures proper serialization.


142-204: Well-designed domain extraction with comprehensive edge case handling.

The priority ordering (ONEX format first, then ENV format, then fallback) is correctly reasoned. The fallback logic on lines 199-201 intentionally handles edge cases like "a.b" topics that don't have trailing segments.


281-433: Solid registration logic with correct fan-out merging.

The registration flow correctly:

  • Validates entry is not None before acquiring lock
  • Checks frozen state under lock protection
  • Validates constraint consistency before merging handlers
  • Updates indexes only for new registrations (existing message types already indexed)
  • Tracks all handler references for startup validation

434-518: LGTM!

register_simple provides a clean convenience API with sensible defaults. freeze() is correctly idempotent with thread-safe lock protection.


524-748: Query methods are well-designed with consistent semantics.

Key strengths:

  • All queries enforce frozen state for thread safety
  • Error messages include helpful context (first 10 registered types)
  • enabled status is consistently respected across all lookup methods
  • Return values are copies/new collections, preserving internal immutability

753-922: Comprehensive startup validation with clear error messages.

The validation methods cover:

  • Missing handler references (when available_handler_ids provided)
  • Handler-less entries and empty domain constraints (defensive checks even though Pydantic validators would catch these during registration)
  • Topic-to-message-type compatibility with detailed error reasons

The fail-fast design ensures configuration issues surface before message processing begins.


1003-1028: LGTM!

The __str__ and __repr__ implementations provide appropriate levels of detail for debugging while limiting output size for large registries.

Comment on lines +248 to +275
# =========================================================================
# Validation Methods
# =========================================================================

def validate_startup(self) -> list[str]:
"""
Perform startup-time validation and return any errors.

Validates registry consistency:
- All handler references are valid
- No conflicting category constraints
- Domain constraints are properly configured

This method should be called after freeze() to ensure fail-fast
behavior before consumers start processing messages.

Returns:
List of validation error messages. Empty list if valid.

Raises:
ModelOnexError: If registry is not frozen (INVALID_STATE)

Thread Safety:
Must be safe for concurrent access after freeze().

.. versionadded:: 0.5.0
"""
...

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for validate_topic_message_type in the codebase
rg -n "validate_topic_message_type" --type=py -C2

Repository: OmniNode-ai/omnibase_infra

Length of output: 4562


🏁 Script executed:

#!/bin/bash
# Look at the protocol file to see what methods are defined
cat -n src/omnibase_infra/runtime/registry/protocol_message_type_registry.py | head -300

Repository: OmniNode-ai/omnibase_infra

Length of output: 11163


🏁 Script executed:

#!/bin/bash
# Check if validate_topic_message_type is in the protocol file
grep -n "validate_topic_message_type" src/omnibase_infra/runtime/registry/protocol_message_type_registry.py

Repository: OmniNode-ai/omnibase_infra

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check how validate_topic_message_type is being called in the codebase
rg "validate_topic_message_type" --type=py -B2 -A2 | head -100

Repository: OmniNode-ai/omnibase_infra

Length of output: 4382


🏁 Script executed:

#!/bin/bash
# Check if ProtocolMessageTypeRegistry is used in type hints anywhere with validate_topic_message_type
rg "ProtocolMessageTypeRegistry" --type=py -A3 | grep -A3 "validate_topic_message_type"

Repository: OmniNode-ai/omnibase_infra

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check where ProtocolMessageTypeRegistry is actually used
rg "ProtocolMessageTypeRegistry" --type=py -C3

Repository: OmniNode-ai/omnibase_infra

Length of output: 5768


🏁 Script executed:

#!/bin/bash
# Check the full implementation of validate_topic_message_type in registry_message_type.py
sed -n '842,920p' src/omnibase_infra/runtime/registry/registry_message_type.py

Repository: OmniNode-ai/omnibase_infra

Length of output: 2555


🏁 Script executed:

#!/bin/bash
# Check if the method is used in other parts of the codebase or if it's part of documented public API
rg "def.*registry.*ProtocolMessageTypeRegistry|registry:.*ProtocolMessageTypeRegistry" --type=py -C2

Repository: OmniNode-ai/omnibase_infra

Length of output: 541


Add validate_topic_message_type to the protocol definition.

The MessageTypeRegistry implementation includes a public method validate_topic_message_type(topic: str, message_type: str) -> tuple[bool, str | None] that validates whether a message type is allowed on a given topic. This method is thoroughly tested and documented, but is not defined in the ProtocolMessageTypeRegistry protocol. If this is part of the intended public API contract for all registry implementations, it should be added to the protocol to ensure consistency across implementations.

🤖 Prompt for AI Agents
In src/omnibase_infra/runtime/registry/protocol_message_type_registry.py around
lines 248 to 275, the ProtocolMessageTypeRegistry is missing the public API
method validate_topic_message_type(topic: str, message_type: str) -> tuple[bool,
str | None]; add this abstract method declaration to the protocol with the
correct typing signature and a short docstring describing that it returns
(allowed: bool, error_message: Optional[str]) so all implementations (like
MessageTypeRegistry) must implement it and static type checkers will enforce the
contract.

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🎨 ONEX Pattern Compliance Checklist

  • Strong typing (no Any types) - Uses object for generic dispatchers per CLAUDE.md
  • Pydantic models for all data structures
  • Protocol-based interfaces (ProtocolMessageTypeRegistry)
  • Naming conventions (Model*, registry_, Protocol)
  • Container-based DI ready (can be wired into ModelONEXContainer)
  • Error handling with ModelOnexError and domain-specific MessageTypeRegistryError
  • Freeze-after-init for thread safety
  • Comprehensive documentation with examples
  • Unit test coverage (73 tests)
  • PEP 604 union syntax (X | None instead of Optional[X])
  • Centralized type aliases reducing union count

Pattern Compliance: 100% ✅


🚀 Integration & Dependencies

Unblocks

  • ✅ OMN-941: Handler Output Model (already done)
  • ✅ Future runtime integration work
  • ✅ Message dispatch engine handler resolution

Dependencies

  • ✅ OMN-934: Runtime Dispatch Engine (completed prerequisite)

Migration Path

No breaking changes. This is new infrastructure that extends existing runtime capabilities.


🏆 Final Verdict: APPROVED ✅

Must Fix Before Merge

None - Code is production-ready.

Overall Scores

  • Code Quality: 5/5 ⭐⭐⭐⭐⭐
  • ONEX Compliance: 5/5 ⭐⭐⭐⭐⭐
  • Documentation: 5/5 ⭐⭐⭐⭐⭐
  • Test Coverage: 5/5 ⭐⭐⭐⭐⭐
  • Security: 5/5 ⭐⭐⭐⭐⭐

Overall: 5/5 ⭐⭐⭐⭐⭐


💬 Summary

This PR represents exemplary work that demonstrates:

  • ✅ Deep understanding of ONEX architectural patterns
  • ✅ Strong software engineering fundamentals
  • ✅ Excellent documentation practices
  • ✅ Comprehensive testing methodology
  • ✅ Security-conscious design principles

The implementation is production-ready and sets a high bar for infrastructure code quality in the ONEX ecosystem. The central message type registry provides the critical mapping layer between message types and handlers with robust domain isolation, fail-fast validation, and thread-safe operation.

Excellent work! This is a foundational piece of infrastructure that will serve the ONEX runtime well. 🚀

…-937]

- Add Design Note to extract_domain_from_topic() docstring explaining
  critical regex pattern ordering (ONEX before env-aware)
- Add inline comments with worked example of ambiguous topic handling
- Add test_register_duplicate_handler_idempotent for handler deduplication
- Add test_completely_invalid_topic_formats for malformed topics
- Add test_edge_case_empty_segment_topics for consecutive dots
- Add test_special_character_topics for unicode and special chars
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Central Message Type Registry [OMN-937]

Summary

This PR implements a well-architected central message type registry that maps message types to handler implementations with comprehensive domain ownership and category constraint enforcement. The implementation demonstrates excellent adherence to ONEX principles and follows infrastructure best practices.

✅ Strengths

1. Excellent ONEX Compliance

  • ✅ No Any types: Uses frozenset, tuple, and proper type unions throughout
  • ✅ Proper type annotations: Uses PEP 604 syntax (X | None) consistently
  • ✅ Strong typing: All models are Pydantic with frozen=True for immutability
  • ✅ Proper file naming: model_*.py, registry_*.py, protocol_*.py conventions followed

2. Thread Safety Design

  • ✅ Freeze-after-init pattern: Clearly separates registration phase (mutable) from query phase (immutable)
  • ✅ Proper locking: Uses threading.Lock to protect registration operations
  • ✅ Immutable models: frozen=True on all Pydantic models ensures thread-safe reads after freeze
  • ✅ Clear state transitions: Registration → Freeze → Query phases well-documented

3. Comprehensive Test Coverage

  • ✅ 73 unit tests covering all functionality
  • ✅ Edge case handling: Tests for None, empty strings, whitespace-only inputs
  • ✅ Domain validation: Cross-domain consumption rules thoroughly tested
  • ✅ Error scenarios: Missing handlers, category mismatches, domain violations all covered

4. Security & Error Handling

  • ✅ Domain isolation by default: Handlers restricted to their domain unless explicitly opted-in
  • ✅ Fail-fast validation: validate_startup() catches configuration issues before runtime
  • ✅ Clear error messages: Descriptive errors with actionable guidance for resolution
  • ✅ Correlation ID support: Ready for distributed tracing integration

5. Code Quality

  • ✅ Excellent documentation: Comprehensive docstrings with examples, design notes, and rationale
  • ✅ Type refinement: Centralized JSON types reduce union count (449 unions, down from 455)
  • ✅ Fan-out support: Multiple handlers per message type with proper deduplication
  • ✅ Extensibility: Easy to add new domains and message types

🔍 Code Quality Observations

Pattern Precedence Documentation (registry_message_type.py:178-226)

Excellent: The extract_domain_from_topic() function includes an exceptional design note explaining why pattern matching order is critical. The inline comments with worked examples show excellent engineering documentation.

Empty Category Validation (model_message_type_entry.py:110-136)

Well done: The @field_validator for allowed_categories prevents registration of unroutable message types with clear error messaging. This fail-fast validation at model construction time is exactly right.

Handler Deduplication (registry_message_type.py:437-443)

Clean implementation: The fan-out registration properly deduplicates handlers using the with_additional_handler() pattern. The immutability pattern (frozen=True) combined with model_copy() ensures thread safety.

🎯 Potential Improvements (Non-Blocking)

1. Domain Extraction Edge Case (Low Priority)

File: registry_message_type.py:94

The fallback case in extract_domain_from_topic() can return an empty string for topic "...". Consider returning None instead for consistency, though the test explicitly documents this behavior.

Impact: Low - validated by tests and documented behavior

2. Lock-Free Query Documentation (Future Enhancement)

File: registry_message_type.py:604-646

The get_handlers() method relies on immutability after freeze for lock-free reads. Consider adding a comment documenting this guarantee to help maintainers understand the thread-safety model.

Impact: Documentation enhancement only - code is already correct

🔒 Security Considerations

Domain Isolation (Excellent)

The registry enforces domain isolation by default with explicit opt-in for cross-domain consumption:

  • ✅ Default deny for cross-domain access
  • ✅ Explicit allowlist via allowed_cross_domains
  • ✅ Clear audit trail of cross-domain permissions
  • ✅ Fail-fast validation prevents misconfigurations

No Secrets Exposure

  • ✅ No credential handling in registry
  • ✅ Handler IDs are non-sensitive identifiers
  • ✅ Error messages do not leak implementation details

📊 Performance Considerations

Lookup Performance (Excellent)

  • ✅ O(1) handler lookup via dictionary
  • ✅ O(1) domain validation via constraint model
  • ✅ Category/domain indexes for efficient filtering
  • ✅ Startup validation is O(n) but only runs once

Memory Efficiency

  • ✅ Immutable models prevent accidental copies
  • ✅ Tuple/frozenset usage minimizes memory overhead
  • ✅ Indexes trade minimal memory for query speed

🧪 Test Coverage Assessment

Coverage: Excellent (73 tests across 3 test files)

Test file breakdown:

  • test_model_domain_constraint.py: 128 lines
  • test_model_message_type_entry.py: 261 lines
  • test_registry_message_type.py: 887 lines

Well-tested scenarios:

  • ✅ Domain extraction edge cases (None, empty, whitespace)
  • ✅ Pattern precedence (ONEX vs environment-aware)
  • ✅ Category and domain constraint validation
  • ✅ Cross-domain consumption rules
  • ✅ Fan-out registration (multiple handlers)
  • ✅ Freeze state transitions
  • ✅ Error conditions

📋 ONEX Guidelines Compliance

Guideline Status Notes
No Any types ✅ Pass Uses proper unions throughout
PEP 604 syntax (X | None) ✅ Pass Consistently used
Pydantic models ✅ Pass All data structures are proper models
Immutability (frozen=True) ✅ Pass All models frozen for thread safety
File naming conventions ✅ Pass Correct prefixes used
Error hierarchy ✅ Pass Extends RuntimeHostError correctly
Documentation ✅ Pass Comprehensive docstrings with examples
Thread safety ✅ Pass Freeze-after-init pattern properly implemented

🎉 Conclusion

This is high-quality production-ready code that demonstrates excellent engineering practices:

  1. Architecture: Clean separation of concerns with protocol-based interfaces
  2. Thread Safety: Proper freeze-after-init pattern with immutable models
  3. Security: Domain isolation by default with explicit opt-in
  4. Testing: Comprehensive coverage including edge cases
  5. Documentation: Exceptional inline documentation and design notes
  6. ONEX Compliance: Perfect adherence to infrastructure guidelines

The code quality exceeds expectations for a foundational infrastructure component. The pattern precedence documentation and edge case handling show mature engineering judgment.

✅ Recommendation

Approve and merge. This PR is ready for production.

The minor suggestions above are enhancements for future consideration, not blockers. The implementation is solid, well-tested, and follows all ONEX conventions.


Reviewed by: Claude Code Assistant
Review Date: 2025-12-20
PR: #67 - feat(runtime): implement central message type registry [OMN-937]

@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: 0

🧹 Nitpick comments (2)
src/omnibase_infra/runtime/registry/registry_message_type.py (2)

854-859: Consider removing redundant handler check.

The check len(entry.handler_ids) == 0 at line 856 can never be true because ModelMessageTypeEntry.handler_ids has a min_length=1 validation constraint. This is defensive but could be misleading since the validation would fail at registration time.

🔎 Proposed removal
-        # Validate that enabled entries have at least one handler
-        for message_type, entry in self._entries.items():
-            if entry.enabled and len(entry.handler_ids) == 0:
-                errors.append(
-                    f"Message type '{message_type}' is enabled but has no handlers."
-                )

This validation is already enforced by the Pydantic model's min_length=1 constraint on handler_ids.


1039-1043: Subtle behavioral difference in __contains__ before/after freeze.

Before freeze, in checks raw dict membership. After freeze, it uses has_message_type which also checks entry.enabled. This means a disabled entry returns True before freeze but False after freeze. Consider documenting this or making behavior consistent.

🔎 Option: Make behavior consistent
 def __contains__(self, message_type: str) -> bool:
     """Check if message type is registered using 'in' operator."""
-    if not self._frozen:
-        return message_type in self._entries
-    return self.has_message_type(message_type)
+    # Check registration only (not enabled status) for consistency
+    return message_type in self._entries

Or document the current behavior explicitly in the docstring.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 0c6f7e6 and 8b111e0.

📒 Files selected for processing (2)
  • src/omnibase_infra/runtime/registry/registry_message_type.py (1 hunks)
  • tests/unit/runtime/registry/test_registry_message_type.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: All data structures must be proper Pydantic models - never use Any types. Use specific types instead.
Use X | None (PEP 604) union syntax for nullable types instead of Optional[X]
Use ProtocolConfigurationError for configuration validation failures, SecretResolutionError for credential resolution failures, InfraConnectionError for connection failures, InfraTimeoutError for operation timeouts, InfraAuthenticationError for auth failures, and InfraUnavailableError for resource unavailable
Use EnumMessageCategory (EVENT, COMMAND, INTENT) for message routing and topic parsing. Use EnumNodeOutputType (EVENT, COMMAND, INTENT, PROJECTION) for execution shape and handler return type validation. PROJECTION only valid for REDUCER nodes
Do not use Any types anywhere in the codebase - always use specific types. If type is not known at definition time, use object as the type parameter instead
Use duck typing through protocols instead of isinstance checks. Protocol resolution based on structural matching, not type checking

Files:

  • tests/unit/runtime/registry/test_registry_message_type.py
  • src/omnibase_infra/runtime/registry/registry_message_type.py
**/registry_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use registry_.py file naming with Registry class naming for standalone registries in domain directories

Files:

  • src/omnibase_infra/runtime/registry/registry_message_type.py
🧠 Learnings (1)
📚 Learning: 2025-12-20T19:53:07.665Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T19:53:07.665Z
Learning: Applies to **/*.py : Use EnumMessageCategory (EVENT, COMMAND, INTENT) for message routing and topic parsing. Use EnumNodeOutputType (EVENT, COMMAND, INTENT, PROJECTION) for execution shape and handler return type validation. PROJECTION only valid for REDUCER nodes

Applied to files:

  • src/omnibase_infra/runtime/registry/registry_message_type.py
🧬 Code graph analysis (1)
src/omnibase_infra/runtime/registry/registry_message_type.py (4)
src/omnibase_infra/enums/enum_message_category.py (2)
  • EnumMessageCategory (34-192)
  • from_topic (121-159)
src/omnibase_infra/errors/infra_errors.py (1)
  • RuntimeHostError (35-100)
src/omnibase_infra/runtime/registry/model_domain_constraint.py (2)
  • ModelDomainConstraint (32-197)
  • validate_consumption (150-197)
src/omnibase_infra/runtime/registry/model_message_type_entry.py (3)
  • ModelMessageTypeEntry (33-270)
  • with_additional_handler (226-256)
  • validate_category (190-224)
🔇 Additional comments (14)
tests/unit/runtime/registry/test_registry_message_type.py (5)

22-180: Comprehensive edge case coverage for domain extraction.

Excellent test coverage documenting expected behavior for malformed topic strings. The edge case tests (lines 86-180) serve as valuable documentation for how the function handles unexpected inputs without crashing.


182-362: Thorough registration tests with conflict detection.

Good coverage of fan-out pattern, idempotent registration, and constraint conflict detection. The tests verify that conflicting category and domain constraints are properly rejected with descriptive error messages.


383-583: Well-structured query tests with fixture pattern.

The populated_registry fixture provides consistent test data, and the tests cover success paths, error handling (not found, category mismatch, domain mismatch), and various filtering combinations. Good verification of pre-freeze query failures.


585-690: Solid validation test coverage.

Tests verify startup validation with missing handlers and topic-message validation for category/domain mismatches. The fail-fast behavior is properly tested.


692-887: Complete coverage of cross-domain, properties, and dunder methods.

Cross-domain tests verify explicit opt-in requirement. Property tests correctly verify that handler_count and domain_count track unique values. Dunder method tests ensure string representations include key state information.

src/omnibase_infra/runtime/registry/registry_message_type.py (9)

68-119: Well-structured error class extending RuntimeHostError.

Correctly extends RuntimeHostError per coding guidelines for infrastructure errors. Uses PEP 604 union syntax (str | None) and properly propagates contextual fields for debugging.


142-245: Robust domain extraction with proper pattern precedence.

The ONEX pattern correctly takes precedence over the environment-aware pattern. The fallback to parts[1] at lines 241-243 handles edge cases gracefully. The extensive documentation of pattern matching order (lines 177-196, 206-226) is thorough.


323-358: Clean initialization with proper data structures.

Uses appropriate types: dict for entries, set for unique tracking, and defaultdict(list) for efficient category/domain indexing. The lock protects registration operations.


363-475: Correct fan-out merge logic with constraint validation.

The registration correctly validates that subsequent registrations for the same message type have matching category and domain constraints before merging handlers. The with_additional_handler pattern preserves immutability of the entry model.


529-560: Idempotent freeze with proper locking.

The freeze method correctly uses the lock for thread safety and is idempotent by design. Good logging of registry state at freeze time.


566-646: Comprehensive validation in get_handlers.

Properly validates: frozen state, entry existence, enabled status, category constraint, and domain constraint. The error message at lines 609-616 helpfully shows registered types for debugging.


713-759: Efficient filtering with set intersection.

The list_message_types method correctly uses set intersection when both category and domain filters are provided, and filters to enabled entries only.


884-963: Solid topic-message type validation.

Correctly extracts both category and domain from the topic string and validates against registered constraints. Error messages are descriptive and actionable.


1-60: Well-documented module with clear design principles.

Excellent module-level docstring explaining the freeze-after-init pattern, thread safety model, performance characteristics, and related tickets. The __all__ export list is explicit and correct.

@jonahgabriel
jonahgabriel merged commit f2c18c8 into main Dec 20, 2025
12 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-937-b1a-implement-central-message-type-registry branch December 20, 2025 20:41
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