Kanban board: Card records stored by id in a Map slot, moved between columns by mutating a field and writing back
Demonstrates records in a keyed store, a column roll-up, and counting by a record field.
A Soma program from the records part of the verified corpus: 55 lines, cells Kanban, KanbanTest. It passes soma check with soma 3.2.4.
How it is built
The cell Kanban declares 4 signals in its face: add(title: String, col: String) -> String, move(id: String, col: String) -> Map, column(col: String) -> List and counts() -> Map; keeps 1 memory slot: cards (Map<String, Map>, persistent and consistent). Handlers: add (line 17), move (line 23), column (line 31) and counts (line 35).
The file also holds 1 test cell (KanbanTest) with 6 assertions, run by soma test.
The source
kanban.cell
// Kanban board: Card records stored by id in a Map slot, moved between
// columns by mutating a field and writing back. Demonstrates records in a
// keyed store, a column roll-up, and counting by a record field.
cell Kanban {
face {
signal add(title: String, col: String) -> String
signal move(id: String, col: String) -> Map
signal column(col: String) -> List
signal counts() -> Map
}
memory {
cards: Map<String, Map> [persistent, consistent]
}
on add(title: String, col: String) {
let id = "card-{next_id()}"
cards.set(id, Card { id: id, title: title, col: col })
return id
}
on move(id: String, col: String) {
let c = cards.get(id)
if c == () { return map("error", "no card") }
c.col = col
cards.set(id, c)
return map("id", id, "col", col)
}
on column(col: String) {
return cards.values |> filter(c => c.col == col) |> pluck("title")
}
on counts() {
let vals = cards.values
return map(
"todo", count(vals, c => c.col == "todo"),
"doing", count(vals, c => c.col == "doing"),
"done", count(vals, c => c.col == "done")
)
}
}
cell test KanbanTest {
rules {
assert starts_with(add("Spec", "todo"), "card-")
assert add("Build", "doing") != ""
assert move(add("Ship", "todo"), "done").col == "done"
assert counts().done == 1
assert len(column("todo")) == 1
assert move("card-999", "done").error == "no card"
}
}What the toolchain says
$ soma check kanban.cell
✓ All checks passed.$ soma test kanban.cell
test KanbanTest ...
✓ assert starts_with(add("Spec", "todo"), "card-")
✓ assert add("Build", "doing") != ""
✓ assert move(add("Ship", "todo"), "done").col == "done"
✓ assert counts().done == 1
✓ assert len(column("todo")) == 1
✓ assert move("card-999", "done").error == "no card"
6 tests: 6 passed, 0 failed$ soma verify kanban.cell
Verifying kanban.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)