soma.cell

Stock and orders — two skeletons, and what one may ask of the other

Frames are not transitive: `Orders.place` says `calls Stock.take`, not `writes items` — the slot belongs to Stock, and `take` has its own frame. So the skeleton answers "who can change the stock?" in one glance: the signals of Stock whose frame says `writes items`, reached only through the calls the other frames list. Writing `items.set(…)` inside an Orders body is refused twice over: another cell's slot, and outside the frame.

A Soma program from the skeleton part of the verified corpus: 133 lines, cells Stock, Orders, StockAndOrders. It passes soma check with soma 3.2.4.

How it is built

The cell Stock declares 3 signals in its face: add(sku: String, n: Int) -> Int, level(sku: String) -> Int and take(sku: String, n: Int) -> Bool; keeps 1 memory slot: items (Map<String, Int>, persistent) under the invariant items >= 0. Handlers: add (line 65), level (line 71) and take (line 75).

The cell Orders declares 4 signals in its face: place(id: String, sku: String, n: Int) -> String, ship(id: String) -> String, cancel(id: String) -> String and state_of(id: String) -> String; keeps 2 memory slots: lines (Map<String, String>, persistent) and counts (Map<String, Int>, persistent) under the invariant counts >= 1. Its state machine order starts in open and allows 4 transitions: open -> packed, open -> refused, packed -> shipped and packed -> cancelled. cancelled, refused and shipped are terminal. Handlers: place (line 84), ship (line 96), cancel (line 101) and state_of (line 107).

The file also holds 1 test cell (StockAndOrders) with 15 assertions, run by soma test.

Generated from soma describe on this file.

The source

stock_and_orders.cell

// STOCK AND ORDERS — two skeletons, and what one may ask of the other.
//
// Frames are not transitive: `Orders.place` says `calls Stock.take`, not
// `writes items` — the slot belongs to Stock, and `take` has its own frame.
// So the skeleton answers "who can change the stock?" in one glance: the
// signals of Stock whose frame says `writes items`, reached only through
// the calls the other frames list. Writing `items.set(…)` inside an Orders
// body is refused twice over: another cell's slot, and outside the frame.
//
//   soma check    stock_and_orders.cell
//   soma skeleton stock_and_orders.cell Stock.take     who calls it, what it owes
//   soma verify   stock_and_orders.cell
//   soma test     stock_and_orders.cell

skeleton Stock {
    memory {
        items: Map<String, Int> [persistent]
        invariant items >= 0
    }

    face {
        signal add(sku: String, n: Int) -> Int
            requires n >= 1
            writes items
            calls level
        signal level(sku: String) -> Int                reads items
        signal take(sku: String, n: Int) -> Bool
            requires n >= 1
            writes items
            calls level
    }
}

skeleton Orders {
    memory {
        lines: Map<String, String> [persistent]       // order -> sku
        counts: Map<String, Int> [persistent]         // order -> units
        invariant counts >= 1
    }

    state order {
        initial: open
        open -> packed
        open -> refused             // not enough stock
        packed -> shipped
        packed -> cancelled         // the units go back
    }

    face {
        signal place(id: String, sku: String, n: Int) -> String
            requires n >= 1
            writes lines, counts
            moves open -> packed, open -> refused
            calls Stock.take
        signal ship(id: String) -> String               moves packed -> shipped
        signal cancel(id: String) -> String
            reads lines, counts
            moves packed -> cancelled
            calls Stock.add
        signal state_of(id: String) -> String           reads order
    }
}

flesh Stock {
    on add(sku, n) {
        let now_has = level(sku) + n
        items.set(sku, now_has)
        return now_has
    }

    on level(sku) {
        return items.get(sku) ?? 0
    }

    on take(sku, n) {
        let have = level(sku)
        if have < n { return false }
        items.set(sku, have - n)
        return true
    }
}

flesh Orders {
    on place(id, sku, n) {
        require lines.get(id) == () else Taken "order '{id}' exists"
        if !take(sku, n) {
            transition(id, "open", "refused")
            return "refused"
        }
        lines.set(id, sku)
        counts.set(id, n)
        transition(id, "open", "packed")
        return "packed"
    }

    on ship(id) {
        transition(id, "packed", "shipped")
        return "shipped"
    }

    on cancel(id) {
        transition(id, "packed", "cancelled")
        add(lines.get(id), counts.get(id))
        return "cancelled"
    }

    on state_of(id) {
        return get_status(id) ?? "open"
    }
}

cell test StockAndOrders {
    rules {
        assert add("bolt", 10) == 10
        assert place("o1", "bolt", 4) == "packed"
        assert level("bolt") == 6
        assert place("o2", "bolt", 7) == "refused"       // 6 left: nothing is taken
        assert level("bolt") == 6
        assert state_of("o2") == "refused"
        assert_fails ship("o2")                          // refused is terminal

        assert cancel("o1") == "cancelled"               // the 4 units come back
        assert level("bolt") == 10
        assert_fails ship("o1")

        assert place("o3", "bolt", 10) == "packed"
        assert ship("o3") == "shipped"
        assert_fails cancel("o3")                        // shipped is terminal
        assert level("bolt") == 0
        assert_fails take("bolt", 0)                     // the contract: n >= 1
    }
}

What the toolchain says

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

$ soma check stock_and_orders.cell
✓ All checks passed.
$ soma test stock_and_orders.cell
test StockAndOrders ...
  ✓ assert add("bolt", 10) == 10
  ✓ assert place("o1", "bolt", 4) == "packed"
  ✓ assert level("bolt") == 6
  ✓ assert place("o2", "bolt", 7) == "refused"
  ✓ assert level("bolt") == 6
  ✓ assert state_of("o2") == "refused"
  ✓ assert_fails ship("o2") — raised invalid transition: refused → shipped — transition(id, "packed", "shipped") declares its source, and 'o2' is in 'refused', not 'packed'
  ✓ assert cancel("o1") == "cancelled"
  ✓ assert level("bolt") == 10
  ✓ assert_fails ship("o1") — raised invalid transition: cancelled → shipped — transition(id, "packed", "shipped") declares its source, and 'o1' is in 'cancelled', not 'packed'
  ✓ assert place("o3", "bolt", 10) == "packed"
  ✓ assert ship("o3") == "shipped"
  ✓ assert_fails cancel("o3") — raised invalid transition: shipped → cancelled — transition(id, "packed", "cancelled") declares its source, and 'o3' is in 'shipped', not 'packed'
  ✓ assert level("bolt") == 0
  ✓ assert_fails take("bolt", 0) — raised take(): the face requires `n >= 1` — called with n = 0

15 tests: 15 passed, 0 failed
$ soma verify stock_and_orders.cell
Verifying stock_and_orders.cell...

State machine 'order': 5 states, initial 'open'
  States: [cancelled, open, packed, refused, shipped]

  ✓ 5 states, 4 transitions
  ✓ all states reachable from 'open'
  ✓ terminal states: [refused, shipped, cancelled]
  ✓ no deadlocks
  ✓ liveness: every state can eventually reach a terminal state
  ✓ think-isolated: CTL safety properties hold regardless of LLM output (3 handlers, 4 literal transitions, 0 dynamic)
  ✓ termination: all 4 handlers structurally terminate
  ✓ refinement: handler `place` ⟶ {open → refused [if !take(sku, n)], open → packed}
  ✓ refinement: handler `ship` ⟶ {packed → shipped}
  ✓ refinement: handler `cancel` ⟶ {packed → cancelled}

Stock/invariants:

  ✓ invariant items >= 0 — writer 'add' proven by induction (writes now_has)
  ✓ invariant items >= 0 — writer 'take' proven by induction (writes have - n)

Orders/invariants:

  ✓ invariant counts >= 1 — writer 'place' proven by induction (writes n)

Stock/contracts:

  ✓ add: requires n >= 1 — proven at 1 call site (cancel); a call from outside is checked at run time
  ✓ take: requires n >= 1 — proven at 1 call site (place); a call from outside is checked at run time

Orders/contracts:

  · place: requires n >= 1 — no call site in the program; checked at run time on every call from outside (kind `requires`)

15 passed, 0 warnings, 0 failures

  Temporal properties for 'order':
  ✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
note: state machines are verified per cell. A value crosses a cell boundary by a face contract (`requires` / `ensures` on the signal); a rule between two cells' machines is proven when soma.toml states it — `[verify.before."Pay.paid"] requires = ["Approval.signed_off"]` explores the product of the machines.
VERIFY OK

Builtins used

get_status, transition

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