Whetstone.
StateGraph and the Dice RollState schemas and reducers
Module 3, Lesson 125 min

State schemas and reducers

An agent loop (module 2) is one prebuilt shape. A StateGraph is you drawing your own shape: named nodes, edges between them, and a typed state object that flows through and accumulates.

Impulse’s cold-mode heartbeat, sense then modulate then roll then route, is the system you love most. This module redraws it as an explicit StateGraph, parity-tested against src/strategy/dice.ts, rendered as a diagram, inspectable. It is the spine every later module upgrades.

State is a schema. You declare what the graph carries and how each field updates.

Declaring state with StateSchema
import {
  StateGraph, StateSchema, ReducedValue, MessagesValue,
  GraphNode, START, END, Command,
} from "@langchain/langgraph";
import * as z from "zod/v4";

const State = new StateSchema({
  messages: MessagesValue,
  retryCount: z.number().default(0),
  allSteps: new ReducedValue(
    z.array(z.string()).default(() => []),
    { inputSchema: z.string(), reducer: (cur, next) => [...cur, next] },
  ),
});

Reducers decide how a field merges when a node returns an update. A plain zod field is last-write-wins. MessagesValue appends messages. ReducedValue lets you define custom merge logic: in the example above, a node returns a single string and the reducer appends it to a running array. Reducers are why concurrent or streamed updates do not clobber each other.

Practice

Try it yourself

Recall

What a reducer is for

Two parts. The second one is where the concept earns its keep, so give a concrete case rather than a category.

What is a reducer for, and when would last-write-wins be the wrong merge strategy?

Reveal answer

A reducer decides how a field's new value merges with its old value when a node returns an update. Last-write-wins is wrong whenever you want to accumulate rather than overwrite, for example a running log of steps taken (allSteps above) or an appended message history (MessagesValue) -- without a reducer, each node's update would clobber the previous one instead of adding to it.

Quiz

StateSchema vs Annotation.Root

You open pod-factory's code and see Annotation.Root. What's the right read?

  1. AAnnotation.Root is the legacy style; StateSchema is the current idiom to write going forward
  2. BAnnotation.Root is a bug and really should be reported
  3. CThey are unrelated APIs for different purposes
  4. DStateSchema replaced Annotation.Root entirely and old code won't run
Show answer

Correct answer: A — Annotation.Root is the legacy style; StateSchema is the current idiom to write going forward

Annotation.Root is the older, legacy way of declaring graph state. pod-factory predates the current idiom. You should be able to read Annotation.Root fluently, but write new graphs with StateSchema.

Sign in to track your progress →