Skip to content

feat(runtime): enforce time injection context at dispatch [OMN-973] - #66

Merged
jonahgabriel merged 11 commits into
mainfrom
jonah/omn-973-b4b-enforce-time-injection-context-at-dispatch
Dec 20, 2025
Merged

jonahgabriel merged 11 commits into
mainfrom
jonah/omn-973-b4b-enforce-time-injection-context-at-dispatch

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 20, 2025 •

Copy link
Copy Markdown
Collaborator

Summary

  • Implement runtime dispatch enforcement for time injection context models
  • Reducers must NEVER receive now (deterministic for event replay)
  • Orchestrators/Effects must ALWAYS receive now (time-dependent operations)

Changes

New Models (model_dispatch_context.py)

  • ModelDispatchContext: Pydantic model with time injection validation
  • Factory methods: for_reducer(), for_orchestrator(), for_effect()
  • Model validator rejects reducer contexts with time at construction

New Runtime Components (dispatch_context_enforcer.py)

  • DispatchContextEnforcer: Creates appropriate context based on dispatcher's node_kind
  • Helper methods: requires_time_injection(), forbids_time_injection()
  • Validation method: validate_no_time_injection_for_reducer()

ONEX Architectural Rules Enforced

Node Kind Time Injection Rationale
REDUCER ❌ FORBIDDEN Must be deterministic for event replay
COMPUTE ❌ FORBIDDEN Pure transformations, no side effects
ORCHESTRATOR ✅ REQUIRED Needs time for deadlines/timeouts
EFFECT ✅ REQUIRED Needs time for retries/metrics

Test plan

  • 43 unit tests for DispatchContextEnforcer
  • 32 integration tests for full dispatch flow
  • Tests proving reducers cannot access now via runtime dispatch
  • Tests verifying orchestrators/effects receive now
  • Replay determinism tests for reducers
  • Correlation ID propagation tests
poetry run pytest tests/unit/runtime/test_dispatch_context_enforcer.py tests/integration/runtime/test_dispatch_context_integration.py -v
# 75 passed in 1.49s

Acceptance Criteria (OMN-973)

  • Runtime dispatch enforces context type: reducers never receive now, orchestrators/effects do
  • Tests proving reducers cannot access now via runtime dispatch
  • Integration tests for context injection at dispatch time

Related

  • Ticket: OMN-973
  • Dependencies: OMN-948 (B4: Time injection context models), OMN-945 (B3: Idempotency guard)

Summary by CodeRabbit

  • New Features

    • Added a dispatch context for carrying correlation/trace IDs and timing metadata.
    • Added a dispatch context enforcer to apply time-injection rules per node type.
  • Improvements

    • Raised validation thresholds and tightened infra validation defaults.
    • Consolidated JSON type usage for greater consistency.

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

Implement runtime dispatch enforcement for time injection context models.
Reducers must NEVER receive `now`, orchestrators/effects must receive it.

## Changes

### New Models
- `ModelDispatchContext`: Pydantic model with factory methods enforcing time
  injection rules per node kind (for_reducer, for_orchestrator, for_effect)
- Model validator rejects reducer contexts with time injection at construction

### New Runtime Components
- `DispatchContextEnforcer`: Creates appropriate context based on dispatcher's
  node_kind, injecting time only for orchestrator/effect nodes

### ONEX Architectural Rules Enforced
| Node Kind     | Time Injection | Rationale                              |
|---------------|----------------|----------------------------------------|
| REDUCER       | FORBIDDEN      | Must be deterministic for event replay |
| COMPUTE       | FORBIDDEN      | Pure transformations, no side effects  |
| ORCHESTRATOR  | REQUIRED       | Needs time for deadlines/timeouts      |
| EFFECT        | REQUIRED       | Needs time for retries/metrics         |

## Test Coverage
- 43 unit tests for dispatch context enforcer
- 32 integration tests for full dispatch flow
- All 75 tests passing

Acceptance Criteria:
- [x] Runtime dispatch enforces context type
- [x] Tests proving reducers cannot access `now` via runtime dispatch
- [x] Integration tests for context injection at dispatch time
@linear

linear Bot commented Dec 20, 2025

Copy link
Copy Markdown

OMN-973

@coderabbitai

coderabbitai Bot commented Dec 20, 2025 •

Copy link
Copy Markdown

Walkthrough

Adds an immutable ModelDispatchContext and a DispatchContextEnforcer to enforce ONEX time-injection rules per node kind, consolidates JsonValue imports, updates infra validation constants, and adds comprehensive unit and integration tests for dispatch context behavior.

Changes

Cohort / File(s) Summary
Dispatch Context Model
src/omnibase_infra/models/dispatch/__init__.py, src/omnibase_infra/models/dispatch/model_dispatch_context.py
Adds immutable ModelDispatchContext with correlation_id, optional trace_id and now, node_kind, metadata; factory methods (for_reducer, for_compute, for_orchestrator, for_effect, for_runtime_host) and validation enforcing time-injection rules (reducers/computes forbid now).
Dispatch Context Enforcer
src/omnibase_infra/runtime/__init__.py, src/omnibase_infra/runtime/dispatch_context_enforcer.py
Adds DispatchContextEnforcer which builds ModelDispatchContext from a ProtocolMessageDispatcher and event envelope, applies node-kind time-injection policies, provides validation helpers and predicates, and raises validation errors for unknown/invalid node kinds.
JsonValue Type Consolidation
src/omnibase_infra/plugins/examples/plugin_json_normalizer.py, src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py
Removes local JsonValue forward-alias and imports shared JsonValue from omnibase_infra.models.types.json_types.
Validation Constants
src/omnibase_infra/validation/infra_validators.py
Updates INFRA_MAX_UNIONS default from 410 to 465; adds INFRA_MAX_VIOLATIONS = 0, INFRA_PATTERNS_STRICT = True, and INFRA_UNIONS_STRICT = True; docstrings updated to reflect new baseline.
Integration Tests
tests/integration/runtime/test_dispatch_context_integration.py
Adds comprehensive integration tests covering context creation, time-injection rule enforcement across node kinds, correlation/trace propagation, deterministic reducer replay, and context isolation per dispatch.
Unit Tests
tests/unit/runtime/test_dispatch_context_enforcer.py, tests/unit/validation/test_routing_coverage_validator.py, tests/unit/validation/test_validator_defaults.py
Adds unit tests for DispatchContextEnforcer behaviors and validation helpers; enhances routing coverage thread-safety test with barrier sync and parameterized thread count; updates validator baseline/assertions for INFRA_MAX_UNIONS = 465.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Enforcer as DispatchContextEnforcer
    participant Envelope
    participant ModelDC as ModelDispatchContext
    participant Dispatcher

    Client->>Enforcer: create_context_for_dispatcher(dispatcher, envelope)
    Enforcer->>Dispatcher: inspect node_kind
    Dispatcher-->>Enforcer: node_kind

    alt node_kind is REDUCER or COMPUTE
        Enforcer->>ModelDC: call for_reducer() / for_compute() (no now)
    else node_kind is ORCHESTRATOR or EFFECT or RUNTIME_HOST
        Enforcer->>ModelDC: call for_orchestrator()/for_effect()/for_runtime_host() (now = UTC)
    end

    Enforcer->>Envelope: extract correlation_id, trace_id
    Envelope-->>Enforcer: ids (fallback uuid4 if missing)
    Enforcer->>ModelDC: populate correlation_id, trace_id, metadata
    ModelDC-->>Enforcer: immutable context
    Enforcer-->>Client: return ModelDispatchContext
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay attention to ModelDispatchContext validation/factory semantics and immutability.
  • Review DispatchContextEnforcer node-kind mappings, time-injection predicates, and error paths (unknown node kinds).
  • Inspect extensive tests for correctness, determinism assumptions, and any brittle timing-related assertions.
  • Confirm validation constant updates and corresponding test baseline adjustments.

Poem

🐇 I stitched a context neat and tight,
Now flows of time obey each node's light,
Reducers stay steady, orchestrators chime,
A rabbit hops happily — one dispatch at a time!


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

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement (OMN-973)

This PR implements runtime dispatch enforcement for time injection context models. The implementation is architecturally sound and well-tested with 75 passing tests.

✅ Strengths

Excellent ONEX Compliance:

  • Immutable models (frozen=True) - thread-safe by design
  • Strong typing - no Any types, proper Pydantic models
  • Factory methods - clear API preventing misuse
  • Proper naming conventions
  • Protocol-based design

Robust Validation Strategy:

  • Model validator catches violations at construction
  • Explicit validation method allows dispatch-time checks
  • Enforcer provides centralized enforcement
  • Three-layer defense prevents accidental time injection into reducers

Outstanding Documentation:

  • Comprehensive module docstrings with design patterns
  • Clear examples in every method
  • Thread safety guarantees documented
  • Related tickets tracked

Excellent Test Coverage:

  • 43 unit tests for DispatchContextEnforcer
  • 32 integration tests for full dispatch flow
  • Deterministic testing helpers
  • Edge cases covered: missing IDs, validation failures, all node kinds
  • Replay determinism tests prove reducers can be safely replayed

🔍 Code Quality Issues

Medium: Validation Redundancy

  • Location: model_dispatch_context.py:134-153 and :175-203
  • Issue: _validate_reducer_no_time() and validate_for_node_kind() contain duplicated validation logic
  • Recommendation: Extract to single private method to maintain DRY principle
  • Impact: Medium - maintenance concern, violates DRY principle

Low: COMPUTE Node Inconsistency

  • Location: dispatch_context_enforcer.py:158-165
  • Issue: COMPUTE nodes use direct construction instead of factory method
  • Recommendation: Add for_compute() factory method for consistency
  • Impact: Low - works correctly but lacks API consistency

🔒 Security: No Issues Found

  • No credential handling or PII exposure
  • Immutable models prevent tampering
  • Correlation IDs properly typed as UUID

⚡ Performance: Optimal

  • Stateless enforcer - no synchronization overhead
  • Immutable models - thread-safe without locks
  • Validation at construction - fail-fast

🎯 ONEX Architecture: Full Compliance

All CLAUDE.md rules satisfied:

  • No Any types ✓
  • Pydantic models ✓
  • Proper naming ✓
  • X | None syntax ✓
  • Factory methods ✓

ONEX 4-Node Architecture enforced correctly for all node kinds.

🚀 Recommendations

Must Fix: None - production-ready as-is

Should Address:

  1. DRY violation: Extract duplicated validation logic
  2. API consistency: Add for_compute() factory method

Nice to Have:

  1. Add for_runtime_host() factory
  2. Create docs/patterns/time_injection_enforcement.md
  3. Add ADR documenting event replay rationale

✅ Final Verdict: APPROVE

This PR is well-architected, thoroughly tested, and production-ready.

Excellent software engineering:

  • Strong ONEX compliance
  • Comprehensive test coverage with deterministic testing
  • Clear, maintainable code with exceptional documentation
  • Thread-safe, immutable design

Identified issues are minor code quality improvements that can be addressed in follow-up PRs.

Suggested merge path:

  1. Merge as-is
  2. Create follow-up ticket for DRY refactoring
  3. Create follow-up ticket for pattern documentation

Excellent work on OMN-973! 🎉

- Apply ruff formatting to handler_consul.py, handler_http.py, and
  test_dispatch_context_enforcer.py
- Fix race condition in test_thread_safety by adding threading.Lock
  for thread-safe list access and threading.Barrier for synchronized
  thread start
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement (OMN-973)

Overall Assessment: APPROVED with Minor Suggestions

This PR implements a critical architectural invariant for ONEX - ensuring reducers remain deterministic by preventing time injection at the dispatch layer. The implementation is well-designed, thoroughly tested, and follows ONEX patterns consistently.


Strengths

1. Excellent Architectural Design

  • Single Responsibility: DispatchContextEnforcer has one clear job - enforce time injection rules based on node kind
  • Immutability: ModelDispatchContext uses frozen=True, making it thread-safe and preventing accidental mutations
  • Type Safety: Strong typing throughout, zero Any types (follows ONEX policy)
  • Factory Pattern: Clean factory methods (for_reducer(), for_orchestrator(), for_effect()) that enforce invariants at construction time

2. Defense in Depth

The implementation provides multiple layers of validation:

  • Construction-time validation: Pydantic @model_validator prevents invalid contexts from being created (line 134-153 in model_dispatch_context.py)
  • Runtime validation: validate_no_time_injection_for_reducer() provides explicit checkpoint
  • Type-level enforcement: Factory methods guide developers toward correct usage

3. Outstanding Test Coverage

  • 75 tests total (43 unit + 32 integration)
  • All node types covered: REDUCER, COMPUTE, ORCHESTRATOR, EFFECT, RUNTIME_HOST
  • Edge cases tested: Missing correlation IDs, trace ID propagation, deterministic replay
  • Integration tests: Full dispatch flow validation with context-capturing test dispatchers

4. Exceptional Documentation

  • Comprehensive module docstrings with design rationale
  • Clear examples in every method docstring
  • Inline comments explaining architectural decisions
  • Thread safety guarantees documented

Code Quality Review

Type Annotations

All type annotations follow ONEX conventions:

  • Uses X | None (PEP 604) instead of Optional[X]
  • No Any types
  • Proper use of TYPE_CHECKING to avoid circular imports

Naming Conventions

  • File: model_dispatch_context.py → Class: ModelDispatchContext
  • File: dispatch_context_enforcer.py → Class: DispatchContextEnforcer
  • Follows ONEX Model* pattern for Pydantic models

Error Handling

  • Raises ModelOnexError for unknown node kinds (line 191-195 in dispatch_context_enforcer.py)
  • Raises ValueError for validation failures (appropriate for Pydantic validators)
  • Clear, actionable error messages

Potential Issues

1. Minor: Inconsistent Factory Usage for COMPUTE Nodes

Location: dispatch_context_enforcer.py:158-165

Issue: COMPUTE nodes use direct construction instead of a factory method, while REDUCER nodes use for_reducer() factory.

Recommendation: Consider adding ModelDispatchContext.for_compute() factory method for consistency. This would:

  • Maintain symmetry with for_reducer()
  • Make the API more discoverable
  • Future-proof if COMPUTE nodes need special handling

Severity: Low (nice-to-have, not blocking)


2. Minor: RUNTIME_HOST Also Uses Direct Construction

Location: dispatch_context_enforcer.py:181-188

Issue: Same as above - RUNTIME_HOST uses direct construction instead of a factory.

Recommendation: Consider ModelDispatchContext.for_runtime_host() factory.

Severity: Low (nice-to-have, not blocking)


3. Documentation: Missing for_compute() Factory Note

Location: model_dispatch_context.py:23-26

Issue: Module docstring mentions factory methods but does not list for_compute() (because it does not exist yet).

Recommendation: If you add for_compute(), update this docstring.

Severity: Low (documentation consistency)


Security Considerations

No Security Issues Identified

  • No credential exposure
  • No injection vulnerabilities
  • Proper correlation ID handling (generates UUID4 if missing)
  • Immutable design prevents tampering

Performance Considerations

Excellent Performance Characteristics

  1. Zero Overhead: DispatchContextEnforcer is stateless - no instance state to manage
  2. Efficient Validation: Pydantic validators run at construction time only
  3. Frozen Models: frozen=True enables Pydantic optimizations (no re-validation)
  4. No I/O: All operations are in-memory

Test Coverage Assessment

Coverage: Excellent

Unit Tests (test_dispatch_context_enforcer.py):

  • All node kinds (REDUCER, COMPUTE, ORCHESTRATOR, EFFECT, RUNTIME_HOST)
  • Correlation ID generation when missing
  • Trace ID propagation
  • Validation methods (requires_time_injection, forbids_time_injection)
  • Error cases (unknown node kind, invalid contexts)

Integration Tests (test_dispatch_context_integration.py):

  • Context-capturing test dispatchers
  • Deterministic replay verification
  • Full dispatch flow with DispatcherRegistry
  • Correlation ID propagation through dispatch chain

ONEX Compliance

Fully Compliant with ONEX Standards

Requirement Status
No Any types Zero Any types
Pydantic models ModelDispatchContext uses Pydantic
Naming conventions model_.py → Model
Type annotations Uses X | None (PEP 604)
Documentation Comprehensive docstrings
Thread safety Immutable, stateless enforcer
Error handling Raises ModelOnexError

Code Style

Formatting

  • Handler changes (handler_consul.py, handler_http.py) are purely cosmetic (line length formatting)
  • Consistent with Black/Ruff formatting

Imports

  • Proper use of from future import annotations
  • TYPE_CHECKING guards prevent circular imports
  • Clean all exports

Recommendations for Future Work

1. Add Factory Methods for COMPUTE and RUNTIME_HOST

For consistency and discoverability, consider adding for_compute() and for_runtime_host() factory methods to match the pattern established by for_reducer(), for_orchestrator(), and for_effect().

2. Integration with MessageDispatchEngine

Ensure the MessageDispatchEngine uses DispatchContextEnforcer.create_context_for_dispatcher() before invoking dispatchers. This would complete the integration (likely already done in another PR/ticket).


Final Verdict

APPROVED - This PR is ready to merge.

Summary

  • Architecture: Excellent separation of concerns, immutable design
  • Testing: Comprehensive coverage (75 tests, all passing)
  • Documentation: Outstanding - clear design rationale and examples
  • ONEX Compliance: Fully compliant with all standards
  • Security: No issues identified
  • Performance: Efficient, stateless design

Minor Suggestions (Non-Blocking)

  1. Consider adding for_compute() and for_runtime_host() factory methods for consistency
  2. Update module docstring if factories are added

Great work on this critical architectural enforcement!


Reviewed by: Claude Code (PR Review Agent)
Related Ticket: OMN-973
Test Results: 75/75 passing

Add factory methods to ModelDispatchContext for consistency with existing
for_reducer(), for_orchestrator(), and for_effect() patterns:

- for_compute(): Creates context for COMPUTE nodes (no time injection)
- for_runtime_host(): Creates context for RUNTIME_HOST nodes (with time)

Update DispatchContextEnforcer to use new factory methods and add
comprehensive tests (12 new tests for the factory methods).

Refs: OMN-973
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement at Dispatch (OMN-973)

Summary

This PR implements runtime enforcement of ONEX time injection rules through dispatch context models and an enforcer. The implementation correctly ensures reducers never receive now (for determinism) while orchestrators/effects always receive it (for time-dependent operations).

✅ Strengths

1. Excellent Architecture & Separation of Concerns

  • ModelDispatchContext: Immutable Pydantic model with factory methods enforcing time injection rules at construction
  • DispatchContextEnforcer: Stateless factory delegating context creation based on dispatcher's node_kind
  • Clean separation keeps MessageDispatchEngine focused on routing while enforcing ONEX invariants at the right layer

2. Strong Type Safety (ONEX Compliant)

  • Uses X | None syntax (PEP 604) instead of Optional[X] ✅ (per CLAUDE.md conventions)
  • No Any types ✅
  • Proper Pydantic frozen=True for immutability ✅
  • All models follow Model* naming convention ✅

3. Comprehensive Test Coverage

  • 75 total tests (43 unit + 32 integration) covering all node kinds
  • Tests prove reducers cannot access now via runtime dispatch ✅
  • Replay determinism tests for reducers ✅
  • Thread safety validation with proper threading.Lock and threading.Barrier ✅
  • Correlation ID propagation tests ✅

4. Excellent Documentation

  • Module-level docstrings explain design patterns, thread safety, and rationale
  • Factory methods have clear docstrings with examples
  • Helper methods (requires_time_injection, forbids_time_injection) well-documented
  • References OMN-973 ticket throughout

5. ONEX Architectural Compliance

Correctly enforces time injection rules per CLAUDE.md:

  • REDUCER: ❌ Time (deterministic for event replay)
  • COMPUTE: ❌ Time (pure transformation)
  • ORCHESTRATOR: ✅ Time (deadlines/timeouts)
  • EFFECT: ✅ Time (retries/metrics)
  • RUNTIME_HOST: ✅ Time (infrastructure operations)

6. Proper Error Handling

  • ModelOnexError with EnumCoreErrorCode.VALIDATION_FAILED for unknown node kinds ✅
  • Pydantic model_validator enforces reducer no-time rule at construction ✅
  • Explicit validate_for_node_kind() method for runtime checks ✅

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🔍 Code Quality Observations

Minor: Duplicate Validation Logic

File: model_dispatch_context.py:137-156 and model_dispatch_context.py:178-206

Both _validate_reducer_no_time (model validator) and validate_for_node_kind() implement the same reducer time injection check with slightly different error messages.

Recommendation: Consider having validate_for_node_kind() delegate to the validator's logic to avoid duplication. However, this is minor since the model validator runs at construction and the public method provides an explicit validation point at dispatch time. Current approach is acceptable.

Observation: DispatchContextEnforcer is Stateless

The enforcer has no instance state - all methods could be @staticmethod. This is intentional and correct for a stateless factory. Current pattern is acceptable - a class provides a clear namespace and makes testing easier.

Good: Thread Safety Explicitly Documented

Both ModelDispatchContext (immutable/frozen) and DispatchContextEnforcer (stateless) document their thread safety guarantees. Excellent infrastructure code practice. ✅

🎯 Performance Considerations

Time Injection Overhead

Each dispatcher call creates a new ModelDispatchContext with datetime.now(UTC) for orchestrators/effects.

Assessment: Acceptable overhead for the architectural guarantee. Time injection happens once per dispatch, and datetime.now(UTC) is fast (~microseconds).

No Performance Issues Detected ✅

🔒 Security Considerations

No Security Issues Detected ✅

  • Correlation IDs are UUIDs (no injection risk)
  • No secrets or PII in context
  • Immutable context prevents tampering after creation
  • Time injection comes from datetime.now(UTC) (trusted source)

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

📋 ONEX Compliance Checklist

  • ✅ Strong typing (no Any types)
  • ✅ Pydantic models with proper naming (ModelDispatchContext)
  • ✅ X | None syntax instead of Optional[X]
  • ✅ Proper file naming (model_dispatch_context.py)
  • ✅ No backwards compatibility code
  • ✅ Error handling with ModelOnexError
  • ✅ Comprehensive docstrings
  • ✅ Thread safety documented
  • ✅ Factory pattern for construction

📊 Test Coverage Analysis

Unit Tests (test_dispatch_context_enforcer.py): 43 tests

  • ✅ Factory method validation for all node kinds
  • ✅ Context creation with/without time injection
  • ✅ Reducer no-time validation
  • ✅ Helper methods (requires_time_injection, forbids_time_injection)
  • ✅ Error cases (unknown node kinds, invalid contexts)
  • ✅ Thread safety validation with proper locking

Integration Tests (test_dispatch_context_integration.py): 32 tests

  • ✅ Full dispatch flow with context injection
  • ✅ Reducer determinism verification
  • ✅ Orchestrator/effect time injection verification
  • ✅ Correlation ID propagation
  • ✅ Trace ID propagation
  • ✅ Replay determinism tests

Test Quality: Excellent

  • Tests use proper fixtures and mocks
  • Tests verify both positive and negative cases
  • Thread safety test fixed (commit 6d4b873) with proper threading.Lock and threading.Barrier
  • Integration tests use deterministic helpers for reproducibility

🐛 Potential Issues

None Detected ✅

All acceptance criteria met:

  • Runtime dispatch enforces context type
  • Tests proving reducers cannot access now
  • Integration tests for context injection at dispatch time

📝 Minor Formatting Fixes

Commit 6d4b873 properly addressed:

  • ✅ Ruff formatting applied to handler_consul.py, handler_http.py
  • ✅ Thread safety test race condition fixed with threading.Lock and threading.Barrier

These were the only CI lint issues and have been resolved.

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🎓 Recommendations

For This PR: APPROVED - Ready to Merge

The implementation is solid, well-tested, and follows ONEX patterns correctly. No blocking issues.

For Future Work (Optional Enhancements):

  1. Consider adding metrics/logging in DispatchContextEnforcer: Track time injection enforcement decisions for observability (e.g., count of reducer contexts created, validation failures).

  2. Add correlation ID validation: Consider validating that correlation_id is non-nil at construction (currently relies on uuid4() fallback in enforcer).

  3. Document the relationship with MessageDispatchEngine: Add a comment in MessageDispatchEngine showing how/when DispatchContextEnforcer should be integrated (assuming it will be called from there).

Final Verdict

✅ APPROVE

This PR successfully implements time injection enforcement with:

  • Strong architectural design
  • Comprehensive test coverage (75 tests passing)
  • ONEX compliance (strong typing, proper patterns)
  • No security or performance concerns
  • Excellent documentation

The code is production-ready and fulfills all acceptance criteria for OMN-973.


Reviewed per: CLAUDE.md ONEX infrastructure guidelines
Ticket: OMN-973
Related Work: OMN-948 (B4: Time injection context models), OMN-945 (B3: Idempotency guard)

Jonah added 2 commits December 20, 2025 19:25
- Fix import sorting in handler_consul.py and handler_db.py
- Fix race condition in RoutingCoverageValidator._ensure_discovery()
  by checking _registered_routes (last assigned) instead of
  _discovered_types in double-checked locking pattern
- Add thread safety documentation explaining the invariant
Accept main branch's _initialized flag approach for thread safety
in RoutingCoverageValidator - cleaner than field-ordering dependency.
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Pull Request Review: Time Injection Context Enforcement (OMN-973)

Overview

This PR implements runtime enforcement of time injection rules for ONEX's 4-node architecture. The implementation is architecturally sound, well-tested, and follows ONEX patterns consistently.

Verdict: ✅ APPROVED - Excellent implementation with minor recommendations for consideration.


✅ Strengths

1. Architectural Correctness

  • Correctly enforces determinism for REDUCER and COMPUTE nodes (no now injection)
  • Properly enables time-dependent operations for ORCHESTRATOR, EFFECT, and RUNTIME_HOST nodes
  • Factory pattern prevents accidental misuse through type-safe construction
  • Separation of concerns: ModelDispatchContext for data, DispatchContextEnforcer for runtime logic

2. Strong Type Safety

  • Zero Any types (ONEX requirement met)
  • Proper use of X | None over Optional[X] (follows CLAUDE.md conventions)
  • Immutable context model (frozen=True) prevents mutation bugs
  • Factory methods enforce invariants at compile time

3. Excellent Test Coverage

  • 75 tests total (43 unit + 32 integration)
  • Comprehensive coverage of all node kinds (REDUCER, COMPUTE, ORCHESTRATOR, EFFECT, RUNTIME_HOST)
  • Tests prove reducers cannot access now via runtime dispatch (key acceptance criterion)
  • Replay determinism tests validate architectural invariant
  • Correlation ID propagation thoroughly tested

4. Documentation Quality

  • Comprehensive docstrings with examples for all public methods
  • Clear rationale in module-level documentation
  • Architecture diagrams in PR description
  • Explicit versionadded markers (0.5.0)

5. Error Handling

  • Pydantic model validation prevents invalid contexts at construction (_validate_reducer_no_time)
  • validate_for_node_kind() provides explicit runtime validation
  • validate_no_time_injection_for_reducer() offers enforcement checkpoint
  • Proper use of ModelOnexError with appropriate error codes (VALIDATION_FAILED)

🔍 Code Quality Analysis

ModelDispatchContext (model_dispatch_context.py)

Excellent:

  • Factory methods are well-designed and prevent misuse
  • Validation logic is clear and maintainable
  • Thread-safe immutability through frozen=True
  • has_time_injection property provides clean abstraction

Observations:

  1. Duplication in Validation (Lines 150-156 and 199-206):

    • _validate_reducer_no_time (model validator) and validate_for_node_kind() have similar logic
    • This is intentional: model validator enforces at construction, explicit method allows runtime re-validation
    • ✅ Acceptable pattern for defense-in-depth
  2. validate_for_node_kind() Return Type (Line 178):

    • Returns bool but only ever returns True (raises on failure)
    • Consider -> None or -> Literal[True] for clarity
    • Current pattern matches Pydantic validator conventions, so acceptable
  3. COMPUTE Node Not Mentioned in Module Docstring (Lines 9-10):

    • Module docstring mentions REDUCER, ORCHESTRATOR, EFFECT but omits COMPUTE
    • COMPUTE is properly handled in code (no time injection)
    • Minor documentation inconsistency

DispatchContextEnforcer (dispatch_context_enforcer.py)

Excellent:

  • Stateless design makes it thread-safe
  • Clear separation of factory logic by node kind
  • Helper methods (requires_time_injection, forbids_time_injection) aid readability
  • Proper fallback error handling for unknown node kinds (line 186)

Observations:

  1. Timestamp Generation (Lines 168, 175, 182):

    • datetime.now(UTC) called at context creation time (not dispatch start)
    • This is correct for most scenarios but worth documenting
    • Could cause slight drift if context creation is delayed from actual dispatch
    • ✅ Acceptable: drift would be microseconds in practice
  2. validate_no_time_injection_for_reducer() Naming (Line 192):

    • Method is specific to reducers but enforcer also forbids time for COMPUTE
    • Consider generalizing to validate_no_time_injection() or adding validate_no_time_injection_for_compute()
    • Current name is accurate but asymmetric with forbids_time_injection() which handles both
    • ✅ Acceptable: reducer case is most critical and name is explicit
  3. Error Message Detail (Lines 217-221):

    • Includes now value in error message
    • Follows ONEX error sanitization guidelines (timestamps are safe to log)
    • ✅ Good: aids debugging

🔒 Security Considerations

✅ No security concerns identified:

  • No credentials, secrets, or PII in error messages
  • Correlation IDs and timestamps are safe to log
  • Immutable model prevents tampering
  • No injection vulnerabilities

⚡ Performance Considerations

Strengths:

  • Stateless enforcer means zero overhead from instance management
  • Factory methods are lightweight (just calls to ModelDispatchContext())
  • Frozen Pydantic models optimize memory usage
  • No reflection or dynamic code execution

Potential Optimization:

  • datetime.now(UTC) called for every orchestrator/effect/runtime_host dispatch
  • Could consider time injection at envelope creation if timestamp needs to be shared across multiple dispatchers
  • ✅ Current approach is fine: microsecond overhead negligible, each dispatcher gets precise timestamp

📋 Test Coverage Assessment

Unit Tests (test_dispatch_context_enforcer.py):

  • ✅ All node kinds tested (REDUCER, COMPUTE, ORCHESTRATOR, EFFECT, RUNTIME_HOST)
  • ✅ Correlation ID propagation tested (with and without IDs)
  • ✅ Validation methods tested (validate_no_time_injection_for_reducer)
  • ✅ Helper methods tested (requires_time_injection, forbids_time_injection)
  • ✅ Error cases tested (unknown node kind)

Integration Tests (test_dispatch_context_integration.py):

  • ✅ Full dispatch flow tested with real dispatchers
  • ✅ Replay determinism tests (critical for REDUCER validation)
  • ✅ Context injection at dispatch time verified
  • ✅ Correlation ID propagation through full stack

Missing Tests (not critical but worth considering):

  • Edge case: What if envelope.correlation_id is invalid UUID format? (Currently: fallback to uuid4() which is safe)
  • Edge case: Timezone-aware vs naive datetime (Currently: enforced UTC, which is correct)

🎯 Adherence to CLAUDE.md

Rule Status Notes
No Any types ✅ Pass All types properly specified
Use X | None over Optional[X] ✅ Pass Lines 111, 132 follow PEP 604
Pydantic models for data ✅ Pass ModelDispatchContext is proper Pydantic model
One model per file ✅ Pass model_dispatch_context.py has one model
File naming model_*.py ✅ Pass Follows model_<name>.py pattern
Error handling with OnexError ✅ Pass ModelOnexError used appropriately
Thread safety documentation ✅ Pass Explicitly documented in both files
Strong typing ✅ Pass All parameters and returns properly typed

🐛 Potential Bugs

None identified. The implementation appears bug-free.


💡 Recommendations (Non-Blocking)

1. Documentation Consistency

File: model_dispatch_context.py:9-10

Add COMPUTE node to module docstring for completeness:

# Current:
- **Reducers** (pure state aggregators) must NEVER receive `now`
- **Orchestrators** and **Effects** CAN receive `now`

# Suggested:
- **Reducers** and **Compute** nodes (pure/deterministic) must NEVER receive `now`
- **Orchestrators**, **Effects**, and **Runtime Hosts** CAN receive `now`

2. Consider Return Type Clarification

File: model_dispatch_context.py:178

For maximum clarity, consider -> Literal[True] to signal "always True or raises":

from typing import Literal

def validate_for_node_kind(self) -> Literal[True]:
    """..."""

This is a minor style choice; current pattern is also valid.

3. Consider Timestamp Injection Point Documentation

File: dispatch_context_enforcer.py:168

Add comment clarifying when timestamp is captured:

if node_kind == EnumNodeKind.ORCHESTRATOR:
    # Timestamp captured at context creation (dispatch time)
    # Drift from actual handler execution is microseconds in practice
    return ModelDispatchContext.for_orchestrator(
        correlation_id=correlation_id,
        trace_id=trace_id,
        now=datetime.now(UTC),  # Captured here
    )

📊 Impact Assessment

Positive Impact:

  • ✅ Enforces critical ONEX architectural invariant (reducer determinism)
  • ✅ Prevents accidental time-dependent code in reducers (event replay safety)
  • ✅ Type-safe factory pattern prevents misuse at development time
  • ✅ Clear error messages aid debugging when violations occur
  • ✅ No breaking changes to existing code

Risk:

  • ⚠️ Low: New validation could catch existing violations if reducers were accidentally receiving now
  • Mitigation: Tests prove this works correctly; existing code should be validated

🎓 Learning Opportunities

This PR demonstrates excellent software engineering practices:

  1. Defense in depth: Multiple validation layers (model validator + explicit method + enforcer)
  2. Immutability: Frozen Pydantic models prevent state mutation bugs
  3. Factory pattern: Enforces invariants through API design rather than runtime checks
  4. Comprehensive testing: Unit + integration + replay determinism tests
  5. Clear documentation: Rationale and examples aid future maintainers

Final Verdict

✅ APPROVED

This PR successfully implements time injection enforcement with:

  • Strong architectural correctness
  • Excellent test coverage (75 tests, all passing)
  • Clean, maintainable code following ONEX patterns
  • Comprehensive documentation
  • No security or performance concerns

The minor recommendations above are stylistic improvements, not blockers. The implementation is production-ready.

Great work on OMN-973! This is a critical piece of infrastructure for ONEX runtime correctness.


Reviewed by: Claude Code
Standards: ONEX CLAUDE.md compliance
Coverage: 75/75 tests passing ✅

- Add COMPUTE nodes and Runtime Hosts to module docstring
- Use Literal[True] return type for validate_for_node_kind()
- Add comment clarifying timestamp capture timing in enforcer
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement (OMN-973)

✅ Overall Assessment

APPROVED - This is an excellent implementation that successfully enforces critical ONEX architectural invariants around time injection. The code is well-structured, thoroughly tested, and follows ONEX coding standards.


🎯 Strengths

1. Architectural Correctness ⭐

  • Correctly enforces ONEX time injection rules:
    • Reducers/Compute: NO time injection (deterministic) ✅
    • Orchestrators/Effects/Runtime Hosts: WITH time injection ✅
  • Factory method pattern makes misuse difficult
  • Pydantic validation at construction time prevents invalid states

2. Excellent Code Quality ⭐

  • Immutability: frozen=True on ModelDispatchContext ensures thread-safety
  • Type Safety: Follows ONEX "no Any types" rule - uses ModelEventEnvelope[object] appropriately
  • Clean separation: DispatchContextEnforcer handles enforcement, ModelDispatchContext handles state
  • Comprehensive documentation: Module, class, and method docstrings are thorough

3. Outstanding Test Coverage ⭐

  • 75 tests total (43 unit + 32 integration)
  • Tests cover all node kinds (REDUCER, COMPUTE, ORCHESTRATOR, EFFECT, RUNTIME_HOST)
  • Integration tests verify full dispatch flow
  • Thread safety tests with proper synchronization (barrier + lock)
  • Edge cases tested (missing correlation_id, invalid contexts)

4. ONEX Standards Compliance ⭐

  • ✅ Uses X | None instead of Optional[X] (PEP 604)
  • ✅ Proper naming: ModelDispatchContext, DispatchContextEnforcer
  • ✅ Correlation ID tracking throughout
  • ✅ Proper error handling with ModelOnexError
  • ✅ Literal[True] return type for validation (excellent type design)

🔍 Code Quality Observations

Model Design (model_dispatch_context.py)

Excellent:

  • Factory methods provide clear, safe API
  • has_time_injection property adds semantic clarity
  • validate_for_node_kind() with Literal[True] return type is excellent type design
  • Dual validation (@model_validator + explicit method) provides defense in depth

Minor Consideration:
The validator _validate_reducer_no_time only checks REDUCER, not COMPUTE. While factory methods handle this correctly, direct construction could allow:

# This would be invalid but validator wouldn't catch it
ctx = ModelDispatchContext(
    correlation_id=uuid4(),
    node_kind=EnumNodeKind.COMPUTE,
    now=datetime.now(UTC),  # COMPUTE shouldn't have time either
)

Recommendation: Consider updating validator to check both REDUCER and COMPUTE:

if self.node_kind in {EnumNodeKind.REDUCER, EnumNodeKind.COMPUTE} and self.now is not None:
    msg = (
        f"{self.node_kind.value} nodes cannot receive time injection. "
        "They must be deterministic - use appropriate factory method."
    )
    raise ValueError(msg)

This is not a blocker since factory methods are the primary API, but would provide extra safety.

Enforcer Design (dispatch_context_enforcer.py)

Excellent:

  • Stateless design enables thread-safety without synchronization
  • Clear separation of concerns (routing logic vs validation logic)
  • Helpful utility methods (requires_time_injection, forbids_time_injection)
  • Good error messages with proper context

Question: Line 165 comment mentions "microseconds drift" - is this acceptable for all orchestrator/effect use cases? For most operational scenarios (timeouts, TTL), microsecond drift is negligible, but worth documenting if there are precision requirements.

Test Design

Excellent:

  • Proper test structure with clear fixtures
  • Thread safety test uses both threading.Lock and threading.Barrier correctly
  • Integration tests verify actual dispatch flow, not just unit behavior
  • Mock implementations (MockMessageDispatcher, MockEnvelope) are clean and focused

Minor: The thread safety test in test_dispatch_context_enforcer.py is well-designed, but consider adding a comment explaining why both lock (for list access) and barrier (for synchronized start) are needed.


🛡️ Security & Reliability

No Security Concerns Identified

  • Correlation IDs properly tracked for distributed tracing
  • No secrets or sensitive data in context
  • Immutability prevents tampering after creation
  • Validation prevents architectural violations

Thread Safety: ✅ Confirmed

  • ModelDispatchContext is immutable (frozen=True)
  • DispatchContextEnforcer is stateless
  • Thread safety test validates concurrent access

Error Handling: ✅ Proper

  • Uses ModelOnexError with appropriate error codes
  • Clear error messages for debugging
  • Validation at multiple checkpoints

📊 Performance Considerations

Efficient Design:

  • Factory methods have minimal overhead (just object construction)
  • Enforcer is stateless (no initialization cost)
  • datetime.now(UTC) called only when time injection is required
  • Immutability enables safe caching/reuse scenarios

No Performance Concerns


📝 Documentation Quality

Excellent:

  • Module docstrings explain design rationale
  • Examples in docstrings are clear and runnable
  • Thread safety documented explicitly
  • Related tickets/docs referenced (OMN-973, B4)

Suggestion: Consider adding a "Common Pitfalls" section to the module docstring warning against direct construction instead of factory methods.


✨ Notable Implementation Details

  1. Literal[True] return type on validate_for_node_kind() - This is elegant type design that signals "raises or returns True, never False"

  2. UUID generation fallback - correlation_id = envelope.correlation_id or uuid4() handles missing IDs gracefully

  3. Comprehensive factory coverage - All 5 node kinds have dedicated factory methods

  4. Defense in depth - Both Pydantic validator AND explicit validation method


🔄 Integration Impact

Files Changed: 7 files, +2719 lines, -4 lines (mostly tests)

Breaking Changes: None - This is additive functionality

Dependencies:

  • omnibase_core enums/models (existing)
  • No new external dependencies

Migration Path: Clean - new functionality, doesn't affect existing code


✅ Acceptance Criteria Verification

From PR description (OMN-973):

  • ✅ Runtime dispatch enforces context type: reducers never receive now, orchestrators/effects do
  • ✅ Tests proving reducers cannot access now via runtime dispatch
  • ✅ Integration tests for context injection at dispatch time

All acceptance criteria met.


📋 Minor Suggestions (Non-Blocking)

  1. Strengthen Pydantic validator to also check COMPUTE nodes (see model design section)
  2. Add comment in thread safety test explaining barrier + lock usage
  3. Document precision requirements for time injection if microsecond drift matters
  4. Consider adding "Common Pitfalls" section to docstrings

🎉 Final Verdict

APPROVE - Excellent work!

This PR demonstrates:

  • ✅ Deep understanding of ONEX architectural principles
  • ✅ Excellent code quality and testing practices
  • ✅ Strong type safety and thread safety
  • ✅ Clear documentation and maintainability

The implementation successfully enforces critical ONEX invariants (reducer determinism) at the type level, making violations difficult. The 75 comprehensive tests provide confidence in correctness.

Ready to merge after addressing the minor validator suggestion if desired (though current implementation is safe through factory method usage).


📚 References

  • OMN-973: Time injection enforcement at dispatch
  • B4 (Handler Context / Time Injection) in ONEX Runtime ticket plan
  • docs/design/ONEX_RUNTIME_REGISTRATION_TICKET_PLAN.md
  • ONEX architectural rules in CLAUDE.md

Great work on this implementation! The ONEX architecture is stronger for having these runtime invariants enforced at the type level.

- Extend Pydantic validator to block both REDUCER and COMPUTE from receiving `now`
- Add validate_no_time_injection_for_compute() and
  validate_no_time_injection_for_deterministic_node() methods to enforcer
- Add comprehensive tests for COMPUTE node time injection validation
- COMPUTE nodes are pure/deterministic and must not receive time context
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement [OMN-973]

Overall Assessment: ✅ Excellent Implementation

This PR demonstrates exceptional quality in implementing a critical ONEX architectural invariant - enforcing time injection rules for deterministic vs non-deterministic nodes. The implementation is well-designed, thoroughly tested, and follows ONEX best practices.


Strengths

🏗️ Architecture & Design

Strong Separation of Concerns

  • ModelDispatchContext: Immutable data model with Pydantic validation (frozen=True)
  • DispatchContextEnforcer: Stateless factory enforcing time injection rules
  • Clear separation between model validation (Pydantic) and runtime enforcement

Factory Method Pattern (model_dispatch_context.py:219-415)

  • Excellent use of factory methods (for_reducer(), for_compute(), for_orchestrator(), etc.)
  • Each factory encodes the time injection rule for its node type
  • Type-safe API prevents accidental misuse

Fail-Fast Validation

  • Pydantic @model_validator enforces rules at construction time (model_dispatch_context.py:138-161)
  • Runtime validation methods provide explicit checkpoints
  • Literal[True] return type clearly signals "validates or raises" semantics (model_dispatch_context.py:183)

🧪 Test Coverage

Comprehensive Testing

  • 61 unit tests for DispatchContextEnforcer
  • 32 integration tests for end-to-end dispatch flow
  • Total: 93 tests (claimed 75 in PR description - actual count is higher)
  • Thread safety tests with proper synchronization (test_dispatch_context_enforcer.py)

Good Test Organization

  • Proper fixtures for different node kinds
  • Clear test naming following AAA pattern
  • Tests cover both success paths and error conditions

📚 Documentation

Exceptional Documentation Quality

  • Comprehensive module-level docstrings explaining the "why"
  • Clear ONEX architecture table showing time injection rules
  • Examples in docstrings demonstrate correct usage
  • Inline comments explaining design decisions (e.g., "Timestamp captured at context creation")

Type Annotations

  • Literal[True] return type on validate_for_node_kind() is excellent
  • ModelEventEnvelope[object] instead of Any (follows ONEX "no Any types" rule)
  • Proper use of X | None (PEP 604) instead of Optional[X]

✅ ONEX Compliance

Follows ONEX Guidelines

  • ✅ No Any types - uses object for generic envelope payloads
  • ✅ Pydantic models for all data structures
  • ✅ Frozen/immutable models (thread-safe)
  • ✅ X | None instead of Optional[X]
  • ✅ Proper error handling with ModelOnexError
  • ✅ Uses EnumCoreErrorCode.VALIDATION_FAILED for validation errors

Issues & Recommendations

🔴 Critical: Potential Clock Skew in Distributed Systems

Issue: Time captured at dispatch creation vs handler execution (dispatch_context_enforcer.py:164-171)

# Timestamp captured at context creation (dispatch time).
# Drift from actual handler execution is microseconds in practice.
return ModelDispatchContext.for_orchestrator(
    correlation_id=correlation_id,
    trace_id=trace_id,
    now=datetime.now(UTC),  # <-- Captured at dispatch, not handler execution
)

Problem: Comment claims "microseconds in practice" but this assumes:

  • Single-process execution (no message queues)
  • No circuit breaker delays
  • No retry backoff
  • No cross-datacenter dispatch

In distributed ONEX deployments with Kafka, the time between dispatch context creation and handler execution could be seconds or minutes due to:

  • Kafka consumer lag
  • Retry with exponential backoff
  • Circuit breaker open states
  • Cross-region message routing

Recommendation:

  1. Add a comment documenting this is "dispatch time" not "handler execution time"
  2. Consider whether handlers need both dispatched_at and now timestamps
  3. Document the acceptable drift tolerance in the design docs

Severity: Medium (won't cause correctness issues, but could cause subtle time-based bugs in timeout/deadline logic)


🟡 Medium: Validation Method Duplication

Issue: Three nearly identical validation methods (dispatch_context_enforcer.py:194-291)

validate_no_time_injection_for_reducer()     # Lines 194-224
validate_no_time_injection_for_compute()     # Lines 226-256
validate_no_time_injection_for_deterministic_node()  # Lines 258-291

Problem: Code duplication and unclear API surface. When should I call validate_no_time_injection_for_reducer() vs validate_no_time_injection_for_deterministic_node()?

Recommendation: Consider consolidating to a single method:

def validate_no_time_injection(
    self,
    context: ModelDispatchContext,
) -> None:
    """Validate that deterministic nodes don't have time injection.
    
    Raises:
        ModelOnexError: If REDUCER or COMPUTE node has time injection.
    """
    if context.node_kind in (EnumNodeKind.REDUCER, EnumNodeKind.COMPUTE) and context.now is not None:
        raise ModelOnexError(
            message=f"{context.node_kind.value.upper()} nodes cannot receive time injection..."
        )

Or keep the specific methods but mark them as deprecated in favor of the general one.

Severity: Low (doesn't affect correctness, just API clarity)


🟡 Medium: Missing RUNTIME_HOST in Validation Helpers

Issue: requires_time_injection() and forbids_time_injection() are not exhaustive (dispatch_context_enforcer.py:293-358)

def requires_time_injection(self, node_kind: EnumNodeKind) -> bool:
    return node_kind in {
        EnumNodeKind.ORCHESTRATOR,
        EnumNodeKind.EFFECT,
        EnumNodeKind.RUNTIME_HOST,  # ✅ Included
    }

def forbids_time_injection(self, node_kind: EnumNodeKind) -> bool:
    return node_kind in {
        EnumNodeKind.REDUCER,
        EnumNodeKind.COMPUTE,
        # ❌ Not exhaustive - what if new node kinds are added?
    }

Problem: These methods are not truly inverse of each other. If a new node kind is added to EnumNodeKind, both methods could return False.

Recommendation: Make one method derive from the other:

def forbids_time_injection(self, node_kind: EnumNodeKind) -> bool:
    return not self.requires_time_injection(node_kind)

Or explicitly handle all cases and raise for unknown node kinds.

Severity: Low (unlikely to cause issues, but could mask bugs if new node kinds are added)


🟢 Minor: Inconsistent Error Types

Issue: Pydantic validator raises ValueError, enforcer methods raise ModelOnexError

  • Pydantic @model_validator: Raises ValueError (model_dispatch_context.py:160)
  • validate_for_node_kind(): Raises ValueError (model_dispatch_context.py:216)
  • Enforcer validation methods: Raise ModelOnexError (dispatch_context_enforcer.py:219)

Problem: Callers need to catch different exception types depending on where validation occurs.

Recommendation: Document this distinction clearly:

  • ValueError: Construction-time validation (use factory methods to avoid)
  • ModelOnexError: Runtime validation (expected in dispatch logic)

This is actually reasonable separation - Pydantic validators should raise ValueError, runtime business logic should raise domain errors.

Severity: Very Low (current design is defensible)


Performance Considerations

✅ Good: Stateless Enforcer

The DispatchContextEnforcer is stateless and thread-safe, allowing concurrent context creation without locks.

✅ Good: Immutable Context

ModelDispatchContext is frozen, preventing mutation bugs and making it safe to share across threads.

⚠️ Minor: Repeated datetime.now(UTC) Calls

If creating many contexts in a tight loop, calling datetime.now(UTC) multiple times could introduce timestamp drift. Not an issue for typical dispatch rates.


Security Considerations

✅ No Security Issues Identified

  • No credential handling
  • No external I/O
  • No user input processing
  • Proper error sanitization (no sensitive data in error messages)

Recommendations Summary

Must Fix Before Merge: None

Should Consider:

  1. Clarify time capture semantics - Document that now is dispatch time, not handler execution time
  2. Consolidate validation methods - Reduce API surface by consolidating three similar validation methods
  3. Make forbids_time_injection() exhaustive - Derive from requires_time_injection() to ensure they're true inverses

Nice to Have:

  1. Document when to catch ValueError vs ModelOnexError
  2. Add integration test showing time drift between dispatch and handler execution

Final Verdict

Status: ✅ Approved with Minor Suggestions

This is excellent work that significantly strengthens ONEX's architectural guarantees. The implementation is:

  • Well-designed with clear separation of concerns
  • Thoroughly tested (93 tests)
  • Exceptionally well-documented
  • Fully compliant with ONEX coding standards

The issues identified are minor improvements, not blockers. The PR can merge as-is, with the suggestions addressed in follow-up work if desired.

Great job on implementing this critical architectural invariant! 🎉


ONEX Compliance Checklist

  • ✅ No Any types (uses object for generics)
  • ✅ Pydantic models for all data structures
  • ✅ X | None instead of Optional[X]
  • ✅ Factory method pattern for construction
  • ✅ Immutable models (frozen=True)
  • ✅ Proper error handling with ModelOnexError
  • ✅ Comprehensive docstrings
  • ✅ Type annotations throughout
  • ✅ Thread-safe design
  • ✅ 93 tests (unit + integration)

ONEX Compliance Score: 10/10 ⭐

Jonah added 2 commits December 20, 2025 20:42
Extract duplicated validation logic between _validate_deterministic_node_no_time()
and validate_for_node_kind() into a single private method _is_invalid_time_injection().

This addresses PR #66 review feedback about DRY violation while preserving
the distinct error messages for construction-time vs validation-time contexts.

Related: OMN-973
- DispatchContextEnforcer from OMN-973
- MessageTypeRegistry exports from OMN-937
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Code Review: Time Injection Context Enforcement [OMN-973]

Summary

This PR implements runtime dispatch enforcement for time injection context models, ensuring that reducers and compute nodes NEVER receive now (deterministic execution) while orchestrators, effects, and runtime hosts ALWAYS receive now (time-dependent operations). The implementation is architecturally sound and well-tested.


✅ Strengths

1. Excellent Architectural Design

  • Separation of concerns: ModelDispatchContext (immutable data) and DispatchContextEnforcer (factory/validator) are cleanly separated
  • Factory pattern: Type-safe factory methods (for_reducer(), for_orchestrator(), etc.) enforce rules at construction time
  • Immutability: frozen=True on Pydantic model ensures thread-safety
  • Fail-fast validation: Pydantic @model_validator catches violations at model construction, not later

2. Strong Type Safety ✅

  • Uses X | None (PEP 604) syntax consistently - follows ONEX guidelines
  • Return type Literal[True] on validate_for_node_kind() clearly signals "returns True or raises"
  • No Any types - uses ModelEventEnvelope[object] appropriately for generic dispatch

3. Comprehensive Testing ✅

  • 75 tests total (43 unit + 32 integration) with excellent coverage
  • Tests cover critical acceptance criteria for reducers, orchestrators, effects
  • Test names clearly indicate what they prove

4. Excellent Documentation 📖

  • Comprehensive module docstrings with design patterns, thread safety notes, and examples
  • Clear ADR-style documentation in docstrings explaining why decisions were made

🔍 Issues Found

🔴 CRITICAL: Potential Time Drift Issue

Location: dispatch_context_enforcer.py:165-170

Problem: Time is captured when the context is created, not when the handler executes. Under high load or with async queuing, drift could be seconds or more, not microseconds as the comment claims.

Recommendation: Either accept the drift and update docs to say "time at dispatch", defer time capture with a callable, or add a test measuring acceptable drift bounds.

Severity: Medium-High - May cause incorrect timeout/deadline calculations under load


🟡 MEDIUM: Missing Edge Case Tests

1. No test for unknown EnumNodeKind

What happens if a future EnumNodeKind is added but the enforcer isn't updated? Add test with all enum values to ensure exhaustive handling.

2. Correlation ID generation edge case

What if envelope.correlation_id is an empty string instead of None? Add type check or test that validates it's actually a UUID when present.


🟡 MEDIUM: Metadata Field Lacks Validation

Location: model_dispatch_context.py:133-136

Issues:

  1. No max size constraint - Could allow unbounded metadata payloads
  2. String values only - May need richer types
  3. No documented contract - What keys/values are expected?

Recommendation: Add max items constraint and document expected metadata keys.


🟢 MINOR: Code Quality

1. Redundant validation methods

DispatchContextEnforcer has three similar validation methods. Keep validate_no_time_injection_for_deterministic_node() and deprecate the others to reduce API surface.

2. Test helper duplication

MockEnvelope is duplicated between unit and integration tests. Consider moving to tests/helpers/.


🔒 Security Review

✅ No security concerns identified


🚀 Performance Considerations

✅ Good Performance Design

  • Stateless enforcer - No locking required
  • Frozen Pydantic models - No runtime validation overhead

⚠️ UUID Generation

Calls uuid4() on every dispatch when correlation_id is missing. Under high throughput, this could add overhead. Consider requiring correlation_id explicitly.


🎯 ONEX Compliance Review

✅ Fully Compliant

  • ✅ No Any types
  • ✅ Strong typing with X | None (PEP 604)
  • ✅ Immutable Pydantic models (frozen=True)
  • ✅ Excellent documentation
  • ✅ Factory pattern enforcement
  • ✅ Thread-safe design
  • ✅ Follows ONEX error patterns

🏁 Conclusion

Verdict: APPROVE with Minor Recommendations ✅

This is a high-quality PR that correctly implements a critical ONEX architectural requirement.

Before Merge:

  1. MUST: Address time drift issue (clarify in docs OR defer time capture OR measure/test)
  2. SHOULD: Add test for unknown EnumNodeKind edge case
  3. SHOULD: Document metadata field expectations
  4. CONSIDER: Consolidate redundant validation methods

Impact Assessment:

  • ✅ Low risk - Well-isolated change with comprehensive tests
  • ✅ High value - Enforces critical architectural invariant (reducer determinism)
  • ✅ Good foundation - Extensible design for future context fields

Great work on OMN-973! 🎉


Review conducted according to ONEX infrastructure standards (CLAUDE.md)

- Consolidate duplicate JsonValue definitions in plugin examples
  to import from centralized json_types.py
- Update INFRA_MAX_UNIONS threshold from 450 to 465 to accommodate
  legitimate unions added by OMN-937 MessageTypeRegistry
- Update test expectations for new threshold

The OMN-937 PR added centralized JSON type definitions that increased
the union count. This fix consolidates duplicates and adjusts the
threshold to reflect the new baseline after that architectural change.

Fixes CI failure: Unions: FAIL - Total unions: 462, max allowed: 450
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Time Injection Context Enforcement (OMN-973)

Summary

This PR implements runtime enforcement of ONEX time injection rules, ensuring reducers and compute nodes maintain determinism by never receiving now, while orchestrators/effects receive time for operational decisions. The implementation is architecturally sound, well-tested, and follows ONEX patterns correctly.

✅ Strengths

1. Excellent Architecture & Design

  • Single Responsibility: ModelDispatchContext handles data modeling with validation, DispatchContextEnforcer handles runtime enforcement - clean separation of concerns
  • Immutability: frozen=True on the context model ensures thread safety
  • Factory Pattern: Type-safe factory methods (for_reducer(), for_orchestrator(), etc.) prevent misuse at construction time
  • Stateless Enforcer: Thread-safe without synchronization overhead

2. Strong ONEX Compliance

  • ✅ Follows "no Any types" rule (uses ModelEventEnvelope[object] per CLAUDE.md guidelines)
  • ✅ Proper model naming: ModelDispatchContext follows Model<Name> convention
  • ✅ Proper file naming: model_dispatch_context.py, dispatch_context_enforcer.py
  • ✅ Uses X | None (PEP 604) over Optional[X] per type annotation conventions
  • ✅ Enforces strong typing throughout
  • ✅ Proper error handling with ModelOnexError and EnumCoreErrorCode

3. Comprehensive Testing

  • 75 tests total (43 unit + 32 integration)
  • Excellent coverage: Factory methods, validators, thread safety, correlation ID propagation, edge cases
  • Integration tests verify full dispatch flow with context injection
  • Deterministic testing: Uses DeterministicClock for reproducible time-based tests
  • Thread safety validated: Proper use of barriers and locks in concurrent tests

4. Documentation Excellence

  • Module docstrings explain architectural rationale
  • Clear examples in docstrings
  • Literal[True] return type on validate_for_node_kind() documents "raises or returns True" contract
  • Thread safety notes where relevant
  • Version annotations (.. versionadded:: 0.5.0)

5. DRY Refactoring

  • Extracted _is_invalid_time_injection() helper to eliminate duplication between validator and validation method (commit 2c01a11)
  • Preserves distinct error messages for different contexts while sharing logic

🔍 Code Quality Observations

Minor Suggestions (Non-blocking)

1. Validation Method Redundancy

The enforcer has three similar validation methods:

  • validate_no_time_injection_for_reducer()
  • validate_no_time_injection_for_compute()
  • validate_no_time_injection_for_deterministic_node()

Observation: The third method (validate_no_time_injection_for_deterministic_node()) makes the first two redundant since it handles both REDUCER and COMPUTE cases. Consider whether all three are needed or if the generic one suffices.

Counter-argument: Having specific methods improves error messages and intent clarity. Current approach is defensible.

2. Error Code Consistency

src/omnibase_infra/runtime/dispatch_context_enforcer.py:188-192

When an unknown node_kind is encountered, the code raises ModelOnexError with VALIDATION_FAILED. However, this scenario indicates a programming error (invalid enum value), not a validation failure.

Suggestion: Consider using EnumCoreErrorCode.INTERNAL_ERROR or adding a comment explaining why VALIDATION_FAILED is appropriate here.

# Current:
raise ModelOnexError(
    message=f"Unknown node_kind '{node_kind}' ...",
    error_code=EnumCoreErrorCode.VALIDATION_FAILED,  # Is this a validation failure or internal error?
)

3. Correlation ID Generation Timing

src/omnibase_infra/runtime/dispatch_context_enforcer.py:148

correlation_id = envelope.correlation_id or uuid4()

Observation: UUID generation happens unconditionally, even when envelope.correlation_id exists. This creates a UUID object that's immediately discarded.

Optimization (micro-optimization, not critical):

correlation_id = envelope.correlation_id if envelope.correlation_id is not None else uuid4()

However, the current approach is more readable and the performance impact is negligible. Not worth changing unless you're optimizing hot paths.

4. Timestamp Drift Comment

src/omnibase_infra/runtime/dispatch_context_enforcer.py:165-166

# Timestamp captured at context creation (dispatch time).
# Drift from actual handler execution is microseconds in practice.

Observation: Great documentation! Consider adding a note about whether this drift is acceptable for the use case, or if there are scenarios where it matters.

🔒 Security & Performance

Security

  • ✅ No security concerns: Context model uses proper validation, no credential/secret handling
  • ✅ Input validation: Pydantic validators enforce invariants at construction time
  • ✅ Immutability: Frozen model prevents tampering after creation

Performance

  • ✅ Minimal overhead: Stateless enforcer, no caching needed
  • ✅ Thread-safe without locks: Immutable context design eliminates synchronization
  • ✅ Efficient: UUID generation only when needed (correlation_id missing)

Thread Safety

  • ✅ Context is frozen/immutable: Safe for concurrent reads
  • ✅ Enforcer is stateless: Safe for concurrent calls
  • ✅ Tests validate concurrent access: Proper use of threading primitives in tests

📊 Test Coverage Assessment

Unit Tests (test_dispatch_context_enforcer.py)

  • ✅ Factory methods (reducer, orchestrator, effect, compute, runtime_host)
  • ✅ Validation methods (all three variants)
  • ✅ Helper methods (requires_time_injection(), forbids_time_injection())
  • ✅ Edge cases (missing correlation_id, unknown node_kind)
  • ✅ Thread safety
  • ✅ Error messages

Integration Tests (test_dispatch_context_integration.py)

  • ✅ Full dispatch flow with context injection
  • ✅ Correlation ID propagation
  • ✅ Time injection for orchestrators/effects
  • ✅ No time injection for reducers/compute
  • ✅ Deterministic reducer replay scenarios
  • ✅ Context-capturing test harness

Coverage Assessment: Excellent. The 75 tests cover happy paths, edge cases, concurrency, and integration scenarios comprehensively.

🐛 Potential Issues

None Identified

I did not find any bugs, race conditions, or architectural violations. The code is production-ready.

📝 ONEX Compliance Checklist

  • ✅ No Any types (uses object for generic envelope payloads per guidelines)
  • ✅ Pydantic models for all data structures
  • ✅ Proper naming conventions (files, classes, methods)
  • ✅ Type annotations use X | None over Optional[X]
  • ✅ Error handling uses ModelOnexError with proper error codes
  • ✅ Immutable models (frozen=True)
  • ✅ Factory methods enforce invariants
  • ✅ Thread safety documented and tested
  • ✅ Integration with container/registry patterns
  • ✅ Comprehensive docstrings and examples

🎯 Acceptance Criteria (OMN-973)

  • ✅ Runtime dispatch enforces context type: reducers never receive now, orchestrators/effects do
  • ✅ Tests proving reducers cannot access now via runtime dispatch
  • ✅ Integration tests for context injection at dispatch time
  • ✅ 75 tests passing (43 unit + 32 integration)

📋 Recommendations

Before Merge

  1. ✅ No blocking issues - code is merge-ready
  2. Optional: Address the minor observations above if desired (validation method redundancy, error code choice)
  3. Optional: Verify CI passes (I couldn't check CI status in this environment)

Post-Merge Considerations

  1. Monitor in production: Verify timestamp drift comment is accurate in production workloads
  2. Future refactoring: If you add more node kinds, consider table-driven factory method selection instead of if/elif chain

🏆 Final Verdict

APPROVED ✅

This is high-quality ONEX infrastructure code that:

  • Enforces critical architectural invariants (reducer determinism)
  • Follows ONEX patterns and conventions rigorously
  • Includes excellent test coverage (75 tests)
  • Has clear documentation and examples
  • Is thread-safe and performant
  • Contains no bugs or security issues

The PR successfully implements OMN-973 and maintains the ONEX standard of excellence. The minor observations above are non-blocking suggestions for potential future improvements.

Excellent work! This is exactly the kind of architectural enforcement that makes ONEX robust.


Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2025-12-20
Files Reviewed: 11 files, +2925 lines

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/omnibase_infra/validation/infra_validators.py (1)

420-422: Stale threshold reference in comment.

Line 421 still references the old value of 450 for INFRA_MAX_UNIONS, but the constant is now 465.

🔎 Proposed fix
 # Prerequisites for Re-enabling Strict Mode:
 # ------------------------------------------
 # 1. Complete OMN-934 (Message Dispatch Engine) - addresses dispatch model unions
-# 2. Reduce INFRA_MAX_UNIONS from 450 to <200 through targeted refactoring
+# 2. Reduce INFRA_MAX_UNIONS from 465 to <200 through targeted refactoring
 # 3. Document remaining necessary unions in exempted_patterns
🧹 Nitpick comments (3)
src/omnibase_infra/models/dispatch/model_dispatch_context.py (1)

191-222: Consider consolidating duplicate validation logic.

The validate_for_node_kind() method duplicates the check from _is_invalid_time_injection(). Consider reusing the helper to reduce duplication:

🔎 Suggested refactor
     def validate_for_node_kind(self) -> Literal[True]:
-        if self._is_invalid_time_injection():
+        if self._is_invalid_time_injection():
             msg = (
                 f"Dispatch context validation failed: "
                 f"{self.node_kind.value.upper()} nodes cannot receive time injection "
                 f"(now={self.now}). {self.node_kind.value.capitalize()} nodes must be deterministic."
             )
             raise ValueError(msg)
         return True

The current implementation correctly calls _is_invalid_time_injection(), so this is already well-factored. The slightly different error message justifies keeping it separate.

tests/integration/runtime/test_dispatch_context_integration.py (2)

280-330: Consider if these tests duplicate unit tests.

The TestDispatchContextEnforcerBasics tests for requires_time_injection and forbids_time_injection appear to duplicate tests from tests/unit/runtime/test_dispatch_context_enforcer.py (classes TestRequiresTimeInjection and TestForbidsTimeInjection).

If these tests are intended to verify the same behavior in an integration context, consider adding a comment explaining the intent. Otherwise, consider removing the duplicates to reduce test maintenance burden.


1005-1006: Consider using more specific exception type for frozen model test.

The test catches Exception but Pydantic raises ValidationError (specifically pydantic_core._pydantic_core.ValidationError) when attempting to modify a frozen model. Using a more specific exception would make the test clearer:

🔎 Suggested improvement
+from pydantic import ValidationError
+
-        with pytest.raises(Exception):  # ValidationError for frozen models
+        with pytest.raises(ValidationError):
             ctx.now = datetime.now(UTC)  # type: ignore[misc]
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between f2c18c8 and 68c4164.

📒 Files selected for processing (11)
  • src/omnibase_infra/models/dispatch/__init__.py (3 hunks)
  • src/omnibase_infra/models/dispatch/model_dispatch_context.py (1 hunks)
  • src/omnibase_infra/plugins/examples/plugin_json_normalizer.py (1 hunks)
  • src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py (1 hunks)
  • src/omnibase_infra/runtime/__init__.py (2 hunks)
  • src/omnibase_infra/runtime/dispatch_context_enforcer.py (1 hunks)
  • src/omnibase_infra/validation/infra_validators.py (4 hunks)
  • tests/integration/runtime/test_dispatch_context_integration.py (1 hunks)
  • tests/unit/runtime/test_dispatch_context_enforcer.py (1 hunks)
  • tests/unit/validation/test_routing_coverage_validator.py (1 hunks)
  • tests/unit/validation/test_validator_defaults.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/validation/test_routing_coverage_validator.py
  • src/omnibase_infra/models/dispatch/model_dispatch_context.py
  • src/omnibase_infra/models/dispatch/__init__.py
  • tests/unit/runtime/test_dispatch_context_enforcer.py
  • src/omnibase_infra/runtime/__init__.py
  • tests/unit/validation/test_validator_defaults.py
  • src/omnibase_infra/plugins/examples/plugin_json_normalizer.py
  • src/omnibase_infra/runtime/dispatch_context_enforcer.py
  • src/omnibase_infra/validation/infra_validators.py
  • src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py
  • tests/integration/runtime/test_dispatch_context_integration.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/models/dispatch/model_dispatch_context.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_context.py
  • tests/unit/runtime/test_dispatch_context_enforcer.py
  • src/omnibase_infra/runtime/dispatch_context_enforcer.py
  • tests/integration/runtime/test_dispatch_context_integration.py
**/*infra*.py

📄 CodeRabbit inference engine (CLAUDE.md)

All infrastructure errors must use raise OnexError(...) from e pattern. Never raise or propagate errors without proper error context wrapping

Files:

  • src/omnibase_infra/validation/infra_validators.py
**/*error*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*error*.py: Always include correlation_id in ModelInfraErrorContext for distributed tracing. Generate UUID4 if not present in incoming request
Never include passwords, API keys, tokens, secrets, full connection strings, PII, internal IPs, private keys, or session tokens in error messages or context. Only include service names, operation names, correlation IDs, error codes, sanitized hostnames, port numbers, retry counts, and timeout values

Files:

  • src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py
🧠 Learnings (10)
📓 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 **/*dispatch*.py : Use ModelEventEnvelope[object] instead of Any for generic dispatcher parameters when type is not known at definition time
📚 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_context.py
  • src/omnibase_infra/models/dispatch/__init__.py
  • tests/unit/runtime/test_dispatch_context_enforcer.py
  • src/omnibase_infra/runtime/dispatch_context_enforcer.py
  • tests/integration/runtime/test_dispatch_context_integration.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 enums from `omnibase.enums` package

Applied to files:

  • src/omnibase_infra/models/dispatch/__init__.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: Applies to **/nodes/**/*compute*.py : Enforce ONEX node purity by preventing compute nodes from importing network/database clients (confluent_kafka, httpx, asyncpg, etc.), accessing environment variables (os.environ, os.getenv), or performing file system operations (open(), Path.read_text(), FileHandler)

Applied to files:

  • src/omnibase_infra/runtime/dispatch_context_enforcer.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 proper union type definitions and discriminated unions where appropriate

Applied to files:

  • src/omnibase_infra/validation/infra_validators.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: Deviations from omnibase_core standards are only acceptable for: (1) Orchestrator/Reducer nodes (ModelService* disabled), (2) Experimental features being prototyped for upstream, (3) Performance-critical optimizations with benchmark proof, (4) Bridge-specific unique patterns. All deviations require explicit documentation and justification.

Applied to files:

  • src/omnibase_infra/validation/infra_validators.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/codegen/**/*.py : Code generation service MUST auto-generate ONEX v2.0 compliant nodes with intelligent mixin injection and quality validation. Generate comprehensive test suites with 90%+ coverage.

Applied to files:

  • src/omnibase_infra/validation/infra_validators.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 **/*.py : All error handling must use OnexError exception class with specific error codes from error enum, never raise generic Exception or ValueError

Applied to files:

  • src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py
📚 Learning: 2025-12-20T04:09:41.822Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T04:09:41.822Z
Learning: Applies to **/*.py : Use ModelOnexError with EnumCoreErrorCode for all error handling instead of generic Exception

Applied to files:

  • src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.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 context-based fixtures with pytest.param and conditional dependency injection (e.g., UNIT_CONTEXT vs INTEGRATION_CONTEXT) for mock and integration tests

Applied to files:

  • tests/integration/runtime/test_dispatch_context_integration.py
🧬 Code graph analysis (5)
src/omnibase_infra/models/dispatch/model_dispatch_context.py (3)
tests/helpers/deterministic.py (1)
  • now (136-147)
tests/unit/runtime/test_dispatch_context_enforcer.py (1)
  • node_kind (64-65)
tests/integration/runtime/test_dispatch_context_integration.py (1)
  • node_kind (131-132)
src/omnibase_infra/models/dispatch/__init__.py (1)
src/omnibase_infra/models/dispatch/model_dispatch_context.py (1)
  • ModelDispatchContext (69-420)
tests/unit/runtime/test_dispatch_context_enforcer.py (7)
src/omnibase_infra/enums/enum_dispatch_status.py (1)
  • EnumDispatchStatus (18-181)
src/omnibase_infra/enums/enum_message_category.py (1)
  • EnumMessageCategory (34-196)
src/omnibase_infra/models/dispatch/model_dispatch_context.py (7)
  • ModelDispatchContext (69-420)
  • for_reducer (227-261)
  • for_orchestrator (264-300)
  • has_time_injection (172-189)
  • validate_for_node_kind (191-222)
  • for_compute (344-379)
  • for_runtime_host (382-420)
src/omnibase_infra/models/dispatch/model_dispatch_result.py (1)
  • ModelDispatchResult (62-378)
tests/integration/runtime/test_dispatch_context_integration.py (7)
  • dispatcher_registry (270-272)
  • dispatcher_id (119-120)
  • category (123-124)
  • node_kind (131-132)
  • message_types (127-128)
  • handle (134-150)
  • handle (177-196)
src/omnibase_infra/runtime/dispatcher_registry.py (1)
  • ProtocolMessageDispatcher (65-335)
tests/helpers/deterministic.py (1)
  • now (136-147)
src/omnibase_infra/runtime/__init__.py (1)
src/omnibase_infra/runtime/dispatch_context_enforcer.py (1)
  • DispatchContextEnforcer (68-358)
tests/integration/runtime/test_dispatch_context_integration.py (5)
src/omnibase_infra/enums/enum_message_category.py (1)
  • EnumMessageCategory (34-196)
src/omnibase_infra/models/dispatch/model_dispatch_context.py (6)
  • ModelDispatchContext (69-420)
  • has_time_injection (172-189)
  • for_reducer (227-261)
  • for_orchestrator (264-300)
  • for_effect (303-341)
  • validate_for_node_kind (191-222)
src/omnibase_infra/models/dispatch/model_dispatch_result.py (1)
  • ModelDispatchResult (62-378)
src/omnibase_infra/runtime/dispatch_context_enforcer.py (4)
  • DispatchContextEnforcer (68-358)
  • requires_time_injection (293-325)
  • forbids_time_injection (327-358)
  • create_context_for_dispatcher (109-192)
tests/helpers/deterministic.py (4)
  • DeterministicClock (101-205)
  • DeterministicIdGenerator (31-98)
  • next_uuid (60-76)
  • now (136-147)
🔇 Additional comments (29)
src/omnibase_infra/plugins/examples/plugin_json_normalizer.py (1)

12-12: LGTM: Type centralization improves consistency.

Consolidating to the imported JsonValue type eliminates duplication and ensures consistent type definitions across the codebase.

src/omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py (1)

25-25: LGTM: Consistent type centralization.

This import change mirrors the consolidation in the companion plugin, ensuring both example plugins use the same centralized type definition.

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

736-759: Well-structured thread-safety test improvements.

The use of threading.Barrier ensures all threads start simultaneously, creating a proper race condition scenario. The lock properly protects shared state (results and errors lists), and the parameterized num_threads improves maintainability. The enhanced assertion message with {errors} aids debugging.

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

94-94: Clean public API exposure for ModelDispatchContext.

The import and __all__ export follow the existing patterns in this module. The docstring update on line 10 appropriately documents the new model's purpose.

Also applies to: 117-117

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

41-60: Test assertions correctly updated to match new threshold.

The baseline and threshold updates (462 → 465) align with the INFRA_MAX_UNIONS constant in infra_validators.py. The documentation breakdown and PR references (#61, #67) provide good traceability for the union count contributions.

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

58-58: Appropriate public API exposure for DispatchContextEnforcer.

The import and export follow established patterns. The "Context enforcement" comment section clearly categorizes the new component. Based on the learnings, the enforcer correctly uses ModelEventEnvelope[object] for generic dispatch parameters.

Also applies to: 165-166

src/omnibase_infra/validation/infra_validators.py (2)

327-358: Threshold update with comprehensive documentation.

The INFRA_MAX_UNIONS update to 465 is well-documented with a clear breakdown of union count contributions. The PR references (#61, #67) and date stamps provide good traceability for future reviews.


713-713: Docstring correctly updated.

The max_unions parameter documentation now reflects the updated default value of 465.

src/omnibase_infra/models/dispatch/model_dispatch_context.py (3)

1-66: LGTM! Well-structured module header and imports.

The module docstring thoroughly documents the ONEX time injection rules, design pattern, thread safety considerations, and provides clear examples. Imports are minimal and appropriate.


69-136: LGTM! Well-defined immutable model with proper type annotations.

The model configuration with frozen=True and extra="forbid" correctly enforces immutability and strict field validation. All fields use PEP 604 union syntax as per coding guidelines.


226-420: LGTM! Factory methods provide excellent compile-time enforcement.

The factory method design is well thought out:

  • for_reducer() and for_compute() don't accept a now parameter, preventing accidental time injection at the call site
  • for_orchestrator(), for_effect(), and for_runtime_host() require now as a mandatory parameter, ensuring time is always provided

This provides compile-time/type-checker enforcement of ONEX time injection rules in addition to the runtime validation.

src/omnibase_infra/runtime/dispatch_context_enforcer.py (4)

1-66: LGTM! Well-documented module with proper imports.

The module docstring clearly explains the ONEX time injection rules and the enforcer's role. The use of TYPE_CHECKING for the ModelEventEnvelope import is a good practice for avoiding circular imports.

The signature correctly uses ModelEventEnvelope[object] as per the coding guidelines and retrieved learnings.


109-192: LGTM! Clean routing logic with appropriate error handling.

The method correctly:

  • Extracts correlation metadata with a fallback for missing correlation_id
  • Routes to the appropriate factory method based on node_kind
  • Documents the acceptable timestamp drift (microseconds in practice)
  • Raises ModelOnexError with VALIDATION_FAILED for unknown node kinds

The early-return pattern keeps the code readable and avoids deep nesting.


194-291: LGTM! Explicit validation methods provide defense-in-depth.

The three validation methods provide clear checkpoints:

  • validate_no_time_injection_for_reducer() - specific to reducers
  • validate_no_time_injection_for_compute() - specific to compute nodes
  • validate_no_time_injection_for_deterministic_node() - covers both

While there's some overlap, having explicit methods for each use case improves API clarity and allows callers to be explicit about their intent.


293-358: Helper predicates are well-designed with complete coverage. The methods use set membership for O(1) lookup and the docstrings clearly document time injection requirements for each node kind. Tests explicitly verify the symmetry invariant: forbids_time_injection() and requires_time_injection() are inverses, and every EnumNodeKind value is consistently classified.

tests/unit/runtime/test_dispatch_context_enforcer.py (6)

36-73: LGTM! Well-implemented mock dispatcher.

The MockMessageDispatcher correctly implements the ProtocolMessageDispatcher protocol with all required properties and the async handle method. The mock returns a valid ModelDispatchResult for testing.


75-85: LGTM! Minimal mock envelope sufficient for testing.

The MockEnvelope provides just the attributes needed by DispatchContextEnforcer.create_context_for_dispatcher(), which accesses envelope.correlation_id and envelope.trace_id. This follows the duck typing approach per coding guidelines.


87-161: LGTM! Well-organized pytest fixtures.

The fixtures provide clean setup for all test scenarios, covering each node kind and envelope configurations. The docstrings clearly describe each fixture's purpose.


467-543: LGTM! Comprehensive helper method tests.

The tests cover all standard node kinds for both requires_time_injection() and forbids_time_injection(), verifying the expected return values for each.


579-694: LGTM! Excellent acceptance criteria tests with architectural documentation.

These tests serve as executable documentation of the ONEX architectural constraints. The extensive docstrings explaining why these rules exist (event sourcing replay determinism) are valuable for future maintainers.

While some tests overlap with earlier test classes, the detailed documentation justifies their inclusion as acceptance criteria verification.


1019-1072: LGTM! Architectural rationale tests provide excellent documentation.

The TestOMN973ArchitecturalRationale class provides executable documentation:

  • test_reducer_determinism_for_event_replay demonstrates the replay scenario
  • test_time_injection_rules_are_symmetric verifies all node kinds are consistently classified

These tests help future developers understand the architectural decisions.

tests/integration/runtime/test_dispatch_context_integration.py (8)

47-59: LGTM! Test payload models are well-defined.

Simple Pydantic models for test payloads. Clean and minimal.


66-86: LGTM! Clean mock envelope factory function.

The create_mock_envelope function creates properly configured MagicMock instances that satisfy the interface expected by DispatchContextEnforcer.


159-200: LGTM! Deterministic dispatcher for replay testing.

The DeterministicResultDispatcher correctly extends ContextCapturingDispatcher to track processed events, enabling replay determinism verification. The # type: ignore[arg-type] comment on line 174 is acceptable for test utilities where kwargs typing is complex.


203-272: LGTM! Well-organized fixtures with deterministic helpers.

The use of DeterministicClock and DeterministicIdGenerator ensures reproducible tests. Each fixture has a clear purpose and docstring.


337-466: LGTM! Thorough context creation tests.

The tests comprehensively verify:

  • Time injection presence/absence for each node kind
  • Correlation and trace ID propagation
  • Fallback correlation ID generation when missing

The time bounds checking (before/after comparisons) is a good pattern for verifying time injection.


473-642: LGTM! Excellent integration tests for dispatch flow.

These tests simulate realistic dispatch scenarios:

  • Single dispatcher dispatch with context verification
  • Mixed dispatcher fan-out verifying each gets appropriate context
  • Verification of both captured context and correlation ID propagation

The async test pattern with explicit context passing demonstrates the intended usage.


649-749: LGTM! Replay determinism tests demonstrate architectural intent.

These tests validate the core ONEX architectural guarantee: reducers produce identical results on replay because they don't receive time injection. The tests effectively document why time injection is forbidden for reducers.


921-948: Clever workaround for testing validation with invalid state.

The test test_validate_no_time_injection_for_reducer_raises_if_has_time uses model_copy(update=...) to bypass Pydantic validation and create an invalid state. This is a valid approach for testing the enforcer's validation method independently of the model's validator.

The comment on lines 942-943 correctly explains that model_copy doesn't re-run validators, which is exactly what enables this test.

Comment on lines +134 to +150
async def handle(
self,
envelope: object,
context: ModelDispatchContext | None = None,
) -> ModelDispatchResult:
"""Handle the message and capture the context for assertions."""
self.captured_envelope = envelope
self.captured_context = context
self.invocation_count += 1

return ModelDispatchResult(
dispatch_id=uuid4(),
status=EnumDispatchStatus.SUCCESS,
topic="test.events.v1",
dispatcher_id=self._dispatcher_id,
message_type=type(envelope).__name__ if envelope else None,
)

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "dispatcher" src --type f -name "*.py" | head -20

Repository: OmniNode-ai/omnibase_infra

Length of output: 300


🏁 Script executed:

fd dispatcher src --type f -name "*.py" | head -20

Repository: OmniNode-ai/omnibase_infra

Length of output: 300


🏁 Script executed:

find src -name "*dispatcher*.py" -type f

Repository: OmniNode-ai/omnibase_infra

Length of output: 250


🏁 Script executed:

rg -A 5 "class ProtocolMessageDispatcher\|async def handle" src/omnibase_infra/runtime/dispatcher_registry.py

Repository: OmniNode-ai/omnibase_infra

Length of output: 52


🏁 Script executed:

rg "ProtocolMessageDispatcher" src --type py

Repository: OmniNode-ai/omnibase_infra

Length of output: 4102


🏁 Script executed:

cat src/omnibase_infra/runtime/dispatcher_registry.py | grep -A 30 "class ProtocolMessageDispatcher"

Repository: OmniNode-ai/omnibase_infra

Length of output: 1539


🏁 Script executed:

cat src/omnibase_infra/runtime/dispatcher_registry.py | grep -A 60 "class ProtocolMessageDispatcher" | tail -40

Repository: OmniNode-ai/omnibase_infra

Length of output: 2027


🏁 Script executed:

cat src/omnibase_infra/runtime/dispatcher_registry.py | grep -A 100 "class ProtocolMessageDispatcher" | grep -A 10 "def handle"

Repository: OmniNode-ai/omnibase_infra

Length of output: 564


🏁 Script executed:

rg -B 5 -A 10 "async def handle" src/omnibase_infra/runtime/dispatcher_registry.py | head -50

Repository: OmniNode-ai/omnibase_infra

Length of output: 1890


🏁 Script executed:

cat -n tests/integration/runtime/test_dispatch_context_integration.py | sed -n '134,150p'

Repository: OmniNode-ai/omnibase_infra

Length of output: 805


The handle method signature does not match ProtocolMessageDispatcher.

The protocol requires handle(self, envelope: ModelEventEnvelope[object]) -> ModelDispatchResult, but the test implementation uses envelope: object and adds an extra context parameter. The envelope parameter must be ModelEventEnvelope[object] per coding guidelines for dispatcher parameters. This signature mismatch prevents the test double from being protocol-compliant and won't accurately verify the dispatch engine's behavior.

🤖 Prompt for AI Agents
In tests/integration/runtime/test_dispatch_context_integration.py around lines
134 to 150, the test handler's signature is not protocol-compliant: change the
method to match ProtocolMessageDispatcher by defining handle(self, envelope:
ModelEventEnvelope[object]) -> ModelDispatchResult (remove the extra context
param), update captured_envelope to store the received ModelEventEnvelope, and
if you need the context for assertions extract and store envelope.context into
captured_context; ensure type annotations/imports for ModelEventEnvelope are
present.

Comment on lines +425 to +447
def test_reducer_context_with_time_raises(
self,
enforcer: DispatchContextEnforcer,
) -> None:
"""Reducer context with time injection should raise."""
# Create invalid context by bypassing factory (simulates manual construction)
# This is a theoretical case - the model validator should catch this
# but we test the enforcer's explicit validation
ctx = ModelDispatchContext(
correlation_id=uuid4(),
node_kind=EnumNodeKind.ORCHESTRATOR, # Use different node kind to create
now=datetime.now(UTC),
)
# Manually modify to simulate a reducer with time (bypassing Pydantic validation)
# Since the model is frozen, we create a new context directly
# This tests what happens if someone constructs an invalid context
invalid_ctx = ModelDispatchContext(
correlation_id=uuid4(),
node_kind=EnumNodeKind.REDUCER,
now=None, # Valid
)
# The valid context should pass
enforcer.validate_no_time_injection_for_reducer(invalid_ctx)

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

Test name and implementation mismatch.

The test test_reducer_context_with_time_raises suggests it should verify that a reducer context with time injection raises an error. However, the test creates a valid reducer context with now=None and asserts it passes validation. The test name and docstring indicate testing for rejection, but the implementation tests for acceptance.

🔎 Consider renaming or restructuring this test

The test's intent seems to be demonstrating that invalid reducer contexts can't be created due to the Pydantic validator. The actual assertion (line 447) validates a valid context. Consider:

  1. Rename to test_valid_reducer_context_passes_validation
  2. Or add a comment clarifying that the Pydantic validator prevents invalid state construction
-    def test_reducer_context_with_time_raises(
+    def test_reducer_context_pydantic_prevents_invalid_state(
         self,
         enforcer: DispatchContextEnforcer,
     ) -> None:
-        """Reducer context with time injection should raise."""
+        """Pydantic validator prevents reducer context with time; valid context passes."""
         # Create invalid context by bypassing factory (simulates manual construction)
-        # This is a theoretical case - the model validator should catch this
-        # but we test the enforcer's explicit validation
+        # Note: The Pydantic model validator catches this at construction time,
+        # so we can only test that valid contexts pass the enforcer's validation.
🤖 Prompt for AI Agents
In tests/unit/runtime/test_dispatch_context_enforcer.py around lines 425-447,
the test named test_reducer_context_with_time_raises is inconsistent: it
documents expecting a reducer-with-time to raise but constructs a valid reducer
(now=None) and asserts it passes; either rename the test to
test_valid_reducer_context_passes_validation and update docstring to reflect
that it verifies a valid reducer context is accepted, or modify the test to
actually construct an invalid reducer context (e.g., create a reducer
ModelDispatchContext that includes a non-None now via bypassing validation) and
assert enforcer.validate_no_time_injection_for_reducer raises the expected
exception; update the test name and docstring accordingly to match the chosen
behavior.

Merge main branch which enabled strict validation mode (OMN-983).

Resolution:
- Keep INFRA_MAX_UNIONS = 465 (current baseline after OMN-937/OMN-973)
- Adopt INFRA_PATTERNS_STRICT = True (strict mode from main)
- Adopt INFRA_UNIONS_STRICT = True (strict mode from main)
- Update test expectations to match

Validation results:
- Union count: 415 (well under 465 threshold)
- All 5 validators: PASS

@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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 68c4164 and 41dbc42.

📒 Files selected for processing (2)
  • src/omnibase_infra/validation/infra_validators.py (2 hunks)
  • tests/unit/validation/test_validator_defaults.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/validation/test_validator_defaults.py
🧰 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:

  • src/omnibase_infra/validation/infra_validators.py
**/*infra*.py

📄 CodeRabbit inference engine (CLAUDE.md)

All infrastructure errors must use raise OnexError(...) from e pattern. Never raise or propagate errors without proper error context wrapping

Files:

  • src/omnibase_infra/validation/infra_validators.py
🔇 Additional comments (1)
src/omnibase_infra/validation/infra_validators.py (1)

636-636: LGTM!

Docstring correctly updated to reflect the new INFRA_MAX_UNIONS default value.

Comment on lines +330 to +337
# Current baseline (462 unions as of 2025-12-20):
# - Most unions are legitimate `X | None` nullable patterns
# - These are NOT flagged as violations, just counted
# - Actual violations (primitive soup, Union[X,None] syntax) are reported separately
#
# Threshold set to 410 - buffer above current baseline (402 after json_types.py addition).
# Threshold set to 465 - buffer above current baseline after OMN-937 and OMN-973 merges.
# Target: Reduce to <200 through dict[str, object] → JsonValue migration.
INFRA_MAX_UNIONS = 410
INFRA_MAX_UNIONS = 465

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify union count in infrastructure code
cd /dev/shm  # Use a temp directory in case we're not in the repo

# Count union type usage in infrastructure code
echo "Counting union operators (|) in src/omnibase_infra/..."
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  gawk -F: '{sum += $2} END {print "Total union operators (|) found:", sum}'

# Show files with highest union usage for context
echo -e "\nTop 10 files by union count:"
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  sort -t: -k2 -rn | \
  head -10

Repository: OmniNode-ai/omnibase_infra

Length of output: 835


🏁 Script executed:

#!/bin/bash

# First check if the file exists
if [ -f "src/omnibase_infra/validation/infra_validators.py" ]; then
  echo "File found. Checking lines 330-337..."
  sed -n '330,337p' src/omnibase_infra/validation/infra_validators.py
  echo ""
  echo "Checking line 636..."
  sed -n '636p' src/omnibase_infra/validation/infra_validators.py
else
  echo "File not found at expected path"
  find . -name "infra_validators.py" -type f 2>/dev/null | head -5
fi

echo ""
echo "=== Counting unions in src/omnibase_infra/ ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  gawk -F: '{sum += $2} END {print "Total union operators (|) found:", sum}'

echo ""
echo "=== Top 10 files by union count ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  sort -t: -k2 -rn | \
  head -10

Repository: OmniNode-ai/omnibase_infra

Length of output: 1343


🏁 Script executed:

#!/bin/bash

# Check if the file exists and show the relevant lines
echo "=== Checking file content ==="
if [ -f "src/omnibase_infra/validation/infra_validators.py" ]; then
  echo "Lines 330-337:"
  sed -n '330,337p' src/omnibase_infra/validation/infra_validators.py
  echo ""
  echo "Line 636:"
  sed -n '636p' src/omnibase_infra/validation/infra_validators.py
else
  echo "File not found"
fi

echo ""
echo "=== Counting union operators in src/omnibase_infra/ ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | gawk -F: '{sum += $2} END {print "Total:", sum}'

echo ""
echo "=== Top files by union count ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | sort -t: -k2 -rn | head -10

Repository: OmniNode-ai/omnibase_infra

Length of output: 1319


🏁 Script executed:

#!/bin/bash

# Verify the union count baseline and review comment details
echo "=== Verifying INFRA_MAX_UNIONS constant and union count ==="
echo ""

# Check the actual file content
if [ -f "src/omnibase_infra/validation/infra_validators.py" ]; then
  echo "File content at lines 330-337:"
  sed -n '330,337p' src/omnibase_infra/validation/infra_validators.py
  echo ""
  echo "File content at line 636:"
  sed -n '636p' src/omnibase_infra/validation/infra_validators.py
else
  echo "File not found - searching for it..."
  find . -name "infra_validators.py" 2>/dev/null
fi

echo ""
echo "=== Counting union operators in infrastructure code ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  gawk -F: '{sum += $2} END {print "Total union operators found: " sum}'

echo ""
echo "=== Top 10 files by union count ==="
rg -t py --count-matches '\|' src/omnibase_infra/ 2>/dev/null | \
  sort -t: -k2 -rn | head -10

Repository: OmniNode-ai/omnibase_infra

Length of output: 1407


Fix the outdated baseline and address the exceeded union threshold.

The documented baseline of 462 unions is incorrect—the actual count is 657 unions, exceeding the threshold of 465 by 192 unions. The constant and documentation (lines 330-337 and 636) must be updated to reflect the current state. Additionally, either enforce the 465 limit through validation or increase the threshold explicitly if the higher count is intentional. The current disconnect between documented and actual values creates confusion about code quality expectations.

🤖 Prompt for AI Agents
In src/omnibase_infra/validation/infra_validators.py around lines 330-337 and
also update the related comment at line ~636, the annotated baseline (462
unions) and INFRA_MAX_UNIONS = 465 are stale—the actual union count is 657;
update the comment text to reflect the current measured count and change
INFRA_MAX_UNIONS to the intended threshold (either set to 657 if you want the
constant to match current state or increase it to a documented new limit), and
then add/enable enforcement: ensure the validator compares the measured union
count to INFRA_MAX_UNIONS and fails CI (raise an exception or return a non-zero
error) when exceeded, or if the higher count is intentional, add a short
rationale comment and update both places to the new threshold to avoid
confusion.

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Code Review Summary

This PR implements runtime enforcement of time injection rules for ONEX's dispatch context model. The implementation is well-designed, thoroughly tested, and architecturally sound. Below is my detailed review.


✅ Strengths

1. Excellent Separation of Concerns

  • ModelDispatchContext: Immutable data model with factory methods and Pydantic validation
  • DispatchContextEnforcer: Stateless enforcer that creates contexts based on dispatcher node kind
  • Clean separation between data model validation (Pydantic) and runtime enforcement (Enforcer)

2. Strong Type Safety & ONEX Compliance

  • ✅ No Any types - Uses ModelEventEnvelope[object] instead (correct per ONEX guidelines)
  • ✅ PEP 604 unions - Uses X | None instead of Optional[X] (matches ONEX style)
  • ✅ Frozen models - frozen=True ensures immutability and thread safety
  • ✅ Factory pattern - Enforces correct construction through for_reducer(), for_orchestrator(), etc.

3. Comprehensive Test Coverage

  • 75 tests total (43 unit + 32 integration) covering all node kinds, factory methods, validation rules, and correlation ID propagation

4. Excellent Documentation

  • Clear module-level docstrings explaining design patterns and rationale
  • Thread safety considerations documented
  • Design decisions explained (e.g., timestamp drift comment)

5. Defense in Depth

  • Multiple validation layers: Pydantic @model_validator, validate_for_node_kind() method, and enforcer validation methods
  • Prevents both accidental and intentional violations

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🔍 Areas for Improvement

1. Missing MessageDispatchEngine Integration ⚠️

Issue: The DispatchContextEnforcer is exported but not yet integrated into MessageDispatchEngine. No usage found in message_dispatch_engine.py.

Recommendation: Either integrate this into MessageDispatchEngine in this PR, or add a clear TODO/ticket reference noting that integration is deferred to a follow-up ticket.


2. Literal[True] Return Type (model_dispatch_context.py:191)

The validate_for_node_kind() -> Literal[True] pattern is unusual. Standard validation methods typically return None. Consider:

  • -> None (standard pattern)
  • Keep Literal[True] but add rationale comment

3. JsonValue Type Migration

The PR includes cleanup migrating JsonValue definitions to centralized location, but in example plugins unrelated to time injection. Consider moving to separate PR for cleaner git history.


4. Validation Baseline Changes (infra_validators.py)

Threshold changes lack explanation. Add note in PR description about why validation baselines changed.


5. Timestamp Capture Consistency (dispatch_context_enforcer.py:165-170)

Consider capturing now ONCE at the start of create_context_for_dispatcher() and reusing for all node kinds. This ensures consistent timestamps across a single dispatch operation.


6. Missing all Export Validation

Add test validating new symbols are properly exported:

def test_dispatch_context_exports():
    from omnibase_infra.models.dispatch import ModelDispatchContext
    from omnibase_infra.runtime import DispatchContextEnforcer
    assert ModelDispatchContext is not None

@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

🛡️ Security & Performance

Security ✅

  • Correlation ID Generation: Uses uuid4() (cryptographically random, prevents collision attacks)
  • No Secrets in Context: metadata is dict[str, str], reducing secret leakage risk
  • Immutable Context: frozen=True prevents tampering after creation

Performance ✅

  • Factory Methods: Minimal overhead (just delegation)
  • Pydantic Validation: Cheap validation (simple field checks), critical for correctness
  • Thread Safety: Frozen model + stateless enforcer = excellent concurrency

🎯 Acceptance Criteria

  • ✅ Runtime dispatch enforces context type (verified in enforcer)
  • ✅ Tests proving reducers cannot access now (43 unit tests)
  • [⚠️] Integration tests exist but don't test actual MessageDispatchEngine integration

🏗️ Architectural Alignment

ONEX Compliance ✅

  • No Any types ✓
  • Strong typing with Pydantic ✓
  • Factory pattern ✓
  • Immutable data models ✓

CLAUDE.md Adherence ✅

  • File naming: model_dispatch_context.py → ModelDispatchContext ✓
  • Type annotations: Uses X | None over Optional[X] ✓
  • Error handling: Uses ModelOnexError with proper codes ✓

✅ Final Verdict

Recommendation: APPROVE with minor suggestions

This is high-quality code that:

  • ✅ Implements time injection enforcement correctly
  • ✅ Has excellent test coverage (75 tests)
  • ✅ Follows ONEX architectural principles
  • ✅ Is well-documented and maintainable
  • ✅ Has no security or performance concerns

The identified issues are minor and don't block merging. Integration with MessageDispatchEngine can be in a follow-up PR.

Great work! The factory pattern with Pydantic validation provides strong guarantees for reducer determinism.


Reviewed by: Claude Code (automated review)
ONEX Compliance: ✅ Verified

@jonahgabriel
jonahgabriel merged commit 4df5770 into main Dec 20, 2025
13 checks passed
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