Launch commit criteria — Challenger: no waiver path to ignition
On 28 January 1986, Challenger launched at 36°F — far below the SRB O-ring qualification temperature. Engineers argued against the launch the night before; the criterion was effectively waived under schedule pressure. 73 seconds later the right SRB field joint failed and seven astronauts died. The Rogers Commission's finding was procedural: the system ALLOWED a red criterion to be argued green. Here the launch commit criteria are a ledger, and ignition is a transition whose handler refuses unless EVERY criterion reads green. There is no waiver argument to have: a red board cannot ignite because the committed state is only entered through the all-green gate, and once SRBs light, liftoff is TERMINAL — solids cannot be shut down, and the machine says so: there is no edge out.
A Soma program from the aerospace part of the verified corpus: 127 lines, cells LaunchCommit, CommitTests. It passes soma check with soma 3.2.4.
How it is built
The cell LaunchCommit declares 5 signals in its face: report_criterion(name: String, green: Int) -> Map, call_hold() -> Map, resume_count() -> Map, ignition() -> Map and board() -> Map; keeps 1 memory slot: criteria (Map<String, Int>, persistent and consistent) under the invariant criteria >= 0 && criteria <= 1. Its state machine countdown starts in counting and allows 4 transitions: counting -> holding, holding -> counting, counting -> committed and committed -> liftoff. liftoff is terminal. Handlers: lcc_names (line 44), report_criterion (line 48), greens_count (line 54), board (line 61), call_hold (line 67), resume_count (line 73) and ignition (line 79).
The file also holds 1 test cell (CommitTests) with 18 assertions, run by soma test.
The source
launch_commit.cell
// ============================================================================
// LAUNCH COMMIT CRITERIA — Challenger: no waiver path to ignition
// ============================================================================
//
// On 28 January 1986, Challenger launched at 36°F — far below the SRB
// O-ring qualification temperature. Engineers argued against the launch
// the night before; the criterion was effectively waived under schedule
// pressure. 73 seconds later the right SRB field joint failed and seven
// astronauts died. The Rogers Commission's finding was procedural: the
// system ALLOWED a red criterion to be argued green.
//
// Here the launch commit criteria are a ledger, and ignition is a
// transition whose handler refuses unless EVERY criterion reads green.
// There is no waiver argument to have: a red board cannot ignite because
// the committed state is only entered through the all-green gate, and
// once SRBs light, liftoff is TERMINAL — solids cannot be shut down,
// and the machine says so: there is no edge out.
// ============================================================================
cell LaunchCommit {
face {
signal report_criterion(name: String, green: Int) -> Map
signal call_hold() -> Map
signal resume_count() -> Map
signal ignition() -> Map
signal board() -> Map
}
memory {
// each criterion is 0 (red) or 1 (green) — nothing else exists
criteria: Map<String, Int> [persistent, consistent]
invariant criteria >= 0 && criteria <= 1
}
// ABSENT: committed -> counting (solids cannot be un-lit)
state countdown {
initial: counting
counting -> holding
holding -> counting
counting -> committed // only through the all-green gate
committed -> liftoff // terminal
}
on lcc_names() {
return list("oring_temp", "range_clear", "upper_winds", "ice_team")
}
on report_criterion(name: String, green: Int) {
let w = try { criteria.set(name, green) }
if w.error != () { return map("ok", false, "reason", "criterion must be 0 or 1") }
return map("ok", true, "criterion", name, "green", green)
}
on greens_count() {
let names = lcc_names()
let g = 0
for n in names { g = g + (criteria.get(n) ?? 0) }
return g
}
on board() {
let total = len(lcc_names())
let g = greens_count()
return map("green", g, "of", total, "go", g == total)
}
on call_hold() {
let r = try { transition("count", "holding") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", get_status("count"))
}
on resume_count() {
let r = try { transition("count", "counting") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", get_status("count"))
}
on ignition() {
let b = board()
// THE GATE. A red criterion is not arguable, not waivable —
// ignition simply does not happen.
if b.go != true {
return map("ok", false, "reason", "LCC red: {b.green} of {b.of} green — no waiver path exists")
}
let r = try { transition("count", "committed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
transition("count", "liftoff")
return map("ok", true, "state", get_status("count"))
}
}
cell test CommitTests {
rules {
// three greens and one red — the Challenger morning
assert report_criterion("range_clear", 1).ok == true
assert report_criterion("upper_winds", 1).ok == true
assert report_criterion("ice_team", 1).ok == true
assert report_criterion("oring_temp", 0).ok == true
assert board().go == false
// ignition with a red board: REFUSED — there is no waiver
assert ignition().ok == false
assert get_status("count") == "counting"
// a criterion can only ever be red or green — "0.5, we argued
// about it" is unrepresentable
assert_fails criteria.set("oring_temp", 2)
assert_fails criteria.set("oring_temp", -1)
// holds work both ways while counting
assert call_hold().ok == true
assert resume_count().ok == true
// all green: ignition commits and lifts off
assert report_criterion("oring_temp", 1).ok == true
assert board().go == true
assert ignition().ok == true
assert get_status("count") == "liftoff"
// liftoff is TERMINAL: solids cannot be shut down or recounted
assert_fails transition("count", "counting")
assert_fails transition("count", "holding")
assert ignition().ok == false
}
}What the toolchain says
$ soma check launch_commit.cell
✓ All checks passed.$ soma test launch_commit.cell
test CommitTests ...
✓ assert report_criterion("range_clear", 1).ok == true
✓ assert report_criterion("upper_winds", 1).ok == true
✓ assert report_criterion("ice_team", 1).ok == true
✓ assert report_criterion("oring_temp", 0).ok == true
✓ assert board().go == false
✓ assert ignition().ok == false
✓ assert get_status("count") == "counting"
✓ assert_fails criteria.set("oring_temp", 2) — raised memory invariant violated on 'criteria': criteria >= 0 && criteria <= 1 — rejected write of 2 (key "oring_temp"); the slot is unchanged
✓ assert_fails criteria.set("oring_temp", -1) — raised memory invariant violated on 'criteria': criteria >= 0 && criteria <= 1 — rejected write of -1 (key "oring_temp"); the slot is unchanged
✓ assert call_hold().ok == true
✓ assert resume_count().ok == true
✓ assert report_criterion("oring_temp", 1).ok == true
✓ assert board().go == true
✓ assert ignition().ok == true
✓ assert get_status("count") == "liftoff"
✓ assert_fails transition("count", "counting") — raised invalid transition: liftoff → counting. Current state: 'liftoff'. Valid targets: []
✓ assert_fails transition("count", "holding") — raised invalid transition: liftoff → holding. Current state: 'liftoff'. Valid targets: []
✓ assert ignition().ok == false
18 tests: 18 passed, 0 failed$ soma verify launch_commit.cell
Verifying launch_commit.cell...
State machine 'countdown': 4 states, initial 'counting'
States: [committed, counting, holding, liftoff]
✓ 4 states, 4 transitions
✓ all states reachable from 'counting'
✓ terminal states: [liftoff]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ think-isolated: CTL safety properties hold regardless of LLM output (3 handlers, 4 literal transitions, 0 dynamic)
✓ termination: all 7 handlers structurally terminate
✓ refinement: handler `call_hold` ⟶ {holding}
✓ refinement: handler `resume_count` ⟶ {counting}
✓ refinement: handler `ignition` ⟶ {committed, liftoff}
· transition sources: `call_hold`, `resume_count`, `ignition` 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)
LaunchCommit/invariants:
⚠ invariant criteria >= 0 && criteria <= 1 — runtime-checked (computed values): report_criterion → criteria because `green` is a parameter (narrow it: `require green >= 0 && green <= 1 else …`)
10 passed, 1 warnings, 0 failures
Temporal properties for 'countdown':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OKBuiltins used
get_status, len, list, map, transition