Parachute sequence — Genesis: the drogue that never fired
On 8 September 2004 the Genesis sample-return capsule hit the Utah desert at 311 km/h. The G-switch that should have sensed deceleration and started the parachute sequence had been installed BACKWARDS — a drawing error that sailed through review. No drogue, no main, just a crater and three years of solar-wind samples shattered. (Its sister design on Stardust, wired correctly, landed perfectly.) The deeper defect: the deploy sequence lived in event timers keyed off one sensor, with nothing structural forcing the order. Here descent is a LINEAR machine — drogue, then main, then touchdown, terminal. Main deployment from entry (a chute shredded at Mach 1.4) does not exist as a transition, touchdown without chutes does not exist as a transition, and the deploy-altitude ledger refuses values outside the corridor.
A Soma program from the aerospace part of the verified corpus: 105 lines, cells ParachuteSequence, SequenceTests. It passes soma check with soma 3.2.4.
How it is built
The cell ParachuteSequence declares 4 signals in its face: fire_drogue(id: String, alt_m: Int) -> Map, fire_main(id: String, alt_m: Int) -> Map, confirm_touchdown(id: String) -> Map and descent_phase(id: String) -> String; keeps 1 memory slot: deploy_alt_m (Map<String, Int>, persistent and consistent) under the invariant deploy_alt_m >= 0 && deploy_alt_m <= 35000. Its state machine descent starts in entry_interface and allows 3 transitions: entry_interface -> drogue_deployed, drogue_deployed -> main_deployed and main_deployed -> touchdown. touchdown is terminal. Handlers: fire_drogue (line 43), fire_main (line 55), confirm_touchdown (line 67) and descent_phase (line 73).
The file also holds 1 test cell (SequenceTests) with 13 assertions, run by soma test.
The source
parachute_sequence.cell
// ============================================================================
// PARACHUTE SEQUENCE — Genesis: the drogue that never fired
// ============================================================================
//
// On 8 September 2004 the Genesis sample-return capsule hit the Utah
// desert at 311 km/h. The G-switch that should have sensed deceleration
// and started the parachute sequence had been installed BACKWARDS — a
// drawing error that sailed through review. No drogue, no main, just a
// crater and three years of solar-wind samples shattered. (Its sister
// design on Stardust, wired correctly, landed perfectly.)
//
// The deeper defect: the deploy sequence lived in event timers keyed off
// one sensor, with nothing structural forcing the order. Here descent is
// a LINEAR machine — drogue, then main, then touchdown, terminal. Main
// deployment from entry (a chute shredded at Mach 1.4) does not exist as
// a transition, touchdown without chutes does not exist as a transition,
// and the deploy-altitude ledger refuses values outside the corridor.
// ============================================================================
cell ParachuteSequence {
face {
signal fire_drogue(id: String, alt_m: Int) -> Map
signal fire_main(id: String, alt_m: Int) -> Map
signal confirm_touchdown(id: String) -> Map
signal descent_phase(id: String) -> String
}
memory {
// recorded deploy altitude, meters above ground — the corridor
deploy_alt_m: Map<String, Int> [persistent, consistent]
invariant deploy_alt_m >= 0 && deploy_alt_m <= 35000
}
// ABSENT: entry_interface -> main_deployed (main at Mach 1.4 shreds)
// ABSENT: entry_interface -> touchdown (the Genesis crater)
state descent {
initial: entry_interface
entry_interface -> drogue_deployed
drogue_deployed -> main_deployed
main_deployed -> touchdown // terminal
}
on fire_drogue(id: String, alt_m: Int) {
// drogue corridor: high, supersonic-capable
if alt_m < 20000 {
return map("ok", false, "reason", "drogue window missed: {alt_m} m is below 20000")
}
let w = try { deploy_alt_m.set("{id}:drogue", alt_m) }
if w.error != () { return map("ok", false, "reason", "altitude outside corridor") }
let r = try { transition(id, "drogue_deployed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "phase", get_status(id), "alt", alt_m)
}
on fire_main(id: String, alt_m: Int) {
// main corridor: low and slow, under a drogue
if alt_m > 8000 {
return map("ok", false, "reason", "too fast/high for main: {alt_m} m")
}
let w = try { deploy_alt_m.set("{id}:main", alt_m) }
if w.error != () { return map("ok", false, "reason", "altitude outside corridor") }
let r = try { transition(id, "main_deployed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "phase", get_status(id), "alt", alt_m)
}
on confirm_touchdown(id: String) {
let r = try { transition(id, "touchdown") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "phase", "touchdown")
}
on descent_phase(id: String) { return get_status(id) }
}
cell test SequenceTests {
rules {
// main without drogue: the transition does not exist
assert_fails transition("cap1", "main_deployed")
assert fire_main("cap1", 6000).ok == false
// the Genesis impact — touchdown straight from entry — is
// equally unrepresentable
assert_fails transition("cap1", "touchdown")
// the correct sequence: drogue high, main low, touchdown
assert fire_drogue("cap1", 33000).ok == true
assert descent_phase("cap1") == "drogue_deployed"
assert fire_main("cap1", 6000).ok == true
assert confirm_touchdown("cap1").ok == true
// touchdown is terminal — no re-entry into the sequence
assert_fails transition("cap1", "entry_interface")
assert_fails transition("cap1", "drogue_deployed")
// window gates: a drogue call at main altitude is refused
assert fire_drogue("cap2", 5000).ok == false
assert descent_phase("cap2") == "entry_interface"
// the altitude ledger refuses values outside the corridor
assert_fails deploy_alt_m.set("cap2:drogue", -50)
assert_fails deploy_alt_m.set("cap2:drogue", 90000)
}
}What the toolchain says
$ soma check parachute_sequence.cell
✓ All checks passed.$ soma test parachute_sequence.cell
test SequenceTests ...
✓ assert_fails transition("cap1", "main_deployed") — raised invalid transition: entry_interface → main_deployed. Current state: 'entry_interface'. Valid targets: [drogue_deployed]
✓ assert fire_main("cap1", 6000).ok == false
✓ assert_fails transition("cap1", "touchdown") — raised invalid transition: entry_interface → touchdown. Current state: 'entry_interface'. Valid targets: [drogue_deployed]
✓ assert fire_drogue("cap1", 33000).ok == true
✓ assert descent_phase("cap1") == "drogue_deployed"
✓ assert fire_main("cap1", 6000).ok == true
✓ assert confirm_touchdown("cap1").ok == true
✓ assert_fails transition("cap1", "entry_interface") — raised invalid transition: touchdown → entry_interface. Current state: 'touchdown'. Valid targets: []
✓ assert_fails transition("cap1", "drogue_deployed") — raised invalid transition: touchdown → drogue_deployed. Current state: 'touchdown'. Valid targets: []
✓ assert fire_drogue("cap2", 5000).ok == false
✓ assert descent_phase("cap2") == "entry_interface"
✓ assert_fails deploy_alt_m.set("cap2:drogue", -50) — raised memory invariant violated on 'deploy_alt_m': deploy_alt_m >= 0 && deploy_alt_m <= 35000 — rejected write of -50 (key "cap2:drogue"); the slot is unchanged
✓ assert_fails deploy_alt_m.set("cap2:drogue", 90000) — raised memory invariant violated on 'deploy_alt_m': deploy_alt_m >= 0 && deploy_alt_m <= 35000 — rejected write of 90000 (key "cap2:drogue"); the slot is unchanged
13 tests: 13 passed, 0 failed$ soma verify parachute_sequence.cell
Verifying parachute_sequence.cell...
State machine 'descent': 4 states, initial 'entry_interface'
States: [drogue_deployed, entry_interface, main_deployed, touchdown]
✓ 4 states, 3 transitions
✓ all states reachable from 'entry_interface'
✓ terminal states: [touchdown]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ think-isolated: CTL safety properties hold regardless of LLM output (3 handlers, 3 literal transitions, 0 dynamic)
✓ termination: all 4 handlers structurally terminate
✓ refinement: handler `fire_drogue` ⟶ {drogue_deployed}
✓ refinement: handler `fire_main` ⟶ {main_deployed}
✓ refinement: handler `confirm_touchdown` ⟶ {touchdown}
· transition sources: `fire_drogue`, `fire_main`, `confirm_touchdown` 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)
ParachuteSequence/invariants:
✓ invariant deploy_alt_m >= 0 — writer 'fire_drogue' proven by induction (writes alt_m)
✓ invariant deploy_alt_m >= 0 — writer 'fire_drogue' proven by induction (writes alt_m)
✓ invariant deploy_alt_m <= 35000 — writer 'fire_main' proven by induction (writes alt_m)
✓ invariant deploy_alt_m <= 35000 — writer 'fire_main' proven by induction (writes alt_m)
⚠ invariant deploy_alt_m >= 0 && deploy_alt_m <= 35000 — runtime-checked (computed values): fire_drogue → deploy_alt_m [deploy_alt_m <= 35000] because `alt_m` is only known to lie in [20000, ∞) — narrow it: `require alt_m <= 35000 else …`, fire_main → deploy_alt_m [deploy_alt_m >= 0] because `alt_m` is only known to lie in (-∞, 8000] — narrow it: `require alt_m >= 0 else …`
14 passed, 1 warnings, 0 failures
Temporal properties for 'descent':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK