W0008: agent_missing_state_machine
W0008 agent_missing_state_machine — an agent cell without a state machine.
A warning of soma check, the checker of the Soma programming language (soma explain W0008).
The rule
soma check warns with W0008 when a cell agent has no state section. An agent's lifecycle is what soma verify can check (reachability, deadlock-freedom, every path reaching a terminal state), so an agent without a state machine has none of that verified behavior. Add a state machine and move each job through it with transition(id, "state") in the handler.
A program that triggers it
wrong.cell
cell agent Summarizer {
cost { tokens: 400 }
on summarize(doc_id: String, text: String) {
let summary = think("Summarize in three lines: {text}", map("max_tokens", 400))
return map("doc", doc_id, "summary", summary)
}
}What soma check prints:
$ soma check wrong.cell
✓ cost: 'tokens' bound proven — peak 400 reply tokens (the max_tokens caps) ≤ declared 400 (prompts are not counted: cap them with set_budget)
warning: agent cell 'Summarizer' has no state machine — add a state section for verified behavior
✓ 1 warning(s), no errors.The fix field of soma check --json: Add a state machine to agent cell 'Summarizer' to enable verified behavior:
state workflow {
initial: idle
idle -> active
active -> done
* -> failed
}
The fix
4 lines are added after line 2; 1 line is added after line 4.
wrong.cell → right.cell
--- wrong.cell
+++ right.cell
@@ -1,6 +1,11 @@
cell agent Summarizer {
cost { tokens: 400 }
+ state job {
+ initial: queued
+ queued -> done
+ }
on summarize(doc_id: String, text: String) {
let summary = think("Summarize in three lines: {text}", map("max_tokens", 400))
+ transition(doc_id, "done")
return map("doc", doc_id, "summary", summary)
}With the fix, soma check accepts the program:
$ soma check right.cell
✓ cost: 'tokens' bound proven — peak 400 reply tokens (the max_tokens caps) ≤ declared 400 (prompts are not counted: cap them with set_budget)
✓ All checks passed (1 note above).