Hazmat bay segregation — Beirut, as a state that cannot exist
On 4 August 2020, 2,750 tonnes of ammonium nitrate stored for six years in Beirut's Hangar 12 — alongside a cache of FIREWORKS — detonated and killed more than 200 people. Tianjin 2015 was the same story: oxidizers warehoused next to fuels, 173 dead. Every dangerous-goods code on earth (IMDG, NFPA 400) says the same thing: oxidizers and flammables never share a bay. And in both cities, the paperwork said they did anyway. Here the bay is a state machine with NO mixed state: a bay holds oxidizer, or flammables, or nothing. The state "both" is not refused — it is unrepresentable, and soma verify proves the reachable set. A tonnage invariant caps any single bay at 25 t of oxidizer: a 2,750-tonne Hangar 12 cannot be written into this ledger at all.
A Soma program from the logistics part of the verified corpus: 120 lines, cells HazmatBay, Hangar12CannotHappen. It passes soma check with soma 3.2.4.
How it is built
The cell HazmatBay declares 4 signals in its face: stow_oxidizer(bay: String, tonnes: Int) -> Map, stow_flammable(bay: String, tonnes: Int) -> Map, clear_bay(bay: String) -> Map and bay_contents(bay: String) -> Map; keeps 2 memory slots: oxidizer_t (Map<String, Int>, persistent and consistent) under the invariant oxidizer_t >= 0 && oxidizer_t <= 25 and flammable_t (Map<String, Int>, persistent and consistent) under the invariant flammable_t >= 0 && flammable_t <= 25. Its state machine bay_class starts in empty and allows 4 transitions: empty -> holds_oxidizer, empty -> holds_flammable, holds_oxidizer -> empty and holds_flammable -> empty. Handlers: stow_oxidizer (line 45), stow_flammable (line 62), clear_bay (line 79) and bay_contents (line 87).
The file also holds 1 test cell (Hangar12CannotHappen) with 14 assertions, run by soma test.
The source
hazmat_bay_segregation.cell
// ============================================================================
// HAZMAT BAY SEGREGATION — Beirut, as a state that cannot exist
// ============================================================================
//
// On 4 August 2020, 2,750 tonnes of ammonium nitrate stored for six years
// in Beirut's Hangar 12 — alongside a cache of FIREWORKS — detonated and
// killed more than 200 people. Tianjin 2015 was the same story: oxidizers
// warehoused next to fuels, 173 dead. Every dangerous-goods code on earth
// (IMDG, NFPA 400) says the same thing: oxidizers and flammables never
// share a bay. And in both cities, the paperwork said they did anyway.
//
// Here the bay is a state machine with NO mixed state: a bay holds
// oxidizer, or flammables, or nothing. The state "both" is not refused —
// it is unrepresentable, and soma verify proves the reachable set.
// A tonnage invariant caps any single bay at 25 t of oxidizer: a
// 2,750-tonne Hangar 12 cannot be written into this ledger at all.
// ============================================================================
cell HazmatBay {
face {
signal stow_oxidizer(bay: String, tonnes: Int) -> Map
signal stow_flammable(bay: String, tonnes: Int) -> Map
signal clear_bay(bay: String) -> Map
signal bay_contents(bay: String) -> Map
}
memory {
// tonnes of oxidizer per bay — NFPA-style cap per storage bay
oxidizer_t: Map<String, Int> [persistent, consistent]
invariant oxidizer_t >= 0 && oxidizer_t <= 25
flammable_t: Map<String, Int> [persistent, consistent]
invariant flammable_t >= 0 && flammable_t <= 25
}
// There is no holds_both. That absence is the whole point.
state bay_class {
initial: empty
empty -> holds_oxidizer
empty -> holds_flammable
holds_oxidizer -> empty
holds_flammable -> empty
}
on stow_oxidizer(bay: String, tonnes: Int) {
if tonnes <= 0 { return map("ok", false, "reason", "tonnage must be positive") }
if get_status(bay) != "holds_oxidizer" {
let r = try { transition(bay, "holds_oxidizer") }
if r.error != () {
return map("ok", false, "reason", "SEGREGATION: bay {bay} holds flammables")
}
}
let cur = oxidizer_t.get(bay) ?? 0
let w = try { oxidizer_t.set(bay, cur + tonnes) }
if w.error != () {
if cur == 0 { transition(bay, "empty") }
return map("ok", false, "reason", "bay cap is 25 t of oxidizer")
}
return map("ok", true, "bay", bay, "oxidizer_t", cur + tonnes)
}
on stow_flammable(bay: String, tonnes: Int) {
if tonnes <= 0 { return map("ok", false, "reason", "tonnage must be positive") }
if get_status(bay) != "holds_flammable" {
let r = try { transition(bay, "holds_flammable") }
if r.error != () {
return map("ok", false, "reason", "SEGREGATION: bay {bay} holds an oxidizer")
}
}
let cur = flammable_t.get(bay) ?? 0
let w = try { flammable_t.set(bay, cur + tonnes) }
if w.error != () {
if cur == 0 { transition(bay, "empty") }
return map("ok", false, "reason", "bay cap is 25 t of flammables")
}
return map("ok", true, "bay", bay, "flammable_t", cur + tonnes)
}
on clear_bay(bay: String) {
oxidizer_t.set(bay, 0)
flammable_t.set(bay, 0)
let r = try { transition(bay, "empty") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "bay", bay)
}
on bay_contents(bay: String) {
return map("bay", bay, "class", get_status(bay),
"oxidizer_t", oxidizer_t.get(bay) ?? 0,
"flammable_t", flammable_t.get(bay) ?? 0)
}
}
cell test Hangar12CannotHappen {
rules {
// ammonium nitrate goes into bay H12 — top-ups of the SAME class are fine
assert stow_oxidizer("H12", 15).ok == true
assert stow_oxidizer("H12", 5).ok == true
assert bay_contents("H12").oxidizer_t == 20
assert bay_contents("H12").class == "holds_oxidizer"
// THE BEIRUT WRITE: fireworks into the same bay. The machine has
// no oxidizer->flammable path; the request is refused.
assert stow_flammable("H12", 3).ok == false
assert_fails transition("H12", "holds_flammable")
assert bay_contents("H12").flammable_t == 0
// and 2,750 tonnes can never be booked into one bay
assert stow_oxidizer("H12", 2750).ok == false
assert_fails oxidizer_t.set("H12", 2750)
assert bay_contents("H12").oxidizer_t == 20
// emptied and re-inspected, the bay may change class
assert clear_bay("H12").ok == true
assert stow_flammable("H12", 5).ok == true
assert bay_contents("H12").class == "holds_flammable"
assert stow_oxidizer("H12", 1).ok == false
}
}What the toolchain says
$ soma check hazmat_bay_segregation.cell
✓ All checks passed.$ soma test hazmat_bay_segregation.cell
test Hangar12CannotHappen ...
✓ assert stow_oxidizer("H12", 15).ok == true
✓ assert stow_oxidizer("H12", 5).ok == true
✓ assert bay_contents("H12").oxidizer_t == 20
✓ assert bay_contents("H12").class == "holds_oxidizer"
✓ assert stow_flammable("H12", 3).ok == false
✓ assert_fails transition("H12", "holds_flammable") — raised invalid transition: holds_oxidizer → holds_flammable. Current state: 'holds_oxidizer'. Valid targets: [empty]
✓ assert bay_contents("H12").flammable_t == 0
✓ assert stow_oxidizer("H12", 2750).ok == false
✓ assert_fails oxidizer_t.set("H12", 2750) — raised memory invariant violated on 'oxidizer_t': oxidizer_t >= 0 && oxidizer_t <= 25 — rejected write of 2750 (key "H12"); the slot is unchanged
✓ assert bay_contents("H12").oxidizer_t == 20
✓ assert clear_bay("H12").ok == true
✓ assert stow_flammable("H12", 5).ok == true
✓ assert bay_contents("H12").class == "holds_flammable"
✓ assert stow_oxidizer("H12", 1).ok == false
14 tests: 14 passed, 0 failed$ soma verify hazmat_bay_segregation.cell
Verifying hazmat_bay_segregation.cell...
State machine 'bay_class': 3 states, initial 'empty'
States: [empty, holds_flammable, holds_oxidizer]
✓ 3 states, 4 transitions
✓ all states reachable from 'empty'
✓ reactive machine: no terminal state, and every reachable state can return to 'empty'
✓ no deadlocks
✓ liveness: every reachable state can return to 'empty' (reactive machine)
✓ think-isolated: CTL safety properties hold regardless of LLM output (3 handlers, 5 literal transitions, 0 dynamic)
✓ termination: all 4 handlers structurally terminate
✓ refinement: handler `stow_oxidizer` ⟶ {holds_oxidizer [if get_status(bay) != "holds_oxidizer"], empty [if w.error != () ∧ if cur == 0]}
✓ refinement: handler `stow_flammable` ⟶ {holds_flammable [if get_status(bay) != "holds_flammable"], empty [if w.error != () ∧ if cur == 0]}
✓ refinement: handler `clear_bay` ⟶ {empty}
· transition sources: `stow_oxidizer`, `stow_flammable`, `clear_bay` 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)
HazmatBay/invariants:
✓ invariant oxidizer_t >= 0 && oxidizer_t <= 25 — writer 'clear_bay' proven (writes 0)
✓ invariant oxidizer_t >= 0 — writer 'stow_oxidizer' proven by induction (writes cur + tonnes)
⚠ invariant oxidizer_t >= 0 && oxidizer_t <= 25 — runtime-checked (computed values): stow_oxidizer → oxidizer_t [oxidizer_t <= 25] because `cur + tonnes` is only known to lie in [1, ∞) — narrow it: `require (cur + tonnes) <= 25 else …`
✓ invariant flammable_t >= 0 && flammable_t <= 25 — writer 'clear_bay' proven (writes 0)
✓ invariant flammable_t >= 0 — writer 'stow_flammable' proven by induction (writes cur + tonnes)
⚠ invariant flammable_t >= 0 && flammable_t <= 25 — runtime-checked (computed values): stow_flammable → flammable_t [flammable_t <= 25] because `cur + tonnes` is only known to lie in [1, ∞) — narrow it: `require (cur + tonnes) <= 25 else …`
14 passed, 2 warnings, 0 failures
Temporal properties for 'bay_class':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK