Open meetings — no public decision without public notice, structurally
Every U.S. state has a Sunshine law because councils kept doing the same thing: decide in private, ratify in public. Texas's Open Meetings Act requires 72 hours of posted notice before a governmental body may act; actions taken in an unnoticed meeting are voidable, and courts have voided them — bond issues, firings, rezonings. The dodge du jour is the "walking quorum": a chain of small private chats that adds up to a deliberating majority no citizen ever saw. Here the Act is the machine. A meeting reaches `in_session` ONLY through `noticed`, and the notice ledger physically cannot record a posting shorter than 72 hours — `invariant notice_hours >= 72` makes the midnight 2-hour notice a write the memory refuses. Votes happen only in session; `adjourned` is terminal, so nothing can be slipped into a meeting after the gavel falls.
A Soma program from the governance part of the verified corpus: 118 lines, cells SunshineLaw, SunshineTests. It passes soma check with soma 3.2.4.
How it is built
The cell SunshineLaw declares 5 signals in its face: post_notice(meeting: String, hours_ahead: Int) -> Map, convene(meeting: String) -> Map, vote_item(meeting: String, item: String, ayes: Int, nays: Int) -> Map, adjourn(meeting: String) -> Map and outcome(meeting: String, item: String) -> String; keeps 2 memory slots: notice_hours (Map<String, Int>, persistent and consistent) under the invariant notice_hours >= 72 and decisions (Map<String, String>, persistent and consistent). Its state machine meeting starts in called and allows 3 transitions: called -> noticed, noticed -> in_session and in_session -> adjourned. adjourned is terminal. Handlers: post_notice (line 48), convene (line 63), vote_item (line 69), adjourn (line 80) and outcome (line 86).
The file also holds 1 test cell (SunshineTests) with 16 assertions, run by soma test.
The source
open_meetings.cell
// ============================================================================
// OPEN MEETINGS — no public decision without public notice, structurally
// ============================================================================
//
// Every U.S. state has a Sunshine law because councils kept doing the
// same thing: decide in private, ratify in public. Texas's Open Meetings
// Act requires 72 hours of posted notice before a governmental body may
// act; actions taken in an unnoticed meeting are voidable, and courts
// have voided them — bond issues, firings, rezonings. The dodge du jour
// is the "walking quorum": a chain of small private chats that adds up
// to a deliberating majority no citizen ever saw.
//
// Here the Act is the machine. A meeting reaches `in_session` ONLY
// through `noticed`, and the notice ledger physically cannot record a
// posting shorter than 72 hours — `invariant notice_hours >= 72` makes
// the midnight 2-hour notice a write the memory refuses. Votes happen
// only in session; `adjourned` is terminal, so nothing can be slipped
// into a meeting after the gavel falls.
// ============================================================================
cell SunshineLaw {
face {
signal post_notice(meeting: String, hours_ahead: Int) -> Map
signal convene(meeting: String) -> Map
signal vote_item(meeting: String, item: String, ayes: Int, nays: Int) -> Map
signal adjourn(meeting: String) -> Map
signal outcome(meeting: String, item: String) -> String
}
memory {
// hours of advance public notice. The statute's floor IS the
// type: 71 hours is a value this ledger cannot hold.
notice_hours: Map<String, Int> [persistent, consistent]
invariant notice_hours >= 72
decisions: Map<String, String> [persistent, consistent]
}
// called -> noticed -> in_session -> adjourned. No edge skips the
// notice; no edge leaves adjourned. soma verify proves both.
state meeting {
initial: called
called -> noticed
noticed -> in_session
in_session -> adjourned
}
on post_notice(meeting: String, hours_ahead: Int) {
if get_status(meeting) != "called" {
let s = get_status(meeting)
return map("ok", false, "reason", "notice must precede everything ({s})")
}
// THE WALL: a 2-hour midnight posting is refused by the memory
let w = try { notice_hours.set(meeting, hours_ahead) }
if w.error != () {
return map("ok", false, "reason",
"{hours_ahead}h notice is below the 72h statutory floor")
}
transition(meeting, "noticed")
return map("ok", true, "meeting", meeting, "hours", hours_ahead)
}
on convene(meeting: String) {
let r = try { transition(meeting, "in_session") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "in_session")
}
on vote_item(meeting: String, item: String, ayes: Int, nays: Int) {
if get_status(meeting) != "in_session" {
let s = get_status(meeting)
return map("ok", false, "reason", "no vote outside an open session ({s})")
}
let verdict = "failed"
if ayes > nays { verdict = "passed" }
decisions.set("{meeting}:{item}", verdict)
return map("ok", true, "item", item, "verdict", verdict)
}
on adjourn(meeting: String) {
let r = try { transition(meeting, "adjourned") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "adjourned")
}
on outcome(meeting: String, item: String) {
return decisions.get("{meeting}:{item}") ?? "no action"
}
}
cell test SunshineTests {
rules {
// the walking quorum's dream: vote with no meeting noticed at all
assert vote_item("oct-7", "rezone", 4, 1).ok == false
assert_fails transition("oct-7", "in_session") // notice is not skippable
// the midnight special: 2 hours of "notice" — unrepresentable
assert post_notice("oct-7", 2).ok == false
assert_fails notice_hours.set("oct-7", 71)
assert get_status("oct-7") == "called"
// lawful notice, lawful session, lawful vote
assert post_notice("oct-7", 96).ok == true
assert vote_item("oct-7", "rezone", 4, 1).ok == false // noticed != in session
assert convene("oct-7").ok == true
assert vote_item("oct-7", "rezone", 4, 1).verdict == "passed"
assert vote_item("oct-7", "bond", 2, 3).verdict == "failed"
// gavel falls: adjourned is terminal, the record is closed
assert adjourn("oct-7").ok == true
assert vote_item("oct-7", "sneaky-item", 5, 0).ok == false
assert_fails transition("oct-7", "in_session")
assert_fails transition("oct-7", "called")
assert outcome("oct-7", "rezone") == "passed"
assert outcome("oct-7", "sneaky-item") == "no action"
}
}What the toolchain says
$ soma check open_meetings.cell
✓ All checks passed.$ soma test open_meetings.cell
test SunshineTests ...
✓ assert vote_item("oct-7", "rezone", 4, 1).ok == false
✓ assert_fails transition("oct-7", "in_session") — raised invalid transition: called → in_session. Current state: 'called'. Valid targets: [noticed]
✓ assert post_notice("oct-7", 2).ok == false
✓ assert_fails notice_hours.set("oct-7", 71) — raised memory invariant violated on 'notice_hours': notice_hours >= 72 — rejected write of 71 (key "oct-7"); the slot is unchanged
✓ assert get_status("oct-7") == "called"
✓ assert post_notice("oct-7", 96).ok == true
✓ assert vote_item("oct-7", "rezone", 4, 1).ok == false
✓ assert convene("oct-7").ok == true
✓ assert vote_item("oct-7", "rezone", 4, 1).verdict == "passed"
✓ assert vote_item("oct-7", "bond", 2, 3).verdict == "failed"
✓ assert adjourn("oct-7").ok == true
✓ assert vote_item("oct-7", "sneaky-item", 5, 0).ok == false
✓ assert_fails transition("oct-7", "in_session") — raised invalid transition: adjourned → in_session. Current state: 'adjourned'. Valid targets: []
✓ assert_fails transition("oct-7", "called") — raised invalid transition: adjourned → called. Current state: 'adjourned'. Valid targets: []
✓ assert outcome("oct-7", "rezone") == "passed"
✓ assert outcome("oct-7", "sneaky-item") == "no action"
16 tests: 16 passed, 0 failed$ soma verify open_meetings.cell
Verifying open_meetings.cell...
State machine 'meeting': 4 states, initial 'called'
States: [adjourned, called, in_session, noticed]
✓ 4 states, 3 transitions
✓ all states reachable from 'called'
✓ terminal states: [adjourned]
✓ 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 5 handlers structurally terminate
✓ refinement: handler `post_notice` ⟶ {called → noticed}
✓ refinement: handler `convene` ⟶ {in_session}
✓ refinement: handler `adjourn` ⟶ {adjourned}
· transition sources: `convene`, `adjourn` 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)
SunshineLaw/invariants:
⚠ invariant notice_hours >= 72 — runtime-checked (computed values): post_notice → notice_hours because `hours_ahead` is a parameter (narrow it: `require hours_ahead >= 72 else …`)
10 passed, 1 warnings, 0 failures
Temporal properties for 'meeting':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK