Drawbridge — the span never moves while the deck is open to traffic
Bascule bridges have killed pedestrians and drivers who were still on the span when it began to rise — tenders raising on a green gate, cars driving around half-lowered barriers. The opening sequence is law: signals stop NEW traffic, gates physically close the deck, and only then may the span move. Here that order is the state machine itself — `traffic_flowing -> span_raised` does not exist, and a raised span has no edge back to traffic without first seating and re-gating. The span angle ledger refuses any value beyond the 75-degree mechanical stop.
A Soma program from the safety_interlocks part of the verified corpus: 85 lines, cells BridgeControl, BridgeTests. It passes soma check with soma 3.2.4.
How it is built
The cell BridgeControl declares 6 signals in its face: stop_traffic(id: String) -> String, lower_gates(id: String) -> String, raise_span(id: String) -> Map, seat_span(id: String) -> String, lift_gates(id: String) -> String and reopen_road(id: String) -> String; keeps 1 memory slot: span_deg (Map<String, Int>, persistent and consistent) under the invariant span_deg >= 0 && span_deg <= 75. Its state machine span starts in traffic_flowing and allows 6 transitions: traffic_flowing -> signals_on, signals_on -> traffic_flowing, signals_on -> gates_down, gates_down -> signals_on, gates_down -> span_raised and span_raised -> gates_down. Handlers: stop_traffic (line 37), lower_gates (line 38), raise_span (line 40), seat_span (line 49), lift_gates (line 55) and reopen_road (line 56).
The file also holds 1 test cell (BridgeTests) with 13 assertions, run by soma test.
The source
drawbridge.cell
// DRAWBRIDGE — the span never moves while the deck is open to traffic.
// Bascule bridges have killed pedestrians and drivers who were still on
// the span when it began to rise — tenders raising on a green gate, cars
// driving around half-lowered barriers. The opening sequence is law:
// signals stop NEW traffic, gates physically close the deck, and only
// then may the span move. Here that order is the state machine itself —
// `traffic_flowing -> span_raised` does not exist, and a raised span has
// no edge back to traffic without first seating and re-gating. The span
// angle ledger refuses any value beyond the 75-degree mechanical stop.
cell BridgeControl {
face {
signal stop_traffic(id: String) -> String
signal lower_gates(id: String) -> String
signal raise_span(id: String) -> Map
signal seat_span(id: String) -> String
signal lift_gates(id: String) -> String
signal reopen_road(id: String) -> String
}
memory {
// span angle in degrees; the rack runs out at 75
span_deg: Map<String, Int> [persistent, consistent]
invariant span_deg >= 0 && span_deg <= 75
}
state span {
initial: traffic_flowing
traffic_flowing -> signals_on // bells + red flashers
signals_on -> traffic_flowing // abort: no vessel after all
signals_on -> gates_down // barriers seal the deck
gates_down -> signals_on // abort before the span moved
gates_down -> span_raised // ONLY now may the leaf move
span_raised -> gates_down // seat + lock before gates lift
}
on stop_traffic(id: String) { transition(id, "signals_on") return get_status(id) }
on lower_gates(id: String) { transition(id, "gates_down") return get_status(id) }
on raise_span(id: String) {
let r = try { transition(id, "span_raised") }
if r.error != () {
return map("ok", false, "reason", "deck not proven closed to traffic")
}
span_deg.set(id, 72)
return map("ok", true, "state", get_status(id), "deg", span_deg.get(id))
}
on seat_span(id: String) {
span_deg.set(id, 0) // leaf seated and locked FIRST
transition(id, "gates_down")
return get_status(id)
}
on lift_gates(id: String) { transition(id, "signals_on") return get_status(id) }
on reopen_road(id: String) { transition(id, "traffic_flowing") return get_status(id) }
}
cell test BridgeTests {
rules {
// raising the span onto live traffic: no such edge, twice over
assert_fails transition("br1", "span_raised")
assert raise_span("br1").ok == false
// the lawful sequence, in order, no skips
assert stop_traffic("br1") == "signals_on"
assert_fails transition("br1", "span_raised") // gates still up
assert lower_gates("br1") == "gates_down"
assert raise_span("br1").ok == true
// a raised span cannot jump back to open road
assert_fails transition("br1", "traffic_flowing")
assert_fails transition("br1", "signals_on")
// come down the same ladder
assert seat_span("br1") == "gates_down"
assert lift_gates("br1") == "signals_on"
assert reopen_road("br1") == "traffic_flowing"
// the rack ledger refuses an over-travel or a negative angle
assert_fails span_deg.set("br1", 90)
assert_fails span_deg.set("br1", -3)
}
}What the toolchain says
$ soma check drawbridge.cell
✓ All checks passed.$ soma test drawbridge.cell
test BridgeTests ...
✓ assert_fails transition("br1", "span_raised") — raised invalid transition: traffic_flowing → span_raised. Current state: 'traffic_flowing'. Valid targets: [signals_on]
✓ assert raise_span("br1").ok == false
✓ assert stop_traffic("br1") == "signals_on"
✓ assert_fails transition("br1", "span_raised") — raised invalid transition: signals_on → span_raised. Current state: 'signals_on'. Valid targets: [traffic_flowing, gates_down]
✓ assert lower_gates("br1") == "gates_down"
✓ assert raise_span("br1").ok == true
✓ assert_fails transition("br1", "traffic_flowing") — raised invalid transition: span_raised → traffic_flowing. Current state: 'span_raised'. Valid targets: [gates_down]
✓ assert_fails transition("br1", "signals_on") — raised invalid transition: span_raised → signals_on. Current state: 'span_raised'. Valid targets: [gates_down]
✓ assert seat_span("br1") == "gates_down"
✓ assert lift_gates("br1") == "signals_on"
✓ assert reopen_road("br1") == "traffic_flowing"
✓ assert_fails span_deg.set("br1", 90) — raised memory invariant violated on 'span_deg': span_deg >= 0 && span_deg <= 75 — rejected write of 90 (key "br1"); the slot is unchanged
✓ assert_fails span_deg.set("br1", -3) — raised memory invariant violated on 'span_deg': span_deg >= 0 && span_deg <= 75 — rejected write of -3 (key "br1"); the slot is unchanged
13 tests: 13 passed, 0 failed$ soma verify drawbridge.cell
Verifying drawbridge.cell...
State machine 'span': 4 states, initial 'traffic_flowing'
States: [gates_down, signals_on, span_raised, traffic_flowing]
✓ 4 states, 6 transitions
✓ all states reachable from 'traffic_flowing'
✓ reactive machine: no terminal state, and every reachable state can return to 'traffic_flowing'
✓ no deadlocks
✓ liveness: every reachable state can return to 'traffic_flowing' (reactive machine)
✓ think-isolated: CTL safety properties hold regardless of LLM output (6 handlers, 6 literal transitions, 0 dynamic)
✓ termination: all 6 handlers structurally terminate
✓ refinement: handler `stop_traffic` ⟶ {signals_on}
✓ refinement: handler `lower_gates` ⟶ {gates_down}
✓ refinement: handler `raise_span` ⟶ {span_raised}
✓ refinement: handler `seat_span` ⟶ {gates_down}
✓ refinement: handler `lift_gates` ⟶ {signals_on}
✓ refinement: handler `reopen_road` ⟶ {traffic_flowing}
· transition sources: `stop_traffic`, `lower_gates`, `raise_span`, `seat_span`, `lift_gates`, `reopen_road` 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)
BridgeControl/invariants:
✓ invariant span_deg >= 0 && span_deg <= 75 — writer 'raise_span' proven (writes 72)
✓ invariant span_deg >= 0 && span_deg <= 75 — writer 'seat_span' proven (writes 0)
15 passed, 0 warnings, 0 failures
Temporal properties for 'span':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK