# Understanding the `cmd/2` Contract in Jido: Pure Logic for Testable Agents

> Discover the Jido cmd/2 contract, a pure function ensuring agent testability. Learn how it creates deterministic unit tests with state transformation and effect directives, eliminating external dependencies.

- Repository: [agentjido/jido](https://github.com/agentjido/jido)
- Tags: deep-dive
- Published: 2026-03-09

---

**The `cmd/2` contract in Jido is a pure function that transforms an agent state and action into an updated state plus effect directives, enabling deterministic unit testing without external dependencies or process overhead.**

The `cmd/2` contract is the foundational abstraction that makes Jido agents both powerful and predictable. In the [agentjido/jido](https://github.com/agentjido/jido) repository, this pure functional interface separates decision logic from side-effect execution, allowing developers to verify complex agent behaviors in isolation.

## What Is the `cmd/2` Contract in Jido?

At its core, `cmd/2` defines the **decision boundary** between an agent's internal state management and the runtime's effect execution. Every Jido agent must implement this contract to specify how it responds to actions.

### Contract Signature and Types

The formal signature implemented in [`lib/jido/agent.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent.ex) is:

```elixir
@callback cmd(agent :: Agent.t(), action :: Agent.action()) :: Agent.cmd_result()

```

This callback returns `Agent.cmd_result()`, which is defined as `{updated_agent, directives}`. The contract is enforced by the `Jido.Agent.Strategy` behaviour, ensuring all strategy implementations return consistent shapes.

### Inputs and Outputs

The `cmd/2` function accepts two arguments:

- **`agent`** – The current immutable agent struct containing the complete state
- **`action`** – A flexible input that can be:
  - A single action struct
  - A `{module, params}` tuple
  - An `%Instruction{}` struct
  - A list containing any combination of the above

The function returns a tuple where:

- **`updated_agent`** – A complete, new agent struct with all state changes applied
- **`directives`** – A list of pure effect descriptors (`%Directive.Emit{}`, `%Directive.Spawn{}`, etc.) that describe what side-effects the runtime should perform

### The Purity Guarantee and Strategy Delegation

The `cmd/2` contract guarantees **deterministic purity** – given the same `agent` and `action`, it always returns the same tuple without performing side-effects or external I/O.

The actual implementation delegates to the agent's **strategy** via `Strategy.cmd/3` after normalizing the inputs. This delegation pattern, defined in [`lib/jido/agent/strategy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy.ex)【lines 11‑18】, allows different execution strategies (direct, delayed, batched) while maintaining the same pure contract.

## Why the `cmd/2` Contract Is Critical for Testability

The `cmd/2` contract transforms Jido agents into highly testable components by enforcing a strict separation between logic and effects. Here is why this matters:

- **Pure functional core** – Because `cmd/2` never touches the outside world, unit tests can call it directly with any agent state and action, asserting on the returned `{agent, directives}` without needing a live `AgentServer`, message passing, or external services.

- **Deterministic output** – The same inputs always yield the same outputs, eliminating flaky tests caused by timing, race conditions, or external state.

- **Separation of concerns via directives** – Side-effects are expressed only as pure directive structs. Tests can inspect the list of directives to verify that the correct external actions (emit a signal, spawn a child, schedule a timeout) would be performed, without actually executing them.

- **Strategy-agnostic testing** – Because the contract delegates to a strategy (`Strategy.cmd/3`), you can test both the **default direct strategy** and any custom strategy by swapping the module in the agent definition. The contract guarantees that every strategy returns the same shape of result, keeping tests consistent.

- **Fast feedback loop** – Pure `cmd/2` calls execute instantly, so test suites run quickly, encouraging a strong unit-test culture.

The `AgentServer` uses `cmd/2` as its **decision-logic entry point** while handling the directives at runtime, reinforcing the clean split between pure logic and effect execution【[`lib/jido/agent_server.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex) lines 6‑8】.

## Implementing the `cmd/2` Contract in Practice

### Basic Usage Example

Here is how to invoke `cmd/2` on a freshly created agent:

```elixir

# Define a simple action

defmodule MyAction do
  defstruct []
end

# Call cmd/2 on a freshly created agent

agent = MyAgent.new()
{agent, directives} = MyAgent.cmd(agent, MyAction)

# `directives` will be a list of %Jido.Agent.Directive{} structs

IO.inspect(directives)

```

*Source:* Example adapted from [`lib/jido/agent.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent.ex) documentation【lines 15‑18】.

### Unit Testing with ExUnit

Because `cmd/2` is pure, you can test agent logic without starting processes or mocking external services:

```elixir
defmodule MyAgentTest do
  use ExUnit.Case

  test "cmd/2 returns updated state and correct directive" do
    agent = MyAgent.new(state: %{counter: 0})

    {agent, directives} = MyAgent.cmd(agent, MyAction)

    # State is updated immutably

    assert agent.state.counter == 0

    # Verify that a specific directive was produced

    assert [%Jido.Agent.Directive.Emit{signal: :my_signal}] = directives
  end
end

```

This test verifies both the state transformation and the side-effect intent without executing actual side-effects.

### Custom Strategy Implementation

You can implement custom strategies while maintaining the `cmd/2` contract. Here is a delayed execution strategy:

```elixir
defmodule MyDelayedStrategy do
  use Jido.Agent.Strategy

  @impl true
  def cmd(agent, instructions, ctx) do
    # Example: schedule each instruction to run after a delay

    directives =
      Enum.map(instructions, fn instr ->
        %Jido.Agent.Directive.Schedule{
          delay_ms: 100,
          action: instr
        }
      end)

    {agent, directives}
  end
end

defmodule MyDelayedAgent do
  use Jido.Agent,
    name: "delayed",
    strategy: MyDelayedStrategy
end

```

The custom strategy still returns `{agent, directives}` as required by the `cmd/2` contract, allowing the same unit-testing approach to be reused regardless of execution semantics.

## Key Source Files

| File | Role |
|------|------|
| [`lib/jido/agent.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent.ex) | Defines the `cmd/2` public API, its invariants, and default callbacks【lines 13‑23】. |
| [`lib/jido/agent/strategy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy.ex) | Declares the `Strategy` behaviour; `cmd/3` is the callback that implements the contract for each strategy【lines 11‑18】. |
| [`lib/jido/agent_server.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex) | Shows how the runtime (`AgentServer`) invokes `cmd/2` as pure decision logic and then executes the returned directives【lines 6‑8】. |
| [`test/support/test_agents.ex`](https://github.com/agentjido/jido/blob/main/test/support/test_agents.ex) | Contains test helpers that illustrate unit-testing of agents via `cmd/2`. |

These files together establish the **pure contract**, its **implementation hooks**, and the **runtime execution path**, making Jido agents highly testable and composable.

## Summary

- The `cmd/2` contract in Jido is a **pure function** that transforms an agent state and action into an updated state plus effect directives.
- It enforces **deterministic, side-effect-free logic** that enables direct unit testing without process overhead or external dependencies.
- Side-effects are expressed as **pure directive structs** (`%Directive.Emit{}`, `%Directive.Spawn{}`, etc.), allowing tests to verify intent without execution.
- The contract delegates to **strategies** via `Strategy.cmd/3`, enabling custom execution semantics while maintaining testability.
- Key implementations reside in [`lib/jido/agent.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent.ex), [`lib/jido/agent/strategy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy.ex), and [`lib/jido/agent_server.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex).

## Frequently Asked Questions

### What makes `cmd/2` different from other agent methods?

Unlike callback methods that might interact with GenServer state or perform I/O directly, `cmd/2` is a **pure function** that strictly separates decision logic from execution. It accepts an immutable agent struct and returns a new struct plus directives, never modifying global state or triggering side-effects during the call. This purity distinguishes it from traditional actor-model methods that often mix logic and effects.

### How does `cmd/2` enable testing without external dependencies?

Because `cmd/2` is deterministic and side-effect-free, tests can invoke it directly with specific agent states and actions, then assert on the returned tuple of `{updated_agent, directives}`. There is no need to start an `AgentServer`, mock external services, or manage process state. Tests verify that the correct directives (like `%Directive.Emit{}` or `%Directive.Spawn{}`) are produced, confirming the agent's intent without executing actual side-effects.

### Can I implement custom logic while maintaining the `cmd/2` contract?

Yes, through **strategies**. The `cmd/2` contract delegates to `Strategy.cmd/3`, allowing you to implement custom execution semantics—such as delayed processing, batching, or transactional boundaries—while still returning the required `{agent, directives}` tuple. As long as your strategy implementation remains pure and returns the correct shape, it maintains the testability benefits of the core contract.

### Where does the actual side-effect execution happen if `cmd/2` is pure?

Side-effects are executed by the **runtime layer**, specifically the `AgentServer` in [`lib/jido/agent_server.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex). The server calls `cmd/2` to obtain the pure decision result, then interprets the returned directives (such as `%Directive.Emit{}` or `%Directive.Schedule{}`) to perform the actual I/O, message passing, or process spawning. This architecture ensures that your business logic remains pure and testable while the runtime handles effectful operations.