Tcas resolution advisory — Überlingen: the opposite maneuver is no state
On 1 July 2002, over Überlingen, a Tu-154 and a DHL 757 were converging at the same level. TCAS told the Tupolev crew CLIMB and the 757 crew DESCEND — a coordinated, deconflicted pair. Then the ground controller, working two stations alone, told the Tupolev to descend. The crew followed the human voice instead of the RA. Both aircraft descended into each other. 71 people died, most of them children. The rule written after that night: once a resolution advisory is active, the RA has authority — an ATC instruction in the opposite sense must not be flown. Here that rule is the machine's shape: from ra_climb the ONLY pilot response state is complying_climb. Flying a descend against a climb RA is not a violation to detect — it is a transition that does not exist. Altitude commands are bounded by the envelope ledger.
A Soma program from the aerospace part of the verified corpus: 121 lines, cells TcasAdvisory, RaTests. It passes soma check with soma 3.2.4.
How it is built
The cell TcasAdvisory declares 6 signals in its face: traffic(id: String) -> Map, traffic_resolved(id: String) -> Map, issue_ra(id: String, sense: String) -> Map, fly_response(id: String, sense: String) -> Map, conflict_clear(id: String) -> Map and ra_state(id: String) -> String; keeps 1 memory slot: vs_fpm (Map<String, Int>, persistent and consistent) under the invariant vs_fpm >= 0 && vs_fpm <= 4400. Its state machine tcas starts in monitoring and allows 9 transitions: monitoring -> advisory, advisory -> monitoring, advisory -> ra_climb, advisory -> ra_descend, ra_climb -> complying_climb, ra_descend -> complying_descend, complying_climb -> clear_of_conflict, complying_descend -> clear_of_conflict and clear_of_conflict -> monitoring. Handlers: guarded_ra (line 52), traffic (line 58), traffic_resolved (line 59), issue_ra (line 61), fly_response (line 70), conflict_clear (line 76) and ra_state (line 82).
The file also holds 1 test cell (RaTests) with 19 assertions, run by soma test.
The source
tcas_ra.cell
// ============================================================================
// TCAS RESOLUTION ADVISORY — Überlingen: the opposite maneuver is no state
// ============================================================================
//
// On 1 July 2002, over Überlingen, a Tu-154 and a DHL 757 were converging
// at the same level. TCAS told the Tupolev crew CLIMB and the 757 crew
// DESCEND — a coordinated, deconflicted pair. Then the ground controller,
// working two stations alone, told the Tupolev to descend. The crew
// followed the human voice instead of the RA. Both aircraft descended
// into each other. 71 people died, most of them children.
//
// The rule written after that night: once a resolution advisory is
// active, the RA has authority — an ATC instruction in the opposite
// sense must not be flown. Here that rule is the machine's shape: from
// ra_climb the ONLY pilot response state is complying_climb. Flying a
// descend against a climb RA is not a violation to detect — it is a
// transition that does not exist. Altitude commands are bounded by the
// envelope ledger.
// ============================================================================
cell TcasAdvisory {
face {
signal traffic(id: String) -> Map
signal traffic_resolved(id: String) -> Map
signal issue_ra(id: String, sense: String) -> Map
signal fly_response(id: String, sense: String) -> Map
signal conflict_clear(id: String) -> Map
signal ra_state(id: String) -> String
}
memory {
// commanded vertical speed, ft/min (magnitude) — RA strength caps
vs_fpm: Map<String, Int> [persistent, consistent]
invariant vs_fpm >= 0 && vs_fpm <= 4400
}
// ABSENT: ra_climb -> complying_descend (the Überlingen maneuver)
// ABSENT: ra_descend -> complying_climb
state tcas {
initial: monitoring
monitoring -> advisory // TA: traffic, traffic
advisory -> monitoring // threat fades
advisory -> ra_climb
advisory -> ra_descend
ra_climb -> complying_climb // the ONLY response to a climb RA
ra_descend -> complying_descend
complying_climb -> clear_of_conflict
complying_descend -> clear_of_conflict
clear_of_conflict -> monitoring // resume normal flight
}
on guarded_ra(id: String, target: String) {
let r = try { transition(id, target) }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", get_status(id))
}
on traffic(id: String) { return guarded_ra(id, "advisory") }
on traffic_resolved(id: String) { return guarded_ra(id, "monitoring") }
on issue_ra(id: String, sense: String) {
if sense != "climb" && sense != "descend" {
return map("ok", false, "reason", "RA sense must be climb or descend")
}
return guarded_ra(id, "ra_{sense}")
}
// The pilot flies a sense. Against the RA, the state does not exist —
// the controller's voice has no transition to offer it.
on fly_response(id: String, sense: String) {
let r = guarded_ra(id, "complying_{sense}")
if r.ok { vs_fpm.set(id, 1500) } // initial RA strength
return r
}
on conflict_clear(id: String) {
let r = guarded_ra(id, "clear_of_conflict")
if r.ok { vs_fpm.set(id, 0) }
return r
}
on ra_state(id: String) { return get_status(id) }
}
cell test RaTests {
rules {
// traffic advisory, then a CLIMB RA
assert traffic("tu154").ok == true
assert issue_ra("tu154", "climb").ok == true
assert ra_state("tu154") == "ra_climb"
// THE Überlingen maneuver: ATC says descend, against the RA.
// There is no such transition; the only response is the RA's.
assert_fails transition("tu154", "complying_descend")
assert fly_response("tu154", "descend").ok == false
assert ra_state("tu154") == "ra_climb"
// complying with the RA proceeds to clear of conflict, then
// back to normal monitoring — and never a shortcut before that
assert fly_response("tu154", "climb").ok == true
assert_fails transition("tu154", "monitoring") // still maneuvering
assert conflict_clear("tu154").ok == true
assert ra_state("tu154") == "clear_of_conflict"
assert traffic_resolved("tu154").ok == true
assert ra_state("tu154") == "monitoring"
// symmetric: a descend RA admits no climb response
assert traffic("dhl611").ok == true
assert issue_ra("dhl611", "descend").ok == true
assert_fails transition("dhl611", "complying_climb")
assert fly_response("dhl611", "descend").ok == true
// no RA out of thin air: monitoring cannot jump to compliance
assert_fails transition("af3", "complying_climb")
// commanded vertical speed never exceeds RA envelope strength
assert_fails vs_fpm.set("tu154", 9000)
assert_fails vs_fpm.set("tu154", -100)
}
}What the toolchain says
$ soma check tcas_ra.cell
✓ All checks passed.$ soma test tcas_ra.cell
test RaTests ...
✓ assert traffic("tu154").ok == true
✓ assert issue_ra("tu154", "climb").ok == true
✓ assert ra_state("tu154") == "ra_climb"
✓ assert_fails transition("tu154", "complying_descend") — raised invalid transition: ra_climb → complying_descend. Current state: 'ra_climb'. Valid targets: [complying_climb]
✓ assert fly_response("tu154", "descend").ok == false
✓ assert ra_state("tu154") == "ra_climb"
✓ assert fly_response("tu154", "climb").ok == true
✓ assert_fails transition("tu154", "monitoring") — raised invalid transition: complying_climb → monitoring. Current state: 'complying_climb'. Valid targets: [clear_of_conflict]
✓ assert conflict_clear("tu154").ok == true
✓ assert ra_state("tu154") == "clear_of_conflict"
✓ assert traffic_resolved("tu154").ok == true
✓ assert ra_state("tu154") == "monitoring"
✓ assert traffic("dhl611").ok == true
✓ assert issue_ra("dhl611", "descend").ok == true
✓ assert_fails transition("dhl611", "complying_climb") — raised invalid transition: ra_descend → complying_climb. Current state: 'ra_descend'. Valid targets: [complying_descend]
✓ assert fly_response("dhl611", "descend").ok == true
✓ assert_fails transition("af3", "complying_climb") — raised invalid transition: monitoring → complying_climb. Current state: 'monitoring'. Valid targets: [advisory]
✓ assert_fails vs_fpm.set("tu154", 9000) — raised memory invariant violated on 'vs_fpm': vs_fpm >= 0 && vs_fpm <= 4400 — rejected write of 9000 (key "tu154"); the slot is unchanged
✓ assert_fails vs_fpm.set("tu154", -100) — raised memory invariant violated on 'vs_fpm': vs_fpm >= 0 && vs_fpm <= 4400 — rejected write of -100 (key "tu154"); the slot is unchanged
19 tests: 19 passed, 0 failed$ soma verify tcas_ra.cell
Verifying tcas_ra.cell...
State machine 'tcas': 7 states, initial 'monitoring'
States: [advisory, clear_of_conflict, complying_climb, complying_descend, monitoring, ra_climb, ra_descend]
✓ 7 states, 9 transitions
✓ all states reachable from 'monitoring'
✓ reactive machine: no terminal state, and every reachable state can return to 'monitoring'
✓ no deadlocks
✓ liveness: every reachable state can return to 'monitoring' (reactive machine)
⚠ dynamic transition targets: handler `guarded_ra` 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_ra` 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_ra` ⟶ <dynamic>
TcasAdvisory/invariants:
✓ invariant vs_fpm >= 0 && vs_fpm <= 4400 — writer 'conflict_clear' proven (writes 0)
✓ invariant vs_fpm >= 0 && vs_fpm <= 4400 — writer 'fly_response' proven (writes 1500)
9 passed, 2 warnings, 0 failures
Temporal properties for 'tcas':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK