Skip to content

test(effect): add comprehensive effect idempotency and retry behavior tests [OMN-954] - #78

Merged
jonahgabriel merged 4 commits into
mainfrom
jonah/omn-954-g4-test-effect-idempotency-and-retry-behavior
Dec 22, 2025
Merged

jonahgabriel merged 4 commits into
mainfrom
jonah/omn-954-g4-test-effect-idempotency-and-retry-behavior

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 22, 2025 •

Copy link
Copy Markdown
Collaborator

Summary

Implements G4 acceptance criteria tests for effect idempotency, circuit breaker, backoff policy, retry exhaustion, and partial failure scenarios.

  • ✅ Duplicate intent safe
  • ✅ Circuit breaker verified
  • ✅ Backoff policy validated
  • ✅ Retry exhaustion tested
  • ✅ Partial failure scenarios tested

Test Coverage (51 tests)

Test File Coverage
test_effect_idempotency.py Duplicate intent safety, natural key deduplication, domain isolation
test_effect_circuit_breaker.py State transitions (CLOSED→OPEN→HALF_OPEN→CLOSED), per-backend isolation
test_effect_retry_backoff.py Exponential backoff timing, retry exhaustion, non-retryable errors
test_effect_partial_failure.py Consul/Postgres partial failure combinations, error aggregation

New Components

Models (src/omnibase_infra/nodes/effects/models/):

  • ModelBackendResult - Individual backend operation outcome
  • ModelRegistryRequest - Dual-backend registration request
  • ModelRegistryResponse - Response with partial failure support (status: success | partial | failed)

Effect Node (src/omnibase_infra/nodes/effects/):

  • RegistryEffect - Effect node for dual-backend registration execution
  • ProtocolConsulClient - Protocol for Consul service registration
  • ProtocolPostgresAdapter - Protocol for PostgreSQL registration persistence

Test plan

  • All 51 new tests pass locally
  • Pre-commit hooks pass (architecture, patterns, linting)
  • Type checking passes (mypy)

Linear

Closes OMN-954

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Registry Effect Node for dual-backend node registration against Consul and PostgreSQL with partial failure handling and targeted retries.
    • Added idempotency tracking with configurable in-memory cache featuring LRU eviction and TTL-based expiration.
  • Documentation

    • Added comprehensive Registry Effect Node documentation including architecture, performance characteristics, configuration options, and usage examples.
  • Tests

    • Added integration tests validating end-to-end registration flows, partial failures, and idempotency behavior.
    • Added unit tests for effect operations, circuit breaker integration, retry logic, and idempotency store behavior.
    • Added performance tests for high-volume registration, concurrent load, cache stress, and latency distribution.

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

… tests [OMN-954]

Implement G4 acceptance criteria tests for effect idempotency, circuit breaker,
backoff policy, retry exhaustion, and partial failure scenarios.

Test Coverage (51 tests):
- test_effect_idempotency.py: Duplicate intent safety, natural key deduplication
- test_effect_circuit_breaker.py: State transitions, per-backend isolation
- test_effect_retry_backoff.py: Exponential backoff, retry exhaustion
- test_effect_partial_failure.py: Consul/Postgres partial failure handling

New Models:
- ModelBackendResult: Individual backend operation outcome
- ModelRegistryRequest: Dual-backend registration request
- ModelRegistryResponse: Response with partial failure support

New Effect:
- RegistryEffect: Effect node for dual-backend registration execution
- ProtocolConsulClient: Protocol for Consul service registration
- ProtocolPostgresAdapter: Protocol for PostgreSQL registration persistence
@linear

linear Bot commented Dec 22, 2025

Copy link
Copy Markdown

OMN-954

@coderabbitai

coderabbitai Bot commented Dec 22, 2025 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 74152c0 and ba08620.

📒 Files selected for processing (17)
  • src/omnibase_infra/nodes/effects/contract.yaml
  • src/omnibase_infra/nodes/effects/models/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_registry_request.py
  • src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py
  • src/omnibase_infra/nodes/effects/registry_effect.py
  • src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py
  • tests/integration/registration/effect/conftest.py
  • tests/integration/registration/effect/test_doubles.py
  • tests/integration/registration/effect/test_registry_effect_integration.py
  • tests/performance/registration/effect/conftest.py
  • tests/performance/registration/effect/test_idempotency_store_performance.py
  • tests/unit/nodes/effects/__init__.py
  • tests/unit/nodes/effects/test_sanitize_backend_error.py
  • tests/unit/registration/effect/conftest.py
  • tests/unit/registration/effect/test_effect_circuit_breaker.py
  • tests/unit/registration/effect/test_effect_idempotency.py
  • tests/unit/registration/effect/test_effect_partial_failure.py

Walkthrough

Introduces the NodeRegistryEffect, an async effect node coordinating dual-backend (Consul and PostgreSQL) node registration. Features idempotency tracking via pluggable stores, partial failure handling, and targeted retries. Includes comprehensive data models, protocol interfaces, core implementation, and extensive test coverage.

Changes

Cohort / File(s) Summary
Core Data Models
src/omnibase_infra/nodes/effects/models/model_backend_result.py
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py
src/omnibase_infra/nodes/effects/models/model_registry_request.py
src/omnibase_infra/nodes/effects/models/model_registry_response.py
New Pydantic models for effect I/O: ModelBackendResult (operation outcomes), ModelEffectIdempotencyConfig (cache/TTL settings), ModelRegistryRequest (registration input), ModelRegistryResponse (with status, per-backend results, and helper methods).
Protocol Interfaces
src/omnibase_infra/nodes/effects/protocol_consul_client.py
src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py
New protocol abstractions for Consul client (register_service), PostgreSQL adapter (upsert), and idempotency store (mark_completed, is_completed, get_completed_backends, clear operations).
Core Implementation
src/omnibase_infra/nodes/effects/registry_effect.py
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py
NodeRegistryEffect async node coordinating dual-backend registration with idempotency checks and error handling; InMemoryEffectIdempotencyStore with LRU eviction, TTL expiration, and async-safe operations.
Public API & Configuration
src/omnibase_infra/nodes/effects/__init__.py
src/omnibase_infra/nodes/effects/models/__init__.py
src/omnibase_infra/nodes/__init__.py
src/omnibase_infra/nodes/effects/contract.yaml
Package initializers exporting models and node; contract YAML defining NodeRegistryEffect version and type (EFFECT_GENERIC).
Documentation
src/omnibase_infra/nodes/effects/README.md
Comprehensive documentation of Registry Effect Node: architecture, memory characteristics, performance, configuration, usage examples, thread-safety, and persistent backend recommendations.
Integration Test Infrastructure
tests/integration/registration/__init__.py
tests/integration/registration/effect/__init__.py
tests/integration/registration/effect/conftest.py
tests/integration/registration/effect/test_doubles.py
Integration test package with conftest providing fresh fixtures (consul_client, postgres_adapter, idempotency_store, registry_effect, sample_request, request_factory); test doubles (StubConsulClient, StubPostgresAdapter) with configurable failure modes and call tracking.
Integration Tests
tests/integration/registration/effect/test_registry_effect_integration.py
Comprehensive end-to-end test suite: full success, partial failures (Consul/PostgreSQL), complete failure, exception handling, retry success after partial failure, idempotency verification, async behavior, and concurrent registration validation.
Unit Test Infrastructure
tests/unit/registration/__init__.py
tests/unit/registration/effect/__init__.py
tests/unit/registration/effect/conftest.py
Unit test package with fixtures for idempotency store, mocked Consul/PostgreSQL backends, sample models, and factory helpers.
Unit Tests: Behavior & Correctness
tests/unit/registration/effect/test_effect_circuit_breaker.py
tests/unit/registration/effect/test_effect_idempotency.py
tests/unit/registration/effect/test_effect_idempotency_store.py
tests/unit/registration/effect/test_effect_partial_failure.py
tests/unit/registration/effect/test_effect_retry_backoff.py
Circuit breaker lifecycle, idempotency logic with natural keys, cache operations (LRU/TTL), partial failure scenarios and error aggregation, and retry backoff with exponential delays.
Performance Tests
tests/performance/__init__.py
tests/performance/registration/__init__.py
tests/performance/registration/effect/__init__.py
tests/performance/registration/effect/conftest.py
Performance test package infrastructure with fixtures for default/small/large cache stores, mock clients, and sample model factories.
Performance Tests: Benchmarks
tests/performance/registration/effect/test_effect_performance.py
tests/performance/registration/effect/test_idempotency_store_performance.py
High-volume sequential, concurrent load (100–500 ops), LRU eviction stress, memory bounds, latency distribution (p50/p95/p99), and throughput tests; idempotency store LRU/TTL/concurrency and mixed workload benchmarks with performance metrics.

Sequence Diagram

sequenceDiagram
    participant Client
    participant NodeRegistryEffect
    participant IdempotencyStore
    participant ConsulClient
    participant PostgresAdapter
    participant Response

    Client->>NodeRegistryEffect: register_node(ModelRegistryRequest)
    
    Note over NodeRegistryEffect: Extract correlation_id
    
    NodeRegistryEffect->>IdempotencyStore: is_completed(correlation_id, "consul")
    alt Consul already completed
        IdempotencyStore-->>NodeRegistryEffect: true
        Note over NodeRegistryEffect: Skip Consul
    else Consul not completed
        IdempotencyStore-->>NodeRegistryEffect: false
        NodeRegistryEffect->>ConsulClient: register_service(...)
        Note over ConsulClient: Execute registration
        ConsulClient-->>NodeRegistryEffect: ModelBackendResult (success/fail)
        alt Consul succeeded
            NodeRegistryEffect->>IdempotencyStore: mark_completed(correlation_id, "consul")
        end
    end
    
    NodeRegistryEffect->>IdempotencyStore: is_completed(correlation_id, "postgres")
    alt PostgreSQL already completed
        IdempotencyStore-->>NodeRegistryEffect: true
        Note over NodeRegistryEffect: Skip PostgreSQL
    else PostgreSQL not completed
        IdempotencyStore-->>NodeRegistryEffect: false
        NodeRegistryEffect->>PostgresAdapter: upsert(...)
        Note over PostgresAdapter: Execute registration
        PostgresAdapter-->>NodeRegistryEffect: ModelBackendResult (success/fail)
        alt PostgreSQL succeeded
            NodeRegistryEffect->>IdempotencyStore: mark_completed(correlation_id, "postgres")
        end
    end
    
    Note over NodeRegistryEffect: Determine overall status<br/>(success / partial / failed)
    
    NodeRegistryEffect->>Response: ModelRegistryResponse<br/>(consul_result, postgres_result,<br/>status, error_summary)
    Response-->>Client: Return response

Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

  • NodeRegistryEffect logic (registry_effect.py): Async dual-backend coordination with exception handling, idempotency checks, and status determination requires careful verification of control flow and error scenarios.
  • InMemoryEffectIdempotencyStore (store_effect_idempotency_inmemory.py): LRU eviction logic, TTL-based expiration with time handling, and concurrent async access with locking patterns need thorough scrutiny.
  • ModelRegistryResponse helpers (model_registry_response.py): Status derivation logic from backend results and aggregation of errors must be validated.
  • Test coverage breadth: Extensive test files across integration, unit, and performance suites with complex mock orchestration and concurrent scenarios; particular attention to retry logic tests, partial failure edge cases, and idempotency verification.
  • Protocol consistency: Verify that all protocol implementations (stubs and mocks in tests) correctly match their abstract contracts.

Poem

🐰 A node hops 'tween two distant lands,
Consul and SQL, holding steady hands—
When one path stumbles, retry takes its stand,
Idempotent and certain, perfectly planned! 🎯


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

@claude

claude Bot commented Dec 22, 2025

Copy link
Copy Markdown

PR Review: Effect Idempotency and Retry Behavior Tests

Overall Assessment

This PR implements comprehensive test coverage for G4 acceptance criteria (effect idempotency, circuit breaker, backoff, retry exhaustion, and partial failure scenarios). The implementation is well-structured with strong adherence to ONEX patterns, but there are several critical architectural issues that must be addressed.


🚨 Critical Issues

1. ONEX Naming Convention Violation - Node Class

Issue: RegistryEffect violates ONEX node naming convention

Location: src/omnibase_infra/nodes/effects/registry_effect.py:64

CLAUDE.md Rule:

Node | node.py | Node<Name><Type> | NodePostgresAdapterEffect

Current:

class RegistryEffect:  # WRONG

Required:

class NodeRegistryEffect:  # CORRECT

Impact: This breaks ONEX architectural consistency. All node classes MUST follow the Node<Name><Type> pattern to enable:

  • Automated discovery and introspection
  • Type-safe node resolution
  • Contract validation
  • Registry patterns

Fix: Rename RegistryEffect → NodeRegistryEffect in:

  • registry_effect.py (class definition)
  • __init__.py exports
  • All test imports
  • Documentation references

2. Missing ONEX Base Class Inheritance

Issue: RegistryEffect does not extend NodeEffect from omnibase_core

CLAUDE.md Rule:

Architecture Rule: omnibase_infra extends base archetypes from omnibase_core. Never define new node archetypes in infra - they belong in core.

Current:

class RegistryEffect:  # Missing base class
    def __init__(self, consul_client: ProtocolConsulClient, ...):
        ...

Required:

from omnibase_core.nodes import NodeEffect, ModelEffectInput, ModelEffectOutput

class NodeRegistryEffect(NodeEffect):
    def __init__(self, container: ModelONEXContainer):
        super().__init__(container)
        # Resolve dependencies via container

Impact: Without base class inheritance:

  • Node does not follow ONEX Effect archetype contract
  • Missing standardized lifecycle methods (execute, health checks)
  • Cannot be registered in ONEX runtime
  • Breaks polymorphic node handling

3. Container-Based Dependency Injection Violation

Issue: Constructor uses direct dependency injection instead of ModelONEXContainer

CLAUDE.md Rule:

Container Injection - def __init__(self, container: ModelONEXContainer)

Current:

def __init__(
    self,
    consul_client: ProtocolConsulClient,
    postgres_adapter: ProtocolPostgresAdapter,
) -> None:

Required:

def __init__(self, container: ModelONEXContainer) -> None:
    super().__init__(container)
    self._consul_client = container.resolve(ProtocolConsulClient)
    self._postgres_adapter = container.resolve(ProtocolPostgresAdapter)

Impact:

  • Violates ONEX infrastructure DI pattern
  • Hard to test with real containers
  • Cannot use infrastructure service wiring
  • Breaks container-based configuration

Reference: docs/patterns/container_dependency_injection.md


4. Memory Leak Risk - Unbounded Cache

Issue: _completed_backends dict grows unbounded

Location: registry_effect.py:101

self._completed_backends: dict[UUID, set[str]] = {}  # Never cleared\!

Problem: Every register_node call adds an entry keyed by correlation_id. In production:

  • Long-running process → thousands of correlation IDs → memory leak
  • No TTL or eviction policy
  • No max size limit

Impact:

  • Production memory exhaustion
  • Performance degradation over time
  • Potential OOM crashes

Recommended Fix:

from cachetools import TTLCache

self._completed_backends: TTLCache = TTLCache(
    maxsize=10000,  # Limit entries
    ttl=3600,       # 1 hour TTL
)

Or use a proper idempotency store pattern with external storage (Redis, Postgres).


5. Idempotency Design Flaw

Issue: In-memory idempotency state does not survive restarts

Location: registry_effect.py:100-101

Problem: _completed_backends is instance state, not persisted:

  1. Node restarts → state lost
  2. Retry after restart → duplicate backend operations
  3. Violates idempotency guarantees

CLAUDE.md Principle:

Idempotency persists across effect restarts

Solution: Use InMemoryIdempotencyStore or PostgresIdempotencyStore:

from omnibase_infra.idempotency import ProtocolIdempotencyStore

def __init__(self, container: ModelONEXContainer):
    super().__init__(container)
    self._idempotency_store = container.resolve(ProtocolIdempotencyStore)

async def register_node(self, request: ModelRegistryRequest):
    # Check if already processed
    if await self._idempotency_store.check_and_record(request.correlation_id):
        # Return cached result or skip
        return self._get_cached_result(request.correlation_id)
    
    # Execute registration...

⚠️ High Priority Issues

6. Error Handling - str(e) May Expose Secrets

Location: registry_effect.py:223, 280

except Exception as e:
    return ModelBackendResult(
        success=False,
        error=str(e),  # ⚠️ May contain credentials\!
        ...
    )

CLAUDE.md Rule:

Error messages MUST be sanitized before inclusion. Never include:

  • Credentials, connection strings, or secrets

Problem: str(e) from connection exceptions often contains:

  • Connection strings with passwords
  • API keys in URLs
  • Internal hostnames

Fix:

except InfraConnectionError as e:
    error = e.model.message  # Already sanitized
except Exception as e:
    # Sanitize generic exceptions
    error = f"{type(e).__name__}: Connection failed"

7. Missing Correlation ID Propagation

Issue: Backend operations don't receive correlation_id

Location: registry_effect.py:191-196, 247-253

result = await self._consul_client.register_service(
    service_id=service_id,
    service_name=service_name,
    tags=request.tags,
    health_check=request.health_check_config,
    # ❌ Missing: correlation_id=request.correlation_id
)

CLAUDE.md Rule:

Always propagate: Pass correlation_id from incoming requests to error context

Impact: Breaks distributed tracing chain - cannot correlate backend operations with requests

Fix: Update protocols to accept correlation_id:

class ProtocolConsulClient(Protocol):
    async def register_service(
        self,
        service_id: str,
        service_name: str,
        tags: list[str],
        correlation_id: UUID,  # Add this
        health_check: dict[str, str] | None = None,
    ) -> dict[str, bool | str]:
        ...

8. Hardcoded Backend IDs

Location: registry_effect.py:205, 262

backend_id="consul",  # Hardcoded string
backend_id="postgres", # Hardcoded string

Recommendation: Use enum for type safety:

from enum import Enum

class EnumBackendType(str, Enum):
    CONSUL = "consul"
    POSTGRES = "postgres"

backend_id=EnumBackendType.CONSUL,

✅ Strengths

Excellent Test Coverage

  • 51 tests covering all G4 acceptance criteria
  • Circuit breaker state machine transitions thoroughly tested
  • Per-backend isolation validated
  • Correlation ID propagation verified
  • Edge cases covered (threshold=1, zero timeout, concurrent ops)

Strong Model Design

  • ModelBackendResult - Clean separation of backend results
  • ModelRegistryResponse - Excellent partial failure handling
  • Immutable models (frozen=True) - Thread-safe by design
  • Helper methods (is_partial_failure, get_failed_backends) - Great DX

Good Documentation

  • Comprehensive module docstrings
  • Clear architecture explanations
  • Test case organization by acceptance criteria
  • Inline comments explain design decisions

ONEX Pattern Compliance

  • ✅ No Any types - uses object for generic payloads
  • ✅ Strong typing throughout
  • ✅ Pydantic models with proper validation
  • ✅ Protocol-based abstractions
  • ✅ Error context with correlation IDs
  • ✅ One model per file convention

📊 Test Quality Analysis

Circuit Breaker Tests (test_effect_circuit_breaker.py)

Strengths:

  • Complete state machine coverage (CLOSED → OPEN → HALF_OPEN → CLOSED)
  • Per-backend isolation validated
  • Thread safety tested with 50 concurrent operations
  • Edge cases: threshold=1, zero timeout

Suggestions:

  • Add test for circuit state persistence across restarts
  • Test circuit behavior under high load (1000+ concurrent ops)

Idempotency Tests (test_effect_idempotency.py)

Concern: Tests mock idempotency but implementation doesn't actually use the store pattern. Tests pass but don't validate real behavior.

Partial Failure Tests (test_effect_partial_failure.py)

Excellent: Tests all combinations (Consul success + Postgres fail, etc.)


🔧 Recommendations

Immediate (Before Merge)

  1. Rename RegistryEffect → NodeRegistryEffect (CRITICAL)
  2. Extend NodeEffect base class (CRITICAL)
  3. Use ModelONEXContainer for DI (CRITICAL)
  4. Fix unbounded cache - add TTL or size limit
  5. Sanitize str(e) in error handling
  6. Propagate correlation_id to backend calls

Follow-Up (Next PR)

  1. Implement persistent idempotency store
  2. Add contract.yaml for node definition
  3. Add integration tests with real container wiring
  4. Add performance tests (high volume, concurrent requests)
  5. Document memory/performance characteristics

🎯 Verdict

APPROVAL WITH REQUIRED CHANGES ✋

This PR represents excellent test coverage and solid engineering, but has critical architectural violations that prevent merging as-is:

  1. Node naming convention (MUST fix)
  2. Missing base class inheritance (MUST fix)
  3. Container DI violation (MUST fix)
  4. Memory leak risk (SHOULD fix)
  5. Idempotency persistence issue (SHOULD fix)

Estimated effort to fix: 4-6 hours

Once the critical issues are addressed, this will be a strong addition to the ONEX infrastructure layer.


📚 References

  • CLAUDE.md: Node naming conventions (line 96)
  • CLAUDE.md: Container-based DI (lines 123-130)
  • CLAUDE.md: Error sanitization (lines 450-480)
  • docs/patterns/container_dependency_injection.md
  • docs/patterns/error_handling_patterns.md
  • omnibase_core.nodes - NodeEffect base class

…MN-954]

Security:
- Replace str(e) with sanitized error messages to prevent secret exposure

Naming Convention:
- Rename RegistryEffect → NodeRegistryEffect per ONEX standards

New Components:
- Add contract.yaml for ONEX node definition
- Add ProtocolEffectIdempotencyStore for pluggable backends
- Add StoreEffectIdempotencyInMemory with LRU/TTL cache
- Add ModelEffectIdempotencyConfig for cache configuration

Testing:
- Add 16 integration tests with container wiring
- Add 25 performance tests (high volume, concurrent)
- Add 19 unit tests for idempotency store

Documentation:
- Add README.md with memory/performance characteristics
- Update docstrings with O notation and throughput benchmarks
@claude

claude Bot commented Dec 22, 2025

Copy link
Copy Markdown

PR Review: Effect Idempotency and Retry Behavior Tests [OMN-954]

Summary

This PR implements comprehensive testing for effect-level idempotency, circuit breaker integration, retry/backoff policies, and partial failure handling. The implementation is solid and production-ready with excellent test coverage (51 tests), thorough documentation, and proper adherence to ONEX conventions.

✅ Strengths

  1. Excellent Documentation - README.md is exceptionally detailed with memory characteristics, performance benchmarks, configuration guidance, and production considerations
  2. Strong Type Safety - Zero Any types, proper use of X | None (PEP 604), all models follow ONEX naming conventions
  3. Comprehensive Test Coverage - 51 tests across unit, integration, and performance categories
  4. Security-Conscious Error Handling - Sanitized error messages, proper correlation ID tracking
  5. Memory-Bounded Design - LRU eviction strategy with configurable limits and clear documentation

🔍 Issues & Recommendations

Should Fix Before Merge

  1. Error Message Sanitization (registry_effect.py:302,362) - Using str() on backend result errors could expose sensitive information. Consider more defensive sanitization.

  2. Missing Contract Fields (contract.yaml) - Contract is missing required fields per ONEX schema: input_model, output_model, dependencies. Add these to align with full ONEX contract pattern.

  3. Idempotency Store Protocol - Custom ProtocolEffectIdempotencyStore instead of existing ProtocolIdempotencyStore. Document rationale for separate protocol.

  4. Circuit Breaker Integration - NodeRegistryEffect does NOT implement MixinAsyncCircuitBreaker. Verify design decision (node-level vs backend-level resilience) and document clearly.

Nice to Have

  1. Rename registry_effect.py to node.py per ONEX conventions (or document exception)
  2. Remove @pytest.mark.unit from performance tests (semantically incorrect)
  3. Standardize all export position across modules

📋 ONEX Guidelines Compliance

✅ No Any types used
✅ Proper Pydantic models (one per file)
✅ PEP 604 union syntax (X | None)
✅ Sanitized error messages
✅ Correlation ID tracking
✅ Protocol-based duck typing
✅ Bounded memory with LRU eviction
✅ Comprehensive test coverage

🧪 Test Quality

  • Unit Tests: Well-structured, comprehensive fixtures, proper mocks, edge cases covered
  • Integration Tests: Container wiring used properly, realistic scenarios
  • Performance Tests: Latency distribution analysis, throughput benchmarks, properly marked for CI exclusion

🚀 Performance

Documented performance is excellent:

  • Idempotency checks: O(1) amortized
  • Throughput: >5,000 ops/sec single-worker, >10,000 concurrent
  • Memory: Linear scaling (100K entries = ~10MB)

🏆 Overall Assessment

Verdict: ✅ APPROVE with minor recommendations

This PR demonstrates excellent engineering with thorough documentation, comprehensive test coverage, production-ready design, and security-conscious implementation. The issues identified are minor and can be addressed in follow-up commits or accepted as-is with documentation.

Recommended Action: Merge after addressing contract.yaml fields and documenting the idempotency protocol design decision.


Related: OMN-954 | Reviewed: 2025-12-22 | By: Claude Code (ONEX Architecture Review)

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

🧹 Nitpick comments (17)
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py (1)

98-130: Consider documenting exception behavior for all protocol methods.

Only mark_completed (Line 93-94) documents potential exceptions. For consistency and clarity, consider documenting exception behavior for is_completed, get_completed_backends, and clear methods, especially since implementations may encounter store unavailability or lock acquisition failures.

Example documentation pattern
 async def is_completed(self, correlation_id: UUID, backend: str) -> bool:
     """Check if a backend is completed for a correlation ID.

     Args:
         correlation_id: Unique identifier for the operation.
         backend: Backend identifier to check.

     Returns:
         True if the backend is completed, False otherwise.
+
+    Raises:
+        RuntimeError: If the store is unavailable.
     """
     ...
src/omnibase_infra/nodes/effects/README.md (1)

1-350: Well-structured and comprehensive documentation.

This README provides excellent coverage of the NodeRegistryEffect including architecture diagrams, memory characteristics, performance benchmarks, configuration guidance, production considerations, and usage examples. The warnings about in-memory store limitations in multi-instance deployments are particularly valuable.

One minor issue: the basic usage example on line 240 uses uuid4() but the import statement on line 217 only shows from unittest.mock import AsyncMock. Consider adding from uuid import uuid4 to the import section of that example for completeness.

🔎 Suggested fix for the example import
 ```python
+from uuid import uuid4
 from unittest.mock import AsyncMock

 from omnibase_infra.nodes.effects import NodeRegistryEffect
src/omnibase_infra/nodes/effects/models/model_registry_request.py (1)

76-79: Consider using Literal type for node_type validation.

The docstring specifies valid values as effect, compute, reducer, orchestrator. Using a Literal type would provide validation at the Pydantic level and better IDE support.

🔎 Optional: Add type constraint for node_type
+from typing import Literal
+
+NodeType = Literal["effect", "compute", "reducer", "orchestrator"]
+
 class ModelRegistryRequest(BaseModel):
     # ...
-    node_type: str = Field(
+    node_type: NodeType = Field(
         ...,
         description="Type of ONEX node (effect, compute, reducer, orchestrator)",
     )

Alternatively, if extensibility is needed, keep str but add a validator for known types.

src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py (1)

26-46: Consider using TypedDict for stronger return type safety.

The return type dict[str, bool | str] is flexible but doesn't enforce the expected structure ({"success": bool, "error"?: str}). A TypedDict would provide better type checking and documentation.

🔎 Optional: Define structured return type
from typing import TypedDict, Protocol
from uuid import UUID


class UpsertResult(TypedDict, total=False):
    success: bool  # required
    error: str     # optional


class ProtocolPostgresAdapter(Protocol):
    async def upsert(
        self,
        node_id: UUID,
        node_type: str,
        node_version: str,
        endpoints: dict[str, str],
        metadata: dict[str, str],
    ) -> UpsertResult:
        ...

This makes the expected return structure explicit while maintaining backward compatibility with existing implementations.

tests/unit/registration/effect/test_effect_idempotency_store.py (1)

430-434: Consider using a more specific exception type.

Using pytest.raises(Exception) is overly broad. Pydantic frozen models raise ValidationError when attempting mutation.

🔎 Proposed fix
+    from pydantic import ValidationError
+
     def test_config_is_frozen(self) -> None:
         """Test that config is immutable."""
         config = ModelEffectIdempotencyConfig()
-        with pytest.raises(Exception):  # Pydantic raises ValidationError
+        with pytest.raises(ValidationError):
             config.max_cache_size = 5000  # type: ignore[misc]
tests/integration/registration/effect/conftest.py (1)

153-160: The __all__ export list is unnecessary in conftest.py.

Pytest automatically discovers fixtures in conftest.py files without requiring explicit exports. The __all__ definition has no effect on fixture availability and can be safely removed.

🔎 Proposed fix
     return _create_request
-
-
-__all__ = [
-    "consul_client",
-    "postgres_adapter",
-    "idempotency_store",
-    "registry_effect",
-    "sample_request",
-    "request_factory",
-]
tests/performance/registration/effect/test_idempotency_store_performance.py (1)

334-358: Workload mix test creates coroutines correctly but could be clearer.

The workload dictionary creates coroutine objects that are awaited later. This works but may be confusing since coroutines are created before store.clear_all() resets the store.

Consider restructuring to create coroutines after the store reset for each workload iteration to ensure clean state, or add a comment clarifying that the coroutines capture the store reference and execute after reset.

src/omnibase_infra/nodes/effects/registry_effect.py (1)

275-276: retries field is always 0 - retry logic not implemented at this level.

The retries field in ModelBackendResult is set to 0 in all cases. This is intentional since retry logic is expected to be handled by the caller or circuit breaker, but it may be worth documenting this explicitly or removing the unused variable.

The retries = 0 assignment on line 276/338 and its usage in ModelBackendResult could be removed if retry counting is handled elsewhere, or the docstring could clarify that retry tracking is delegated to a higher layer.

tests/performance/registration/effect/conftest.py (1)

22-22: Unused TYPE_CHECKING import.

The TYPE_CHECKING constant is imported but never used in this file. Remove it to keep imports clean.

🔎 Proposed fix
 from datetime import UTC, datetime
-from typing import TYPE_CHECKING
 from unittest.mock import AsyncMock, MagicMock
 from uuid import UUID, uuid4
tests/unit/registration/effect/test_effect_idempotency.py (2)

64-71: Fixture duplicates the one in conftest.py.

This inmemory_idempotency_store fixture is defined both here and in tests/unit/registration/effect/conftest.py. The conftest fixture will be automatically available, so this local definition is redundant and could lead to confusion.

🔎 Proposed fix

Remove lines 64-71 since the fixture is already defined in conftest.py:

-@pytest.fixture
-def inmemory_idempotency_store() -> InMemoryIdempotencyStore:
-    """Create an InMemoryIdempotencyStore for testing.
-
-    Returns:
-        A fresh InMemoryIdempotencyStore instance.
-    """
-    return InMemoryIdempotencyStore()
-
-

74-99: Mock fixtures duplicate those in conftest.py.

The mock_consul_client and mock_postgres_client fixtures here are similar to those in the conftest. Consider using the shared fixtures from conftest.py to reduce duplication. However, note that the conftest version uses synchronous MagicMock for register/deregister while this version uses AsyncMock - verify which is correct for your use case.

src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (1)

314-343: Internal async methods don't actually await.

_maybe_cleanup_expired, _cleanup_expired_entries, and _evict_lru_if_needed are declared async but don't contain any await expressions. They could be regular sync methods. However, this is a minor style point and doesn't affect correctness - keeping them async maintains consistency if future I/O operations are added.

tests/unit/registration/effect/test_effect_retry_backoff.py (1)

329-333: Access circuit breaker state via public methods instead of internal attributes.

The test directly accesses private attributes _circuit_breaker_initialized, _circuit_breaker_failures, and _circuit_breaker_open (8 occurrences). ConsulHandler exposes circuit breaker status through the public health_check() method, which returns circuit_breaker_state and circuit_breaker_failure_count. Other handlers in the codebase follow the pattern of providing a public get_circuit_state() method for tests to safely access circuit breaker state with proper lock protection. Consider either adding a public state accessor method to ConsulHandler (consistent with other handlers) or refactoring these assertions to use health_check().

tests/unit/registration/effect/test_effect_circuit_breaker.py (1)

49-49: Remove unused TypeVar.

T is declared but never used in this module.

🔎 Proposed fix
-T = TypeVar("T")
-
-
 class MockConsulBackend:
tests/performance/registration/effect/test_effect_performance.py (1)

503-509: Consider adding a public method for cache memory estimation.

Accessing store._cache directly breaks encapsulation. While acceptable in tests, a public get_cache_memory_bytes() method on InMemoryEffectIdempotencyStore would be cleaner and more maintainable.

tests/integration/registration/effect/test_registry_effect_integration.py (2)

601-602: Remove unused fixture or use it.

request_factory is suppressed as unused but the comment says it's "available for debugging." Either remove from function signature or use it to create the test requests instead of manual ModelRegistryRequest construction.

🔎 Option 1: Remove unused fixture
     async def test_different_correlation_ids_independent(
         self,
         registry_effect: NodeRegistryEffect,
         consul_client: StubConsulClient,
         postgres_adapter: StubPostgresAdapter,
-        request_factory: Callable[..., ModelRegistryRequest],
     ) -> None:
         """Test that different correlation IDs are processed independently.

         Verifies that idempotency is keyed by correlation_id, not node_id.
         """
-        # Suppress unused fixture warning - fixture is available for debugging
-        _ = request_factory
-
         # Arrange - Same node_id but different correlation_ids
         base_node_id = uuid4()

712-712: Move import to module level.

import asyncio inside the test method is unconventional. Move it to the module-level imports for consistency.

🔎 Proposed fix

At module level (after line 36):

 import pytest
+import asyncio

In the test method:

-        import asyncio
-
         effect = NodeRegistryEffect(
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 67c8edf and 74152c0.

📒 Files selected for processing (33)
  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/README.md
  • src/omnibase_infra/nodes/effects/__init__.py
  • src/omnibase_infra/nodes/effects/contract.yaml
  • src/omnibase_infra/nodes/effects/models/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_backend_result.py
  • src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py
  • src/omnibase_infra/nodes/effects/models/model_registry_request.py
  • src/omnibase_infra/nodes/effects/models/model_registry_response.py
  • src/omnibase_infra/nodes/effects/protocol_consul_client.py
  • src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py
  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
  • src/omnibase_infra/nodes/effects/registry_effect.py
  • src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py
  • tests/integration/registration/__init__.py
  • tests/integration/registration/effect/__init__.py
  • tests/integration/registration/effect/conftest.py
  • tests/integration/registration/effect/test_doubles.py
  • tests/integration/registration/effect/test_registry_effect_integration.py
  • tests/performance/__init__.py
  • tests/performance/registration/__init__.py
  • tests/performance/registration/effect/__init__.py
  • tests/performance/registration/effect/conftest.py
  • tests/performance/registration/effect/test_effect_performance.py
  • tests/performance/registration/effect/test_idempotency_store_performance.py
  • tests/unit/registration/__init__.py
  • tests/unit/registration/effect/__init__.py
  • tests/unit/registration/effect/conftest.py
  • tests/unit/registration/effect/test_effect_circuit_breaker.py
  • tests/unit/registration/effect/test_effect_idempotency.py
  • tests/unit/registration/effect/test_effect_idempotency_store.py
  • tests/unit/registration/effect/test_effect_partial_failure.py
  • tests/unit/registration/effect/test_effect_retry_backoff.py
🧰 Additional context used
📓 Path-based instructions (8)
**/contract.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

**/contract.yaml: NEVER create versioned directories like v1_0_0/, v2/, v1/, etc. Version through contracts using contract_version field in contract.yaml. Semantic versioning is metadata, not file structure.
Version through contracts using contract_version field in contract.yaml. Do not use versioned directory names. Example of CORRECT usage: meta: { contract_version: '1.0.0', node_version: '1.2.3' } in contract.yaml.

Files:

  • src/omnibase_infra/nodes/effects/contract.yaml
**/nodes/*/contract.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

Canonical node structure (NEW components): nodes// with contract.yaml, node.py, models/, and registry/ directories. Do not create versioned directory structures like nodes//v1_0_0/. Legacy v1_0_0 directories will be migrated per H1 ticket.

Files:

  • src/omnibase_infra/nodes/effects/contract.yaml
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: NEVER use Any types in Python code. Always use specific types. Use X | None (PEP 604) syntax instead of Optional[X] for nullable types.
Use EnumMessageCategory (values: EVENT, COMMAND, INTENT) for message routing, topic parsing, and dispatcher selection. Use EnumNodeOutputType (values: EVENT, COMMAND, INTENT, PROJECTION) for execution shape validation and handler return type validation. PROJECTION exists only in EnumNodeOutputType and is only valid for REDUCER nodes.
Use X | None syntax (PEP 604) for nullable types instead of Optional[X]. Example: def get_user(id: str) -> User | None: instead of def get_user(id: str) -> Optional[User]:
All services MUST use ModelONEXContainer for dependency injection. Bootstrap pattern: container = ModelONEXContainer() followed by wire_infrastructure_services(container) and service = container.service_registry.resolve_service(ServiceType).
Always propagate correlation_id from incoming requests to error context. Auto-generate using uuid4() if no correlation_id exists. Use UUID format for all new correlation IDs. Include correlation_id in all error context for distributed tracing.
NEVER include in error messages or context: passwords, API keys, tokens, secrets, full connection strings with credentials, PII (names, emails, SSNs, phone numbers), internal IP addresses (in production logs), private keys or certificates, session tokens or cookies.
SAFE to include in error messages: service names (e.g., 'postgresql', 'kafka'), operation names (e.g., 'connect', 'query'), correlation IDs (always include for tracing), error codes, sanitized hostnames, port numbers, retry counts, timeout values, resource identifiers (non-sensitive).
Use ProtocolConfigurationError for config validation failures, SecretResolutionError for secret/credential resolution, InfraConnectionError for connection failures, InfraTimeoutError for operation timeouts, InfraAuthenticationError for auth/authz failures, `InfraUnava...

Files:

  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py
  • src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py
  • tests/integration/registration/effect/__init__.py
  • tests/unit/registration/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_backend_result.py
  • src/omnibase_infra/nodes/effects/__init__.py
  • src/omnibase_infra/nodes/effects/protocol_consul_client.py
  • src/omnibase_infra/nodes/effects/models/__init__.py
  • tests/integration/registration/__init__.py
  • tests/performance/registration/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_registry_request.py
  • tests/unit/registration/effect/test_effect_idempotency.py
  • tests/integration/registration/effect/conftest.py
  • tests/performance/registration/effect/test_effect_performance.py
  • tests/performance/registration/effect/conftest.py
  • tests/performance/__init__.py
  • tests/unit/registration/effect/test_effect_partial_failure.py
  • src/omnibase_infra/nodes/effects/models/model_registry_response.py
  • tests/unit/registration/effect/conftest.py
  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
  • tests/unit/registration/effect/test_effect_retry_backoff.py
  • tests/performance/registration/effect/__init__.py
  • tests/integration/registration/effect/test_registry_effect_integration.py
  • tests/unit/registration/effect/test_effect_circuit_breaker.py
  • tests/unit/registration/effect/__init__.py
  • src/omnibase_infra/nodes/effects/registry_effect.py
  • tests/integration/registration/effect/test_doubles.py
  • tests/unit/registration/effect/test_effect_idempotency_store.py
  • tests/performance/registration/effect/test_idempotency_store_performance.py
  • src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py
**/protocol*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Protocol files should use protocol_<name>.py for standalone protocols (e.g., protocol_event_bus.py contains ProtocolEventBus). Use protocols.py for domain-grouped protocols when multiple cohesive protocols belong to a specific domain or node module.

Files:

  • src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py
  • src/omnibase_infra/nodes/effects/protocol_consul_client.py
  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
**/model_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

All data structures must be proper Pydantic models. One model per file named as model_<name>.py with class pattern Model<Name>. Files must contain exactly one Model* class.

Files:

  • src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py
  • src/omnibase_infra/nodes/effects/models/model_backend_result.py
  • src/omnibase_infra/nodes/effects/models/model_registry_request.py
  • src/omnibase_infra/nodes/effects/models/model_registry_response.py
**/*adapter*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*adapter*.py: All infrastructure adapters and services should use MixinAsyncCircuitBreaker for fault tolerance and automatic recovery. Initialize with _init_circuit_breaker(threshold=<int>, reset_timeout=<float>, service_name=<str>, transport_type=EnumInfraTransportType.<TYPE>). Check circuit breaker before operations with: async with self._circuit_breaker_lock: await self._check_circuit_breaker(...)
Use exponential backoff for transient connection failures: wait_time = 2 ** attempt (1s, 2s, 4s...). Implement circuit breaker pattern for unavailable services to prevent cascading failures. Use graceful degradation for timeout errors by falling back to secondary sources or cached data.

Files:

  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
**/registry_*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Standalone Registries must use file pattern registry_<purpose>.py with class pattern Registry<Purpose>. Examples: registry_handler.py → RegistryHandler, registry_policy.py → RegistryPolicy.

Files:

  • src/omnibase_infra/nodes/effects/registry_effect.py
**/registry*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Node-specific Registries: File registry_infra_<node_name>.py, Class RegistryInfra<NodeName>. Standalone Registries: File registry_<purpose>.py, Class Registry<Purpose>. Examples: registry_infra_postgres_adapter.py → RegistryInfraPostgresAdapter, registry_handler.py → RegistryHandler.

Files:

  • src/omnibase_infra/nodes/effects/registry_effect.py
🧠 Learnings (43)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: EFFECT Nodes must inherit from `NodeEffect` or use `NodeEffectService` for service implementation and must handle external system interactions (API calls, database operations, file system operations, message queue interactions)
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contracts/contract_*.yaml : All ONEX node contract definitions must use the new subcontract architecture pattern, breaking down complex contracts into separate contract_actions.yaml, contract_models.yaml, contract_validation.yaml, and optional contract_cli.yaml and contract_capabilities.yaml files for separation of concerns, maintainability, reusability, modularity, and future tool-as-a-service readiness

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contract.yaml : All ONEX node contract definitions must follow the linked document architecture pattern with contract.yaml linking to node_config.yaml and deployment_config.yaml as associated documents

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/nodes/*/contract.yaml : Canonical node structure (NEW components): nodes/<adapter>/ with contract.yaml, node.py, models/, and registry/ directories. Do not create versioned directory structures like nodes/<adapter>/v1_0_0/. Legacy v1_0_0 directories will be migrated per H1 ticket.

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-30T21:55:10.298Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T21:55:10.298Z
Learning: Applies to src/omninode_bridge/nodes/**/contract.yaml : All contract YAML files for ONEX v2.0 nodes MUST define subcontract references, input/output models, and FSM configurations. Use YAML 1.2 syntax.

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contract.yaml : All ONEX node contract definitions must support optional documents pattern with optional flag and required_capability field for future extensibility

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contracts/contract_capabilities.yaml : All ONEX node execution capability definitions, if applicable, must be included in contract_capabilities.yaml with supported_node_types, supported_delivery_modes, and performance_constraints specifications

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T16:32:55.606Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: Applies to **/*contract*.yaml : All ONEX nodes must have validated YAML contracts following the contract-driven development pattern with input_state and output_state schema definitions

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/contract.yaml : Version through contracts using `contract_version` field in `contract.yaml`. Do not use versioned directory names. Example of CORRECT usage: `meta: { contract_version: '1.0.0', node_version: '1.2.3' }` in contract.yaml.

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/contracts/contract_cli.yaml : All ONEX node CLI interface definitions, if applicable, must be included in contract_cli.yaml with entrypoint and commands specifications

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T17:24:41.687Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T17:24:41.687Z
Learning: Applies to **/v[0-9]_[0-9]_[0-9]/contract.yaml : The main contract.yaml file serves as the source of truth for node interfaces and should reference subcontracts using $ref patterns

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
📚 Learning: 2025-11-24T16:32:55.606Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: EFFECT Nodes must inherit from `NodeEffect` or use `NodeEffectService` for service implementation and must handle external system interactions (API calls, database operations, file system operations, message queue interactions)

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/__init__.py
  • src/omnibase_infra/nodes/effects/README.md
  • src/omnibase_infra/nodes/effects/registry_effect.py
📚 Learning: 2025-12-03T16:55:49.755Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T16:55:49.755Z
Learning: Applies to agents/**/*.py : Implement ONEX-compliant agent architecture with four node types: Effect (External I/O), Compute (Pure transforms), Reducer (State/persistence), and Orchestrator (Workflow coordination)

Applied to files:

  • src/omnibase_infra/nodes/effects/contract.yaml
  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/__init__.py
  • src/omnibase_infra/nodes/effects/README.md
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/node.py : All ONEX node base classes and I/O models come from `omnibase_core.nodes`: NodeEffect, NodeCompute, NodeReducer, NodeOrchestrator, ModelEffectInput, ModelEffectOutput, ModelComputeInput, ModelComputeOutput, ModelReducerInput, ModelReducerOutput, ModelOrchestratorInput, ModelOrchestratorOutput. Never define new node archetypes in infra.

Applied to files:

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

Applied to files:

  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/__init__.py
  • src/omnibase_infra/nodes/effects/models/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_registry_request.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/**/*.py : Name node classes following ONEX patterns: Effect nodes as Node{Name}Effect (e.g., NodeIntelligenceAdapterEffect), Compute nodes as Node{Name}Compute (e.g., NodeVectorizationCompute), Reducer nodes as Node{Name}Reducer (e.g., NodeIntelligenceReducer), Orchestrator nodes as Node{Name}Orchestrator (e.g., NodeIntelligenceOrchestrator)

Applied to files:

  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/__init__.py
📚 Learning: 2025-11-24T16:32:55.606Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: REDUCER Nodes must inherit from `NodeReducer` or use `NodeReducerService` and must be responsible for state management, FSM coordination, data aggregation, event reduction, and state validation

Applied to files:

  • src/omnibase_infra/nodes/__init__.py
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/registry*.py : Node-specific Registries: File `registry_infra_<node_name>.py`, Class `RegistryInfra<NodeName>`. Standalone Registries: File `registry_<purpose>.py`, Class `Registry<Purpose>`. Examples: `registry_infra_postgres_adapter.py` → `RegistryInfraPostgresAdapter`, `registry_handler.py` → `RegistryHandler`.

Applied to files:

  • src/omnibase_infra/nodes/__init__.py
  • src/omnibase_infra/nodes/effects/models/model_registry_response.py
  • src/omnibase_infra/nodes/effects/registry_effect.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Use Protocol for interface definitions when implementations may live outside core codebase; use Pydantic models only for base classes with shared logic

Applied to files:

  • src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.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 **/testing/testing_scenario_harness.py : Scenario harness must resolve registry from scenario configuration and fallback to canonical tools when resolver fails; never leave registry as None when node requires it

Applied to files:

  • tests/integration/registration/effect/__init__.py
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/node_tests/**/*.py : All ONEX node tests must be organized in a `node_tests/` directory using scenario-driven testing patterns with fixture-injected tests

Applied to files:

  • tests/integration/registration/effect/__init__.py
  • tests/unit/registration/__init__.py
  • tests/performance/registration/__init__.py
  • tests/integration/registration/effect/conftest.py
  • tests/performance/__init__.py
  • tests/unit/registration/effect/conftest.py
  • tests/performance/registration/effect/__init__.py
  • tests/unit/registration/effect/__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: Organize tests following the structure: tests/conftest.py for shared fixtures, tests/unit/ for unit tests (no infrastructure), tests/integration/ for integration tests (requires Kafka/DBs), tests/nodes/ for node-specific tests

Applied to files:

  • tests/unit/registration/__init__.py
  • tests/integration/registration/effect/conftest.py
  • tests/performance/registration/effect/conftest.py
  • tests/performance/__init__.py
  • tests/unit/registration/effect/conftest.py
  • tests/unit/registration/effect/__init__.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Applies to nodes/effect/**/*.py : Use handler envelopes from `omnibase_infra` for all I/O operations (HTTP, database, Kafka) instead of custom clients

Applied to files:

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

Applied to files:

  • src/omnibase_infra/nodes/effects/protocol_consul_client.py
  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
📚 Learning: 2025-11-28T18:58:53.781Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-28T18:58:53.781Z
Learning: Organize models under `src/omnibase_core/models/` by domain including: base, cli, common, config, core, contracts, discovery, health, infrastructure, logging, metadata, nodes, operations, results, security, service, tools, validation, and workflows

Applied to files:

  • src/omnibase_infra/nodes/effects/models/__init__.py
📚 Learning: 2025-11-24T16:33:32.747Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/standards.mdc:0-0
Timestamp: 2025-11-24T16:33:32.747Z
Learning: Applies to **/*.py : Import models from shared core paths using `omnibase.model.core.model_*` pattern

Applied to files:

  • src/omnibase_infra/nodes/effects/models/__init__.py
📚 Learning: 2025-11-24T17:23:49.777Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T17:23:49.777Z
Learning: Applies to **/node_*/v[0-9]*_[0-9]*_[0-9]*/models/model_contract_*.py : All ONEX node auto-generated Pydantic models must be organized in a `models/` directory with files for state.py, model_contract_actions.py, model_contract_models.py, model_contract_validation.py, model_contract_cli.py (optional), model_contract_capabilities.py (optional), and error_codes.py, generated from the corresponding contract definitions

Applied to files:

  • src/omnibase_infra/nodes/effects/models/__init__.py
📚 Learning: 2025-11-24T17:22:32.195Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/canonical_patterns.mdc:0-0
Timestamp: 2025-11-24T17:22:32.195Z
Learning: Applies to src/omnibase/nodes/*/ARCHITECTURE_DECISIONS.md : ARCHITECTURE_DECISIONS.md must document design rationale and decisions for the node implementation with clear reasoning for each choice

Applied to files:

  • src/omnibase_infra/nodes/effects/README.md
📚 Learning: 2025-11-24T16:33:51.604Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T16:33:51.604Z
Learning: Applies to tests/**/conftest.py : Test fixtures must be defined in `conftest.py` and should provide reusable sample data, UUIDs, semantic versions, and model data

Applied to files:

  • tests/integration/registration/effect/conftest.py
  • tests/performance/registration/effect/conftest.py
  • tests/unit/registration/effect/conftest.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/registration/effect/conftest.py
  • tests/performance/registration/effect/conftest.py
  • tests/unit/registration/effect/conftest.py
📚 Learning: 2025-11-24T17:24:54.193Z
Learnt from: CR
Repo: OmniNode-ai/omniclaude PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T17:24:54.193Z
Learning: Applies to **/*test*.py : Use registry=None in test harness to force registry resolver usage instead of manually creating registry instances

Applied to files:

  • tests/integration/registration/effect/conftest.py
  • tests/unit/registration/effect/conftest.py
  • tests/integration/registration/effect/test_registry_effect_integration.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Applies to tests/**/*.py : Use pytest markers `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.slow`, and `pytest.mark.performance` for test categorization

Applied to files:

  • tests/performance/__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: Use pytest markers for test organization: pytest -m unit for unit tests only, pytest -m integration for integration tests, pytest -m slow for slow tests, pytest -m performance for performance benchmarks

Applied to files:

  • tests/performance/__init__.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Run tests using: `pytest` for all tests, `pytest tests/unit` for unit tests only, `pytest tests/integration` for integration tests, `pytest -k "test_name"` for single tests, and `pytest --cov=src/omniagent --cov-report=html` for coverage reports

Applied to files:

  • tests/performance/__init__.py
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/registry/registry_infra_*.py : Node-Specific Registries must use file pattern `registry_infra_<node_name>.py` with class pattern `RegistryInfra<NodeName>`. Example: `registry_infra_postgres_adapter.py` → `RegistryInfraPostgresAdapter`.

Applied to files:

  • src/omnibase_infra/nodes/effects/models/model_registry_response.py
  • src/omnibase_infra/nodes/effects/registry_effect.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 **/testing/testing_scenario_harness.py : Registry resolver should use dynamic fixture detection based on constructor signatures using inspect.signature to determine which parameters to inject

Applied to files:

  • tests/unit/registration/effect/conftest.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 canonical test execution pattern with pytest.mark.parametrize, get_scenario_paths(), and scenario_test_harness.run_scenario_test() with registry=None for registry resolver

Applied to files:

  • tests/unit/registration/effect/conftest.py
📚 Learning: 2025-12-06T22:21:32.649Z
Learnt from: CR
Repo: OmniNode-ai/omniagent PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T22:21:32.649Z
Learning: Applies to nodes/effect/**/*.py : Use `DbAdapter` envelope pattern for database operations instead of custom PostgreSQL clients

Applied to files:

  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
  • src/omnibase_infra/nodes/effects/registry_effect.py
  • tests/integration/registration/effect/test_doubles.py
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/node.py : Node files must be named `node.py` with class pattern `Node<Name><Type>`. Example: `NodePostgresAdapterEffect`, `NodePostgresAdapterCompute`.

Applied to files:

  • src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py
📚 Learning: 2025-11-29T17:13:38.776Z
Learnt from: CR
Repo: OmniNode-ai/omniarchon PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-29T17:13:38.776Z
Learning: Applies to **/*.py : Implement retry logic with 3 attempts and exponential backoff (1s → 2s → 4s) for resilient service communication.

Applied to files:

  • tests/unit/registration/effect/test_effect_retry_backoff.py
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/*adapter*.py : All infrastructure adapters and services should use `MixinAsyncCircuitBreaker` for fault tolerance and automatic recovery. Initialize with `_init_circuit_breaker(threshold=<int>, reset_timeout=<float>, service_name=<str>, transport_type=EnumInfraTransportType.<TYPE>)`. Check circuit breaker before operations with: `async with self._circuit_breaker_lock: await self._check_circuit_breaker(...)`

Applied to files:

  • tests/unit/registration/effect/test_effect_circuit_breaker.py
📚 Learning: 2025-12-22T00:11:20.308Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_infra PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-22T00:11:20.308Z
Learning: Applies to **/*.py : Always propagate correlation_id from incoming requests to error context. Auto-generate using `uuid4()` if no correlation_id exists. Use UUID format for all new correlation IDs. Include correlation_id in all error context for distributed tracing.

Applied to files:

  • tests/unit/registration/effect/test_effect_circuit_breaker.py
📚 Learning: 2025-11-24T16:32:55.606Z
Learnt from: CR
Repo: OmniNode-ai/omninode_bridge PR: 0
File: .cursor/rules/node_standards.mdc:0-0
Timestamp: 2025-11-24T16:32:55.606Z
Learning: Applies to tests/unit/infrastructure/**/test_*.py : All node implementations must have comprehensive unit tests following the testing pattern in `tests/unit/infrastructure/` with tests for node initialization and node execution

Applied to files:

  • tests/unit/registration/effect/__init__.py
🧬 Code graph analysis (15)
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py (2)
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (4)
  • mark_completed (181-205)
  • is_completed (207-234)
  • get_completed_backends (236-259)
  • clear (261-268)
src/omnibase_infra/nodes/effects/registry_effect.py (1)
  • get_completed_backends (395-404)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (2)
  • max_cache_size (172-174)
  • cache_ttl_seconds (177-179)
src/omnibase_infra/nodes/effects/models/__init__.py (4)
src/omnibase_infra/nodes/effects/models/model_backend_result.py (1)
  • ModelBackendResult (39-112)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
  • ModelEffectIdempotencyConfig (37-89)
src/omnibase_infra/nodes/effects/models/model_registry_request.py (1)
  • ModelRegistryRequest (33-111)
src/omnibase_infra/nodes/effects/models/model_registry_response.py (1)
  • ModelRegistryResponse (52-251)
src/omnibase_infra/nodes/effects/models/model_registry_request.py (4)
tests/performance/registration/effect/conftest.py (1)
  • correlation_id (134-140)
tests/unit/registration/effect/test_effect_partial_failure.py (1)
  • correlation_id (110-116)
tests/unit/registration/effect/conftest.py (1)
  • correlation_id (246-252)
tests/helpers/deterministic.py (1)
  • now (136-147)
tests/unit/registration/effect/test_effect_idempotency.py (2)
src/omnibase_infra/idempotency/store_inmemory.py (2)
  • InMemoryIdempotencyStore (30-261)
  • get_record (236-261)
src/omnibase_infra/idempotency/models/model_idempotency_record.py (1)
  • ModelIdempotencyRecord (20-83)
tests/integration/registration/effect/conftest.py (4)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
  • ModelEffectIdempotencyConfig (37-89)
src/omnibase_infra/nodes/effects/models/model_registry_request.py (1)
  • ModelRegistryRequest (33-111)
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (3)
  • InMemoryEffectIdempotencyStore (89-362)
  • max_cache_size (172-174)
  • cache_ttl_seconds (177-179)
tests/integration/registration/effect/test_doubles.py (2)
  • StubConsulClient (68-185)
  • StubPostgresAdapter (188-308)
tests/performance/registration/effect/test_effect_performance.py (3)
src/omnibase_infra/idempotency/store_inmemory.py (1)
  • InMemoryIdempotencyStore (30-261)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
  • ModelEffectIdempotencyConfig (37-89)
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py (3)
  • mark_completed (82-96)
  • is_completed (98-108)
  • get_completed_backends (110-119)
tests/unit/registration/effect/test_effect_partial_failure.py (4)
src/omnibase_infra/nodes/effects/models/model_backend_result.py (1)
  • ModelBackendResult (39-112)
src/omnibase_infra/nodes/effects/models/model_registry_request.py (1)
  • ModelRegistryRequest (33-111)
src/omnibase_infra/nodes/effects/models/model_registry_response.py (7)
  • ModelRegistryResponse (52-251)
  • get_failed_backends (227-238)
  • get_successful_backends (240-251)
  • is_partial_failure (211-217)
  • is_complete_success (203-209)
  • is_complete_failure (219-225)
  • from_backend_results (149-201)
src/omnibase_infra/nodes/effects/registry_effect.py (4)
  • NodeRegistryEffect (87-404)
  • register_node (192-261)
  • get_completed_backends (395-404)
  • clear_completed_backends (385-393)
src/omnibase_infra/nodes/effects/models/model_registry_response.py (1)
src/omnibase_infra/nodes/effects/models/model_backend_result.py (1)
  • ModelBackendResult (39-112)
tests/unit/registration/effect/conftest.py (4)
src/omnibase_infra/idempotency/store_inmemory.py (1)
  • InMemoryIdempotencyStore (30-261)
src/omnibase_infra/models/registration/model_node_capabilities.py (1)
  • ModelNodeCapabilities (13-167)
src/omnibase_infra/models/registration/model_node_metadata.py (1)
  • ModelNodeMetadata (14-76)
src/omnibase_infra/models/registration/model_node_registration.py (1)
  • ModelNodeRegistration (24-155)
src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py (1)
tests/integration/registration/effect/test_doubles.py (1)
  • upsert (258-308)
tests/integration/registration/effect/test_registry_effect_integration.py (5)
src/omnibase_infra/nodes/effects/registry_effect.py (4)
  • NodeRegistryEffect (87-404)
  • register_node (192-261)
  • get_completed_backends (395-404)
  • clear_completed_backends (385-393)
src/omnibase_infra/nodes/effects/models/model_registry_request.py (1)
  • ModelRegistryRequest (33-111)
tests/integration/registration/effect/test_doubles.py (4)
  • StubConsulClient (68-185)
  • StubPostgresAdapter (188-308)
  • set_exception (117-123)
  • set_exception (237-243)
tests/integration/registration/effect/conftest.py (6)
  • registry_effect (71-96)
  • consul_client (37-43)
  • postgres_adapter (47-53)
  • sample_request (100-116)
  • request_factory (120-150)
  • idempotency_store (57-67)
src/omnibase_infra/nodes/effects/models/model_registry_response.py (5)
  • is_complete_success (203-209)
  • is_partial_failure (211-217)
  • is_complete_failure (219-225)
  • get_failed_backends (227-238)
  • get_successful_backends (240-251)
tests/integration/registration/effect/test_doubles.py (2)
src/omnibase_infra/nodes/effects/protocol_consul_client.py (1)
  • register_service (24-42)
src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py (1)
  • upsert (26-46)
tests/unit/registration/effect/test_effect_idempotency_store.py (4)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
  • ModelEffectIdempotencyConfig (37-89)
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (10)
  • InMemoryEffectIdempotencyStore (89-362)
  • max_cache_size (172-174)
  • cache_ttl_seconds (177-179)
  • is_completed (207-234)
  • mark_completed (181-205)
  • get_completed_backends (236-259)
  • clear (261-268)
  • get_cache_size (279-288)
  • clear_all (270-277)
  • cleanup_expired (290-299)
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py (4)
  • is_completed (98-108)
  • mark_completed (82-96)
  • get_completed_backends (110-119)
  • clear (121-130)
src/omnibase_infra/nodes/effects/registry_effect.py (1)
  • get_completed_backends (395-404)
tests/performance/registration/effect/test_idempotency_store_performance.py (3)
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py (1)
  • ModelEffectIdempotencyConfig (37-89)
src/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py (10)
  • InMemoryEffectIdempotencyStore (89-362)
  • max_cache_size (172-174)
  • cache_ttl_seconds (177-179)
  • clear_all (270-277)
  • mark_completed (181-205)
  • get_cache_size (279-288)
  • cleanup_expired (290-299)
  • is_completed (207-234)
  • get_completed_backends (236-259)
  • clear (261-268)
src/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py (4)
  • mark_completed (82-96)
  • is_completed (98-108)
  • get_completed_backends (110-119)
  • clear (121-130)

Comment on lines +1 to +16
# SPDX-License-Identifier: MIT
# Copyright (c) 2025 OmniNode Team
#
# ONEX Node Contract
# Node: NodeRegistryEffect
#
# This contract defines the interface for the Registry Effect node,
# which handles dual-backend node registration against Consul and PostgreSQL.
# Required root-level fields per ModelYamlContract schema
contract_version:
major: 1
minor: 0
patch: 0
node_type: EFFECT_GENERIC
description: >
Effect node for dual-backend node registration. Executes registration operations against both Consul (service discovery) and PostgreSQL (registration record persistence), with support for partial failure handling and targeted retries.

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
# Read the full contract.yaml file
echo "=== Full contract.yaml content ==="
cat -n src/omnibase_infra/nodes/effects/contract.yaml

echo -e "\n=== Search for other contract.yaml files in nodes directory ==="
find src/omnibase_infra/nodes -name 'contract.yaml' -type f

echo -e "\n=== Compare with another contract.yaml structure ==="
fd -t f 'contract.yaml' src/omnibase_infra/nodes | head -2 | while read f; do
  echo "--- File: $f ---"
  head -60 "$f"
  echo ""
done

Repository: OmniNode-ai/omnibase_infra

Length of output: 1746


🏁 Script executed:

#!/bin/bash
# Check the node directory structure
echo "=== Effects node directory structure ==="
ls -la src/omnibase_infra/nodes/effects/

echo -e "\n=== Check if any other required files exist ==="
find src/omnibase_infra/nodes/effects -type f

Repository: OmniNode-ai/omnibase_infra

Length of output: 1686


Contract.yaml is incomplete and uses incorrect version format.

The contract is missing required fields per canonical ONEX structure. Correct the following:

  1. Fix version metadata format: Change from nested contract_version: major/minor/patch to meta: { contract_version: '1.0.0', node_version: '...' } (semantic versioning per canonical pattern).

  2. Add required state schemas: Include input_state and output_state definitions with schema inheritance from OnexInputState and OnexOutputState.

  3. Node directory structure: Add missing registry/ directory to complete canonical structure (contract.yaml, node.py, models/, registry/).

See recent learnings for canonical structure requirements.

🤖 Prompt for AI Agents
In src/omnibase_infra/nodes/effects/contract.yaml lines 1-16: the contract uses
an incorrect version layout and is missing required fields and canonical
structure; update the top-level metadata to a single meta mapping with semantic
contract_version (e.g. "1.0.0") and a node_version string, add required state
schema entries input_state and output_state that inherit from OnexInputState and
OnexOutputState respectively (include schema refs or extends per the ONEX schema
pattern), and ensure the node directory contains a registry/ subdirectory
alongside contract.yaml, node.py and models/ so the canonical structure is
complete; keep values and placeholders consistent with other ONEX contracts and
validate against the ModelYamlContract schema after changes.

Comment thread tests/integration/registration/effect/test_doubles.py
Comment thread tests/performance/registration/effect/test_idempotency_store_performance.py Outdated
Comment thread tests/unit/registration/effect/test_effect_idempotency.py Outdated
Changes:
- Complete contract.yaml with ONEX-compliant structure (io_operations,
  definitions, proper dependencies format, semantic versioning objects)
- Add Literal type for node_type validation in ModelRegistryRequest
- Document exception behavior in ProtocolEffectIdempotencyStore methods
- Convert non-awaiting async methods to sync in InMemoryEffectIdempotencyStore
- Add get_estimated_memory_bytes() method for cache memory estimation
- Document ONEX naming convention rationale in registry_effect.py
- Remove unused TypeVar from test_effect_circuit_breaker.py
- Consolidate duplicate fixtures across test conftest.py files
- Add documentation for uuid5 collision-resistance in natural key generation
- Fix mock_consul_client to use AsyncMock for async service methods

All 111 tests pass.
@claude

claude Bot commented Dec 22, 2025

Copy link
Copy Markdown

✅ Code Review - PR #78: Registry Effect Node Implementation

Summary

This is an outstanding PR that demonstrates exemplary ONEX architecture principles and testing practices. The implementation of NodeRegistryEffect with comprehensive idempotency handling, partial failure support, and extensive test coverage (111 tests) exceeds typical quality standards.


🌟 Strengths

1. Exceptional ONEX Compliance

✅ Perfect naming conventions:

  • NodeRegistryEffect follows Node pattern
  • Models: ModelBackendResult, ModelRegistryRequest, ModelRegistryResponse
  • Files: model_backend_result.py, registry_effect.py, store_effect_idempotency_inmemory.py

✅ Strong typing throughout:

  • Zero Any types (uses object for generic envelope payloads per CLAUDE.md guidelines)
  • All models are proper Pydantic with frozen=True for immutability
  • Literal types for status enums ("success" | "partial" | "failed")

✅ Contract-driven design:

  • contract.yaml with semantic versioning (contract_version, node_version)
  • Protocol-based dependencies (ProtocolConsulClient, ProtocolPostgresAdapter, ProtocolEffectIdempotencyStore)
  • Clear I/O operation definitions

2. Excellent Architecture & Design

✅ Partial failure handling (OMN-954 acceptance criteria):

  • Per-backend result tracking with ModelBackendResult
  • Three-state response: success, partial, failed
  • Idempotency store enables targeted retries (only failed backends re-execute)

✅ Memory-bounded idempotency store:

  • LRU eviction with configurable max_cache_size (default 10K entries, ~1MB)
  • TTL-based expiration (default 1 hour)
  • O(1) operations using OrderedDict
  • Well-documented memory characteristics (get_estimated_memory_bytes())

✅ Security-first error sanitization:

  • _sanitize_backend_error() prevents credential exposure
  • Safe error patterns: connection refused, timeout, authentication failed
  • Never exposes raw error messages that might contain secrets
  • Generic fallback: "{backend} operation failed"

✅ Comprehensive documentation:

  • 350-line README.md with memory characteristics, configuration guide, production warnings
  • Inline docstrings with O-notation, throughput benchmarks
  • Clear design rationale notes (e.g., naming convention explanation in registry_effect.py:5-17)

3. Outstanding Test Coverage (111 tests)

✅ Unit tests (60 tests):

  • Idempotency: duplicate intent safety, natural key conflicts, domain isolation
  • Circuit breaker: state transitions (CLOSED→OPEN→HALF_OPEN→CLOSED), per-backend isolation
  • Retry/backoff: exponential backoff timing, retry exhaustion, non-retryable errors
  • Partial failure: Consul/Postgres combinations, error aggregation
  • Error sanitization: 13 test cases for secret exposure prevention

✅ Integration tests (16 tests):

  • End-to-end registration flows with container wiring
  • Partial failure retry scenarios
  • Idempotency across effect restarts

✅ Performance tests (25 tests):

  • High volume: 1000 sequential ops < 2s
  • Concurrent load: 100+ ops via asyncio.gather
  • Cache stress: LRU eviction, memory bounds verification
  • Latency distribution: p50, p95, p99 measurements

✅ Test quality:

  • Clear AAA structure (Arrange-Act-Assert)
  • Descriptive test names explaining what's validated
  • Good use of fixtures (conftest.py consolidation)
  • Performance markers (@pytest.mark.performance) for CI skipping

🔍 Observations & Minor Suggestions

1. Protocol Duplication (Low Priority)

Observation: ProtocolEffectIdempotencyStore (src/omnibase_infra/nodes/effects/) is conceptually similar to ProtocolIdempotencyStore (omnibase_spi). The rationale is documented in protocol_effect_idempotency_store.py:8-18.

Suggestion: Consider whether these could be unified with a more flexible protocol in a future refactoring. For now, the separation is justified and well-documented.

2. In-Memory Store Production Warning (Well-Documented)

Observation: The README.md correctly warns about InMemoryEffectIdempotencyStore limitations:

  • ❌ Not persistent across restarts
  • ❌ Not distributed (single-process only)
  • ❌ Multi-instance deployments will have inconsistent state

Suggestion: The documentation is excellent. Future work should implement RedisEffectIdempotencyStore or PostgresEffectIdempotencyStore for production use.

3. Contract.yaml Completeness

Good: The contract includes:

  • Semantic versioning (version.major/minor/patch)
  • I/O operations with field definitions
  • Dependencies with protocol references
  • Capabilities list

Minor: The definitions section includes model schemas, which is great, but the schemas are somewhat generic. This is acceptable for MVP—more specific validation rules can be added as the contract matures.

4. Error Sanitization Pattern

Excellent implementation: The _sanitize_backend_error() function (registry_effect.py:131-173) is well-designed with pattern matching and safe fallbacks.

Minor observation: The function could benefit from being extracted to a shared utility if this pattern is reused across other effects. For now, keeping it local is fine.


🛡️ Security Assessment

✅ No security concerns identified:

  • Error sanitization prevents credential exposure
  • No direct use of str(e) on backend errors
  • Correlation IDs properly propagated for tracing
  • No PII exposure in logs or error messages
  • Test doubles properly mock external clients

📊 Performance Considerations

✅ Performance characteristics well-documented:

  • Idempotency store: O(1) lookups, >5K ops/sec single-worker, >10K concurrent
  • Memory bounded by configuration
  • TTL cleanup is lazy (doesn't block operations)

✅ Performance tests validate thresholds:

  • Sequential 1000 ops < 2s
  • Concurrent 100 ops < 1s
  • Latency p99 < 100ms

No concerns for the intended use case (node registration is not ultra-high frequency).


🚀 Recommendations

Before Merge:

  1. ✅ All tests pass (111/111) - confirmed in PR description
  2. ✅ Pre-commit hooks pass - confirmed
  3. ✅ Type checking passes - confirmed

Post-Merge (Future Tickets):

  1. Implement persistent idempotency store:

    • RedisEffectIdempotencyStore or PostgresEffectIdempotencyStore
    • Critical for production multi-instance deployments
  2. Add circuit breaker integration (if not already present):

    • The README mentions circuit breaker support
    • Consider adding circuit breaker per backend (Consul, PostgreSQL)
  3. Monitor memory usage in production:

    • The get_estimated_memory_bytes() method is excellent for this
    • Add metrics/observability for cache hit rates, evictions, TTL expirations

✅ Final Verdict

APPROVE ✅

This PR is production-ready for single-instance deployments and demonstrates:

  • Exceptional ONEX architecture compliance
  • Comprehensive test coverage (111 tests across unit/integration/performance)
  • Security-conscious error handling
  • Clear documentation with production warnings
  • Well-designed partial failure handling

The in-memory idempotency store limitation is clearly documented and acceptable for MVP. The path forward (persistent store for multi-instance) is well-understood.

Outstanding work @jonahgabriel! This sets a high bar for Effect node implementations in the ONEX ecosystem.


📝 Related Tickets

  • OMN-954: Registry Effect Node testing (closes)

Review conducted following CLAUDE.md guidelines for ONEX infrastructure.

@jonahgabriel
jonahgabriel merged commit a904799 into main Dec 22, 2025
4 of 6 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-954-g4-test-effect-idempotency-and-retry-behavior branch December 22, 2025 14:29
jonahgabriel added a commit that referenced this pull request Dec 23, 2025
PR #78 (OMN-954) added nodes/effects/ module with legitimate X | None
nullable patterns, increasing union count from 530 to 544.

- Update INFRA_MAX_UNIONS from 540 to 555 (11 buffer above baseline)
- Document threshold history with PR #78 additions
- Update test docstrings to reflect new baseline

All 3119 unit tests and 142 integration tests pass.
jonahgabriel added a commit that referenced this pull request Dec 23, 2025
* fix(ci): resolve 49 test failures on main branch

- Add required correlation_id to ModelNodeIntrospectionEvent tests (44 tests)
- Create local ProtocolIdempotencyStore since missing from omnibase_spi
- Fix ModelDuplicateResponse serialization with .model_dump()
- Update INFRA_MAX_UNIONS threshold from 485 to 510

Fixes:
- ModelNodeIntrospectionEvent now requires correlation_id field
- ProtocolIdempotencyStore import from non-existent omnibase_spi module
- Union count threshold exceeded (503 vs 485)

* docs(pr-review): address PR #77 review feedback

Add documentation requested by reviewers:

- Add TODO comment for ProtocolIdempotencyStore migration to omnibase_spi 0.5.0+
- Document union threshold history and root cause (481→503 unions)
  - ProtocolIdempotencyStore protocol addition
  - ModelNodeIntrospectionEvent correlation_id field additions
  - CI test failure fixes on main branch

* fix(runtime): remove redundant model_dump() call on dict

_create_duplicate_response already returns a dict (calls model_dump
internally), so the caller shouldn't call model_dump again.

Fixes mypy error: "dict[str, object]" has no attribute "model_dump"

* docs(pr-review): address PR #77 review feedback [OMN-999]

- Replace TODO(OMN-XXX) with OMN-999 in protocol_idempotency_store.py
- Enhance migration note with 4-step migration path to omnibase_spi 0.5.0
- Add comprehensive fail-open semantics documentation in _check_idempotency
- Fix INFRA_MAX_UNIONS baseline documentation (512→503)

Addresses all CRITICAL, MAJOR, and MINOR review items from PR #77.

* docs(pr-review): address PR #77 release-ready review feedback [OMN-1000]

Address all PR #77 review issues for release readiness:

CRITICAL fixes:
- Create Linear ticket OMN-1000 for protocol migration to omnibase_spi
- Update TODO placeholder with actual ticket reference (OMN-1000)
- Add comprehensive protocol tests (18 new tests)
- Add security documentation section to protocol docstring

MAJOR fixes:
- Update INFRA_MAX_UNIONS threshold 525→540 for new protocol tests
- Synchronize threshold history between validator and test file
- Add prominent fail-open warning block to _check_idempotency method

New test file:
- tests/unit/idempotency/test_protocol_idempotency_store.py
  - TestProtocolDefinition: runtime_checkable, required methods, async
  - TestProtocolMethodSignatures: parameter validation
  - TestProtocolConformance: InMemory + Postgres implementation checks
  - TestProtocolTypeAnnotations: type hint verification
  - TestNonConformingImplementation: negative test cases

All 122 tests passing.

* fix(validation): bump union threshold for OMN-954 effects module

PR #78 (OMN-954) added nodes/effects/ module with legitimate X | None
nullable patterns, increasing union count from 530 to 544.

- Update INFRA_MAX_UNIONS from 540 to 555 (11 buffer above baseline)
- Document threshold history with PR #78 additions
- Update test docstrings to reflect new baseline

All 3119 unit tests and 142 integration tests pass.
jonahgabriel added a commit that referenced this pull request Dec 23, 2025
Merged origin/main into jonah/omn-c1-registration-orchestrator.
Resolved conflicts in:
- src/omnibase_infra/validation/infra_validators.py
- tests/unit/validation/test_validator_defaults.py

Combined threshold history documentation from both branches:
- OMN-950: reducer tests (540 unions)
- OMN-954: effect idempotency tests PR #78 (544 unions)
- OMN-C1: registration orchestrator PR #79 (555 unions)
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