soma.cell

Blast furnace top — never open a door against live CO gas

Blast furnace top gas is roughly a quarter carbon monoxide at positive pressure; opening an access door on a live furnace has gassed entire maintenance crews. The kill chain is isolation -> venting -> gas test, and every fatality is a skipped link. Here the door has no edge from any pressurized state: `live -> door_open` and `isolated -> door_open` do not exist, only `vented` reaches the door. The gas-test ledger is the final wall — entry is logged with the measured CO ppm, and the memory itself refuses a reading above the 30 ppm entry limit.

A Soma program from the safety_interlocks part of the verified corpus: 83 lines, cells FurnaceTop, FurnaceTests. It passes soma check with soma 3.2.4.

How it is built

The cell FurnaceTop declares 5 signals in its face: isolate(id: String) -> String, resume_blast(id: String) -> String, vent_and_flare(id: String) -> String, open_door(id: String, co_ppm: Int) -> Map and close_door(id: String) -> String; keeps 1 memory slot: entry_co (Map<String, Int>, persistent and consistent) under the invariant entry_co >= 0 && entry_co <= 30. Its state machine furnace starts in live and allows 6 transitions: live -> isolated, isolated -> live, isolated -> vented, vented -> door_open, door_open -> vented and vented -> live. Handlers: isolate (line 36), resume_blast (line 37), vent_and_flare (line 38), open_door (line 40) and close_door (line 54).

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

Generated from soma describe on this file.

The source

blast_furnace_door.cell

// BLAST FURNACE TOP — never open a door against live CO gas.
// Blast furnace top gas is roughly a quarter carbon monoxide at positive
// pressure; opening an access door on a live furnace has gassed entire
// maintenance crews. The kill chain is isolation -> venting -> gas test,
// and every fatality is a skipped link. Here the door has no edge from
// any pressurized state: `live -> door_open` and `isolated -> door_open`
// do not exist, only `vented` reaches the door. The gas-test ledger is
// the final wall — entry is logged with the measured CO ppm, and the
// memory itself refuses a reading above the 30 ppm entry limit.

cell FurnaceTop {
    face {
        signal isolate(id: String) -> String
        signal resume_blast(id: String) -> String
        signal vent_and_flare(id: String) -> String
        signal open_door(id: String, co_ppm: Int) -> Map
        signal close_door(id: String) -> String
    }

    memory {
        // CO at the door on entry, ppm — 30 is the ceiling, structurally
        entry_co: Map<String, Int> [persistent, consistent]
        invariant entry_co >= 0 && entry_co <= 30
    }

    state furnace {
        initial: live
        live -> isolated          // blast off, goggle valves shut
        isolated -> live          // resume without ever opening
        isolated -> vented        // bleeders open, top gas flared off
        vented -> door_open       // the ONLY edge to an open door
        door_open -> vented
        vented -> live            // reseal, purge, resume blast
    }

    on isolate(id: String)       { transition(id, "isolated")  return get_status(id) }
    on resume_blast(id: String)  { transition(id, "live")  return get_status(id) }
    on vent_and_flare(id: String) { transition(id, "vented")  return get_status(id) }

    on open_door(id: String, co_ppm: Int) {
        // gas test FIRST: a hot reading is refused by the ledger itself
        let w = try { entry_co.set(id, co_ppm) }
        if w.error != () {
            return map("ok", false, "state", get_status(id),
                       "reason", "gas test failed: {co_ppm} ppm CO (limit 30)")
        }
        let r = try { transition(id, "door_open") }
        if r.error != () {
            return map("ok", false, "reason", "furnace not vented — door stays shut")
        }
        return map("ok", true, "state", get_status(id), "co_ppm", co_ppm)
    }

    on close_door(id: String) { transition(id, "vented")  return get_status(id) }
}

cell test FurnaceTests {
    rules {
        // a live furnace's door does not open — no such edge
        assert_fails transition("f1", "door_open")
        assert open_door("f1", 5).ok == false

        // isolated is NOT vented: still pressurized, still no door
        assert isolate("f1") == "isolated"
        assert_fails transition("f1", "door_open")

        // vented + clean gas test: entry granted
        assert vent_and_flare("f1") == "vented"
        assert open_door("f1", 8).ok == true

        // an open door cannot coexist with a live furnace
        assert_fails transition("f1", "live")
        assert close_door("f1") == "vented"

        // a hot gas test is refused BY THE MEMORY, door stays shut
        assert open_door("f1", 250).ok == false
        assert_fails entry_co.set("f1", 31)

        // reseal and resume
        assert resume_blast("f1") == "live"
    }
}

What the toolchain says

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

$ soma check blast_furnace_door.cell
✓ All checks passed.
$ soma test blast_furnace_door.cell
test FurnaceTests ...
  ✓ assert_fails transition("f1", "door_open") — raised invalid transition: live → door_open. Current state: 'live'. Valid targets: [isolated]
  ✓ assert open_door("f1", 5).ok == false
  ✓ assert isolate("f1") == "isolated"
  ✓ assert_fails transition("f1", "door_open") — raised invalid transition: isolated → door_open. Current state: 'isolated'. Valid targets: [live, vented]
  ✓ assert vent_and_flare("f1") == "vented"
  ✓ assert open_door("f1", 8).ok == true
  ✓ assert_fails transition("f1", "live") — raised invalid transition: door_open → live. Current state: 'door_open'. Valid targets: [vented]
  ✓ assert close_door("f1") == "vented"
  ✓ assert open_door("f1", 250).ok == false
  ✓ assert_fails entry_co.set("f1", 31) — raised memory invariant violated on 'entry_co': entry_co >= 0 && entry_co <= 30 — rejected write of 31 (key "f1"); the slot is unchanged
  ✓ assert resume_blast("f1") == "live"

11 tests: 11 passed, 0 failed
$ soma verify blast_furnace_door.cell
Verifying blast_furnace_door.cell...

State machine 'furnace': 4 states, initial 'live'
  States: [door_open, isolated, live, vented]

  ✓ 4 states, 6 transitions
  ✓ all states reachable from 'live'
  ✓ reactive machine: no terminal state, and every reachable state can return to 'live'
  ✓ no deadlocks
  ✓ liveness: every reachable state can return to 'live' (reactive machine)
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (5 handlers, 5 literal transitions, 0 dynamic)
  ✓ termination: all 5 handlers structurally terminate
  ✓ refinement: handler `isolate` ⟶ {isolated}
  ✓ refinement: handler `resume_blast` ⟶ {live}
  ✓ refinement: handler `vent_and_flare` ⟶ {vented}
  ✓ refinement: handler `open_door` ⟶ {door_open}
  ✓ refinement: handler `close_door` ⟶ {vented}
  · transition sources: `isolate`, `resume_blast`, `vent_and_flare`, `open_door`, `close_door` 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)

FurnaceTop/invariants:

  ⚠ invariant entry_co >= 0 && entry_co <= 30 — runtime-checked (computed values): open_door → entry_co because `co_ppm` is a parameter (narrow it: `require co_ppm >= 0 && co_ppm <= 30 else …`)

12 passed, 1 warnings, 0 failures

  Temporal properties for 'furnace':
  ✓ 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/blast_furnace_door.cell · examples/corpus/safety_interlocks/blast_furnace_door.cell