Data model
The value system that data-defs, records, collections, captures, and load all share: six value kinds, their JSON mapping, access paths,
conditions, and text coercion. Source: packages/parser/src/ast.ts.
The six DataValue kinds
| Kind | Literal form | Standalone render |
|---|---|---|
| stringValue | prose or a quoted string | escaped text |
| numberValue | 42 or 3.14 | the number as text |
| booleanValue | true or false | true or false as text |
| nullValue | empty in a table cell | |
| record | a brace map | a wit-record table |
| collection | a bracket list | a wit-collection list |
#title: Field notes ~ stringValue #count: 42 ~ numberValue #draft: true ~ booleanValue #note: null ~ nullValue #site: { name - Docs, year - 2026 } ~ record #tags: [ ref, spec, wit ] ~ collection
Records carry an ordered fields list of key/value pairs; collections
carry an ordered items list. Data-def records need the hyphen delimiter; the colon form is call-site only.
Access paths
A dotted lookup selects a field. Access is fuzzy: camelCase, snake_case, and spaced keys all match.
#person: { firstName - Ada, role - Author } Name is @person.first_name → Name is Ada
Iteration
(each @collection as item) … (end) binds each element to item. Inside the loop, reference it with @item (a
bare reference), or @item.field for a record field — not ::item:: (that is capture interpolation, a
different mechanism).
#hands: [ { name - Marlow }, { name - Singleton } ] @ul (each @hands as item) @li @item.name li@ (end) ul@
- Marlow
- Singleton
Iterating a non-collection raises E_NOT_ITERABLE. Fixture: 13-iteration/*.
Conditions
The test in an (if …) is either existence or a comparison.
| Form | True when |
|---|---|
| if path | the path resolves to a value |
| if path is X | the value equals X — identity |
| if path equals X | the value equals X — comparison |
(if @author) By @author.name (end) (if @status is shipped) Live (else) Draft (end)Fixture:
12-conditionals/*.
JSON to Wit mapping
The only shape that crosses the load seam.
| JSON | Wit value |
|---|---|
| object | record |
| array | collection |
| string | stringValue |
| number | numberValue |
| boolean | booleanValue |
| null or undefined | nullValue |
Text coercion
When a value is placed in text (a table cell or inline), it is coerced:
number and boolean → their string form; nullValue → an empty string.
typedValue
A capture param carries a shape-probed typedValue when it looks like a collection, record, or scalar literal; pure prose
keeps only its string value. This is what lets |rows @ref| and
typed captures work — the table renderer reads typedValue.
See also
Syntax· Tables · Config · Limitations.