Amendment — supermajority ratification, and a "yes" that cannot be unsaid
In 1868, Ohio and New Jersey ratified the Fourteenth Amendment — then, as politics shifted, voted to RESCIND their ratifications. Congress and the Secretary of State counted them anyway: under Article V practice, ratification is a one-way act. The same fight resurfaced with the ERA in the 1970s. The constitutional answer became doctrine: a state's "yes" enters the permanent record; there is no take-back. This module makes both halves structural. A ratification slot can hold exactly the value 1 — the rescission (writing it back to 0) is refused by the MEMORY, not by a clerk's ruling. And the amendment cannot reach `ratified` (terminal, proven by soma verify) until a supermajority of 7 of the 10 member states is actually on the books.
A Soma program from the governance part of the verified corpus: 129 lines, cells ArticleFive, AmendmentTests. It passes soma check with soma 3.2.4.
How it is built
The cell ArticleFive declares 5 signals in its face: propose(amendment: String) -> Map, ratify(amendment: String, state_name: String) -> Map, rescind(amendment: String, state_name: String) -> Map, proclaim(amendment: String) -> Map and yes_count(amendment: String) -> Int; keeps 1 memory slot: record_of (Map<String, Int>, persistent and consistent) under the invariant record_of >= 1 && record_of <= 1. Its state machine amendment starts in drafted and allows 3 transitions: drafted -> proposed, proposed -> ratified and proposed -> expired. expired and ratified are terminal. Handlers: propose (line 44), ratify (line 50), rescind (line 65), proclaim (line 77) and yes_count (line 87).
The file also holds 1 test cell (AmendmentTests) with 19 assertions, run by soma test.
The source
amendment.cell
// ============================================================================
// AMENDMENT — supermajority ratification, and a "yes" that cannot be unsaid
// ============================================================================
//
// In 1868, Ohio and New Jersey ratified the Fourteenth Amendment — then,
// as politics shifted, voted to RESCIND their ratifications. Congress
// and the Secretary of State counted them anyway: under Article V
// practice, ratification is a one-way act. The same fight resurfaced
// with the ERA in the 1970s. The constitutional answer became doctrine:
// a state's "yes" enters the permanent record; there is no take-back.
//
// This module makes both halves structural. A ratification slot can hold
// exactly the value 1 — the rescission (writing it back to 0) is refused
// by the MEMORY, not by a clerk's ruling. And the amendment cannot reach
// `ratified` (terminal, proven by soma verify) until a supermajority of
// 7 of the 10 member states is actually on the books.
// ============================================================================
cell ArticleFive {
face {
signal propose(amendment: String) -> Map
signal ratify(amendment: String, state_name: String) -> Map
signal rescind(amendment: String, state_name: String) -> Map
signal proclaim(amendment: String) -> Map
signal yes_count(amendment: String) -> Int
}
memory {
// amendment:state -> 1. The ONLY value the slot can hold.
// Rescission writes 0; absence-by-overwrite is unrepresentable.
record_of: Map<String, Int> [persistent, consistent]
invariant record_of >= 1 && record_of <= 1
}
// ratified is TERMINAL — there is no edge out. A ratified amendment
// is the constitution now; the machine has no way to un-make it.
state amendment {
initial: drafted
drafted -> proposed
proposed -> ratified
proposed -> expired
}
on propose(amendment: String) {
let r = try { transition(amendment, "proposed") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "proposed")
}
on ratify(amendment: String, state_name: String) {
if get_status(amendment) != "proposed" {
let s = get_status(amendment)
return map("ok", false, "reason", "nothing to ratify: amendment is {s}")
}
let k = "{amendment}:{state_name}"
if record_of.get(k) != () {
return map("ok", false, "reason", "{state_name} already ratified — once")
}
record_of.set(k, 1)
return map("ok", true, "state_name", state_name, "yes", yes_count(amendment))
}
// Ohio, 1868: "we take it back." The memory says no: removing the
// yes means writing record_of down to 0, a value the slot cannot hold.
on rescind(amendment: String, state_name: String) {
let k = "{amendment}:{state_name}"
let cur = record_of.get(k)
if cur == () { return map("ok", false, "reason", "{state_name} never ratified") }
let w = try { record_of.set(k, cur - 1) }
if w.error != () {
return map("ok", false, "reason", "ratification is a one-way act: {w.error}")
}
return map("ok", true)
}
// supermajority: 7 of 10 states, checked against the permanent record
on proclaim(amendment: String) {
let yes = yes_count(amendment)
if yes < 7 {
return map("ok", false, "reason", "supermajority not reached: {yes} of 10, need 7")
}
let r = try { transition(amendment, "ratified") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "ratified", "yes", yes)
}
on yes_count(amendment: String) {
let n = 0
for k in record_of.keys() {
if starts_with(k, "{amendment}:") { n = n + 1 }
}
return n
}
}
cell test AmendmentTests {
rules {
// no ratifying a draft that was never proposed
assert ratify("A28", "ohio").ok == false
assert propose("A28").ok == true
// six states say yes — one short of the 7/10 supermajority
assert ratify("A28", "s1").ok == true
assert ratify("A28", "s2").ok == true
assert ratify("A28", "s3").ok == true
assert ratify("A28", "s4").ok == true
assert ratify("A28", "s5").ok == true
assert ratify("A28", "s6").ok == true
assert ratify("A28", "s6").ok == false // double-ratify: no
assert proclaim("A28").ok == false // 6 < 7
// THE RESCISSION: s3 tries to take its yes back. The slot can
// only hold 1 — writing 0 is refused by the memory itself.
assert rescind("A28", "s3").ok == false
assert_fails record_of.set("A28:s3", 0)
assert yes_count("A28") == 6
// the seventh state arrives; the supermajority is real
assert ratify("A28", "s7").ok == true
assert proclaim("A28").ok == true
// ratified is forever: no edge out, no late rescission
assert_fails transition("A28", "proposed")
assert_fails transition("A28", "expired")
assert rescind("A28", "s7").ok == false
assert yes_count("A28") == 7
}
}What the toolchain says
$ soma check amendment.cell
✓ All checks passed.$ soma test amendment.cell
test AmendmentTests ...
✓ assert ratify("A28", "ohio").ok == false
✓ assert propose("A28").ok == true
✓ assert ratify("A28", "s1").ok == true
✓ assert ratify("A28", "s2").ok == true
✓ assert ratify("A28", "s3").ok == true
✓ assert ratify("A28", "s4").ok == true
✓ assert ratify("A28", "s5").ok == true
✓ assert ratify("A28", "s6").ok == true
✓ assert ratify("A28", "s6").ok == false
✓ assert proclaim("A28").ok == false
✓ assert rescind("A28", "s3").ok == false
✓ assert_fails record_of.set("A28:s3", 0) — raised memory invariant violated on 'record_of': record_of >= 1 && record_of <= 1 — rejected write of 0 (key "A28:s3"); the slot is unchanged
✓ assert yes_count("A28") == 6
✓ assert ratify("A28", "s7").ok == true
✓ assert proclaim("A28").ok == true
✓ assert_fails transition("A28", "proposed") — raised invalid transition: ratified → proposed. Current state: 'ratified'. Valid targets: []
✓ assert_fails transition("A28", "expired") — raised invalid transition: ratified → expired. Current state: 'ratified'. Valid targets: []
✓ assert rescind("A28", "s7").ok == false
✓ assert yes_count("A28") == 7
19 tests: 19 passed, 0 failed$ soma verify amendment.cell
Verifying amendment.cell...
State machine 'amendment': 4 states, initial 'drafted'
States: [drafted, expired, proposed, ratified]
✓ 4 states, 3 transitions
✓ all states reachable from 'drafted'
✓ terminal states: [ratified, expired]
✓ 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 5 handlers structurally terminate
⚠ refinement: declared transition `proposed → expired` is never reached by any handler — spec may be aspirational or stale
✓ refinement: handler `propose` ⟶ {proposed}
✓ refinement: handler `proclaim` ⟶ {ratified}
· transition sources: `propose`, `proclaim` 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)
ArticleFive/invariants:
✓ invariant record_of >= 1 && record_of <= 1 — writer 'ratify' proven (writes 1)
✓ invariant record_of >= 1 && record_of <= 1 — writer 'rescind' can never commit cur - 1 to 'record_of': the write is always rejected (and the handler catches it)
11 passed, 1 warnings, 0 failures
Temporal properties for 'amendment':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK