[BUG] `legacy/multi_agent.py` can treat partial section completion as full research completion
Bug Description
The packaged legacy/multi_agent.py path currently has a premature join in the supervisor/research-team wiring.
The multi-agent graph fans out section research through Send(...), but each research_team completion routes directly back into supervisor:
supervisor_builder.add_node("research_team", research_builder.compile())
...
supervisor_builder.add_edge("research_team", "supervisor")At the same time, supervisor(...) uses this condition:
if state.get("completed_sections") and not state.get("final_report"):
research_complete_message = {
"role": "user",
"content": "Research is complete. Now write the introduction and conclusion ..."
}So the presence of any completed section is enough to switch the supervisor into completion mode.
That means the graph can move on to introduction/conclusion writing from partial section state instead of waiting for all parallel section researchers to finish.
Steps to Reproduce
From a clean checkout:
uv syncCreate repro_partial_join.py in the repo root:
import asyncio
import sys
import types
sys.path.insert(0, "src")
# Stub just enough imports to exercise the real supervisor logic without
# requiring live models, MCP servers, or external search providers.
langchain = types.ModuleType("langchain")
chat_models = types.ModuleType("langchain.chat_models")
class FakeLLM:
def __init__(self):
self.captured = None
def bind_tools(self, tools, **kwargs):
return self
async def ainvoke(self, messages):
self.captured = messages
return types.SimpleNamespace(tool_calls=[])
fake_llm = FakeLLM()
chat_models.init_chat_model = lambda *args, **kwargs: fake_llm
langchain.chat_models = chat_models
sys.modules["langchain"] = langchain
sys.modules["langchain.chat_models"] = chat_models
mcp_client_mod = types.ModuleType("langchain_mcp_adapters.client")
mcp_client_mod.MultiServerMCPClient = type(
"FakeMCP",
(),
{"__init__": lambda self, *a, **k: None, "get_tools": lambda self: []},
)
sys.modules["langchain_mcp_adapters"] = types.ModuleType("langchain_mcp_adapters")
sys.modules["langchain_mcp_adapters.client"] = mcp_client_mod
legacy_utils = types.ModuleType("legacy.utils")
legacy_utils.get_config_value = lambda v: v
legacy_utils.tavily_search = None
legacy_utils.duckduckgo_search = None
legacy_utils.get_today_str = lambda: "2026-06-25"
sys.modules["legacy.utils"] = legacy_utils
from legacy.multi_agent import supervisor
from legacy.state import Section
state = {
"messages": [{"role": "user", "content": "start report"}],
"completed_sections": [
Section(
name="Body A",
description="A",
research=True,
content="SECTION A COMPLETE",
)
],
"final_report": "",
}
config = {"configurable": {"supervisor_model": "fake", "search_api": "none"}}
async def main():
out = await supervisor(state, config)
captured = fake_llm.captured
print("OUT_KEYS", sorted(out.keys()))
print("CAPTURED_LEN", len(captured))
print("LAST_MSG_ROLE", captured[-1]["role"])
print("LAST_MSG_CONTENT", captured[-1]["content"])
asyncio.run(main())Run it with:
uv run python repro_partial_join.pyObserved output:
OUT_KEYS ['messages']
CAPTURED_LEN 3
LAST_MSG_ROLE user
LAST_MSG_CONTENT Research is complete. Now write the introduction and conclusion for the report. Here are the completed main body sections:
SECTION A COMPLETEExpected Behavior
The supervisor should only move into the “research complete / write intro+conclusion” phase after all parallel section researchers have completed.
One completed section should not be enough to trigger convergence.
Actual Behavior
The supervisor switches into completion mode as soon as completed_sections is non-empty.
Because each research_team child flows directly back into supervisor, a single finished child can trigger intro/conclusion writing while other section researchers are still pending.
Suggested Fix
This looks like a missing wait-for-all barrier on the research_team join.
Possible fix directions:
- add an explicit aggregation/join node that waits for all section researchers to finish before re-entering
supervisor - or track the planned section count and only emit the “Research is complete” prompt when
len(completed_sections)reaches that full expected count
Environment Information
- Operating System: reproduced on Linux
- open_deep_research commit:
1f24f1142db24f81e29eb88751985a00ec8ed580 - Python environment created with
uv sync
Impact
This is a graph-level correctness bug in the packaged legacy implementation.
If the graph converges after only one child completes, the run can:
- start writing introduction/conclusion from incomplete body coverage
- omit sections that were still in progress
- produce a final report that looks complete even though not all planned section researchers finished
That makes it a silent partial-result bug rather than a clean failure, which is harder for users and maintainers to detect.
Source: langchain-ai/open_deep_research