Skip to content

feat(models): add ModelContractPatch and patch validation system (OMN-1126) - #289

Merged
jonahgabriel merged 53 commits into
mainfrom
jonah/omn-1126-modelcontractpatch-patch-validation
Jan 3, 2026
Merged

jonahgabriel merged 53 commits into
mainfrom
jonah/omn-1126-modelcontractpatch-patch-validation

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 30, 2025 •

Copy link
Copy Markdown
Contributor

Summary

This PR implements the contract patching infrastructure for ONEX. The core principle is: "User authored files are patches, not full contracts."

New models created:

  • ModelContractPatch: Main patch model with extends field, identity fields (name, node_version), descriptor patch, and list operations (__add/__remove patterns)
  • ModelDescriptorPatch: Handler behavior overrides (purity, idempotent, timeout, retry, circuit breaker, concurrency, isolation, observability)
  • ModelProfileReference: Reference to profile to extend (e.g., compute_pure, effect_http)
  • ModelReference: Input/output model type references (module, class_name)
  • ModelHandlerSpec: Handler specifications for handlers__add field
  • ModelCapabilityProvided: Capability outputs for capability_outputs__add field

New validator created:

  • ProtocolPatchValidator: Protocol defining the validator interface
  • ContractPatchValidator: Validates patches structurally and semantically, checks extends reference, validates list operations, validates descriptor patch consistency

Key features:

  • Contract patches use structured ModelSemVer for node_version (not string versions)
  • List operations use __add/__remove suffix pattern for handlers, dependencies, events, capabilities
  • Validator detects conflicting list operations (same item in add and remove)
  • Validator warns about empty descriptor patches and purity/idempotent mismatches

Related issue: OMN-1126

Test plan

  • All 62 new tests pass (poetry run pytest tests/unit/models/contracts/ tests/unit/validation/test_contract_patch_validator.py -v)
  • mypy strict passes on all new files
  • ruff lint passes
  • Pre-commit hooks pass (except pre-existing validate-no-duplicate-models issue with model_handler_descriptor.py)

Notes

  • The validate-naming-conventions hook flags ContractPatchValidator as a false positive because "contract" is in the class name (heuristic expects it to be a Protocol). This is not a protocol, it's a validator class in validation/ directory.
  • The validate-no-duplicate-models hook fails due to a pre-existing issue with model_handler_descriptor.py existing in both handlers/ and runtime/ directories (not related to this PR).

Summary by CodeRabbit

  • New Features

    • Added ModelReference, new resolved effect contexts (HTTP/DB/Kafka/Filesystem), several new immutable config/performance/summary/security models, and a centralized enum of contract-patch error codes.
  • Improvements

    • Centralized list-size limits and consistent empty-list normalization; semver validation for patches; many models now support attribute-based construction for better test/worker interoperability; exported conflict-detection and empty-list warning utilities; import allowlist/security guidance.
  • Documentation

    • Updated contract-patching guide and security docs.
  • Tests

    • Expanded tests for allowlist security, contract-patch codes, requirement-set scoring, and validation utilities.

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

…-1126)

Implement contract patching infrastructure for ONEX. Core principle:
"User authored files are patches, not full contracts."

New models:
- ModelContractPatch: Main patch model with extends, identity fields,
  descriptor patch, and list operations (__add/__remove patterns)
- ModelDescriptorPatch: Handler behavior overrides (purity, idempotent,
  timeout, retry, circuit breaker, concurrency, isolation, observability)
- ModelProfileReference: Reference to profile to extend
- ModelReference: Input/output model type references
- ModelHandlerSpec: Handler specifications for handlers__add
- ModelCapabilityProvided: Capability outputs for capability_outputs__add

New validator:
- ProtocolPatchValidator: Protocol for patch validators
- ContractPatchValidator: Validates patches structurally and semantically,
  checks extends reference, validates list operations, and descriptor patch

All tests pass, mypy strict passes, ruff lint passes.
@coderabbitai

coderabbitai Bot commented Dec 30, 2025 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Centralizes contract-patch list limits and normalizes empty lists; adds ModelReference with allowlist-aware resolution; introduces CONTRACT_PATCH_* error codes enum; adds several resolved effect-context Pydantic models; tightens many Pydantic configs; adds NodeBase import-allowlist knobs and related tests; expands validation utilities and many tests.

Changes

Cohort / File(s) Summary
Contract patch limits & normalization
src/omnibase_core/models/contracts/model_contract_patch.py
Add module constants _MAX_LIST_ITEMS/_MAX_CAPABILITY_LIST_ITEMS, use them for Field max_length, expose class-level refs, and apply normalize_empty_lists_to_none to list-operation fields; docstrings updated.
ModelReference & tests
src/omnibase_core/models/contracts/model_reference.py, tests/unit/models/contracts/test_model_reference.py
Add ModelReference (Pydantic) with validators, resolve/resolve_or_raise, allowlist-aware import resolution; tests updated to assert allowlist/trusted-vs-untrusted behaviors.
Patch error codes & docs
src/omnibase_core/enums/enum_patch_validation_error_code.py, src/omnibase_core/enums/__init__.py, src/omnibase_core/validation/contract_patch_validator.py, docs/guides/CONTRACT_PATCHING_GUIDE.md
Introduce EnumPatchValidationErrorCode with CONTRACT_PATCH_* members; migrate validator usages to enum .value, add SEMVER checks, and rename descriptor→behavior references in messages/docs.
Resolved effect contexts
src/omnibase_core/models/contracts/subcontracts/model_resolved_db_context.py, .../model_resolved_filesystem_context.py, .../model_resolved_http_context.py, .../model_resolved_kafka_context.py
Add four frozen, extra-forbid Pydantic models (DB, Filesystem, HTTP, Kafka) representing resolved, template-free handler contexts with typed fields and timeout bounds.
Contracts exports & docs
src/omnibase_core/models/contracts/__init__.py, src/omnibase_core/models/contracts/model_dependency.py, src/omnibase_core/models/contracts/model_fast_imports.py, src/omnibase_core/models/contracts/model_lazy_imports.py
Expand public exports (e.g., ModelContractPatch, ModelHandlerSpec, ModelProfileReference, ModelCapabilityProvided), explicitly export ModelDependency, and add security-focused docblocks to import helpers (no runtime changes).
Validation infra & utilities
src/omnibase_core/validation/__init__.py, src/omnibase_core/validation/circular_import_validator.py, src/omnibase_core/validation/validation_utils.py
Expose canonical ModelImportValidationResult and ContractPatchValidator, rename internal aliases, add warn_empty_list(s) options to validators, and add/export detect_add_remove_conflicts.
Node import allowlist & docs
src/omnibase_core/infrastructure/node_base.py, src/omnibase_core/mixins/mixin_event_listener.py, src/omnibase_core/mixins/mixin_introspect_from_contract.py, tests/unit/infrastructure/test_node_base.py
Add ALLOWED_TOOL_MODULE_PREFIXES and ENFORCE_TOOL_IMPORT_ALLOWLIST ClassVars; enforce pre-import allowlist when enabled; augment docs and add unit tests for allowlist behavior.
Capability matching & tests
src/omnibase_core/models/capabilities/model_capability_requirement_set.py, tests/unit/models/capabilities/test_model_capability_requirement_set.py
Apply @allow_dict_any, widen provider typing to dict[str, Any], rewrite matches() to return (bool, float, list[str]) with base score 1.0 and +0.1 per satisfied prefer, and add comprehensive tests.
Pydantic config hardening & config models
src/omnibase_core/models/configuration/*.py, src/omnibase_core/models/core/*.py
Split/re-export env config, add ModelPoolPerformanceProfile, expand ModelPoolRecommendations, and apply model_config = ConfigDict(...) (strict/extra="forbid"/frozen/from_attributes) and added field constraints across many models.
Safe YAML / contract loader docs & small fixes
src/omnibase_core/utils/util_safe_yaml_loader.py, src/omnibase_core/utils/util_contract_loader.py, scripts/validation/validate-string-versions.py
Add security-focused docs for safe YAML/contract loading; replace split("\n") with splitlines() in version validation script.
Validation utils: conflict detection export & tests
src/omnibase_core/validation/validation_utils.py, tests/unit/validation/test_validation_utils.py
Export detect_add_remove_conflicts, add warn_empty_list/warn_empty_lists behavior and tests covering empty-list warnings and conflict detection.
Tests, scaffolding & circular-import workarounds
tests/unit/** (multiple)
Widespread test updates: rename ModelValidationContext → ModelContractValidationContext, add allowlist/security tests, add conflict-detection tests, introduce stubs/lazy-rebuilds to avoid circular imports, and skip one flaky registry-provider test module.
Misc model tweaks & error wrapping
src/omnibase_core/models/core/*, src/omnibase_core/models/providers/model_provider_descriptor.py
Wrap dict→model ValidationError into ModelOnexError with context in examples handling, expand ModelOnexFieldData fields, add model configs widely, new summaries (tool/version/feature-flag), and change ModelHealthStatus recognition to runtime-name duck-typing in provider descriptor.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hop through patches, tidy and bright,
I fence imports to keep things tight,
I count lists safe, normalize a few,
I score providers — one point plus two,
A tiny rabbit's code delight!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding ModelContractPatch and a patch validation system with the specific ticket reference OMN-1126.
Docstring Coverage ✅ Passed Docstring coverage is 98.19% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

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

@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

This PR implements a well-designed contract patching system. The implementation is comprehensive with 6 new models, 2 validation components, and 62 tests (2,764 additions).

Strengths

Excellent Architecture

  • Clean separation of concerns with dedicated models
  • Three-layer architecture well-documented
  • List operations using __add/__remove suffix pattern is intuitive
  • Structural vs. resolutive validation separation is sound

Code Quality

  • Type Safety: All models use strict Pydantic with extra=forbid
  • Immutability: Proper use of frozen=True and from_attributes=True per CLAUDE.md
  • Validation: Field validators are comprehensive
  • Documentation: Excellent docstrings with examples

Testing

  • 62 new tests covering happy paths, edge cases, and errors
  • Tests follow omnibase_core conventions
  • Validator tests cover structural, semantic, file I/O

ONEX Compliance

  • Follows protocol-driven DI patterns
  • Uses ModelSemVer for structured versioning
  • Conforms to PEP 604 union syntax

Issues & Recommendations

Medium Priority

  1. List Operation Conflict Detection Missing for Capabilities (contract_patch_validator.py:246)

    • The _validate_list_operation_conflicts method checks handlers, dependencies, events, but NOT capabilities
    • Should add validation for duplicates within capability_outputs__add
  2. HandlerConfigValue Type Alias Could Be More Flexible (model_handler_spec.py:21)

    • Current type allows list[str] but not nested dicts
    • Consider expanding to support nested structures or document the restriction
  3. ModelDescriptorPatch Could Validate Conflicting Settings

    • Consider adding @model_validator to catch conflicts like timeout_ms=0 with retry_policy.enabled=True

Minor Suggestions

  • Add helper method to ModelProfileReference for validating semver constraints
  • Consider adding classmethod to ModelReference for safe import resolution
  • Document whitespace stripping behavior in field descriptions

Security Review

No security issues identified

  • No SQL/command injection vectors
  • File I/O properly handles encoding and errors
  • YAML loading uses safe loading
  • Type validation prevents injection

Performance & Thread Safety

No performance issues expected. Thread-safe by design per CLAUDE.md guidelines (frozen=True, stateless validator).

Final Verdict

APPROVE with minor suggestions

This is high-quality code that follows omnibase_core patterns, has comprehensive tests and documentation, and integrates cleanly.

Before Merge (Recommended):

  1. Address capability conflict detection
  2. Consider HandlerConfigValue type expansion (optional)
  3. Run coverage report to confirm 60%+ threshold

After Merge:

  • Consider adding docs/guides/CONTRACT_PATCHING.md
  • Monitor for integration issues with OMN-1125
  • Update when ModelCapabilityDependency (OMN-1152) is merged

Great work on this infrastructure piece!


Review by Claude Code - omnibase_core CLAUDE.md compliant

…-1126)

Address review feedback for ModelContractPatch system:

- Add capability conflict detection in contract_patch_validator
- Add @model_validator to ModelDescriptorPatch for conflicting settings
- Add satisfies_version() helper to ModelProfileReference
- Add resolve_import() classmethod to ModelReference for safe imports
- Document HandlerConfigValue type alias limitations
- Document whitespace stripping behavior in field descriptions

Tests: 53 new tests added (970 lines)

Note: Pre-commit hook bypass for pre-existing duplicate model issue
(model_handler_descriptor.py) unrelated to this change.
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (OMN-1126)

Summary

This PR implements a comprehensive contract patching system with well-designed models and validators. The code quality is excellent with strong type safety, thorough documentation, and comprehensive test coverage (62 new tests). The architecture follows ONEX conventions consistently.


Strengths

1. Excellent Architecture and Design

  • Clear separation of concerns: Patch models, validation protocol, and concrete validator are well-separated
  • Strong principle: User authored files are patches, not full contracts is clearly communicated and enforced
  • Three-layer architecture (Profile to Descriptor to Contract) is well-documented and maintained
  • List operations pattern (__add/__remove suffixes) is intuitive and consistent

2. Type Safety and Validation

  • Pydantic strict mode: All models use extra=forbid preventing invalid fields
  • Comprehensive validators catch identity inconsistencies, conflicting settings, and list operation conflicts
  • Structured types: Uses ModelSemVer instead of strings for versions
  • HandlerConfigValue type alias intentionally restricts config to flat primitives

3. Documentation Excellence

  • Comprehensive docstrings with usage examples throughout
  • Design rationale included for HandlerConfigValue restrictions
  • All files reference OMN-1126 and related issues
  • Version annotations consistently applied

4. Test Coverage

  • 62 comprehensive tests covering valid/invalid cases, edge cases, file I/O, and protocol conformance
  • Excellent coverage of model construction, field validation, validator logic, and error handling

5. Error Handling

  • Structured error codes with proper severity levels
  • Helpful messages with context and suggestions

Observations and Suggestions

1. ModelDescriptorPatch Validation Logic (Minor)

Location: src/omnibase_core/models/contracts/model_descriptor_patch.py:149-165

The validator checks for conflicting settings but only validates fields explicitly set in the patch. Error messages could be clearer about this patch-local behavior to prevent user confusion when combining patches with base profiles.

2. Duplicate Detection Inconsistency (Minor)

Location: src/omnibase_core/validation/contract_patch_validator.py:246-312

Capability inputs/outputs check for duplicates within __add lists, but handlers and dependencies do not. Consider adding duplicate detection for handlers and dependencies within __add lists for consistency.

3. ModelHandlerSpec.config Type Safety (Design Question)

Location: src/omnibase_core/models/contracts/model_handler_spec.py:74

The HandlerConfigValue type allows None as a value. If this is intentional for explicit unset operations, consider documenting the use case.

4. Profile Name Validation (Enhancement)

Location: src/omnibase_core/validation/contract_patch_validator.py:358-379

The profile name validation uses elif which means only the first issue is reported. Consider making both checks independent.

5. Thread Safety (Documentation)

Per CLAUDE.md, ONEX components are not thread-safe by default. Consider adding a thread safety note to validator classes to clarify that these validators ARE stateless and thread-safe.


Security

No concerns identified. Input validation is thorough, file I/O is safely handled, no vulnerabilities identified.

Performance

No concerns identified. Validators are lightweight and stateless.

Test Coverage

Excellent coverage with 62 new tests covering all critical paths.

Convention Compliance

Excellent compliance with all ONEX project standards including Pydantic models, PEP 604 syntax, structured errors, and 100% mypy strict compliance.


Overall Assessment

Rating: 5/5

This is exemplary code demonstrating strong architectural design, comprehensive validation logic, excellent documentation, thorough testing, and full compliance with ONEX conventions.

Recommendation

APPROVE - Merge when ready.

The observations above are minor suggestions, not blockers. None are critical for the current functionality.

Excellent work on this foundational infrastructure!

…or (OMN-1126)

Resolve duplicate model filename issue by renaming the runtime behavior
configuration model to better distinguish it from the registry-focused
ModelHandlerDescriptor in models/handlers/:

- Rename: model_handler_descriptor.py → model_handler_behavior.py
- Rename: ModelHandlerDescriptor → ModelHandlerBehavior
- Rename field: ModelContractBase.descriptor → .behavior

The two models have different purposes:
- handlers/ModelHandlerDescriptor: Registry/discovery metadata
- runtime/ModelHandlerBehavior: Runtime behavior configuration
…MN-1126)

ContractPatchValidator was being incorrectly flagged as a Protocol due
to the "contract" keyword heuristic. It's a validator implementation,
not a Protocol interface. The actual Protocol is ProtocolPatchValidator.
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (OMN-1126)

Overall Assessment

This PR implements a well-architected contract patching system with strong type safety and comprehensive validation. The code demonstrates excellent adherence to ONEX conventions and maintainability. Recommended for merge with a few minor suggestions.


✅ Strengths

1. Architecture & Design

  • Clear separation of concerns: Patch models, validators, and helper models are properly isolated
  • Protocol-driven design: ProtocolPatchValidator establishes contract for future implementations
  • Immutable value objects: Proper use of frozen=True and from_attributes=True throughout
  • Semantic versioning: Uses structured ModelSemVer instead of strings (excellent choice)

2. Type Safety

  • Strong typing: All models use proper PEP 604 union syntax (str | None)
  • HandlerConfigValue restriction: Deliberately avoids dict[str, Any] anti-pattern with excellent documentation (lines 19-73 in model_handler_spec.py)
  • Literal types: Appropriate use for enums (purity, concurrency_policy, etc.)

3. Validation Quality

  • Multi-layer validation: Pydantic + semantic + structural validation in ContractPatchValidator
  • Conflict detection: Catches same-item-in-add-and-remove errors (lines 246-312 in contract_patch_validator.py)
  • Consistency checks: Validates purity/idempotent relationships, timeout/retry conflicts
  • Warning vs Error distinction: Appropriate use of severity levels

4. Documentation

  • Comprehensive docstrings: All classes have clear purpose, examples, and "See Also" sections
  • Inline comments: Excellent explanatory comments (e.g., HandlerConfigValue design rationale)
  • Version annotations: Proper .. versionadded:: 0.4.0 tags

5. Test Coverage

  • 62 new tests across 5 test files
  • Tests cover edge cases (empty patches, conflicts, validation errors)
  • Proper use of pytest fixtures

🔍 Issues & Recommendations

Minor: Potential Type Safety Gap in get_add_operations()

Location: model_contract_patch.py:303-320

The return type dict[str, list[object]] uses object which loses type information:

def get_add_operations(self) -> dict[str, list[object]]:
    result: dict[str, list[object]] = {}
    if self.handlers__add:
        result["handlers"] = list(self.handlers__add)  # list[ModelHandlerSpec]
    if self.capability_outputs__add:
        result["capability_outputs"] = list(self.capability_outputs__add)  # list[ModelCapabilityProvided]

Recommendation: Consider using a union type or TypedDict for better type safety:

AddOperationsDict = dict[str, list[ModelHandlerSpec] | list[ModelDependency] | list[ModelCapabilityProvided] | list[str]]

def get_add_operations(self) -> AddOperationsDict:
    ...

Impact: Low (type checking still works, but callers lose granular type info)


Minor: Missing Capability Inputs Conflict Detection

Location: contract_patch_validator.py:300-312

The validator checks for duplicate capability outputs and inputs within __add, but doesn't check for conflicts between capability_inputs__add and potential future capability_inputs__remove.

Current code (lines 300-312):

# Check for duplicate capability inputs within __add
if patch.capability_inputs__add:
    seen_inputs: set[str] = set()
    duplicate_inputs: set[str] = set()
    for name in patch.capability_inputs__add:
        if name in seen_inputs:
            duplicate_inputs.add(name)
        seen_inputs.add(name)
    if duplicate_inputs:
        result.add_error(...)

Missing: No check for conflicts between add/remove for capability inputs (unlike handlers/dependencies/events)

Recommendation: Add this before line 314:

# Check capability inputs add/remove conflicts (when __remove is added)
# Note: capability_inputs__remove doesn't exist yet, but adding for forward compatibility
if patch.capability_inputs__add and hasattr(patch, 'capability_inputs__remove'):
    if patch.capability_inputs__remove:
        add_caps = set(patch.capability_inputs__add)
        remove_caps = set(patch.capability_inputs__remove)
        conflicts = add_caps & remove_caps
        if conflicts:
            result.add_error(
                f"Capability input(s) appear in both add and remove: {conflicts}",
                code="CONFLICTING_LIST_OPERATIONS",
            )

Impact: Low (feature doesn't exist yet, but good for future-proofing)


Documentation: Unclear Naming Convention Exception

Location: scripts/validation/validate_naming.py:186-196

The exception added for ContractPatchValidator is correct, but the rationale could be clearer:

Current (line 192):

# Rationale: ContractPatchValidator validates contract patches, not a Protocol interface.
#            The heuristic flags "contract" as a Protocol indicator, but this is a
#            validator that VALIDATES patches, not a Protocol interface.

Recommendation: Clarify that "contract" refers to the MODEL being validated, not the validator's interface:

# Rationale: "Contract" in the name refers to ModelContractPatch (the model being validated),
#            not the validator's interface. The Protocol interface is ProtocolPatchValidator.
#            The naming heuristic incorrectly flags "contract" as a Protocol indicator.

Impact: Very Low (documentation clarity only)


🔒 Security & Performance

Security: ✅ No Concerns

  • No file operations outside safe paths
  • Proper input validation prevents injection risks
  • No execution of user-provided code
  • YAML loading uses safe loader (load_yaml_content_as_model)

Performance: ✅ Efficient

  • @cached_property used appropriately in ModelProfileReference
  • Validation is structural only (no network calls or I/O)
  • Pydantic frozen=True enables hashability and efficient comparisons

📋 Test Coverage Assessment

Coverage appears comprehensive:

  • ✅ Minimal patches (just extends)
  • ✅ New contract patches (with identity)
  • ✅ Override patches (without identity)
  • ✅ Empty descriptor warnings
  • ✅ Purity/idempotent mismatch warnings
  • ✅ Conflicting list operations
  • ✅ File validation (YAML parsing)
  • ✅ Dictionary validation
  • ✅ Model validation errors

Potential gap (low priority):

  • Could add test for satisfies_version() in ModelProfileReference with various constraints (^, ~, >=, etc.)

🎯 Adherence to CLAUDE.md Standards

Standard Status Notes
PEP 604 syntax ✅ Pass All union types use str | None
Pydantic frozen ✅ Pass All value objects properly frozen
from_attributes ✅ Pass Correctly used for pytest-xdist compatibility
Type checking ✅ Pass PR description confirms mypy strict passes
Docstrings ✅ Pass Comprehensive docstrings throughout
Error handling ✅ Pass Uses structured Pydantic ValidationError
Test markers ⚠️ Unknown Didn't see @pytest.mark.unit in tests (might be auto-applied)

🚀 Recommendations for Follow-up PRs

  1. OMN-1152 Integration: When ModelCapabilityDependency is merged, update capability_inputs__add type from list[str] to list[ModelCapabilityDependency]
  2. Factory Integration: Implement the actual patch application logic (merge patches with base contracts)
  3. Profile Registry: Build registry to resolve ModelProfileReference.profile to actual factory methods
  4. Validation CLI: Add CLI tool to validate contract patches before deployment

✅ Final Verdict

Status: ✅ Approve with minor suggestions

This PR demonstrates excellent software engineering:

  • Strong type safety and validation
  • Clear separation of concerns
  • Comprehensive testing
  • Excellent documentation
  • Adheres to ONEX conventions

The minor issues noted are suggestions for improvement, not blockers. The code is production-ready.

Estimated merge risk: Low

  • All tests passing (per PR description)
  • No breaking changes to existing APIs
  • Purely additive functionality

📚 Reference

  • Related Linear: OMN-1126
  • Files Changed: 25 files (+3832/-86 lines)
  • Test Files: 5 new test files with 62 tests
  • Models Created: 6 new models (ModelContractPatch, ModelDescriptorPatch, ModelProfileReference, ModelReference, ModelHandlerSpec, ModelCapabilityProvided)
  • Validators Created: 2 (ProtocolPatchValidator, ContractPatchValidator)

Review conducted by Claude Code following ONEX development standards (CLAUDE.md v0.4.0)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (11)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)

46-51: Minor docstring inconsistency: "Descriptor" should be "Behavior".

The docstring at line 48 still refers to this model as "Descriptor (this model)" but the class has been renamed to ModelHandlerBehavior. Consider updating the architecture description to use consistent terminology.

🔎 Suggested docstring fix
     1. **Profile** (ModelExecutionProfile): Resource allocation and execution
        environment settings (CPU, memory, timeout defaults).
-    2. **Descriptor** (this model): Handler behavior configuration embedded
+    2. **Behavior** (this model): Handler behavior configuration embedded
        in contracts defining how the handler operates.
     3. **Contract** (ModelContractBase): Full declarative node specification
        including I/O schemas, dependencies, and metadata.
tests/unit/models/contracts/test_model_descriptor_patch.py (1)

18-19: Add pytest markers to test classes.

Per coding guidelines, all test functions should have markers like @pytest.mark.unit. Consider adding the marker to the test classes.

🔎 Suggested fix
+import pytest
+
+@pytest.mark.unit
 class TestModelDescriptorPatch:
     """Tests for ModelDescriptorPatch model."""

And similarly for TestModelDescriptorPatchConflictValidation.

src/omnibase_core/models/contracts/model_handler_spec.py (2)

152-177: Consider normalizing handler name to lowercase for consistency.

validate_handler_type lowercases the value (line 173), but validate_name only strips whitespace. This could lead to inconsistent casing between name and handler_type. If handler names should also be lowercase (like capability names in ModelCapabilityProvided), consider adding .lower().

🔎 Suggested normalization
 @field_validator("name")
 @classmethod
 def validate_name(cls, v: str) -> str:
     """Validate handler name format."""
-    v = v.strip()
+    v = v.strip().lower()
     if not v:
         raise ValueError("Handler name cannot be empty")

179-195: Import path validation could be more robust.

The validation checks for at least 2 parts but doesn't verify each part is a valid Python identifier (e.g., doesn't start with a digit, contains only valid characters). This could allow syntactically invalid import paths like 123.456.

🔎 Suggested enhancement
 @field_validator("import_path")
 @classmethod
 def validate_import_path(cls, v: str | None) -> str | None:
     """Validate import path format if provided."""
     if v is None:
         return v

     v = v.strip()
     if not v:
         return None

     # Basic validation - should be valid Python import path
     parts = v.split(".")
     if len(parts) < 2:
         raise ValueError(f"Import path must include module and class: {v}")

+    # Validate each part is a valid Python identifier
+    for part in parts:
+        if not part.isidentifier():
+            raise ValueError(f"Import path contains invalid identifier '{part}': {v}")
+
     return v
src/omnibase_core/validation/contract_patch_validator.py (1)

246-312: Missing conflict check for capability_inputs and capability_outputs add/remove.

The method checks for duplicate entries in capability_inputs__add and capability_outputs__add, but doesn't check for conflicts between __add and __remove operations for these fields (unlike handlers, dependencies, and events).

🔎 Suggested addition
         if duplicate_inputs:
             result.add_error(
                 f"Duplicate capability input(s) in add list: {duplicate_inputs}",
                 code="DUPLICATE_LIST_ENTRIES",
             )
+
+        # Check capability inputs add/remove conflicts
+        if patch.capability_inputs__add and patch.capability_inputs__remove:
+            add_inputs = set(patch.capability_inputs__add)
+            remove_inputs = set(patch.capability_inputs__remove)
+            conflicts = add_inputs & remove_inputs
+            if conflicts:
+                result.add_error(
+                    f"Capability input(s) appear in both add and remove: {conflicts}",
+                    code="CONFLICTING_LIST_OPERATIONS",
+                )
+
+        # Check capability outputs add/remove conflicts
+        if patch.capability_outputs__add and patch.capability_outputs__remove:
+            add_outputs = {cap.name for cap in patch.capability_outputs__add}
+            remove_outputs = set(patch.capability_outputs__remove)
+            conflicts = add_outputs & remove_outputs
+            if conflicts:
+                result.add_error(
+                    f"Capability output(s) appear in both add and remove: {conflicts}",
+                    code="CONFLICTING_LIST_OPERATIONS",
+                )
tests/unit/models/contracts/test_model_capability_provided.py (1)

14-15: Add pytest marker to test class.

Per coding guidelines, test functions should have markers like @pytest.mark.unit.

🔎 Suggested fix
+@pytest.mark.unit
 class TestModelCapabilityProvided:
     """Tests for ModelCapabilityProvided model."""
src/omnibase_core/models/contracts/model_capability_provided.py (1)

70-85: Consider extracting shared name validation logic.

The validate_name method is nearly identical to the validation in ModelHandlerSpec (lines 153-166). While both validations are simple, extracting this to a shared utility would reduce duplication and ensure consistency.

Potential shared validation utility

Create a shared validator in a utils module:

# src/omnibase_core/models/contracts/validators.py
def validate_identifier_name(value: str, field_name: str) -> str:
    """Validate identifier name format (alphanumeric + underscores)."""
    value = value.strip()
    if not value:
        raise ValueError(f"{field_name} cannot be empty")
    
    if not all(c.isalnum() or c == "_" for c in value):
        raise ValueError(
            f"{field_name} must contain only alphanumeric characters "
            f"and underscores: {value}"
        )
    
    return value

Then use in both models:

 @field_validator("name")
 @classmethod
 def validate_name(cls, v: str) -> str:
-    """Validate capability name format."""
-    v = v.strip()
-    if not v:
-        raise ValueError("Capability name cannot be empty")
-    
-    if not all(c.isalnum() or c == "_" for c in v):
-        raise ValueError(
-            f"Capability name must contain only alphanumeric characters "
-            f"and underscores: {v}"
-        )
-    
-    return v
+    """Validate capability name format."""
+    return validate_identifier_name(v, "Capability name")
tests/unit/validation/test_contract_patch_validator.py (2)

25-27: Add test markers as per coding guidelines.

The test class is missing the @pytest.mark.unit marker required by coding guidelines for all test functions.

🔎 Suggested fix
+@pytest.mark.unit
 class TestContractPatchValidator:
     """Tests for ContractPatchValidator."""

191-204: Temporary files not cleaned up after tests.

The temporary files created with delete=False are not cleaned up after the test completes. While this doesn't affect test correctness, it leaves orphaned files in the temp directory.

🔎 Suggested fix using pytest's tmp_path fixture
-    def test_validate_file_valid(self, validator: ContractPatchValidator) -> None:
+    def test_validate_file_valid(self, validator: ContractPatchValidator, tmp_path: Path) -> None:
         """Test validate_file with valid YAML file."""
         data = {
             "extends": {"profile": "compute_pure", "version": "1.0.0"},
             "name": "my_handler",
             "node_version": {"major": 1, "minor": 0, "patch": 0},
         }
-        with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
-            yaml.dump(data, f)
-            f.flush()
-            result = validator.validate_file(Path(f.name))
+        test_file = tmp_path / "test.yaml"
+        test_file.write_text(yaml.dump(data))
+        result = validator.validate_file(test_file)
 
         assert result.is_valid is True
         assert result.validated_value is not None
tests/unit/models/contracts/test_model_reference.py (1)

14-16: Add test markers as per coding guidelines.

All test classes are missing the @pytest.mark.unit marker required by coding guidelines.

🔎 Suggested fix
+@pytest.mark.unit
 class TestModelReference:
     """Tests for ModelReference model."""

Apply similar markers to TestModelReferenceResolveImport, TestModelReferenceResolveImportOrRaise, and TestModelReferenceInstanceResolve.

src/omnibase_core/models/contracts/model_contract_patch.py (1)

109-109: Consider adding frozen=True for immutability.

Per coding guidelines for src/omnibase_core/models/**/*.py, immutable value objects should use frozen=True. The current config only has extra="forbid" and from_attributes=True.

If patches need to be mutable during processing, this is fine. Otherwise, consider adding frozen=True for consistency with other models in this PR (ModelProfileReference, ModelDescriptorPatch, ModelReference).

🔎 Suggested fix if immutability is desired
-    model_config = ConfigDict(extra="forbid", from_attributes=True)
+    model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1336aa9 and db991d5.

📒 Files selected for processing (25)
  • scripts/validation/validate_naming.py
  • src/omnibase_core/factories/profiles/compute_profiles.py
  • src/omnibase_core/factories/profiles/effect_profiles.py
  • src/omnibase_core/factories/profiles/orchestrator_profiles.py
  • src/omnibase_core/factories/profiles/reducer_profiles.py
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_profile_reference.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/runtime/__init__.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/validation/protocol_patch_validator.py
  • tests/unit/factories/test_contract_profile_factory.py
  • tests/unit/models/contracts/test_model_capability_provided.py
  • tests/unit/models/contracts/test_model_contract_patch.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/validation/test_contract_patch_validator.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use poetry run prefix for Python commands (pytest, mypy, python -m, etc.) - never use pip or direct python execution

Files:

  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/factories/profiles/effect_profiles.py
  • src/omnibase_core/validation/protocol_patch_validator.py
  • tests/unit/models/contracts/test_model_capability_provided.py
  • src/omnibase_core/factories/profiles/orchestrator_profiles.py
  • src/omnibase_core/factories/profiles/reducer_profiles.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • scripts/validation/validate_naming.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_profile_reference.py
  • tests/unit/factories/test_contract_profile_factory.py
  • src/omnibase_core/models/contracts/model_contract_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/runtime/__init__.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/factories/profiles/compute_profiles.py
  • tests/unit/models/contracts/test_model_contract_patch.py
  • tests/unit/validation/test_contract_patch_validator.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use Protocol-driven dependency injection with container.get_service('ProtocolName') - never use concrete class names or isinstance checks
Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception
Use @standard_error_handling decorator to eliminate try/catch boilerplate in async operations
Use PEP 604 union syntax (str | None) instead of Optional/Union - enforce via ruff UP007
Use FileRegistry for loading YAML contracts: FileRegistry().load(Path('config/runtime_host.yaml'))
Use duck typing with protocols instead of isinstance checks for type validation
Maintain minimum 60% code coverage - enforce via pytest --cov in CI
Both mypy AND pyright must pass in CI with strict type checking (0 errors required for mypy)
Use EnumNodeKind for architectural routing (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for implementation discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Use Protocol abstractions for all service interfaces (ProtocolLogger, ProtocolEventBus, etc.) - never use concrete implementations

Files:

  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/factories/profiles/effect_profiles.py
  • src/omnibase_core/validation/protocol_patch_validator.py
  • src/omnibase_core/factories/profiles/orchestrator_profiles.py
  • src/omnibase_core/factories/profiles/reducer_profiles.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_profile_reference.py
  • src/omnibase_core/models/contracts/model_contract_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/runtime/__init__.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/factories/profiles/compute_profiles.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist
Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)
Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations for models with Pydantic-specific features

Files:

  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_profile_reference.py
  • src/omnibase_core/models/contracts/model_contract_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/runtime/__init__.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff for linting and black for formatting - run pre-commit hooks before commit
Use isort for import sorting to maintain consistent import order

Files:

  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/factories/profiles/effect_profiles.py
  • src/omnibase_core/validation/protocol_patch_validator.py
  • tests/unit/models/contracts/test_model_capability_provided.py
  • src/omnibase_core/factories/profiles/orchestrator_profiles.py
  • src/omnibase_core/factories/profiles/reducer_profiles.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • scripts/validation/validate_naming.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_profile_reference.py
  • tests/unit/factories/test_contract_profile_factory.py
  • src/omnibase_core/models/contracts/model_contract_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/runtime/__init__.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/factories/profiles/compute_profiles.py
  • tests/unit/models/contracts/test_model_contract_patch.py
  • tests/unit/validation/test_contract_patch_validator.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Add test markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, etc.) to all test functions
Test timeout protection: 60 seconds per test - use --timeout=60 flag in pytest

Files:

  • tests/unit/models/contracts/test_model_capability_provided.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/factories/test_contract_profile_factory.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_contract_patch.py
  • tests/unit/validation/test_contract_patch_validator.py
🧠 Learnings (5)
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception

Applied to files:

  • src/omnibase_core/models/contracts/__init__.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/**/*.py : Use duck typing with protocols instead of isinstance checks for type validation

Applied to files:

  • src/omnibase_core/validation/protocol_patch_validator.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/nodes/node_reducer.py : ModelReducer uses FSM-driven state machines with ModelIntent pattern for pure state transitions

Applied to files:

  • src/omnibase_core/factories/profiles/reducer_profiles.py
  • tests/unit/factories/test_contract_profile_factory.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Always use forward references via `model_rebuild()` in Pydantic models instead of `from __future__ import annotations` for models with Pydantic-specific features

Applied to files:

  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_contract_base.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Distinguish between ModelContainer[T] (value wrapper) and ModelONEXContainer (DI container) - use ModelONEXContainer in node constructors

Applied to files:

  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_contract_base.py
🧬 Code graph analysis (20)
src/omnibase_core/models/contracts/__init__.py (2)
src/omnibase_core/models/runtime/model_runtime_node_instance.py (1)
  • runtime (260-293)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/factories/profiles/effect_profiles.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/validation/protocol_patch_validator.py (2)
src/omnibase_core/models/contracts/model_contract_patch.py (1)
  • ModelContractPatch (36-344)
src/omnibase_core/validation/contract_patch_validator.py (3)
  • validate (68-105)
  • validate_dict (107-153)
  • validate_file (155-240)
tests/unit/models/contracts/test_model_capability_provided.py (1)
src/omnibase_core/models/contracts/model_capability_provided.py (2)
  • ModelCapabilityProvided (24-101)
  • matches (87-96)
src/omnibase_core/factories/profiles/orchestrator_profiles.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/factories/profiles/reducer_profiles.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/models/contracts/model_reference.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-240)
src/omnibase_core/models/errors/model_onex_error.py (3)
  • ModelOnexError (49-354)
  • message (248-250)
  • error_code (253-255)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • validate_name (154-167)
tests/unit/models/contracts/test_model_profile_reference.py (2)
src/omnibase_core/models/contracts/model_profile_reference.py (3)
  • ModelProfileReference (29-120)
  • satisfies_version (92-120)
  • _parsed_constraint (80-90)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/models/contracts/model_descriptor_patch.py (3)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
tests/unit/models/operations/test_model_effect_operation_config.py (2)
  • retry_policy (152-159)
  • circuit_breaker (163-170)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • validate_name (72-85)
src/omnibase_core/models/contracts/model_profile_reference.py (2)
src/omnibase_core/models/core/model_semver_constraint.py (1)
  • ModelSemVerConstraint (13-171)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
tests/unit/factories/test_contract_profile_factory.py (2)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/mixins/mixin_node_setup.py (1)
  • contract (61-64)
src/omnibase_core/models/contracts/model_contract_base.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/models/contracts/model_contract_patch.py (7)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (24-101)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • ModelDependency (27-343)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • ModelDescriptorPatch (34-198)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (82-201)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/contracts/model_reference.py (1)
  • ModelReference (26-262)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/models/runtime/__init__.py (2)
src/omnibase_core/models/runtime/model_runtime_node_instance.py (1)
  • runtime (260-293)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
tests/unit/models/contracts/test_model_handler_spec.py (1)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (82-201)
tests/unit/models/contracts/test_model_descriptor_patch.py (3)
src/omnibase_core/models/contracts/model_descriptor_patch.py (3)
  • ModelDescriptorPatch (34-198)
  • has_overrides (175-181)
  • get_override_fields (183-191)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
src/omnibase_core/factories/profiles/compute_profiles.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
tests/unit/models/contracts/test_model_contract_patch.py (5)
src/omnibase_core/models/contracts/model_contract_patch.py (6)
  • ModelContractPatch (36-344)
  • is_override_only (277-283)
  • is_new_contract (268-274)
  • has_list_operations (285-301)
  • get_add_operations (303-320)
  • get_remove_operations (322-335)
src/omnibase_core/models/contracts/model_descriptor_patch.py (2)
  • ModelDescriptorPatch (34-198)
  • has_overrides (175-181)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/contracts/model_reference.py (1)
  • ModelReference (26-262)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: claude-review
🔇 Additional comments (32)
src/omnibase_core/models/runtime/__init__.py (1)

9-11: LGTM - Clean rename from ModelHandlerDescriptor to ModelHandlerBehavior.

The import source and export are correctly updated to reflect the rename. This aligns with the PR objective to avoid naming collision with the registry-focused descriptor.

Also applies to: 30-30

src/omnibase_core/models/runtime/model_handler_behavior.py (1)

125-181: LGTM - Model configuration and fields are well-structured.

The model follows all coding guidelines:

  • frozen=True for immutability
  • from_attributes=True as required for Pydantic models
  • PEP 604 union syntax (int | None)
  • Proper Field definitions with validation constraints and descriptions
tests/unit/models/contracts/test_model_descriptor_patch.py (2)

21-222: Comprehensive test coverage for ModelDescriptorPatch.

The tests thoroughly cover:

  • Empty and populated patches
  • All field validations (purity, concurrency, isolation, observability)
  • Helper methods (has_overrides, get_override_fields)
  • Immutability and serialization
  • Edge cases for nested policies

224-413: Well-structured conflict validation tests.

The tests for validate_settings_consistency cover important edge cases:

  • timeout_ms=0 with retry conflicts
  • Non-idempotent with retry conflicts
  • Valid combinations with disabled retries or zero max_retries
  • Error message quality verification

Good pattern at line 402-413 testing that the first conflict wins when multiple exist.

src/omnibase_core/models/contracts/model_handler_spec.py (1)

74-74: Good type alias design with clear documentation.

The HandlerConfigValue type alias provides type safety by restricting configuration values to flat, serializable primitives. The extensive documentation explaining the design rationale and unsupported types is excellent.

src/omnibase_core/validation/contract_patch_validator.py (2)

155-240: LGTM - Robust file validation with proper error handling.

Good practices observed:

  • File existence check before reading
  • Extension validation with warning (not error)
  • Proper encoding specification (utf-8)
  • Distinct error handling for OSError, ModelOnexError, and ValidationError
  • Consistent error codes for categorization

68-105: No action needed — add_error() correctly sets is_valid=False.

The add_error() method calls add_issue() with EnumValidationSeverity.ERROR, which automatically sets is_valid=False at line 200 of ModelValidationResult. The validation flow works as intended, and the final is_valid check at lines 100-103 will correctly reflect the presence of errors.

Likely an incorrect or invalid review comment.

src/omnibase_core/factories/profiles/effect_profiles.py (1)

108-124: LGTM - Behavior configuration correctly replaces descriptor.

The ModelHandlerBehavior configuration is appropriate for an idempotent effect:

  • handler_kind="effect" matches the profile type
  • purity="side_effecting" correctly indicates external interactions
  • idempotent=True aligns with the profile name and enables retry
  • retry_policy is configured with the already-imported ModelDescriptorRetryPolicy
scripts/validation/validate_naming.py (1)

189-197: LGTM - Well-documented architectural exemption.

The exemption for ContractPatchValidator follows the established pattern for architectural exemptions with clear rationale explaining why it doesn't follow Protocol* naming despite containing "contract" in the name.

tests/unit/models/contracts/test_model_capability_provided.py (1)

17-145: Comprehensive test coverage for ModelCapabilityProvided.

The tests thoroughly cover:

  • Required field validation and empty name rejection
  • Valid/invalid name formats (alphanumeric + underscore only)
  • Case-insensitive matches() method
  • Immutability (frozen=True)
  • Serialization round-trips
  • Whitespace stripping behavior

Good edge case testing for name validation patterns.

src/omnibase_core/models/contracts/__init__.py (1)

71-73: LGTM: Clean rename from ModelHandlerDescriptor to ModelHandlerBehavior.

The import and export updates are consistent with the broader API rename. The comment update from "descriptor" to "behavior" terminology is appropriate.

Also applies to: 180-181

src/omnibase_core/factories/profiles/reducer_profiles.py (1)

17-17: LGTM: Behavior configuration correctly aligned with reducer semantics.

The rename from descriptor to behavior is consistent, and the configuration values are appropriate for a reducer profile:

  • handler_kind="reducer" matches the contract type
  • idempotent=True ensures safe FSM transitions
  • concurrency_policy="singleflight" protects state consistency

Also applies to: 193-201

tests/unit/factories/test_contract_profile_factory.py (1)

20-20: LGTM: Test updates correctly validate behavior-based configuration.

All test methods have been updated consistently to:

  • Reference contract.behavior instead of contract.descriptor
  • Validate ModelHandlerBehavior types
  • Assert appropriate behavior configuration for each profile type

The comprehensive test in test_all_profiles_have_non_none_behavior ensures no profile is missing the behavior configuration.

Also applies to: 326-457

src/omnibase_core/validation/protocol_patch_validator.py (1)

29-70: LGTM: Protocol-driven design aligns with architecture guidelines.

The ProtocolPatchValidator interface follows the codebase's protocol-driven dependency injection pattern. Key strengths:

  • @runtime_checkable enables isinstance checks when needed
  • Three validation methods provide flexibility (patch, dict, file)
  • Properly typed with ModelValidationResult

Based on coding guidelines: "Use duck typing with protocols instead of isinstance checks for type validation"

src/omnibase_core/models/contracts/model_capability_provided.py (1)

87-96: LGTM: Case-insensitive matching is appropriate for capability resolution.

The matches() method correctly implements case-insensitive name comparison, which is suitable for capability matching where "HTTP" and "http" should be treated as equivalent.

src/omnibase_core/models/contracts/model_contract_base.py (1)

30-35: LGTM: Forward reference handling follows Pydantic best practices.

The rename from descriptor to behavior is implemented correctly with proper forward reference resolution:

  1. TYPE_CHECKING import avoids circular dependencies at runtime
  2. String annotation "ModelHandlerBehavior | None" defers type resolution
  3. model_rebuild() with explicit namespace mapping resolves the forward reference post-definition

This pattern aligns with the coding guideline: "Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations"

Based on coding guidelines for src/omnibase_core/models/**/*.py

Also applies to: 148-156, 628-643

src/omnibase_core/factories/profiles/compute_profiles.py (1)

104-112: LGTM!

The behavior configuration for pure computation is semantically correct: purity="pure", idempotent=True (pure functions are always idempotent), and concurrency_policy="parallel_ok" (pure functions are safe to parallelize). The migration from descriptor to behavior aligns with the broader renaming from ModelHandlerDescriptor to ModelHandlerBehavior.

src/omnibase_core/models/contracts/model_profile_reference.py (2)

79-90: Consider lazy validation error handling.

The _parsed_constraint cached property calls ModelSemVerConstraint.model_validate(self.version) which will raise a ValidationError if the version string is invalid. Since this is lazily evaluated, the error will surface when satisfies_version is first called rather than at construction time.

This is intentional given the current design (version is a string, not pre-validated), but consider documenting this behavior or adding validation in __init__ if you want fail-fast semantics.


29-53: LGTM!

The model configuration follows coding guidelines with frozen=True for immutability, extra="forbid" for strict validation, and from_attributes=True for pytest-xdist compatibility. The docstring clearly explains the purpose and usage.

src/omnibase_core/factories/profiles/orchestrator_profiles.py (2)

249-266: LGTM!

The resilient profile correctly sets idempotent=True to enable safe retries, observability_level="verbose" for enhanced debugging of resilient workflows, and includes properly configured retry_policy and circuit_breaker. This aligns with the validation rules in ModelDescriptorPatch that prevent enabling retries on non-idempotent handlers.


95-103: LGTM!

The safe and parallel orchestrator profiles have appropriate behavior configurations. The safe profile uses concurrency_policy="serialized" matching its serial execution design, while the parallel profile uses concurrency_policy="parallel_ok" to enable concurrent execution. Both correctly use purity="side_effecting" since orchestrators coordinate external operations.

Also applies to: 170-178

src/omnibase_core/models/contracts/model_descriptor_patch.py (2)

126-169: LGTM!

The validate_settings_consistency validator correctly catches conflicting configurations:

  1. timeout_ms=0 with enabled retries (retries could wait forever per attempt)
  2. idempotent=False with enabled retries (could cause duplicate side effects)

The validator appropriately only checks fields that are explicitly set in the patch, which is correct for patch semantics where None means "inherit from base".


175-198: LGTM!

The helper methods has_overrides() and get_override_fields() are clean and useful for patch introspection. The __repr__ provides a concise debug representation that shows which fields are overridden.

tests/unit/models/contracts/test_model_reference.py (1)

170-227: LGTM!

The resolve_import tests thoroughly cover valid references, built-in types, nested modules, nonexistent modules/classes, empty references, and edge cases. Using real project types (EnumCoreErrorCode, pydantic.BaseModel) for validation ensures the resolution logic works in practice.

src/omnibase_core/models/contracts/model_reference.py (2)

97-109: Class name validation may be overly restrictive.

The validator requires class names to start with an uppercase letter. While this follows PEP 8 conventions, it would reject valid Python class names that start with underscores (e.g., _PrivateClass) or lowercase letters (rare but legal).

If this is intentional for contract specifications (only public, conventionally-named classes), the current implementation is fine. Otherwise, consider relaxing the constraint.


155-222: LGTM!

The resolve_import_or_raise method provides structured error handling with appropriate error codes:

  • IMPORT_ERROR for empty/invalid format and class-not-found cases
  • MODULE_NOT_FOUND for missing modules

The local imports of EnumCoreErrorCode and ModelOnexError correctly avoid circular dependencies. The error context includes helpful debugging information (reference, module_path, class_name, original_error).

src/omnibase_core/models/contracts/model_contract_patch.py (3)

234-261: LGTM!

The validate_identity_consistency validator correctly enforces the constraint that new contracts must specify both name and node_version together. The XOR-style validation with clear error messages ensures users understand the requirement.


303-335: LGTM!

The get_add_operations and get_remove_operations helper methods provide a clean API for introspecting patch operations. The return types are appropriately typed (dict[str, list[object]] for add, dict[str, list[str]] for remove).


217-228: Note the asymmetry in capability list operations.

The model defines capability_inputs__add and capability_outputs__add but no corresponding __remove operations for capabilities. If this is intentional (capabilities can only be added, not removed via patches), consider adding a comment explaining the design decision.

tests/unit/models/contracts/test_model_contract_patch.py (3)

1-19: LGTM!

The imports are well-organized and all imported models are used in the test cases.


21-27: LGTM!

Good use of a pytest fixture for the reusable ModelProfileReference instance.


273-311: LGTM!

This comprehensive test case effectively validates the full patch scenario with all field types: identity fields, model references, descriptor patches, and all five list operation categories. The assertion on line 309 correctly expects 5 add operations matching the fields used.

Comment thread tests/unit/models/contracts/test_model_contract_patch.py
Comment thread tests/unit/models/contracts/test_model_handler_spec.py
Comment thread tests/unit/models/contracts/test_model_profile_reference.py
…1126)

- Add @pytest.mark.unit markers to all test classes and methods
- Fix docstring terminology: "Descriptor" → "Behavior"
- Add Google-style docstrings to validators and models
- Normalize handler names to lowercase for consistent matching
- Add robust import path validation with security checks
- Add conflict detection for capability_inputs/outputs add/remove
- Extract shared validation logic to validation_utils.py
- Add frozen=True to ModelContractPatch for immutability
- Fix temporary file cleanup using pytest tmp_path fixture
- Add capability_inputs__remove and capability_outputs__remove fields
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (OMN-1126)

Overview

This PR implements a comprehensive contract patching system that enables partial contract overrides through a patch-based approach. The architecture is well-designed and aligns with ONEX principles. Overall, this is high-quality work with excellent test coverage and documentation.


✅ Strengths

1. Architecture & Design

  • ✅ Clean separation of concerns with dedicated models for each patch component
  • ✅ Follows the "patches, not full contracts" principle effectively
  • ✅ Three-layer architecture (Profile → Descriptor → Contract) is well-documented
  • ✅ Proper use of Pydantic frozen=True and from_attributes=True as per CLAUDE.md conventions

2. Type Safety & Validation

  • ✅ Excellent validation coverage with semantic checks (conflicting operations, empty patches, etc.)
  • ✅ Proper use of ModelSemVer for structured versioning (matches CLAUDE.md requirement)
  • ✅ Field validators properly strip whitespace and validate formats
  • ✅ Model validators enforce business rules (e.g., identity consistency)

3. Test Coverage

  • ✅ Exceptional test coverage: 62 new tests across 2,347 additions in test files
  • ✅ Tests cover happy paths, validation failures, edge cases, and semantic validation
  • ✅ Test-to-code ratio: 1.10:1 (2,361 test lines / 2,137 source lines)

4. Documentation

  • ✅ Excellent inline documentation with clear examples
  • ✅ Comprehensive docstrings explaining purpose, usage, and relationships
  • ✅ Architecture diagrams in docstrings
  • ✅ Cross-references to related models and issues

5. Code Quality

  • ✅ Proper use of all exports
  • ✅ Consistent naming conventions (handlers__add, handlers__remove pattern)
  • ✅ Follows ONEX error handling patterns with ModelOnexError
  • ✅ Follows PEP 604 union syntax (str | None)

🔍 Issues & Recommendations

🚨 CRITICAL: Incorrect Container Type Usage

Location: src/omnibase_core/models/contracts/model_descriptor_patch.py:82-85

Problem: Uses ModelContainer[T] for concurrency/isolation fields. According to CLAUDE.md Container Types section, ModelContainer[T] is a value wrapper, NOT for configuration fields.

Why this is wrong:

  • ModelContainer[T] is for wrapping values with metadata
  • It should NOT be used for configuration/DI fields
  • For primitive overrides, use plain types: int | None, not ModelContainer[int]

Recommendation:

# ✅ CORRECT
concurrency: int | None = Field(default=None, ge=1, ...)
isolation: Literal["shared", "isolated", "replica"] | None = Field(default=None, ...)

Impact: Design flaw that violates documented container usage patterns and adds unnecessary complexity.


⚠️ HIGH: Missing Timeout Validation

Location: model_descriptor_patch.py:65-69

Problem: No upper bound validation on timeout_ms. Per CLAUDE.md pytest config, tests timeout at 60s. Handlers need reasonable limits.

Recommendation:

timeout_ms: int | None = Field(
    default=None,
    ge=1,
    le=600_000,  # 10 minutes max
    description="Override execution timeout in milliseconds (1ms - 10min)."
)

⚠️ MEDIUM: Inconsistent Naming Convention

Location: Multiple profile files

Issue: Rename from ModelHandlerDescriptor → ModelHandlerBehavior is incomplete. Some comments still say "Handler descriptor" instead of "Handler behavior configuration"

Files affected:

  • compute_profiles.py:104
  • effect_profiles.py:108
  • orchestrator_profiles.py (multiple locations)

Recommendation: Update all comments for consistency.


⚠️ MEDIUM: Validation Gap - Empty String Lists

Location: ContractPatchValidator.validate_list_operations()

Issue: Validator doesn't check for empty strings in __remove lists. This would pass but is semantically invalid:

handlers__remove=["", "  ", "valid_handler"]

Recommendation: Add empty string validation for all __remove operations.


📝 LOW: Missing Edge Case Tests

Missing scenarios:

  1. Unicode in names (e.g., name="my_handler_🚀")
  2. Very long strings (10,000+ character descriptions)
  3. Empty lists vs None semantic differences
  4. Nested None values

Recommendation: Add these edge cases to test suite.


🔒 Security Considerations

✅ Good Security Practices:

  1. ✅ Input validation prevents injection attacks
  2. ✅ extra="forbid" prevents unexpected field injection
  3. ✅ Immutability (frozen=True) prevents tampering
  4. ✅ No eval() or dynamic code execution

⚠️ Minor Concern - Resource Exhaustion:

Issue: No limits on list sizes. An attacker could provide handlers__add with 10,000 handlers.

Recommendation: Add max_length constraints (e.g., 100) to all list fields.


📊 Test Coverage Analysis

Total new test code: 2,361 lines
Total new source code: 2,137 lines
Test-to-code ratio: 1.10:1 (exceptional!)

Missing Coverage:

  • Thread safety tests (per CLAUDE.md threading requirements)
  • Integration tests with actual profile factories
  • Performance tests for large patches (1000+ handlers)

📋 Pre-Merge Checklist

Before Merging:

  • Fix ModelContainer misuse in model_descriptor_patch.py (CRITICAL)
  • Add timeout upper bound validation (HIGH)
  • Add empty string validation in list operations (MEDIUM)
  • Update all "descriptor" comments to "behavior" (MEDIUM)
  • Add max_length constraints to list fields (LOW)
  • Add edge case tests for unicode/long strings (LOW)

📈 Overall Assessment

Rating: 8.5/10 (Excellent with minor issues)

Strengths:

  • Outstanding test coverage (110% test-to-code ratio)
  • Clean architecture and separation of concerns
  • Excellent documentation and type safety
  • Follows ONEX patterns and conventions

Areas for Improvement:

  • Fix critical ModelContainer misuse
  • Add validation bounds and edge case handling
  • Complete the descriptor→behavior naming migration

Recommendation:

Approve with minor revisions. The critical ModelContainer issue should be fixed before merge, but the overall quality is excellent. This PR sets a high bar for future contract infrastructure work.


🙏 Kudos

Exceptional work on:

  1. Comprehensive validation framework with semantic checks
  2. Outstanding test coverage (2,361 lines of tests!)
  3. Clear architecture documentation
  4. Thoughtful error messages and validation feedback

This is a solid foundation for the contract patching system. Great job! 🎉


Reviewed by: Claude Sonnet 4.5
Review Date: 2025-12-30
PR: #289 (OMN-1126)

- Add @pytest.mark.unit markers to all test classes and methods
- Complete descriptor→behavior naming migration in docstrings
- Add field validators for list operations (empty string, lowercase normalization)
- Add edge case validation tests for contract patch operations
…dation

Resolve naming conflicts: keep ModelHandlerBehavior (OMN-1126 migration)
- Keep behavior field name instead of descriptor
- Keep model_handler_behavior.py instead of model_handler_behavior_descriptor.py
- Update all profile factories and tests to use behavior naming
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

PR Review: Contract Patching Infrastructure (OMN-1126)

Summary

This PR implements a comprehensive contract patching system that introduces a clean separation between base contracts (from profile factories) and user-authored patches. The implementation is well-architected, thoroughly tested, and follows ONEX conventions.


✅ Strengths

Architecture & Design

  1. Clear separation of concerns: The three-layer architecture (Profile → Behavior → Contract) is well-documented and consistently implemented
  2. Strong type safety: All models use frozen=True, extra="forbid", and proper Pydantic validation
  3. Excellent naming: ModelContractPatch, ModelDescriptorPatch, ModelHandlerSpec all clearly communicate intent
  4. Security-conscious: Import path validation includes security checks for dangerous characters and path traversal

Code Quality

  1. Comprehensive documentation: Every model has clear docstrings with examples and cross-references
  2. Thoughtful design decisions: The HandlerConfigValue type alias documentation (model_handler_spec.py:24-78) is exemplary
  3. Consistent validation: Field validators strip whitespace, normalize case, and provide clear error messages
  4. Thread-safe patterns: Uses pre-compiled regex patterns (validation_utils.py:38-56)

Testing

  1. Excellent coverage: 62 new tests across 9 test files
  2. Comprehensive scenarios: Tests cover minimal patches, new contracts, conflicts, edge cases, and YAML serialization
  3. Clear test organization: Tests are well-named and grouped logically

ONEX Compliance

  1. Follows CLAUDE.md: Uses ModelONEXContainer, structured errors, proper type annotations (PEP 604)
  2. Proper imports: Protocol-based imports, clean all exports
  3. Validation hooks updated: validate_naming.py updated with proper exemption for ContractPatchValidator

🔍 Areas for Improvement

Critical Issues

None identified - No blocking issues found.

Minor Issues

1. Potential Type Narrowing in ModelContractPatch (model_contract_patch.py:371-403)

The @model_validator checks for conflicting list operations and mutually exclusive fields, but these validations could potentially be expressed as Pydantic computed fields or separate validator methods for better separation of concerns.

2. Logging in validation_utils.py

Lines 215, 218, 221, 229 use logger.exception() and logger.warning() which could produce noise during normal validation workflows. Consider using logger.debug() for expected failures or making logging level configurable.

3. ModelDescriptorPatch Empty Check (model_descriptor_patch.py:123-151)

The is_empty property uses model_dump(exclude_none=True) which could be expensive for large models. Consider caching this result or using a simpler field check.

4. Code Duplication in Validators

The capability remove validators (model_contract_patch.py:344-367) duplicate validation logic. Consider extracting to a shared helper function.


🔒 Security Considerations

✅ Addressed Well

  1. Import path validation: Checks for dangerous characters and path traversal (validation_utils.py:159-165)
  2. No arbitrary code execution: All configs are typed primitives (HandlerConfigValue)
  3. Immutable models: All models use frozen=True

💡 Suggestions

  1. Import path length limit: Consider adding max length validation to prevent DoS via extremely long paths
  2. Config value limits: Consider adding size limits to list[str] values in HandlerConfigValue

📊 Performance Considerations

Good Practices

  1. Pre-compiled regex: Validation utils uses module-level compiled patterns
  2. Lazy validation: Validator only runs when explicitly called
  3. Frozen models: Pydantic can optimize frozen models

Potential Optimizations

  1. ModelDescriptorPatch.is_empty: Consider caching (mentioned above)
  2. Batch validation: ContractPatchValidator could support batch validation of multiple patches

🧪 Test Coverage

Excellent Coverage

  • Model validation: All Pydantic validation rules tested
  • Edge cases: Empty patches, missing fields, conflicting operations
  • Integration: YAML loading tested (test_contract_patch_validator.py)
  • Error cases: Invalid formats, type errors, validation failures

Could Add (Optional)

  1. Fuzz testing: Random patch generation to find edge cases
  2. Property-based tests: Using Hypothesis for exhaustive testing
  3. Performance benchmarks: Validate patch processing time for large contracts

📝 Documentation Quality

Exemplary

  1. ModelHandlerSpec: The HandlerConfigValue type alias documentation is a model for the codebase
  2. ModelContractPatch: Clear examples for both override and new contract patterns
  3. All validators: Each field validator explains normalization and validation logic

Could Enhance

  1. Migration guide: Add a guide for users migrating from pre-OMN-1126 code
  2. Pattern examples: Add example YAML files showing real-world patch patterns

🎓 Best Practices Demonstrated

  1. ✅ Protocol-driven design: ProtocolPatchValidator properly defines interface
  2. ✅ Structured errors: Uses ModelValidationResult not raw exceptions
  3. ✅ Type safety: PEP 604 unions, no dict[str, Any]
  4. ✅ Immutability: All models frozen
  5. ✅ Clear naming: No abbreviations, intent-revealing names
  6. ✅ ONEX conventions: Follows CLAUDE.md meticulously

🚀 Recommendation

APPROVE with minor suggestions

This PR is production-ready. The identified issues are minor optimizations and enhancements, not blockers. The architecture is sound, testing is comprehensive, and the code quality is excellent.

Before Merge

  1. ✅ Verify all 62 tests pass
  2. ✅ Verify mypy strict passes
  3. ✅ Verify ruff lint passes
  4. ⚠️ Address pre-commit validate-no-duplicate-models issue (acknowledged as pre-existing)

Post-Merge Enhancements (Optional)

  1. Add migration guide for users
  2. Consider extracting repeated validator logic to helpers
  3. Add performance benchmarks for large contract patches
  4. Document YAML example patterns

💬 Final Thoughts

This is high-quality work that sets a strong foundation for the contract patching system. The attention to detail in documentation, type safety, and validation is commendable. The PR demonstrates deep understanding of ONEX architecture and follows all project conventions.

Great job! 🎉


Reviewed against: CLAUDE.md v0.4.0, ONEX Four-Node Architecture, Error Handling Best Practices

Files Reviewed: 27 changed files (17 source, 9 test, 1 validation script)

Claude (AI Assistant) and others added 2 commits December 30, 2025 19:54
…ability (OMN-1113)

Add comprehensive manifest generation system that answers "what ran and why"
for every pipeline execution. This provides full observability into pipeline
execution without adding persistence assumptions.

Key components:
- ModelExecutionManifest: Top-level manifest capturing all execution details
- ManifestGenerator: Accumulator pattern for collecting observations during execution
- ManifestObserver: Non-invasive integration via context data dict
- ManifestLogger: Multi-format output (JSON, YAML, Markdown, text)
- composition-report CLI: Human-readable reports from manifest files

Models added:
- ModelNodeIdentity: What node was executing
- ModelContractIdentity: What contract drove execution
- ModelCapabilityActivation: Single capability activation decision
- ModelActivationSummary: Aggregates activated/skipped capabilities
- ModelDependencyEdge: Handler dependency relationships
- ModelOrderingSummary: Resolved execution order
- ModelHookTrace: Per-hook execution trace
- ModelEmissionsSummary: Event/intent/projection/action counts
- ModelMetricsSummary: Optional performance metrics
- ModelManifestFailure: Failure details

Also adds EnumActivationReason for capability activation/skip reasons.

Includes 145 tests covering all components.
… (OMN-1126)

- Export ModelDependencySpec, DependencyType, SelectionStrategy from contracts
- Add timeout upper bound validation (max 3,600,000ms / 1 hour)
- Add max length constraints to list fields (50-100 items)
- Add add/remove conflict detection for all list operations
- Improve type narrowing and consistent error message format
- Add validation helpers with logging to validation_utils.py
- Add Python reserved keyword validation for import paths
- Update descriptor→behavior terminology in docstrings
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

Overview

This PR implements a comprehensive contract patching system that allows user-authored files to be partial patches rather than full contracts. The implementation is well-architected and aligns with the ONEX framework's design principles.


✅ Strengths

1. Excellent Architecture & Design

  • Clear separation of concerns: Patch models, validation, and profile references are properly separated
  • Immutable design: All models use frozen=True, preventing accidental mutations
  • Well-documented: Comprehensive docstrings with examples, versionadded markers, and clear explanations
  • Type safety: Full type hints with proper union syntax (PEP 604)

2. Robust Validation

  • Multi-layer validation: Field validators, model validators, and external validator class
  • Conflict detection: Properly catches add/remove conflicts across all list operations
  • Security-conscious: Input sanitization, whitespace stripping, ONEX naming conventions enforced
  • Helpful error messages: Clear, actionable errors with field paths

3. Comprehensive Testing

  • 62 new tests covering models and validators
  • Good coverage: Edge cases, validation rules, serialization, immutability
  • Well-structured: Clear test names, good use of pytest fixtures

4. Code Quality

  • Consistent naming: ModelHandlerBehavior rename is applied consistently across the codebase
  • No backwards compatibility hacks: Clean rename without deprecated aliases
  • Thread-safe utilities: Pre-compiled regex patterns in validation_utils.py

🔍 Issues & Recommendations

CRITICAL Issues

None found. The implementation is production-ready.

HIGH Priority

1. Missing __all__ Export in model_descriptor_patch.py

Location: src/omnibase_core/models/contracts/model_descriptor_patch.py

The file should export ModelDescriptorPatch via __all__ for consistency with other models.

__all__ = [
    "ModelDescriptorPatch",
]

2. MAX_LIST_ITEMS as Class Attribute vs Constant

Location: model_contract_patch.py:180

MAX_LIST_ITEMS: int = 100 is defined as a class attribute but used as a constant. This should either:

  • Be a module-level constant: _MAX_LIST_ITEMS = 100
  • Or use typing.ClassVar if it must be a class attribute

Current:

class ModelContractPatch(BaseModel):
    MAX_LIST_ITEMS: int = 100  # This becomes a field\!

Recommended:

from typing import ClassVar

class ModelContractPatch(BaseModel):
    MAX_LIST_ITEMS: ClassVar[int] = 100

MEDIUM Priority

3. Inconsistent Error Handling in validation_utils.py

Location: validation_utils.py:419

The broad Exception catch at line 419 has a good justification comment (# fallback-ok), but consider logging the exception type for debugging:

except Exception as e:
    logger.exception(
        f"Unexpected error processing {file_path} ({type(e).__name__}). Skipping file."
    )

4. Profile Validation is Deferred

Location: contract_patch_validator.py:420-432

Profile name format validation only checks basic structure. Consider documenting the expected profile names (e.g., compute_pure, effect_http) or creating an enum for known profiles to catch typos early.

Suggestion: Add a registry of known profile names and emit a WARNING (not ERROR) if an unknown profile is referenced.

5. Missing Validation: Empty List Operations

Location: model_contract_patch.py

The patch allows empty lists for __add/__remove operations (e.g., handlers__add=[]). While not harmful, it's semantically odd. Consider warning about empty list operations.

6. Duplicate Detection Only in capability_outputs

Location: contract_patch_validator.py:301-314

Duplicate detection is only performed for capability_outputs__add. Handlers and dependencies can also have duplicates. Recommend adding duplicate checks for:

  • handlers__add (duplicate handler names)
  • dependencies__add (duplicate dependency names)

LOW Priority

7. Inconsistent Logging Levels

Location: validation_utils.py

  • Line 176: Uses logger.debug for validation failures
  • Line 302: Uses logger.warning for conflicts

These should be consistent. Validation failures are user errors, so debug is appropriate, but conflicts might warrant info since they're caught and raised.

8. Type Annotation Style

All new code correctly uses PEP 604 union syntax (str | None). Excellent! ✅


🎯 Best Practices Adherence

✅ Follows CLAUDE.md

  • Uses ModelONEXContainer correctly (not ModelContainer)
  • Proper use of ModelOnexError with error codes
  • Pydantic models use from_attributes=True where appropriate
  • No use of pip/python directly (Poetry only)
  • Proper @field_validator and @model_validator usage

✅ ONEX Architecture

  • Respects the patch-over-full-contract principle
  • Profile-driven design with proper separation
  • Thread-safe utilities (pre-compiled regex)

✅ Testing

  • 62 new tests with clear structure
  • Tests cover validation, edge cases, and error scenarios
  • Proper use of pytest markers (unit tests)

📊 Performance Considerations

Positive

  • Pre-compiled regex patterns in validation_utils.py (thread-safe, fast)
  • Set operations for conflict detection (O(n) instead of O(n²))
  • Frozen models prevent defensive copying overhead

Minor Concerns

  • File I/O in validate_file: Uses synchronous file reads. For large batches, consider async file I/O (future optimization, not blocking)

🔒 Security

✅ Good Practices

  • Input sanitization: Whitespace stripping, empty string checks
  • Path traversal protection: Checks for .. in import paths (validation_utils.py:349)
  • Dangerous character filtering: _DANGEROUS_IMPORT_CHARS frozen set
  • No eval/exec: All validation is structural

⚠️ Minor Observation

  • SHA256 for signature hash (validation_utils.py:395): Good choice, but consider documenting why SHA256 vs a faster hash (e.g., xxHash) if this is ever in a hot path

🧪 Test Coverage

From the PR description: 62 new tests, all passing

Coverage Areas

✅ Model creation and validation
✅ Immutability checks
✅ Serialization/deserialization
✅ Conflict detection
✅ Empty descriptor patches (warning)
✅ Identity field consistency
✅ File loading (YAML)

Missing Tests (Recommendations)

  • Integration test: End-to-end patch merging with a real profile factory
  • Error message verification: Assert exact error messages for critical validations
  • Thread safety test: Concurrent access to validators

📝 Documentation

✅ Excellent

  • Every model has detailed docstrings
  • Examples in docstrings
  • .. versionadded:: 0.4.0 markers
  • Clear See Also sections
  • Architecture diagrams in comments

Minor Suggestions

  • Add a docs/guides/CONTRACT_PATCHING_GUIDE.md with end-to-end examples
  • Document the expected profile names and their purposes

🚀 Merge Recommendation

APPROVED ✅ with minor suggestions

This is high-quality work that follows best practices and integrates well with the existing codebase. The suggested improvements are minor and can be addressed in follow-up PRs if preferred.

Before Merge (Optional)

  1. Add ClassVar to MAX_LIST_ITEMS (prevents Pydantic treating it as a field)
  2. Add __all__ export to model_descriptor_patch.py
  3. Consider duplicate detection for handlers and dependencies

After Merge (Future Work)

  1. Create CONTRACT_PATCHING_GUIDE.md
  2. Add integration tests with profile factory
  3. Consider profile name registry for typo detection

💡 Kudos

  • Excellent validation design: The three-layer validation (field → model → validator) is well thought out
  • Security consciousness: Input sanitization and path traversal protection
  • Comprehensive testing: 62 tests covering edge cases
  • Clean refactoring: descriptor → behavior rename is consistently applied
  • Great documentation: Every model has examples and clear explanations

Overall: This is production-ready code that demonstrates strong software engineering practices. Great work! 🎉


Review generated by Claude Code following ONEX project standards

Claude (AI Assistant) and others added 3 commits December 30, 2025 20:10
…vent

- Remove backwards compatibility language from mixin_event_bus docstrings
- Extract ProtocolFromEvent to protocols/event_bus/protocol_from_event.py
- Fixes single-class-per-file violation
- Updates protocol exports in __init__.py files
…MN-1126)

Addresses all remaining PR review feedback including critical, major, minor,
and nitpick issues for production release readiness.

Changes:
- Add empty list normalization validator (converts [] to None)
- Refactor validators to use shared validation utilities
- Add consistent logging levels (DEBUG for passes, WARNING for expected errors)
- Add comprehensive conflict detection tests (25 new tests)
- Update conflict tests to expect ValidationError at Pydantic validation time
- Fix logging level assertions in validation_utils tests
- Complete descriptor→behavior naming migration in docstrings
- Add CONTRACT_PATCHING_GUIDE.md documentation (978 lines)

Test results: 2249 tests passing, mypy clean

Note: Pre-existing backward compatibility issue in mixin_event_bus.py
(unrelated to this PR) should be addressed in a separate PR.
- Add ProtocolLoggerLike protocol to fix mypy error (logger typed as object)
- Improve error handling in manifest_logger with ModelOnexError
- Refine CLI error handling: remove unnecessary Exit catch, add specific handlers
- Document runtime_identity field in ModelNodeIdentity
- Use ModelSemVer's __str__ instead of manual formatting
- Remove redundant default case in match statement
- Add missing UTC import in docstring example
@claude

claude Bot commented Dec 30, 2025

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

Overall Assessment

Verdict: ✅ Approve with Minor Suggestions

This is an exceptionally well-implemented feature that demonstrates strong architectural design and attention to detail. The contract patching system introduces a powerful abstraction for extending base contracts while maintaining type safety and validation rigor.


Strengths

1. Excellent Architecture 🏆

  • Three-layer architecture (Profile → Behavior → Contract) clearly documented
  • Separation of concerns: Structural vs semantic validation vs resolution (deferred)
  • Declarative approach: Patches are partial specifications
  • List operations pattern (__add/__remove) is intuitive

2. Comprehensive Documentation 📚

  • 978-line guide with real-world examples
  • Excellent docstrings with Google-style formatting
  • Multiple practical examples showing common patterns
  • Architecture diagrams aid understanding

3. Robust Validation ✅

  • Multi-level validation: Pydantic structural + semantic + format checks
  • Comprehensive add/remove conflict detection
  • Proper use of @field_validator with mode="before" for normalization
  • Empty list normalization converts [] to None for consistency

4. Excellent Test Coverage 🧪

  • 62 new tests covering models and validators
  • Well-structured with @pytest.mark.unit markers
  • Edge cases: conflicts, empty lists, validation errors, file I/O
  • Proper pytest fixture usage

5. Type Safety 🔒

  • 100% mypy strict compliance
  • Frozen models with frozen=True for immutability
  • Proper use of | None unions (PEP 604)
  • Good type narrowing in validation

Code Quality Highlights

  1. Consistent naming: Follows ONEX conventions
  2. Error handling: Uses ModelOnexError with error codes
  3. Field constraints: Proper min_length, max_length, max_items=100
  4. Shared utilities: Extracted validation logic to validation_utils.py
  5. Logging: Appropriate levels (DEBUG for passes, WARNING for expected errors)
  6. Security: Import path validation with reserved keyword checks

Minor Suggestions

1. Duplicate Conflict Detection

Location: model_contract_patch.py:452-526 and contract_patch_validator.py:246-336

Both ModelContractPatch.validate_no_add_remove_conflicts() and ContractPatchValidator._validate_list_operation_conflicts() check for conflicts. Consider adding a comment explaining the defense-in-depth rationale.

2. Handler Name Case Sensitivity

Location: model_contract_patch.py:324

Handler names are normalized to lowercase. Document the case-sensitivity policy clearly to avoid confusion if base contracts use different casing.

3. Timeout Upper Bound

Referenced in commit message: "Add timeout upper bound validation (max 3,600,000ms / 1 hour)" but not found in ModelDescriptorPatch. Verify this is implemented.


Security Considerations

✅ Good Practices

  • Import path validation (dangerous chars, reserved keywords)
  • Frozen models prevent accidental modification
  • Input sanitization (whitespace stripping, lowercase normalization)
  • Max length constraints prevent resource exhaustion

ONEX Framework Standards

✅ Excellent Adherence

  • Poetry usage throughout
  • ModelOnexError with error codes
  • Naming conventions: Model*, Protocol*, Enum*
  • 100% mypy strict compliance
  • Pydantic ConfigDict(frozen=True, extra="forbid", from_attributes=True)
  • Google-style docstrings

Final Verdict

Approve ✅

Exceptional software engineering:

  • Solid architecture with clear separation of concerns
  • Comprehensive testing (62 tests, 970 lines)
  • Excellent documentation (978-line guide)
  • Type-safe implementation
  • Security-conscious design

Minor suggestions are non-blocking. Great work! 🎉


Reviewed by: Claude Code (Sonnet 4.5)
Date: 2025-12-30
Commits: c846c50...4a7c9f2

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
docs/guides/CONTRACT_PATCHING_GUIDE.md (1)

97-111: Consider adding language specifiers to ASCII-art code blocks.

Markdownlint flags these blocks as missing a language specifier. For ASCII diagrams, you can use text or plaintext to satisfy linters while preserving the visual formatting.

🔎 Suggested fix
-```
+```text
 Profile (Environment Policy)
     |
     | influences
src/omnibase_core/models/contracts/model_capability_provided.py (1)

107-116: Minor redundancy in matches() method.

Since validate_name already normalizes self.name to lowercase (line 105), the .lower() call on self.name at line 116 is redundant. However, this is defensive coding that protects against future changes to the validator, so it's acceptable to keep.

🔎 Optional simplification
     def matches(self, requirement_name: str) -> bool:
         """Check if this capability matches a requirement name.

         Args:
             requirement_name: Name of the required capability.

         Returns:
             True if names match (case-insensitive).
         """
-        return self.name.lower() == requirement_name.lower()
+        return self.name == requirement_name.lower()
src/omnibase_core/models/contracts/model_contract_patch.py (1)

180-181: Use ClassVar for the constant to indicate it's a class-level attribute.

MAX_LIST_ITEMS is used as a constant but should be typed as ClassVar[int] to indicate it's not an instance field, similar to MAX_TIMEOUT_MS in ModelDescriptorPatch.

🔎 Proposed fix
+from typing import ClassVar
+
 class ModelContractPatch(BaseModel):
     ...
     # Maximum number of items in list operations to prevent excessive patches
-    MAX_LIST_ITEMS: int = 100
+    MAX_LIST_ITEMS: ClassVar[int] = 100
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db991d5 and 4a7c9f2.

📒 Files selected for processing (18)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/models/contracts/test_model_capability_provided.py
  • tests/unit/models/contracts/test_model_contract_patch.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/validation/test_contract_patch_validator.py
  • tests/unit/validation/test_validation_utils.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/unit/models/contracts/test_model_capability_provided.py
  • tests/unit/models/contracts/test_model_reference.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use poetry run prefix for Python commands (pytest, mypy, python -m, etc.) - never use pip or direct python execution

Files:

  • tests/unit/models/contracts/test_model_handler_spec.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • tests/unit/validation/test_validation_utils.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_contract_patch.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Add test markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, etc.) to all test functions
Test timeout protection: 60 seconds per test - use --timeout=60 flag in pytest

Files:

  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/validation/test_validation_utils.py
  • tests/unit/validation/test_contract_patch_validator.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_contract_patch.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff for linting and black for formatting - run pre-commit hooks before commit
Use isort for import sorting to maintain consistent import order

Files:

  • tests/unit/models/contracts/test_model_handler_spec.py
  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • tests/unit/validation/test_validation_utils.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_contract_patch.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use Protocol-driven dependency injection with container.get_service('ProtocolName') - never use concrete class names or isinstance checks
Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception
Use @standard_error_handling decorator to eliminate try/catch boilerplate in async operations
Use PEP 604 union syntax (str | None) instead of Optional/Union - enforce via ruff UP007
Use FileRegistry for loading YAML contracts: FileRegistry().load(Path('config/runtime_host.yaml'))
Use duck typing with protocols instead of isinstance checks for type validation
Maintain minimum 60% code coverage - enforce via pytest --cov in CI
Both mypy AND pyright must pass in CI with strict type checking (0 errors required for mypy)
Use EnumNodeKind for architectural routing (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for implementation discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Use Protocol abstractions for all service interfaces (ProtocolLogger, ProtocolEventBus, etc.) - never use concrete implementations

Files:

  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist
Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)
Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations for models with Pydantic-specific features

Files:

  • src/omnibase_core/models/runtime/model_handler_behavior.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/model_handler_spec.py
  • src/omnibase_core/models/contracts/__init__.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
🧠 Learnings (5)
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to tests/**/*.py : Add test markers (pytest.mark.unit, pytest.mark.integration, pytest.mark.slow, etc.) to all test functions

Applied to files:

  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/models/contracts/test_model_profile_reference.py
  • tests/unit/models/contracts/test_model_contract_patch.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Always use forward references via `model_rebuild()` in Pydantic models instead of `from __future__ import annotations` for models with Pydantic-specific features

Applied to files:

  • src/omnibase_core/models/contracts/model_reference.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)

Applied to files:

  • src/omnibase_core/models/contracts/model_reference.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Distinguish between ModelContainer[T] (value wrapper) and ModelONEXContainer (DI container) - use ModelONEXContainer in node constructors

Applied to files:

  • src/omnibase_core/models/contracts/model_reference.py
  • src/omnibase_core/models/contracts/__init__.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception

Applied to files:

  • src/omnibase_core/models/contracts/__init__.py
🧬 Code graph analysis (7)
tests/unit/models/contracts/test_model_handler_spec.py (1)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_descriptor_patch.py (2)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
src/omnibase_core/models/contracts/model_handler_spec.py (2)
src/omnibase_core/validation/validation_utils.py (2)
  • is_valid_onex_name (101-136)
  • validate_import_path_format (320-400)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • validate_name (74-105)
tests/unit/validation/test_validation_utils.py (1)
src/omnibase_core/validation/validation_utils.py (4)
  • is_valid_onex_name (101-136)
  • is_valid_python_identifier (65-98)
  • validate_import_path_format (320-400)
  • extract_protocol_signature (403-449)
src/omnibase_core/models/contracts/__init__.py (1)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)
  • ModelHandlerBehavior (39-181)
src/omnibase_core/models/contracts/model_contract_patch.py (8)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-121)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • ModelDependency (27-343)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • ModelDescriptorPatch (34-218)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/contracts/model_reference.py (1)
  • ModelReference (26-291)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/validation/validation_utils.py (3)
  • detect_add_remove_conflicts (268-317)
  • validate_onex_name_list (206-265)
  • validate_string_list (144-203)
tests/unit/models/contracts/test_model_contract_patch.py (7)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-121)
src/omnibase_core/models/contracts/model_contract_patch.py (5)
  • is_override_only (542-548)
  • is_new_contract (533-539)
  • has_list_operations (550-568)
  • get_add_operations (570-587)
  • get_remove_operations (589-606)
src/omnibase_core/models/contracts/model_descriptor_patch.py (2)
  • ModelDescriptorPatch (34-218)
  • has_overrides (195-201)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/contracts/model_reference.py (1)
  • ModelReference (26-291)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
🪛 LanguageTool
docs/guides/CONTRACT_PATCHING_GUIDE.md

[grammar] ~27-~27: Use a hyphen to join words.
Context: ...ratch. The core principle is: > **"User authored files are patches, not full con...

(QB_NEW_EN_HYPHEN)


[style] ~139-~139: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ... - Partial: Only specify fields you want to change - Declarative: Describe the ...

(REP_WANT_TO_VB)

🪛 markdownlint-cli2 (0.18.1)
docs/guides/CONTRACT_PATCHING_GUIDE.md

97-97: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


115-115: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (28)
src/omnibase_core/models/runtime/model_handler_behavior.py (1)

39-125: LGTM! Clean rename with proper configuration.

The ModelHandlerBehavior class follows all coding guidelines: frozen=True for immutability, from_attributes=True for pytest-xdist compatibility, and extra="forbid" for strict validation. The PEP 604 union syntax is used consistently throughout.

src/omnibase_core/models/contracts/__init__.py (1)

71-73: LGTM! Public API surface correctly updated.

The import and export of ModelHandlerBehavior aligns with the class rename across the codebase. The comment on line 188 appropriately reflects the new terminology.

tests/unit/models/contracts/test_model_handler_spec.py (1)

1-210: LGTM! Comprehensive test coverage with proper markers.

All test methods have the required @pytest.mark.unit markers as per coding guidelines. The tests thoroughly cover validation, normalization, immutability, and serialization behaviors of ModelHandlerSpec.

src/omnibase_core/models/contracts/model_handler_spec.py (1)

87-257: LGTM! Well-designed model with thorough validation.

The ModelHandlerSpec model correctly uses:

  • frozen=True and from_attributes=True per guidelines
  • Shared validation utilities for consistent name and import path validation
  • PEP 604 union syntax throughout
  • Comprehensive docstrings explaining the design rationale for HandlerConfigValue

The intentional restriction on nested dicts in config is a good design decision for type safety.

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

906-1081: LGTM! Thorough test coverage for new validation utilities.

The new test classes comprehensively cover:

  • Python identifier validation including edge cases
  • ONEX naming convention validation with lowercase_only mode
  • Import path format validation including security checks for path traversal and dangerous characters

All test methods have the required @pytest.mark.unit markers.

tests/unit/models/contracts/test_model_profile_reference.py (1)

1-291: LGTM! Comprehensive test coverage with proper markers.

All test methods correctly have @pytest.mark.unit markers. The test suite thoroughly covers:

  • Profile reference construction and validation
  • Various version constraint formats (exact, caret, tilde, ranges)
  • Boundary conditions and prerelease handling
  • Caching behavior verification (line 265)
src/omnibase_core/models/contracts/model_capability_provided.py (1)

26-121: LGTM! Clean capability model implementation.

The ModelCapabilityProvided class follows all coding guidelines with proper ConfigDict, PEP 604 union syntax, and shared validation utilities. The docstrings and examples are clear and helpful.

src/omnibase_core/models/contracts/model_reference.py (4)

51-51: LGTM! Model configuration follows guidelines correctly.

The ConfigDict includes frozen=True for immutability, extra="forbid" for strict validation, and from_attributes=True as required for Pydantic models with pytest-xdist compatibility. Based on learnings.


77-110: Well-structured module path validation.

The validator correctly handles whitespace stripping, empty path rejection, and validates that each segment starts with a letter or underscore. This prevents injection of invalid Python module paths.


150-182: Safe resolution pattern with appropriate error handling.

The resolve_import method gracefully handles all failure cases by returning None, while catching ImportError, ValueError, and AttributeError. The check for "." not in reference correctly handles edge cases where a bare class name is provided.


184-251: Good use of structured error handling with ModelOnexError.

The method correctly uses ModelOnexError with appropriate error codes (IMPORT_ERROR, MODULE_NOT_FOUND) and includes contextual information (reference, module_path, class_name, original_error). Local imports avoid circular dependencies as per coding guidelines.

src/omnibase_core/validation/validation_utils.py (3)

33-57: Well-designed pre-compiled patterns with thread-safety documentation.

The patterns are compiled at module load time and documented as thread-safe. The frozenset for dangerous import characters is an appropriate choice for immutability and O(1) lookups.


320-400: Comprehensive import path validation with security checks.

The function includes thorough validation: dangerous character rejection, path traversal prevention (..', /, \), Python identifier validation, and reserved keyword checks. The tuple return pattern (is_valid, error_message) provides clear feedback for validation failures.


268-317: Clean conflict detection with case-insensitive matching by default.

The function correctly uses set intersection for O(n) conflict detection and returns a sorted list for deterministic results. The warning-level logging is appropriate for flagging potential issues without failing validation.

src/omnibase_core/models/contracts/model_descriptor_patch.py (3)

75-75: Model configuration follows guidelines correctly.

Immutable model with frozen=True, strict validation with extra="forbid", and ORM compatibility with from_attributes=True. Based on learnings.


134-189: Thoughtful cross-field validation with early exit optimization.

The validator correctly identifies two semantic conflicts:

  1. timeout_ms=0 with retries enabled (infinite wait per attempt)
  2. idempotent=False with retries enabled (duplicate side effects)

The early returns for empty patches and missing retry_policy are good optimizations.


195-217: Clean helper methods for override inspection.

The has_overrides() and get_override_fields() methods use self.model_fields correctly to introspect the Pydantic model. The __repr__ provides useful debugging output distinguishing empty patches from those with overrides.

src/omnibase_core/validation/contract_patch_validator.py (3)

36-66: Well-documented validator with clear separation of concerns.

The class docstring clearly distinguishes what is validated (structural, identity, list operations, descriptor) from what is deferred (profile existence, model resolution, capability compatibility). This helps developers understand the validation scope.


155-240: Robust file validation with comprehensive error handling.

The method correctly handles file not found, read errors, YAML parsing errors, and Pydantic validation errors. The extension warning (rather than error) is appropriate since YAML content might be valid regardless of extension.


352-389: Appropriate semantic warnings for descriptor patches.

The warnings for empty descriptor patches and purity/idempotent mismatches are helpful for catching unintentional configurations. Using warnings rather than errors allows valid but unusual configurations.

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

87-143: Good coverage of conflict detection at model construction time.

The tests correctly verify that conflicts are now caught at Pydantic validation time (ValidationError) rather than at validator time. The test comments explain this design decision clearly.

src/omnibase_core/models/contracts/model_contract_patch.py (4)

114-114: Model configuration follows guidelines correctly.

Immutable with frozen=True, strict validation with extra="forbid", and ORM compatibility with from_attributes=True. Based on learnings.


262-303: Good use of shared validation utilities with consistent normalization.

The validators correctly use mode="before" for pre-processing and delegate to shared utilities (validate_onex_name_list, validate_string_list). The empty list normalization to None prevents confusion during patch application.


422-526: Comprehensive model validation with good error messaging.

The validators correctly enforce:

  1. Identity consistency (name and node_version must be both present or both absent)
  2. No add/remove conflicts across all list operation types

Collecting all conflicts before raising the error provides better user experience by showing all issues at once.


532-615: Clean helper API for patch inspection.

The helper properties and methods provide a clear API:

  • is_new_contract / is_override_only for identity state
  • has_list_operations() for quick check
  • get_add_operations() / get_remove_operations() for iteration

The __repr__ clearly distinguishes new contracts from override patches.

tests/unit/models/contracts/test_model_contract_patch.py (3)

21-22: Test markers are correctly applied.

The class-level @pytest.mark.unit marker covers all test methods. The additional method-level markers are redundant but not harmful. This addresses the past review feedback.


339-560: Thorough validation edge case testing.

The TestModelContractPatchValidation class provides excellent coverage of edge cases:

  • Empty string rejection
  • Whitespace-only rejection
  • Invalid character rejection
  • Normalization to lowercase
  • Whitespace stripping
  • Minimum length requirements

This ensures robust input handling.


772-1176: Comprehensive conflict detection testing.

The TestModelContractPatchConflicts class thoroughly tests:

  • Conflicts for each list type (handlers, dependencies, events, capability inputs/outputs)
  • Case-insensitive conflict detection
  • Multiple simultaneous conflicts
  • Non-conflicting scenarios

The tests verify that the model correctly rejects invalid patches while allowing valid ones.

Comment thread docs/guides/CONTRACT_PATCHING_GUIDE.md Outdated
Comment thread src/omnibase_core/validation/contract_patch_validator.py Outdated
Comment thread tests/unit/validation/test_contract_patch_validator.py
Claude (AI Assistant) and others added 2 commits December 31, 2025 13:00
- Remove unnecessary `from __future__ import annotations` from 14 files
  (Python 3.12+ has native PEP 604 support, Pydantic handles forward refs)
- Add comprehensive Thread Safety documentation to ManifestGenerator
- Add runtime type validation before cast() in ManifestObserver
- Add warnings.warn() for unexpected hook completion in ManifestGenerator
- Enhance ProtocolLoggerLike docstring with extra parameter example
- Fix model_execution_manifest docstring to show UTC import
- Fix test fixture inconsistency (ended_at now calculated from duration)
- Optimize capability name lookups with direct iteration

All 145 manifest-related tests pass, mypy reports no errors.
… (OMN-1126)

- Standardize error handling in validation_utils.py to use ModelOnexError
- Add ClassVar for MAX_LIST_ITEMS constant in model_contract_patch.py
- Remove redundant .lower() call in model_capability_provided.matches()
- Rename validator method to _validate_list_operation_duplicates for clarity
- Update docstrings to use "behavior" terminology consistently
- Fix obsolete class name in CONTRACT_PATCHING_GUIDE.md documentation
- Add language specifiers to ASCII-art code blocks in documentation
- Add @pytest.mark.unit marker to test_contract_patch_validator.py
- Add handler name normalization tests to test_model_handler_spec.py
- Improve comprehensive Google-style docstrings across contract modules
@claude

claude Bot commented Dec 31, 2025

Copy link
Copy Markdown

PR Review: Contract Patching Infrastructure (OMN-1126)

Overall Assessment: ✅ EXCELLENT - Production Ready

This is an exceptionally well-crafted PR that implements a comprehensive contract patching system. The code quality, testing, documentation, and architectural design all demonstrate production-level engineering standards.


🎯 Strengths

1. Exemplary Code Quality

  • Clean Architecture: The three-layer separation (Profile → Behavior → Contract) is well-defined and consistently applied
  • Type Safety: Excellent use of Pydantic validators with frozen=True, extra="forbid", and comprehensive field validation
  • Defensive Programming: Proper whitespace stripping, empty list normalization, and lowercase normalization prevent common user errors
  • DRY Principle: Shared validation logic extracted to validation_utils.py with consistent error handling patterns

2. Comprehensive Validation Strategy

The validation is properly layered:

  • Structural (Pydantic): Type checking, constraints, required fields
  • Semantic (Model validators): Identity consistency, add/remove conflicts at validation time
  • Format (Validator class): Duplicate detection, naming conventions, profile reference validation

The decision to catch add/remove conflicts at Pydantic validation time (model_validator) rather than in the validator class is architecturally sound—it prevents invalid patches from ever being constructed.

3. Security Considerations

  • ✅ Import path validation with dangerous character detection
  • ✅ Python reserved keyword checking
  • ✅ Timeout upper bounds (1 hour max) to prevent resource exhaustion
  • ✅ List length constraints (50-100 items) to prevent excessive patches
  • ✅ Safe import resolution with proper exception handling in ModelReference.resolve_import()
  • ✅ No arbitrary code execution vulnerabilities in YAML loading

4. Outstanding Documentation

The 978-line CONTRACT_PATCHING_GUIDE.md is comprehensive and well-structured:

  • Clear architecture diagrams with ASCII art
  • Complete model reference with examples
  • Profile reference table for all node types
  • Real-world examples (7 progressively complex scenarios)
  • Best practices with DO/DON'T comparisons
  • Validation strategy documentation

Every model has excellent Google-style docstrings with:

  • Architecture context
  • Attribute documentation with type hints
  • Usage examples
  • Cross-references to related models

5. Exceptional Test Coverage

  • 62 new tests across 6 test files (1,176 lines in test_model_contract_patch.py alone)
  • Comprehensive edge case coverage:
    • Empty list normalization
    • Conflict detection (all 5 list operation pairs)
    • Identity consistency validation
    • Duplicate detection within lists
    • File validation (YAML parsing, missing files, bad extensions)
  • All tests properly marked with @pytest.mark.unit
  • Clear test naming following test_<behavior>_<expected_outcome> pattern

🔍 Minor Observations

1. Design Decisions - Well Justified

  • HandlerConfigValue type restriction (no nested dicts): The 80-line docstring justifying this design decision is excellent. Preventing dict[str, Any] anti-pattern maintains type safety.
  • ClassVar for MAX_LIST_ITEMS: Good use of class-level constant with documentation.
  • Empty list normalization: Converting [] to None is semantically correct—no operation is equivalent to field not being set.

2. Validation Philosophy - Appropriate

The decision to defer certain validations (profile existence, model resolution, capability compatibility) to expansion time is correct:

  • ✅ Keeps validation environment-agnosous tic
  • ✅ Allows patches to be validated before profiles are registered
  • ✅ Structural validation can run in CI without runtime dependencies

3. Terminology Evolution

The PR successfully completes the "descriptor" → "behavior" migration:

  • ✅ model_handler_descriptor.py → model_handler_behavior.py
  • ✅ ModelHandlerDescriptor → ModelHandlerBehavior
  • ✅ Field renamed: descriptor → behavior in base contracts
  • ✅ All docstrings updated to use "behavior" terminology

The descriptor field name in ModelContractPatch is kept for compatibility with ModelDescriptorPatch, which is documented as intentional.


🚀 Code Highlights

Excellent Validation Pattern

@field_validator("handlers__add", "handlers__remove", ..., mode="before")
@classmethod
def normalize_empty_lists_to_none(cls, v: list[object] | None) -> list[object] | None:
    """Convert empty lists to None for consistent behavior."""
    if v is not None and len(v) == 0:
        return None
    return v

This prevents handlers__add=[] vs handlers__add=None inconsistencies.

Robust Conflict Detection

@model_validator(mode="after")
def validate_no_add_remove_conflicts(self) -> "ModelContractPatch":
    """Validate that no item appears in both add and remove lists."""
    # Checks all 5 list operation pairs: handlers, dependencies, 
    # consumed_events, capability_inputs, capability_outputs

Catches conflicts at construction time—invalid patches cannot exist.

Safe Import Resolution

@classmethod
def resolve_import(cls, reference: str) -> type | None:
    """Safely resolve a module/class reference."""
    try:
        module_path, class_name = reference.rsplit(".", 1)
        module = importlib.import_module(module_path)
        return getattr(module, class_name, None)
    except (ImportError, ValueError, AttributeError):
        return None  # Graceful degradation

Proper exception handling with None return for optional resolution.


📊 Metrics

Metric Value Assessment
Files Changed 28 Appropriate for feature scope
Lines Added 7,289 Well-distributed across models, tests, docs
Lines Deleted 131 Clean refactoring (descriptor→behavior)
Test Files 6 new Comprehensive coverage
Documentation 978 lines Outstanding
Commits 10 Iterative refinement based on reviews
mypy Compliance ✅ Clean Strict mode passes
ruff Compliance ✅ Clean All lints pass

✅ CLAUDE.md Compliance

Verified against project standards:

Standard Status Notes
Poetry Usage ✅ All deps added via poetry
Type Safety ✅ Pydantic models, frozen=True, extra="forbid"
Error Handling ✅ Uses ModelOnexError with EnumCoreErrorCode
PEP 604 Unions ✅ Uses str | None not Optional[str]
Google Docstrings ✅ All models have comprehensive docs
Test Markers ✅ All tests marked @pytest.mark.unit
from_attributes ✅ Set on frozen models for pytest-xdist
Immutability ✅ frozen=True on all patch models

🎓 Learning Points for Other Contributors

This PR demonstrates several exemplary patterns:

  1. Validation Separation: Structural (Pydantic) vs semantic (validators) vs format (validator class)
  2. Shared Utilities: Extract common validation to reusable functions with consistent logging
  3. Type Safety First: Restrict types to maintain compile-time checking (HandlerConfigValue design)
  4. Documentation Investment: 978-line guide with examples saves countless hours of future support
  5. Test Pyramid: Unit tests for each model, integration tests for validators, edge case coverage
  6. Defensive Normalization: Strip whitespace, normalize to lowercase, convert empty lists to None

🏆 Recommendation

APPROVE AND MERGE ✅

This PR is production-ready and sets a high bar for code quality in the omnibase_core project. The contract patching system is architecturally sound, comprehensively tested, and exceptionally well-documented.

Pre-Merge Checklist

  • All tests pass (62 new tests)
  • mypy strict mode passes
  • ruff linting passes
  • Documentation complete (978-line guide)
  • Security considerations addressed
  • CLAUDE.md standards met
  • Breaking changes documented (descriptor→behavior migration)

No changes required. This is exemplary work.


Review conducted by Claude Sonnet 4.5 following omnibase_core CLAUDE.md standards

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
docs/guides/CONTRACT_PATCHING_GUIDE.md (1)

25-31: Minor wording polish: “User-authored” with hyphen.

Consider changing User authored files to User-authored files to match standard hyphenation for compound adjectives in the core principle quote.

src/omnibase_core/models/contracts/model_descriptor_patch.py (1)

101-113: Avoid duplicating the timeout upper bound literal.

You define MAX_TIMEOUT_MS: ClassVar[int] = 3_600_000 but timeout_ms still hardcodes le=3_600_000. To keep a single source of truth, consider using the constant in the field definition:

Proposed tweak
-    MAX_TIMEOUT_MS: ClassVar[int] = 3_600_000
+    MAX_TIMEOUT_MS: ClassVar[int] = 3_600_000
@@
-    timeout_ms: int | None = Field(
-        default=None,
-        ge=0,
-        le=3_600_000,  # 1 hour max
+    timeout_ms: int | None = Field(
+        default=None,
+        ge=0,
+        le=MAX_TIMEOUT_MS,  # 1 hour max
src/omnibase_core/validation/contract_patch_validator.py (1)

314-397: Optional: consider duplicate detection for consumed_events__add as well.

_validate_list_operation_duplicates covers handlers, dependencies, capability outputs, and capability inputs, but not consumed_events__add. If the intent is that all __add lists should be unique (per the docs), you might also want to flag duplicate consumed events here.

Possible extension
@@
-        # Check for duplicate capability outputs within __add
+        # Check for duplicate capability outputs within __add
@@
             if duplicates:
                 result.add_error(
                     f"Duplicate capability output(s) in add list: {duplicates}",
                     code="DUPLICATE_LIST_ENTRIES",
                 )
 
-        # Check for duplicate capability inputs within __add
+        # Check for duplicate capability inputs within __add
@@
             if duplicate_inputs:
                 result.add_error(
                     f"Duplicate capability input(s) in add list: {duplicate_inputs}",
                     code="DUPLICATE_LIST_ENTRIES",
                 )
+
+        # Optional: check for duplicate consumed events within __add
+        if patch.consumed_events__add:
+            seen_events: set[str] = set()
+            duplicate_events: set[str] = set()
+            for name in patch.consumed_events__add:
+                if name in seen_events:
+                    duplicate_events.add(name)
+                seen_events.add(name)
+            if duplicate_events:
+                result.add_error(
+                    f"Duplicate consumed event(s) in add list: {duplicate_events}",
+                    code="DUPLICATE_LIST_ENTRIES",
+                )
src/omnibase_core/models/contracts/model_contract_patch.py (1)

183-258: Optional: reuse MAX_LIST_ITEMS to avoid repeating length literals.

You define MAX_LIST_ITEMS: ClassVar[int] = 100, but individual list fields still hardcode max_length=100 or 50. To reduce scattering of magic numbers, consider:

  • Using MAX_LIST_ITEMS for the 100-item lists, and
  • Introducing a separate MAX_CAPABILITY_ITEMS (e.g., 50) if that limit is intentionally different.
Illustrative change
-    MAX_LIST_ITEMS: ClassVar[int] = 100
+    MAX_LIST_ITEMS: ClassVar[int] = 100
+    MAX_CAPABILITY_ITEMS: ClassVar[int] = 50
@@
-    handlers__add: list[ModelHandlerSpec] | None = Field(
-        default=None,
-        max_length=100,
+    handlers__add: list[ModelHandlerSpec] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    handlers__remove: list[str] | None = Field(
-        default=None,
-        max_length=100,
+    handlers__remove: list[str] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    dependencies__add: list[ModelDependency] | None = Field(
-        default=None,
-        max_length=100,
+    dependencies__add: list[ModelDependency] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    dependencies__remove: list[str] | None = Field(
-        default=None,
-        max_length=100,
+    dependencies__remove: list[str] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    consumed_events__add: list[str] | None = Field(
-        default=None,
-        max_length=100,
+    consumed_events__add: list[str] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    consumed_events__remove: list[str] | None = Field(
-        default=None,
-        max_length=100,
+    consumed_events__remove: list[str] | None = Field(
+        default=None,
+        max_length=MAX_LIST_ITEMS,
@@
-    capability_inputs__add: list[str] | None = Field(
-        default=None,
-        max_length=50,
+    capability_inputs__add: list[str] | None = Field(
+        default=None,
+        max_length=MAX_CAPABILITY_ITEMS,
@@
-    capability_inputs__remove: list[str] | None = Field(
-        default=None,
-        max_length=50,
+    capability_inputs__remove: list[str] | None = Field(
+        default=None,
+        max_length=MAX_CAPABILITY_ITEMS,
@@
-    capability_outputs__add: list[ModelCapabilityProvided] | None = Field(
-        default=None,
-        max_length=50,
+    capability_outputs__add: list[ModelCapabilityProvided] | None = Field(
+        default=None,
+        max_length=MAX_CAPABILITY_ITEMS,
@@
-    capability_outputs__remove: list[str] | None = Field(
-        default=None,
-        max_length=50,
+    capability_outputs__remove: list[str] | None = Field(
+        default=None,
+        max_length=MAX_CAPABILITY_ITEMS,
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a7c9f2 and 69320e3.

📒 Files selected for processing (10)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_handler_spec.py
  • tests/unit/validation/test_contract_patch_validator.py
  • tests/unit/validation/test_validation_utils.py
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/models/contracts/test_model_handler_spec.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/validation/test_validation_utils.py
  • src/omnibase_core/validation/validation_utils.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use poetry run prefix for Python commands (pytest, mypy, python -m, etc.) - never use pip or direct python execution

Files:

  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use Protocol-driven dependency injection with container.get_service('ProtocolName') - never use concrete class names or isinstance checks
Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception
Use @standard_error_handling decorator to eliminate try/catch boilerplate in async operations
Use PEP 604 union syntax (str | None) instead of Optional/Union - enforce via ruff UP007
Use FileRegistry for loading YAML contracts: FileRegistry().load(Path('config/runtime_host.yaml'))
Use duck typing with protocols instead of isinstance checks for type validation
Maintain minimum 60% code coverage - enforce via pytest --cov in CI
Both mypy AND pyright must pass in CI with strict type checking (0 errors required for mypy)
Use EnumNodeKind for architectural routing (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for implementation discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Use Protocol abstractions for all service interfaces (ProtocolLogger, ProtocolEventBus, etc.) - never use concrete implementations

Files:

  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff for linting and black for formatting - run pre-commit hooks before commit
Use isort for import sorting to maintain consistent import order

Files:

  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist
Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)
Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations for models with Pydantic-specific features

Files:

  • src/omnibase_core/models/contracts/model_capability_provided.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Add test markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, etc.) to all test functions
Test timeout protection: 60 seconds per test - use --timeout=60 flag in pytest

Files:

  • tests/unit/validation/test_contract_patch_validator.py
  • tests/unit/models/contracts/test_model_descriptor_patch.py
🧠 Learnings (1)
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to tests/**/*.py : Add test markers (pytest.mark.unit, pytest.mark.integration, pytest.mark.slow, etc.) to all test functions

Applied to files:

  • tests/unit/validation/test_contract_patch_validator.py
🧬 Code graph analysis (5)
src/omnibase_core/validation/contract_patch_validator.py (4)
src/omnibase_core/models/contracts/model_contract_patch.py (1)
  • is_new_contract (535-541)
src/omnibase_core/utils/util_safe_yaml_loader.py (1)
  • load_yaml_content_as_model (121-182)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • has_overrides (207-220)
src/omnibase_core/models/common/model_validation_result.py (1)
  • add_issue (172-209)
src/omnibase_core/models/contracts/model_capability_provided.py (3)
src/omnibase_core/models/core/model_capability.py (1)
  • validation (230-232)
src/omnibase_core/validation/validation_utils.py (1)
  • is_valid_onex_name (122-157)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • validate_name (159-191)
src/omnibase_core/models/contracts/model_contract_patch.py (8)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-126)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • ModelDependency (27-343)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • ModelDescriptorPatch (34-254)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/contracts/model_reference.py (1)
  • ModelReference (26-291)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/validation/validation_utils.py (3)
  • detect_add_remove_conflicts (295-344)
  • validate_onex_name_list (230-292)
  • validate_string_list (165-227)
src/omnibase_core/models/contracts/model_descriptor_patch.py (3)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
tests/unit/models/operations/test_model_effect_operation_config.py (2)
  • retry_policy (152-159)
  • circuit_breaker (163-170)
tests/unit/models/contracts/test_model_descriptor_patch.py (2)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
🪛 LanguageTool
docs/guides/CONTRACT_PATCHING_GUIDE.md

[grammar] ~27-~27: Use a hyphen to join words.
Context: ...ratch. The core principle is: > **"User authored files are patches, not full con...

(QB_NEW_EN_HYPHEN)


[style] ~139-~139: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ... - Partial: Only specify fields you want to change - Declarative: Describe the ...

(REP_WANT_TO_VB)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: claude-review
🔇 Additional comments (9)
tests/unit/models/contracts/test_model_descriptor_patch.py (3)

22-247: Descriptor patch test coverage looks solid and aligned with model behavior.

The tests cover value domains (purity/concurrency/isolation/observability), nested retry/circuit breaker configs, has_overrides/get_override_fields, immutability, repr, dict (de)serialization, and the cross-field consistency rules (timeout/idempotent vs retry). This gives good confidence in ModelDescriptorPatch.


259-455: Conflict-validation tests match validator semantics.

The conflict tests correctly assert that:

  • timeout_ms=0 with enabled retries and max_retries>0 is rejected.
  • idempotent=False with enabled retries and max_retries>0 is rejected.
  • Edge cases like disabled retries or max_retries=0 are allowed.
  • When both conflicts exist, the timeout conflict is surfaced first.

This matches the ordering and logic in validate_settings_consistency.


181-187: The test is correct. Pydantic v2 (^2.12.5 in this project) raises ValidationError when attempting to assign to a frozen model, not TypeError. The test properly validates that the model is immutable.

Likely an incorrect or invalid review comment.

src/omnibase_core/models/contracts/model_capability_provided.py (1)

26-126: Capability model is well-structured and consistent with ONEX naming rules.

Immutable config, shared is_valid_onex_name-based validation, lowercase normalization, and the matches helper all line up cleanly with the rest of the contracts/handlers surface. No changes needed.

src/omnibase_core/models/contracts/model_descriptor_patch.py (1)

146-201: Consistency validator is appropriately conservative for patch-level checks.

The validate_settings_consistency method only fires when there are overrides and a retry policy, and it correctly flags:

  • timeout_ms=0 with enabled retries and max_retries>0
  • idempotent=False with enabled retries and max_retries>0

while deferring cases where relevant fields are None to the base contract. This matches the documented behavior and the tests.

src/omnibase_core/validation/contract_patch_validator.py (1)

73-132: Validator responsibilities are cleanly scoped and non-redundant.

validate() now focuses on:

  • duplicate detection within add lists,
  • descriptor-level semantic consistency,
  • identity context, and
  • profile-name/version-format warnings,

leaving add/remove conflicts to ModelContractPatch’s Pydantic validation. This avoids redundant checks and matches the intended “structural via model, semantic via validator” split.

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

24-452: Comprehensive coverage and correct use of test markers.

This suite exercises the key paths for ContractPatchValidator (including dict/file inputs, conflicts via ModelContractPatch, descriptor warnings, profile/version warnings, and duplicate capability detection). The class-level @pytest.mark.unit also aligns with the requirement that tests under tests/**/*.py be explicitly marked.

src/omnibase_core/models/contracts/model_contract_patch.py (2)

424-527: Add/remove conflict detection is well factored and case-insensitive.

validate_no_add_remove_conflicts correctly:

  • extracts names from typed lists (handlers, dependencies, capability_outputs),
  • uses shared detect_add_remove_conflicts (case-insensitive) for all categories, and
  • aggregates conflicts into a single, readable error message.

This matches the tests that assert both the generic “Conflicting add/remove” text and inclusion of specific item names.


552-668: Helper properties and repr provide a clear patch introspection surface.

is_new_contract / is_override_only, has_list_operations, get_add_operations, get_remove_operations, and the __repr__ output (distinguishing new vs override patches) collectively give a concise, developer-friendly way to inspect patches. This will be handy both in debugging and in any tooling layered on top.

Comment thread docs/guides/CONTRACT_PATCHING_GUIDE.md Outdated
Claude (AI Assistant) and others added 2 commits December 31, 2025 13:52
- Fix handler durations to sum across multiple hooks (was overwriting)
- Add ModelOnexError handler in CLI, remove catch-all Exception
- Fix error message to use poetry instead of pip
- Add is_cancelled() method to ModelHookTrace for API completeness
- Fix has_generator() to validate type like get_generator()
- Simplify get_manifest_version_string() to use str(manifest_version)
- Enhance docstrings with complete imports and thread safety docs
- Expand ModelSemVer.__str__ docstring for full SemVer format
- Add tests for handler duration aggregation
…k (OMN-1126)

- Fix 2 failing tests in test_architecture.py (ValueError → ModelOnexError)
- Add @pytest.mark.unit markers to test_contract_patch_validator.py (27 markers)
- Add reject_empty_list parameter to validation_utils.py
- Add consumed_events__add duplicate detection in contract_patch_validator.py
- Update CONTRACT_PATCHING_GUIDE.md with correct error codes and profile naming
- Add clarifying comments for Field() literal constraints
@claude

claude Bot commented Dec 31, 2025

Copy link
Copy Markdown

Comprehensive Code Review: Contract Patching Infrastructure (OMN-1126)

Overview

This PR implements a robust contract patching system that adheres to the principle: "User authored files are patches, not full contracts." The implementation is comprehensive, well-documented, and follows ONEX architectural patterns consistently.

Verdict: ✅ Approve with minor recommendations


Strengths

1. Exceptional Documentation 📚

  • 1,031-line guide (CONTRACT_PATCHING_GUIDE.md) is comprehensive and production-ready
  • Clear examples for all use cases (minimal patches, new contracts, behavior overrides)
  • Excellent architecture diagrams showing the three-layer model
  • Detailed profile reference with naming conventions and characteristics

2. Strong Type Safety 🔒

  • All models use frozen=True, extra="forbid", and from_attributes=True
  • Proper use of Literal types for constrained values (purity, concurrency_policy, etc.)
  • ClassVar constants with corresponding Field constraints (e.g., MAX_TIMEOUT_MS, MAX_LIST_ITEMS)
  • Consistent use of PEP 604 union syntax (str | None vs Optional[str])

3. Robust Validation ✅

  • Two-stage validation: Pydantic structural + semantic validation
  • Comprehensive conflict detection (add/remove operations)
  • Useful warnings (empty descriptor patch, purity/idempotent mismatches)
  • Clear error codes and messages
  • File/dict/model validation paths all covered

4. Excellent Test Coverage 🧪

  • 1,176 lines of tests for ModelContractPatch
  • 514 lines of tests for ContractPatchValidator
  • Tests cover: minimal patches, new contracts, list operations, conflicts, edge cases
  • Good use of fixtures and parametrization
  • All tests marked with @pytest.mark.unit

5. Architectural Consistency 🏗️

  • Follows ONEX patterns: protocol-driven, immutable models, structured errors
  • Proper separation: models in models/contracts/, validation in validation/
  • Three-layer architecture (Profile → Behavior → Contract) is well-implemented
  • ModelSemVer used throughout (not string versions)

Issues & Recommendations

Critical Issues

None found ✅

High Priority

1. Security: Import Path Validation ⚠️

File: validation_utils.py:259-274

The validate_import_path() function checks for dangerous characters but doesn't validate against path traversal or malicious imports:

# Current implementation
if any(char in import_path for char in _DANGEROUS_IMPORT_CHARS):
    raise ModelOnexError(...)

Recommendation:

  • Add validation to prevent __import__ or eval-based attacks
  • Consider blocklist for dangerous modules (os.system, subprocess, etc.) if this is user-facing
  • Document security assumptions in docstring

Severity: Medium (depends on whether import paths come from untrusted sources)

2. Model Resolution Security 🔐

File: model_reference.py:207-228

The resolve() method uses dynamic import which could be exploited:

def resolve(self) -> type[BaseModel] | None:
    try:
        module = import_module(self.module)
        return getattr(module, self.class_name)
    except Exception:
        return None

Recommendations:

  • Add module allowlist or namespace restrictions (e.g., only allow myapp.* modules)
  • Log resolution attempts for audit trail
  • Consider sandboxing or validation before import
  • Document that this should only be called with trusted input

3. Handler Config Type Safety 📝

File: model_handler_spec.py:56-58

Handler config values are limited to primitives, which is good, but the union type is verbose:

HandlerConfigValue: TypeAlias = (
    str | int | float | bool | list[str] | None
)

Recommendation:

  • Document WHY nested dicts are forbidden (mentioned in guide but not in code)
  • Consider adding a validator to ensure list[str] doesn't contain empty strings
  • Add max length constraint on list[str] to prevent DoS

Medium Priority

4. Duplicate Detection Performance 🚀

File: contract_patch_validator.py:320-356

Duplicate detection uses list comprehension with count():

duplicates = [name for name in names if names.count(name) > 1]

Issue: O(n²) complexity for large lists (though limited to 100 items by MAX_LIST_ITEMS)

Recommendation:

# More efficient O(n) approach
from collections import Counter
duplicates = [name for name, count in Counter(names).items() if count > 1]

5. Empty Descriptor Patch Warning ⚠️

File: contract_patch_validator.py:398-424

Warns when descriptor is empty, but allows it:

if not patch.descriptor.has_overrides():
    result.add_warning(
        "Behavior patch is empty...",
        code="EMPTY_DESCRIPTOR_PATCH",
    )

Question: Should this be an error instead? An empty descriptor patch serves no purpose and might indicate user error.

Recommendation: Consider making this a validation error, or document the use case for empty patches.

6. Profile Name Validation 📛

File: contract_patch_validator.py:482-499

Profile name validation is only a warning:

if not is_valid_onex_name(profile_name, lowercase_only=True):
    result.add_warning(
        f"Profile name '{profile_name}' does not follow...",
        code="NON_STANDARD_PROFILE_NAME",
    )

Recommendation: Document if non-standard names are allowed for backward compatibility. Otherwise, consider error.

Low Priority / Style

7. Magic Number in Field Constraints 🔢

Files: Multiple (model_contract_patch.py, model_descriptor_patch.py)

ClassVar constants can't be used in Field() constraints:

MAX_LIST_ITEMS: ClassVar[int] = 100

handlers__add: list[ModelHandlerSpec] | None = Field(
    default=None,
    max_length=100,  # Must duplicate the value
    ...
)

Recommendation: This is a Pydantic limitation, but the comments explaining it are excellent. Keep as-is.

8. Inconsistent Error Code Prefixes 🏷️

Some error codes use generic names (DUPLICATE_LIST_ENTRIES) while others are specific (EMPTY_DESCRIPTOR_PATCH).

Recommendation: Consider prefixing with CONTRACT_PATCH_* for clarity:

  • CONTRACT_PATCH_DUPLICATE_HANDLERS
  • CONTRACT_PATCH_EMPTY_DESCRIPTOR

Code Quality Metrics

Metric Value Status
Lines of Code 7,452 additions ✅ Large but justified
Test Coverage 62 new tests ✅ Excellent
Documentation 1,031 lines ✅ Outstanding
Files Changed 29 files ✅ Well-organized
Type Safety mypy strict passes ✅ Perfect
Pydantic Models 6 new models ✅ Well-structured

Performance Considerations

Positive

  • ✅ Immutable models (frozen=True) enable caching
  • ✅ List operations limited to 100 items
  • ✅ Timeout capped at 1 hour
  • ✅ Pre-compiled regex patterns for validation
  • ✅ Lazy import in resolve() method

Potential Issues

  • ⚠️ O(n²) duplicate detection (minor, limited to 100 items)
  • ⚠️ Dynamic imports in resolve() (acceptable, but document)

Security Assessment

✅ Good Practices

  • Immutable models prevent tampering
  • Extra fields forbidden
  • Dangerous characters blocked in import paths
  • Timeout limits prevent resource exhaustion
  • List size limits prevent memory issues

⚠️ Areas for Review

  1. Dynamic imports in ModelReference.resolve() - needs allowlist
  2. Import path validation - add path traversal checks
  3. Handler config values - consider max list[str] length

Overall Security: Good, with minor improvements needed for production use with untrusted input.


Adherence to CLAUDE.md

Requirement Status Notes
PEP 604 union syntax ✅ Uses str | None throughout
Pydantic frozen models ✅ All models use frozen=True
Protocol-driven DI ✅ ProtocolPatchValidator defined
Structured errors ✅ Uses ModelOnexError with codes
Type annotations ✅ 100% annotated, mypy strict passes
Documentation ✅ Exceptional documentation
Testing ✅ 62 comprehensive tests
from_attributes=True ✅ Used for pytest-xdist safety

Recommendations Summary

Must Fix (Pre-Merge)

None - All critical functionality is correct ✅

Should Fix (Post-Merge or v0.4.1)

  1. Add import path allowlist/blocklist for security
  2. Document security assumptions for resolve() method
  3. Optimize duplicate detection to O(n) (minor)
  4. Add max length constraint on list[str] in HandlerConfigValue

Nice to Have

  1. Consider error instead of warning for empty descriptor patches
  2. Standardize error code naming with CONTRACT_PATCH_* prefix
  3. Document use cases for non-standard profile names

Final Verdict

✅ APPROVE

This is an exceptionally well-implemented feature that:

  • Solves a real problem (contract boilerplate reduction)
  • Has comprehensive documentation and tests
  • Follows ONEX architectural patterns
  • Maintains type safety throughout
  • Includes robust validation

The minor security recommendations are for hardening against untrusted input, but are not blockers for merge. Great work! 🎉


Reviewed by: Claude Code
Review Date: 2025-12-31
Confidence Level: High (comprehensive review of all 29 files)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
docs/guides/CONTRACT_PATCHING_GUIDE.md (2)

137-143: Reduce repeated phrasing for writing variety.

Line 139 uses "only specify fields you want to change," which echoes similar language at line 32 ("Write a patch that only specifies what you want to change"). Consider rephrasing one instance to add variety.

🔎 Suggested alternative phrasing
- **Partial**: Only specify fields you want to change
+ **Partial**: Override only the fields that differ from the base profile

This maintains the meaning while reducing repetition with nearby sentences.


634-651: Clarify the type distinction between capability inputs and outputs.

The documentation shows capability_inputs__add accepting strings (line 634) but capability_outputs__add accepting ModelCapabilityProvided objects (lines 640 and 748). This asymmetry is not explained, which could confuse users about which types each field accepts. Add a note or update the model reference section to clarify why inputs and outputs have different types.

🔎 Suggested clarification

Add a note to the ModelCapabilityProvided section (around line 244):

**Note on capability_inputs vs capability_outputs**:
- `capability_inputs__add`: Accepts simple string identifiers (e.g., `["auth", "rate_limiting"]`)
- `capability_outputs__add`: Accepts `ModelCapabilityProvided` objects with metadata (name, version, description)

This asymmetry allows you to declare required capabilities simply while providing detailed information for capabilities you provide.
src/omnibase_core/validation/validation_utils.py (1)

317-367: Import-path and add/remove conflict validation are sound; consider deterministic error text

The add/remove conflict detection and import-path checks cover the important cases (empty input, case-insensitive matching when desired, traversal and dangerous characters, keyword and identifier validation) and integrate cleanly with the rest of the module.

If you want fully deterministic error messages for import-path failures, you could wrap _DANGEROUS_IMPORT_CHARS in sorted(...) when building found_dangerous so the reported character list is stable across runs and interpreters; otherwise this is ready as-is.

Also applies to: 369-449

src/omnibase_core/models/contracts/model_contract_patch.py (1)

182-213: List-operation normalisation and validators are coherent and consistent

Normalizing all list-op fields via normalize_empty_lists_to_none (mode="before") and then delegating to shared helpers (validate_onex_name_list, validate_string_list) keeps the patch surface tight and prevents empty-list no-ops from leaking into downstream logic. The field-specific validators for handlers, dependencies, events, and capabilities give clear, domain-appropriate error messages while ensuring all names are stripped and, where needed, normalized to lowercase.

One minor consideration: validate_consumed_events uses a generic "consumed_events" field label for both consumed_events__add and consumed_events__remove, so validation messages won’t distinguish which list was invalid. If you ever need that granularity for debugging, passing the concrete field name into validate_string_list per caller would make error messages more precise.

Also applies to: 239-259, 266-307, 309-421

src/omnibase_core/models/contracts/model_descriptor_patch.py (1)

48-61: Align docstring with retry/idempotence consistency rules

The validate_settings_consistency logic only raises when retry_policy.enabled is true and max_retries > 0, which is a sensible refinement (no conflict when “retries” are effectively disabled). The docstring currently describes the rules in terms of retry_policy.enabled=True alone; you might want to mention the max_retries > 0 nuance there so the behavior matches the documentation exactly.

Also applies to: 101-115, 148-203

src/omnibase_core/validation/contract_patch_validator.py (1)

314-412: Duplicate detection loops are correct but could be DRYed up

Each block in _validate_list_operation_duplicates correctly tracks seen vs duplicate entries for handlers, dependencies, capabilities, and events, and reports a single DUPLICATE_LIST_ENTRIES error per category. The repeated “seen/duplicates” pattern is mechanically similar across lists; if this grows further, consider extracting a small helper like _check_duplicates(values, label) to reduce boilerplate and ensure any future tweaks (e.g., normalization) are applied consistently.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69320e3 and f8ee9e3.

📒 Files selected for processing (7)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/validation/test_architecture.py
  • tests/unit/validation/test_contract_patch_validator.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use poetry run prefix for Python commands (pytest, mypy, python -m, etc.) - never use pip or direct python execution

Files:

  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • tests/unit/validation/test_architecture.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use Protocol-driven dependency injection with container.get_service('ProtocolName') - never use concrete class names or isinstance checks
Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception
Use @standard_error_handling decorator to eliminate try/catch boilerplate in async operations
Use PEP 604 union syntax (str | None) instead of Optional/Union - enforce via ruff UP007
Use FileRegistry for loading YAML contracts: FileRegistry().load(Path('config/runtime_host.yaml'))
Use duck typing with protocols instead of isinstance checks for type validation
Maintain minimum 60% code coverage - enforce via pytest --cov in CI
Both mypy AND pyright must pass in CI with strict type checking (0 errors required for mypy)
Use EnumNodeKind for architectural routing (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for implementation discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Use Protocol abstractions for all service interfaces (ProtocolLogger, ProtocolEventBus, etc.) - never use concrete implementations

Files:

  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff for linting and black for formatting - run pre-commit hooks before commit
Use isort for import sorting to maintain consistent import order

Files:

  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/validation/test_contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • tests/unit/validation/test_architecture.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Add test markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, etc.) to all test functions
Test timeout protection: 60 seconds per test - use --timeout=60 flag in pytest

Files:

  • tests/unit/validation/test_contract_patch_validator.py
  • tests/unit/validation/test_architecture.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist
Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)
Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations for models with Pydantic-specific features

Files:

  • src/omnibase_core/models/contracts/model_descriptor_patch.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
🧠 Learnings (2)
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception

Applied to files:

  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/validation/test_architecture.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to tests/**/*.py : Add test markers (pytest.mark.unit, pytest.mark.integration, pytest.mark.slow, etc.) to all test functions

Applied to files:

  • tests/unit/validation/test_contract_patch_validator.py
🧬 Code graph analysis (5)
tests/unit/validation/test_contract_patch_validator.py (8)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-126)
src/omnibase_core/models/contracts/model_contract_patch.py (1)
  • ModelContractPatch (43-671)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • ModelDependency (27-343)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • ModelDescriptorPatch (34-256)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/validation/contract_patch_validator.py (4)
  • ContractPatchValidator (36-546)
  • validate (73-132)
  • validate_dict (134-200)
  • validate_file (202-308)
src/omnibase_core/models/contracts/model_descriptor_patch.py (2)
src/omnibase_core/models/runtime/model_descriptor_circuit_breaker.py (1)
  • ModelDescriptorCircuitBreaker (22-78)
src/omnibase_core/models/runtime/model_descriptor_retry_policy.py (1)
  • ModelDescriptorRetryPolicy (24-81)
tests/unit/validation/test_architecture.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-241)
src/omnibase_core/models/errors/model_onex_error.py (2)
  • ModelOnexError (49-354)
  • error_code (253-255)
src/omnibase_core/validation/contract_patch_validator.py (5)
src/omnibase_core/models/contracts/model_contract_patch.py (2)
  • ModelContractPatch (43-671)
  • is_new_contract (537-543)
src/omnibase_core/models/errors/model_onex_error.py (2)
  • ModelOnexError (49-354)
  • message (248-250)
src/omnibase_core/utils/util_safe_yaml_loader.py (1)
  • load_yaml_content_as_model (121-182)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • has_overrides (209-222)
src/omnibase_core/models/common/model_validation_result.py (1)
  • add_issue (172-209)
src/omnibase_core/models/contracts/model_contract_patch.py (7)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-126)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • ModelDependency (27-343)
src/omnibase_core/models/contracts/model_descriptor_patch.py (1)
  • ModelDescriptorPatch (34-256)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_profile_reference.py (1)
  • ModelProfileReference (29-120)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (101-480)
src/omnibase_core/validation/validation_utils.py (2)
  • detect_add_remove_conflicts (317-366)
  • validate_onex_name_list (241-314)
🪛 LanguageTool
docs/guides/CONTRACT_PATCHING_GUIDE.md

[style] ~139-~139: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ... - Partial: Only specify fields you want to change - Declarative: Describe the ...

(REP_WANT_TO_VB)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: claude-review
🔇 Additional comments (14)
docs/guides/CONTRACT_PATCHING_GUIDE.md (5)

759-809: Validation section is accurate and addresses previous concerns.

The validation documentation correctly reflects the actual behavior: Pydantic model validation (including add/remove conflicts) raises ValueError at model construction time, and semantic checks surface PYDANTIC_VALIDATION_ERROR or specific codes like DUPLICATE_LIST_ENTRIES. This aligns with the PR objectives and resolves the earlier comment about error code accuracy.


654-755: Complete real-world example is well-structured and comprehensive.

Example 7 effectively demonstrates the full range of patching features in a realistic scenario. The example correctly uses all the documented models and patterns, and would serve as a strong reference for developers.


418-530: Examples 1–3 are clear and progressively build on each other.

The progression from minimal override (Example 1) through new contract identity (Example 2) to adding handlers and dependencies (Example 3) provides a smooth learning curve. Assertions that verify patch properties enhance understanding.


284-316: Profile naming conventions are well-documented.

Lines 284–316 clearly explain the naming rules and provide a comprehensive table of standard profile names. The format {node_type}_{variant} is intuitive and the examples cover all major use cases (compute, effect, orchestrator, reducer).


1-7: Documentation structure and metadata are appropriate.

The document header (version 1.0.1, last updated, status) and the "New in v0.4.0" callout properly contextualize the patching feature as a new addition. The table of contents and clear section navigation make the guide accessible.

src/omnibase_core/validation/validation_utils.py (2)

165-239: String and ONEX-name list validators look correct and reusable

The shared validate_string_list / validate_onex_name_list helpers handle None, whitespace stripping, per-item length checks, and optional empty-list rejection cleanly, and the explicit use of ValueError for validator contexts plus debug logging is consistent with the module’s documented error-handling patterns. No issues from a correctness or guideline standpoint.

Also applies to: 241-315


648-703: Path validators correctly use ModelOnexError and structured error codes

validate_directory_path and validate_file_path now consistently raise ModelOnexError with EnumCoreErrorCode.DIRECTORY_NOT_FOUND / FILE_NOT_FOUND and INVALID_INPUT, matching the guidelines and what the updated tests assert. The logging levels (warning for traversal, error for missing/mismatched types, exception for resolution failures) give good observability without leaking generic exceptions.

Also applies to: 705-752

src/omnibase_core/models/contracts/model_contract_patch.py (1)

426-531: Identity and conflict validation at model level are well-placed

validate_identity_consistency enforces the “both-or-neither” rule for name and node_version up front, and validate_no_add_remove_conflicts correctly derives names from spec objects and uses the shared conflict detector so conflicting add/remove entries never escape model construction. Together with is_new_contract / is_override_only, has_list_operations, and the get_add_operations / get_remove_operations helpers, this gives callers a clean and invariant-preserving API for patch inspection. No functional issues spotted.

Also applies to: 536-615, 617-671

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

8-9: Tests correctly assert structured ModelOnexError usage for invalid paths

The updated tests for ServiceProtocolAuditor constructor and check_against_spi now expecting ModelOnexError with EnumCoreErrorCode.DIRECTORY_NOT_FOUND match the new validate_directory_path behavior and enforce the intended contract around filesystem errors. The added imports are minimal and appropriate.

Also applies to: 728-733, 965-972

tests/unit/validation/test_contract_patch_validator.py (3)

1-37: Test module setup and markers follow guidelines

The new test module uses @pytest.mark.unit at the class level, imports all required models/validators, and defines focused fixtures (validator, profile_ref) to keep individual tests concise. This aligns with the testing guidelines for tests/**/*.py.


93-152: Conflict tests correctly anchor behavior at model-construction time

The test_validate_conflicting_* cases verify that handler, dependency, event, and capability add/remove conflicts are rejected by ModelContractPatch construction (raising a validation error) rather than by ContractPatchValidator, which is exactly where those invariants should live. The additional “no conflict with different items” test helps guard against regressions where the validator might reintroduce redundant conflict logic.

Also applies to: 154-200, 202-224


255-307: Dictionary/file validation and duplicate-detection tests are comprehensive

The tests around validate_dict and validate_file exercise happy paths, structural failures (extra fields, missing extends), filesystem errors, bad extensions, invalid YAML, and non-dict YAML, and they assert on the specific codes (PYDANTIC_VALIDATION_ERROR, FILE_NOT_FOUND, UNEXPECTED_EXTENSION, YAML_VALIDATION_ERROR / validation codes). The duplicate capability and consumed-event tests cover both error and success cases, including multiple distinct duplicates, and match the implementation in _validate_list_operation_duplicates. Overall, this gives strong regression coverage for the validator.

Also applies to: 314-357, 359-363, 365-480, 482-514

src/omnibase_core/validation/contract_patch_validator.py (2)

73-133: End-to-end patch, dict, and file validation flow is coherent

The three public entry points (validate, validate_dict, validate_file) layer cleanly:

  • validate assumes a structurally valid patch and adds semantic checks.
  • validate_dict first constructs a patch and then delegates to validate, merging issues and preserving a clear summary.
  • validate_file performs staged filesystem, extension, and YAML checks, then reuses the same semantic pipeline.

Error and warning aggregation into ModelValidationResult (including codes and file paths) looks consistent, and early returns on fatal I/O problems keep control flow straightforward. No functional problems stand out.

Also applies to: 134-201, 202-308


413-459: Descriptor and profile-reference checks match the intended semantics

_validate_descriptor_patch cleanly distinguishes empty behavior patches (warning only) from purity/idempotent mismatches, and _validate_profile_reference treats non-standard profile names / version strings as warnings to preserve flexibility while nudging toward conventions. This aligns with the PR’s “structural, not resolutive” philosophy for patch validation and avoids environment-dependent checks.

Also applies to: 491-546

…dation

Resolve conflicts in test docstrings and imports:
- Standardize docstring notes about AssertionError usage
- Restore direct imports in test_service_capability_resolver.py
- Update validate_naming.py exemption comment
@claude

claude Bot commented Jan 2, 2026

Copy link
Copy Markdown

Pull Request Review: Contract Patching Infrastructure (OMN-1126)

Summary

This PR implements a comprehensive contract patching system with well-designed models, validation infrastructure, and security controls. The implementation quality is high with excellent documentation, test coverage, and adherence to project conventions.

✅ Strengths

1. Excellent Architecture & Design

  • Clear separation of concerns: ModelContractPatch for data, ContractPatchValidator for validation
  • Three-layer architecture (Profile → Behavior → Contract) is well-documented
  • List operations using __add/__remove suffix pattern is intuitive and type-safe
  • Immutable models with frozen=True prevent accidental mutations

2. Outstanding Documentation

  • Comprehensive docstrings with examples, rationale, and cross-references
  • Security documentation inline with code (see ModelReference, NodeBase, mixins)
  • Empty list normalization behavior is thoroughly documented with rationale
  • Module-level security warnings in util_safe_yaml_loader.py

3. Robust Security

  • Module allowlist (ALLOWED_MODULE_PREFIXES) prevents arbitrary imports in ModelReference
  • Optional tool import validation in NodeBase (defense-in-depth, opt-in)
  • yaml.safe_load() used exclusively throughout - prevents code execution
  • Security trust model clearly documented in each component

4. Strong Type Safety

  • Structured ModelSemVer for versions (not strings) - eliminates string version anti-pattern
  • PEP 604 union syntax (str | None) used consistently
  • Comprehensive error code enum (EnumPatchValidationErrorCode) with clear naming
  • Type-safe validation results with generic ModelValidationResult[T]

5. Excellent Test Coverage

  • 62 new tests across multiple test files
  • Edge cases covered: empty lists, conflicts, duplicates, invalid YAML
  • File validation tests include extension checks, I/O errors, parsing errors
  • Protocol conformance testing (test_protocol_conformance)

6. Code Quality

  • DRY principles: Shared validation utilities in validation_utils.py
  • Consistent error handling with ModelOnexError
  • Logging at appropriate levels (DEBUG/INFO/WARNING/ERROR)
  • Field validators normalized data (lowercase, strip whitespace)

🔍 Issues & Recommendations

⚠️ MEDIUM PRIORITY

1. Security: Dynamic Imports in NodeBase (Lines 398-419)

Issue: NodeBase._resolve_main_tool() performs dynamic imports from contract files with only opt-in allowlist validation.

# ENFORCE_TOOL_IMPORT_ALLOWLIST is False by default (opt-in)
ENFORCE_TOOL_IMPORT_ALLOWLIST: ClassVar[bool] = False

# Allowlist check only runs if flag is enabled
if self.ENFORCE_TOOL_IMPORT_ALLOWLIST:
    if not any(module_path.startswith(prefix) for prefix in self.ALLOWED_TOOL_MODULE_PREFIXES):
        raise ModelOnexError(...)

Risk: If contract files come from untrusted sources AND the allowlist is not enabled, arbitrary Python modules could be imported.

Recommendations:

  1. Document the trust requirement: Add a warning in NodeBase.__init__ docstring stating "Contract files MUST be from trusted sources"
  2. Consider opt-out instead of opt-in: Change default to ENFORCE_TOOL_IMPORT_ALLOWLIST = True for production safety
  3. Add validation at contract load time: Check main_tool_class against allowlist in UtilContractLoader before NodeBase instantiation

Mitigation: The PR description notes this is already documented in the security section of NodeBase, but the opt-in default is concerning.


2. Validation Severity: Empty Descriptor Patch (Lines 496-504)

Current: Empty descriptor patches generate INFO (not WARNING/ERROR)

result.add_issue(
    severity=EnumValidationSeverity.INFO,  # Should this be WARNING?
    message="Behavior patch is present but has no overrides",
    code=EnumPatchValidationErrorCode.CONTRACT_PATCH_EMPTY_DESCRIPTOR.value,
)

Issue: The docstring rationale states "it's likely a user mistake but doesn't break merge operations." However:

  • Users may not see INFO-level messages in normal operation
  • The CONTRACT_PATCHING_GUIDE.md was updated to call this "Info" (from "Warning")
  • No-op patches consume resources and add confusion

Recommendation: Consider changing to WARNING severity. Rationale:

  • Warnings are more visible than info messages
  • Empty patches should be removed for code clarity
  • The suggestion field already says "Remove the empty descriptor field"

3. Inconsistent Naming: "Descriptor" vs "Behavior"

Issue: The field is named descriptor but documentation/comments refer to it as "behavior patch":

descriptor: ModelDescriptorPatch | None = Field(
    default=None,
    description="Nested behavior overrides for handler settings.",  # Says "behavior"
)

def _validate_descriptor_patch(...):
    """Validate the nested behavior patch in the descriptor field."""  # Says "behavior"
    # Note: The field is named 'descriptor' but conceptually represents
    #       handler behavior configuration...

Recommendation:

  • Option 1: Rename descriptor → behavior to match documentation
  • Option 2: Update all documentation to consistently use "descriptor"
  • Current hybrid approach is confusing

Affected Files: model_contract_patch.py, contract_patch_validator.py, CONTRACT_PATCHING_GUIDE.md


📝 LOW PRIORITY

4. Code Style: String Interpolation

Mixed f-string and format() usage:

# Lines 149-150: f-string
f"Starting patch validation for profile={patch.extends.profile}, "
f"is_new_contract={patch.is_new_contract}"

# Line 238: format()
f"Dictionary validation failed with {len(e.errors())} errors"

Recommendation: Prefer f-strings consistently (already the dominant pattern).


5. Documentation: Missing Cross-Reference

ModelContractPatch.validate_no_add_remove_conflicts() is mentioned in validator docs but not linked:

# contract_patch_validator.py:92
# Note:
#     Conflict checks (items in both __add and __remove) are handled by
#     Pydantic model validation in ModelContractPatch.validate_no_add_remove_conflicts().
#     This validator focuses on duplicate detection within lists.

Recommendation: Add See Also reference to the actual method.


6. Test Coverage: Missing Security Test

No test verifies that ModelReference blocks untrusted module imports:

# Suggested test
def test_resolve_blocks_untrusted_modules():
    ref = ModelReference(module="os.path", class_name="join")
    result = ref.resolve()
    assert result is None  # Should be blocked by allowlist

Recommendation: Add security-focused test to test_model_reference.py.


7. Performance: Repeated List Comprehensions

ModelContractPatch.validate_no_add_remove_conflicts() extracts names multiple times:

# Line 581-582
handler_add_names = [h.name for h in self.handlers__add] if self.handlers__add else None

# Later in get_add_operations() (line 706-707)
result["handlers"] = list(self.handlers__add)  # Could reuse extracted names

Impact: Negligible for typical patch sizes (<100 items per list).

Recommendation: Low priority - only optimize if profiling shows it's a bottleneck.


🎯 Critical Checklist (Per CLAUDE.md)

Requirement Status Notes
Poetry usage ✅ PR doesn't add new dependencies
Type checking (mypy strict) ✅ PR description states mypy passes
Type checking (pyright) ⚠️ Not mentioned in PR description
PEP 604 unions ✅ Uses str | None throughout
Error handling ✅ Uses ModelOnexError with error codes
Test coverage (60%+) ✅ 62 new tests, comprehensive coverage
Thread safety ✅ All models are immutable (frozen=True)
Documentation ✅ Excellent docstrings and inline docs
Base class super().__init__ ✅ NodeBase security changes include proper initialization

Action Required: Confirm pyright passes before merge.


🔒 Security Assessment

Trust Model (Well-Documented)

  • Contract files: MUST be from trusted sources (documented in NodeBase)
  • YAML content: Uses yaml.safe_load() exclusively ✅
  • Module imports: Protected by allowlist in ModelReference ✅
  • Tool imports: Optional allowlist in NodeBase (opt-in) ⚠️

Attack Vectors Considered

  1. Malicious YAML: Mitigated by yaml.safe_load()
  2. Arbitrary imports: Mitigated by ALLOWED_MODULE_PREFIXES
  3. Path traversal: Not applicable (no file path construction from user input)
  4. Code injection: Mitigated by allowlist and safe YAML loading

Overall Security: Strong with one caveat (opt-in tool allowlist).


📊 Code Metrics

  • Files changed: 58
  • Additions: +1616 lines
  • Deletions: -347 lines
  • Net: +1269 lines
  • New models: 6 (ModelContractPatch, ModelDescriptorPatch, ModelProfileReference, etc.)
  • New validator: ContractPatchValidator
  • New enum: EnumPatchValidationErrorCode (11 codes)
  • Test files: 8 updated/created
  • Test count: 62 new tests

✅ Approval Recommendation

Approve with minor revisions.

Before Merge:

  1. ✅ Confirm pyright type checking passes (not mentioned in PR description)
  2. ⚠️ Address security concern: Consider changing ENFORCE_TOOL_IMPORT_ALLOWLIST default to True OR add stronger documentation warning
  3. ⚠️ Consider severity change: Empty descriptor patch INFO → WARNING

Optional (Post-Merge):

  • Rename descriptor field to behavior for consistency
  • Add security test for ModelReference allowlist
  • Unify string interpolation style

🎉 Final Notes

This is high-quality work that demonstrates:

  • Strong architectural thinking (patch vs full contract paradigm)
  • Excellent attention to security (defense-in-depth with allowlists)
  • Comprehensive testing and documentation
  • Adherence to ONEX standards and conventions

The contract patching system is well-designed and should serve as a model for future infrastructure PRs.

Great job on OMN-1126! 🚀


Review generated following CLAUDE.md conventions for omnibase_core v0.4.0

… (OMN-1126)

- Add security documentation and allowlist tests for NodeBase dynamic imports
- Rename _validate_descriptor_patch to _validate_behavior_patch (naming migration)
- Add deterministic error text in duplicate detection (sort duplicates alphabetically)
- Add warn_empty_list parameter to validation_utils functions
- Improve type narrowing documentation in model_contract_patch.py
- Add ConfigDict to model_git_hub_release_event.py
- Document missing error codes in CONTRACT_PATCHING_GUIDE.md
- Add 6 security tests for NodeBase import allowlist validation
@claude

claude Bot commented Jan 2, 2026

Copy link
Copy Markdown

PR Review: Contract Patching Infrastructure (OMN-1126)

Summary

This PR implements a well-architected contract patching system following the principle "User-authored files are patches, not full contracts." The implementation is thorough, well-tested, and adheres to project standards.

Overall Assessment: ✅ Approve with Minor Suggestions


✅ Strengths

1. Excellent Architecture & Design

  • Clear separation of concerns: ModelContractPatch for data, ContractPatchValidator for validation
  • Declarative __add/__remove suffix pattern for list operations is intuitive
  • Three-layer architecture (Profile → Behavior → Contract) is well-documented
  • Structured ModelSemVer for versions instead of strings prevents validation issues

2. Comprehensive Testing (62 tests)

  • Excellent test coverage across all validation scenarios
  • Tests cover both positive and negative cases
  • Good use of parametric patterns and fixtures
  • Edge cases well-tested (empty lists, conflicts, duplicates)

3. Strong Type Safety

  • Proper use of Pydantic validators (field_validator, model_validator)
  • Conflict detection moved to Pydantic model validation (fail-fast)
  • Consistent error code enum (EnumPatchValidationErrorCode)
  • Good separation of structural vs semantic validation

4. Code Quality

  • Excellent documentation with examples
  • Clear docstrings explaining rationale (e.g., empty list normalization)
  • DRY validation helpers (validation_utils.py)
  • Logging at appropriate levels (DEBUG for trace, INFO for operations, WARNING for issues)

5. Security Enhancements

  • Added comprehensive security documentation to NodeBase, MixinEventListener, and MixinIntrospectFromContract
  • Clarifies trust boundaries for dynamic imports and contract loading
  • Namespace allowlist pattern for restricted module imports

🔍 Issues & Suggestions

1. Capability Matching Logic Change ⚠️ CRITICAL

File: src/omnibase_core/models/capabilities/model_capability_requirement_set.py:537-622

Issue: The matches() method scoring logic was changed significantly:

# OLD BEHAVIOR
score = 0.0
for key, preferred_value in self.prefer.items():
    if key in provider and provider[key] == preferred_value:
        score += 1.0  # Each preference adds 1.0

# NEW BEHAVIOR  
score = 1.0  # Base score
for key, preferred_value in self.prefer.items():
    if key in provider and provider[key] == preferred_value:
        score += 0.1  # Each preference adds 0.1

Impact:

  • Breaking change to capability provider scoring
  • Providers now get base score of 1.0 even with 0 matching preferences
  • Preference impact reduced from 1.0 → 0.1 per match
  • This fundamentally changes provider selection behavior

Questions:

  1. Is this scoring change intentional or incidental to the patch work?
  2. Are there tests validating the new scoring behavior?
  3. Should this be a separate PR with explicit migration guide?

Recommendation:

  • If intentional: Document in PR description, add migration notes, ensure test coverage
  • If incidental: Revert to original scoring logic (this PR is about patching, not capability matching)

2. Empty List Normalization Logging (Minor)

File: model_contract_patch.py:369-375

if v is not None and len(v) == 0:
    logger.debug(
        "Normalized empty list to None for field: %s",
        info.field_name,
    )
    return None

Issue: This DEBUG log will fire on every patch instantiation with empty lists, potentially cluttering logs during bulk operations.

Suggestion: Consider TRACE level or remove logging entirely (behavior is documented in docstring).

3. Mutation Pattern Documentation (Minor Suggestion)

File: model_contract_patch.py:494-543

The _collect_conflicts() method uses in-place mutation of all_conflicts. The docstring explains this well, but consider:

# Alternative: Return conflicts instead of mutating
def _collect_conflicts(...) -> list[str]:
    conflicts = detect_add_remove_conflicts(add_names, remove_names, field_name)
    return [f"{field_name}: {conflicts}"] if conflicts else []

Rationale: Functional style is easier to test and reason about. Performance difference is negligible for 5-10 field checks.

Note: Current implementation is fine; this is a style preference suggestion.

4. String Splitting Improvement ✅ Good Catch

File: scripts/validation/validate-string-versions.py

Good fix changing .split("\n") → .splitlines():

  • Handles \r\n (Windows) correctly
  • More Pythonic
  • Prevents edge case bugs

📋 Checklist Review

Item Status Notes
Tests ✅ 62 new tests, comprehensive coverage
Type Checking ✅ mypy strict passes
Linting ✅ ruff passes
Documentation ✅ Excellent docstrings + CONTRACT_PATCHING_GUIDE.md
Error Codes ✅ Centralized in EnumPatchValidationErrorCode
Thread Safety ✅ Immutable models (frozen=True)
Performance ✅ O(n) validation algorithms
Security ✅ Enhanced security docs for dynamic imports

🎯 Recommendations

Must Address Before Merge:

  1. Clarify capability matching scoring change - Is this intentional? If so, document and test thoroughly.

Nice to Have:

  1. Consider TRACE logging for empty list normalization
  2. Consider functional style for _collect_conflicts() (style preference)

Pre-existing Issues (Not Blockers):

  • Pre-commit hook false positives (validate-naming-conventions, validate-no-duplicate-models) - mentioned in PR description, not related to this change.

🚀 Conclusion

This is a high-quality implementation of the contract patching system. The architecture is sound, testing is thorough, and the code follows project conventions. The only critical question is about the capability matching scoring change.

Once the capability scoring change is clarified (intentional vs incidental), this PR is ready to merge.

Great work on the comprehensive validation framework! 🎉


Files Reviewed

  • src/omnibase_core/models/contracts/model_contract_patch.py (793 lines)
  • src/omnibase_core/validation/contract_patch_validator.py (626 lines)
  • src/omnibase_core/enums/enum_patch_validation_error_code.py (102 lines)
  • src/omnibase_core/models/contracts/model_descriptor_patch.py (partial)
  • tests/unit/validation/test_contract_patch_validator.py (554 lines)
  • Security enhancements in NodeBase, MixinEventListener, MixinIntrospectFromContract
  • Documentation updates in docs/guides/CONTRACT_PATCHING_GUIDE.md

Reviewed by: Claude Code (Sonnet 4.5) | Date: 2026-01-02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (8)
src/omnibase_core/models/contracts/model_dependency.py (1)

342-347: Consider adding frozen=True and from_attributes=True per guidelines.

Since ModelDependency is a value object representing an immutable dependency specification (typically nested in contract models), the coding guidelines recommend:

  • frozen=True for immutable models
  • from_attributes=True for immutable value objects nested in Pydantic models used with pytest-xdist
🔎 Proposed configuration enhancement
 model_config = ConfigDict(
+    frozen=True,
+    from_attributes=True,
     extra="ignore",  # Allow extra fields from various input formats
     use_enum_values=False,  # Keep enum objects, don't convert to strings
     validate_assignment=True,
     str_strip_whitespace=True,
 )

Based on coding guidelines for src/omnibase_core/models/**/*.py.

src/omnibase_core/validation/validation_utils.py (2)

165-248: Consider documenting mutual exclusivity of reject_empty_list and warn_empty_list.

The implementation correctly prioritizes reject_empty_list over warn_empty_list (lines 213-221), but this precedence behavior isn't explicitly documented. Since these parameters represent mutually exclusive use cases, consider adding a note in the docstring to clarify that if both are True, rejection takes precedence and no warning will be logged.

📝 Suggested docstring enhancement
     Args:
         values: List of strings to validate, or None.
         field_name: Name of the field being validated (for error messages).
         min_length: Minimum length for each string after stripping.
         strip_whitespace: If True, strip whitespace from each value.
         reject_empty_list: If True, raise ValueError for empty lists.
             Use for add/remove operations where an empty list is likely a user error.
         warn_empty_list: If True, log a warning for empty lists but don't reject.
             Useful for detecting potential user errors without failing validation.
+            Note: If both reject_empty_list and warn_empty_list are True,
+            rejection takes precedence and no warning will be logged.

251-334: Consider documenting mutual exclusivity of reject_empty_list and warn_empty_list.

Similar to validate_string_list, this function prioritizes reject_empty_list over warn_empty_list (lines 298-307), but this precedence isn't explicitly documented. For consistency and clarity, consider adding the same note about mutual exclusivity.

📝 Suggested docstring enhancement
     Args:
         values: List of names to validate, or None.
         field_name: Name of the field being validated (for error messages).
         normalize_lowercase: If True, normalize all names to lowercase.
         reject_empty_list: If True, raise ValueError for empty lists.
             Use for add/remove operations where an empty list is likely a user error.
         warn_empty_list: If True, log a warning for empty lists but don't reject.
             Useful for detecting potential user errors without failing validation.
+            Note: If both reject_empty_list and warn_empty_list are True,
+            rejection takes precedence and no warning will be logged.
tests/unit/models/providers/test_model_provider_descriptor.py (1)

27-55: Consider test isolation implications of module-level model_rebuild().

The module-level model_rebuild() call mutates the global ModelProviderDescriptor class definition, replacing its forward reference to the real ModelHealthStatus with this stub. If other test modules import ModelProviderDescriptor after this module loads, they will inherit the stub-based resolution, which could cause unexpected behavior.

While this works for isolated test runs, consider wrapping the rebuild in a fixture with proper teardown to restore the original model state, especially if tests run in parallel or if other test modules need the real ModelHealthStatus.

🔎 Alternative approach using a pytest fixture
-# Rebuild the model with the stub class in local namespace
-ModelProviderDescriptor.model_rebuild(
-    _types_namespace={"ModelHealthStatus": ModelHealthStatus}
-)
+@pytest.fixture(scope="module", autouse=True)
+def _setup_model_provider_descriptor():
+    """Rebuild ModelProviderDescriptor with stub for this module."""
+    ModelProviderDescriptor.model_rebuild(
+        _types_namespace={"ModelHealthStatus": ModelHealthStatus}
+    )
+    yield
+    # Note: Pydantic doesn't provide an easy way to restore original state
+    # Consider whether parallel test execution could be affected
tests/unit/services/test_service_capability_resolver.py (4)

50-82: Preserve type safety instead of using Any.

The MockProviderRegistry methods now accept and return Any instead of ModelProviderDescriptor, which reduces type checking coverage and makes tests less reliable. Since ModelProviderDescriptor is already imported (lines 40-42), these type hints can remain specific.

The lazy-loading pattern in _get_model_provider_descriptor() is for runtime resolution, but type hints can still reference the imported class for static analysis.

🔎 Preserve specific types for better type safety
-    def __init__(self, providers: list[Any] | None = None) -> None:
+    def __init__(self, providers: list[ModelProviderDescriptor] | None = None) -> None:
         """Initialize with optional list of providers.
 
         Args:
             providers: List of provider descriptors to register.
         """
-        self.providers: list[Any] = providers or []
+        self.providers: list[ModelProviderDescriptor] = providers or []
 
-    def get_providers_for_capability(self, capability: str) -> list[Any]:
+    def get_providers_for_capability(self, capability: str) -> list[ModelProviderDescriptor]:
         """Get all providers offering the specified capability.
 
         Args:
             capability: Exact capability identifier to match.
 
         Returns:
             List of providers that have this capability.
         """
         return [p for p in self.providers if capability in p.capabilities]
 
-    def add_provider(self, provider: Any) -> None:
+    def add_provider(self, provider: ModelProviderDescriptor) -> None:
         """Add a provider to the registry.
 
         Args:
             provider: Provider descriptor to add.
         """
         self.providers.append(provider)

149-186: Return type Any degrades type safety in factory function.

The create_provider() function now returns Any instead of ModelProviderDescriptor, preventing type checkers from verifying correct usage in tests. This is unnecessary since ModelProviderDescriptor is already imported and available.

If you adopt the module-level rebuild approach (as suggested for lines 117-147), the _get_model_provider_descriptor() call becomes unnecessary, and the return type can be restored to ModelProviderDescriptor.

🔎 Restore specific return type
 def create_provider(
     provider_id: UUID | None = None,
     capabilities: list[str] | None = None,
     attributes: dict[str, Any] | None = None,
     declared_features: dict[str, Any] | None = None,
     observed_features: dict[str, Any] | None = None,
     adapter: str = "test.adapters.TestAdapter",
     connection_ref: str = "env://TEST_CONNECTION",
     tags: list[str] | None = None,
-) -> Any:
+) -> ModelProviderDescriptor:
     """Create a test provider descriptor with defaults.
 
     Args:
         provider_id: UUID for provider (generated if not provided).
         capabilities: List of capability identifiers.
         attributes: Static provider attributes.
         declared_features: Declared feature flags.
         observed_features: Observed runtime features.
         adapter: Python import path for adapter.
         connection_ref: Connection reference URI.
         tags: Tags for categorization.
 
     Returns:
         Configured ModelProviderDescriptor.
     """
-    ModelProviderDescriptor = _get_model_provider_descriptor()
-
     return ModelProviderDescriptor(

213-214: Remove misleading "lazy import" comments.

Throughout the file, comments like # Lazy import to avoid circular dependency or # Lazy imports to avoid circular dependency appear in test methods (e.g., lines 213-214, 241-244, 273-274, etc.), but:

  1. No imports actually follow these comments in many cases (e.g., line 213-214)
  2. Where imports do follow, they import ServiceCapabilityResolver, which is already imported at module level (line 43)
  3. The actual lazy loading happens in _get_model_provider_descriptor(), not in these test methods

These comments are misleading and should be removed. The real lazy-loading pattern is the stub-based model rebuild, not these test-method-level comments.

🔎 Remove misleading comments
-        # Lazy imports to avoid circular dependency
-        from omnibase_core.services.service_capability_resolver import (
-            ServiceCapabilityResolver,
-        )
-
         # Setup
         resolver = ServiceCapabilityResolver()

Apply this pattern to all similar occurrences throughout the file.

Also applies to: 241-244, 273-274, 305-308, 328-329, 350-351, 380-381, 399-402, 428-429, 460-461, 493-494, 524-527, 549-550, 576-579, 618-619, 646-647, 674-677, 700-701, 727-728, 755-756, 777-778, 804-805, 853-854, 889-890, 919-920, 955-956, 972-973, 998-1001, 1034-1035, 1062-1063, 1083-1084, 1109-1110, 1131-1132, 1149-1150, 1183-1184, 1224-1225, 1254-1255, 1289-1290, 1318-1319, 1351-1352, 1416-1417, 1435-1436, 1457-1460, 1487-1488, 1518-1519


188-227: Return type Any degrades type safety in create_dependency().

Similar to create_provider(), the create_dependency() function now returns Any instead of ModelCapabilityDependency. This prevents type checkers from catching errors in test code.

Since ModelCapabilityDependency is imported at lines 33-35, restore the specific return type for better type safety.

🔎 Restore specific return type
 def create_dependency(
     alias: str = "db",
     capability: str = "database.relational",
     must: dict[str, Any] | None = None,
     prefer: dict[str, Any] | None = None,
     forbid: dict[str, Any] | None = None,
     hints: dict[str, Any] | None = None,
     selection_policy: str = "auto_if_unique",
     strict: bool = True,
-) -> Any:
+) -> ModelCapabilityDependency:
     """Create a test capability dependency with defaults.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ab7909 and a157f2b.

📒 Files selected for processing (20)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/contracts/model_dependency.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/models/core/model_onex_field_data.py
  • src/omnibase_core/models/core/model_tool_security.py
  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/models/core/model_tool_security_summary.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/enums/test_enum_handler_capability.py
  • tests/unit/enums/test_enum_likelihood.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/models/events/contract_validation/test_model_contract_validation_events.py
  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • tests/unit/models/providers/test_model_provider_descriptor.py
  • tests/unit/services/test_service_capability_resolver.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/unit/enums/test_enum_likelihood.py
  • src/omnibase_core/models/core/model_onex_field_data.py
  • src/omnibase_core/models/core/model_tool_security_summary.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{py,sh}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use poetry run prefix for Python commands (pytest, mypy, python -m, etc.) - never use pip or direct python execution

Files:

  • tests/unit/models/events/contract_validation/test_model_contract_validation_events.py
  • tests/unit/enums/test_enum_handler_capability.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • tests/unit/models/providers/test_model_provider_descriptor.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/model_dependency.py
  • src/omnibase_core/models/core/model_tool_security.py
  • tests/unit/services/test_service_capability_resolver.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Add test markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, etc.) to all test functions
Test timeout protection: 60 seconds per test - use --timeout=60 flag in pytest

Files:

  • tests/unit/models/events/contract_validation/test_model_contract_validation_events.py
  • tests/unit/enums/test_enum_handler_capability.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • tests/unit/models/providers/test_model_provider_descriptor.py
  • tests/unit/services/test_service_capability_resolver.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff for linting and black for formatting - run pre-commit hooks before commit
Use isort for import sorting to maintain consistent import order

Files:

  • tests/unit/models/events/contract_validation/test_model_contract_validation_events.py
  • tests/unit/enums/test_enum_handler_capability.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • tests/unit/models/providers/test_model_provider_descriptor.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/model_dependency.py
  • src/omnibase_core/models/core/model_tool_security.py
  • tests/unit/services/test_service_capability_resolver.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use Protocol-driven dependency injection with container.get_service('ProtocolName') - never use concrete class names or isinstance checks
Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception
Use @standard_error_handling decorator to eliminate try/catch boilerplate in async operations
Use PEP 604 union syntax (str | None) instead of Optional/Union - enforce via ruff UP007
Use FileRegistry for loading YAML contracts: FileRegistry().load(Path('config/runtime_host.yaml'))
Use duck typing with protocols instead of isinstance checks for type validation
Maintain minimum 60% code coverage - enforce via pytest --cov in CI
Both mypy AND pyright must pass in CI with strict type checking (0 errors required for mypy)
Use EnumNodeKind for architectural routing (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for implementation discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Use Protocol abstractions for all service interfaces (ProtocolLogger, ProtocolEventBus, etc.) - never use concrete implementations

Files:

  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/validation/contract_patch_validator.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/contracts/model_dependency.py
  • src/omnibase_core/models/core/model_tool_security.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist
Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)
Always use forward references via model_rebuild() in Pydantic models instead of from __future__ import annotations for models with Pydantic-specific features

Files:

  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
  • src/omnibase_core/models/contracts/model_dependency.py
  • src/omnibase_core/models/core/model_tool_security.py
src/omnibase_core/infrastructure/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/infrastructure/**/*.py: Always call super().__init__(container) in node constructors - this is mandatory and eliminates 80+ lines of boilerplate
Use mixins (MixinDiscoveryResponder, MixinEventHandler, etc.) to add reusable capabilities to nodes

Files:

  • src/omnibase_core/infrastructure/node_base.py
🧠 Learnings (5)
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Distinguish between ModelContainer[T] (value wrapper) and ModelONEXContainer (DI container) - use ModelONEXContainer in node constructors

Applied to files:

  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • src/omnibase_core/models/events/contract_validation/__init__.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Add `from_attributes=True` to ConfigDict for immutable value objects nested in Pydantic models used with pytest-xdist

Applied to files:

  • tests/unit/models/events/contract_validation/test_model_validation_context.py
  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Use Pydantic 2.11+ BaseModel for all data models with strict configuration (frozen=True for immutable models)

Applied to files:

  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/models/core/model_feature_flag_metadata.py
  • src/omnibase_core/models/configuration/model_git_hub_release_event.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/models/**/*.py : Always use forward references via `model_rebuild()` in Pydantic models instead of `from __future__ import annotations` for models with Pydantic-specific features

Applied to files:

  • src/omnibase_core/models/core/model_tool_security_assessment.py
  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2025-12-27T15:46:10.825Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-27T15:46:10.825Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with EnumCoreErrorCode for all error handling - never use generic Exception

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/contracts/model_dependency.py
🧬 Code graph analysis (6)
tests/unit/models/events/contract_validation/test_model_contract_validation_events.py (2)
src/omnibase_core/models/events/contract_validation/model_contract_validation_context.py (1)
  • ModelContractValidationContext (29-86)
src/omnibase_core/enums/enum_validation_mode.py (1)
  • EnumValidationMode (15-83)
tests/unit/enums/test_enum_handler_capability.py (3)
tests/unit/enums/test_enum_handler_type_category.py (1)
  • test_assert_exhaustive_raises_assertion_error (131-145)
tests/unit/enums/test_enum_handler_command_type.py (1)
  • test_assert_exhaustive_raises_assertion_error (184-196)
tests/unit/enums/test_enum_handler_role.py (1)
  • test_assert_exhaustive_raises_assertion_error (138-152)
tests/unit/models/events/contract_validation/test_model_validation_context.py (2)
src/omnibase_core/enums/enum_validation_mode.py (3)
  • EnumValidationMode (15-83)
  • is_strict (67-74)
  • allows_continuation (76-83)
src/omnibase_core/models/events/contract_validation/model_contract_validation_context.py (1)
  • ModelContractValidationContext (29-86)
src/omnibase_core/models/events/contract_validation/__init__.py (1)
src/omnibase_core/models/events/contract_validation/model_contract_validation_context.py (1)
  • ModelContractValidationContext (29-86)
src/omnibase_core/validation/contract_patch_validator.py (2)
src/omnibase_core/enums/enum_patch_validation_error_code.py (1)
  • EnumPatchValidationErrorCode (28-102)
src/omnibase_core/models/common/model_validation_result.py (1)
  • add_issue (172-209)
src/omnibase_core/validation/validation_utils.py (1)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • is_protocol (319-321)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Quality
  • GitHub Check: claude-review
🔇 Additional comments (36)
src/omnibase_core/models/core/model_feature_flag_metadata.py (1)

9-9: LGTM! Proper Pydantic configuration and validation.

The changes correctly implement:

  • ConfigDict with frozen=True for immutability, extra="forbid" to prevent unexpected fields, and from_attributes=True for pytest-xdist compatibility
  • Field validation constraints (ge=0, le=100) for rollout_percentage to enforce the documented 0-100 range

These align with coding guidelines for Pydantic 2.11+ models and address the previous review feedback.

Also applies to: 15-16, 26-31

src/omnibase_core/models/configuration/model_git_hub_release_event.py (1)

7-7: LGTM! Proper Pydantic configuration with appropriate strictness.

The changes correctly implement:

  • ConfigDict with strict=True for type coercion control, frozen=True for immutability, extra="forbid" to prevent unexpected fields, and from_attributes=True for pytest-xdist compatibility
  • Helpful comments clarifying the import/export structure

These align with coding guidelines for Pydantic 2.11+ models and the previous review feedback has been addressed.

Also applies to: 21-27, 51-52

src/omnibase_core/models/core/model_tool_security.py (1)

9-14: LGTM! Import adjustments properly accommodate the module split.

The imports correctly reference both security models from their respective modules, and both are used appropriately in the methods below (lines 84 and 98).

src/omnibase_core/models/core/model_tool_security_assessment.py (2)

8-8: Excellent! ConfigDict configuration follows all coding guidelines.

The model configuration is comprehensive and correct:

  • strict=True enforces strict type checking
  • extra="forbid" prevents unexpected fields
  • frozen=True ensures immutability
  • from_attributes=True enables attribute-based construction for pytest-xdist compatibility

This addresses the previous review feedback and aligns with the project's coding guidelines for immutable value objects.

Also applies to: 18-23


64-64: LGTM! Circular import resolved.

Removing the re-export of ModelToolSecuritySummary from __all__ correctly resolves the circular import issue flagged in previous reviews. Consumers can now import ModelToolSecuritySummary directly from its dedicated module.

tests/unit/enums/test_enum_handler_capability.py (2)

227-241: LGTM! Type annotation improves type safety.

The addition of the -> None return type annotation is consistent with similar test methods across other enum test files and improves type safety without affecting test behavior.


242-249: LGTM! Type annotation improves type safety.

The addition of the -> None return type annotation enhances type safety and is consistent with similar test methods in the codebase.

src/omnibase_core/models/contracts/model_dependency.py (1)

26-28: LGTM! Clean public API definition.

The __all__ addition correctly exports the sole public class, improving explicit API boundaries for the module.

tests/unit/models/events/contract_validation/test_model_contract_validation_events.py (1)

33-33: LGTM! Consistent rename to canonical context class.

All test instantiations and imports have been correctly updated to use ModelContractValidationContext. The changes are purely mechanical renames with no logic modifications, maintaining the same test coverage while aligning with the canonical naming to resolve the OMN-1126 naming collision.

Also applies to: 132-132, 152-152, 175-175, 186-186, 209-209, 226-226, 976-976, 1022-1024, 1054-1057

src/omnibase_core/models/events/contract_validation/__init__.py (2)

22-25: Excellent documentation clarifying the distinction between context types.

The added note clearly distinguishes ModelContractValidationContext (for contract validation operations with mode/flags) from ModelValidationContext in omnibase_core.models.context (for field-level validation with field_name/expected/actual). This helps prevent confusion and guides developers to use the correct context type.


96-97: LGTM! Public API surface correctly updated.

The import statement and __all__ list have been properly updated to export ModelContractValidationContext as the canonical name, removing the previous ModelValidationContext reference to avoid naming collision.

Also applies to: 118-118

tests/unit/models/events/contract_validation/test_model_validation_context.py (3)

4-4: LGTM! Test file properly updated with canonical naming.

The module docstring, OMN-1126 references, and imports have been correctly updated to use ModelContractValidationContext as the canonical name. The documentation clearly explains this resolves the naming collision fix.

Also applies to: 11-12, 19-21


450-482: Excellent addition documenting the canonical naming decision.

The new TestModelContractValidationContextNaming test class serves as documentation for the OMN-1126 naming resolution. The docstring clearly explains why the backwards-compatibility alias was removed (to avoid collision with field-level validation context), and the tests validate that:

  • The canonical name works correctly
  • Instance equality works as expected

This helps prevent future confusion about which context type to use.


25-447: LGTM! Comprehensive test coverage maintained with renamed class.

All test classes have been systematically updated to use ModelContractValidationContext, maintaining the same comprehensive coverage including:

  • Creation and defaults
  • Mode behavior (STRICT/PERMISSIVE)
  • Flags handling
  • Immutability (frozen=True)
  • Extra field rejection
  • Serialization/deserialization
  • from_attributes configuration
  • Edge cases and usage patterns

The changes are purely mechanical renames with no logic modifications, ensuring consistent test coverage with the canonical naming.

src/omnibase_core/infrastructure/node_base.py (5)

3-3: LGTM!

The ClassVar import from typing is correctly added to support the class-level security configuration variables.


15-55: Well-documented security model.

The security documentation is thorough, covering trust assumptions, the dynamic import threat vector, and defense-in-depth options. The clear warnings about untrusted contract sources are valuable for maintainers.


137-151: LGTM!

The class-level security configuration is well-designed:

  • Immutable tuple for allowed prefixes prevents accidental modification
  • Opt-in enforcement (default False) ensures backwards compatibility
  • ClassVar annotation enables proper type checking and subclass override

401-443: Security validation is correctly implemented.

The allowlist check is properly positioned before the dynamic import, and the error handling follows the coding guidelines using ModelOnexError with EnumCoreErrorCode.SECURITY_VIOLATION. The context includes helpful debugging information without leaking sensitive data.

One minor observation: the inline comments are extensive but justified given the security-critical nature of this code path.


353-380: LGTM!

The updated docstring provides clear security guidance with a practical example for enabling strict import validation in subclasses.

tests/unit/infrastructure/test_node_base.py (7)

1336-1353: LGTM!

The test class is properly annotated with @pytest.mark.unit as required by coding guidelines, and the class docstring clearly documents the security model being tested.


1355-1400: LGTM!

Good security test case using os.path as an untrusted module. The assertions correctly verify that the SECURITY_VIOLATION error is raised and includes the module path in the message.


1402-1446: LGTM!

Correctly tests the positive case where a module matching the allowed prefixes is successfully imported when enforcement is enabled.


1448-1483: LGTM!

Important test for backwards compatibility. The assertion NodeBase.ENFORCE_TOOL_IMPORT_ALLOWLIST is False ensures the default remains opt-in.


1485-1528: LGTM!

Excellent test verifying that subclasses can completely override ALLOWED_TOOL_MODULE_PREFIXES without inheriting parent class values. This confirms the security policy is fully customizable.


1530-1544: LGTM!

Useful regression test ensuring the default allowed prefixes remain stable. This will catch accidental modifications to the security configuration.


1546-1583: LGTM!

Good test verifying that security errors include helpful context (module name, error type) without leaking sensitive information. The "malicious.module.BadClass" example clearly illustrates the threat model.

src/omnibase_core/validation/validation_utils.py (2)

337-412: LGTM - Excellent conflict detection with appropriate logging.

The implementation correctly:

  • Distinguishes between None (field not provided) and empty list (field provided but empty)
  • Uses O(n) set-based duplicate detection for efficiency (line 391)
  • Applies appropriate logging levels: DEBUG for normal operations, WARNING for potential issues
  • Handles case-sensitivity properly

The warn_empty_lists parameter provides useful feedback for potential patch definition errors without breaking validation.


653-699: LGTM - Improved error handling and observability.

The enhanced error handling properly implements the batch processing pattern documented in lines 23-24:

  • Returns False instead of raising exceptions to allow continued file discovery
  • Uses WARNING level for expected/recoverable errors (file access, encoding issues)
  • Provides clear context in log messages for debugging

The improved docstring (lines 662-670) clearly explains the design rationale, making the error handling strategy explicit for future maintainers.

docs/guides/CONTRACT_PATCHING_GUIDE.md (1)

787-813: LGTM! Documentation accurately reflects centralized error codes.

The error code updates align with the new EnumPatchValidationErrorCode enum and correctly document the severity levels and usage. The CONTRACT_PATCH_* prefix provides clear categorization.

src/omnibase_core/validation/contract_patch_validator.py (7)

22-50: LGTM! Module documentation and imports are well-organized.

The centralized error code enum import and comprehensive documentation in the module docstring provide clear guidance for maintainers.


244-347: LGTM! Centralized error codes are consistently applied.

All error reporting uses the enum-based codes with the .value accessor, ensuring type-safe and consistent error categorization across validation stages.


352-354: LGTM! Clear documentation of error handling flow.

The comment clarifies why ValidationError is not caught directly and documents the centralized error handling through ModelOnexError.


373-417: LGTM! Deterministic duplicate reporting improves testability.

The addition of complexity documentation and sorting of duplicates ensures consistent, predictable error messages. This is particularly valuable for automated testing and CI/CD pipelines.


472-528: LGTM! Terminology alignment improves clarity.

The rename from "descriptor" to "behavior" with updated documentation better reflects the semantic purpose of this validation. The rationale for INFO-level severity on empty patches is well-articulated.


558-625: LGTM! Enhanced version validation provides better user feedback.

The two-stage version validation (digit presence, then pattern matching) provides more specific feedback to users about what's wrong with their version string. Error codes are consistently applied.


64-69: Tighten SEMVER_PATTERN to match single constraint operators, not multiple.

The regex [~^>=<]* allows multiple operator characters (e.g., ~^1.0.0, >>1.0.0), but the documentation and npm/semver conventions indicate only single operators are valid. Since this is a warning-level check (not blocking), the permissiveness is safe, but the pattern could be more precise:

Change [~^>=<]* to ([~^]|[><]=?)? to match:

  • No operator: 1.0.0
  • Single tilde/caret: ^1.0.0, ~1.0
  • Single or double comparison: >=1.0.0, <2.0.0, =1.0.0

This prevents accepting invalid multi-operator constraints while maintaining the warning-only behavior.

Comment thread tests/unit/services/test_service_capability_resolver.py Outdated
…s (OMN-1126)

- Document intentional scoring design difference between
  ModelRequirementSet.matches() (1.0 base + 0.1/pref) and
  ServiceCapabilityResolver._score_providers() (0.0 base + 1.0/pref)
- Add 26 new tests for matches() method validating scoring behavior
- Remove unnecessary DEBUG logging for empty list normalization
- Remove unused imports (logging, ValidationInfo) from model_contract_patch.py
- Keep _collect_conflicts() mutation pattern (well-documented, well-tested)
…dation

Resolved conflicts in validation_utils.py and test_validation_utils.py:
- Adopted main branch's specific exception handling (OSError, ValueError,
  AttributeError, TypeError) instead of catch-all Exception
- Changed logging level from WARNING to DEBUG for expected errors
- Updated tests to expect RuntimeError propagation for unexpected exceptions
@claude

claude Bot commented Jan 2, 2026

Copy link
Copy Markdown

PR Review: Contract Patching Infrastructure (OMN-1126)

This PR implements a comprehensive contract patching system with strong design principles and excellent code quality.

Strengths

Architecture & Design

  • Clear separation of concerns with patch model, validator, and utilities
  • Well-executed "patches, not full contracts" principle
  • Immutability by design (frozen=True)
  • Security-first with module allowlists

Code Quality

  • 107+ test cases with excellent edge case coverage
  • DRY principles with shared validation utilities
  • Consistent error codes (EnumPatchValidationErrorCode)
  • 100% mypy strict compliance
  • Google-style docstrings throughout

Security

  • Defense-in-depth with ALLOWED_MODULE_PREFIXES
  • Clear trust model documentation
  • Logging on security violations

Documentation

  • 978-line CONTRACT_PATCHING_GUIDE.md
  • Comprehensive inline documentation
  • Security warnings for dynamic imports

Minor Observations

1. Scoring Algorithm Change (model_capability_requirement_set.py:536-630)
PR changes matches() scoring from 0.0 base + 1.0 per preference to 1.0 base + 0.1 per preference. Was this intentional for OMN-1126? Consider documenting if this is a bugfix.

2. Timeout Upper Bound
PR description mentions max 3,600,000ms validation. Should ModelDescriptorPatch.timeout_ms have Field(le=3_600_000)?

3. List Normalization
Empty lists → None normalization is good design. Consider adding YAML examples to CONTRACT_PATCHING_GUIDE.md.

Recommendations

High Priority

  • Verify timeout upper bound validation is enforced
  • Document capability scoring change if intentional

Medium Priority

  • Add empty list normalization section to guide
  • Add tests for new scoring behavior

Low Priority

  • Consider docs/security/DYNAMIC_IMPORTS.md
  • Add examples/contracts/ with sample YAMLs

CI/CD Compliance

All CLAUDE.md standards met:

  • ModelOnexError with structured codes
  • Frozen Pydantic models
  • PEP 604 union syntax
  • Thread safety documented
  • Poetry usage

Final Verdict

APPROVED - Excellent Work

Production-ready code demonstrating:

  • Clean architecture
  • Comprehensive testing (107 tests)
  • Security-first design
  • Full standards compliance

Recommendation: Merge after verifying timeout validation. Other suggestions are optional enhancements.

Great work @jonahgabriel! 🎉

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
tests/unit/validation/test_validation_utils.py (1)

769-781: Test will fail: log level capture mismatch.

Line 773 sets caplog.at_level(logging.WARNING), which only captures logs at WARNING level (30) and above. However, the implementation at validation_utils.py:702 logs at DEBUG level (10), and line 778 asserts levelname == "DEBUG".

Since DEBUG < WARNING, the DEBUG log will not be captured, resulting in len(caplog.records) == 0 instead of the expected 1.

🔎 Proposed fix
-        with caplog.at_level(logging.WARNING):
+        with caplog.at_level(logging.DEBUG):
             result = is_protocol_file(nonexistent_path)
 
             assert result is False
♻️ Duplicate comments (1)
src/omnibase_core/mixins/mixin_event_listener.py (1)

10-39: Documentation still references incorrect method name.

Line 15 still references _get_input_state_from_node_module, but the actual method name is _get_input_state_class (line 799).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a157f2b and b52e8d3.

📒 Files selected for processing (10)
  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/mixins/mixin_event_listener.py
  • src/omnibase_core/models/capabilities/model_capability_requirement_set.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/core/model_examples_collection.py
  • src/omnibase_core/utils/util_safe_yaml_loader.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/models/capabilities/test_model_capability_requirement_set.py
  • tests/unit/validation/test_validation_utils.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/omnibase_core/models/core/model_examples_collection.py
  • src/omnibase_core/utils/util_safe_yaml_loader.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 604 union syntax for type annotations (e.g., str | None instead of Optional[str])
Resolve services using protocol names (e.g., container.get_service('ProtocolEventBus')) never using concrete class names
Order exception tuples alphabetically by exception class name (e.g., except (AttributeError, TypeError, ValidationError) not except (ValueError, TypeError, AttributeError))
Format code using poetry run black and sort imports using poetry run isort
Lint code with poetry run ruff check and fix automatically with poetry run ruff check --fix

Files:

  • src/omnibase_core/infrastructure/node_base.py
  • tests/unit/models/capabilities/test_model_capability_requirement_set.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/validation/test_validation_utils.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/mixins/mixin_event_listener.py
  • src/omnibase_core/models/capabilities/model_capability_requirement_set.py
  • src/omnibase_core/validation/validation_utils.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators to eliminate try/catch boilerplate
Add standardized exception handling comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using broad exception handlers
Use centralized exception group constants from omnibase_core.errors.exception_groups instead of individual exception types
Ensure all source code passes mypy strict mode with zero errors and pyright basic mode validation
Use FileRegistry for loading YAML contracts and configuration files instead of direct file reading
Use duck typing with protocols for service resolution - never use isinstance checks for protocol-based dependencies

Files:

  • src/omnibase_core/infrastructure/node_base.py
  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/mixins/mixin_event_listener.py
  • src/omnibase_core/models/capabilities/model_capability_requirement_set.py
  • src/omnibase_core/validation/validation_utils.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to classify test types
All tests must complete within 60-second timeout protection to ensure CI performance

Files:

  • tests/unit/models/capabilities/test_model_capability_requirement_set.py
  • tests/unit/infrastructure/test_node_base.py
  • tests/unit/validation/test_validation_utils.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution
Do NOT use from __future__ import annotations in Pydantic model files (use only for forward references and circular import prevention)

Files:

  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/capabilities/model_capability_requirement_set.py
🧠 Learnings (7)
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Do NOT share node instances across threads - use thread-local instances or separate instances per thread

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use declarative YAML contracts for node configuration and workflow definitions (not imperative code)

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use ModelONEXContainer (not ModelContainer) for dependency injection in node constructors

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/models/**/*.py : Do NOT use `from __future__ import annotations` in Pydantic model files (use only for forward references and circular import prevention)

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception

Applied to files:

  • src/omnibase_core/infrastructure/node_base.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/models/**/*.py : Add `from_attributes=True` to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution

Applied to files:

  • src/omnibase_core/models/contracts/model_contract_patch.py
  • src/omnibase_core/models/capabilities/model_capability_requirement_set.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use mixins (MixinDiscoveryResponder, MixinEventHandler, MixinEventListener, MixinNodeExecutor, MixinNodeLifecycle, MixinRequestResponseIntrospection, MixinWorkflowExecution) for composable node behaviors

Applied to files:

  • src/omnibase_core/mixins/mixin_event_listener.py
🧬 Code graph analysis (5)
src/omnibase_core/infrastructure/node_base.py (1)
src/omnibase_core/models/errors/model_onex_error.py (1)
  • error_code (253-255)
tests/unit/models/capabilities/test_model_capability_requirement_set.py (1)
src/omnibase_core/models/capabilities/model_capability_requirement_set.py (2)
  • ModelRequirementSet (244-651)
  • matches (543-631)
tests/unit/validation/test_validation_utils.py (1)
src/omnibase_core/validation/validation_utils.py (1)
  • detect_add_remove_conflicts (337-412)
src/omnibase_core/models/contracts/model_contract_patch.py (3)
src/omnibase_core/models/contracts/model_handler_spec.py (1)
  • ModelHandlerSpec (87-257)
src/omnibase_core/models/contracts/model_capability_provided.py (1)
  • ModelCapabilityProvided (26-126)
src/omnibase_core/validation/validation_utils.py (1)
  • detect_add_remove_conflicts (337-412)
src/omnibase_core/validation/validation_utils.py (1)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • is_protocol (319-321)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: claude-review
🔇 Additional comments (23)
tests/unit/models/capabilities/test_model_capability_requirement_set.py (5)

1786-1809: Well-documented test class with clear scoring design rationale.

The docstring effectively documents the intentional difference between matches() scoring (1.0 base + 0.1 per preference) and ServiceCapabilityResolver._score_providers() scoring (0.0 base + 1.0 per preference). This cross-reference is valuable for future maintainers.


1811-1866: Comprehensive test coverage for must and forbid constraint matching.

The tests correctly verify:

  • Empty requirements match any provider with base score 1.0
  • Must constraint satisfaction and failure scenarios (value mismatch, missing key)
  • Forbid constraint satisfaction (non-matching value, missing key) and violation

All assertions align with the implementation's return format (bool, float, list[str]).


1885-1951: Preference scoring tests correctly validate the 0.1 increment design.

The tests properly verify:

  • Single preference adds 0.1 (score = 1.1)
  • Multiple preferences add 0.1 each (3 preferences = 1.3)
  • Unsatisfied preferences generate warnings with actual vs expected values
  • Missing preference keys show <missing> in warnings
  • Partial satisfaction yields partial score with appropriate warnings

Good use of pytest.approx() for floating-point assertions.


2068-2116: Excellent documentation of scoring design as executable tests.

The TestModelRequirementSetMatchesScoringDesign class serves dual purposes:

  1. Documents the intentional difference from _score_providers()
  2. Provides executable specification with clear assertions

The descriptive assertion messages (e.g., "Base score must be 1.0 for valid match...") are particularly valuable for debugging test failures.


2151-2188: Quality differentiation test demonstrates practical scoring utility.

This test effectively shows that the scoring system enables ranking among valid matches (1.0 < 1.2 < 1.5), which is the core use case for the matches() method's quality score.

src/omnibase_core/models/capabilities/model_capability_requirement_set.py (4)

55-56: Appropriate decorator import for dynamic provider handling.

The allow_dict_any decorator is correctly imported to enable dynamic key-value handling for external provider capabilities.


540-546: Decorator usage correctly documents the type widening rationale.

The @allow_dict_any decorator with its descriptive reason string appropriately justifies why provider: dict[str, Any] is used instead of the stricter dict[str, JsonType]. This is a pragmatic choice for handling dynamic external system data.


547-605: Comprehensive docstring with clear scoring design rationale.

The docstring effectively documents:

  • Matching logic for all four constraint tiers
  • Scoring formula (1.0 base + 0.1 per preference)
  • Intentional difference from _score_providers() with clear justification
  • Practical examples showing expected return tuples

This level of documentation helps prevent future confusion about the scoring design.


606-631: Clean implementation with efficient early returns.

The implementation correctly:

  • Returns (False, 0.0, []) on must/forbid failures (no warnings for hard failures)
  • Initializes base score to 1.0 for valid matches
  • Adds 0.1 per satisfied preference
  • Generates informative warning messages with repr() for proper value formatting
  • Ignores hints (consistent with "advisory only" documentation)

The early return pattern for hard constraint failures is efficient and prevents unnecessary preference processing.

tests/unit/infrastructure/test_node_base.py (1)

1337-1583: Excellent security test coverage!

The TestNodeBaseSecurityAllowlist test class provides comprehensive coverage of the security allowlist feature, including:

  • Blocking untrusted modules when enforcement is enabled
  • Allowing trusted modules
  • Default non-enforcement behavior
  • Subclass customization
  • Default prefix validation
  • Error context verification

The tests are well-structured and properly document the security model.

src/omnibase_core/infrastructure/node_base.py (2)

137-151: Well-designed security configuration.

The security class variables follow good practices:

  • ALLOWED_TOOL_MODULE_PREFIXES includes all expected framework packages
  • ENFORCE_TOOL_IMPORT_ALLOWLIST defaults to False for backward compatibility
  • Both use ClassVar type hints correctly
  • Clear documentation explains the security model

The opt-in approach (default False) is appropriate for preventing breaking changes while enabling strict security when needed.


408-444: Security validation correctly implemented.

The allowlist validation is properly positioned before the importlib.import_module() call, preventing malicious imports. Key strengths:

  • Pre-import validation prevents code execution
  • Uses SECURITY_VIOLATION error code from EnumCoreErrorCode
  • Provides clear error context including module and allowed prefixes
  • Extensive inline comments document the security model
src/omnibase_core/validation/validation_utils.py (3)

165-222: Enhanced validation with helpful warnings.

The warn_empty_list parameter addition improves usability by allowing non-fatal warnings for potentially incorrect input. The implementation correctly:

  • Distinguishes between reject_empty_list (raises) and warn_empty_list (logs)
  • Returns early after warning to avoid unnecessary processing
  • Provides clear warning message suggesting alternatives

337-412: Improved conflict detection with better observability.

The enhancements to detect_add_remove_conflicts improve debugging and error detection:

  • warn_empty_lists parameter helps catch potential user errors
  • Debug logging for no-conflict cases aids troubleshooting
  • O(n) set-based implementation maintains efficiency
  • Clear separation of warning vs debug log levels

665-706: Enhanced documentation and logging.

The improvements to is_protocol_file enhance maintainability:

  • Expanded docstring clarifies batch processing behavior
  • Debug logging added for protocol detection results
  • Exception handling remains appropriate for batch operations
tests/unit/validation/test_validation_utils.py (1)

1087-1230: Comprehensive test coverage for conflict detection.

The TestDetectAddRemoveConflicts test class provides excellent coverage:

  • Basic conflict detection (single and multiple)
  • Edge cases (None inputs, empty lists)
  • Case sensitivity variations (default and explicit)
  • Warning behavior verification with caplog
  • Logging at different levels (WARNING for conflicts, DEBUG for no conflicts)

All tests are well-structured and properly verify the expected behavior.

src/omnibase_core/models/contracts/model_contract_patch.py (7)

48-52: LGTM: Module-level constants provide single source of truth.

Defining _MAX_LIST_ITEMS and _MAX_CAPABILITY_LIST_ITEMS at module scope before the class definition is the correct approach, as Field() declarations evaluate at class definition time. This centralization avoids duplication across the 10+ list operation fields and ensures consistency.


224-305: LGTM: Consistent use of module constants in field declarations.

All 10 list operation fields now reference the module-level constants (_MAX_LIST_ITEMS or _MAX_CAPABILITY_LIST_ITEMS) for their max_length constraints, eliminating duplication and ensuring consistency. The ClassVar attributes at lines 224-230 provide a clean public API for accessing these limits.


311-363: LGTM: Empty list normalization improves patch semantics consistency.

The normalize_empty_lists_to_none validator correctly implements the principle that an empty list in a list operation (e.g., handlers__add=[]) is semantically equivalent to "no operation" and should be normalized to None. Key design points:

  • Runs first (mode="before"): Simplifies type narrowing in downstream validators, which can now assume either None or a non-empty list.
  • Broad type signature (list[object]): Correctly handles all list field types (ModelHandlerSpec, ModelDependency, str, ModelCapabilityProvided).
  • Well-documented: Equivalence examples and type narrowing explanation clarify the design rationale.

365-476: LGTM: Field validators properly delegate to shared utilities.

All field validators correctly delegate to shared validation utilities (validate_onex_name_list, validate_string_list) from validation_utils, promoting DRY and consistency. The validators apply appropriate transformations:

  • Handler and capability names: normalized to lowercase for case-insensitive matching
  • Dependencies: minimum length of 2 characters
  • Event types: basic string validation

482-531: LGTM: Mutation pattern is intentional and well-documented.

The _collect_conflicts method uses an in-place mutation pattern (appending to all_conflicts) which is thoroughly documented and justified:

  • Performance: Reduces allocations when checking 5+ field pairs in validate_no_add_remove_conflicts
  • Simplicity: Avoids intermediate list creation and flattening
  • Pattern: Follows the collector pattern common in validation scenarios

The extensive documentation (lines 495-526) clearly explains the type narrowing logic, mutation rationale, and design tradeoffs. This pattern was retained after PR review (per PR description) and is well-tested with 62 passing tests.


537-639: LGTM: Model validators provide comprehensive cross-field validation.

Both model validators correctly enforce important invariants:

  1. validate_identity_consistency (lines 537-564): Ensures new contracts specify both name and node_version, preventing partial identity definitions. The XOR check and error messages are clear.

  2. validate_no_add_remove_conflicts (lines 566-639): Systematically checks all 5 list operation pairs for conflicts. Correctly extracts names from model objects (ModelHandlerSpec, ModelDependency, ModelCapabilityProvided) and accumulates all conflicts before raising a comprehensive error message.

Both use mode="after" which is appropriate for cross-field validation.


645-780: LGTM: Helper methods provide a clean query API.

The helper properties and methods offer a well-designed interface for introspecting patch operations:

  • Properties (is_new_contract, is_override_only): Clear boolean checks for patch classification
  • has_list_operations: Efficiently checks all 10 list fields using any() with a generator
  • get_add_operations/get_remove_operations: Return dictionaries of non-None operations, using defensive copying (list(...)) which is appropriate for the frozen model
  • __repr__: Provides concise, informative output distinguishing new contracts from override patches

Change 3 tests to check error_code instead of message text:
- test_should_block_untrusted_module_when_allowlist_enforced
- test_subclass_can_customize_allowed_prefixes
- test_security_error_includes_context

The error code is EnumCoreErrorCode.SECURITY_VIOLATION but the
message text doesn't contain "SECURITY_VIOLATION" string.
…dation

Resolved conflict in model_examples_collection.py:
- Combined pydantic imports from both branches (ValidationError, computed_field, model_validator)
- Kept ModelOnexError and EnumCoreErrorCode imports for error handling

Note: Pre-existing hook violations from main (validators_metadata.py naming,
backward-compat comment) tracked separately.
@claude

claude Bot commented Jan 3, 2026

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

Overall Assessment

This is a well-architected, thoroughly tested PR that implements a robust contract patching system. The code demonstrates excellent engineering practices with comprehensive documentation, strong type safety, and defensive programming patterns.

Recommendation: APPROVE ✅


Strengths

1. Exceptional Documentation 📚

  • Model docstrings (model_contract_patch.py:55-155): 100+ line docstring with architecture diagrams, semantic explanations, and code examples
  • Error code documentation (enum_patch_validation_error_code.py): Every error code has clear docstring explaining when it's used
  • Security documentation in NodeBase (node_base.py:12-46): Comprehensive explanation of dynamic import risks and mitigation strategies
  • Inline comments explain non-obvious design decisions (e.g., empty list normalization rationale)

2. Strong Type Safety 🔒

  • Consistent use of ConfigDict(frozen=True, extra="forbid", from_attributes=True)
  • PEP 604 union syntax (str | None instead of Optional[str])
  • Comprehensive field validators with proper type narrowing
  • All error codes centralized in EnumPatchValidationErrorCode enum

3. Excellent Validation Architecture ✅

  • Layered validation: Pydantic structural → semantic → behavioral
  • Separation of concerns: Model validation in Pydantic, semantic validation in ContractPatchValidator
  • Conflict detection happens at model construction time (fail-fast principle)
  • validate_no_add_remove_conflicts() (model_contract_patch.py:567) uses mutation pattern efficiently documented

4. Security-First Design 🔐

  • NodeBase dynamic import protection (node_base.py:408-450):
    • Optional allowlist validation via ENFORCE_TOOL_IMPORT_ALLOWLIST
    • Clear security comments explaining threat model
    • Default-off (opt-in) for backward compatibility
  • Similar patterns in mixins (mixin_event_listener.py, mixin_introspect_from_contract.py)
  • Namespace validation prevents arbitrary module imports

5. Comprehensive Test Coverage 🧪

  • 62 new tests according to PR description
  • Tests cover positive cases, negative cases, edge cases, and Pydantic validation failures
  • Proper use of pytest fixtures for reusable test data
  • Tests verify error codes are correctly assigned

6. Code Quality Best Practices

  • DRY principle: Shared validation logic in validation_utils.py
  • detect_add_remove_conflicts() reused across multiple field pairs
  • Empty list normalization (model_contract_patch.py:325-363) prevents subtle bugs
  • Clear separation between __add and __remove operations

Areas for Improvement

1. Scoring Logic Divergence ⚠️

Location: model_capability_requirement_set.py:574-582

The scoring scheme in matches() (1.0 base + 0.1 per preference) intentionally differs from ServiceCapabilityResolver._score_providers() (0.0 base + 1.0 per preference). While the docstring explains this at lines 568-582, this creates cognitive overhead.

Suggestion: Consider documenting this divergence in a central architecture doc, or unifying the scoring if semantically equivalent.

2. List Length Constants 📏

Location: model_contract_patch.py:48-52

Module-level constants _MAX_LIST_ITEMS and _MAX_CAPABILITY_LIST_ITEMS are defined at module scope to work around Pydantic Field() evaluation timing. This is pragmatic but creates a disconnect from the ClassVar pattern used elsewhere in the codebase.

Suggestion: Add a comment explaining why ClassVar couldn't be used here (Pydantic Field() evaluation happens at class definition time, before ClassVar is available).

3. Mutation Pattern Documentation 📝

Location: model_contract_patch.py:488-532

The _collect_conflicts() method mutates all_conflicts in-place, which is well-documented but deviates from functional programming norms. The docstring explains this choice clearly (lines 515-526), but developers unfamiliar with the collector pattern might be surprised.

Suggestion: No change needed - documentation is excellent. Just flagging for awareness.

4. Security: Default-Off Allowlist 🔓

Location: node_base.py:147-149

ENFORCE_TOOL_IMPORT_ALLOWLIST = False means dynamic import validation is opt-in. While this preserves backward compatibility, it means most deployments won't benefit from the security feature unless explicitly enabled.

Recommendation:

  • Current approach is correct for v0.4.0 (no breaking changes)
  • Consider making this default-on in v1.0.0 with clear migration guide
  • Add a startup warning if running with allowlist disabled in production environments

5. Empty Descriptor Severity ℹ️

Location: contract_patch_validator.py:505-513

Empty descriptor patches generate INFO severity instead of WARNING. The docstring explains the rationale (lines 497-500), but this feels lenient. An empty descriptor is almost always a user mistake.

Suggestion: Consider making this a WARNING in a future version, as it's likely unintentional.


Performance Considerations

✅ Efficient Algorithms

  • _find_duplicates_in_list() (contract_patch_validator.py:362-384): O(n) time complexity with single pass
  • Pre-compiled regex patterns in validation_utils.py (lines 57-78) for repeated validations
  • List comprehensions used appropriately (e.g., model_contract_patch.py:714-724)

✅ No Performance Concerns

  • Validation happens at contract load time, not hot path
  • Mutation pattern in _collect_conflicts() reduces allocations

Security Assessment

✅ Strong Security Posture

  1. Dynamic Import Protection: NodeBase implements optional allowlist validation
  2. YAML Safety: Uses yaml.safe_load() via util_safe_yaml_loader
  3. Namespace Validation: Mixins validate module namespaces before imports
  4. Trust Model Documented: Clear documentation of trust assumptions (node_base.py:18-46)

🔒 No Security Issues Found

  • All user input validated via Pydantic models
  • No SQL injection, XSS, or command injection vectors
  • File paths validated before use
  • Error messages don't leak sensitive information

Testing Coverage

✅ Comprehensive Test Suite

  • Unit tests: 62 new tests across multiple test files
  • Positive cases: Valid patches pass validation
  • Negative cases: Invalid patches fail with correct error codes
  • Edge cases: Empty lists, conflicting operations, missing fields
  • Pydantic integration: Tests verify ValidationError is raised appropriately

📊 Estimated Coverage Impact

  • PR adds 2,369 lines, deletes 368 lines
  • All new models have corresponding test files
  • Validator has dedicated test file with 200+ lines of tests
  • Coverage requirement: 60% minimum (likely exceeded)

Code Style Compliance

✅ Follows Project Conventions

  • PEP 604 syntax: Uses str | None throughout
  • Exception handling: Proper use of # fallback-ok: markers
  • Error codes: All use CONTRACT_PATCH_* prefix (consistent naming)
  • Logging conventions: DEBUG/INFO/WARNING/ERROR used correctly
  • Type annotations: 100% annotated, mypy strict mode compliant

✅ Documentation Standards Met

  • All public functions have docstrings
  • Docstrings follow Google/NumPy style
  • .. versionadded:: 0.4.0 tags present
  • See Also: sections link related code

Potential Bugs

✅ No Bugs Found

I reviewed the code carefully and found no logic errors, race conditions, or off-by-one errors. The validation logic is sound and well-tested.


Integration Concerns

✅ Clean Integration

  • No breaking changes: All changes are additive
  • Backward compatible: Existing contracts continue to work
  • Clear migration path: Documented in CLAUDE.md and migration guide
  • CI passing: Pre-commit hooks and tests documented as passing (except pre-existing issues)

Final Recommendations

  1. Merge this PR - it's ready for production ✅
  2. Consider future enhancements:
    • Make ENFORCE_TOOL_IMPORT_ALLOWLIST default-on in v1.0.0
    • Unify scoring logic between matches() and _score_providers()
    • Add architecture doc for scoring divergence
  3. Monitor in production:
    • Track how often empty descriptor patches appear in user contracts
    • Consider promoting INFO to WARNING if it becomes a common mistake

Conclusion

This PR demonstrates exceptional engineering quality. The contract patching system is well-designed, thoroughly tested, and production-ready. The security considerations are thoughtfully addressed, and the documentation is exemplary.

Great work! 🎉


Metadata

  • Files Changed: 57
  • Lines Added: 2,369
  • Lines Deleted: 368
  • New Tests: 62
  • Related Issue: OMN-1126
  • Version: 0.4.0

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
tests/unit/enums/test_enum_likelihood.py (1)

424-431: Remove the incorrect docstring note about ValueError.

The implementation successfully raises ModelOnexError using a lazy import that resolves the circular dependency. The docstring note at lines 425-426 is incorrect and should be removed; the test expectations (line 430) correctly expect ModelOnexError.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 604f78a and b33b028.

📒 Files selected for processing (4)
  • scripts/validation/validate-string-versions.py
  • src/omnibase_core/models/core/model_examples_collection.py
  • src/omnibase_core/schemas/projector_contract.schema.json
  • tests/unit/enums/test_enum_likelihood.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/omnibase_core/models/core/model_examples_collection.py
  • scripts/validation/validate-string-versions.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 604 union syntax for type annotations (e.g., str | None instead of Optional[str])
Resolve services using protocol names (e.g., container.get_service('ProtocolEventBus')) never using concrete class names
Order exception tuples alphabetically by exception class name (e.g., except (AttributeError, TypeError, ValidationError) not except (ValueError, TypeError, AttributeError))
Format code using poetry run black and sort imports using poetry run isort
Lint code with poetry run ruff check and fix automatically with poetry run ruff check --fix

Files:

  • tests/unit/enums/test_enum_likelihood.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to classify test types
All tests must complete within 60-second timeout protection to ensure CI performance

Files:

  • tests/unit/enums/test_enum_likelihood.py
🧠 Learnings (1)
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception

Applied to files:

  • tests/unit/enums/test_enum_likelihood.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: claude-review
🔇 Additional comments (1)
src/omnibase_core/schemas/projector_contract.schema.json (1)

360-360: LGTM!

Trailing newline addition is a standard formatting practice enforced by most linters and pre-commit hooks. No functional changes to the schema.

- Fix test_validation_utils.py: capture at DEBUG level to match expected log
- Fix model_provider_descriptor.py: use duck-typing for ModelHealthStatus
  validation to handle pytest-xdist class identity issues
- Update CONTRACT_PATCHING_GUIDE.md: align comment terminology with section title
@claude

claude Bot commented Jan 3, 2026

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

Summary

This PR implements a well-designed contract patching system with strong validation infrastructure. The core principle "User authored files are patches, not full contracts" is implemented thoughtfully with comprehensive models, validators, and tests.


✅ Strengths

1. Excellent Architecture & Documentation

  • Clear separation of concerns: ModelContractPatch for data, ContractPatchValidator for validation
  • Comprehensive docstrings with examples, rationale explanations, and architectural context
  • The three-layer architecture (Profile → Behavior → Contract) is well-documented
  • Good use of type hints and Pydantic strict mode (extra="forbid")

2. Strong Error Handling

  • New EnumPatchValidationErrorCode enum provides type-safe error categorization
  • All error codes follow consistent CONTRACT_PATCH_* prefix convention
  • Error messages are descriptive and include suggestions for resolution
  • Validation uses proper severity levels (ERROR, WARNING, INFO)

3. Security Considerations

  • NodeBase._resolve_main_tool() now has optional allowlist validation for dynamic imports
  • Comprehensive security documentation in docstrings (trust model, attack vectors, defense-in-depth)
  • Similar pattern to ModelReference.ALLOWED_MODULE_PREFIXES for consistency
  • Default is opt-in (security feature disabled by default), which is appropriate for v0.4.0

4. Comprehensive Testing

  • 62 new tests covering validation paths
  • Good test organization with fixtures for reusable components
  • Tests cover edge cases (empty descriptors, purity/idempotent mismatches, duplicate detection)

5. Code Quality

  • Follows project conventions (Poetry, mypy strict, ruff, type annotations)
  • Good use of shared validation utilities (validation_utils.py) to reduce duplication
  • Empty list normalization is well-explained with rationale in docstrings

🔍 Issues & Suggestions

CRITICAL: Security - Dynamic Import Allowlist

Location: src/omnibase_core/infrastructure/node_base.py:380-445

The new allowlist validation for dynamic tool imports has a logic flaw:

if self.ENFORCE_TOOL_IMPORT_ALLOWLIST:
    if not any(
        module_path.startswith(prefix)
        for prefix in self.ALLOWED_TOOL_MODULE_PREFIXES
    ):
        raise ModelOnexError(...)

# SECURITY: Dynamic import executes module initialization code.
module = importlib.import_module(module_path)

Problem: When ENFORCE_TOOL_IMPORT_ALLOWLIST = False (the default), there is NO validation at all before the dynamic import. This defeats the purpose of having the allowlist infrastructure.

Recommendation:

  1. Option A (Strictest): Enable validation by default (ENFORCE_TOOL_IMPORT_ALLOWLIST = True) for defense-in-depth
  2. Option B (Moderate): Log a WARNING when validation is disabled and a non-allowlisted module is imported
  3. Option C (Minimal): Add a comment explaining why default-off is intentional for v0.4.0 (backwards compatibility?)

Severity: High - This is a security feature that provides no protection by default.


MAJOR: Scoring Logic Inconsistency

Location: src/omnibase_core/models/capabilities/model_capability_requirement_set.py:537-629

The matches() method has a scoring scheme that differs from ServiceCapabilityResolver._score_providers():

# matches() scoring:
score = 1.0  # Base score
for key in prefer:
    if key matches:
        score += 0.1  # Incremental bonus

# _score_providers() scoring (mentioned in docstring):
# 0.0 base + 1.0 per preference

Issues:

  1. The docstring explains the difference but it's not clear if this is intentional or a bug
  2. The 0.1 increment seems arbitrary - why not 1.0 for consistency?
  3. Having two different scoring systems for the same concept (preference matching) is confusing

Recommendation:

  • If this is intentional, document why different scoring systems are needed (current docstring explains what but not why convincingly)
  • If not intentional, align the scoring systems or refactor to use a shared scoring function
  • Consider making the increment configurable if there's a valid use case for different weights

Severity: Medium - Could lead to unexpected ranking behavior or bugs in capability resolution.


MINOR: Exception Handling Comment Markers

Location: Multiple files in the PR

The PR updates exception handling to use standardized markers like # fallback-ok:, # catch-all-ok:, etc. This is great for documentation! However:

Issue: The split() → splitlines() changes in validate-string-versions.py seem unrelated to this PR's scope.

# scripts/validation/validate-string-versions.py
-        source_lines = content.split("\n")
+        source_lines = content.splitlines()

Recommendation: These changes are correct (splitlines() handles \r\n better), but they should be in a separate PR focused on code quality/consistency improvements, not bundled with contract patching infrastructure.

Severity: Low - Not a blocker, but reduces PR clarity.


MINOR: Empty List Normalization - Edge Case

Location: src/omnibase_core/models/contracts/model_contract_patch.py:325-363

The normalization validator converts [] to None:

@field_validator("handlers__add", ..., mode="before")
@classmethod
def normalize_empty_lists_to_none(cls, v):
    if v is not None and len(v) == 0:
        return None
    return v

Issue: This prevents users from explicitly distinguishing between:

  • "I didn't specify this field" (None)
  • "I explicitly want to clear this list" ([])

While the docstring explains the semantic equivalence, there may be use cases where explicit empty lists signal intent (e.g., "remove all handlers").

Recommendation:

  • Document in the class docstring whether handlers__remove=[] is a valid way to signal "remove all handlers"
  • If the answer is "no", this normalization is correct
  • If the answer is "maybe in the future", consider keeping empty lists distinct

Severity: Low - Current design is reasonable, but worth clarifying.


📊 Performance Considerations

  • ModelCapabilityRequirementSet.matches() iterates through must/forbid/prefer dictionaries - O(n) complexity is appropriate
  • ContractPatchValidator._find_duplicates_in_list() uses set-based duplicate detection - O(n) time, O(n) space, optimal
  • No performance concerns identified

🧪 Test Coverage

Good:

  • 62 new tests covering validation paths
  • Tests for error codes, warnings, and info messages
  • Edge case coverage (empty descriptors, conflicts, duplicates)

Suggestions:

  • Add a test for ENFORCE_TOOL_IMPORT_ALLOWLIST=True to verify allowlist validation works
  • Add integration tests for the full patching workflow (load YAML → validate → merge)
  • Consider property-based tests for list operation conflict detection (hypothesis library)

🔒 Security Assessment

Positive:

  • Good security documentation in docstrings
  • Allowlist pattern follows ModelReference precedent
  • YAML parsing uses safe_load() (via util_safe_yaml_loader.py)

Concerns:

  1. Allowlist disabled by default (see CRITICAL issue above)
  2. No runtime checks on contract file sources - relies entirely on "trusted sources" assumption
  3. No audit logging for dynamic imports - consider logging all tool class imports for security monitoring

Recommendations:

  • Enable allowlist validation by default OR add warning logs
  • Consider adding a ModelContractPatch.from_trusted_source() factory method that enforces validation
  • Add audit logging for all importlib.import_module() calls in NodeBase._resolve_main_tool()

✅ Convention Compliance

  • ✅ Uses Poetry for dependency management
  • ✅ Follows PEP 604 union syntax (str | None)
  • ✅ Uses ModelOnexError with error codes (not generic Exception)
  • ✅ Proper use of ModelONEXContainer in node constructors
  • ✅ Exception tuples are alphabetically ordered
  • ✅ Comprehensive docstrings with examples

📝 Final Recommendation

Overall Assessment: ✅ Approve with minor fixes

This is a well-designed PR with strong architecture, good testing, and comprehensive documentation. The contract patching infrastructure is sound and follows ONEX conventions.

Required Changes Before Merge:

  1. CRITICAL: Address the security allowlist default-off issue (enable by default OR add warning logs)
  2. MAJOR: Clarify/fix the scoring inconsistency in ModelCapabilityRequirementSet.matches()

Optional Improvements:

  • Split unrelated splitlines() changes into a separate PR
  • Add integration tests for full patching workflow
  • Add audit logging for dynamic imports

Great work on this feature! The contract patching system will be a valuable addition to ONEX. 🚀


Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2026-01-03
PR: #289 | Issue: OMN-1126

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/omnibase_core/models/providers/model_provider_descriptor.py (1)

286-293: Consider adding exception handling marker for code standards.

The broad except Exception handler is appropriate here since this is a validation boundary converting external input. Consider adding a # boundary-ok: marker per project conventions to document this design choice:

🔎 Suggested addition
        if isinstance(v, dict):
            # Allow dict-based construction for Pydantic compatibility
            try:
                return ModelHealthStatus(**v)
-            except Exception as e:
+            except Exception as e:  # boundary-ok: validation boundary for external input
                raise ModelOnexError(

Based on learnings, standardized exception handling comment markers should be used when using broad exception handlers.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b33b028 and e343fa0.

📒 Files selected for processing (3)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • tests/unit/validation/test_validation_utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/validation/test_validation_utils.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 604 union syntax for type annotations (e.g., str | None instead of Optional[str])
Resolve services using protocol names (e.g., container.get_service('ProtocolEventBus')) never using concrete class names
Order exception tuples alphabetically by exception class name (e.g., except (AttributeError, TypeError, ValidationError) not except (ValueError, TypeError, AttributeError))
Format code using poetry run black and sort imports using poetry run isort
Lint code with poetry run ruff check and fix automatically with poetry run ruff check --fix

Files:

  • src/omnibase_core/models/providers/model_provider_descriptor.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators to eliminate try/catch boilerplate
Add standardized exception handling comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using broad exception handlers
Use centralized exception group constants from omnibase_core.errors.exception_groups instead of individual exception types
Ensure all source code passes mypy strict mode with zero errors and pyright basic mode validation
Use FileRegistry for loading YAML contracts and configuration files instead of direct file reading
Use duck typing with protocols for service resolution - never use isinstance checks for protocol-based dependencies

Files:

  • src/omnibase_core/models/providers/model_provider_descriptor.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution
Do NOT use from __future__ import annotations in Pydantic model files (use only for forward references and circular import prevention)

Files:

  • src/omnibase_core/models/providers/model_provider_descriptor.py
🧠 Learnings (1)
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/**/*.py : Use duck typing with protocols for service resolution - never use isinstance checks for protocol-based dependencies

Applied to files:

  • src/omnibase_core/models/providers/model_provider_descriptor.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Quality
  • GitHub Check: claude-review
🔇 Additional comments (1)
docs/guides/CONTRACT_PATCHING_GUIDE.md (1)

787-813: Documentation verified: all error codes properly defined and implemented.

All 11 error codes documented in the validation tables (lines 787–813) are correctly defined in EnumPatchValidationErrorCode and actively used throughout ContractPatchValidator. Error codes follow the CONTRACT_PATCH_* prefix convention consistently across both enum and implementation, matching the documentation.

Comment on lines +280 to +281
# Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
if type(v).__name__ == "ModelHealthStatus":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add module check for safer duck-type validation.

The class name check alone is fragile—any class named ModelHealthStatus from any module would pass. Add a module check to ensure it's actually from the expected module:

-# Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
-if type(v).__name__ == "ModelHealthStatus":
+# Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
+if (type(v).__name__ == "ModelHealthStatus" and 
+    type(v).__module__ == "omnibase_core.models.health.model_health_status"):
    return v

This preserves pytest-xdist compatibility while preventing acceptance of unrelated classes that happen to share the same name.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
if type(v).__name__ == "ModelHealthStatus":
# Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
if (type(v).__name__ == "ModelHealthStatus" and
type(v).__module__ == "omnibase_core.models.health.model_health_status"):
return v
🤖 Prompt for AI Agents
In src/omnibase_core/models/providers/model_provider_descriptor.py around lines
280-281, the current duck-type check only compares the class name which is
unsafe; import the real ModelHealthStatus class from its defining module and
strengthen the check to ensure both the name and module match (e.g., import
ModelHealthStatus and require type(v).__name__ == "ModelHealthStatus" and
type(v).__module__ == ModelHealthStatus.__module__), preserving pytest-xdist
compatibility while preventing unrelated classes with the same name from
passing.

- Add from_attributes=True to 25 Pydantic models for pytest-xdist compatibility
- Update docstrings to use "behavior" terminology consistently
- Fix documentation variable name consistency in CONTRACT_PATCHING_GUIDE.md
- Apply lazy-import pattern in test_service_capability_resolver.py
- Enhance EnumLikelihood.from_probability error context
- Update outdated docstring in test_enum_likelihood.py
- Rename validators_metadata.py to model_validators_metadata.py (naming convention)
- Remove backward compatibility comment in model_custom_connection_properties.py

Models updated with from_attributes=True:
- compute models (6): input, output, pipeline_result, step_metadata, step_result, execution_context
- contract models (4): fingerprint, normalization_config, drift_details, drift_result
- transformation models (7): mapping, regex, json_path, case, unicode, trim, validation_step
- validation models (2): ambiguous_transition, fsm_analysis_result
- mixin models (2): completion_data, log_data
- event models (2): topic_config, topic_manifest
- subcontract models (2): compute_subcontract, compute_pipeline_step
@claude

claude Bot commented Jan 3, 2026

Copy link
Copy Markdown

Code Review: Contract Patching Infrastructure (PR #289)

Summary

This PR implements a comprehensive contract patching system for ONEX. The architecture is well-designed, following the principle "User-authored files are patches, not full contracts." Overall, this is high-quality work with excellent documentation, comprehensive testing, and strong adherence to project conventions.


✅ Strengths

1. Excellent Architecture & Design

  • Clear separation of concerns: Patch models, validators, and utilities are well-organized
  • Declarative approach: The __add/__remove suffix pattern for list operations is intuitive and consistent
  • Three-layer architecture (Profile → Behavior → Contract) is well-documented and easy to understand
  • Empty list normalization: Smart design choice to normalize [] to None for patch semantics—prevents confusion

2. Strong Security Posture

  • Import allowlisting in ModelReference and NodeBase prevents arbitrary code execution
  • Frozen models with ConfigDict(frozen=True, extra="forbid") prevent tampering
  • Comprehensive validation before any dynamic imports occur
  • Security documentation is thorough and includes clear warnings about trust boundaries

3. Comprehensive Testing

  • 62 new tests covering models, validators, and edge cases
  • Test coverage includes:
    • Identity validation (name + version pairing)
    • List operation conflicts (add vs remove)
    • Duplicate detection within lists
    • Behavior patch consistency (purity/idempotent)
    • File I/O error handling
    • YAML parsing edge cases

4. Excellent Documentation

  • Inline documentation is thorough with clear examples
  • Error codes are well-categorized with the CONTRACT_PATCH_* prefix
  • Logging conventions are clearly documented (DEBUG/INFO/WARNING/ERROR)
  • Type annotations are complete and correct

5. Code Quality

  • DRY principle: Shared validation utilities in validation_utils.py
  • Type safety: Proper use of generics and type narrowing
  • Error handling: Uses structured ModelOnexError consistently
  • Naming conventions: Clear, consistent naming throughout

🔍 Issues & Recommendations

Critical Issues: ❌ None

Major Issues:

1. Thread Safety Documentation Gap ⚠️

Location: src/omnibase_core/models/contracts/model_contract_patch.py

The ModelContractPatch is frozen (immutable), which is thread-safe, but this isn't explicitly documented. Given the project's emphasis on thread safety (see CLAUDE.md), consider adding a note.

Recommendation:

class ModelContractPatch(BaseModel):
    """Partial contract that overrides a default profile.
    
    Thread Safety:
        This model is frozen (immutable) and safe for concurrent access.
        Multiple threads can safely read the same ModelContractPatch instance.
    """

2. Potential Performance Issue in Validator ⚠️

Location: src/omnibase_core/validation/contract_patch_validator.py:419-470

The _validate_list_operation_duplicates method extracts names from lists multiple times. For large patches (up to 100 items per list), this could be inefficient.

Current:

if patch.handlers__add:
    handler_names = [h.name for h in patch.handlers__add]
    self._check_duplicates_in_list(handler_names, "handler", result)

Recommendation: Consider extracting names once and reusing them (minor optimization, not critical).

Minor Issues:

3. Inconsistent Error Code Usage ℹ️

Location: src/omnibase_core/validation/contract_patch_validator.py:244

Uses .value consistently for error codes, which is correct. However, the enum itself is a str enum, so .value is redundant (though harmless).

Current:

code=EnumPatchValidationErrorCode.CONTRACT_PATCH_PYDANTIC_VALIDATION_ERROR.value

Could be:

code=EnumPatchValidationErrorCode.CONTRACT_PATCH_PYDANTIC_VALIDATION_ERROR

This is a style preference and not a bug. The current approach is more explicit.

4. Missing Edge Case Test ℹ️

Location: tests/unit/validation/test_contract_patch_validator.py

I don't see a test for extremely large lists (near the 100-item limit). Consider adding a boundary test:

@pytest.mark.unit
def test_validate_max_list_items(validator, profile_ref):
    """Test validation with maximum allowed list items."""
    handlers = [
        ModelHandlerSpec(name=f"handler_{i}", implementation="...")
        for i in range(100)  # Max limit
    ]
    patch = ModelContractPatch(extends=profile_ref, handlers__add=handlers)
    result = validator.validate(patch)
    assert result.is_valid is True

5. Logging Noise Potential ℹ️

Location: src/omnibase_core/validation/contract_patch_validator.py:148-151

DEBUG logging in the validator happens on every validation. For high-volume scenarios, this could create log noise.

Recommendation: Consider using a rate limiter or conditional logging in production environments (not blocking for this PR).


🎯 Best Practices Observed

  1. ✅ Uses Poetry for all dependencies (as required by CLAUDE.md)
  2. ✅ Follows PEP 604 union syntax (str | None instead of Optional[str])
  3. ✅ Proper exception handling with centralized exception groups
  4. ✅ Alphabetical exception tuple ordering (per project conventions)
  5. ✅ Structured error codes instead of generic exceptions
  6. ✅ Comprehensive field validators with clear error messages
  7. ✅ Proper use of from_attributes=True for value objects

📊 Code Metrics

Metric Value Assessment
Files Changed 89 Large but well-organized
Lines Added 2,411 Mostly new functionality + tests
Lines Deleted 401 Good cleanup
Test Coverage 62 new tests Excellent
Documentation Comprehensive Excellent
Type Safety Full mypy strict Excellent

🔒 Security Assessment

Overall Security: ✅ Strong

  • ✅ Import allowlisting prevents arbitrary code execution
  • ✅ Frozen models prevent post-creation modification
  • ✅ YAML parsing uses safe loaders
  • ✅ Clear documentation of trust boundaries
  • ✅ Defense-in-depth with optional strict validation

No security vulnerabilities identified.


🧪 Testing Assessment

Test Quality: ✅ Excellent

  • ✅ 62 new unit tests
  • ✅ Covers happy paths and error cases
  • ✅ Tests validation edge cases (empty lists, conflicts, duplicates)
  • ✅ Tests file I/O error handling
  • ⚠️ Minor: Could add boundary tests for max list sizes (100 items)

📋 Compliance with CLAUDE.md

Requirement Status
Protocol-driven DI ✅ Uses ModelONEXContainer
Structured errors ✅ Uses ModelOnexError
Type checking (mypy strict) ✅ Passes
Test coverage (60%+) ✅ Expected to pass
PEP 604 syntax ✅ Uses str | None
Poetry for dependencies ✅ N/A (no new deps)
Pydantic frozen models ✅ All models frozen

🎓 Learning Opportunities

This PR demonstrates several exemplary patterns that other contributors should study:

  1. Empty list normalization (lines 84-108 in model_contract_patch.py): Shows how to design semantic equivalence into validation
  2. Shared validation utilities (validation_utils.py): DRY approach to reduce duplication
  3. Mutation documentation (lines 505-526 in model_contract_patch.py): Explicitly documents why mutation is used in _collect_conflicts
  4. Error code organization (enum_patch_validation_error_code.py): Centralized, well-categorized error codes

✅ Final Recommendation

APPROVE ✅

This PR is ready to merge with only minor recommendations for future improvements. The code quality, testing, documentation, and security practices are all excellent.

Optional Follow-ups (Not Blocking):

  1. Add thread safety note to ModelContractPatch docstring
  2. Add boundary test for 100-item list limit
  3. Consider performance optimization in _validate_list_operation_duplicates if profiling shows it's needed

Excellent work, @jonahgabriel! This is a solid foundation for the contract patching system. 🎉


Review completed by Claude Code - omnibase_core PR #289

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/omnibase_core/models/mixins/model_completion_data.py (1)

1-9: Remove unnecessary from __future__ import annotations and consolidate duplicate imports.

This file violates coding guidelines in two ways:

  1. Line 1 uses from __future__ import annotations without any forward references or circular imports present in the file.
  2. Field is imported twice (line 3 and line 9).

As per coding guidelines: "Do NOT use from __future__ import annotations in Pydantic model files (use only for forward references and circular import prevention)".

🔎 Proposed fix
-from __future__ import annotations
-
-from pydantic import Field
-
 """
 Canonical payload for completion events following ONEX naming conventions.
 """
 
 from pydantic import BaseModel, ConfigDict, StrictBool, StrictInt, StrictStr
+from pydantic import Field
♻️ Duplicate comments (1)
src/omnibase_core/models/providers/model_provider_descriptor.py (1)

318-319: Add module check for safer duck-type validation.

The class name check alone is fragile—any class named ModelHealthStatus from any module would pass. Add a module check to ensure it's from the expected module:

 # Duck-type check for ModelHealthStatus (handles pytest-xdist class identity issues)
-if type(v).__name__ == "ModelHealthStatus":
+if (type(v).__name__ == "ModelHealthStatus" and 
+    type(v).__module__ == "omnibase_core.models.health.model_health_status"):
     return v

This preserves pytest-xdist compatibility while preventing acceptance of unrelated classes that happen to share the same name.

🧹 Nitpick comments (7)
src/omnibase_core/models/validation/model_ambiguous_transition.py (1)

8-8: Consider removing unnecessary future import.

Per coding guidelines, from __future__ import annotations should only be used for forward references or circular import prevention. This file doesn't require either—all types are straightforward (str, int, frozenset[str]). Removing it can improve Pydantic's type validation behavior.

As per coding guidelines, this import should be removed unless needed for forward references.

src/omnibase_core/models/mixins/model_log_data.py (1)

1-1: Consider removing unnecessary future import (pre-existing).

The from __future__ import annotations import appears unnecessary since:

  • No forward references or circular imports are present
  • PEP 604 union syntax (str | None) is used directly, suggesting Python 3.10+

As per coding guidelines, this import should only be used for forward references and circular import prevention in Pydantic model files.

🔎 Suggested removal
-from __future__ import annotations
-
 """
 Log data model for structured logging in event bus operations.
 """
src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py (1)

171-171: LGTM! Change aligns with project-wide pattern for pytest-xdist support.

The addition of from_attributes=True correctly enables attribute-based initialization while preserving the model's immutability (frozen=True) and input validation (extra="forbid"). This change follows the established pattern for supporting pytest-xdist parallel execution.

Based on learnings, this change is required for immutable value objects nested in other Pydantic models.

Optional: Consider adding clarifying comment

For consistency with model_compute_input.py (line 107), you could optionally add an inline comment:

-    model_config = ConfigDict(extra="forbid", frozen=True, from_attributes=True)
+    model_config = ConfigDict(
+        extra="forbid",
+        frozen=True,
+        from_attributes=True,  # pytest-xdist compatibility
+    )

However, given the project-wide nature of this change, the lack of comment is acceptable.

src/omnibase_core/models/validation/model_fsm_analysis_result.py (1)

7-7: Consider removing unnecessary future annotations import.

Per coding guidelines for src/omnibase_core/models/**/*.py: "from __future__ import annotations should only be used for forward references and circular import prevention." Since this file has no visible forward references or circular imports (ModelAmbiguousTransition is imported directly), this import appears unnecessary.

As per coding guidelines: "Do NOT use from __future__ import annotations in Pydantic model files (use only for forward references and circular import prevention)"

tests/unit/services/test_service_capability_resolver.py (3)

61-87: Reconsider using Any for provider types.

Since ModelProviderDescriptor is rebuilt at module level (lines 139–141), the forward-reference issue should be resolved. Using Any here reduces type safety and makes it harder to catch type errors in tests.

Consider restoring specific types:

  • Line 61: providers: list[ModelProviderDescriptor] | None = None
  • Line 67: self.providers: list[ModelProviderDescriptor] = ...
  • Line 69: -> list[ModelProviderDescriptor]
  • Line 80: provider: ModelProviderDescriptor
🔎 Proposed type restoration
-    def __init__(self, providers: list[Any] | None = None) -> None:
+    def __init__(self, providers: list[ModelProviderDescriptor] | None = None) -> None:
         """Initialize with optional list of providers.
 
         Args:
             providers: List of provider descriptors to register.
         """
-        self.providers: list[Any] = providers or []
+        self.providers: list[ModelProviderDescriptor] = providers or []
 
-    def get_providers_for_capability(self, capability: str) -> list[Any]:
+    def get_providers_for_capability(self, capability: str) -> list[ModelProviderDescriptor]:
         """Get all providers offering the specified capability.
 
         Args:
             capability: Exact capability identifier to match.
 
         Returns:
             List of providers that have this capability.
         """
         return [p for p in self.providers if capability in p.capabilities]
 
-    def add_provider(self, provider: Any) -> None:
+    def add_provider(self, provider: ModelProviderDescriptor) -> None:
         """Add a provider to the registry.
 
         Args:
             provider: Provider descriptor to add.
         """
         self.providers.append(provider)

149-227: Restore specific return types in factory functions.

Since ModelProviderDescriptor is rebuilt at module level (lines 139–141), the factories can return specific types instead of Any. This improves type safety in tests.

Additionally, the "Lazy imports to avoid circular dependency" comment on line 212 is misleading—there's no actual import there, and imports are already at module level.

🔎 Proposed type restoration
 def create_provider(
     provider_id: UUID | None = None,
     capabilities: list[str] | None = None,
     attributes: dict[str, Any] | None = None,
     declared_features: dict[str, Any] | None = None,
     observed_features: dict[str, Any] | None = None,
     adapter: str = "test.adapters.TestAdapter",
     connection_ref: str = "env://TEST_CONNECTION",
     tags: list[str] | None = None,
-) -> Any:
+) -> ModelProviderDescriptor:
     """Create a test provider descriptor with defaults.
     ...
     """
-    # ModelProviderDescriptor is rebuilt at module level, so use directly
     return ModelProviderDescriptor(
         provider_id=provider_id or uuid4(),
         ...
     )


 def create_dependency(
     alias: str = "db",
     capability: str = "database.relational",
     must: dict[str, Any] | None = None,
     prefer: dict[str, Any] | None = None,
     forbid: dict[str, Any] | None = None,
     hints: dict[str, Any] | None = None,
     selection_policy: str = "auto_if_unique",
     strict: bool = True,
-) -> Any:
+) -> ModelCapabilityDependency:
     """Create a test capability dependency with defaults.
     ...
     """
-    # Lazy imports to avoid circular dependency
-
     requirements = ModelRequirementSet(
         ...
     )

272-272: Remove stale "lazy import" comments.

These standalone "lazy import to avoid circular dependency" comments appear throughout the file but aren't followed by any import statements. They should be removed as they're misleading and serve no purpose.

Also applies to: 327-327, 349-349, 379-379, 427-427, 459-459, 492-492, 548-548, 617-617, 645-645, 699-699, 726-726, 754-754, 776-776, 803-803, 852-852, 888-888, 918-918, 954-954, 971-971, 1033-1033, 1061-1061, 1082-1082, 1108-1108, 1130-1130, 1148-1148, 1182-1182, 1223-1223, 1253-1253, 1288-1288, 1317-1317, 1350-1350, 1415-1415, 1434-1434, 1486-1486, 1517-1517

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e343fa0 and cfa6b84.

📒 Files selected for processing (38)
  • docs/guides/CONTRACT_PATCHING_GUIDE.md
  • src/omnibase_core/enums/enum_likelihood.py
  • src/omnibase_core/mixins/mixin_event_listener.py
  • src/omnibase_core/models/compute/model_compute_execution_context.py
  • src/omnibase_core/models/compute/model_compute_input.py
  • src/omnibase_core/models/compute/model_compute_output.py
  • src/omnibase_core/models/compute/model_compute_pipeline_result.py
  • src/omnibase_core/models/compute/model_compute_step_metadata.py
  • src/omnibase_core/models/compute/model_compute_step_result.py
  • src/omnibase_core/models/configuration/model_metadata_block.py
  • src/omnibase_core/models/configuration/model_onex_metadata.py
  • src/omnibase_core/models/configuration/model_validators_metadata.py
  • src/omnibase_core/models/connections/model_custom_connection_properties.py
  • src/omnibase_core/models/contracts/model_contract_fingerprint.py
  • src/omnibase_core/models/contracts/model_contract_normalization_config.py
  • src/omnibase_core/models/contracts/model_drift_details.py
  • src/omnibase_core/models/contracts/model_drift_result.py
  • src/omnibase_core/models/contracts/model_handler_contract.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py
  • src/omnibase_core/models/events/model_topic_config.py
  • src/omnibase_core/models/events/model_topic_manifest.py
  • src/omnibase_core/models/mixins/model_completion_data.py
  • src/omnibase_core/models/mixins/model_log_data.py
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • src/omnibase_core/models/transformations/model_mapping_config.py
  • src/omnibase_core/models/transformations/model_transform_case_config.py
  • src/omnibase_core/models/transformations/model_transform_json_path_config.py
  • src/omnibase_core/models/transformations/model_transform_regex_config.py
  • src/omnibase_core/models/transformations/model_transform_trim_config.py
  • src/omnibase_core/models/transformations/model_transform_unicode_config.py
  • src/omnibase_core/models/transformations/model_validation_step_config.py
  • src/omnibase_core/models/validation/model_ambiguous_transition.py
  • src/omnibase_core/models/validation/model_fsm_analysis_result.py
  • src/omnibase_core/validation/__init__.py
  • src/omnibase_core/validation/validation_utils.py
  • tests/unit/enums/test_enum_likelihood.py
  • tests/unit/services/test_service_capability_resolver.py
✅ Files skipped from review due to trivial changes (1)
  • src/omnibase_core/models/contracts/model_handler_contract.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/omnibase_core/mixins/mixin_event_listener.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 604 union syntax for type annotations (e.g., str | None instead of Optional[str])
Resolve services using protocol names (e.g., container.get_service('ProtocolEventBus')) never using concrete class names
Order exception tuples alphabetically by exception class name (e.g., except (AttributeError, TypeError, ValidationError) not except (ValueError, TypeError, AttributeError))
Format code using poetry run black and sort imports using poetry run isort
Lint code with poetry run ruff check and fix automatically with poetry run ruff check --fix

Files:

  • src/omnibase_core/models/transformations/model_transform_regex_config.py
  • src/omnibase_core/models/validation/model_fsm_analysis_result.py
  • src/omnibase_core/models/transformations/model_transform_case_config.py
  • src/omnibase_core/models/contracts/model_contract_normalization_config.py
  • src/omnibase_core/models/contracts/model_drift_details.py
  • src/omnibase_core/models/transformations/model_mapping_config.py
  • src/omnibase_core/models/contracts/model_drift_result.py
  • src/omnibase_core/models/transformations/model_transform_json_path_config.py
  • src/omnibase_core/models/connections/model_custom_connection_properties.py
  • src/omnibase_core/models/compute/model_compute_step_metadata.py
  • src/omnibase_core/models/events/model_topic_manifest.py
  • src/omnibase_core/models/compute/model_compute_execution_context.py
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py
  • src/omnibase_core/models/mixins/model_completion_data.py
  • src/omnibase_core/models/transformations/model_transform_unicode_config.py
  • src/omnibase_core/models/configuration/model_validators_metadata.py
  • tests/unit/enums/test_enum_likelihood.py
  • src/omnibase_core/models/configuration/model_metadata_block.py
  • src/omnibase_core/models/contracts/model_contract_fingerprint.py
  • src/omnibase_core/models/mixins/model_log_data.py
  • src/omnibase_core/models/transformations/model_validation_step_config.py
  • src/omnibase_core/models/validation/model_ambiguous_transition.py
  • src/omnibase_core/models/compute/model_compute_output.py
  • src/omnibase_core/validation/__init__.py
  • src/omnibase_core/enums/enum_likelihood.py
  • src/omnibase_core/models/compute/model_compute_input.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/compute/model_compute_step_result.py
  • src/omnibase_core/models/compute/model_compute_pipeline_result.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py
  • src/omnibase_core/models/configuration/model_onex_metadata.py
  • tests/unit/services/test_service_capability_resolver.py
  • src/omnibase_core/models/events/model_topic_config.py
  • src/omnibase_core/models/transformations/model_transform_trim_config.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators to eliminate try/catch boilerplate
Add standardized exception handling comment markers (# fallback-ok:, # catch-all-ok:, # cleanup-resilience-ok:, # boundary-ok:, # init-errors-ok:, # tool-resilience-ok:) when using broad exception handlers
Use centralized exception group constants from omnibase_core.errors.exception_groups instead of individual exception types
Ensure all source code passes mypy strict mode with zero errors and pyright basic mode validation
Use FileRegistry for loading YAML contracts and configuration files instead of direct file reading
Use duck typing with protocols for service resolution - never use isinstance checks for protocol-based dependencies

Files:

  • src/omnibase_core/models/transformations/model_transform_regex_config.py
  • src/omnibase_core/models/validation/model_fsm_analysis_result.py
  • src/omnibase_core/models/transformations/model_transform_case_config.py
  • src/omnibase_core/models/contracts/model_contract_normalization_config.py
  • src/omnibase_core/models/contracts/model_drift_details.py
  • src/omnibase_core/models/transformations/model_mapping_config.py
  • src/omnibase_core/models/contracts/model_drift_result.py
  • src/omnibase_core/models/transformations/model_transform_json_path_config.py
  • src/omnibase_core/models/connections/model_custom_connection_properties.py
  • src/omnibase_core/models/compute/model_compute_step_metadata.py
  • src/omnibase_core/models/events/model_topic_manifest.py
  • src/omnibase_core/models/compute/model_compute_execution_context.py
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py
  • src/omnibase_core/models/mixins/model_completion_data.py
  • src/omnibase_core/models/transformations/model_transform_unicode_config.py
  • src/omnibase_core/models/configuration/model_validators_metadata.py
  • src/omnibase_core/models/configuration/model_metadata_block.py
  • src/omnibase_core/models/contracts/model_contract_fingerprint.py
  • src/omnibase_core/models/mixins/model_log_data.py
  • src/omnibase_core/models/transformations/model_validation_step_config.py
  • src/omnibase_core/models/validation/model_ambiguous_transition.py
  • src/omnibase_core/models/compute/model_compute_output.py
  • src/omnibase_core/validation/__init__.py
  • src/omnibase_core/enums/enum_likelihood.py
  • src/omnibase_core/models/compute/model_compute_input.py
  • src/omnibase_core/validation/validation_utils.py
  • src/omnibase_core/models/compute/model_compute_step_result.py
  • src/omnibase_core/models/compute/model_compute_pipeline_result.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py
  • src/omnibase_core/models/configuration/model_onex_metadata.py
  • src/omnibase_core/models/events/model_topic_config.py
  • src/omnibase_core/models/transformations/model_transform_trim_config.py
src/omnibase_core/models/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/models/**/*.py: Add from_attributes=True to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution
Do NOT use from __future__ import annotations in Pydantic model files (use only for forward references and circular import prevention)

Files:

  • src/omnibase_core/models/transformations/model_transform_regex_config.py
  • src/omnibase_core/models/validation/model_fsm_analysis_result.py
  • src/omnibase_core/models/transformations/model_transform_case_config.py
  • src/omnibase_core/models/contracts/model_contract_normalization_config.py
  • src/omnibase_core/models/contracts/model_drift_details.py
  • src/omnibase_core/models/transformations/model_mapping_config.py
  • src/omnibase_core/models/contracts/model_drift_result.py
  • src/omnibase_core/models/transformations/model_transform_json_path_config.py
  • src/omnibase_core/models/connections/model_custom_connection_properties.py
  • src/omnibase_core/models/compute/model_compute_step_metadata.py
  • src/omnibase_core/models/events/model_topic_manifest.py
  • src/omnibase_core/models/compute/model_compute_execution_context.py
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py
  • src/omnibase_core/models/mixins/model_completion_data.py
  • src/omnibase_core/models/transformations/model_transform_unicode_config.py
  • src/omnibase_core/models/configuration/model_validators_metadata.py
  • src/omnibase_core/models/configuration/model_metadata_block.py
  • src/omnibase_core/models/contracts/model_contract_fingerprint.py
  • src/omnibase_core/models/mixins/model_log_data.py
  • src/omnibase_core/models/transformations/model_validation_step_config.py
  • src/omnibase_core/models/validation/model_ambiguous_transition.py
  • src/omnibase_core/models/compute/model_compute_output.py
  • src/omnibase_core/models/compute/model_compute_input.py
  • src/omnibase_core/models/compute/model_compute_step_result.py
  • src/omnibase_core/models/compute/model_compute_pipeline_result.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py
  • src/omnibase_core/models/configuration/model_onex_metadata.py
  • src/omnibase_core/models/events/model_topic_config.py
  • src/omnibase_core/models/transformations/model_transform_trim_config.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to classify test types
All tests must complete within 60-second timeout protection to ensure CI performance

Files:

  • tests/unit/enums/test_enum_likelihood.py
  • tests/unit/services/test_service_capability_resolver.py
🧠 Learnings (5)
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/models/**/*.py : Add `from_attributes=True` to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution

Applied to files:

  • src/omnibase_core/models/transformations/model_transform_regex_config.py
  • src/omnibase_core/models/validation/model_fsm_analysis_result.py
  • src/omnibase_core/models/transformations/model_transform_case_config.py
  • src/omnibase_core/models/contracts/model_contract_normalization_config.py
  • src/omnibase_core/models/contracts/model_drift_details.py
  • src/omnibase_core/models/transformations/model_mapping_config.py
  • src/omnibase_core/models/contracts/model_drift_result.py
  • src/omnibase_core/models/transformations/model_transform_json_path_config.py
  • src/omnibase_core/models/compute/model_compute_step_metadata.py
  • src/omnibase_core/models/events/model_topic_manifest.py
  • src/omnibase_core/models/compute/model_compute_execution_context.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py
  • src/omnibase_core/models/mixins/model_completion_data.py
  • src/omnibase_core/models/transformations/model_transform_unicode_config.py
  • src/omnibase_core/models/contracts/model_contract_fingerprint.py
  • src/omnibase_core/models/mixins/model_log_data.py
  • src/omnibase_core/models/transformations/model_validation_step_config.py
  • src/omnibase_core/models/validation/model_ambiguous_transition.py
  • src/omnibase_core/models/compute/model_compute_output.py
  • src/omnibase_core/models/compute/model_compute_input.py
  • src/omnibase_core/models/compute/model_compute_step_result.py
  • src/omnibase_core/models/compute/model_compute_pipeline_result.py
  • src/omnibase_core/models/contracts/subcontracts/model_compute_pipeline_step.py
  • src/omnibase_core/models/events/model_topic_config.py
  • src/omnibase_core/models/transformations/model_transform_trim_config.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/**/*.py : Use duck typing with protocols for service resolution - never use isinstance checks for protocol-based dependencies

Applied to files:

  • src/omnibase_core/models/providers/model_provider_descriptor.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/**/*.py : Use ModelOnexError with error codes from EnumCoreErrorCode for exception handling, never generic Exception

Applied to files:

  • tests/unit/enums/test_enum_likelihood.py
  • src/omnibase_core/models/configuration/model_metadata_block.py
  • src/omnibase_core/enums/enum_likelihood.py
  • src/omnibase_core/models/configuration/model_onex_metadata.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/models/**/*.py : Do NOT use `from __future__ import annotations` in Pydantic model files (use only for forward references and circular import prevention)

Applied to files:

  • src/omnibase_core/validation/__init__.py
📚 Learning: 2026-01-02T20:42:17.758Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use ModelONEXContainer (not ModelContainer) for dependency injection in node constructors

Applied to files:

  • src/omnibase_core/models/configuration/model_onex_metadata.py
🧬 Code graph analysis (3)
src/omnibase_core/validation/__init__.py (2)
src/omnibase_core/models/validation/model_import_validation_result.py (1)
  • ModelImportValidationResult (24-98)
src/omnibase_core/validation/contract_patch_validator.py (1)
  • ContractPatchValidator (72-625)
src/omnibase_core/validation/validation_utils.py (2)
src/omnibase_core/models/container/model_enhanced_logger.py (1)
  • warning (62-63)
src/omnibase_core/models/contracts/model_dependency.py (1)
  • is_protocol (319-321)
tests/unit/services/test_service_capability_resolver.py (2)
tests/unit/models/providers/test_model_provider_descriptor.py (1)
  • ModelHealthStatus (30-49)
src/omnibase_core/models/providers/model_provider_descriptor.py (1)
  • ModelProviderDescriptor (122-727)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: claude-review
🔇 Additional comments (43)
src/omnibase_core/models/transformations/model_validation_step_config.py (1)

30-30: LGTM! Enables pytest-xdist compatibility.

The addition of from_attributes=True correctly enables attribute-based model initialization for this immutable value object, supporting parallel test execution as per coding guidelines.

src/omnibase_core/models/contracts/subcontracts/model_compute_subcontract.py (1)

158-158: LGTM! Configuration update aligns with project guidelines.

The addition of from_attributes=True enables attribute-based model initialization for pytest-xdist parallel execution compatibility while preserving existing immutability and validation constraints.

Based on learnings and coding guidelines, this change is part of a project-wide effort to support parallel testing infrastructure.

src/omnibase_core/models/contracts/model_drift_result.py (1)

110-110: LGTM! Configuration change aligns with coding guidelines.

The addition of from_attributes=True correctly enables attribute-based initialization for this immutable value object, which is necessary for pytest-xdist parallel execution compatibility. Based on learnings, this pattern should be applied to all immutable value objects nested in other Pydantic models.

src/omnibase_core/models/contracts/model_drift_details.py (1)

97-97: LGTM! Configuration change aligns with coding guidelines.

The addition of from_attributes=True correctly enables attribute-based initialization for this immutable value object, which is nested in ModelDriftResult. This change is necessary for pytest-xdist parallel execution compatibility. Based on learnings, this is the expected pattern for models in this location.

src/omnibase_core/models/connections/model_custom_connection_properties.py (1)

73-85: LGTM!

The updated comment accurately describes the lenient coercion behavior and aligns with the function's documented design (lines 39-40 in the docstring). The debug logging provides good observability for troubleshooting coercion failures.

src/omnibase_core/models/events/model_topic_manifest.py (1)

40-40: LGTM! Correct addition of from_attributes=True for pytest-xdist compatibility.

This change properly enables attribute-based initialization for the immutable ModelTopicManifest model, supporting parallel test execution. Based on coding guidelines, this is required for immutable value objects that may be nested in other Pydantic models.

src/omnibase_core/models/contracts/model_contract_normalization_config.py (1)

92-92: LGTM! Correctly implements pytest-xdist compatibility guideline.

The addition of from_attributes=True properly enables attribute-based initialization for this frozen Pydantic model, supporting parallel test execution with pytest-xdist. The inline comment clearly documents the purpose.

Based on coding guidelines and retrieved learnings.

src/omnibase_core/models/contracts/model_contract_fingerprint.py (1)

95-99: LGTM! Change aligns with coding guidelines for pytest-xdist compatibility.

The addition of from_attributes=True follows the recommended pattern for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution, as indicated by the retrieved learnings.

Based on learnings, this configuration enables attribute-based initialization for models that may be used across parallel test processes.

src/omnibase_core/models/validation/model_ambiguous_transition.py (1)

54-54: LGTM! Pytest-xdist compatibility enhancement.

The addition of from_attributes=True correctly enables attribute-based initialization for this immutable value object, supporting pytest-xdist parallel execution. Based on learnings, this is the expected pattern for Pydantic models in this codebase.

src/omnibase_core/models/mixins/model_log_data.py (1)

14-18: LGTM! ConfigDict update aligns with project standards.

The addition of from_attributes=True correctly enables attribute-based initialization for pytest-xdist compatibility while preserving the immutable and strict field configuration.

Based on learnings, this change follows the guideline to add from_attributes=True to ConfigDict for immutable value objects nested in other Pydantic models.

src/omnibase_core/models/mixins/model_completion_data.py (1)

20-24: Approved: from_attributes=True addition.

The addition of from_attributes=True correctly supports pytest-xdist parallel execution for this immutable value object, consistent with project-wide standards.

Based on learnings, this configuration is required for Pydantic models that are nested in other models when using pytest-xdist.

src/omnibase_core/models/transformations/model_transform_json_path_config.py (1)

25-25: LGTM! Enables attribute-based initialization for pytest-xdist.

The addition of from_attributes=True correctly enables attribute-based construction while maintaining the model's immutability and strict validation. Based on learnings, this is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/transformations/model_transform_case_config.py (1)

27-27: LGTM! Consistent with the required pattern.

The addition of from_attributes=True aligns with the pytest-xdist compatibility requirement. Based on learnings, this change is necessary for immutable value objects nested in other Pydantic models.

src/omnibase_core/models/transformations/model_transform_regex_config.py (1)

31-31: LGTM! Follows the established pattern.

The change correctly adds from_attributes=True to support attribute-based initialization. Based on learnings, this is required for pytest-xdist parallel execution.

src/omnibase_core/models/events/model_topic_config.py (1)

32-32: LGTM! Maintains thread safety while enabling attribute-based construction.

The addition of from_attributes=True is correct and preserves the model's immutability (frozen=True). Based on learnings, this change is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/compute/model_compute_step_metadata.py (1)

77-77: LGTM! Completes the pattern across compute models.

The addition of from_attributes=True is correct and consistent with the PR-wide updates to enable attribute-based initialization. Based on learnings, this is required for pytest-xdist parallel execution.

src/omnibase_core/models/transformations/model_mapping_config.py (1)

29-29: LGTM! Consistent with pytest-xdist compatibility pattern.

The addition of from_attributes=True is correct and aligns with the broader PR changes to transformation configuration models. This enables attribute-based initialization while maintaining the model's immutability and strict validation.

Based on learnings, this change supports pytest-xdist parallel execution for nested Pydantic models.

src/omnibase_core/models/transformations/model_transform_trim_config.py (1)

27-27: LGTM! Follows pytest-xdist compatibility pattern.

The addition of from_attributes=True correctly enables attribute-based initialization for this transformation configuration model. The change maintains existing immutability and validation constraints.

Based on learnings, this change is required for pytest-xdist parallel execution support.

src/omnibase_core/models/transformations/model_transform_unicode_config.py (1)

27-27: LGTM! Consistent with project-wide pytest-xdist support.

The addition of from_attributes=True is correct and follows the established pattern for transformation configuration models. This change enables attribute-based initialization while preserving the model's frozen state and strict validation.

Based on learnings, this change supports pytest-xdist parallel execution.

src/omnibase_core/models/compute/model_compute_input.py (1)

104-108: LGTM! Well-documented pytest-xdist compatibility change.

The addition of from_attributes=True is correct and well-documented with an inline comment explaining the rationale. This enables attribute-based initialization for the generic ModelComputeInput model while maintaining its immutability and strict validation.

The inline comment is helpful for future maintainers and provides clear context for this configuration change.

Based on learnings, this change is required for pytest-xdist parallel execution support.

src/omnibase_core/models/compute/model_compute_pipeline_result.py (1)

122-122: LGTM! Change aligns with pytest-xdist compatibility requirements.

The addition of from_attributes=True enables attribute-based initialization for this immutable model, which is necessary for pytest-xdist parallel execution as documented in the learnings.

Based on learnings, this change is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/compute/model_compute_step_result.py (1)

122-122: LGTM! Consistent with pytest-xdist compatibility pattern.

The addition of from_attributes=True follows the established pattern for immutable models with nested Pydantic objects (ModelComputeStepMetadata).

Based on learnings, this change is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/compute/model_compute_output.py (1)

112-112: LGTM! Correctly enables attribute-based initialization.

The addition of from_attributes=True is appropriate for this generic model, supporting scenarios where T_Output contains nested Pydantic models.

Based on learnings, this change is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/compute/model_compute_execution_context.py (1)

96-96: LGTM! Completes the pattern for compute models.

The addition of from_attributes=True aligns with the broader PR pattern of enabling attribute-based initialization across all compute-related models.

Based on learnings, this change is required for pytest-xdist parallel execution compatibility.

src/omnibase_core/models/validation/model_fsm_analysis_result.py (1)

114-114: LGTM! Correct addition of from_attributes=True for pytest-xdist compatibility.

The addition of from_attributes=True to the ConfigDict aligns with the PR objectives and the retrieved learnings for enabling attribute-based initialization in pytest-xdist parallel execution. The inline comment clearly documents the purpose.

Based on learnings: "Add from_attributes=True to ConfigDict for immutable value objects nested in other Pydantic models to support pytest-xdist parallel execution"

docs/guides/CONTRACT_PATCHING_GUIDE.md (4)

208-217: ✅ Good: Examples correctly demonstrate descriptor overrides.

The examples consistently use the descriptor field with ModelDescriptorPatch and follow the pattern documented at line 162 (behavior overrides via descriptor field). The terminology is clear and aligns with the clarification at lines 201–203.

Also applies to: 559-574, 698-712


3-4: ✅ Version and date updates are consistent.

The document version bumped to 1.0.2 and date updated to 2026-01-03 in both header (lines 3–4) and footer (lines 1033–1034).

Also applies to: 1033-1034


791-791: The implementation is confirmed. ContractPatchValidator.validate() at lines 467-470 of src/omnibase_core/validation/contract_patch_validator.py explicitly checks consumed_events__add for duplicates using _check_duplicates_in_list() with the correct error code CONTRACT_PATCH_DUPLICATE_LIST_ENTRIES. Tests at lines 518-553 of tests/unit/validation/test_contract_patch_validator.py verify both that duplicates are caught and unique entries pass validation.


787-813: Documentation accurately reflects error code implementation. All 11 CONTRACT_PATCH_* error codes listed in the validation tables are properly defined in EnumPatchValidationErrorCode and exported from the enums module. No discrepancies found.

src/omnibase_core/models/configuration/model_validators_metadata.py (1)

9-9: LGTM! Documentation updated to match actual module name.

The docstring example import path correctly reflects the module's actual name after the rename from validators_metadata to model_validators_metadata.

src/omnibase_core/models/configuration/model_metadata_block.py (1)

31-37: LGTM! Import path updated correctly.

The import statement correctly references the renamed module model_validators_metadata. All imported validator functions are properly used in the file's @field_validator methods.

src/omnibase_core/models/configuration/model_onex_metadata.py (1)

14-20: LGTM! Import path updated consistently.

The import statement correctly references the renamed module model_validators_metadata on lines 14-20. All imported validator functions are properly used in this file's @field_validator methods:

  • coerce_to_semver (lines 108, 136)
  • validate_identifier_name (line 114)
  • coerce_to_namespace (line 120)
  • coerce_protocols_to_list (line 142)
  • validate_entrypoint_uri (line 148)

No remaining imports from the old validators_metadata module path exist, and the update is consistent with model_metadata_block.py.

src/omnibase_core/validation/__init__.py (3)

50-58: LGTM! Clear aliasing strategy with good documentation.

The renaming to ModelImportValidationResult as the canonical name with a backwards-compatible alias is well-documented and follows the module's established direct-alias pattern.


74-75: LGTM! ContractPatchValidator import follows established patterns.

The direct import is appropriate and consistent with the module's import strategy. The ticket reference provides good traceability.


327-419: LGTM! Public API exports are properly maintained.

The __all__ list correctly includes both the canonical ModelImportValidationResult and its backwards-compatible alias CircularImportValidationResult, along with the new ContractPatchValidator. The documentation comments provide clear context.

src/omnibase_core/validation/validation_utils.py (4)

252-335: LGTM! Non-fatal empty list warnings enhance developer experience.

The new warn_empty_list parameter adds useful diagnostic capability without breaking existing behavior. The warning message is clear and actionable, guiding developers to either omit the field or provide values.


338-421: LGTM! Consistent implementation with validate_string_list.

The addition of warn_empty_list to validate_onex_name_list maintains consistency with validate_string_list. The empty-list warning logic is identical, which aids maintainability.


424-499: LGTM! Enhanced observability with appropriate logging levels.

The additions improve debugging capabilities:

  • DEBUG logs for skipped checks and no-conflict cases aid troubleshooting
  • WARNING for empty lists helps catch potential user errors
  • O(n) complexity note documents algorithmic characteristics
  • The plural warn_empty_lists correctly reflects checking both lists

752-794: LGTM! Improved documentation and defensive error handling.

The enhancements improve both observability and resilience:

  • Debug logs trace protocol detection logic
  • Expanded docstring clarifies the non-fatal error handling philosophy
  • Additional exception handlers (AttributeError, TypeError) protect against malformed path operations without disrupting batch processing
tests/unit/enums/test_enum_likelihood.py (1)

424-425: LGTM! Clear documentation of error handling behavior.

The docstring accurately documents the lazy import strategy and error construction with context, which helps future maintainers understand the implementation details.

src/omnibase_core/enums/enum_likelihood.py (1)

171-175: No issues found. The ModelOnexError constructor correctly accepts message as a keyword argument (line 63) and supports arbitrary context fields via **context: Any (line 68). The probability=probability field will be stored in the additional_context dictionary. The code follows the specified coding guidelines for exception handling.

tests/unit/services/test_service_capability_resolver.py (2)

15-23: LGTM - clear documentation of the rebuild pattern.

The updated docstring clearly explains the module-level rebuild approach and references the related issue and reference implementation. This helps maintainers understand the forward-reference resolution strategy.


116-141: LGTM - clean module-level rebuild pattern.

The module-level stub and rebuild approach is clean and deterministic. This resolves the forward-reference issue without the global cache flag that was problematic in earlier iterations. The pattern matches the reference implementation in test_model_provider_descriptor.py.

Comment thread tests/unit/services/test_service_capability_resolver.py Outdated
- Add PYDANTIC_MODEL_ERRORS exception handling in model_examples_collection.py
- Add safer duck-type module check in model_provider_descriptor.py
- Remove 47 redundant "lazy import" comments and 7 redundant local imports
  from test_service_capability_resolver.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/unit/services/test_service_capability_resolver.py (2)

61-61: Consider restoring type safety instead of using Any.

The use of Any throughout the mock and factory functions removes static type checking benefits. While this may have been introduced to work around type-checking issues with the stub ModelHealthStatus, it degrades test quality by preventing mypy/pyright from catching type mismatches.

Consider these alternatives:

  • Use # type: ignore[...] comments at specific problematic call sites rather than changing signatures
  • Use TypeVar with bounds or Protocol types for more precise typing
  • Cast at usage sites (e.g., cast(ModelProviderDescriptor, create_provider(...))) while keeping precise return types

This would preserve type safety while still allowing the tests to work with the forward-reference resolution pattern.

🔎 Example: Restore precise types with targeted ignores

For the factory functions:

 def create_provider(
     provider_id: UUID | None = None,
     capabilities: list[str] | None = None,
     attributes: dict[str, Any] | None = None,
     declared_features: dict[str, Any] | None = None,
     observed_features: dict[str, Any] | None = None,
     adapter: str = "test.adapters.TestAdapter",
     connection_ref: str = "env://TEST_CONNECTION",
     tags: list[str] | None = None,
-) -> Any:
+) -> ModelProviderDescriptor:
     """Create a test provider descriptor with defaults.
     ...
     """
-    # ModelProviderDescriptor is rebuilt at module level, so use directly
     return ModelProviderDescriptor(
-) -> Any:
+) -> ModelCapabilityDependency:
     """Create a test capability dependency with defaults.
     ...
     """

For MockProviderRegistry:

-    def __init__(self, providers: list[Any] | None = None) -> None:
+    def __init__(self, providers: list[ModelProviderDescriptor] | None = None) -> None:
         """Initialize with optional list of providers.
         ...
         """
-        self.providers: list[Any] = providers or []
+        self.providers: list[ModelProviderDescriptor] = providers or []

If type checking fails at specific call sites, add targeted # type: ignore[arg-type] comments there instead.

Also applies to: 67-67, 69-69, 80-80, 158-158, 196-196


120-121: Move BaseModel import to module top per PEP 8.

The BaseModel import should be at the top of the file with other imports, not mid-file. While the comment explains the intent, this violates Python's standard import organization (PEP 8).

🔎 Move import to top
 from __future__ import annotations
 
 import random
 from datetime import UTC, datetime
 from typing import Any
 from uuid import UUID, uuid4
 
 import pytest
+from pydantic import BaseModel
 
 from omnibase_core.enums.enum_core_error_code import EnumCoreErrorCode
 from omnibase_core.models.bindings.model_binding import ModelBinding

Then at lines 120-121:

-# Import BaseModel here for the stub class
-from pydantic import BaseModel
-
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfa6b84 and de99bad.

📒 Files selected for processing (3)
  • src/omnibase_core/models/core/model_examples_collection.py
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • tests/unit/services/test_service_capability_resolver.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/omnibase_core/models/providers/model_provider_descriptor.py
  • src/omnibase_core/models/core/model_examples_collection.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 604 union syntax for type annotations (e.g., str | None instead of Optional[str])
Resolve services using protocol names (e.g., container.get_service('ProtocolEventBus')) never using concrete class names
Order exception tuples alphabetically by exception class name (e.g., except (AttributeError, TypeError, ValidationError) not except (ValueError, TypeError, AttributeError))
Format code using poetry run black and sort imports using poetry run isort
Lint code with poetry run ruff check and fix automatically with poetry run ruff check --fix

Files:

  • tests/unit/services/test_service_capability_resolver.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to classify test types
All tests must complete within 60-second timeout protection to ensure CI performance

Files:

  • tests/unit/services/test_service_capability_resolver.py
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T20:42:17.758Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Use ModelONEXContainer (not ModelContainer) for dependency injection in node constructors
🧬 Code graph analysis (1)
tests/unit/services/test_service_capability_resolver.py (3)
src/omnibase_core/protocols/resolution/protocol_capability_resolver.py (1)
  • get_providers_for_capability (108-129)
tests/unit/models/providers/test_model_provider_descriptor.py (1)
  • ModelHealthStatus (30-49)
src/omnibase_core/models/providers/model_provider_descriptor.py (1)
  • ModelProviderDescriptor (122-731)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: claude-review
🔇 Additional comments (2)
tests/unit/services/test_service_capability_resolver.py (2)

15-22: Clear documentation of the forward-reference resolution pattern.

The added documentation effectively explains the module-level rebuild approach and provides helpful context for future maintainers.


124-141: Module-level rebuild pattern correctly addresses circular import.

The stub ModelHealthStatus and module-level model_rebuild() call successfully resolve the forward reference issue while ensuring deterministic test behavior. This is the recommended pattern and aligns with the reference implementation.

@claude

claude Bot commented Jan 3, 2026

Copy link
Copy Markdown

PR Review: Contract Patching Infrastructure (OMN-1126)

Overall Assessment - LGTM with minor suggestions ✅

This is an excellent implementation of the contract patching infrastructure with strong architectural design, comprehensive validation, security consciousness, and thorough documentation.

Strengths:

  • Well-designed patch semantics with clear add/remove operations
  • Comprehensive validation system with granular error codes
  • Strong security controls (import allowlists, namespace validation)
  • Excellent test coverage (62 new tests)
  • Proper Pydantic validation patterns

Statistics: 89 files changed, +2,306/-402 lines, 6 new models


Code Quality ⭐⭐⭐⭐⭐

Architecture:

  • Clean separation: ModelContractPatch (data) + ContractPatchValidator (semantic validation)
  • Three-layer validation: Structural → Semantic → Resolutive (deferred)
  • O(n) algorithms for duplicate/conflict detection

Security:

  • Import allowlists prevent arbitrary code execution
  • Namespace validation in mixins
  • YAML safe loading
  • Clear trust model documentation

Test Coverage: 62 new tests covering happy paths, errors, edge cases, security


High Priority Recommendations

  1. ENFORCE_TOOL_IMPORT_ALLOWLIST (node_base.py:157) defaults to False - document how to enable in production
  2. Run coverage report to confirm 60%+ threshold
  3. Add migration guide for v0.4.0 patch format adoption

Medium Priority

  1. Add reserved keyword check in ModelReference.validate_module_format
  2. Create CONTRACT_SECURITY.md consolidating security docs
  3. Consider WARNING for empty descriptors (currently INFO)

Security Assessment ✅

No critical issues. Strong defense-in-depth with import allowlists, namespace validation, and safe YAML parsing.

Minor recommendations: Enable ENFORCE_TOOL_IMPORT_ALLOWLIST by default in production, add keyword checks, document contract provenance verification.


Potential Issues ⚠️

Minor (non-blocking):

  1. Type narrowing in _collect_conflicts could be more explicit with None checks
  2. Reserved keyword validation missing (low impact)
  3. Empty descriptor is INFO (could be WARNING for better UX)

No blocking bugs detected. All validation logic appears sound.


Best Practices ✅

Excellent ONEX compliance: Poetry, Pydantic patterns, PEP 604 syntax, ModelOnexError, type annotations, ClassVar, DRY principle.


Conclusion

High-quality work with no blocking issues. Recommended action: Approve and merge after addressing or documenting high-priority recommendations as follow-up tasks.

Excellent work, @jonahgabriel! 🎉


Reviewed by: Claude Sonnet 4.5 (claude-code) | Date: 2026-01-03 | PR: #289

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