soma.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.

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.

Generated from soma describe on this file.

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

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

$ 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

Builtins used

get_status, map, transition

Source file: /corpus/aerospace/tcas_ra.cell · examples/corpus/aerospace/tcas_ra.cell