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

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.

Generated from soma describe on this file.

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

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

$ 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 OK

Builtins used

get_status, len, list, map, transition

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