soma.cell

Permit — a building permit that cannot skip a review

New York's self-certification program let architects stamp their own permits "to speed things up" — DOB audits later found buildings approved with zoning and structural reviews simply skipped, and the city spent a decade revoking permits for occupied buildings. The skip is possible when "reviewed" is a checkbox. Here the approval chain is the state machine itself: `issued` is only reachable THROUGH zoning, structural, and fire review, soma verify proves it, and `denied` and `issued` are terminal. The fee ledger is a second wall: a permit cannot be charged twice.

A Soma program from the civic part of the verified corpus: 118 lines, cells BuildingPermit, PermitTests. It passes soma check with soma 3.2.4.

How it is built

The cell BuildingPermit declares 7 signals in its face: pay_fee(id: String) -> Map, pass_zoning(id: String) -> Map, pass_structural(id: String) -> Map, pass_fire(id: String) -> Map, issue(id: String) -> Map, deny(id: String) -> Map and permit_state(id: String) -> String; keeps 1 memory slot: fees (Map<String, Int>, persistent and consistent) under the invariant fees >= 0 && fees <= 1. Its state machine permit starts in filed and allows 8 transitions: filed -> zoning_ok, zoning_ok -> structural_ok, structural_ok -> fire_ok, fire_ok -> issued, filed -> denied, zoning_ok -> denied, structural_ok -> denied and fire_ok -> denied. denied and issued are terminal. Handlers: permit_state (line 45), pay_fee (line 47), pass_zoning (line 56), pass_structural (line 62), pass_fire (line 68), issue (line 74) and deny (line 83).

The file also holds 1 test cell (PermitTests) with 17 assertions, run by soma test.

Generated from soma describe on this file.

The source

permit.cell

// ============================================================================
// PERMIT — a building permit that cannot skip a review
// ============================================================================
// New York's self-certification program let architects stamp their own
// permits "to speed things up" — DOB audits later found buildings approved
// with zoning and structural reviews simply skipped, and the city spent a
// decade revoking permits for occupied buildings. The skip is possible when
// "reviewed" is a checkbox. Here the approval chain is the state machine
// itself: `issued` is only reachable THROUGH zoning, structural, and fire
// review, soma verify proves it, and `denied` and `issued` are terminal.
// The fee ledger is a second wall: a permit cannot be charged twice.
// ============================================================================

cell BuildingPermit {
    face {
        signal pay_fee(id: String) -> Map
        signal pass_zoning(id: String) -> Map
        signal pass_structural(id: String) -> Map
        signal pass_fire(id: String) -> Map
        signal issue(id: String) -> Map
        signal deny(id: String) -> Map
        signal permit_state(id: String) -> String
    }

    memory {
        // permit -> fee payments. Double-billing is unrepresentable.
        fees: Map<String, Int> [persistent, consistent]
        invariant fees >= 0 && fees <= 1
    }

    // The chain. There is NO edge from filed to issued: every issued
    // permit passed all three reviews, by construction.
    state permit {
        initial: filed
        filed -> zoning_ok
        zoning_ok -> structural_ok
        structural_ok -> fire_ok
        fire_ok -> issued
        filed -> denied
        zoning_ok -> denied
        structural_ok -> denied
        fire_ok -> denied
    }

    on permit_state(id: String) { return get_status(id) }

    on pay_fee(id: String) {
        let prior = fees.get(id) ?? 0
        let w = try { fees.set(id, prior + 1) }
        if w.error != () {
            return map("ok", false, "reason", "fee already collected for {id}")
        }
        return map("ok", true, "permit", id)
    }

    on pass_zoning(id: String) {
        let r = try { transition(id, "zoning_ok") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "stage", permit_state(id))
    }

    on pass_structural(id: String) {
        let r = try { transition(id, "structural_ok") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "stage", permit_state(id))
    }

    on pass_fire(id: String) {
        let r = try { transition(id, "fire_ok") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "stage", permit_state(id))
    }

    on issue(id: String) {
        if (fees.get(id) ?? 0) == 0 {
            return map("ok", false, "reason", "fee unpaid for {id}")
        }
        let r = try { transition(id, "issued") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "permit", id)
    }

    on deny(id: String) {
        let r = try { transition(id, "denied") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "permit", id)
    }
}

cell test PermitTests {
    rules {
        // the self-certification shortcut does not exist
        assert pay_fee("p1").ok == true
        assert issue("p1").ok == false                    // filed -> issued: no edge
        assert_fails transition("p1", "issued")
        assert_fails transition("p1", "fire_ok")          // and no skipping ahead

        // the only road is through every review
        assert pass_zoning("p1").ok == true
        assert pass_structural("p1").ok == true
        assert pass_fire("p1").ok == true
        assert issue("p1").ok == true
        assert permit_state("p1") == "issued"
        assert_fails transition("p1", "filed")            // issued is forever
        assert_fails fees.set("p1", 2)                    // no double-billing

        // an unpaid permit stalls at the cashier even after reviews
        assert pass_zoning("p2").ok == true
        assert pass_structural("p2").ok == true
        assert pass_fire("p2").ok == true
        assert issue("p2").ok == false                    // fee unpaid

        // a denial is terminal too
        assert deny("p3").ok == true
        assert_fails transition("p3", "zoning_ok")
    }
}

What the toolchain says

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

$ soma check permit.cell
✓ All checks passed.
$ soma test permit.cell
test PermitTests ...
  ✓ assert pay_fee("p1").ok == true
  ✓ assert issue("p1").ok == false
  ✓ assert_fails transition("p1", "issued") — raised invalid transition: filed → issued. Current state: 'filed'. Valid targets: [zoning_ok, denied]
  ✓ assert_fails transition("p1", "fire_ok") — raised invalid transition: filed → fire_ok. Current state: 'filed'. Valid targets: [zoning_ok, denied]
  ✓ assert pass_zoning("p1").ok == true
  ✓ assert pass_structural("p1").ok == true
  ✓ assert pass_fire("p1").ok == true
  ✓ assert issue("p1").ok == true
  ✓ assert permit_state("p1") == "issued"
  ✓ assert_fails transition("p1", "filed") — raised invalid transition: issued → filed. Current state: 'issued'. Valid targets: []
  ✓ assert_fails fees.set("p1", 2) — raised memory invariant violated on 'fees': fees >= 0 && fees <= 1 — rejected write of 2 (key "p1"); the slot is unchanged
  ✓ assert pass_zoning("p2").ok == true
  ✓ assert pass_structural("p2").ok == true
  ✓ assert pass_fire("p2").ok == true
  ✓ assert issue("p2").ok == false
  ✓ assert deny("p3").ok == true
  ✓ assert_fails transition("p3", "zoning_ok") — raised invalid transition: denied → zoning_ok. Current state: 'denied'. Valid targets: []

17 tests: 17 passed, 0 failed
$ soma verify permit.cell
Verifying permit.cell...

State machine 'permit': 6 states, initial 'filed'
  States: [denied, filed, fire_ok, issued, structural_ok, zoning_ok]

  ✓ 6 states, 8 transitions
  ✓ all states reachable from 'filed'
  ✓ terminal states: [issued, denied]
  ✓ no deadlocks
  ✓ liveness: every state can eventually reach a terminal state
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (5 handlers, 5 literal transitions, 0 dynamic)
  ✓ termination: all 7 handlers structurally terminate
  ✓ refinement: handler `pass_zoning` ⟶ {zoning_ok}
  ✓ refinement: handler `pass_structural` ⟶ {structural_ok}
  ✓ refinement: handler `pass_fire` ⟶ {fire_ok}
  ✓ refinement: handler `issue` ⟶ {issued}
  ✓ refinement: handler `deny` ⟶ {denied}
  · transition sources: `pass_zoning`, `pass_structural`, `pass_fire`, `issue`, `deny` 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)

BuildingPermit/invariants:

  ✓ invariant fees >= 0 — writer 'pay_fee' proven by induction (writes prior + 1)
  ⚠ invariant fees >= 0 && fees <= 1 — runtime-checked (computed values): pay_fee → fees [fees <= 1] because `prior + 1` is only known to lie in [1, 2] — narrow it: `require (prior + 1) <= 1 else …`

13 passed, 1 warnings, 0 failures

  Temporal properties for 'permit':
  ✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK

Builtins used

get_status, map, transition

Source file: /corpus/civic/permit.cell · examples/corpus/civic/permit.cell