Repository navigation
test(effect): add comprehensive effect idempotency and retry behavior tests [OMN-954] - #78
Conversation
… 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
|
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 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. 📒 Files selected for processing (17)
WalkthroughIntroduces 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes
Poem
Comment |
PR Review: Effect Idempotency and Retry Behavior TestsOverall AssessmentThis 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 Issues1. ONEX Naming Convention Violation - Node ClassIssue: Location: CLAUDE.md Rule:
Current: class RegistryEffect: # WRONGRequired: class NodeRegistryEffect: # CORRECTImpact: This breaks ONEX architectural consistency. All node classes MUST follow the
Fix: Rename
2. Missing ONEX Base Class InheritanceIssue: CLAUDE.md Rule:
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 containerImpact: Without base class inheritance:
3. Container-Based Dependency Injection ViolationIssue: Constructor uses direct dependency injection instead of CLAUDE.md Rule:
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:
Reference: 4. Memory Leak Risk - Unbounded CacheIssue: Location: self._completed_backends: dict[UUID, set[str]] = {} # Never cleared\!Problem: Every
Impact:
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 FlawIssue: In-memory idempotency state does not survive restarts Location: Problem:
CLAUDE.md Principle:
Solution: Use 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...
|
…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
PR Review: Effect Idempotency and Retry Behavior Tests [OMN-954]SummaryThis 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
🔍 Issues & RecommendationsShould Fix Before Merge
Nice to Have
📋 ONEX Guidelines Compliance✅ No Any types used 🧪 Test Quality
🚀 PerformanceDocumented performance is excellent:
🏆 Overall AssessmentVerdict: ✅ 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) |
There was a problem hiding this comment.
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 foris_completed,get_completed_backends, andclearmethods, 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
NodeRegistryEffectincluding 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 showsfrom unittest.mock import AsyncMock. Consider addingfrom uuid import uuid4to 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 NodeRegistryEffectsrc/omnibase_infra/nodes/effects/models/model_registry_request.py (1)
76-79: Consider usingLiteraltype fornode_typevalidation.The docstring specifies valid values as
effect,compute,reducer,orchestrator. Using aLiteraltype 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
strbut add a validator for known types.src/omnibase_infra/nodes/effects/protocol_postgres_adapter.py (1)
26-46: Consider usingTypedDictfor stronger return type safety.The return type
dict[str, bool | str]is flexible but doesn't enforce the expected structure ({"success": bool, "error"?: str}). ATypedDictwould 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 raiseValidationErrorwhen 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.pyfiles 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:retriesfield is always 0 - retry logic not implemented at this level.The
retriesfield inModelBackendResultis 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 = 0assignment 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: UnusedTYPE_CHECKINGimport.The
TYPE_CHECKINGconstant 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, uuid4tests/unit/registration/effect/test_effect_idempotency.py (2)
64-71: Fixture duplicates the one in conftest.py.This
inmemory_idempotency_storefixture is defined both here and intests/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_clientandmock_postgres_clientfixtures 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 synchronousMagicMockforregister/deregisterwhile this version usesAsyncMock- 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_neededare declaredasyncbut don't contain anyawaitexpressions. 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 publichealth_check()method, which returnscircuit_breaker_stateandcircuit_breaker_failure_count. Other handlers in the codebase follow the pattern of providing a publicget_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 usehealth_check().tests/unit/registration/effect/test_effect_circuit_breaker.py (1)
49-49: Remove unused TypeVar.
Tis 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._cachedirectly breaks encapsulation. While acceptable in tests, a publicget_cache_memory_bytes()method onInMemoryEffectIdempotencyStorewould be cleaner and more maintainable.tests/integration/registration/effect/test_registry_effect_integration.py (2)
601-602: Remove unused fixture or use it.
request_factoryis 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 manualModelRegistryRequestconstruction.🔎 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 asyncioinside 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 asyncioIn the test method:
- import asyncio - effect = NodeRegistryEffect(
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Lite
📒 Files selected for processing (33)
src/omnibase_infra/nodes/__init__.pysrc/omnibase_infra/nodes/effects/README.mdsrc/omnibase_infra/nodes/effects/__init__.pysrc/omnibase_infra/nodes/effects/contract.yamlsrc/omnibase_infra/nodes/effects/models/__init__.pysrc/omnibase_infra/nodes/effects/models/model_backend_result.pysrc/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.pysrc/omnibase_infra/nodes/effects/models/model_registry_request.pysrc/omnibase_infra/nodes/effects/models/model_registry_response.pysrc/omnibase_infra/nodes/effects/protocol_consul_client.pysrc/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.pysrc/omnibase_infra/nodes/effects/protocol_postgres_adapter.pysrc/omnibase_infra/nodes/effects/registry_effect.pysrc/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.pytests/integration/registration/__init__.pytests/integration/registration/effect/__init__.pytests/integration/registration/effect/conftest.pytests/integration/registration/effect/test_doubles.pytests/integration/registration/effect/test_registry_effect_integration.pytests/performance/__init__.pytests/performance/registration/__init__.pytests/performance/registration/effect/__init__.pytests/performance/registration/effect/conftest.pytests/performance/registration/effect/test_effect_performance.pytests/performance/registration/effect/test_idempotency_store_performance.pytests/unit/registration/__init__.pytests/unit/registration/effect/__init__.pytests/unit/registration/effect/conftest.pytests/unit/registration/effect/test_effect_circuit_breaker.pytests/unit/registration/effect/test_effect_idempotency.pytests/unit/registration/effect/test_effect_idempotency_store.pytests/unit/registration/effect/test_effect_partial_failure.pytests/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 likev1_0_0/,v2/,v1/, etc. Version through contracts usingcontract_versionfield incontract.yaml. Semantic versioning is metadata, not file structure.
Version through contracts usingcontract_versionfield incontract.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 useAnytypes in Python code. Always use specific types. UseX | None(PEP 604) syntax instead ofOptional[X]for nullable types.
UseEnumMessageCategory(values: EVENT, COMMAND, INTENT) for message routing, topic parsing, and dispatcher selection. UseEnumNodeOutputType(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.
UseX | Nonesyntax (PEP 604) for nullable types instead ofOptional[X]. Example:def get_user(id: str) -> User | None:instead ofdef get_user(id: str) -> Optional[User]:
All services MUST useModelONEXContainerfor dependency injection. Bootstrap pattern:container = ModelONEXContainer()followed bywire_infrastructure_services(container)andservice = container.service_registry.resolve_service(ServiceType).
Always propagate correlation_id from incoming requests to error context. Auto-generate usinguuid4()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).
UseProtocolConfigurationErrorfor config validation failures,SecretResolutionErrorfor secret/credential resolution,InfraConnectionErrorfor connection failures,InfraTimeoutErrorfor operation timeouts,InfraAuthenticationErrorfor auth/authz failures, `InfraUnava...
Files:
src/omnibase_infra/nodes/__init__.pysrc/omnibase_infra/nodes/effects/protocol_effect_idempotency_store.pysrc/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.pytests/integration/registration/effect/__init__.pytests/unit/registration/__init__.pysrc/omnibase_infra/nodes/effects/models/model_backend_result.pysrc/omnibase_infra/nodes/effects/__init__.pysrc/omnibase_infra/nodes/effects/protocol_consul_client.pysrc/omnibase_infra/nodes/effects/models/__init__.pytests/integration/registration/__init__.pytests/performance/registration/__init__.pysrc/omnibase_infra/nodes/effects/models/model_registry_request.pytests/unit/registration/effect/test_effect_idempotency.pytests/integration/registration/effect/conftest.pytests/performance/registration/effect/test_effect_performance.pytests/performance/registration/effect/conftest.pytests/performance/__init__.pytests/unit/registration/effect/test_effect_partial_failure.pysrc/omnibase_infra/nodes/effects/models/model_registry_response.pytests/unit/registration/effect/conftest.pysrc/omnibase_infra/nodes/effects/protocol_postgres_adapter.pytests/unit/registration/effect/test_effect_retry_backoff.pytests/performance/registration/effect/__init__.pytests/integration/registration/effect/test_registry_effect_integration.pytests/unit/registration/effect/test_effect_circuit_breaker.pytests/unit/registration/effect/__init__.pysrc/omnibase_infra/nodes/effects/registry_effect.pytests/integration/registration/effect/test_doubles.pytests/unit/registration/effect/test_effect_idempotency_store.pytests/performance/registration/effect/test_idempotency_store_performance.pysrc/omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py
**/protocol*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Protocol files should use
protocol_<name>.pyfor standalone protocols (e.g.,protocol_event_bus.pycontainsProtocolEventBus). Useprotocols.pyfor 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.pysrc/omnibase_infra/nodes/effects/protocol_consul_client.pysrc/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>.pywith class patternModel<Name>. Files must contain exactly oneModel*class.
Files:
src/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.pysrc/omnibase_infra/nodes/effects/models/model_backend_result.pysrc/omnibase_infra/nodes/effects/models/model_registry_request.pysrc/omnibase_infra/nodes/effects/models/model_registry_response.py
**/*adapter*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*adapter*.py: All infrastructure adapters and services should useMixinAsyncCircuitBreakerfor 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>.pywith class patternRegistry<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, ClassRegistryInfra<NodeName>. Standalone Registries: Fileregistry_<purpose>.py, ClassRegistry<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.yamlsrc/omnibase_infra/nodes/__init__.pysrc/omnibase_infra/nodes/effects/__init__.pysrc/omnibase_infra/nodes/effects/README.mdsrc/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.yamlsrc/omnibase_infra/nodes/__init__.pysrc/omnibase_infra/nodes/effects/__init__.pysrc/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__.pysrc/omnibase_infra/nodes/effects/models/model_effect_idempotency_config.pysrc/omnibase_infra/nodes/effects/__init__.pysrc/omnibase_infra/nodes/effects/models/__init__.pysrc/omnibase_infra/nodes/effects/README.mdsrc/omnibase_infra/nodes/effects/models/model_registry_request.pysrc/omnibase_infra/nodes/effects/models/model_registry_response.pysrc/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__.pysrc/omnibase_infra/nodes/effects/__init__.pysrc/omnibase_infra/nodes/effects/models/__init__.pysrc/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__.pysrc/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__.pysrc/omnibase_infra/nodes/effects/models/model_registry_response.pysrc/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__.pytests/unit/registration/__init__.pytests/performance/registration/__init__.pytests/integration/registration/effect/conftest.pytests/performance/__init__.pytests/unit/registration/effect/conftest.pytests/performance/registration/effect/__init__.pytests/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__.pytests/integration/registration/effect/conftest.pytests/performance/registration/effect/conftest.pytests/performance/__init__.pytests/unit/registration/effect/conftest.pytests/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__.pysrc/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.pysrc/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.pytests/performance/registration/effect/conftest.pytests/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.pytests/performance/registration/effect/conftest.pytests/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.pytests/unit/registration/effect/conftest.pytests/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.pysrc/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.pysrc/omnibase_infra/nodes/effects/registry_effect.pytests/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)
| # 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. |
There was a problem hiding this comment.
🧩 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 ""
doneRepository: 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 fRepository: 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:
-
Fix version metadata format: Change from nested
contract_version: major/minor/patchtometa: { contract_version: '1.0.0', node_version: '...' }(semantic versioning per canonical pattern). -
Add required state schemas: Include
input_stateandoutput_statedefinitions with schema inheritance fromOnexInputStateandOnexOutputState. -
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.
…-effect-idempotency-and-retry-behavior
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.
✅ Code Review - PR #78: Registry Effect Node ImplementationSummaryThis 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. 🌟 Strengths1. Exceptional ONEX Compliance✅ Perfect naming conventions:
✅ Strong typing throughout:
✅ Contract-driven design:
2. Excellent Architecture & Design✅ Partial failure handling (OMN-954 acceptance criteria):
✅ Memory-bounded idempotency store:
✅ Security-first error sanitization:
✅ Comprehensive documentation:
3. Outstanding Test Coverage (111 tests)✅ Unit tests (60 tests):
✅ Integration tests (16 tests):
✅ Performance tests (25 tests):
✅ Test quality:
🔍 Observations & Minor Suggestions1. 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:
Suggestion: The documentation is excellent. Future work should implement RedisEffectIdempotencyStore or PostgresEffectIdempotencyStore for production use. 3. Contract.yaml CompletenessGood: The contract includes:
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 PatternExcellent 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:
📊 Performance Considerations✅ Performance characteristics well-documented:
✅ Performance tests validate thresholds:
No concerns for the intended use case (node registration is not ultra-high frequency). 🚀 RecommendationsBefore Merge:
Post-Merge (Future Tickets):
✅ Final VerdictAPPROVE ✅ This PR is production-ready for single-instance deployments and demonstrates:
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
Review conducted following CLAUDE.md guidelines for ONEX infrastructure. |
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.
* 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.
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)
Summary
Implements G4 acceptance criteria tests for effect idempotency, circuit breaker, backoff policy, retry exhaustion, and partial failure scenarios.
Test Coverage (51 tests)
test_effect_idempotency.pytest_effect_circuit_breaker.pytest_effect_retry_backoff.pytest_effect_partial_failure.pyNew Components
Models (
src/omnibase_infra/nodes/effects/models/):ModelBackendResult- Individual backend operation outcomeModelRegistryRequest- Dual-backend registration requestModelRegistryResponse- Response with partial failure support (status: success | partial | failed)Effect Node (
src/omnibase_infra/nodes/effects/):RegistryEffect- Effect node for dual-backend registration executionProtocolConsulClient- Protocol for Consul service registrationProtocolPostgresAdapter- Protocol for PostgreSQL registration persistenceTest plan
Linear
Closes OMN-954
Summary by CodeRabbit
Release Notes
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.