Grain conveyor pull-cord — a latched trip, and a broken safety circuit that stays broken
Grain handling kills dozens every year; engulfment victims are often pulled in by a conveyor a coworker restarted while they were still in the boot pit. Two properties are structural here. Anti-restart: a pull-cord trip latches, and `tripped` has NO edge to `running` — the only exit is `reset_walked`, a state a human reaches by walking the belt line. Fail-safe wiring: the pull- cord loop is supervised, and a severed wire lands in `circuit_fault`, a TERMINAL state soma verify proves has no exit — a conveyor whose safety circuit is cut can never be software-restarted.
A Soma program from the safety_interlocks part of the verified corpus: 88 lines, cells ConveyorGuard, ConveyorTests. It passes soma check with soma 3.2.4.
How it is built
The cell ConveyorGuard declares 5 signals in its face: start_belt(id: String) -> Map, stop_belt(id: String) -> String, pull_cord(id: String) -> String, walk_the_line(id: String) -> String and wire_severed(id: String) -> String; keeps 1 memory slot: walkdowns (Map<String, Int>, persistent and consistent) under the invariant walkdowns >= 0 && walkdowns <= 1000. Its state machine belt starts in stopped and allows 8 transitions: stopped -> running, running -> stopped, running -> tripped, tripped -> reset_walked, reset_walked -> stopped, running -> circuit_fault, stopped -> circuit_fault and tripped -> circuit_fault. circuit_fault is terminal. Handlers: start_belt (line 39), stop_belt (line 48), pull_cord (line 49), walk_the_line (line 51) and wire_severed (line 58).
The file also holds 1 test cell (ConveyorTests) with 14 assertions, run by soma test.
The source
conveyor_pullcord.cell
// GRAIN CONVEYOR PULL-CORD — a latched trip, and a broken safety
// circuit that stays broken. Grain handling kills dozens every year;
// engulfment victims are often pulled in by a conveyor a coworker
// restarted while they were still in the boot pit. Two properties are
// structural here. Anti-restart: a pull-cord trip latches, and `tripped`
// has NO edge to `running` — the only exit is `reset_walked`, a state a
// human reaches by walking the belt line. Fail-safe wiring: the pull-
// cord loop is supervised, and a severed wire lands in `circuit_fault`,
// a TERMINAL state soma verify proves has no exit — a conveyor whose
// safety circuit is cut can never be software-restarted.
cell ConveyorGuard {
face {
signal start_belt(id: String) -> Map
signal stop_belt(id: String) -> String
signal pull_cord(id: String) -> String
signal walk_the_line(id: String) -> String
signal wire_severed(id: String) -> String
}
memory {
// completed walkdowns since the last trip — the reset evidence
walkdowns: Map<String, Int> [persistent, consistent]
invariant walkdowns >= 0 && walkdowns <= 1000
}
state belt {
initial: stopped
stopped -> running
running -> stopped // normal stop
running -> tripped // pull-cord: LATCHED
tripped -> reset_walked // a person walked the full belt line
reset_walked -> stopped // re-arm; start is a separate command
running -> circuit_fault // supervision saw the loop go open
stopped -> circuit_fault
tripped -> circuit_fault
}
on start_belt(id: String) {
let r = try { transition(id, "running") }
if r.error != () {
return map("ok", false, "state", get_status(id),
"reason", "belt is latched out — no restart from here")
}
return map("ok", true, "state", get_status(id))
}
on stop_belt(id: String) { transition(id, "stopped") return get_status(id) }
on pull_cord(id: String) { transition(id, "tripped") return get_status(id) }
on walk_the_line(id: String) {
transition(id, "reset_walked")
walkdowns.set(id, (walkdowns.get(id) ?? 0) + 1)
transition(id, "stopped")
return get_status(id)
}
on wire_severed(id: String) { transition(id, "circuit_fault") return get_status(id) }
}
cell test ConveyorTests {
rules {
// normal run and stop
assert start_belt("c1").ok == true
assert stop_belt("c1") == "stopped"
// pull-cord trip LATCHES: no restart, the edge is absent
assert start_belt("c1").ok == true
assert pull_cord("c1") == "tripped"
assert start_belt("c1").ok == false
assert_fails transition("c1", "running")
// only a walked line releases the latch, then a fresh start
assert walk_the_line("c1") == "stopped"
assert start_belt("c1").ok == true
assert stop_belt("c1") == "stopped"
// a severed pull-cord wire is TERMINAL — proven, and pinned here
assert wire_severed("c1") == "circuit_fault"
assert start_belt("c1").ok == false
assert_fails transition("c1", "stopped")
assert_fails transition("c1", "reset_walked")
// the walkdown ledger never goes negative
assert_fails walkdowns.set("c1", -1)
}
}What the toolchain says
$ soma check conveyor_pullcord.cell
✓ All checks passed.$ soma test conveyor_pullcord.cell
test ConveyorTests ...
✓ assert start_belt("c1").ok == true
✓ assert stop_belt("c1") == "stopped"
✓ assert start_belt("c1").ok == true
✓ assert pull_cord("c1") == "tripped"
✓ assert start_belt("c1").ok == false
✓ assert_fails transition("c1", "running") — raised invalid transition: tripped → running. Current state: 'tripped'. Valid targets: [reset_walked, circuit_fault]
✓ assert walk_the_line("c1") == "stopped"
✓ assert start_belt("c1").ok == true
✓ assert stop_belt("c1") == "stopped"
✓ assert wire_severed("c1") == "circuit_fault"
✓ assert start_belt("c1").ok == false
✓ assert_fails transition("c1", "stopped") — raised invalid transition: circuit_fault → stopped. Current state: 'circuit_fault'. Valid targets: []
✓ assert_fails transition("c1", "reset_walked") — raised invalid transition: circuit_fault → reset_walked. Current state: 'circuit_fault'. Valid targets: []
✓ assert_fails walkdowns.set("c1", -1) — raised memory invariant violated on 'walkdowns': walkdowns >= 0 && walkdowns <= 1000 — rejected write of -1 (key "c1"); the slot is unchanged
14 tests: 14 passed, 0 failed$ soma verify conveyor_pullcord.cell
Verifying conveyor_pullcord.cell...
State machine 'belt': 5 states, initial 'stopped'
States: [circuit_fault, reset_walked, running, stopped, tripped]
✓ 5 states, 8 transitions
✓ all states reachable from 'stopped'
✓ terminal states: [circuit_fault]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ think-isolated: CTL safety properties hold regardless of LLM output (5 handlers, 6 literal transitions, 0 dynamic)
✓ termination: all 5 handlers structurally terminate
✓ refinement: handler `start_belt` ⟶ {running}
✓ refinement: handler `stop_belt` ⟶ {stopped}
✓ refinement: handler `pull_cord` ⟶ {tripped}
✓ refinement: handler `walk_the_line` ⟶ {reset_walked, reset_walked → stopped}
✓ refinement: handler `wire_severed` ⟶ {circuit_fault}
· transition sources: `start_belt`, `stop_belt`, `pull_cord`, `walk_the_line`, `wire_severed` call 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)
ConveyorGuard/invariants:
✓ invariant walkdowns >= 0 — writer 'walk_the_line' proven by induction (writes (walkdowns.get(id) ?? 0) + 1)
⚠ invariant walkdowns >= 0 && walkdowns <= 1000 — runtime-checked (computed values): walk_the_line → walkdowns [walkdowns <= 1000] because `walkdowns` has no known range
13 passed, 1 warnings, 0 failures
Temporal properties for 'belt':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK