{
  "schema": "leumas.docs.page/1",
  "id": "skill:leumas-lmx#lmg",
  "slug": "skills/leumas-lmx/lmg",
  "kind": "tools",
  "bucket": "skill",
  "title": "lmg — the grid-authoring language",
  "name": "lmg",
  "eyebrow": "the grid-authoring language",
  "chip": null,
  "summary": "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.",
  "keywords": [
    "leumas-lmx",
    "alike",
    "programming",
    "playground",
    "required",
    "grid",
    "cell types",
    "bridge"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# `lmg` — the grid-authoring language\n\nWrite a Leumas programming grid as text and run it. **Safe** mode, **no bridge required** — it works\nfrom the CLI, the API and the Playground alike.\n\n```lmg\nGRID \"pricing\"\n\nLAYER default\n  A1 variable 1200\n  A2 variable 0.08\n  B1 code $A1 * (1 + $A2)\n  C1 conditional $B1 > 1000 ? \"high\" : \"low\"\n  D1 template \"tier {{ $C1 }} at {{ $B1 }}\"\n\nRUN B1, C1, D1\nGRAPH\n```\n\n```\ngrid \"pricing\" — 1 layer(s), 5 cell(s)\ndefault.B1 = 1296\ndefault.C1 = high\ndefault.D1 = tier high at 1296\n--- dependency order ---\n  default.A1\n  default.A2\n  default.B1  ← default.A1, default.A2\n  default.C1  ← default.B1\n  default.D1  ← default.C1, default.B1\n```\n\n---\n\n## What a grid actually is\n\n**Not a spreadsheet.** A grid is layers of cells, and **a cell is an action**. `A1` is not a number in\na box; it is a `variable` action that produces one. `B1` is a `code` action. There are ~21 cell types.\n\nCells reference each other with `$refs`, and `@leumas/grids` owns everything hard about that: it\nresolves refs concurrently, memoises so asking twice costs one run, checks for cycles, and evaluates\n`code` cells in a `node:vm` sandbox with no `process`, `require` or `fetch`.\n\n`lmg` writes no evaluator. It is a syntax for that document, and the document it produces is the same\nshape the Studio grid editor persists.\n\n---\n\n## Syntax\n\n| Line | Meaning |\n|---|---|\n| `GRID \"name\"` | names the grid (optional) |\n| `LAYER <name>` | starts a layer; a name may contain spaces |\n| `<REF> <type> <rest>` | a cell |\n| `<REF> <type> do` … `end` | a cell whose body spans lines |\n| `RUN a, b, c` | execute and print those cells |\n| `DUMP` | print every cell computed so far |\n| `GRAPH` | print the dependency order |\n\n`#` starts a comment — **except inside a `do … end` body**, which is taken verbatim. A `#` in a code\ncell is JavaScript, and eating it would corrupt exactly the cells most likely to have one.\n\nCell refs are uppercase letters then digits (`A1`, `B12`, `AA3`) — that is the ref grammar\n`@leumas/grids` defines, not a choice made here. Cells declared before any `LAYER` go into a layer\ncalled `default`.\n\n### The one parsing rule\n\nAfter `<REF> <type>`:\n\n- if the rest of the line **starts with `--`**, it is parsed as flags and nothing else;\n- otherwise the **entire** rest of the line is the cell's primary field, `--` and all.\n\nSo `B1 code $A1 - -2` means what it looks like, and `B1 json --input=$A1 --op=get --path=name` also\nmeans what it looks like. Nothing has to guess.\n\nFlag values are JSON-coerced (`--limit=5` is the number 5, `--keys=[\"a\"]` is an array), and a bare\n`--flag` is `true`.\n\n---\n\n## `$refs`\n\n| Form | Resolves to |\n|---|---|\n| `$A1` | `A1` in the **same layer as the cell writing it** |\n| `$default.A1` | `A1` in the layer literally named `default` |\n| `$Layer 2.B2` | `B2` in the 2nd layer, 1-based |\n\n`RUN` targets are different: a bare `RUN B1` has no owning layer, so it resolves to **the layer that\ndeclares `B1`**. If two layers declare it, `lmg` refuses and asks you to qualify it rather than\npicking one.\n\n---\n\n## Cell types\n\nEvery type in `allCellAdapters` is available. The ones with a bare-rest-of-line form:\n\n| Type | Bare form fills | Example |\n|---|---|---|\n| `variable` / `value` | `value` | `A1 variable 1200` |\n| `code` | `code` | `B1 code $A1 * 2` |\n| `conditional` | see below | `C1 conditional $B1 > 100 ? \"hi\" : \"lo\"` |\n| `template` | `template` | `D1 template \"tier {{ $C1 }}\"` |\n| `aggregate` `map` `transform` `switch` `json` `csv` `regex` `validate` `hash` | `input` | `E1 json $D1` |\n| `merge` `random` | `mode` | `F1 random float` |\n| `datetime` | `op` | `G1 datetime now` |\n| `state` | `key` | `H1 state cart` |\n| `endpoint` `url` | `url` | *(needs `LMX_ENABLE_NET`)* |\n| `media` | `items` | |\n\nAny other type still works — name its fields with flags. A type with no bare form is an error rather\nthan a guess, because a wrong guess *runs*.\n\n### `conditional` sugar\n\n```lmg\nC1 conditional $B1 > 1000 ? \"high\" : \"low\"\n```\n\ndesugars to `--condition=\"$B1 > 1000\" --trueResult=high --falseResult=low`. The explicit flags are\naccepted too. `?` and `:` inside quotes are ignored, so\n`conditional $A1 = \"who? me:\" ? \"yes\" : \"no\"` splits where you expect.\n\n### `template` slots take `$refs`, not bare names\n\n```lmg\nD1 template \"tier {{ $C1 }} at {{ $B1 | fixed:2 }}\"\n```\n\n`{{ C1 }}` without the `$` 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",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 6883,
    "hash": "92c9e036428a45bb045e0453c770d5e94509291c"
  },
  "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": []
  }
}
