soma.cell

A media-player transport state machine

stopped -> playing <-> paused, with stop reachable from play/pause. A typed (sum-type) machine so an illegal transport command is caught statically by the variant name. This is a reactive cyclic system: the proof obligation is deadlock-freedom.

A Soma program from the state_machines part of the verified corpus: 57 lines, cells Transport, MediaPlayer, MediaPlayerTests. It passes soma check with soma 3.2.4.

How it is built

The cell MediaPlayer declares 6 signals in its face: play_track(id: String) -> String, pause_track(id: String) -> String, resume_track(id: String) -> String, stop_track(id: String) -> String, transport_of(id: String) -> String and next(id: String) -> List<String>. Its state machine transport starts in Stopped and allows 5 transitions: Stopped -> Playing, Playing -> Paused, Paused -> Playing, Playing -> Stopped and Paused -> Stopped. Handlers: play_track (line 30), pause_track (line 31), resume_track (line 32), stop_track (line 33), transport_of (line 34) and next (line 35).

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

Generated from soma describe on this file.

The source

media_player.cell

// A media-player transport state machine.
// stopped -> playing <-> paused, with stop reachable from play/pause.
// A typed (sum-type) machine so an illegal transport command is caught
// statically by the variant name. This is a reactive cyclic system: the
// proof obligation is deadlock-freedom.

cell type Transport {
    variants { Stopped  Playing  Paused }
}

cell MediaPlayer {
    face {
        signal play_track(id: String) -> String
        signal pause_track(id: String) -> String
        signal resume_track(id: String) -> String
        signal stop_track(id: String) -> String
        signal transport_of(id: String) -> String
        signal next(id: String) -> List<String>
    }

    state transport: Transport {
        initial: Stopped
        Stopped -> Playing
        Playing -> Paused
        Paused  -> Playing
        Playing -> Stopped
        Paused  -> Stopped
    }

    on play_track(id: String)   { transition(id, Playing) return get_status(id) }
    on pause_track(id: String)  { transition(id, Paused)  return get_status(id) }
    on resume_track(id: String) { transition(id, Playing) return get_status(id) }
    on stop_track(id: String)   { transition(id, Stopped) return get_status(id) }
    on transport_of(id: String) { return get_status(id) }
    on next(id: String)         { return valid_transitions(id) }
}

cell test MediaPlayerTests {
    rules {
        // play, pause, resume, stop
        assert play_track("m1") == "Playing"
        assert pause_track("m1") == "Paused"
        assert resume_track("m1") == "Playing"
        assert stop_track("m1") == "Stopped"

        // pause then stop directly
        assert play_track("m2") == "Playing"
        assert pause_track("m2") == "Paused"
        assert stop_track("m2") == "Stopped"

        // illegal: cannot pause something that is stopped
        assert_fails pause_track("m3")
        // illegal: a stopped transport cannot jump straight to Paused
        assert_fails transition("m4", Paused)
    }
}

What the toolchain says

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

$ soma check media_player.cell
✓ All checks passed.
$ soma test media_player.cell
test MediaPlayerTests ...
  ✓ assert play_track("m1") == "Playing"
  ✓ assert pause_track("m1") == "Paused"
  ✓ assert resume_track("m1") == "Playing"
  ✓ assert stop_track("m1") == "Stopped"
  ✓ assert play_track("m2") == "Playing"
  ✓ assert pause_track("m2") == "Paused"
  ✓ assert stop_track("m2") == "Stopped"
  ✓ assert_fails pause_track("m3") — raised invalid transition: Stopped → Paused. Current state: 'Stopped'. Valid targets: [Playing]
  ✓ assert_fails transition("m4", Paused) — raised invalid transition: Stopped → Paused. Current state: 'Stopped'. Valid targets: [Playing]

9 tests: 9 passed, 0 failed
$ soma verify media_player.cell
Verifying media_player.cell...

State machine 'transport': 3 states, initial 'Stopped'
  States: [Paused, Playing, Stopped]

  ✓ 3 states, 5 transitions
  ✓ all states reachable from 'Stopped'
  ✓ reactive machine: no terminal state, and every reachable state can return to 'Stopped'
  ✓ no deadlocks
  ✓ liveness: every reachable state can return to 'Stopped' (reactive machine)
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (4 handlers, 4 literal transitions, 0 dynamic)
  ✓ termination: all 6 handlers structurally terminate
  ✓ refinement: handler `play_track` ⟶ {Playing}
  ✓ refinement: handler `pause_track` ⟶ {Paused}
  ✓ refinement: handler `resume_track` ⟶ {Playing}
  ✓ refinement: handler `stop_track` ⟶ {Stopped}
  · transition sources: `play_track`, `pause_track`, `resume_track`, `stop_track` 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)

11 passed, 0 warnings, 0 failures

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

Builtins used

get_status, transition, valid_transitions

Source file: /corpus/state_machines/media_player.cell · examples/corpus/state_machines/media_player.cell