The killer feature: an agent whose termination is PROVEN by soma verify
The lifecycle has a single terminal set {done, failed} reachable from every state, a `* -> failed` catch-all, and the matching soma.toml below drives `soma verify` to prove deadlock-freedom and liveness (eventually done|failed). Check: soma check verified_termination.cell Verify: soma verify verified_termination.cell (uses the soma.toml beside it) Run: SOMA_LLM_MOCK=echo soma run verified_termination.cell --signal solve "x" Test: SOMA_LLM_MOCK=echo soma test verified_termination.cell
A Soma program from the agents part of the verified corpus: 52 lines, cells Solver, SolverTests. It passes soma check with soma 3.2.4.
How it is built
The cell Solver declares 1 signal in its face: solve(task: String) -> Map. Its state machine workflow starts in idle and allows 5 transitions: idle -> understanding, understanding -> solving, solving -> verifying, verifying -> done and * -> failed. done and failed are terminal. Handlers: solve (line 26).
The file also holds 1 test cell (SolverTests) with 1 assertion, run by soma test.
The source
soma.toml
[package]
name = "agents-corpus"
version = "0.1.0"
description = "Verified AI-agent example cells (training corpus)"
# Agent LLM config. `mock = "echo"` keeps every example in this directory
# hermetic: `soma test` / `soma run` need no network and no API key, and
# results are deterministic. For a live run, delete the `mock` line (or
# override with SOMA_LLM_MOCK) and provide the key via env.
[agent]
provider = "anthropic"
model = "claude-opus-4-6"
key = "${ANTHROPIC_API_KEY}"
mock = "echo"
# Termination proof for verified_termination.cell's `workflow` machine:
# every path eventually reaches a terminal, no deadlocks.
# `cells` scopes these properties to Solver — the other agents in this
# directory have their own state names and must not inherit them.
[verify]
cells = ["Solver"]
deadlock_free = true
eventually = ["done", "failed"]
[verify.after.understanding]
eventually = ["solving", "failed"]
[verify.after.solving]
eventually = ["verifying", "failed"]
[verify.after.verifying]
eventually = ["done", "failed"]verified_termination.cell
// The killer feature: an agent whose termination is PROVEN by soma verify.
// The lifecycle has a single terminal set {done, failed} reachable from every
// state, a `* -> failed` catch-all, and the matching soma.toml below drives
// `soma verify` to prove deadlock-freedom and liveness (eventually done|failed).
//
// Check: soma check verified_termination.cell
// Verify: soma verify verified_termination.cell (uses the soma.toml beside it)
// Run: SOMA_LLM_MOCK=echo soma run verified_termination.cell --signal solve "x"
// Test: SOMA_LLM_MOCK=echo soma test verified_termination.cell
cell agent Solver {
face {
signal solve(task: String) -> Map
promise "eventually reaches done or failed"
}
state workflow {
initial: idle
idle -> understanding
understanding -> solving
solving -> verifying
verifying -> done
* -> failed
}
on solve(task: String) {
set_budget(4000)
let id = to_string(next_id())
transition(id, "understanding")
let u = try { think("Restate the task precisely: {task}") }
if u.error != () { transition(id, "failed") return map("status", "failed", "error", u.error) }
transition(id, "solving")
let s = try { think("Solve it: {u.value}") }
if s.error != () { transition(id, "failed") return map("status", "failed", "error", s.error) }
transition(id, "verifying")
let v = try { think("Double-check the solution: {s.value}") }
if v.error != () { transition(id, "failed") return map("status", "failed", "error", v.error) }
transition(id, "done")
map("status", "done", "task", task, "tokens", tokens_used())
}
}
cell test SolverTests {
rules {
assert solve("compute 2+2").status == "done"
}
}What the toolchain says
$ soma check verified_termination.cell
note: promise on 'Solver' is documentation (not machine-verifiable): "eventually reaches done or failed"
--> verified_termination.cell:14:17
|
14 | promise "eventually reaches done or failed"
| ^
✓ All checks passed (1 note above).$ soma test verified_termination.cell
test SolverTests ...
✓ assert solve("compute 2+2").status == "done"
1 tests: 1 passed, 0 failed$ soma verify verified_termination.cell
Verifying verified_termination.cell...
State machine 'workflow': 6 states, initial 'idle'
States: [done, failed, idle, solving, understanding, verifying]
✓ 6 states, 9 transitions
✓ all states reachable from 'idle'
✓ terminal states: [failed]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ wildcard transitions: * -> [failed]
⚠ `* -> failed` also adds done -> failed — so done is not final. If it should stay final, write `* -> failed except [done]`
✓ think-isolated: CTL safety properties hold regardless of LLM output (1 handlers, 7 literal transitions, 0 dynamic)
✓ termination: all 1 handlers structurally terminate
✓ refinement: handler `solve` ⟶ {understanding, failed [if u.error != ()], solving, failed [if s.error != ()], verifying, failed [if v.error != ()], done}
· transition sources: `solve` calls transition(id, target) without declaring the source — the runtime refuses a move the graph forbids, but the proof does not see where the handler starts; `transition(id, from, to)` names the edge (checked by `soma check`, refused at run time from any other state)
✓ effects: handler `solve` calls think() at 6 sites (the cost line counts loop rounds) — no tools available
10 passed, 1 warnings, 0 failures
Temporal properties for 'workflow':
✓ deadlock_free — no deadlocks in any reachable state
✓ eventually(state in [done, failed]) — all paths eventually satisfy the predicate
✓ after('understanding', state in [solving, failed]) — after 'understanding', all paths eventually satisfy the predicate
✓ after('solving', state in [verifying, failed]) — after 'solving', all paths eventually satisfy the predicate
✓ after('verifying', state in [done, failed]) — after 'verifying', all paths eventually satisfy the predicate
soma.toml: 4 user-defined properties loaded
Temporal: 5 passed, 0 failed
VERIFY OKBuiltins used
map, next_id, set_budget, think, to_string, tokens_used, transition