Skip to content

feat(executor): implement missing workflow executor features [OMN-656] - #157

Merged
jonahgabriel merged 5 commits into
mainfrom
jonah/omn-656-mvp-05-nodeorchestrator-sequential-executor
Dec 11, 2025
Merged

jonahgabriel merged 5 commits into
mainfrom
jonah/omn-656-mvp-05-nodeorchestrator-sequential-executor

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 11, 2025 •

Copy link
Copy Markdown
Contributor

Summary

  • Add _build_workflow_context for step output aggregation between workflow steps
  • Add _validate_json_payload for early payload validation during action creation
  • Enhance _get_topological_order with priority-aware ordering using heapq
  • Add verify_workflow_integrity for contract hash verification (tamper detection)
  • Add TypedDictWorkflowContext for type-safe context structure

Test plan

  • All 21 existing workflow executor tests pass
  • mypy --strict passes on all modified files
  • Pre-commit hooks pass
  • CI pipeline passes

Linear ticket

OMN-656

Summary by CodeRabbit

  • New Features

    • Added workflow integrity checks with deterministic hashing and a public verification API; execution results now include integrity metadata
    • Introduced a typed workflow execution context to propagate step outputs safely across steps and waves
    • Deterministic, priority-aware step ordering with action-priority clamping and stricter JSON payload validation
  • Tests

    • Expanded unit tests covering context propagation, payload validation, hashing, ordering, execution flows, performance, and edge cases

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

Implement the 4 missing features for NodeOrchestrator Sequential Executor:

- Add _build_workflow_context for step output aggregation
- Add _validate_json_payload for early payload validation
- Enhance _get_topological_order with priority-aware ordering using heapq
- Add verify_workflow_integrity for contract hash verification
- Add TypedDictWorkflowContext for type-safe context structure

All features pass mypy --strict and existing unit tests.
@coderabbitai

coderabbitai Bot commented Dec 11, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a TypedDict for workflow execution context and re-exports it; introduces deterministic SHA-256 workflow hashing and integrity verification, JSON payload validation, priority-clamped priority ordering, and workflow_context propagation into step execution. Model and metadata types gained hashing helpers/fields and tests were added/expanded.

Changes

Cohort / File(s) Summary
Type definition
src/omnibase_core/types/typed_dict_workflow_context.py
New module defining TypedDictWorkflowContext (TypedDict) with fields: workflow_uuid_str: str, completed_steps: list[str], step_outputs: dict[str, object], step_count: int. Exported via __all__.
API re-export
src/omnibase_core/types/__init__.py
Imports and re-exports TypedDictWorkflowContext by adding it to __all__.
Workflow executor
src/omnibase_core/utils/workflow_executor.py
Adds _compute_workflow_hash() and public verify_workflow_integrity() for deterministic SHA-256 hashing/verification; builds and propagates a JSON-friendly workflow_context into step contexts; introduces _validate_json_payload() and _json_default_for_workflow() for serialization; clamps step->action priority and uses a priority-aware heap/topological ordering; appends workflow_hash to result.metadata; updates __all__.
Model step context
src/omnibase_core/models/workflow/execution/model_declarative_workflow_step_context.py
ModelDeclarativeWorkflowStepContext.__init__ extended with optional `workflow_context: TypedDictWorkflowContext
Workflow model hashing
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py
Adds compute_workflow_hash(self) -> str to deterministically SHA-256 hash a workflow definition (excluding runtime metadata) and with_computed_hash(self) -> ModelWorkflowDefinition to return a copy with workflow_hash set.
Workflow metadata
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
Adds optional field `workflow_hash: str
Tests — executor & model hashing
tests/unit/utils/test_workflow_executor.py, tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
Extensive new/expanded tests covering hash computation/format/determinism, verify_workflow_integrity, _build_workflow_context scenarios, _validate_json_payload behavior, priority ordering/topological sort behavior, workflow_context propagation, large-scale performance/size limit scenarios, and immutability/idempotence of with_computed_hash().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review focus:
    • src/omnibase_core/utils/workflow_executor.py — hashing integration, payload serialization behavior, priority clamping and topological ordering logic, and metadata augmentation.
    • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py — deterministic serialization and correct exclusion of runtime fields for hashing.
    • Tests — ensure determinism assumptions and edge-case coverage match implementation.
    • ModelDeclarativeWorkflowStepContext wiring to confirm workflow_context availability and typing.

Possibly related PRs

Poem

🐰 I nibbled bytes beneath the moonlit stash,
I drew a map and sealed it with a hash,
I counted hops and kept each hop in sight,
I sorted leaps by priority at night,
Now workflows hop along, all snug and rash.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: implementing missing workflow executor features including context building, JSON validation, priority-aware ordering, integrity verification, and type-safe context structure.
Docstring Coverage ✅ Passed Docstring coverage is 98.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jonah/omn-656-mvp-05-nodeorchestrator-sequential-executor

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/omnibase_core/utils/workflow_executor.py (2)

729-791: Add payload validation or update docstring.

The docstring (Lines 743-744) claims the function raises ModelOnexError for non-JSON-serializable payloads, but the implementation doesn't call _validate_json_payload. Since _validate_json_payload exists but is unused, consider:

  1. Add validation before constructing the action
  2. Or remove the "Raises" claim if validation is deferred

Apply this diff to add pre-flight validation:

 def _create_action_for_step(
     step: ModelWorkflowStep,
     workflow_id: UUID,
 ) -> ModelAction:
     """
     Create action for workflow step.
 
     Args:
         step: Workflow step to create action for
         workflow_id: Parent workflow ID
 
     Returns:
         ModelAction for step execution
 
     Raises:
         ModelOnexError: If payload is not JSON-serializable
     """
+    # Build payload
+    payload = {
+        "workflow_id": str(workflow_id),
+        "step_id": str(step.step_id),
+        "step_name": step.step_name,
+    }
+    
+    # Validate JSON serializability
+    _validate_json_payload(payload, context=step.step_name)
+    
     # Map step type to action type
     action_type_map = {

820-826: Add None check for step.priority before clamping.

Line 825 uses min(step.priority, 10) which will raise TypeError if step.priority is None. While ModelWorkflowStep may have a default value, defensive coding suggests handling the None case explicitly.

Apply this diff to add None handling:

     step_order_key: dict[UUID, tuple[int, int]] = {}
     for idx, step in enumerate(workflow_steps):
-        clamped_priority = min(step.priority, 10)
+        clamped_priority = min(step.priority or 1, 10)
         step_order_key[step.step_id] = (clamped_priority, idx)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 607e870 and 5d54b5a.

📒 Files selected for processing (3)
  • src/omnibase_core/types/__init__.py (2 hunks)
  • src/omnibase_core/types/typed_dict_workflow_context.py (1 hunks)
  • src/omnibase_core/utils/workflow_executor.py (7 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{py,pyc,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use Poetry for Python package management and task execution - never use pip or python directly

Files:

  • src/omnibase_core/types/__init__.py
  • src/omnibase_core/types/typed_dict_workflow_context.py
  • src/omnibase_core/utils/workflow_executor.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with EnumCoreErrorCode for all error handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators.error_handling to eliminate try/catch boilerplate
Use protocol names (e.g., 'ProtocolEventBus') with container.get_service(), never concrete class names
Use EnumNodeKind for high-level architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation types
Maintain 100% strict mypy compliance - all functions must have type annotations and zero mypy errors
Do NOT share node instances across threads without explicit synchronization - most ONEX components are not thread-safe by default
Implement mixin system using available mixins (MixinDiscoveryResponder, MixinEventHandler, MixinNodeExecutor, etc.) for cross-cutting concerns
Use ModelEventEnvelope for inter-service communication and event-driven processing patterns
Python version requirement: 3.12+
Structure project following the omnibase_core directory layout with clear separation of concerns (constants, container, decorators, discovery, enums, errors, events, infrastructure, logging, mixins, models, nodes, primitives, types, utils, validation)

Files:

  • src/omnibase_core/types/__init__.py
  • src/omnibase_core/types/typed_dict_workflow_context.py
  • src/omnibase_core/utils/workflow_executor.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Import node classes from omnibase_core.nodes (NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator), not from infrastructure submodules

Files:

  • src/omnibase_core/types/__init__.py
  • src/omnibase_core/types/typed_dict_workflow_context.py
  • src/omnibase_core/utils/workflow_executor.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Format code with black and sort imports with isort before committing

Files:

  • src/omnibase_core/types/__init__.py
  • src/omnibase_core/types/typed_dict_workflow_context.py
  • src/omnibase_core/utils/workflow_executor.py
🧬 Code graph analysis (2)
src/omnibase_core/types/__init__.py (1)
src/omnibase_core/types/typed_dict_workflow_context.py (1)
  • TypedDictWorkflowContext (12-29)
src/omnibase_core/utils/workflow_executor.py (3)
src/omnibase_core/types/typed_dict_workflow_context.py (1)
  • TypedDictWorkflowContext (12-29)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-163)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (1)
  • ModelWorkflowDefinition (16-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Smoke Tests
  • GitHub Check: claude-review
🔇 Additional comments (8)
src/omnibase_core/types/typed_dict_workflow_context.py (1)

12-29: LGTM - Well-structured TypedDict definition.

The TypedDict provides clear type safety for workflow context with appropriate field types and comprehensive documentation.

src/omnibase_core/types/__init__.py (1)

182-182: LGTM - Consistent with module export patterns.

The import and export additions follow the established conventions in this module.

Also applies to: 299-299

src/omnibase_core/utils/workflow_executor.py (6)

11-13: LGTM - Necessary imports for new features.

The added imports support the hash computation, priority-aware topological ordering, and type-safe context building.

Also applies to: 37-37


839-860: LGTM - Correct priority-aware topological ordering.

The heap-based Kahn's algorithm correctly prioritizes steps with lower priority values and earlier declaration order when dependencies are equal. The implementation properly handles the in-degree tracking and neighbor updates.


913-941: LGTM - Proper deterministic hash computation.

The function correctly uses sorted keys and a default serializer to ensure consistent hashing across executions. SHA-256 is appropriate for integrity verification.


944-988: LGTM - Robust integrity verification implementation.

The function properly handles optional hash verification, provides clear error messages on mismatch, and follows ONEX error handling patterns with ModelOnexError and EnumCoreErrorCode.


992-998: LGTM - Appropriate public API exposure.

Correctly exports verify_workflow_integrity as a public function while keeping internal helpers private.


613-684: Verify that _build_workflow_context is integrated into execution flow.

The function is well-implemented but appears unused. Neither _execute_sequential nor _execute_parallel call this function or populate the step_outputs dictionary that it expects. The execution functions initialize step_outputs tracking but never capture actual outputs from steps.

Run the following script to verify if this function is called anywhere in the codebase:

#!/bin/bash
# Search for calls to _build_workflow_context
rg -n '_build_workflow_context\s*\(' --type=py

Comment thread src/omnibase_core/utils/workflow_executor.py Outdated
@claude

claude Bot commented Dec 11, 2025

Copy link
Copy Markdown

PR Review: feat(executor): implement missing workflow executor features [OMN-656]

Summary

This PR adds 4 new utility functions to the workflow executor for step output aggregation, JSON payload validation, priority-aware topological ordering, and workflow integrity verification. The implementation is well-documented with comprehensive docstrings and examples.


✅ Strengths

1. Code Quality & Documentation

  • Excellent docstrings with detailed examples for all new functions
  • Clear type annotations throughout
  • Follows repository conventions for structured errors using ModelOnexError
  • Proper use of EnumCoreErrorCode.VALIDATION_ERROR for error handling

2. Type Safety

  • New TypedDictWorkflowContext provides type-safe structure
  • Properly exported in __all__ and types module
  • Mypy strict mode compliance mentioned in test plan

3. Priority-Aware Ordering

  • Smart use of heapq for priority queue implementation in _get_topological_order
  • Priority clamping (1-1000 → 1-10) is well-documented
  • Tuple-based ordering (priority, declaration_index) ensures deterministic results

4. Security - Integrity Verification

  • verify_workflow_integrity provides tamper detection via SHA-256 hashing
  • Gracefully handles None expected_hash for backwards compatibility
  • Uses deterministic JSON serialization with sort_keys=True and default=str

⚠️ Issues & Concerns

1. CRITICAL: Dead Code - Functions Are Not Used 🚨

The 4 new functions are defined but never called in the workflow executor:

# Defined but not used anywhere in execution flow:
- _build_workflow_context()
- _validate_json_payload() 
- verify_workflow_integrity()
- _compute_workflow_hash()

Evidence:

  • Grep shows these functions only appear in their definitions and docstring examples
  • No calls in _execute_sequential, _execute_parallel, or _execute_batch
  • Not called in execute_workflow main entry point

Impact:

  • PR title claims "implement missing features" but they're not integrated
  • Functions will rot without test coverage or actual usage
  • verify_workflow_integrity is exported in __all__ but never imported/used anywhere

Recommendation: Either:

  1. Integrate these functions into the execution flow where needed, OR
  2. Mark this as "scaffolding for future work" and create follow-up tickets

2. Missing Test Coverage 🔴

Zero tests for the new functionality:

  • No tests for _build_workflow_context
  • No tests for _validate_json_payload
  • No tests for verify_workflow_integrity / _compute_workflow_hash
  • No tests for priority-aware ordering in _get_topological_order

Current test status:

  • Test file has 21 test classes covering existing workflow executor behavior
  • None exercise the new priority logic or validation functions
  • PR checklist shows "[ ] CI pipeline passes" unchecked

Recommendation:

# Minimum required tests:
class TestBuildWorkflowContext:
    def test_context_structure()
    def test_uuid_to_string_conversion()
    
class TestValidateJsonPayload:
    def test_valid_payload()
    def test_invalid_payload_raises()  # Lambda, set, etc.
    
class TestWorkflowIntegrity:
    def test_hash_computation_deterministic()
    def test_verify_success()
    def test_verify_tamper_detection()
    def test_verify_none_hash_skips()
    
class TestPriorityOrdering:
    def test_priority_ordering()  # Low priority value = higher precedence
    def test_declaration_order_tiebreaker()
    def test_priority_clamping()  # Verify min(priority, 10) behavior

3. Priority Field Semantics Issue ⚠️

Inconsistency between model and implementation:

# ModelWorkflowStep.priority field:
priority: int = Field(
    default=100,
    description="Step execution priority (higher = more priority)",  # ❌ Wrong!
    ge=1, le=1000,
)

# But _get_topological_order implementation:
heapq.heappush(heap, (priority, idx, step_id))  # Lower value = higher priority

Problem:

  • Model says "higher value = more priority" (like nice values in Unix)
  • Heap implementation treats lower values as higher priority (min-heap)
  • Users will set priority=1000 expecting highest priority but get lowest

Fix options:

  1. Update model docstring: "Step execution priority (lower = higher priority, like Unix nice)"
  2. Negate in implementation: heapq.heappush(heap, (-priority, idx, step_id)) (use max-heap)
  3. Add validation tests showing expected behavior

4. Potential Hash Collision Risk 🔶

def _compute_workflow_hash(workflow_definition: ModelWorkflowDefinition) -> str:
    workflow_dict = workflow_definition.model_dump()
    serialized = json.dumps(workflow_dict, sort_keys=True, default=str)
    return hashlib.sha256(serialized.encode()).hexdigest()

Issue: default=str converts all non-JSON types to strings, potentially causing collisions:

UUID("00000000-0000-0000-0000-000000000001") → "00000000-0000-0000-0000-000000000001"
datetime(2025, 1, 1) → "2025-01-01 00:00:00"  # Depends on repr

Risk:

  • Different UUIDs/datetimes might serialize identically
  • model_dump() already handles UUIDs → str, so default=str is redundant for well-typed models

Recommendation:

# Option 1: Use model_dump with mode='json' (Pydantic handles serialization)
workflow_dict = workflow_definition.model_dump(mode='json')
serialized = json.dumps(workflow_dict, sort_keys=True)  # No default needed

# Option 2: Explicit handler
def json_default(obj):
    if isinstance(obj, UUID):
        return str(obj)
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
    
serialized = json.dumps(workflow_dict, sort_keys=True, default=json_default)

5. Minor: TypedDict Naming Convention 📝

class TypedDictWorkflowContext(TypedDict):
    workflow_uuid_str: str  # ❌ Redundant "uuid" in field name
    completed_steps: list[str]
    step_outputs: dict[str, object]

Observation:

  • Field name workflow_uuid_str is verbose - context already implies it's a workflow identifier
  • Inconsistent with completed_steps (not completed_step_uuids_str)

Suggestion:

class TypedDictWorkflowContext(TypedDict):
    workflow_id: str  # Simpler, matches payload convention in _create_action_for_step
    completed_steps: list[str]
    step_outputs: dict[str, object]
    step_count: int

6. Missing Integration Points 🔗

Where should these functions be called?

  1. _validate_json_payload → Should be called in _create_action_for_step:
def _create_action_for_step(step, workflow_id):
    payload = {
        "workflow_id": str(workflow_id),
        "step_id": str(step.step_id),
        "step_name": step.step_name,
    }
    _validate_json_payload(payload, context=step.step_name)  # ← Add here
    return ModelAction(...)
  1. verify_workflow_integrity → Should be called at workflow start:
async def execute_workflow(workflow_definition, workflow_steps, workflow_id, ...):
    # Get hash from contract metadata if present
    expected_hash = workflow_definition.workflow_metadata.get("contract_hash")
    verify_workflow_integrity(workflow_definition, expected_hash)  # ← Add here
    # ... continue execution
  1. _build_workflow_context → Should be used in sequential/parallel execution:
async def _execute_sequential(...):
    completed_step_ids: set[UUID] = set()
    step_outputs: dict[UUID, object] = {}
    
    for step in ordered_steps:
        # Build context for this step
        context = _build_workflow_context(workflow_id, completed_step_ids, step_outputs)
        # Pass context to step execution somehow...

Question: How does WorkflowStepExecutionContext relate to TypedDictWorkflowContext? Should they be merged?


🔍 Performance Considerations

Priority Clamping

clamped_priority = min(step.priority, 10)  # Clamps 1-1000 → 1-10

Question: Why clamp to 10? This reduces granularity from 1000 buckets to 10 buckets.

  • If intentional for reducing heap operations, document the tradeoff
  • If unintentional, consider: clamped_priority = step.priority (no clamping)

Hash Computation Performance

# Called on every verify_workflow_integrity call
workflow_dict = workflow_definition.model_dump()  # Potentially expensive for large workflows
serialized = json.dumps(workflow_dict, sort_keys=True, default=str)

Recommendation: If workflows are large (100+ steps), consider:

  • Caching computed hash in workflow definition (memoization)
  • Computing hash once during contract load, storing in metadata

🛡️ Security Considerations

✅ Good: Tamper Detection

  • SHA-256 is cryptographically secure for integrity checks
  • Proper error context exposes both hashes for debugging

⚠️ Consideration: Hash Storage

  • Where is expected_hash stored? If in the workflow definition itself, it can be tampered with
  • Best practice: Store hash externally (database, separate metadata file)

📋 Recommendations Summary

Must Fix (Blocking)

  1. Integrate functions into execution flow or mark as scaffolding
  2. Add test coverage for all 4 new functions and priority ordering
  3. Fix priority semantics - clarify model docstring vs implementation

Should Fix (High Priority)

  1. Use model_dump(mode='json') instead of default=str for hash computation
  2. Simplify TypedDict field name (workflow_uuid_str → workflow_id)

Consider

  1. Document priority clamping rationale or remove clamping
  2. Add security notes about external hash storage for integrity checks
  3. Clarify relationship between TypedDictWorkflowContext and WorkflowStepExecutionContext

📊 Review Checklist

  • ✅ Code follows repository conventions (Poetry, mypy strict, error handling)
  • ✅ Type safety with proper annotations
  • ✅ Excellent documentation
  • ❌ Missing test coverage (0 tests for new functions)
  • ❌ Dead code (functions not integrated into execution)
  • ⚠️ Priority semantics unclear/inconsistent
  • ⚠️ Hash computation uses risky default=str

🎯 Verdict

Conditional Approval - Code quality is high, but integration and testing are required before merge.

The implementation is solid scaffolding, but without:

  1. Actual usage in the execution flow
  2. Test coverage demonstrating behavior
  3. Resolution of priority semantics

...this PR adds maintenance burden without delivering working features.

Suggested Path Forward:

  • Option A: Add integration + tests in this PR (recommended)
  • Option B: Merge as "scaffolding" and create immediate follow-up tickets with acceptance criteria for integration

File References:

  • src/omnibase_core/utils/workflow_executor.py:613-684 - _build_workflow_context
  • src/omnibase_core/utils/workflow_executor.py:687-727 - _validate_json_payload
  • src/omnibase_core/utils/workflow_executor.py:802-860 - _get_topological_order (priority logic)
  • src/omnibase_core/utils/workflow_executor.py:913-988 - Hash functions
  • src/omnibase_core/types/typed_dict_workflow_context.py:1-32 - TypedDict
  • src/omnibase_core/models/contracts/model_workflow_step.py:119-124 - Priority field

Linear: OMN-656

- Integrate _validate_json_payload into _create_action_for_step
- Add 21 new unit tests for workflow executor functions:
  - TestBuildWorkflowContext (4 tests)
  - TestValidateJsonPayload (6 tests)
  - TestVerifyWorkflowIntegrity (3 tests)
  - TestComputeWorkflowHash (3 tests)
  - TestPriorityOrdering (5 tests)

All 42 tests pass, mypy --strict passes.
@claude

claude Bot commented Dec 11, 2025

Copy link
Copy Markdown

Code Review - PR #157: Workflow Executor Enhancements

Summary

This PR adds four important features to the workflow executor: step output aggregation, JSON payload validation, priority-aware topological ordering, and workflow integrity verification. The implementation is solid overall with excellent test coverage (477 new test lines). Here's my detailed feedback:


✅ Strengths

1. Excellent Test Coverage

  • 477 lines of new tests for 240 lines of new code (~2:1 ratio)
  • Comprehensive edge case coverage for all new functions
  • Tests are well-organized into clear test classes

2. Type Safety

  • New TypedDictWorkflowContext provides compile-time type checking
  • Proper type annotations throughout (dict[str, object], str | None)
  • Follows project's strict mypy compliance standards

3. Clear Documentation

  • Detailed docstrings with Args/Returns/Raises sections
  • Excellent inline examples showing usage patterns
  • Comments explain design choices (e.g., priority clamping)

4. Fail-Fast Validation

  • _validate_json_payload catches serialization issues early (workflow_executor.py:778-779)
  • Prevents runtime failures later in the event system
  • Provides clear error context with step names

🔍 Issues & Suggestions

1. CRITICAL: Priority Behavior May Be Unintuitive ⚠️

Location: _get_topological_order (workflow_executor.py:826-832)

Issue: The comment says "lower value = higher priority" but this contradicts common intuition where priority 1000 should execute before priority 1.

# Current implementation:
clamped_priority = min(step.priority, 10)  # Priority 1 executes first

Questions:

  • Is this intentional? Does ModelWorkflowStep document that lower = higher priority?
  • Should the docstring be more explicit about this behavior?
  • Consider renaming to execution_order if this isn't truly "priority"

Recommendation: Add a docstring to ModelWorkflowStep.priority field clarifying that lower values execute first.


2. Hash Determinism Concerns ⚠️

Location: _compute_workflow_hash (workflow_executor.py:944)

Issue: Using default=str for JSON serialization may not be deterministic across Python versions or UUID implementations.

# Current:
serialized = json.dumps(workflow_dict, sort_keys=True, default=str)

Problems:

  • str(uuid.UUID(...)) representation could theoretically vary
  • str(datetime(...)) format might change across locales/versions
  • Pydantic's model_dump() output could change between Pydantic versions

Recommendation:

# More robust approach:
serialized = json.dumps(
    workflow_dict, 
    sort_keys=True, 
    default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else str(o)
)

Or better yet, use model_dump(mode='json') which guarantees JSON-serializable output:

workflow_dict = workflow_definition.model_dump(mode='json')
serialized = json.dumps(workflow_dict, sort_keys=True)

3. Unused Function ℹ️

Location: _build_workflow_context (workflow_executor.py:613-684)

Observation: This well-designed function is not called anywhere in the current codebase.

Questions:

  • Is this for future use in sequential/parallel execution?
  • Should it be used now in _execute_sequential or _execute_parallel?
  • If not used yet, consider marking as TODO or creating a follow-up ticket

Recommendation: Either integrate it now or add a comment: # TODO: Integrate in OMN-XXX for step context propagation


4. Payload Validation Edge Case

Location: _validate_json_payload (workflow_executor.py:687-725)

Minor Issue: The function validates the payload dict but doesn't validate nested step configuration that might come from step.config or step.parameters.

Current scope (workflow_executor.py:772-779):

payload: dict[str, object] = {
    "workflow_id": str(workflow_id),  # Always serializable
    "step_id": str(step_id),          # Always serializable  
    "step_name": step.step_name,      # Always serializable
}

Question: Should validation also cover step.config or step.parameters if they're added to the payload later?

Recommendation: Fine for now, but document this limitation in the docstring.


5. Test Coverage Gap

Location: test_workflow_executor.py:774-778

Observation: Tests check for nested context structure but this seems fragile:

additional_ctx = exc_info.value.context.get("additional_context", {})
inner_ctx = additional_ctx.get("context", {})

Issue: This depends on ModelOnexError's internal **context wrapping behavior. If ModelOnexError changes, these tests break.

Recommendation: Either:

  1. Use exc_info.value.context["payload_keys"] directly if ModelOnexError supports it
  2. Add a helper method to ModelOnexError: get_nested_context(key)

🛡️ Security

✅ No security concerns:

  • Hash function (SHA-256) is appropriate for integrity checks
  • No SQL injection or code execution risks
  • Validation prevents malicious payloads

📊 Performance

✅ Minimal performance impact:

  • heapq operations are O(n log n) - same complexity as original deque approach
  • JSON serialization for validation happens once per step (acceptable)
  • Hash computation is only on-demand (not in hot path)

Note: The priority heap adds slight overhead vs. simple deque, but provides valuable ordering guarantees.


🔧 Code Quality

Minor Style Issues:

  1. Inconsistent import style (workflow_executor.py:11):

    import heapq  # Should be with other stdlib imports
  2. Magic number (workflow_executor.py:831):

    clamped_priority = min(step.priority, 10)  # Extract to constant

    Suggest:

    MAX_ACTION_PRIORITY = 10  # ModelAction's maximum priority value
    clamped_priority = min(step.priority, MAX_ACTION_PRIORITY)

📋 Checklist

  • ✅ Tests pass (21 existing + new tests)
  • ✅ mypy --strict compliance
  • ✅ Well-documented code
  • ✅ Follows ONEX patterns
  • ⏳ CI pipeline pending
  • ❓ Integration testing needed for _build_workflow_context

🎯 Recommendations

Before Merge:

  1. Fix hash determinism - Use model_dump(mode='json') (HIGH)
  2. Document priority semantics - Clarify lower=higher (MEDIUM)
  3. Extract magic constant - MAX_ACTION_PRIORITY = 10 (LOW)

Follow-up Tickets:

  1. Integrate _build_workflow_context in actual execution flow
  2. Add integration test for priority ordering in real workflows
  3. Consider adding workflow hash to ModelWorkflowDefinition.metadata

✨ Conclusion

Overall Assessment: Approve with minor changes ✅

This is high-quality work with excellent test coverage. The new features are well-designed and the code follows project conventions. The hash determinism issue should be addressed before merge, but everything else can be follow-up work.

Great job on the comprehensive testing and documentation! 🚀

Estimated Effort to Address: 15-30 minutes for critical fixes.

- Add "Used by" docstring to _validate_json_payload (addresses dead code concern)
- Add None check for step.priority in _get_topological_order
- Integrate _build_workflow_context in sequential/parallel execution
- Add workflow_hash to result metadata for integrity verification
- Add WorkflowStepExecutionContext.workflow_context parameter
- Add 14 new tests for all changes (56 total tests now)
@claude

claude Bot commented Dec 11, 2025

Copy link
Copy Markdown

Code Review - PR #157: Workflow Executor Features

✅ Overall Assessment

This PR successfully implements 4 critical missing features for NodeOrchestrator's Sequential Executor. The implementation is well-structured, thoroughly tested, and follows omnibase_core conventions. Recommendation: Approve with minor suggestions below.


🎯 Strengths

1. Excellent Test Coverage (1,211 new lines of tests)

  • 56 total tests across all new features
  • Well-organized test classes for each function
  • Edge cases thoroughly covered (empty inputs, None handling, invalid data)
  • Tests verify both success and error paths

2. Type Safety ✅

  • New TypedDictWorkflowContext provides compile-time type safety
  • All functions have proper type annotations
  • Should pass mypy --strict (as claimed)

3. Documentation Quality 📚

  • Comprehensive docstrings with examples
  • Clear rationale for design decisions (e.g., priority clamping)
  • "Used by" comments address dead code concerns

4. Proper Error Handling

  • Uses ModelOnexError consistently (not generic exceptions) ✅
  • Validation errors include helpful context
  • Error messages are actionable

🔍 Code Quality Analysis

_build_workflow_context ✅

Purpose: Aggregate step outputs for inter-step data flow

Strengths:

  • Clean UUID→string conversion for JSON compatibility
  • Well-documented with extensive examples
  • Proper integration in both sequential and parallel execution paths

Observations:

  • Line 508-514: Context is created but not currently used in parallel execution
    _context = WorkflowStepExecutionContext(
        step, workflow_id, completed_step_ids, wave_context
    )
    # Context available for future step execution logic
    Question: Is this intentionally preparing for future work? The comment suggests yes, but clarify if this is technical debt or intentional scaffolding.

_validate_json_payload ✅

Purpose: Fail-fast validation for JSON serializability

Strengths:

  • Early validation prevents runtime errors downstream
  • Clear error messages with step context
  • Integrated into _create_action_for_step (line 820)

Minor Suggestion:

  • Consider adding default=str to json.dumps() to handle common types (UUID, datetime) gracefully:
    json.dumps(payload, default=str)  # More forgiving for UUID/datetime
    However, current strict approach may be intentional to enforce explicit string conversion. If so, document this design choice.

_get_topological_order (Priority-Aware) ✅

Purpose: Deterministic step ordering with priority and declaration order

Strengths:

  • Elegant use of heapq for priority queue
  • Clamping priority from 1-1000 to 1-10 is well-documented
  • None-safe with explicit default (line 873)
  • Heap tuple structure (priority, index, step_id) ensures deterministic ordering

Excellent Design:

# Lower priority value = higher priority (min-heap behavior)
clamped_priority = min(step.priority, 10) if step.priority is not None else 1
step_order_key[step.step_id] = (clamped_priority, idx)

Question: Why clamp to 1-10 when ModelWorkflowStep allows 1-1000? Is this for compatibility with ModelAction.priority (which has 1-10 range)? If so, document this cross-model constraint.


verify_workflow_integrity ✅

Purpose: Tamper detection via SHA-256 hashing

Strengths:

  • Deterministic hashing with sort_keys=True
  • Uses default=str for non-JSON types (UUID, datetime)
  • Graceful None handling (skip verification if no hash provided)
  • Hash stored in result metadata (line 155)

Security Consideration:

  • SHA-256 is appropriate for integrity verification (not cryptographic signing)
  • If contracts need cryptographic verification, consider HMAC or digital signatures in future

Performance Note:

  • model_dump() + json.dumps() could be expensive for large workflows
  • For massive workflows (>1000 steps), consider caching or lazy hashing
  • Current implementation is fine for typical use cases

🧪 Testing Analysis

Test Structure ✅

  • TestBuildWorkflowContext (4 tests): Empty, single, multiple steps, partial outputs
  • TestValidateJsonPayload (6 tests): Valid, empty, lambda, custom objects, nested
  • TestVerifyWorkflowIntegrity (3 tests): None hash, matching hash, mismatching hash
  • TestComputeWorkflowHash (3 tests): Deterministic, step changes, metadata changes
  • TestPriorityOrdering (5 tests): Priority ordering, declaration order, mixed

Coverage Gaps (Minor)

  1. Large-scale workflows: No tests for 100+ steps (performance/memory)
  2. Concurrent execution: No thread-safety tests (though likely not required)
  3. Unicode/special characters: Payload validation could test non-ASCII data

🔒 Security Review

✅ No Security Concerns Identified

  • Input validation is thorough
  • No SQL injection risks (no database queries)
  • No command injection risks (no shell execution)
  • Hash computation is deterministic and tamper-evident

JSON Serialization Safety

  • _validate_json_payload prevents arbitrary object injection
  • Lambda and custom class rejection is correct (tests on lines 927, 949)

⚡ Performance Considerations

Algorithmic Complexity

Function Complexity Notes
_build_workflow_context O(n) n = number of completed steps
_validate_json_payload O(p) p = payload size (JSON serialization)
_get_topological_order O(n log n + e) n = steps, e = edges (heap operations)
_compute_workflow_hash O(d) d = workflow definition size

All acceptable for typical workflow sizes (10-100 steps).

Memory Usage

  • step_outputs dict grows linearly with workflow size
  • For workflows with large outputs (e.g., 1GB data blobs), consider output references instead of inline storage

📋 Recommendations

Critical (Must Fix)

None - all blocking issues resolved

High Priority (Should Fix)

  1. Clarify unused context variable (line 508-514 in _execute_parallel)

    • If intentional scaffolding, add TODO/FIXME comment
    • If dead code, remove it
  2. Document priority clamping rationale

    • Explain why 1-1000 → 1-10 conversion exists
    • Link to ModelAction.priority constraint if applicable

Low Priority (Nice to Have)

  1. Add performance tests for large workflows (100+ steps)
  2. Consider relaxing JSON validation to allow UUID/datetime with default=str
  3. Add workflow hash to contract metadata (not just result metadata) for persistence

🎓 Adherence to CLAUDE.md

Requirement Status Evidence
✅ Use ModelOnexError (not Exception) Pass Lines 763-767, 970
✅ Poetry for all commands Pass Mentioned in commit messages
✅ Type annotations (mypy strict) Pass All functions typed
✅ Comprehensive docstrings Pass Extensive examples provided
✅ Test coverage (60%+ required) Pass 56 new tests
✅ No backwards compatibility hacks Pass Clean implementation

📊 Final Metrics

  • Lines Added: 1,534 (+285 production, +1,211 tests, +38 types)
  • Lines Deleted: 19 (mostly refactored code)
  • Test-to-Code Ratio: 4.2:1 (excellent!)
  • Functions Added: 4 core + 1 public API
  • Type Safety: 100% (all functions typed)

✅ Approval Recommendation

APPROVE - This PR is production-ready with minor documentation improvements suggested above.

Rationale:

  1. Implements all 4 missing features as specified
  2. Comprehensive test coverage (56 tests, 4.2:1 ratio)
  3. Follows all omnibase_core conventions
  4. No security vulnerabilities identified
  5. Performance characteristics acceptable
  6. Type-safe and well-documented

Suggested Next Steps:

  1. Address documentation clarifications (priority clamping, unused context)
  2. Merge after CI passes (all 20 splits should complete in ~3 minutes based on benchmarks)
  3. Consider follow-up PR for performance tests on large workflows

🙏 Acknowledgment

Excellent work on this implementation! The attention to detail (deterministic hashing, priority-aware ordering, comprehensive tests) demonstrates strong engineering discipline.

Correlation ID: review-pr157-20251211-workflow-executor

…sh [OMN-656]

- Clarify unused _context variable with TODO comment for future scaffolding
- Document priority clamping rationale (1-1000 → 1-10 conversion)
- Add 11 performance tests for large workflows (100+ steps)
- Relax JSON validation to allow UUID/datetime with custom handler
- Add workflow_hash field to contract metadata for persistence/caching

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py (1)

43-50: LGTM! Optional workflow hash field for persistence and caching.

The field is appropriately typed as str | None with a clear description. Consider whether to add a field validator to ensure the hash is a valid 64-character hex string when set, which would catch invalid hashes at model construction time rather than at runtime.

Optional validation could be added:

from pydantic import field_validator

@field_validator("workflow_hash")
@classmethod
def validate_hash_format(cls, v: str | None) -> str | None:
    if v is not None and (len(v) != 64 or not all(c in "0123456789abcdef" for c in v)):
        raise ValueError("workflow_hash must be a 64-character hex string")
    return v
tests/unit/utils/test_workflow_executor.py (1)

988-1001: UUID permissive mode test has unused datetime import.

Line 990 imports datetime but it's not used in this specific test method. However, it's used in the subsequent test test_datetime_passes_in_permissive_mode at line 1005. Consider moving the import to the module level or to the test that uses it.

     def test_uuid_passes_in_permissive_mode(self) -> None:
         """Test UUID objects are accepted in default permissive mode (strict=False)."""
-        from datetime import datetime
-
         from omnibase_core.utils.workflow_executor import _validate_json_payload
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37fae89 and a5f5635.

📒 Files selected for processing (5)
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (2 hunks)
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py (1 hunks)
  • src/omnibase_core/utils/workflow_executor.py (19 hunks)
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (1 hunks)
  • tests/unit/utils/test_workflow_executor.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,pyc,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use Poetry for Python package management and task execution - never use pip or python directly

Files:

  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py
  • tests/unit/utils/test_workflow_executor.py
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with EnumCoreErrorCode for all error handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators.error_handling to eliminate try/catch boilerplate
Use protocol names (e.g., 'ProtocolEventBus') with container.get_service(), never concrete class names
Use EnumNodeKind for high-level architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation types
Maintain 100% strict mypy compliance - all functions must have type annotations and zero mypy errors
Do NOT share node instances across threads without explicit synchronization - most ONEX components are not thread-safe by default
Implement mixin system using available mixins (MixinDiscoveryResponder, MixinEventHandler, MixinNodeExecutor, etc.) for cross-cutting concerns
Use ModelEventEnvelope for inter-service communication and event-driven processing patterns
Python version requirement: 3.12+
Structure project following the omnibase_core directory layout with clear separation of concerns (constants, container, decorators, discovery, enums, errors, events, infrastructure, logging, mixins, models, nodes, primitives, types, utils, validation)

Files:

  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Import node classes from omnibase_core.nodes (NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator), not from infrastructure submodules

Files:

  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py
  • tests/unit/utils/test_workflow_executor.py
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Format code with black and sort imports with isort before committing

Files:

  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py
  • src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py
  • tests/unit/utils/test_workflow_executor.py
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Ensure minimum 60% test coverage for all code changes, tracked via pytest --cov
Use pytest with 4 parallel workers by default locally (via pytest-xdist -n4) and respect 60-second test timeout

Files:

  • tests/unit/utils/test_workflow_executor.py
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
🧬 Code graph analysis (3)
tests/unit/utils/test_workflow_executor.py (1)
src/omnibase_core/utils/workflow_executor.py (8)
  • _compute_workflow_hash (1045-1073)
  • _build_workflow_context (676-747)
  • _validate_json_payload (777-849)
  • verify_workflow_integrity (1076-1120)
  • _get_topological_order (932-992)
  • _create_action_for_step (852-921)
  • get_execution_order (280-355)
  • _has_dependency_cycles (995-1042)
tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (4)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (3)
  • ModelWorkflowDefinition (21-118)
  • compute_workflow_hash (53-89)
  • with_computed_hash (91-118)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py (1)
  • ModelWorkflowDefinitionMetadata (14-57)
src/omnibase_core/models/primitives/model_semver.py (1)
  • ModelSemVer (24-143)
src/omnibase_core/utils/workflow_executor.py (1)
  • _compute_workflow_hash (1045-1073)
src/omnibase_core/utils/workflow_executor.py (3)
src/omnibase_core/types/typed_dict_workflow_context.py (1)
  • TypedDictWorkflowContext (12-29)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-163)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (1)
  • ModelWorkflowDefinition (21-118)
⏰ 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: claude-review
  • GitHub Check: Smoke Tests
🔇 Additional comments (21)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (2)

53-89: Well-structured deterministic hashing implementation.

The implementation correctly:

  • Excludes workflow_hash to avoid circular dependency
  • Uses sort_keys=True for deterministic JSON output
  • Uses default=str to handle UUIDs and datetimes

However, the docstring states it excludes "runtime metadata (timestamps, UUIDs generated at runtime)" but the code only explicitly excludes workflow_hash. If any other runtime-generated fields exist or are added later, they would be included in the hash. Verify this aligns with the intended behavior.


91-118: Immutable pattern correctly applied for hash computation.

The use of model_copy(update=...) ensures the original instance remains unchanged while creating a new instance with the computed hash. This is the correct Pydantic pattern for immutable updates.

tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (3)

1-43: Comprehensive test fixture and imports.

The fixture provides a well-structured baseline ModelWorkflowDefinition for testing hash functionality. All necessary imports are present and used.


337-366: Important integration test validating contract vs executor hash behavior.

This test documents the intentional design difference:

  • Contract's compute_workflow_hash() excludes workflow_hash field for persistence use cases
  • Executor's _compute_workflow_hash includes all fields for runtime integrity

The test correctly verifies both produce valid 64-char hex strings without asserting they must be equal, which aligns with the documented design intent.


399-434: Deduplication test validates content-based hashing.

The test correctly demonstrates that identical workflow definitions produce identical hashes, enabling cache deduplication. The use of an inner factory function ensures clean, isolated test data.

tests/unit/utils/test_workflow_executor.py (4)

746-772: Good test coverage for workflow hash in execution results.

This test validates the complete flow: hash is computed, stored in metadata, has correct format (64 hex chars), and matches the expected computed hash. This ensures integrity verification works end-to-end.


897-940: Thorough validation testing for JSON payload serializability.

Tests cover key scenarios:

  • Valid payloads pass
  • Empty payloads pass
  • Lambdas raise ModelOnexError with correct error code
  • Custom objects raise errors
  • Error context includes payload keys and step context

The assertion on nested context structure (lines 936-940) correctly navigates the ModelOnexError context hierarchy.


1738-1795: Defensive tests for None priority handling are well-designed.

Using MagicMock to simulate None priority values tests code resilience against potential future model changes. The tests verify that:

  • _get_topological_order treats None priority as default (priority 1)
  • _create_action_for_step defaults None priority to 1

This defensive coding approach aligns with robust software practices.


2029-2042: Performance test class is well-structured with clear documentation.

The test patterns (linear chain, wide parallel, mixed, diamond) cover realistic workflow topologies. Performance targets (<5s for 100 steps, <100ms for topological sort) are reasonable and account for CI variability.

src/omnibase_core/utils/workflow_executor.py (12)

8-27: Excellent module-level documentation for priority clamping.

The docstring clearly explains:

  • The intentional design of step priority (1-1000) vs action priority (1-10)
  • The clamping rule: action_priority = min(step.priority, 10)
  • That this is expected behavior, not an error condition
  • References to related architecture documentation

This prevents future confusion and aligns with the coding guidelines for comprehensive documentation.


146-147: Workflow hash computation and metadata storage correctly integrated.

The hash is computed after validation passes (line 147) and stored in result metadata (line 176), enabling downstream integrity verification. This follows the pattern documented in the PR objectives.

Also applies to: 175-176


412-432: Workflow context building and step output tracking for sequential execution.

The implementation correctly:

  • Builds context from prior step outputs before each step
  • Passes workflow_context to WorkflowStepExecutionContext
  • Stores action.payload as step output for subsequent steps

Note: Using action.payload as the step output means downstream steps receive the action's payload structure, not actual execution results. This is consistent with the current design where the executor emits actions rather than executing steps directly.


549-559: TODO comment for future context usage in parallel execution.

The TODO comment at lines 550-556 documents scaffolding for future enhancements. The context variable is prefixed with underscore (_context) to indicate it's currently unused, which is the correct Python convention for intentionally unused variables. This is acceptable for incremental development.


676-747: Well-documented workflow context builder.

_build_workflow_context is cleanly implemented with:

  • Clear docstring with usage example
  • UUID-to-string conversion for JSON compatibility
  • Returns properly typed TypedDictWorkflowContext
  • Aggregates completed steps and their outputs

750-774: Restrictive JSON default handler is a good security practice.

_json_default_for_workflow only allows UUID and datetime types, rejecting all other non-JSON-native types. This prevents accidental serialization of sensitive objects and provides clear error messages when unsupported types are encountered.


777-849: Comprehensive JSON payload validation with strict mode support.

The implementation:

  • Validates payloads are JSON-serializable before action creation
  • Supports strict mode (only native JSON types) and permissive mode (UUID/datetime allowed)
  • Raises ModelOnexError with helpful context (payload keys, step context)
  • Detailed docstring explains both modes with examples

This addresses the past review comment about unused validation - it's now called in _create_action_for_step at line 903.


890-921: Action creation with validated payloads.

The implementation now:

  1. Builds the payload dict explicitly (lines 896-900)
  2. Validates JSON serializability via _validate_json_payload (line 903)
  3. Creates the ModelAction with the validated payload

This addresses the past review comment about integrating payload validation into action creation.


932-992: Priority-aware topological ordering using heap-based Kahn's algorithm.

The implementation correctly:

  • Clamps priorities to 1-10 range (line 957)
  • Uses (clamped_priority, declaration_index, step_id) tuples for heap ordering
  • Lower priority value = higher importance (processed first)
  • Falls back to declaration order for equal priorities
  • Handles None priority defensively (defaults to 1)

This provides deterministic ordering that respects both dependencies and priority hints.


1045-1073: Executor hash computation includes all fields (intentionally different from contract hash).

Unlike ModelWorkflowDefinition.compute_workflow_hash() which excludes workflow_hash, this executor-level function hashes the entire workflow definition including all fields. This is intentional:

  • Contract hash: for persistence/caching (stable across hash-setting)
  • Executor hash: for runtime integrity verification (complete snapshot)

The tests in test_model_workflow_definition.py (lines 337-366) explicitly document and verify this design difference.


1076-1120: Integrity verification with proper null-safety and error context.

verify_workflow_integrity:

  • Correctly skips verification when expected_hash is None
  • Uses timing-safe comparison (string equality) for hash matching
  • Raises ModelOnexError with comprehensive context (expected/actual hash, workflow name)
  • Error code VALIDATION_ERROR is appropriate for tamper detection

1123-1130: Public API correctly updated to expose verify_workflow_integrity.

The __all__ list now includes the new public function, making it available for external consumers to verify workflow integrity before execution.

@claude

claude Bot commented Dec 11, 2025

Copy link
Copy Markdown

PR Review: Workflow Executor Features [OMN-656]

Summary

This PR adds critical missing features to the workflow executor with high quality implementation, comprehensive test coverage (1,886 added test lines), and excellent documentation.


✅ Strengths

1. Exceptional Documentation

  • Module-level docstring: Excellent explanation of priority clamping design rationale
  • Comprehensive function docstrings with purpose, args, returns, raises, and examples
  • Strategic inline comments for complex logic
  • Architecture references to design docs

2. Outstanding Test Coverage

80+ test cases covering:

  • Happy paths: Sequential/parallel/batch execution modes
  • Edge cases: Empty workflows, disabled steps, circular dependencies
  • Error scenarios: Invalid payloads, hash mismatches, step failures
  • Performance tests: 100-200 step workflows with timing validation
  • Priority ordering: Comprehensive tests for clamping and declaration order
  • Context building: Step output aggregation across waves

Test file: 2,629 lines with detailed scenario coverage.

3. Type Safety

  • New TypedDictWorkflowContext provides structured typing
  • Strategic type hints: dict[str, object] for payloads, UUID for IDs
  • Proper None handling in optional fields

4. Security & Integrity

  • Deterministic hashing: SHA-256 with sorted JSON keys
  • Tamper detection: verify_workflow_integrity validates contracts
  • JSON payload validation: Fail-fast validation prevents serialization failures
  • Circular dependency prevention in hash computation

5. Priority-Aware Topological Ordering

Lines 932-992: Excellent heapq-based implementation:

  • Clamped priority (1-10) for consistent ordering
  • Declaration order tiebreaker
  • Kahn's algorithm O(V + E) complexity
  • Well-commented design rationale

6. Wave-Based Context Sharing

Lines 585-589: Smart parallel execution isolation - steps in same wave can't see each other's outputs, but subsequent waves access prior outputs.


🔍 Areas for Improvement

1. Duplicate Hash Computation Logic

Locations: model_workflow_definition.py:54-90 and workflow_executor.py:1045-1073

Recommendation: Consolidate by delegating executor's _compute_workflow_hash to model's compute_workflow_hash method for consistency.

2. Unused Context Variable

Location: workflow_executor.py:557

_context created but never used in parallel path. TODO comment explains future scaffolding. Current approach acceptable with OMN-656 reference.

3. Test Coverage Gaps (Minor)

  • Add test for with_computed_hash() on frozen Pydantic model
  • Consider test for workflow size limits (DoS prevention)

🛡️ Security Assessment

Strengths:
✅ Input validation prevents injection
✅ Deterministic hashing prevents collisions
✅ Tamper detection for contracts
✅ No eval/exec
✅ Pydantic validation

Overall: Strong security posture with proper validation.


📊 Performance

Strengths:
✅ O(V + E) Kahn's algorithm
✅ Lazy per-wave context building
✅ asyncio.gather for parallel execution
✅ Performance tests validate 100-200 step workflows

No regressions expected.


🧪 Test Coverage

Total: 80+ test cases, 2,629 lines

Coverage exceeds project 60% requirement. Exceptional quality.


📝 Code Quality

CLAUDE.md Adherence:
✅ Poetry usage
✅ 100% type coverage
✅ ModelOnexError (no generic Exception)
✅ Comprehensive docstrings
✅ Pydantic best practices
✅ No backwards compatibility hacks

Best Practices:
✅ Pure functions
✅ Separation of concerns
✅ Fail-fast validation
✅ Strategic comments


🎯 Recommendations

High Priority:

  1. Add test for with_computed_hash() on frozen model
  2. Consolidate hash computation logic

Medium Priority:
3. Add workflow size limit (DoS prevention)


🏁 Final Verdict

Status: ✅ APPROVED (with minor recommendations)

Exceptional engineering quality:

  • ✅ Comprehensive test coverage
  • ✅ Excellent documentation
  • ✅ Type safety
  • ✅ Security
  • ✅ Performance
  • ✅ Convention adherence

Production-ready with minor improvement opportunities. Test coverage is exemplary.


Reviewed by: Claude Code (Sonnet 4.5)
Date: 2025-12-11

…OMN-656]

- Delegate _compute_workflow_hash() to model's compute_workflow_hash()
  for DRY consistency and reduced code duplication
- Remove unused hashlib import from workflow_executor.py
- Add TestModelWorkflowDefinitionFrozenBehavior tests for immutability
- Add TestWorkflowSizeLimits tests for DoS prevention (7 tests):
  - Large workflow step count (100 steps)
  - Deep dependency chains (50 steps)
  - Fan-in patterns (20 predecessors)
  - Execution order determinism
  - Multiple fan-in points
  - Graceful validation failure
  - Varying step types
@claude

claude Bot commented Dec 11, 2025

Copy link
Copy Markdown

Code Review - PR #157: Workflow Executor Enhancements

Summary

This PR adds critical missing features to the workflow executor: step output aggregation, payload validation, priority-aware ordering, and integrity verification. The implementation is well-structured with excellent test coverage (2,300+ lines of new tests).


✅ Strengths

1. Excellent Documentation

  • Comprehensive docstrings with examples for all new functions
  • Module-level docstring explains priority clamping design decision
  • Clear inline comments explaining complex logic (heap ordering, context building)
  • Architecture references to CONTRACT_DRIVEN_NODEORCHESTRATOR_V1_0.md

2. Strong Type Safety

  • New TypedDictWorkflowContext provides type-safe context structure
  • Proper use of dict[str, object] for JSON-serializable payloads
  • Strategic use of object type for runtime flexibility (as per project conventions)
  • All functions have complete type annotations

3. Robust Test Coverage

  • 2,300+ lines of new tests covering:
    • Context building with various step output scenarios
    • Payload validation (valid/invalid cases, strict mode)
    • Hash computation and integrity verification
    • Priority-aware ordering with edge cases
    • Execution flows with context propagation
  • Test file grew from ~740 lines to 3,043 lines
  • 503 lines of new tests for ModelWorkflowDefinition hash methods

4. Security & Validation

  • _validate_json_payload() fails fast on non-serializable payloads
  • verify_workflow_integrity() detects tampering via SHA-256 hash
  • Custom JSON serializer restricts allowed types (UUID, datetime only)
  • Early validation prevents downstream errors

5. Performance Optimization

  • Priority-aware heap ordering (heapq) instead of naive queue
  • Deterministic ordering: priority → declaration order
  • Efficient O(n log n) topological sort with priorities
  • Step output aggregation avoids recomputation

🔍 Areas for Consideration

1. Unused Context in Parallel Execution (Minor)

Location: workflow_executor.py:542-557

The _context variable is created but not used in the parallel execution path.

Recommendation:

  • ✅ This is acceptable as scaffolding for future use (clearly marked with TODO)
  • Consider tracking in Linear ticket OMN-656 for future integration
  • The sequential path already uses context.completed_at, so pattern is established

Severity: 🟢 Low (intentional scaffolding)


2. Hash Computation Performance (Minor)

Location: model_workflow_definition.py:54-90

Observation: Hash computation uses model_dump() which creates a full deep copy of the workflow definition, then serializes to JSON.

Performance Impact:

  • For large workflows (100+ steps), this could be ~1-5ms overhead
  • Hash is computed at workflow execution start (already validated)

Recommendations:

  1. ✅ Current implementation is correct for correctness
  2. Consider caching hash if workflows are executed multiple times
  3. Profile with real workloads before optimizing

Severity: 🟢 Low (premature optimization concern)


3. Priority Clamping Visibility (Minor)

Location: workflow_executor.py:889

Priority clamping from 1-1000 (step priority) to 1-10 (action priority) is silent.

Recommendation:

  • Current design is intentional (module docstring explains it's expected behavior)
  • Consider debug-level logging if troubleshooting priority issues in production

Severity: 🟢 Low (design decision, not a bug)


4. JSON Serialization Edge Cases (Minor)

Location: workflow_executor.py:749-769

Concern: _json_default_for_workflow only handles UUID and datetime. Other Pydantic models in payloads will fail.

Recommendation:

  • ✅ Current behavior is correct (fail fast on non-serializable types)
  • Document expected payload types in workflow executor module docstring

Severity: 🟢 Low (intentional restriction)


5. Circular Dependency in Hash Computation (Info)

Location: model_workflow_definition.py:82-83

Analysis:
The code safely excludes workflow_hash from hash computation to avoid circular dependency, with proper guard against missing metadata.

✅ Well-handled: Safe guard prevents KeyError if metadata is missing.

Severity: 🟢 None (well-designed)


🛡️ Security Review

✅ Secure Practices

  1. Input Validation: All payloads validated before action creation
  2. Integrity Verification: SHA-256 hash detects tampering
  3. Type Safety: TypedDict prevents injection via context manipulation
  4. Fail Fast: Invalid payloads raise ModelOnexError immediately

⚠️ Recommendations

  1. Hash Algorithm: Consider SHA-3 or BLAKE2 for future-proofing (SHA-256 is fine for now)
  2. Payload Size: Consider max size limit for step_outputs to prevent memory exhaustion
  3. DoS Protection: Large workflows (1000+ steps) could cause performance issues during hash computation

Overall Security: 🟢 Strong


📊 Test Coverage Analysis

New Tests

  • test_workflow_executor.py: +2,299 lines (context building, validation, ordering, execution)
  • test_model_workflow_definition.py: +503 lines (hash computation, integrity verification)

Coverage Breakdown

  • ✅ Context Building: Complete (happy path + edge cases)
  • ✅ Payload Validation: Excellent (strict/non-strict modes, error cases)
  • ✅ Hash Computation: Thorough (determinism, exclusions, tamper detection)
  • ✅ Priority Ordering: Comprehensive (heap ordering, declaration order tie-breaking)
  • ✅ Execution Flows: Well-covered (sequential/parallel with context)

Test Quality: 🟢 Excellent (comprehensive coverage of new functionality)


🚀 Performance Considerations

Positive

  1. Heap-based ordering: O(n log n) vs O(n²) naive approach
  2. Single-pass context building: No redundant recomputation
  3. Lazy hash computation: Only when explicitly called

Watch Items

  1. Large workflows: Profile with 100+ steps to ensure hash computation is acceptable
  2. Deep step outputs: Large step_outputs dicts could impact memory in parallel mode
  3. JSON serialization: Multiple json.dumps() calls in validation - consider caching if hot path

Performance: 🟢 Good (no obvious bottlenecks)


✅ Code Quality Checklist

  • ✅ Type Safety: 100% mypy strict compliance
  • ✅ Error Handling: Uses ModelOnexError (project standard)
  • ✅ Naming: Clear, descriptive function names with underscores for internal functions
  • ✅ Documentation: Excellent docstrings with examples
  • ✅ Testing: 60%+ coverage requirement exceeded
  • ✅ Project Conventions: Follows CLAUDE.md guidelines
    • Uses protocol-driven DI patterns
    • Structured error handling with EnumCoreErrorCode
    • No generic exceptions
    • Comprehensive type annotations
  • ✅ No Backwards-Compat Hacks: Clean implementation (project is pre-v0.4.0)

🎯 Final Verdict

Overall Assessment: ✅ Approved with Minor Suggestions

This PR is production-ready with the following strengths:

  1. Solves critical missing functionality (output aggregation, integrity checks)
  2. Excellent test coverage (2,800+ new test lines)
  3. Strong type safety and documentation
  4. Follows project conventions meticulously
  5. No breaking changes or security vulnerabilities

Recommended Actions Before Merge

  1. ✅ Merge as-is - all concerns are minor and intentional design decisions
  2. 🔵 Optional: Add debug logging for priority clamping (can be done in follow-up)
  3. 🔵 Optional: Profile hash computation with large workflows (can be done post-merge)

Post-Merge Recommendations

  1. Monitor CI performance with new tests (20 parallel splits should handle 3,000+ test lines)
  2. Document expected payload types in workflow executor module docstring
  3. Consider caching hash for workflows executed multiple times

Great work on this PR! 🎉 The implementation is solid, well-tested, and ready for production use.


Reviewed by: Claude Code (Sonnet 4.5)
Linear Ticket: OMN-656

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (1)

337-367: Consider clarifying the expected behavior difference between contract and executor hashes.

The test test_executor_hash_includes_all_fields documents that contract hash and executor hash may differ, but the assertions only verify both are valid SHA-256 strings without asserting they're equal or different. The comment (lines 358-362) explains this is intentional, but the test doesn't actually verify the "may differ" claim.

If the intent is to document that hashes may differ, consider adding an explicit assertion or comment clarifying when they would differ:

         # This is expected and intentional design
         # The important invariant is that each is deterministic and content-based
         assert isinstance(contract_hash, str)
         assert isinstance(executor_hash, str)
         assert len(contract_hash) == 64
         assert len(executor_hash) == 64
+        # Note: These hashes are currently equal because workflow_hash is None.
+        # They would differ if workflow_hash were set to a non-None value.
tests/unit/utils/test_workflow_executor.py (1)

2237-2276: Performance test timing assertions may be flaky in CI.

The 5-second timeout for 100 sequential steps is generous, but CI environments can have significant variability. Consider adding a note about potential flakiness or using a more generous threshold for CI.

The timing assertions (e.g., elapsed_time < 5.0) may cause intermittent failures in resource-constrained CI environments. Consider either:

  1. Adding a larger tolerance for CI
  2. Skipping timing assertions in CI via environment variable check
  3. Documenting expected behavior if tests become flaky
         # Performance assertion: should complete in under 5 seconds
         # This is generous to account for CI variability
+        # If flaky in CI, consider increasing to 10.0 or skipping timing check
         assert elapsed_time < 5.0, (
src/omnibase_core/utils/workflow_executor.py (1)

548-558: TODO comment references non-existent ticket format.

The TODO comment references "OMN-656" but the codebase uses Linear tickets with format "OMN-XXX". The variable _context is created but only its assignment is tracked, not used further in the parallel path.

The _context variable is assigned but not used in the parallel execution path. Consider either:

  1. Using it (e.g., for timing/logging)
  2. Removing the scaffolding if not needed soon
  3. Using _ = prefix convention if intentionally unused
-            # TODO(OMN-656): Use _context for step execution logic that needs prior outputs.
+            # TODO: Use step context for step execution logic that needs prior outputs.
             # Currently scaffolded for future use cases:
             # - Accessing prior wave outputs via _context.workflow_context
             # - Tracking step timing via _context.started_at/_context.completed_at
             # - Error context via _context.error
             # The sequential execution path uses context.completed_at; parallel path
             # will need similar integration when step execution becomes more complex.
-            _context = WorkflowStepExecutionContext(
+            _ = WorkflowStepExecutionContext(  # noqa: F841 - scaffolded for future use
                 step, workflow_id, completed_step_ids, wave_context
             )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5f5635 and dc41a54.

📒 Files selected for processing (3)
  • src/omnibase_core/utils/workflow_executor.py (19 hunks)
  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (1 hunks)
  • tests/unit/utils/test_workflow_executor.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,pyc,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use Poetry for Python package management and task execution - never use pip or python directly

Files:

  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
  • tests/unit/utils/test_workflow_executor.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Import node classes from omnibase_core.nodes (NodeCompute, NodeEffect, NodeReducer, NodeOrchestrator), not from infrastructure submodules

Files:

  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
  • tests/unit/utils/test_workflow_executor.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Format code with black and sort imports with isort before committing

Files:

  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • src/omnibase_core/utils/workflow_executor.py
  • tests/unit/utils/test_workflow_executor.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Ensure minimum 60% test coverage for all code changes, tracked via pytest --cov
Use pytest with 4 parallel workers by default locally (via pytest-xdist -n4) and respect 60-second test timeout

Files:

  • tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py
  • tests/unit/utils/test_workflow_executor.py
src/omnibase_core/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/omnibase_core/**/*.py: Use ModelOnexError with EnumCoreErrorCode for all error handling, never generic Exception
Use @standard_error_handling decorator from omnibase_core.decorators.error_handling to eliminate try/catch boilerplate
Use protocol names (e.g., 'ProtocolEventBus') with container.get_service(), never concrete class names
Use EnumNodeKind for high-level architectural classification (EFFECT, COMPUTE, REDUCER, ORCHESTRATOR) and EnumNodeType for specific implementation types
Maintain 100% strict mypy compliance - all functions must have type annotations and zero mypy errors
Do NOT share node instances across threads without explicit synchronization - most ONEX components are not thread-safe by default
Implement mixin system using available mixins (MixinDiscoveryResponder, MixinEventHandler, MixinNodeExecutor, etc.) for cross-cutting concerns
Use ModelEventEnvelope for inter-service communication and event-driven processing patterns
Python version requirement: 3.12+
Structure project following the omnibase_core directory layout with clear separation of concerns (constants, container, decorators, discovery, enums, errors, events, infrastructure, logging, mixins, models, nodes, primitives, types, utils, validation)

Files:

  • src/omnibase_core/utils/workflow_executor.py
🧬 Code graph analysis (3)
tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (2)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (3)
  • ModelWorkflowDefinition (21-118)
  • compute_workflow_hash (53-89)
  • with_computed_hash (91-118)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition_metadata.py (1)
  • ModelWorkflowDefinitionMetadata (14-57)
src/omnibase_core/utils/workflow_executor.py (3)
src/omnibase_core/types/typed_dict_workflow_context.py (1)
  • TypedDictWorkflowContext (12-29)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-163)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (2)
  • ModelWorkflowDefinition (21-118)
  • compute_workflow_hash (53-89)
tests/unit/utils/test_workflow_executor.py (6)
src/omnibase_core/models/contracts/subcontracts/model_workflow_definition.py (1)
  • ModelWorkflowDefinition (21-118)
src/omnibase_core/utils/workflow_executor.py (9)
  • _compute_workflow_hash (1044-1066)
  • _build_workflow_context (675-746)
  • _validate_json_payload (776-848)
  • verify_workflow_integrity (1069-1113)
  • _get_topological_order (931-991)
  • _create_action_for_step (851-920)
  • get_execution_order (279-354)
  • validate_workflow_definition (180-276)
  • _has_dependency_cycles (994-1041)
src/omnibase_core/enums/enum_core_error_code.py (1)
  • EnumCoreErrorCode (9-163)
src/omnibase_core/enums/enum_workflow_coordination.py (1)
  • EnumFailureRecoveryStrategy (40-46)
src/omnibase_core/models/workflow/execution/model_declarative_workflow_step_context.py (1)
  • ModelDeclarativeWorkflowStepContext (15-45)
src/omnibase_core/types/typed_dict_workflow_context.py (1)
  • TypedDictWorkflowContext (12-29)
⏰ 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: claude-review
  • GitHub Check: Smoke Tests
🔇 Additional comments (23)
tests/unit/models/contracts/subcontracts/test_model_workflow_definition.py (4)

1-42: Well-structured test file with comprehensive coverage.

The fixture and imports are correctly organized. The simple_workflow_definition fixture provides a clean reusable base for all tests with all required fields properly initialized.


45-80: Good coverage of workflow_hash field behaviors in metadata.

Tests cover all three scenarios: default None, explicit set, and explicit None. The test at line 61 using "a" * 64 is a valid approach to simulate a SHA-256 hex string for testing purposes.


83-165: Thorough testing of compute_workflow_hash method.

Tests correctly validate:

  • SHA-256 format (64 hex characters)
  • Determinism across multiple calls
  • Different definitions produce different hashes
  • The workflow_hash field is excluded from computation (preventing circular dependency)

399-434: Well-designed deduplication test.

Using a factory function create_workflow() to create two identical but separate instances is the correct approach to test that content-based hashing works for deduplication scenarios.

tests/unit/utils/test_workflow_executor.py (7)

745-807: Good test coverage for workflow hash in metadata.

Tests verify hash presence, format, determinism, and consistency across execution modes. The assertion that hashes are consistent regardless of execution mode (sequential vs parallel) correctly validates that the hash is based on the definition, not the execution.


810-894: Comprehensive tests for _build_workflow_context.

Tests cover all scenarios: empty steps, single step, multiple steps, and partial outputs. The test at lines 876-894 correctly handles the edge case where not all completed steps have corresponding outputs.


897-1059: Thorough JSON payload validation tests.

Excellent coverage including:

  • Valid payloads (basic and empty)
  • Invalid payloads (lambda, custom objects, nested)
  • Permissive vs strict mode behavior
  • UUID and datetime handling
  • Error message verification with context

1061-1113: Well-structured integrity verification tests.

Tests cover the three main scenarios: None hash (skip), matching hash (pass), and mismatched hash (error with context). The assertion structure properly navigates the nested context from ModelOnexError.


1738-1795: Defensive None priority handling tests are valuable.

Using MagicMock to simulate None priority values tests defensive code paths that guard against future model changes. The docstrings clearly explain that this tests defensive handling rather than expected runtime behavior.


2029-2235: Performance test helper method is well-designed.

The _create_large_workflow_steps helper supports multiple dependency patterns (linear, parallel, mixed, diamond) which enables comprehensive performance testing. The implementation correctly handles edge cases like small workflow sizes.


2632-2848: Size limit tests provide good boundary testing.

Tests cover large step counts, deep dependency chains, wide fan-in, and determinism verification. The test at lines 2800-2848 for execution order determinism is particularly valuable for debugging reproducibility.

src/omnibase_core/utils/workflow_executor.py (12)

8-28: Excellent module-level documentation for priority clamping.

The docstring clearly explains the design rationale for priority clamping (1-1000 → 1-10) and references the relevant architecture documentation. This prevents confusion about why priorities are clamped and documents it as intentional behavior.


145-176: Workflow hash computation and metadata integration is correct.

The hash is computed after validation passes (line 146), ensuring only valid workflows get hashed. The hash is then added to result metadata (line 175) for integrity verification by consumers.


383-431: Workflow context integration in sequential execution is well-implemented.

The context is properly built before each step execution (lines 412-414), passed to the step context (line 418), and step outputs are stored for subsequent steps (line 431). This enables data flow between sequential steps.


504-605: Parallel execution context handling is correct.

Wave context is built once per wave (lines 586-588) before parallel execution, ensuring steps in the same wave cannot see each other's outputs (they run concurrently). Step outputs are stored after completion (line 605) for subsequent waves.


675-746: _build_workflow_context is well-documented and correctly implemented.

The function properly converts UUIDs to strings for JSON compatibility (lines 734, 737-739) and returns a TypedDict with all required fields. The docstring includes a comprehensive example.


749-773: Custom JSON serializer correctly limits allowed types.

The _json_default_for_workflow function explicitly allows only UUID and datetime, raising TypeError for all other types. This is the correct behavior for catching programming errors early while supporting common workflow types.


776-848: Payload validation addresses the previously flagged issue.

The _validate_json_payload function is now properly integrated and called from _create_action_for_step (line 902). This resolves the past review comment about the unused validation function.


889-920: Action creation with validation is correctly implemented.

Priority clamping (line 892), payload construction (lines 895-899), validation (line 902), and action creation are all properly sequenced. The validation call ensures fail-fast behavior for non-serializable payloads.


931-991: Priority-aware topological ordering is correctly implemented.

The implementation uses heapq for efficient priority-aware ordering with:

  • Priority clamping to 1-10 (line 956)
  • Declaration order as tiebreaker (line 957)
  • Defensive None handling defaulting to 1 (line 956)

The heap tuple (priority, idx, step_id) ensures correct ordering by priority first, then declaration order.


1044-1066: _compute_workflow_hash correctly delegates to model method.

The thin wrapper delegates to ModelWorkflowDefinition.compute_workflow_hash() for consistency. The docstring accurately describes the delegation and the exclusion of workflow_hash field.


1069-1113: verify_workflow_integrity is well-implemented.

The function correctly:

  • Skips verification when expected_hash is None (line 1097-1098)
  • Computes actual hash and compares (lines 1101, 1104)
  • Raises ModelOnexError with detailed context on mismatch (lines 1105-1112)

The context includes expected_hash, actual_hash, and workflow_name for debugging.


1116-1123: Public API correctly updated.

The __all__ list now includes verify_workflow_integrity, making it part of the module's public API. This aligns with the PR objectives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant