Review with holes — a design that is proven before it is finished
Three of the seven signals have a body; the other four are HOLES: the skeleton declares them, no flesh fills them yet. That is a state the toolchain understands: VERIFY OK (skeleton — the holes above are open obligations) after the holes it calls): accept and decline before decide calling one → fails with kind "not_implemented", naming the signal The tests below exercise what exists and pin how a hole behaves.
A Soma program from the skeleton part of the verified corpus: 90 lines, cells Review, ReviewSoFar. It passes soma check with soma 3.2.4.
How it is built
The cell Review declares 7 signals in its face: draft(id: String, author: String) -> String, submit(id: String, author: String) -> String, score(id: String, points: Int) -> Int, accept(id: String) -> String, decline(id: String) -> String, decide(id: String) -> String and revise(id: String, author: String) -> String; keeps 2 memory slots: authors (Map<String, String>, persistent) and scores (Map<String, Int>, persistent) under the invariant scores >= 0 && scores <= 10. Its state machine paper starts in draft and allows 4 transitions: draft -> submitted, submitted -> approved, submitted -> rejected and rejected -> draft. approved is terminal. Handlers: draft (line 57), submit (line 63), score (line 69), accept (line 45), decline (line 46), decide (line 47) and revise (line 50).
The file also holds 1 test cell (ReviewSoFar) with 9 assertions, run by soma test.
The source
review_with_holes.cell
// REVIEW WITH HOLES — a design that is proven before it is finished.
//
// Three of the seven signals have a body; the other four are HOLES: the
// skeleton declares them, no flesh fills them yet. That is a state the
// toolchain understands:
//
// soma check → passes, and counts the holes
// soma verify → proves the lifecycle, then
// VERIFY OK (skeleton — the holes above are open obligations)
// soma holes → lists them in the order to write them (a hole comes
// after the holes it calls): accept and decline before decide
// calling one → fails with kind "not_implemented", naming the signal
//
// The tests below exercise what exists and pin how a hole behaves.
//
// soma holes review_with_holes.cell
// soma skeleton review_with_holes.cell Review.decide
// soma test review_with_holes.cell
skeleton Review {
memory {
authors: Map<String, String> [persistent]
scores: Map<String, Int> [persistent]
invariant scores >= 0 && scores <= 10
}
state paper {
initial: draft
draft -> submitted
submitted -> approved
submitted -> rejected
rejected -> draft // revise and resubmit
}
face {
signal draft(id: String, author: String) -> String
writes authors
signal submit(id: String, author: String) -> String
reads authors
moves draft -> submitted
signal score(id: String, points: Int) -> Int
requires points >= 0
requires points <= 10
writes scores
signal accept(id: String) -> String moves submitted -> approved // hole
signal decline(id: String) -> String moves submitted -> rejected // hole
signal decide(id: String) -> String // hole
reads scores
calls accept, decline
signal revise(id: String, author: String) -> String // hole
reads authors
moves rejected -> draft
}
}
flesh Review {
on draft(id, author) {
require authors.get(id) == () else Taken "paper '{id}' exists"
authors.set(id, author)
return id
}
on submit(id, author) {
require authors.get(id) == author else Forbidden "only its author submits a paper"
transition(id, "draft", "submitted")
return "submitted"
}
on score(id, points) {
scores.set(id, points)
return points
}
}
cell test ReviewSoFar {
rules {
assert draft("p1", "ada") == "p1"
assert_fails submit("p1", "bob") matching "Forbidden"
assert submit("p1", "ada") == "submitted"
assert score("p1", 8) == 8
assert_fails score("p1", 11) // the contract, before the invariant
assert_fails scores.set("p1", 0 - 1) // the invariant itself
// a hole fails by name; nothing it would have done has happened
assert_fails decide("p1") matching "not_implemented"
assert_fails accept("p1") matching "Review.accept is a hole"
assert get_status("p1") == "submitted"
}
}What the toolchain says
$ soma check review_with_holes.cell
✓ All checks passed.
○ 4 holes — signals of a skeleton with no body yet: Review.accept, Review.decline, Review.decide, Review.revise (`soma holes review_with_holes.cell`)$ soma test review_with_holes.cell
test ReviewSoFar ...
✓ assert draft("p1", "ada") == "p1"
✓ assert_fails submit("p1", "bob") matching "Forbidden" — raised Forbidden: only its author submits a paper
✓ assert submit("p1", "ada") == "submitted"
✓ assert score("p1", 8) == 8
✓ assert_fails score("p1", 11) — raised score(): the face requires `points <= 10` — called with points = 11
✓ assert_fails scores.set("p1", 0 - 1) — raised memory invariant violated on 'scores': scores >= 0 && scores <= 10 — rejected write of -1 (key "p1"); the slot is unchanged
✓ assert_fails decide("p1") matching "not_implemented" — raised not_implemented: Review.decide is a hole of the skeleton: no flesh gives it a body yet
✓ assert_fails accept("p1") matching "Review.accept is a hole" — raised not_implemented: Review.accept is a hole of the skeleton: no flesh gives it a body yet
✓ assert get_status("p1") == "submitted"
9 tests: 9 passed, 0 failed$ soma verify review_with_holes.cell
Verifying review_with_holes.cell...
State machine 'paper': 4 states, initial 'draft'
States: [approved, draft, rejected, submitted]
✓ 4 states, 4 transitions
✓ all states reachable from 'draft'
✓ terminal states: [approved]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
· think-isolated: judged when the 4 holes of the program have a body
· termination: 4 holes — judged when they have a body
✓ termination: all 3 handlers structurally terminate
✓ refinement: handler `submit` ⟶ {draft → submitted}
· refinement: `accept` is a hole — its frame allows {submitted → approved}
· refinement: `decline` is a hole — its frame allows {submitted → rejected}
· refinement: `revise` is a hole — its frame allows {rejected → draft}
Review/invariants:
✓ invariant scores >= 0 && scores <= 10 — writer 'score' proven by induction (writes points)
Review/contracts:
· score: requires points >= 0 — no call site in the program; checked at run time on every call from outside (kind `requires`)
· score: requires points <= 10 — no call site in the program; checked at run time on every call from outside (kind `requires`)
8 passed, 0 warnings, 0 failures
Temporal properties for 'paper':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
○ 4 holes — signals of a skeleton with no body yet: Review.accept, Review.decline, Review.decide, Review.revise (`soma holes review_with_holes.cell`)
VERIFY OK (skeleton — the holes above are open obligations)