Foia — the statutory clock cannot be silently ignored
5 U.S.C. § 552 gives an agency 20 working days to answer a FOIA request. In practice requests sit for years — agencies just keep them "processing" and courts call the silence a constructive denial (journalists sued the FBI over requests pending for a decade). The dodge works because nothing FORCES the escalation. Here the day counter has a hard ceiling: day 21 cannot be written for a request still in processing — the refused write trips the escalation transition instead. Overdue requests land on the commissioner's desk structurally, and `fulfilled` is terminal: an answered request cannot be quietly re-opened to dodge reporting.
A Soma program from the civic part of the verified corpus: 99 lines, cells Foia, FoiaTests. It passes soma check with soma 3.2.4.
How it is built
The cell Foia declares 6 signals in its face: assign_officer(id: String) -> Map, log_day(id: String) -> Map, simulate_days(id: String, n: Int) -> String, fulfill(id: String) -> Map, days_open(id: String) -> Int and request_state(id: String) -> String; keeps 1 memory slot: days (Map<String, Int>, persistent and consistent) under the invariant days >= 0 && days <= 20. Its state machine request starts in received and allows 4 transitions: received -> processing, processing -> fulfilled, processing -> overdue and overdue -> fulfilled. fulfilled is terminal. Handlers: request_state (line 41), assign_officer (line 43), log_day (line 50), simulate_days (line 65), fulfill (line 70) and days_open (line 76).
The file also holds 1 test cell (FoiaTests) with 14 assertions, run by soma test.
The source
foia.cell
// ============================================================================
// FOIA — the statutory clock cannot be silently ignored
// ============================================================================
// 5 U.S.C. § 552 gives an agency 20 working days to answer a FOIA request.
// In practice requests sit for years — agencies just keep them "processing"
// and courts call the silence a constructive denial (journalists sued the
// FBI over requests pending for a decade). The dodge works because nothing
// FORCES the escalation. Here the day counter has a hard ceiling: day 21
// cannot be written for a request still in processing — the refused write
// trips the escalation transition instead. Overdue requests land on the
// commissioner's desk structurally, and `fulfilled` is terminal: an answered
// request cannot be quietly re-opened to dodge reporting.
// ============================================================================
cell Foia {
face {
signal assign_officer(id: String) -> Map
signal log_day(id: String) -> Map
signal simulate_days(id: String, n: Int) -> String
signal fulfill(id: String) -> Map
signal days_open(id: String) -> Int
signal request_state(id: String) -> String
}
memory {
// request -> working days in processing. Day 21 is unrepresentable:
// the write refuses, and the refusal IS the escalation trigger.
days: Map<String, Int> [persistent, consistent]
invariant days >= 0 && days <= 20
}
// fulfilled is terminal: an answered request is answered forever.
state request {
initial: received
received -> processing
processing -> fulfilled
processing -> overdue
overdue -> fulfilled
}
on request_state(id: String) { return get_status(id) }
on assign_officer(id: String) {
let r = try { transition(id, "processing") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
days.set(id, 0)
return map("ok", true, "request", id)
}
on log_day(id: String) {
if request_state(id) != "processing" {
return map("ok", false, "reason", "request is {request_state(id)}")
}
let d = days.get(id) ?? 0
let w = try { days.set(id, d + 1) }
if w.error != () {
// The statute, enforced by the memory: day 21 does not exist
// for an unanswered request. Escalate instead.
transition(id, "overdue")
return map("ok", true, "escalated", true, "day", d)
}
return map("ok", true, "escalated", false, "day", d + 1)
}
on simulate_days(id: String, n: Int) {
for i in range(0, n) { log_day(id) }
return request_state(id)
}
on fulfill(id: String) {
let r = try { transition(id, "fulfilled") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "after_days", days_open(id))
}
on days_open(id: String) { return days.get(id) ?? 0 }
}
cell test FoiaTests {
rules {
assert fulfill("r1").ok == false // can't answer the unassigned
assert assign_officer("r1").ok == true
assert simulate_days("r1", 20) == "processing" // 20 days: still lawful
assert days_open("r1") == 20
assert_fails days.set("r1", 21) // day 21: unrepresentable
assert simulate_days("r1", 1) == "overdue" // the clock escalates
assert log_day("r1").ok == false // overdue clock is stopped
assert fulfill("r1").ok == true // commissioner closes it
assert_fails transition("r1", "processing") // no quiet re-opening
assert_fails transition("r1", "overdue")
// a fast request never touches escalation
assert assign_officer("r2").ok == true
assert simulate_days("r2", 5) == "processing"
assert fulfill("r2").ok == true
assert request_state("r2") == "fulfilled"
}
}What the toolchain says
$ soma check foia.cell
✓ All checks passed.$ soma test foia.cell
test FoiaTests ...
✓ assert fulfill("r1").ok == false
✓ assert assign_officer("r1").ok == true
✓ assert simulate_days("r1", 20) == "processing"
✓ assert days_open("r1") == 20
✓ assert_fails days.set("r1", 21) — raised memory invariant violated on 'days': days >= 0 && days <= 20 — rejected write of 21 (key "r1"); the slot is unchanged
✓ assert simulate_days("r1", 1) == "overdue"
✓ assert log_day("r1").ok == false
✓ assert fulfill("r1").ok == true
✓ assert_fails transition("r1", "processing") — raised invalid transition: fulfilled → processing. Current state: 'fulfilled'. Valid targets: []
✓ assert_fails transition("r1", "overdue") — raised invalid transition: fulfilled → overdue. Current state: 'fulfilled'. Valid targets: []
✓ assert assign_officer("r2").ok == true
✓ assert simulate_days("r2", 5) == "processing"
✓ assert fulfill("r2").ok == true
✓ assert request_state("r2") == "fulfilled"
14 tests: 14 passed, 0 failed$ soma verify foia.cell
Verifying foia.cell...
State machine 'request': 4 states, initial 'received'
States: [fulfilled, overdue, processing, received]
✓ 4 states, 4 transitions
✓ all states reachable from 'received'
✓ terminal states: [fulfilled]
✓ 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 6 handlers structurally terminate
✓ refinement: handler `assign_officer` ⟶ {processing}
✓ refinement: handler `log_day` ⟶ {overdue [if w.error != ()]}
✓ refinement: handler `fulfill` ⟶ {fulfilled}
· transition sources: `assign_officer`, `log_day`, `fulfill` 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)
Foia/invariants:
✓ invariant days >= 0 && days <= 20 — writer 'assign_officer' proven (writes 0)
✓ invariant days >= 0 — writer 'log_day' proven by induction (writes d + 1)
⚠ invariant days >= 0 && days <= 20 — runtime-checked (computed values): log_day → days [days <= 20] because `d + 1` is only known to lie in [1, 21] — narrow it: `require (d + 1) <= 20 else …`
12 passed, 1 warnings, 0 failures
Temporal properties for 'request':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK