Cabin pressurization — Helios 522: the manual-mode takeoff, unrepresentable
On 14 August 2005, Helios Airways Flight 522 took off from Larnaca with the pressurization mode selector still in MANUAL — left there after an overnight maintenance leak check. The cabin never pressurized. As the 737 climbed, the cabin altitude climbed with it; the warning horn was mistaken for the takeoff-config horn; the crew lost consciousness from hypoxia, and 121 people died when the aircraft ran out of fuel. The failure was a MODE that survived a phase change: maintenance state leaked into flight state. Here those phases are one machine. The only path to airborne goes THROUGH auto_verified — the checklist step where the selector is confirmed in AUTO. From manual_maint there is no edge to airborne at all. And the cabin-altitude ledger refuses to record a hypoxic cabin: the controller's setpoint cannot leave the safe band.
A Soma program from the aerospace part of the verified corpus: 106 lines, cells CabinPressure, PressurizationTests. It passes soma check with soma 3.2.4.
How it is built
The cell CabinPressure declares 6 signals in its face: enter_maintenance(id: String) -> Map, restore_auto(id: String) -> Map, confirm_auto(id: String) -> Map, takeoff(id: String) -> Map, set_cabin_alt(id: String, ft: Int) -> Map and press_phase(id: String) -> String; keeps 1 memory slot: cabin_alt_ft (Map<String, Int>, persistent and consistent) under the invariant cabin_alt_ft >= 0 && cabin_alt_ft <= 8000. Its state machine pressurization starts in preflight and allows 5 transitions: preflight -> manual_maint, manual_maint -> preflight, preflight -> auto_verified, auto_verified -> airborne and airborne -> preflight. Handlers: guarded_step (line 50), enter_maintenance (line 56), restore_auto (line 57), confirm_auto (line 58), takeoff (line 60), set_cabin_alt (line 66) and press_phase (line 74).
The file also holds 1 test cell (PressurizationTests) with 14 assertions, run by soma test.
The source
cabin_pressurization.cell
// ============================================================================
// CABIN PRESSURIZATION — Helios 522: the manual-mode takeoff, unrepresentable
// ============================================================================
//
// On 14 August 2005, Helios Airways Flight 522 took off from Larnaca with
// the pressurization mode selector still in MANUAL — left there after an
// overnight maintenance leak check. The cabin never pressurized. As the
// 737 climbed, the cabin altitude climbed with it; the warning horn was
// mistaken for the takeoff-config horn; the crew lost consciousness from
// hypoxia, and 121 people died when the aircraft ran out of fuel.
//
// The failure was a MODE that survived a phase change: maintenance state
// leaked into flight state. Here those phases are one machine. The only
// path to airborne goes THROUGH auto_verified — the checklist step where
// the selector is confirmed in AUTO. From manual_maint there is no edge
// to airborne at all. And the cabin-altitude ledger refuses to record a
// hypoxic cabin: the controller's setpoint cannot leave the safe band.
// ============================================================================
cell CabinPressure {
face {
signal enter_maintenance(id: String) -> Map
signal restore_auto(id: String) -> Map
signal confirm_auto(id: String) -> Map
signal takeoff(id: String) -> Map
signal set_cabin_alt(id: String, ft: Int) -> Map
signal press_phase(id: String) -> String
}
memory {
// cabin altitude setpoint, feet — 8000 is the certification
// ceiling for a pressurized cabin; the hypoxic cabin Helios flew
// with is not a value this slot can hold
cabin_alt_ft: Map<String, Int> [persistent, consistent]
invariant cabin_alt_ft >= 0 && cabin_alt_ft <= 8000
}
// ABSENT: manual_maint -> airborne (the Helios takeoff)
// ABSENT: preflight -> airborne (no takeoff without the check)
state pressurization {
initial: preflight
preflight -> manual_maint // ground leak check, selector MANUAL
manual_maint -> preflight // selector restored — back to start,
// the verification must be REDONE
preflight -> auto_verified // checklist: selector confirmed AUTO
auto_verified -> airborne
airborne -> preflight // landed: next flight re-verifies
}
on guarded_step(id: String, target: String) {
let r = try { transition(id, target) }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "phase", get_status(id))
}
on enter_maintenance(id: String) { return guarded_step(id, "manual_maint") }
on restore_auto(id: String) { return guarded_step(id, "preflight") }
on confirm_auto(id: String) { return guarded_step(id, "auto_verified") }
on takeoff(id: String) {
let r = guarded_step(id, "airborne")
if r.ok { cabin_alt_ft.set(id, 8000) } // cruise cabin altitude
return r
}
on set_cabin_alt(id: String, ft: Int) {
let w = try { cabin_alt_ft.set(id, ft) }
if w.error != () {
return map("ok", false, "reason", "cabin altitude {ft} ft is outside the safe band")
}
return map("ok", true, "cabin_alt", ft)
}
on press_phase(id: String) { return get_status(id) }
}
cell test PressurizationTests {
rules {
// THE Helios takeoff: airborne straight out of maintenance mode.
// The edge does not exist.
assert enter_maintenance("ac1").ok == true
assert_fails transition("ac1", "airborne")
assert takeoff("ac1").ok == false
// restoring the selector is NOT enough — verification must rerun
assert restore_auto("ac1").ok == true
assert_fails transition("ac1", "airborne")
// only the verified path reaches the air
assert confirm_auto("ac1").ok == true
assert takeoff("ac1").ok == true
assert press_phase("ac1") == "airborne"
// the hypoxic cabin cannot be recorded as a setpoint:
// Helios 522's cabin passed 30000 ft — this slot never will
assert_fails cabin_alt_ft.set("ac1", 30000)
assert_fails cabin_alt_ft.set("ac1", -100)
assert set_cabin_alt("ac1", 14000).ok == false
assert set_cabin_alt("ac1", 7500).ok == true
// after landing, the next departure re-verifies from scratch
assert restore_auto("ac1").ok == true
assert takeoff("ac1").ok == false
}
}What the toolchain says
$ soma check cabin_pressurization.cell
✓ All checks passed.$ soma test cabin_pressurization.cell
test PressurizationTests ...
✓ assert enter_maintenance("ac1").ok == true
✓ assert_fails transition("ac1", "airborne") — raised invalid transition: manual_maint → airborne. Current state: 'manual_maint'. Valid targets: [preflight]
✓ assert takeoff("ac1").ok == false
✓ assert restore_auto("ac1").ok == true
✓ assert_fails transition("ac1", "airborne") — raised invalid transition: preflight → airborne. Current state: 'preflight'. Valid targets: [manual_maint, auto_verified]
✓ assert confirm_auto("ac1").ok == true
✓ assert takeoff("ac1").ok == true
✓ assert press_phase("ac1") == "airborne"
✓ assert_fails cabin_alt_ft.set("ac1", 30000) — raised memory invariant violated on 'cabin_alt_ft': cabin_alt_ft >= 0 && cabin_alt_ft <= 8000 — rejected write of 30000 (key "ac1"); the slot is unchanged
✓ assert_fails cabin_alt_ft.set("ac1", -100) — raised memory invariant violated on 'cabin_alt_ft': cabin_alt_ft >= 0 && cabin_alt_ft <= 8000 — rejected write of -100 (key "ac1"); the slot is unchanged
✓ assert set_cabin_alt("ac1", 14000).ok == false
✓ assert set_cabin_alt("ac1", 7500).ok == true
✓ assert restore_auto("ac1").ok == true
✓ assert takeoff("ac1").ok == false
14 tests: 14 passed, 0 failed$ soma verify cabin_pressurization.cell
Verifying cabin_pressurization.cell...
State machine 'pressurization': 4 states, initial 'preflight'
States: [airborne, auto_verified, manual_maint, preflight]
✓ 4 states, 5 transitions
✓ all states reachable from 'preflight'
✓ reactive machine: no terminal state, and every reachable state can return to 'preflight'
✓ no deadlocks
✓ liveness: every reachable state can return to 'preflight' (reactive machine)
⚠ dynamic transition targets: handler `guarded_step` 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 7 handlers structurally terminate
⚠ refinement (partial — dynamic targets fall back to runtime check): handler `guarded_step` 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 `guarded_step` ⟶ <dynamic>
CabinPressure/invariants:
✓ invariant cabin_alt_ft >= 0 && cabin_alt_ft <= 8000 — writer 'takeoff' proven (writes 8000)
⚠ invariant cabin_alt_ft >= 0 && cabin_alt_ft <= 8000 — runtime-checked (computed values): set_cabin_alt → cabin_alt_ft because `ft` is a parameter (narrow it: `require ft >= 0 && ft <= 8000 else …`)
8 passed, 3 warnings, 0 failures
Temporal properties for 'pressurization':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK