soma.cell

Dock door interlock — the trailer that drives off with the forklift

OSHA logs several deaths a year from one warehouse scenario: a driver, thinking loading is done, pulls the trailer away from the dock while a forklift is still crossing the dock plate. The forklift drops through the gap — operators have been crushed under their own machines. The industry fix is a vehicle restraint (a hook that grabs the trailer's rear bar) interlocked with the dock door. In ladder logic that interlock is wiring you hope is right. Here it is a theorem: the door can only be open in the `loading` state, and `loading` is only reachable with the restraint LOCKED. The trailer can only depart from `trailer_at_door` — restraint off, door closed. The state "door open, trailer free to move" does not exist to race into.

A Soma program from the logistics part of the verified corpus: 86 lines, cells DockDoor, RestraintInterlock. It passes soma check with soma 3.2.4.

How it is built

The cell DockDoor declares 7 signals in its face: trailer_arrives(door: String) -> Map, lock_restraint(door: String) -> Map, open_door(door: String) -> Map, close_door(door: String) -> Map, release_restraint(door: String) -> Map, trailer_departs(door: String) -> Map and door_state(door: String) -> String. Its state machine dock starts in vacant and allows 6 transitions: vacant -> trailer_at_door, trailer_at_door -> restrained, restrained -> loading, loading -> restrained, restrained -> trailer_at_door and trailer_at_door -> vacant. Handlers: trailer_arrives (line 44), lock_restraint (line 45), open_door (line 46), close_door (line 47), release_restraint (line 48), trailer_departs (line 49), move_dock (line 51) and door_state (line 57).

The file also holds 1 test cell (RestraintInterlock) with 13 assertions, run by soma test.

Generated from soma describe on this file.

The source

dock_door_interlock.cell

// ============================================================================
// DOCK DOOR INTERLOCK — the trailer that drives off with the forklift
// ============================================================================
//
// OSHA logs several deaths a year from one warehouse scenario: a driver,
// thinking loading is done, pulls the trailer away from the dock while a
// forklift is still crossing the dock plate. The forklift drops through
// the gap — operators have been crushed under their own machines. The
// industry fix is a vehicle restraint (a hook that grabs the trailer's
// rear bar) interlocked with the dock door.
//
// In ladder logic that interlock is wiring you hope is right. Here it is
// a theorem: the door can only be open in the `loading` state, and
// `loading` is only reachable with the restraint LOCKED. The trailer can
// only depart from `trailer_at_door` — restraint off, door closed. The
// state "door open, trailer free to move" does not exist to race into.
// ============================================================================

cell DockDoor {
    face {
        signal trailer_arrives(door: String) -> Map
        signal lock_restraint(door: String) -> Map
        signal open_door(door: String) -> Map
        signal close_door(door: String) -> Map
        signal release_restraint(door: String) -> Map
        signal trailer_departs(door: String) -> Map
        signal door_state(door: String) -> String
    }

    //   vacant           no trailer, door closed
    //   trailer_at_door  trailer present, restraint OFF, door closed
    //   restrained       hook engaged on the rear-impact bar, door closed
    //   loading          door open — ONLY reachable while restrained
    state dock {
        initial: vacant
        vacant          -> trailer_at_door
        trailer_at_door -> restrained
        restrained      -> loading
        loading         -> restrained        // door closed before unhooking
        restrained      -> trailer_at_door   // unhook only with door shut
        trailer_at_door -> vacant            // departure: door shut, hook off
    }

    on trailer_arrives(door: String) { return move_dock(door, "trailer_at_door") }
    on lock_restraint(door: String) { return move_dock(door, "restrained") }
    on open_door(door: String) { return move_dock(door, "loading") }
    on close_door(door: String) { return move_dock(door, "restrained") }
    on release_restraint(door: String) { return move_dock(door, "trailer_at_door") }
    on trailer_departs(door: String) { return move_dock(door, "vacant") }

    on move_dock(door: String, to: String) {
        let r = try { transition(door, to) }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "door", door, "state", to)
    }

    on door_state(door: String) { return get_status(door) }
}

cell test RestraintInterlock {
    rules {
        // the legal cycle
        assert trailer_arrives("D4").ok == true
        assert lock_restraint("D4").ok == true
        assert open_door("D4").ok == true
        assert door_state("D4") == "loading"

        // THE KILLER: trailer departs while the door is open and a
        // forklift is on the plate. From `loading` the only exit closes
        // the door first — departure raises.
        assert_fails transition("D4", "vacant")
        assert_fails transition("D4", "trailer_at_door")

        // you also cannot unhook the restraint with the door open
        assert close_door("D4").ok == true
        assert release_restraint("D4").ok == true
        assert trailer_departs("D4").ok == true
        assert door_state("D4") == "vacant"

        // and you cannot open the door before the restraint is locked
        assert trailer_arrives("D7").ok == true
        assert_fails transition("D7", "loading")
        assert open_door("D7").ok == false
    }
}

What the toolchain says

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

$ soma check dock_door_interlock.cell
✓ All checks passed.
$ soma test dock_door_interlock.cell
test RestraintInterlock ...
  ✓ assert trailer_arrives("D4").ok == true
  ✓ assert lock_restraint("D4").ok == true
  ✓ assert open_door("D4").ok == true
  ✓ assert door_state("D4") == "loading"
  ✓ assert_fails transition("D4", "vacant") — raised invalid transition: loading → vacant. Current state: 'loading'. Valid targets: [restrained]
  ✓ assert_fails transition("D4", "trailer_at_door") — raised invalid transition: loading → trailer_at_door. Current state: 'loading'. Valid targets: [restrained]
  ✓ assert close_door("D4").ok == true
  ✓ assert release_restraint("D4").ok == true
  ✓ assert trailer_departs("D4").ok == true
  ✓ assert door_state("D4") == "vacant"
  ✓ assert trailer_arrives("D7").ok == true
  ✓ assert_fails transition("D7", "loading") — raised invalid transition: trailer_at_door → loading. Current state: 'trailer_at_door'. Valid targets: [restrained, vacant]
  ✓ assert open_door("D7").ok == false

13 tests: 13 passed, 0 failed
$ soma verify dock_door_interlock.cell
Verifying dock_door_interlock.cell...

State machine 'dock': 4 states, initial 'vacant'
  States: [loading, restrained, trailer_at_door, vacant]

  ✓ 4 states, 6 transitions
  ✓ all states reachable from 'vacant'
  ✓ reactive machine: no terminal state, and every reachable state can return to 'vacant'
  ✓ no deadlocks
  ✓ liveness: every reachable state can return to 'vacant' (reactive machine)
  ⚠ dynamic transition targets: handler `move_dock` uses a dynamic (computed) transition target — the state these handlers move to is computed at run time (from input or an LLM answer), so the machine's safety is checked at run time for them, not proven
  ✓ termination: all 8 handlers structurally terminate
  ⚠ refinement (partial — dynamic targets fall back to runtime check): handler `move_dock` calls transition() with a computed target — which edge it takes is checked at run time (invalid_transition), not proven; write the target as a literal state to have it verified
  ✓ refinement (partial — dynamic targets fall back to runtime check): handler `move_dock` ⟶ <dynamic>

7 passed, 2 warnings, 0 failures

  Temporal properties for 'dock':
  ✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK

Builtins used

get_status, map, transition

Source file: /corpus/logistics/dock_door_interlock.cell · examples/corpus/logistics/dock_door_interlock.cell