Fuel crossfeed — Air Transat 236: never feed a leak from the good tank
On 24 August 2001, Air Transat Flight 236 (an A330 over the Atlantic) developed a fuel leak in the right engine's feed line. Following the imbalance checklist, the crew opened the crossfeed valve — and pumped the LEFT tank's fuel into the leak too. Both engines flamed out; the aircraft glided 75 miles, dead-stick, to a runway in the Azores. 306 people survived on the captain's gliding skill, not on the system. The procedure trap: crossfeed is the correct response to an imbalance and the catastrophic response to a leak — and the two look identical on the gauges unless you check totals. Here that check is structural: opening crossfeed REQUIRES a fuel-total reconciliation, a detected leak drives the machine to leak_isolated — a TERMINAL state with no edge to crossfeed_open, ever — and the tank ledger refuses negative or overfull quantities outright.
A Soma program from the aerospace part of the verified corpus: 118 lines, cells FuelCrossfeed, CrossfeedTests. It passes soma check with soma 3.2.4.
How it is built
The cell FuelCrossfeed declares 6 signals in its face: load_tanks(id: String, left_kg: Int, right_kg: Int) -> Map, burn(id: String, side: String, kg: Int) -> Map, open_crossfeed(id: String, expected_total_kg: Int) -> Map, shut_crossfeed(id: String) -> Map, feed_mode(id: String) -> String and tank_total(id: String) -> Int; keeps 1 memory slot: tank_kg (Map<String, Int>, persistent and consistent) under the invariant tank_kg >= 0 && tank_kg <= 7000. Its state machine feed starts in isolated_feed and allows 4 transitions: isolated_feed -> crossfeed_open, crossfeed_open -> isolated_feed, isolated_feed -> leak_isolated and crossfeed_open -> leak_isolated. leak_isolated is terminal. Handlers: load_tanks (line 47), burn (line 55), tank_total (line 62), open_crossfeed (line 69), shut_crossfeed (line 81) and feed_mode (line 87).
The file also holds 1 test cell (CrossfeedTests) with 17 assertions, run by soma test.
The source
fuel_crossfeed.cell
// ============================================================================
// FUEL CROSSFEED — Air Transat 236: never feed a leak from the good tank
// ============================================================================
//
// On 24 August 2001, Air Transat Flight 236 (an A330 over the Atlantic)
// developed a fuel leak in the right engine's feed line. Following the
// imbalance checklist, the crew opened the crossfeed valve — and pumped
// the LEFT tank's fuel into the leak too. Both engines flamed out; the
// aircraft glided 75 miles, dead-stick, to a runway in the Azores. 306
// people survived on the captain's gliding skill, not on the system.
//
// The procedure trap: crossfeed is the correct response to an imbalance
// and the catastrophic response to a leak — and the two look identical
// on the gauges unless you check totals. Here that check is structural:
// opening crossfeed REQUIRES a fuel-total reconciliation, a detected
// leak drives the machine to leak_isolated — a TERMINAL state with no
// edge to crossfeed_open, ever — and the tank ledger refuses negative
// or overfull quantities outright.
// ============================================================================
cell FuelCrossfeed {
face {
signal load_tanks(id: String, left_kg: Int, right_kg: Int) -> Map
signal burn(id: String, side: String, kg: Int) -> Map
signal open_crossfeed(id: String, expected_total_kg: Int) -> Map
signal shut_crossfeed(id: String) -> Map
signal feed_mode(id: String) -> String
signal tank_total(id: String) -> Int
}
memory {
// per-tank quantity, kg — capacity 7000; a negative tank or an
// overfill is rejected at the write
tank_kg: Map<String, Int> [persistent, consistent]
invariant tank_kg >= 0 && tank_kg <= 7000
}
// ABSENT: leak_isolated -> crossfeed_open (the Air Transat valve)
state feed {
initial: isolated_feed // each tank feeds its own engine
isolated_feed -> crossfeed_open
crossfeed_open -> isolated_feed
isolated_feed -> leak_isolated // terminal
crossfeed_open -> leak_isolated // terminal
}
on load_tanks(id: String, left_kg_v: Int, right_kg_v: Int) {
let l = try { tank_kg.set("{id}:L", left_kg_v) }
if l.error != () { return map("ok", false, "reason", "left tank load refused") }
let r = try { tank_kg.set("{id}:R", right_kg_v) }
if r.error != () { return map("ok", false, "reason", "right tank load refused") }
return map("ok", true, "total", tank_total(id))
}
on burn(id: String, side: String, kg: Int) {
let cur = tank_kg.get("{id}:{side}") ?? 0
let w = try { tank_kg.set("{id}:{side}", cur - kg) }
if w.error != () { return map("ok", false, "reason", "tank cannot go negative") }
return map("ok", true, "remaining", cur - kg)
}
on tank_total(id: String) {
return (tank_kg.get("{id}:L") ?? 0) + (tank_kg.get("{id}:R") ?? 0)
}
// Crossfeed opens ONLY after the totals reconcile. Fuel that left the
// tanks faster than the engines burned it is a LEAK — and the machine
// goes to leak_isolated instead, permanently.
on open_crossfeed(id: String, expected_total_kg: Int) {
let actual = tank_total(id)
if expected_total_kg - actual > 200 {
transition(id, "leak_isolated")
let missing = expected_total_kg - actual
return map("ok", false, "reason", "LEAK: {missing} kg unaccounted — crossfeed isolated for the flight")
}
let r = try { transition(id, "crossfeed_open") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "mode", get_status(id))
}
on shut_crossfeed(id: String) {
let r = try { transition(id, "isolated_feed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "mode", get_status(id))
}
on feed_mode(id: String) { return get_status(id) }
}
cell test CrossfeedTests {
rules {
// a plain imbalance: totals reconcile, crossfeed is the right call
assert load_tanks("f1", 5000, 5000).ok == true
assert burn("f1", "R", 1500).ok == true // right engine ran the APU leg
assert burn("f1", "L", 500).ok == true
assert open_crossfeed("f1", 8000).ok == true // 10000 loaded - 2000 burned
assert feed_mode("f1") == "crossfeed_open"
assert shut_crossfeed("f1").ok == true
// THE Air Transat case: 1800 kg simply GONE overboard
assert load_tanks("f2", 5000, 5000).ok == true
assert burn("f2", "R", 2300).ok == true // 500 burned + 1800 leaked
assert burn("f2", "L", 500).ok == true
assert open_crossfeed("f2", 9000).ok == false // books say 9000, tanks hold 7200
assert feed_mode("f2") == "leak_isolated"
// leak_isolated is TERMINAL: the valve can never open this flight
assert_fails transition("f2", "crossfeed_open")
assert_fails transition("f2", "isolated_feed")
assert open_crossfeed("f2", 7200).ok == false
// the ledger itself: no negative tanks, no overfills
assert_fails tank_kg.set("f1:L", -10)
assert_fails tank_kg.set("f1:L", 9000)
assert burn("f1", "L", 99999).ok == false
}
}What the toolchain says
$ soma check fuel_crossfeed.cell
warning: parameter 2 of `load_tanks`: the face calls it `left_kg`, the handler `left_kg_v` — an HTTP JSON body is matched by NAME (the handler's), and `soma describe` publishes the face's: use one name
--> fuel_crossfeed.cell:23:9
|
23 | signal load_tanks(id: String, left_kg: Int, right_kg: Int) -…
| ^
warning: parameter 3 of `load_tanks`: the face calls it `right_kg`, the handler `right_kg_v` — an HTTP JSON body is matched by NAME (the handler's), and `soma describe` publishes the face's: use one name
--> fuel_crossfeed.cell:23:9
|
23 | signal load_tanks(id: String, left_kg: Int, right_kg: Int) -…
| ^
✓ 2 warning(s), no errors.$ soma test fuel_crossfeed.cell
test CrossfeedTests ...
✓ assert load_tanks("f1", 5000, 5000).ok == true
✓ assert burn("f1", "R", 1500).ok == true
✓ assert burn("f1", "L", 500).ok == true
✓ assert open_crossfeed("f1", 8000).ok == true
✓ assert feed_mode("f1") == "crossfeed_open"
✓ assert shut_crossfeed("f1").ok == true
✓ assert load_tanks("f2", 5000, 5000).ok == true
✓ assert burn("f2", "R", 2300).ok == true
✓ assert burn("f2", "L", 500).ok == true
✓ assert open_crossfeed("f2", 9000).ok == false
✓ assert feed_mode("f2") == "leak_isolated"
✓ assert_fails transition("f2", "crossfeed_open") — raised invalid transition: leak_isolated → crossfeed_open. Current state: 'leak_isolated'. Valid targets: []
✓ assert_fails transition("f2", "isolated_feed") — raised invalid transition: leak_isolated → isolated_feed. Current state: 'leak_isolated'. Valid targets: []
✓ assert open_crossfeed("f2", 7200).ok == false
✓ assert_fails tank_kg.set("f1:L", -10) — raised memory invariant violated on 'tank_kg': tank_kg >= 0 && tank_kg <= 7000 — rejected write of -10 (key "f1:L"); the slot is unchanged
✓ assert_fails tank_kg.set("f1:L", 9000) — raised memory invariant violated on 'tank_kg': tank_kg >= 0 && tank_kg <= 7000 — rejected write of 9000 (key "f1:L"); the slot is unchanged
✓ assert burn("f1", "L", 99999).ok == false
17 tests: 17 passed, 0 failed$ soma verify fuel_crossfeed.cell
Verifying fuel_crossfeed.cell...
State machine 'feed': 3 states, initial 'isolated_feed'
States: [crossfeed_open, isolated_feed, leak_isolated]
✓ 3 states, 4 transitions
✓ all states reachable from 'isolated_feed'
✓ terminal states: [leak_isolated]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ think-isolated: CTL safety properties hold regardless of LLM output (2 handlers, 3 literal transitions, 0 dynamic)
✓ termination: all 6 handlers structurally terminate
✓ refinement: handler `open_crossfeed` ⟶ {leak_isolated [if expected_total_kg - actual > 200], crossfeed_open}
✓ refinement: handler `shut_crossfeed` ⟶ {isolated_feed}
· transition sources: `open_crossfeed`, `shut_crossfeed` 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)
FuelCrossfeed/invariants:
⚠ invariant tank_kg >= 0 && tank_kg <= 7000 — runtime-checked (computed values): burn → tank_kg because `kg` is a parameter (narrow it: `require (cur - kg) >= 0 && (cur - kg) <= 7000 else …`), load_tanks → tank_kg because `left_kg_v` is a parameter (narrow it: `require left_kg_v >= 0 && left_kg_v <= 7000 else …`), load_tanks → tank_kg because `right_kg_v` is a parameter (narrow it: `require right_kg_v >= 0 && right_kg_v <= 7000 else …`)
9 passed, 1 warnings, 0 failures
Temporal properties for 'feed':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK