Workbooks Documentation

OQL (Org Query Language)

OQL is the WASM kernel at the centre of the workbooks runtime. It is a Rust library compiled to wasm32-wasi, wrapped as a WIT component, and embedded in the runtime binary. Every parse, render, tangle, and lint operation goes through OQL. The desktop app also embeds it — local workbook rendering requires no server.

What OQL is

OQL is not a query language in the database sense. The name reflects its purpose: it is the query interface over an Org file. You ask it to parse a workbook into headlines, extract a build plan, render to HTML, or validate structure. The answers come back as structured data or HTML — no side effects, no I/O.

The Rust source is in runtime/kernel/. The compiled output is oql.wasm. The Elixir wrapper is runtime/host/oql.ex.

Exported functions

OQL exports five functions, all accessible via Workbooks.OQL.*:

parseheadlines

Workbooks.OQL.parse_headlines(org :: String.t()) :: [map()]

Returns every heading in the org source as a list of maps:

[
  {
    "level": 1,
    "title": "My Workbook",
    "tags": [],
    "properties": {"WORKBOOK_VERSION": "1"},
    "position": {"start": 0, "end": 45}
  },
  {
    "level": 2,
    "title": "Compute",
    "tags": [],
    "properties": {"in": "x", "out": "result"},
    "position": {"start": 46, "end": 120}
  }
]

This is used by wb query, toolkit discovery, and any agent tool that needs to inspect workbook structure without rendering it.

tangleplan

Workbooks.OQL.tangle_plan(org :: String.t()) :: map()

Returns the build plan: worlds, components, edges, imports, exports. This is the DAG that drives WASM compilation and execution. See The Build Plan for the complete schema.

render

Workbooks.OQL.render(org :: String.t()) :: String.t()

Renders the org source to an HTML fragment (not a full page — headings, prose, code blocks, tables, lists). The caller wraps it in a page shell (PublicWeb.static_page/2 or PublicWeb.site_page/5).

The orgize Rust parser is used for rendering. Org elements map to semantic HTML: headings → <h2> / <h3>, source blocks → <pre><code>, tables → <table>, inline code → <code>, links → <a>.

Note: #+begin_example blocks that contain nested Org markers should use comma-escaping (prefix each inner marker with ,) to prevent the parser from closing the block early. Alternatively, use #+begin_src org for literal Org examples.

lint

Workbooks.OQL.lint(org :: String.t()) :: [map()]

Returns a list of issue maps. Currently a stub returning [] — full static analysis (undeclared deps, type mismatches, unreachable components) is a planned addition.

validate

Workbooks.OQL.validate(org :: String.t()) :: :ok | {:error, [String.t()]}

Checks that the org source is a valid workbook: has a :WORKBOOK_VERSION: property, no structural errors. Returns :ok or an error tuple with a list of human-readable issues.

checkupgrade

Workbooks.OQL.check_upgrade(org :: String.t()) :: map()

Returns upgrade advice for a workbook at an older WORKBOOK_VERSION. Returns a map with needs_upgrade: false if the workbook is current.

The Rust kernel

The kernel is in runtime/kernel/src/lib.rs. Key types:

TypeRole
HeadlineA parsed org heading with level, title, tags, properties, children
SrcA source block with language, content, and header args
ArgsParsed :in:, :out:, :deps:, :sig: properties
CompA component: heading ID + args
SigAn explicit WIT function signature from :sig:
WorldA compilable unit: source + language

The orgize crate does the actual Org parsing. The kernel walks the orgize AST to collect headings and source blocks, then builds the plan from the collected data.

WIT interface

OQL is exposed as a WIT component. The WIT world is in runtime/kernel/wit/world.wit. The five exported functions map to the five Workbooks.OQL.* calls. The Wasmex NIF calls the WIT exports directly; no serialization layer between Elixir and the WASM boundary.

Where oql.wasm comes from

At mix deps.compile time, scripts/provision-tools.sh ensures javy, wasm-tools, and wasi-adapter are on PATH, then mix compile runs the Rust build (cargo build --target wasm32-wasi), wraps the output with wasm-tools component new, and embeds the result via a Mix task into the Wasmex NIF. The final oql.wasm lives at priv/oql.wasm and is baked into the escript/release.