UAT: LangGraph.execute() returns initial state immediately without awaiting node execution — graph nodes never actually run #3604

Open
opened 2026-04-05 20:19:34 +00:00 by freemo · 1 comment
Owner

Metadata

  • Branch: fix/langgraph-execute-node-execution
  • Commit Message: fix(langgraph): implement proper node execution in LangGraph.execute() method
  • Milestone: v3.6.0
  • Parent Epic: #366

Background

Per docs/specification.md, LangGraph integration enables graph-based agent orchestration where actors can be configured as a composed LangGraph of multiple actors and tool nodes. The LangGraph.execute() method is the primary entry point for running a graph.

Current Behavior

LangGraph.execute() in src/cleveragents/langgraph/graph.py (lines 79-91) has a critical implementation gap:

async def execute(self, input_data: GraphState | dict[str, Any]) -> GraphState:
    state = (
        input_data
        if isinstance(input_data, GraphState)
        else GraphState.from_dict(cast(dict[str, Any], input_data))
    )
    # Replace state manager state for a fresh execution context
    self.state_manager.state = state
    self.state_manager.state_stream.on_next(state)
    start_stream = f"__{self.name}_node_start__"
    if start_stream in self.stream_router.streams:
        self.stream_router.send_message(start_stream, state)
    return self.state_manager.get_state()  # ← Returns IMMEDIATELY

The method:

  1. Sets the initial state
  2. Sends a message to the start stream (which triggers async node execution via RxPy)
  3. Immediately returns the current (initial) state without waiting for any node to execute

The node executors registered in _register_node_executor() are async functions that run via concurrent.futures.ThreadPoolExecutor. The stream_router.send_message() call triggers these executors asynchronously, but execute() returns before any executor has a chance to run.

Result: Every call to LangGraph.execute() returns the unmodified input state. No graph nodes ever execute. The execution_history remains empty after every call.

Expected Behavior

LangGraph.execute() should:

  1. Set the initial state
  2. Execute nodes in topological order (respecting the graph's edge definitions)
  3. For nodes with no incoming edges (entry nodes): execute them first
  4. For subsequent nodes: execute after their predecessors complete
  5. For parallel groups (when parallel_execution=True): execute concurrently
  6. Wait for all node executions to complete before returning the final state

The _topological_levels() method already computes the correct execution order. The _find_parallel_groups() method already identifies parallel execution groups. These are computed but never used in execute().

Code Locations

  • src/cleveragents/langgraph/graph.py lines 79-91: execute() method — returns immediately without node execution
  • src/cleveragents/langgraph/graph.py lines 96-116: _topological_levels() — computed but unused in execute()
  • src/cleveragents/langgraph/graph.py lines 229-249: _find_parallel_groups() — computed but unused in execute()
  • src/cleveragents/langgraph/nodes.py: Node.execute() — the actual node execution logic (correct)

Steps to Reproduce

import asyncio
from cleveragents.langgraph.graph import LangGraph, GraphConfig
from cleveragents.langgraph.nodes import NodeConfig, NodeType, Edge
from cleveragents.langgraph.state import GraphState

config = GraphConfig(
    name="test",
    nodes={
        "start": NodeConfig(name="start", type=NodeType.START),
        "process": NodeConfig(name="process", type=NodeType.FUNCTION),
        "end": NodeConfig(name="end", type=NodeType.END),
    },
    edges=[
        Edge(source="start", target="process"),
        Edge(source="process", target="end"),
    ],
)
graph = LangGraph(config=config)
initial_state = GraphState(messages=[{"role": "user", "content": "hello"}])
result = asyncio.run(graph.execute(initial_state))
# result == initial_state (unchanged) — "process" node never ran
assert graph.get_execution_history() == []  # No nodes executed

Impact

LangGraph integration is completely non-functional. Any actor configured as a LangGraph will silently return the input state unchanged, with no node execution occurring. This breaks the core M7 feature of graph-based agent orchestration.

Subtasks

  • Rewrite execute() to use _topological_levels() for ordered node execution
  • Implement sequential execution: for each topological level, execute all nodes in that level
  • Implement parallel execution: when parallel_execution=True, execute nodes in the same level concurrently using asyncio.gather()
  • Ensure execution_history is populated correctly after execution
  • Handle conditional edges (edges with condition set) — skip edges whose condition evaluates to False
  • Write Behave unit tests verifying: single-node graph executes, multi-node sequential graph executes in order, parallel nodes execute concurrently, execution_history is populated
  • Verify all nox stages pass; coverage ≥ 97%

Definition of Done

  • LangGraph.execute() correctly executes all nodes in topological order
  • execution_history is populated after execution
  • Parallel execution works when parallel_execution=True
  • Unit tests pass for all execution scenarios
  • All nox stages pass; coverage ≥ 97%

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

## Metadata - **Branch**: `fix/langgraph-execute-node-execution` - **Commit Message**: `fix(langgraph): implement proper node execution in LangGraph.execute() method` - **Milestone**: v3.6.0 - **Parent Epic**: #366 ## Background Per `docs/specification.md`, LangGraph integration enables graph-based agent orchestration where actors can be configured as a composed LangGraph of multiple actors and tool nodes. The `LangGraph.execute()` method is the primary entry point for running a graph. ## Current Behavior `LangGraph.execute()` in `src/cleveragents/langgraph/graph.py` (lines 79-91) has a critical implementation gap: ```python async def execute(self, input_data: GraphState | dict[str, Any]) -> GraphState: state = ( input_data if isinstance(input_data, GraphState) else GraphState.from_dict(cast(dict[str, Any], input_data)) ) # Replace state manager state for a fresh execution context self.state_manager.state = state self.state_manager.state_stream.on_next(state) start_stream = f"__{self.name}_node_start__" if start_stream in self.stream_router.streams: self.stream_router.send_message(start_stream, state) return self.state_manager.get_state() # ← Returns IMMEDIATELY ``` The method: 1. Sets the initial state 2. Sends a message to the start stream (which triggers async node execution via RxPy) 3. **Immediately returns the current (initial) state** without waiting for any node to execute The node executors registered in `_register_node_executor()` are async functions that run via `concurrent.futures.ThreadPoolExecutor`. The `stream_router.send_message()` call triggers these executors asynchronously, but `execute()` returns before any executor has a chance to run. **Result**: Every call to `LangGraph.execute()` returns the unmodified input state. No graph nodes ever execute. The `execution_history` remains empty after every call. ## Expected Behavior `LangGraph.execute()` should: 1. Set the initial state 2. Execute nodes in topological order (respecting the graph's edge definitions) 3. For nodes with no incoming edges (entry nodes): execute them first 4. For subsequent nodes: execute after their predecessors complete 5. For parallel groups (when `parallel_execution=True`): execute concurrently 6. **Wait for all node executions to complete** before returning the final state The `_topological_levels()` method already computes the correct execution order. The `_find_parallel_groups()` method already identifies parallel execution groups. These are computed but never used in `execute()`. ## Code Locations - `src/cleveragents/langgraph/graph.py` lines 79-91: `execute()` method — returns immediately without node execution - `src/cleveragents/langgraph/graph.py` lines 96-116: `_topological_levels()` — computed but unused in `execute()` - `src/cleveragents/langgraph/graph.py` lines 229-249: `_find_parallel_groups()` — computed but unused in `execute()` - `src/cleveragents/langgraph/nodes.py`: `Node.execute()` — the actual node execution logic (correct) ## Steps to Reproduce ```python import asyncio from cleveragents.langgraph.graph import LangGraph, GraphConfig from cleveragents.langgraph.nodes import NodeConfig, NodeType, Edge from cleveragents.langgraph.state import GraphState config = GraphConfig( name="test", nodes={ "start": NodeConfig(name="start", type=NodeType.START), "process": NodeConfig(name="process", type=NodeType.FUNCTION), "end": NodeConfig(name="end", type=NodeType.END), }, edges=[ Edge(source="start", target="process"), Edge(source="process", target="end"), ], ) graph = LangGraph(config=config) initial_state = GraphState(messages=[{"role": "user", "content": "hello"}]) result = asyncio.run(graph.execute(initial_state)) # result == initial_state (unchanged) — "process" node never ran assert graph.get_execution_history() == [] # No nodes executed ``` ## Impact LangGraph integration is completely non-functional. Any actor configured as a LangGraph will silently return the input state unchanged, with no node execution occurring. This breaks the core M7 feature of graph-based agent orchestration. ## Subtasks - [ ] Rewrite `execute()` to use `_topological_levels()` for ordered node execution - [ ] Implement sequential execution: for each topological level, execute all nodes in that level - [ ] Implement parallel execution: when `parallel_execution=True`, execute nodes in the same level concurrently using `asyncio.gather()` - [ ] Ensure `execution_history` is populated correctly after execution - [ ] Handle conditional edges (edges with `condition` set) — skip edges whose condition evaluates to False - [ ] Write Behave unit tests verifying: single-node graph executes, multi-node sequential graph executes in order, parallel nodes execute concurrently, execution_history is populated - [ ] Verify all nox stages pass; coverage ≥ 97% ## Definition of Done - [ ] `LangGraph.execute()` correctly executes all nodes in topological order - [ ] `execution_history` is populated after execution - [ ] Parallel execution works when `parallel_execution=True` - [ ] Unit tests pass for all execution scenarios - [ ] All nox stages pass; coverage ≥ 97% --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
freemo added this to the v3.6.0 milestone 2026-04-05 20:19:39 +00:00
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • Priority: Critical — LangGraph.execute() returns initial state immediately without awaiting node execution. Graph execution is completely non-functional — no nodes are actually executed.
  • Milestone: v3.6.0 (already assigned)
  • Story Points: 5 — L — Core execution engine fix requiring understanding of the async execution model.
  • MoSCoW: Must Have — Graph execution is the fundamental mechanism by which actors perform work. Without this, the entire actor execution pipeline is broken.

Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: ca-project-owner

Issue triaged by project owner: - **State**: Verified - **Priority**: Critical — `LangGraph.execute()` returns initial state immediately without awaiting node execution. Graph execution is completely non-functional — no nodes are actually executed. - **Milestone**: v3.6.0 (already assigned) - **Story Points**: 5 — L — Core execution engine fix requiring understanding of the async execution model. - **MoSCoW**: Must Have — Graph execution is the fundamental mechanism by which actors perform work. Without this, the entire actor execution pipeline is broken. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: ca-project-owner
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Blocks
#366 Epic: Post-MVP Deferred Work
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#3604
No description provided.