Block grammar

Every runnable block in a .work file is written the same way:

<kind> [lang] :name … do … end

That one line of grammar covers all of it. Let's take the header apart.

The header, token by token

server elixir :submit
server place :submit do
  def run(order), do: Store.create(Order, order)
end

Reading the header server place :submit:

So sandbox rust :scrape do is kind sandbox, language rust, name scrape. And client :widget do is a browser island named widget.

do … end is the delimiter

A block opens with a non-indented line that ends in do, and closes with the first end at column 0. Everything between is the body — real code in the block's language. Plain prose never ends in do, so there's no ambiguity about where a block begins.

The body can have its own do … end pairs — an if, a case, a nested function. Keep those ends indented: the block closes at the first non-indented end, so the only end written hard against the left margin is the one that closes the block. Write the language naturally with normal indentation and it just works.

A name without a colon

Some blocks name themselves the way the host language does — defmodule Workbook do declares something called Workbook. When there's no :atom, the parser takes the bare name. You'll mostly use :name, but both are understood.

Why it's a tree, not a regex

This grammar isn't matched with fragile text patterns. Because do … end is the delimiter and the header tokenizes cleanly, each block parses into a real syntax node — kind, language, name, and body are read straight off the structure. That single parse is shared by everything that touches a workbook: highlighting, the dependency graph, and the capability audit. Write the block once; every tool reads it the same way.

Next, the lane that surrounds the blocks: The prose lane & refs.