UAT: LangGraph tool node type (NodeType.TOOL) is a stub — _execute_tool() always returns {tool_executed: True} without invoking any registered tools #3668

Open
opened 2026-04-05 21:17:41 +00:00 by freemo · 0 comments
Owner

Metadata

  • Branch: fix/langgraph-tool-node-stub-implementation
  • Commit Message: fix(langgraph): implement tool node execution in _execute_tool() instead of returning stub
  • Milestone: v3.6.0
  • Parent Epic: #366

Background

Per docs/specification.md (lines 20912–20923), the tool node type is a first-class node type in actor graphs:

| `tool` | Node invoking tools | `tools: [<tool_ref>, ...]` |

The spec shows tool nodes referencing registered tools:

nodes:
  run_db_migrate:
    type: tool
    tools:
      - local/create-subplan

Tool nodes are used for executing registered tools (including MCP tools, built-in tools, and custom tools) as part of actor graph workflows.

Current Behavior

The _execute_tool() method in src/cleveragents/langgraph/nodes.py (lines 289–290) is a complete stub:

async def _execute_tool(self) -> dict[str, Any]:
    return {"tool_executed": True, "node": self.name}

This method:

  1. Does not accept state: GraphState as a parameter (unlike all other _execute_* methods)
  2. Does not look up any tools from self.config.tools
  3. Does not invoke any tool implementations
  4. Always returns {"tool_executed": True} regardless of what tools are configured

The NodeConfig model does have a tools: list[str] field (line 46), but it is never used.

Expected Behavior (per spec)

When a tool node executes, it should:

  1. Look up each tool name in self.config.tools from the tool registry (or from self.agents if tools are passed as agents)
  2. Invoke each tool with the current state's context
  3. Collect the results and return them as state updates

Code Location

  • src/cleveragents/langgraph/nodes.py lines 289–290: _execute_tool() — stub implementation
  • src/cleveragents/langgraph/nodes.py line 118: elif self.type == NodeType.TOOL: result = await self._execute_tool() — note the missing state argument

Steps to Reproduce

from cleveragents.langgraph.nodes import Node, NodeConfig, NodeType
from cleveragents.langgraph.state import GraphState
import asyncio

config = NodeConfig(
    name="run_tool",
    type=NodeType.TOOL,
    tools=["local/create-subplan"]
)
node = Node(config)
state = GraphState(messages=[{"role": "user", "content": "create a subplan"}])
result = asyncio.run(node.execute(state))
# Expected: tool "local/create-subplan" is invoked with state context
# Actual: result == {"current_node": "run_tool", "tool_executed": True, "node": "run_tool"}
# No tool was actually invoked

Impact

All actor graph nodes of type tool are non-functional. Any actor graph that uses tool nodes (e.g., for invoking local/create-subplan to spawn child plans, or MCP tools, or built-in tools) will silently return {tool_executed: True} without actually executing the tool. This breaks the hierarchical plan execution model where execution actors use tool nodes to spawn child plans.

Subtasks

  • Update _execute_tool() to accept state: GraphState parameter
  • Implement tool lookup from self.config.tools list (look up in self.agents dict or a tool registry)
  • Invoke each tool with appropriate state context
  • Return tool results as state updates
  • Update the call site in execute() to pass state to _execute_tool(state)
  • Write Behave unit tests verifying tool node execution
  • Verify all nox stages pass; coverage ≥ 97%

Definition of Done

  • _execute_tool() accepts state: GraphState parameter
  • Tool nodes look up and invoke tools from self.config.tools
  • Tool results are returned as state updates
  • Unit tests pass for tool node execution
  • All nox stages pass; coverage ≥ 97%
  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.

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

## Metadata - **Branch**: `fix/langgraph-tool-node-stub-implementation` - **Commit Message**: `fix(langgraph): implement tool node execution in _execute_tool() instead of returning stub` - **Milestone**: v3.6.0 - **Parent Epic**: #366 ## Background Per `docs/specification.md` (lines 20912–20923), the `tool` node type is a first-class node type in actor graphs: ``` | `tool` | Node invoking tools | `tools: [<tool_ref>, ...]` | ``` The spec shows tool nodes referencing registered tools: ```yaml nodes: run_db_migrate: type: tool tools: - local/create-subplan ``` Tool nodes are used for executing registered tools (including MCP tools, built-in tools, and custom tools) as part of actor graph workflows. ## Current Behavior The `_execute_tool()` method in `src/cleveragents/langgraph/nodes.py` (lines 289–290) is a complete stub: ```python async def _execute_tool(self) -> dict[str, Any]: return {"tool_executed": True, "node": self.name} ``` This method: 1. Does not accept `state: GraphState` as a parameter (unlike all other `_execute_*` methods) 2. Does not look up any tools from `self.config.tools` 3. Does not invoke any tool implementations 4. Always returns `{"tool_executed": True}` regardless of what tools are configured The `NodeConfig` model does have a `tools: list[str]` field (line 46), but it is never used. ## Expected Behavior (per spec) When a `tool` node executes, it should: 1. Look up each tool name in `self.config.tools` from the tool registry (or from `self.agents` if tools are passed as agents) 2. Invoke each tool with the current state's context 3. Collect the results and return them as state updates ## Code Location - `src/cleveragents/langgraph/nodes.py` lines 289–290: `_execute_tool()` — stub implementation - `src/cleveragents/langgraph/nodes.py` line 118: `elif self.type == NodeType.TOOL: result = await self._execute_tool()` — note the missing `state` argument ## Steps to Reproduce ```python from cleveragents.langgraph.nodes import Node, NodeConfig, NodeType from cleveragents.langgraph.state import GraphState import asyncio config = NodeConfig( name="run_tool", type=NodeType.TOOL, tools=["local/create-subplan"] ) node = Node(config) state = GraphState(messages=[{"role": "user", "content": "create a subplan"}]) result = asyncio.run(node.execute(state)) # Expected: tool "local/create-subplan" is invoked with state context # Actual: result == {"current_node": "run_tool", "tool_executed": True, "node": "run_tool"} # No tool was actually invoked ``` ## Impact All actor graph nodes of type `tool` are non-functional. Any actor graph that uses tool nodes (e.g., for invoking `local/create-subplan` to spawn child plans, or MCP tools, or built-in tools) will silently return `{tool_executed: True}` without actually executing the tool. This breaks the hierarchical plan execution model where execution actors use tool nodes to spawn child plans. ## Subtasks - [ ] Update `_execute_tool()` to accept `state: GraphState` parameter - [ ] Implement tool lookup from `self.config.tools` list (look up in `self.agents` dict or a tool registry) - [ ] Invoke each tool with appropriate state context - [ ] Return tool results as state updates - [ ] Update the call site in `execute()` to pass `state` to `_execute_tool(state)` - [ ] Write Behave unit tests verifying tool node execution - [ ] Verify all nox stages pass; coverage ≥ 97% ## Definition of Done - [ ] `_execute_tool()` accepts `state: GraphState` parameter - [ ] Tool nodes look up and invoke tools from `self.config.tools` - [ ] Tool results are returned as state updates - [ ] Unit tests pass for tool node execution - [ ] All nox stages pass; coverage ≥ 97% - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
freemo added this to the v3.6.0 milestone 2026-04-05 21:17:46 +00:00
freemo removed this from the v3.6.0 milestone 2026-04-06 23:31:46 +00:00
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#3668
No description provided.