Project 5: Deterministic Counter Agent
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | 2 |
| Time | 1.5 weeks |
| Main Stack | jido + optional jido_action |
| Alternatives | GenServer-only counter, ETS counter |
| Why Now | Clean baseline for understanding cmd/2 invariants |
What You Will Build
A pure agent state machine that increments and snapshots counters through explicit actions, proving that state transitions are deterministic and testable using only function-level operations.
Real World Outcome
$ mix test test/counter_agent_test.exs
.....
Finished in 0.4 seconds (0.00s async, 0.4s sync)
All tests passed
The counter state should be reproducible from event history:
- start_count=0
- after three
incrementactions => 3 - after
resetaction => 0
The Core Question You Are Answering
“What guarantees do we get when state transitions are modeled as pure
cmd/2operations instead of mutable process logic?”
Why This Project Matters
Jido’s design emphasizes pure transformation in cmd/2: action input + state in, transformed state + directives out. This gives deterministic behavior and easier reasoning for testing and audit.
Core Architecture
Input Action
|
v
+----------------------+
| Action module |
| validate schema |
| create update delta |
+----------+-----------+
|
v
+----------------------+
| Agent.cmd(agent, act) |
| returns state_update |
| + directive list |
+----------+-----------+
|
+-------+--------+
| |
v v
Updated State External Effects
(agent state) (Emit, none in this project)
Deep Dive Plan
1. State Schema
Define a tiny schema:
countintegerversioninteger- optional
metadatamap
2. Actions
Implement and validate:
IncrementDecrementResetSetStep
3. Command Contract
Enforce invariants:
- command result always has complete agent
- directives must never mutate state
- impossible transitions return explicit errors (
{:error, reason})
4. Test First
Test:
- deterministic replay of action history
- property-style checks for commutative increments and boundaries
- schema validation failures
5. Runtime Integration
Add agent server lifecycle:
- start/persist local registry ID
- synchronous vs asynchronous signal entry points
Concepts You Must Understand First
cmd/2contract- returned state+directives pattern and purity.
- State schema validation
- schema errors are control signals, not runtime noise.
- Directive isolation
- directives describe effects; they do not touch state directly.
Questions to Guide Your Design
- Testability
- What test gives proof of deterministic replay?
- Validation
- Which invalid actions should be hard-fail immediately?
- Concurrency
- How do multiple signals to same agent preserve consistency?
Thinking Exercise
Given action sequence: Increment(2), Increment(3), Decrement(1), Reset, Increment(5), what is final state?
Then add one faulty action Increment("bad"). Predict full command result behavior.
Interview Questions They Will Ask
- “What is the significance of returning a complete state in
cmd/2?” - “Why are directives separated from state transitions?”
- “How do you model failed transitions in tests?”
- “What makes deterministic replay practical in production?”
- “How does jido improve on raw GenServer for this case?”
Hints in Layers
Hint 1: Start with one state schema No branching until schema is locked.
Hint 2: Keep actions tiny One action should mean one transition.
Hint 3: Enforce explicit errors Never raise on bad action data inside action handlers.
Hint 4: Build a replay script Serialize action list -> apply from zero -> assert final state.
Common Pitfalls and Debugging
- Problem: Tests pass but runtime state drifts.
- Why: runtime adapter mutated state outside
cmd/2. - Fix: route all updates through action handlers only.
- Quick test: inspect code paths and ensure no direct state assignment.
- Why: runtime adapter mutated state outside
- Problem: Directive logic leaking into state.
- Why: effect branch writes state map.
- Fix: isolate directives as output, never state input.
- Quick test: assert state hash unchanged when directive-only action runs.
- Problem: Invalid schema silently coerced.
- Why: overly permissive schema defaults.
- Fix: strict validation defaults and explicit error returns.
- Quick test: pass invalid action values intentionally.
Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Pure function design | Elixir in Action | State and Data Structures |
| OTP and processes | Programming Elixir | GenServers and message design |
Definition of Done
- State transitions are deterministic for the same action history
- Invalid actions return structured errors
- directives remain separate from state mutation
- test suite validates replay semantics
- agent server lifecycle works with one-line start/stop calls
References
- https://hexdocs.pm/jido/2.0.0-rc.4/readme.html
- https://hexdocs.pm/jido/2.0.0-rc.4/readme.html#The%20
cmd/2%20Contract