Autopilot authority — Aeroflot 593: the half-disconnected autopilot
On 23 March 1994, Aeroflot Flight 593 (an A310) crashed into a Siberian hillside, killing 75. The captain's children were at the controls; the boy's yoke force partially overrode the autopilot — it silently dropped the AILERON channel while the rest stayed engaged. No light, no horn. The crew flew for crucial seconds believing the autopilot had the aircraft. Nobody had authority, and nobody knew. The defect was authority as PER-AXIS FLAGS that could disagree. Here authority is ONE state. Yoke force past the breakout threshold drives the machine to override_latched — fully, loudly, all axes — and the only exit is an explicit crew acknowledgment. There is no edge from override_latched back to engaged: a fought autopilot can never silently take the aircraft back. A memory invariant caps servo torque so the actuator can never be commanded past its breakout rating.
A Soma program from the aerospace part of the verified corpus: 108 lines, cells AutopilotAuthority, AuthorityTests. It passes soma check with soma 3.2.4.
How it is built
The cell AutopilotAuthority declares 5 signals in its face: engage(id: String) -> Map, disengage(id: String) -> Map, yoke_input(id: String, force_n: Int) -> Map, acknowledge_override(id: String) -> Map and authority(id: String) -> String; keeps 1 memory slot: servo_nm (Map<String, Int>, persistent and consistent) under the invariant servo_nm >= 0 && servo_nm <= 30. Its state machine autopilot starts in disengaged and allows 4 transitions: disengaged -> engaged, engaged -> disengaged, engaged -> override_latched and override_latched -> disengaged. Handlers: engage (line 46), disengage (line 53), yoke_input (line 60), acknowledge_override (line 71) and authority (line 77).
The file also holds 1 test cell (AuthorityTests) with 12 assertions, run by soma test.
The source
autopilot_authority.cell
// ============================================================================
// AUTOPILOT AUTHORITY — Aeroflot 593: the half-disconnected autopilot
// ============================================================================
//
// On 23 March 1994, Aeroflot Flight 593 (an A310) crashed into a Siberian
// hillside, killing 75. The captain's children were at the controls; the
// boy's yoke force partially overrode the autopilot — it silently dropped
// the AILERON channel while the rest stayed engaged. No light, no horn.
// The crew flew for crucial seconds believing the autopilot had the
// aircraft. Nobody had authority, and nobody knew.
//
// The defect was authority as PER-AXIS FLAGS that could disagree. Here
// authority is ONE state. Yoke force past the breakout threshold drives
// the machine to override_latched — fully, loudly, all axes — and the
// only exit is an explicit crew acknowledgment. There is no edge from
// override_latched back to engaged: a fought autopilot can never silently
// take the aircraft back. A memory invariant caps servo torque so the
// actuator can never be commanded past its breakout rating.
// ============================================================================
cell AutopilotAuthority {
face {
signal engage(id: String) -> Map
signal disengage(id: String) -> Map
signal yoke_input(id: String, force_n: Int) -> Map
signal acknowledge_override(id: String) -> Map
signal authority(id: String) -> String
}
memory {
// commanded servo torque, N·m — breakout clutch rated at 30
servo_nm: Map<String, Int> [persistent, consistent]
invariant servo_nm >= 0 && servo_nm <= 30
}
// Authority is one state, never per-axis flags.
// ABSENT: override_latched -> engaged (no silent re-engagement)
state autopilot {
initial: disengaged
disengaged -> engaged
engaged -> disengaged // crew button: clean handover
engaged -> override_latched // yoke force: AP surrenders ALL axes
override_latched -> disengaged // ONLY via explicit acknowledgment
}
on engage(id: String) {
let r = try { transition(id, "engaged") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
servo_nm.set(id, 12)
return map("ok", true, "authority", get_status(id))
}
on disengage(id: String) {
let r = try { transition(id, "disengaged") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
servo_nm.set(id, 0)
return map("ok", true, "authority", get_status(id))
}
on yoke_input(id: String, force_n: Int) {
// below breakout force the AP holds; past it, authority transfers
// COMPLETELY — the Aeroflot 593 half-state has no representation
if get_status(id) == "engaged" && force_n > 15 {
transition(id, "override_latched")
servo_nm.set(id, 0)
return map("ok", true, "authority", "override_latched", "warn", "AP OFF — pilot has ALL axes")
}
return map("ok", true, "authority", get_status(id))
}
on acknowledge_override(id: String) {
let r = try { transition(id, "disengaged") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "authority", get_status(id))
}
on authority(id: String) { return get_status(id) }
}
cell test AuthorityTests {
rules {
// engage from cold
assert engage("ap1").ok == true
assert authority("ap1") == "engaged"
// light yoke pressure: the AP keeps the aircraft
assert yoke_input("ap1", 5).authority == "engaged"
// force past breakout: AP surrenders EVERYTHING, latched
assert yoke_input("ap1", 40).authority == "override_latched"
// THE Aeroflot 593 hazard: a silent re-engage. The edge is absent.
assert_fails transition("ap1", "engaged")
assert engage("ap1").ok == false
// only an explicit acknowledgment clears the latch...
assert acknowledge_override("ap1").ok == true
assert authority("ap1") == "disengaged"
// ...and then a deliberate engage is legal again
assert engage("ap1").ok == true
assert disengage("ap1").ok == true
// the servo can never be commanded past its breakout clutch
assert_fails servo_nm.set("ap1", 90)
assert_fails servo_nm.set("ap1", -1)
}
}What the toolchain says
$ soma check autopilot_authority.cell
✓ All checks passed.$ soma test autopilot_authority.cell
test AuthorityTests ...
✓ assert engage("ap1").ok == true
✓ assert authority("ap1") == "engaged"
✓ assert yoke_input("ap1", 5).authority == "engaged"
✓ assert yoke_input("ap1", 40).authority == "override_latched"
✓ assert_fails transition("ap1", "engaged") — raised invalid transition: override_latched → engaged. Current state: 'override_latched'. Valid targets: [disengaged]
✓ assert engage("ap1").ok == false
✓ assert acknowledge_override("ap1").ok == true
✓ assert authority("ap1") == "disengaged"
✓ assert engage("ap1").ok == true
✓ assert disengage("ap1").ok == true
✓ assert_fails servo_nm.set("ap1", 90) — raised memory invariant violated on 'servo_nm': servo_nm >= 0 && servo_nm <= 30 — rejected write of 90 (key "ap1"); the slot is unchanged
✓ assert_fails servo_nm.set("ap1", -1) — raised memory invariant violated on 'servo_nm': servo_nm >= 0 && servo_nm <= 30 — rejected write of -1 (key "ap1"); the slot is unchanged
12 tests: 12 passed, 0 failed$ soma verify autopilot_authority.cell
Verifying autopilot_authority.cell...
State machine 'autopilot': 3 states, initial 'disengaged'
States: [disengaged, engaged, override_latched]
✓ 3 states, 4 transitions
✓ all states reachable from 'disengaged'
✓ reactive machine: no terminal state, and every reachable state can return to 'disengaged'
✓ no deadlocks
✓ liveness: every reachable state can return to 'disengaged' (reactive machine)
✓ think-isolated: CTL safety properties hold regardless of LLM output (4 handlers, 4 literal transitions, 0 dynamic)
✓ termination: all 5 handlers structurally terminate
✓ refinement: handler `engage` ⟶ {engaged}
✓ refinement: handler `disengage` ⟶ {disengaged}
✓ refinement: handler `yoke_input` ⟶ {engaged → override_latched [if get_status(id) == "engaged" && force_n > 15]}
✓ refinement: handler `acknowledge_override` ⟶ {disengaged}
· transition sources: `engage`, `disengage`, `acknowledge_override` 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)
AutopilotAuthority/invariants:
✓ invariant servo_nm >= 0 && servo_nm <= 30 — writer 'disengage' proven (writes 0)
✓ invariant servo_nm >= 0 && servo_nm <= 30 — writer 'engage' proven (writes 12)
✓ invariant servo_nm >= 0 && servo_nm <= 30 — writer 'yoke_input' proven (writes 0)
14 passed, 0 warnings, 0 failures
Temporal properties for 'autopilot':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK