Repository navigation
fix: address PR #180 review with proper typed Pydantic models - #186
Conversation
Add 16 new common model files for better code organization: - model_config_schema_property.py - model_custom_health_metrics.py - model_effect_metadata.py - model_event_subscription_config.py - model_graph_node_data.py - model_graph_node_inputs.py - model_intent_payload.py - model_introspection_custom_metrics.py - model_mixin_config_schema.py - model_node_capabilities_metadata.py - model_operation_data.py - model_reducer_metadata.py - model_request_metadata.py - model_shutdown_metrics.py - model_tool_metadata_fields.py - model_tool_result_data.py Split typed_dict_status_summaries.py into separate files: - typed_dict_active_summary.py - typed_dict_error_summary.py - typed_dict_maintenance_summary.py Update 15 YAML contract files with fingerprints and schema changes. Add documentation: - NAMING_CONVENTIONS.md - PYDANTIC_BEST_PRACTICES.md Add scripts/regenerate_fingerprints.py utility. Part 2 of 3 in PR #168 split.
Contract Changes: - Fix fingerprint prefix format: v1.1.0: → 1.1.0: (canonical semver) - Add version divergence comment in node_graph_reducer.yaml Model Changes: - Add ConfigDict(extra="forbid") to all common models - Add from_attributes=True to metadata models for ORM compatibility - Fix priority field typing: str → LiteralEventPriority in effect/request metadata - Add docstrings explaining all-optional fields pattern for metadata models - Add note about dict[str, str] per ONEX strict typing standards Documentation Changes: - Update NAMING_CONVENTIONS.md fingerprint format (remove 'v' prefix) - Document both hierarchical and simple profile_tags formats Note: Kept dict[str, str] types per project strict typing standards (dict[str, Any] is prohibited by pre-commit hooks). Complex values should be serialized to JSON strings. [OMN-675]
MAJOR fixes: - Fix fingerprint prefix in contract_registry_reducer.yaml (v1.1.0: -> 1.1.0:) - Add handler documentation to event_bus_wiring_effect.yaml and node_graph_reducer.yaml - Improve fingerprint script error handling with FingerprintResult class - Update PYDANTIC_BEST_PRACTICES.md for consistency with implementations MINOR/NITPICK improvements: - Add required_properties invariant validation to 3 schema models - Add priority field constraints (ge=0, le=100) to subscription/graph models - Expand enum field support to non-string values (int, float, bool, None) - Add comprehensive fingerprint script tests (54 tests) Test coverage: - Type validation tests (default/type mismatches) - Dry-run mode verification - Fingerprint consistency tests - Malformed YAML handling - File size limit enforcement Note: Pre-commit hooks flag pre-existing issues in model_typed_metadata.py (16 classes in one file, str IDs) - these are not introduced by this PR.
CRITICAL fixes: - Fix FSM expression in node_graph_reducer.yaml (is_not_empty → min_length 1) - Fix fingerprint consistency test to use .fingerprint attribute NEW TYPED MODELS (replacing dict[str, str] anti-pattern): - ModelGraphNodeParameter: Individual typed parameter with value inference - ModelGraphNodeParameters: Container with from_dict/to_dict helpers - ModelOutputReference: Validated "node_id.output_name" references - ModelOutputMapping: Container for output mappings - ModelQueryParameters: Query params with ParameterValue union type - ModelEnvelopePayload: Typed event payload with common fields MAJOR fixes: - Add from_attributes=True to ConfigDict in 3 models - Add type↔default consistency validator to ModelConfigSchemaProperty - Improve update_fingerprint_in_yaml() for dict-style versions - Add TODO comments for unimplemented subscription handlers - Fix fingerprint prefix format (remove v prefix) in example contract - Update model_graph_node_inputs.py to use typed models - Update model_operation_data.py to use typed models - Update model_typed_metadata.py to use typed models - Change partition_id/window_id to UUID type MINOR fixes: - Implement 2 placeholder tests with proper assertions - Add 22 new tests for type validation - Update documentation versions to v0.4.0 Note: model_typed_metadata.py single-class-per-file violation is pre-existing (16 classes existed before this PR). Tracked separately.
|
Warning Rate limit exceeded@jonahgabriel has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 53 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
WalkthroughAdds many new strongly-typed Pydantic runtime/event models and re-exports, annotates event-bus wiring contract with planned subscription handler metadata, tightens FSM terminal semantics and Pydantic configs, extends YAML fingerprint insertion logic and tests, and migrates several dict-typed fields to typed models. (36 words) Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Comment |
Keep our changes: - Typed Pydantic models (ModelQueryParameters, ModelEnvelopePayload, etc.) - from_attributes=True in ConfigDict - Improved update_fingerprint_in_yaml() with dict-style version support - Better test assertions using .fingerprint attribute - TODO comments for unimplemented handlers - Fingerprint prefix without 'v' (1.1.0: not v1.1.0:)
Code Review - PR #186: Typed Pydantic Models✅ Overall AssessmentThis is an excellent PR that significantly improves type safety across the codebase by replacing Recommendation: APPROVE with minor suggestions 🎯 Strengths1. Architecture & Design Excellence
2. Comprehensive Testing
3. Security Considerations
4. API Design
🔍 Code Quality AnalysisModel Design Patterns ✅ModelGraphNodeParameters (lines 16-129): # Excellent type inference logic
if isinstance(value, bool): # ✅ Checked BEFORE int (bool is int subclass)
param_type = "boolean"
elif isinstance(value, int):
param_type = "integer"ModelOutputReference (lines 57-70): # ✅ Robust validation with clear error messages
if "." not in v:
raise ValueError(
f"source_reference must be in format 'node_id.output_name', got: {v}"
)Type-Default Validator ( # ✅ Handles bool subclass edge case correctly
if declared_type in ("integer", "int") and isinstance(self.default, bool):
raise ModelOnexError(...) # Prevents True/False as integers🐛 Issues & Concerns🟢 No Critical Issues FoundAll identified issues from PR #180 have been properly addressed:
🟡 Minor Suggestions1. Fingerprint Format ConsistencyFile: # Changed from:
fingerprint: "v1.1.0:920b41e8af2f"
# To:
fingerprint: "1.1.0:920b41e8af2f"Question: Is this intentional removal of the "v" prefix? Ensure this matches the spec in 2. UUID Type Change DocumentationFile: partition_id: UUID | None = Field(...)
window_id: UUID | None = Field(...)Suggestion: Add a migration note in the PR description explaining the change from 3. Error Context ConsistencyFile: context={
"declared_type": self.type,
"actual_type": actual_type_name,
"default_value": str(self.default), # ✅ Good
}Observation: Excellent practice converting 4. Test Coverage for Edge CasesFile: Suggestion: Consider adding tests for:
📊 Performance & MaintainabilityPerformance ✅
Maintainability ✅
🧪 Testing RecommendationsBefore Merge:
Post-Merge:
🎓 Best Practices Demonstrated
📝 SummaryThis PR represents high-quality engineering work that:
Code Quality Score: 9.5/10 Minor deductions only for:
Recommended Action: APPROVE and merge after confirming full test suite passes. 🔗 References
Great work, @jonahgabriel! 🎉 |
- Enhanced module-level docstrings with examples for all 6 new typed models - Added security documentation (MAX_PARAMETERS, max_length constraints, URL encoding) - Cross-referenced related classes in docstrings for discoverability - Simplified UUID field descriptions without legacy migration patterns Addresses PR #186 review feedback: - Security Considerations: Documented existing security measures - Error Context Consistency: Verified consistent patterns - Generate Docstrings: Added comprehensive docstrings to all new models - UUID Type Change: Simplified documentation (no legacy support per ONEX policy) Note: single-class-per-file violation in model_typed_metadata.py is pre-existing (16 classes existed before this PR) - tracked separately for future refactor.
…adata PR #186 review identified a critical issue: ModelGraphNodeInputs was defined in two files with different implementations: - model_graph_node_inputs.py (complete: has from_attributes=True, helper methods) - model_typed_metadata.py (incomplete: lacks from_attributes, no helpers) This commit: - Removes the duplicate class from model_typed_metadata.py - Updates __init__.py to import from the correct module - Removes ModelGraphNodeInputs from model_typed_metadata's __all__ list The correct implementation in model_graph_node_inputs.py includes: - ConfigDict(extra="forbid", from_attributes=True) - get_parameter_dict() helper method - get_output_mapping_dict() helper method Note: single-class-per-file violation in model_typed_metadata.py is pre-existing (15 classes existed before this PR) - tracked separately.
- Remove incorrect 'v' prefix expectation from fingerprint version tests
(actual format is '1.1.0:hash', not 'v1.1.0:hash')
- Fix mixin discovery test to use integer default (3) instead of string ("3")
- Updates 3 test methods in test_contract_v110_fields.py
- Fixes 16 CI test failures in Split 7/20
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/architecture/CONTAINER_TYPES.md (1)
214-222: Update the omnibase_spi release note—v0.2.0 was released on November 5, 2025.The note currently states "Full protocol compliance pending omnibase_spi v0.2.0 release," but v0.2.0 is already published on PyPI with the comprehensive protocol-validation framework included. Update the wording to reflect the current release status (e.g., "Full protocol compliance available with omnibase_spi v0.2.0+" or remove the "pending" language entirely).
🧹 Nitpick comments (4)
docs/architecture/CONTAINER_TYPES.md (1)
3-6: Metadata bump looks fine; consider updating “Version History” for consistency.Header now says Version: 0.4.0 / Last Updated: 2025-12-13 (Lines 4-5), but the “Version History” section only lists v0.1.0 (Line 424). If your docs treat header version as a released doc revision, add a v0.4.0 entry (even a short bullet) to avoid reader confusion.
src/omnibase_core/models/common/model_query_parameters.py (1)
84-91: Minor: Consider using keyword argument for context in ModelOnexError.The
ModelOnexErrorconstructor signature shows**contextas the last parameter. The current usage passescontext={}which creates a nested dict undercontextkey. This may work but could be clearer.raise ModelOnexError( error_code=EnumCoreErrorCode.VALIDATION_ERROR, message=f"Parameter count ({len(self.items)}) exceeds maximum ({self.MAX_PARAMETERS})", - context={ - "parameter_count": len(self.items), - "max_parameters": self.MAX_PARAMETERS, - }, + parameter_count=len(self.items), + max_parameters=self.MAX_PARAMETERS, )src/omnibase_core/models/common/model_output_mapping.py (1)
70-98: Consider caching for O(1) lookup if reference lists grow large.The
get_source_referenceandget_referencemethods use O(n) linear search through the references list. This is fine for typical small mappings, but if reference counts grow significantly, consider adding an internal_by_local_name: dict[str, ModelOutputReference]cache populated during construction.src/omnibase_core/models/common/model_typed_metadata.py (1)
248-258: Type mapping is well-structured but could be extended.The
type_validationsdict handles common JSON Schema types. Consider whetherarrayandobjecttypes need validation (currently not in the mapping), though those may have different default value structures.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
contracts/runtime/event_bus_wiring_effect.yaml(1 hunks)contracts/runtime/node_graph_reducer.yaml(1 hunks)docs/architecture/CONTAINER_TYPES.md(1 hunks)docs/guides/ENUM_NODE_KIND_MIGRATION.md(2 hunks)docs/testing/CI_TEST_STRATEGY.md(1 hunks)examples/contracts/effect/filesystem_operations.yaml(1 hunks)scripts/regenerate_fingerprints.py(2 hunks)scripts/validation/README.md(1 hunks)src/omnibase_core/models/common/__init__.py(2 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_event_subscription_config.py(1 hunks)src/omnibase_core/models/common/model_graph_node_inputs.py(3 hunks)src/omnibase_core/models/common/model_graph_node_parameter.py(1 hunks)src/omnibase_core/models/common/model_graph_node_parameters.py(1 hunks)src/omnibase_core/models/common/model_operation_data.py(4 hunks)src/omnibase_core/models/common/model_output_mapping.py(1 hunks)src/omnibase_core/models/common/model_output_reference.py(1 hunks)src/omnibase_core/models/common/model_query_parameters.py(1 hunks)src/omnibase_core/models/common/model_typed_metadata.py(9 hunks)tests/unit/contracts/test_contract_v110_fields.py(5 hunks)tests/unit/discovery/test_mixin_discovery.py(1 hunks)tests/unit/models/common/test_model_typed_metadata.py(1 hunks)tests/unit/scripts/test_regenerate_fingerprints.py(4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/common/model_graph_node_parameter.pysrc/omnibase_core/models/common/model_envelope_payload.pytests/unit/models/common/test_model_typed_metadata.pyscripts/regenerate_fingerprints.pysrc/omnibase_core/models/common/model_query_parameters.pysrc/omnibase_core/models/common/__init__.pysrc/omnibase_core/models/common/model_output_mapping.pysrc/omnibase_core/models/common/model_graph_node_parameters.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_event_subscription_config.pysrc/omnibase_core/models/common/model_output_reference.pytests/unit/discovery/test_mixin_discovery.pytests/unit/contracts/test_contract_v110_fields.pysrc/omnibase_core/models/common/model_graph_node_inputs.pysrc/omnibase_core/models/common/model_typed_metadata.pytests/unit/scripts/test_regenerate_fingerprints.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/common/model_graph_node_parameter.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/common/model_query_parameters.pysrc/omnibase_core/models/common/__init__.pysrc/omnibase_core/models/common/model_output_mapping.pysrc/omnibase_core/models/common/model_graph_node_parameters.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_event_subscription_config.pysrc/omnibase_core/models/common/model_output_reference.pysrc/omnibase_core/models/common/model_graph_node_inputs.pysrc/omnibase_core/models/common/model_typed_metadata.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/models/common/test_model_typed_metadata.pytests/unit/discovery/test_mixin_discovery.pytests/unit/contracts/test_contract_v110_fields.pytests/unit/scripts/test_regenerate_fingerprints.py
🧠 Learnings (5)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to tests/**/*.py : Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Applied to files:
docs/testing/CI_TEST_STRATEGY.md
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Applied to files:
docs/guides/ENUM_NODE_KIND_MIGRATION.md
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: No backwards compatibility is guaranteed until v1.0.0 - breaking changes may occur between commits during pre-release development (v0.4.0)
Applied to files:
docs/guides/ENUM_NODE_KIND_MIGRATION.md
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Applied to files:
src/omnibase_core/models/common/model_graph_node_parameters.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/common/model_event_subscription_config.py
🧬 Code graph analysis (7)
src/omnibase_core/models/common/model_query_parameters.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)error_code(253-255)message(248-250)context(275-278)
src/omnibase_core/models/common/model_output_mapping.py (1)
src/omnibase_core/models/common/model_output_reference.py (2)
ModelOutputReference(39-108)source_node_id(101-103)
src/omnibase_core/models/common/model_graph_node_parameters.py (2)
src/omnibase_core/models/common/model_graph_node_parameter.py (1)
ModelGraphNodeParameter(33-117)src/omnibase_core/models/common/model_graph_node_inputs.py (1)
get_parameter_dict(59-66)
src/omnibase_core/models/common/model_operation_data.py (2)
src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(42-293)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-309)
src/omnibase_core/models/common/model_graph_node_inputs.py (2)
src/omnibase_core/models/common/model_graph_node_parameters.py (2)
ModelGraphNodeParameters(35-145)get_parameter_dict(64-71)src/omnibase_core/models/common/model_output_mapping.py (2)
ModelOutputMapping(31-144)get_mapping_dict(61-68)
src/omnibase_core/models/common/model_typed_metadata.py (7)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(42-293)src/omnibase_core/models/common/model_graph_node_parameters.py (1)
ModelGraphNodeParameters(35-145)src/omnibase_core/models/common/model_output_mapping.py (1)
ModelOutputMapping(31-144)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-309)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)
tests/unit/scripts/test_regenerate_fingerprints.py (1)
scripts/regenerate_fingerprints.py (2)
update_fingerprint_in_yaml(290-389)compute_fingerprint_for_contract(194-235)
🔇 Additional comments (46)
docs/testing/CI_TEST_STRATEGY.md (1)
300-301: Metadata updated correctly for v0.4.0 release cycle.The version and last-updated timestamps align with the broader documentation refresh across the PR. The performance monitoring thresholds in the document (lines 286–288) are consistent with the CI performance expectations outlined in the learnings.
docs/guides/ENUM_NODE_KIND_MIGRATION.md (1)
3-4: Metadata updates are consistent and properly synchronized.The version and last-updated date have been correctly updated to 0.4.0 and 2025-12-13 in both the header and footer. The documentation content itself remains unchanged, which is appropriate since this PR does not alter the enum migration guidance described herein.
Also applies to: 269-270
contracts/runtime/event_bus_wiring_effect.yaml (1)
224-230: Well-structured TODO with clear action items.The subscription handler registration TODO is comprehensive, with explicitly numbered steps (1–5) that provide clear guidance on implementation sequence. This is helpful documentation for future work.
examples/contracts/effect/filesystem_operations.yaml (1)
70-70: LGTM - Fingerprint prefix normalization.The removal of the 'v' prefix aligns with the PR's broader effort to normalize version formats in fingerprints. This maintains consistency with the updated fingerprinting script and related tests.
scripts/validation/README.md (1)
789-790: LGTM - Documentation metadata update.The version bump to v0.4.0 and updated date align with the broader documentation updates mentioned in the PR objectives.
contracts/runtime/node_graph_reducer.yaml (1)
111-116: LGTM - FSM expression fix.The change from
is_not_emptytomin_length 1provides explicit data-length validation semantics. This correctly ensures the node registry contains at least one node definition before transitioning to the wiring state, and the expression now clearly matches the description.src/omnibase_core/models/common/model_event_subscription_config.py (1)
18-18: LGTM - ConfigDict enhancement follows guidelines.Adding
from_attributes=Trueis appropriate for this model since it's designed to be nested in other Pydantic models (replacinglist[dict[str, Any]]inModelYamlContract). Based on learnings, this is required for immutable value objects nested in other Pydantic models.tests/unit/discovery/test_mixin_discovery.py (1)
60-66: LGTM - Test fixture type correction.Changing the default from
"3"(string) to3(integer) correctly aligns withtype="integer". This is a necessary fix given the new type-default consistency validation introduced in this PR forModelConfigSchemaProperty.tests/unit/models/common/test_model_typed_metadata.py (6)
30-127: LGTM - Comprehensive type mismatch error tests.The test class covers all PR #180 requirements with proper error code assertions. The special handling for
boolas a subclass ofint(lines 88-114) is important, and the fallback to catch eitherModelOnexErrororValueErrorfor list defaults (line 122) correctly handles Pydantic's own validation.
135-218: LGTM - Valid combination tests.Good coverage of valid type-default combinations including widening conversions (
int→number/float) andNonedefaults. The tests correctly verify that the validator doesn't reject legitimate configurations.
226-276: LGTM - Type alias handling tests.Good approach testing both valid and invalid defaults for each alias (
str/string,int/integer,bool/boolean). This ensures the validator correctly maps Python type names to schema types.
284-316: LGTM - Unknown type handling.Correctly verifies that unknown/custom types (
array,object,custom_type) skip type validation. This is a good design decision for extensibility and forward compatibility.
324-369: LGTM - Integration tests with ModelMixinConfigSchema.Good verification that type validation errors propagate correctly when
ModelConfigSchemaPropertyis nested withinModelMixinConfigSchema. This ensures the validation works in real-world usage patterns.
377-411: LGTM - Error context verification.Good verification that error context includes
declared_type,actual_type, anddefault_value. This provides useful debugging information for developers encountering type mismatch errors.tests/unit/contracts/test_contract_v110_fields.py (1)
9-9: LGTM! Fingerprint format changes are consistent.The removal of the 'v' prefix from fingerprint version format is consistently applied across docstrings, test assertions, and regex patterns. This aligns with the PR objective to normalize fingerprint handling.
Also applies to: 112-112, 131-145, 189-194
src/omnibase_core/models/common/model_graph_node_parameters.py (1)
57-57: LGTM! Well-designed container with robust type inference.The model configuration follows coding guidelines (from_attributes=True for nested models), and the type inference logic correctly checks bool before int to handle Python's inheritance hierarchy. The defensive fallback at lines 131-135 with pragma: no cover is appropriate.
Also applies to: 59-145
scripts/regenerate_fingerprints.py (2)
238-288: LGTM! Well-designed helper for version block detection.The
_find_version_block_end()function correctly handles both inline and dict-style version blocks by tracking indentation levels. The empty line handling at lines 276-279 prevents premature block termination.
290-389: LGTM! Robust multi-path fingerprint insertion logic.The enhanced
update_fingerprint_in_yaml()properly handles multiple YAML formats with clear prioritization:
- Replace existing fingerprint
- Insert after inline version
- Insert after dict-style version block (using the new helper)
- Insert after contract_version
- Insert after name field
- Fallback to end-of-file
The string manipulation at lines 352-356 correctly maintains YAML formatting by stripping trailing whitespace before and leading newlines after the insertion point.
src/omnibase_core/models/common/model_graph_node_parameter.py (1)
84-117: LGTM! Clean parameter model with appropriate constraints.The model follows the established ModelToolParameter pattern and uses appropriate field constraints (min_length, max_length) for validation. The parameter_type enum via json_schema_extra aligns with the type inference logic in ModelGraphNodeParameters.
src/omnibase_core/models/common/model_output_reference.py (1)
62-98: LGTM! Robust validation and clean property accessors.The source_reference format validation at lines 85-98 correctly enforces the "node_id.output_name" format with non-empty parts. The properties safely parse the validated reference using
split(".", 1). Security constraints (max_length=512 for source_reference) prevent memory exhaustion attacks.Also applies to: 100-108
src/omnibase_core/models/common/model_envelope_payload.py (4)
39-93: LGTM! Well-designed envelope payload with flexible data field.The model correctly separates common typed fields (event_type, source, timestamp, correlation_id) from event-specific data in a flexible dictionary. The PayloadDataValue type alias provides appropriate type safety while maintaining flexibility.
95-144: LGTM! Clean factory methods with proper field extraction.The
from_dict()method correctly separates known typed fields from unknown fields (lines 117-120), placing extras in the data dictionary. The string conversion at lines 123-128 ensures type consistency.
146-191: LGTM! Appropriate serialization with correct type handling.The
to_string_dict()method properly handles type conversions:
- Booleans to "true"/"false" (lines 185-186)
- Lists to comma-separated strings (lines 187-188)
- Other types via str() (line 190)
This provides clean interoperability with string-based APIs.
229-253: LGTM! Immutable update pattern preserves data integrity.Both
set_data()andwith_timestamp()correctly usemodel_copy()to return new instances rather than mutating the original, which is essential for event-driven workflows where payloads should be immutable.src/omnibase_core/models/common/model_operation_data.py (1)
9-14: Verify compatibility with consuming code.The field type changes from
dict[str, str]toModelQueryParametersandModelEnvelopePayloadare breaking changes. Ensure that all code consuming ModelOperationData has been updated to use the new typed models and their helper methods (e.g.,to_dict(),to_string_dict()).Run this script to verify that consuming code has been updated:
#!/bin/bash # Check for dict-style access patterns on ModelOperationData fields that may need updating # Search for ModelOperationData usage with parameters or envelope_payload rg -n --type=py -A 3 -B 3 "ModelOperationData\(" | rg "(parameters|envelope_payload)" # Search for direct dict access patterns that might break rg -n --type=py "operation_data\.(parameters|envelope_payload)\[" # Search for dict construction patterns rg -n --type=py "parameters\s*=\s*\{.*\}" | rg "ModelOperationData"Also applies to: 29-29, 50-53, 94-97
src/omnibase_core/models/common/model_graph_node_inputs.py (1)
40-49: Verify helper method implementation and consuming code patterns.The review requests verification of helper methods (
get_parameter_dict()andget_output_mapping_dict()at lines 59-76) and consuming code patterns. Confirm that:
- Helper methods exist at the specified line ranges
- All code accessing parameters or from_outputs uses these helpers instead of direct dict access
- ConfigDict setting at lines 40-49 includes
from_attributes=Trueas required by the coding guidelinessrc/omnibase_core/models/common/__init__.py (2)
11-23: New typed model imports look correct.The imports correctly bring in the new typed models (ModelEnvelopePayload, ModelGraphNodeParameter, ModelGraphNodeParameters, ModelOutputMapping, ModelOutputReference, ModelQueryParameters) and their associated type aliases. The organization follows the existing alphabetical pattern.
49-90: Export list properly updated with new typed models.The
__all__list correctly includes all new typed models and type aliases. Note thatModelGraphNodeInputsis imported (line 14) but intentionally excluded from__all__, which aligns with the PR's de-emphasis of this type as mentioned in the summary.src/omnibase_core/models/common/model_query_parameters.py (4)
1-31: Well-documented module with clear security considerations.The module docstring provides excellent documentation including usage examples, security considerations (MAX_PARAMETERS, URL encoding), and cross-references. This follows best practices for public API documentation.
70-92: Configuration and validation follow ONEX patterns correctly.The
ConfigDictcorrectly includesfrom_attributes=Trueas per coding guidelines for models used in parallel test execution. The validator properly raisesModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERRORfollowing the project's structured error handling pattern.
197-217: Correct handling of bool/int edge case in get_int.The explicit check
not isinstance(value, bool)on line 210 correctly handles Python's quirk whereboolis a subclass ofint. This preventsTrue/Falsefrom being incorrectly returned as integers.
273-297: Immutable-style mutators are well-designed.The
setandremovemethods correctly return new instances rather than mutating in place, which aligns with the immutable value object pattern mentioned in the coding guidelines. This is safe for parallel test execution and nested model usage.tests/unit/scripts/test_regenerate_fingerprints.py (6)
455-462: Correctly updated to use FingerprintResult.fingerprint attribute.The test now properly accesses the
fingerprintattribute fromFingerprintResultobjects. The filtering logicfp.fingerprint is not Nonecorrectly handles cases where fingerprint computation fails.
1105-1131: Good test coverage for dict-style version blocks.This test verifies the PR #180 requirement for handling structured version blocks with
major,minor,patchfields. The assertion thatfingerprint_pos > version_block_endconfirms correct insertion order.
1133-1151: Test covers contract_version field handling.This test addresses the flexible contract format using
contract_versioninstead of structured version blocks. The positioning assertion ensures fingerprint is inserted after the contract_version line.
1153-1167: Test for fallback when no version field exists.Good edge case coverage - ensures fingerprint is still inserted (after
namefield) even when version information is absent from the contract.
1169-1190: Preservation test ensures no field corruption.This test validates that inserting fingerprint after dict-style version blocks doesn't corrupt other fields like
node_type. Important regression protection.
1213-1237: Error handling tests updated for FingerprintResult structure.The tests now correctly check
result.fingerprint is Noneandresult.error is not Nonefor validation errors. The fallback logic on lines 1233-1236 properly handles permissive models that may still compute fingerprints.src/omnibase_core/models/common/model_output_mapping.py (2)
31-59: Well-structured typed output mapping model.The class correctly replaces
dict[str, str]patterns with strongly-typedModelOutputReferenceinstances. TheConfigDictproperly includesfrom_attributes=Trueas per coding guidelines.
121-144: Factory method correctly constructs typed references from dict.The
from_dictclassmethod properly transforms adict[str, str]into typedModelOutputReferenceinstances, enabling gradual migration from untyped patterns. The implementation correctly passessource_referenceandlocal_nameto the reference constructor.src/omnibase_core/models/common/model_typed_metadata.py (6)
8-20: New imports support typed model integration.The imports correctly bring in
UUIDfrom standard library,Selffor return type annotations, and the new typed models (ModelEnvelopePayload,ModelGraphNodeParameters,ModelOutputMapping,ModelQueryParameters). TheModelOnexErrorimport enables structured error handling in validators.
225-310: Comprehensive type↔default consistency validator.The validator thoroughly handles all type combinations and correctly addresses Python's quirk where
boolis a subclass ofint. The explicit rejection of boolean values for integer and numeric types (lines 264-293) prevents subtle bugs. The error messages are clear and include helpful context.
425-428: Breaking change: envelope_payload now uses ModelEnvelopePayload.Similar to
parameters, this field now requires the typed model. Thedefault_factoryprovides safe empty initialization.
799-804: Consistent priority constraints across models.The
ModelEventSubscriptionConfig.priorityfield uses the same 0-100 bounds asModelGraphNodeData.priority, maintaining consistency across the codebase.
549-598: Verify UUID type compatibility across codebaseThe UUID typing change for
partition_idandwindow_idis a solid improvement for type safety. However, before approval, ensure all usages of these fields throughout the codebase have been updated to handle UUID types. Specifically, check for:
- Any code that assigns string values to these fields
- Serialization/deserialization logic that may expect string format
- API calls or data sources that provide these as strings
- Test fixtures and mock data using these fields
The docstring example is clear and helpful for consumers understanding the new requirements.
381-384: Breaking change: ModelOperationData.parameters field now uses ModelQueryParameters instead of dict[str, str].Code accessing the
parametersfield directly will need to update from dict-style access (e.g.,params[key]) to use the.itemsproperty or typed accessor methods like.get(). Thedefault_factory=ModelQueryParametersensures empty initialization remains backward-compatible. For dict conversion, useModelQueryParameters.from_dict()orModelQueryParameters.from_string_dict().
Merged origin/main into fix/pr-180-review-feedback. - Kept improved documentation from main while preserving the fingerprint version format fix (no 'v' prefix) - Removed empty service/ directories (duplicate of services/)
PR Review: Typed Pydantic Models ImplementationSummaryThis PR successfully addresses feedback from PR #180 by replacing ✅ Strengths1. Excellent Type Safety ImprovementsThe new typed models replace untyped dictionaries with explicit validation:
Each model includes proper validation, helper methods, and comprehensive documentation. 2. Strong Security ConsiderationsGood security practices throughout:
3. Comprehensive Test CoverageExcellent test additions:
4. Critical FSM FixFixed invalid FSM expression: 5. Improved Fingerprint ScriptEnhanced
🔍 Code Quality ObservationsPositive Patterns
Minor Observations
🟡 Suggestions for Consideration1. ModelTypedMetadata Single-File ViolationThe PR notes this pre-existing issue:
Recommendation: Since this PR adds MORE models to this file (
2. Type Alias ConsistencyThree type aliases defined across modules:
Question: Are these intentionally different types, or could they be unified? If different, the distinction could be documented. 3. Fingerprint Prefix Format ChangeChanged from Recommendation: Ensure this is documented in:
4. Test Coverage for New ModelsWhile
Recommendation: Add unit test files for each new model covering:
5. UUID Type ChangeChanged Question: Are existing contracts/data using string UUIDs? May need migration guidance. 🟢 Performance Considerations
🔒 Security AssessmentExcellent Security Practices ✅
No Security Concerns Found ✅📋 Pre-Merge Checklist
🎯 Final RecommendationAPPROVE with minor follow-up recommendations This PR successfully achieves its goal of replacing
The suggestions above are non-blocking improvements that can be addressed in follow-up PRs:
Great work on addressing PR #180 feedback comprehensively! 🎉 Reviewed by: Claude Code Agent |
- Fix priority constraint in ModelGraphNodeData: change le=100 to le=1000 to match ModelWorkflowStep and allow priority=500 used in tests - Replace OMN-XXX placeholders with generic TODO comments in event_bus_wiring_effect.yaml - Use keyword arguments for ModelOnexError context in model_query_parameters.py for cleaner API usage Note: Pre-commit hook failures are pre-existing issues unrelated to this change: - service/ vs services/ directory duplication (tracked separately) - model_typed_metadata.py single-class-per-file (tracked separately)
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/omnibase_core/models/common/model_query_parameters.py (1)
43-45: Consider the repo’s constrained-TypeVar pattern for primitive value unions.
This is totally workable as-is, but if you want consistency with existing models, consider aligning the “primitive value” typing style. Based on learnings, the codebase prefers constrainedTypeVar("T", str, int, float, bool)patterns over hardcoded unions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
contracts/runtime/event_bus_wiring_effect.yaml(1 hunks)src/omnibase_core/models/common/model_graph_node_data.py(1 hunks)src/omnibase_core/models/common/model_query_parameters.py(1 hunks)src/omnibase_core/models/common/model_typed_metadata.py(9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- contracts/runtime/event_bus_wiring_effect.yaml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/common/model_query_parameters.pysrc/omnibase_core/models/common/model_graph_node_data.pysrc/omnibase_core/models/common/model_typed_metadata.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/common/model_query_parameters.pysrc/omnibase_core/models/common/model_graph_node_data.pysrc/omnibase_core/models/common/model_typed_metadata.py
🧠 Learnings (1)
📚 Learning: 2025-09-23T22:28:00.333Z
Learnt from: jonahgabriel
Repo: OmniNode-ai/omnibase_core PR: 32
File: examples/practical_migration_example.py:125-134
Timestamp: 2025-09-23T22:28:00.333Z
Learning: The omnibase_core codebase extensively uses constrained TypeVars for primitive types with the pattern `T = TypeVar("T", str, int, float, bool)` across multiple models including ModelTypedMetrics, ModelGenericMetadata, and ModelExecutionResult. This is the preferred approach over hardcoded union types like `str | int | bool | float | None`.
Applied to files:
src/omnibase_core/models/common/model_query_parameters.py
🧬 Code graph analysis (2)
src/omnibase_core/models/common/model_query_parameters.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)error_code(253-255)message(248-250)
src/omnibase_core/models/common/model_typed_metadata.py (7)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(42-293)src/omnibase_core/models/common/model_graph_node_parameters.py (1)
ModelGraphNodeParameters(35-145)src/omnibase_core/models/common/model_output_mapping.py (1)
ModelOutputMapping(31-144)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-307)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)
⏰ 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: Smoke Tests
- GitHub Check: claude-review
🔇 Additional comments (6)
src/omnibase_core/models/common/model_graph_node_data.py (1)
22-27: Priority constraint/doc now consistent (0–1000).
This looks correct and should avoid rejecting higher priorities that were previously in use.src/omnibase_core/models/common/model_typed_metadata.py (4)
225-310: Type↔default validator looks correct (incl. bool-subclass edge cases).
Nice touch rejectingboolforint/numberdespite subclassing.
381-384: Verify deserialization/migration behavior for newly-typed fields (ModelQueryParameters,ModelEnvelopePayload).
If any upstream code still provides a “raw”dict[str, str](instead of{"items": {...}}/ the envelope shape), Pydantic may reject it; confirm all construction paths were updated or add amode="before"adapter.Also applies to: 425-428
591-598: UUID migration: confirm callers rely on UUID parsing (strings) and not arbitrary IDs.
Pydantic usually parses UUID strings, but anything non-UUID will now fail validation.
715-720: Priority range widened to 0–1000—good.
This resolves the earlier “priority=500 becomes invalid” class of breakages while keeping constraints.src/omnibase_core/models/common/model_query_parameters.py (1)
83-89: No action required—the original code is correct.ModelOnexError's
__init__signature explicitly accepts**context: Any, which meansparameter_count=andmax_parameters=are valid and will be properly captured as context. The suggestion to wrap them in acontext={...}dict is unnecessary.Likely an incorrect or invalid review comment.
PR #186 Code Review - Comprehensive Analysis🎯 Executive SummaryOverall Assessment: ✅ EXCELLENT - APPROVE WITH CONFIDENCE This PR successfully addresses all feedback from PR #180 by replacing ✅ Code Quality: EXCELLENTNew Models (6 files) - All OutstandingModelEnvelopePayload (
ModelGraphNodeParameter (
ModelGraphNodeParameters (
ModelOutputReference (
ModelOutputMapping (
ModelQueryParameters (
✅ Type Safety: EXCELLENTZero Strict Union Types:
from_attributes=True Usage: ✅ Correctly applied to 14 models for pytest-xdist parallel execution compatibility (per CLAUDE.md guidelines) ✅ Architecture Compliance: EXCELLENTONEX Patterns Followed:
Updated Files Integration:
✅ Testing: COMPREHENSIVE22 New Tests in
80+ Tests in
Quality: Clear docstrings, proper fixtures, comprehensive error assertions ✅ Security: EXCELLENTDoS Prevention:
Injection Prevention:
Type Safety:
|
Additional fixes from PR #186 review: - Add O(1) caching for ModelOutputMapping reference lookups - Add security constraints (MAX limits, length validation) to: - ModelEnvelopePayload: MAX_FIELD_LENGTH, MAX_DATA_ENTRIES - ModelOperationData: MAX_HEADERS, MAX_BODY_SIZE, etc. - ModelOutputReference: max_length on description/transform - Update Version History in CONTAINER_TYPES.md for v0.4.0
PR Review: Typed Pydantic Models MigrationOverall Assessment: ✅ APPROVED with minor suggestions This is an excellent refactoring that addresses the anti-pattern of using 🎯 Strengths1. Strong Type Safety ⭐The new models ( # OLD - loses type information
parameters: dict[str, str] = Field(...)
# NEW - preserves types with validation
parameters: ModelGraphNodeParameters = Field(...)
# Supports: str | int | float | bool | list[str] | dict[str, str]2. Excellent Documentation 📚
3. Security Hardening 🔒All models implement proper security constraints:
4. Performance Optimization ⚡
_by_local_name: dict[str, ModelOutputReference] = PrivateAttr(default_factory=dict)
def get_reference(self, local_name: str) -> ModelOutputReference | None:
return self._by_local_name.get(local_name) # O(1) lookup5. Comprehensive Testing ✅
6. Critical Bug Fixes 🐛
💡 Minor Suggestions1. Type Inference Edge CaseIn else: # pragma: no cover
param_type = "string" # type: ignore[unreachable]
value = str(value)Suggestion: While this is defensive programming, consider logging a warning if this fallback is triggered in production to detect unexpected runtime behavior: else: # pragma: no cover
logger.warning(f"Unexpected parameter type {type(value)} for {name}, converting to string")
param_type = "string"
value = str(value)2. Boolean String ParsingIn return value.lower() in ("true", "1", "yes", "on")Suggestion: Document this behavior in the docstring, as it's more permissive than standard Python 3. Error Context Consistency
raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"Parameter count ({len(self.items)}) exceeds maximum ({self.MAX_PARAMETERS})",
parameter_count=len(self.items),
max_parameters=self.MAX_PARAMETERS,
)Verify all other 4. Test Coverage for Cache Invalidation
@model_validator(mode="after")
def _build_lookup_cache(self) -> "ModelOutputMapping":
self._by_local_name = {ref.local_name: ref for ref in self.references}
return selfQuestion: Does this cache rebuild correctly if the model is copied with 5. Documentation Version UpdatesGood job updating docs to v0.4.0! Verify these files are also updated:
🔍 Code Quality Checklist
📝 Pre-existing Issues (Not Blockers)As noted in your PR description, these exist but are tracked separately:
These are acceptable technical debt items to address in future PRs. 🚀 Recommendations
🎓 Learning Points for TeamThis PR demonstrates excellent patterns:
Great work addressing all the feedback from PR #180! This significantly improves the codebase's type safety and maintainability. Recommendation: ✅ APPROVE (pending CI green) |
- Add type↔value consistency validation for default/enum fields in ModelTypedMetadata - Fix FSM contract is_recoverable constraint for terminal stopped state - Add handler_status and handler_implementation metadata to event_bus_wiring contract - Improve regenerate_fingerprints.py to handle flow-style YAML and OSError - Remove redundant validate_assignment=True from frozen models (6 files) - Update documentation version references from v0.4.0 to v0.3.6 - Add 43 new tests for type validation and 3 new contract handler tests
…dback # Conflicts: # contracts/runtime/node_graph_reducer.yaml
Remove the 'v' prefix from fingerprint version strings in all contract
YAML files to match the expected format in v1.1.0 contract spec.
The fingerprint format should be "{version}:{12-char-hash}" where version
is X.Y.Z without a 'v' prefix (e.g., "1.1.0:c753f440fda3").
This matches the contract_version dict format {major: X, minor: Y, patch: Z}
and fixes test_fingerprint_version_matches_contract_version failures.
Files updated:
- contracts/runtime/contract_registry_reducer.yaml
- contracts/runtime/contract_loader_effect.yaml
- contracts/runtime/runtime_orchestrator.yaml
- contracts/runtime/event_bus_wiring_effect.yaml
- examples/contracts/reducer_metrics_aggregator.yaml
- examples/contracts/compute/user_profile_normalizer.yaml
- examples/contracts/orchestrator_data_pipeline.yaml
- examples/contracts/effect/db_query.yaml
- examples/contracts/effect/kafka_produce.yaml
- examples/contracts/effect/http_api_call.yaml
- examples/contracts/effect/resilient_effect.yaml
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/conventions/NAMING_CONVENTIONS.md (1)
371-382: Update line 474 to reflect current project version: changeomnibase_core v0.3.6toomnibase_core v0.4.0.The documentation consistently references v0.4.0 throughout (CHANGELOG.md, CLAUDE.md, guides, architecture docs), but line 474 still shows v0.3.6. The examples at lines 371-382 are generic semver illustrations and are correctly formatted.
src/omnibase_core/models/common/model_typed_metadata.py (1)
410-484: Remove or reconcile duplicate ModelOperationData implementations—security constraints missing from exported version.Two incompatible implementations of
ModelOperationDataexist:
src/omnibase_core/models/common/model_operation_data.py(full-featured, unused):
- Includes security constants:
MAX_HEADERS(100),MAX_BODY_SIZE(1MB),MAX_CONTENT_SIZE(10MB),MAX_MESSAGE_SIZE(1MB)- Enforces
max_lengthon string fields (action: 256, target: 1024, table: 256, query: 65536, url: 2048, method: 16, path: 4096, queue_name: 256)- Includes
@model_validatorwith_validate_sizes()to prevent DoS attacks- Configures
ConfigDict(extra="forbid", from_attributes=True)- Not imported anywhere
src/omnibase_core/models/common/model_typed_metadata.py(simplified, exported):
- No security constants
- No
max_lengthconstraints on string fields- No
@model_validatorfor size validation- No
ConfigDictsettings- Exported via
__init__.pyas the public APIThe weaker version without security constraints is what library users receive. The security-hardened implementation is orphaned. Consolidate to a single, security-hardened implementation and remove the duplicate. If the simpler version was intentional for a specific use case, document that clearly and export both with distinct names.
♻️ Duplicate comments (1)
contracts/runtime/event_bus_wiring_effect.yaml (1)
237-237: Placeholder "OMN-XXX" still present.The past review flagged this placeholder. While marked as addressed,
OMN-XXXremains at line 237. Either replace with an actual ticket number or remove the placeholder reference.
🧹 Nitpick comments (3)
src/omnibase_core/models/common/model_output_mapping.py (1)
65-69: Consider validating for duplicatelocal_namevalues.The cache building logic
{ref.local_name: ref for ref in self.references}will silently overwrite duplicates ifreferencescontains multiple entries with the samelocal_name. Whilefrom_dict()cannot produce duplicates (dict keys are unique), direct instantiation with a list could.@model_validator(mode="after") def _build_lookup_cache(self) -> "ModelOutputMapping": """Build the lookup cache after model initialization.""" + seen: set[str] = set() + for ref in self.references: + if ref.local_name in seen: + raise ValueError(f"Duplicate local_name in references: {ref.local_name}") + seen.add(ref.local_name) self._by_local_name = {ref.local_name: ref for ref in self.references} return selfsrc/omnibase_core/models/common/model_envelope_payload.py (1)
264-274: Consider using UTC for timestamp consistency in distributed systems.
datetime.now()returns local time, which can cause inconsistencies in distributed event-driven systems across time zones.+from datetime import datetime, timezone + def with_timestamp(self, timestamp: datetime | None = None) -> ModelEnvelopePayload: """Create a new instance with updated timestamp. Args: timestamp: Timestamp to set (defaults to now in UTC). Returns: New ModelEnvelopePayload instance with timestamp. """ - ts = timestamp or datetime.now() + ts = timestamp or datetime.now(timezone.utc) return self.model_copy(update={"timestamp": ts.isoformat()})src/omnibase_core/models/common/model_typed_metadata.py (1)
855-860: Verify intentional priority range difference between models.The
priorityfield has different ranges across models:
ModelGraphNodeData.priority: 0-1000 (line 771-776)ModelEventSubscriptionConfig.priority: 0-100 (line 855-860)This may be intentional (different contexts require different priority granularity), but please confirm:
- Is the 10x difference in range intentional?
- Should the field descriptions explicitly note that these priority scales are not directly comparable?
If intentional, consider adding clarifying notes to the descriptions:
priority: int | None = Field( default=None, - description="Node execution priority (0-1000, higher = more urgent)", + description="Node execution priority (0-1000, higher = more urgent). Note: Not directly comparable to subscription priority (0-100 scale)", ge=0, le=1000, )priority: int = Field( default=0, - description="Subscription priority (0-100, higher = more urgent)", + description="Subscription priority (0-100, higher = more urgent). Note: Not directly comparable to node execution priority (0-1000 scale)", ge=0, le=100, )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
contracts/runtime/event_bus_wiring_effect.yaml(2 hunks)contracts/runtime/node_graph_reducer.yaml(3 hunks)docs/architecture/CONTAINER_TYPES.md(2 hunks)docs/conventions/NAMING_CONVENTIONS.md(3 hunks)docs/conventions/PYDANTIC_BEST_PRACTICES.md(1 hunks)scripts/regenerate_fingerprints.py(3 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_operation_data.py(4 hunks)src/omnibase_core/models/common/model_output_mapping.py(1 hunks)src/omnibase_core/models/common/model_output_reference.py(1 hunks)src/omnibase_core/models/common/model_typed_metadata.py(9 hunks)src/omnibase_core/models/contracts/model_contract_reducer.py(0 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsm_state_definition.py(0 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.py(0 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsm_subcontract.py(0 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.py(0 hunks)src/omnibase_core/models/core/model_service_discovery_config.py(1 hunks)tests/unit/contracts/test_runtime_contracts.py(1 hunks)tests/unit/models/common/test_model_typed_metadata.py(1 hunks)
💤 Files with no reviewable changes (5)
- src/omnibase_core/models/contracts/subcontracts/model_fsm_subcontract.py
- src/omnibase_core/models/contracts/subcontracts/model_fsm_state_definition.py
- src/omnibase_core/models/contracts/model_contract_reducer.py
- src/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.py
- src/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.py
✅ Files skipped from review due to trivial changes (1)
- docs/conventions/PYDANTIC_BEST_PRACTICES.md
🚧 Files skipped from review as they are similar to previous changes (2)
- src/omnibase_core/models/common/model_output_reference.py
- docs/architecture/CONTAINER_TYPES.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/core/model_service_discovery_config.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_output_mapping.pyscripts/regenerate_fingerprints.pytests/unit/models/common/test_model_typed_metadata.pysrc/omnibase_core/models/common/model_typed_metadata.pytests/unit/contracts/test_runtime_contracts.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/core/model_service_discovery_config.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_output_mapping.pysrc/omnibase_core/models/common/model_typed_metadata.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/models/common/test_model_typed_metadata.pytests/unit/contracts/test_runtime_contracts.py
🧠 Learnings (1)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/core/model_service_discovery_config.py
🧬 Code graph analysis (6)
src/omnibase_core/models/common/model_envelope_payload.py (7)
src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (2)
event_type(55-57)source(50-52)src/omnibase_core/models/security/model_security_policy_data.py (1)
data(27-32)src/omnibase_core/models/common/model_output_mapping.py (1)
from_dict(133-155)src/omnibase_core/models/common/model_query_parameters.py (6)
from_dict(93-102)get(168-178)from_string_dict(105-116)to_dict(118-124)to_string_dict(126-136)has(260-269)src/omnibase_core/models/common/model_graph_node_parameters.py (1)
from_dict(101-145)src/omnibase_core/protocols/event_bus/protocol_event_message.py (2)
key(28-30)value(33-35)scripts/regenerate_fingerprints.py (1)
to_dict(132-142)
src/omnibase_core/models/common/model_operation_data.py (2)
src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(42-314)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-307)
src/omnibase_core/models/common/model_output_mapping.py (1)
src/omnibase_core/models/common/model_output_reference.py (2)
ModelOutputReference(39-110)source_node_id(103-105)
tests/unit/models/common/test_model_typed_metadata.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
error_code(253-255)message(248-250)context(275-278)
src/omnibase_core/models/common/model_typed_metadata.py (5)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(42-314)src/omnibase_core/models/common/model_graph_node_parameters.py (1)
ModelGraphNodeParameters(35-145)src/omnibase_core/models/common/model_output_mapping.py (1)
ModelOutputMapping(31-155)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-307)
tests/unit/contracts/test_runtime_contracts.py (2)
src/omnibase_core/models/infrastructure/model_compute_cache.py (1)
get(98-137)src/omnibase_core/protocols/event_bus/protocol_event_message.py (1)
topic(23-25)
🔇 Additional comments (24)
src/omnibase_core/models/core/model_service_discovery_config.py (1)
7-7: Consider addingfrom_attributes=Trueper coding guidelines.The removal of
validate_assignment=Trueis appropriate sincefrozen=Truealready prevents mutation. However, based on learnings, if this model is nested in other Pydantic models or used in parallel test execution, it should includefrom_attributes=Truein the ConfigDict.- model_config = ConfigDict(frozen=True, extra="forbid") + model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)Please verify whether
ModelConfigis nested within other Pydantic models or used in parallel tests. If so, addfrom_attributes=True.contracts/runtime/node_graph_reducer.yaml (3)
6-6: Fingerprint format normalized correctly.The "v" prefix removal aligns with the PR objective of standardizing fingerprint format to
<semver>:<hash>without the leading "v".
86-86: Appropriate addition ofis_recoverable: falsefor terminal state.Explicitly marking the terminal
stoppedstate as non-recoverable improves clarity and aligns with the FSM semantics where terminal states cannot be exited.
113-113: Critical fix: FSM expression updated fromis_not_emptytomin_length 1.This is a correctness fix. The
min_length 1expression is more explicit and semantically appropriate for validating that the node registry contains at least one entry, as noted in the PR objectives.scripts/regenerate_fingerprints.py (3)
238-288: Well-structured helper for version block detection.The logic correctly handles both inline and block-style version formats. The indentation comparison approach is sound.
Minor edge case: At line 274, when the version is inline (e.g.,
version: "1.0.0"), you returnversion_start + len(first_line). This doesn't include the newline character, which is typically fine since subsequent insertion adds its own newline. Just ensure callers handle this consistently.
329-456: Comprehensive insertion strategy with good prioritization.The cascading fallback pattern (contract_version flow → contract_version inline → contract_version block → version flow → etc.) provides robust handling for various YAML formats. The ONEX contract conventions are correctly prioritized.
514-521: Good addition of explicit OSError handling for file reads.Separating
OSErrorfromUnicodeDecodeErrorwith descriptive error messages improves debuggability.src/omnibase_core/models/common/model_output_mapping.py (2)
55-55: Correct ConfigDict configuration per coding guidelines.The inclusion of
from_attributes=Truefollows the project's coding guidelines for immutable value objects that may be nested in other Pydantic models.
132-155: Clean factory method implementation.The
from_dictmethod correctly constructsModelOutputReferenceinstances with proper field mapping. This enables seamless migration from untypeddict[str, str]patterns.contracts/runtime/event_bus_wiring_effect.yaml (2)
37-37: Fingerprint format normalized correctly.Consistent with other contract files in this PR, the "v" prefix has been removed.
242-268: Handler metadata structure looks comprehensive.The
handler_status: plannedandhandler_implementationsections provide good documentation for unimplemented handlers, enabling future code generation and validation.tests/unit/models/common/test_model_typed_metadata.py (4)
32-40: Comprehensive test class with clear PR requirement mapping.The docstring clearly maps to PR #180 requirements, making it easy to trace test coverage back to specifications.
90-105: Good coverage for Python's bool-is-subclass-of-int edge case.Testing that
boolvalues are explicitly rejected forintegertypes addresses a subtle Python quirk whereisinstance(True, int)returnsTrue.
640-680: Well-structured combined validation tests.Testing both
defaultandenumvalidation together ensures the validators don't interfere with each other and errors are reported for the correct field.
392-398: The context structure is correct and matches the implementation.The nested access pattern
context.get("additional_context", {}).get("context", {})is intentional and correct. WhenModelOnexErroris raised with keyword arguments likecontext={"declared_type": "integer", "actual_type": "float", "default_value": "3.5"}, the constructor stores these inadditional_context(model_onex_error.py lines 116-131), creating the structureadditional_context={"context": {...}}. The tests will not pass silently with empty dicts—they receive the actual values set during error creation. This pattern is documented and used consistently across the codebase.tests/unit/contracts/test_runtime_contracts.py (3)
840-869: Good validation of handler_status field requirements.The explicit allowlist of valid statuses (
planned,implemented,deprecated) and clear error messages will help catch contract inconsistencies early.
871-906: Comprehensive handler implementation validation.Checking for required fields (
class,method,input_schema) ensures contracts provide sufficient metadata for code generation and runtime validation.
907-928: Pragmatic approach to comment validation.Reading raw YAML content to verify documentation comments is necessary since
yaml.safe_loadstrips comments. The checks for "Implementation Status:" and tracking references ensure planned handlers are properly documented.src/omnibase_core/models/common/model_envelope_payload.py (4)
73-73: Correct ConfigDict configuration per coding guidelines.The inclusion of
from_attributes=Truefollows project conventions for immutable value objects.
107-114: Good DoS prevention with data size validation.The validator enforces the MAX_DATA_ENTRIES limit to prevent memory exhaustion attacks.
186-212: Consistent serialization in to_string_dict.The boolean-to-string conversion (
"true"/"false") and list joining (,) provide predictable serialization. Note that list items containing commas would be ambiguous during deserialization.
116-151: Well-designed factory method with field extraction.The
from_dictmethod cleanly separates known typed fields from extra data, enabling migration from untyped dictionaries while preserving all payload information.src/omnibase_core/models/common/model_typed_metadata.py (2)
605-654: LGTM! Strong typing with UUID for distributed identifiers.The change from
str | NonetoUUID | Noneforpartition_idandwindow_idprovides stronger type safety and is appropriate for distributed system identifiers. The updated docstring with usage example is helpful.
225-367: LGTM! Comprehensive type validation with proper error handling.The type consistency validators are well-implemented with:
- Correct handling of Python's
boolsubclass relationship withint- Explicit rejection of boolean values for integer and numeric types
- Detailed error context using
ModelOnexErrorwithPARAMETER_TYPE_MISMATCH- Clear error messages explaining the type mismatch
The validation logic properly enforces type safety for schema properties.
| **Last Updated**: 2025-12-14 | ||
| **Project**: omnibase_core v0.3.6 |
There was a problem hiding this comment.
Fix unclosed markdown code fence (docs render break).
The file starts a 4-backtick fence at Line 474 but doesn’t close it, which will likely cause the “Last Updated / Project” metadata (and potentially more) to render as code. Please close the fence (or remove it if unintended).
**Last Updated**: 2025-12-14
**Project**: omnibase_core v0.3.6
-````
+````
+Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In docs/conventions/NAMING_CONVENTIONS.md around lines 473-474 there is an
unclosed 4-backtick markdown fence that causes subsequent metadata to render as
code; fix it by either adding a matching closing fence (a line containing four
backticks) immediately after the fenced block or, if the fence was accidental,
remove the opening 4-backtick line so the following metadata renders normally.
PR Review: Typed Pydantic Models for Query Parameters and Envelope PayloadsThis is an excellent PR that successfully addresses the feedback from PR #180 by replacing ✅ Strengths1. Exceptional Type SafetyThe new typed models eliminate the
Impact: Eliminates an entire class of runtime type errors and improves IDE autocomplete/type checking. 2. Comprehensive Security HardeningAll models include excellent DoS prevention measures: # ModelQueryParameters
MAX_PARAMETERS: ClassVar[int] = 100
# ModelEnvelopePayload
MAX_FIELD_LENGTH: ClassVar[int] = 512
MAX_DATA_ENTRIES: ClassVar[int] = 100
# ModelOperationData
MAX_HEADERS: ClassVar[int] = 100
MAX_BODY_SIZE: ClassVar[int] = 1_048_576 # 1MB
MAX_CONTENT_SIZE: ClassVar[int] = 10_485_760 # 10MBPlus: URL encoding via 3. Excellent Performance Optimization
_by_local_name: dict[str, ModelOutputReference] = PrivateAttr(default_factory=dict)
@model_validator(mode="after")
def _build_lookup_cache(self) -> ModelOutputMapping:
self._by_local_name = {ref.local_name: ref for ref in self.references}
return selfImpact: Avoids O(n) list scans for repeated lookups in graph execution. 4. Outstanding DocumentationEvery new model has:
Example from Security:
- `MAX_PARAMETERS = 100` prevents DoS via parameter flooding
- URL encoding via `urllib.parse.urlencode` prevents injection attacks
- Type validation ensures only safe value types are accepted5. Proper Error HandlingConsistent use of raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"Parameter count ({len(self.items)}) exceeds maximum",
parameter_count=len(self.items),
max_parameters=self.MAX_PARAMETERS,
)Note: One minor inconsistency in 6. Robust TestingAdded 707 lines of new tests covering:
Test Quality: Tests use proper assertions with error code validation and message checking. 7. Contract Fingerprint Script ImprovementsThe
🟡 Minor Observations1. Duplicate Code in Helper MethodsSeveral models have nearly identical 2. Validation Consistency
Recommendation: Document when to use 3. Type Inference FallbackIn else: # pragma: no cover
# Fallback for unexpected types - defensive code for runtime safety
param_type = "string" # type: ignore[unreachable]
value = str(value)This defensive fallback is good, but consider adding logging to detect when it's triggered in production. 🟢 Best Practices Followed✅ ONEX Conventions:
✅ Poetry Usage: All Python commands use ✅ Type Safety:
✅ Immutability Patterns: Models use ✅ Thread Safety: Pydantic models are immutable after creation (thread-safe reads) 🎯 PR Goals Achievement
🔍 Code Quality Metrics
📝 Final Verdict✅ APPROVE with high confidence This PR demonstrates excellent software engineering:
The minor observations noted above are truly minor and don't warrant blocking this PR. This is a model example of how to refactor legacy patterns to modern typed implementations. Recommended merge path: Squash merge to preserve clean commit history, as the 16 commits represent an iterative development process addressing review feedback. 🙏 AcknowledgmentsExcellent work addressing all feedback from PR #180 systematically. The comprehensive docstrings, security hardening, and test coverage go above and beyond the original requirements. The O(1) caching optimization in |
- Fix get_bool() to return default for unrecognized strings instead of False - Fix ModelOperationData validator to use Self return type and ModelOnexError - Add duplicate local_name validation to ModelOutputMapping - Fix timezone handling in ModelEnvelopePayload.with_timestamp() to use UTC - Add from_attributes=True to ModelConfig for parallel test compatibility - Update NAMING_CONVENTIONS.md version from v0.3.6 to v0.4.0 - Fix OMN-XXX placeholder with proper Tracking comment in contract
Code Review - PR #186SummaryThis PR successfully addresses the feedback from PR #180 by replacing untyped ✅ Strengths1. Excellent Type Safety Architecture
2. Robust Validation
3. Comprehensive Testing
4. Script Improvements
🔴 Critical Issues1. Incomplete Timezone Migration (Breaking Change)Location: ts = timestamp or datetime.now(UTC) # ✅ Correctvs. older code that may still exist: datetime.now(timezone.utc) # ⚠️ Legacy patternIssue: The codebase uses both Action: Run a codebase-wide search for poetry run grep -r "timezone.utc" src/omnibase_core/2. Version Number Inconsistency in DocumentationLocations:
Issue: Version mismatch creates confusion about which version these changes target. Action: Unify all documentation to reference the SAME version number. If this PR is for v0.4.0, update PYDANTIC_BEST_PRACTICES.md accordingly. 🟡 Minor Issues & Suggestions1. Fingerprint Script Enhancement - Missing Contract TypeLocation: The script has 9 fallback strategies for fingerprint insertion, but ONEX contracts commonly have a # 7.5 Try to insert after contract_type field
contract_type_pattern = re.compile(
r"^(\s*)(contract_type:\s*[\w_]+)\s*$",
re.MULTILINE,
)
contract_type_match = contract_type_pattern.search(content)
if contract_type_match:
# ... (similar pattern to node_type insertion)2. ModelEnvelopePayload - to_string_dict() List FlatteningLocation: elif isinstance(value, list):
result[key] = ",".join(value) # Comma-separatedIssue: This assumes all list items are strings, but Suggestion: Either add a runtime check or update the docstring to clarify that only elif isinstance(value, list):
# Type hint ensures value is list[str], safe to join
result[key] = ",".join(value)3. TODO Comments - Add TrackingLocation: The TODO comments are excellent (clear, actionable, with implementation details). However, they reference creating a "Linear ticket when development begins" but don't have ticket IDs yet. Suggestion: If there's a tracking system (Linear, Jira, GitHub Issues), create placeholder tickets now and reference them in the TODOs: # TODO: Implement subscription handler registration (OMN-XXX)4. Test Coverage - Missing Success CasesLocation: The new tests comprehensively cover error cases (type mismatches), which is excellent. However, I don't see corresponding success cases testing that valid configurations work: def test_valid_integer_default_with_integer_type() -> None:
"""Test that default=42 with type='integer' succeeds."""
prop = ModelConfigSchemaProperty(
type="integer",
default=42,
description="Test parameter",
)
assert prop.default == 42
def test_valid_float_default_with_number_type() -> None:
"""Test that default=3.5 with type='number' succeeds."""
prop = ModelConfigSchemaProperty(
type="number",
default=3.5,
description="Test parameter",
)
assert prop.default == 3.5Impact: Low (validation logic is sound), but positive test cases improve coverage completeness. 5. FSM Expression Update - Good Catch!Location: # OLD: expression: node_registry is_not_empty
# NEW: expression: node_registry min_length 1This is a good fix aligning with ONEX FSM expression syntax. Verify that the FSM expression parser supports 🟢 Best Practices Followed
📊 Test Impact AnalysisTest Suite: 12,198+ tests The new tests are lightweight (model validation only, no I/O), so they should not impact CI performance. 🎯 RecommendationsBefore Merge:
After Merge:
🏆 Overall AssessmentRating: ⭐⭐⭐⭐½ (4.5/5) This is excellent work that significantly improves type safety and maintainability. The architecture follows ONEX best practices, validation is robust, and testing is comprehensive. Primary concerns:
Once the version number is fixed, this PR is ready to merge. The optional suggestions are enhancements, not blockers. Great job addressing the PR #180 feedback! 🎉 Review generated with reference to CLAUDE.md project conventions. |
There was a problem hiding this comment.
Actionable comments posted: 6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
contracts/runtime/event_bus_wiring_effect.yaml(1 hunks)docs/conventions/NAMING_CONVENTIONS.md(3 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_operation_data.py(4 hunks)src/omnibase_core/models/common/model_output_mapping.py(1 hunks)src/omnibase_core/models/common/model_query_parameters.py(1 hunks)src/omnibase_core/models/core/model_service_discovery_config.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/conventions/NAMING_CONVENTIONS.md
- src/omnibase_core/models/common/model_query_parameters.py
- src/omnibase_core/models/core/model_service_discovery_config.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_output_mapping.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_output_mapping.py
🧬 Code graph analysis (2)
src/omnibase_core/models/common/model_envelope_payload.py (3)
src/omnibase_core/models/common/model_output_mapping.py (1)
from_dict(144-166)src/omnibase_core/models/common/model_query_parameters.py (4)
from_dict(93-102)from_string_dict(105-116)to_dict(118-124)to_string_dict(126-136)src/omnibase_core/models/common/model_graph_node_parameters.py (1)
from_dict(101-145)
src/omnibase_core/models/common/model_output_mapping.py (1)
src/omnibase_core/models/common/model_output_reference.py (2)
ModelOutputReference(39-110)source_node_id(103-105)
⏰ 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 18/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: claude-review
🔇 Additional comments (2)
contracts/runtime/event_bus_wiring_effect.yaml (1)
224-237: Prior feedback (OMN-XXX placeholder) properly addressed; TODO is now generic with tracking note.The changes resolve the past review comment by converting the issue placeholder to a generic TODO and adding a note about creating a Linear ticket when development begins. This is the appropriate resolution.
src/omnibase_core/models/common/model_operation_data.py (1)
128-159: LGTM! Validator correctly implements feedback.The
_validate_sizesvalidator correctly:
- Uses
Selfreturn type for consistency- Raises
ModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERRORper coding guidelines- Includes contextual data (counts, sizes) in error messages for debugging
This implementation properly addresses the past review feedback and follows established patterns.
| topics: | ||
| - topic: onex.runtime.node_graph.ready | ||
| description: Triggered when node graph is fully instantiated | ||
| handler: wire_all_subscriptions | ||
| handler_status: planned # NOT_IMPLEMENTED - requires SubscriptionHandlerRegistry | ||
| handler_implementation: | ||
| class: NodeEventBusWiringEffect | ||
| method: _handle_node_graph_ready | ||
| input_schema: ModelNodeGraphReadyEvent | ||
| output_schema: ModelWiringResultEvent | ||
| idempotent: true | ||
| - topic: onex.runtime.node.registered | ||
| description: Handle new node registration for hot reload | ||
| handler: wire_single_subscription | ||
| handler_status: planned # NOT_IMPLEMENTED - requires SubscriptionHandlerRegistry | ||
| handler_implementation: | ||
| class: NodeEventBusWiringEffect | ||
| method: _handle_node_registered | ||
| input_schema: ModelNodeRegisteredEvent | ||
| output_schema: ModelSubscriptionCreatedEvent | ||
| idempotent: true | ||
| - topic: onex.runtime.node.unregistered | ||
| description: Handle node removal | ||
| handler: remove_subscription | ||
| handler_status: planned # NOT_IMPLEMENTED - requires SubscriptionHandlerRegistry | ||
| handler_implementation: | ||
| class: NodeEventBusWiringEffect | ||
| method: _handle_node_unregistered | ||
| input_schema: ModelNodeUnregisteredEvent | ||
| output_schema: ModelSubscriptionRemovedEvent | ||
| idempotent: true |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that all referenced event schema models are defined in the codebase.
# Search for model definitions of event-related schemas referenced in the YAML
echo "=== Searching for event model definitions ==="
rg -n 'class\s+(ModelNodeGraphReadyEvent|ModelNodeRegisteredEvent|ModelNodeUnregisteredEvent|ModelWiringResultEvent|ModelSubscriptionCreatedEvent|ModelSubscriptionRemovedEvent|ModelRuntimeReadyEvent|ModelSubscriptionFailedEvent|ModelWiringErrorEvent)' --type py
echo ""
echo "=== Checking if these models are exported from __init__.py or __all__ ==="
rg -n '__all__\s*=' src/omnibase_core/models/ -A 20 | grep -E '(ModelNodeGraphReadyEvent|ModelNodeRegisteredEvent|ModelNodeUnregisteredEvent|ModelWiringResultEvent|ModelSubscriptionCreatedEvent|ModelSubscriptionRemovedEvent|ModelRuntimeReadyEvent|ModelSubscriptionFailedEvent|ModelWiringErrorEvent)' || echo "No matches found in __all__ exports"
echo ""
echo "=== Listing all model files to understand structure ==="
find src/omnibase_core/models -name "*.py" -type f | head -20Repository: OmniNode-ai/omnibase_core
Length of output: 1361
🏁 Script executed:
#!/bin/bash
# Broader search for event-related models
echo "=== Searching for any Event model definitions ==="
rg -n 'class\s+Model.*Event' --type py src/omnibase_core/models/
echo ""
echo "=== Checking runtime-related model directories ==="
find src/omnibase_core/models -type d -name "*runtime*" -o -name "*event*" -o -name "*node*"
echo ""
echo "=== Searching for files containing 'runtime' or 'event' in models ==="
find src/omnibase_core/models -name "*.py" -type f | xargs grep -l "runtime\|event" 2>/dev/null | head -20
echo ""
echo "=== Checking if referenced models are imported from elsewhere ==="
rg -n 'ModelNodeGraphReadyEvent|ModelRuntimeReadyEvent' --type pyRepository: OmniNode-ai/omnibase_core
Length of output: 20913
🏁 Script executed:
#!/bin/bash
# Check runtime directory contents
echo "=== Contents of src/omnibase_core/models/runtime ==="
ls -la src/omnibase_core/models/runtime/
echo ""
echo "=== Files in runtime directory ==="
find src/omnibase_core/models/runtime -name "*.py" -type f
echo ""
echo "=== Final confirmation - search for the exact model names ==="
rg "ModelNodeGraphReadyEvent|ModelNodeRegisteredEvent|ModelNodeUnregisteredEvent|ModelWiringResultEvent|ModelSubscriptionCreatedEvent|ModelSubscriptionRemovedEvent|ModelRuntimeReadyEvent|ModelSubscriptionFailedEvent|ModelWiringErrorEvent" --type pyRepository: OmniNode-ai/omnibase_core
Length of output: 575
Create the missing event schema models referenced in the handler implementations.
The YAML references nine event schema models that do not exist in the codebase:
- Subscription handlers: ModelNodeGraphReadyEvent, ModelNodeRegisteredEvent, ModelNodeUnregisteredEvent, ModelWiringResultEvent, ModelSubscriptionCreatedEvent, ModelSubscriptionRemovedEvent
- Publication handlers: ModelRuntimeReadyEvent, ModelSubscriptionFailedEvent, ModelWiringErrorEvent
These models must be defined and exported before the contract can be validated at runtime. Define them in src/omnibase_core/models/runtime/ or another appropriate location and ensure they are properly exported from the models package.
…-trips Address CodeRabbit PR #186 review feedback: ## Self return types and ModelOnexError - model_envelope_payload.py: _validate_data_size(), set_data(), with_timestamp() - model_output_mapping.py: _build_lookup_cache(), from_dict() ## Remove unnecessary dict round-trip patterns - model_connection_info.py: Remove redundant model_dump/model_validate - model_git_hub_actions_workflow.py: Use direct model validation - model_secret_config.py: Use model_validate instead of **model_dump() - model_backend_config.py: Add from_attributes=True for direct validation
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/unit/scripts/test_regenerate_fingerprints.py (1)
455-463: Fingerprint consistency test can silently pass; assert success path.
Right now, if allcompute_fingerprint_for_contract()calls returnfingerprint=None, the test still passes. Sincecontract_datais valid, this should assert that fingerprints were produced and no errors occurred.- valid_fingerprints = [ - fp.fingerprint for fp in fingerprints if fp.fingerprint is not None - ] - if valid_fingerprints: - first = str(valid_fingerprints[0]) - assert all(str(fp) == first for fp in valid_fingerprints) + assert all(fp.error is None for fp in fingerprints) + assert all(fp.fingerprint is not None for fp in fingerprints) + first = str(fingerprints[0].fingerprint) + assert all(str(fp.fingerprint) == first for fp in fingerprints)
🧹 Nitpick comments (2)
tests/unit/scripts/test_regenerate_fingerprints.py (2)
723-727:__qualname__check is good for xdist; make it stronger + consistent.
Comparing only__qualname__could still collide;__module__ + "." + __qualname__is safer. Also, later tests in this file still do direct class equality (reducer/orchestrator/gateway), which may reintroduce the same xdist flake you’re avoiding here.- assert model.__qualname__ == "ModelContractCompute" + assert f"{model.__module__}.{model.__qualname__}" == ( + "omnibase_core.models.contracts.model_contract_compute.ModelContractCompute" + ) ... - assert model.__qualname__ == "ModelContractEffect" + assert f"{model.__module__}.{model.__qualname__}" == ( + "omnibase_core.models.contracts.model_contract_effect.ModelContractEffect" + )(Recommended follow-up: apply the same string-identity assertion pattern in
TestAdditionalModelDetectionto avoid inconsistent behavior under pytest-xdist.)Also applies to: 741-745
1215-1221: Edge-case tests align withFingerprintResult; consider tightening error expectations.
These changes look aligned with the new API surface. One minor improvement: rather than loosely checking"validation" in error, consider asserting on an error code/structured prefix ifcompute_fingerprint_for_contract()standardizes those (to reduce message-coupling).Also applies to: 1230-1239
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
docs/ci/DEPRECATION_WARNINGS.md(1 hunks)docs/guides/DECLARATIVE_NODE_IMPORT_RULES.md(3 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_output_mapping.py(1 hunks)src/omnibase_core/models/configuration/model_git_hub_actions_workflow.py(1 hunks)src/omnibase_core/models/connections/model_connection_info.py(2 hunks)src/omnibase_core/models/security/model_backend_config.py(1 hunks)src/omnibase_core/models/security/model_secret_config.py(1 hunks)tests/unit/scripts/test_regenerate_fingerprints.py(6 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/guides/DECLARATIVE_NODE_IMPORT_RULES.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/omnibase_core/models/common/model_output_mapping.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/security/model_backend_config.pysrc/omnibase_core/models/connections/model_connection_info.pysrc/omnibase_core/models/security/model_secret_config.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/common/model_envelope_payload.pytests/unit/scripts/test_regenerate_fingerprints.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/security/model_backend_config.pysrc/omnibase_core/models/connections/model_connection_info.pysrc/omnibase_core/models/security/model_secret_config.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/common/model_envelope_payload.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/scripts/test_regenerate_fingerprints.py
🧠 Learnings (1)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/security/model_backend_config.pysrc/omnibase_core/models/security/model_secret_config.py
🧬 Code graph analysis (3)
src/omnibase_core/models/security/model_secret_config.py (1)
src/omnibase_core/models/security/model_backend_config.py (1)
ModelBackendConfig(12-67)
src/omnibase_core/models/configuration/model_git_hub_actions_workflow.py (1)
src/omnibase_core/models/configuration/model_git_hub_workflow_data.py (1)
ModelGitHubWorkflowData(8-46)
tests/unit/scripts/test_regenerate_fingerprints.py (2)
scripts/regenerate_fingerprints.py (2)
update_fingerprint_in_yaml(290-456)compute_fingerprint_for_contract(194-235)tests/unit/contracts/test_contract_v110_fields.py (7)
contract_data(91-93)contract_data(503-505)contract_data(639-641)contract_data(737-739)contract_data(898-900)contract_data(1086-1088)contract_data(1148-1150)
⏰ 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 16/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: claude-review
🔇 Additional comments (18)
docs/ci/DEPRECATION_WARNINGS.md (1)
139-139: Good template improvement.Updating the placeholder from
OMN-XXXtoYOUR-TICKETmakes the example more universally applicable and allows teams to adapt it to their own ticket system conventions. The documentation is well-structured with clear examples, step-by-step guidance, and a helpful pre-release checklist.src/omnibase_core/models/connections/model_connection_info.py (1)
139-155: Review comment is incorrect - the sub-model validators support the refactoring.Both
ModelConnectionPoolandModelConnectionSecurityhave@model_validator(mode="after")decorators:
ModelConnectionPool.validate_pool_configuration()at line 76ModelConnectionSecurity.validate_ssl_configuration()at line 45The refactoring is valid. Construction-time validators in both sub-models ensure configuration consistency, and
validate_assignment=Truein the parent'smodel_configenforces re-validation on field updates. The removal of explicit validation calls is justified and does not silently skip validation logic.src/omnibase_core/models/security/model_backend_config.py (1)
9-9: LGTM! Enables attribute-based initialization.The addition of
from_attributes=TruetoConfigDictis correct and enablesModelBackendConfigto be constructed from object attributes. This aligns with the pattern being applied across the codebase and works together with the change inmodel_secret_config.py(line 230) that usesmodel_validate().Based on learnings, this configuration is appropriate for immutable value objects that are nested in other Pydantic models.
Also applies to: 15-16
src/omnibase_core/models/security/model_secret_config.py (1)
230-230: LGTM! Leverages Pydantic v2 validation pattern.The change from
ModelBackendConfig(**backend_config_data.model_dump())toModelBackendConfig.model_validate(backend_config_data)is an improvement that:
- Uses Pydantic v2's recommended
model_validate()API- Leverages the
from_attributes=Trueconfiguration added toModelBackendConfig(file 1, line 15)- Provides more robust validation compared to the dict-unpacking approach
This works correctly because
model_validate()withfrom_attributes=Truereads attributes directly from theModelBackendConfigDatainstance.src/omnibase_core/models/configuration/model_git_hub_actions_workflow.py (1)
64-74: Optimization verified — direct validation with from_attributes=True eliminates serialization round-trip.The change from
cls.model_validate(data.model_dump())tocls.model_validate(data)improves performance by avoiding unnecessary dict serialization. Thefrom_attributes=Trueconfiguration onModelGitHubActionsWorkflowenables Pydantic to read attributes directly from theModelGitHubWorkflowDataobject.The type conversion from
ModelGitHubWorkflowData's broader types (e.g.,on: str | list[str] | dict[str, object]) toModelGitHubActionsWorkflow's typed nested models (e.g.,on: ModelWorkflowTriggers) is handled by Pydantic's coercion withstrict=False. Since tests pass, mypy strict mode compliance is maintained, and pyright basic mode validation succeeds, the type conversion works correctly in practice.src/omnibase_core/models/common/model_envelope_payload.py (13)
42-42: LGTM!The
PayloadDataValueunion type provides good flexibility for event payload data while maintaining type safety.
76-80: LGTM!Configuration follows coding guidelines with
from_attributes=True, and security constants provide DoS protection.
83-108: LGTM!Field definitions are well-structured with appropriate max_length constraints for security and clear descriptions.
110-120: LGTM!The validator correctly uses
Selfreturn type andModelOnexErrorwith appropriate error context, addressing previous review feedback.
159-171: LGTM!The
from_string_dictmethod provides a clean conversion path from string dictionaries.
173-190: LGTM!The
to_dictmethod correctly handles optional fields and performs defensive copying of the data dictionary.
192-218: LGTM!The
to_string_dictmethod correctly flattens nested PayloadDataValue types to strings with appropriate handling for booleans and lists.
220-254: LGTM!The
getandget_datamethods provide clear separation between unified access and data-dict-specific access.
256-268: LGTM!The
set_datamethod correctly usesSelfreturn type and follows the immutable update pattern, addressing previous review feedback.
270-280: LGTM!The
with_timestampmethod correctly usesSelfreturn type and provides timezone-aware timestamps in ISO 8601 format, addressing previous review feedback.
282-320: LGTM!The utility methods (
has,__len__,__bool__,__contains__) provide intuitive dictionary-like behavior while respecting the typed field structure.
323-323: LGTM!The exports correctly expose both the model and the type alias as public API.
122-157: The implementation is correct and intentional. Thefrom_dictmethod is designed to acceptdict[str, PayloadDataValue](which includes int, float, bool, list[str]) and normalize the typed fields to strings. This is the documented contract of the method:
- All typed fields (
event_type,source,timestamp,correlation_id) are declared asstr | None- The
from_dictmethod accepts flexible input types but normalizes them to strings for consistent storage- The
datadict preserves original types as designed (line 147)- A separate
from_string_dictmethod exists for cases requiring string-only input, which internally callsfrom_dict- No production code directly calls
from_dict; all usage goes throughfrom_string_dictThis pattern—accepting multiple input types but normalizing typed fields to strings—is intentional and not a bug.
Create typed event models referenced in event_bus_wiring_effect.yaml, following ONEX single-class-per-file convention. Base: - ModelRuntimeEventBase: Common fields for all runtime events Node lifecycle: - ModelNodeRegisteredEvent: Node registration for hot-reload - ModelNodeUnregisteredEvent: Node removal and cleanup Subscription lifecycle: - ModelSubscriptionCreatedEvent: Successful subscription confirmation - ModelSubscriptionRemovedEvent: Subscription cleanup - ModelSubscriptionFailedEvent: Subscription failure tracking Runtime state: - ModelRuntimeReadyEvent: Runtime fully initialized - ModelWiringErrorEvent: Critical wiring errors Graph events: - ModelNodeGraphReadyEvent: Node graph ready for wiring - ModelWiringResultEvent: Wiring operation outcome Supporting typed models (replacing dict[str, Any]): - ModelNodeGraphInfo: Typed node information for graphs - ModelWiringErrorInfo: Typed error details for wiring Addresses PR #186 review feedback for missing event schema models.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (16)
src/omnibase_core/models/events/model_wiring_error_info.py (1)
36-41: Consider constrainingerror_codebeyond plainstr(if internal-only).
If these events are primarily internal, usingEnumOnexErrorCode(orEnumOnexErrorCode | str) or adding a validator can prevent “unknown code” drift.src/omnibase_core/models/events/model_node_graph_info.py (1)
34-36:node_typeshould likely beEnumNodeKind(or validate allowed strings).
Right now the docs imply a closed set; typing it asstrinvites drift/typos. As per coding guidelines, preferEnumNodeKindfor architectural classification.-from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field + +from omnibase_core.nodes.enums.enum_node_kind import EnumNodeKind @@ - node_type: str = Field( + node_type: EnumNodeKind = Field( description="Type of node (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR)", )src/omnibase_core/models/events/model_wiring_result_event.py (2)
75-78: Avoiderrors or []; useerrors if errors is not None else [].
This prevents treating an explicitly-provided empty list as “unset”.- errors=errors or [], + errors=errors if errors is not None else [],Also applies to: 80-107
40-69: Consider validating counter consistency (totals vs successful+failed).
A small@model_validator(mode="after")can catch mismatched metrics early (especially if these events drive monitoring/alerts).src/omnibase_core/models/events/model_runtime_ready_event.py (2)
48-56: Double-check whether datetimes should be timezone-aware/UTC.
If the rest of the event surface is intended to be UTC, consider switching to timezone-aware defaults consistently (here and inModelRuntimeEventBase).
66-87: Prefernodes_wired if nodes_wired is not None else [].
Same rationale as othercreate()helpers—avoid conflating “provided empty” with “unset”.- nodes_wired=nodes_wired or [], + nodes_wired=nodes_wired if nodes_wired is not None else [],src/omnibase_core/models/events/model_node_graph_ready_event.py (2)
55-72: Prefernodes if nodes is not None else []increate().
Avoids treating an explicitly-provided empty list as “unset”.- nodes=nodes or [], + nodes=nodes if nodes is not None else [],
40-48: Optional: validate/derivenode_countfromnodeswhen provided.
Helps prevent publishing self-inconsistent events.Also applies to: 55-72
src/omnibase_core/models/events/model_runtime_event_base.py (2)
24-27: Consider addingfrom_attributes=Trueto ConfigDict.Per the coding guidelines, immutable value objects nested in other Pydantic models or used in parallel test execution should include
from_attributes=Truein ConfigDict. Since this base class is inherited by many event models, adding this setting would ensure consistent ORM-style attribute access.model_config = ConfigDict( extra="forbid", validate_assignment=True, + from_attributes=True, )
37-40: Use timezone-aware datetime for distributed event tracing.
datetime.nowwithout a timezone argument creates a naive datetime, which can cause ambiguity in distributed systems or when events cross timezone boundaries. Consider usingdatetime.now(timezone.utc)ordatetime.utcnow()for consistency.+from datetime import datetime, timezone -from datetime import datetime ... timestamp: datetime = Field( - default_factory=datetime.now, + default_factory=lambda: datetime.now(timezone.utc), description="When this event was created", )src/omnibase_core/models/events/model_subscription_failed_event.py (1)
58-61: Use timezone-aware datetime forfailed_atfield.Same issue as the base class -
datetime.nowcreates naive datetime. For consistency in error tracking and debugging across services, consider usingdatetime.now(timezone.utc).+from datetime import datetime, timezone -from datetime import datetime ... failed_at: datetime = Field( - default_factory=datetime.now, + default_factory=lambda: datetime.now(timezone.utc), description="When the failure occurred", )src/omnibase_core/models/events/model_subscription_removed_event.py (1)
45-48: Use timezone-aware datetime forremoved_atfield.Consistent with feedback on other files -
datetime.nowcreates naive datetime. For audit trail consistency, consider usingdatetime.now(timezone.utc).+from datetime import datetime, timezone -from datetime import datetime ... removed_at: datetime = Field( - default_factory=datetime.now, + default_factory=lambda: datetime.now(timezone.utc), description="When the subscription was removed", )src/omnibase_core/models/events/model_node_registered_event.py (1)
16-73: Model + factory follow established runtime-event pattern; consider simplifying “required field” declarations.
Optionally replaceField(default=...)with no default for required fields (node_id,node_name,node_type) for readability/consistency (if that matches repo conventions).src/omnibase_core/models/events/model_subscription_created_event.py (2)
49-56: Consider standardizing timestamps to UTC-aware datetimes across runtime events.
Right nowsubscribed_atusesdatetime.now(naive/local). If these events cross process boundaries, a UTC-aware convention may prevent subtle bugs. (Likely a separate “standardize timestamps” PR sinceModelRuntimeEventBase.timestampappears similar.)
58-77: Factory is consistent; minor redundancy aroundsubscription_id.
Optional: ifsubscription_id is None, rely on the model field’sdefault_factoryinstead of generating one increate()(keeps single source of truth for defaults).src/omnibase_core/models/common/model_envelope_payload.py (1)
78-120: Consider enforcing key/value length limits (DoS hardening) and reusingMAX_FIELD_LENGTH.You define
MAX_FIELD_LENGTHbut don’t use it, and currently there’s no cap ondatakey length (or string values indata). If this is part of an external event-bus contract, it’s worth validating keys/strings similarly to the top-level fields.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/events/__init__.py(1 hunks)src/omnibase_core/models/events/model_node_graph_info.py(1 hunks)src/omnibase_core/models/events/model_node_graph_ready_event.py(1 hunks)src/omnibase_core/models/events/model_node_registered_event.py(1 hunks)src/omnibase_core/models/events/model_node_unregistered_event.py(1 hunks)src/omnibase_core/models/events/model_runtime_event_base.py(1 hunks)src/omnibase_core/models/events/model_runtime_events.py(1 hunks)src/omnibase_core/models/events/model_runtime_ready_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_created_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_failed_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_removed_event.py(1 hunks)src/omnibase_core/models/events/model_wiring_error_event.py(1 hunks)src/omnibase_core/models/events/model_wiring_error_info.py(1 hunks)src/omnibase_core/models/events/model_wiring_result_event.py(1 hunks)tests/unit/scripts/test_regenerate_fingerprints.py(7 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/events/model_node_graph_info.pysrc/omnibase_core/models/events/model_subscription_created_event.pysrc/omnibase_core/models/events/model_runtime_event_base.pysrc/omnibase_core/models/events/model_subscription_failed_event.pysrc/omnibase_core/models/events/model_subscription_removed_event.pytests/unit/scripts/test_regenerate_fingerprints.pysrc/omnibase_core/models/events/model_wiring_error_info.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/events/__init__.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/events/model_wiring_result_event.pysrc/omnibase_core/models/events/model_runtime_events.pysrc/omnibase_core/models/events/model_wiring_error_event.pysrc/omnibase_core/models/events/model_node_unregistered_event.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/events/model_node_graph_info.pysrc/omnibase_core/models/events/model_subscription_created_event.pysrc/omnibase_core/models/events/model_runtime_event_base.pysrc/omnibase_core/models/events/model_subscription_failed_event.pysrc/omnibase_core/models/events/model_subscription_removed_event.pysrc/omnibase_core/models/events/model_wiring_error_info.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/events/__init__.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/events/model_wiring_result_event.pysrc/omnibase_core/models/events/model_runtime_events.pysrc/omnibase_core/models/events/model_wiring_error_event.pysrc/omnibase_core/models/events/model_node_unregistered_event.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/scripts/test_regenerate_fingerprints.py
🧠 Learnings (1)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Import nodes from `omnibase_core.nodes` using the unified import path - use `NodeCompute`, `NodeReducer`, `NodeOrchestrator`, `NodeEffect` (NOT with 'Declarative' suffix)
Applied to files:
src/omnibase_core/models/events/__init__.pysrc/omnibase_core/models/events/model_runtime_events.py
🧬 Code graph analysis (14)
src/omnibase_core/models/events/model_node_graph_info.py (1)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)
src/omnibase_core/models/events/model_subscription_created_event.py (9)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)src/omnibase_core/protocols/event_bus/protocol_event_message.py (1)
topic(23-25)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-66)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)
src/omnibase_core/models/events/model_runtime_event_base.py (2)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/common/model_output_reference.py (1)
source_node_id(103-105)
src/omnibase_core/models/events/model_subscription_failed_event.py (9)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)src/omnibase_core/protocols/event_bus/protocol_event_message.py (1)
topic(23-25)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-66)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
create(67-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)
src/omnibase_core/models/events/model_subscription_removed_event.py (5)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-66)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)
src/omnibase_core/models/events/model_wiring_error_info.py (3)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/protocols/event_bus/protocol_event_message.py (1)
topic(23-25)src/omnibase_core/models/errors/model_onex_error.py (1)
error_code(253-255)
src/omnibase_core/models/common/model_envelope_payload.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)error_code(253-255)message(248-250)
src/omnibase_core/models/events/model_runtime_ready_event.py (4)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/events/__init__.py (12)
src/omnibase_core/models/events/model_node_graph_info.py (1)
ModelNodeGraphInfo(15-44)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
ModelNodeGraphReadyEvent(24-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
ModelNodeRegisteredEvent(21-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
ModelNodeUnregisteredEvent(21-66)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
ModelRuntimeReadyEvent(22-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
ModelSubscriptionCreatedEvent(22-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
ModelSubscriptionFailedEvent(22-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
ModelSubscriptionRemovedEvent(22-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
ModelWiringErrorEvent(21-87)src/omnibase_core/models/events/model_wiring_error_info.py (1)
ModelWiringErrorInfo(15-45)src/omnibase_core/models/events/model_wiring_result_event.py (1)
ModelWiringResultEvent(24-107)
src/omnibase_core/models/events/model_node_registered_event.py (8)
src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-66)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/events/model_wiring_result_event.py (12)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_wiring_error_info.py (1)
ModelWiringErrorInfo(15-45)src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-66)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
create(67-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)
src/omnibase_core/models/events/model_runtime_events.py (12)
src/omnibase_core/models/events/model_node_graph_info.py (1)
ModelNodeGraphInfo(15-44)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
ModelNodeGraphReadyEvent(24-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
ModelNodeRegisteredEvent(21-73)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
ModelNodeUnregisteredEvent(21-66)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
ModelRuntimeReadyEvent(22-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
ModelSubscriptionCreatedEvent(22-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
ModelSubscriptionFailedEvent(22-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
ModelSubscriptionRemovedEvent(22-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
ModelWiringErrorEvent(21-87)src/omnibase_core/models/events/model_wiring_error_info.py (1)
ModelWiringErrorInfo(15-45)src/omnibase_core/models/events/model_wiring_result_event.py (1)
ModelWiringResultEvent(24-107)
src/omnibase_core/models/events/model_wiring_error_event.py (2)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/events/model_node_unregistered_event.py (8)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-44)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-73)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
⏰ 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 9/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: claude-review
🔇 Additional comments (13)
tests/unit/scripts/test_regenerate_fingerprints.py (5)
455-462: LGTM! Proper handling of FingerprintResult structure.The update correctly accesses the
.fingerprintattribute from the FingerprintResult object and filters out None values before comparison. This aligns with the new typed model structure introduced in this PR.
698-700: Excellent fix! Addresses the past review comment.The guard checks for
-1before position comparisons prevent vacuously true assertions. The descriptive assertion messages will help diagnose failures if the expected strings aren't found.✅ This directly addresses the concern raised in the past review comment.
725-728: Good workaround for pytest-xdist class identity issues.Using
__qualname__comparison is a pragmatic solution for multi-process test execution where each worker imports classes independently. The inline comments clearly explain why direct class identity checks (isor==) would fail.Also applies to: 743-746
1109-1200: Excellent comprehensive test coverage for YAML fingerprint insertion!These four new test methods thoroughly cover different insertion scenarios:
- Dict-style version blocks (major/minor/patch)
- contract_version fields
- Fallback to name field when no version exists
- Field preservation during insertion
All tests properly implement
-1guard checks before position assertions, addressing the past review comment. The clear docstrings and PR requirement references make the test intent explicit.
1223-1228: LGTM! Error handling updated for FingerprintResult model.The tests properly validate the new FingerprintResult structure by checking both
.fingerprintand.errorattributes. The conditional logic at lines 1241-1246 appropriately handles the case where ModelYamlContract may be more permissive than strict models.Also applies to: 1238-1246
src/omnibase_core/models/events/model_wiring_error_info.py (1)
15-45: Solid strict event payload model (forbid extra + validate assignment).
Looks consistent with the other runtime event models’ “strict” posture.src/omnibase_core/models/events/model_node_graph_info.py (1)
37-44: Nice defaults (default_factory=list) and optionalcontract_path.
No issues with the container fields.src/omnibase_core/models/events/model_subscription_failed_event.py (1)
63-84: Factory method structure looks good.The
create()factory method follows the established pattern from other event models, properly forwarding required fields and optional parameters includingcorrelation_idfor tracing.src/omnibase_core/models/events/model_node_unregistered_event.py (1)
21-66: LGTM!The
ModelNodeUnregisteredEventimplementation is well-structured. The factory method correctly handles the optionalactive_subscriptionsparameter with a default empty list, and thereasonfield has a sensible default value. The model follows the established pattern from other event models in this PR.src/omnibase_core/models/events/model_subscription_removed_event.py (1)
54-71: Factory method is well-designed.The
create()factory method correctly requiressubscription_id,node_id, andtopicas positional arguments while keepingreasonandcorrelation_idas keyword-only optionals. This aligns with the patterns inModelSubscriptionCreatedEventandModelSubscriptionFailedEvent.src/omnibase_core/models/events/model_wiring_error_event.py (1)
21-87: LGTM!The
ModelWiringErrorEventimplementation is comprehensive and well-designed:
- Proper
ge=0constraints onsuccessful_subscriptionsandfailed_subscriptions- Optional
stack_tracefield for debuggingpartial_successflag to indicate recoverable vs. complete failures- Factory method correctly handles the optional
affected_nodeslistThe model provides good observability for wiring failures.
src/omnibase_core/models/events/__init__.py (1)
36-64: Public re-export surface looks consistent and well-scoped.src/omnibase_core/models/events/model_runtime_events.py (1)
1-96: Clean centralized runtime-event surface; dependency direction is correct (leaf → aggregator only). No circular imports detected—all leaf modules remain independent of the aggregator.
| # Flexible data container for event-specific attributes | ||
| data: dict[str, PayloadDataValue] = Field( | ||
| default_factory=dict, | ||
| description="Additional event-specific payload data", | ||
| ) | ||
|
|
||
| @model_validator(mode="after") | ||
| def _validate_data_size(self) -> Self: | ||
| """Validate data dict size to prevent DoS attacks.""" | ||
| if len(self.data) > self.MAX_DATA_ENTRIES: | ||
| raise ModelOnexError( | ||
| error_code=EnumCoreErrorCode.VALIDATION_ERROR, | ||
| message=f"Data dict exceeds maximum size of {self.MAX_DATA_ENTRIES} entries", | ||
| data_entries=len(self.data), | ||
| max_entries=self.MAX_DATA_ENTRIES, | ||
| ) | ||
| return self | ||
|
|
||
| @classmethod | ||
| def from_dict(cls, data: dict[str, PayloadDataValue]) -> Self: | ||
| """Create from a dictionary of payload data. | ||
|
|
||
| Extracts known fields (event_type, source, timestamp, correlation_id) | ||
| and places remaining fields in the data dictionary. | ||
|
|
||
| Args: | ||
| data: Dictionary of payload key-value pairs. | ||
|
|
||
| Returns: | ||
| New ModelEnvelopePayload instance. | ||
| """ | ||
| known_fields = {"event_type", "source", "timestamp", "correlation_id"} | ||
|
|
||
| # Extract typed fields | ||
| event_type_val = data.get("event_type") | ||
| source_val = data.get("source") | ||
| timestamp_val = data.get("timestamp") | ||
| correlation_id_val = data.get("correlation_id") | ||
|
|
||
| # Collect unknown fields into data dict | ||
| extra_data: dict[str, PayloadDataValue] = {} | ||
| for key, value in data.items(): | ||
| if key not in known_fields: | ||
| extra_data[key] = value | ||
|
|
||
| return cls( | ||
| event_type=str(event_type_val) if event_type_val is not None else None, | ||
| source=str(source_val) if source_val is not None else None, | ||
| timestamp=str(timestamp_val) if timestamp_val is not None else None, | ||
| correlation_id=str(correlation_id_val) | ||
| if correlation_id_val is not None | ||
| else None, | ||
| data=extra_data, | ||
| ) | ||
|
|
||
| @classmethod | ||
| def from_string_dict(cls, data: dict[str, str]) -> Self: | ||
| """Create from a string dictionary. | ||
|
|
||
| Args: | ||
| data: Dictionary of string key-value pairs. | ||
|
|
||
| Returns: | ||
| New ModelEnvelopePayload instance. | ||
| """ | ||
| # Convert to PayloadDataValue dict to satisfy type checker | ||
| converted: dict[str, PayloadDataValue] = dict(data) | ||
| return cls.from_dict(converted) | ||
|
|
||
| def to_dict(self) -> dict[str, PayloadDataValue | dict[str, PayloadDataValue]]: | ||
| """Convert to dictionary format. | ||
|
|
||
| Returns: | ||
| Dictionary representation with all fields. | ||
| """ | ||
| result: dict[str, PayloadDataValue | dict[str, PayloadDataValue]] = {} | ||
| if self.event_type is not None: | ||
| result["event_type"] = self.event_type | ||
| if self.source is not None: | ||
| result["source"] = self.source | ||
| if self.timestamp is not None: | ||
| result["timestamp"] = self.timestamp | ||
| if self.correlation_id is not None: | ||
| result["correlation_id"] = self.correlation_id | ||
| if self.data: | ||
| result["data"] = self.data.copy() | ||
| return result | ||
|
|
||
| def to_string_dict(self) -> dict[str, str]: | ||
| """Convert to dict[str, str] format. | ||
|
|
||
| Flattens the structure to a simple string dictionary. | ||
|
|
||
| Returns: | ||
| Dictionary with string keys and values. | ||
| """ | ||
| result: dict[str, str] = {} | ||
| if self.event_type is not None: | ||
| result["event_type"] = self.event_type | ||
| if self.source is not None: | ||
| result["source"] = self.source | ||
| if self.timestamp is not None: | ||
| result["timestamp"] = self.timestamp | ||
| if self.correlation_id is not None: | ||
| result["correlation_id"] = self.correlation_id | ||
| # Flatten data items as string values | ||
| for key, value in self.data.items(): | ||
| if value is not None: | ||
| if isinstance(value, bool): | ||
| result[key] = "true" if value else "false" | ||
| elif isinstance(value, list): | ||
| result[key] = ",".join(value) | ||
| else: | ||
| result[key] = str(value) | ||
| return result | ||
|
|
||
| def get(self, key: str, default: PayloadDataValue = None) -> PayloadDataValue: | ||
| """Get a payload value by key. | ||
|
|
||
| Checks both typed fields and data dictionary. | ||
|
|
||
| Args: | ||
| key: Payload key to look up. | ||
| default: Default value if key not found. | ||
|
|
||
| Returns: | ||
| Payload value or default. | ||
| """ | ||
| # Check typed fields first | ||
| if key == "event_type": | ||
| return self.event_type if self.event_type is not None else default | ||
| if key == "source": | ||
| return self.source if self.source is not None else default | ||
| if key == "timestamp": | ||
| return self.timestamp if self.timestamp is not None else default | ||
| if key == "correlation_id": | ||
| return self.correlation_id if self.correlation_id is not None else default | ||
| # Then check data dictionary | ||
| return self.data.get(key, default) | ||
|
|
||
| def get_data(self, key: str, default: PayloadDataValue = None) -> PayloadDataValue: | ||
| """Get a value from the data dictionary. | ||
|
|
||
| Args: | ||
| key: Data key to look up. | ||
| default: Default value if key not found. | ||
|
|
||
| Returns: | ||
| Data value or default. | ||
| """ | ||
| return self.data.get(key, default) | ||
|
|
||
| def set_data(self, key: str, value: PayloadDataValue) -> Self: | ||
| """Set a value in the data dictionary, returning a new instance. | ||
|
|
||
| Args: | ||
| key: Data key to set. | ||
| value: Value to set. | ||
|
|
||
| Returns: | ||
| New ModelEnvelopePayload instance with updated data. | ||
| """ | ||
| new_data = self.data.copy() | ||
| new_data[key] = value | ||
| return self.model_copy(update={"data": new_data}) | ||
|
|
||
| def with_timestamp(self, timestamp: datetime | None = None) -> Self: | ||
| """Create a new instance with updated timestamp. | ||
|
|
||
| Args: | ||
| timestamp: Timestamp to set (defaults to UTC now). | ||
|
|
||
| Returns: | ||
| New ModelEnvelopePayload instance with timestamp. | ||
| """ | ||
| ts = timestamp or datetime.now(UTC) | ||
| return self.model_copy(update={"timestamp": ts.isoformat()}) | ||
|
|
||
| def has(self, key: str) -> bool: | ||
| """Check if a key exists in typed fields or data. | ||
|
|
||
| Args: | ||
| key: Key to check. | ||
|
|
||
| Returns: | ||
| True if key exists, False otherwise. | ||
| """ | ||
| if key in ("event_type", "source", "timestamp", "correlation_id"): | ||
| return getattr(self, key) is not None | ||
| return key in self.data | ||
|
|
||
| def __len__(self) -> int: | ||
| """Return the number of non-None fields plus data items.""" | ||
| count = len(self.data) | ||
| if self.event_type is not None: | ||
| count += 1 | ||
| if self.source is not None: | ||
| count += 1 | ||
| if self.timestamp is not None: | ||
| count += 1 | ||
| if self.correlation_id is not None: | ||
| count += 1 | ||
| return count | ||
|
|
||
| def __bool__(self) -> bool: | ||
| """Return True if there are any payload items.""" | ||
| return ( | ||
| self.event_type is not None | ||
| or self.source is not None | ||
| or self.timestamp is not None | ||
| or self.correlation_id is not None | ||
| or bool(self.data) | ||
| ) | ||
|
|
||
| def __contains__(self, key: str) -> bool: | ||
| """Check if key exists.""" | ||
| return self.has(key) | ||
|
|
There was a problem hiding this comment.
Prevent reserved-key collisions inside data (can silently override typed fields in to_string_dict()).
Today set_data("event_type", "...") is allowed, and to_string_dict() will overwrite the typed "event_type" entry when it flattens self.data. This is a correctness footgun (and can also confuse downstream consumers).
Suggested: reject reserved keys in data (validator) and in set_data().
@@
class ModelEnvelopePayload(BaseModel):
@@
# Flexible data container for event-specific attributes
data: dict[str, PayloadDataValue] = Field(
@@
@model_validator(mode="after")
def _validate_data_size(self) -> Self:
"""Validate data dict size to prevent DoS attacks."""
+ reserved = {"event_type", "source", "timestamp", "correlation_id", "data"}
+ overlap = reserved.intersection(self.data.keys())
+ if overlap:
+ raise ModelOnexError(
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
+ message="Reserved keys are not allowed in envelope payload data",
+ reserved_keys=sorted(overlap),
+ )
if len(self.data) > self.MAX_DATA_ENTRIES:
@@
def set_data(self, key: str, value: PayloadDataValue) -> Self:
@@
+ if key in {"event_type", "source", "timestamp", "correlation_id", "data"}:
+ raise ModelOnexError(
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
+ message="Cannot set reserved key via set_data(); use typed fields instead",
+ key=key,
+ )
new_data = self.data.copy()
new_data[key] = value
return self.model_copy(update={"data": new_data})🤖 Prompt for AI Agents
In src/omnibase_core/models/common/model_envelope_payload.py around lines
104-321, the data dict currently allows reserved keys (event_type, source,
timestamp, correlation_id) which can collide with typed fields when flattened;
add protection by (1) declaring a reserved_keys set
({"event_type","source","timestamp","correlation_id"}) and updating the
post-validator to raise ModelOnexError if any reserved key appears in self.data
(in addition to existing size check), (2) changing set_data to check the key
against reserved_keys and raise ModelOnexError instead of inserting, and (3)
ensuring from_dict/from_string_dict do not place reserved keys into extra_data
(either by filtering them out or raising) so the class never accepts reserved
keys into the data dict.
Code Review - PR #186: Address PR #180 Review with Typed Pydantic ModelsOverall Assessment: ✅ Strong PR with Minor Security Improvements Needed This PR successfully addresses the feedback from PR #180 by replacing 🎯 Strengths1. Excellent Type Safety
2. Strong Pydantic Patterns
3. Security-Conscious Design
4. Critical Fixes Implemented
🔴 Critical Security Issues (Must Fix Before Merge)1. ModelGraphNodeParameters - No Maximum Parameter CountFile: Issue: No validation on Fix: from typing import ClassVar, Self
from pydantic import model_validator
from omnibase_core.errors import ModelOnexError
from omnibase_core.enums.enum_core_error_code import EnumCoreErrorCode
class ModelGraphNodeParameters(BaseModel):
MAX_PARAMETERS: ClassVar[int] = 100 # Add this constant
parameters: list[ModelGraphNodeParameter] = Field(default_factory=list)
@model_validator(mode="after")
def _validate_parameter_count(self) -> Self: # Add this validator
if len(self.parameters) > self.MAX_PARAMETERS:
raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"Parameters list exceeds maximum size of {self.MAX_PARAMETERS} entries",
parameter_count=len(self.parameters),
max_parameters=self.MAX_PARAMETERS,
)
return selfSeverity: 🚨 CRITICAL - Memory exhaustion risk 2. ModelOutputMapping - No Maximum Reference CountFile: Issue: No validation on Fix: class ModelOutputMapping(BaseModel):
MAX_REFERENCES: ClassVar[int] = 100 # Add this constant
@model_validator(mode="after")
def _build_lookup_cache(self) -> Self:
# Add this check FIRST before existing duplicate validation
if len(self.references) > self.MAX_REFERENCES:
raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"References list exceeds maximum size of {self.MAX_REFERENCES} entries",
reference_count=len(self.references),
max_references=self.MAX_REFERENCES,
)
# ... existing duplicate check and cache building logicSeverity: 🚨 CRITICAL - Memory exhaustion risk 3. ModelEnvelopePayload - Nested List Size LimitsFile: Issue: The Fix: class ModelEnvelopePayload(BaseModel):
MAX_DATA_ENTRIES: ClassVar[int] = 100
MAX_LIST_LENGTH: ClassVar[int] = 100 # Add this constant
@model_validator(mode="after")
def _validate_data_size(self) -> Self:
if len(self.data) > self.MAX_DATA_ENTRIES:
raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"Data dict exceeds maximum size of {self.MAX_DATA_ENTRIES} entries",
data_entries=len(self.data),
max_entries=self.MAX_DATA_ENTRIES,
)
# Add list length validation
for key, value in self.data.items():
if isinstance(value, list) and len(value) > self.MAX_LIST_LENGTH:
raise ModelOnexError(
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
message=f"List value for key '{key}' exceeds maximum length of {self.MAX_LIST_LENGTH}",
key=key,
list_length=len(value),
max_length=self.MAX_LIST_LENGTH,
)
return selfSeverity: 🔴 HIGH - Memory exhaustion risk (lower priority than above since 100 lists of 10k is needed) 🟠 High Priority Issues4. ModelGraphNodeParameter - Missing max_length on descriptionFile: Issue: Fix: description: str | None = Field(
default=None,
description="Parameter description",
max_length=1024, # Add this constraint
)Severity: 🟠 HIGH - Memory exhaustion risk 🟡 Minor Issues & Observations5. ModelOutputMapping - PrivateAttr Cache Thread SafetyFile: The Recommendation: Add a test case: def test_model_copy_preserves_cache():
"""Verify that model_copy() rebuilds the lookup cache."""
original = ModelOutputMapping(references=[...])
copied = original.model_copy()
# Verify cache exists and works
assert copied.get_by_local_name("some_name") is not None✅ Positive Findings Worth HighlightingModelOutputReference - Exemplary ImplementationThis model is a perfect example of ONEX best practices:
Recommendation: Use this as the template for future models. 📊 Test Coverage AnalysisTest Files Modified:
Missing Test Coverage:
Recommendation: Add security limit tests immediately: def test_graph_node_parameters_max_limit():
"""Test that exceeding MAX_PARAMETERS raises error."""
with pytest.raises(ModelOnexError) as exc_info:
ModelGraphNodeParameters(
parameters=[ModelGraphNodeParameter(...) for _ in range(101)]
)
assert exc_info.value.error_code == EnumCoreErrorCode.VALIDATION_ERROR🔧 Fingerprint Script ImprovementsExcellent work on
Minor Observation: The fingerprint format now correctly removes 📝 Documentation UpdatesAll documentation version updates are appropriate:
🚀 Recommendations Before MergeMust-Have (Blocking):
Should-Have (Highly Recommended):
Nice-to-Have:
🎓 Learning OpportunitiesThis PR demonstrates excellent understanding of:
Areas for continued growth:
Final VerdictStatus: This is high-quality work that addresses all PR #180 feedback comprehensively. The 3 critical security issues are straightforward to fix and don't affect the overall architecture. Once the maximum size validations are added, this PR will be ready to merge. Estimated Fix Time: 30 minutes Great job on the typed models - this is a significant improvement to the codebase! 🎉 Reviewed by: Claude Code Agent (Sonnet 4.5) |
- Replace model_config_schema_property.py with full implementation from model_typed_metadata.py including: - type: str field (supports aliases: 'bool', 'int', 'str', 'float') - Full validate_type_default_consistency validator - Full validate_type_enum_consistency validator - Full _validate_value_type helper with ModelOnexError - Fix Self return type in model_mixin_config_schema.py validator - Convert model_typed_metadata.py to unified re-export module (all 15 classes now in individual files per ONEX rules) All 43 typed_metadata tests pass including type alias tests. Addresses PR #186 review feedback.
Code Review - PR #186SummaryThis PR successfully addresses feedback from PR #180 by replacing untyped ✅ Strengths1. Excellent Type Safety ImplementationThe new typed models (
Example from # Security constants
MAX_FIELD_LENGTH: ClassVar[int] = 512
MAX_DATA_ENTRIES: ClassVar[int] = 100
@model_validator(mode="after")
def _validate_data_size(self) -> Self:
if len(self.data) > self.MAX_DATA_ENTRIES:
raise ModelOnexError(...)2. Robust Validation LogicThe
3. Comprehensive Test Coverage
4. Improved Fingerprint ScriptThe
5. Documentation Quality
🟡 Minor Issues1. Inconsistent Version Metadata (Low Priority)Some docs show # docs/conventions/PYDANTIC_BEST_PRACTICES.md
**Last Updated**: 2025-12-14
**Project**: omnibase_core v0.3.6 # ← Should be v0.4.0?Recommendation: Ensure all docs reflect the current project version consistently. 2. FSM Expression Change Needs Documentation (Low Priority)The FSM expression change in # OLD (invalid operator)
expression: node_registry is_not_empty
# NEW (proper FSM expression)
expression: node_registry min_length 1Recommendation: Consider adding a comment explaining why 3. ModelConfigSchemaProperty Validation DuplicationBoth Current approach is acceptable, but consider: Could these be combined into a single validator for slightly better performance? (Not critical since validation happens at model creation time only.) 🟢 Code Quality ObservationsError Handling - Excellent# Proper use of ModelOnexError with context
raise ModelOnexError(
message=(
f"Type mismatch: {field_name} value has type '{actual_type_name}' "
f"but declared type is '{self.type}'. "
f"Boolean values are not valid for integer type."
),
error_code=EnumCoreErrorCode.PARAMETER_TYPE_MISMATCH,
context=context,
)✅ Follows ONEX error handling best practices Immutability Pattern - Excellentdef set_data(self, key: str, value: PayloadDataValue) -> Self:
"""Set a value in the data dictionary, returning a new instance."""
new_data = self.data.copy()
new_data[key] = value
return self.model_copy(update={"data": new_data})✅ Proper immutable update pattern for event-driven workflows Security - Excellentsource_reference: str = Field(
...,
description="Reference to source output in format 'node_id.output_name'",
min_length=3, # Minimum: "a.b"
max_length=512, # Prevent memory exhaustion
)
@field_validator("source_reference")
@classmethod
def validate_source_reference_format(cls, v: str) -> str:
if "." not in v:
raise ValueError(...)
# Validates format to prevent injection✅ Defense in depth: both schema validation AND format validation 🔵 Performance Considerations
🔒 Security Assessment✅ No security concerns identified
📊 Test Coverage AnalysisBased on the PR diff:
Coverage appears comprehensive. Awaiting CI coverage report for confirmation. 🎯 RecommendationsPriority 1 - Before MergeNone. All critical items addressed. Priority 2 - Post-Merge
Priority 3 - Future Enhancement
✅ Approval StatusAPPROVED ✅ This PR:
Recommendation: Merge once CI passes (Code Quality, Pyright, and test splits are still in progress). 📝 Checklist
Great work on this PR! The transition from File Reference: See typed models in:
|
Changes: - model_envelope_payload.py: Fix from_dict/to_dict round-trip mismatch, add RESERVED_KEYS constant, prefix reserved keys in to_string_dict() to prevent collision with typed fields - Runtime event models: Use timezone-aware UTC datetimes for timestamp, subscribed_at, removed_at, failed_at, ready_at fields - Event factory methods: Replace `x or []` patterns with explicit `x if x is not None else []` to preserve empty lists - Add from_attributes=True to ConfigDict for pytest-xdist compatibility in model_runtime_event_base, model_node_graph_info, model_wiring_error_info Files modified (13): - src/omnibase_core/models/common/model_envelope_payload.py - src/omnibase_core/models/events/model_runtime_event_base.py - src/omnibase_core/models/events/model_subscription_*.py (3 files) - src/omnibase_core/models/events/model_runtime_ready_event.py - src/omnibase_core/models/events/model_wiring_*.py (2 files) - src/omnibase_core/models/events/model_node_*.py (3 files) - src/omnibase_core/models/events/model_node_graph_info.py - src/omnibase_core/models/events/model_wiring_error_info.py Verified: mypy strict (0 errors), 998 unit tests pass
Code Review - PR #186: Typed Pydantic Models MigrationOverall Assessment: ✅ APPROVED with minor suggestions This is an excellent PR that successfully addresses all feedback from PR #180. The migration from 🟢 Strengths1. Comprehensive Type Safety
2. Excellent Validation
3. Comprehensive Test Coverage
4. Developer Experience
5. ONEX Compliance
6. Critical Fixes
🟡 Observations & Minor Suggestions1. Fingerprint Script ComplexityFile: The Suggestion: Consider extracting version format detection into a separate helper: def _detect_version_format(content: str) -> tuple[str, int, int]:
"""Returns (format_type, start_pos, end_pos)."""
# Tries patterns in order, returns first match
...
def update_fingerprint_in_yaml(content: str, new_fingerprint: str) -> str:
format_type, start, end = _detect_version_format(content)
# Insert fingerprint based on detected format
...This would improve maintainability and make the logic easier to test. 2. Error Context NestingFile: Error context requires nested access: inner_context = context.get("additional_context", {}).get("context", {})Observation: This nesting pattern works but feels fragile. If def get_validation_context(self) -> dict[str, Any]:
"""Extract validation context, handling nesting."""
return self.context.get("additional_context", {}).get("context", {})3. Documentation Version InconsistenciesFiles: Multiple doc files Some files show
Question: Is this intentional (different docs for different release targets)? If not, standardize to current project version. 4. Subscription Handler Implementation StatusFile: Excellent documentation of planned handlers with TODO comments! Suggestion: The TODO mentions creating a Linear ticket when development begins. Consider adding a placeholder ticket number now (e.g., 🟢 Performance & SecuritySecurity Strengths
Performance Considerations
🟢 Compatibility & MigrationBreaking Changes
Migration PathThe models provide excellent migration helpers: # OLD CODE
params: dict[str, str] = {"limit": "10"}
# NEW CODE (gradual migration)
params = ModelQueryParameters.from_string_dict({"limit": "10"})
# NEW CODE (full type safety)
params = ModelQueryParameters.from_dict({"limit": 10})📊 Test CoverageTest Suite Stats:
Coverage Additions:
🎯 Alignment with Project StandardsCLAUDE.md Compliance
ONEX Patterns
✅ RecommendationAPPROVE - This PR is ready to merge. Merge Checklist
Post-Merge Actions (Optional)
Great work addressing the review feedback with such thoroughness! The typed model migration significantly improves code quality and maintainability. 🎉 Reviewed by: Claude Code (omnibase_core v0.4.0) |
There was a problem hiding this comment.
Actionable comments posted: 3
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/common/model_mixin_config_schema.py (1)
14-33: Addfrom_attributes=Trueto ConfigDict for nested model compatibility.Since
ModelMixinConfigSchemais nested inModelMixinInfo(as noted in the docstring), it should includemodel_config = ConfigDict(from_attributes=True)per the coding guidelines and retrieved learnings. This ensures reliable construction from attribute-based sources.As per coding guidelines, applies to models nested in other Pydantic models.
Apply this diff:
class ModelMixinConfigSchema(BaseModel): """ Typed configuration schema for mixins. Replaces dict[str, Any] config_schema field in ModelMixinInfo with explicit typed fields for mixin configuration. """ + model_config = ConfigDict(from_attributes=True) + properties: dict[str, ModelConfigSchemaProperty] = Field( default_factory=dict, description="Schema properties mapped by name", )Add the import at the top:
-from pydantic import BaseModel, Field, model_validator +from pydantic import BaseModel, ConfigDict, Field, model_validator
🧹 Nitpick comments (4)
src/omnibase_core/models/common/model_config_schema_property.py (2)
22-34: Consider addingfrom_attributes=Trueto ConfigDict.Per coding guidelines, Pydantic models that are nested in other models or used in parallel test execution should include
from_attributes=Truein theirConfigDict. SinceModelConfigSchemaPropertyis used inconfig_schemafields of other models, this configuration may be needed.+from pydantic import BaseModel, ConfigDict, Field, model_validator + class ModelConfigSchemaProperty(BaseModel): """ Typed schema property for mixin configuration. Replaces nested dict[str, Any] in config_schema field with explicit typed fields for JSON Schema properties. Supports type aliases for common Python type names: - 'str' is equivalent to 'string' - 'int' is equivalent to 'integer' - 'bool' is equivalent to 'boolean' - 'float' is equivalent to 'number' """ + + model_config = ConfigDict(from_attributes=True)
111-206: Well-structured validation logic.The type validation correctly handles the Python quirk where
boolis a subclass ofint, preventing boolean values from being accepted for integer/number types. The error messages are clear and include useful context.Optional: The context dictionary creation (lines 151-157, 171-177, 190-196) could be extracted to a helper to reduce duplication, but the current approach keeps each error case self-contained and readable.
src/omnibase_core/models/common/model_mixin_config_schema.py (1)
35-52: Modernized return type annotation; consider updating similar patterns.The change from
"ModelMixinConfigSchema"toSelfis correct and follows modern Python typing best practices. However, note thatModelYamlSchemaObject._validate_required_properties_subset()(lines 33-49 inmodel_yaml_schema_object.py) uses the older quoted-string pattern"ModelYamlSchemaObject". Consider updating it for consistency.src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
40-48: Consider adding a validator fornode_countconsistency.The
node_countfield could potentially diverge fromlen(nodes). Consider whether this is intentional (e.g.,node_countrepresents expected count vs actual) or if a validator should ensure consistency.If they should always match:
+ from pydantic import field_validator + + @field_validator("node_count", mode="after") + @classmethod + def validate_node_count(cls, v: int, info) -> int: + nodes = info.data.get("nodes", []) + if nodes and v != len(nodes): + raise ValueError(f"node_count ({v}) must match len(nodes) ({len(nodes)})") + return vIf divergence is acceptable by design, a brief docstring note would clarify the intent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
src/omnibase_core/models/common/model_config_schema_property.py(4 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_mixin_config_schema.py(2 hunks)src/omnibase_core/models/common/model_typed_metadata.py(1 hunks)src/omnibase_core/models/events/model_node_graph_info.py(1 hunks)src/omnibase_core/models/events/model_node_graph_ready_event.py(1 hunks)src/omnibase_core/models/events/model_node_registered_event.py(1 hunks)src/omnibase_core/models/events/model_node_unregistered_event.py(1 hunks)src/omnibase_core/models/events/model_runtime_event_base.py(1 hunks)src/omnibase_core/models/events/model_runtime_ready_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_created_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_failed_event.py(1 hunks)src/omnibase_core/models/events/model_subscription_removed_event.py(1 hunks)src/omnibase_core/models/events/model_wiring_error_event.py(1 hunks)src/omnibase_core/models/events/model_wiring_error_info.py(1 hunks)src/omnibase_core/models/events/model_wiring_result_event.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/omnibase_core/models/common/model_typed_metadata.py
🚧 Files skipped from review as they are similar to previous changes (5)
- src/omnibase_core/models/events/model_node_unregistered_event.py
- src/omnibase_core/models/events/model_node_graph_info.py
- src/omnibase_core/models/events/model_subscription_failed_event.py
- src/omnibase_core/models/events/model_wiring_error_info.py
- src/omnibase_core/models/events/model_subscription_removed_event.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/events/model_subscription_created_event.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/common/model_config_schema_property.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/events/model_wiring_error_event.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_wiring_result_event.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/common/model_mixin_config_schema.pysrc/omnibase_core/models/events/model_runtime_event_base.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/events/model_subscription_created_event.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/common/model_config_schema_property.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/events/model_wiring_error_event.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_wiring_result_event.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/common/model_mixin_config_schema.pysrc/omnibase_core/models/events/model_runtime_event_base.py
🧠 Learnings (1)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/common/model_mixin_config_schema.py
🧬 Code graph analysis (7)
src/omnibase_core/models/events/model_subscription_created_event.py (9)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-45)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-75)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-68)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
create(67-87)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/events/model_node_registered_event.py (7)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-45)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-68)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/common/model_envelope_payload.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)error_code(253-255)message(248-250)
src/omnibase_core/models/events/model_wiring_result_event.py (13)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-45)src/omnibase_core/models/events/model_wiring_error_info.py (1)
ModelWiringErrorInfo(15-46)src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)tests/fixtures/fixture_intent_publisher.py (1)
success(123-148)src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
create(56-72)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-75)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-68)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
create(67-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)
src/omnibase_core/models/events/model_node_graph_ready_event.py (11)
src/omnibase_core/models/events/model_node_graph_info.py (1)
ModelNodeGraphInfo(15-45)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(16-45)src/omnibase_core/protocols/event_bus/protocol_event_bus_headers.py (1)
event_type(55-57)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(55-75)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-68)src/omnibase_core/models/events/model_runtime_ready_event.py (1)
create(67-87)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_error_event.py (1)
create(65-87)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/common/model_mixin_config_schema.py (1)
src/omnibase_core/models/core/model_yaml_schema_object.py (1)
_validate_required_properties_subset(34-50)
src/omnibase_core/models/events/model_runtime_event_base.py (2)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/common/model_output_reference.py (1)
source_node_id(103-105)
⏰ 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). (19)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: claude-review
🔇 Additional comments (28)
src/omnibase_core/models/common/model_config_schema_property.py (4)
14-19: LGTM!Imports are appropriate and follow coding guidelines by using
ModelOnexErrorwithEnumCoreErrorCodefor structured error handling.
65-84: LGTM!The validator correctly checks type consistency only when a default value is present, with comprehensive docstring explaining the type mapping rules.
86-109: LGTM!The enum validation correctly iterates over values, skips
Noneentries, and provides useful context viaenum_indexfor error reporting.
209-209: LGTM!The
__all__export correctly reflects the simplified public API surface.src/omnibase_core/models/common/model_mixin_config_schema.py (1)
7-7: LGTM: Modern typing pattern adopted.The
Selfimport enables cleaner return type annotations for methods that return the instance itself, improving type safety and subclass compatibility.src/omnibase_core/models/events/model_runtime_ready_event.py (1)
1-87: LGTM!The event model is well-structured with proper field definitions, constraints (
ge=0for counts), and follows the established pattern from sibling event models. Thecreate()factory method correctly handles optional parameters with sensible defaults. The class properly inheritsConfigDictwithfrom_attributes=TruefromModelRuntimeEventBase, satisfying the coding guidelines for immutable value objects.src/omnibase_core/models/events/model_runtime_event_base.py (1)
16-45: Well-designed base model with proper configuration.The
ConfigDictconfiguration is appropriate:
extra="forbid"ensures strict validationvalidate_assignment=Truevalidates on field updatesfrom_attributes=Truesatisfies coding guidelines for nested Pydantic modelsThe UTC-aware timestamp default ensures consistent timezone handling across the event system.
src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
55-72: Factory method follows established patterns.The
create()method correctly handles optional parameters and maintains consistency with sibling event models likeModelRuntimeReadyEventandModelWiringResultEvent.src/omnibase_core/models/events/model_wiring_error_event.py (2)
33-40: Required fields correctly use ellipsis pattern.Using
default=...forerror_codeanderror_messagecorrectly marks these as required fields, which is appropriate for error events that must provide failure context. This aligns with the pattern inModelSubscriptionFailedEvent.
64-87: Factory method signature appropriately requires error details.The
create()method correctly requireserror_codeanderror_messageas positional parameters while keeping other fields as keyword-only with defaults. This enforces that error events always carry meaningful error information.src/omnibase_core/models/events/model_node_registered_event.py (1)
54-75: Factory method correctly enforces required parameters.The
create()method signature properly requiresnode_id,node_name, andnode_typeas positional arguments, ensuring callers provide essential node identification. The keyword-only parameters with defaults follow the established pattern from sibling event models.src/omnibase_core/models/events/model_wiring_result_event.py (3)
1-22: LGTM! Clean module setup.The imports, constants, and public API exports are well-organized and follow the established pattern from other runtime events.
24-78: LGTM! Properly constrained fields with correct defaults.The field definitions are well-structured:
- Appropriate
ge=0constraints on all numeric counters- Correct use of
default_factory=listfor mutable defaults- Clear descriptions for each field
80-107: LGTM! Factory method is consistent and follows established patterns.The
create()method correctly:
- Matches all field defaults
- Handles the optional
errorslist with proper None fallback (line 105)- Propagates
correlation_idto the base class- Follows the same pattern as other event factory methods in the codebase
src/omnibase_core/models/events/model_subscription_created_event.py (3)
1-20: LGTM! Well-organized module setup.The imports and constants follow the established runtime event pattern, with appropriate datetime utilities for timestamp handling.
22-56: LGTM! Field definitions are properly typed and constrained.The fields are well-structured:
- Required fields (
node_id,topic) correctly usedefault=...(lines 38, 42)- Auto-generated fields use
default_factoryfor fresh instances (lines 34, 50)- Optional fields properly typed with
Nonedefaults
58-77: LGTM! Factory method correctly handles required and optional parameters.The
create()method properly:
- Places required parameters (
node_id,topic) before*for positional usage- Handles optional
subscription_idwith UUID generation fallback (line 71)- Matches the pattern used in
ModelSubscriptionRemovedEventandModelNodeRegisteredEventsrc/omnibase_core/models/common/model_envelope_payload.py (11)
183-197: LGTM!Simple and correct delegation to
from_dictwith appropriate type conversion.
199-216: LGTM!Correct serialization with defensive copy of the data dict.
251-273: LGTM!Clean lookup with appropriate fallback to the data dictionary.
275-285: LGTM!Simple accessor for the data dictionary.
287-299: LGTM!Immutable update pattern correctly implemented with
model_copy. The reserved key handling via prefixing into_string_dictis a valid design choice.
301-311: LGTM!Correct immutable update with timezone-aware default timestamp.
313-324: LGTM!Consistent membership check for both typed fields and data dictionary.
326-337: LGTM!Correct length calculation counting both typed fields and data entries.
339-347: LGTM!Correct truthiness check consistent with
__len__.
349-351: LGTM!Correct delegation to
has()forinoperator support.
354-354: LGTM!Appropriate public API exports.
| if isinstance(nested_data, dict): | ||
| # to_dict() output format: {"data": {...}} | ||
| for key, value in nested_data.items(): | ||
| # Ensure value is PayloadDataValue (not nested dict) | ||
| if not isinstance(value, dict): | ||
| extra_data[key] = value | ||
| else: | ||
| # Flat format: collect unknown fields into data dict | ||
| for key, raw_value in data.items(): | ||
| if key not in known_fields and not isinstance(raw_value, dict): | ||
| # After isinstance check, raw_value is PayloadDataValue | ||
| # (dict type is excluded from the union) | ||
| extra_data[key] = raw_value |
There was a problem hiding this comment.
Silent data loss when dict values are encountered.
The code silently drops dict values at lines 163-164 and 168 without warning. Per coding guidelines, invalid data should raise ModelOnexError rather than being silently ignored. This could mask data corruption issues.
Additionally, when both nested data and flat extra keys are present, flat keys are silently ignored (the else branch at line 165 is never reached).
Apply this diff to validate and reject nested dicts:
if isinstance(nested_data, dict):
# to_dict() output format: {"data": {...}}
for key, value in nested_data.items():
# Ensure value is PayloadDataValue (not nested dict)
- if not isinstance(value, dict):
- extra_data[key] = value
+ if isinstance(value, dict):
+ raise ModelOnexError(
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
+ message="Nested dict values are not allowed in envelope payload data",
+ key=key,
+ )
+ extra_data[key] = value
else:
# Flat format: collect unknown fields into data dict
for key, raw_value in data.items():
- if key not in known_fields and not isinstance(raw_value, dict):
- # After isinstance check, raw_value is PayloadDataValue
- # (dict type is excluded from the union)
- extra_data[key] = raw_value
+ if key not in known_fields:
+ if isinstance(raw_value, dict):
+ raise ModelOnexError(
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
+ message="Nested dict values are not allowed in envelope payload data",
+ key=key,
+ )
+ extra_data[key] = raw_value🤖 Prompt for AI Agents
In src/omnibase_core/models/common/model_envelope_payload.py around lines
159-171, the current logic silently drops dict values (both in nested_data and
flat extra fields) and never rejects cases where both nested "data" and flat
extra keys exist; update the validation so that any dict value encountered where
a PayloadDataValue is expected raises ModelOnexError (include the offending key
and context in the error message) and add a check that detects when both nested
data and flat extra keys are present and raises ModelOnexError instead of
ignoring one branch; ensure errors are raised for nested_data[key] being a dict
and for raw_value being a dict, and include enough context in the exception to
trace the invalid field.
| for key, value in self.data.items(): | ||
| if value is not None: | ||
| # Prefix reserved keys to prevent collision with typed fields | ||
| output_key = f"data_{key}" if key in self.RESERVED_KEYS else key | ||
| if isinstance(value, bool): | ||
| result[output_key] = "true" if value else "false" | ||
| elif isinstance(value, list): | ||
| result[output_key] = ",".join(value) | ||
| else: | ||
| result[output_key] = str(value) |
There was a problem hiding this comment.
Asymmetric serialization breaks round-trip with from_string_dict.
Reserved keys in data are prefixed with "data_" during serialization, but from_string_dict doesn't reverse this transformation. This means:
payload = ModelEnvelopePayload().set_data("event_type", "collision")
output = payload.to_string_dict() # {"data_event_type": "collision"}
restored = ModelEnvelopePayload.from_string_dict(output)
# restored.data == {"data_event_type": "collision"}, NOT {"event_type": "collision"}Consider either:
- Documenting this as one-way serialization (not round-trip compatible), or
- Adding logic in
from_string_dictto strip"data_"prefix for reserved keys
🤖 Prompt for AI Agents
In src/omnibase_core/models/common/model_envelope_payload.py around lines 239 to
248, serialization prefixes reserved keys in self.data with "data_" but
from_string_dict does not reverse that, causing asymmetric round-trip behavior;
update from_string_dict to detect keys starting with "data_" and, when the
remainder is a member of RESERVED_KEYS, strip the "data_" prefix before placing
the key into the reconstructed data dict (preserve existing behavior for
non-reserved or non-prefixed keys), ensuring round-trip consistency between
to_string_dict and from_string_dict.
CRITICAL: - ModelEventSubscriptionConfig: Change priority constraint from 0-100 to 0-1000 to align with other models and prevent rejection of valid test data MAJOR: - ModelEnvelopePayload: Add warnings for silent data loss when dict values are skipped, improve reserved-key collision handling in to_string_dict() - ModelNodeRegisteredEvent: Change node_type from str to EnumNodeKind for type safety and ONEX pattern consistency - ModelEventEnvelope: Use timezone-aware datetime.now(UTC) for distributed event tracing consistency NITPICKS: - ModelNodeGraphReadyEvent/ModelRuntimeReadyEvent: Add node_count consistency validators to ensure node_count matches len(nodes) when both provided - event_bus_wiring_effect.yaml: Remove OMN-XXX placeholder text TESTS: - Add comprehensive test suite for ModelEnvelopePayload (34 tests) - Add comprehensive test suite for ModelQueryParameters (42 tests)
Code Review: PR #186 - Typed Pydantic Models MigrationSummaryThis PR successfully addresses feedback from PR #180 by replacing dict[str, str] patterns with properly typed Pydantic models. The implementation demonstrates strong adherence to ONEX architecture principles and best practices. Strengths1. Excellent Type SafetyThe new typed models eliminate dict[str, str] anti-patterns:
Impact: Moves from runtime type checking to compile-time validation with mypy/pyright. 2. Security-First DesignAll models include appropriate security constraints:
3. Comprehensive Test Coverage
Tests cover edge cases, security constraints, and round-trip serialization. 4. Proper Validation LogicModelConfigSchemaProperty includes sophisticated type-default consistency validation with type alias support and bool-to-int coercion prevention. 5. Excellent DocumentationClear module docstrings with examples, security constraints documented, proper Sphinx cross-references. Minor Observations
Code QualityFollows ONEX Patterns:
Security ReviewNo security concerns identified:
Performance ImpactExpected: Neutral to Positive
RecommendationsMust Fix: None - PR is production-ready Should Consider:
Nice to Have:
Final Verdict: APPROVEThis PR represents high-quality work that significantly improves type safety and code maintainability:
Merge Recommendation: Approve after CI passes The minor observations are documentation clarifications, not blocking issues. Great work on this migration! The typed models are well-designed and will provide significant long-term benefits. Reviewed by: Claude Sonnet 4.5 |
- Fix 4 incorrect file paths for documented models - Add total count (29 models use this pattern) - Add discovery command for finding all models - Add additional key models: ModelContractBase, ModelFSMStateSnapshot - Reference model categories: common, events, configuration
- Remove invalid metadata={"error": ...} from ModelNodeCapabilities in
create_error_response() - error message already in additional_info
- Add tracking reference for planned handlers in event_bus_wiring_effect.yaml
Fixes test_error_handling, test_factory_methods, and
test_planned_handlers_are_documented failures in PR #186 CI.
Code Review - PR #186: Typed Pydantic ModelsThank you for this comprehensive PR addressing the feedback from #180! This is a significant improvement to the codebase, replacing untyped ✅ Strengths1. Excellent Type Safety ImprovementsThe new typed models (
2. Comprehensive Documentation
3. Thorough Testing
4. Security Hardening
5. Fingerprint Script Improvements
🔍 Areas for Improvement1. Critical: Inconsistent
|
- Add from_attributes=True to 13 frozen FSM/contract models for pytest-xdist compatibility (model_fsm_state, model_fsm_transition, model_intent, etc.) - Fix 22 occurrences of `or []` pattern → `is not None else []` across 20 files to preserve empty lists instead of treating them as falsy - Add strict mode to ModelEnvelopePayload.to_string_dict() with reserved-key collision detection and has_reserved_key_collision() method - Add comprehensive timezone handling documentation to model_runtime_event_base.py and model_envelope_payload.py - Add descriptive error messages to find()-based assertions in test_regenerate_fingerprints.py All 9,005 model tests pass. No breaking changes.
Code Review: PR #186 - Typed Pydantic Models🎯 Overall AssessmentRating: ✅ Excellent - This PR demonstrates strong software engineering practices and successfully addresses the review feedback from PR #180. Summary: This PR replaces untyped ✅ Strengths1. Architecture & Design ⭐
2. Security & Safety 🔒
3. Type Safety Improvements 💪
4. Test Coverage ✅
5. Documentation 📚
🔍 Code Quality ObservationsExcellent Patterns
💡 Suggestions for ImprovementMinor Issues
Performance Considerations
🧪 Test Coverage AnalysisNew Test Files:
Coverage Highlights:
Missing Coverage (optional enhancements):
🔐 Security ReviewStrengths:
No security issues identified. 📊 Metrics
🎯 RecommendationsMust-Do Before Merge
Nice-to-Have
Follow-Up Work
🏆 Final VerdictApprove with minor suggestions. This PR demonstrates excellent software engineering:
The suggestions above are minor quality improvements - none are blockers. Great work addressing the PR #180 feedback! This significantly improves the codebase type safety and maintainability. 🎉 Review conducted following omnibase_core CLAUDE.md v0.4.0 standards |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
src/omnibase_core/models/node_metadata/model_node_capabilities_info.py (1)
122-133: ExplicitNonehandling foroperationsis correct and safer than truthiness checkSwitching to
supported_operations=operations if operations is not None else []matches the type hint (list[str] | None) and keeps behavior identical forNone, empty, and non-empty lists, while avoiding accidental defaulting for other falsy non-Nonevalues. This is consistent with the broader “explicitNone” pattern in the PR and looks good.If you want to be extra defensive, you could consider normalizing non-list inputs here (e.g., enforcing
list[str]at this boundary) rather than relying solely on Pydantic coercion, but that’s strictly optional given the current typed call sites.tests/unit/models/common/test_model_envelope_payload.py (1)
1-410: Add pytest markers to categorize these tests as unit testsThe behavioral coverage here is strong and matches the envelope model well. To align with the testing guidelines for
tests/**/*.py, consider adding a module-level marker such as:pytestmark = pytest.mark.unitThis keeps classification consistent without having to decorate each test individually.
src/omnibase_core/models/common/model_envelope_payload.py (1)
230-334: Clarify thatto_string_dict()/from_string_dict()are not guaranteed to be a strict round-trip for reserved keysGiven the current design,
to_string_dict()prefixes reserved keys indatawith"data_"and warns, whilefrom_string_dict()simply forwards the string dict intofrom_dict()without attempting to strip"data_"back to the original reserved names. This means:payload = ModelEnvelopePayload(data={"event_type": "x"}) s = payload.to_string_dict() # {"data_event_type": "x"} restored = ModelEnvelopePayload.from_string_dict(s) # restored.data == {"data_event_type": "x"}, not {"event_type": "x"}The implementation and tests are self-consistent, but this behavior is asymmetric. If this one-way mapping is intentional, consider documenting it explicitly in the docstring for
from_string_dict()(or the module docstring) so callers don’t assume a strict round-trip for reserved keys.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (45)
CLAUDE.md(1 hunks)contracts/runtime/event_bus_wiring_effect.yaml(1 hunks)src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_event_subscription_config.py(2 hunks)src/omnibase_core/models/common/model_validation_result.py(2 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsm_state_definition.py(1 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.py(1 hunks)src/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.py(1 hunks)src/omnibase_core/models/core/model_hub_configuration.py(1 hunks)src/omnibase_core/models/core/model_missing_tool.py(1 hunks)src/omnibase_core/models/core/model_node_introspection.py(1 hunks)src/omnibase_core/models/core/model_node_reference.py(3 hunks)src/omnibase_core/models/discovery/model_discovery_error.py(1 hunks)src/omnibase_core/models/discovery/model_discovery_response.py(2 hunks)src/omnibase_core/models/discovery/model_discovery_timeout_error.py(1 hunks)src/omnibase_core/models/discovery/model_introspection_response_event.py(2 hunks)src/omnibase_core/models/discovery/model_nodeintrospectionevent.py(1 hunks)src/omnibase_core/models/discovery/model_tooldiscoveryresponse.py(1 hunks)src/omnibase_core/models/events/model_event_envelope.py(4 hunks)src/omnibase_core/models/events/model_node_graph_ready_event.py(1 hunks)src/omnibase_core/models/events/model_node_registered_event.py(1 hunks)src/omnibase_core/models/events/model_runtime_event_base.py(1 hunks)src/omnibase_core/models/events/model_runtime_ready_event.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_data_primary.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_operation.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_state.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_transition.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_transition_action.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_transition_condition.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_transition_result.py(1 hunks)src/omnibase_core/models/health/model_health_status.py(2 hunks)src/omnibase_core/models/health/model_tool_health.py(5 hunks)src/omnibase_core/models/metadata/model_node_info_summary.py(1 hunks)src/omnibase_core/models/metadata/model_structured_tags.py(3 hunks)src/omnibase_core/models/node_metadata/model_function_documentation.py(1 hunks)src/omnibase_core/models/node_metadata/model_function_relationships.py(2 hunks)src/omnibase_core/models/node_metadata/model_node_capabilities_info.py(1 hunks)src/omnibase_core/models/node_metadata/model_node_organization_metadata.py(1 hunks)src/omnibase_core/models/orchestrator/model_action.py(1 hunks)src/omnibase_core/models/reducer/model_intent.py(1 hunks)src/omnibase_core/models/registry/model_registry_health_report.py(3 hunks)src/omnibase_core/models/security/model_secure_credentials.py(1 hunks)src/omnibase_core/models/security/model_signature_chain.py(1 hunks)tests/unit/models/common/test_model_envelope_payload.py(1 hunks)tests/unit/models/common/test_model_query_parameters.py(1 hunks)
✅ Files skipped from review due to trivial changes (6)
- src/omnibase_core/models/core/model_missing_tool.py
- CLAUDE.md
- src/omnibase_core/models/node_metadata/model_node_organization_metadata.py
- src/omnibase_core/models/node_metadata/model_function_relationships.py
- src/omnibase_core/models/discovery/model_discovery_response.py
- src/omnibase_core/models/security/model_signature_chain.py
🚧 Files skipped from review as they are similar to previous changes (2)
- contracts/runtime/event_bus_wiring_effect.yaml
- src/omnibase_core/models/contracts/subcontracts/model_fsm_state_definition.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
tests/unit/models/common/test_model_envelope_payload.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/fsm/model_fsm_transition.pysrc/omnibase_core/models/fsm/model_fsm_data_primary.pysrc/omnibase_core/models/security/model_secure_credentials.pysrc/omnibase_core/models/fsm/model_fsm_operation.pysrc/omnibase_core/models/common/model_event_subscription_config.pysrc/omnibase_core/models/discovery/model_discovery_error.pysrc/omnibase_core/models/reducer/model_intent.pysrc/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.pysrc/omnibase_core/models/discovery/model_tooldiscoveryresponse.pysrc/omnibase_core/models/fsm/model_fsm_transition_condition.pysrc/omnibase_core/models/discovery/model_introspection_response_event.pysrc/omnibase_core/models/metadata/model_node_info_summary.pysrc/omnibase_core/models/fsm/model_fsm_transition_action.pysrc/omnibase_core/models/events/model_event_envelope.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/fsm/model_fsm_transition_result.pysrc/omnibase_core/models/registry/model_registry_health_report.pytests/unit/models/common/test_model_query_parameters.pysrc/omnibase_core/models/core/model_node_introspection.pysrc/omnibase_core/models/fsm/model_fsm_state.pysrc/omnibase_core/models/discovery/model_nodeintrospectionevent.pysrc/omnibase_core/models/core/model_hub_configuration.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/discovery/model_discovery_timeout_error.pysrc/omnibase_core/models/health/model_tool_health.pysrc/omnibase_core/models/events/model_runtime_event_base.pysrc/omnibase_core/models/node_metadata/model_function_documentation.pysrc/omnibase_core/models/node_metadata/model_node_capabilities_info.pysrc/omnibase_core/models/metadata/model_structured_tags.pysrc/omnibase_core/models/common/model_validation_result.pysrc/omnibase_core/models/orchestrator/model_action.pysrc/omnibase_core/models/core/model_node_reference.pysrc/omnibase_core/models/health/model_health_status.pysrc/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.pysrc/omnibase_core/models/common/model_envelope_payload.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/models/common/test_model_envelope_payload.pytests/unit/models/common/test_model_query_parameters.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/fsm/model_fsm_transition.pysrc/omnibase_core/models/fsm/model_fsm_data_primary.pysrc/omnibase_core/models/security/model_secure_credentials.pysrc/omnibase_core/models/fsm/model_fsm_operation.pysrc/omnibase_core/models/common/model_event_subscription_config.pysrc/omnibase_core/models/discovery/model_discovery_error.pysrc/omnibase_core/models/reducer/model_intent.pysrc/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.pysrc/omnibase_core/models/discovery/model_tooldiscoveryresponse.pysrc/omnibase_core/models/fsm/model_fsm_transition_condition.pysrc/omnibase_core/models/discovery/model_introspection_response_event.pysrc/omnibase_core/models/metadata/model_node_info_summary.pysrc/omnibase_core/models/fsm/model_fsm_transition_action.pysrc/omnibase_core/models/events/model_event_envelope.pysrc/omnibase_core/models/events/model_runtime_ready_event.pysrc/omnibase_core/models/fsm/model_fsm_transition_result.pysrc/omnibase_core/models/registry/model_registry_health_report.pysrc/omnibase_core/models/core/model_node_introspection.pysrc/omnibase_core/models/fsm/model_fsm_state.pysrc/omnibase_core/models/discovery/model_nodeintrospectionevent.pysrc/omnibase_core/models/core/model_hub_configuration.pysrc/omnibase_core/models/events/model_node_registered_event.pysrc/omnibase_core/models/discovery/model_discovery_timeout_error.pysrc/omnibase_core/models/health/model_tool_health.pysrc/omnibase_core/models/events/model_runtime_event_base.pysrc/omnibase_core/models/node_metadata/model_function_documentation.pysrc/omnibase_core/models/node_metadata/model_node_capabilities_info.pysrc/omnibase_core/models/metadata/model_structured_tags.pysrc/omnibase_core/models/common/model_validation_result.pysrc/omnibase_core/models/orchestrator/model_action.pysrc/omnibase_core/models/core/model_node_reference.pysrc/omnibase_core/models/health/model_health_status.pysrc/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.pysrc/omnibase_core/models/common/model_envelope_payload.py
🧠 Learnings (3)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/fsm/model_fsm_transition.pysrc/omnibase_core/models/fsm/model_fsm_data_primary.pysrc/omnibase_core/models/fsm/model_fsm_operation.pysrc/omnibase_core/models/common/model_event_subscription_config.pysrc/omnibase_core/models/reducer/model_intent.pysrc/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.pysrc/omnibase_core/models/fsm/model_fsm_transition_condition.pysrc/omnibase_core/models/fsm/model_fsm_transition_action.pysrc/omnibase_core/models/fsm/model_fsm_transition_result.pysrc/omnibase_core/models/fsm/model_fsm_state.pysrc/omnibase_core/models/orchestrator/model_action.pysrc/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Applied to files:
src/omnibase_core/models/events/model_node_registered_event.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Applied to files:
src/omnibase_core/models/common/model_envelope_payload.py
🧬 Code graph analysis (14)
tests/unit/models/common/test_model_envelope_payload.py (1)
src/omnibase_core/models/common/model_envelope_payload.py (10)
ModelEnvelopePayload(67-445)to_dict(246-263)from_dict(154-228)to_string_dict(265-334)from_string_dict(231-244)set_data(372-384)with_timestamp(386-405)get(336-358)get_data(360-370)has(407-418)
src/omnibase_core/models/events/model_node_graph_ready_event.py (8)
src/omnibase_core/models/events/model_node_graph_info.py (1)
ModelNodeGraphInfo(15-45)src/omnibase_core/models/events/model_runtime_ready_event.py (2)
validate_node_count_consistency(67-77)create(80-119)src/omnibase_core/models/events/model_node_registered_event.py (1)
create(56-76)src/omnibase_core/models/events/model_node_unregistered_event.py (1)
create(50-68)src/omnibase_core/models/events/model_subscription_failed_event.py (1)
create(64-84)src/omnibase_core/models/events/model_subscription_created_event.py (1)
create(59-77)src/omnibase_core/models/events/model_subscription_removed_event.py (1)
create(55-71)src/omnibase_core/models/events/model_wiring_result_event.py (1)
create(81-107)
src/omnibase_core/models/metadata/model_node_info_summary.py (3)
src/omnibase_core/models/node_metadata/model_function_node.py (1)
tags(85-87)src/omnibase_core/models/node_metadata/model_function_node_metadata_class.py (2)
tags(146-148)categories(151-153)src/omnibase_core/models/node_metadata/model_node_metadata_info.py (2)
tags(154-156)categories(159-161)
src/omnibase_core/models/events/model_event_envelope.py (1)
src/omnibase_core/models/infrastructure/model_duration.py (1)
total_seconds(160-162)
src/omnibase_core/models/events/model_runtime_ready_event.py (2)
src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(35-64)src/omnibase_core/models/events/model_node_graph_ready_event.py (2)
validate_node_count_consistency(56-66)create(69-100)
tests/unit/models/common/test_model_query_parameters.py (1)
src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-312)
src/omnibase_core/models/core/model_node_introspection.py (1)
src/omnibase_core/models/node_metadata/model_node_metadata_info.py (1)
capabilities(149-151)
src/omnibase_core/models/discovery/model_nodeintrospectionevent.py (1)
src/omnibase_core/models/metadata/model_node_info_summary.py (2)
tags(189-191)tags(194-196)
src/omnibase_core/models/health/model_tool_health.py (1)
src/omnibase_core/models/metadata/model_node_info_summary.py (2)
dependencies(209-211)dependencies(214-216)
src/omnibase_core/models/events/model_runtime_event_base.py (3)
src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
uuid(157-176)src/omnibase_core/models/metadata/model_node_info_summary.py (2)
description(109-111)description(114-116)src/omnibase_core/models/common/model_output_reference.py (1)
source_node_id(103-105)
src/omnibase_core/models/node_metadata/model_function_documentation.py (1)
src/omnibase_core/models/node_metadata/model_function_node_metadata_class.py (1)
examples(85-87)
src/omnibase_core/models/node_metadata/model_node_capabilities_info.py (1)
src/omnibase_core/models/node_metadata/model_node_information.py (1)
supported_operations(162-164)
src/omnibase_core/models/core/model_node_reference.py (1)
src/omnibase_core/models/node_metadata/model_node_metadata_info.py (1)
capabilities(149-151)
src/omnibase_core/models/common/model_envelope_payload.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)error_code(253-255)message(248-250)
⏰ 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). (8)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: claude-review
🔇 Additional comments (33)
src/omnibase_core/models/core/model_node_introspection.py (1)
123-123: LGTM! Explicit None check preserves intentional empty lists.This refinement correctly distinguishes between
None(no value provided) and[](explicitly empty capabilities). The explicitis not Nonecheck is more type-safe and aligns with the broader repository pattern mentioned in the AI summary for consistent None-aware defaults.src/omnibase_core/models/reducer/model_intent.py (1)
135-141: ConfigDict update tofrom_attributes=Trueis correct and matches project guidelinesAdding
from_attributes=Truehere is consistent with usingModelIntentas an immutable, nested value object and aligns with the broader move to attribute-based construction; it plays nicely withfrozen=Trueandextra="forbid"and doesn’t introduce new edge cases. Please just re-run the usual typed test suite to confirm no behavioral surprises in reducer/effect wiring that constructModelIntentfrom non-dict sources.Based on learnings, this matches the
from_attributes=Truerecommendation for immutable nested Pydantic models.src/omnibase_core/models/orchestrator/model_action.py (1)
157-162: LGTM! Aligns with coding guidelines for immutable value objects.The addition of
from_attributes=Trueenables the model to be constructed from attribute-based sources (e.g., ORM objects, dataclasses) in addition to dict input. This is particularly beneficial for:
- Nesting within other Pydantic models
- Parallel test execution scenarios
- Converting from objects with matching attributes
The change preserves all existing immutability and validation semantics.
Based on learnings, this follows the established pattern for immutable value objects in the codebase.
src/omnibase_core/models/contracts/subcontracts/model_fsmtransitionaction.py (1)
96-101: ConfigDictfrom_attributes=Trueaddition is correct and aligns with guidelinesUsing
from_attributes=Truefor this frozen, likely nested FSM subcontract matches the guidance for immutable Pydantic value objects and should improve attribute-based parsing without changing runtime behavior of existing instances. No further changes needed here.Please confirm this model is covered by your existing FSM/contract tests (e.g., via
poetry run pytest) so that the ConfigDict change is exercised alongside the other FSM subcontract models.src/omnibase_core/models/registry/model_registry_health_report.py (1)
563-584: ExplicitNonehandling for tools/services health lists is a solid improvementSwitching from a truthiness-based fallback (e.g.,
tools_health or []) to an explicittools_health if tools_health is not None else []avoids two subtle issues:
- It preserves a caller‑supplied empty list instead of silently replacing it with a new list instance, which is less surprising and better for tests that may assert object identity or rely on prior population.
- It eliminates unintended behavior for other falsy but valid sequence inputs (e.g., tuples) by no longer treating them as “missing”; they now flow through to Pydantic’s normal parsing.
Given the model field types (
list[...]withdefault_factory=list), this keeps constructor behavior consistent with the type hints and validation semantics across all three factory helpers. No further changes needed here.Also applies to: 585-607, 608-628
src/omnibase_core/models/metadata/model_node_info_summary.py (1)
539-542: ExplicitNonehandling here looks goodUsing
tags if tags is not None else [](and the same forcategories) keeps the runtime behavior while making the types explicit and avoiding any truthiness edge cases. This is clearer and more type-checker-friendly than relying on generic falsy checks.src/omnibase_core/models/discovery/model_discovery_timeout_error.py (1)
35-35: LGTM! More explicit None handling.The explicit None check is more defensive and aligns with the type hint
list[ModelDiscoveredTool] | None. This preserves the original list reference when an empty list is passed in, rather than treating all falsy values identically.src/omnibase_core/models/discovery/model_discovery_error.py (1)
33-33: LGTM! Explicit None check improves clarity.This change standardizes None handling to match the type hint
list[str] | Noneand aligns with the repository-wide pattern mentioned in the PR summary. The explicit check is more defensive and preserves the original reference for empty lists.src/omnibase_core/models/metadata/model_structured_tags.py (1)
294-294: LGTM! Explicit None handling improves clarity.The change from
custom_tags or []tocustom_tags if custom_tags is not None else []across all three factory methods is a good improvement. It makes the intent explicit, correctly aligns with thelist[str] | Nonetype hint, and preserves an intentionally passed empty list rather than treating it as falsy.Also applies to: 318-318, 341-341
src/omnibase_core/models/health/model_health_status.py (1)
218-218: LGTM! Improved explicitness in None handling.The changes from
issues or []toissues if issues is not None else []make the None-handling behavior more explicit and semantically clearer. This preserves the distinction between an explicitly passed empty list and None, which improves code maintainability.Also applies to: 232-232
src/omnibase_core/models/discovery/model_nodeintrospectionevent.py (1)
173-173: Good refactor: explicit None handling improves clarity.The change from
tags or []totags if tags is not None else []is more explicit and follows a clearer pattern for handling optional parameters. While functionally equivalent in this case, it better communicates intent by explicitly converting onlyNoneto an empty list.src/omnibase_core/models/discovery/model_tooldiscoveryresponse.py (1)
93-132: ExplicitNonehandling forpartial_toolsis clearer and correctUsing
tools = partial_tools if partial_tools is not None else []makes the intent explicit, keeps the factory robust whenpartial_toolsis omitted, and aligns with the PR’s move toward stricter typing and explicit defaults. No further changes needed here.src/omnibase_core/models/node_metadata/model_function_documentation.py (1)
90-100: ExplicitNonehandling forexampleslooks goodUsing
examples if examples is not None else []avoids truthy/falsy coercion and cleanly distinguishesNonefrom an intentionally empty list, which fits the stricter typing pattern in this PR. No issues from a correctness or API-consistency standpoint.src/omnibase_core/models/events/model_event_envelope.py (1)
13-13: Excellent UTC standardization for distributed event timing.The migration from naive to UTC-aware timestamps is a best-practice improvement for event-driven systems. All time operations are now consistently timezone-aware:
- Envelope creation uses
datetime.now(UTC)(line 79)- Expiration checks use
datetime.now(UTC)(line 290)- Elapsed time calculations use
datetime.now(UTC)(line 309)This prevents timezone-related bugs in cross-service communication and ensures accurate time comparisons across the distributed system.
Also applies to: 79-80, 290-290, 309-309
src/omnibase_core/models/core/model_node_reference.py (1)
72-83: ExplicitcapabilitiesNone handling in factory methods looks correctUsing
capabilities = capabilities if capabilities is not None else []in all three factory methods avoids passingNoneinto a non-optional list field and correctly preserves intentional empty lists; this is consistent and safe with no shared mutable default issues.Also applies to: 86-98, 100-113
src/omnibase_core/models/security/model_secure_credentials.py (1)
431-431: LGTM! Explicit None handling improves clarity.The change from
fallback_prefixes or []tofallback_prefixes if fallback_prefixes is not None else []is a semantic improvement. It preserves an explicitly provided empty list while only defaulting to[]whenNoneis passed, making the intent clearer.src/omnibase_core/models/health/model_tool_health.py (1)
301-301: LGTM! Consistent explicit None handling across factory methods.All five factory methods consistently apply the explicit None check pattern (
dependencies if dependencies is not None else []), which improves clarity and preserves explicitly provided empty lists.Also applies to: 333-333, 544-544, 575-575, 610-610
src/omnibase_core/models/discovery/model_introspection_response_event.py (2)
138-138: LGTM! Explicit None handling for tools parameter.The change to explicit None checking improves clarity and correctly preserves explicitly provided empty lists.
224-237: No changes needed—error message handling is correctly implemented.Verification confirms error messages are properly stored in
additional_info.error_messageacross all code paths. The test suite explicitly validates this pattern, no legacy code accessescapabilities.metadatafor errors, and all call sites (inmixin_request_response_introspection.py,mixin_node_service.py, andmixin_node_executor.py) correctly use the factory method to create error responses with the error message in the expected location.src/omnibase_core/models/common/model_event_subscription_config.py (1)
18-18: LGTM! Enables attribute-based initialization.Adding
from_attributes=Truealigns with the coding guidelines and enables attribute-based construction for this model.Based on learnings, this is appropriate for immutable value objects nested in other Pydantic models.
src/omnibase_core/models/fsm/model_fsm_transition_action.py (1)
155-155: LGTM! Enables attribute-based initialization for frozen model.Adding
from_attributes=Trueis appropriate for this frozen, immutable FSM model that is likely nested in other Pydantic models.Based on learnings, this aligns with the guideline for immutable value objects.
src/omnibase_core/models/fsm/model_fsm_transition.py (1)
78-78: LGTM! Enables attribute-based initialization for frozen FSM model.Adding
from_attributes=Trueis appropriate for this frozen, immutable transition model and aligns with the coding guidelines for immutable value objects.Based on learnings, this pattern is correct for models used in parallel test execution or nested in other Pydantic models.
src/omnibase_core/models/core/model_hub_configuration.py (1)
63-65: LGTM! Explicit None handling in factory method.The change to explicit None checking (
managed_tool_names if managed_tool_names is not None else []) improves clarity and correctly distinguishes betweenNoneand an explicitly provided empty list.src/omnibase_core/models/fsm/model_fsm_state.py (1)
106-106: LGTM! Enables attribute-based initialization for frozen FSM state model.Adding
from_attributes=Trueis appropriate for this frozen, immutable state model and aligns with the coding guidelines for immutable value objects nested in other Pydantic models.Based on learnings, this is the correct pattern.
src/omnibase_core/models/fsm/model_fsm_transition_result.py (1)
127-132: LGTM! Correct application offrom_attributes=True.This change enables attribute-based initialization for the immutable FSM transition result model, which is nested in other Pydantic models. The configuration aligns with the coding guidelines and learning context.
Based on learnings: For immutable value objects that are nested in other Pydantic models, add
from_attributes=Trueto ConfigDict.src/omnibase_core/models/fsm/model_fsm_data_primary.py (1)
161-166: LGTM! Correct application offrom_attributes=True.This change enables attribute-based initialization for the immutable FSM data model, which is nested in other Pydantic models. Consistent with the broader PR pattern.
Based on learnings: For immutable value objects that are nested in other Pydantic models, add
from_attributes=Trueto ConfigDict.src/omnibase_core/models/fsm/model_fsm_operation.py (1)
90-95: LGTM! Correct application offrom_attributes=True.This change enables attribute-based initialization for the immutable FSM operation model. Consistent with the pattern across FSM models in this PR.
Based on learnings: For immutable value objects that are nested in other Pydantic models, add
from_attributes=Trueto ConfigDict.src/omnibase_core/models/fsm/model_fsm_transition_condition.py (1)
92-97: LGTM! Correct application offrom_attributes=True.This change enables attribute-based initialization for the immutable FSM transition condition model. Aligns with the coding guidelines and PR objectives.
Based on learnings: For immutable value objects that are nested in other Pydantic models, add
from_attributes=Trueto ConfigDict.src/omnibase_core/models/common/model_validation_result.py (1)
124-152: LGTM! Improved None vs. empty list handling.The changes from
or []toif is not None else []are more explicit and preserve the identity of explicitly provided empty lists. This improves type safety by distinguishing between "not provided" (None) and "explicitly empty" ([]).src/omnibase_core/models/contracts/subcontracts/model_fsm_state_transition.py (1)
124-129: LGTM! Correct replacement ofvalidate_assignmentwithfrom_attributes.Since this model is frozen (immutable),
validate_assignmentis unnecessary. Replacing it withfrom_attributes=Trueenables attribute-based initialization for nested models, which is the intended behavior for immutable value objects.Based on learnings: For immutable value objects that are nested in other Pydantic models, add
from_attributes=Trueto ConfigDict.src/omnibase_core/models/events/model_runtime_ready_event.py (1)
1-119: LGTM! Well-structured runtime ready event model.The implementation follows established patterns from similar event models (e.g.,
ModelNodeGraphReadyEvent). The validator correctly ensures consistency betweennode_countandnodes_wiredwhen both are provided, while allowingnode_countto serve as the authoritative count when the detailed node list is not provided.Key observations:
- The factory method properly derives
node_countfromnodes_wiredwhen not explicitly provided- The validator is intentionally asymmetric, allowing
node_countto exceedlen(nodes_wired)(since the node list is optional detail)- Field defaults and validation constraints are appropriate
src/omnibase_core/models/events/model_runtime_event_base.py (1)
1-64: Runtime event base model looks solid and matches UTC/strictness requirementsThe shared event fields, UTC-aware timestamp default, and strict
model_config(extra="forbid",validate_assignment=True,from_attributes=True) are consistent with the documented runtime event guidance and should compose well with the concrete event models.src/omnibase_core/models/events/model_node_registered_event.py (1)
22-76: Node registration event aligns with existing event patterns and EnumNodeKind usageThe model cleanly extends
ModelRuntimeEventBase, usesEnumNodeKindfornode_type, and thecreate()factory correctly handles optional fields and avoids mutable defaults viadefault_factory/None+[]. This is consistent with the broader runtime event surface.
| @classmethod | ||
| def from_dict( | ||
| cls, data: dict[str, PayloadDataValue | dict[str, PayloadDataValue]] | ||
| ) -> Self: | ||
| """Create from a dictionary of payload data. | ||
|
|
||
| Extracts known fields (event_type, source, timestamp, correlation_id) | ||
| and places remaining fields in the data dictionary. | ||
|
|
||
| Handles round-trip compatibility with to_dict() by recognizing | ||
| nested "data" dictionaries from to_dict() output. | ||
|
|
||
| Args: | ||
| data: Dictionary of payload key-value pairs. May contain a nested | ||
| "data" dict from to_dict() output for round-trip support. | ||
|
|
||
| Returns: | ||
| New ModelEnvelopePayload instance. | ||
|
|
||
| Warns: | ||
| UserWarning: When dict values are encountered and skipped (potential | ||
| data loss). Nested dicts are not supported in PayloadDataValue. | ||
| """ | ||
| known_fields = {"event_type", "source", "timestamp", "correlation_id", "data"} | ||
| skipped_keys: list[str] = [] | ||
|
|
||
| # Extract typed fields | ||
| event_type_val = data.get("event_type") | ||
| source_val = data.get("source") | ||
| timestamp_val = data.get("timestamp") | ||
| correlation_id_val = data.get("correlation_id") | ||
|
|
||
| # Handle nested "data" dict from to_dict() output for round-trip support | ||
| nested_data = data.get("data") | ||
| extra_data: dict[str, PayloadDataValue] = {} | ||
|
|
||
| if isinstance(nested_data, dict): | ||
| # to_dict() output format: {"data": {...}} | ||
| for key, value in nested_data.items(): | ||
| # Ensure value is PayloadDataValue (not nested dict) | ||
| # Note: isinstance check is defensive - type system says dict values | ||
| # can't be dicts, but runtime may receive malformed input | ||
| if isinstance(value, dict): # type: ignore[unreachable] | ||
| skipped_keys.append(f"data.{key}") # type: ignore[unreachable] | ||
| else: | ||
| extra_data[key] = value | ||
| else: | ||
| # Flat format: collect unknown fields into data dict | ||
| for key, raw_value in data.items(): | ||
| if key not in known_fields: | ||
| if isinstance(raw_value, dict): | ||
| skipped_keys.append(key) | ||
| else: | ||
| # After isinstance check, raw_value is PayloadDataValue | ||
| # (dict type is excluded from the union) | ||
| extra_data[key] = raw_value | ||
|
|
||
| # Warn about skipped dict values to prevent silent data loss | ||
| if skipped_keys: | ||
| warnings.warn( | ||
| f"ModelEnvelopePayload.from_dict() skipped {len(skipped_keys)} dict " | ||
| f"value(s) that cannot be represented in PayloadDataValue: " | ||
| f"{skipped_keys}. Nested dicts are not supported.", | ||
| UserWarning, | ||
| stacklevel=2, | ||
| ) | ||
|
|
||
| return cls( | ||
| event_type=str(event_type_val) if event_type_val is not None else None, | ||
| source=str(source_val) if source_val is not None else None, | ||
| timestamp=str(timestamp_val) if timestamp_val is not None else None, | ||
| correlation_id=str(correlation_id_val) | ||
| if correlation_id_val is not None | ||
| else None, | ||
| data=extra_data, | ||
| ) |
There was a problem hiding this comment.
Fix silent drop of flat extra keys when a nested "data" dict is present in from_dict()
When the input contains both a nested "data" dict and additional top-level extra keys, only the nested dict is processed:
nested_datais adict→ theif isinstance(nested_data, dict)branch runs.- The
else:branch (which walks flat extras) is skipped. - Extra non-typed, non-
"data"keys are neither added toextra_datanor recorded inskipped_keys, so they silently disappear.
Example input that will currently lose "extra_field" without any warning:
payload = ModelEnvelopePayload.from_dict(
{
"event_type": "test",
"data": {"key": "value"},
"extra_field": "should_not_be_dropped",
}
)
# payload.data == {"key": "value"} # "extra_field" lostYou can avoid this by always processing flat extras, regardless of whether a nested "data" dict exists:
@@
- # Handle nested "data" dict from to_dict() output for round-trip support
- nested_data = data.get("data")
- extra_data: dict[str, PayloadDataValue] = {}
-
- if isinstance(nested_data, dict):
- # to_dict() output format: {"data": {...}}
- for key, value in nested_data.items():
- # Ensure value is PayloadDataValue (not nested dict)
- # Note: isinstance check is defensive - type system says dict values
- # can't be dicts, but runtime may receive malformed input
- if isinstance(value, dict): # type: ignore[unreachable]
- skipped_keys.append(f"data.{key}") # type: ignore[unreachable]
- else:
- extra_data[key] = value
- else:
- # Flat format: collect unknown fields into data dict
- for key, raw_value in data.items():
- if key not in known_fields:
- if isinstance(raw_value, dict):
- skipped_keys.append(key)
- else:
- # After isinstance check, raw_value is PayloadDataValue
- # (dict type is excluded from the union)
- extra_data[key] = raw_value
+ # Handle nested "data" dict from to_dict() output for round-trip support
+ nested_data = data.get("data")
+ extra_data: dict[str, PayloadDataValue] = {}
+
+ if isinstance(nested_data, dict):
+ # to_dict() output format: {"data": {...}}
+ for key, value in nested_data.items():
+ # Ensure value is PayloadDataValue (not nested dict)
+ # Note: isinstance check is defensive - type system says dict values
+ # can't be dicts, but runtime may receive malformed input
+ if isinstance(value, dict): # type: ignore[unreachable]
+ skipped_keys.append(f"data.{key}") # type: ignore[unreachable]
+ else:
+ extra_data[key] = value
+ elif nested_data is not None:
+ # Non-dict "data" value is not representable; treat as skipped
+ skipped_keys.append("data")
+
+ # Always collect flat-format unknown fields into data dict
+ for key, raw_value in data.items():
+ if key in known_fields:
+ continue
+ if isinstance(raw_value, dict):
+ skipped_keys.append(key)
+ else:
+ # After isinstance check, raw_value is PayloadDataValue
+ extra_data[key] = raw_valueThis preserves all non-typed extras (or records them in skipped_keys if they’re dicts) regardless of whether data is present, eliminating silent data loss for mixed inputs.
| priority: int = Field( | ||
| default=0, | ||
| description="Subscription priority (0-100, higher = more urgent)", | ||
| description="Subscription priority (0-1000, higher = more urgent)", | ||
| ge=0, | ||
| le=100, | ||
| le=1000, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for hardcoded priority values or ranges that might assume 0-100
rg -nP --type=py 'priority\s*[><=]+\s*\d+' -g '!test_*' -g '!*_test.py'Repository: OmniNode-ai/omnibase_core
Length of output: 2883
🏁 Script executed:
#!/bin/bash
# Search for priority-related constants
rg -nP --type=py '(MAX|MIN).*PRIORITY|PRIORITY.*(MAX|MIN)' -g '!test_*' -g '!*_test.py'Repository: OmniNode-ai/omnibase_core
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Search for priority field usage in event subscriptions
rg -nC3 --type=py 'priority.*Field|Field.*priority' -g '!test_*' -g '!*_test.py'Repository: OmniNode-ai/omnibase_core
Length of output: 41666
🏁 Script executed:
#!/bin/bash
# Check the actual file to see the current state
cat -n src/omnibase_core/models/common/model_event_subscription_config.pyRepository: OmniNode-ai/omnibase_core
Length of output: 1752
🏁 Script executed:
#!/bin/bash
# Search for usage of model_filter_conditions and specifically min_priority/max_priority
rg -nC5 'model_filter_conditions|min_priority|max_priority' --type=py -g '!test_*' -g '!*_test.py' | head -100Repository: OmniNode-ai/omnibase_core
Length of output: 4306
🏁 Script executed:
#!/bin/bash
# Check the filter_conditions file more carefully
cat -n src/omnibase_core/models/contracts/model_filter_conditions.py | sed -n '65,85p'Repository: OmniNode-ai/omnibase_core
Length of output: 667
🏁 Script executed:
#!/bin/bash
# Search for actual usage patterns of ModelFilterConditions
rg -nC5 'filter.*priority|priority.*filter' --type=py -g '!test_*' -g '!*_test.py' | head -80Repository: OmniNode-ai/omnibase_core
Length of output: 2313
🏁 Script executed:
#!/bin/bash
# Check if ModelFilterConditions is used in event subscription processing
rg -nC3 'ModelFilterConditions' --type=py | grep -A10 -B10 'EventSubscription\|event.*subscription'Repository: OmniNode-ai/omnibase_core
Length of output: 1925
🏁 Script executed:
#!/bin/bash
# Search for where ModelFilterConditions is actually instantiated or used
rg -nC3 'ModelFilterConditions\(' --type=py -g '!test_*' -g '!*_test.py'Repository: OmniNode-ai/omnibase_core
Length of output: 704
🏁 Script executed:
#!/bin/bash
# Check if there's any processing logic that uses min_priority/max_priority
rg -nC5 '\.min_priority|\.max_priority' --type=py -g '!test_*' -g '!*_test.py'Repository: OmniNode-ai/omnibase_core
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Search for event subscription usage and if priority is used in filtering
rg -nC5 'ModelEventSubscription|event.*subscription' --type=py -g '!test_*' -g '!*_test.py' | head -120Repository: OmniNode-ai/omnibase_core
Length of output: 10684
🏁 Script executed:
#!/bin/bash
# Check if min_priority or max_priority appear in any YAML contracts or test files
fd -e yaml -e yml | xargs grep -l "min_priority\|max_priority" 2>/dev/null | head -20Repository: OmniNode-ai/omnibase_core
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Verify that ModelFilterConditions is not actually used to validate event subscription priorities
# Look for filter usage in contracts or runtime
rg -nC10 'filter.*subscription|subscription.*filter' --type=py -g '!test_*' -g '!*_test.py' | head -50Repository: OmniNode-ai/omnibase_core
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Check the actual event subscription model to understand the relationship
cat -n src/omnibase_core/models/contracts/model_event_subscription.py 2>/dev/null || echo "File not found"Repository: OmniNode-ai/omnibase_core
Length of output: 2293
Update ModelFilterConditions priority constraints for consistency.
The subscription priority range expansion to 0-1000 is not used in existing event handling logic and won't cause runtime breakage. However, ModelFilterConditions in src/omnibase_core/models/contracts/model_filter_conditions.py (lines 69-80) still constrains min_priority and max_priority to le=100. For validation consistency, these should be updated to le=1000 to match the subscription priority range.
🤖 Prompt for AI Agents
In src/omnibase_core/models/contracts/model_filter_conditions.py around lines 69
to 80, the ModelFilterConditions dataclass still uses le=100 for min_priority
and max_priority validation; update both Field validators to le=1000 so they
match the subscription priority range (0-1000) used in
model_event_subscription_config.py, ensuring consistent validation across
models.
…ents - Add validate_assignment=True to 7 mutable models with validators - model_output_reference, model_operation_data, model_query_parameters - model_envelope_payload, model_validation_issue, model_value_container - model_mixin_config_schema - Fix explicit None handling patterns (x or [] -> x if x is not None else []) - model_yaml_value, model_schema_value (2 occurrences) - model_flexible_value, model_mixin_metadata_collection (8 occurrences) - model_unified_hub_contract (2 occurrences), model_generic_collection - Replace ValueError with ModelOnexError in model_node_graph_ready_event - Use EnumCoreErrorCode.VALIDATION_ERROR for consistency - Add comprehensive round-trip documentation to ModelEnvelopePayload - Document that to_string_dict/from_string_dict are NOT round-trip compatible - Explain transformations: reserved key prefixing, type serialization - Add @pytest.mark.unit markers to 9 test classes in test_model_query_parameters.py - Add descriptive assertion messages to find()-based ordering tests - test_regenerate_fingerprints.py: 4 ordering assertions improved
Code Review - PR #186: Typed Pydantic ModelsExcellent work addressing the PR #180 feedback! This PR successfully replaces dict[str, str] patterns with proper typed Pydantic models across 6 new model files. StrengthsStrong Type Safety
Security Considerations
Excellent Documentation
Test Coverage
Code Quality ObservationsModelEnvelopePayload (521 lines)
ModelConfigSchemaProperty
Fingerprint Script Enhancement
Critical Fixes - Well Done
Minor Improvements
Testing RecommendationsEdge Cases:
Security ReviewPassed all checks:
Note: ModelEnvelopePayload.to_string_dict() uses comma-joining for lists - could be ambiguous if list values contain commas. Consider documenting this limitation. Metrics
Final VerdictRecommendation: APPROVE This PR demonstrates excellent software engineering practices:
The minor suggestions are optional improvements and don't block approval. Code is production-ready and follows all ONEX conventions from CLAUDE.md. Pre-Merge Checklist:
Great work! |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/omnibase_core/models/events/model_node_graph_ready_event.py (1)
57-71: Validator now correctly usesModelOnexError.The past review feedback has been addressed—the validator now raises
ModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERRORinstead of a bareValueError.One note: the validation only triggers when
nodesis non-empty, allowingnode_count=5with an emptynodeslist. If this is intentional (e.g., node details are optional metadata), the current logic is fine. Otherwise, consider validating thatnode_count == 0whennodesis empty.src/omnibase_core/models/common/model_envelope_payload.py (2)
445-457: Reserved keys indatacan silently override typed fields into_string_dict().Currently
set_data("event_type", "collision")is allowed, andto_string_dict()will produce"data_event_type"butfrom_string_dict()won't reverse this. More critically, this allows confusing states wheredatacontains keys that shadow typed field names.Consider rejecting reserved keys in
set_data():def set_data(self, key: str, value: PayloadDataValue) -> Self: """Set a value in the data dictionary, returning a new instance.""" + if key in self.RESERVED_KEYS: + raise ModelOnexError( + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + message="Cannot set reserved key via set_data(); use typed fields instead", + key=key, + ) new_data = self.data.copy() new_data[key] = value return self.model_copy(update={"data": new_data})
191-210: Silent data loss when both nested"data"dict and flat extra keys are present.When the input contains both a nested
"data"dict and additional top-level extra keys, only the nested dict is processed. Theelsebranch (lines 201-210) is skipped entirely, causing flat extra keys to be silently dropped.Example that loses
"extra_field":payload = ModelEnvelopePayload.from_dict({ "event_type": "test", "data": {"key": "value"}, "extra_field": "should_not_be_dropped", # Silently lost! }) # payload.data == {"key": "value"} # "extra_field" is goneConsider always processing flat extras regardless of whether nested
"data"exists:if isinstance(nested_data, dict): # to_dict() output format: {"data": {...}} for key, value in nested_data.items(): if isinstance(value, dict): # type: ignore[unreachable] skipped_keys.append(f"data.{key}") # type: ignore[unreachable] else: extra_data[key] = value - else: - # Flat format: collect unknown fields into data dict - for key, raw_value in data.items(): - if key not in known_fields: - if isinstance(raw_value, dict): - skipped_keys.append(key) - else: - extra_data[key] = raw_value + + # Always collect flat-format unknown fields into data dict + for key, raw_value in data.items(): + if key in known_fields: + continue + if isinstance(raw_value, dict): + skipped_keys.append(key) + else: + extra_data[key] = raw_value
🧹 Nitpick comments (4)
src/omnibase_core/models/common/model_value_container.py (1)
168-168: Remove redundant import statement.The
mathmodule is already imported at line 20. The import statement on line 168 is redundant.Apply this diff:
if isinstance(self.value, float): # Check for NaN and infinity - import math - if math.isnan(self.value) or math.isinf(self.value):src/omnibase_core/models/common/model_validation_issue.py (1)
25-26: Consider addingfrom_attributes=Trueto ConfigDict.Based on learnings,
ModelValidationIssueis likely nested in other Pydantic models (e.g., validation result containers). The coding guidelines state: "For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict."Other models in this PR (e.g.,
ModelOutputReference,ModelEnvelopePayload,ModelOperationData) consistently includefrom_attributes=True.- model_config = ConfigDict(extra="forbid", validate_assignment=True) + model_config = ConfigDict(extra="forbid", from_attributes=True, validate_assignment=True)tests/unit/scripts/test_regenerate_fingerprints.py (1)
725-728: Inconsistent model comparison pattern across test classes.The
__qualname__comparison workaround for pytest-xdist is correct here, but theTestAdditionalModelDetectionclass (lines 957-1054) still uses direct object identity comparisons (e.g.,assert model == ModelContractReducer). This inconsistency could cause test failures when running with pytest-xdist.Consider applying the same
__qualname__pattern consistently across all model detection tests, or extract a helper function:def assert_model_type(model: type | None, expected_name: str) -> None: """Assert model type using __qualname__ to handle pytest-xdist.""" assert model is not None assert model.__qualname__ == expected_namesrc/omnibase_core/models/core/model_mixin_metadata_collection.py (1)
195-219: Top-level list fields now validate more strictly; consider mirroring explicit list-type checksThe shift from
... or []to the*_val is not None else []pattern for:
requirescompatible_withincompatible_withusage_examplesimplementation_notesbrings these fields in line with the new “only
Nonedefaults to empty, anything else is validated” approach, which is good.Two small follow-ups you might consider to fully match the strictness used for
code_patterns.*andperformance.typical_use_cases:
- Add explicit list-type guards before passing to Pydantic
Right now, if a YAML author accidentally uses a scalar or mapping (e.g.,
requires: ""orrequires: {}), it will be handed straight toModelMixinMetadataand left to Pydantic to coerce/raise, which can lead to less targeted error messages. You could pre-validate these the same way you do for the nested list fields, e.g.:- # Create metadata - requires_val = mixin_data.get("requires") - compatible_val = mixin_data.get("compatible_with") - incompatible_val = mixin_data.get("incompatible_with") - usage_val = mixin_data.get("usage_examples") - impl_notes_val = mixin_data.get("implementation_notes") + # Top-level list fields with explicit None-handling and type checks + requires_val = mixin_data.get("requires") + if requires_val is not None and not isinstance(requires_val, list): + raise ModelOnexError( + message=( + f"requires for mixin '{key}' must be a list, " + f"got {type(requires_val).__name__}" + ), + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + ) + + compatible_val = mixin_data.get("compatible_with") + if compatible_val is not None and not isinstance(compatible_val, list): + raise ModelOnexError( + message=( + f"compatible_with for mixin '{key}' must be a list, " + f"got {type(compatible_val).__name__}" + ), + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + ) + + incompatible_val = mixin_data.get("incompatible_with") + if incompatible_val is not None and not isinstance( + incompatible_val, list + ): + raise ModelOnexError( + message=( + f"incompatible_with for mixin '{key}' must be a list, " + f"got {type(incompatible_val).__name__}" + ), + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + ) + + usage_val = mixin_data.get("usage_examples") + if usage_val is not None and not isinstance(usage_val, list): + raise ModelOnexError( + message=( + f"usage_examples for mixin '{key}' must be a list, " + f"got {type(usage_val).__name__}" + ), + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + ) + + impl_notes_val = mixin_data.get("implementation_notes") + if impl_notes_val is not None and not isinstance(impl_notes_val, list): + raise ModelOnexError( + message=( + f"implementation_notes for mixin '{key}' must be a list, " + f"got {type(impl_notes_val).__name__}" + ), + error_code=EnumCoreErrorCode.VALIDATION_ERROR, + ) @@ - category=mixin_data.get("category", "utility"), - requires=requires_val if requires_val is not None else [], - compatible_with=compatible_val - if compatible_val is not None - else [], - incompatible_with=incompatible_val - if incompatible_val is not None - else [], + category=mixin_data.get("category", "utility"), + requires=requires_val if requires_val is not None else [], + compatible_with=compatible_val if compatible_val is not None else [], + incompatible_with=( + incompatible_val if incompatible_val is not None else [] + ), @@ - config_schema=config_schema, - usage_examples=usage_val if usage_val is not None else [], + config_schema=config_schema, + usage_examples=usage_val if usage_val is not None else [], @@ - code_patterns=code_patterns, - implementation_notes=impl_notes_val - if impl_notes_val is not None - else [], + code_patterns=code_patterns, + implementation_notes=( + impl_notes_val if impl_notes_val is not None else [] + ),
- Confirm existing YAML doesn’t rely on non-list placeholders
Because this change tightens behavior (non-
Nonenon-lists will now be rejected rather than silently treated as empty), it’s worth double-checking that no currentmixin_metadata.yamlfiles use things likerequires: ""as a placeholder.You can rely on your existing tests for these models, or quickly scan the YAML files in the repo for such patterns.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
src/omnibase_core/models/common/model_envelope_payload.py(1 hunks)src/omnibase_core/models/common/model_flexible_value.py(1 hunks)src/omnibase_core/models/common/model_mixin_config_schema.py(3 hunks)src/omnibase_core/models/common/model_operation_data.py(4 hunks)src/omnibase_core/models/common/model_output_reference.py(1 hunks)src/omnibase_core/models/common/model_query_parameters.py(1 hunks)src/omnibase_core/models/common/model_schema_value.py(2 hunks)src/omnibase_core/models/common/model_validation_issue.py(2 hunks)src/omnibase_core/models/common/model_value_container.py(2 hunks)src/omnibase_core/models/core/model_generic_collection.py(1 hunks)src/omnibase_core/models/core/model_mixin_metadata_collection.py(4 hunks)src/omnibase_core/models/core/model_unified_hub_contract.py(1 hunks)src/omnibase_core/models/events/model_node_graph_ready_event.py(1 hunks)src/omnibase_core/models/utils/model_yaml_value.py(1 hunks)tests/unit/models/common/test_model_query_parameters.py(1 hunks)tests/unit/scripts/test_regenerate_fingerprints.py(7 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/omnibase_core/models/utils/model_yaml_value.py
- src/omnibase_core/models/common/model_schema_value.py
🚧 Files skipped from review as they are similar to previous changes (3)
- src/omnibase_core/models/common/model_query_parameters.py
- tests/unit/models/common/test_model_query_parameters.py
- src/omnibase_core/models/common/model_mixin_config_schema.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - usepoetry runfor all Python commands (pytest, mypy, black, isort, etc.), never use pip or direct python execution
Callsuper().__init__(container)in all node class constructors - this is MANDATORY and eliminates 80+ lines of boilerplate initialization code
Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Usecontainer.get_service("ProtocolName")for dependency injection - always use protocol names, never concrete class names or isinstance checks
Use @standard_error_handling decorator on methods to eliminate 6+ lines of try/catch boilerplate
Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Use FileRegistry for loading RuntimeHostContract YAML files - handles fail-fast error reporting with specific error codes (FILE_NOT_FOUND, CONFIGURATION_PARSE_ERROR, CONTRACT_VALIDATION_ERROR, etc.)
Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Use EnumNodeKind for architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation type (TRANSFORMER, AGGREGATOR, GATEWAY, etc.)
Format code usingpoetry run blackand sort imports usingpoetry run isortbefore committing
Lint code usingpoetry run ruff checkto detect issues and apply auto-fixes withpoetry run ruff check --fix
Files:
src/omnibase_core/models/core/model_generic_collection.pysrc/omnibase_core/models/common/model_value_container.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_flexible_value.pysrc/omnibase_core/models/common/model_validation_issue.pysrc/omnibase_core/models/core/model_mixin_metadata_collection.pysrc/omnibase_core/models/core/model_unified_hub_contract.pytests/unit/scripts/test_regenerate_fingerprints.pysrc/omnibase_core/models/common/model_output_reference.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve and maintain 100% mypy strict mode compliance with 0 errors across all source files
Achieve and maintain pyright basic mode compliance with 0 errors (targets Python 3.12)
For immutable value objects that are nested in other Pydantic models or used in parallel test execution, addfrom_attributes=Trueto ConfigDict
Use protocol-based dependency injection with container.get_service() - no isinstance checks with protocols (rely on duck typing instead)
Files:
src/omnibase_core/models/core/model_generic_collection.pysrc/omnibase_core/models/common/model_value_container.pysrc/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_node_graph_ready_event.pysrc/omnibase_core/models/common/model_operation_data.pysrc/omnibase_core/models/common/model_flexible_value.pysrc/omnibase_core/models/common/model_validation_issue.pysrc/omnibase_core/models/core/model_mixin_metadata_collection.pysrc/omnibase_core/models/core/model_unified_hub_contract.pysrc/omnibase_core/models/common/model_output_reference.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Maintain minimum 60% code coverage requirement for all tests
Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow, @pytest.mark.smoke, @pytest.mark.performance) to categorize tests
Run tests with CI performance expectations: average 2m58s per split across 20 parallel runners, with acceptable range 2m30s-3m30s and critical threshold >4m30s
Files:
tests/unit/scripts/test_regenerate_fingerprints.py
🧠 Learnings (4)
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/**/*.py : For immutable value objects that are nested in other Pydantic models or used in parallel test execution, add `from_attributes=True` to ConfigDict
Applied to files:
src/omnibase_core/models/common/model_value_container.pysrc/omnibase_core/models/common/model_validation_issue.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Use the correct container type: ModelONEXContainer for dependency injection in node constructors, ModelContainer[T] only for wrapping single values with metadata
Applied to files:
src/omnibase_core/models/common/model_value_container.pysrc/omnibase_core/models/events/model_node_graph_ready_event.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to **/*.py : Always raise structured errors using ModelOnexError with appropriate EnumCoreErrorCode, never use generic Exception
Applied to files:
src/omnibase_core/models/common/model_envelope_payload.pysrc/omnibase_core/models/events/model_node_graph_ready_event.py
📚 Learning: 2025-12-13T20:59:19.537Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T20:59:19.537Z
Learning: Applies to src/omnibase_core/mixins/**/*.py : Create reusable behavior mixins with accompanying mixin_metadata.yaml for discovery and capability matching
Applied to files:
src/omnibase_core/models/core/model_mixin_metadata_collection.py
🧬 Code graph analysis (6)
src/omnibase_core/models/common/model_envelope_payload.py (2)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)error_code(253-255)message(248-250)
src/omnibase_core/models/events/model_node_graph_ready_event.py (3)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/events/model_node_graph_info.py (1)
ModelNodeGraphInfo(15-45)src/omnibase_core/models/events/model_runtime_event_base.py (1)
ModelRuntimeEventBase(35-64)
src/omnibase_core/models/common/model_operation_data.py (4)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)message(248-250)error_code(253-255)src/omnibase_core/models/common/model_envelope_payload.py (1)
ModelEnvelopePayload(67-518)src/omnibase_core/models/common/model_query_parameters.py (1)
ModelQueryParameters(47-314)
src/omnibase_core/models/common/model_flexible_value.py (1)
src/omnibase_core/models/common/model_schema_value.py (1)
to_value(143-165)
src/omnibase_core/models/core/model_mixin_metadata_collection.py (1)
src/omnibase_core/models/core/model_mixin_metadata.py (1)
ModelMixinMetadata(22-119)
tests/unit/scripts/test_regenerate_fingerprints.py (2)
scripts/regenerate_fingerprints.py (2)
update_fingerprint_in_yaml(290-456)compute_fingerprint_for_contract(194-235)tests/unit/contracts/test_contract_v110_fields.py (7)
contract_data(91-93)contract_data(503-505)contract_data(639-641)contract_data(737-739)contract_data(898-900)contract_data(1086-1088)contract_data(1148-1150)
⏰ 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 (14)
src/omnibase_core/models/core/model_generic_collection.py (1)
192-197: LGTM! Refactor improves clarity and precision.The named inner function
_has_tagis clearer than the previous inline lambda. Additionally, usingif tags is not Noneinstead ofor []is more precise—it only replacesNonerather than treating all falsy values (like an empty list) as missing tags.src/omnibase_core/models/common/model_flexible_value.py (1)
260-263: Explicit None-handling for LIST values is correct and consistentUsing
(self.list_value if self.list_value is not None else [])keeps behavior equivalent to the previousor []for normal lists, but makes theNonevs empty-list distinction explicit and aligns with theModelSchemaValue.to_valuepattern. No issues here.src/omnibase_core/models/events/model_node_graph_ready_event.py (3)
1-24: LGTM!Clean module structure with appropriate imports. The imports for
ModelOnexErrorandEnumCoreErrorCodealign with the coding guidelines for structured error handling.
26-55: LGTM!Well-defined Pydantic model that properly inherits
from_attributes=TruefromModelRuntimeEventBase. Field constraints (ge=0) anddefault_factoryusage for mutable defaults are correct. As per coding guidelines, nested Pydantic models should havefrom_attributes=True, which is satisfied through inheritance.
73-105: LGTM!Well-designed factory method that intelligently derives
node_countfromlen(nodes)when not explicitly provided, preventing inconsistency errors. The keyword-only arguments and comprehensive docstring make the API clear and type-safe.src/omnibase_core/models/common/model_output_reference.py (1)
39-115: LGTM!Well-designed strongly-typed model with:
- Proper validation of the "node_id.output_name" format
- Sensible field constraints (min/max lengths)
- Clean property accessors using
split(".", 1)to correctly handle output names that may contain dots- Complete ConfigDict with
from_attributes=Trueper coding guidelinessrc/omnibase_core/models/core/model_unified_hub_contract.py (1)
175-183: LGTM!The change from
or []to explicitis not Nonechecks is a good improvement. The originalor []pattern would replace bothNoneand empty lists with[], potentially masking intentionally empty configurations. The explicit None check preserves empty lists as semantically meaningful values.src/omnibase_core/models/common/model_envelope_payload.py (1)
99-153: LGTM on the model configuration and validation.Good implementation:
ConfigDictwithextra="forbid",from_attributes=True,validate_assignment=Trueper coding guidelines- Security constants properly defined as
ClassVar_validate_data_sizecorrectly usesModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERROR- Returns
Selfas per Pydantic patternssrc/omnibase_core/models/common/model_operation_data.py (1)
27-161: LGTM!Excellent migration to strongly-typed models:
parametersfield now usesModelQueryParameterswith typed values and query string supportenvelope_payloadfield now usesModelEnvelopePayloadwith typed event fields- Proper
ConfigDictwithfrom_attributes=Trueper coding guidelines- Security constants and
_validate_sizesvalidator correctly implemented usingModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERROR- Returns
SelfconsistentlyThis addresses the past review feedback and aligns well with the PR objective of replacing
dict[str, str]patterns.tests/unit/scripts/test_regenerate_fingerprints.py (4)
455-462: LGTM!The update correctly adapts to the
FingerprintResultstructure by accessing.fingerprintattribute. The filtering logic properly handlesNonefingerprints, and thestr()conversion provides defensive handling for comparison.
698-700: LGTM! Guard checks properly address past review feedback.The assertions now correctly verify that both
version:andfingerprint:are present before comparing their positions, preventing false positives whenfind()returns-1.
1227-1232: LGTM!The test correctly validates the
FingerprintResultstructure, checking thatfingerprintisNoneanderrorcontains relevant messaging when schema validation fails.
1242-1250: LGTM!The conditional assertion logic appropriately handles the edge case where
ModelYamlContractmay be more permissive, ensuring that iffingerprintisNone, thenerrormust be set.src/omnibase_core/models/core/model_mixin_metadata_collection.py (1)
127-129: More precise None-handling for nested YAML lists is a solid improvementUsing explicit
is not Nonechecks forcode_patterns.methods,code_patterns.properties, andperformance.typical_use_caseswhile keeping theisinstance(..., list)guards means:
Noneis still treated as “no entries” and safely normalized to[].- Other falsey but invalid values (e.g., empty string,
0,{}) now trigger a clearModelOnexErrorinstead of being silently coerced.This tightens validation without surprising callers and should make misconfigured
mixin_metadata.yamlfiles fail fast with good error messages. No changes requested here.Also applies to: 140-144, 172-176
| model_config = ConfigDict(extra="forbid", validate_assignment=True) | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add from_attributes=True to align with immutable value object pattern.
Based on learnings, immutable value objects nested in other Pydantic models or used in parallel test execution should include from_attributes=True in their ConfigDict. ModelValueContainer fits this pattern as a generic value wrapper.
Apply this diff:
- model_config = ConfigDict(extra="forbid", validate_assignment=True)
+ model_config = ConfigDict(extra="forbid", validate_assignment=True, from_attributes=True)Based on learnings, this configuration is required for immutable value objects that are nested or used in parallel contexts.
🤖 Prompt for AI Agents
In src/omnibase_core/models/common/model_value_container.py around lines 43-44,
the ConfigDict for ModelValueContainer is missing from_attributes=True; update
the model_config to include from_attributes=True in addition to extra="forbid"
and validate_assignment=True so the immutable value-object behavior works
correctly when nested or used in parallel test contexts.
| def test_add_fingerprint_after_name_when_no_version(self) -> None: | ||
| """Test adding fingerprint after name field when no version exists.""" | ||
| content = """name: TestNoVersion | ||
| node_type: COMPUTE_GENERIC | ||
| description: No version field | ||
| """ | ||
| new_fp = "0.0.0:abcdef123456" | ||
|
|
||
| updated = update_fingerprint_in_yaml(content, new_fp) | ||
|
|
||
| # Fingerprint should be added after name | ||
| assert 'fingerprint: "0.0.0:abcdef123456"' in updated | ||
| name_pos = updated.find("name:") | ||
| fingerprint_pos = updated.find("fingerprint:") | ||
| assert name_pos != -1, "name: not found in output" | ||
| assert fingerprint_pos != -1, "fingerprint: not found in output" | ||
| assert fingerprint_pos > name_pos, "fingerprint should appear after name" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find and examine the update_fingerprint_in_yaml function
fd -t f "regenerate_fingerprints.py" --exclude testRepository: OmniNode-ai/omnibase_core
Length of output: 154
🏁 Script executed:
#!/bin/bash
# Search for the implementation of update_fingerprint_in_yaml
rg -n "def update_fingerprint_in_yaml" scripts/Repository: OmniNode-ai/omnibase_core
Length of output: 139
🏁 Script executed:
#!/bin/bash
# Get the full implementation to understand insertion priority
rg -n -A 80 "def update_fingerprint_in_yaml" scripts/regenerate_fingerprints.py | head -150Repository: OmniNode-ai/omnibase_core
Length of output: 3564
🏁 Script executed:
#!/bin/bash
# Continue reading the implementation to see the insertion priority for node_type and name
sed -n '370,450p' scripts/regenerate_fingerprints.pyRepository: OmniNode-ai/omnibase_core
Length of output: 3001
🏁 Script executed:
#!/bin/bash
# Get the rest of the function to confirm the complete priority order
sed -n '436,450p' scripts/regenerate_fingerprints.pyRepository: OmniNode-ai/omnibase_core
Length of output: 539
🏁 Script executed:
#!/bin/bash
# Verify the exact test content at lines 1165-1181
sed -n '1165,1181p' tests/unit/scripts/test_regenerate_fingerprints.pyRepository: OmniNode-ai/omnibase_core
Length of output: 843
Test name and comment mislead about actual insertion behavior.
The test is named test_add_fingerprint_after_name_when_no_version, but the implementation prioritizes node_type (step 7) before name (step 8). Since the test content includes both fields, the fingerprint will be inserted after node_type, not after name. The assertion assert fingerprint_pos > name_pos still passes because node_type appears after name in the YAML, making the test pass for the wrong reason.
To accurately test the name-only fallback, either:
- Remove the
node_typefield from test content to truly test step 8, or - Rename to
test_add_fingerprint_after_node_type_when_no_versionto reflect actual behavior
🤖 Prompt for AI Agents
tests/unit/scripts/test_regenerate_fingerprints.py around lines 1165-1181: the
test claims to verify insertion after name when no version exists, but the test
YAML includes node_type so the code inserts after node_type (step 7) and the
assertion passes for the wrong reason; to fix, remove the node_type line from
the test content so the YAML only contains name and description (ensuring the
fingerprint must be inserted after name), keep the same assertions verifying
fingerprint exists and appears after name, and update the test docstring/name if
you prefer the alternate approach of renaming to reflect current behavior
instead of changing the fixture.
…ed mapping - Fix ModelTypedMapping test to use correct serialization format - Add JSON Schema field mapping validator to ModelConfigSchemaProperty - Add flat-to-structured format converter for ModelMixinConfigSchema - Allow extra fields in config schema property for JSON Schema support Fixes 18 CI test failures in PR #186
|
@claude review this |
|
Claude finished @jonahgabriel's task in 3m 29s —— View job Code Review: PR #186 - Typed Pydantic Models✅ Review CompleteI've completed a comprehensive review of this PR implementing typed Pydantic models to replace SummaryScope: 98 files changed (+5,106 / -942 lines) across 28 commits 🎯 Key Strengths1. Excellent Type Safety ✅All 6 new models demonstrate strict typing with zero
Compliance: All models follow ONEX canonical patterns per CLAUDE.md:
2. Robust Security Hardening ✅DoS prevention across all models:
3. Comprehensive Test Coverage ✅22 new type validation tests in
Example test excellence: def test_float_default_with_integer_type_raises_error(self) -> None:
"""PR #180 Requirement: default=3.5 with type="integer" raises error."""
with pytest.raises(ModelOnexError) as exc_info:
ModelConfigSchemaProperty(type="integer", default=3.5)
assert exc_info.value.error_code == EnumCoreErrorCode.PARAMETER_TYPE_MISMATCH4. Critical Fixes Verified ✅FSM Expression Fix (contracts/runtime/node_graph_reducer.yaml:112): # BEFORE: expression: node_registry is_not_empty # Non-standard
# AFTER: expression: node_registry min_length 1 # Standard FSM operator✅ This aligns with ONEX FSM operator conventions and prevents runtime FSM evaluation errors. Fingerprint Consistency: 80 new tests for fingerprint regeneration script ensure contract integrity after model changes. 5. Production-Ready Documentation ✅All models include:
Example (ModelOutputReference): """Reference to an output from another graph node.
The source_reference follows the format: "node_id.output_name"
- node_id: The unique identifier of the source node
- output_name: The name of the output field from that node
Security:
- source_reference has max_length=512 to prevent memory exhaustion
- Format validation prevents injection of malformed references
"""🔍 Detailed Model AnalysisModelGraphNodeParameter & ModelGraphNodeParametersPurpose: Replace Key Features:
Design Decision ✅: The type inference ordering (bool → str → int → float → list → dict) is correct and prevents ModelOutputReference & ModelOutputMappingPurpose: Type-safe data flow between graph nodes Key Features:
Security ✅: max_length constraints prevent unbounded memory allocation. ModelQueryParametersPurpose: Type-safe query parameters for effect operations Key Features:
Security ✅: MAX_PARAMETERS=100 prevents DoS via parameter flooding (src/omnibase_core/models/common/model_query_parameters.py:75-92). Design Excellence: Boolean string parsing ( ModelEnvelopePayloadPurpose: Type-safe event payload for event-driven processing Key Features:
Security ✅: MAX_DATA_ENTRIES=100, MAX_FIELD_LENGTH=512 (src/omnibase_core/models/common/model_envelope_payload.py:104-105). Documentation Excellence ✅: Explicit warnings about non-round-trip compatibility of 🎨 Architectural AlignmentONEX Compliance ✅All models follow canonical ONEX patterns per CLAUDE.md:
Model Count Verification ✅CLAUDE.md states "29 total models using Verified in this PR: 14 models in
✅ All 6 new models correctly use 🚀 Performance ConsiderationsEfficient Lookups ✅
Memory Safety ✅
Immutability Patterns ✅
📊 Testing AssessmentCoverage Excellence ✅22 type validation tests across 8 test classes:
Test Quality ✅All tests follow best practices:
CI Status ✅
🔒 Security ReviewDoS Prevention ✅
Injection Prevention ✅
Input Validation ✅
💡 Notable Design Decisions1. Timestamp as String (Not datetime) ✅Rationale (ModelEnvelopePayload docstring lines 12-30):
✅ Excellent design choice - separates transport concerns from runtime processing. 2. Reserved Key Prefixing ✅Rationale (ModelEnvelopePayload lines 367-405):
✅ Defensive programming - prevents subtle bugs from key collisions. 3. Non-Round-Trip Serialization ✅Rationale (ModelEnvelopePayload/ModelQueryParameters):
✅ Transparent design - prioritizes HTTP compatibility over round-trip guarantee, with clear documentation. 4. Type Inference Ordering ✅Rationale (ModelGraphNodeParameters.from_dict() line 119): # Check bool before int since bool is a subclass of int in Python
if isinstance(value, bool):
param_type = "boolean"
elif isinstance(value, int):
param_type = "integer"✅ Correct handling of Python's 🎯 Breaking Changes Analysis
|
Summary
Addresses all feedback from PR #180 review by creating proper typed Pydantic models instead of
dict[str, str]patterns.🔴 CRITICAL Fixes
node_graph_reducer.yaml(is_not_empty→min_length 1).fingerprintattribute🆕 New Typed Models (6 files)
ModelGraphNodeParameterModelGraphNodeParametersfrom_dict()/to_dict()helpersModelOutputReference"node_id.output_name"formatModelOutputMappingModelQueryParametersParameterValueunion typeModelEnvelopePayloadWhy Typed Models > dict[str, str]
🟠 MAJOR Fixes
from_attributes=Trueto ConfigDict in 3 modelsModelConfigSchemaPropertyupdate_fingerprint_in_yaml()for dict-style versionsvprefix) in example contractmodel_graph_node_inputs.pyto use typed modelsmodel_operation_data.pyto use typed modelsmodel_typed_metadata.pyto use typed modelspartition_id/window_idto UUID type🟡 MINOR Fixes
Test Plan
Notes
model_typed_metadata.pysingle-class-per-file violation is pre-existing (16 classes existed before this PR) - tracked separately for future refactorSummary by CodeRabbit
New Features
Documentation
Refactor
Tests
✏️ Tip: You can customize this high-level summary in your review settings.