soma.cell

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 }

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).