Repository navigation
feat(nodes): implement state serialization for NodeReducer and NodeOrchestrator [OMN-739, OMN-740] - #190
Conversation
…chestrator [OMN-739, OMN-740] Add state snapshot/restore capabilities for workflow replay and debugging: NodeReducer (OMN-739): - Add snapshot_state() -> ModelFSMStateSnapshot | None - Add restore_state(snapshot: ModelFSMStateSnapshot) -> None - Add get_state_snapshot() -> dict[str, object] NodeOrchestrator (OMN-740): - Create ModelWorkflowStateSnapshot model for workflow state - Add snapshot_workflow_state() -> ModelWorkflowStateSnapshot | None - Add restore_workflow_state(snapshot: ModelWorkflowStateSnapshot) -> None - Add get_workflow_snapshot() -> dict[str, object] Infrastructure: - Add _workflow_state tracking to MixinWorkflowExecution - Export ModelWorkflowStateSnapshot from models/workflow/execution/ - Add WorkflowContextType type alias for flexible context Testing: - Add 28 unit tests for state serialization - All 147 node tests pass
WalkthroughAdds immutable workflow snapshot model and WorkflowContextType; tracks workflow state in the mixin; exposes snapshot/restore and JSON-serializable APIs on NodeOrchestrator and NodeReducer; adds PII sanitization, validation, and comprehensive unit tests; many model timestamps changed to timezone-aware UTC; docstrings expanded with thread-safety notes. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client / Test harness
participant NO as NodeOrchestrator
participant NR as NodeReducer
participant Store as Persistence
rect rgb(220,235,255)
note over Client,NO: Create snapshots
Client->>NO: snapshot_workflow_state()
NO-->>Client: ModelWorkflowStateSnapshot | None
Client->>NR: snapshot_state()
NR-->>Client: ModelFSMStateSnapshot | None
Client->>Store: save(serialized snapshots)
Store-->>Client: ack
end
rect rgb(235,255,235)
note over Client,NO: Restore flow
Client->>Store: load(serialized snapshots)
Store-->>Client: snapshots
Client->>NO: restore_workflow_state(snapshot)
NO->>NO: _validate_workflow_snapshot(snapshot)
alt valid
NO-->>NO: set internal _workflow_state
else invalid
NO-->>Client: error
end
Client->>NR: restore_state(fsm_snapshot)
NR->>NR: _validate_fsm_snapshot(snapshot, contract)
alt valid
NR-->>NR: replace FSM state
else invalid
NR-->>Client: error
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Code Review: State Serialization for NodeReducer and NodeOrchestratorThank you for this well-structured PR implementing state serialization! Overall, this is high-quality code with excellent documentation. Here's my detailed review: ✅ Strengths1. Excellent Documentation
2. Strong Type Safety
3. Comprehensive Test Coverage
4. Follows ONEX Architecture Patterns
5. Immutability Patterns
|
Code Review: State Serialization for NodeReducer and NodeOrchestratorThank you for this comprehensive implementation! This PR adds critical state serialization capabilities for workflow replay and recovery. Overall, the implementation is well-structured and follows ONEX patterns closely. Here's my detailed feedback: ✅ Strengths1. Excellent Documentation
2. Strong Test Coverage
3. Follows ONEX Patterns
4. Practical Design
|
| Priority | Issue | Action |
|---|---|---|
| P0 (Critical) | datetime.utcnow() deprecated |
Replace with datetime.now(timezone.utc) |
| P1 (High) | Thread safety | Document non-thread-safe behavior |
| P1 (High) | Return type mismatch | Change dict[str, object] → dict[str, Any] |
| P2 (Medium) | from_attributes=True |
Document why it's needed or remove |
| P3 (Low) | Performance docs | Note model_dump() overhead |
| P3 (Low) | State serialization guide | Add user-facing documentation |
✅ Approval Recommendation
This PR is nearly ready to merge pending the critical datetime fix. The implementation is solid, well-tested, and follows ONEX patterns. Great work!
Next Steps:
- Fix
datetime.utcnow()→datetime.now(timezone.utc) - Add thread safety documentation to restoration methods
- Fix return type annotations
- Wait for CI to pass
- Merge!
Reviewed by: Claude Sonnet 4.5 (AI Code Review)
CLAUDE.md Version: 0.4.0
Review Date: 2025-12-14
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/omnibase_core/mixins/mixin_workflow_execution.py(3 hunks)src/omnibase_core/models/workflow/execution/__init__.py(2 hunks)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(2 hunks)src/omnibase_core/nodes/node_reducer.py(2 hunks)src/omnibase_core/types/type_workflow_context.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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/workflow/execution/__init__.pysrc/omnibase_core/mixins/mixin_workflow_execution.pytests/unit/nodes/test_node_state_serialization.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/types/type_workflow_context.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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/workflow/execution/__init__.pysrc/omnibase_core/mixins/mixin_workflow_execution.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/types/type_workflow_context.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.py
src/omnibase_core/mixins/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Create reusable behavior mixins with accompanying mixin_metadata.yaml for discovery and capability matching
Files:
src/omnibase_core/mixins/mixin_workflow_execution.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/nodes/test_node_state_serialization.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.py
🧠 Learnings (2)
📚 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/mixins/mixin_workflow_execution.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/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/nodes/node_orchestrator.py
🧬 Code graph analysis (3)
src/omnibase_core/models/workflow/execution/__init__.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-222)
src/omnibase_core/mixins/mixin_workflow_execution.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-222)
src/omnibase_core/nodes/node_orchestrator.py (3)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-222)src/omnibase_core/models/core/model_node_metadata_block.py (1)
model_dump(323-326)src/omnibase_core/protocols/types/protocol_serializable.py (1)
model_dump(23-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Code Quality
- GitHub Check: claude-review
🔇 Additional comments (7)
src/omnibase_core/types/type_workflow_context.py (1)
1-25: LGTM! Well-documented type alias.The WorkflowContextType alias is appropriately placed in the /types/ directory with clear documentation explaining the intentional use of
dict[str, Any]for workflow flexibility. The design decision is well-justified for varying workflow implementations.src/omnibase_core/mixins/mixin_workflow_execution.py (1)
21-23: LGTM! Clean state tracking integration.The addition of
_workflow_stateattribute with proper typing and initialization cleanly integrates workflow state tracking into the mixin. The documentation updates accurately reflect the new stateful behavior.Also applies to: 54-56, 65-66
src/omnibase_core/models/workflow/execution/__init__.py (1)
12-12: LGTM! Correct public API exposure.The ModelWorkflowStateSnapshot import and export properly exposes the new model to the workflow/execution module public API.
Also applies to: 21-21
tests/unit/nodes/test_node_state_serialization.py (1)
1-762: LGTM! Comprehensive test coverage.Excellent test suite with 28 tests covering:
- FSM and workflow state snapshot/restoration
- Error handling (e.g., restore without loaded contract)
- Round-trip serialization maintaining integrity
- Immutability enforcement
- Context merging behavior
- JSON serialization with proper UUID/datetime handling
The tests are well-structured and validate all critical paths for the new state serialization features.
src/omnibase_core/nodes/node_reducer.py (1)
339-436: LGTM! Clean FSM state serialization API.The three new methods provide a well-designed API for FSM state observability:
snapshot_state(): Non-mutating read-only access to current staterestore_state(): Properly validates FSM contract is loaded before restorationget_state_snapshot(): Convenient JSON-serializable dictionary representationDocumentation is thorough with clear examples for each method.
src/omnibase_core/nodes/node_orchestrator.py (1)
411-502: LGTM! Well-designed workflow state serialization API.The three new methods provide consistent workflow state management:
snapshot_workflow_state(): Returns immutable snapshot for serializationrestore_workflow_state(): Restores state from snapshot with clear immutability notesget_workflow_snapshot(): JSON-serializable dictionary representationThe API mirrors NodeReducer's FSM state methods, providing a consistent experience. Documentation includes practical examples for each method.
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
37-222: Excellent immutable snapshot design with clear contracts.The ModelWorkflowStateSnapshot implementation is well-designed:
- Proper use of
frozen=Truefor immutability enforcementfrom_attributes=Truecorrectly included for parallel test usage (per guidelines)- Thorough documentation explaining immutability contracts and limitations
- Clean factory and transition methods (
create_initial,with_step_completed,with_step_failed)- Proper list/dict copying to avoid aliasing in transition methods
- Context merging logic is correct
The comprehensive warnings about mutable containers (lists, dicts) and proper usage patterns demonstrate thoughtful API design.
| created_at: datetime = Field( | ||
| default_factory=datetime.utcnow, description="Snapshot creation time" | ||
| ) |
There was a problem hiding this comment.
Use datetime.now(timezone.utc) instead of deprecated datetime.utcnow().
The datetime.utcnow() method is deprecated in Python 3.12+. Use datetime.now(timezone.utc) for timezone-aware UTC timestamps.
Apply this diff:
+from datetime import datetime, timezone
-from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, ConfigDict, FieldAnd update the field:
created_at: datetime = Field(
- default_factory=datetime.utcnow, description="Snapshot creation time"
+ default_factory=lambda: datetime.now(timezone.utc), description="Snapshot creation time"
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| created_at: datetime = Field( | |
| default_factory=datetime.utcnow, description="Snapshot creation time" | |
| ) | |
| from datetime import datetime, timezone | |
| from uuid import UUID | |
| from pydantic import BaseModel, ConfigDict, Field | |
| # ... other code ... | |
| created_at: datetime = Field( | |
| default_factory=lambda: datetime.now(timezone.utc), description="Snapshot creation time" | |
| ) |
🤖 Prompt for AI Agents
In src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py
around lines 115 to 117, replace the deprecated default_factory=datetime.utcnow
with a timezone-aware factory: use a callable that returns
datetime.now(timezone.utc) and ensure timezone is imported from datetime; update
the Field default_factory to that callable so the created_at value is
timezone-aware UTC.
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc) in mixin_metadata.yaml - Add thread safety documentation to 7 state serialization models Models updated with thread safety docs: - ModelWorkflowStateSnapshot (frozen=True, thread-safe for reads) - ModelFSMStateSnapshot (frozen=True, thread-safe for reads) - ModelNodeState (dataclass, NOT thread-safe) - ModelState (mutable, NOT thread-safe) - ModelCanonicalStateBase (mutable, NOT thread-safe) - ModelFsmState (frozen=True with deep immutability, thread-safe) - ModelStateUpdate (mutable, NOT thread-safe) Note: dict[str, object] return types remain unchanged as this is the codebase standard (dict[str, Any] is an anti-pattern per pre-commit hooks). Related tickets: - Beta: OMN-798, OMN-799, OMN-800 - Production: OMN-801, OMN-802, OMN-803, OMN-804
Pull Request Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization methods for 🟢 StrengthsArchitecture & Design
Test Coverage
Code Quality
🔴 Critical Issues1. Deprecated
|
Change get_state_snapshot() and get_workflow_snapshot() to return the
actual Pydantic models instead of untyped dictionaries:
- NodeReducer.get_state_snapshot() now returns ModelFSMStateSnapshot | None
- NodeOrchestrator.get_workflow_snapshot() now returns ModelWorkflowStateSnapshot | None
This follows ONEX conventions of using strongly-typed models everywhere
rather than untyped dict[str, object] or dict[str, Any] patterns.
Callers who need JSON serialization can call model.model_dump() directly.
Breaking Change:
- Return type changed from dict[str, object] to Model | None
- Empty dict {} return is now None for uninitialized state
Code Review - PR #190: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization methods for ✅ Strengths1. Excellent Code Quality
2. Outstanding Documentation
3. Comprehensive Test Coverage
4. Follows ONEX Patterns
|
…alternative - Change default_factory from datetime.utcnow to lambda: datetime.now(timezone.utc) - Add from_attributes=True justification comment for pytest-xdist compatibility - Addresses PR #190 review feedback
Code Review: State Serialization for NodeReducer and NodeOrchestratorOverviewThis PR implements state serialization methods for ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Testing
3. Documentation Quality
4. Deprecation Fixes
🔍 Code Quality ObservationsModelWorkflowStateSnapshot DesignGood:
Potential Concern: # Lines 125-137 - these fields are mutable despite frozen=True
completed_step_ids: list[UUID] = Field(default_factory=list)
failed_step_ids: list[UUID] = Field(default_factory=list)
context: WorkflowContextType = Field(default_factory=dict)Question: Have you considered using completed_step_ids: tuple[UUID, ...] = Field(default_factory=tuple)
failed_step_ids: tuple[UUID, ...] = Field(default_factory=tuple)This would:
The trade-off is slightly more verbose update code in completed_step_ids=(*self.completed_step_ids, step_id) # tuple
# vs
completed_step_ids=[*self.completed_step_ids, step_id] # list (current)Unused Import (Minor)File: from datetime import UTC, datetime, timezoneThe 🧪 Testing ConsiderationsCoverage AnalysisThe 28 tests provide excellent coverage of the API surface. One area to consider: Concurrent Access Testing (Optional Enhancement): @pytest.mark.asyncio
async def test_snapshot_thread_safety(self) -> None:
"""Verify snapshot is safe for concurrent reads."""
snapshot = ModelWorkflowStateSnapshot.create_initial(uuid4())
async def read_snapshot():
return snapshot.current_step_index
# Multiple concurrent reads should succeed
results = await asyncio.gather(*[read_snapshot() for _ in range(100)])
assert all(r == 0 for r in results)This would catch regression if someone later makes the model mutable. Integration Testing (Future)The unit tests are comprehensive. For future work, consider integration tests that verify:
🔒 Security & PerformanceSecurity✅ No security concerns identified
Performance✅ Efficient implementation
Potential Optimization (Low Priority): # Only copy if new_context provided
updated_context = self.context if new_context is None else {**self.context, **new_context}Current implementation always copies: updated_context = (
{**self.context} if new_context is None else {**self.context, **new_context}
)📋 Alignment with CLAUDE.md✅ Excellent Adherence
✅ Pydantic Patterns
🎯 RecommendationsPriority 1 (Before Merge):
Priority 2 (Consider for This PR):
Priority 3 (Future Enhancements):
🎉 SummaryThis is high-quality work that demonstrates strong understanding of:
The state serialization API is clean, well-documented, and follows established conventions. The only significant consideration is whether to enforce true immutability with tuples instead of relying on documented contracts with mutable lists. Recommendation: ✅ Approve with minor fixes (remove unused import, verify CI passes) The tuple vs. list question is worth discussing but shouldn't block this PR given the excellent documentation of the immutability contract. Great work @jonahgabriel! The foundation for workflow replay and debugging is solid. 🚀 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/omnibase_core/nodes/node_orchestrator.py (1)
23-25: Prefer importingModelWorkflowStateSnapshotfrom the public package export (reduce import churn).Since
omnibase_core.models.workflow.executionnow exportsModelWorkflowStateSnapshot, consider importing from the package to stabilize import paths.-from omnibase_core.models.workflow.execution.model_workflow_state_snapshot import ( - ModelWorkflowStateSnapshot, -) +from omnibase_core.models.workflow.execution import ModelWorkflowStateSnapshotsrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
123-138: Add basic field constraints (e.g., non-negativecurrent_step_index) to prevent invalid snapshots.Right now
current_step_indexcan be negative (and there’s no validation for that). Consider addingge=0:- current_step_index: int = Field(default=0, description="Current step index") + current_step_index: int = Field( + default=0, + ge=0, + description="Current step index", + )
169-244: Good container-copying in transition helpers; consider stronger immutability for inner containers if you want enforcement (not just convention).
with_step_completed()/with_step_failed()correctly avoid list aliasing and merge context cleanly. If you ever want to enforce immutability rather than document it, consider switching the public types to immutable containers (e.g.,tuple[UUID, ...]for step IDs) and/or wrappingcontextto avoid accidental mutation (acknowledging this may be more friction with Pydantic/JSON).tests/unit/nodes/test_node_state_serialization.py (1)
1-12: Drop the test-file shebang to avoid implying direct execution outside Poetry.Since tests should be executed via
poetry run pytest, the shebang is noise and can mislead.-#!/usr/bin/env python3 """Unit tests for node state serialization methods [OMN-739, OMN-740].
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(2 hunks)src/omnibase_core/nodes/node_reducer.py(2 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.pytests/unit/nodes/test_node_state_serialization.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/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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/nodes/test_node_state_serialization.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/nodes/node_orchestrator.py
🧬 Code graph analysis (3)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
create_initial(134-158)
src/omnibase_core/nodes/node_reducer.py (3)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)src/omnibase_core/models/errors/model_onex_error.py (3)
ModelOnexError(49-354)message(248-250)error_code(253-255)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
src/omnibase_core/nodes/node_orchestrator.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-243)
⏰ 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 18/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: claude-review
- Differentiate snapshot_workflow_state() (returns model) from get_workflow_snapshot() (returns dict) in NodeOrchestrator - Add FSM state validation in NodeReducer.restore_state() to prevent impossible state injection against loaded contract - Add ge=0 constraint to current_step_index in ModelWorkflowStateSnapshot - Remove unregistered @pytest.mark.timeout(60) markers (4 occurrences) - Remove test file shebang to avoid implying direct execution - Add 7 new validation tests for FSM snapshot restoration
Code Review: State Serialization for NodeReducer and NodeOrchestratorOverviewThis PR implements state serialization capabilities for ✅ Strengths1. Excellent Architecture & Design
2. Robust Validation
3. Comprehensive Testing
4. Documentation Quality
5. ONEX Convention Compliance
🔍 Areas for Consideration1. Timezone Handling (Minor)Location: created_at: datetime = Field(
default_factory=lambda: datetime.now(UTC),
description="Snapshot creation time",
)Observation: Good use of Recommendation: ✅ No action needed - already correct. 2. Immutability Contract vs. Python Reality (Documentation)Location: The extensive documentation about immutability limitations is excellent and honest about Python/Pydantic constraints. However, there's a potential runtime enforcement gap: Current: Relies on documentation and developer discipline to not mutate Potential Enhancement (future consideration, not blocking): from typing import Sequence, Mapping
# More immutable type hints (runtime behavior unchanged, but signals intent)
completed_step_ids: Sequence[UUID] = Field(...)
failed_step_ids: Sequence[UUID] = Field(...)
context: Mapping[str, Any] = Field(...)Or deeper immutability: from pydantic import field_validator
@field_validator('completed_step_ids', 'failed_step_ids')
@classmethod
def freeze_lists(cls, v: list[UUID]) -> tuple[UUID, ...]:
return tuple(v)Recommendation: 📝 Current documentation-based approach is acceptable. Consider for v0.5.0 if mutation bugs arise in practice. 3. Validation Timing (Minor Optimization)Location: def restore_state(self, snapshot: ModelFSMStateSnapshot) -> None:
if not self.fsm_contract:
raise ModelOnexError(...)
# Validate snapshot against contract before restoring
self._validate_fsm_snapshot(snapshot, self.fsm_contract)
self._fsm_state = snapshotObservation: Validation is comprehensive but runs on every restore. For high-frequency snapshot/restore scenarios, this could be a bottleneck. Potential Optimization (not critical): # Add optional validation flag for trusted snapshots
def restore_state(
self,
snapshot: ModelFSMStateSnapshot,
validate: bool = True
) -> None:
if not self.fsm_contract:
raise ModelOnexError(...)
if validate:
self._validate_fsm_snapshot(snapshot, self.fsm_contract)
self._fsm_state = snapshotRecommendation: ⏭️ Defer until performance profiling shows this is a bottleneck. Prefer safety over optimization for v0.4.0. 4. Missing Workflow State ValidationLocation: Critical Observation: def restore_workflow_state(self, snapshot: ModelWorkflowStateSnapshot) -> None:
"""Restore workflow state from snapshot."""
self._workflow_state = snapshot # ⚠️ No validation!Potential Issues:
Recommendation: 🔴 HIGH PRIORITY - Add validation similar to def _validate_workflow_snapshot(
self,
snapshot: ModelWorkflowStateSnapshot,
definition: ModelWorkflowDefinition,
) -> None:
"""Validate workflow snapshot against workflow definition."""
# Get valid step IDs from workflow definition
valid_step_ids = {node.node_id for node in definition.execution_graph.nodes}
# Validate completed step IDs
for step_id in snapshot.completed_step_ids:
if step_id not in valid_step_ids:
raise ModelOnexError(
message=f"Invalid workflow snapshot: completed step {step_id} not in workflow",
error_code=EnumCoreErrorCode.INVALID_STATE,
context={...}
)
# Validate failed step IDs
for step_id in snapshot.failed_step_ids:
if step_id not in valid_step_ids:
raise ModelOnexError(...)
# Validate step index bounds
total_steps = len(definition.execution_graph.nodes)
if snapshot.current_step_index > total_steps:
raise ModelOnexError(...)
def restore_workflow_state(self, snapshot: ModelWorkflowStateSnapshot) -> None:
if not self.workflow_definition:
raise ModelOnexError(
message="Workflow definition not loaded",
error_code=EnumCoreErrorCode.VALIDATION_ERROR,
)
self._validate_workflow_snapshot(snapshot, self.workflow_definition)
self._workflow_state = snapshotImpact: Without this validation, corrupted or mismatched workflow state could cause hard-to-debug runtime errors during workflow execution. 5. Test Coverage for Workflow ValidationLocation: Observation: Tests for
Missing: Equivalent validation tests for Recommendation: 📝 Add tests once workflow validation is implemented: def test_restore_workflow_state_rejects_invalid_step_ids(...)
def test_restore_workflow_state_rejects_out_of_bounds_step_index(...)
def test_restore_workflow_state_raises_without_definition(...)6. WorkflowContextType Design (Acceptable Trade-off)Location: WorkflowContextType = dict[str, Any]Observation: Uses
Recommendation: ✅ Current approach is acceptable. This is a legitimate use case for 7. Mixin State Tracking PatternLocation: class MixinWorkflowExecution:
_workflow_state: ModelWorkflowStateSnapshot | None
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._workflow_state = NoneObservation: The mixin now tracks state, changing from "stateless" to stateful (docstring updated correctly in commit Consideration: Should Question for Author:
Recommendation: 📝 Clarify the state update contract in documentation or add integration tests showing state evolution during workflow execution. 🔒 Security Considerations✅ No Security Issues Found
🎯 Performance Considerations✅ Generally Efficient
Potential Optimization (not critical): If workflows have 1000+ steps, the 📊 Code Quality Metrics
🎓 Best Practices Demonstrated
📋 Recommendations Summary🔴 Must Address Before Merge
📝 Should Consider
⏭️ Future Enhancements (v0.5.0+)
✅ Final VerdictAPPROVE with requested changes ✨ This is high-quality work that follows ONEX conventions and provides a solid foundation for workflow state persistence. The main gap is the missing validation in Estimated effort to address: ~30 minutes
Great work on this feature! The state serialization capabilities will be invaluable for debugging and workflow recovery. 🚀 Review performed by Claude Code on PR #190 |
…alidation - Replace all datetime.utcnow() with datetime.now(UTC) in 33 model files - Add workflow snapshot validation to NodeOrchestrator.restore_workflow_state() - Fix NodeReducer.get_state_snapshot() to return dict[str, object] for consistency - Add 6 new tests for workflow snapshot validation - Update existing tests for dict return type Addresses: datetime.utcnow() deprecation (Python 3.12+), API consistency, workflow state validation parity with FSM validation.
Code Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization methods for ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Test Coverage
3. Thread Safety Documentation
4. UTC Datetime Migration
|
…limits - Add ModelWorkflowStateSnapshot to public workflow module exports - Document recommended context dict size limits (100 keys, 1MB, 5 levels)
Code Review - PR #190: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization methods for ✅ Strengths1. Excellent Architecture & Design
2. Documentation Quality
3. Test Coverage
4. Consistency with Project Standards
|
…limits - Use public package exports for ModelWorkflowStateSnapshot and ModelFSMStateSnapshot - Add schema_version field with ge=1 constraint for versioned deserialization - Add PII handling documentation and schema versioning strategy docs - Clean up unused timezone import (only UTC needed) - Update imports in 6 files to use stable public API paths Files changed: - model_workflow_state_snapshot.py: schema versioning, PII docs - node_orchestrator.py, node_reducer.py: public imports - mixin_workflow_execution.py, fsm_executor.py: public imports - test files: public imports for snapshot models
Pull Request Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization methods for ✅ Strengths1. Excellent Type Safety
2. Robust ValidationThe
3. Comprehensive Test Coverage28 new tests covering:
4. Documentation Quality
5. Architecture Alignment
6. Timezone Awareness ImprovementsExcellent cleanup replacing
|
| Category | Tests | Status |
|---|---|---|
| NodeReducer snapshot/restore | 14 | ✅ |
| NodeOrchestrator snapshot/restore | 13 | ✅ |
| Helper methods | 8 | ✅ |
| Edge cases (validation) | 6 | ✅ |
| Round-trip integrity | 2 | ✅ |
Missing: Integration tests for state capture during live workflow execution (see #4 above).
🔧 Performance Considerations
Positive:
snapshot_state()is O(1) - just returns reference ✅- No deep copying unless serializing to dict ✅
Watch:
contextdict inModelWorkflowStateSnapshothas no size limit- Documented recommendation: max 100 keys, 1MB serialized ✅
- Consider adding Pydantic validator for production safety
🛡️ Security Considerations
Excellent PII Warnings:
ModelWorkflowStateSnapshot includes comprehensive PII handling guidance:
- Never log context at INFO level ✅
- Sanitize before external storage ✅
- Encrypt at rest if PII possible ✅
Recommendation: Consider adding @field_serializer to redact sensitive context keys during logging:
@field_serializer("context")
def _sanitize_context_for_logging(self, value: dict[str, Any]) -> dict[str, Any]:
"""Redact sensitive keys from context during serialization."""
SENSITIVE_KEYS = {"password", "api_key", "token", "secret"}
return {
k: "[REDACTED]" if k.lower() in SENSITIVE_KEYS else v
for k, v in value.items()
}📝 Documentation Quality
Strengths:
- ✅ Comprehensive docstrings with examples
- ✅ Clear type annotations
- ✅ Thread safety warnings
- ✅ Immutability contract documented
- ✅ Schema versioning strategy explained
Suggestions:
- Add migration guide for schema version upgrades
- Document rollback strategy if
restore_state()fails mid-workflow
🎯 Final Verdict
Overall: STRONG APPROVAL with Minor Improvements Recommended ⭐⭐⭐⭐
Rationale:
- Excellent architecture, type safety, and test coverage
- Solid validation and error handling
- Minor issues with immutability enforcement and workflow state tracking
- No blocking bugs - issues are design improvements
Recommended Actions:
Before Merge (Optional but Recommended):
- Fix immutability contract violation in
ModelWorkflowStateSnapshot(use tuples) OR document as known limitation - Add validation for step_ids overlap in
_validate_workflow_snapshot() - Verify
_workflow_stateis populated during workflow execution (add integration test or documentation)
Post-Merge (Can be follow-up PRs):
4. Add schema version constant
5. Enhance error messages with actual timestamps
6. Add PII sanitization for context field
📚 References
- ONEX Architecture:
docs/architecture/ONEX_FOUR_NODE_ARCHITECTURE.md - Thread Safety Guide:
docs/guides/THREADING.md - Immutability Reference:
src/omnibase_core/models/fsm/model_fsm_state.py:38-77 - Error Handling:
docs/conventions/ERROR_HANDLING_BEST_PRACTICES.md
Great work on this implementation! The state serialization architecture is well-designed and will enable powerful debugging and persistence capabilities for ONEX workflows. 🚀
Correlation ID: review-omn-739-740-20251215
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/omnibase_core/models/security/model_signature_chain.py (2)
272-287: Fix type mismatch incalculate_trust_score
signature.node_idis defined asUUID(seemodel_nodesignature.pyline 36), butcalculate_trust_scoreacceptstrusted_nodes: set[str]and compares them directly:if signature.node_id in trusted_nodes: # UUID compared against set[str]This membership test will always fail silently because UUID objects will never match strings. The trust score will always be lower than intended.
For consistency with related methods:
get_signature_by_node(node_id: UUID)expects UUIDget_unique_signers()returnsset[str]by stringifying node IDsget_routing_path()stringifies node IDsChange the parameter type to
set[UUID]to match the internal UUID type and other query methods:def calculate_trust_score(self, trusted_nodes: set[UUID]) -> float:Or, if callers pass
set[str], normalize in the comparison:if str(signature.node_id) in trusted_nodes:
293-311: Fix runtime bug:list[Any](algorithms)is not callableLine 303 calls
list[Any](algorithms), butlist[Any]is a typing generic alias that cannot be invoked at runtime. This will raiseTypeError: 'types.GenericAlias' object is not callablewhenget_chain_summary()is executed.Change to:
"algorithms": list(algorithms),Note: Multiple similar instances exist elsewhere in the codebase (
list[Any](...)pattern) and should also be corrected.src/omnibase_core/models/core/model_instance_metadata.py (1)
73-117: MakeModelInstanceMetadataimmutable to match the codebase pattern.
ModelInstanceMetadatais nested inModelNodeInstanceand must be immutable. Update methods (add_label,add_annotation,add_tag) to return new instances viamodel_copy()instead of mutating in-place, following the pattern used inModelOnexSecurityContext.add_role(). AddConfigDict(frozen=True, from_attributes=True)to enforce immutability.
🧹 Nitpick comments (5)
src/omnibase_core/models/core/model_node_announce_metadata.py (1)
70-73: Timezone‑aware default_factory for timestamp is correctUsing
default_factory=lambda: datetime.now(UTC)produces an aware UTCdatetime, fixing the previous naiveutcnowbehavior while keeping evaluation at instance creation time. If this pattern is repeated in multiple models, consider a small shared helper (e.g.,def utcnow() -> datetime: return datetime.now(UTC)) to keep things DRY and consistent.src/omnibase_core/models/core/model_action_validation_result.py (1)
7-7: Timezone-awarevalidated_atdefault looks correct; consider centralizing UTC helperSwitching from a naive default to
datetime.now(UTC)gives an aware UTC timestamp and aligns with the PR’s move toward consistent timezone-aware fields; the change here looks good. If this pattern is repeated across many models in the codebase, consider introducing a small shared helper (e.g.,utc_now()) to keep the behavior consistent and simplify future changes.Also applies to: 45-47
src/omnibase_core/models/services/model_error_details.py (1)
5-5: LGTM! Timezone-aware UTC timestamps adopted project-wide.The shift from
datetime.utcnow(naive UTC) todatetime.now(UTC)(timezone-aware UTC) is a best practice that eliminates ambiguity and prevents common datetime comparison bugs. This change is consistently applied across all model files in this PR.The existing
field_serializermethod converts datetimes to ISO format strings, which handles both naive and aware datetimes during serialization. Backward compatibility with previously serialized data is maintained—Pydantic's default datetime deserialization transparently handles both formats, so existing stored data with naive or aware timestamps will deserialize correctly.src/omnibase_core/nodes/node_orchestrator.py (1)
23-24: Workflow snapshot serialization/restore logic looks solid; consider clarifying JSON behaviorThe new workflow snapshot APIs are well-structured:
snapshot_workflow_state()exposes the frozenModelWorkflowStateSnapshotinstance, matching the immutability contract documented on the model._validate_workflow_snapshot()correctly rejects futurecreated_atvalues, handling both naive and timezone-aware datetimes and providing structuredModelOnexErrorcontext.restore_workflow_state()validates before assignment, keeping state restoration guarded.get_workflow_snapshot()returnsself._workflow_state.model_dump(), which is exactly what the new tests assert against (native UUID/datetime types).One small optional refinement: the docstring calls the returned dict “JSON-serializable”, but
model_dump()still contains UUID and datetime objects. Since your example already usesjson.dumps(snapshot_dict, default=str), it might be clearer either to:
- Explicitly mention the need for
default=str(or equivalent) in the “Returns” section, or- Switch to
self._workflow_state.model_dump(mode="json")if you ever want a strictly JSON-ready dict and are willing to adjust the tests accordingly.Also applies to: 28-29, 406-579
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
27-177: Workflow snapshot model is well-designed; consider preservingschema_versionin transitionsThis model aligns nicely with your FSM snapshot pattern:
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)gives you immutable, strict, test-friendly value semantics.created_atusesdatetime.now(UTC)via a lambda, avoiding deprecatedutcnow()and ensuring timezone-aware timestamps.with_step_completed()/with_step_failed():
- Always create fresh dicts/lists (copy-on-write) for
context,completed_step_ids, andfailed_step_ids, preventing aliasing.- Increment
current_step_indexmonotonically and respect the immutability contract.One optional refinement: both transition helpers currently rely on the default
schema_versionrather than explicitly copyingself.schema_version. If you later introduce schema version > 1 and call these helpers on older snapshots, you may want to preserve the originalschema_version:return ModelWorkflowStateSnapshot( - workflow_id=self.workflow_id, + workflow_id=self.workflow_id, + schema_version=self.schema_version, current_step_index=self.current_step_index + 1, ... )Not urgent for
schema_version == 1, but it can make future migrations more predictable.Also applies to: 179-282, 285-285
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (41)
src/omnibase_core/mixins/mixin_workflow_execution.py(3 hunks)src/omnibase_core/models/core/model_action_base.py(2 hunks)src/omnibase_core/models/core/model_action_validation_result.py(2 hunks)src/omnibase_core/models/core/model_audit_entry.py(2 hunks)src/omnibase_core/models/core/model_business_impact.py(2 hunks)src/omnibase_core/models/core/model_capability.py(1 hunks)src/omnibase_core/models/core/model_error_summary.py(2 hunks)src/omnibase_core/models/core/model_health_check_component.py(2 hunks)src/omnibase_core/models/core/model_health_check_result.py(2 hunks)src/omnibase_core/models/core/model_instance_metadata.py(2 hunks)src/omnibase_core/models/core/model_metadata_base.py(1 hunks)src/omnibase_core/models/core/model_monitoring_metrics.py(2 hunks)src/omnibase_core/models/core/model_node_announce_metadata.py(2 hunks)src/omnibase_core/models/core/model_node_discovery.py(2 hunks)src/omnibase_core/models/core/model_onex_audit_event.py(2 hunks)src/omnibase_core/models/core/model_onex_internal_processing_context.py(2 hunks)src/omnibase_core/models/core/model_onex_reply_class.py(1 hunks)src/omnibase_core/models/core/model_onex_security_context_class.py(1 hunks)src/omnibase_core/models/core/model_parse_metadata.py(1 hunks)src/omnibase_core/models/core/model_performance_profile.py(2 hunks)src/omnibase_core/models/core/model_route_hop.py(2 hunks)src/omnibase_core/models/core/model_security_assessment.py(2 hunks)src/omnibase_core/models/core/model_tag.py(2 hunks)src/omnibase_core/models/core/model_trend_data.py(2 hunks)src/omnibase_core/models/discovery/model_event_descriptor.py(2 hunks)src/omnibase_core/models/discovery/model_event_discovery_response.py(2 hunks)src/omnibase_core/models/discovery/model_hub_registration_event.py(2 hunks)src/omnibase_core/models/discovery/model_outputmetadata.py(2 hunks)src/omnibase_core/models/health/model_health_status.py(2 hunks)src/omnibase_core/models/security/model_audit_data.py(2 hunks)src/omnibase_core/models/security/model_security_policy.py(2 hunks)src/omnibase_core/models/security/model_signature_chain.py(1 hunks)src/omnibase_core/models/services/model_custom_fields.py(1 hunks)src/omnibase_core/models/services/model_error_details.py(2 hunks)src/omnibase_core/models/workflow/__init__.py(2 hunks)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(2 hunks)src/omnibase_core/nodes/node_reducer.py(3 hunks)src/omnibase_core/utils/fsm_executor.py(1 hunks)tests/unit/models/fsm/test_model_fsm_state_snapshot.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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_onex_reply_class.pysrc/omnibase_core/models/core/model_metadata_base.pysrc/omnibase_core/models/core/model_performance_profile.pysrc/omnibase_core/models/discovery/model_outputmetadata.pysrc/omnibase_core/models/core/model_monitoring_metrics.pysrc/omnibase_core/models/core/model_business_impact.pysrc/omnibase_core/models/services/model_custom_fields.pysrc/omnibase_core/models/core/model_security_assessment.pysrc/omnibase_core/models/security/model_security_policy.pysrc/omnibase_core/models/core/model_node_discovery.pysrc/omnibase_core/models/core/model_parse_metadata.pysrc/omnibase_core/models/core/model_onex_security_context_class.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/models/discovery/model_hub_registration_event.pysrc/omnibase_core/models/core/model_trend_data.pysrc/omnibase_core/models/workflow/__init__.pysrc/omnibase_core/models/core/model_error_summary.pysrc/omnibase_core/models/security/model_audit_data.pysrc/omnibase_core/models/core/model_onex_internal_processing_context.pysrc/omnibase_core/models/health/model_health_status.pysrc/omnibase_core/nodes/node_reducer.pytests/unit/models/fsm/test_model_fsm_state_snapshot.pysrc/omnibase_core/models/discovery/model_event_discovery_response.pysrc/omnibase_core/models/core/model_health_check_result.pysrc/omnibase_core/models/core/model_health_check_component.pysrc/omnibase_core/models/core/model_audit_entry.pysrc/omnibase_core/models/security/model_signature_chain.pysrc/omnibase_core/models/core/model_instance_metadata.pysrc/omnibase_core/models/core/model_capability.pysrc/omnibase_core/models/core/model_action_base.pysrc/omnibase_core/models/discovery/model_event_descriptor.pysrc/omnibase_core/models/services/model_error_details.pysrc/omnibase_core/models/core/model_route_hop.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/utils/fsm_executor.pysrc/omnibase_core/models/core/model_node_announce_metadata.pysrc/omnibase_core/models/core/model_action_validation_result.pytests/unit/nodes/test_node_state_serialization.pysrc/omnibase_core/models/core/model_tag.pysrc/omnibase_core/models/core/model_onex_audit_event.pysrc/omnibase_core/mixins/mixin_workflow_execution.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_onex_reply_class.pysrc/omnibase_core/models/core/model_metadata_base.pysrc/omnibase_core/models/core/model_performance_profile.pysrc/omnibase_core/models/discovery/model_outputmetadata.pysrc/omnibase_core/models/core/model_monitoring_metrics.pysrc/omnibase_core/models/core/model_business_impact.pysrc/omnibase_core/models/services/model_custom_fields.pysrc/omnibase_core/models/core/model_security_assessment.pysrc/omnibase_core/models/security/model_security_policy.pysrc/omnibase_core/models/core/model_node_discovery.pysrc/omnibase_core/models/core/model_parse_metadata.pysrc/omnibase_core/models/core/model_onex_security_context_class.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/models/discovery/model_hub_registration_event.pysrc/omnibase_core/models/core/model_trend_data.pysrc/omnibase_core/models/workflow/__init__.pysrc/omnibase_core/models/core/model_error_summary.pysrc/omnibase_core/models/security/model_audit_data.pysrc/omnibase_core/models/core/model_onex_internal_processing_context.pysrc/omnibase_core/models/health/model_health_status.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/models/discovery/model_event_discovery_response.pysrc/omnibase_core/models/core/model_health_check_result.pysrc/omnibase_core/models/core/model_health_check_component.pysrc/omnibase_core/models/core/model_audit_entry.pysrc/omnibase_core/models/security/model_signature_chain.pysrc/omnibase_core/models/core/model_instance_metadata.pysrc/omnibase_core/models/core/model_capability.pysrc/omnibase_core/models/core/model_action_base.pysrc/omnibase_core/models/discovery/model_event_descriptor.pysrc/omnibase_core/models/services/model_error_details.pysrc/omnibase_core/models/core/model_route_hop.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/utils/fsm_executor.pysrc/omnibase_core/models/core/model_node_announce_metadata.pysrc/omnibase_core/models/core/model_action_validation_result.pysrc/omnibase_core/models/core/model_tag.pysrc/omnibase_core/models/core/model_onex_audit_event.pysrc/omnibase_core/mixins/mixin_workflow_execution.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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/fsm/test_model_fsm_state_snapshot.pytests/unit/nodes/test_node_state_serialization.py
src/omnibase_core/mixins/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Create reusable behavior mixins with accompanying mixin_metadata.yaml for discovery and capability matching
Files:
src/omnibase_core/mixins/mixin_workflow_execution.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/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/core/model_node_discovery.pysrc/omnibase_core/nodes/node_orchestrator.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/core/model_health_check_result.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/mixins/mixin_workflow_execution.py
🧬 Code graph analysis (9)
src/omnibase_core/models/security/model_security_policy.py (1)
src/omnibase_core/models/node_metadata/model_node_information.py (2)
updated_at(132-134)updated_at(137-139)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
create_initial(134-158)
src/omnibase_core/models/workflow/__init__.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-282)
src/omnibase_core/nodes/node_reducer.py (2)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)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/models/fsm/test_model_fsm_state_snapshot.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)
src/omnibase_core/nodes/node_orchestrator.py (8)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-282)src/omnibase_core/models/metadata/model_metadata_field_info.py (1)
created_at(197-212)src/omnibase_core/models/metadata/model_node_info_summary.py (2)
created_at(159-161)created_at(164-166)src/omnibase_core/models/node_metadata/model_node_information.py (2)
created_at(122-124)created_at(127-129)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/models/core/model_node_metadata_block.py (1)
model_dump(323-326)src/omnibase_core/protocols/types/protocol_serializable.py (1)
model_dump(23-35)
src/omnibase_core/utils/fsm_executor.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)
tests/unit/nodes/test_node_state_serialization.py (8)
src/omnibase_core/enums/enum_workflow_coordination.py (1)
EnumFailureRecoveryStrategy(40-46)src/omnibase_core/models/contracts/subcontracts/model_fsm_subcontract.py (1)
ModelFSMSubcontract(39-463)src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (1)
ModelWorkflowDefinition(21-119)src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (2)
ModelFSMStateSnapshot(56-210)transition_to(160-210)src/omnibase_core/models/primitives/model_semver.py (1)
ModelSemVer(24-146)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (4)
ModelWorkflowStateSnapshot(37-282)create_initial(180-206)with_step_completed(208-244)with_step_failed(246-282)src/omnibase_core/nodes/node_orchestrator.py (4)
NodeOrchestrator(31-579)snapshot_workflow_state(410-447)restore_workflow_state(497-536)get_workflow_snapshot(538-579)src/omnibase_core/nodes/node_reducer.py (4)
NodeReducer(42-566)snapshot_state(347-383)restore_state(475-523)get_state_snapshot(525-566)
src/omnibase_core/mixins/mixin_workflow_execution.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(37-282)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Code Quality
- GitHub Check: claude-review
🔇 Additional comments (33)
src/omnibase_core/models/core/model_node_announce_metadata.py (1)
3-3: UTC import aligns with timezone-aware datetime usageImporting
UTCfromdatetimeis correct for producing aware UTC timestamps and matches the project’s Python 3.12 target.src/omnibase_core/models/discovery/model_hub_registration_event.py (1)
6-6: Timezone-aware registration timestamp change looks correctSwitching to
from datetime import UTC, datetimeanddefault_factory=lambda: datetime.now(UTC)gives a timezone-aware UTCregistration_timestamp, which is preferable for event data and is compatible with the stated Python 3.12 target. The use of alambdafordefault_factoryalso correctly avoids evaluating the timestamp at import time.Please confirm that all runtimes for this service are on Python ≥3.11 so that the
datetime.UTCconstant is always available, and re-run the test suite (e.g.poetry run pytest) in that environment to be safe.Also applies to: 45-47
src/omnibase_core/models/core/model_tag.py (2)
8-8: LGTM: Required import for timezone-aware timestamps.The UTC import is correctly added to support the timezone-aware timestamp generation at line 43.
43-43: Excellent: Modernized to timezone-aware timestamps.The change from
datetime.utcnow(deprecated in Python 3.12+, returns naive datetime) todatetime.now(UTC)(returns timezone-aware datetime) is a best-practice improvement that:
- Uses the recommended API for Python 3.12+
- Produces explicit timezone-aware timestamps for more reliable serialization
- Aligns with the PR's state serialization objectives
src/omnibase_core/models/core/model_monitoring_metrics.py (2)
5-5: UTC import aligns with timezone-aware usageImporting
UTCalongsidedatetimeis appropriate for Python 3.11+ and matches the move to UTC-aware timestamps elsewhere in the codebase.
100-103: Timezone-aware default for collection_timestamp looks correctSwitching
collection_timestamptodefault_factory=lambda: datetime.now(UTC)provides an aware UTC timestamp and is consistent with the serializer using.isoformat(). No issues from a typing or runtime perspective.src/omnibase_core/models/core/model_onex_reply_class.py (1)
40-43: UTC‑aware timestamp default is correct and consistent with age calculationsSwitching the
timestampfield’sdefault_factorytolambda: datetime.now(UTC)makes the default explicitly timezone‑aware and matches theget_age_seconds()logic that already usesdatetime.now(UTC). This removes potential naive vs. awaredatetimesubtraction issues without changing the public API. Looks good as‑is.src/omnibase_core/models/core/model_health_check_result.py (1)
6-29: Timezone-aware timestamp default is correct; confirm downstream assumptionsSwitching the default from a naive
utcnow()todatetime.now(UTC)is a good move and aligns with UTC-aware semantics; the existing serializer usingvalue.isoformat()will now emit an explicit offset for the default value. Please just double‑check any callers/consumers that:
- assume
timestampis naive, or- parse the serialized string expecting no timezone information,
to ensure there are no subtle compatibility issues.
src/omnibase_core/models/services/model_custom_fields.py (1)
52-55: Timezone-awarelast_modifieddefault is correct and consistentSwitching the default to
datetime.now(UTC)makeslast_modifiedtimezone-aware in UTC and aligns with the UTC usage elsewhere in this model (e.g.,set_field,remove_field). No issues from a typing or Pydantic perspective.src/omnibase_core/models/core/model_performance_profile.py (1)
5-5: UTC‑aware default forprofile_timestamplooks correct; verify downstream expectationsSwitching the default to
datetime.now(UTC)gives you an aware UTC timestamp, which is a solid improvement over naiveutcnow(). Note thatisoformat()on this field will now emit an explicit offset (e.g.,...+00:00) instead of a naive timestamp, so it’s worth double‑checking that any consumers (JSON schemas, logs, or external integrations) are fine with the slightly different serialized shape.Also applies to: 140-143
src/omnibase_core/models/security/model_signature_chain.py (1)
48-55: UTC-aware timestamp defaults look goodUsing
default_factory=lambda: datetime.now(UTC)for bothcreated_atandlast_modifiedaligns the defaults with the documented “(UTC)” semantics and with the rest of the model (e.g.,add_signatureandget_signature_age_statsalready usedatetime.now(UTC)). No issues here.src/omnibase_core/models/core/model_error_summary.py (1)
5-6: LGTM! Timezone-aware UTC timestamps are the correct approach.The migration from
datetime.utcnow()todatetime.now(UTC)aligns with Python 3.12+ best practices whereutcnow()is deprecated. The existingfield_serializerusingisoformat()will correctly serialize the timezone-aware datetime with the UTC offset.Also applies to: 25-28
src/omnibase_core/models/core/model_audit_entry.py (1)
1-1: LGTM! Consistent with the timezone-aware timestamp pattern.The
timestampfield now uses timezone-aware UTC, and the existing serializer on lines 133-136 handles bothtimestampandreview_timestampcorrectly withisoformat().Also applies to: 26-29
src/omnibase_core/models/discovery/model_outputmetadata.py (1)
1-1: LGTM! Timezone-aware timestamp for execution metadata.The change is consistent with the codebase-wide migration to
datetime.now(UTC). Pydantic 2.x handles timezone-aware datetime serialization correctly by default.Also applies to: 15-18
src/omnibase_core/models/core/model_route_hop.py (1)
8-8: LGTM! Proper timezone-aware timestamp for route hop tracking.The serializer on line 74-77 and the
__str__method both useisoformat(), which will correctly include the UTC offset in the output.Also applies to: 35-38
src/omnibase_core/models/security/model_audit_data.py (1)
7-7: LGTM! Timezone-aware audit timestamp.Consistent with the codebase-wide pattern for UTC-aware timestamps.
Also applies to: 16-19
src/omnibase_core/models/core/model_onex_audit_event.py (1)
7-7: LGTM! Timezone-aware event timestamp.Consistent with the audit event pattern across the codebase.
Also applies to: 18-21
src/omnibase_core/models/security/model_security_policy.py (1)
4-4: LGTM! Timezone-aware timestamps for security policy tracking.Both
created_atandupdated_atnow usedatetime.now(UTC), and the existingfield_serializeron lines 96-99 handles all datetime fields correctly.Also applies to: 32-37
src/omnibase_core/models/core/model_metadata_base.py (1)
20-20: LGTM - Timezone-aware timestamp upgrade.The migration from
datetime.utcnowtodatetime.now(UTC)is correct and aligns with modern Python best practices. This change ensurescreated_atis timezone-aware and matches the existingupdate_timestamp()implementation.src/omnibase_core/models/core/model_action_base.py (1)
32-32: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and maintains consistency with the broader codebase shift to timezone-aware timestamps.src/omnibase_core/models/discovery/model_event_descriptor.py (1)
169-169: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and aligns with the repository-wide shift to timezone-aware timestamps.src/omnibase_core/models/core/model_onex_security_context_class.py (1)
107-107: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and maintains consistency with the existing validator implementations that already usedatetime.now(UTC)for comparisons.src/omnibase_core/models/core/model_instance_metadata.py (2)
56-61: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)for bothcreated_atandlast_updatedfields is correct and maintains consistency with the broader codebase shift to timezone-aware timestamps.
66-71: LGTM - Useful metadata extensions.The addition of
sla_tierandcustom_attributesfields is reasonable and follows the existing pattern of flexible metadata storage (labels, annotations, tags).src/omnibase_core/models/core/model_node_discovery.py (1)
19-19: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and aligns with the repository-wide shift to timezone-aware timestamps.src/omnibase_core/models/core/model_capability.py (1)
71-71: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and maintains consistency with the existingis_expired()method that already usesdatetime.now(UTC)for expiration checks.src/omnibase_core/models/core/model_onex_internal_processing_context.py (1)
42-42: LGTM - Timezone-aware timestamp upgrade.The migration to
datetime.now(UTC)is correct and aligns with the repository-wide shift to timezone-aware timestamps.tests/unit/models/fsm/test_model_fsm_state_snapshot.py (1)
16-16: Import path update to package-levelmodels.fsmlooks correctSwitching to
from omnibase_core.models.fsm import ModelFSMStateSnapshotmatches the new public API surface and keeps tests aligned with production code.src/omnibase_core/utils/fsm_executor.py (1)
26-26: Alias import forFSMStateis consistent with new FSM model exportsUsing
from omnibase_core.models.fsm import ModelFSMStateSnapshot as FSMStatekeeps this utility aligned with the canonical FSM snapshot import path and avoids deep module coupling.src/omnibase_core/models/workflow/__init__.py (1)
18-24: ExposingModelWorkflowStateSnapshotfrom the workflow package is appropriateImporting and adding
ModelWorkflowStateSnapshotto__all__correctly promotes the execution snapshot model to the public workflow API, matching how nodes and tests consume it.Also applies to: 26-39
src/omnibase_core/mixins/mixin_workflow_execution.py (1)
21-27: Workflow execution mixin state tracking is wired correctlyAdding
_workflow_state: ModelWorkflowStateSnapshot | Noneand initializing it toNoneinMixinWorkflowExecution.__init__cleanly centralizes workflow-state storage for orchestrators while preserving the cooperativesuper().__init__pattern used across mixins.Also applies to: 30-54, 55-65
src/omnibase_core/nodes/node_reducer.py (1)
9-10: FSM snapshot validation and restore behavior are correctly hardenedThe new FSM snapshot APIs for
NodeReducerare in good shape:
snapshot_state()andget_state_snapshot()cleanly separate typed vs. dict-based access without mutating internal state._validate_fsm_snapshot():
- Builds
valid_statesfrom the contract and rejects snapshots whosecurrent_stateor anyhistoryentry is not defined, raisingModelOnexErrorwithEnumCoreErrorCode.INVALID_STATEand useful context.- Performs a conservative future-timestamp check over common context keys (
created_at,timestamp,snapshot_time), handling bothdatetimeinstances and ISO 8601 strings, including naive datetimes (normalized to UTC).restore_state()correctly enforces that a contract is loaded before restoring and validates snapshots prior to assignment.These changes substantially reduce the risk of injecting impossible or temporally-invalid FSM states and match the expectations codified in the new tests.
Also applies to: 27-40, 347-566
tests/unit/nodes/test_node_state_serialization.py (1)
1-143: State-serialization test coverage is thorough and aligns with the new APIsThis module does a strong job validating the new snapshot/restore behavior:
TestNodeReducerStateSerializationandTestNodeOrchestratorStateSerializationcover:
- Uninitialized cases (
snapshot_*and dict-based getters returnNone).- Happy-path snapshot/restore round-trips.
- Contract presence checks (raising
ModelOnexErrorwhen missing).- Validation failures for invalid FSM states/history and future timestamps (both datetime objects and ISO strings).
TestNodeOrchestratorWorkflowSnapshotValidationexercises timestamp sanity checks for past, recent, naive, and futurecreated_atvalues and asserts thatworkflow_idis propagated into the error context.TestModelWorkflowStateSnapshotandTestModelFSMStateSnapshotverify:
- Factory methods (
create_initial).- Immutability behavior (frozen models reject reassignment).
- Transition helpers (
with_step_completed,with_step_failed,transition_to) preserve originals and correctly merge/extend state.model_dump()andmodel_dump(mode="json")produce dictionaries that can be serialized withjson.dumps.Overall, the tests closely track the intended contracts of the new snapshot models and node methods, and they should provide good regression protection for future changes.
Also applies to: 155-444, 455-855, 857-1133
…lidation Before Merge (High Priority): - Changed completed_step_ids/failed_step_ids from list[UUID] to tuple[UUID, ...] for true immutability (tuples cannot be modified after creation) - Added step_ids overlap validation in _validate_workflow_snapshot() to prevent a step being both completed AND failed - Added update_workflow_state() method to MixinWorkflowExecution for manual state tracking, auto-captures state after execute_workflow_from_contract() Post-Merge Enhancements: - Added WORKFLOW_STATE_SNAPSHOT_SCHEMA_VERSION constant for versioned deserialization - Enhanced timestamp error messages to include actual ISO 8601 timestamps and time difference for easier debugging - Added sanitize_context_for_logging() method to redact PII patterns (email, phone, SSN, credit card, IP address) from context data before logging Tests: Added 50+ new tests covering validation, state population, and PII sanitization
Code Review - PR #190: State Serialization for NodeReducer and NodeOrchestratorOverviewThis PR implements state serialization methods for ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Validation
3. Documentation Quality
4. Test Coverage
5. ONEX Compliance
🔍 Issues & RecommendationsCRITICAL: Type Inconsistency in
|
- Register timeout pytest marker in pyproject.toml to fix strict-markers warnings - Fix test class docstrings to accurately reflect return types: - snapshot_state() returns ModelFSMStateSnapshot | None - get_state_snapshot() returns dict[str, object] | None - snapshot_workflow_state() returns ModelWorkflowStateSnapshot | None - get_workflow_snapshot() returns dict[str, object] | None
Code Review: State Serialization for NodeReducer and NodeOrchestratorOverall Assessment✅ Strong Implementation - This PR implements comprehensive state serialization for both NodeReducer and NodeOrchestrator with excellent documentation, thorough testing, and careful attention to immutability concerns. The code is well-architected and aligns with ONEX patterns. Strengths1. Excellent Documentation 📚
2. Strong Type Safety ✅
3. Thorough Testing 🧪
4. Security Considerations 🔒
5. datetime.utcnow() → datetime.now(UTC) Migration ⏰
Issues Found🔴 CRITICAL: Mutable Context Dict Violates Immutability ContractLocation: Problem: While the model is frozen, the # Current implementation allows this violation:
snapshot = ModelWorkflowStateSnapshot.create_initial(uuid4())
snapshot.context["new_key"] = "value" # ❌ This works but violates immutability!Why This Matters:
Recommended Solutions (in order of preference):
Current State: The docstring warns against mutation (lines 19, 66, 138-142), but Python doesn't enforce this. This creates a "please don't do this" contract that's easy to violate accidentally.
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
tests/unit/nodes/test_node_state_serialization.py (2)
145-153: Docstring correctly describes return types.The class docstring now accurately states that get_state_snapshot() returns dict[str, object] | None, addressing the previous review feedback.
445-455: Docstring correctly describes return types.The class docstring now accurately states that get_workflow_snapshot() returns dict[str, object] | None, addressing the previous review feedback.
🧹 Nitpick comments (3)
src/omnibase_core/nodes/node_reducer.py (1)
534-575: Consider usingmode="json"for proper JSON serialization.The current implementation uses
model_dump()which may return non-JSON-serializable types (e.g.,datetimeobjects). For a truly JSON-serializable dict, consider usingmode="json":def get_state_snapshot(self) -> dict[str, object] | None: ... if self._fsm_state is None: return None - return self._fsm_state.model_dump() + return self._fsm_state.model_dump(mode="json")This ensures datetime and UUID values are converted to strings, matching the documented "JSON-serializable" behavior.
src/omnibase_core/nodes/node_orchestrator.py (2)
465-536: Move import to module level for consistency.The
datetimeandUTCimport at line 492 is inside the method. For consistency with the rest of the codebase and to avoid repeated import overhead, consider moving it to the module level.from uuid import UUID +from datetime import UTC, datetime from omnibase_core.enums.enum_core_error_code import EnumCoreErrorCodeThen remove line 492:
- from datetime import UTC, datetime - # Timestamp sanity check: created_at should not be in the futureThe validation logic itself is solid - checking for future timestamps and overlapping step IDs between completed and failed sets ensures snapshot integrity.
581-622: Consider usingmode="json"for true JSON serialization.Same as noted for
NodeReducer.get_state_snapshot(), themodel_dump()call may return non-JSON-serializable types. For consistency with the documented "JSON-serializable dictionary" behavior:def get_workflow_snapshot(self) -> dict[str, object] | None: ... if self._workflow_state is None: return None - return self._workflow_state.model_dump() + return self._workflow_state.model_dump(mode="json")This ensures UUIDs and datetimes are converted to strings for direct JSON serialization.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
pyproject.toml(1 hunks)src/omnibase_core/mixins/mixin_workflow_execution.py(5 hunks)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(2 hunks)src/omnibase_core/nodes/node_reducer.py(3 hunks)tests/unit/models/workflow/__init__.py(1 hunks)tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- tests/unit/models/workflow/init.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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/nodes/node_reducer.pytests/unit/models/workflow/test_model_workflow_state_snapshot_pii.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/mixins/mixin_workflow_execution.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pytests/unit/nodes/test_node_state_serialization.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/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/mixins/mixin_workflow_execution.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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/workflow/test_model_workflow_state_snapshot_pii.pytests/unit/nodes/test_node_state_serialization.py
src/omnibase_core/mixins/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Create reusable behavior mixins with accompanying mixin_metadata.yaml for discovery and capability matching
Files:
src/omnibase_core/mixins/mixin_workflow_execution.py
🧠 Learnings (2)
📚 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/nodes/node_orchestrator.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 tests/**/*.py : Use pytest markers (pytest.mark.unit, pytest.mark.integration, pytest.mark.slow, pytest.mark.smoke, pytest.mark.performance) to categorize tests
Applied to files:
pyproject.toml
🧬 Code graph analysis (4)
src/omnibase_core/nodes/node_reducer.py (7)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)src/omnibase_core/models/reducer/model_reducer_input.py (1)
ModelReducerInput(67-123)src/omnibase_core/models/reducer/model_reducer_output.py (1)
ModelReducerOutput(23-66)src/omnibase_core/models/contracts/subcontracts/model_fsm_subcontract.py (1)
ModelFSMSubcontract(39-463)src/omnibase_core/infrastructure/node_base.py (1)
current_state(763-771)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
ModelWorkflowStateSnapshot(46-404)sanitize_context_for_logging(243-328)
src/omnibase_core/mixins/mixin_workflow_execution.py (2)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(46-404)src/omnibase_core/utils/workflow_executor.py (1)
execute_workflow(229-350)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
create_initial(134-158)
🪛 Gitleaks (8.30.0)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py
[high] 253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: claude-review
🔇 Additional comments (26)
pyproject.toml (1)
294-308: LGTM! Well-configured test stability settings.The timeout and retry configuration is appropriate:
- 60s per-test timeout prevents hanging tests
- Thread-based timeout method is compatible with pytest-asyncio
--reruns=2with 1s delay handles transient failures gracefully--dist=loadgroupallows grouping related teststests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (3)
1-23: Comprehensive PII sanitization test coverage.The test module thoroughly covers the
sanitize_context_for_loggingfunctionality including email, phone, SSN, credit card, IP redaction, nested structures, key-based redaction, custom patterns, type preservation, and immutability guarantees.
251-258: Static analysis false positive - test data is intentional.The Gitleaks warning about "generic-api-key" at line 253 is a false positive. This test data (
"JWT-abc123xyz") is intentionally used to verify that custom JWT patterns are correctly redacted. The test validates that custom patterns work as expected.
319-340: Good immutability verification.The test correctly verifies that
sanitize_context_for_loggingreturns a sanitized copy without modifying the original context, including nested structures. This aligns with the immutability contract documented inModelWorkflowStateSnapshot.src/omnibase_core/nodes/node_reducer.py (3)
350-386: LGTM! Clean snapshot API with proper null handling.The
snapshot_state()method correctly returns the internal FSM state or None, enabling safe serialization for replay and debugging. The docstring clearly distinguishes this fromget_state_snapshot()for JSON use cases.
388-482: Solid FSM snapshot validation addressing prior review feedback.The validation correctly:
- Validates
current_stateagainst contract state definitions- Validates all
historystates against contract- Performs timestamp sanity checks on context values
One note: The timestamp validation searches for keys in
snapshot.context(created_at,timestamp,snapshot_time), whileModelWorkflowStateSnapshothas a dedicatedcreated_atfield. This asymmetry is acceptable since FSM snapshots may embed timestamps differently, but consider documenting this behavior difference.
484-532: Proper restore with validation before assignment.The
restore_state()method correctly validates the snapshot against the loaded contract before restoring, preventing injection of invalid states. The error handling is consistent with the codebase patterns usingModelOnexErrorwith appropriate error codes.src/omnibase_core/mixins/mixin_workflow_execution.py (3)
53-65: LGTM! Proper workflow state initialization.The
_workflow_stateattribute is correctly typed and initialized toNone. The mixin pattern appropriately delegates tosuper().__init__(**kwargs)for cooperative multiple inheritance.
67-119: Clean immutable snapshot creation.The
update_workflow_state()method correctly:
- Creates new immutable snapshots (ModelWorkflowStateSnapshot is frozen)
- Converts lists to tuples for step IDs, ensuring immutability
- Provides sensible defaults for optional parameters
173-191: The step ID format assumption is correct.WorkflowExecutionResult.completed_stepsandWorkflowExecutionResult.failed_stepsare defined aslist[str]in the model, and throughout the workflow executor they are populated withstr(step.step_id)conversions. TheUUID(step_id)parsing in lines 175-176 is safe and will work correctly.src/omnibase_core/nodes/node_orchestrator.py (2)
414-463: LGTM! Well-documented snapshot method with clear usage guidance.The
snapshot_workflow_state()method provides a clean API for accessing the current workflow state. The docstring clearly explains the two ways state is populated (automatic after execution vs. manual) and provides good usage examples.
538-579: LGTM! Proper restore with validation.The
restore_workflow_state()method correctly validates the snapshot before assignment, preventing invalid state injection. The docstring accurately describes the immutability guarantees.src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (8)
27-44: LGTM! Clean imports and well-documented schema version constant.The timezone-aware UTC import and schema version constant are properly defined. The documentation clearly explains the versioning strategy.
46-162: Excellent documentation and configuration.The comprehensive docstring covers immutability contracts, PII handling, schema versioning, thread safety, and usage examples. The ConfigDict settings are appropriate for an immutable value object, and the from_attributes=True rationale is well-explained.
164-183: Well-designed PII patterns with thoughtful ordering.The regex patterns cover common PII types, and the ordering consideration (credit card before phone) prevents partial matches. This is a solid foundation for context sanitization.
185-211: Field definitions are correct and address previous feedback.The use of
datetime.now(UTC)for the created_at field properly addresses the deprecateddatetime.utcnow()issue from the previous review. The tuple types for step IDs ensure true immutability, and field constraints are appropriate.
213-240: Clean factory method following established patterns.The create_initial() method follows the same pattern as ModelFSMStateSnapshot.create_initial (reference snippet), providing a consistent API across snapshot models.
242-328: Comprehensive PII sanitization with recursive support.The sanitize_context_for_logging() method properly handles nested structures and provides flexible customization through additional_patterns and redact_keys. The recursive implementation preserves non-string types while sanitizing string values.
330-404: Immutable update methods correctly preserve snapshot integrity.Both with_step_completed() and with_step_failed() properly maintain immutability by creating new instances. Context merging and tuple unpacking for step IDs are handled correctly, and the current_step_index is appropriately incremented.
406-407: LGTM! Proper public API exports.The all list correctly exports both the model class and the schema version constant for public use.
tests/unit/nodes/test_node_state_serialization.py (6)
1-143: LGTM! Well-structured test module with comprehensive fixtures.The module docstring clearly explains the test scope, imports are appropriate, and the fixtures provide clean test data for FSM and workflow definitions. The module-level pytest.mark.unit marker correctly categorizes all tests.
155-443: Comprehensive test coverage for NodeReducer state serialization.The test methods thoroughly cover snapshot creation, restoration, validation, and error handling. The async round-trip test and edge case validations (future timestamps, invalid states) provide excellent coverage.
457-911: Thorough test coverage for workflow state serialization.The tests comprehensively cover manual state updates, automatic state capture during workflow execution, restoration, and round-trip scenarios. The async tests properly verify state persistence across workflow executions.
913-1232: Excellent validation test coverage with edge case handling.The tests thoroughly validate timestamp sanity checks (future/past, aware/naive), step ID overlap detection, and error context inclusion. The error context access pattern correctly handles the nested structure of ModelOnexError.
1234-1418: Comprehensive model behavior tests.The tests thoroughly validate the ModelWorkflowStateSnapshot model's factory methods, immutability guarantees, transition methods, context merging, and JSON serialization. The tests correctly verify that frozen models raise ValidationError on modification attempts.
1420-1510: Well-structured FSM snapshot model tests.The tests provide good coverage of ModelFSMStateSnapshot behavior, mirroring the structure of the workflow snapshot tests. Factory methods, immutability, state transitions, history preservation, and serialization are all properly validated.
- Register timeout marker in pyproject.toml (already done) - Fix test class docstrings to match actual return types - Add context mutation tests (TestContextMutationProtection) - Add concurrent access tests (TestConcurrentAccessSafety) - Add mode="json" to get_workflow_snapshot() for proper JSON serialization - Differentiate snapshot_workflow_state() vs get_workflow_snapshot() APIs - Add comprehensive from_attributes=True justification comments - Create ADR-002 for context mutability design decision - Extend PII patterns with IPv6, API keys, GUIDs - Add ge=0 constraint to current_step_index field - All 61 state serialization tests passing
Code Review: State Serialization for NodeReducer and NodeOrchestratorOverall AssessmentStatus: ✅ APPROVE with minor suggestions This is a well-implemented PR that adds essential state serialization capabilities to NodeReducer and NodeOrchestrator. The code quality is excellent, with comprehensive documentation, strong type safety, and thorough test coverage. Strengths1. Excellent Documentation
2. Strong Type Safety
3. Robust Security & Privacy
4. Well-Designed APIs
5. Comprehensive Testing
Minor Suggestions (Non-blocking)1. Deep Copy ErgonomicsConsider adding a 2. Context Size Enforcement
3. Mixin State InitializationAdd comment clarifying where Architecture Alignment✅ Protocol-driven DI patterns Final VerdictProduction-ready with minor suggestions for future enhancements. Core implementation is solid, well-tested, and follows ONEX patterns. Recommendations Before Merge
Great work on this feature! State serialization will be valuable for workflow debugging, replay, and persistence. Reviewed by: Claude Code (Sonnet 4.5) |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/omnibase_core/nodes/node_orchestrator.py (1)
535-535: Inline import of datetime utilities is redundant.The
datetimeandUTCare already available at module level (used elsewhere in the codebase pattern). While this works, inline imports add slight overhead and are inconsistent with the import style innode_reducer.pywhich imports at the top.- from datetime import UTC, datetime - - # Timestamp sanity check: created_at should not be in the future - now = datetime.now(UTC) + # Import at module level instead: + # from datetime import UTC, datetime + from datetime import UTC, datetime # Move to top of file + + now = datetime.now(UTC)Consider moving this import to the module level for consistency with
node_reducer.py.src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
405-505: Clarify or adjust nesting-depth semantics for lists/tuples in_max_depth
_max_depth()currently increments depth only when descending into dicts; lists/tuples recurse without increasingcurrent, so a deeply nested list tree under a single key still reports shallow depth. The docstring says “Max nesting depth of context dict (default: 5 levels)”, which some readers may interpret as overall structure depth including list/tuple containers.Consider either:
- Updating the docstring to explicitly say that only dict nesting contributes to
max_depth, or- Incrementing
currentwhen recursing into lists/tuples as well, if you want container depth (not just dict depth) to be enforced.This is mostly about aligning behavior with expectations; the current implementation is correct as long as that contract is clear.
506-580: Preserveschema_versionexplicitly when creating derived snapshots
with_step_completed()andwith_step_failed()construct newModelWorkflowStateSnapshotinstances but omitschema_version, so it’s implicitly reset to the module default. If you ever deserialize or migrate snapshots with a non‑defaultschema_version, calling these helpers would silently normalize them back to the current constant.To keep version information stable across transitions while still getting a fresh
created_at, you can pass throughself.schema_versionexplicitly:def with_step_completed( self, step_id: UUID, *, new_context: WorkflowContextType | None = None, ) -> ModelWorkflowStateSnapshot: @@ - return ModelWorkflowStateSnapshot( - workflow_id=self.workflow_id, - current_step_index=self.current_step_index + 1, - completed_step_ids=(*self.completed_step_ids, step_id), - failed_step_ids=self.failed_step_ids, # Tuples are immutable, no copy needed - context=updated_context, - ) + return ModelWorkflowStateSnapshot( + schema_version=self.schema_version, + workflow_id=self.workflow_id, + current_step_index=self.current_step_index + 1, + completed_step_ids=(*self.completed_step_ids, step_id), + failed_step_ids=self.failed_step_ids, # Tuples are immutable, no copy needed + context=updated_context, + ) @@ - return ModelWorkflowStateSnapshot( - workflow_id=self.workflow_id, - current_step_index=self.current_step_index + 1, - completed_step_ids=self.completed_step_ids, # Tuples are immutable, no copy needed - failed_step_ids=(*self.failed_step_ids, step_id), - context=updated_context, - ) + return ModelWorkflowStateSnapshot( + schema_version=self.schema_version, + workflow_id=self.workflow_id, + current_step_index=self.current_step_index + 1, + completed_step_ids=self.completed_step_ids, # Tuples are immutable, no copy needed + failed_step_ids=(*self.failed_step_ids, step_id), + context=updated_context, + )That keeps schema versioning stable while still giving each new snapshot its own
created_at.tests/unit/nodes/test_node_state_serialization.py (1)
1576-1591: Aligntest_fsm_snapshot_returned_context_mutation_protectionwith its behaviorThe test name and docstring say it verifies that mutating the returned context does not affect the snapshot, but the body never mutates
retrieved_context; it only re-readssnapshot.context. That makes the test misleading and effectively a no-op beyond a simple read.Consider either:
- Updating the name/docstring and comments to reflect that you’re only asserting read access, or
- Actually mutating
retrieved_contextand asserting on the snapshot if you want to guarantee that behavior.For example, if you prefer to just clarify intent:
- def test_fsm_snapshot_returned_context_mutation_protection(self) -> None: - """Test that mutating returned context does not affect FSM snapshot state.""" + def test_fsm_snapshot_returned_context_is_readable(self) -> None: + """Test that returned context can be read without asserting on mutation semantics.""" @@ - # Get the context and try to mutate it - retrieved_context = snapshot.context - # Note: frozen Pydantic models may still allow dict mutation - # depending on implementation details - - # Create a new access to verify original is unchanged - # The key assertion is that the model maintains its state + # Get the context; we intentionally do not assert on mutations here because + # Pydantic's frozen models may still expose a mutable dict implementation. + retrieved_context = snapshot.context + + # The key assertion is that the model maintains its state for read access assert snapshot.context["key"] == "value"This keeps the test aligned with what it actually guarantees today.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
docs/architecture/decisions/ADR-002-context-mutability-design-decision.md(1 hunks)src/omnibase_core/models/workflow/execution/__init__.py(1 hunks)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(2 hunks)src/omnibase_core/nodes/node_reducer.py(3 hunks)tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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/workflow/execution/__init__.pytests/unit/models/workflow/test_model_workflow_state_snapshot_pii.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.pytests/unit/nodes/test_node_state_serialization.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/workflow/execution/__init__.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.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/workflow/test_model_workflow_state_snapshot_pii.pytests/unit/nodes/test_node_state_serialization.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.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/nodes/node_orchestrator.py
🧬 Code graph analysis (4)
src/omnibase_core/models/workflow/execution/__init__.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(57-580)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
ModelWorkflowStateSnapshot(57-580)sanitize_context_for_logging(318-403)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
create_initial(134-158)
src/omnibase_core/nodes/node_orchestrator.py (3)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(57-580)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
🪛 Gitleaks (8.30.0)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py
[high] 253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 LanguageTool
docs/architecture/decisions/ADR-002-context-mutability-design-decision.md
[style] ~148-~148: To elevate your writing, try using a synonym here.
Context: ...ulty**: Mutation bugs may be subtle and hard to trace - Contract Violations: Mal...
(HARD_TO)
⏰ 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 8/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: claude-review
🔇 Additional comments (13)
docs/architecture/decisions/ADR-002-context-mutability-design-decision.md (1)
1-228: Well-structured ADR documenting the context mutability design decision.This ADR comprehensively covers the design rationale for using convention-based immutability, alternatives considered, implementation patterns, and thread safety implications. The decision aligns well with the actual implementation in
ModelWorkflowStateSnapshotand provides actionable guidance for developers.src/omnibase_core/models/workflow/execution/__init__.py (1)
12-31: Clean public API exports for the new workflow state snapshot model and constants.The new exports follow the existing module pattern, maintain alphabetical ordering in
__all__, and properly exposeModelWorkflowStateSnapshotalong with context limit constants for external use.tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (1)
1-493: Comprehensive test coverage for PII sanitization functionality.Excellent test organization covering email, phone, SSN, credit card, IP (both v4 and v6), API keys, UUIDs, nested structures, key-based redaction, custom patterns, type preservation, and edge cases. The tests verify both redaction behavior and original context immutability.
The Gitleaks warning on line 253 (
"JWT-abc123xyz") is a false positive—this is intentional test data demonstrating custom pattern matching, not a real API key.src/omnibase_core/nodes/node_reducer.py (4)
350-422: Well-documented snapshot_state() method with appropriate thread safety warnings.The implementation correctly returns the internal reference with comprehensive documentation about immutability contracts, thread safety, PII risks, and recommended isolation patterns via
copy.deepcopy().
520-607: restore_state() properly validates snapshots before applying.The implementation addresses the previous review feedback by validating the snapshot's current state and history against the FSM contract before restoration. The documentation clearly explains the no-deep-copy behavior and thread safety implications.
609-675: get_state_snapshot() provides safe JSON-serializable output.Using
model_dump()creates an independent dict that callers can safely serialize or modify without affecting internal node state. The docstring appropriately warns about PII sanitization before logging.
478-518: The timestamp validation logic is correct for the ModelFSMStateSnapshot structure.
ModelFSMStateSnapshotdoes not have a top-levelcreated_atfield—onlycurrent_state,context, andhistory. The validation correctly checkssnapshot.contextfor timestamp keys (created_at,timestamp,snapshot_time), which is the appropriate pattern for this model. This differs fromModelWorkflowStateSnapshot, which includes acreated_atfield validated directly. No changes needed.Likely an incorrect or invalid review comment.
src/omnibase_core/nodes/node_orchestrator.py (4)
414-506: Well-documented snapshot_workflow_state() with clear API distinction from get_workflow_snapshot().The docstring clearly explains the difference between the two methods (typed model for internal use vs. dict for external/JSON use), state population patterns, thread safety, and PII considerations. This addresses the previous review feedback about API clarity.
508-579: _validate_workflow_snapshot() provides appropriate sanity checks.The validation correctly checks:
- Timestamp is not in the future (handles naive datetimes by assuming UTC)
- No overlap between completed and failed step IDs
The docstring appropriately explains why there's no step index validation (unlike FSM which has defined states).
581-666: restore_workflow_state() validates before applying state.Good implementation with validation, clear documentation about no-deep-copy behavior, and comprehensive thread safety warnings. The example showing how to create an isolated copy before restore is helpful.
668-754: get_workflow_snapshot() uses mode="json" for proper serialization.Using
model_dump(mode="json")ensures UUIDs become strings and datetimes become ISO format strings, making the output directly JSON-serializable without custom encoders. This addresses the previous review feedback and provides clear API distinction fromsnapshot_workflow_state().src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
207-258: PII sanitization helper matches docstring and avoids mutating original contextThe
_PII_PATTERNSplussanitize_context_for_logging()implementation looks solid: it builds a new structure recursively for dicts/lists/tuples, applies patterns in documented order (default first, then anyadditional_patterns), and honorsredact_keyscase‑insensitively while leaving non‑string types untouched. The originalcontextdict is not mutated, which aligns with the immutability/defensive‑copy expectations in the docstring.Also applies to: 317-404
tests/unit/nodes/test_node_state_serialization.py (1)
1-144: Comprehensive, behavior-focused coverage for node and snapshot stateThe test module provides thorough coverage for FSM/workflow snapshotting, restoration, validation (timestamps, overlapping step IDs), JSON serialization, immutability, and thread-safety contracts. Use of
pytestmark = pytest.mark.unit, structured fixtures, and explicitModelOnexErrorassertions all align well with the project’s testing and error-handling guidelines.Also applies to: 145-444
…omments - Add deep_copy parameter to snapshot methods for ergonomic deep copying - Add enforce parameter to validate_context_size() with env var support - Add clarifying comments for _workflow_state initialization in mixin
Code Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization capabilities for ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Validation
3. PII Handling (
|
- Add (?<!\d) lookbehind to phone pattern to prevent matching digit sequences embedded in UUIDs (e.g., 446655440000 in UUID suffix) - Rewrite IPv6 patterns to use lookahead/lookbehind instead of \b to correctly handle :: in compressed addresses like 2001:db8::1 - Fix ::1 (loopback) and :: (unspecified) patterns that \b couldn't match at string boundaries Root cause: Phone regex matched trailing UUID digits before UUID pattern could process them, corrupting the redaction output. Fixes CI test failures in test_model_workflow_state_snapshot_pii.py
Code Review: State Serialization for NodeReducer and NodeOrchestratorThis is a well-structured PR that implements state serialization for both NodeReducer and NodeOrchestrator nodes. The implementation follows ONEX patterns and includes comprehensive documentation. Here's my detailed feedback: ✅ Strengths1. Excellent Documentation & ADR
2. Comprehensive Test Coverage
3. Security-Conscious Implementation
4. Migration to Timezone-Aware Datetimes
5. Type Safety
6. Architectural Alignment
|
| Priority | Issue | Action |
|---|---|---|
| 🔴 CRITICAL | Deep copy default is False |
Change default to deep_copy=True for safety |
| 🟡 HIGH | No thread safety enforcement | Add debug-mode assertions for thread violations |
| 🟡 MEDIUM | UUID redaction in PII patterns | Make UUID redaction opt-in, not default |
| 🟢 LOW | Naive datetime handling | Emit warning when naive datetimes are converted |
| 🟢 LOW | workflow_id=None semantics |
Document when None is valid vs error condition |
🎯 Overall Assessment
This is high-quality work with excellent documentation, comprehensive tests, and thoughtful design decisions. The ADR-002 demonstrates deep understanding of Python's immutability constraints and makes a pragmatic choice.
Primary concern: The deep_copy=False default creates a significant foot-gun that undermines the immutability contract. Changing this to deep_copy=True would make the API safer by default while allowing performance-conscious users to opt into shallow copies.
Recommendation: ✅ Approve with changes - address the deep copy default, then merge.
Test Coverage: ✅ Excellent (1,834 lines of tests)
Documentation: ✅ Excellent (ADR, comprehensive docstrings)
Security: ✅ Strong (PII sanitization, size validation)
Architecture: ✅ Follows ONEX patterns
Type Safety: ✅ Strong typing with Pydantic
Great work overall! 🚀
…t docstrings - Preserve schema_version in with_step_completed() and with_step_failed() transitions to ensure derived snapshots maintain source schema version - Add mode="json" to NodeReducer.get_state_snapshot() for proper JSON-native serialization (consistent with NodeOrchestrator.get_workflow_snapshot()) - Rename misleading test method to test_fsm_snapshot_context_access_returns_consistent_value with accurate docstring describing actual test behavior
PR Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state snapshot/restore capabilities for NodeReducer and NodeOrchestrator, enabling workflow replay, debugging, and state persistence. The implementation is comprehensive, well-documented, and follows ONEX architectural patterns. ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Testing
3. Security & Privacy
4. Code Quality
5. Documentation
🔍 Areas for Improvement1. Potential Performance Issue - Deep Copy in Loops (Medium Priority)Location: The Concern: def snapshot_state(self, *, deep_copy: bool = False) -> ModelFSMStateSnapshot | None:
if deep_copy:
return copy.deepcopy(self._fsm_state) # Expensive for large contexts
return self._fsm_stateRecommendation:
2. Missing Validation in
|
High Priority (Should Fix): - Add schema version validation in restore_workflow_state() with test - Add validation warnings when restoring to terminal/completed workflow states - Extract SNAPSHOT_FUTURE_TOLERANCE_SECONDS constant (60s) for clock skew Medium Priority (Nice to Have): - Make UUID redaction opt-in (redact_uuids=False by default) in sanitize_context_for_logging() - Add automatic context size validation on snapshot creation via ONEX_CONTEXT_SIZE_WARN_ON_CREATE env var Documentation: - Add concrete thread safety examples (unsafe vs safe patterns) in class docstrings - Document deep copy performance implications with O-notation complexity Test: 62 tests pass (1 new schema version validation test added)
2684b32 to
23ccb8c
Compare
Merged main branch into feature branch, resolving conflicts in Thread Safety documentation sections. Combined: - MVP design decision context (from main) - Mutable state components listing (from main) - Current limitations and production path (from main) - Concrete code examples for safe/unsafe patterns (from feature)
PR Review: State Serialization for NodeReducer and NodeOrchestratorOverall AssessmentQuality: ✅ Excellent | Recommendation: APPROVE with minor observations This is a well-architected implementation that adds critical state serialization capabilities to enable workflow replay, debugging, and persistence. The code demonstrates strong adherence to the project's conventions and shows thoughtful design decisions. Code Quality Highlights ✅1. Architecture & Design
2. Error Handling
3. Thread Safety
4. Test Coverage
5. Security Considerations
Minor Observations & Suggestions1. Context Size Validation (Low Priority)The context size limits are documented but rely on environment variable configuration: CONTEXT_SIZE_WARN_ON_CREATE_ENV: str = "ONEX_CONTEXT_SIZE_WARN_ON_CREATE"Observation: This is opt-in validation. Consider mentioning in the PR description whether you've considered making this always-on with warnings (non-blocking) in production deployments. Impact: Low - The limits are reasonable (1MB, 100 keys, 5 levels nesting) and documented. 2. Clock Skew Tolerance (Documentation)The 60-second tolerance for future timestamps is hardcoded: SNAPSHOT_FUTURE_TOLERANCE_SECONDS: int = 60Observation: This is reasonable for most distributed systems, but might be worth documenting the rationale for 60s (NTP drift tolerance + network latency). Impact: Very Low - The value is sensible and well-documented in the code. 3. Deep Copy Performance (Consideration)The def snapshot_state(self, *, deep_copy: bool = False) -> ModelFSMStateSnapshot | None:
if deep_copy:
return copy.deepcopy(self._fsm_state)
return self._fsm_state # Reference, not copy!Observation: The documentation warns about this, but callers must remember to use Impact: Low - Good documentation mitigates most risk. 4. Migration from datetime.utcnow() (Positive)Excellent update across 30+ files migrating from deprecated # Before (deprecated)
timestamp=datetime.utcnow()
# After (correct)
timestamp=datetime.now(UTC)Observation: This is a best practice and prevents timezone-naive timestamp issues. Great consistency improvement! Performance ConsiderationsPositive
Watch Items
Security AnalysisStrengths
Recommendations
Test Coverage AnalysisCoverage Quality: ExcellentThe test file demonstrates comprehensive coverage:
Specific Test Strengths
CLAUDE.md Compliance✅ Follows Project Conventions
✅ Architecture Alignment
Specific Code Reviewnode_reducer.py:413-531 (snapshot_state)def snapshot_state(self, *, deep_copy: bool = False) -> ModelFSMStateSnapshot | None:Excellent:
node_orchestrator.py:533-649 (_validate_workflow_snapshot)def _validate_workflow_snapshot(self, snapshot: ModelWorkflowStateSnapshot) -> None:Excellent:
model_workflow_state_snapshot.py:91-150 (Class docstring)Outstanding:
Potential Issues❌ None FoundAfter thorough review, I found no critical bugs, security vulnerabilities, or major issues. The implementation is production-ready. Final RecommendationsBefore Merge
Post-Merge Considerations
SummaryThis PR delivers high-quality state serialization capabilities with:
Recommendation: APPROVE ✅ Great work on this implementation! The attention to detail in documentation, testing, and security considerations is exemplary. Review conducted using omnibase_core/CLAUDE.md v0.4.0 conventions |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/omnibase_core/nodes/node_reducer.py (1)
413-531: FSM snapshot API and validation look solid; only a minor doc nitThe
snapshot_state,_validate_fsm_snapshot,restore_state, andget_state_snapshotimplementations are consistent, type-safe, and aligned with the tests:
- Contract-based validation of
current_stateandhistoryprevents impossible FSM states.- Timestamp handling correctly normalizes naive datetimes to UTC and applies the shared
SNAPSHOT_FUTURE_TOLERANCE_SECONDS.- JSON snapshot helper uses
model_dump(mode="json")and optionaldeep_copyfor isolation, which is exactly what the tests expect.Minor nit: the
snapshot_state/get_state_snapshotdocstrings referencevalidate_context_size()without qualifying where that lives; since this class doesn’t define it, consider either dropping that sentence or pointing explicitly to the helper on the workflow snapshot model to avoid confusion.src/omnibase_core/nodes/node_orchestrator.py (1)
495-989: Workflow snapshot/restore implementation aligns with the model and testsThe new workflow snapshot APIs are well-structured and consistent with the
ModelWorkflowStateSnapshotcontract:
snapshot_workflow_state(deep_copy=...)parallels the FSM API and correctly usescopy.deepcopyfor isolation when requested._validate_workflow_snapshot:
- Enforces
schema_versionagainstWORKFLOW_STATE_SNAPSHOT_SCHEMA_VERSION.- Handles naive vs. aware
created_attimestamps with UTC normalization and a shared skew tolerance.- Detects overlaps between
completed_step_idsandfailed_step_idsand raisesModelOnexErrorwithVALIDATION_ERRORand detailed context.- Emits structured warnings (not hard failures) for “all failed” and “appears complete” terminal cases, which is a good balance between safety and replay/debug use cases.
restore_workflow_statedefers to the validator, then stores the snapshot as-is, matching the immutability guarantees in the model.get_workflow_snapshot(deep_copy=...)correctly usesmodel_dump(mode="json")and optionaldeep_copy, and the docstring accurately documents the JSON-native shapes that tests assert on.If you want to be extra explicit, you could extend the
restore_workflow_statedocstring’s “Raises” section to also mention schema-version mismatches as aVALIDATION_ERROR, but behavior-wise this all looks correct.src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
91-741: Snapshot model, PII redaction, and context-size validation are well designedThe
ModelWorkflowStateSnapshotimplementation looks robust and matches how it’s consumed:
- Immutability:
ConfigDict(frozen=True, extra="forbid", from_attributes=True)plus tuple-based step IDs give a clear immutability contract;with_step_completed/with_step_failedcorrectly create new instances with merged context and incremented step index.- PII redaction:
_PII_PATTERNSand_UUID_PATTERNcover the cases exercised by the new PII tests (emails, phones, SSN, credit cards, IPv4/IPv6, common API-key formats, optional UUID redaction).sanitize_context_for_loggingrecursively copies structures, respectsredact_keys, and preserves non-string types without mutating the original context.- Context-size controls:
validate_context_sizesensibly tracks total key count, JSON-serialized size, and nesting depth, returns(is_valid, warnings), and can escalate to aModelOnexErrorwithEnumCoreErrorCode.VALIDATION_ERRORwhen strict-mode is enabled viaenforceorONEX_CONTEXT_SIZE_STRICT.- The
@model_validatorhook forONEX_CONTEXT_SIZE_WARN_ON_CREATEis a nice non-breaking guardrail, emitting structured warnings with workflow_id context.Minor consistency nit: inside
validate_context_sizeyou importModelOnexErrorfromomnibase_core.errors, while nodes import it fromomnibase_core.models.errors.model_onex_error. If both paths are valid, consider standardizing on one import location for clarity.tests/unit/nodes/test_node_state_serialization.py (1)
145-1884: State-serialization test coverage is comprehensive and aligned with the new APIsThis module does a good job exercising the new snapshot/restore behavior end-to-end:
TestNodeReducerStateSerializationandTestNodeOrchestratorStateSerializationcover both typed snapshot methods and the dict-based helpers, including uninitialized behavior, round-trip restore, invalid state/history, timestamp sanity checks, and schema-version mismatch.TestModelWorkflowStateSnapshotandTestModelFSMStateSnapshotvalidate immutability, factory methods, transition helpers, and serialization (model_dumpvsmode="json"), matching how the nodes consume these models.- Context mutation and concurrent-access tests are a nice touch—they encode the “frozen snapshot vs mutable node” contract and the “no shared node instances across threads” guidance directly into the test suite, which should help catch future regressions. Based on learnings, this matches the project’s threading strategy.
If you want to go even further, you could add a small assertion around the
deep_copy=Truevariants ofsnapshot_state/snapshot_workflow_state/get_*_snapshotto prove isolation of nested structures, but the current suite already provides strong confidence in the serialization APIs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(4 hunks)src/omnibase_core/nodes/node_reducer.py(4 hunks)tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pytests/unit/nodes/test_node_state_serialization.pytests/unit/models/workflow/test_model_workflow_state_snapshot_pii.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/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import nodes from
omnibase_core.nodesusing the unified import path - useNodeCompute,NodeReducer,NodeOrchestrator,NodeEffect(NOT with 'Declarative' suffix)
Files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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/nodes/test_node_state_serialization.pytests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py
🧠 Learnings (2)
📚 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 : Do NOT share node instances across threads - use thread-local instances or separate instances per thread for NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator
Applied to files:
src/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/nodes/node_orchestrator.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: ModelAction pattern should be used in ORCHESTRATOR nodes for lease-based single-writer semantics and workflow-driven action definitions
Applied to files:
src/omnibase_core/nodes/node_orchestrator.py
🧬 Code graph analysis (3)
src/omnibase_core/nodes/node_reducer.py (2)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (1)
ModelFSMStateSnapshot(56-210)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
src/omnibase_core/nodes/node_orchestrator.py (4)
src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)src/omnibase_core/logging/emit.py (1)
emit_log_event_sync(154-183)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(91-741)src/omnibase_core/models/errors/model_onex_error.py (4)
ModelOnexError(49-354)message(248-250)error_code(253-255)context(275-278)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
src/omnibase_core/models/errors/model_onex_error.py (4)
context(275-278)ModelOnexError(49-354)message(248-250)error_code(253-255)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
🪛 Gitleaks (8.30.0)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py
[high] 253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 8/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: claude-review
🔇 Additional comments (2)
src/omnibase_core/nodes/node_reducer.py (1)
32-57: Future-timestamp tolerance constant is reasonable and well-documentedThe introduction of
SNAPSHOT_FUTURE_TOLERANCE_SECONDSand the associated_ERR_FUTURE_TIMESTAMPmessaging gives clear, debuggable behavior around clock skew in FSM snapshots. The inline docs around skew tolerance and where it’s applied in_validate_fsm_snapshot()are helpful and match the behavior in the tests.src/omnibase_core/nodes/node_orchestrator.py (1)
75-127: Thread-safety documentation for NodeOrchestrator matches architectural guidanceThe expanded “Thread Safety” section (unsafe shared-instance example plus three safe patterns) clearly communicates that
NodeOrchestratorinstances are not thread-safe and should not be shared across threads, while snapshots are safe to share. This lines up with the repo’s threading guidance and is reinforced by the new tests inTestConcurrentAccessSafety.
| def test_redacts_aws_access_key(self) -> None: | ||
| """Test redaction of AWS access key.""" | ||
| context = {"key": "AKIAIOSFODNN7EXAMPLE"} | ||
| result = ModelWorkflowStateSnapshot.sanitize_context_for_logging(context) | ||
| assert result["key"] == "[API_KEY_REDACTED]" | ||
|
|
||
| def test_redacts_github_pat(self) -> None: | ||
| """Test redaction of GitHub personal access token.""" | ||
| # Pattern requires exactly 36 alphanumeric chars after ghp_ (use letters only) | ||
| context = {"key": "ghp_abcdefghijklmnopqrstuvwxyzABCDEFGHIJ"} # 36 chars | ||
| result = ModelWorkflowStateSnapshot.sanitize_context_for_logging(context) | ||
| assert result["key"] == "[API_KEY_REDACTED]" | ||
|
|
||
| def test_redacts_github_oauth_token(self) -> None: | ||
| """Test redaction of GitHub OAuth token.""" | ||
| # Pattern requires exactly 36 alphanumeric chars after gho_ (use letters only) | ||
| context = {"key": "gho_abcdefghijklmnopqrstuvwxyzABCDEFGHIJ"} # 36 chars | ||
| result = ModelWorkflowStateSnapshot.sanitize_context_for_logging(context) | ||
| assert result["key"] == "[API_KEY_REDACTED]" | ||
|
|
||
| def test_redacts_slack_token(self) -> None: | ||
| """Test redaction of Slack token.""" | ||
| # Use letters to avoid phone number pattern matching first | ||
| context = {"key": "xoxb-abcdefghijk-lmnopqrstuv-wxyzABCDEFGH"} | ||
| result = ModelWorkflowStateSnapshot.sanitize_context_for_logging(context) | ||
| assert result["key"] == "[API_KEY_REDACTED]" |
There was a problem hiding this comment.
Avoid literal fake API keys so Gitleaks doesn’t block the pipeline
These tests correctly exercise the API-key redaction patterns, but the hard‑coded AWS/GitHub/Slack‑style tokens are being flagged by Gitleaks as potential real secrets.
To keep the behavior while avoiding secret‑scanner noise, build the tokens at runtime instead of embedding full literals:
def test_redacts_aws_access_key(self) -> None:
"""Test redaction of AWS access key."""
- context = {"key": "AKIAIOSFODNN7EXAMPLE"}
+ # Construct a fake AWS-style key at runtime to avoid secret scanners
+ aws_key = "AKIA" + "IOSFODNN7EXAMPLE"
+ context = {"key": aws_key}
def test_redacts_github_pat(self) -> None:
"""Test redaction of GitHub personal access token."""
- # Pattern requires exactly 36 alphanumeric chars after ghp_ (use letters only)
- context = {"key": "ghp_abcdefghijklmnopqrstuvwxyzABCDEFGHIJ"} # 36 chars
+ # Pattern requires exactly 36 alphanumeric chars after ghp_
+ token = "ghp_" + ("a" * 36) # 36 chars
+ context = {"key": token}
def test_redacts_github_oauth_token(self) -> None:
"""Test redaction of GitHub OAuth token."""
- # Pattern requires exactly 36 alphanumeric chars after gho_ (use letters only)
- context = {"key": "gho_abcdefghijklmnopqrstuvwxyzABCDEFGHIJ"} # 36 chars
+ # Pattern requires exactly 36 alphanumeric chars after gho_
+ token = "gho_" + ("a" * 36) # 36 chars
+ context = {"key": token}
def test_redacts_slack_token(self) -> None:
"""Test redaction of Slack token."""
- # Use letters to avoid phone number pattern matching first
- context = {"key": "xoxb-abcdefghijk-lmnopqrstuv-wxyzABCDEFGH"}
+ # Construct fake Slack-style token at runtime to avoid secret scanners
+ token = "xoxb-" + ("A" * 24)
+ context = {"key": token}This keeps the regex behavior identical (same shapes at runtime) while preventing Gitleaks from seeing full secret-like literals.
🤖 Prompt for AI Agents
In tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py around
lines 438-463, the tests include hard-coded AWS/GitHub/Slack-like token literals
which trigger Gitleaks; replace those static secret-like strings with
runtime-constructed equivalents so the regex behavior is unchanged but no full
secret literal appears in source. Concretely, build the AWS key by concatenating
"AKIA" + a generated string of 16 allowed chars, build GitHub tokens by
concatenating the prefix ("ghp_" or "gho_") with a generated 36-char
alphanumeric string (e.g. by repeating or joining parts at runtime), and build
the Slack token similarly (e.g. assemble "xoxb-" + segments created at runtime
ensuring letters to avoid phone patterns); keep assertions the same to expect
"[API_KEY_REDACTED]".
…, and validation Comprehensive resolution of PR #190 review issues for state serialization feature: CRITICAL FIXES: - Fix datetime.utcnow() deprecation in documentation examples - Add Gitleaks allowlist for intentional test fixtures (API key patterns) - Verify timeout marker registration (already compliant) DOCUMENTATION ENHANCEMENTS: - Add comprehensive docstrings to NodeReducer state methods: * snapshot_state() - Return type, thread safety, deep copy behavior * restore_state() - Validation, thread safety WARNING, error conditions * get_state_snapshot() - Dict return type, JSON serialization details - Add comprehensive docstrings to NodeOrchestrator workflow methods: * snapshot_workflow_state() - Return type, thread safety, deep copy behavior * restore_workflow_state() - Validation, thread safety WARNING * get_workflow_snapshot() - Dict return type, JSON serialization details - Add thread safety WARNING sections with concrete unsafe/safe examples - Reference docs/guides/THREADING.md in all docstrings - Document performance characteristics (O(n) deep copy overhead) - Fix test class docstrings to match actual return types SECURITY & VALIDATION: - Add validate_no_pii() class method to ModelWorkflowStateSnapshot * Strict PII detection helper for compliance environments * Supports email, phone, SSN, credit card, IPv4, IPv6, API keys * Configurable UUID detection via check_uuids parameter * Custom pattern support via additional_patterns * Detailed violation reporting with path information - Verify FSM snapshot validation (already fully implemented): * Invalid state detection against FSM contract * Terminal state protection (security feature) * Required context validation * History consistency validation * Timestamp sanity checks with 60s clock skew tolerance NITPICKS & POLISH: - Export WORKFLOW_STATE_SNAPSHOT_SCHEMA_VERSION from public package - Fix imports to use public exports (reduce import churn) - Add clarifying comment for _max_depth semantics - Verify field constraints, JSON mode, schema_version preservation (all correct) API CLARIFICATION: - Verify API differentiation (already correct): * snapshot_workflow_state() returns ModelWorkflowStateSnapshot (Pydantic model) * get_workflow_snapshot() returns dict[str, object] (JSON-serializable) FILES MODIFIED: - docs/reference/API_DOCUMENTATION.md - Fix datetime example - tests/unit/models/core/test_model_route_hop.py - Update comment - tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py - Add Gitleaks allowlist, 21 new tests - src/omnibase_core/nodes/node_reducer.py - Add comprehensive docstrings - src/omnibase_core/nodes/node_orchestrator.py - Add comprehensive docstrings, fix imports - tests/unit/nodes/test_node_state_serialization.py - Fix docstrings, fix imports - src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py - Add validate_no_pii(), clarifying comment - src/omnibase_core/models/workflow/execution/__init__.py - Export constant - src/omnibase_core/models/workflow/__init__.py - Export constant VERIFICATION: ✅ All tests pass (62 state serialization, 64 orchestrator, 74 PII tests) ✅ mypy strict mode: 0 errors ✅ pyright basic mode: 0 errors ✅ Zero datetime.utcnow() usages remain ✅ Comprehensive thread safety documentation ✅ Enhanced PII detection capabilities Resolves all CRITICAL, MAJOR, MINOR, and NITPICK issues from PR #190 review. Ready for release. Correlation-ID: 95cac850-05a3-43e2-9e57-ccbbef683f43
Pull Request Review: State Serialization for NodeReducer and NodeOrchestratorSummaryThis PR implements state serialization capabilities for Overall Assessment: ✅ Approve with Minor Recommendations Code Quality: Excellent ⭐Strengths
Test Coverage: Excellent ⭐⭐
Security Considerations: Very Good 🔒Strengths
Recommendations
Performance Considerations: Good ⚡Strengths
Recommendations
Potential Issues & Questions1.
|
| File | Lines | Assessment |
|---|---|---|
model_workflow_state_snapshot.py |
+884 | ⭐ Excellent - comprehensive model with validation, PII handling, size checks |
node_reducer.py |
+731 | ✅ Good - clean state serialization methods with clear docs |
node_orchestrator.py |
+660 | ✅ Good - consistent with NodeReducer, proper workflow state handling |
test_node_state_serialization.py |
+2136 | ⭐ Excellent - comprehensive test coverage |
test_model_workflow_state_snapshot_pii.py |
+718 | ⭐ Excellent - thorough PII validation tests |
ADR-002-context-mutability-design-decision.md |
+228 | ⭐ Excellent - model ADR documentation |
Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2025-12-15
PR: #190
Closes: OMN-739, OMN-740
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
src/omnibase_core/nodes/node_orchestrator.py (2)
772-779: Consider refining the "workflow appears complete" heuristic.The condition
snapshot.current_step_index >= total_trackedcompares a step index (position in workflow) to the count of tracked steps (completed + failed). These may not correlate - a workflow could have more steps than what's been tracked, or step indices could increment without tracking every step.This is a non-blocking warning, so it won't cause failures, but the heuristic may produce false positives. Consider documenting this limitation in the comment or adjusting the message to indicate it's an approximation.
- # Check if workflow appears complete (all steps either completed or failed) - # This heuristic: step_index >= total_tracked AND we have some tracked steps + # Check if workflow appears complete (all steps either completed or failed) + # This heuristic compares step_index to tracked step count - may be inaccurate + # if workflow has untracked steps or non-sequential step indices elif total_tracked > 0 and snapshot.current_step_index >= total_tracked:
849-884: Docstring has duplicated "Context Considerations" section.The "Context Considerations" heading appears twice (lines 849-859 and 861-884), covering similar content about thread safety and PII risks. Consider consolidating into a single section to improve readability.
src/omnibase_core/nodes/node_reducer.py (2)
737-740: Clarify naive datetime timezone assumption in timestamp validation.When a naive datetime is encountered, it's assumed to be UTC via
replace(tzinfo=UTC). This assumption may not be correct for all contexts (e.g., context timestamps could be in local time). Consider documenting this assumption in the docstring or raising a warning.# Ensure timezone-aware comparison now = datetime.now(UTC) if snapshot_time.tzinfo is None: + # Assume naive datetime is UTC for consistency snapshot_time = snapshot_time.replace(tzinfo=UTC)
848-876: Remove duplicate "Context Considerations" section heading.The docstring contains two consecutive "Context Considerations:" sections (lines 848 and 860). The first covers thread safety with lock patterns, and the second covers PII/size considerations. Consider merging them under a single heading or renaming one for clarity.
- Context Considerations: - This method modifies the internal ``_fsm_state`` attribute, which - is NOT thread-safe: - - - **NOT Safe**: Calling from multiple threads simultaneously - - **NOT Safe**: Calling while another thread reads via ``snapshot_state()`` - - **Recommended**: Use external synchronization (lock) if concurrent - access is required, or use separate NodeReducer instances per thread - - The provided snapshot should not be mutated after calling this method, - as the node stores a direct reference (not a deep copy). - - Context Considerations: + PII and Size Considerations: The restored snapshot's ``context`` dict may contain sensitive data:src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
520-524: Consider pre-compiling regex patterns for performance.The regex patterns are recompiled on each call to
re.sub()inside the loop. For hot paths or large contexts, pre-compiling could improve performance.+ # Pre-compile patterns once at class level + _COMPILED_PII_PATTERNS: ClassVar[list[tuple[re.Pattern[str], str]]] = [ + (re.compile(pattern), replacement) + for pattern, replacement in _PII_PATTERNS + ]However, since this is a sanitization utility typically called before logging (not a hot path), this is a low-priority optimization.
tests/unit/nodes/test_node_state_serialization.py (1)
347-391: Recommend using timezone-aware datetimes in tests for consistency.The tests use naive datetimes (e.g.,
datetime(2099, 12, 31, 23, 59, 59)at line 358 anddatetime(2020, 1, 1, 0, 0, 0)at line 433). While the implementation handles these by assuming UTC, using timezone-aware datetimes would be more explicit and consistent with the model'screated_atfield which usesdatetime.now(UTC).- future_time = datetime(2099, 12, 31, 23, 59, 59) + future_time = datetime(2099, 12, 31, 23, 59, 59, tzinfo=UTC)- past_time = datetime(2020, 1, 1, 0, 0, 0) + past_time = datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC)This also requires adding
UTCto the import at line 12.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (21)
docs/reference/API_DOCUMENTATION.md(1 hunks)src/omnibase_core/models/common/model_effect_metadata.py(1 hunks)src/omnibase_core/models/common/model_node_capabilities_metadata.py(1 hunks)src/omnibase_core/models/common/model_reducer_metadata.py(1 hunks)src/omnibase_core/models/common/model_request_metadata.py(1 hunks)src/omnibase_core/models/common/model_tool_metadata_fields.py(1 hunks)src/omnibase_core/models/configuration/model_git_hub_actions_workflow.py(1 hunks)src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.py(1 hunks)src/omnibase_core/models/configuration/model_git_hub_workflow_data.py(1 hunks)src/omnibase_core/models/configuration/model_git_hub_workflow_defaults.py(1 hunks)src/omnibase_core/models/container/model_service_metadata.py(1 hunks)src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py(1 hunks)src/omnibase_core/models/fsm/model_fsm_state_snapshot.py(2 hunks)src/omnibase_core/models/workflow/__init__.py(2 hunks)src/omnibase_core/models/workflow/execution/__init__.py(1 hunks)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py(1 hunks)src/omnibase_core/nodes/node_orchestrator.py(4 hunks)src/omnibase_core/nodes/node_reducer.py(4 hunks)tests/unit/models/core/test_model_route_hop.py(1 hunks)tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py(1 hunks)tests/unit/nodes/test_node_state_serialization.py(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- tests/unit/models/core/test_model_route_hop.py
- docs/reference/API_DOCUMENTATION.md
- src/omnibase_core/models/common/model_request_metadata.py
🚧 Files skipped from review as they are similar to previous changes (1)
- src/omnibase_core/models/fsm/model_fsm_state_snapshot.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Always use Poetry for Python package management and task execution - never use pip or python directly. Usepoetry runfor all commands (e.g.,poetry run pytest,poetry run mypy,poetry run black)
Always callsuper().__init__(container)in node class constructors where container is of typeModelONEXContainer- this is MANDATORY and handles all boilerplate initialization
UseModelONEXContainer(notModelContainer[T]) in node constructors for dependency injection.ModelContainer[T]is only for wrapping single values with metadata, never for DI in node__init__
Always raiseModelOnexErrorwith anEnumCoreErrorCode, never use genericException. Use the@standard_error_handlingdecorator to eliminate boilerplate try/catch blocks
Use protocol-based dependency injection withcontainer.get_service("ProtocolName")- never get services by concrete class names, and never use isinstance checks with protocols (use duck typing instead)
UseEnumNodeKindfor architectural classification in the ONEX workflow (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR, RUNTIME_HOST) andEnumNodeTypefor specific implementation type discovery (COMPUTE_GENERIC, TRANSFORMER, AGGREGATOR, etc.)
Do NOT poll background jobs repeatedly (AI Agent Anti-Pattern) - instead call operations once with longer timeout (e.g.,wait_up_to=300) or continue with other work. Pre-commit/pre-push can take 2-5 minutes on this codebase (1865+ files with mypy strict)
Files:
src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.pysrc/omnibase_core/models/container/model_service_metadata.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_data.pysrc/omnibase_core/models/common/model_effect_metadata.pysrc/omnibase_core/models/common/model_tool_metadata_fields.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_defaults.pysrc/omnibase_core/models/common/model_node_capabilities_metadata.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/models/workflow/execution/__init__.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pytests/unit/models/workflow/test_model_workflow_state_snapshot_pii.pysrc/omnibase_core/models/common/model_reducer_metadata.pysrc/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.pysrc/omnibase_core/nodes/node_reducer.pytests/unit/nodes/test_node_state_serialization.pysrc/omnibase_core/models/workflow/__init__.py
src/omnibase_core/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/omnibase_core/**/*.py: Achieve 100% mypy strict mode compliance - all functions must have type annotations, warn on functions returning Any, and pass pyright basic mode checks
Format code with Black and sort imports with isort - runpoetry run black src/ tests/andpoetry run isort src/ tests/before committing
Use Pydantic 2.11+ models with proper validation, field descriptions, and examples - ensure models are immutable (frozen=True) where appropriate
Never use generic exceptions - always useModelOnexErrorwith appropriateEnumCoreErrorCodevalues for structured error handling with Pydantic models and context
Use base node classes (NodeEffect, NodeCompute, NodeReducer, NodeOrchestrator) from omnibase_core.nodes - they provide unidirectional left-to-right data flow (EFFECT → COMPUTE → REDUCER → ORCHESTRATOR) with zero boilerplate initialization
Files:
src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.pysrc/omnibase_core/models/container/model_service_metadata.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_data.pysrc/omnibase_core/models/common/model_effect_metadata.pysrc/omnibase_core/models/common/model_tool_metadata_fields.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_defaults.pysrc/omnibase_core/models/common/model_node_capabilities_metadata.pysrc/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/models/workflow/execution/__init__.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/models/common/model_reducer_metadata.pysrc/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.pysrc/omnibase_core/nodes/node_reducer.pysrc/omnibase_core/models/workflow/__init__.py
src/omnibase_core/models/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Add
from_attributes=TruetoConfigDictfor immutable value objects that are nested in other Pydantic models or used in pytest-xdist parallel test execution (enables attribute-based validation instead of class identity checks)
Files:
src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.pysrc/omnibase_core/models/container/model_service_metadata.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_data.pysrc/omnibase_core/models/common/model_effect_metadata.pysrc/omnibase_core/models/common/model_tool_metadata_fields.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_defaults.pysrc/omnibase_core/models/common/model_node_capabilities_metadata.pysrc/omnibase_core/models/workflow/execution/__init__.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/models/common/model_reducer_metadata.pysrc/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.pysrc/omnibase_core/models/workflow/__init__.py
src/omnibase_core/nodes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Import node classes from
omnibase_core.nodes(NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator) - these are the primary v0.4.0+ implementations with declarative YAML contract support
Files:
src/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Do not share node instances across threads without explicit synchronization - each thread must use its own instance or thread-local storage. Nodes are NOT thread-safe by default
Run tests withpoetry run pytest tests/using parallelization (-n4 by default) with 60s timeout per test - disable parallelism only for debugging with-n 0 -xvs
Maintain minimum 60% code coverage across the test suite (12,000+ tests collected) - run withpoetry run pytest tests/ --cov=src/omnibase_core --cov-report=term-missing
Files:
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.pytests/unit/nodes/test_node_state_serialization.py
🧠 Learnings (7)
📚 Learning: 2025-12-15T04:56:21.045Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.045Z
Learning: Applies to src/omnibase_core/models/**/*.py : Add `from_attributes=True` to `ConfigDict` for immutable value objects that are nested in other Pydantic models or used in pytest-xdist parallel test execution (enables attribute-based validation instead of class identity checks)
Applied to files:
src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.pysrc/omnibase_core/models/container/model_service_metadata.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_data.pysrc/omnibase_core/models/common/model_effect_metadata.pysrc/omnibase_core/models/common/model_tool_metadata_fields.pysrc/omnibase_core/models/configuration/model_git_hub_actions_workflow.pysrc/omnibase_core/models/configuration/model_git_hub_workflow_defaults.pysrc/omnibase_core/models/common/model_node_capabilities_metadata.pysrc/omnibase_core/models/common/model_reducer_metadata.pysrc/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
📚 Learning: 2025-12-15T04:56:21.046Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.046Z
Learning: Applies to src/omnibase_core/**/*.py : Use Pydantic 2.11+ models with proper validation, field descriptions, and examples - ensure models are immutable (frozen=True) where appropriate
Applied to files:
src/omnibase_core/models/container/model_service_metadata.pysrc/omnibase_core/models/common/model_tool_metadata_fields.pysrc/omnibase_core/models/common/model_node_capabilities_metadata.pysrc/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.pysrc/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
📚 Learning: 2025-12-15T04:56:21.046Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.046Z
Learning: Applies to src/omnibase_core/**/*.py : Use base node classes (NodeEffect, NodeCompute, NodeReducer, NodeOrchestrator) from omnibase_core.nodes - they provide unidirectional left-to-right data flow (EFFECT → COMPUTE → REDUCER → ORCHESTRATOR) with zero boilerplate initialization
Applied to files:
src/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.py
📚 Learning: 2025-12-15T04:56:21.045Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.045Z
Learning: Applies to tests/**/*.py : Do not share node instances across threads without explicit synchronization - each thread must use its own instance or thread-local storage. Nodes are NOT thread-safe by default
Applied to files:
src/omnibase_core/nodes/node_orchestrator.pysrc/omnibase_core/nodes/node_reducer.pytests/unit/nodes/test_node_state_serialization.py
📚 Learning: 2025-12-15T04:56:21.045Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.045Z
Learning: Applies to src/omnibase_core/nodes/**/*.py : Import node classes from `omnibase_core.nodes` (NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator) - these are the primary v0.4.0+ implementations with declarative YAML contract support
Applied to files:
src/omnibase_core/nodes/node_reducer.py
📚 Learning: 2025-12-15T04:56:21.046Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.046Z
Learning: Applies to **/*.py : Do NOT poll background jobs repeatedly (AI Agent Anti-Pattern) - instead call operations once with longer timeout (e.g., `wait_up_to=300`) or continue with other work. Pre-commit/pre-push can take 2-5 minutes on this codebase (1865+ files with mypy strict)
Applied to files:
tests/unit/nodes/test_node_state_serialization.py
📚 Learning: 2025-12-15T04:56:21.046Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T04:56:21.046Z
Learning: Applies to tests/**/*.py : Run tests with `poetry run pytest tests/` using parallelization (-n4 by default) with 60s timeout per test - disable parallelism only for debugging with `-n 0 -xvs`
Applied to files:
tests/unit/nodes/test_node_state_serialization.py
🧬 Code graph analysis (5)
src/omnibase_core/models/workflow/execution/__init__.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(91-873)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
src/omnibase_core/models/errors/model_onex_error.py (4)
context(275-278)ModelOnexError(49-354)message(248-250)error_code(253-255)src/omnibase_core/enums/enum_core_error_code.py (1)
EnumCoreErrorCode(9-168)
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (2)
sanitize_context_for_logging(423-537)validate_no_pii(540-663)
tests/unit/nodes/test_node_state_serialization.py (2)
src/omnibase_core/models/fsm/model_fsm_state_snapshot.py (3)
ModelFSMStateSnapshot(56-214)create_initial(138-162)transition_to(164-214)src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (4)
ModelWorkflowStateSnapshot(91-873)create_initial(394-420)with_step_completed(797-834)with_step_failed(836-873)
src/omnibase_core/models/workflow/__init__.py (1)
src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (1)
ModelWorkflowStateSnapshot(91-873)
⏰ 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 8/20)
- GitHub Check: Tests (Split 10/20)
- GitHub Check: Tests (Split 20/20)
- GitHub Check: Tests (Split 17/20)
- GitHub Check: Tests (Split 14/20)
- GitHub Check: Tests (Split 15/20)
- GitHub Check: Tests (Split 11/20)
- GitHub Check: Tests (Split 12/20)
- GitHub Check: Tests (Split 19/20)
- GitHub Check: Tests (Split 18/20)
- GitHub Check: Tests (Split 16/20)
- GitHub Check: Tests (Split 4/20)
- GitHub Check: Tests (Split 13/20)
- GitHub Check: Tests (Split 2/20)
- GitHub Check: Tests (Split 7/20)
- GitHub Check: Tests (Split 3/20)
- GitHub Check: Tests (Split 5/20)
- GitHub Check: Tests (Split 9/20)
- GitHub Check: Tests (Split 6/20)
- GitHub Check: Tests (Split 1/20)
- GitHub Check: claude-review
🔇 Additional comments (39)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py (1)
50-60: Model config matches Pydantic/value-object guidelinesThe added
model_config(immutability viafrozen=True,validate_assignment=True, andfrom_attributes=Truewithextra="forbid"anduse_enum_values=False) is consistent with the value-object and pytest-xdist guidance for models undersrc/omnibase_core/models/**. The explanatory comment also clearly documents the rationale and reference to CLAUDE.md.As per coding guidelines and retrieved learnings, this looks correct.
src/omnibase_core/models/common/model_tool_metadata_fields.py (1)
22-26: LGTM! Excellent documentation offrom_attributes=Trueconfiguration.The explanatory comment clearly describes why
from_attributes=Trueis needed for pytest-xdist parallel execution and references the documentation. This configuration change follows the coding guidelines and enables Pydantic to instantiate the model from objects with matching attributes across different class identities.As per coding guidelines and learnings.
src/omnibase_core/models/common/model_node_capabilities_metadata.py (1)
22-26: LGTM! Consistent implementation.The
from_attributes=Trueconfiguration and explanatory comment follow the same pattern as other models in this PR, ensuring consistent behavior across the codebase for pytest-xdist parallel execution.src/omnibase_core/models/common/model_effect_metadata.py (1)
24-28: LGTM! Consistent configuration.The
from_attributes=Truesetup matches the pattern established across the other models in this PR.src/omnibase_core/models/configuration/model_git_hub_actions_workflow.py (1)
21-30: LGTM! Well-configured model with proper documentation.The
model_configincludes comprehensive settings (frozen=False,strict=False,extra="forbid",from_attributes=True) along with clear documentation explaining thefrom_attributes=Truebehavior.src/omnibase_core/models/container/model_service_metadata.py (1)
51-55: LGTM! Proper configuration for service metadata.The
from_attributes=Trueconfiguration with explanatory comment follows the established pattern and coding guidelines.src/omnibase_core/models/configuration/model_git_hub_workflow_defaults.py (2)
11-20: LGTM! Consistent model configuration.The
model_configwithfrom_attributes=Trueandextra="forbid"follows the pattern established across the PR, with clear documentation.
22-24: LGTM! New field properly documented.The
runfield for default shell and working directory is properly typed and includes a clear description.src/omnibase_core/models/configuration/model_git_hub_workflow_data.py (1)
25-34: LGTM! Comprehensive configuration.The
model_configwithfrom_attributes=Trueand supporting settings is properly documented and consistent with related workflow models.src/omnibase_core/models/configuration/model_git_hub_workflow_concurrency.py (1)
11-21: LGTM! Complete configuration for workflow concurrency.The
model_configincludes all necessary settings includingfrom_attributes=Trueandpopulate_by_name=True(needed for thecancel-in-progressfield alias), with clear documentation.src/omnibase_core/models/common/model_reducer_metadata.py (1)
24-28: LGTM - helpful documentation for thefrom_attributes=Trueconfig.The comment clearly explains the purpose and references the relevant documentation. This helps maintainers understand why this configuration is necessary for parallel test execution.
src/omnibase_core/models/workflow/execution/__init__.py (1)
12-18: LGTM - proper public API exposure for workflow state snapshot.The new exports follow the established pattern and maintain alphabetical ordering in
__all__. This correctly exposes theModelWorkflowStateSnapshotand its associated schema constants for external consumption.Also applies to: 22-25, 31-31
src/omnibase_core/nodes/node_orchestrator.py (5)
8-15: LGTM - imports correctly organized for new snapshot functionality.The new imports for
copy,timedelta, logging utilities, andModelWorkflowStateSnapshotare appropriately added to support the state serialization feature.Also applies to: 27-30
33-65: LGTM - well-documented constants for snapshot validation.The 60-second tolerance for clock skew is reasonable for distributed systems. Error messages provide good context with named format placeholders for debugging.
91-127: LGTM - comprehensive thread safety documentation.The updated docstring provides clear, practical examples for safe and unsafe patterns. The three safe patterns (separate instances, external synchronization, and immutable snapshots) give developers actionable guidance. Based on learnings, nodes are NOT thread-safe by default, and this documentation correctly conveys that.
495-642: LGTM - clean implementation with good defensive copy option.The
snapshot_workflow_state()method correctly handles thedeep_copyparameter to address isolation concerns. The distinction fromget_workflow_snapshot()is clearly documented. The extensive docstring provides valuable context for thread safety and PII considerations.
915-1073: LGTM - correctly implements dict serialization as requested in past review.The
get_workflow_snapshot()now returnsdict[str, object] | Nonewith proper JSON-native serialization viamodel_dump(mode="json"). Thedeep_copyparameter provides isolation when needed. This addresses the past review comment about API duplication and dict-returning methods.src/omnibase_core/models/workflow/__init__.py (1)
18-25: LGTM - proper re-export at workflow package level.The new exports follow the established pattern and maintain organization with API models and Execution models clearly separated. This enables consumers to import
ModelWorkflowStateSnapshotandWORKFLOW_STATE_SNAPSHOT_SCHEMA_VERSIONdirectly fromomnibase_core.models.workflow.Also applies to: 35-35, 39-39
tests/unit/models/workflow/test_model_workflow_state_snapshot_pii.py (4)
1-24: LGTM - well-organized test module with comprehensive PII sanitization coverage.The test file is well-structured with clear test classes for each category of PII redaction. The
pytestmark = pytest.mark.unitcorrectly marks all tests as unit tests.
555-718: LGTM - comprehensive test coverage forvalidate_no_piimethod.The test class covers all PII pattern types, nested structures, custom patterns, and the important behavior of reporting only the first match per value. The tests align well with the implementation described in the relevant code snippets.
271-314: LGTM - important type preservation tests.These tests verify that non-string types (integers, floats, booleans, None, UUID objects, datetimes) pass through sanitization unchanged. This is critical for maintaining data integrity in workflow contexts.
438-466: No gitleaks configuration found in CI pipeline — inline comments are unnecessary.The repository does not have gitleaks configured or running in CI workflows. The
# gitleaks:allowinline comments have no effect since gitleaks is not part of the pipeline. The test tokens are safe as-is and do not require the inline allowlist directives.Likely an incorrect or invalid review comment.
src/omnibase_core/nodes/node_reducer.py (4)
8-74: LGTM: Well-structured imports and error constants.The new imports are appropriate for the snapshot functionality, and the error messages are descriptive with format placeholders for context. The
SNAPSHOT_FUTURE_TOLERANCE_SECONDSconstant is well-documented for distributed system clock skew tolerance.
108-147: Excellent thread-safety documentation with practical examples.The updated docstring provides clear guidance on safe threading patterns, which aligns with the coding guideline that nodes are NOT thread-safe by default. The code examples effectively demonstrate both unsafe and safe patterns.
430-557: Well-designed snapshot method with proper isolation support.The
deep_copyparameter provides flexibility for different use cases, and the docstring thoroughly documents thread safety, PII considerations, and performance trade-offs. The implementation is straightforward and correct.
938-1078: LGTM: Clean JSON serialization with proper mode="json" usage.The method correctly uses Pydantic's
mode="json"for JSON-native type conversion, and thedeep_copyparameter provides isolation when needed. The docstring clearly explains the difference fromsnapshot_state().src/omnibase_core/models/workflow/execution/model_workflow_state_snapshot.py (6)
1-38: Well-documented immutability considerations in module docstring.The module docstring clearly explains the limitations of Python's frozen Pydantic models with mutable containers and establishes the immutability contract for context dicts. This upfront documentation helps prevent misuse.
40-88: LGTM: Well-documented constants with sensible defaults.The context size limits and environment variables are appropriately documented, and the ONEX_ prefix convention is followed for environment variables.
338-341: LGTM: Timezone-aware datetime properly implemented.The
created_atfield correctly usesdatetime.now(UTC)instead of the deprecateddatetime.utcnow(), addressing the previous review comment.
343-391: Well-designed opt-in context size validation on creation.The validator provides a non-blocking way to detect context size issues during development without breaking existing code. The conditional import inside the validator is acceptable since this path is only taken when the environment variable is set.
665-795: LGTM: Comprehensive context size validation with documented behavior.The method provides flexible validation with configurable limits and enforcement. The note explaining that nesting depth only counts dict nesting (not lists) is helpful for understanding the semantics.
797-873: Clean immutable transition methods.Both
with_step_completed()andwith_step_failed()correctly implement the immutable update pattern, creating new snapshots with proper context isolation using spread operators.tests/unit/nodes/test_node_state_serialization.py (7)
52-142: LGTM: Well-structured test fixtures.The fixtures provide a clean setup for testing FSM and workflow state serialization. The FSM contract includes a complete state machine with initial, operational, and terminal states.
145-153: LGTM: Docstring accurately describes return types.The class docstring correctly differentiates between
snapshot_state()returning a strongly-typedModelFSMStateSnapshotandget_state_snapshot()returning a JSON-serializable dict.
699-1172: Comprehensive test coverage for NodeOrchestrator state serialization.The tests thoroughly cover the new workflow state serialization API including
snapshot_workflow_state(),restore_workflow_state(),update_workflow_state(), andget_workflow_snapshot(). The async tests forexecute_workflow_from_contract()verify that workflow state is automatically captured post-execution.
1174-1538: LGTM: Thorough validation testing for workflow snapshot restoration.The tests cover critical validation scenarios including future timestamp rejection, step ID overlap detection, and schema version mismatch handling. These tests ensure the restore operation is robust against invalid or corrupted snapshots.
1540-1817: LGTM: Comprehensive model behavior tests.The tests verify immutability guarantees, transition methods, and serialization behavior for both
ModelWorkflowStateSnapshotandModelFSMStateSnapshot. The immutability tests properly expectValidationErrorwhen attempting to modify frozen fields.
1840-1849: Test may pass despite potential nested mutation issue.The test correctly notes that "nested dicts may or may not be deep copied depending on implementation." Since Pydantic doesn't deep copy by default, the assertion at line 1846 (
snapshot.context["key"] == "original_value") passes because top-level key mutation doesn't affect the snapshot's copy. However, the nested dict mutation at line 1843 (nested_dict["inner"] = "mutated_inner") would likely affect the snapshot.Consider adding an explicit assertion to document this known limitation:
# Snapshot should not be affected by mutations assert snapshot.context["key"] == "original_value" assert "new_key" not in snapshot.context - # Note: nested dicts may or may not be deep copied depending on implementation - # This tests the current behavior + # Note: nested dicts are NOT deep copied by Pydantic frozen models. + # This is a known limitation documented in the model docstring. + # Verifying that nested mutation DOES affect the snapshot: + assert snapshot.context["nested"]["inner"] == "mutated_inner" # Known limitationOr if this is undesirable behavior, consider deep copying context in the model.
1948-2136: LGTM: Appropriate thread safety documentation tests.The tests verify that thread safety warnings are documented in class docstrings and demonstrate correct usage patterns (separate instances per thread). Avoiding actual threading in favor of documentation verification is a pragmatic approach that prevents flaky CI while ensuring developers are informed about safety requirements. This aligns with the coding guideline that nodes are NOT thread-safe by default. Based on learnings, each thread should use its own instance.
Summary
Implement state serialization methods for NodeReducer and NodeOrchestrator to enable:
Changes
NodeReducer (OMN-739)
snapshot_state() -> ModelFSMStateSnapshot | Nonerestore_state(snapshot: ModelFSMStateSnapshot) -> Noneget_state_snapshot() -> dict[str, object]NodeOrchestrator (OMN-740)
ModelWorkflowStateSnapshotmodel for workflow statesnapshot_workflow_state() -> ModelWorkflowStateSnapshot | Nonerestore_workflow_state(snapshot: ModelWorkflowStateSnapshot) -> Noneget_workflow_snapshot() -> dict[str, object]Infrastructure
_workflow_statetracking toMixinWorkflowExecutionModelWorkflowStateSnapshotfrommodels/workflow/execution/WorkflowContextTypetype alias for flexible contextFiles Changed
node_reducer.pynode_orchestrator.pymixin_workflow_execution.pymodel_workflow_state_snapshot.pytype_workflow_context.pytest_node_state_serialization.pyTest plan
Linear Issues
Summary by CodeRabbit
New Features
Tests
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.