Disaster aid — one emergency grant per household, however many times asked
After Katrina, GAO estimated roughly $1 billion of FEMA individual assistance was improper — much of it duplicate registrations: the same household applying through different names, addresses, or call centers, each application paid because the dedup ran AFTER the checks went out. Here the duplicate is unrepresentable: a household's grant count is a memory invariant capped at 1, so the second disbursement is refused at the write, whichever intake channel it arrived through. The aid program also has a one-way lifecycle — once the program is audited, soma verify proves it never re-opens for "supplemental" off-book payments.
A Soma program from the civic part of the verified corpus: 116 lines, cells DisasterAid, DisasterAidTests. It passes soma check with soma 3.2.4.
How it is built
The cell DisasterAid declares 7 signals in its face: declare_disaster() -> Map, disburse(household: String, amount: Int) -> Map, close_registration() -> Map, complete_audit() -> Map, grants_to(household: String) -> Int, outlay_total() -> Int and phase() -> String; keeps 2 memory slots: granted (Map<String, Int>, persistent and consistent) under the invariant granted >= 0 && granted <= 1 and outlay (Map<String, Int>, persistent and consistent) under the invariant outlay >= 0 && outlay <= 5000. Its state machine program starts in declared and allows 3 transitions: declared -> registration_open, registration_open -> registration_closed and registration_closed -> audited. audited is terminal. Handlers: phase (line 43), declare_disaster (line 45), disburse (line 51), close_registration (line 72), complete_audit (line 78), grants_to (line 84) and outlay_total (line 86).
The file also holds 1 test cell (DisasterAidTests) with 16 assertions, run by soma test.
The source
disaster_aid.cell
// ============================================================================
// DISASTER AID — one emergency grant per household, however many times asked
// ============================================================================
// After Katrina, GAO estimated roughly $1 billion of FEMA individual
// assistance was improper — much of it duplicate registrations: the same
// household applying through different names, addresses, or call centers,
// each application paid because the dedup ran AFTER the checks went out.
// Here the duplicate is unrepresentable: a household's grant count is a
// memory invariant capped at 1, so the second disbursement is refused at the
// write, whichever intake channel it arrived through. The aid program also
// has a one-way lifecycle — once the program is audited, soma verify proves
// it never re-opens for "supplemental" off-book payments.
// ============================================================================
cell DisasterAid {
face {
signal declare_disaster() -> Map
signal disburse(household: String, amount: Int) -> Map
signal close_registration() -> Map
signal complete_audit() -> Map
signal grants_to(household: String) -> Int
signal outlay_total() -> Int
signal phase() -> String
}
memory {
// household -> emergency grants. A second grant is unrepresentable.
granted: Map<String, Int> [persistent, consistent]
invariant granted >= 0 && granted <= 1
outlay: Map<String, Int> [persistent, consistent]
invariant outlay >= 0 && outlay <= 5000
}
// audited is terminal: a closed program pays no one, ever again.
state program {
initial: declared
declared -> registration_open
registration_open -> registration_closed
registration_closed -> audited
}
on phase() { return get_status("program") }
on declare_disaster() {
let r = try { transition("program", "registration_open") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true)
}
on disburse(household: String, amount: Int) {
if phase() != "registration_open" {
return map("ok", false, "reason", "program is {phase()}")
}
if amount <= 0 { return map("ok", false, "reason", "amount must be positive") }
let prior = granted.get(household) ?? 0
// WALL 1: the duplicate registration, refused at the write —
// a different call center is still the same household key.
let g = try { granted.set(household, prior + 1) }
if g.error != () {
return map("ok", false, "reason", "{household} already assisted")
}
// WALL 2: the per-household statutory cap on the check itself.
let w = try { outlay.set(household, amount) }
if w.error != () {
granted.set(household, prior) // roll the grant back
return map("ok", false, "reason", "amount {amount} exceeds the 5000 cap")
}
return map("ok", true, "household", household, "amount", amount)
}
on close_registration() {
let r = try { transition("program", "registration_closed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "outlay", outlay_total())
}
on complete_audit() {
let r = try { transition("program", "audited") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "outlay", outlay_total())
}
on grants_to(household: String) { return granted.get(household) ?? 0 }
on outlay_total() {
let names = outlay.keys()
let n = 0
for h in names { n = n + (outlay.get(h) ?? 0) }
return n
}
}
cell test DisasterAidTests {
rules {
assert disburse("hh-12", 3000).ok == false // no declaration yet
assert declare_disaster().ok == true
assert disburse("hh-12", 3000).ok == true
// the duplicate registration, through a "different" call center
assert disburse("hh-12", 3000).ok == false
assert_fails granted.set("hh-12", 2) // unrepresentable
assert grants_to("hh-12") == 1
// the oversized check bounces and leaves no phantom grant behind
assert disburse("hh-77", 9000).ok == false
assert grants_to("hh-77") == 0
assert_fails outlay.set("hh-77", 9000)
assert disburse("hh-77", 5000).ok == true // the cap itself is fine
assert outlay_total() == 8000
assert close_registration().ok == true
assert disburse("hh-99", 100).ok == false // registration closed
assert complete_audit().ok == true
assert_fails transition("program", "registration_open") // audited forever
assert_fails transition("program", "registration_closed")
}
}What the toolchain says
$ soma check disaster_aid.cell
✓ All checks passed.$ soma test disaster_aid.cell
test DisasterAidTests ...
✓ assert disburse("hh-12", 3000).ok == false
✓ assert declare_disaster().ok == true
✓ assert disburse("hh-12", 3000).ok == true
✓ assert disburse("hh-12", 3000).ok == false
✓ assert_fails granted.set("hh-12", 2) — raised memory invariant violated on 'granted': granted >= 0 && granted <= 1 — rejected write of 2 (key "hh-12"); the slot is unchanged
✓ assert grants_to("hh-12") == 1
✓ assert disburse("hh-77", 9000).ok == false
✓ assert grants_to("hh-77") == 0
✓ assert_fails outlay.set("hh-77", 9000) — raised memory invariant violated on 'outlay': outlay >= 0 && outlay <= 5000 — rejected write of 9000 (key "hh-77"); the slot is unchanged
✓ assert disburse("hh-77", 5000).ok == true
✓ assert outlay_total() == 8000
✓ assert close_registration().ok == true
✓ assert disburse("hh-99", 100).ok == false
✓ assert complete_audit().ok == true
✓ assert_fails transition("program", "registration_open") — raised invalid transition: audited → registration_open. Current state: 'audited'. Valid targets: []
✓ assert_fails transition("program", "registration_closed") — raised invalid transition: audited → registration_closed. Current state: 'audited'. Valid targets: []
16 tests: 16 passed, 0 failed$ soma verify disaster_aid.cell
Verifying disaster_aid.cell...
State machine 'program': 4 states, initial 'declared'
States: [audited, declared, registration_closed, registration_open]
✓ 4 states, 3 transitions
✓ all states reachable from 'declared'
✓ terminal states: [audited]
✓ 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 7 handlers structurally terminate
✓ refinement: handler `declare_disaster` ⟶ {registration_open}
✓ refinement: handler `close_registration` ⟶ {registration_closed}
✓ refinement: handler `complete_audit` ⟶ {audited}
· transition sources: `declare_disaster`, `close_registration`, `complete_audit` 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)
DisasterAid/invariants:
✓ invariant granted >= 0 — writer 'disburse' proven by induction (writes prior + 1)
✓ invariant granted >= 0 && granted <= 1 — writer 'disburse' proven by induction (writes prior)
⚠ invariant granted >= 0 && granted <= 1 — runtime-checked (computed values): disburse → granted [granted <= 1] because `prior + 1` is only known to lie in [1, 2] — narrow it: `require (prior + 1) <= 1 else …`
✓ invariant outlay >= 0 — writer 'disburse' proven by induction (writes amount)
⚠ invariant outlay >= 0 && outlay <= 5000 — runtime-checked (computed values): disburse → outlay [outlay <= 5000] because `amount` is only known to lie in [1, ∞) — narrow it: `require amount <= 5000 else …`
13 passed, 2 warnings, 0 failures
Temporal properties for 'program':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK