soma.cell

Census — after certification, the count is history, not a database row

The decennial census count, once certified and delivered for apportionment (13 U.S.C. § 141), decides House seats and a decade of federal funding. The 2020 cycle showed why post-hoc edits are radioactive: any quiet change to a certified tract count would shift money and seats with no audit trail. Here certification is a terminal state — soma verify proves no transition leaves it — so amendment handlers go dead at that instant, and the count itself refuses impossible values (a negative population cannot be written, whatever a buggy import believes).

A Soma program from the civic part of the verified corpus: 106 lines, cells Census, CensusTests. It passes soma check with soma 3.2.4.

How it is built

The cell Census declares 7 signals in its face: record_count(tract: String, people: Int) -> Map, start_review() -> Map, amend(tract: String, people: Int) -> Map, certify() -> Map, tract_count(tract: String) -> Int, total_population() -> Int and phase() -> String; keeps 1 memory slot: pop (Map<String, Int>, persistent and consistent) under the invariant pop >= 0. Its state machine count starts in collecting and allows 2 transitions: collecting -> reviewing and reviewing -> certified. certified is terminal. Handlers: phase (line 39), record_count (line 41), start_review (line 52), amend (line 59), certify (line 70), tract_count (line 76) and total_population (line 78).

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

Generated from soma describe on this file.

The source

census.cell

// ============================================================================
// CENSUS — after certification, the count is history, not a database row
// ============================================================================
// The decennial census count, once certified and delivered for apportionment
// (13 U.S.C. § 141), decides House seats and a decade of federal funding.
// The 2020 cycle showed why post-hoc edits are radioactive: any quiet change
// to a certified tract count would shift money and seats with no audit trail.
// Here certification is a terminal state — soma verify proves no transition
// leaves it — so amendment handlers go dead at that instant, and the count
// itself refuses impossible values (a negative population cannot be written,
// whatever a buggy import believes).
// ============================================================================

cell Census {
    face {
        signal record_count(tract: String, people: Int) -> Map
        signal start_review() -> Map
        signal amend(tract: String, people: Int) -> Map
        signal certify() -> Map
        signal tract_count(tract: String) -> Int
        signal total_population() -> Int
        signal phase() -> String
    }

    memory {
        // tract -> persons. A negative count is unrepresentable.
        pop: Map<String, Int> [persistent, consistent]
        invariant pop >= 0
    }

    // certified has no exit: the count delivered to the President
    // is the count, forever.
    state count {
        initial: collecting
        collecting -> reviewing
        reviewing -> certified
    }

    on phase() { return get_status("count") }

    on record_count(tract: String, people: Int) {
        if phase() != "collecting" {
            return map("ok", false, "reason", "enumeration is {phase()}")
        }
        let w = try { pop.set(tract, people) }
        if w.error != () {
            return map("ok", false, "reason", "impossible count for {tract}")
        }
        return map("ok", true, "tract", tract, "people", people)
    }

    on start_review() {
        let r = try { transition("count", "reviewing") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true)
    }

    // Amendments exist — but ONLY in review, between collection and seal.
    on amend(tract: String, people: Int) {
        if phase() != "reviewing" {
            return map("ok", false, "reason", "no amendments while {phase()}")
        }
        let w = try { pop.set(tract, people) }
        if w.error != () {
            return map("ok", false, "reason", "impossible count for {tract}")
        }
        return map("ok", true, "tract", tract, "people", people)
    }

    on certify() {
        let r = try { transition("count", "certified") }
        if r.error != () { return map("ok", false, "reason", "{r.error}") }
        return map("ok", true, "population", total_population())
    }

    on tract_count(tract: String) { return pop.get(tract) ?? 0 }

    on total_population() {
        let tracts = pop.keys()
        let n = 0
        for t in tracts { n = n + (pop.get(t) ?? 0) }
        return n
    }
}

cell test CensusTests {
    rules {
        assert record_count("t1", 4200).ok == true
        assert record_count("t2", 1800).ok == true
        assert record_count("t3", -50).ok == false        // negative people: refused
        assert_fails pop.set("t3", -50)                   // by the memory itself
        assert amend("t1", 4300).ok == false              // no amending mid-collection
        assert start_review().ok == true
        assert record_count("t4", 99).ok == false         // collection is over
        assert amend("t1", 4250).ok == true               // review may correct
        assert tract_count("t1") == 4250
        assert certify().ok == true
        assert total_population() == 6050
        assert amend("t1", 9999).ok == false              // certified: handlers go dead
        assert record_count("t9", 1).ok == false
        assert tract_count("t1") == 4250                  // the number did not move
        assert_fails transition("count", "reviewing")     // and never will
        assert_fails transition("count", "collecting")
    }
}

What the toolchain says

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

$ soma check census.cell
✓ All checks passed.
$ soma test census.cell
test CensusTests ...
  ✓ assert record_count("t1", 4200).ok == true
  ✓ assert record_count("t2", 1800).ok == true
  ✓ assert record_count("t3", -50).ok == false
  ✓ assert_fails pop.set("t3", -50) — raised memory invariant violated on 'pop': pop >= 0 — rejected write of -50 (key "t3"); the slot is unchanged
  ✓ assert amend("t1", 4300).ok == false
  ✓ assert start_review().ok == true
  ✓ assert record_count("t4", 99).ok == false
  ✓ assert amend("t1", 4250).ok == true
  ✓ assert tract_count("t1") == 4250
  ✓ assert certify().ok == true
  ✓ assert total_population() == 6050
  ✓ assert amend("t1", 9999).ok == false
  ✓ assert record_count("t9", 1).ok == false
  ✓ assert tract_count("t1") == 4250
  ✓ assert_fails transition("count", "reviewing") — raised invalid transition: certified → reviewing. Current state: 'certified'. Valid targets: []
  ✓ assert_fails transition("count", "collecting") — raised invalid transition: certified → collecting. Current state: 'certified'. Valid targets: []

16 tests: 16 passed, 0 failed
$ soma verify census.cell
Verifying census.cell...

State machine 'count': 3 states, initial 'collecting'
  States: [certified, collecting, reviewing]

  ✓ 3 states, 2 transitions
  ✓ all states reachable from 'collecting'
  ✓ terminal states: [certified]
  ✓ no deadlocks
  ✓ liveness: every state can eventually reach a terminal state
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (2 handlers, 2 literal transitions, 0 dynamic)
  ✓ termination: all 7 handlers structurally terminate
  ✓ refinement: handler `start_review` ⟶ {reviewing}
  ✓ refinement: handler `certify` ⟶ {certified}
  · transition sources: `start_review`, `certify` 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)

Census/invariants:

  ⚠ invariant pop >= 0 — runtime-checked (computed values): amend → pop because `people` is a parameter (narrow it: `require people >= 0 else …`), record_count → pop because `people` is a parameter (narrow it: `require people >= 0 else …`)

9 passed, 1 warnings, 0 failures

  Temporal properties for 'count':
  ✓ 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/census.cell · examples/corpus/civic/census.cell