soma.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: 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.

Generated from soma describe on this file.

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

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

$ 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)

Builtins used

get_status, transition

Source file: /corpus/skeleton/review_with_holes.cell · examples/corpus/skeleton/review_with_holes.cell