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

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.

Generated from soma describe on this file.

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

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ 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

Builtins used

get_status, map, transition

Source file: /corpus/safety_interlocks/drawbridge.cell · examples/corpus/safety_interlocks/drawbridge.cell