Docs
/
the grid-authoring language

lmg

Write a Leumas programming grid as text and run it. Safe mode, no bridge required — it works from the CLI, the API and the Playground alike.

lmg — the grid-authoring language

Write a Leumas programming grid as text and run it. Safe mode, no bridge required — it works from the CLI, the API and the Playground alike.

GRID "pricing"

LAYER default
  A1 variable 1200
  A2 variable 0.08
  B1 code $A1 * (1 + $A2)
  C1 conditional $B1 > 1000 ? "high" : "low"
  D1 template "tier {{ $C1 }} at {{ $B1 }}"

RUN B1, C1, D1
GRAPH
grid "pricing" — 1 layer(s), 5 cell(s)
default.B1 = 1296
default.C1 = high
default.D1 = tier high at 1296
--- dependency order ---
  default.A1
  default.A2
  default.B1  ← default.A1, default.A2
  default.C1  ← default.B1
  default.D1  ← default.C1, default.B1

What a grid actually is

Not a spreadsheet. A grid is layers of cells, and a cell is an action. A1 is not a number in a box; it is a variable action that produces one. B1 is a code action. There are ~21 cell types.

Cells reference each other with $refs, and @leumas/grids owns everything hard about that: it resolves refs concurrently, memoises so asking twice costs one run, checks for cycles, and evaluates code cells in a node:vm sandbox with no process, require or fetch.

lmg writes no evaluator. It is a syntax for that document, and the document it produces is the same shape the Studio grid editor persists.


Syntax

LineMeaning
GRID "name"names the grid (optional)
LAYER <name>starts a layer; a name may contain spaces
<REF> <type> <rest>a cell
<REF> <type> doenda cell whose body spans lines
RUN a, b, cexecute and print those cells
DUMPprint every cell computed so far
GRAPHprint the dependency order

# starts a comment — except inside a do … end body, which is taken verbatim. A # in a code cell is JavaScript, and eating it would corrupt exactly the cells most likely to have one.

Cell refs are uppercase letters then digits (A1, B12, AA3) — that is the ref grammar @leumas/grids defines, not a choice made here. Cells declared before any LAYER go into a layer called default.

The one parsing rule

After <REF> <type>:

  • if the rest of the line starts with --, it is parsed as flags and nothing else;
  • otherwise the entire rest of the line is the cell's primary field, -- and all.

So B1 code $A1 - -2 means what it looks like, and B1 json --input=$A1 --op=get --path=name also means what it looks like. Nothing has to guess.

Flag values are JSON-coerced (--limit=5 is the number 5, --keys=["a"] is an array), and a bare --flag is true.


$refs

FormResolves to
$A1A1 in the same layer as the cell writing it
$default.A1A1 in the layer literally named default
$Layer 2.B2B2 in the 2nd layer, 1-based

RUN targets are different: a bare RUN B1 has no owning layer, so it resolves to the layer that declares B1. If two layers declare it, lmg refuses and asks you to qualify it rather than picking one.


Cell types

Every type in allCellAdapters is available. The ones with a bare-rest-of-line form:

TypeBare form fillsExample
variable / valuevalueA1 variable 1200
codecodeB1 code $A1 * 2
conditionalsee belowC1 conditional $B1 > 100 ? "hi" : "lo"
templatetemplateD1 template "tier {{ $C1 }}"
aggregate map transform switch json csv regex validate hashinputE1 json $D1
merge randommodeF1 random float
datetimeopG1 datetime now
statekeyH1 state cart
endpoint urlurl(needs LMX_ENABLE_NET)
mediaitems

Any other type still works — name its fields with flags. A type with no bare form is an error rather than a guess, because a wrong guess runs.

conditional sugar

C1 conditional $B1 > 1000 ? "high" : "low"

desugars to --condition="$B1 > 1000" --trueResult=high --falseResult=low. The explicit flags are accepted too. ? and : inside quotes are ignored, so conditional $A1 = "who? me:" ? "yes" : "no" splits where you expect.

template slots take $refs, not bare names

D1 template "tier {{ $C1 }} at {{ $B1 | fixed:2 }}"

{{ C1 }} without the $ is not a ref — it renders the literal text C1. Helpers after | come from the template adapter: upper, lower, json, fixed:n, default:x.


[critical] The network gate

Two cell types make outbound requests: endpoint and url. Every other LMX mode that can reach the network (lmnet) is admin-gated behind LMX_ENABLE_NET, so lmg — a free mode — cannot be the open door to the same reach.

Without LMX_ENABLE_NET=1 those two adapters are replaced, not removed, and fail with:

"endpoint" cells reach the network — set LMX_ENABLE_NET=1 to allow them.

Removed, they would read as "unknown cell type", which sends the author hunting for a typo instead of at the policy.

The privileged lmx, adapter and exec cell types that leumas-api injects into its own grids domain are also out of reach here: a free script must not reach the ecosystem through a cell when it cannot reach it through adapter().


One implementation detail worth knowing

lmg writes each cell's options into specialConfig, and a code cell's source into a top-level code field — not into props.

Every adapter merges props over specialConfig, so either would execute correctly. But buildDependencyGraph reads only code and specialConfig. A props-authored grid therefore runs perfectly and reports an empty dependency graphGRAPH prints every cell with no edges, and nothing in the output says it is lying. Writing specialConfig also makes an lmg grid identical to one the Studio editor produced.


What it deliberately cannot do

  • Open or save a stored grid. Inline grids only, so there is no ownership question and no write

path around /api/grids and its PassNode meter.

  • Reach the ecosystem. No lmx/adapter/exec cells.
  • Reach the network without the flag.

Running it

leumas-lmx ./pricing.lmg
curl -s -X POST localhost:3000/api/lmx/run \
  -H 'content-type: application/json' \
  -d '{"mode":"lmg","source":"LAYER default\n  A1 variable 4\n  B1 code $A1 * 3\nRUN B1"}'

Bundled examples: shared/engines/lmx/src/scripts/demo.lmg, shared/engines/lmx/src/scripts/pricing-model.lmg. Tests: shared/engines/lmx/src/test/lmg.test.js. Guard: pnpm smoke:lmx.

Source .claude/skills/leumas-lmx/reference/lmg.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt
Docs
/
the grid-authoring language

lmg

Write a Leumas programming grid as text and run it. Safe mode, no bridge required — it works from the CLI, the API and the Playground alike.

lmg — the grid-authoring language

Write a Leumas programming grid as text and run it. Safe mode, no bridge required — it works from the CLI, the API and the Playground alike.

GRID "pricing"

LAYER default
  A1 variable 1200
  A2 variable 0.08
  B1 code $A1 * (1 + $A2)
  C1 conditional $B1 > 1000 ? "high" : "low"
  D1 template "tier {{ $C1 }} at {{ $B1 }}"

RUN B1, C1, D1
GRAPH
grid "pricing" — 1 layer(s), 5 cell(s)
default.B1 = 1296
default.C1 = high
default.D1 = tier high at 1296
--- dependency order ---
  default.A1
  default.A2
  default.B1  ← default.A1, default.A2
  default.C1  ← default.B1
  default.D1  ← default.C1, default.B1

What a grid actually is

Not a spreadsheet. A grid is layers of cells, and a cell is an action. A1 is not a number in a box; it is a variable action that produces one. B1 is a code action. There are ~21 cell types.

Cells reference each other with $refs, and @leumas/grids owns everything hard about that: it resolves refs concurrently, memoises so asking twice costs one run, checks for cycles, and evaluates code cells in a node:vm sandbox with no process, require or fetch.

lmg writes no evaluator. It is a syntax for that document, and the document it produces is the same shape the Studio grid editor persists.


Syntax

LineMeaning
GRID "name"names the grid (optional)
LAYER <name>starts a layer; a name may contain spaces
<REF> <type> <rest>a cell
<REF> <type> doenda cell whose body spans lines
RUN a, b, cexecute and print those cells
DUMPprint every cell computed so far
GRAPHprint the dependency order

# starts a comment — except inside a do … end body, which is taken verbatim. A # in a code cell is JavaScript, and eating it would corrupt exactly the cells most likely to have one.

Cell refs are uppercase letters then digits (A1, B12, AA3) — that is the ref grammar @leumas/grids defines, not a choice made here. Cells declared before any LAYER go into a layer called default.

The one parsing rule

After <REF> <type>:

  • if the rest of the line starts with --, it is parsed as flags and nothing else;
  • otherwise the entire rest of the line is the cell's primary field, -- and all.

So B1 code $A1 - -2 means what it looks like, and B1 json --input=$A1 --op=get --path=name also means what it looks like. Nothing has to guess.

Flag values are JSON-coerced (--limit=5 is the number 5, --keys=["a"] is an array), and a bare --flag is true.


$refs

FormResolves to
$A1A1 in the same layer as the cell writing it
$default.A1A1 in the layer literally named default
$Layer 2.B2B2 in the 2nd layer, 1-based

RUN targets are different: a bare RUN B1 has no owning layer, so it resolves to the layer that declares B1. If two layers declare it, lmg refuses and asks you to qualify it rather than picking one.


Cell types

Every type in allCellAdapters is available. The ones with a bare-rest-of-line form:

TypeBare form fillsExample
variable / valuevalueA1 variable 1200
codecodeB1 code $A1 * 2
conditionalsee belowC1 conditional $B1 > 100 ? "hi" : "lo"
templatetemplateD1 template "tier {{ $C1 }}"
aggregate map transform switch json csv regex validate hashinputE1 json $D1
merge randommodeF1 random float
datetimeopG1 datetime now
statekeyH1 state cart
endpoint urlurl(needs LMX_ENABLE_NET)
mediaitems

Any other type still works — name its fields with flags. A type with no bare form is an error rather than a guess, because a wrong guess runs.

conditional sugar

C1 conditional $B1 > 1000 ? "high" : "low"

desugars to --condition="$B1 > 1000" --trueResult=high --falseResult=low. The explicit flags are accepted too. ? and : inside quotes are ignored, so conditional $A1 = "who? me:" ? "yes" : "no" splits where you expect.

template slots take $refs, not bare names

D1 template "tier {{ $C1 }} at {{ $B1 | fixed:2 }}"

{{ C1 }} without the $ is not a ref — it renders the literal text C1. Helpers after | come from the template adapter: upper, lower, json, fixed:n, default:x.


[critical] The network gate

Two cell types make outbound requests: endpoint and url. Every other LMX mode that can reach the network (lmnet) is admin-gated behind LMX_ENABLE_NET, so lmg — a free mode — cannot be the open door to the same reach.

Without LMX_ENABLE_NET=1 those two adapters are replaced, not removed, and fail with:

"endpoint" cells reach the network — set LMX_ENABLE_NET=1 to allow them.

Removed, they would read as "unknown cell type", which sends the author hunting for a typo instead of at the policy.

The privileged lmx, adapter and exec cell types that leumas-api injects into its own grids domain are also out of reach here: a free script must not reach the ecosystem through a cell when it cannot reach it through adapter().


One implementation detail worth knowing

lmg writes each cell's options into specialConfig, and a code cell's source into a top-level code field — not into props.

Every adapter merges props over specialConfig, so either would execute correctly. But buildDependencyGraph reads only code and specialConfig. A props-authored grid therefore runs perfectly and reports an empty dependency graphGRAPH prints every cell with no edges, and nothing in the output says it is lying. Writing specialConfig also makes an lmg grid identical to one the Studio editor produced.


What it deliberately cannot do

  • Open or save a stored grid. Inline grids only, so there is no ownership question and no write

path around /api/grids and its PassNode meter.

  • Reach the ecosystem. No lmx/adapter/exec cells.
  • Reach the network without the flag.

Running it

leumas-lmx ./pricing.lmg
curl -s -X POST localhost:3000/api/lmx/run \
  -H 'content-type: application/json' \
  -d '{"mode":"lmg","source":"LAYER default\n  A1 variable 4\n  B1 code $A1 * 3\nRUN B1"}'

Bundled examples: shared/engines/lmx/src/scripts/demo.lmg, shared/engines/lmx/src/scripts/pricing-model.lmg. Tests: shared/engines/lmx/src/test/lmg.test.js. Guard: pnpm smoke:lmx.

Source .claude/skills/leumas-lmx/reference/lmg.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt
is not a ref — it renders the literal text `C1`. Helpers after `|` come\nfrom the template adapter: `upper`, `lower`, `json`, `fixed:n`, `default:x`.\n\n---\n\n## [critical] The network gate\n\nTwo cell types make outbound requests: **`endpoint`** and **`url`**. Every other LMX mode that can\nreach the network (`lmnet`) is admin-gated behind `LMX_ENABLE_NET`, so `lmg` — a *free* mode — cannot\nbe the open door to the same reach.\n\nWithout `LMX_ENABLE_NET=1` those two adapters are **replaced**, not removed, and fail with:\n\n```\n\"endpoint\" cells reach the network — set LMX_ENABLE_NET=1 to allow them.\n```\n\nRemoved, they would read as \"unknown cell type\", which sends the author hunting for a typo instead of\nat the policy.\n\nThe privileged `lmx`, `adapter` and `exec` cell types that `leumas-api` injects into its own grids\ndomain are also out of reach here: a free script must not reach the ecosystem through a cell when it\ncannot reach it through `adapter()`.\n\n---\n\n## One implementation detail worth knowing\n\n`lmg` writes each cell's options into **`specialConfig`**, and a code cell's source into a top-level\n**`code`** field — not into `props`.\n\nEvery adapter merges `props` over `specialConfig`, so either would execute correctly. But\n`buildDependencyGraph` reads only `code` and `specialConfig`. A props-authored grid therefore *runs*\nperfectly and reports an **empty dependency graph** — `GRAPH` prints every cell with no edges, and\nnothing in the output says it is lying. Writing `specialConfig` also makes an `lmg` grid identical to\none the Studio editor produced.\n\n---\n\n## What it deliberately cannot do\n\n- **Open or save a stored grid.** Inline grids only, so there is no ownership question and no write\n path around `/api/grids` and its PassNode meter.\n- **Reach the ecosystem.** No `lmx`/`adapter`/`exec` cells.\n- **Reach the network** without the flag.\n\n---\n\n## Running it\n\n```bash\nleumas-lmx ./pricing.lmg\n```\n\n```bash\ncurl -s -X POST localhost:3000/api/lmx/run \\\n -H 'content-type: application/json' \\\n -d '{\"mode\":\"lmg\",\"source\":\"LAYER default\\n A1 variable 4\\n B1 code $A1 * 3\\nRUN B1\"}'\n```\n\nBundled examples: `shared/engines/lmx/src/scripts/demo.lmg`, `shared/engines/lmx/src/scripts/pricing-model.lmg`.\nTests: `shared/engines/lmx/src/test/lmg.test.js`. Guard: `pnpm smoke:lmx`.\n","source":{"path":".claude/skills/leumas-lmx/reference/lmg.md","commit":"","provenance":"no-git"},"urls":{"html":"/p/skills/leumas-lmx/lmg","json":"/docs/skills/leumas-lmx/lmg.json","md":"/docs/skills/leumas-lmx/lmg.md"},"links":{"composes":[],"usedBy":[],"product":[],"howTo":[],"skills":[]}}}