Whetstone.
Subgraphs and Multi-Agent OrchestrationCommand.PARENT handoffs, and building a mini-wave
Module 7, Lesson 225 min

Command.PARENT handoffs, and building a mini-wave

A node inside a subgraph can route in the parent graph by targeting Command.PARENT.

A reviewer subgraph routing the parent
import { Command } from "@langchain/langgraph";

const reviewNode = (state) => {
  if (state.verdict === "reject") {
    return new Command({
      update: { feedback: state.notes },
      goto: "implement",          // a node in the PARENT graph
      graph: Command.PARENT,
    });
  }
  return new Command({ goto: "done", graph: Command.PARENT });
};

That is how a rejection loops back to the implementer: the reviewer subgraph reaches up and routes the parent. Without Command.PARENT, goto only sees nodes inside the same subgraph.

Because the reviewer routes with a Command, its destinations must be declared in an ends array, and the placement is the part that catches people: the array goes on the parent’s addNode for the subgraph, naming nodes in the parent’s namespace.

ends for a parent-routing subgraph goes on the parent
const supervisor = new StateGraph(SupervisorState)
  .addNode("implement", implementer)
  .addNode("review", reviewer, { ends: ["implement", "done"] })
  .addNode("done", doneNode)
  .addEdge(START, "implement")
  .addEdge("implement", "review")
  .compile();

Putting ends on the reviewer subgraph’s own addNode instead fails differently, with Found edge ending at unknown node \implement`, because implementis not a node inside the reviewer. Omitting it entirely fails at the parent's.compile()withUnreachableNodeError: Node `done` is not reachable`.

Practice

Try it yourself

Quiz

Why Command.PARENT is required for the loop-back

Why does the reviewer subgraph need Command.PARENT to loop back to the implementer, rather than a plain goto: "implement"?

  1. Agoto: "implement" would work fine here; Command.PARENT is just a clearer style choice
  2. BCommand.PARENT is required for all Command objects, even within the same graph
  3. Cgoto: "implement" would cause an infinite loop without Command.PARENT
  4. Dgoto only resolves nodes in the reviewer's subgraph, but "implement" is in the parent graph
Show answer

Correct answer: D — goto only resolves nodes in the reviewer's subgraph, but "implement" is in the parent graph

goto resolves within the current graph's own node namespace by default. "implement" doesn't exist inside the reviewer subgraph -- it's a node in the parent supervisor graph. Command.PARENT tells the runtime to resolve and route within the parent's graph instead.

Recall

A bug subgraph isolation prevents

Answer from the mini-wave rather than in the abstract. A named safety property is only worth anything if you can name what it stops.

Name a specific bug that subgraph state isolation prevents.

Reveal answer

Without isolation, the implementer's internal scratch state (e.g. draft code, intermediate reasoning, retry counters specific to implementation) could leak into and pollute the reviewer's state, or vice versa -- for example the reviewer might accidentally see or be influenced by the implementer's internal retry count, conflating it with its own review-rejection count, corrupting the "loop back exactly once" guard logic.

Do

Build the mini-wave: supervisor, implementer, reviewer

Build a hand-rolled supervisor dispatching an implementer subgraph to a reviewer subgraph with a bounded fix loop.

  • Create the build under ~/dev/groundschool/builds/m7-mini-wave.
  • Build an implementer subgraph (its own StateGraph, compiled) that produces some output artifact.
  • Build a reviewer subgraph that inspects the implementer's output and returns a verdict (approve/reject) plus feedback notes.
  • Build a supervisor StateGraph that adds both compiled subgraphs as nodes, dispatches work to the implementer first, then routes its output to the reviewer.
  • In the reviewer's reject path, use Command.PARENT to goto the implementer node in the supervisor graph, carrying feedback.
  • Declare the reviewer's destinations in an ends array on the SUPERVISOR's addNode for the reviewer subgraph, not on the subgraph's own node: naming a parent node inside the subgraph fails with 'Found edge ending at unknown node', and omitting ends fails with UnreachableNodeError.
  • Add a retry-count guard so a second rejection escalates instead of looping again.
  • Capture a transcript showing the Command.PARENT handoffs, and assert the implementer's internal keys never appear in the reviewer's state.
Done whenThe supervisor routes work to the implementer, then to the reviewer; a rejection loops back to the implementer exactly once before escalating; and the transcript plus an isolation assertion prove subgraph state never leaks across.
Check

Reject loop is bounded and state stays isolated

Confirm the fix loop and isolation properties.

You should see

A reviewer rejection loops back to the implementer exactly once before escalating to a human, Command.PARENT handoffs are visible in the transcript, and the implementer's internal state keys never appear in the reviewer's state.

Sign in to track your progress →